aboutsummaryrefslogtreecommitdiffstats
path: root/erts
diff options
context:
space:
mode:
Diffstat (limited to 'erts')
-rw-r--r--erts/AUTHORS2
-rw-r--r--erts/Makefile.in2
-rw-r--r--erts/aclocal.m4159
-rwxr-xr-xerts/autoconf/configure.vxworks2
-rw-r--r--erts/autoconf/vxworks/sed.general2
-rw-r--r--erts/autoconf/vxworks/sed.vxworks_cpu322
-rw-r--r--erts/autoconf/vxworks/sed.vxworks_ppc322
-rw-r--r--erts/autoconf/vxworks/sed.vxworks_ppc6032
-rw-r--r--erts/autoconf/vxworks/sed.vxworks_ppc603_nolongcall2
-rw-r--r--erts/autoconf/vxworks/sed.vxworks_ppc8602
-rw-r--r--erts/autoconf/vxworks/sed.vxworks_simlinux2
-rw-r--r--erts/autoconf/vxworks/sed.vxworks_simso2
-rw-r--r--erts/autoconf/vxworks/sed.vxworks_sparc2
-rw-r--r--erts/configure.in566
-rw-r--r--erts/doc/Makefile2
-rw-r--r--erts/doc/src/Makefile17
-rw-r--r--erts/doc/src/absform.xml1281
-rw-r--r--erts/doc/src/alt_dist.xml1306
-rw-r--r--erts/doc/src/book.xml2
-rw-r--r--erts/doc/src/communication.xml69
-rw-r--r--erts/doc/src/crash_dump.xml957
-rw-r--r--erts/doc/src/driver.xml453
-rw-r--r--erts/doc/src/driver_entry.xml644
-rw-r--r--erts/doc/src/epmd.xml447
-rw-r--r--erts/doc/src/erl.xml2239
-rw-r--r--erts/doc/src/erl_dist_protocol.xml1964
-rw-r--r--erts/doc/src/erl_driver.xml4887
-rw-r--r--erts/doc/src/erl_ext_dist.xml891
-rw-r--r--erts/doc/src/erl_nif.xml4063
-rw-r--r--erts/doc/src/erl_prim_loader.xml151
-rw-r--r--erts/doc/src/erl_tracer.xml778
-rw-r--r--erts/doc/src/erlang.xml7959
-rw-r--r--erts/doc/src/erlc.xml276
-rw-r--r--erts/doc/src/erlsrv.xml615
-rw-r--r--erts/doc/src/erts_alloc.xml1077
-rw-r--r--erts/doc/src/escript.xml400
-rw-r--r--erts/doc/src/inet_cfg.xml439
-rw-r--r--erts/doc/src/init.xml246
-rw-r--r--erts/doc/src/introduction.xml56
-rw-r--r--erts/doc/src/match_spec.xml1057
-rw-r--r--erts/doc/src/notes.xml1700
-rw-r--r--erts/doc/src/notes_history.xml2
-rw-r--r--erts/doc/src/part.xml4
-rw-r--r--erts/doc/src/part_notes.xml2
-rw-r--r--erts/doc/src/part_notes_history.xml2
-rw-r--r--erts/doc/src/ref_man.xml11
-rw-r--r--erts/doc/src/run_erl.xml258
-rw-r--r--erts/doc/src/specs.xml1
-rw-r--r--erts/doc/src/start.xml38
-rw-r--r--erts/doc/src/start_erl.xml203
-rw-r--r--erts/doc/src/time_correction.xml288
-rw-r--r--erts/doc/src/tty.xml69
-rw-r--r--erts/doc/src/werl.xml107
-rw-r--r--erts/doc/src/zlib.xml693
-rw-r--r--erts/emulator/Makefile2
-rw-r--r--erts/emulator/Makefile.in128
-rw-r--r--erts/emulator/beam/atom.c11
-rw-r--r--erts/emulator/beam/atom.h10
-rw-r--r--erts/emulator/beam/atom.names73
-rw-r--r--erts/emulator/beam/beam_bif_load.c1534
-rw-r--r--erts/emulator/beam/beam_bp.c301
-rw-r--r--erts/emulator/beam/beam_bp.h50
-rw-r--r--erts/emulator/beam/beam_catches.c2
-rw-r--r--erts/emulator/beam/beam_catches.h2
-rw-r--r--erts/emulator/beam/beam_debug.c152
-rw-r--r--erts/emulator/beam/beam_emu.c2809
-rw-r--r--erts/emulator/beam/beam_load.c1128
-rw-r--r--erts/emulator/beam/beam_load.h135
-rw-r--r--erts/emulator/beam/beam_ranges.c204
-rw-r--r--erts/emulator/beam/benchmark.c301
-rw-r--r--erts/emulator/beam/benchmark.h295
-rw-r--r--erts/emulator/beam/bif.c1104
-rw-r--r--erts/emulator/beam/bif.h112
-rw-r--r--erts/emulator/beam/bif.tab34
-rw-r--r--erts/emulator/beam/big.c457
-rw-r--r--erts/emulator/beam/big.h34
-rw-r--r--erts/emulator/beam/binary.c85
-rw-r--r--erts/emulator/beam/break.c247
-rw-r--r--erts/emulator/beam/code_ix.c7
-rw-r--r--erts/emulator/beam/code_ix.h4
-rw-r--r--erts/emulator/beam/copy.c1409
-rw-r--r--erts/emulator/beam/dist.c124
-rw-r--r--erts/emulator/beam/dist.h6
-rw-r--r--erts/emulator/beam/dtrace-wrapper.h2
-rw-r--r--erts/emulator/beam/elib_memmove.c2
-rw-r--r--erts/emulator/beam/erl_afit_alloc.c6
-rw-r--r--erts/emulator/beam/erl_afit_alloc.h2
-rw-r--r--erts/emulator/beam/erl_alloc.c517
-rw-r--r--erts/emulator/beam/erl_alloc.h68
-rw-r--r--erts/emulator/beam/erl_alloc.types81
-rw-r--r--erts/emulator/beam/erl_alloc_util.c463
-rw-r--r--erts/emulator/beam/erl_alloc_util.h96
-rw-r--r--erts/emulator/beam/erl_ao_firstfit_alloc.c8
-rw-r--r--erts/emulator/beam/erl_ao_firstfit_alloc.h2
-rw-r--r--erts/emulator/beam/erl_arith.c13
-rw-r--r--erts/emulator/beam/erl_async.c53
-rw-r--r--erts/emulator/beam/erl_async.h2
-rw-r--r--erts/emulator/beam/erl_bestfit_alloc.c6
-rw-r--r--erts/emulator/beam/erl_bestfit_alloc.h2
-rw-r--r--erts/emulator/beam/erl_bif_binary.c955
-rw-r--r--erts/emulator/beam/erl_bif_chksum.c2
-rw-r--r--erts/emulator/beam/erl_bif_ddll.c43
-rw-r--r--erts/emulator/beam/erl_bif_guard.c7
-rw-r--r--erts/emulator/beam/erl_bif_info.c762
-rw-r--r--erts/emulator/beam/erl_bif_lists.c103
-rw-r--r--erts/emulator/beam/erl_bif_op.c16
-rw-r--r--erts/emulator/beam/erl_bif_os.c2
-rw-r--r--erts/emulator/beam/erl_bif_port.c99
-rw-r--r--erts/emulator/beam/erl_bif_re.c14
-rw-r--r--erts/emulator/beam/erl_bif_trace.c779
-rw-r--r--erts/emulator/beam/erl_bif_unique.c32
-rw-r--r--erts/emulator/beam/erl_bif_unique.h14
-rw-r--r--erts/emulator/beam/erl_binary.h17
-rw-r--r--erts/emulator/beam/erl_bits.c6
-rw-r--r--erts/emulator/beam/erl_bits.h6
-rw-r--r--erts/emulator/beam/erl_cpu_topology.c2
-rw-r--r--erts/emulator/beam/erl_cpu_topology.h2
-rw-r--r--erts/emulator/beam/erl_db.c52
-rw-r--r--erts/emulator/beam/erl_db.h4
-rw-r--r--erts/emulator/beam/erl_db_hash.c66
-rw-r--r--erts/emulator/beam/erl_db_hash.h2
-rw-r--r--erts/emulator/beam/erl_db_tree.c244
-rw-r--r--erts/emulator/beam/erl_db_tree.h2
-rw-r--r--erts/emulator/beam/erl_db_util.c859
-rw-r--r--erts/emulator/beam/erl_db_util.h30
-rw-r--r--erts/emulator/beam/erl_debug.c49
-rw-r--r--erts/emulator/beam/erl_debug.h7
-rw-r--r--erts/emulator/beam/erl_driver.h43
-rw-r--r--erts/emulator/beam/erl_drv_nif.h11
-rw-r--r--erts/emulator/beam/erl_drv_thread.c2
-rw-r--r--erts/emulator/beam/erl_fun.c79
-rw-r--r--erts/emulator/beam/erl_fun.h14
-rw-r--r--erts/emulator/beam/erl_gc.c1997
-rw-r--r--erts/emulator/beam/erl_gc.h52
-rw-r--r--erts/emulator/beam/erl_goodfit_alloc.c6
-rw-r--r--erts/emulator/beam/erl_goodfit_alloc.h2
-rw-r--r--erts/emulator/beam/erl_hl_timer.c144
-rw-r--r--erts/emulator/beam/erl_hl_timer.h4
-rw-r--r--erts/emulator/beam/erl_init.c209
-rw-r--r--erts/emulator/beam/erl_instrument.c76
-rw-r--r--erts/emulator/beam/erl_instrument.h6
-rw-r--r--erts/emulator/beam/erl_lock_check.c21
-rw-r--r--erts/emulator/beam/erl_lock_check.h2
-rw-r--r--erts/emulator/beam/erl_lock_count.c6
-rw-r--r--erts/emulator/beam/erl_lock_count.h2
-rw-r--r--erts/emulator/beam/erl_map.c261
-rw-r--r--erts/emulator/beam/erl_map.h32
-rw-r--r--erts/emulator/beam/erl_math.c2
-rw-r--r--erts/emulator/beam/erl_message.c1816
-rw-r--r--erts/emulator/beam/erl_message.h430
-rw-r--r--erts/emulator/beam/erl_monitors.c4
-rw-r--r--erts/emulator/beam/erl_monitors.h2
-rw-r--r--erts/emulator/beam/erl_msacc.c488
-rw-r--r--erts/emulator/beam/erl_msacc.h434
-rw-r--r--erts/emulator/beam/erl_mtrace.c2
-rw-r--r--erts/emulator/beam/erl_mtrace.h2
-rw-r--r--erts/emulator/beam/erl_nif.c1297
-rw-r--r--erts/emulator/beam/erl_nif.h78
-rw-r--r--erts/emulator/beam/erl_nif_api_funcs.h39
-rw-r--r--erts/emulator/beam/erl_node_container_utils.h6
-rw-r--r--erts/emulator/beam/erl_node_tables.c183
-rw-r--r--erts/emulator/beam/erl_node_tables.h21
-rw-r--r--erts/emulator/beam/erl_port.h164
-rw-r--r--erts/emulator/beam/erl_port_task.c56
-rw-r--r--erts/emulator/beam/erl_port_task.h4
-rw-r--r--erts/emulator/beam/erl_printf_term.c33
-rw-r--r--erts/emulator/beam/erl_printf_term.h5
-rw-r--r--erts/emulator/beam/erl_process.c4917
-rw-r--r--erts/emulator/beam/erl_process.h492
-rw-r--r--erts/emulator/beam/erl_process_dict.c263
-rw-r--r--erts/emulator/beam/erl_process_dict.h19
-rw-r--r--erts/emulator/beam/erl_process_dump.c125
-rw-r--r--erts/emulator/beam/erl_process_lock.c193
-rw-r--r--erts/emulator/beam/erl_process_lock.h40
-rw-r--r--erts/emulator/beam/erl_ptab.c2
-rw-r--r--erts/emulator/beam/erl_ptab.h12
-rw-r--r--erts/emulator/beam/erl_sched_spec_pre_alloc.c2
-rw-r--r--erts/emulator/beam/erl_sched_spec_pre_alloc.h2
-rw-r--r--erts/emulator/beam/erl_smp.h2
-rw-r--r--erts/emulator/beam/erl_sock.h2
-rw-r--r--erts/emulator/beam/erl_sys_driver.h2
-rw-r--r--erts/emulator/beam/erl_term.c117
-rw-r--r--erts/emulator/beam/erl_term.h314
-rw-r--r--erts/emulator/beam/erl_thr_progress.c31
-rw-r--r--erts/emulator/beam/erl_thr_progress.h2
-rw-r--r--erts/emulator/beam/erl_thr_queue.c34
-rw-r--r--erts/emulator/beam/erl_thr_queue.h6
-rw-r--r--erts/emulator/beam/erl_threads.h36
-rw-r--r--erts/emulator/beam/erl_time.h4
-rw-r--r--erts/emulator/beam/erl_time_sup.c39
-rw-r--r--erts/emulator/beam/erl_trace.c3273
-rw-r--r--erts/emulator/beam/erl_trace.h98
-rw-r--r--erts/emulator/beam/erl_unicode.c60
-rw-r--r--erts/emulator/beam/erl_unicode.h2
-rw-r--r--erts/emulator/beam/erl_unicode_normalize.h2
-rw-r--r--erts/emulator/beam/erl_utils.h71
-rw-r--r--erts/emulator/beam/erl_vm.h34
-rw-r--r--erts/emulator/beam/erl_zlib.c2
-rw-r--r--erts/emulator/beam/erl_zlib.h2
-rw-r--r--erts/emulator/beam/erlang_dtrace.d31
-rw-r--r--erts/emulator/beam/erlang_lttng.c (renamed from erts/etc/ose/run_erl.h)22
-rw-r--r--erts/emulator/beam/erlang_lttng.h424
-rw-r--r--erts/emulator/beam/error.h2
-rw-r--r--erts/emulator/beam/export.c14
-rw-r--r--erts/emulator/beam/export.h10
-rw-r--r--erts/emulator/beam/external.c345
-rw-r--r--erts/emulator/beam/external.h9
-rw-r--r--erts/emulator/beam/global.h506
-rw-r--r--erts/emulator/beam/hash.c167
-rw-r--r--erts/emulator/beam/hash.h37
-rw-r--r--erts/emulator/beam/index.c13
-rw-r--r--erts/emulator/beam/index.h21
-rw-r--r--erts/emulator/beam/io.c1509
-rw-r--r--erts/emulator/beam/lttng-wrapper.h107
-rw-r--r--erts/emulator/beam/module.c14
-rw-r--r--erts/emulator/beam/module.h10
-rw-r--r--erts/emulator/beam/ops.tab904
-rw-r--r--erts/emulator/beam/packet_parser.c2
-rw-r--r--erts/emulator/beam/packet_parser.h2
-rw-r--r--erts/emulator/beam/register.c72
-rw-r--r--erts/emulator/beam/register.h6
-rw-r--r--erts/emulator/beam/safe_hash.c2
-rw-r--r--erts/emulator/beam/safe_hash.h7
-rw-r--r--erts/emulator/beam/sys.h304
-rw-r--r--erts/emulator/beam/time.c22
-rw-r--r--erts/emulator/beam/utils.c611
-rw-r--r--erts/emulator/beam/version.h2
-rw-r--r--erts/emulator/drivers/common/efile_drv.c24
-rw-r--r--erts/emulator/drivers/common/erl_efile.h8
-rw-r--r--erts/emulator/drivers/common/gzio.h2
-rw-r--r--erts/emulator/drivers/common/gzio_zutil.h2
-rw-r--r--erts/emulator/drivers/common/inet_drv.c1268
-rw-r--r--erts/emulator/drivers/common/ram_file_drv.c2
-rw-r--r--erts/emulator/drivers/common/zlib_drv.c2
-rw-r--r--erts/emulator/drivers/ose/ose_efile.c1125
-rw-r--r--erts/emulator/drivers/ose/ose_signal_drv.c897
-rw-r--r--erts/emulator/drivers/ose/ttsl_drv.c69
-rw-r--r--erts/emulator/drivers/unix/bin_drv.c2
-rw-r--r--erts/emulator/drivers/unix/multi_drv.c2
-rw-r--r--erts/emulator/drivers/unix/sig_drv.c2
-rw-r--r--erts/emulator/drivers/unix/ttsl_drv.c151
-rw-r--r--erts/emulator/drivers/unix/unix_efile.c115
-rw-r--r--erts/emulator/drivers/vxworks/vxworks_resolv.c2
-rw-r--r--erts/emulator/drivers/win32/registry_drv.c2
-rw-r--r--erts/emulator/drivers/win32/ttsl_drv.c2
-rw-r--r--erts/emulator/drivers/win32/win_con.c2
-rw-r--r--erts/emulator/drivers/win32/win_con.h2
-rw-r--r--erts/emulator/drivers/win32/win_efile.c2
-rw-r--r--erts/emulator/hipe/TODO2
-rw-r--r--erts/emulator/hipe/elf64ppc.x2
-rw-r--r--erts/emulator/hipe/hipe_amd64.c118
-rw-r--r--erts/emulator/hipe/hipe_amd64.h2
-rw-r--r--erts/emulator/hipe/hipe_amd64.tab2
-rw-r--r--erts/emulator/hipe/hipe_amd64_asm.m424
-rw-r--r--erts/emulator/hipe/hipe_amd64_bifs.m45
-rw-r--r--erts/emulator/hipe/hipe_amd64_gc.h2
-rw-r--r--erts/emulator/hipe/hipe_amd64_glue.S4
-rw-r--r--erts/emulator/hipe/hipe_amd64_glue.h2
-rw-r--r--erts/emulator/hipe/hipe_amd64_primops.h2
-rw-r--r--erts/emulator/hipe/hipe_arch.h2
-rw-r--r--erts/emulator/hipe/hipe_arm.c2
-rw-r--r--erts/emulator/hipe/hipe_arm.h2
-rw-r--r--erts/emulator/hipe/hipe_arm.tab2
-rw-r--r--erts/emulator/hipe/hipe_arm_asm.m434
-rw-r--r--erts/emulator/hipe/hipe_arm_bifs.m435
-rw-r--r--erts/emulator/hipe/hipe_arm_gc.h2
-rw-r--r--erts/emulator/hipe/hipe_arm_glue.S6
-rw-r--r--erts/emulator/hipe/hipe_arm_glue.h2
-rw-r--r--erts/emulator/hipe/hipe_arm_primops.h2
-rw-r--r--erts/emulator/hipe/hipe_bif0.c92
-rw-r--r--erts/emulator/hipe/hipe_bif0.h2
-rw-r--r--erts/emulator/hipe/hipe_bif0.tab2
-rw-r--r--erts/emulator/hipe/hipe_bif1.c756
-rw-r--r--erts/emulator/hipe/hipe_bif1.h2
-rw-r--r--erts/emulator/hipe/hipe_bif1.tab23
-rw-r--r--erts/emulator/hipe/hipe_bif2.c2
-rw-r--r--erts/emulator/hipe/hipe_bif2.tab2
-rw-r--r--erts/emulator/hipe/hipe_bif64.c2
-rw-r--r--erts/emulator/hipe/hipe_bif64.h2
-rw-r--r--erts/emulator/hipe/hipe_bif64.tab2
-rw-r--r--erts/emulator/hipe/hipe_bif_list.m449
-rw-r--r--erts/emulator/hipe/hipe_debug.c4
-rw-r--r--erts/emulator/hipe/hipe_debug.h2
-rw-r--r--erts/emulator/hipe/hipe_gbif_list.h2
-rw-r--r--erts/emulator/hipe/hipe_gc.c159
-rw-r--r--erts/emulator/hipe/hipe_gc.h2
-rw-r--r--erts/emulator/hipe/hipe_mkliterals.c8
-rw-r--r--erts/emulator/hipe/hipe_mode_switch.c140
-rw-r--r--erts/emulator/hipe/hipe_mode_switch.h10
-rw-r--r--erts/emulator/hipe/hipe_native_bif.c52
-rw-r--r--erts/emulator/hipe/hipe_native_bif.h2
-rw-r--r--erts/emulator/hipe/hipe_ops.tab2
-rw-r--r--erts/emulator/hipe/hipe_ppc.c2
-rw-r--r--erts/emulator/hipe/hipe_ppc.h2
-rw-r--r--erts/emulator/hipe/hipe_ppc.tab2
-rw-r--r--erts/emulator/hipe/hipe_ppc64.tab2
-rw-r--r--erts/emulator/hipe/hipe_ppc_asm.m42
-rw-r--r--erts/emulator/hipe/hipe_ppc_bifs.m438
-rw-r--r--erts/emulator/hipe/hipe_ppc_gc.h2
-rw-r--r--erts/emulator/hipe/hipe_ppc_glue.S2
-rw-r--r--erts/emulator/hipe/hipe_ppc_glue.h2
-rw-r--r--erts/emulator/hipe/hipe_ppc_primops.h2
-rw-r--r--erts/emulator/hipe/hipe_primops.h2
-rw-r--r--erts/emulator/hipe/hipe_process.h8
-rw-r--r--erts/emulator/hipe/hipe_risc_gc.h15
-rw-r--r--erts/emulator/hipe/hipe_risc_glue.h2
-rw-r--r--erts/emulator/hipe/hipe_risc_stack.c2
-rw-r--r--erts/emulator/hipe/hipe_signal.h2
-rw-r--r--erts/emulator/hipe/hipe_sparc.c2
-rw-r--r--erts/emulator/hipe/hipe_sparc.h2
-rw-r--r--erts/emulator/hipe/hipe_sparc.tab2
-rw-r--r--erts/emulator/hipe/hipe_sparc_asm.m42
-rw-r--r--erts/emulator/hipe/hipe_sparc_bifs.m436
-rw-r--r--erts/emulator/hipe/hipe_sparc_gc.h2
-rw-r--r--erts/emulator/hipe/hipe_sparc_glue.S2
-rw-r--r--erts/emulator/hipe/hipe_sparc_glue.h2
-rw-r--r--erts/emulator/hipe/hipe_sparc_primops.h2
-rw-r--r--erts/emulator/hipe/hipe_stack.c2
-rw-r--r--erts/emulator/hipe/hipe_stack.h5
-rw-r--r--erts/emulator/hipe/hipe_x86.c6
-rw-r--r--erts/emulator/hipe/hipe_x86.h2
-rw-r--r--erts/emulator/hipe/hipe_x86.tab2
-rw-r--r--erts/emulator/hipe/hipe_x86_abi.txt2
-rw-r--r--erts/emulator/hipe/hipe_x86_asm.m42
-rw-r--r--erts/emulator/hipe/hipe_x86_bifs.m45
-rw-r--r--erts/emulator/hipe/hipe_x86_gc.h19
-rw-r--r--erts/emulator/hipe/hipe_x86_glue.S2
-rw-r--r--erts/emulator/hipe/hipe_x86_glue.h2
-rw-r--r--erts/emulator/hipe/hipe_x86_primops.h2
-rw-r--r--erts/emulator/hipe/hipe_x86_signal.c203
-rw-r--r--erts/emulator/hipe/hipe_x86_stack.c2
-rw-r--r--erts/emulator/internal_doc/Tracing.md2
-rw-r--r--erts/emulator/internal_doc/dec.erl2
-rw-r--r--erts/emulator/internal_doc/erl_ext_dist.txt2
-rw-r--r--erts/emulator/nifs/common/erl_tracer_nif.c257
-rw-r--r--erts/emulator/pcre/pcre.mk2
-rw-r--r--erts/emulator/sys/common/erl_check_io.c39
-rw-r--r--erts/emulator/sys/common/erl_check_io.h2
-rw-r--r--erts/emulator/sys/common/erl_mmap.c715
-rw-r--r--erts/emulator/sys/common/erl_mmap.h137
-rw-r--r--erts/emulator/sys/common/erl_mseg.c511
-rw-r--r--erts/emulator/sys/common/erl_mseg.h27
-rw-r--r--erts/emulator/sys/common/erl_mtrace_sys_wrap.c2
-rw-r--r--erts/emulator/sys/common/erl_os_monotonic_time_extender.c2
-rw-r--r--erts/emulator/sys/common/erl_poll.c52
-rw-r--r--erts/emulator/sys/common/erl_poll.h24
-rw-r--r--erts/emulator/sys/common/erl_sys_common_misc.c2
-rw-r--r--erts/emulator/sys/common/erl_util_queue.h2
-rw-r--r--erts/emulator/sys/ose/beam.lmconf26
-rw-r--r--erts/emulator/sys/ose/driver_int.h42
-rw-r--r--erts/emulator/sys/ose/erl_main.c54
-rw-r--r--erts/emulator/sys/ose/erl_ose_sys.h356
-rw-r--r--erts/emulator/sys/ose/erl_ose_sys_ddll.c127
-rw-r--r--erts/emulator/sys/ose/erl_poll.c818
-rw-r--r--erts/emulator/sys/ose/erts.sig17
-rw-r--r--erts/emulator/sys/ose/gcc_4.4.3_lm_ppc.lcf182
-rw-r--r--erts/emulator/sys/ose/gcc_4.6.3_lm_ppc.lcf242
-rw-r--r--erts/emulator/sys/ose/sys.c1847
-rw-r--r--erts/emulator/sys/ose/sys_float.c845
-rw-r--r--erts/emulator/sys/ose/sys_time.c57
-rw-r--r--erts/emulator/sys/unix/driver_int.h2
-rw-r--r--erts/emulator/sys/unix/erl_child_setup.c625
-rw-r--r--erts/emulator/sys/unix/erl_child_setup.h77
-rw-r--r--erts/emulator/sys/unix/erl_main.c2
-rw-r--r--erts/emulator/sys/unix/erl_unix_sys.h61
-rw-r--r--erts/emulator/sys/unix/erl_unix_sys_ddll.c2
-rw-r--r--erts/emulator/sys/unix/sys.c2042
-rw-r--r--erts/emulator/sys/unix/sys_drivers.c1878
-rw-r--r--erts/emulator/sys/unix/sys_float.c112
-rw-r--r--erts/emulator/sys/unix/sys_time.c132
-rw-r--r--erts/emulator/sys/unix/sys_uds.c155
-rw-r--r--erts/emulator/sys/unix/sys_uds.h57
-rw-r--r--erts/emulator/sys/win32/dosmap.c2
-rw-r--r--erts/emulator/sys/win32/driver_int.h2
-rw-r--r--erts/emulator/sys/win32/erl_main.c2
-rw-r--r--erts/emulator/sys/win32/erl_poll.c13
-rw-r--r--erts/emulator/sys/win32/erl_win32_sys_ddll.c2
-rw-r--r--erts/emulator/sys/win32/erl_win_dyn_driver.h2
-rw-r--r--erts/emulator/sys/win32/erl_win_sys.h20
-rw-r--r--erts/emulator/sys/win32/sys.c28
-rw-r--r--erts/emulator/sys/win32/sys_env.c2
-rw-r--r--erts/emulator/sys/win32/sys_float.c5
-rw-r--r--erts/emulator/sys/win32/sys_interrupt.c2
-rw-r--r--erts/emulator/sys/win32/sys_time.c3
-rw-r--r--erts/emulator/test/Makefile13
-rw-r--r--erts/emulator/test/a_SUITE.erl79
-rw-r--r--erts/emulator/test/after_SUITE.erl201
-rw-r--r--erts/emulator/test/alloc_SUITE.erl204
-rw-r--r--erts/emulator/test/alloc_SUITE_data/cpool.c2
-rw-r--r--erts/emulator/test/alloc_SUITE_data/migration.c2
-rw-r--r--erts/emulator/test/alloc_SUITE_data/threads.c17
-rw-r--r--erts/emulator/test/async_ports_SUITE.erl39
-rw-r--r--erts/emulator/test/beam_SUITE.erl135
-rw-r--r--erts/emulator/test/beam_literals_SUITE.erl190
-rw-r--r--erts/emulator/test/bif_SUITE.erl592
-rw-r--r--erts/emulator/test/big_SUITE.erl152
-rw-r--r--erts/emulator/test/big_SUITE_data/literal_test.erl2
-rw-r--r--erts/emulator/test/binary_SUITE.erl847
-rw-r--r--erts/emulator/test/bs_bincomp_SUITE.erl4
-rw-r--r--erts/emulator/test/bs_bit_binaries_SUITE.erl92
-rw-r--r--erts/emulator/test/bs_construct_SUITE.erl379
-rw-r--r--erts/emulator/test/bs_match_bin_SUITE.erl58
-rw-r--r--erts/emulator/test/bs_match_int_SUITE.erl98
-rw-r--r--erts/emulator/test/bs_match_misc_SUITE.erl234
-rw-r--r--erts/emulator/test/bs_match_tail_SUITE.erl79
-rw-r--r--erts/emulator/test/bs_utf_SUITE.erl112
-rw-r--r--erts/emulator/test/busy_port_SUITE.erl641
-rw-r--r--erts/emulator/test/busy_port_SUITE_data/Makefile.src2
-rw-r--r--erts/emulator/test/busy_port_SUITE_data/hard_busy_drv.c2
-rw-r--r--erts/emulator/test/busy_port_SUITE_data/hs_busy_drv.c2
-rw-r--r--erts/emulator/test/busy_port_SUITE_data/scheduling_drv.c2
-rw-r--r--erts/emulator/test/busy_port_SUITE_data/soft_busy_drv.c2
-rw-r--r--erts/emulator/test/call_trace_SUITE.erl1622
-rw-r--r--erts/emulator/test/code_SUITE.erl1138
-rw-r--r--erts/emulator/test/code_SUITE_data/another_code_test.erl2
-rw-r--r--erts/emulator/test/code_SUITE_data/cpbugx.erl2
-rw-r--r--erts/emulator/test/code_SUITE_data/fun_confusion.erl2
-rw-r--r--erts/emulator/test/code_SUITE_data/literals.erl9
-rw-r--r--erts/emulator/test/code_SUITE_data/many_funs.erl2
-rw-r--r--erts/emulator/test/code_SUITE_data/my_code_test.erl4
-rw-r--r--erts/emulator/test/code_SUITE_data/my_code_test2.erl32
-rw-r--r--erts/emulator/test/code_SUITE_data/versions.erl2
-rw-r--r--erts/emulator/test/code_parallel_load_SUITE.erl51
-rw-r--r--erts/emulator/test/crypto_SUITE.erl401
-rw-r--r--erts/emulator/test/crypto_reference.erl2
-rw-r--r--erts/emulator/test/ddll_SUITE.erl1611
-rw-r--r--erts/emulator/test/decode_packet_SUITE.erl553
-rw-r--r--erts/emulator/test/dgawd_handler.erl2
-rw-r--r--erts/emulator/test/dirty_nif_SUITE.erl547
-rw-r--r--erts/emulator/test/dirty_nif_SUITE_data/Makefile.src6
-rw-r--r--erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c275
-rw-r--r--erts/emulator/test/distribution_SUITE.erl2780
-rw-r--r--erts/emulator/test/distribution_SUITE_data/run.erl2
-rw-r--r--erts/emulator/test/driver_SUITE.erl3144
-rw-r--r--erts/emulator/test/driver_SUITE_data/async_blast_drv.c2
-rw-r--r--erts/emulator/test/driver_SUITE_data/consume_timeslice_drv.c2
-rw-r--r--erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c2
-rw-r--r--erts/emulator/test/driver_SUITE_data/otp_9302_drv.c2
-rw-r--r--erts/emulator/test/driver_SUITE_data/thr_free_drv.c2
-rw-r--r--erts/emulator/test/driver_SUITE_data/thr_msg_blast_drv.c2
-rw-r--r--erts/emulator/test/driver_SUITE_data/timer_drv.c44
-rw-r--r--erts/emulator/test/efile_SUITE.erl146
-rw-r--r--erts/emulator/test/emulator.spec.ose2
-rw-r--r--erts/emulator/test/emulator_smoke.spec12
-rw-r--r--erts/emulator/test/erl_drv_thread_SUITE.erl122
-rw-r--r--erts/emulator/test/erl_link_SUITE.erl1525
-rw-r--r--erts/emulator/test/erts_debug_SUITE.erl83
-rw-r--r--erts/emulator/test/estone_SUITE.erl61
-rw-r--r--erts/emulator/test/evil_SUITE.erl464
-rw-r--r--erts/emulator/test/exception_SUITE.erl633
-rw-r--r--erts/emulator/test/float_SUITE.erl396
-rw-r--r--erts/emulator/test/float_SUITE_data/fp_drv.c1
-rw-r--r--erts/emulator/test/float_SUITE_data/has_fpe_bug.erl2
-rw-r--r--erts/emulator/test/fun_SUITE.erl800
-rw-r--r--erts/emulator/test/fun_r13_SUITE.erl94
-rw-r--r--erts/emulator/test/gc_SUITE.erl82
-rw-r--r--erts/emulator/test/guard_SUITE.erl327
-rw-r--r--erts/emulator/test/hash_SUITE.erl90
-rw-r--r--erts/emulator/test/hibernate_SUITE.erl258
-rw-r--r--erts/emulator/test/hipe_SUITE.erl67
-rw-r--r--erts/emulator/test/hipe_SUITE_data/literals.erl26
-rw-r--r--erts/emulator/test/hipe_SUITE_data/ref_cell.erl64
-rw-r--r--erts/emulator/test/ignore_cores.erl16
-rw-r--r--erts/emulator/test/list_bif_SUITE.erl171
-rw-r--r--erts/emulator/test/long_timers_test.erl190
-rw-r--r--erts/emulator/test/lttng_SUITE.erl502
-rw-r--r--erts/emulator/test/lttng_SUITE_data/Makefile.src7
-rw-r--r--erts/emulator/test/lttng_SUITE_data/caller_drv.c159
-rw-r--r--erts/emulator/test/map_SUITE.erl404
-rw-r--r--erts/emulator/test/match_spec_SUITE.erl1168
-rw-r--r--erts/emulator/test/message_queue_data_SUITE.erl209
-rw-r--r--erts/emulator/test/module_info_SUITE.erl47
-rw-r--r--erts/emulator/test/monitor_SUITE.erl1096
-rw-r--r--erts/emulator/test/mtx_SUITE.erl406
-rw-r--r--erts/emulator/test/mtx_SUITE_data/Makefile.src2
-rw-r--r--erts/emulator/test/mtx_SUITE_data/mtx_SUITE.c2
-rw-r--r--erts/emulator/test/multi_load_SUITE.erl181
-rw-r--r--erts/emulator/test/nested_SUITE.erl98
-rw-r--r--erts/emulator/test/nif_SUITE.erl1671
-rw-r--r--erts/emulator/test/nif_SUITE_data/Makefile.src3
-rw-r--r--erts/emulator/test/nif_SUITE_data/echo_drv.c62
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_SUITE.c340
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_mod.c2
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_mod.erl16
-rw-r--r--erts/emulator/test/nif_SUITE_data/tester.erl5
-rw-r--r--erts/emulator/test/node_container_SUITE.erl1629
-rw-r--r--erts/emulator/test/nofrag_SUITE.erl108
-rw-r--r--erts/emulator/test/num_bif_SUITE.erl106
-rw-r--r--erts/emulator/test/old_mod.erl36
-rw-r--r--erts/emulator/test/old_scheduler_SUITE.erl351
-rw-r--r--erts/emulator/test/op_SUITE.erl307
-rw-r--r--erts/emulator/test/port_SUITE.erl2609
-rw-r--r--erts/emulator/test/port_SUITE_data/Makefile.src8
-rw-r--r--erts/emulator/test/port_SUITE_data/dead_port.c2
-rw-r--r--erts/emulator/test/port_SUITE_data/port_test.c51
-rw-r--r--erts/emulator/test/port_SUITE_data/port_test.erl2
-rw-r--r--erts/emulator/test/port_SUITE_data/sleep_failure_drv.c76
-rw-r--r--erts/emulator/test/port_bif_SUITE.erl98
-rw-r--r--erts/emulator/test/port_trace_SUITE.erl652
-rw-r--r--erts/emulator/test/port_trace_SUITE_data/Makefile.src3
-rw-r--r--erts/emulator/test/port_trace_SUITE_data/echo_drv.c296
-rw-r--r--erts/emulator/test/prim_eval_SUITE.erl78
-rw-r--r--erts/emulator/test/process_SUITE.erl455
-rw-r--r--erts/emulator/test/pseudoknot_SUITE.erl2
-rw-r--r--erts/emulator/test/random_iolist.erl18
-rw-r--r--erts/emulator/test/receive_SUITE.erl37
-rw-r--r--erts/emulator/test/ref_SUITE.erl53
-rw-r--r--erts/emulator/test/register_SUITE.erl64
-rw-r--r--erts/emulator/test/save_calls_SUITE.erl266
-rw-r--r--erts/emulator/test/scheduler_SUITE.erl1927
-rw-r--r--erts/emulator/test/send_term_SUITE.erl182
-rw-r--r--erts/emulator/test/sensitive_SUITE.erl507
-rw-r--r--erts/emulator/test/signal_SUITE.erl442
-rw-r--r--erts/emulator/test/smoke_test_SUITE.erl52
-rw-r--r--erts/emulator/test/statistics_SUITE.erl550
-rw-r--r--erts/emulator/test/system_info_SUITE.erl243
-rw-r--r--erts/emulator/test/system_profile_SUITE.erl114
-rw-r--r--erts/emulator/test/time_SUITE.erl330
-rw-r--r--erts/emulator/test/timer_bif_SUITE.erl728
-rw-r--r--erts/emulator/test/trace_SUITE.erl2565
-rw-r--r--erts/emulator/test/trace_bif_SUITE.erl343
-rw-r--r--erts/emulator/test/trace_call_count_SUITE.erl316
-rw-r--r--erts/emulator/test/trace_call_time_SUITE.erl663
-rw-r--r--erts/emulator/test/trace_local_SUITE.erl1492
-rw-r--r--erts/emulator/test/trace_local_SUITE_data/trace_local_dummy.erl2
-rw-r--r--erts/emulator/test/trace_meta_SUITE.erl76
-rw-r--r--erts/emulator/test/trace_nif_SUITE.erl337
-rw-r--r--erts/emulator/test/trace_port_SUITE.erl796
-rw-r--r--erts/emulator/test/tracer_SUITE.erl660
-rw-r--r--erts/emulator/test/tracer_SUITE_data/Makefile.src8
-rw-r--r--erts/emulator/test/tracer_SUITE_data/tracer_test.c116
-rw-r--r--erts/emulator/test/tracer_test.erl55
-rw-r--r--erts/emulator/test/tuple_SUITE.erl12
-rw-r--r--erts/emulator/test/unique_SUITE.erl256
-rw-r--r--erts/emulator/test/z_SUITE.erl346
-rwxr-xr-xerts/emulator/utils/beam_makeops413
-rwxr-xr-xerts/emulator/utils/beam_strip2
-rwxr-xr-xerts/emulator/utils/count2
-rw-r--r--erts/emulator/utils/loaded2
-rwxr-xr-xerts/emulator/utils/make_alloc_types2
-rwxr-xr-xerts/emulator/utils/make_compiler_flags2
-rwxr-xr-xerts/emulator/utils/make_driver_tab15
-rwxr-xr-xerts/emulator/utils/make_preload2
-rwxr-xr-xerts/emulator/utils/make_tables2
-rwxr-xr-xerts/emulator/utils/make_version2
-rw-r--r--erts/emulator/utils/mkver.c2
-rw-r--r--erts/emulator/valgrind/suppress.halfword56
-rw-r--r--erts/emulator/zlib/zlib.mk2
-rw-r--r--erts/epmd/Makefile2
-rw-r--r--erts/epmd/epmd.mk2
-rw-r--r--erts/epmd/src/Makefile2
-rw-r--r--erts/epmd/src/Makefile.in39
-rw-r--r--erts/epmd/src/epmd.c10
-rw-r--r--erts/epmd/src/epmd.h2
-rw-r--r--erts/epmd/src/epmd_cli.c2
-rw-r--r--erts/epmd/src/epmd_int.h24
-rw-r--r--erts/epmd/src/epmd_srv.c17
-rw-r--r--erts/epmd/test/Makefile2
-rw-r--r--erts/epmd/test/epmd_SUITE.erl1209
-rw-r--r--erts/etc/Makefile2
-rw-r--r--erts/etc/common/Makefile2
-rw-r--r--erts/etc/common/Makefile.in85
-rw-r--r--erts/etc/common/ct_run.c26
-rw-r--r--erts/etc/common/dialyzer.c26
-rw-r--r--erts/etc/common/erlc.c26
-rw-r--r--erts/etc/common/erlexec.c33
-rw-r--r--erts/etc/common/escript.c26
-rw-r--r--erts/etc/common/heart.c60
-rw-r--r--erts/etc/common/inet_gethost.c4
-rw-r--r--erts/etc/common/run_erl_common.c696
-rw-r--r--erts/etc/common/run_erl_common.h97
-rw-r--r--erts/etc/common/to_erl_common.c717
-rw-r--r--erts/etc/common/to_erl_common.h29
-rw-r--r--erts/etc/common/typer.c26
-rw-r--r--erts/etc/ose/etc.lmconf20
-rw-r--r--erts/etc/ose/run_erl.c664
-rw-r--r--erts/etc/ose/run_erl_main.c80
-rw-r--r--erts/etc/unix/Install.src2
-rw-r--r--erts/etc/unix/Makefile2
-rw-r--r--erts/etc/unix/README2
-rw-r--r--erts/etc/unix/RELNOTES2
-rw-r--r--erts/etc/unix/cerl.src20
-rw-r--r--erts/etc/unix/dyn_erl.c2
-rw-r--r--erts/etc/unix/erl.src.src2
-rw-r--r--erts/etc/unix/etp-commands.in400
-rw-r--r--erts/etc/unix/etp-thr.py2
-rw-r--r--erts/etc/unix/etp_commands.erl2
-rw-r--r--erts/etc/unix/etp_commands.mk2
-rw-r--r--erts/etc/unix/format_man_pages4
-rw-r--r--erts/etc/unix/run_erl.c649
-rw-r--r--erts/etc/unix/run_erl.h (renamed from erts/etc/common/run_erl_vsn.h)9
-rw-r--r--erts/etc/unix/safe_string.c (renamed from erts/etc/common/safe_string.c)13
-rw-r--r--erts/etc/unix/safe_string.h (renamed from erts/etc/common/safe_string.h)13
-rw-r--r--erts/etc/unix/setuid_socket_wrap.c2
-rw-r--r--erts/etc/unix/start.src2
-rw-r--r--erts/etc/unix/start_erl.src2
-rw-r--r--erts/etc/unix/to_erl.c591
-rw-r--r--erts/etc/win32/Install.c2
-rw-r--r--erts/etc/win32/Makefile2
-rw-r--r--erts/etc/win32/Nmakefile.start_erl2
-rw-r--r--erts/etc/win32/beam.rc2
-rwxr-xr-xerts/etc/win32/cygwin_tools/erl2
-rwxr-xr-xerts/etc/win32/cygwin_tools/erlc2
-rwxr-xr-xerts/etc/win32/cygwin_tools/javac.sh2
-rwxr-xr-xerts/etc/win32/cygwin_tools/make_bootstrap_ini.sh2
-rwxr-xr-xerts/etc/win32/cygwin_tools/make_local_ini.sh2
-rwxr-xr-xerts/etc/win32/cygwin_tools/mingw/ar.sh2
-rwxr-xr-xerts/etc/win32/cygwin_tools/mingw/cc.sh2
-rw-r--r--erts/etc/win32/cygwin_tools/mingw/coffix.c2
-rwxr-xr-xerts/etc/win32/cygwin_tools/mingw/emu_cc.sh2
-rwxr-xr-xerts/etc/win32/cygwin_tools/mingw/ld.sh2
-rwxr-xr-xerts/etc/win32/cygwin_tools/mingw/mc.sh2
-rwxr-xr-xerts/etc/win32/cygwin_tools/mingw/rc.sh2
-rwxr-xr-xerts/etc/win32/cygwin_tools/vc/ar.sh2
-rwxr-xr-xerts/etc/win32/cygwin_tools/vc/cc.sh2
-rw-r--r--erts/etc/win32/cygwin_tools/vc/cc_wrap.c2
-rw-r--r--erts/etc/win32/cygwin_tools/vc/coffix.c2
-rwxr-xr-xerts/etc/win32/cygwin_tools/vc/emu_cc.sh2
-rwxr-xr-xerts/etc/win32/cygwin_tools/vc/ld.sh2
-rw-r--r--erts/etc/win32/cygwin_tools/vc/ld_wrap.c2
-rwxr-xr-xerts/etc/win32/cygwin_tools/vc/mc.sh2
-rwxr-xr-xerts/etc/win32/cygwin_tools/vc/rc.sh2
-rw-r--r--erts/etc/win32/erl.c2
-rw-r--r--erts/etc/win32/erl.rc2
-rw-r--r--erts/etc/win32/erl_log.c2
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_global.h2
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_interactive.c2
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_interactive.h2
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_main.c2
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_registry.c2
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_registry.h2
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_service.c2
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_service.h2
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_util.c2
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_util.h2
-rw-r--r--erts/etc/win32/init_file.c2
-rw-r--r--erts/etc/win32/init_file.h2
-rw-r--r--erts/etc/win32/msys_tools/erl2
-rw-r--r--erts/etc/win32/msys_tools/erlc2
-rw-r--r--erts/etc/win32/msys_tools/javac.sh2
-rw-r--r--erts/etc/win32/msys_tools/make_bootstrap_ini.sh2
-rw-r--r--erts/etc/win32/msys_tools/make_local_ini.sh2
-rw-r--r--erts/etc/win32/msys_tools/vc/ar.sh2
-rw-r--r--erts/etc/win32/msys_tools/vc/cc.sh2
-rw-r--r--erts/etc/win32/msys_tools/vc/coffix.c2
-rw-r--r--erts/etc/win32/msys_tools/vc/emu_cc.sh2
-rw-r--r--erts/etc/win32/msys_tools/vc/ld.sh2
-rw-r--r--erts/etc/win32/msys_tools/vc/mc.sh2
-rw-r--r--erts/etc/win32/msys_tools/vc/rc.sh2
-rw-r--r--erts/etc/win32/nsis/Makefile2
-rwxr-xr-xerts/etc/win32/nsis/dll_version_helper.sh2
-rw-r--r--erts/etc/win32/nsis/erlang20.nsi2
-rwxr-xr-xerts/etc/win32/nsis/find_redist.sh2
-rw-r--r--erts/etc/win32/port_entry.c2
-rw-r--r--erts/etc/win32/resource.h2
-rw-r--r--erts/etc/win32/start_erl.c2
-rw-r--r--erts/etc/win32/win_erlexec.c2
-rw-r--r--erts/example/Makefile2
-rw-r--r--erts/example/matrix_nif.c2
-rw-r--r--erts/example/matrix_nif.erl2
-rw-r--r--erts/example/next_perm.cc2
-rw-r--r--erts/example/next_perm.erl2
-rw-r--r--erts/example/pg_async.c2
-rw-r--r--erts/example/pg_async.erl2
-rw-r--r--erts/example/pg_async2.c2
-rw-r--r--erts/example/pg_async2.erl2
-rw-r--r--erts/example/pg_encode.c2
-rw-r--r--erts/example/pg_encode.h2
-rw-r--r--erts/example/pg_encode2.c2
-rw-r--r--erts/example/pg_encode2.h2
-rw-r--r--erts/example/pg_sync.c2
-rw-r--r--erts/example/pg_sync.erl2
-rw-r--r--erts/example/time_compat.erl6
-rw-r--r--erts/include/erl_fixed_size_int_types.h2
-rw-r--r--erts/include/erl_int_sizes_config.h.in5
-rw-r--r--erts/include/erl_memory_trace_parser.h2
-rw-r--r--erts/include/erl_native_features_config.h.in22
-rw-r--r--erts/include/internal/README2
-rw-r--r--erts/include/internal/erl_errno.h2
-rw-r--r--erts/include/internal/erl_memory_trace_protocol.h2
-rw-r--r--erts/include/internal/erl_misc_utils.h31
-rw-r--r--erts/include/internal/erl_printf.h9
-rw-r--r--erts/include/internal/erl_printf_format.h16
-rw-r--r--erts/include/internal/erts_internal.mk.in2
-rw-r--r--erts/include/internal/ethr_atomics.h2
-rw-r--r--erts/include/internal/ethr_internal.h3
-rw-r--r--erts/include/internal/ethr_mutex.h20
-rw-r--r--erts/include/internal/ethr_optimized_fallbacks.h2
-rw-r--r--erts/include/internal/ethread.h147
-rw-r--r--erts/include/internal/ethread.mk.in2
-rw-r--r--erts/include/internal/ethread_header_config.h.in2
-rw-r--r--erts/include/internal/ethread_inline.h2
-rw-r--r--erts/include/internal/i386/atomic.h2
-rw-r--r--erts/include/internal/i386/ethr_dw_atomic.h2
-rw-r--r--erts/include/internal/i386/ethr_membar.h2
-rw-r--r--erts/include/internal/i386/ethread.h2
-rw-r--r--erts/include/internal/i386/rwlock.h2
-rw-r--r--erts/include/internal/i386/spinlock.h2
-rw-r--r--erts/include/internal/libatomic_ops/ethr_atomic.h2
-rw-r--r--erts/include/internal/libatomic_ops/ethr_dw_atomic.h2
-rw-r--r--erts/include/internal/libatomic_ops/ethr_membar.h2
-rw-r--r--erts/include/internal/libatomic_ops/ethread.h2
-rw-r--r--erts/include/internal/ose/ethr_event.h114
-rw-r--r--erts/include/internal/ppc32/atomic.h2
-rw-r--r--erts/include/internal/ppc32/ethr_membar.h2
-rw-r--r--erts/include/internal/ppc32/ethread.h2
-rw-r--r--erts/include/internal/ppc32/rwlock.h2
-rw-r--r--erts/include/internal/ppc32/spinlock.h2
-rw-r--r--erts/include/internal/pthread/ethr_event.h2
-rw-r--r--erts/include/internal/sparc32/atomic.h2
-rw-r--r--erts/include/internal/sparc32/ethr_membar.h2
-rw-r--r--erts/include/internal/sparc32/ethread.h2
-rw-r--r--erts/include/internal/sparc32/rwlock.h2
-rw-r--r--erts/include/internal/sparc32/spinlock.h2
-rw-r--r--erts/include/internal/sparc64/ethread.h2
-rw-r--r--erts/include/internal/tile/atomic.h2
-rw-r--r--erts/include/internal/tile/ethr_membar.h2
-rw-r--r--erts/include/internal/tile/ethread.h2
-rw-r--r--erts/include/internal/win/ethr_atomic.h2
-rw-r--r--erts/include/internal/win/ethr_dw_atomic.h2
-rw-r--r--erts/include/internal/win/ethr_event.h2
-rw-r--r--erts/include/internal/win/ethr_membar.h2
-rw-r--r--erts/include/internal/win/ethread.h2
-rw-r--r--erts/include/internal/x86_64/ethread.h2
-rw-r--r--erts/lib/internal/README2
-rw-r--r--erts/lib_src/Makefile2
-rw-r--r--erts/lib_src/Makefile.in3
-rw-r--r--erts/lib_src/common/erl_memory_trace_parser.c2
-rw-r--r--erts/lib_src/common/erl_misc_utils.c8
-rw-r--r--erts/lib_src/common/erl_printf.c39
-rw-r--r--erts/lib_src/common/erl_printf_format.c27
-rw-r--r--erts/lib_src/common/ethr_atomics.c2
-rw-r--r--erts/lib_src/common/ethr_aux.c51
-rw-r--r--erts/lib_src/common/ethr_cbf.c2
-rw-r--r--erts/lib_src/common/ethr_mutex.c4
-rw-r--r--erts/lib_src/ose/ethr_event.c220
-rw-r--r--erts/lib_src/ose/ethread.c833
-rw-r--r--erts/lib_src/pthread/ethr_event.c76
-rw-r--r--erts/lib_src/pthread/ethr_x86_sse2_asm.c2
-rw-r--r--erts/lib_src/pthread/ethread.c2
-rwxr-xr-xerts/lib_src/utils/make_atomics_api2
-rw-r--r--erts/lib_src/win/ethr_event.c2
-rw-r--r--erts/lib_src/win/ethread.c2
-rw-r--r--erts/preloaded/Makefile2
-rw-r--r--erts/preloaded/ebin/erl_prim_loader.beambin56336 -> 55808 bytes
-rw-r--r--erts/preloaded/ebin/erl_tracer.beambin0 -> 2204 bytes
-rw-r--r--erts/preloaded/ebin/erlang.beambin102112 -> 105140 bytes
-rw-r--r--erts/preloaded/ebin/erts_code_purger.beambin0 -> 12088 bytes
-rw-r--r--erts/preloaded/ebin/erts_dirty_process_code_checker.beambin0 -> 2144 bytes
-rw-r--r--erts/preloaded/ebin/erts_internal.beambin6500 -> 11156 bytes
-rw-r--r--erts/preloaded/ebin/erts_literal_area_collector.beambin0 -> 3304 bytes
-rw-r--r--erts/preloaded/ebin/init.beambin48764 -> 50064 bytes
-rw-r--r--erts/preloaded/ebin/otp_ring0.beambin1468 -> 1448 bytes
-rw-r--r--erts/preloaded/ebin/prim_eval.beambin1340 -> 1448 bytes
-rw-r--r--erts/preloaded/ebin/prim_file.beambin45004 -> 44768 bytes
-rw-r--r--erts/preloaded/ebin/prim_inet.beambin72716 -> 76496 bytes
-rw-r--r--erts/preloaded/ebin/prim_zip.beambin23424 -> 23156 bytes
-rw-r--r--erts/preloaded/ebin/zlib.beambin14176 -> 14140 bytes
-rw-r--r--erts/preloaded/src/Makefile8
-rw-r--r--erts/preloaded/src/add_abstract_code2
-rw-r--r--erts/preloaded/src/erl_prim_loader.erl549
-rw-r--r--erts/preloaded/src/erl_tracer.erl65
-rw-r--r--erts/preloaded/src/erlang.erl336
-rw-r--r--erts/preloaded/src/erts.app.src5
-rw-r--r--erts/preloaded/src/erts_code_purger.erl429
-rw-r--r--erts/preloaded/src/erts_dirty_process_code_checker.erl82
-rw-r--r--erts/preloaded/src/erts_internal.erl204
-rw-r--r--erts/preloaded/src/erts_literal_area_collector.erl113
-rw-r--r--erts/preloaded/src/init.erl607
-rw-r--r--erts/preloaded/src/otp_ring0.erl2
-rw-r--r--erts/preloaded/src/prim_eval.S43
-rw-r--r--erts/preloaded/src/prim_eval.erl2
-rw-r--r--erts/preloaded/src/prim_inet.erl357
-rw-r--r--erts/preloaded/src/prim_zip.erl2
-rw-r--r--erts/preloaded/src/zip_internal.hrl2
-rw-r--r--erts/preloaded/src/zlib.erl2
-rw-r--r--erts/start_scripts/Makefile7
-rw-r--r--erts/start_scripts/no_dot_erlang.rel.src2
-rw-r--r--erts/start_scripts/start_all_example.rel.src2
-rw-r--r--erts/start_scripts/start_clean.rel.src2
-rw-r--r--erts/start_scripts/start_sasl.rel.src2
-rw-r--r--erts/test/Makefile4
-rw-r--r--erts/test/erl_print_SUITE.erl509
-rw-r--r--erts/test/erl_print_SUITE_data/Makefile.src2
-rw-r--r--erts/test/erl_print_SUITE_data/character_test.h2
-rw-r--r--erts/test/erl_print_SUITE_data/erl_print_tests.c2
-rw-r--r--erts/test/erl_print_SUITE_data/integer_64_test.h2
-rw-r--r--erts/test/erl_print_SUITE_data/integer_test.h2
-rw-r--r--erts/test/erl_print_SUITE_data/snprintf_test.h2
-rw-r--r--erts/test/erl_print_SUITE_data/string_test.h2
-rw-r--r--erts/test/erlc_SUITE.erl331
-rw-r--r--erts/test/erlc_SUITE_data/include/erl_test.hrl2
-rw-r--r--erts/test/erlc_SUITE_data/src/erl_test_bad.erl2
-rw-r--r--erts/test/erlc_SUITE_data/src/erl_test_missing_header.erl2
-rw-r--r--erts/test/erlc_SUITE_data/src/erl_test_ok.erl2
-rw-r--r--erts/test/erlc_SUITE_data/src/yecc_test_bad.yrl2
-rw-r--r--erts/test/erlc_SUITE_data/src/yecc_test_ok.yrl2
-rw-r--r--erts/test/erlexec_SUITE.erl242
-rw-r--r--erts/test/erlexec_SUITE_data/Makefile.src2
-rw-r--r--erts/test/erlexec_SUITE_data/erlexec_tests.c2
-rw-r--r--erts/test/ethread_SUITE.erl281
-rw-r--r--erts/test/ethread_SUITE_data/Makefile.src2
-rw-r--r--erts/test/ethread_SUITE_data/ethread_tests.c2
-rw-r--r--erts/test/ignore_cores.erl16
-rw-r--r--erts/test/install_SUITE.erl384
-rw-r--r--erts/test/nt_SUITE.erl705
-rw-r--r--erts/test/nt_SUITE_data/Makefile.src2
-rw-r--r--erts/test/nt_SUITE_data/nt_info.c2
-rw-r--r--erts/test/otp_SUITE.erl588
-rw-r--r--erts/test/run_erl_SUITE.erl190
-rw-r--r--erts/test/run_erl_SUITE_data/defuncter.pl2
-rw-r--r--erts/test/run_erl_SUITE_data/run_erl_test.pl2
-rw-r--r--erts/test/system_smoke.spec7
-rw-r--r--erts/test/upgrade_SUITE.erl24
-rw-r--r--erts/test/upgrade_SUITE_data/start.src2
-rw-r--r--erts/test/utils/gccifier.c2
-rwxr-xr-xerts/test/utils/gccifier.sh2
-rw-r--r--erts/test/z_SUITE.erl61
-rw-r--r--erts/vsn.mk4
819 files changed, 84468 insertions, 72006 deletions
diff --git a/erts/AUTHORS b/erts/AUTHORS
index d8746f65b2..5555502099 100644
--- a/erts/AUTHORS
+++ b/erts/AUTHORS
@@ -1,7 +1,7 @@
%CopyrightBegin%
- Copyright Ericsson AB 1999-2009. All Rights Reserved.
+ Copyright Ericsson AB 1999-2016. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/erts/Makefile.in b/erts/Makefile.in
index feed00dad5..3052dc3065 100644
--- a/erts/Makefile.in
+++ b/erts/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2006-2013. All Rights Reserved.
+# Copyright Ericsson AB 2006-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/aclocal.m4 b/erts/aclocal.m4
index ec9b66bf29..80bf236188 100644
--- a/erts/aclocal.m4
+++ b/erts/aclocal.m4
@@ -1,7 +1,7 @@
dnl
dnl %CopyrightBegin%
dnl
-dnl Copyright Ericsson AB 1998-2015. All Rights Reserved.
+dnl Copyright Ericsson AB 1998-2016. All Rights Reserved.
dnl
dnl Licensed under the Apache License, Version 2.0 (the "License");
dnl you may not use this file except in compliance with the License.
@@ -74,21 +74,6 @@ AC_ARG_VAR(erl_xcomp_clock_gettime_cpu_time, [clock_gettime() can be used for re
AC_ARG_VAR(erl_xcomp_after_morecore_hook, [__after_morecore_hook can track malloc()s core memory usage: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_dlsym_brk_wrappers, [dlsym(RTLD_NEXT, _) brk wrappers can track malloc()s core memory usage: yes|no (only used when cross compiling)])
-dnl Cross compilation variables for OSE
-AC_ARG_VAR(erl_xcomp_ose_ldflags_pass1, [Linker flags for the OSE module (pass 1) (only used when cross compiling for OSE)])
-AC_ARG_VAR(erl_xcomp_ose_ldflags_pass2, [Linker flags for the OSE module (pass 2) (only used when cross compiling for OSE)])
-AC_ARG_VAR(erl_xcomp_ose_OSEROOT, [OSE installation root directory (only used when cross compiling for OSE)])
-AC_ARG_VAR(erl_xcomp_ose_STRIP, [Strip utility shipped with the OSE distribution(only used when cross compiling for OSE)])
-AC_ARG_VAR(erl_xcomp_ose_LM_POST_LINK, [OSE postlink tool (only used when cross compiling for OSE)])
-AC_ARG_VAR(erl_xcomp_ose_LM_SET_CONF, [Sets the configuration for an OSE load module (only used when cross compiling for OSE)])
-AC_ARG_VAR(erl_xcomp_ose_LM_ELF_SIZE, [Prints the section size information for an OSE load module (only used when cross compiling for OSE)])
-AC_ARG_VAR(erl_xcomp_ose_LM_LCF, [OSE load module linker configuration file (only used when cross compiling for OSE)])
-AC_ARG_VAR(erl_xcomp_ose_BEAM_LM_CONF, [BEAM OSE load module default configuration file (only used when cross compiling for OSE)])
-AC_ARG_VAR(erl_xcomp_ose_EPMD_LM_CONF, [EPMD OSE load module default configuration file (only used when cross compiling for OSE)])
-AC_ARG_VAR(erl_xcomp_ose_RUN_ERL_LM_CONF, [run_erl_lm OSE load module default configuration file (only used when cross compiling for OSE)])
-AC_ARG_VAR(erl_xcomp_ose_CONFD, [OSE confd source file])
-AC_ARG_VAR(erl_xcomp_ose_CRT0_LM, [OSE crt0 lm source file])
-
])
AC_DEFUN(ERL_XCOMP_SYSROOT_INIT,
@@ -143,13 +128,13 @@ MIXED_MSYS=no
AC_MSG_CHECKING(for mixed cygwin or msys and native VC++ environment)
if test "X$host" = "Xwin32" -a "x$GCC" != "xyes"; then
if test -x /usr/bin/msys-?.0.dll; then
- CFLAGS="-O2"
+ CFLAGS="$CFLAGS -O2"
MIXED_MSYS=yes
AC_MSG_RESULT([MSYS and VC])
MIXED_MSYS_VC=yes
CPPFLAGS="$CPPFLAGS -DERTS_MIXED_MSYS_VC"
elif test -x /usr/bin/cygpath; then
- CFLAGS="-O2"
+ CFLAGS="$CFLAGS -O2"
MIXED_CYGWIN=yes
AC_MSG_RESULT([Cygwin and VC])
MIXED_CYGWIN_VC=yes
@@ -177,7 +162,7 @@ if test "x$MIXED_MSYS" != "xyes"; then
AC_MSG_CHECKING(for mixed cygwin and native MinGW environment)
if test "X$host" = "Xwin32" -a "x$GCC" = x"yes"; then
if test -x /usr/bin/cygpath; then
- CFLAGS="-O2"
+ CFLAGS="$CFLAGS -O2"
MIXED_CYGWIN=yes
AC_MSG_RESULT([yes])
MIXED_CYGWIN_MINGW=yes
@@ -503,8 +488,6 @@ AC_CACHE_VAL(ac_cv_sys_ipv6_support,
#ifdef __WIN32__
#include <winsock2.h>
#include <ws2tcpip.h>
-#elif __OSE__
-#error "no ipv6"
#else
#include <netinet/in.h>
#endif],
@@ -517,8 +500,6 @@ else
#ifdef __WIN32__
#include <winsock2.h>
#include <ws2tcpip.h>
-#elif __OSE__
-#error "no ipv6"
#else
#include <netinet/in.h>
#endif],
@@ -755,10 +736,23 @@ AC_DEFUN(ERL_MONOTONIC_CLOCK,
;;
esac
+ clock_gettime_lib=""
+ AC_CHECK_LIB(rt, clock_gettime, [clock_gettime_lib="-lrt"])
+
+ save_LIBS="$LIBS"
+ LIBS="$LIBS $clock_gettime_lib"
+
+ if test "$LD_MAY_BE_WEAK" != "no"; then
+ trust_test="#error May not be there due to weak linking"
+ else
+ trust_test=""
+ fi
+
AC_CACHE_CHECK([for clock_gettime(CLOCK_MONOTONIC_RAW, _)], erl_cv_clock_gettime_monotonic_raw,
[
- AC_TRY_COMPILE([
+ AC_TRY_LINK([
#include <time.h>
+$trust_test
],
[
struct timespec ts;
@@ -774,8 +768,9 @@ AC_DEFUN(ERL_MONOTONIC_CLOCK,
AC_CACHE_CHECK([for clock_gettime() with ${check_msg}monotonic clock type], erl_cv_clock_gettime_monotonic_$1,
[
for clock_type in $prefer_resolution_clock_gettime_monotonic $default_resolution_clock_gettime_monotonic $high_resolution_clock_gettime_monotonic $low_resolution_clock_gettime_monotonic; do
- AC_TRY_COMPILE([
+ AC_TRY_LINK([
#include <time.h>
+$trust_test
],
[
struct timespec ts;
@@ -790,7 +785,15 @@ AC_DEFUN(ERL_MONOTONIC_CLOCK,
done
])
- AC_CHECK_FUNCS([clock_getres clock_get_attributes gethrtime])
+ LIBS="$save_LIBS"
+
+ if test "$LD_MAY_BE_WEAK" != "no"; then
+ check_for_clock_getres=
+ else
+ check_for_clock_getres=clock_getres
+ fi
+
+ AC_CHECK_FUNCS([$check_for_clock_getres clock_get_attributes gethrtime])
AC_CACHE_CHECK([for mach clock_get_time() with monotonic clock type], erl_cv_mach_clock_get_time_monotonic,
[
@@ -859,7 +862,7 @@ AC_DEFUN(ERL_MONOTONIC_CLOCK,
break
fi
done
- AC_CHECK_LIB(rt, clock_gettime, [erl_monotonic_clock_lib="-lrt"])
+ erl_monotonic_clock_lib=$clock_gettime_lib
;;
mach_clock_get_time)
erl_monotonic_clock_id=SYSTEM_CLOCK
@@ -898,11 +901,24 @@ AC_DEFUN(ERL_WALL_CLOCK,
;;
esac
+ clock_gettime_lib=""
+ AC_CHECK_LIB(rt, clock_gettime, [clock_gettime_lib="-lrt"])
+
+ save_LIBS="$LIBS"
+ LIBS="$LIBS $clock_gettime_lib"
+
+ if test "$LD_MAY_BE_WEAK" != "no"; then
+ trust_test="#error May not be there due to weak linking"
+ else
+ trust_test=""
+ fi
+
AC_CACHE_CHECK([for clock_gettime() with ${check_msg}wall clock type], erl_cv_clock_gettime_wall_$1,
[
for clock_type in $prefer_resolution_clock_gettime_wall $default_resolution_clock_gettime_wall $high_resolution_clock_gettime_wall $low_resolution_clock_gettime_wall; do
- AC_TRY_COMPILE([
+ AC_TRY_LINK([
#include <time.h>
+$trust_test
],
[
struct timespec ts;
@@ -917,7 +933,15 @@ AC_DEFUN(ERL_WALL_CLOCK,
done
])
- AC_CHECK_FUNCS([clock_getres clock_get_attributes gettimeofday])
+ LIBS="$save_LIBS"
+
+ if test "$LD_MAY_BE_WEAK" != "no"; then
+ check_for_clock_getres=
+ else
+ check_for_clock_getres=clock_getres
+ fi
+
+ AC_CHECK_FUNCS([$check_for_clock_getres clock_get_attributes gettimeofday])
AC_CACHE_CHECK([for mach clock_get_time() with wall clock type], erl_cv_mach_clock_get_time_wall,
[
@@ -938,6 +962,7 @@ AC_DEFUN(ERL_WALL_CLOCK,
erl_cv_mach_clock_get_time_wall=no)
])
+ erl_wall_clock_lib=
erl_wall_clock_low_resolution=no
erl_wall_clock_id=
case $1-$erl_cv_clock_gettime_wall_$1-$erl_cv_mach_clock_get_time_wall-$ac_cv_func_gettimeofday-$host_os in
@@ -951,6 +976,7 @@ AC_DEFUN(ERL_WALL_CLOCK,
;;
*-CLOCK_*-*-*-*)
erl_wall_clock_func=clock_gettime
+ erl_wall_clock_lib=$clock_gettime_lib
erl_wall_clock_id=$erl_cv_clock_gettime_wall_$1
for low_res_id in $low_resolution_clock_gettime_wall; do
if test $erl_wall_clock_id = $low_res_id; then
@@ -991,12 +1017,6 @@ if test "X$host_os" = "Xwin32"; then
THR_LIBS=
THR_LIB_NAME=win32_threads
THR_LIB_TYPE=win32_threads
-elif test "X$host_os" = "Xose"; then
- AC_MSG_RESULT(yes)
- THR_DEFS="-DOSE_THREADS"
- THR_LIBS=
- THR_LIB_NAME=ose_threads
- THR_LIB_TYPE=ose_threads
else
AC_MSG_RESULT(no)
THR_DEFS=
@@ -1504,6 +1524,13 @@ ETHR_LIB_NAME=
ethr_modified_default_stack_size=
+AC_ARG_WITH(threadnames,
+AS_HELP_STRING([--with-threadnames], [use pthread_setname to set the thread names (default)])
+AS_HELP_STRING([--without-threadnames],
+ [do not set any thread names]),
+[],
+[with_threadnames=yes])
+
dnl Name of lib where ethread implementation is located
ethr_lib_name=ethread
@@ -1583,22 +1610,9 @@ case "$THR_LIB_NAME" in
fi
;;
- pthread|ose_threads)
- case "$THR_LIB_NAME" in
- pthread)
- ETHR_THR_LIB_BASE_DIR=pthread
- AC_DEFINE(ETHR_PTHREADS, 1, [Define if you have pthreads])
- ;;
- ose_threads)
- AC_DEFINE(ETHR_OSE_THREADS, 1,
- [Define if you have OSE style threads])
- ETHR_THR_LIB_BASE_DIR=ose
- AC_CHECK_HEADER(ose_spi/ose_spi.h,
- AC_DEFINE(HAVE_OSE_SPI_H, 1,
- [Define if you have the "ose_spi/ose_spi.h" header file.]))
- ;;
- esac
- if test "x$THR_LIB_NAME" = "xpthread"; then
+ pthread)
+ ETHR_THR_LIB_BASE_DIR=pthread
+ AC_DEFINE(ETHR_PTHREADS, 1, [Define if you have pthreads])
case $host_os in
openbsd*)
# The default stack size is insufficient for our needs
@@ -1657,7 +1671,6 @@ case "$THR_LIB_NAME" in
*) ;;
esac
- fi
dnl We sometimes need ETHR_DEFS in order to find certain headers
dnl (at least for pthread.h on osf1).
saved_cppflags="$CPPFLAGS"
@@ -1698,11 +1711,29 @@ case "$THR_LIB_NAME" in
AC_DEFINE(ETHR_TIME_WITH_SYS_TIME, 1, \
[Define if you can safely include both <sys/time.h> and <time.h>.]))
+ AC_MSG_CHECKING([for usable PTHREAD_STACK_MIN])
+ pthread_stack_min=no
+ AC_TRY_COMPILE([
+#include <limits.h>
+#if defined(ETHR_NEED_NPTL_PTHREAD_H)
+#include <nptl/pthread.h>
+#elif defined(ETHR_HAVE_MIT_PTHREAD_H)
+#include <pthread/mit/pthread.h>
+#elif defined(ETHR_HAVE_PTHREAD_H)
+#include <pthread.h>
+#endif
+ ],
+ [return PTHREAD_STACK_MIN;],
+ [pthread_stack_min=yes])
+
+ AC_MSG_RESULT([$pthread_stack_min])
+ test $pthread_stack_min != yes || {
+ AC_DEFINE(ETHR_HAVE_USABLE_PTHREAD_STACK_MIN, 1, [Define if you can use PTHREAD_STACK_MIN])
+ }
dnl
dnl Check for functions
dnl
- if test "x$THR_LIB_NAME" = "xpthread"; then
AC_CHECK_FUNC(pthread_spin_lock, \
[ethr_have_native_spinlock=yes \
AC_DEFINE(ETHR_HAVE_PTHREAD_SPIN_LOCK, 1, \
@@ -1720,7 +1751,7 @@ case "$THR_LIB_NAME" in
AC_DEFINE(ETHR_HAVE_SCHED_YIELD, 1, [Define if you have the sched_yield() function.])
AC_MSG_CHECKING([whether sched_yield() returns an int])
sched_yield_ret_int=no
- AC_TRY_COMPILE([
+ AC_TRY_LINK([
#ifdef ETHR_HAVE_SCHED_H
#include <sched.h>
#endif
@@ -1739,7 +1770,7 @@ case "$THR_LIB_NAME" in
AC_DEFINE(ETHR_HAVE_PTHREAD_YIELD, 1, [Define if you have the pthread_yield() function.])
AC_MSG_CHECKING([whether pthread_yield() returns an int])
pthread_yield_ret_int=no
- AC_TRY_COMPILE([
+ AC_TRY_LINK([
#if defined(ETHR_NEED_NPTL_PTHREAD_H)
#include <nptl/pthread.h>
#elif defined(ETHR_HAVE_MIT_PTHREAD_H)
@@ -1890,12 +1921,12 @@ case "$THR_LIB_NAME" in
[pthread_setname_np("name");],
pthread_setname=darwin)
AC_MSG_RESULT([$pthread_setname])
- case $pthread_setname in
- linux) AC_DEFINE(ETHR_HAVE_PTHREAD_SETNAME_NP_2, 1,
+ case $with_threadnames-$pthread_setname in
+ yes-linux) AC_DEFINE(ETHR_HAVE_PTHREAD_SETNAME_NP_2, 1,
[Define if you have linux style pthread_setname_np]);;
- bsd) AC_DEFINE(ETHR_HAVE_PTHREAD_SET_NAME_NP_2, 1,
+ yes-bsd) AC_DEFINE(ETHR_HAVE_PTHREAD_SET_NAME_NP_2, 1,
[Define if you have bsd style pthread_set_name_np]);;
- darwin) AC_DEFINE(ETHR_HAVE_PTHREAD_SETNAME_NP_1, 1,
+ yes-darwin) AC_DEFINE(ETHR_HAVE_PTHREAD_SETNAME_NP_1, 1,
[Define if you have darwin style pthread_setname_np]);;
*) ;;
esac
@@ -1922,8 +1953,6 @@ case "$THR_LIB_NAME" in
esac
CFLAGS=$old_CFLAGS
- fi ## test "x$THR_LIB_NAME" = "xpthread"
-
if test "X$disable_native_ethr_impls" = "Xyes"; then
ethr_have_native_atomics=no
else
@@ -2478,7 +2507,13 @@ if test $erl_monotonic_clock_low_resolution = yes; then
AC_DEFINE(ERTS_HAVE_LOW_RESOLUTION_OS_MONOTONIC_LOW, [1], [Define if you have a low resolution OS monotonic clock])
fi
-xrtlib="$erl_monotonic_clock_lib"
+xrtlib=
+if test "$erl_monotonic_clock_lib" != ""; then
+ xrtlib="$erl_monotonic_clock_lib"
+fi
+if test "$erl_wall_clock_lib" != ""; then
+ xrtlib="$erl_wall_clock_lib"
+fi
if test "x$erl_monotonic_clock_id" != "x"; then
AC_DEFINE_UNQUOTED(MONOTONIC_CLOCK_ID_STR, ["$erl_monotonic_clock_id"], [Define as a string of monotonic clock id to use])
AC_DEFINE_UNQUOTED(MONOTONIC_CLOCK_ID, [$erl_monotonic_clock_id], [Define to monotonic clock id to use])
diff --git a/erts/autoconf/configure.vxworks b/erts/autoconf/configure.vxworks
index 96dd1f8401..a13e0a6c56 100755
--- a/erts/autoconf/configure.vxworks
+++ b/erts/autoconf/configure.vxworks
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/autoconf/vxworks/sed.general b/erts/autoconf/vxworks/sed.general
index 5adee4db45..96a70e4148 100644
--- a/erts/autoconf/vxworks/sed.general
+++ b/erts/autoconf/vxworks/sed.general
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2013. All Rights Reserved.
+# Copyright Ericsson AB 1997-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/autoconf/vxworks/sed.vxworks_cpu32 b/erts/autoconf/vxworks/sed.vxworks_cpu32
index e3d54246ab..71663676e7 100644
--- a/erts/autoconf/vxworks/sed.vxworks_cpu32
+++ b/erts/autoconf/vxworks/sed.vxworks_cpu32
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2010. All Rights Reserved.
+# Copyright Ericsson AB 1997-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/autoconf/vxworks/sed.vxworks_ppc32 b/erts/autoconf/vxworks/sed.vxworks_ppc32
index 012760e127..2146e862fd 100644
--- a/erts/autoconf/vxworks/sed.vxworks_ppc32
+++ b/erts/autoconf/vxworks/sed.vxworks_ppc32
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2006-2013. All Rights Reserved.
+# Copyright Ericsson AB 2006-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/autoconf/vxworks/sed.vxworks_ppc603 b/erts/autoconf/vxworks/sed.vxworks_ppc603
index 55af52571b..fca1ba76d9 100644
--- a/erts/autoconf/vxworks/sed.vxworks_ppc603
+++ b/erts/autoconf/vxworks/sed.vxworks_ppc603
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2010. All Rights Reserved.
+# Copyright Ericsson AB 2000-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/autoconf/vxworks/sed.vxworks_ppc603_nolongcall b/erts/autoconf/vxworks/sed.vxworks_ppc603_nolongcall
index e0c0891aeb..51c589d79a 100644
--- a/erts/autoconf/vxworks/sed.vxworks_ppc603_nolongcall
+++ b/erts/autoconf/vxworks/sed.vxworks_ppc603_nolongcall
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2010. All Rights Reserved.
+# Copyright Ericsson AB 1997-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/autoconf/vxworks/sed.vxworks_ppc860 b/erts/autoconf/vxworks/sed.vxworks_ppc860
index 8828339e34..485504e706 100644
--- a/erts/autoconf/vxworks/sed.vxworks_ppc860
+++ b/erts/autoconf/vxworks/sed.vxworks_ppc860
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2010. All Rights Reserved.
+# Copyright Ericsson AB 1997-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/autoconf/vxworks/sed.vxworks_simlinux b/erts/autoconf/vxworks/sed.vxworks_simlinux
index 950fb79ec5..10cd7bbb82 100644
--- a/erts/autoconf/vxworks/sed.vxworks_simlinux
+++ b/erts/autoconf/vxworks/sed.vxworks_simlinux
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2008-2013. All Rights Reserved.
+# Copyright Ericsson AB 2008-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/autoconf/vxworks/sed.vxworks_simso b/erts/autoconf/vxworks/sed.vxworks_simso
index 6f2796f04e..cd30f8c2b2 100644
--- a/erts/autoconf/vxworks/sed.vxworks_simso
+++ b/erts/autoconf/vxworks/sed.vxworks_simso
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2013. All Rights Reserved.
+# Copyright Ericsson AB 2005-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/autoconf/vxworks/sed.vxworks_sparc b/erts/autoconf/vxworks/sed.vxworks_sparc
index e67c34af26..a3758423e8 100644
--- a/erts/autoconf/vxworks/sed.vxworks_sparc
+++ b/erts/autoconf/vxworks/sed.vxworks_sparc
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/configure.in b/erts/configure.in
index 4d6f88e63b..7700085df2 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -2,7 +2,7 @@ dnl Process this file with autoconf to produce a configure script. -*-m4-*-
dnl %CopyrightBegin%
dnl
-dnl Copyright Ericsson AB 1997-2015. All Rights Reserved.
+dnl Copyright Ericsson AB 1997-2016. All Rights Reserved.
dnl
dnl Licensed under the Apache License, Version 2.0 (the "License");
dnl you may not use this file except in compliance with the License.
@@ -62,9 +62,6 @@ if test x"${ERL_TOP}/erts" != x"$srcdir"; then
fi
erl_top=${ERL_TOP}
-# Remove old configuration information
-/bin/rm -f "$ERL_TOP/erts/CONF_INFO"
-
# echo XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# echo X
# echo "X srcdir = $srcdir"
@@ -102,11 +99,10 @@ ERL_XCOMP_SYSROOT_INIT
AC_ISC_POSIX
-AC_CONFIG_HEADER($host/config.h:config.h.in include/internal/$host/ethread_header_config.h:include/internal/ethread_header_config.h.in include/$host/erl_int_sizes_config.h:include/erl_int_sizes_config.h.in include/$host/erl_native_features_config.h:include/erl_native_features_config.h.in)
+AC_CONFIG_HEADER($host/config.h:config.h.in include/internal/$host/ethread_header_config.h:include/internal/ethread_header_config.h.in include/$host/erl_int_sizes_config.h:include/erl_int_sizes_config.h.in)
dnl ----------------------------------------------------------------------
dnl Optional features.
dnl ----------------------------------------------------------------------
-enable_child_waiter_thread=no
ENABLE_ALLOC_TYPE_VARS=
AC_SUBST(ENABLE_ALLOC_TYPE_VARS)
@@ -125,6 +121,7 @@ AS_HELP_STRING([--enable-bootstrap-only],
enable_hipe=no
enable_sctp=no
enable_dirty_schedulers=no
+ enable_new_purge=no
fi
])
@@ -141,15 +138,14 @@ AS_HELP_STRING([--enable-dirty-schedulers], [enable dirty scheduler support]),
[ case "$enableval" in
no) enable_dirty_schedulers=no ;;
*) enable_dirty_schedulers=yes ;;
- esac ], enable_dirty_schedulers=no)
+ esac ], enable_dirty_schedulers=default)
-AC_ARG_ENABLE(halfword-emulator,
-AS_HELP_STRING([--enable-halfword-emulator],
- [enable halfword emulator (only for 64bit builds). Note: Halfword emulator is marked as deprecated and scheduled for removal in future major release.]),
+AC_ARG_ENABLE(new-purge-strategy,
+AS_HELP_STRING([--enable-new-purge-strategy], [enable new code purge strategy]),
[ case "$enableval" in
- no) enable_halfword_emualtor=no ;;
- *) enable_halfword_emulator=yes ;;
- esac ], enable_halfword_emulator=unknown)
+ no) enable_new_purge=no ;;
+ *) enable_new_purge=yes ;;
+ esac ], enable_new_purge=default)
AC_ARG_ENABLE(smp-support,
AS_HELP_STRING([--enable-smp-support], [enable smp support])
@@ -200,12 +196,18 @@ AS_HELP_STRING([--disable-kernel-poll], [disable kernel poll support]),
AC_ARG_ENABLE(sctp,
-AS_HELP_STRING([--enable-sctp], [enable sctp support (default)])
+AS_HELP_STRING([--enable-sctp], [enable sctp support (default)
+to on demand load the SCTP library in runtime if needed])
+AS_HELP_STRING([--enable-sctp=lib], [enable sctp support
+to link against the SCTP library])
AS_HELP_STRING([--disable-sctp], [disable sctp support]),
-[ case "$enableval" in
- no) enable_sctp=no ;;
- *) enable_sctp=yes ;;
- esac ], enable_sctp=unknown)
+[ case "x$enableval" in
+ xno|xyes|xlib|x)
+ ;;
+ x*)
+ AC_MSG_ERROR("invalid value --enable-sctp=$enableval")
+ ;;
+ esac ])
AC_ARG_ENABLE(hipe,
AS_HELP_STRING([--enable-hipe], [enable hipe support])
@@ -224,24 +226,6 @@ AS_HELP_STRING([--enable-fp-exceptions],
esac
],enable_fp_exceptions=auto)
-AC_ARG_ENABLE(darwin-universal,
-AS_HELP_STRING([--enable-darwin-universal],
- [build universal binaries on darwin i386]),
-[ case "$enableval" in
- no) enable_darwin_universal=no ;;
- *) enable_darwin_univeral=yes ;;
- esac
-],enable_darwin_universal=no)
-
-
-AC_ARG_ENABLE(darwin-64bit,
-AS_HELP_STRING([--enable-darwin-64bit], [build 64bit binaries on darwin]),
-[ case "$enableval" in
- no) enable_darwin_64bit=no ;;
- *) enable_darwin_64bit=yes ;;
- esac
-],enable_darwin_64bit=no)
-
AC_ARG_ENABLE(m64-build,
AS_HELP_STRING([--enable-m64-build],
[build 64bit binaries using the -m64 flag to (g)cc]),
@@ -256,18 +240,13 @@ AS_HELP_STRING([--enable-m32-build],
[build 32bit binaries using the -m32 flag to (g)cc]),
[ case "$enableval" in
no) enable_m32_build=no ;;
- *)
- if test X${enable_darwin_64bit} = Xyes -o X${enable_m64_build} = Xyes;
- then
- AC_MSG_ERROR([(--enable-darwin-64bit or --enable-m64-build) and --enable-m32-build are mutually exclusive]) ;
- fi ;
- enable_m32_build=yes ;;
+ *) enable_m32_build=yes ;;
esac
],enable_m32_build=no)
AC_ARG_WITH(dynamic-trace,
-AS_HELP_STRING([--with-dynamic-trace={dtrace|systemtap}],
- [specify use of dynamic trace framework, dtrace or systemtap])
+AS_HELP_STRING([--with-dynamic-trace={dtrace|lttng|systemtap}],
+ [specify use of dynamic trace framework, dtrace, lttng or systemtap])
AS_HELP_STRING([--without-dynamic-trace],
[don't enable any dynamic tracing (default)]))
@@ -277,6 +256,10 @@ fi
case "$with_dynamic_trace" in
no) DYNAMIC_TRACE_FRAMEWORK=;;
+ lttng)
+ AC_DEFINE(USE_LTTNG,[1],
+ [Define if you want to use lttng for dynamic tracing])
+ DYNAMIC_TRACE_FRAMEWORK=lttng;;
dtrace)
AC_DEFINE(USE_DTRACE,[1],
[Define if you want to use dtrace for dynamic tracing])
@@ -312,10 +295,12 @@ AS_HELP_STRING([--enable-vm-probes],
fi)
AC_SUBST(USE_VM_PROBES)
-if test X"$use_vm_probes" = X"yes"; then
- USE_VM_PROBES=yes
- AC_DEFINE(USE_VM_PROBES,[1],
- [Define to enable VM dynamic trace probes])
+if test X"$DYNAMIC_TRACE_FRAMEWORK" != X"lttng"; then
+ if test X"$use_vm_probes" = X"yes"; then
+ USE_VM_PROBES=yes
+ AC_DEFINE(USE_VM_PROBES,[1],
+ [Define to enable VM dynamic trace probes])
+ fi
fi
AC_ARG_WITH(assumed-cache-line-size,
@@ -351,6 +336,21 @@ AS_HELP_STRING([--disable-saved-compile-time], [disable saved compile time]),
AC_DEFINE_UNQUOTED(ERTS_SAVED_COMPILE_TIME, $save_compile_time, [Save compile time?])
+AC_ARG_WITH(microstate-accounting,
+AS_HELP_STRING([--with-microstate-accounting={yes|extra}],
+ [enable microstate account, possibly with extra detailed states])
+AS_HELP_STRING([--without-microstate-accounting],
+ [don't enable microstate accounting]),
+[],[with_microstate_accounting=yes])
+
+case "$with_microstate_accounting" in
+ yes) AC_DEFINE(ERTS_ENABLE_MSACC,[1],
+ [Define as 1 if you want to enable microstate accounting, 2 if you want extra states]) ;;
+ extra) AC_DEFINE(ERTS_ENABLE_MSACC,[2],
+ [Define as 1 if you want to enable microstate accounting, 2 if you want extra states]) ;;
+ *) ;;
+esac
+
dnl Magic test for clearcase.
OTP_RELEASE=
if test "${ERLANG_COMMERCIAL_BUILD}" != ""; then
@@ -371,42 +371,7 @@ AC_MSG_CHECKING([OTP version])
AC_MSG_RESULT([$OTP_VERSION])
AC_SUBST(OTP_VERSION)
-dnl OK, we might have darwin switches off different kinds, lets
-dnl check it all before continuing.
-TMPSYS=`uname -s`-`uname -m`
-if test X${enable_darwin_universal} = Xyes; then
- if test X${enable_darwin_64bit} = Xyes; then
- AC_MSG_ERROR([--enable-darwin-universal and --enable-darwin-64bit mutually exclusive])
- fi
- enable_hipe=no
- case $CFLAGS in
- *-arch\ ppc*)
- ;;
- *)
- CFLAGS="-arch ppc $CFLAGS"
- ;;
- esac
- case $CFLAGS in
- *-arch\ i386*)
- ;;
- *)
- CFLAGS="-arch i386 $CFLAGS"
- ;;
- esac
-fi
-if test X${enable_darwin_64bit} = Xyes; then
- case "$TMPSYS" in
- Darwin-i386|Darwin-x86_64)
- ;;
- Darwin*)
- AC_MSG_ERROR([--enable-darwin-64bit only supported on x86 hosts])
- ;;
- *)
- AC_MSG_ERROR([--enable-darwin-64bit only supported on Darwin])
- ;;
- esac
-fi
-if test X${enable_darwin_64bit} = Xyes -o X${enable_m64_build} = Xyes; then
+if test X${enable_m64_build} = Xyes; then
case $CFLAGS in
*-m64*)
;;
@@ -470,9 +435,6 @@ case $host_os in
# -D_WIN32_WINNT=* from CPPFLAGS is saved in ETHR_DEFS.
CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0600 -DWINVER=0x0600"
;;
- darwin*)
- CPPFLAGS="$CPPFLAGS -D_XOPEN_SOURCE"
- ;;
*)
;;
esac
@@ -578,6 +540,7 @@ fi
if test "x$GCC" = xyes; then
# Treat certain GCC warnings as errors
LM_TRY_ENABLE_CFLAG([-Werror=return-type], [WERRORFLAGS])
+ LM_TRY_ENABLE_CFLAG([-Werror=implicit], [WERRORFLAGS])
# until the emulator can handle this, I suggest we turn it off!
#WFLAGS="-Wall -Wshadow -Wcast-qual -Wmissing-declarations"
@@ -731,32 +694,13 @@ case $ARCH-$OPSYS in
esac
;;
*-darwin*)
- if test X${enable_darwin_universal} = Xyes; then
- AC_MSG_NOTICE([Adjusting LDFLAGS for universal binaries])
-
- case $LDFLAGS in
- *-arch\ ppc*)
- ;;
- *)
- LDFLAGS="-arch ppc $LDFLAGS"
- ;;
- esac
- case $LDFLAGS in
- *-arch\ i386*)
- ;;
- *)
- LDFLAGS="-arch i386 $LDFLAGS"
- ;;
- esac
- else
- case $LDFLAGS in
- *-m32*)
- ;;
- *)
- LDFLAGS="-m32 $LDFLAGS"
- ;;
- esac
- fi
+ case $LDFLAGS in
+ *-m32*)
+ ;;
+ *)
+ LDFLAGS="-m32 $LDFLAGS"
+ ;;
+ esac
;;
*)
if test X${enable_m64_build} = Xyes; then
@@ -796,34 +740,40 @@ esac
AC_SUBST(LIBCARBON)
-dnl Check if we should/can build a halfword emulator
-
-AC_MSG_CHECKING(if we are building a halfword emulator (32bit heap on 64bit machine))
-if test "$enable_halfword_emulator" = "yes"; then
- if test "$ARCH" = "amd64"; then
- AC_DEFINE(HALFWORD_HEAP_EMULATOR, [1],
- [Define if building a halfword-heap 64bit emulator])
- ENABLE_ALLOC_TYPE_VARS="$ENABLE_ALLOC_TYPE_VARS halfword"
- AC_MSG_RESULT([yes])
+_search_path=/bin:/usr/bin:/usr/local/bin:$PATH
- test -f "$ERL_TOP/erts/CONF_INFO" ||
- echo "" > "$ERL_TOP/erts/CONF_INFO"
- cat >> $ERL_TOP/erts/CONF_INFO <<EOF
+AC_PATH_PROG(RM, rm, false, $_search_path)
+if test "$ac_cv_path_RM" = false; then
+ AC_MSG_ERROR([No 'rm' command found])
+fi
- The HALFWORD emulator has been enabled.
- This is a DEPRECATED feature scheduled for removal
- in a future major release.
+AC_PATH_PROG(MKDIR, mkdir, false, $_search_path)
+if test "$ac_cv_path_MKDIR" = false; then
+ AC_MSG_ERROR([No 'mkdir' command found])
+fi
-EOF
- else
- AC_MSG_ERROR(no; halfword emulator not supported on this architecture)
- fi
-else
- AC_MSG_RESULT([no])
+AC_PATH_PROG(CP, cp, false, $_search_path)
+if test "$ac_cv_path_CP" = false; then
+ AC_MSG_ERROR([No 'cp' command found])
fi
+_search_path=
+# Remove old configuration information.
+# Next line should be placed after AC_PATH_PROG(RM, ...), but before
+# first output to CONN_INFO. So this is just the right place.
+$RM -f "$ERL_TOP/erts/CONF_INFO"
+
+dnl Check if we should/can build a sharing-preserving emulator
+AC_MSG_CHECKING(if we are building a sharing-preserving emulator)
+if test "$enable_sharing_preserving" = "yes"; then
+ AC_DEFINE(SHCOPY, [1],
+ [Define if building a sharing-preserving emulator])
+ AC_MSG_RESULT([yes])
+else
+ AC_MSG_RESULT([no])
+fi
dnl some tests below will call this if we haven't already - and autoconf
@@ -845,32 +795,13 @@ if test "$ac_cv_prog_AR" = false; then
AC_MSG_ERROR([No 'ar' command found in PATH])
fi
-_search_path=/bin:/usr/bin:/usr/local/bin:$PATH
-
-AC_PATH_PROG(RM, rm, false, $_search_path)
-if test "$ac_cv_path_RM" = false; then
- AC_MSG_ERROR([No 'rm' command found])
-fi
-
-AC_PATH_PROG(MKDIR, mkdir, false, $_search_path)
-if test "$ac_cv_path_MKDIR" = false; then
- AC_MSG_ERROR([No 'mkdir' command found])
-fi
-
-AC_PATH_PROG(CP, cp, false, $_search_path)
-if test "$ac_cv_path_CP" = false; then
- AC_MSG_ERROR([No 'cp' command found])
-fi
-
-_search_path=
-
#
# Get programs needed for building the documentation
#
## Delete previous failed configure results
if test -f doc/CONF_INFO; then
- rm doc/CONF_INFO
+ $RM doc/CONF_INFO
fi
AC_CHECK_PROGS(XSLTPROC, xsltproc)
@@ -949,10 +880,23 @@ dnl what the user say. This might not be the right way to do it, but
dnl for now that is the way we do it.
USER_LD=$LD
USER_LDFLAGS="$LDFLAGS"
-case $host in
- *ose) ;;
- *) LD='$(CC)' ;;
+LD='$(CC)'
+case $host_os in
+ darwin*)
+ saved_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS -Wl,-no_weak_imports"
+ AC_TRY_LINK([],[],
+ [
+ LD_MAY_BE_WEAK=no
+ ],
+ [
+ LD_MAY_BE_WEAK=yes
+ LDFLAGS="$saved_LDFLAGS"
+ ]);;
+ *)
+ LD_MAY_BE_WEAK=no;;
esac
+
AC_SUBST(LD)
LDFLAG_RUNTIME_LIBRARY_PATH="$CFLAG_RUNTIME_LIBRARY_PATH"
@@ -967,8 +911,6 @@ dnl This is the os flavour, should be unix, ose, vxworks or win32
case $host in
win32)
ERLANG_OSTYPE=win32 ;;
- *ose)
- ERLANG_OSTYPE=ose ;;
*)
ERLANG_OSTYPE=unix ;;
esac
@@ -1099,6 +1041,44 @@ case $ERTS_BUILD_SMP_EMU in
;;
esac
+AC_MSG_CHECKING(whether dirty schedulers should be enabled)
+case $ERTS_BUILD_SMP_EMU-$enable_dirty_schedulers in
+ yes-yes)
+ DIRTY_SCHEDULER_SUPPORT=yes;;
+ yes-default)
+ ## Maybe yes for OTP 19...
+ DIRTY_SCHEDULER_SUPPORT=no;;
+ no-default)
+ DIRTY_SCHEDULER_SUPPORT=no;;
+ no-yes)
+ AC_MSG_ERROR([No smp emulator will be built, but dirty schedulers requested]);;
+ *)
+ DIRTY_SCHEDULER_SUPPORT=no;;
+esac
+AC_MSG_RESULT($DIRTY_SCHEDULER_SUPPORT)
+AC_SUBST(DIRTY_SCHEDULER_SUPPORT)
+
+AC_MSG_CHECKING(whether the new code purge strategy should be enabled)
+case $enable_new_purge-$enable_dirty_schedulers in
+ yes-*)
+ AC_MSG_RESULT(yes)
+ enable_new_purge=yes;;
+ default-yes)
+ AC_MSG_RESULT(yes; forced by dirty scheduler support)
+ enable_new_purge=yes;;
+ no-yes)
+ AC_MSG_ERROR([Dirty schedulers enabled, but new code purge strategy disabled]);;
+ *)
+ AC_MSG_RESULT(no)
+ enable_new_purge=no;;
+esac
+
+if test $enable_new_purge = yes; then
+ AC_DEFINE(ERTS_NEW_PURGE_STRATEGY, 1, [Define if you want to use the new code purge strategy])
+fi
+NEW_PURGE_STRATEGY=$enable_new_purge
+AC_SUBST(NEW_PURGE_STRATEGY)
+
if test $ERTS_BUILD_SMP_EMU = yes; then
if test $found_threads = no; then
@@ -1277,7 +1257,7 @@ case "$enable_threads"-"$found_threads" in
AC_MSG_RESULT(yes; enabled by user) ;;
unknown-yes)
case $host_os in
- solaris*|linux*|darwin*|win32|ose)
+ solaris*|linux*|darwin*|win32)
emu_threads=yes
AC_MSG_RESULT(yes; default on this platform)
;;
@@ -1301,14 +1281,6 @@ esac
if test $emu_threads != yes; then
enable_lock_check=no
enable_lock_count=no
- AC_MSG_CHECKING(whether dirty schedulers should be enabled)
- if test "x$enable_dirty_schedulers" != "xno"; then
- AC_DEFINE(ERL_NIF_DIRTY_SCHEDULER_SUPPORT, 1, [Dirty scheduler support])
- AC_DEFINE(ERL_DRV_DIRTY_SCHEDULER_SUPPORT, 1, [Dirty scheduler support])
- AC_MSG_RESULT(yes)
- else
- AC_MSG_RESULT(no)
- fi
else
# Threads enabled for emulator
EMU_THR_LIB_NAME=$ETHR_LIB_NAME
@@ -1328,22 +1300,7 @@ else
EMU_THR_DEFS="$EMU_THR_DEFS -DERTS_ENABLE_LOCK_COUNT"
fi
- AC_MSG_CHECKING(whether dirty schedulers should be enabled)
- if test "x$enable_dirty_schedulers" != "xno"; then
- EMU_THR_DEFS="$EMU_THR_DEFS -DERTS_DIRTY_SCHEDULERS"
- AC_DEFINE(ERTS_DIRTY_SCHEDULERS, 1, [Define if the emulator supports dirty schedulers])
- AC_DEFINE(ERL_NIF_DIRTY_SCHEDULER_SUPPORT, 1, [Dirty scheduler support])
- AC_DEFINE(ERL_DRV_DIRTY_SCHEDULER_SUPPORT, 1, [Dirty scheduler support])
- AC_MSG_RESULT(yes)
- else
- AC_MSG_RESULT(no)
- fi
-
- disable_child_waiter_thread=no
case $host_os in
- solaris*)
- enable_child_waiter_thread=yes
- ;;
linux*)
AC_MSG_CHECKING([whether dlopen() needs to be called before first call to dlerror()])
if test "x$ETHR_THR_LIB_BASE_TYPE" != "xposix_nptl"; then
@@ -1353,16 +1310,6 @@ else
else
AC_MSG_RESULT(no)
fi
- if test "x$ETHR_THR_LIB_BASE_TYPE" != "xposix_nptl"; then
- # Child waiter thread cannot be enabled
- disable_child_waiter_thread=yes
- enable_child_waiter_thread=no
- fi
- ;;
- win32|ose)
- # Child waiter thread cannot be enabled
- disable_child_waiter_thread=yes
- enable_child_waiter_thread=no
;;
*)
;;
@@ -1382,24 +1329,6 @@ else
esac
done
EMU_THR_DEFS=$new_emu_thr_defs
-
- AC_MSG_CHECKING(whether the child waiter thread should be enabled)
- if test $enable_child_waiter_thread = yes; then
- AC_DEFINE(ENABLE_CHILD_WAITER_THREAD,[1],
- [Define if you want to enable child waiter thread])
- AC_MSG_RESULT(yes)
- else
- case $ERTS_BUILD_SMP_EMU-$disable_child_waiter_thread in
- yes-no)
- AC_MSG_RESULT([yes on SMP build, but not on non-SMP build]);;
- *-yes)
- AC_DEFINE(DISABLE_CHILD_WAITER_THREAD,[1],
- [Define if you want to disable child waiter thread])
- AC_MSG_RESULT(no);;
- *)
- AC_MSG_RESULT(no);;
- esac
- fi
fi
AC_SUBST(EMU_THR_LIB_NAME)
@@ -1531,19 +1460,27 @@ dnl
# if -lsocket doesn't work by itself.
#--------------------------------------------------------------------
+tk_oldLibs=$LIBS
erl_checkBoth=0
+SOCKET_LIBS=""
AC_CHECK_FUNC(connect, erl_checkSocket=0, erl_checkSocket=1)
if test "$erl_checkSocket" = 1; then
- AC_CHECK_LIB(socket, main, LIBS="$LIBS -lsocket", erl_checkBoth=1)
+ AC_CHECK_LIB(socket, main, SOCKET_LIBS="-lsocket", erl_checkBoth=1)
fi
+
if test "$erl_checkBoth" = 1; then
- tk_oldLibs=$LIBS
LIBS="$LIBS -lsocket -lnsl"
- AC_CHECK_FUNC(accept, erl_checkNsl=0, [LIBS=$tk_oldLibs])
+ AC_CHECK_FUNC(accept, SOCKET_LIBS="-lsocket -lnsl")
fi
-AC_CHECK_FUNC(gethostbyname, , AC_CHECK_LIB(nsl, main, [LIBS="$LIBS -lnsl"]))
+
+LIBS="$tk_oldLibs $SOCKET_LIBS"
+AC_CHECK_FUNC(gethostbyname, , AC_CHECK_LIB(nsl, main, [SOCKET_LIBS="$SOCKET_LIBS -lnsl"]))
AC_CHECK_FUNC(gethostbyname_r,have_gethostbyname_r=yes)
+LIBS="$tk_oldLibs $SOCKET_LIBS"
+
+AC_SUBST(SOCKET_LIBS)
+
dnl
dnl These gethostbyname thingies use old style AC_DEFINE for BC with ancient
dnl autoconf...
@@ -1671,7 +1608,7 @@ AC_CHECK_HEADERS(fcntl.h limits.h unistd.h syslog.h dlfcn.h ieeefp.h \
sys/ioctl.h sys/time.h sys/uio.h \
sys/socket.h sys/sockio.h sys/socketio.h \
net/errno.h malloc.h arpa/nameser.h libdlpi.h \
- pty.h util.h utmp.h langinfo.h poll.h sdkddkver.h)
+ pty.h util.h libutil.h utmp.h langinfo.h poll.h sdkddkver.h)
AC_CHECK_MEMBERS([struct ifreq.ifr_hwaddr], [], [],
[#ifdef __WIN32__
@@ -1795,6 +1732,8 @@ if test "x$enable_sctp" != "xno" ; then
fi
if test x"$ac_cv_header_netinet_sctp_h" = x"yes"; then
+ AS_IF([test "x$enable_sctp" = "xlib"],
+ AC_CHECK_LIB(sctp, sctp_bindx))
AC_CHECK_FUNCS([sctp_bindx sctp_peeloff sctp_getladdrs sctp_freeladdrs sctp_getpaddrs sctp_freepaddrs])
AC_CHECK_DECLS([SCTP_UNORDERED, SCTP_ADDR_OVER, SCTP_ABORT,
SCTP_EOF, SCTP_SENDALL, SCTP_ADDR_CONFIRMED,
@@ -2048,7 +1987,7 @@ getaddrinfo("","",NULL,NULL);
if test $have_getaddrinfo = yes; then
AC_MSG_RESULT([yes])
AC_MSG_CHECKING([whether getaddrinfo accepts enough flags])
- AC_TRY_COMPILE([
+ AC_TRY_LINK([
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_WINSOCK2_H
@@ -2123,7 +2062,7 @@ AC_CHECK_FUNCS([getipnodebyname getipnodebyaddr gethostbyname2])
AC_CHECK_FUNCS([ieee_handler fpsetmask finite isnan isinf res_gethostbyname dlopen \
pread pwrite memmove strerror strerror_r strncasecmp \
gethrtime localtime_r gmtime_r inet_pton \
- memcpy mallopt sbrk _sbrk __sbrk brk _brk __brk \
+ mmap mremap memcpy mallopt sbrk _sbrk __sbrk brk _brk __brk \
flockfile fstat strlcpy strlcat setsid posix2time time2posix \
setlocale nl_langinfo poll mlockall ppoll])
@@ -2141,8 +2080,7 @@ fi
case X$erl_xcomp_posix_memalign in
Xno) ;;
- Xyes) AC_DEFINE(HAVE_POSIX_MEMALIGN,[1],
- [Define to 1 if you have the `posix_memalign' function.]) ;;
+ Xyes) have_posix_memalign=yes ;;
*)
AC_CHECK_FUNC(
[posix_memalign],
@@ -2157,15 +2095,19 @@ int main(void) {
return error;
return 0;
}
-],AC_DEFINE(HAVE_POSIX_MEMALIGN,[1],
- [Define to 1 if you have the `posix_memalign' function.])
+],have_posix_memalign=yes
)
else
- AC_DEFINE(HAVE_POSIX_MEMALIGN,[1],
- [Define to 1 if you have the `posix_memalign' function.])
+ have_posix_memalign=yes
fi]);;
esac
+if test "$have_posix_memalign" = "yes"; then
+ AC_DEFINE(HAVE_POSIX_MEMALIGN,[1],
+ [Define to 1 if you have the `posix_memalign' function.])
+fi
+
+
dnl writev on OS X snow leopard is broken for files > 4GB
case $host_os in
darwin10.8.0)
@@ -2175,17 +2117,6 @@ case $host_os in
AC_CHECK_FUNCS([writev]) ;;
esac
-case $host_os in
- *ose)
- AC_MSG_CHECKING([for mmap])
- AC_MSG_RESULT(not using for OSE)
- AC_MSG_CHECKING([for mremap])
- AC_MSG_RESULT(not using for OSE) ;;
- *)
- AC_CHECK_FUNCS([mmap mremap]) ;;
-esac
-
-
AC_CHECK_DECLS([posix2time, time2posix],,,[#include <time.h>])
disable_vfork=false
@@ -2243,7 +2174,7 @@ fi
dnl Need by run_erl.
AC_CHECK_FUNCS([openpty])
-AC_CHECK_HEADERS(net/if_dl.h ifaddrs.h netpacket/packet.h)
+AC_CHECK_HEADERS(net/if_dl.h ifaddrs.h netpacket/packet.h sys/un.h)
AC_CHECK_FUNCS([getifaddrs])
dnl Checks for variables in6addr_any and in6addr_loopback,
@@ -2313,7 +2244,7 @@ dnl Checks for features/quirks in the system that affects Erlang.
dnl ----------------------------------------------------------------------
AC_MSG_CHECKING([for sched_getaffinity/sched_setaffinity])
-AC_TRY_COMPILE([#include <sched.h>],
+AC_TRY_LINK([#include <sched.h>],
[
#ifndef CPU_SETSIZE
#error no CPU_SETSIZE
@@ -2336,7 +2267,7 @@ fi
AC_MSG_CHECKING([for pset functionality])
-AC_TRY_COMPILE([#include <sys/pset.h>],
+AC_TRY_LINK([#include <sys/pset.h>],
[
int res;
psetid_t id = PS_MYID;
@@ -2354,7 +2285,7 @@ if test $pset_functionality = yes; then
fi
AC_MSG_CHECKING([for processor_bind functionality])
-AC_TRY_COMPILE([
+AC_TRY_LINK([
#include <sys/types.h>
#include <sys/processor.h>
#include <sys/procset.h>
@@ -2370,7 +2301,7 @@ if test $processor_bind_functionality = yes; then
fi
AC_MSG_CHECKING([for cpuset_getaffinity/cpuset_setaffinity])
-AC_TRY_COMPILE([
+AC_TRY_LINK([
#include <sys/param.h>
#include <sys/cpuset.h>
],
@@ -2569,7 +2500,7 @@ if test "x$ac_cv_func_sbrk" = "xyes"; then
for rtype in $ret_types; do
for atype in $arg_types; do
IFS=$save_ifs
- AC_TRY_COMPILE([#include <sys/types.h>
+ AC_TRY_LINK([#include <sys/types.h>
#include <unistd.h>],
[$rtype sbrk($atype incr);],
[erts_cv_sbrk_ret_arg_types="$rtype,$atype"])
@@ -2606,7 +2537,7 @@ if test $ac_cv_func_brk = yes; then
for rtype in $ret_types; do
for atype in $arg_types; do
IFS=$save_ifs
- AC_TRY_COMPILE([#include <sys/types.h>
+ AC_TRY_LINK([#include <sys/types.h>
#include <unistd.h>],
[$rtype brk($atype endds);],
[erts_cv_brk_ret_arg_types="$rtype,$atype"])
@@ -2834,17 +2765,26 @@ LM_SYS_IPV6
LM_SYS_MULTICAST
ERL_TIME_CORRECTION
AC_CHECK_PROG(M4, m4, m4)
+
+
+dnl HiPE cannot run on 64-bit without MAP_FIXED and MAP_NORESERVE
+if test X${enable_hipe} != Xno && test X$ac_cv_sizeof_void_p != X4; then
+ AC_CHECK_DECLS([MAP_FIXED, MAP_NORESERVE], [], [], [#include <sys/mman.h>])
+ if test X$ac_cv_have_decl_MAP_FIXED != Xyes || test X$ac_cv_have_decl_MAP_NORESERVE != Xyes; then
+ if test X${enable_hipe} = Xyes; then
+ AC_MSG_ERROR([HiPE on 64-bit needs MAP_FIXED and MAP_NORESERVE flags for mmap()])
+ else
+ enable_hipe=no
+ AC_MSG_WARN([Disable HiPE due to lack of MAP_FIXED and MAP_NORESERVE flags for mmap()])
+ fi
+ fi
+fi
+
dnl check to auto-enable hipe here...
if test "$cross_compiling" != "yes" && test X${enable_hipe} != Xno; then
if test -z "$M4"; then
enable_hipe=no
AC_MSG_NOTICE([HiPE disabled as no valid m4 is found in PATH])
- elif test "$enable_halfword_emulator" = "yes"; then
- if test X${enable_hipe} = Xyes; then
- AC_MSG_ERROR([HiPE can not be combined with halfword emulator (yet)])
- else
- AC_MSG_NOTICE([HiPE auto-disabled on halfword emulator])
- fi
else
case "$ARCH-$OPSYS" in
x86-linux|amd64-linux|x86-darwin*|amd64-darwin*|ppc-linux|ppc64-linux|ppc-darwin|arm-linux|amd64-freebsd|x86-freebsd|x86-sol2|amd64-sol2|ultrasparc-linux)
@@ -2854,43 +2794,22 @@ if test "$cross_compiling" != "yes" && test X${enable_hipe} != Xno; then
fi
fi
-case $ARCH-$OPSYS in
- amd64-darwin*|x86-darwin*)
- AC_MSG_CHECKING([For modern (leopard) style mcontext_t])
- AC_TRY_COMPILE([
- #include <stdlib.h>
- #include <sys/types.h>
- #include <unistd.h>
- #include <mach/mach.h>
- #include <pthread.h>
- #include <machine/signal.h>
- #include <ucontext.h>
- ],[
- #if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__)
- #define __DARWIN__ 1
- #endif
+dnl Check to disable -fPIE and friends for HiPE on amd64
+if test X${enable_hipe} = Xyes && test X$ARCH = Xamd64; then
+ AC_TRY_COMPILE(, [#if defined(__pie__) || defined(__PIE__)
+ #error -fPIE is enabled by default
+ #endif],
+ [AC_MSG_NOTICE([No -fPIE enabled by default])],
+ [AC_MSG_WARN([Security feature -fPIE will be disabled for HiPE])
+ STATIC_CFLAGS="-fno-PIE $STATIC_CFLAGS"
+ saved_LDFLAGS=$LDFLAGS
+ LDFLAGS="-no-pie $LDFLAGS"
+ AC_TRY_LINK(,, [],
+ [AC_MSG_WARN([Linked does not accept option -no-pie])
+ LDFLAGS=$saved_LDFLAGS])])
- #ifndef __DARWIN__
- #error inpossible
- #else
+fi
- mcontext_t mc = NULL;
- int x = mc->__fs.__fpu_mxcsr;
-
- #endif
- ],darwin_mcontext_leopard=yes,
- darwin_mcontext_leopard=no)
- if test X"$darwin_mcontext_leopard" = X"yes"; then
- AC_DEFINE(DARWIN_MODERN_MCONTEXT,[],[Modern style mcontext_t in MacOSX])
- AC_MSG_RESULT(yes)
- else
- AC_MSG_RESULT(no)
- fi
- ;;
- *)
- darwin_mcontext_leopard=no
- ;;
-esac
if test X${enable_fp_exceptions} = Xauto ; then
case $host_os in
@@ -2919,7 +2838,7 @@ if test X${enable_fp_exceptions} != Xyes ; then
FPE=unreliable
else
- AC_MSG_CHECKING([for unreliable floating point execptions])
+ AC_MSG_CHECKING([for unreliable floating point exceptions])
AC_TRY_RUN([
@@ -3507,6 +3426,13 @@ if test X${enable_hipe} = Xyes; then
AC_DEFINE(HIPE,[1],[Define to enable HiPE])
HIPE_HELPERS="xmerl syntax_tools edoc"
ENABLE_ALLOC_TYPE_VARS="$ENABLE_ALLOC_TYPE_VARS hipe"
+ case "$ARCH" in
+ amd64)
+ # For now exec_alloc is only used for hipe on amd64
+ AC_MSG_NOTICE([Enable exec_alloc for hipe code allocation])
+ ENABLE_ALLOC_TYPE_VARS="$ENABLE_ALLOC_TYPE_VARS exec_alloc"
+ ;;
+ esac
fi
fi
AC_SUBST(HIPE_HELPERS)
@@ -3650,7 +3576,7 @@ fi
#
if test $have_kernel_poll = epoll; then
AC_MSG_CHECKING([whether epoll is level triggered])
- AC_TRY_COMPILE([#include <sys/epoll.h>],[
+ AC_TRY_LINK([#include <sys/epoll.h>],[
#ifdef EPOLLET
/* Edge triggered option exist, assume level triggered
is default */
@@ -3773,7 +3699,7 @@ dnl crypto
#
#--------------------------------------------------------------------
-DED_SYS_INCLUDE="-I${ERL_TOP}/erts/emulator/beam -I${ERL_TOP}/erts/include -I${ERL_TOP}/erts/include/$host -I${ERL_TOP}/erts/include/internal -I${ERL_TOP}/erts/include/internal/$host -I${ERL_TOP}/erts/emulator/sys/$ERLANG_OSTYPE"
+DED_SYS_INCLUDE="-I${ERL_TOP}/erts/emulator/beam -I${ERL_TOP}/erts/include -I${ERL_TOP}/erts/include/$host -I${ERL_TOP}/erts/include/internal -I${ERL_TOP}/erts/include/internal/$host -I${ERL_TOP}/erts/emulator/sys/$ERLANG_OSTYPE -I${ERL_TOP}/erts/emulator/sys/common"
if test "X$ETHR_DEFS" = "X"; then
DED_THR_DEFS="-D_THREAD_SAFE -D_REENTRANT"
@@ -3832,14 +3758,8 @@ case $host_os in
DED_LDFLAGS="-m64 $DED_LDFLAGS"
;;
*)
- if test X${enable_darwin_universal} != Xyes; then
- DED_LDFLAGS="-m32 $DED_LDFLAGS"
- fi
;;
esac
- if test X${enable_darwin_universal} = Xyes; then
- DED_LDFLAGS="-arch ppc -arch i386 $DED_LDFLAGS"
- fi
DED_LD="$CC"
DED_LD_FLAG_RUNTIME_LIBRARY_PATH="$CFLAG_RUNTIME_LIBRARY_PATH"
;;
@@ -3924,14 +3844,20 @@ dnl
LM_FIND_EMU_CC
dnl
-dnl DTrace
+dnl DTrace & LTTNG
dnl
case $DYNAMIC_TRACE_FRAMEWORK in
dtrace|systemtap)
AC_CHECK_TOOL(DTRACE, dtrace, none)
test "$DTRACE" = "none" && AC_MSG_ERROR([No dtrace utility found.]);
+ enable_lttng_test=no
enable_dtrace_test=yes;;
- *) enable_dtrace_test=no;;
+ lttng)
+ enable_lttng_test=yes
+ enable_dtrace_test=no;;
+ *)
+ enable_lttng_test=no
+ enable_dtrace_test=no;;
esac
AC_SUBST(DTRACE)
@@ -3972,7 +3898,7 @@ if test "$enable_dtrace_test" = "yes" ; then
[$RM -f $DTRACE_2STEP_TEST
dtrace -G $DTRACE_CPP $DTRACE_BITS_FLAG -Iemulator/beam -o $DTRACE_2STEP_TEST -s emulator/beam/erlang_dtrace.d conftest.$OBJEXT 2>&AS_MESSAGE_LOG_FD
if test -f $DTRACE_2STEP_TEST; then
- rm $DTRACE_2STEP_TEST
+ $RM $DTRACE_2STEP_TEST
DTRACE_ENABLED_2STEP=yes
fi],
[])
@@ -3998,6 +3924,37 @@ if test "$enable_dtrace_test" = "yes" ; then
fi
fi
+if test "$enable_lttng_test" = "yes" ; then
+ AC_CHECK_HEADERS(lttng/tracepoint.h)
+ AC_CHECK_HEADERS(lttng/tracepoint-event.h)
+ dnl The macro tracepoint_enabled is not present in older lttng versions
+ dnl checking for tracepoint_enabled
+ AC_MSG_CHECKING([for tracepoint_enabled in lttng/tracepoint.h])
+ AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM(
+ [#include <lttng/tracepoint.h>
+ #define TRACEPOINT_PROVIDER org_erlang_otp
+ TRACEPOINT_EVENT(
+ org_erlang_otp,
+ dummy,
+ TP_ARGS(int, my_int),
+ TP_FIELDS(ctf_integer(int, my_int, my_int)))
+ #define TRACEPOINT_CREATE_PROBES
+ #define TRACEPOINT_DEFINE],
+ [if(tracepoint_enabled(org_erlang_otp,dummy)) do {} while(0)])],
+ [AC_MSG_RESULT([yes])],
+ [AC_MSG_ERROR([no (available in lttng-ust v2.7)])])
+ if test "x$ac_cv_header_lttng_tracepoint_h" = "xyes" \
+ -a "x$ac_cv_header_lttng_tracepoint_event_h" = "xyes"; then
+ # No straight forward way to test for liblttng-ust when no public symbol exists,
+ # just add the lib.
+ LIBS="$LIBS -llttng-ust -ldl"
+ else
+ AC_MSG_ERROR([No LTTng support found.])
+ fi
+fi
+
+
dnl
dnl SSL, SSH and CRYPTO need the OpenSSL libraries
dnl
@@ -4148,7 +4105,7 @@ ssl_done=yes # Default only one run
# Remove all SKIP files from previous runs
for a in ssl crypto ssh; do
- /bin/rm -f $ERL_TOP/lib/$a/SKIP
+ $RM -f $ERL_TOP/lib/$a/SKIP
done
SSL_DYNAMIC_ONLY=$enable_dynamic_ssl
@@ -4745,7 +4702,7 @@ need_java="jinterface ic/java_src"
# Remove all SKIP files from previous runs
for a in $need_java ; do
- /bin/rm -f $ERL_TOP/lib/$a/SKIP
+ $RM -f $ERL_TOP/lib/$a/SKIP
done
if test "X$with_javac" = "Xno"; then
@@ -4796,7 +4753,7 @@ dnl this deliberately does not believe that 'gcc' is a C++ compiler
AC_CHECK_TOOLS(CXX, [$CCC c++ g++ CC cxx cc++ cl], false)
# Remove SKIP file from previous run
-/bin/rm -f $ERL_TOP/lib/orber/SKIP
+$RM -f $ERL_TOP/lib/orber/SKIP
if test "$CXX" = false; then
echo "No C++ compiler found" > $ERL_TOP/lib/orber/SKIP
@@ -4934,8 +4891,7 @@ dnl Output the result.
dnl ----------------------------------------------------------------------
dnl Note that the output files are relative to $srcdir
-
-AC_CONFIG_FILES(
+AC_CONFIG_FILES([
emulator/$host/Makefile:emulator/Makefile.in
epmd/src/$host/Makefile:epmd/src/Makefile.in
etc/common/$host/Makefile:etc/common/Makefile.in
@@ -4945,18 +4901,22 @@ AC_CONFIG_FILES(
Makefile:Makefile.in
../make/$host/otp.mk:../make/otp.mk.in
../make/$host/otp_ded.mk:../make/otp_ded.mk.in
- ../make/$host/ose_lm.mk:../make/ose_lm.mk.in)
+])
+
+AC_CONFIG_FILES([../make/make_emakefile:../make/make_emakefile.in],
+ [chmod +x ../make/make_emakefile])
+
dnl
dnl The ones below should be moved to their respective lib
dnl
-AC_CONFIG_FILES(
+AC_CONFIG_FILES([
../lib/ic/c_src/$host/Makefile:../lib/ic/c_src/Makefile.in
../lib/os_mon/c_src/$host/Makefile:../lib/os_mon/c_src/Makefile.in
../lib/crypto/c_src/$host/Makefile:../lib/crypto/c_src/Makefile.in
../lib/orber/c_src/$host/Makefile:../lib/orber/c_src/Makefile.in
../lib/runtime_tools/c_src/$host/Makefile:../lib/runtime_tools/c_src/Makefile.in
../lib/tools/c_src/$host/Makefile:../lib/tools/c_src/Makefile.in
- )
+ ])
AC_CONFIG_FILES([../make/install_dir_data.sh:../make/install_dir_data.sh.in], [chmod +x ../make/install_dir_data.sh])
diff --git a/erts/doc/Makefile b/erts/doc/Makefile
index d415e544f3..f26a43592e 100644
--- a/erts/doc/Makefile
+++ b/erts/doc/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/doc/src/Makefile b/erts/doc/src/Makefile
index 83f4c58560..b96cbbce40 100644
--- a/erts/doc/src/Makefile
+++ b/erts/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2012. All Rights Reserved.
+# Copyright Ericsson AB 1997-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -50,12 +50,14 @@ XML_REF1_FILES = epmd.xml \
XML_REF3_EFILES = \
erl_prim_loader.xml \
erlang.xml \
+ erl_tracer.xml \
init.xml \
zlib.xml
XML_REF3_FILES = \
driver_entry.xml \
erl_nif.xml \
+ erl_tracer.xml \
erl_driver.xml \
erl_prim_loader.xml \
erlang.xml \
@@ -154,18 +156,9 @@ clean:
rm -f $(SPECDIR)/*
rm -f errs core *~
-$(SPECDIR)/specs_driver_entry.xml:
+$(SPECDIR)/specs_%.xml:
escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \
- -o$(dir $@) -module driver_entry
-$(SPECDIR)/specs_erl_nif.xml:
- escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \
- -o$(dir $@) -module erl_nif
-$(SPECDIR)/specs_erl_driver.xml:
- escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \
- -o$(dir $@) -module erl_driver
-$(SPECDIR)/specs_erts_alloc.xml:
- escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \
- -o$(dir $@) -module erts_alloc
+ -o$(dir $@) -module $(patsubst $(SPECDIR)/specs_%.xml,%,$@)
# ----------------------------------------------------
# Release Target
diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml
index 186c9a1143..ab00d47425 100644
--- a/erts/doc/src/absform.xml
+++ b/erts/doc/src/absform.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2001</year><year>2015</year>
+ <year>2001</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -26,411 +26,623 @@
<prepared>Arndt Jonasson</prepared>
<responsible>Kenneth Lundin</responsible>
<docno>1</docno>
- <approved>Jultomten</approved>
+ <approved></approved>
<checked></checked>
- <date>00-12-01</date>
+ <date>2000-12-01</date>
<rev>A</rev>
<file>absform.xml</file>
</header>
- <p></p>
- <p>This document describes the standard representation of parse trees for Erlang
- programs as Erlang terms. This representation is known as the <em>abstract format</em>.
- Functions dealing with such parse trees are <c>compile:forms/[1,2]</c>
- and functions in the modules
- <c>epp</c>,
- <c>erl_eval</c>,
- <c>erl_lint</c>,
- <c>erl_pp</c>,
- <c>erl_parse</c>,
- and
- <c>io</c>.
- They are also used as input and output for parse transforms (see the module
- <c>compile</c>).</p>
+ <p>This section describes the standard representation of parse trees for Erlang
+ programs as Erlang terms. This representation is known as the <em>abstract
+ format</em>. Functions dealing with such parse trees are
+ <seealso marker="compiler:compile#forms/1">
+ <c>compile:forms/1,2</c></seealso> and functions in the following
+ modules:</p>
+
+ <list type="bulleted">
+ <item><seealso marker="stdlib:epp">
+ <c>epp(3)</c></seealso></item>
+ <item><seealso marker="stdlib:erl_eval">
+ <c>erl_eval(3)</c></seealso></item>
+ <item><seealso marker="stdlib:erl_lint">
+ <c>erl_lint(3)</c></seealso></item>
+ <item><seealso marker="stdlib:erl_parse">
+ <c>erl_parse(3)</c></seealso></item>
+ <item><seealso marker="stdlib:erl_pp">
+ <c>erl_pp(3)</c></seealso></item>
+ <item><seealso marker="stdlib:io">
+ <c>io(3)</c></seealso></item>
+ </list>
+
+ <p>The functions are also used as input and output for parse transforms, see
+ the <seealso marker="compiler:compile"><c>compile(3)</c></seealso>
+ module.</p>
+
<p>We use the function <c>Rep</c> to denote the mapping from an Erlang source
construct <c>C</c> to its abstract format representation <c>R</c>, and write
- <c>R = Rep(C)</c>.
- </p>
- <p>The word <c>LINE</c> below represents an integer, and denotes the
+ <c>R = Rep(C)</c>.</p>
+
+ <p>The word <c>LINE</c> in this section represents an integer, and denotes the
number of the line in the source file where the construction occurred.
- Several instances of <c>LINE</c> in the same construction may denote
+ Several instances of <c>LINE</c> in the same construction can denote
different lines.</p>
- <p>Since operators are not terms in their own right, when operators are
- mentioned below, the representation of an operator should be taken to
+
+ <p>As operators are not terms in their own right, when operators are
+ mentioned below, the representation of an operator is to be taken to
be the atom with a printname consisting of the same characters as the
- operator.
- </p>
+ operator.</p>
<section>
<title>Module Declarations and Forms</title>
- <p>A module declaration consists of a sequence of forms that are either
+ <p>A module declaration consists of a sequence of forms, which are either
function declarations or attributes.</p>
+
<list type="bulleted">
- <item>If D is a module declaration consisting of the forms
- <c>F_1</c>, ..., <c>F_k</c>, then
- Rep(D) = <c>[Rep(F_1), ..., Rep(F_k)]</c>.</item>
- <item>If F is an attribute <c>-module(Mod)</c>, then
- Rep(F) = <c>{attribute,LINE,module,Mod}</c>.</item>
- <item>If F is an attribute <c>-behavior(Behavior)</c>, then
- Rep(F) = <c>{attribute,LINE,behavior,Behavior}</c>.</item>
- <item>If F is an attribute <c>-behaviour(Behaviour)</c>, then
- Rep(F) = <c>{attribute,LINE,behaviour,Behaviour}</c>.</item>
- <item>If F is an attribute <c>-export([Fun_1/A_1, ..., Fun_k/A_k])</c>, then
- Rep(F) = <c>{attribute,LINE,export,[{Fun_1,A_1}, ..., {Fun_k,A_k}]}</c>.</item>
- <item>If F is an attribute <c>-import(Mod,[Fun_1/A_1, ..., Fun_k/A_k])</c>, then
- Rep(F) = <c>{attribute,LINE,import,{Mod,[{Fun_1,A_1}, ..., {Fun_k,A_k}]}}</c>.</item>
- <item>If F is an attribute <c>-export_type([Type_1/A_1, ..., Type_k/A_k])</c>, then
- Rep(F) = <c>{attribute,LINE,export_type,[{Type_1,A_1}, ..., {Type_k,A_k}]}</c>.</item>
- <item>If F is an attribute <c>-compile(Options)</c>, then
- Rep(F) = <c>{attribute,LINE,compile,Options}</c>.</item>
- <item>If F is an attribute <c>-file(File,Line)</c>, then
- Rep(F) = <c>{attribute,LINE,file,{File,Line}}</c>.</item>
- <item>If F is a record declaration
- <c>-record(Name,{V_1, ..., V_k})</c>, then Rep(F) =
- <c>{attribute,LINE,record,{Name,[Rep(V_1), ..., Rep(V_k)]}}</c>.
- For Rep(V), see below.</item>
- <item>If F is a type declaration
- <c>-Type Name(V_1, ..., V_k) :: T</c>, where
- <c>Type</c> is either the atom <c>type</c> or the atom <c>opaque</c>,
- each <c>V_i</c> is a variable, and <c>T</c> is a type, then Rep(F) =
- <c>{attribute,LINE,Type,{Name,Rep(T),[Rep(V_1), ..., Rep(V_k)]}}</c>.
- </item>
- <item>If F is a function specification
- <c>-Spec Name Ft_1; ...; Ft_k</c>,
- where <c>Spec</c> is either the atom <c>spec</c> or the atom
- <c>callback</c>, and each <c>Ft_i</c> is a possibly constrained
- function type with an argument sequence of the same length
- <c>Arity</c>, then Rep(F) =
- <c>{attribute,Line,Spec,{{Name,Arity},[Rep(Ft_1), ..., Rep(Ft_k)]}}</c>.
- </item>
- <item>If F is a function specification
- <c>-spec Mod:Name Ft_1; ...; Ft_k</c>,
- where each <c>Ft_i</c> is a possibly constrained
- function type with an argument sequence of the same length
- <c>Arity</c>, then Rep(F) =
- <c>{attribute,Line,spec,{{Mod,Name,Arity},[Rep(Ft_1), ..., Rep(Ft_k)]}}</c>.
- </item>
- <item>If F is a wild attribute <c>-A(T)</c>, then
- Rep(F) = <c>{attribute,LINE,A,T}</c>.
- <br></br></item>
- <item>If F is a function declaration
- <c>Name Fc_1 ; ... ; Name Fc_k</c>,
- where each <c>Fc_i</c> is a function clause with a
- pattern sequence of the same length <c>Arity</c>, then
- Rep(F) = <c>{function,LINE,Name,Arity,[Rep(Fc_1), ...,Rep(Fc_k)]}</c>.
+ <item>
+ <p>If D is a module declaration consisting of the forms
+ <c>F_1</c>, ..., <c>F_k</c>, then
+ Rep(D) = <c>[Rep(F_1), ..., Rep(F_k)]</c>.</p>
+ </item>
+ <item>
+ <p>If F is an attribute <c>-export([Fun_1/A_1, ..., Fun_k/A_k])</c>,
+ then Rep(F) =
+ <c>{attribute,LINE,export,[{Fun_1,A_1}, ..., {Fun_k,A_k}]}</c>.</p>
+ </item>
+ <item>
+ <p>If F is an attribute <c>-import(Mod,[Fun_1/A_1, ..., Fun_k/A_k])</c>,
+ then Rep(F) =
+ <c>{attribute,LINE,import,{Mod,[{Fun_1,A_1}, ...,
+ {Fun_k,A_k}]}}</c>.</p>
+ </item>
+ <item>
+ <p>If F is an attribute <c>-module(Mod)</c>, then
+ Rep(F) = <c>{attribute,LINE,module,Mod}</c>.</p>
+ </item>
+ <item>
+ <p>If F is an attribute <c>-file(File,Line)</c>, then
+ Rep(F) = <c>{attribute,LINE,file,{File,Line}}</c>.</p>
+ </item>
+ <item>
+ <p>If F is a function declaration <c>Name Fc_1 ; ... ; Name Fc_k</c>,
+ where each <c>Fc_i</c> is a function clause with a pattern sequence of
+ the same length <c>Arity</c>, then Rep(F) =
+ <c>{function,LINE,Name,Arity,[Rep(Fc_1), ...,Rep(Fc_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If F is a function specification <c>-Spec Name Ft_1; ...; Ft_k</c>,
+ where <c>Spec</c> is either the atom <c>spec</c> or the atom
+ <c>callback</c>, and each <c>Ft_i</c> is a possibly constrained
+ function type with an argument sequence of the same length
+ <c>Arity</c>, then Rep(F) =
+ <c>{attribute,Line,Spec,{{Name,Arity},[Rep(Ft_1), ...,
+ Rep(Ft_k)]}}</c>.</p>
+ </item>
+ <item>
+ <p>If F is a function specification
+ <c>-spec Mod:Name Ft_1; ...; Ft_k</c>, where each <c>Ft_i</c> is a
+ possibly constrained function type with an argument sequence of the
+ same length <c>Arity</c>, then Rep(F) =
+ <c>{attribute,Line,spec,{{Mod,Name,Arity},[Rep(Ft_1), ...,
+ Rep(Ft_k)]}}</c>.</p>
+ </item>
+ <item>
+ <p>If F is a record declaration <c>-record(Name,{V_1, ..., V_k})</c>,
+ where each <c>V_i</c> is a record field, then Rep(F) =
+ <c>{attribute,LINE,record,{Name,[Rep(V_1), ..., Rep(V_k)]}}</c>.
+ For Rep(V), see below.</p>
+ </item>
+ <item>
+ <p>If F is a type declaration <c>-Type Name(V_1, ..., V_k) :: T</c>,
+ where <c>Type</c> is either the atom <c>type</c> or the atom
+ <c>opaque</c>, each <c>V_i</c> is a variable, and <c>T</c> is a type,
+ then Rep(F) =
+ <c>{attribute,LINE,Type,{Name,Rep(T),[Rep(V_1), ...,
+ Rep(V_k)]}}</c>.</p>
+ </item>
+ <item>
+ <p>If F is a wild attribute <c>-A(T)</c>, then
+ Rep(F) = <c>{attribute,LINE,A,T}</c>.</p>
</item>
</list>
<section>
<title>Record Fields</title>
- <p>Each field in a record declaration may have an optional
- explicit default initializer expression, as well as an
+ <p>Each field in a record declaration can have an optional,
+ explicit, default initializer expression, and an
optional type.</p>
+
<list type="bulleted">
- <item>If V is <c>A</c>, then
- Rep(V) = <c>{record_field,LINE,Rep(A)}</c>.</item>
- <item>If V is <c>A = E</c>,
- where <c>E</c> is an expression, then
- Rep(V) = <c>{record_field,LINE,Rep(A),Rep(E)}</c>.</item>
- <item>If V is <c>A :: T</c>, where <c>T</c> is a
- type and it does not contain
- <c>undefined</c> syntactically, then Rep(V) =
- <c>{typed_record_field,{record_field,LINE,Rep(A)},Rep(undefined | T)}</c>.
+ <item>
+ <p>If V is <c>A</c>, then
+ Rep(V) = <c>{record_field,LINE,Rep(A)}</c>.</p>
+ </item>
+ <item>
+ <p>If V is <c>A = E</c>, where <c>E</c> is an expression, then
+ Rep(V) = <c>{record_field,LINE,Rep(A),Rep(E)}</c>.</p>
+ </item>
+ <item>
+ <p>If V is <c>A :: T</c>, where <c>T</c> is a type, then Rep(V) =
+ <c>{typed_record_field,{record_field,LINE,Rep(A)},Rep(T)}</c>.</p>
</item>
- <item>If V is <c>A :: T</c>, where <c>T</c> is a type, then Rep(V) =
- <c>{typed_record_field,{record_field,LINE,Rep(A)},Rep(T)}</c>.
- </item>
- <item>If V is <c>A = E :: T</c>, where
- <c>E</c> is an expression and <c>T</c> is a type, then Rep(V) =
- <c>{typed_record_field,{record_field,LINE,Rep(A),Rep(E)},Rep(T)}</c>.
+ <item>
+ <p>If V is <c>A = E :: T</c>, where
+ <c>E</c> is an expression and <c>T</c> is a type, then Rep(V) =
+ <c>{typed_record_field,{record_field,LINE,Rep(A),Rep(E)},Rep(T)}</c>.
+ </p>
</item>
</list>
</section>
<section>
- <title>Representation of Parse Errors and End-of-file</title>
+ <title>Representation of Parse Errors and End-of-File</title>
<p>In addition to the representations of forms, the list that represents
- a module declaration (as returned by functions in <c>erl_parse</c> and
- <c>epp</c>) may contain tuples <c>{error,E}</c> and
- <c>{warning,W}</c>, denoting syntactically incorrect forms and
- warnings, and <c>{eof,LINE}</c>, denoting an end-of-stream
- encountered before a complete form had been parsed.</p>
+ a module declaration (as returned by functions in
+ <seealso marker="stdlib:epp"><c>epp(3)</c></seealso> and
+ <seealso marker="stdlib:erl_parse"><c>erl_parse(3)</c></seealso>)
+ can contain the following:</p>
+
+ <list type="bulleted">
+ <item>Tuples <c>{error,E}</c> and <c>{warning,W}</c>, denoting
+ syntactically incorrect forms and warnings</item>
+ <item><c>{eof,LINE}</c>, denoting an end-of-stream
+ encountered before a complete form had been parsed</item>
+ </list>
</section>
</section>
<section>
<title>Atomic Literals</title>
<p>There are five kinds of atomic literals, which are represented in the
- same way in patterns, expressions and guards:</p>
+ same way in patterns, expressions, and guards:</p>
+
<list type="bulleted">
- <item>If L is an integer or character literal, then
- Rep(L) = <c>{integer,LINE,L}</c>.</item>
- <item>If L is a float literal, then
- Rep(L) = <c>{float,LINE,L}</c>.</item>
- <item>If L is a string literal consisting of the characters
- <c>C_1</c>, ..., <c>C_k</c>, then
- Rep(L) = <c>{string,LINE,[C_1, ..., C_k]}</c>.</item>
- <item>If L is an atom literal, then
- Rep(L) = <c>{atom,LINE,L}</c>.</item>
+ <item>
+ <p>If L is an atom literal, then Rep(L) = <c>{atom,LINE,L}</c>.</p>
+ </item>
+ <item>
+ <p>If L is a character literal, then Rep(L) = <c>{char,LINE,L}</c>.</p>
+ </item>
+ <item>
+ <p>If L is a float literal, then Rep(L) = <c>{float,LINE,L}</c>.</p>
+ </item>
+ <item>
+ <p>If L is an integer literal, then
+ Rep(L) = <c>{integer,LINE,L}</c>.</p>
+ </item>
+ <item>
+ <p>If L is a string literal consisting of the characters
+ <c>C_1</c>, ..., <c>C_k</c>, then
+ Rep(L) = <c>{string,LINE,[C_1, ..., C_k]}</c>.</p>
+ </item>
</list>
- <p>Note that negative integer and float literals do not occur as such; they are
- parsed as an application of the unary negation operator.</p>
+
+ <p>Notice that negative integer and float literals do not occur as such;
+ they are parsed as an application of the unary negation operator.</p>
</section>
<section>
<title>Patterns</title>
- <p>If <c>Ps</c> is a sequence of patterns <c>P_1, ..., P_k</c>, then
+ <p>If Ps is a sequence of patterns <c>P_1, ..., P_k</c>, then
Rep(Ps) = <c>[Rep(P_1), ..., Rep(P_k)]</c>. Such sequences occur as the
list of arguments to a function or fun.</p>
+
<p>Individual patterns are represented as follows:</p>
+
<list type="bulleted">
- <item>If P is an atomic literal L, then Rep(P) = Rep(L).</item>
- <item>If P is a compound pattern <c>P_1 = P_2</c>, then
- Rep(P) = <c>{match,LINE,Rep(P_1),Rep(P_2)}</c>.</item>
- <item>If P is a variable pattern <c>V</c>, then
- Rep(P) = <c>{var,LINE,A}</c>,
- where A is an atom with a printname consisting of the same characters as
- <c>V</c>.</item>
- <item>If P is a universal pattern <c>_</c>, then
- Rep(P) = <c>{var,LINE,'_'}</c>.</item>
- <item>If P is a tuple pattern <c>{P_1, ..., P_k}</c>, then
- Rep(P) = <c>{tuple,LINE,[Rep(P_1), ..., Rep(P_k)]}</c>.</item>
- <item>If P is a nil pattern <c>[]</c>, then
- Rep(P) = <c>{nil,LINE}</c>.</item>
- <item>If P is a cons pattern <c>[P_h | P_t]</c>, then
- Rep(P) = <c>{cons,LINE,Rep(P_h),Rep(P_t)}</c>.</item>
- <item>If E is a binary pattern <c>&lt;&lt;P_1:Size_1/TSL_1, ..., P_k:Size_k/TSL_k>></c>, then
- Rep(E) = <c>{bin,LINE,[{bin_element,LINE,Rep(P_1),Rep(Size_1),Rep(TSL_1)}, ..., {bin_element,LINE,Rep(P_k),Rep(Size_k),Rep(TSL_k)}]}</c>.
- For Rep(TSL), see below.
- An omitted <c>Size</c> is represented by <c>default</c>. An omitted <c>TSL</c>
- (type specifier list) is represented by <c>default</c>.</item>
- <item>If P is <c>P_1 Op P_2</c>, where <c>Op</c> is a binary operator (this
- is either an occurrence of <c>++</c> applied to a literal string or character
- list, or an occurrence of an expression that can be evaluated to a number
- at compile time),
- then Rep(P) = <c>{op,LINE,Op,Rep(P_1),Rep(P_2)}</c>.</item>
- <item>If P is <c>Op P_0</c>, where <c>Op</c> is a unary operator (this is an
- occurrence of an expression that can be evaluated to a number at compile
- time), then Rep(P) = <c>{op,LINE,Op,Rep(P_0)}</c>.</item>
- <item>If P is a record pattern <c>#Name{Field_1=P_1, ..., Field_k=P_k}</c>,
- then Rep(P) =
- <c>{record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(P_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(P_k)}]}</c>.</item>
- <item>If P is <c>#Name.Field</c>, then
- Rep(P) = <c>{record_index,LINE,Name,Rep(Field)}</c>.</item>
- <item>If P is <c>( P_0 )</c>, then
- Rep(P) = <c>Rep(P_0)</c>,
- that is, patterns cannot be distinguished from their bodies.</item>
+ <item>
+ <p>If P is an atomic literal <c>L</c>, then Rep(P) = Rep(L).</p>
+ </item>
+ <item>
+ <p>If P is a bitstring pattern
+ <c>&lt;&lt;P_1:Size_1/TSL_1, ..., P_k:Size_k/TSL_k>></c>, where each
+ <c>Size_i</c> is an expression that can be evaluated to an integer,
+ and each <c>TSL_i</c> is a type specificer list, then Rep(P) =
+ <c>{bin,LINE,[{bin_element,LINE,Rep(P_1),Rep(Size_1),Rep(TSL_1)},
+ ..., {bin_element,LINE,Rep(P_k),Rep(Size_k),Rep(TSL_k)}]}</c>.
+ For Rep(TSL), see below.
+ An omitted <c>Size_i</c> is represented by <c>default</c>.
+ An omitted <c>TSL_i</c> is represented by <c>default</c>.</p>
+ </item>
+ <item>
+ <p>If P is a compound pattern <c>P_1 = P_2</c>, then Rep(P) =
+ <c>{match,LINE,Rep(P_1),Rep(P_2)}</c>.</p>
+ </item>
+ <item>
+ <p>If P is a cons pattern <c>[P_h | P_t]</c>, then Rep(P) =
+ <c>{cons,LINE,Rep(P_h),Rep(P_t)}</c>.</p>
+ </item>
+ <item>
+ <p>If P is a map pattern <c>#{A_1, ..., A_k}</c>, where each
+ <c>A_i</c> is an association <c>P_i_1 := P_i_2</c>, then Rep(P) =
+ <c>{map,LINE,[Rep(A_1), ..., Rep(A_k)]}</c>.
+ For Rep(A), see below.</p>
+ </item>
+ <item>
+ <p>If P is a nil pattern <c>[]</c>, then Rep(P) =
+ <c>{nil,LINE}</c>.</p>
+ </item>
+ <item>
+ <p>If P is an operator pattern <c>P_1 Op P_2</c>, where <c>Op</c> is a
+ binary operator (this is either an occurrence of <c>++</c> applied to
+ a literal string or character list, or an occurrence of an expression
+ that can be evaluated to a number at compile time), then Rep(P) =
+ <c>{op,LINE,Op,Rep(P_1),Rep(P_2)}</c>.</p>
+ </item>
+ <item>
+ <p>If P is an operator pattern <c>Op P_0</c>, where <c>Op</c> is a
+ unary operator (this is an occurrence of an expression that can be
+ evaluated to a number at compile time), then Rep(P) =
+ <c>{op,LINE,Op,Rep(P_0)}</c>.</p>
+ </item>
+ <item>
+ <p>If P is a parenthesized pattern <c>( P_0 )</c>, then Rep(P) =
+ <c>Rep(P_0)</c>, that is, parenthesized patterns cannot be
+ distinguished from their bodies.</p>
+ </item>
+ <item>
+ <p>If P is a record field index pattern <c>#Name.Field</c>,
+ where <c>Field</c> is an atom, then Rep(P) =
+ <c>{record_index,LINE,Name,Rep(Field)}</c>.</p>
+ </item>
+ <item>
+ <p>If P is a record pattern <c>#Name{Field_1=P_1, ..., Field_k=P_k}</c>,
+ where each <c>Field_i</c> is an atom or <c>_</c>, then Rep(P) =
+ <c>{record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(P_1)}, ...,
+ {record_field,LINE,Rep(Field_k),Rep(P_k)}]}</c>.</p>
+ </item>
+ <item>
+ <p>If P is a tuple pattern <c>{P_1, ..., P_k}</c>, then Rep(P) =
+ <c>{tuple,LINE,[Rep(P_1), ..., Rep(P_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If P is a universal pattern <c>_</c>, then Rep(P) =
+ <c>{var,LINE,'_'}</c>.</p></item>
+ <item>
+ <p>If P is a variable pattern <c>V</c>, then Rep(P) =
+ <c>{var,LINE,A}</c>, where A is an atom with a printname consisting
+ of the same characters as <c>V</c>.</p>
+ </item>
</list>
- <p>Note that every pattern has the same source form as some expression, and is
- represented the same way as the corresponding expression.</p>
+
+ <p>Notice that every pattern has the same source form as some expression,
+ and is represented in the same way as the corresponding expression.</p>
</section>
<section>
<title>Expressions</title>
- <p>A body B is a sequence of expressions <c>E_1, ..., E_k</c>, and
- Rep(B) = <c>[Rep(E_1), ..., Rep(E_k)]</c>.</p>
- <p>An expression E is one of the following alternatives:</p>
+ <p>A body B is a non-empty sequence of expressions <c>E_1, ..., E_k</c>,
+ and Rep(B) = <c>[Rep(E_1), ..., Rep(E_k)]</c>.</p>
+
+ <p>An expression E is one of the following:</p>
+
<list type="bulleted">
- <item>If P is an atomic literal <c>L</c>, then Rep(P) = Rep(L).</item>
- <item>If E is <c>P = E_0</c>, then
- Rep(E) = <c>{match,LINE,Rep(P),Rep(E_0)}</c>.</item>
- <item>If E is a variable <c>V</c>, then Rep(E) = <c>{var,LINE,A}</c>,
- where <c>A</c> is an atom with a printname consisting of the same
- characters as <c>V</c>.</item>
- <item>If E is a tuple skeleton <c>{E_1, ..., E_k}</c>, then
- Rep(E) = <c>{tuple,LINE,[Rep(E_1), ..., Rep(E_k)]}</c>.</item>
- <item>If E is <c>[]</c>, then
- Rep(E) = <c>{nil,LINE}</c>.</item>
- <item>If E is a cons skeleton <c>[E_h | E_t]</c>, then
- Rep(E) = <c>{cons,LINE,Rep(E_h),Rep(E_t)}</c>.</item>
- <item>If E is a binary constructor <c>&lt;&lt;V_1:Size_1/TSL_1, ..., V_k:Size_k/TSL_k>></c>, then Rep(E) =
- <c>{bin,LINE,[{bin_element,LINE,Rep(V_1),Rep(Size_1),Rep(TSL_1)}, ..., {bin_element,LINE,Rep(V_k),Rep(Size_k),Rep(TSL_k)}]}</c>.
- For Rep(TSL), see below.
- An omitted <c>Size</c> is represented by <c>default</c>. An omitted <c>TSL</c>
- (type specifier list) is represented by <c>default</c>.</item>
- <item>If E is <c>E_1 Op E_2</c>, where <c>Op</c> is a binary operator,
- then Rep(E) = <c>{op,LINE,Op,Rep(E_1),Rep(E_2)}</c>.</item>
- <item>If E is <c>Op E_0</c>, where <c>Op</c> is a unary operator, then
- Rep(E) = <c>{op,LINE,Op,Rep(E_0)}</c>.</item>
- <item>If E is <c>#Name{Field_1=E_1, ..., Field_k=E_k}</c>,
- then Rep(E) =
- <c>{record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(E_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(E_k)}]}</c>.</item>
- <item>If E is <c>E_0#Name{Field_1=E_1, ..., Field_k=E_k}</c>, then
- Rep(E) =
- <c>{record,LINE,Rep(E_0),Name,[{record_field,LINE,Rep(Field_1),Rep(E_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(E_k)}]}</c>.</item>
- <item>If E is <c>#Name.Field</c>, then
- Rep(E) = <c>{record_index,LINE,Name,Rep(Field)}</c>.</item>
- <item>If E is <c>E_0#Name.Field</c>, then
- Rep(E) = <c>{record_field,LINE,Rep(E_0),Name,Rep(Field)}</c>.</item>
- <item>If E is <c>#{W_1, ..., W_k}</c> where each
- <c>W_i</c> is a map assoc or exact field, then Rep(E) =
- <c>{map,LINE,[Rep(W_1), ..., Rep(W_k)]}</c>. For Rep(W), see
- below.</item>
- <item>If E is <c>E_0#{W_1, ..., W_k}</c> where
- <c>W_i</c> is a map assoc or exact field, then Rep(E) =
- <c>{map,LINE,Rep(E_0),[Rep(W_1), ..., Rep(W_k)]}</c>.
- For Rep(W), see below.</item>
- <item>If E is <c>catch E_0</c>, then
- Rep(E) = <c>{'catch',LINE,Rep(E_0)}</c>.</item>
- <item>If E is <c>E_0(E_1, ..., E_k)</c>, then
- Rep(E) = <c>{call,LINE,Rep(E_0),[Rep(E_1), ..., Rep(E_k)]}</c>.</item>
- <item>If E is <c>E_m:E_0(E_1, ..., E_k)</c>, then Rep(E) =
- <c>{call,LINE,{remote,LINE,Rep(E_m),Rep(E_0)},[Rep(E_1), ..., Rep(E_k)]}</c>.
- </item>
- <item>If E is a list comprehension <c>[E_0 || W_1, ..., W_k]</c>,
- where each <c>W_i</c> is a generator or a filter, then Rep(E) =
- <c>{lc,LINE,Rep(E_0),[Rep(W_1), ..., Rep(W_k)]}</c>. For Rep(W), see
- below.</item>
- <item>If E is a binary comprehension
- <c>&lt;&lt;E_0 || W_1, ..., W_k>></c>,
- where each <c>W_i</c> is a generator or a filter, then
- Rep(E) = <c>{bc,LINE,Rep(E_0),[Rep(W_1), ..., Rep(W_k)]}</c>.
- For Rep(W), see below.</item>
- <item>If E is <c>begin B end</c>, where <c>B</c> is a body, then
- Rep(E) = <c>{block,LINE,Rep(B)}</c>.</item>
- <item>If E is <c>if Ic_1 ; ... ; Ic_k end</c>,
- where each <c>Ic_i</c> is an if clause then Rep(E) =
- <c>{'if',LINE,[Rep(Ic_1), ..., Rep(Ic_k)]}</c>.</item>
- <item>If E is <c>case E_0 of Cc_1 ; ... ; Cc_k end</c>,
- where <c>E_0</c> is an expression and each <c>Cc_i</c> is a
- case clause then Rep(E) =
- <c>{'case',LINE,Rep(E_0),[Rep(Cc_1), ..., Rep(Cc_k)]}</c>.</item>
- <item>If E is <c>try B catch Tc_1 ; ... ; Tc_k end</c>,
- where <c>B</c> is a body and each <c>Tc_i</c> is a catch clause then
- Rep(E) =
- <c>{'try',LINE,Rep(B),[],[Rep(Tc_1), ..., Rep(Tc_k)],[]}</c>.</item>
- <item>If E is <c>try B of Cc_1 ; ... ; Cc_k catch Tc_1 ; ... ; Tc_n end</c>,
- where <c>B</c> is a body,
- each <c>Cc_i</c> is a case clause and
- each <c>Tc_j</c> is a catch clause then Rep(E) =
- <c>{'try',LINE,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[Rep(Tc_1), ..., Rep(Tc_n)],[]}</c>.</item>
- <item>If E is <c>try B after A end</c>,
- where <c>B</c> and <c>A</c> are bodies then Rep(E) =
- <c>{'try',LINE,Rep(B),[],[],Rep(A)}</c>.</item>
- <item>If E is <c>try B of Cc_1 ; ... ; Cc_k after A end</c>,
- where <c>B</c> and <c>A</c> are a bodies and
- each <c>Cc_i</c> is a case clause then Rep(E) =
- <c>{'try',LINE,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[],Rep(A)}</c>.</item>
- <item>If E is <c>try B catch Tc_1 ; ... ; Tc_k after A end</c>,
- where <c>B</c> and <c>A</c> are bodies and
- each <c>Tc_i</c> is a catch clause then Rep(E) =
- <c>{'try',LINE,Rep(B),[],[Rep(Tc_1), ..., Rep(Tc_k)],Rep(A)}</c>.</item>
- <item>If E is <c>try B of Cc_1 ; ... ; Cc_k catch Tc_1 ; ... ; Tc_n after A end</c>,
- where <c>B</c> and <c>A</c> are a bodies,
- each <c>Cc_i</c> is a case clause and
- each <c>Tc_j</c> is a catch clause then
- Rep(E) =
- <c>{'try',LINE,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[Rep(Tc_1), ..., Rep(Tc_n)],Rep(A)}</c>.</item>
- <item>If E is <c>receive Cc_1 ; ... ; Cc_k end</c>,
- where each <c>Cc_i</c> is a case clause then Rep(E) =
- <c>{'receive',LINE,[Rep(Cc_1), ..., Rep(Cc_k)]}</c>.</item>
- <item>If E is <c>receive Cc_1 ; ... ; Cc_k after E_0 -> B_t end</c>,
- where each <c>Cc_i</c> is a case clause,
- <c>E_0</c> is an expression and <c>B_t</c> is a body, then Rep(E) =
- <c>{'receive',LINE,[Rep(Cc_1), ..., Rep(Cc_k)],Rep(E_0),Rep(B_t)}</c>.</item>
- <item>If E is <c>fun Name / Arity</c>, then
- Rep(E) = <c>{'fun',LINE,{function,Name,Arity}}</c>.</item>
- <item>If E is <c>fun Module:Name/Arity</c>, then Rep(E) =
- <c>{'fun',LINE,{function,Rep(Module),Rep(Name),Rep(Arity)}}</c>.
- (Before the R15 release: Rep(E) =
- <c>{'fun',LINE,{function,Module,Name,Arity}}</c>.)</item>
- <item>If E is <c>fun Fc_1 ; ... ; Fc_k end</c>
- where each <c>Fc_i</c> is a function clause then Rep(E) =
- <c>{'fun',LINE,{clauses,[Rep(Fc_1), ..., Rep(Fc_k)]}}</c>.</item>
- <item>If E is <c>fun Name Fc_1 ; ... ; Name Fc_k end</c>
- where <c>Name</c> is a variable and each
- <c>Fc_i</c> is a function clause then Rep(E) =
- <c>{named_fun,LINE,Name,[Rep(Fc_1), ..., Rep(Fc_k)]}</c>.
- </item>
- <item>If E is <c>( E_0 )</c>, then
- Rep(E) = <c>Rep(E_0)</c>, that is, parenthesized
- expressions cannot be distinguished from their bodies.</item>
+ <item>
+ <p>If E is an atomic literal <c>L</c>, then Rep(E) = Rep(L).</p>
+ </item>
+ <item>
+ <p>If E is a bitstring comprehension
+ <c>&lt;&lt;E_0 || Q_1, ..., Q_k>></c>,
+ where each <c>Q_i</c> is a qualifier, then Rep(E) =
+ <c>{bc,LINE,Rep(E_0),[Rep(Q_1), ..., Rep(Q_k)]}</c>.
+ For Rep(Q), see below.</p>
+ </item>
+ <item>
+ <p>If E is a bitstring constructor
+ <c>&lt;&lt;E_1:Size_1/TSL_1, ..., E_k:Size_k/TSL_k>></c>,
+ where each <c>Size_i</c> is an expression and each
+ <c>TSL_i</c> is a type specificer list, then Rep(E) =
+ <c>{bin,LINE,[{bin_element,LINE,Rep(E_1),Rep(Size_1),Rep(TSL_1)},
+ ..., {bin_element,LINE,Rep(E_k),Rep(Size_k),Rep(TSL_k)}]}</c>.
+ For Rep(TSL), see below.
+ An omitted <c>Size_i</c> is represented by <c>default</c>.
+ An omitted <c>TSL_i</c> is represented by <c>default</c>.</p>
+ </item>
+ <item>
+ <p>If E is a block expression <c>begin B end</c>,
+ where <c>B</c> is a body, then Rep(E) =
+ <c>{block,LINE,Rep(B)}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a case expression <c>case E_0 of Cc_1 ; ... ; Cc_k end</c>,
+ where <c>E_0</c> is an expression and each <c>Cc_i</c> is a
+ case clause, then Rep(E) =
+ <c>{'case',LINE,Rep(E_0),[Rep(Cc_1), ..., Rep(Cc_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a catch expression <c>catch E_0</c>, then Rep(E) =
+ <c>{'catch',LINE,Rep(E_0)}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a cons skeleton <c>[E_h | E_t]</c>, then Rep(E) =
+ <c>{cons,LINE,Rep(E_h),Rep(E_t)}</c>.</p>
+ </item>
+ <item>
+ <p>>If E is a fun expression <c>fun Name/Arity</c>, then Rep(E) =
+ <c>{'fun',LINE,{function,Name,Arity}}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a fun expression <c>fun Module:Name/Arity</c>, then Rep(E) =
+ <c>{'fun',LINE,{function,Rep(Module),Rep(Name),Rep(Arity)}}</c>.
+ (Before Erlang/OTP R15: Rep(E) =
+ <c>{'fun',LINE,{function,Module,Name,Arity}}</c>.)</p>
+ </item>
+ <item>
+ <p>If E is a fun expression <c>fun Fc_1 ; ... ; Fc_k end</c>,
+ where each <c>Fc_i</c> is a function clause, then Rep(E) =
+ <c>{'fun',LINE,{clauses,[Rep(Fc_1), ..., Rep(Fc_k)]}}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a fun expression <c>fun Name Fc_1 ; ... ; Name Fc_k end</c>,
+ where <c>Name</c> is a variable and each
+ <c>Fc_i</c> is a function clause, then Rep(E) =
+ <c>{named_fun,LINE,Name,[Rep(Fc_1), ..., Rep(Fc_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a function call <c>E_0(E_1, ..., E_k)</c>, then Rep(E) =
+ <c>{call,LINE,Rep(E_0),[Rep(E_1), ..., Rep(E_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a function call <c>E_m:E_0(E_1, ..., E_k)</c>, then Rep(E) =
+ <c>{call,LINE,{remote,LINE,Rep(E_m),Rep(E_0)},[Rep(E_1), ...,
+ Rep(E_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If E is an if expression <c>if Ic_1 ; ... ; Ic_k end</c>,
+ where each <c>Ic_i</c> is an if clause, then Rep(E) =
+ <c>{'if',LINE,[Rep(Ic_1), ..., Rep(Ic_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a list comprehension <c>[E_0 || Q_1, ..., Q_k]</c>,
+ where each <c>Q_i</c> is a qualifier, then Rep(E) =
+ <c>{lc,LINE,Rep(E_0),[Rep(Q_1), ..., Rep(Q_k)]}</c>.
+ For Rep(Q), see below.</p>
+ </item>
+ <item>
+ <p>If E is a map creation <c>#{A_1, ..., A_k}</c>,
+ where each <c>A_i</c> is an association <c>E_i_1 => E_i_2</c>
+ or <c>E_i_1 := E_i_2</c>, then Rep(E) =
+ <c>{map,LINE,[Rep(A_1), ..., Rep(A_k)]}</c>.
+ For Rep(A), see below.</p>
+ </item>
+ <item>
+ <p>If E is a map update <c>E_0#{A_1, ..., A_k}</c>,
+ where each <c>A_i</c> is an association <c>E_i_1 => E_i_2</c>
+ or <c>E_i_1 := E_i_2</c>, then Rep(E) =
+ <c>{map,LINE,Rep(E_0),[Rep(A_1), ..., Rep(A_k)]}</c>.
+ For Rep(A), see below.</p>
+ </item>
+ <item>
+ <p>If E is a match operator expression <c>P = E_0</c>,
+ where <c>P</c> is a pattern, then Rep(E) =
+ <c>{match,LINE,Rep(P),Rep(E_0)}</c>.</p>
+ </item>
+ <item>
+ <p>If E is nil, <c>[]</c>, then Rep(E) = <c>{nil,LINE}</c>.</p>
+ </item>
+ <item>
+ <p>If E is an operator expression <c>E_1 Op E_2</c>,
+ where <c>Op</c> is a binary operator other than match operator
+ <c>=</c>, then Rep(E) =
+ <c>{op,LINE,Op,Rep(E_1),Rep(E_2)}</c>.</p>
+ </item>
+ <item>
+ <p>If E is an operator expression <c>Op E_0</c>,
+ where <c>Op</c> is a unary operator, then Rep(E) =
+ <c>{op,LINE,Op,Rep(E_0)}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a parenthesized expression <c>( E_0 )</c>, then Rep(E) =
+ <c>Rep(E_0)</c>, that is, parenthesized expressions cannot be
+ distinguished from their bodies.</p>
+ </item>
+ <item>
+ <p>If E is a receive expression <c>receive Cc_1 ; ... ; Cc_k end</c>,
+ where each <c>Cc_i</c> is a case clause, then Rep(E) =
+ <c>{'receive',LINE,[Rep(Cc_1), ..., Rep(Cc_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a receive expression
+ <c>receive Cc_1 ; ... ; Cc_k after E_0 -> B_t end</c>,
+ where each <c>Cc_i</c> is a case clause, <c>E_0</c> is an expression,
+ and <c>B_t</c> is a body, then Rep(E) =
+ <c>{'receive',LINE,[Rep(Cc_1), ...,
+ Rep(Cc_k)],Rep(E_0),Rep(B_t)}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a record creation
+ <c>#Name{Field_1=E_1, ..., Field_k=E_k}</c>,
+ where each <c>Field_i</c> is an atom or <c>_</c>, then Rep(E) =
+ <c>{record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(E_1)},
+ ..., {record_field,LINE,Rep(Field_k),Rep(E_k)}]}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a record field access <c>E_0#Name.Field</c>,
+ where <c>Field</c> is an atom, then Rep(E) =
+ <c>{record_field,LINE,Rep(E_0),Name,Rep(Field)}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a record field index <c>#Name.Field</c>,
+ where <c>Field</c> is an atom, then Rep(E) =
+ <c>{record_index,LINE,Name,Rep(Field)}</c>.</p></item>
+ <item>
+ <p>If E is a record update
+ <c>E_0#Name{Field_1=E_1, ..., Field_k=E_k}</c>,
+ where each <c>Field_i</c> is an atom, then Rep(E) =
+ <c>{record,LINE,Rep(E_0),Name,[{record_field,LINE,Rep(Field_1),Rep(E_1)},
+ ..., {record_field,LINE,Rep(Field_k),Rep(E_k)}]}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a tuple skeleton <c>{E_1, ..., E_k}</c>, then Rep(E) =
+ <c>{tuple,LINE,[Rep(E_1), ..., Rep(E_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a try expression <c>try B catch Tc_1 ; ... ; Tc_k end</c>,
+ where <c>B</c> is a body and each <c>Tc_i</c> is a catch clause,
+ then Rep(E) =
+ <c>{'try',LINE,Rep(B),[],[Rep(Tc_1), ..., Rep(Tc_k)],[]}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a try expression
+ <c>try B of Cc_1 ; ... ; Cc_k catch Tc_1 ; ... ; Tc_n end</c>,
+ where <c>B</c> is a body, each <c>Cc_i</c> is a case clause, and
+ each <c>Tc_j</c> is a catch clause, then Rep(E) =
+ <c>{'try',LINE,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[Rep(Tc_1), ...,
+ Rep(Tc_n)],[]}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a try expression <c>try B after A end</c>,
+ where <c>B</c> and <c>A</c> are bodies, then Rep(E) =
+ <c>{'try',LINE,Rep(B),[],[],Rep(A)}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a try expression
+ <c>try B of Cc_1 ; ... ; Cc_k after A end</c>,
+ where <c>B</c> and <c>A</c> are a bodies,
+ and each <c>Cc_i</c> is a case clause, then Rep(E) =
+ <c>{'try',LINE,Rep(B),[Rep(Cc_1), ...,
+ Rep(Cc_k)],[],Rep(A)}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a try expression
+ <c>try B catch Tc_1 ; ... ; Tc_k after A end</c>,
+ where <c>B</c> and <c>A</c> are bodies,
+ and each <c>Tc_i</c> is a catch clause, then Rep(E) =
+ <c>{'try',LINE,Rep(B),[],[Rep(Tc_1), ...,
+ Rep(Tc_k)],Rep(A)}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a try expression
+ <c>try B of Cc_1 ; ... ; Cc_k catch Tc_1 ; ... ; Tc_n after A
+ end</c>, where <c>B</c> and <c>A</c> are a bodies,
+ each <c>Cc_i</c> is a case clause,
+ and each <c>Tc_j</c> is a catch clause, then Rep(E) =
+ <c>{'try',LINE,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[Rep(Tc_1), ...,
+ Rep(Tc_n)],Rep(A)}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a variable <c>V</c>, then Rep(E) = <c>{var,LINE,A}</c>,
+ where <c>A</c> is an atom with a printname consisting of the same
+ characters as <c>V</c>.</p>
+ </item>
</list>
<section>
- <title>Generators and Filters</title>
- <p>When W is a generator or a filter (in the body of a list or
- binary comprehension), then:</p>
+ <title>Qualifiers</title>
+ <p>A qualifier Q is one of the following:</p>
+
<list type="bulleted">
- <item>If W is a generator <c>P &lt;- E</c>, where <c>P</c> is
- a pattern and <c>E</c> is an expression, then
- Rep(W) = <c>{generate,LINE,Rep(P),Rep(E)}</c>.</item>
- <item>If W is a generator <c>P &lt;= E</c>, where <c>P</c> is
- a pattern and <c>E</c> is an expression, then
- Rep(W) = <c>{b_generate,LINE,Rep(P),Rep(E)}</c>.</item>
- <item>If W is a filter <c>E</c>, which is an expression, then
- Rep(W) = <c>Rep(E)</c>.</item>
+ <item>
+ <p>If Q is a filter <c>E</c>, where <c>E</c> is an expression, then
+ Rep(Q) = <c>Rep(E)</c>.</p>
+ </item>
+ <item>
+ <p>If Q is a generator <c>P &lt;- E</c>, where <c>P</c> is
+ a pattern and <c>E</c> is an expression, then Rep(Q) =
+ <c>{generate,LINE,Rep(P),Rep(E)}</c>.</p>
+ </item>
+ <item>
+ <p>If Q is a bitstring generator <c>P &lt;= E</c>, where <c>P</c> is
+ a pattern and <c>E</c> is an expression, then Rep(Q) =
+ <c>{b_generate,LINE,Rep(P),Rep(E)}</c>.</p>
+ </item>
</list>
</section>
<section>
- <title>Binary Element Type Specifiers</title>
- <p>A type specifier list TSL for a binary element is a sequence of type
- specifiers <c>TS_1 - ... - TS_k</c>.
+ <title>Bitstring Element Type Specifiers</title>
+ <p>A type specifier list TSL for a bitstring element is a sequence
+ of type specifiers <c>TS_1 - ... - TS_k</c>, and
Rep(TSL) = <c>[Rep(TS_1), ..., Rep(TS_k)]</c>.</p>
- <p>When TS is a type specifier for a binary element, then:</p>
+
<list type="bulleted">
- <item>If TS is an atom <c>A</c>, then Rep(TS) = <c>A</c>.</item>
- <item>If TS is a couple <c>A:Value</c> where <c>A</c> is an atom
- and <c>Value</c> is an integer, then Rep(TS) =
- <c>{A,Value}</c>.</item>
+ <item>
+ <p>If TS is a type specifier <c>A</c>, where <c>A</c> is an atom,
+ then Rep(TS) = <c>A</c>.</p>
+ </item>
+ <item>
+ <p>If TS is a type specifier <c>A:Value</c>,
+ where <c>A</c> is an atom and <c>Value</c> is an integer,
+ then Rep(TS) = <c>{A,Value}</c>.</p>
+ </item>
</list>
</section>
<section>
- <title>Map Assoc and Exact Fields</title>
- <p>When W is an assoc or exact field (in the body of a map), then:</p>
+ <title>Associations</title>
+ <p>An association A is one of the following:</p>
+
<list type="bulleted">
- <item>If W is an assoc field <c>K => V</c>, where
- <c>K</c> and <c>V</c> are both expressions,
- then Rep(W) = <c>{map_field_assoc,LINE,Rep(K),Rep(V)}</c>.
- </item>
- <item>If W is an exact field <c>K := V</c>, where
- <c>K</c> and <c>V</c> are both expressions,
- then Rep(W) = <c>{map_field_exact,LINE,Rep(K),Rep(V)}</c>.
- </item>
+ <item>
+ <p>If A is an association <c>K => V</c>,
+ then Rep(A) = <c>{map_field_assoc,LINE,Rep(K),Rep(V)}</c>.</p>
+ </item>
+ <item>
+ <p>If A is an association <c>K := V</c>,
+ then Rep(A) = <c>{map_field_exact,LINE,Rep(K),Rep(V)}</c>.</p>
+ </item>
</list>
</section>
</section>
<section>
<title>Clauses</title>
- <p>There are function clauses, if clauses, case clauses
+ <p>There are function clauses, if clauses, case clauses,
and catch clauses.</p>
- <p>A clause <c>C</c> is one of the following alternatives:</p>
+
+ <p>A clause C is one of the following:</p>
+
<list type="bulleted">
- <item>If C is a function clause <c>( Ps ) -> B</c>
- where <c>Ps</c> is a pattern sequence and <c>B</c> is a body, then
- Rep(C) = <c>{clause,LINE,Rep(Ps),[],Rep(B)}</c>.</item>
- <item>If C is a function clause <c>( Ps ) when Gs -> B</c>
- where <c>Ps</c> is a pattern sequence,
- <c>Gs</c> is a guard sequence and <c>B</c> is a body, then
- Rep(C) = <c>{clause,LINE,Rep(Ps),Rep(Gs),Rep(B)}</c>.</item>
- <item>If C is an if clause <c>Gs -> B</c>
- where <c>Gs</c> is a guard sequence and <c>B</c> is a body, then
- Rep(C) = <c>{clause,LINE,[],Rep(Gs),Rep(B)}</c>.</item>
- <item>If C is a case clause <c>P -> B</c>
- where <c>P</c> is a pattern and <c>B</c> is a body, then
- Rep(C) = <c>{clause,LINE,[Rep(P)],[],Rep(B)}</c>.</item>
- <item>If C is a case clause <c>P when Gs -> B</c>
- where <c>P</c> is a pattern,
- <c>Gs</c> is a guard sequence and <c>B</c> is a body, then
- Rep(C) = <c>{clause,LINE,[Rep(P)],Rep(Gs),Rep(B)}</c>.</item>
- <item>If C is a catch clause <c>P -> B</c>
- where <c>P</c> is a pattern and <c>B</c> is a body, then
- Rep(C) = <c>{clause,LINE,[Rep({throw,P,_})],[],Rep(B)}</c>.</item>
- <item>If C is a catch clause <c>X : P -> B</c>
- where <c>X</c> is an atomic literal or a variable pattern,
- <c>P</c> is a pattern and <c>B</c> is a body, then
- Rep(C) = <c>{clause,LINE,[Rep({X,P,_})],[],Rep(B)}</c>.</item>
- <item>If C is a catch clause <c>P when Gs -> B</c>
- where <c>P</c> is a pattern, <c>Gs</c> is a guard sequence
- and <c>B</c> is a body, then
- Rep(C) = <c>{clause,LINE,[Rep({throw,P,_})],Rep(Gs),Rep(B)}</c>.</item>
- <item>If C is a catch clause <c>X : P when Gs -> B</c>
- where <c>X</c> is an atomic literal or a variable pattern,
- <c>P</c> is a pattern, <c>Gs</c> is a guard sequence
- and <c>B</c> is a body, then
- Rep(C) = <c>{clause,LINE,[Rep({X,P,_})],Rep(Gs),Rep(B)}</c>.</item>
+ <item>
+ <p>If C is a case clause <c>P -> B</c>,
+ where <c>P</c> is a pattern and <c>B</c> is a body, then
+ Rep(C) = <c>{clause,LINE,[Rep(P)],[],Rep(B)}</c>.</p>
+ </item>
+ <item>
+ <p>If C is a case clause <c>P when Gs -> B</c>,
+ where <c>P</c> is a pattern,
+ <c>Gs</c> is a guard sequence, and <c>B</c> is a body, then
+ Rep(C) = <c>{clause,LINE,[Rep(P)],Rep(Gs),Rep(B)}</c>.</p>
+ </item>
+ <item>
+ <p>If C is a catch clause <c>P -> B</c>,
+ where <c>P</c> is a pattern and <c>B</c> is a body, then
+ Rep(C) = <c>{clause,LINE,[Rep({throw,P,_})],[],Rep(B)}</c>.</p>
+ </item>
+ <item>
+ <p>If C is a catch clause <c>X : P -> B</c>,
+ where <c>X</c> is an atomic literal or a variable pattern,
+ <c>P</c> is a pattern, and <c>B</c> is a body, then
+ Rep(C) = <c>{clause,LINE,[Rep({X,P,_})],[],Rep(B)}</c>.</p>
+ </item>
+ <item>
+ <p>If C is a catch clause <c>P when Gs -> B</c>,
+ where <c>P</c> is a pattern, <c>Gs</c> is a guard sequence,
+ and <c>B</c> is a body, then
+ Rep(C) = <c>{clause,LINE,[Rep({throw,P,_})],Rep(Gs),Rep(B)}</c>.</p>
+ </item>
+ <item>
+ <p>If C is a catch clause <c>X : P when Gs -> B</c>,
+ where <c>X</c> is an atomic literal or a variable pattern,
+ <c>P</c> is a pattern, <c>Gs</c> is a guard sequence,
+ and <c>B</c> is a body, then
+ Rep(C) = <c>{clause,LINE,[Rep({X,P,_})],Rep(Gs),Rep(B)}</c>.</p>
+ </item>
+ <item>
+ <p>If C is a function clause <c>( Ps ) -> B</c>,
+ where <c>Ps</c> is a pattern sequence and <c>B</c> is a body, then
+ Rep(C) = <c>{clause,LINE,Rep(Ps),[],Rep(B)}</c>.</p>
+ </item>
+ <item>
+ <p>If C is a function clause <c>( Ps ) when Gs -> B</c>,
+ where <c>Ps</c> is a pattern sequence,
+ <c>Gs</c> is a guard sequence and <c>B</c> is a body, then
+ Rep(C) = <c>{clause,LINE,Rep(Ps),Rep(Gs),Rep(B)}</c>.</p>
+ </item>
+ <item>
+ <p>If C is an if clause <c>Gs -> B</c>,
+ where <c>Gs</c> is a guard sequence and <c>B</c> is a body, then
+ Rep(C) = <c>{clause,LINE,[],Rep(Gs),Rep(B)}</c>.</p>
+ </item>
</list>
</section>
@@ -438,177 +650,292 @@
<title>Guards</title>
<p>A guard sequence Gs is a sequence of guards <c>G_1; ...; G_k</c>, and
Rep(Gs) = <c>[Rep(G_1), ..., Rep(G_k)]</c>. If the guard sequence is
- empty, Rep(Gs) = <c>[]</c>.</p>
- <p>A guard G is a nonempty sequence of guard tests
+ empty, then Rep(Gs) = <c>[]</c>.</p>
+
+ <p>A guard G is a non-empty sequence of guard tests
<c>Gt_1, ..., Gt_k</c>, and Rep(G) =
<c>[Rep(Gt_1), ..., Rep(Gt_k)]</c>.</p>
- <p>A guard test <c>Gt</c> is one of the following alternatives:</p>
+
+ <p>A guard test Gt is one of the following:</p>
+
<list type="bulleted">
- <item>If Gt is an atomic literal L, then Rep(Gt) = Rep(L).</item>
- <item>If Gt is a variable pattern <c>V</c>, then
- Rep(Gt) = <c>{var,LINE,A}</c>, where A is an atom with
- a printname consisting of the same characters as <c>V</c>.</item>
- <item>If Gt is a tuple skeleton <c>{Gt_1, ..., Gt_k}</c>, then
- Rep(Gt) = <c>{tuple,LINE,[Rep(Gt_1), ..., Rep(Gt_k)]}</c>.</item>
- <item>If Gt is <c>[]</c>, then Rep(Gt) = <c>{nil,LINE}</c>.</item>
- <item>If Gt is a cons skeleton <c>[Gt_h | Gt_t]</c>, then
- Rep(Gt) = <c>{cons,LINE,Rep(Gt_h),Rep(Gt_t)}</c>.</item>
- <item>If Gt is a binary constructor
- <c>&lt;&lt;Gt_1:Size_1/TSL_1, ..., Gt_k:Size_k/TSL_k>></c>, then
- Rep(Gt) = <c>{bin,LINE,[{bin_element,LINE,Rep(Gt_1),Rep(Size_1),Rep(TSL_1)}, ..., {bin_element,LINE,Rep(Gt_k),Rep(Size_k),Rep(TSL_k)}]}</c>.
- For Rep(TSL), see above.
- An omitted <c>Size</c> is represented by <c>default</c>.
- An omitted <c>TSL</c> (type specifier list) is represented
- by <c>default</c>.</item>
- <item>If Gt is <c>Gt_1 Op Gt_2</c>, where <c>Op</c>
- is a binary operator, then Rep(Gt) =
- <c>{op,LINE,Op,Rep(Gt_1),Rep(Gt_2)}</c>.</item>
- <item>If Gt is <c>Op Gt_0</c>, where <c>Op</c> is a unary operator, then
- Rep(Gt) = <c>{op,LINE,Op,Rep(Gt_0)}</c>.</item>
- <item>If Gt is <c>#Name{Field_1=Gt_1, ..., Field_k=Gt_k}</c>, then
- Rep(E) =
- <c>{record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(Gt_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(Gt_k)}]}</c>.</item>
- <item>If Gt is <c>#Name.Field</c>, then
- Rep(Gt) = <c>{record_index,LINE,Name,Rep(Field)}</c>.</item>
- <item>If Gt is <c>Gt_0#Name.Field</c>, then
- Rep(Gt) = <c>{record_field,LINE,Rep(Gt_0),Name,Rep(Field)}</c>.</item>
- <item>If Gt is <c>A(Gt_1, ..., Gt_k)</c>, where <c>A</c> is an atom, then
- Rep(Gt) = <c>{call,LINE,Rep(A),[Rep(Gt_1), ..., Rep(Gt_k)]}</c>.</item>
- <item>If Gt is <c>A_m:A(Gt_1, ..., Gt_k)</c>, where <c>A_m</c> is
- the atom <c>erlang</c> and <c>A</c> is an atom or an operator, then
- Rep(Gt) = <c>{call,LINE,{remote,LINE,Rep(A_m),Rep(A)},[Rep(Gt_1), ..., Rep(Gt_k)]}</c>.</item>
- <item>If Gt is <c>{A_m,A}(Gt_1, ..., Gt_k)</c>, where <c>A_m</c> is
- the atom <c>erlang</c> and <c>A</c> is an atom or an operator, then
- Rep(Gt) = <c>{call,LINE,Rep({A_m,A}),[Rep(Gt_1), ..., Rep(Gt_k)]}</c>.
- </item>
- <item>If Gt is <c>( Gt_0 )</c>, then
- Rep(Gt) = <c>Rep(Gt_0)</c>, that is, parenthesized
- guard tests cannot be distinguished from their bodies.</item>
+ <item>
+ <p>If Gt is an atomic literal <c>L</c>, then Rep(Gt) = Rep(L).</p>
+ </item>
+ <item>
+ <p>If Gt is a bitstring constructor
+ <c>&lt;&lt;Gt_1:Size_1/TSL_1, ..., Gt_k:Size_k/TSL_k>></c>,
+ where each <c>Size_i</c> is a guard test and each
+ <c>TSL_i</c> is a type specificer list, then Rep(Gt) =
+ <c>{bin,LINE,[{bin_element,LINE,Rep(Gt_1),Rep(Size_1),Rep(TSL_1)},
+ ..., {bin_element,LINE,Rep(Gt_k),Rep(Size_k),Rep(TSL_k)}]}</c>.
+ For Rep(TSL), see above.
+ An omitted <c>Size_i</c> is represented by <c>default</c>.
+ An omitted <c>TSL_i</c> is represented by <c>default</c>.</p>
+ </item>
+ <item>
+ <p>If Gt is a cons skeleton <c>[Gt_h | Gt_t]</c>, then Rep(Gt) =
+ <c>{cons,LINE,Rep(Gt_h),Rep(Gt_t)}</c>.</p>
+ </item>
+ <item>
+ <p>If Gt is a function call <c>A(Gt_1, ..., Gt_k)</c>,
+ where <c>A</c> is an atom, then Rep(Gt) =
+ <c>{call,LINE,Rep(A),[Rep(Gt_1), ..., Rep(Gt_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If Gt is a function call <c>A_m:A(Gt_1, ..., Gt_k)</c>,
+ where <c>A_m</c> is the atom <c>erlang</c> and <c>A</c> is
+ an atom or an operator, then Rep(Gt) =
+ <c>{call,LINE,{remote,LINE,Rep(A_m),Rep(A)},[Rep(Gt_1), ...,
+ Rep(Gt_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If Gt is a map creation <c>#{A_1, ..., A_k}</c>,
+ where each <c>A_i</c> is an association <c>Gt_i_1 => Gt_i_2</c>
+ or <c>Gt_i_1 := Gt_i_2</c>, then Rep(Gt) =
+ <c>{map,LINE,[Rep(A_1), ..., Rep(A_k)]}</c>.
+ For Rep(A), see above.</p>
+ </item>
+ <item>
+ <p>If Gt is a map update <c>Gt_0#{A_1, ..., A_k}</c>,
+ where each <c>A_i</c> is an association <c>Gt_i_1 => Gt_i_2</c>
+ or <c>Gt_i_1 := Gt_i_2</c>, then Rep(Gt) =
+ <c>{map,LINE,Rep(Gt_0),[Rep(A_1), ..., Rep(A_k)]}</c>.
+ For Rep(A), see above.</p>
+ </item>
+ <item>
+ <p>If Gt is nil, <c>[]</c>, then Rep(Gt) = <c>{nil,LINE}</c>.</p>
+ </item>
+ <item>
+ <p>If Gt is an operator guard test <c>Gt_1 Op Gt_2</c>,
+ where <c>Op</c> is a binary operator other than match
+ operator <c>=</c>, then Rep(Gt) =
+ <c>{op,LINE,Op,Rep(Gt_1),Rep(Gt_2)}</c>.</p>
+ </item>
+ <item>
+ <p>If Gt is an operator guard test <c>Op Gt_0</c>,
+ where <c>Op</c> is a unary operator, then Rep(Gt) =
+ <c>{op,LINE,Op,Rep(Gt_0)}</c>.</p>
+ </item>
+ <item>
+ <p>If Gt is a parenthesized guard test <c>( Gt_0 )</c>, then Rep(Gt) =
+ <c>Rep(Gt_0)</c>, that is, parenthesized
+ guard tests cannot be distinguished from their bodies.</p>
+ </item>
+ <item>
+ <p>If Gt is a record creation
+ <c>#Name{Field_1=Gt_1, ..., Field_k=Gt_k}</c>,
+ where each <c>Field_i</c> is an atom or <c>_</c>, then Rep(Gt) =
+ <c>{record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(Gt_1)},
+ ..., {record_field,LINE,Rep(Field_k),Rep(Gt_k)}]}</c>.</p>
+ </item>
+ <item>
+ <p>If Gt is a record field access <c>Gt_0#Name.Field</c>,
+ where <c>Field</c> is an atom, then Rep(Gt) =
+ <c>{record_field,LINE,Rep(Gt_0),Name,Rep(Field)}</c>.</p>
+ </item>
+ <item>
+ <p>If Gt is a record field index <c>#Name.Field</c>,
+ where <c>Field</c> is an atom, then Rep(Gt) =
+ <c>{record_index,LINE,Name,Rep(Field)}</c>.</p>
+ </item>
+ <item>
+ <p>If Gt is a tuple skeleton <c>{Gt_1, ..., Gt_k}</c>, then Rep(Gt) =
+ <c>{tuple,LINE,[Rep(Gt_1), ..., Rep(Gt_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If Gt is a variable pattern <c>V</c>, then Rep(Gt) =
+ <c>{var,LINE,A}</c>, where A is an atom with
+ a printname consisting of the same characters as <c>V</c>.</p>
+ </item>
</list>
- <p>Note that every guard test has the same source form as some expression,
- and is represented the same way as the corresponding expression.</p>
+
+ <p>Notice that every guard test has the same source form as some expression,
+ and is represented in the same way as the corresponding expression.</p>
</section>
<section>
<title>Types</title>
<list type="bulleted">
- <item>If T is an annotated type <c>Anno :: Type</c>,
- where <c>Anno</c> is a variable and
- <c>Type</c> is a type, then Rep(T) =
- <c>{ann_type,LINE,[Rep(Anno),Rep(Type)]}</c>.</item>
- <item>If T is an atom or integer literal L, then Rep(T) = Rep(L).
- </item>
- <item>If T is <c>L Op R</c>,
- where <c>Op</c> is a binary operator and <c>L</c> and <c>R</c>
- are types (this is an occurrence of an expression that can be
- evaluated to an integer at compile time), then
- Rep(T) = <c>{op,LINE,Op,Rep(L),Rep(R)}</c>.</item>
- <item>If T is <c>Op A</c>, where <c>Op</c> is a
- unary operator and <c>A</c> is a type (this is an occurrence of
- an expression that can be evaluated to an integer at compile time),
- then Rep(T) = <c>{op,LINE,Op,Rep(A)}</c>.</item>
- <item>If T is a bitstring type <c>&lt;&lt;_:M,_:_*N>></c>,
- where <c>M</c> and <c>N</c> are singleton integer types, then Rep(T) =
- <c>{type,LINE,binary,[Rep(M),Rep(N)]}</c>.</item>
- <item>If T is the empty list type <c>[]</c>, then Rep(T) =
- <c>{type,Line,nil,[]}</c>.</item>
- <item>If T is a fun type <c>fun()</c>, then Rep(T) =
- <c>{type,LINE,'fun',[]}</c>.</item>
- <item>If T is a fun type <c>fun((...) -> B)</c>,
- where <c>B</c> is a type, then
- Rep(T) = <c>{type,LINE,'fun',[{type,LINE,any},Rep(B)]}</c>.
- </item>
- <item>If T is a fun type <c>fun(Ft)</c>, where
- <c>Ft</c> is a function type,
- then Rep(T) = <c>Rep(Ft)</c>.</item>
- <item>If T is an integer range type <c>L .. H</c>,
- where <c>L</c> and <c>H</c> are singleton integer types, then
- Rep(T) = <c>{type,LINE,range,[Rep(L),Rep(H)]}</c>.</item>
- <item>If T is a map type <c>map()</c>, then Rep(T) =
- <c>{type,LINE,map,any}</c>.</item>
- <item>If T is a map type <c>#{P_1, ..., P_k}</c>, where each
- <c>P_i</c> is a map pair type, then Rep(T) =
- <c>{type,LINE,map,[Rep(P_1), ..., Rep(P_k)]}</c>.</item>
- <item>If T is a map pair type <c>K => V</c>, where
- <c>K</c> and <c>V</c> are types, then Rep(T) =
- <c>{type,LINE,map_field_assoc,[Rep(K),Rep(V)]}</c>.</item>
- <item>If T is a predefined (or built-in) type <c>N(A_1, ..., A_k)</c>,
- where each <c>A_i</c> is a type, then Rep(T) =
- <c>{type,LINE,N,[Rep(A_1), ..., Rep(A_k)]}</c>.</item>
- <item>If T is a record type <c>#Name{F_1, ..., F_k}</c>,
- where each <c>F_i</c> is a record field type, then Rep(T) =
- <c>{type,LINE,record,[Rep(Name),Rep(F_1), ..., Rep(F_k)]}</c>.
- </item>
- <item>If T is a record field type <c>Name :: Type</c>,
- where <c>Type</c> is a type, then Rep(T) =
- <c>{type,LINE,field_type,[Rep(Name),Rep(Type)]}</c>.</item>
- <item>If T is a remote type <c>M:N(A_1, ..., A_k)</c>, where
- each <c>A_i</c> is a type, then Rep(T) =
- <c>{remote_type,LINE,[Rep(M),Rep(N),[Rep(A_1), ..., Rep(A_k)]]}</c>.
- </item>
- <item>If T is a tuple type <c>tuple()</c>, then Rep(T) =
- <c>{type,LINE,tuple,any}</c>.</item>
- <item>If T is a tuple type <c>{A_1, ..., A_k}</c>, where
- each <c>A_i</c> is a type, then Rep(T) =
- <c>{type,LINE,tuple,[Rep(A_1), ..., Rep(A_k)]}</c>.</item>
- <item>If T is a type union <c>T_1 | ... | T_k</c>,
- where each <c>T_i</c> is a type, then Rep(T) =
- <c>{type,LINE,union,[Rep(T_1), ..., Rep(T_k)]}</c>.</item>
- <item>If T is a type variable <c>V</c>, then Rep(T) =
- <c>{var,LINE,A}</c>, where <c>A</c> is an atom with a printname
- consisting of the same characters as <c>V</c>. A type variable
- is any variable except underscore (<c>_</c>).</item>
- <item>If T is a user-defined type <c>N(A_1, ..., A_k)</c>,
- where each <c>A_i</c> is a type, then Rep(T) =
- <c>{user_type,LINE,N,[Rep(A_1), ..., Rep(A_k)]}</c>.</item>
- <item>If T is <c>( T_0 )</c>, then Rep(T) = <c>Rep(T_0)</c>,
- that is, parenthesized types cannot be distinguished from their
- bodies.</item>
+ <item>
+ <p>If T is an annotated type <c>A :: T_0</c>,
+ where <c>A</c> is a variable, then Rep(T) =
+ <c>{ann_type,LINE,[Rep(A),Rep(T_0)]}</c>.</p>
+ </item>
+ <item>
+ <p>If T is an atom or integer literal L, then Rep(T) = Rep(L).</p>
+ </item>
+ <item>
+ <p>If T is a bitstring type <c>&lt;&lt;_:M,_:_*N>></c>,
+ where <c>M</c> and <c>N</c> are singleton integer types, then Rep(T) =
+ <c>{type,LINE,binary,[Rep(M),Rep(N)]}</c>.</p>
+ </item>
+ <item>
+ <p>If T is the empty list type <c>[]</c>, then Rep(T) =
+ <c>{type,Line,nil,[]}</c>.</p>
+ </item>
+ <item>
+ <p>If T is a fun type <c>fun()</c>, then Rep(T) =
+ <c>{type,LINE,'fun',[]}</c>.</p>
+ </item>
+ <item>
+ <p>If T is a fun type <c>fun((...) -> T_0)</c>, then Rep(T) =
+ <c>{type,LINE,'fun',[{type,LINE,any},Rep(T_0)]}</c>.</p>
+ </item>
+ <item>
+ <p>If T is a fun type <c>fun(Ft)</c>, where
+ <c>Ft</c> is a function type, then Rep(T) = <c>Rep(Ft)</c>.
+ For Rep(Ft), see below.</p>
+ </item>
+ <item>
+ <p>If T is an integer range type <c>L .. H</c>,
+ where <c>L</c> and <c>H</c> are singleton integer types, then Rep(T) =
+ <c>{type,LINE,range,[Rep(L),Rep(H)]}</c>.</p>
+ </item>
+ <item>
+ <p>If T is a map type <c>map()</c>, then Rep(T) =
+ <c>{type,LINE,map,any}</c>.</p>
+ </item>
+ <item>
+ <p>If T is a map type <c>#{A_1, ..., A_k}</c>, where each
+ <c>A_i</c> is an association type, then Rep(T) =
+ <c>{type,LINE,map,[Rep(A_1), ..., Rep(A_k)]}</c>.
+ For Rep(A), see below.</p>
+ </item>
+ <item>
+ <p>If T is an operator type <c>T_1 Op T_2</c>,
+ where <c>Op</c> is a binary operator (this is an occurrence of
+ an expression that can be evaluated to an integer at compile
+ time), then Rep(T) =
+ <c>{op,LINE,Op,Rep(T_1),Rep(T_2)}</c>.</p>
+ </item>
+ <item>
+ <p>If T is an operator type <c>Op T_0</c>, where <c>Op</c> is a
+ unary operator (this is an occurrence of an expression that can
+ be evaluated to an integer at compile time), then Rep(T) =
+ <c>{op,LINE,Op,Rep(T_0)}</c>.</p>
+ </item>
+ <item>
+ <p>If T is <c>( T_0 )</c>, then Rep(T) = <c>Rep(T_0)</c>, that is,
+ parenthesized types cannot be distinguished from their bodies.</p>
+ </item>
+ <item>
+ <p>If T is a predefined (or built-in) type <c>N(T_1, ..., T_k)</c>,
+ then Rep(T) = <c>{type,LINE,N,[Rep(T_1), ..., Rep(T_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If T is a record type <c>#Name{F_1, ..., F_k}</c>,
+ where each <c>F_i</c> is a record field type, then Rep(T) =
+ <c>{type,LINE,record,[Rep(Name),Rep(F_1), ..., Rep(F_k)]}</c>.
+ For Rep(F), see below.</p>
+ </item>
+ <item>
+ <p>If T is a remote type <c>M:N(T_1, ..., T_k)</c>, then Rep(T) =
+ <c>{remote_type,LINE,[Rep(M),Rep(N),[Rep(T_1), ...,
+ Rep(T_k)]]}</c>.</p>
+ </item>
+ <item>
+ <p>If T is a tuple type <c>tuple()</c>, then Rep(T) =
+ <c>{type,LINE,tuple,any}</c>.</p>
+ </item>
+ <item>
+ <p>If T is a tuple type <c>{T_1, ..., T_k}</c>, then Rep(T) =
+ <c>{type,LINE,tuple,[Rep(T_1), ..., Rep(T_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If T is a type union <c>T_1 | ... | T_k</c>, then Rep(T) =
+ <c>{type,LINE,union,[Rep(T_1), ..., Rep(T_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If T is a type variable <c>V</c>, then Rep(T) =
+ <c>{var,LINE,A}</c>, where <c>A</c> is an atom with a printname
+ consisting of the same characters as <c>V</c>. A type variable
+ is any variable except underscore (<c>_</c>).</p>
+ </item>
+ <item>
+ <p>If T is a user-defined type <c>N(T_1, ..., T_k)</c>, then Rep(T) =
+ <c>{user_type,LINE,N,[Rep(T_1), ..., Rep(T_k)]}</c>.</p>
+ </item>
</list>
<section>
<title>Function Types</title>
+ <p>A function type Ft is one of the following:</p>
+
<list type="bulleted">
- <item>If Ft is a constrained function type <c>Ft_1 when Fc</c>,
- where <c>Ft_1</c> is a function type and
- <c>Fc</c> is a function constraint, then Rep(T) =
- <c>{type,LINE,bounded_fun,[Rep(Ft_1),Rep(Fc)]}</c>.</item>
- <item>If Ft is a function type <c>(A_1, ..., A_n) -> B</c>,
- where each <c>A_i</c> and <c>B</c> are types, then
- Rep(Ft) = <c>{type,LINE,'fun',[{type,LINE,product,[Rep(A_1),
- ..., Rep(A_n)]},Rep(B)]}</c>.</item>
+ <item>
+ <p>If Ft is a constrained function type <c>Ft_1 when Fc</c>,
+ where <c>Ft_1</c> is a function type and
+ <c>Fc</c> is a function constraint, then Rep(T) =
+ <c>{type,LINE,bounded_fun,[Rep(Ft_1),Rep(Fc)]}</c>.
+ For Rep(Fc), see below.</p>
+ </item>
+ <item>
+ <p>If Ft is a function type <c>(T_1, ..., T_n) -> T_0</c>,
+ where each <c>T_i</c> is a type, then Rep(Ft) =
+ <c>{type,LINE,'fun',[{type,LINE,product,[Rep(T_1), ...,
+ Rep(T_n)]},Rep(T_0)]}</c>.</p>
+ </item>
</list>
</section>
<section>
<title>Function Constraints</title>
- <p>A function constraint Fc is a nonempty sequence of constraints
- <c>C_1, ..., C_k</c>, and
- Rep(Fc) = <c>[Rep(C_1), ..., Rep(C_k)]</c>.</p>
+ <p>A function constraint Fc is a non-empty sequence of constraints
+ <c>C_1, ..., C_k</c>, and
+ Rep(Fc) = <c>[Rep(C_1), ..., Rep(C_k)]</c>.</p>
+
+ <list type="bulleted">
+ <item>If C is a constraint <c>is_subtype(V, T)</c> or <c>V :: T</c>,
+ where <c>V</c> is a type variable
+ and <c>T</c> is a type, then Rep(C) =
+ <c>{type,LINE,constraint,[{atom,LINE,is_subtype},[Rep(V),Rep(T)]]}</c>.
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>Association Types</title>
<list type="bulleted">
- <item>If C is a constraint <c>is_subtype(V, T)</c> or <c>V :: T</c>,
- where <c>V</c> is a type variable and <c>T</c> is a type, then
- Rep(C) = <c>{type,LINE,constraint,[{atom,LINE,is_subtype},[Rep(V),Rep(T)]]}</c>.
- </item>
+ <item>
+ <p>If A is an association type <c>K => V</c>,
+ where <c>K</c> and <c>V</c> are types, then Rep(A) =
+ <c>{type,LINE,map_field_assoc,[Rep(K),Rep(V)]}</c>.</p>
+ </item>
+ <item>
+ <p>If A is an association type <c>K := V</c>,
+ where <c>K</c> and <c>V</c> are types, then Rep(A) =
+ <c>{type,LINE,map_field_exact,[Rep(K),Rep(V)]}</c>.</p>
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>Record Field Types</title>
+ <list type="bulleted">
+ <item>If F is a record field type <c>Name :: Type</c>,
+ where <c>Type</c> is a type, then Rep(F) =
+ <c>{type,LINE,field_type,[Rep(Name),Rep(Type)]}</c>.
+ </item>
</list>
</section>
</section>
<section>
- <title>The Abstract Format After Preprocessing</title>
- <p>The compilation option <c>debug_info</c> can be given to the
+ <title>The Abstract Format after Preprocessing</title>
+ <p>The compilation option <c>debug_info</c> can be specified to the
compiler to have the abstract code stored in
- the <c>abstract_code</c> chunk in the BEAM file
+ the <c>abstract_code</c> chunk in the Beam file
(for debugging purposes).</p>
- <p>In OTP R9C and later, the <c>abstract_code</c> chunk will
- contain</p>
- <p><c>{raw_abstract_v1,AbstractCode}</c></p>
- <p>where <c>AbstractCode</c> is the abstract code as described
- in this document.</p>
- <p>In releases of OTP prior to R9C, the abstract code after some more
- processing was stored in the BEAM file. The first element of the
- tuple would be either <c>abstract_v1</c> (R7B) or <c>abstract_v2</c>
- (R8B).</p>
+
+ <p>As from Erlang/OTP R9C, the <c>abstract_code</c> chunk contains
+ <c>{raw_abstract_v1,AbstractCode}</c>, where <c>AbstractCode</c> is the
+ abstract code as described in this section.</p>
+
+ <p>In OTP releases before R9C, the abstract code after some more
+ processing was stored in the Beam file. The first element of the
+ tuple would be either <c>abstract_v1</c> (in OTP R7B) or
+ <c>abstract_v2</c> (in OTP R8B).</p>
</section>
</chapter>
diff --git a/erts/doc/src/alt_dist.xml b/erts/doc/src/alt_dist.xml
index 2263302707..be969a8267 100644
--- a/erts/doc/src/alt_dist.xml
+++ b/erts/doc/src/alt_dist.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2000</year><year>2013</year>
+ <year>2000</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -22,7 +22,8 @@
</legalnotice>
- <title>How to implement an alternative carrier for the Erlang distribution</title>
+ <title>How to Implement an Alternative Carrier for the Erlang Distribution
+ </title>
<prepared>Patrik Nyblom</prepared>
<responsible></responsible>
<docno></docno>
@@ -32,203 +33,270 @@
<rev>PA2</rev>
<file>alt_dist.xml</file>
</header>
- <p>This document describes how one can implement ones own carrier
+ <p>This section describes how to implement an alternative carrier
protocol for the Erlang distribution. The distribution is normally
- carried by the TCP/IP protocol. What's explained here is the method for
- replacing TCP/IP with another protocol. </p>
- <p>The document is a step by step explanation of the <c><![CDATA[uds_dist]]></c> example
- application (seated in the kernel applications <c><![CDATA[examples]]></c> directory).
- The <c><![CDATA[uds_dist]]></c> application implements distribution over Unix domain
- sockets and is written for the Sun Solaris 2 operating environment. The
- mechanisms are however general and applies to any operating system Erlang
- runs on. The reason the C code is not made portable, is simply readability.</p>
- <note><p>This document was written a long time ago. Most of it is still
- valid, but some things have changed since it was first written.
- Most notably the driver interface. There have been some updates
- to the documentation of the driver presented in this documentation,
- but more could be done and are planned for the future. The
- reader is encouraged to also read the
- <seealso marker="erl_driver">erl_driver</seealso>, and the
- <seealso marker="erl_driver">driver_entry</seealso> documentation.
- </p></note>
+ carried by TCP/IP. Here is explained a method for replacing TCP/IP
+ with another protocol.</p>
+
+ <p>The section is a step-by-step explanation of the
+ <c><![CDATA[uds_dist]]></c> example application (in the
+ Kernel application <c><![CDATA[examples]]></c> directory). The
+ <c><![CDATA[uds_dist]]></c> application implements distribution over Unix
+ domain sockets and is written for the Sun Solaris 2 operating environment.
+ The mechanisms are however general and apply to any operating system Erlang
+ runs on. The reason the C code is not made portable, is simply
+ readability.</p>
+
+ <note>
+ <p>This section was written a long time ago. Most of it is still
+ valid, but some things have changed since then.
+ Most notably is the driver interface. Some updates have been made
+ to the documentation of the driver presented here,
+ but more can be done and is planned for the future.
+ The reader is encouraged to read the
+ <seealso marker="erl_driver"><c>erl_driver</c></seealso> and
+ <seealso marker="driver_entry"><c>driver_entry</c></seealso>
+ documentation also.</p>
+ </note>
<section>
<title>Introduction</title>
- <p>To implement a new carrier for the Erlang distribution, one must first
- make the protocol available to the Erlang machine, which involves writing
- an Erlang driver. There is no way one can use a port program,
- there <em>has</em> to
- be an Erlang driver. Erlang drivers can either be statically
- linked
- to the emulator, which can be an alternative when using the open source
- distribution of Erlang, or dynamically loaded into the Erlang machines
- address space, which is the only alternative if a precompiled version of
- Erlang is to be used. </p>
- <p>Writing an Erlang driver is by no means easy. The driver is written
- as a couple of call-back functions called by the Erlang emulator when
- data is sent to the driver or the driver has any data available on a file
- descriptor. As the driver call-back routines execute in the main
- thread of the Erlang machine, the call-back functions can perform
- no blocking activity whatsoever. The call-backs should only set up
- file descriptors for waiting and/or read/write available data. All
- I/O has to be non blocking. Driver call-backs are however executed
- in sequence, why a global state can safely be updated within the
- routines. </p>
- <p>When the driver is implemented, one would preferably write an
- Erlang interface for the driver to be able to test the
- functionality of the driver separately. This interface can then
- be used by the distribution module which will cover the details of
- the protocol from the <c><![CDATA[net_kernel]]></c>. The easiest path is to
- mimic the <c><![CDATA[inet]]></c> and <c><![CDATA[inet_tcp]]></c> interfaces, but a lot of
- functionality in those modules need not be implemented. In the
- example application, only a few of the usual interfaces are
- implemented, and they are much simplified.</p>
- <p>When the protocol is available to Erlang through a driver and an
- Erlang interface module, a distribution module can be
- written. The distribution module is a module with well defined
- call-backs, much like a <c><![CDATA[gen_server]]></c> (there is no compiler support
- for checking the call-backs though). The details of finding other
- nodes (i.e. talking to epmd or something similar), creating a
- listen port (or similar), connecting to other nodes and performing
- the handshakes/cookie verification are all implemented by this
- module. There is however a utility module, <c><![CDATA[dist_util]]></c>, that
- will do most of the hard work of handling handshakes, cookies,
- timers and ticking. Using <c><![CDATA[dist_util]]></c> makes implementing a
- distribution module much easier and that's what we are doing in
- the example application.</p>
- <p>The last step is to create boot scripts to make the protocol
- implementation available at boot time. The implementation can be
- debugged by starting the distribution when all of the system is
- running, but in a real system the distribution should start very
- early, why a boot-script and some command line parameters are
- necessary. This last step also implies that the Erlang code in the
- interface and distribution modules is written in such a way that
- it can be run in the startup phase. Most notably there can be no
- calls to the <c><![CDATA[application]]></c> module or to any modules not
- loaded at boot-time (i.e. only <c><![CDATA[kernel]]></c>, <c><![CDATA[stdlib]]></c> and the
- application itself can be used).</p>
+ <p>To implement a new carrier for the Erlang distribution, the main
+ steps are as follows.</p>
+
+ <section>
+ <title>Writing an Erlang Driver</title>
+ <p>First, the protocol must be available to the Erlang machine, which
+ involves writing an Erlang driver. A port program cannot be used,
+ an Erlang driver is required. Erlang drivers can be:</p>
+
+ <list type="bulleted">
+ <item>
+ <p>Statically linked to the emulator, which can be an alternative
+ when using the open source distribution of Erlang, or</p>
+ </item>
+ <item>
+ <p>Dynamically loaded into the Erlang machines address space,
+ which is the only alternative if a precompiled version of
+ Erlang is to be used</p>
+ </item>
+ </list>
+
+ <p>Writing an Erlang driver is not easy. The driver is written
+ as some callback functions called by the Erlang emulator when
+ data is sent to the driver, or the driver has any data available on
+ a file descriptor. As the driver callback routines execute in the main
+ thread of the Erlang machine, the callback functions can perform
+ no blocking activity whatsoever. The callbacks are only to set up
+ file descriptors for waiting and/or read/write available data. All
+ I/O must be non-blocking. Driver callbacks are however executed
+ in sequence, why a global state can safely be updated within the
+ routines.</p>
+ </section>
+
+ <section>
+ <title>Writing an Erlang Interface for the Driver</title>
+ <p>When the driver is implemented, one would preferably write an
+ Erlang interface for the driver to be able to test the
+ functionality of the driver separately. This interface can then
+ be used by the distribution module, which will cover the details of
+ the protocol from the <c><![CDATA[net_kernel]]></c>.</p>
+
+ <p>The easiest path
+ is to mimic the <c><![CDATA[inet]]></c> and <c><![CDATA[inet_tcp]]></c>
+ interfaces, but not much
+ functionality in those modules needs to be implemented. In the
+ example application, only a few of the usual interfaces are
+ implemented, and they are much simplified.</p>
+ </section>
+
+ <section>
+ <title>Writing a Distribution Module</title>
+ <p>When the protocol is available to Erlang through a driver and an
+ Erlang interface module, a distribution module can be written.
+ The distribution module is a module with well-defined callbacks,
+ much like a <c><![CDATA[gen_server]]></c> (there is no compiler support
+ for checking the callbacks, though). This module implements:</p>
+
+ <list type="bulleted">
+ <item>The details of finding other nodes (that is, talking to
+ <c>epmd</c> or something similar)</item>
+ <item>Creating a listen port (or similar)</item>
+ <item>Connecting to other nodes</item>
+ <item>Performing the handshakes/cookie verification</item>
+ </list>
+
+ <p>There is however a utility module, <c><![CDATA[dist_util]]></c>, which
+ does most of the hard work of handling handshakes, cookies, timers,
+ and ticking. Using <c><![CDATA[dist_util]]></c> makes implementing a
+ distribution module much easier and that is done in
+ the example application.</p>
+ </section>
+
+ <section>
+ <title>Creating Boot Scripts</title>
+ <p>The last step is to create boot scripts to make the protocol
+ implementation available at boot time. The implementation can be
+ debugged by starting the distribution when all the system is
+ running, but in a real system the distribution is to start very
+ early, why a boot script and some command-line parameters are
+ necessary.</p>
+
+ <p>This step also implies that the Erlang code in the
+ interface and distribution modules is written in such a way that
+ it can be run in the startup phase. In particular, there can be no
+ calls to the <c><![CDATA[application]]></c> module or to any modules
+ not loaded at boot time. That is, only <c><![CDATA[Kernel]]></c>,
+ <c><![CDATA[STDLIB]]></c>, and the application itself can be used.</p>
+ </section>
</section>
<section>
- <title>The driver</title>
- <p>Although Erlang drivers in general may be beyond the scope of this
- document, a brief introduction seems to be in place.</p>
+ <title>The Driver</title>
+ <p>Although Erlang drivers in general can be beyond the scope of this
+ section, a brief introduction seems to be in place.</p>
<section>
- <title>Drivers in general</title>
+ <title>Drivers in General</title>
<p>An Erlang driver is a native code module written in C (or
- assembler) which serves as an interface for some special operating
+ assembler), which serves as an interface for some special operating
system service. This is a general mechanism that is used
throughout the Erlang emulator for all kinds of I/O. An Erlang
driver can be dynamically linked (or loaded) to the Erlang
emulator at runtime by using the <c><![CDATA[erl_ddll]]></c> Erlang
module. Some of the drivers in OTP are however statically linked
- to the runtime system, but that's more an optimization than a
+ to the runtime system, but that is more an optimization than a
necessity.</p>
- <p>The driver data-types and the functions available to the driver
- writer are defined in the header file <c><![CDATA[erl_driver.h]]></c> (there
- is also an deprecated version called <c><![CDATA[driver.h]]></c>, don't use
- that one.) seated in Erlang's include directory (and in
- $ERL_TOP/erts/emulator/beam in the source code
- distribution). Refer to that file for function prototypes etc.</p>
+
+ <p>The driver data types and the functions available to the driver
+ writer are defined in header file <c><![CDATA[erl_driver.h]]></c>
+ seated in Erlang's include directory. See the
+ <seealso marker="erts:erl_driver">erl_driver</seealso> documentation
+ for details of which functions are available.</p>
+
<p>When writing a driver to make a communications protocol available
to Erlang, one should know just about everything worth knowing
- about that particular protocol. All operation has to be non
- blocking and all possible situations should be accounted for in
- the driver. A non stable driver will affect and/or crash the
- whole Erlang runtime system, which is seldom what's wanted. </p>
+ about that particular protocol. All operation must be
+ non-blocking and all possible situations are to be accounted for in
+ the driver. A non-stable driver will affect and/or crash the
+ whole Erlang runtime system.</p>
+
<p>The emulator calls the driver in the following situations:</p>
+
<list type="bulleted">
- <item>When the driver is loaded. This call-back has to have a
- special name and will inform the emulator of what call-backs should
- be used by returning a pointer to a <c><![CDATA[ErlDrvEntry]]></c> struct,
- which should be properly filled in (see below).</item>
- <item>When a port to the driver is opened (by a <c><![CDATA[open_port]]></c>
- call from Erlang). This routine should set up internal data
- structures and return an opaque data entity of the type
- <c><![CDATA[ErlDrvData]]></c>, which is a data-type large enough to hold a
- pointer. The pointer returned by this function will be the first
- argument to all other call-backs concerning this particular
- port. It is usually called the port handle. The emulator only
- stores the handle and does never try to interpret it, why it can
- be virtually anything (well anything not larger than a pointer
- that is) and can point to anything if it is a pointer. Usually
- this pointer will refer to a structure holding information about
- the particular port, as i t does in our example.</item>
- <item>When an Erlang process sends data to the port. The data will
- arrive as a buffer of bytes, the interpretation is not defined,
- but is up to the implementor. This call-back returns nothing to the
- caller, answers are sent to the caller as messages (using a
- routine called <c><![CDATA[driver_output]]></c> available to all
- drivers). There is also a way to talk in a synchronous way to
- drivers, described below. There can be an additional call-back
- function for handling data that is fragmented (sent in a deep
- io-list). That interface will get the data in a form suitable for
- Unix <c><![CDATA[writev]]></c> rather than in a single buffer. There is no
- need for a distribution driver to implement such a call-back, so
- we wont.</item>
- <item>When a file descriptor is signaled for input. This call-back
- is called when the emulator detects input on a file descriptor
- which the driver has marked for monitoring by using the interface
- <c><![CDATA[driver_select]]></c>. The mechanism of driver select makes it
- possible to read non blocking from file descriptors by calling
- <c><![CDATA[driver_select]]></c> when reading is needed and then do the actual
- reading in this call-back (when reading is actually possible). The
- typical scenario is that <c><![CDATA[driver_select]]></c> is called when an
- Erlang process orders a read operation, and that this routine
- sends the answer when data is available on the file descriptor.</item>
- <item>When a file descriptor is signaled for output. This call-back
- is called in a similar way as the previous, but when writing to a
- file descriptor is possible. The usual scenario is that Erlang
- orders writing on a file descriptor and that the driver calls
- <c><![CDATA[driver_select]]></c>. When the descriptor is ready for output,
- this call-back is called an the driver can try to send the
- output. There may of course be queuing involved in such
- operations, and there are some convenient queue routines available
- to the driver writer to use in such situations.</item>
- <item>When a port is closed, either by an Erlang process or by the
- driver calling one of the <c><![CDATA[driver_failure_XXX]]></c> routines. This
- routine should clean up everything connected to one particular
- port. Note that when other call-backs call a
- <c><![CDATA[driver_failure_XXX]]></c> routine, this routine will be
- immediately called and the call-back routine issuing the error can
- make no more use of the data structures for the port, as this
- routine surely has freed all associated data and closed all file
- descriptors. If the queue utility available to driver writes is
- used, this routine will however <em>not</em> be called until the
- queue is empty.</item>
- <item>When an Erlang process calls <c>erlang:port_control/3</c>,
- which is a synchronous interface to drivers. The control interface
- is used to set driver options, change states of ports etc. We'll
- use this interface quite a lot in our example.</item>
- <item>When a timer expires. The driver can set timers with the
- function <c><![CDATA[driver_set_timer]]></c>. When such timers expire, a
- specific call-back function is called. We will not use timers in
- our example.</item>
- <item>When the whole driver is unloaded. Every resource allocated
- by the driver should be freed.</item>
+ <item>
+ <p>When the driver is loaded. This callback must have a special
+ name and inform the emulator of what callbacks are to be used
+ by returning a pointer to a <c><![CDATA[ErlDrvEntry]]></c> struct,
+ which is to be properly filled in (see below).</p>
+ </item>
+ <item>
+ <p>When a port to the driver is opened (by a
+ <c><![CDATA[open_port]]></c> call from Erlang). This routine is to
+ set up internal data structures and return an opaque data entity of
+ the type <c><![CDATA[ErlDrvData]]></c>, which is a data type large
+ enough to hold a pointer.
+ The pointer returned by this function is the first
+ argument to all other callbacks concerning this particular
+ port. It is usually called the port handle. The emulator only
+ stores the handle and does never try to interpret it, why it can
+ be virtually anything (anything not larger than a pointer
+ that is) and can point to anything if it is a pointer. Usually
+ this pointer refers to a structure holding information about
+ the particular port, as it does in the example.</p>
+ </item>
+ <item>
+ <p>When an Erlang process sends data to the port. The data
+ arrives as a buffer of bytes, the interpretation is not defined,
+ but is up to the implementor. This callback returns nothing to the
+ caller, answers are sent to the caller as messages (using a
+ routine called <c><![CDATA[driver_output]]></c> available to all
+ drivers). There is also a way to talk in a synchronous way to
+ drivers, described below. There can be an additional callback
+ function for handling data that is fragmented (sent in a deep
+ io-list). That interface gets the data in a form suitable for
+ Unix <c><![CDATA[writev]]></c> rather than in a single buffer.
+ There is no need for a distribution driver to implement such a
+ callback, so we will not.</p>
+ </item>
+ <item>
+ <p>When a file descriptor is signaled for input. This callback
+ is called when the emulator detects input on a file descriptor
+ that the driver has marked for monitoring by using the interface
+ <c><![CDATA[driver_select]]></c>. The mechanism of driver select
+ makes it possible to read non-blocking from file descriptors by
+ calling <c><![CDATA[driver_select]]></c> when reading is needed, and
+ then do the reading in this callback (when reading is possible).
+ The typical scenario is that <c><![CDATA[driver_select]]></c> is
+ called when an Erlang process orders a read operation, and that
+ this routine sends the answer when data is available on the file
+ descriptor.</p>
+ </item>
+ <item>
+ <p>When a file descriptor is signaled for output. This callback
+ is called in a similar way as the previous, but when writing to a
+ file descriptor is possible. The usual scenario is that Erlang
+ orders writing on a file descriptor and that the driver calls
+ <c><![CDATA[driver_select]]></c>. When the descriptor is ready for
+ output, this callback is called and the driver can try to send the
+ output. Queuing can be involved in such operations, and there are
+ convenient queue routines available to the driver writer to use.</p>
+ </item>
+ <item>
+ <p>When a port is closed, either by an Erlang process or by the
+ driver calling one of the <c><![CDATA[driver_failure_XXX]]></c>
+ routines. This routine is to clean up everything connected to one
+ particular port. When other callbacks call a
+ <c><![CDATA[driver_failure_XXX]]></c> routine, this routine is
+ immediately called. The callback routine issuing the error can
+ make no more use of the data structures for the port, as this
+ routine surely has freed all associated data and closed all file
+ descriptors. If the queue utility available to driver writer is
+ used, this routine is however <em>not</em> called until the
+ queue is empty.</p>
+ </item>
+ <item>
+ <p>When an Erlang process calls
+ <seealso marker="erlang#port_control/3">
+ <c>erlang:port_control/3</c></seealso>,
+ which is a synchronous interface to drivers. The control interface
+ is used to set driver options, change states of ports, and so on.
+ This interface is used a lot in the example.</p>
+ </item>
+ <item>
+ <p>When a timer expires. The driver can set timers with the function
+ <c><![CDATA[driver_set_timer]]></c>. When such timers expire, a
+ specific callback function is called. No timers are used in
+ the example.</p>
+ </item>
+ <item>
+ <p>When the whole driver is unloaded. Every resource allocated
+ by the driver is to be freed.</p>
+ </item>
</list>
</section>
<section>
- <title>The distribution driver's data structures</title>
- <p>The driver used for Erlang distribution should implement a
- reliable, order maintaining, variable length packet oriented
- protocol. All error correction, re-sending and such need to be
+ <title>The Data Structures of the Distribution Driver</title>
+ <p>The driver used for Erlang distribution is to implement a
+ reliable, order maintaining, variable length packet-oriented
+ protocol. All error correction, resending and such need to be
implemented in the driver or by the underlying communications
- protocol. If the protocol is stream oriented (as is the case with
+ protocol. If the protocol is stream-oriented (as is the case with
both TCP/IP and our streamed Unix domain sockets), some mechanism
for packaging is needed. We will use the simple method of having a
- header of four bytes containing the length of the package in a big
- endian 32 bit integer (as Unix domain sockets only can be used
- between processes on the same machine, we actually don't need to
- code the integer in some special endianess, but I'll do it anyway
- because in most situation you do need to do it. Unix domain
- sockets are reliable and order maintaining, so we don't need to
- implement resends and such in our driver.</p>
- <p>Lets start writing our example Unix domain sockets driver by
- declaring prototypes and filling in a static ErlDrvEntry
- structure.</p>
+ header of four bytes containing the length of the package in a
+ big-endian 32-bit integer. As Unix domain sockets only can be used
+ between processes on the same machine, we do not need to
+ code the integer in some special endianess, but we will do it anyway
+ because in most situation you need to do it. Unix domain
+ sockets are reliable and order maintaining, so we do not need to
+ implement resends and such in the driver.</p>
+
+ <p>We start writing the example Unix domain sockets driver by
+ declaring prototypes and filling in a static <c>ErlDrvEntry</c>
+ structure:</p>
+
<code type="none"><![CDATA[
( 1) #include <stdio.h>
( 2) #include <stdlib.h>
@@ -286,94 +354,122 @@
(51) NULL, /* process_exit callback */
(52) NULL /* stop_select callback */
(53) };]]></code>
- <p>On line 1 to 10 we have included the OS headers needed for our
- driver. As this driver is written for Solaris, we know that the
- header <c><![CDATA[uio.h]]></c> exists, why we can define the preprocessor
- variable <c><![CDATA[HAVE_UIO_H]]></c> before we include <c><![CDATA[erl_driver.h]]></c>
- at line 12. The definition of <c><![CDATA[HAVE_UIO_H]]></c> will make the
+
+ <p>On line 1-10 the OS headers needed for the driver are included.
+ As this driver is written for Solaris, we know that the
+ header <c><![CDATA[uio.h]]></c> exists. So the preprocessor variable
+ <c><![CDATA[HAVE_UIO_H]]></c> can be defined before
+ <c><![CDATA[erl_driver.h]]></c> is included on line 12.
+ The definition of <c><![CDATA[HAVE_UIO_H]]></c> will make the
I/O vectors used in Erlang's driver queues to correspond to the
operating systems ditto, which is very convenient.</p>
- <p>The different call-back functions are declared ("forward
- declarations") on line 16 to 23.</p>
- <p>The driver structure is similar for statically linked in
- drivers and dynamically loaded. However some of the fields
- should be left empty (i.e. initialized to NULL) in the
+
+ <p>On line 16-23 the different callback functions are declared ("forward
+ declarations").</p>
+
+ <p>The driver structure is similar for statically linked-in
+ drivers and dynamically loaded. However, some of the fields
+ are to be left empty (that is, initialized to NULL) in the
different types of drivers. The first field (the <c><![CDATA[init]]></c>
function pointer) is always left blank in a dynamically loaded
- driver, which can be seen on line 26. The NULL on line 37
- should always be there, the field is no longer used and is
- retained for backward compatibility. We use no timers in this
- driver, why no call-back for timers is needed. The <c>outputv</c> field
+ driver, see line 26. <c>NULL</c> on line 37
+ is always to be there, the field is no longer used and is
+ retained for backward compatibility. No timers are used in this
+ driver, why no callback for timers is needed. The <c>outputv</c> field
(line 40) can be used to implement an interface similar to
Unix <c><![CDATA[writev]]></c> for output. The Erlang runtime
- system could previously not use <c>outputv</c> for the
- distribution, but since erts version 5.7.2 it can.
- Since this driver was written before erts version 5.7.2 it does
- not use the <c>outputv</c> callback. Using the <c>outputv</c>
- callback is preferred since it reduces copying of data. (We
- will however use scatter/gather I/O internally in the driver).</p>
- <p>As of erts version 5.5.3 the driver interface was extended with
- version control and the possibility to pass capability information.
- Capability flags are present at line 48. As of erts version 5.7.4
- the
- <seealso marker="driver_entry#driver_flags">ERL_DRV_FLAG_SOFT_BUSY</seealso>
- flag is required for drivers that are to be used by the distribution.
- The soft busy flag implies that the driver is capable of handling
- calls to the <c>output</c> and <c>outputv</c> callbacks even though
- it has marked itself as busy. This has always been a requirement
- on drivers used by the distribution, but there have previously not
- been any capability information available about this. For more
- information see
- <seealso marker="erl_driver#set_busy_port">set_busy_port()</seealso>).
-</p>
+ system could previously not use <c>outputv</c> for the
+ distribution, but it can as from ERTS 5.7.2.
+ As this driver was written before ERTS 5.7.2 it does
+ not use the <c>outputv</c> callback. Using the <c>outputv</c>
+ callback is preferred, as it reduces copying of data. (We
+ will however use scatter/gather I/O internally in the driver.)</p>
+
+ <p>As from ERTS 5.5.3 the driver interface was extended with
+ version control and the possibility to pass capability information.
+ Capability flags are present on line 48. As from ERTS 5.7.4 flag
+ <seealso marker="driver_entry#driver_flags">
+ <c>ERL_DRV_FLAG_SOFT_BUSY</c></seealso> is required for drivers that
+ are to be used by the distribution. The soft busy flag implies that the
+ driver can handle calls to the <c>output</c> and <c>outputv</c>
+ callbacks although it has marked itself as busy. This has always been a
+ requirement on drivers used by the distribution, but no capability
+ information has been available about this previously. For more
+ information. see <seealso marker="erl_driver#set_busy_port">
+ <c>erl_driver:set_busy_port()</c></seealso>).</p>
+
<p>This driver was written before the runtime system had SMP support.
- The driver will still function in the runtime system with SMP support,
- but performance will suffer from lock contention on the driver lock
- used for the driver. This can be alleviated by reviewing and perhaps
- rewriting the code so that each instance of the driver safely can
- execute in parallel. When instances safely can execute in parallel it
- is safe to enable instance specific locking on the driver. This is done
- by passing
- <seealso marker="driver_entry#driver_flags">ERL_DRV_FLAG_USE_PORT_LOCKING</seealso>
- as a driver flag. This is left as an exercise for the reader.</p>
- <p>Our defined call-backs thus are:</p>
- <list type="bulleted">
- <item>uds_start, which shall initiate data for a port. We wont
- create any actual sockets here, just initialize data structures.</item>
- <item>uds_stop, the function called when a port is closed.</item>
- <item>uds_command, which will handle messages from Erlang. The
- messages can either be plain data to be sent or more subtle
- instructions to the driver. We will use this function mostly for
- data pumping.</item>
- <item>uds_input, this is the call-back which is called when we have
- something to read from a socket.</item>
- <item>uds_output, this is the function called when we can write to a
- socket.</item>
- <item>uds_finish, which is called when the driver is unloaded. A
- distribution driver will actually (or hopefully) never be unloaded,
- but we include this for completeness. Being able to clean up after
- oneself is always a good thing.</item>
- <item>uds_control, the <c>erlang:port_control/2</c> call-back, which
- will be used a lot in this implementation.</item>
- </list>
- <p>The ports implemented by this driver will operate in two major
- modes, which i will call the <em>command</em> and <em>data</em>
- modes. In command mode, only passive reading and writing (like
- gen_tcp:recv/gen_tcp:send) can be
- done, and this is the mode the port will be in during the
- distribution handshake. When the connection is up, the port will
- be switched to data mode and all data will be immediately read and
- passed further to the Erlang emulator. In data mode, no data
- arriving to the uds_command will be interpreted, but just packaged
- and sent out on the socket. The uds_control call-back will do the
- switching between those two modes.</p>
- <p>While the <c><![CDATA[net_kernel]]></c> informs different subsystems that the
- connection is coming up, the port should accept data to send, but
- not receive any data, to avoid that data arrives from another node
- before every kernel subsystem is prepared to handle it. We have a
- third mode for this intermediate stage, lets call it the
- <em>intermediate</em> mode.</p>
- <p>Lets define an enum for the different types of ports we have:</p>
+ The driver will still function in the runtime system with SMP support,
+ but performance will suffer from lock contention on the driver lock
+ used for the driver. This can be alleviated by reviewing and perhaps
+ rewriting the code so that each instance of the driver safely can
+ execute in parallel. When instances safely can execute in parallel, it
+ is safe to enable instance-specific locking on the driver. This is done
+ by passing <seealso marker="driver_entry#driver_flags">
+ <c>ERL_DRV_FLAG_USE_PORT_LOCKING</c></seealso> as a driver flag. This
+ is left as an exercise for the reader.</p>
+
+ <p>Thus, the defined callbacks are as follows:</p>
+
+ <taglist>
+ <tag><c>uds_start</c></tag>
+ <item>
+ <p>Must initiate data for a port. We do not create any sockets
+ here, only initialize data structures.</p>
+ </item>
+ <tag><c>uds_stop</c></tag>
+ <item>
+ <p>Called when a port is closed.</p>
+ </item>
+ <tag><c>uds_command</c></tag>
+ <item>
+ <p>Handles messages from Erlang. The
+ messages can either be plain data to be sent or more subtle
+ instructions to the driver. This function is here mostly for
+ data pumping.</p>
+ </item>
+ <tag><c>uds_input</c></tag>
+ <item>
+ <p>Called when there is something to read from a socket.</p>
+ </item>
+ <tag><c>uds_output</c></tag>
+ <item>
+ <p>Called when it is possible to write to a socket.</p>
+ </item>
+ <tag><c>uds_finish</c></tag>
+ <item>
+ <p>Called when the driver is unloaded. A distribution driver will
+ never be unloaded, but we include this for completeness. To be
+ able to clean up after oneself is always a good thing.</p>
+ </item>
+ <tag><c>uds_control</c></tag>
+ <item>
+ <p>The <seealso marker="erlang#port_control/3">
+ <c>erlang:port_control/3</c></seealso> callback, which is
+ used a lot in this implementation.</p>
+ </item>
+ </taglist>
+
+ <p>The ports implemented by this driver operate in two major modes,
+ named <c>command</c> and <c>data</c>. In <c>command</c> mode,
+ only passive reading and writing (like
+ <c>gen_tcp:recv</c>/<c>gen_tcp:send</c>) can be done. The port is in
+ this mode during the distribution handshake. When the connection is up,
+ the port is switched to <c>data</c> mode and all data is immediately
+ read and passed further to the Erlang emulator. In <c>data</c>
+ mode, no data arriving to <c>uds_command</c> is interpreted, only
+ packaged and sent out on the socket. The <c>uds_control</c> callback
+ does the switching between those two modes.</p>
+
+ <p>While <c><![CDATA[net_kernel]]></c> informs different subsystems
+ that the connection is coming up, the port is to accept data to send.
+ However, the port should not receive any data, to avoid that data
+ arrives from another node before every kernel subsystem is prepared
+ to handle it. A third mode, named <c>intermediate</c>, is used for this
+ intermediate stage.</p>
+
+ <p>An enum is defined for the different types of ports:</p>
+
<code type="none"><![CDATA[
( 1) typedef enum {
( 2) portTypeUnknown, /* An uninitialized port */
@@ -384,40 +480,63 @@
( 7) portTypeCommand, /* A connected open port in command mode */
( 8) portTypeIntermediate, /* A connected open port in special
( 9) half active mode */
-(10) portTypeData /* A connectec open port in data mode */
+(10) portTypeData /* A connected open port in data mode */
(11) } PortType; ]]></code>
- <p>Lets look at the different types:</p>
- <list type="bulleted">
- <item>portTypeUnknown - The type a port has when it's opened, but
- not actually bound to any file descriptor.</item>
- <item>portTypeListener - A port that is connected to a listen
- socket. This port will not do especially much, there will be no data
- pumping done on this socket, but there will be read data available
- when one is trying to do an accept on the port.</item>
- <item>portTypeAcceptor - This is a port that is to represent the
- result of an accept operation. It is created when one wants to
- accept from a listen socket, and it will be converted to a
- portTypeCommand when the accept succeeds.</item>
- <item>portTypeConnector - Very similar to portTypeAcceptor, an
- intermediate stage between the request for a connect operation and
- that the socket is really connected to an accepting ditto in the
- other end. As soon as the sockets are connected, the port will
- switch type to portTypeCommand.</item>
- <item>portTypeCommand - A connected socket (or accepted socket if
- you want) that is in the command mode mentioned earlier.</item>
- <item>portTypeIntermediate - The intermediate stage for a connected
- socket. There should be no processing of input for this socket.</item>
- <item>portTypeData - The mode where data is pumped through the port
- and the uds_command routine will regard every call as a call where
- sending is wanted. In this mode all input available will be read and
- sent to Erlang as soon as it arrives on the socket, much like in the
- active mode of a <c><![CDATA[gen_tcp]]></c> socket.</item>
- </list>
- <p>Now lets look at the state we'll need for our ports. One can note
- that not all fields are used for all types of ports and that one
- could save some space by using unions, but that would clutter the
- code with multiple indirections, so i simply use one struct for
- all types of ports, for readability.</p>
+
+ <p>The different types are as follows:</p>
+
+ <taglist>
+ <tag><c>portTypeUnknown</c></tag>
+ <item>
+ <p>The type a port has when it is opened, but
+ not bound to any file descriptor.</p>
+ </item>
+ <tag><c>portTypeListener</c></tag>
+ <item>
+ <p>A port that is connected to a listen socket. This port does not
+ do much, no data pumping is done on this socket, but read data is
+ available when one is trying to do an accept on the port.</p>
+ </item>
+ <tag><c>portTypeAcceptor</c></tag>
+ <item>
+ <p>This port is to represent the result of an accept operation. It is
+ created when one wants to accept from a listen socket, and it is
+ converted to a <c>portTypeCommand</c> when the accept succeeds.</p>
+ </item>
+ <tag><c>portTypeConnector</c></tag>
+ <item>
+ <p>Very similar to <c>portTypeAcceptor</c>, an
+ intermediate stage between the request for a connect operation and
+ that the socket is connected to an accepting ditto in the
+ other end. When the sockets are connected, the port
+ switches type to <c>portTypeCommand</c>.</p>
+ </item>
+ <tag><c>portTypeCommand</c></tag>
+ <item>
+ <p>A connected socket (or accepted socket) in <c>command</c> mode
+ mentioned earlier.</p>
+ </item>
+ <tag><c>portTypeIntermediate</c></tag>
+ <item>
+ <p>The intermediate stage for a connected socket.
+ There is to be no processing of input for this socket.</p>
+ </item>
+ <tag><c>portTypeData</c></tag>
+ <item>
+ <p>The mode where data is pumped through the port and the
+ <c>uds_command</c> routine regards every call as a call where
+ sending is wanted. In this mode, all input available is read and
+ sent to Erlang when it arrives on the socket, much like in the
+ active mode of a <c><![CDATA[gen_tcp]]></c> socket.</p>
+ </item>
+ </taglist>
+
+ <p>We study the state that is needed for the ports. Notice
+ that not all fields are used for all types of ports. Some space
+ could be saved by using unions, but that would clutter the
+ code with multiple indirections, so here is used one struct for
+ all types of ports, for readability:</p>
+
<code type="none"><![CDATA[
( 1) typedef unsigned char Byte;
( 2) typedef unsigned int Word;
@@ -428,7 +547,7 @@
( 6) int lockfd; /* The file descriptor for a lock file in
( 7) case of listen sockets */
( 8) Byte creation; /* The creation serial derived from the
-( 9) lockfile */
+( 9) lock file */
(10) PortType type; /* Type of port */
(11) char *name; /* Short name of socket for unlink */
(12) Word sent; /* Bytes sent */
@@ -442,114 +561,154 @@
(20) input buffer */
(21) Byte *buffer; /* The actual input buffer */
(22) } UdsData; ]]></code>
+
<p>This structure is used for all types of ports although some
fields are useless for some types. The least memory consuming
solution would be to arrange this structure as a union of
- structures, but the multiple indirections in the code to
- access a field in such a structure will clutter the code to
+ structures. However, the multiple indirections in the code to
+ access a field in such a structure would clutter the code too
much for an example.</p>
- <p>Let's look at the fields in our structure:</p>
- <list type="bulleted">
- <item>fd - The file descriptor of the socket associated with the
- port.</item>
- <item>port - The port identifier for the port which this structure
- corresponds to. It is needed for most <c><![CDATA[driver_XXX]]></c>
- calls from the driver back to the emulator.</item>
+
+ <p>The fields in the structure are as follows:</p>
+
+ <taglist>
+ <tag><c>fd</c></tag>
<item>
- <p>lockfd - If the socket is a listen socket, we use a separate
+ <p>The file descriptor of the socket associated with the port.</p>
+ </item>
+ <tag><c>port</c></tag>
+ <item>
+ <p>The port identifier for the port that this structure
+ corresponds to. It is needed for most <c><![CDATA[driver_XXX]]></c>
+ calls from the driver back to the emulator.</p>
+ </item>
+ <tag><c>lockfd</c></tag>
+ <item>
+ <p>If the socket is a listen socket, we use a separate
(regular) file for two purposes:</p>
<list type="bulleted">
- <item>We want a locking mechanism that gives no race
- conditions, so that we can be sure of if another Erlang
- node uses the listen socket name we require or if the
- file is only left there from a previous (crashed)
- session.</item>
<item>
- <p>We store the <em>creation</em> serial number in the
- file. The <em>creation</em> is a number that should
+ <p>We want a locking mechanism that gives no race
+ conditions, to be sure if another Erlang
+ node uses the listen socket name we require or if the
+ file is only left there from a previous (crashed) session.</p>
+ </item>
+ <item>
+ <p>We store the <c>creation</c> serial number in the
+ file. The <c>creation</c> is a number that is to
change between different instances of different Erlang
emulators with the same name, so that process
- identifiers from one emulator won't be valid when sent
+ identifiers from one emulator do not become valid when sent
to a new emulator with the same distribution name. The
- creation can be between 0 and 3 (two bits) and is stored
- in every process identifier sent to another node. </p>
- <p>In a system with TCP based distribution, this data is
+ creation can be from 0 through 3 (two bits) and is stored
+ in every process identifier sent to another node.</p>
+ <p>In a system with TCP-based distribution, this data is
kept in the <em>Erlang port mapper daemon</em>
(<c><![CDATA[epmd]]></c>), which is contacted when a distributed
- node starts. The lock-file and a convention for the UDS
- listen socket's name will remove the need for
+ node starts. The lock file and a convention for the UDS
+ listen socket's name remove the need for
<c><![CDATA[epmd]]></c> when using this distribution module. UDS
is always restricted to one host, why avoiding a port
mapper is easy.</p>
</item>
</list>
</item>
- <item>creation - The creation number for a listen socket, which is
- calculated as (the value found in the lock-file + 1) rem
- 4. This creation value is also written back into the
- lock-file, so that the next invocation of the emulator will
- found our value in the file.</item>
- <item>type - The current type/state of the port, which can be one
- of the values declared above.</item>
- <item>name - The name of the socket file (the path prefix
- removed), which allows for deletion (<c><![CDATA[unlink]]></c>) when the
- socket is closed.</item>
- <item>sent - How many bytes that have been sent over the
- socket. This may wrap, but that's no problem for the
- distribution, as the only thing that interests the Erlang
- distribution is if this value has changed (the Erlang
- net_kernel <em>ticker</em> uses this value by calling the
- driver to fetch it, which is done through the
- <c>erlang:port_control</c> routine).</item>
- <item>received - How many bytes that are read (received) from the
- socket, used in similar ways as <c><![CDATA[sent]]></c>.</item>
- <item>partner - A pointer to another port structure, which is
- either the listen port from which this port is accepting a
- connection or the other way around. The "partner relation"
- is always bidirectional.</item>
- <item>next - Pointer to next structure in a linked list of all
- port structures. This list is used when accepting
- connections and when the driver is unloaded.</item>
- <item>buffer_size, buffer_pos, header_pos, buffer - data for input
- buffering. Refer to the source code (in the kernel/examples
- directory) for details about the input buffering. That
- certainly goes beyond the scope of this document.</item>
- </list>
+ <tag><c>creation</c></tag>
+ <item>
+ <p>The creation number for a listen socket, which is
+ calculated as (the value found in the lock-file + 1) rem 4.
+ This creation value is also written back into the
+ lock file, so that the next invocation of the emulator
+ finds our value in the file.</p>
+ </item>
+ <tag><c>type</c></tag>
+ <item>
+ <p>The current type/state of the port, which can be one
+ of the values declared above.</p>
+ </item>
+ <tag><c>name</c></tag>
+ <item>
+ <p>The name of the socket file (the path prefix removed),
+ which allows for deletion (<c><![CDATA[unlink]]></c>) when the
+ socket is closed.</p>
+ </item>
+ <tag><c>sent</c></tag>
+ <item>
+ <p>How many bytes that have been sent over the
+ socket. This can wrap, but that is no problem for the
+ distribution, as the Erlang distribution is only interested in
+ if this value has changed. (The Erlang
+ <c>net_kernel</c> <c>ticker</c> uses this value by calling the
+ driver to fetch it, which is done through the
+ <seealso marker="erlang#port_control/3">
+ <c>erlang:port_control/3</c></seealso> routine.)</p>
+ </item>
+ <tag><c>received</c></tag>
+ <item>
+ <p>How many bytes that are read (received) from the
+ socket, used in similar ways as <c><![CDATA[sent]]></c>.</p>
+ </item>
+ <tag><c>partner</c></tag>
+ <item>
+ <p>A pointer to another port structure, which is
+ either the listen port from which this port is accepting a
+ connection or conversely. The "partner relation"
+ is always bidirectional.</p>
+ </item>
+ <tag><c>next</c></tag>
+ <item>
+ <p>Pointer to next structure in a linked list of all
+ port structures. This list is used when accepting
+ connections and when the driver is unloaded.</p>
+ </item>
+ <tag><c>buffer_size</c>, <c>buffer_pos</c>, <c>header_pos</c>,
+ <c>buffer</c></tag>
+ <item>
+ <p>Data for input buffering. For details about the input buffering,
+ see the source code in directory <c>kernel/examples</c>. That
+ certainly goes beyond the scope of this section.</p>
+ </item>
+ </taglist>
</section>
<section>
- <title>Selected parts of the distribution driver implementation</title>
- <p>The distribution drivers implementation is not completely
- covered in this text, details about buffering and other things
+ <title>Selected Parts of the Distribution Driver Implementation</title>
+ <p>The implemenation of the distribution driver is not completely
+ covered here, details about buffering and other things
unrelated to driver writing are not explained. Likewise are
some peculiarities of the UDS protocol not explained in
detail. The chosen protocol is not important.</p>
- <p>Prototypes for the driver call-back routines can be found in
+
+ <p>Prototypes for the driver callback routines can be found in
the <c><![CDATA[erl_driver.h]]></c> header file.</p>
+
<p>The driver initialization routine is (usually) declared with a
macro to make the driver easier to port between different
- operating systems (and flavours of systems). This is the only
- routine that has to have a well defined name. All other
- call-backs are reached through the driver structure. The macro
+ operating systems (and flavors of systems). This is the only
+ routine that must have a well-defined name. All other
+ callbacks are reached through the driver structure. The macro
to use is named <c><![CDATA[DRIVER_INIT]]></c> and takes the driver name
- as parameter.</p>
+ as parameter:</p>
+
<code type="none"><![CDATA[
(1) /* Beginning of linked list of ports */
(2) static UdsData *first_data;
-
(3) DRIVER_INIT(uds_drv)
(4) {
(5) first_data = NULL;
(6) return &uds_driver_entry;
(7) } ]]></code>
+
<p>The routine initializes the single global data structure and
- returns a pointer to the driver entry. The routine will be
- called when <c><![CDATA[erl_ddll:load_driver]]></c> is called from Erlang.</p>
- <p>The <c><![CDATA[uds_start]]></c> routine is called when a port is opened
- from Erlang. In our case, we only allocate a structure and
+ returns a pointer to the driver entry. The routine is called
+ when <c><![CDATA[erl_ddll:load_driver]]></c> is called from Erlang.</p>
+
+ <p>The <c><![CDATA[uds_start]]></c> routine is called when a port is
+ opened from Erlang. In this case, we only allocate a structure and
initialize it. Creating the actual socket is left to the
<c><![CDATA[uds_command]]></c> routine.</p>
+
<code type="none"><![CDATA[
( 1) static ErlDrvData uds_start(ErlDrvPort port, char *buff)
( 2) {
@@ -574,15 +733,18 @@
(21)
(22) return((ErlDrvData) ud);
(23) } ]]></code>
- <p>Every data item is initialized, so that no problems will arise
+
+ <p>Every data item is initialized, so that no problems arise
when a newly created port is closed (without there being any
corresponding socket). This routine is called when
- <c><![CDATA[open_port({spawn, "uds_drv"},[])]]></c> is called from Erlang.</p>
- <p>The <c><![CDATA[uds_command]]></c> routine is the routine called when an
- Erlang process sends data to the port. All asynchronous
- commands when the port is in <em>command mode</em> as well as
- the sending of all data when the port is in <em>data mode</em>
- is handled in this9s routine. Let's have a look at it:</p>
+ <c><![CDATA[open_port({spawn, "uds_drv"},[])]]></c> is called from
+ Erlang.</p>
+
+ <p>The <c><![CDATA[uds_command]]></c> routine is the routine called when
+ an Erlang process sends data to the port. This routine handles all
+ asynchronous commands when the port is in <c>command</c> mode and
+ the sending of all data when the port is in <c>data</c> mode:</p>
+
<code type="none"><![CDATA[
( 1) static void uds_command(ErlDrvData handle, char *buff, int bufflen)
( 2) {
@@ -636,57 +798,77 @@
(49) return;
(50) }
(51) } ]]></code>
- <p>The command routine takes three parameters; the handle
- returned for the port by <c><![CDATA[uds_start]]></c>, which is a pointer
- to the internal port structure, the data buffer and the length
+
+ <p>The command routine takes three parameters; the handle returned for
+ the port by <c><![CDATA[uds_start]]></c>, which is a pointer
+ to the internal port structure, the data buffer, and the length
of the data buffer. The buffer is the data sent from Erlang
- (a list of bytes) converted to an C array (of bytes). </p>
- <p>If Erlang sends i.e. the list <c><![CDATA[[$a,$b,$c]]]></c> to the port,
- the <c><![CDATA[bufflen]]></c> variable will be <c><![CDATA[3]]></c> ant the
- <c><![CDATA[buff]]></c> variable will contain <c><![CDATA[{'a','b','c'}]]></c> (no
- null termination). Usually the first byte is used as an
- opcode, which is the case in our driver to (at least when the
- port is in command mode). The opcodes are defined as:</p>
- <list type="bulleted">
- <item>'L'&lt;socketname&gt;: Create and listen on socket with the
- given name.</item>
- <item>'A'&lt;listennumber as 32 bit bigendian&gt;: Accept from the
- listen socket identified by the given identification
- number. The identification number is retrieved with the
- uds_control routine.</item>
- <item>'C'&lt;socketname&gt;: Connect to the socket named
- &lt;socketname&gt;.</item>
- <item>'S'&lt;data&gt;: Send the data &lt;data&gt; on the
- connected/accepted socket (in command mode). The sending is
- acked when the data has left this process.</item>
- <item>'R': Receive one packet of data.</item>
- </list>
- <p>One may wonder what is meant by "one packet of data" in the
- 'R' command. This driver always sends data packeted with a 4
- byte header containing a big endian 32 bit integer that
+ (a list of bytes) converted to an C array (of bytes).</p>
+
+ <p>If Erlang sends, for example, the list <c><![CDATA[[$a,$b,$c]]]></c>
+ to the port, the <c><![CDATA[bufflen]]></c> variable is
+ <c><![CDATA[3]]></c> and the <c><![CDATA[buff]]></c> variable contains
+ <c><![CDATA[{'a','b','c'}]]></c> (no
+ <c>NULL</c> termination). Usually the first byte is used as an
+ opcode, which is the case in this driver too (at least when the
+ port is in <c>command</c> mode). The opcodes are defined as follows:</p>
+
+ <taglist>
+ <tag><c>'L'&lt;socket name&gt;</c></tag>
+ <item>
+ <p>Creates and listens on socket with the specified name.</p>
+ </item>
+ <tag><c>'A'&lt;listen number as 32-bit big-endian&gt;</c></tag>
+ <item>
+ <p>Accepts from the listen socket identified by the specified
+ identification number. The identification number is retrieved with
+ the <c>uds_control</c> routine.</p>
+ </item>
+ <tag><c>'C'&lt;socket name&gt;</c></tag>
+ <item>
+ <p>Connects to the socket named &lt;socket name&gt;.</p>
+ </item>
+ <tag><c>'S'&lt;data&gt;</c></tag>
+ <item>
+ <p>Sends the data &lt;data&gt; on the
+ connected/accepted socket (in <c>command</c> mode). The sending is
+ acknowledged when the data has left this process.</p>
+ </item>
+ <tag><c>'R'</c></tag>
+ <item>
+ <p>Receives one packet of data.</p>
+ </item>
+ </taglist>
+
+ <p>"One packet of data" in command <c>'R'</c> can be explained
+ as follows. This driver always sends data packaged with a 4
+ byte header containing a big-endian 32-bit integer that
represents the length of the data in the packet. There is no
need for different packet sizes or some kind of streamed
- mode, as this driver is for the distribution only. One may
- wonder why the header word is coded explicitly in big endian
- when an UDS socket is local to the host. The answer simply is
- that I see it as a good practice when writing a distribution
- driver, as distribution in practice usually cross the host
- boundaries. </p>
- <p>On line 4-8 we handle the case where the port is in data or
- intermediate mode, the rest of the routine handles the
- different commands. We see (first on line 15) that the routine
- uses the <c><![CDATA[driver_failure_posix()]]></c> routine to report
- errors. One important thing to remember is that the failure
- routines make a call to our <c><![CDATA[uds_stop]]></c> routine, which
- will remove the internal port data. The handle (and the casted
- handle <c><![CDATA[ud]]></c>) is therefore <em>invalid pointers</em> after a
- <c><![CDATA[driver_failure]]></c> call and we should <em>immediately return</em>. The runtime system will send exit signals to all
+ mode, as this driver is for the distribution only.
+ Why is the header word coded explicitly in big-endian when a UDS
+ socket is local to the host? It is good practice when writing a
+ distribution driver, as distribution in practice usually crosses
+ the host boundaries.</p>
+
+ <p>On line 4-8 is handled the case where the port is in <c>data</c> mode
+ or <c>intermediate</c> mode and the remaining routine handles the
+ different commands. The routine uses the
+ <c><![CDATA[driver_failure_posix()]]></c> routine to report errors
+ (see, for example, line 15). Notice that the failure routines make
+ a call to the <c><![CDATA[uds_stop]]></c> routine, which will
+ remove the internal port data. The handle (and the casted handle
+ <c><![CDATA[ud]]></c>) is therefore <em>invalid pointers</em> after a
+ <c><![CDATA[driver_failure]]></c> call and we should <em>return
+ immediately</em>. The runtime system will send exit signals to all
linked processes.</p>
- <p>The uds_input routine gets called when data is available on a
- file descriptor previously passed to the <c><![CDATA[driver_select]]></c>
- routine. Typically this happens when a read command is issued
- and no data is available. Lets look at the <c><![CDATA[do_recv]]></c>
- routine:</p>
+
+ <p>The <c>uds_input</c> routine is called when data is available on a
+ file descriptor previously passed to the
+ <c><![CDATA[driver_select]]></c> routine. This occurs typically when
+ a read command is issued and no data is available. The
+ <c><![CDATA[do_recv]]></c> routine is as follows:</p>
+
<code type="none"><![CDATA[
( 1) static void do_recv(UdsData *ud)
( 2) {
@@ -716,29 +898,34 @@
(26) }
(27) }
(28) } ]]></code>
+
<p>The routine tries to read data until a packet is read or the
<c><![CDATA[buffered_read_package]]></c> routine returns a
- <c><![CDATA[NORMAL_READ_FAILURE]]></c> (an internally defined constant for
- the module that means that the read operation resulted in an
- <c><![CDATA[EWOULDBLOCK]]></c>). If the port is in command mode, the
- reading stops when one package is read, but if it is in data
- mode, the reading continues until the socket buffer is empty
- (read failure). If no more data can be read and more is wanted
- (always the case when socket is in data mode) driver_select is
- called to make the <c><![CDATA[uds_input]]></c> call-back be called when
- more data is available for reading.</p>
- <p>When the port is in data mode, all data is sent to Erlang in a
- format that suits the distribution, in fact the raw data will
+ <c><![CDATA[NORMAL_READ_FAILURE]]></c> (an internally defined constant
+ for the module, which means that the read operation resulted in an
+ <c><![CDATA[EWOULDBLOCK]]></c>). If the port is in <c>command</c> mode,
+ the reading stops when one package is read. If the port is in
+ <c>data</c> mode, the reading continues until the socket buffer is empty
+ (read failure). If no more data can be read and more is wanted (which
+ is always the case when the socket is in <c>data</c> mode),
+ <c>driver_select</c> is called to make the <c><![CDATA[uds_input]]></c>
+ callback be called when more data is available for reading.</p>
+
+ <p>When the port is in <c>data</c> mode, all data is sent to Erlang in a
+ format that suits the distribution. In fact, the raw data will
never reach any Erlang process, but will be
translated/interpreted by the emulator itself and then
delivered in the correct format to the correct processes. In
- the current emulator version, received data should be tagged
- with a single byte of 100. Thats what the macro
- <c><![CDATA[DIST_MAGIC_RECV_TAG]]></c> is defined to. The tagging of data
- in the distribution will possibly change in the future.</p>
- <p>The <c><![CDATA[uds_input]]></c> routine will handle other input events
- (like nonblocking <c><![CDATA[accept]]></c>), but most importantly handle
+ the current emulator version, received data is to be tagged
+ with a single byte of 100. That is what the macro
+ <c><![CDATA[DIST_MAGIC_RECV_TAG]]></c> is defined to. The tagging of
+ data in the distribution can be changed in the future.</p>
+
+ <p>The <c><![CDATA[uds_input]]></c> routine handles other input events
+ (like non-blocking <c><![CDATA[accept]]></c>), but most importantly
+ handle
data arriving at the socket by calling <c><![CDATA[do_recv]]></c>:</p>
+
<code type="none"><![CDATA[
( 1) static void uds_input(ErlDrvData handle, ErlDrvEvent event)
( 2) {
@@ -768,13 +955,16 @@
(24) }
(25) do_recv(ud);
(26) } ]]></code>
- <p>The important line here is the last line in the function, the
- <c><![CDATA[do_read]]></c> routine is called to handle new input. The rest
- of the function handles input on a listen socket, which means
- that there should be possible to do an accept on the
+
+ <p>The important line is the last line in the function: the
+ <c><![CDATA[do_read]]></c> routine is called to handle new input.
+ The remaining function handles input on a listen socket, which means
+ that it is to be possible to do an accept on the
socket, which is also recognized as a read event.</p>
- <p>The output mechanisms are similar to the input. Lets first
- look at the <c><![CDATA[do_send]]></c> routine:</p>
+
+ <p>The output mechanisms are similar to the input.
+ The <c><![CDATA[do_send]]></c> routine is as follows:</p>
+
<code type="none"><![CDATA[
( 1) static void do_send(UdsData *ud, char *buff, int bufflen)
( 2) {
@@ -816,33 +1006,36 @@
(37) driver_enqv(ud->port, &eio, written);
(38) send_out_queue(ud);
(39) } ]]></code>
+
<p>This driver uses the <c><![CDATA[writev]]></c> system call to send data
- onto the socket. A combination of writev and the driver output
- queues is very convenient. An <em>ErlIOVec</em> structure
- contains a <em>SysIOVec</em> (which is equivalent to the
- <c><![CDATA[struct iovec]]></c> structure defined in <c><![CDATA[uio.h]]></c>. The
- ErlIOVec also contains an array of <em>ErlDrvBinary</em>
+ onto the socket. A combination of <c>writev</c> and the driver output
+ queues is very convenient. An <c>ErlIOVec</c> structure
+ contains a <c>SysIOVec</c> (which is equivalent to the
+ <c><![CDATA[struct iovec]]></c> structure defined in
+ <c><![CDATA[uio.h]]></c>. The
+ <c>ErlIOVec</c> also contains an array of <c>ErlDrvBinary</c>
pointers, of the same length as the number of buffers in the
I/O vector itself. One can use this to allocate the binaries
- for the queue "manually" in the driver, but we'll just fill
- the binary array with NULL values (line 7) , which will make
- the runtime system allocate its own buffers when we call
- <c><![CDATA[driver_enqv]]></c> (line 37).</p>
- <p></p>
+ for the queue "manually" in the driver, but here
+ the binary array is filled with <c>NULL</c> values (line 7).
+ The runtime system then allocates its own buffers when
+ <c><![CDATA[driver_enqv]]></c> is called (line 37).</p>
+
<p>The routine builds an I/O vector containing the header bytes
and the buffer (the opcode has been removed and the buffer
length decreased by the output routine). If the queue is
- empty, we'll write the data directly to the socket (or at
+ empty, we write the data directly to the socket (or at
least try to). If any data is left, it is stored in the queue
- and then we try to send the queue (line 38). An ack is sent
- when the message is delivered completely (line 22). The
- <c><![CDATA[send_out_queue]]></c> will send acks if the sending is
- completed there. If the port is in command mode, the Erlang
+ and then we try to send the queue (line 38). An acknowledgement
+ is sent when the message is delivered completely (line 22). The
+ <c><![CDATA[send_out_queue]]></c> sends acknowledgements if the sending
+ is completed there. If the port is in <c>command</c> mode, the Erlang
code serializes the send operations so that only one packet
- can be waiting for delivery at a time. Therefore the ack can
- be sent simply whenever the queue is empty.</p>
- <p></p>
- <p>A short look at the <c><![CDATA[send_out_queue]]></c> routine:</p>
+ can be waiting for delivery at a time. Therefore the acknowledgement
+ can be sent whenever the queue is empty.</p>
+
+ <p>The <c><![CDATA[send_out_queue]]></c> routine is as follows:</p>
+
<code type="none"><![CDATA[
( 1) static int send_out_queue(UdsData *ud)
( 2) {
@@ -874,20 +1067,24 @@
(28) ud->sent += wrote;
(29) }
(30) } ]]></code>
- <p>What we do is simply to pick out an I/O vector from the queue
- (which is the whole queue as an <em>SysIOVec</em>). If the I/O
- vector is to long (IO_VECTOR_MAX is defined to 16), the vector
+
+ <p>We simply pick out an I/O vector from the queue
+ (which is the whole queue as a <c>SysIOVec</c>). If the I/O
+ vector is too long (<c>IO_VECTOR_MAX</c> is defined to 16), the vector
length is decreased (line 15), otherwise the <c><![CDATA[writev]]></c>
- (line 17) call will
- fail. Writing is tried and anything written is dequeued (line
- 27). If the write fails with <c><![CDATA[EWOULDBLOCK]]></c> (note that all
- sockets are in nonblocking mode), <c><![CDATA[driver_select]]></c> is
+ call (line 17) fails. Writing is tried and anything written is dequeued
+ (line 27).
+ If the write fails with <c><![CDATA[EWOULDBLOCK]]></c> (notice that all
+ sockets are in non-blocking mode), <c><![CDATA[driver_select]]></c> is
called to make the <c><![CDATA[uds_output]]></c> routine be called when
there is space to write again.</p>
- <p>We will continue trying to write until the queue is empty or
- the writing would block.</p>
- <p>The routine above are called from the <c><![CDATA[uds_output]]></c>
- routine, which looks like this:</p>
+
+ <p>We continue trying to write until the queue is empty or
+ the writing blocks.</p>
+
+ <p>The routine above is called from the <c><![CDATA[uds_output]]></c>
+ routine:</p>
+
<code type="none"><![CDATA[
( 1) static void uds_output(ErlDrvData handle, ErlDrvEvent event)
( 2) {
@@ -900,51 +1097,80 @@
( 9) }
(10) send_out_queue(ud);
(11) } ]]></code>
- <p>The routine is simple, it first handles the fact that the
+
+ <p>The routine is simple: it first handles the fact that the
output select will concern a socket in the business of
connecting (and the connecting blocked). If the socket is in
- a connected state it simply sends the output queue, this
- routine is called when there is possible to write to a socket
+ a connected state, it simply sends the output queue. This
+ routine is called when it is possible to write to a socket
where we have an output queue, so there is no question what to
do.</p>
+
<p>The driver implements a control interface, which is a
synchronous interface called when Erlang calls
- <c><![CDATA[erlang:port_control/3]]></c>. This is the only interface
- that can control the driver when it is in data mode and it may
+ <seealso marker="erlang#port_control/3">
+ <c>erlang:port_control/3</c></seealso>. Only this interface
+ can control the driver when it is in <c>data</c> mode. It can
be called with the following opcodes:</p>
- <list type="bulleted">
- <item>'C': Set port in command mode.</item>
- <item>'I': Set port in intermediate mode.</item>
- <item>'D': Set port in data mode.</item>
- <item>'N': Get identification number for listen port, this
- identification number is used in an accept command to the
- driver, it is returned as a big endian 32 bit integer, which
- happens to be the file identifier for the listen socket.</item>
- <item>'S': Get statistics, which is the number of bytes received,
- the number of bytes sent and the number of bytes pending in
- the output queue. This data is used when the distribution
- checks that a connection is alive (ticking). The statistics
- is returned as 3 32 bit big endian integers.</item>
- <item>'T': Send a tick message, which is a packet of length
- 0. Ticking is done when the port is in data mode, so the
- command for sending data cannot be used (besides it ignores
- zero length packages in command mode). This is used by the
- ticker to send dummy data when no other traffic is present.
- <em>Note</em> that it is important that the interface for
- sending ticks is not blocking. This implementation uses
- <c>erlang:port_control/3</c> which does not block the caller.
- If <c>erlang:port_command</c> is used, use
- <c>erlang:port_command/3</c> and pass <c>[force]</c> as
- option list; otherwise, the caller can be blocked indefinitely
- on a busy port and prevent the system from taking down a
- connection that is not functioning.</item>
- <item>'R': Get creation number of listen socket, which is used to
- dig out the number stored in the lock file to differentiate
- between invocations of Erlang nodes with the same name.</item>
- </list>
+
+ <taglist>
+ <tag><c>'C'</c></tag>
+ <item>
+ <p>Sets port in <c>command</c> mode.</p>
+ </item>
+ <tag><c>'I'</c></tag>
+ <item>
+ <p>Sets port in <c>intermediate</c> mode.</p>
+ </item>
+ <tag><c>'D'</c></tag>
+ <item>
+ <p>Sets port in <c>data</c> mode.</p>
+ </item>
+ <tag><c>'N'</c></tag>
+ <item>
+ <p>Gets identification number for listen port. This
+ identification number is used in an accept command to the
+ driver. It is returned as a big-endian 32-bit integer, which
+ is the file identifier for the listen socket.</p>
+ </item>
+ <tag><c>'S'</c></tag>
+ <item>
+ <p>Gets statistics, which is the number of bytes received,
+ the number of bytes sent, and the number of bytes pending in
+ the output queue. This data is used when the distribution
+ checks that a connection is alive (ticking). The statistics
+ is returned as three 32-bit big-endian integers.</p>
+ </item>
+ <tag><c>'T'</c></tag>
+ <item>
+ <p>Sends a tick message, which is a packet of length 0.
+ Ticking is done when the port is in <c>data</c> mode, so the
+ command for sending data cannot be used (besides it ignores
+ zero length packages in <c>command</c> mode). This is used by the
+ ticker to send dummy data when no other traffic is present.</p>
+ <p><em>Note:</em> It is important that the interface for
+ sending ticks is not blocking. This implementation uses
+ <seealso marker="erlang#port_control/3">
+ <c>erlang:port_control/3</c></seealso>, which does not block the
+ caller. If <c>erlang:port_command</c> is used, use
+ <seealso marker="erlang#port_command/3">
+ <c>erlang:port_command/3</c></seealso> and pass <c>[force]</c> as
+ option list; otherwise the caller can be blocked indefinitely
+ on a busy port and prevent the system from taking down a
+ connection that is not functioning.</p>
+ </item>
+ <tag><c>'R'</c></tag>
+ <item>
+ <p>Gets creation number of a listen socket, which is used to
+ dig out the number stored in the lock file to differentiate
+ between invocations of Erlang nodes with the same name.</p>
+ </item>
+ </taglist>
+
<p>The control interface gets a buffer to return its value in,
- but is free to allocate its own buffer is the provided one is
- to small. Here is the code for <c><![CDATA[uds_control]]></c>:</p>
+ but is free to allocate its own buffer if the provided one is
+ too small. The <c><![CDATA[uds_control]]></c> code is as follows:</p>
+
<code type="none"><![CDATA[
( 1) static int uds_control(ErlDrvData handle, unsigned int command,
( 2) char* buf, int count, char** res, int res_size)
@@ -1025,47 +1251,55 @@
(75) }
(76) #undef ENSURE
(77) } ]]></code>
- <p>The macro <c><![CDATA[ENSURE]]></c> (line 5 to 10) is used to ensure that
- the buffer is large enough for our answer. We switch on the
- command and take actions, there is not much to say about this
- routine. Worth noting is that we always has read select active
- on a port in data mode (achieved by calling <c><![CDATA[do_recv]]></c> on
- line 45), but turn off read selection in intermediate and
- command modes (line 27 and 36).</p>
- <p>The rest of the driver is more or less UDS specific and not of
+
+ <p>The macro <c><![CDATA[ENSURE]]></c> (line 5-10) is used to ensure that
+ the buffer is large enough for the answer. We switch on the command and
+ take actions. We always have read select active on a port in <c>data</c>
+ mode (achieved by calling <c><![CDATA[do_recv]]></c> on line 45), but
+ we turn off read selection in <c>intermediate</c> and <c>command</c>
+ modes (line 27 and 36).</p>
+
+ <p>The rest of the driver is more or less UDS-specific and not of
general interest.</p>
</section>
</section>
<section>
- <title>Putting it all together</title>
- <p>To test the distribution, one can use the
- <c><![CDATA[net_kernel:start/1]]></c> function, which is useful as it starts
- the distribution on a running system, where tracing/debugging
- can be performed. The <c><![CDATA[net_kernel:start/1]]></c> routine takes a
- list as its single argument. The lists first element should be
- the node name (without the "@hostname") as an atom, and the second (and
- last) element should be one of the atoms <c><![CDATA[shortnames]]></c> or
- <c><![CDATA[longnames]]></c>. In the example case <c><![CDATA[shortnames]]></c> is
- preferred. </p>
- <p>For net kernel to find out which distribution module to use, the
- command line argument <c><![CDATA[-proto_dist]]></c> is used. The argument
- is followed by one or more distribution module names, with the
- "_dist" suffix removed, i.e. uds_dist as a distribution module
+ <title>Putting It All Together</title>
+ <p>To test the distribution, the <c><![CDATA[net_kernel:start/1]]></c>
+ function can be used. It is useful, as it starts the distribution on a
+ running system, where tracing/debugging can be performed.
+ The <c><![CDATA[net_kernel:start/1]]></c> routine takes a
+ list as its single argument. The list first element in the list is to be
+ the node name (without the "@hostname") as an atom. The second (and
+ last) element is to be one of the atoms <c><![CDATA[shortnames]]></c> or
+ <c><![CDATA[longnames]]></c>. In the example case,
+ <c><![CDATA[shortnames]]></c> is preferred.</p>
+
+ <p>For <c>net_kernel</c> to find out which distribution module to use,
+ command-line argument <c><![CDATA[-proto_dist]]></c> is used. It
+ is followed by one or more distribution module names, with suffix
+ "_dist" removed, that is, <c>uds_dist</c> as a distribution module
is specified as <c><![CDATA[-proto_dist uds]]></c>.</p>
- <p>If no epmd (TCP port mapper daemon) is used, one should also
- specify the command line option <c><![CDATA[-no_epmd]]></c>, which will make
- Erlang skip the epmd startup, both as a OS process and as an
+
+ <p>If no <c>epmd</c> (TCP port mapper daemon) is used, also command-line
+ option <c><![CDATA[-no_epmd]]></c> is to be specified, which makes
+ Erlang skip the <c>epmd</c> startup, both as an OS process and as an
Erlang ditto.</p>
+
<p>The path to the directory where the distribution modules reside
- must be known at boot, which can either be achieved by
- specifying <c><![CDATA[-pa <path>]]></c> on the command line or by building
- a boot script containing the applications used for your
- distribution protocol (in the uds_dist protocol, it's only the
- uds_dist application that needs to be added to the script).</p>
- <p>The distribution will be started at boot if all the above is
+ must be known at boot. This can be achieved either by
+ specifying <c><![CDATA[-pa <path>]]></c> on the command line or by
+ building a boot script containing the applications used for your
+ distribution protocol. (In the <c>uds_dist</c> protocol, only the
+ <c>uds_dist</c> application needs to be added to the script.)</p>
+
+ <p>The distribution starts at boot if all the above is
specified and an <c><![CDATA[-sname <name>]]></c> flag is present at the
- command line, here follows two examples: </p>
+ command line.</p>
+
+ <p><em>Example 1:</em></p>
+
<pre>
$ <input>erl -pa $ERL_TOP/lib/kernel/examples/uds_dist/ebin -proto_dist uds -no_epmd</input>
Erlang (BEAM) emulator version 5.0
@@ -1074,7 +1308,9 @@ Eshell V5.0 (abort with ^G)
1> <input>net_kernel:start([bing,shortnames]).</input>
{ok,&lt;0.30.0>}
(bing@hador)2></pre>
- <p>...</p>
+
+ <p><em>Example 2:</em></p>
+
<pre>
$ <input>erl -pa $ERL_TOP/lib/kernel/examples/uds_dist/ebin -proto_dist uds \ </input>
<input> -no_epmd -sname bong</input>
@@ -1082,8 +1318,10 @@ Erlang (BEAM) emulator version 5.0
Eshell V5.0 (abort with ^G)
(bong@hador)1></pre>
- <p>One can utilize the ERL_FLAGS environment variable to store the
+
+ <p>The <c>ERL_FLAGS</c> environment variable can be used to store the
complicated parameters in:</p>
+
<pre>
$ <input>ERL_FLAGS=-pa $ERL_TOP/lib/kernel/examples/uds_dist/ebin \ </input>
<input> -proto_dist uds -no_epmd</input>
@@ -1093,8 +1331,8 @@ Erlang (BEAM) emulator version 5.0
Eshell V5.0 (abort with ^G)
(bang@hador)1></pre>
- <p>The <c><![CDATA[ERL_FLAGS]]></c> should preferably not include the name of
- the node.</p>
+
+ <p><c><![CDATA[ERL_FLAGS]]></c> should not include the node name.</p>
</section>
</chapter>
diff --git a/erts/doc/src/book.xml b/erts/doc/src/book.xml
index 12eda03ee5..a0780c91d9 100644
--- a/erts/doc/src/book.xml
+++ b/erts/doc/src/book.xml
@@ -4,7 +4,7 @@
<book xmlns:xi="http://www.w3.org/2001/XInclude">
<header titlestyle="normal">
<copyright>
- <year>1997</year><year>2013</year>
+ <year>1997</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/erts/doc/src/communication.xml b/erts/doc/src/communication.xml
index 3deea3e4af..7e18a73aa8 100644
--- a/erts/doc/src/communication.xml
+++ b/erts/doc/src/communication.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2012</year><year>2013</year>
+ <year>2012</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -26,63 +26,72 @@
<prepared>Rickard Green</prepared>
<responsible></responsible>
<docno></docno>
- <approved></approved>
+ <approved>uy</approved>
<checked></checked>
<date>2012-12-03</date>
<rev>PA1</rev>
<file>communication.xml</file>
</header>
<p>Communication in Erlang is conceptually performed using
- asynchronous signaling. All different executing entities
- such as processes, and ports communicate via asynchronous
+ asynchronous signaling. All different executing entities,
+ such as processes and ports, communicate through asynchronous
signals. The most commonly used signal is a message. Other
- common signals are exit, link, unlink, monitor, demonitor
+ common signals are exit, link, unlink, monitor, and demonitor
signals.</p>
+
<section>
<title>Passing of Signals</title>
- <p>The amount of time that passes between a signal being sent
+ <p>The amount of time that passes between a signal is sent
and the arrival of the signal at the destination is unspecified
- but positive. If the receiver has terminated, the signal will
- not arrive, but it is possible that it triggers another signal.
- For example, a link signal sent to a non-existing process will
- trigger an exit signal which will be sent back to where the link
+ but positive. If the receiver has terminated, the signal does
+ not arrive, but it can trigger another signal.
+ For example, a link signal sent to a non-existing process
+ triggers an exit signal, which is sent back to where the link
signal originated from. When communicating over the distribution,
- signals may be lost if the distribution channel goes down.</p>
- <p>The only signal ordering guarantee given is the following. If
+ signals can be lost if the distribution channel goes down.</p>
+
+ <p>The only signal ordering guarantee given is the following: if
an entity sends multiple signals to the same destination entity,
- the order will be preserved. That is, if <c>A</c> sends
+ the order is preserved; that is, if <c>A</c> sends
a signal <c>S1</c> to <c>B</c>, and later sends
- the signal <c>S2</c> to <c>B</c>, <c>S1</c> is guaranteed not to
+ signal <c>S2</c> to <c>B</c>, <c>S1</c> is guaranteed not to
arrive after <c>S2</c>.</p>
</section>
+
<section>
<title>Synchronous Communication</title>
<p>Some communication is synchronous. If broken down into pieces,
- a synchronous communication operation, consists of two asynchronous
- signals. One request signal and one reply signal. An example of
- such a synchronous communication is a call to <c>process_info/2</c>
- when the first argument is not <c>self()</c>. The caller will send
- an asynchronous signal requesting information, and will then
- wait for the reply signal containing the requested information. When
- the request signal reaches its destination the destination process
+ a synchronous communication operation consists of two asynchronous
+ signals; one request signal and one reply signal. An example of
+ such a synchronous communication is a call to
+ <seealso marker="erlang:process_info/2">
+ <c>erlang:process_info/2</c></seealso>
+ when the first argument is not <c>self()</c>. The caller sends
+ an asynchronous signal requesting information, and then
+ waits for the reply signal containing the requested information. When
+ the request signal reaches its destination, the destination process
replies with the requested information.</p>
</section>
+
<section>
<title>Implementation</title>
- <p>The implementation of different asynchronous signals in the
- VM may vary over time, but the behaviour will always respect this
+ <p>The implementation of different asynchronous signals in the virtual
+ machine can vary over time, but the behavior always respects this
concept of asynchronous signals being passed between entities
as described above.</p>
- <p>By inspecting the implementation you might notice that some
- specific signal actually gives a stricter guarantee than described
+
+ <p>By inspecting the implementation, you might notice that some
+ specific signal gives a stricter guarantee than described
above. It is of vital importance that such knowledge about the
- implementation is <em>not</em> used by Erlang code, since the
- implementation might change at any time without prior notice.</p>
- <p>Some example of major implementation changes:</p>
+ implementation is <em>not</em> used by Erlang code, as the
+ implementation can change at any time without prior notice.</p>
+
+ <p>Examples of major implementation changes:</p>
+
<list type="bulleted">
- <item>As of ERTS version 5.5.2 exit signals to processes are truly
+ <item>As from ERTS 5.5.2 exit signals to processes are truly
asynchronously delivered.</item>
- <item>As of ERTS version 5.10 all signals from processes to ports
+ <item>As from ERTS 5.10 all signals from processes to ports
are truly asynchronously delivered.</item>
</list>
</section>
diff --git a/erts/doc/src/crash_dump.xml b/erts/doc/src/crash_dump.xml
index 61c9159823..a9aeb1888c 100644
--- a/erts/doc/src/crash_dump.xml
+++ b/erts/doc/src/crash_dump.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1999</year><year>2013</year>
+ <year>1999</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -22,7 +22,7 @@
</legalnotice>
- <title>How to interpret the Erlang crash dumps</title>
+ <title>How to Interpret the Erlang Crash Dumps</title>
<prepared>Patrik Nyblom</prepared>
<responsible></responsible>
<docno></docno>
@@ -32,401 +32,529 @@
<rev>PA1</rev>
<file>crash_dump.xml</file>
</header>
- <p>This document describes the <c><![CDATA[erl_crash.dump]]></c> file generated
- upon abnormal exit of the Erlang runtime system.</p>
- <p><em>Important:</em> For OTP release R9C the Erlang crash dump has
- had a major facelift. This means that the information in this
- document will not be directly applicable for older dumps. However,
- if you use the Crashdump Viewer tool on older dumps, the crash
- dumps are translated into a format similar to this.</p>
- <p>The system will write the crash dump in the current directory of
+ <p>This section describes the <c><![CDATA[erl_crash.dump]]></c> file
+ generated upon abnormal exit of the Erlang runtime system.</p>
+
+ <note>
+ <p>The Erlang crash dump had a major facelift in Erlang/OTP R9C. The
+ information in this section is therefore not directly applicable for
+ older dumps. However, if you use <seealso marker="observer:crashdump_viewer">
+ <c>crashdump_viewer(3)</c></seealso> on older dumps,
+ the crash dumps are translated into a format similar to this.</p>
+ </note>
+
+ <p>The system writes the crash dump in the current directory of
the emulator or in the file pointed out by the environment variable
(whatever that means on the current operating system)
- ERL_CRASH_DUMP. For a crash dump to be written, there has to be a
- writable file system mounted.</p>
+ <c>ERL_CRASH_DUMP</c>. For a crash dump to be written, a
+ writable file system must be mounted.</p>
+
<p>Crash dumps are written mainly for one of two reasons: either the
- builtin function <c><![CDATA[erlang:halt/1]]></c> is called explicitly with a
- string argument from running Erlang code, or else the runtime
+ built-in function <c><![CDATA[erlang:halt/1]]></c> is called explicitly
+ with a string argument from running Erlang code, or the runtime
system has detected an error that cannot be handled. The most
- usual reason that the system can't handle the error is that the
+ usual reason that the system cannot handle the error is that the
cause is external limitations, such as running out of memory. A
- crash dump due to an internal error may be caused by the system
+ crash dump caused by an internal error can be caused by the system
reaching limits in the emulator itself (like the number of atoms
- in the system, or too many simultaneous ets tables). Usually the
+ in the system, or too many simultaneous ETS tables). Usually the
emulator or the operating system can be reconfigured to avoid the
crash, which is why interpreting the crash dump correctly is
important.</p>
+
<p>On systems that support OS signals, it is also possible to stop
- the runtime system and generate a crash dump by sending the SIGUSR1.</p>
- <p>The erlang crash dump is a readable text file, but it might not be
- very easy to read. Using the Crashdump Viewer tool in the
- <c><![CDATA[observer]]></c> application will simplify the task. This is an
- wx-widget based tool for browsing Erlang crash dumps.</p>
+ the runtime system and generate a crash dump by sending the <c>SIGUSR1</c>
+ signal.</p>
+
+ <p>The Erlang crash dump is a readable text file, but it can be difficult
+ to read. Using the Crashdump Viewer tool in the
+ <c><![CDATA[Observer]]></c> application simplifies the task. This is a
+ wx-widget-based tool for browsing Erlang crash dumps.</p>
<section>
<marker id="general_info"></marker>
- <title>General information</title>
- <p>The first part of the dump shows the creation time for the dump,
- a slogan indicating the reason for the dump, the system version,
- of the node from which the dump originates, the compile time of
- the emulator running the originating node, the number of
- atoms in the atom table and the runtime system thread that caused
- the crash dump to happen.
- </p>
+ <title>General Information</title>
+ <p>The first part of the crash dump shows the following:</p>
+
+ <list type="bulleted">
+ <item>The creation time for the dump</item>
+ <item>A slogan indicating the reason for the dump</item>
+ <item>The system version of the node from which the dump originates</item>
+ <item>The compile time of the emulator running the originating node</item>
+ <item>The number of atoms in the atom table</item>
+ <item>The runtime system thread that caused the crash dump</item>
+ </list>
<section>
- <title>Reasons for crash dumps (slogan)</title>
- <p>The reason for the dump is noted in the beginning of the file
- as <em>Slogan: &lt;reason&gt;</em> (the word "slogan" has historical
- roots). If the system is halted by the BIF
+ <title>Reasons for Crash Dumps (Slogan)</title>
+ <p>The reason for the dump is shown in the beginning of the file as:</p>
+
+ <pre>
+Slogan: &lt;reason&gt;</pre>
+
+ <p>If the system is halted by the BIF
<c><![CDATA[erlang:halt/1]]></c>, the slogan is the string parameter
passed to the BIF, otherwise it is a description generated by
the emulator or the (Erlang) kernel. Normally the message
- should be enough to understand the problem, but nevertheless
- some messages are described here. Note however that the
- suggested reasons for the crash are <em>only suggestions</em>. The exact reasons for the errors may vary
+ is enough to understand the problem, but
+ some messages are described here. Notice that the
+ suggested reasons for the crash are <em>only suggestions</em>.
+ The exact reasons for the errors can vary
depending on the local applications and the underlying
operating system.</p>
- <list type="bulleted">
- <item>"<em>&lt;A&gt;</em>: Cannot allocate <em>&lt;N&gt;</em>
- bytes of memory (of type "<em>&lt;T&gt;</em>")." - The system
- has run out of memory. &lt;A&gt; is the allocator that failed
- to allocate memory, &lt;N&gt; is the number of bytes that
- &lt;A&gt; tried to allocate, and &lt;T&gt; is the memory block
- type that the memory was needed for. The most common case is
- that a process stores huge amounts of data. In this case
- &lt;T&gt; is most often <c><![CDATA[heap]]></c>, <c><![CDATA[old_heap]]></c>,
- <c><![CDATA[heap_frag]]></c>, or <c><![CDATA[binary]]></c>. For more information on
- allocators see
- <seealso marker="erts_alloc">erts_alloc(3)</seealso>.</item>
- <item>"<em>&lt;A&gt;</em>: Cannot reallocate <em>&lt;N&gt;</em>
- bytes of memory (of type "<em>&lt;T&gt;</em>")." - Same as
- above with the exception that memory was being reallocated
- instead of being allocated when the system ran out of memory.</item>
- <item>"Unexpected op code <em>N</em>" - Error in compiled
- code, <c><![CDATA[beam]]></c> file damaged or error in the compiler.</item>
- <item>"Module <em>Name</em> undefined" <c><![CDATA[|]]></c> "Function
- <em>Name</em> undefined" <c><![CDATA[|]]></c> "No function
- <em>Name</em>:<em>Name</em>/1" <c><![CDATA[|]]></c> "No function
- <em>Name</em>:start/2" - The kernel/stdlib applications are
- damaged or the start script is damaged.</item>
- <item>"Driver_select called with too large file descriptor
- <c><![CDATA[N]]></c>" - The number of file descriptors for sockets
- exceed 1024 (Unix only). The limit on file-descriptors in
- some Unix flavors can be set to over 1024, but only 1024
- sockets/pipes can be used simultaneously by Erlang (due to
- limitations in the Unix <c><![CDATA[select]]></c> call). The number of
- open regular files is not affected by this.</item>
- <item>"Received SIGUSR1" - Sending the SIGUSR1 signal to a
- Erlang machine (Unix only) forces a crash dump. This slogan reflects
- that the Erlang machine crash-dumped due to receiving that signal.</item>
- <item>"Kernel pid terminated (<em>Who</em>)
- (<em>Exit-reason</em>)" - The kernel supervisor has detected
- a failure, usually that the <c><![CDATA[application_controller]]></c>
- has shut down (<c><![CDATA[Who]]></c> = <c><![CDATA[application_controller]]></c>,
- <c><![CDATA[Why]]></c> = <c><![CDATA[shutdown]]></c>). The application controller
- may have shut down for a number of reasons, the most usual
- being that the node name of the distributed Erlang node is
- already in use. A complete supervisor tree "crash" (i.e.,
- the top supervisors have exited) will give about the same
- result. This message comes from the Erlang code and not from
- the virtual machine itself. It is always due to some kind of
- failure in an application, either within OTP or a
- "user-written" one. Looking at the error log for your
- application is probably the first step to take.</item>
- <item>"Init terminating in do_boot ()" - The primitive Erlang boot
- sequence was terminated, most probably because the boot
- script has errors or cannot be read. This is usually a
- configuration error - the system may have been started with
- a faulty <c><![CDATA[-boot]]></c> parameter or with a boot script from
- the wrong version of OTP.</item>
- <item>"Could not start kernel pid (<em>Who</em>) ()" - One of the
- kernel processes could not start. This is probably due to
- faulty arguments (like errors in a <c><![CDATA[-config]]></c> argument)
- or faulty configuration files. Check that all files are in
- their correct location and that the configuration files (if
- any) are not damaged. Usually there are also messages
- written to the controlling terminal and/or the error log
- explaining what's wrong.</item>
- </list>
- <p>Other errors than the ones mentioned above may occur, as the
- <c><![CDATA[erlang:halt/1]]></c> BIF may generate any message. If the
+
+ <taglist>
+ <tag><em>&lt;A&gt;: Cannot allocate &lt;N&gt; bytes of memory (of type
+ "&lt;T&gt;")</em></tag>
+ <item>
+ <p>The system has run out of memory. &lt;A&gt; is the allocator that
+ failed to allocate memory, &lt;N&gt; is the number of bytes that
+ &lt;A&gt; tried to allocate, and &lt;T&gt; is the memory block
+ type that the memory was needed for. The most common case is
+ that a process stores huge amounts of data. In this case
+ &lt;T&gt; is most often <c><![CDATA[heap]]></c>,
+ <c><![CDATA[old_heap]]></c>, <c><![CDATA[heap_frag]]></c>, or
+ <c><![CDATA[binary]]></c>. For more information on allocators, see
+ <seealso marker="erts_alloc"><c>erts_alloc(3)</c></seealso>.</p>
+ </item>
+ <tag><em>&lt;A&gt;: Cannot reallocate &lt;N&gt; bytes of memory (of
+ type "&lt;T&gt;")</em></tag>
+ <item>
+ <p>Same as above except that memory was reallocated
+ instead of allocated when the system ran out of memory.</p>
+ </item>
+ <tag><em>Unexpected op code &lt;N&gt;</em></tag>
+ <item>
+ <p>Error in compiled code, <c><![CDATA[beam]]></c> file damaged, or
+ error in the compiler.</p>
+ </item>
+ <tag><em>Module &lt;Name&gt; undefined <c><![CDATA[|]]></c> Function
+ &lt;Name&gt; undefined <c><![CDATA[|]]></c> No function
+ &lt;Name&gt;:&lt;Name&gt;/1 <c><![CDATA[|]]></c> No function
+ &lt;Name&gt;:start/2</em></tag>
+ <item>
+ <p>The Kernel/STDLIB applications are
+ damaged or the start script is damaged.</p>
+ </item>
+ <tag><em>Driver_select called with too large file descriptor
+ <c><![CDATA[N]]></c></em></tag>
+ <item>
+ <p>The number of file descriptors for sockets
+ exceeds 1024 (Unix only). The limit on file descriptors in
+ some Unix flavors can be set to over 1024, but only 1024
+ sockets/pipes can be used simultaneously by Erlang (because of
+ limitations in the Unix <c><![CDATA[select]]></c> call). The number
+ of open regular files is not affected by this.</p>
+ </item>
+ <tag><em>Received SIGUSR1</em></tag>
+ <item>
+ <p>Sending the <c>SIGUSR1</c> signal to an Erlang machine (Unix only)
+ forces a crash dump. This slogan reflects that the Erlang machine
+ crash-dumped because of receiving that signal.</p>
+ </item>
+ <tag><em>Kernel pid terminated (&lt;Who&gt;) (&lt;Exit
+ reason&gt;)</em></tag>
+ <item>
+ <p>The kernel supervisor has detected a failure, usually that the
+ <c><![CDATA[application_controller]]></c> has shut down
+ (<c><![CDATA[Who]]></c> = <c><![CDATA[application_controller]]></c>,
+ <c><![CDATA[Why]]></c> = <c><![CDATA[shutdown]]></c>).
+ The application controller
+ can have shut down for many reasons, the most usual
+ is that the node name of the distributed Erlang node is
+ already in use. A complete supervisor tree "crash" (that is,
+ the top supervisors have exited) gives about the same
+ result. This message comes from the Erlang code and not from
+ the virtual machine itself. It is always because of some
+ failure in an application, either within OTP or a
+ "user-written" one. Looking at the error log for your
+ application is probably the first step to take.</p>
+ </item>
+ <tag><em>Init terminating in do_boot ()</em></tag>
+ <item>
+ <p>The primitive Erlang boot sequence was terminated, most probably
+ because the boot script has errors or cannot be read. This is
+ usually a configuration error; the system can have been started
+ with a faulty <c><![CDATA[-boot]]></c> parameter or with a boot
+ script from the wrong OTP version.</p>
+ </item>
+ <tag><em>Could not start kernel pid (&lt;Who&gt;) ()</em></tag>
+ <item>
+ <p>One of the kernel processes could not start. This is probably
+ because of faulty arguments (like errors in a
+ <c><![CDATA[-config]]></c> argument)
+ or faulty configuration files. Check that all files are in
+ their correct location and that the configuration files (if
+ any) are not damaged. Usually messages are also
+ written to the controlling terminal and/or the error log
+ explaining what is wrong.</p>
+ </item>
+ </taglist>
+
+ <p>Other errors than these can occur, as the
+ <c><![CDATA[erlang:halt/1]]></c> BIF can generate any message. If the
message is not generated by the BIF and does not occur in the
- list above, it may be due to an error in the emulator. There
- may however be unusual messages that I haven't mentioned, that
- still are connected to an application failure. There is a lot
- more information available, so more thorough reading of the
- crash dump may reveal the crash reason. The size of processes,
- the number of ets tables and the Erlang data on each process
- stack can be useful for tracking down the problem.</p>
+ list above, it can be because of an error in the emulator. There
+ can however be unusual messages, not mentioned here, which
+ are still connected to an application failure. There is much
+ more information available, so a thorough reading of the
+ crash dump can reveal the crash reason. The size of processes,
+ the number of ETS tables, and the Erlang data on each process
+ stack can be useful to find the problem.</p>
</section>
<section>
- <title>Number of atoms</title>
+ <title>Number of Atoms</title>
<p>The number of atoms in the system at the time of the crash is
shown as <em>Atoms: &lt;number&gt;</em>. Some ten thousands atoms is
- perfectly normal, but more could indicate that the BIF
- <c><![CDATA[erlang:list_to_atom/1]]></c> is used to dynamically generate a
- lot of <em>different</em> atoms, which is never a good idea.</p>
+ perfectly normal, but more can indicate that the BIF
+ <c><![CDATA[erlang:list_to_atom/1]]></c> is used to generate many
+ <em>different</em> atoms dynamically, which is never a good idea.</p>
</section>
</section>
<section>
<marker id="scheduler"></marker>
- <title>Scheduler information</title>
- <p>Under the tag <em>=scheduler</em> information about the current state
- and statistics of the schedulers in the runtime system is displayed.
- On OSs that do allow instant suspension of other threads, the data within
- this section will reflect what the runtime system looks like at the moment
- when the crash happens.</p>
+ <title>Scheduler Information</title>
+ <p>Under the tag <em>=scheduler</em> is shown information about the current
+ state and statistics of the schedulers in the runtime system. On
+ operating systems that allow suspension of other threads, the
+ data within this section reflects what the runtime system looks like
+ when a crash occurs.</p>
+
<p>The following fields can exist for a process:</p>
+
<taglist>
<tag><em>=scheduler:id</em></tag>
- <item>Header, states the scheduler identifier.</item>
+ <item>
+ <p>Heading. States the scheduler identifier.</p>
+ </item>
<tag><em>Scheduler Sleep Info Flags</em></tag>
- <item>If empty the scheduler was doing some work.
- If not empty the scheduler is either in some state of sleep,
- or suspended. This entry is only present in a SMP enabled emulator</item>
+ <item>
+ <p>If empty, the scheduler was doing some work.
+ If not empty, the scheduler is either in some state of sleep,
+ or suspended. This entry is only present in an SMP-enabled
+ emulator.</p>
+ </item>
<tag><em>Scheduler Sleep Info Aux Work</em></tag>
- <item>If not empty, a scheduler internal auxiliary work is scheduled
- to be done.</item>
+ <item>
+ <p>If not empty, a scheduler internal auxiliary work is scheduled
+ to be done.</p>
+ </item>
<tag><em>Current Port</em></tag>
- <item>The port identifier of the port that is currently being
- executed by the scheduler.</item>
+ <item>
+ <p>The port identifier of the port that is currently
+ executed by the scheduler.</p>
+ </item>
<tag><em>Current Process</em></tag>
- <item>The process identifier of the process that is currently being
- executed by the scheduler. If there is such a process this entry is
- followed by the <em>State</em>,<em>Internal State</em>,
- <em>Program Counter</em>, <em>CP</em> of that same process. See
- <seealso marker="#processes">Process Information</seealso> for a
- description what the different entries mean. Keep in mind that
- this is a snapshot of what the entries are exactly when the crash
- dump is starting to be generated. Therefore they will most likely
- be different (and more telling) then the entries for the same
- processes found in the <em>=proc</em> section. If there is no currently
- running process, only the <em>Current Process</em> entry will be printed.
+ <item>
+ <p>The process identifier of the process that is currently
+ executed by the scheduler. If there is such a process, this entry is
+ followed by the <em>State</em>, <em>Internal State</em>,
+ <em>Program Counter</em>, and <em>CP</em> of that same process.
+ The entries are described in section
+ <seealso marker="#processes">Process Information</seealso>.</p>
+ <p>Notice that this is a snapshot of what the entries are exactly when
+ the crash dump is starting to be generated. Therefore they are most
+ likely different (and more telling) than the entries for the same
+ processes found in the <em>=proc</em> section. If there is no
+ currently running process, only the <em>Current Process</em> entry is
+ shown.</p>
</item>
<tag><em>Current Process Limited Stack Trace</em></tag>
- <item>This entry only shows up if there is a current process. It is very
- similar to <seealso marker="#proc_data"><em>=proc_stack</em></seealso>,
- except that only the function frames are printed (i.e. the stack variables
- are omited). It is also limited to only print the top and bottom part
- of the stack. If the stack is small (less that 512 slots) then the
- entire stack will be printed. If not, an entry stating
- <code>skipping ## slots</code> will be printed where ## is
- replaced by the number of slots that has been skipped.</item>
+ <item>
+ <p>This entry is shown only if there is a current process. It is
+ similar to <seealso marker="#proc_data">
+ <em>=proc_stack</em></seealso>, except that only the function frames
+ are shown (that is, the stack variables are omitted).
+ Also, only the top and bottom part of the stack are shown. If the
+ stack is small (&lt; 512 slots), the entire stack is shown. Otherwise
+ the entry <em>skipping ## slots</em> is shown, where <c>##</c>
+ is replaced by the number of slots that has been skipped.</p>
+ </item>
<tag><em>Run Queue</em></tag>
- <item>Displays statistics about how many processes and ports
- of different priorities are scheduled on this scheduler.</item>
+ <item>
+ <p>Shows statistics about how many processes and ports
+ of different priorities are scheduled on this scheduler.</p>
+ </item>
<tag><em>** crashed **</em></tag>
- <item>This entry is normally not printed. It signifies that getting
- the rest of the information about this scheduler failed for some reason.
+ <item>
+ <p>This entry is normally not shown. It signifies that getting the rest
+ of the information about this scheduler failed for some reason.</p>
</item>
</taglist>
</section>
<section>
<marker id="memory"></marker>
- <title>Memory information</title>
- <p>Under the tag <em>=memory</em> you will find information similar
- to what you can obtain on a living node with
- <seealso marker="erts:erlang#erlang:memory/0">erlang:memory()</seealso>.</p>
+ <title>Memory Information</title>
+ <p>Under the tag <em>=memory</em> is shown information similar
+ to what can be obtainted on a living node with
+ <seealso marker="erts:erlang#erlang:memory/0">
+ <c>erlang:memory()</c></seealso>.</p>
</section>
<section>
<marker id="internal_tables"></marker>
- <title>Internal table information</title>
- <p>The tags <em>=hash_table:&lt;table_name&gt;</em> and
- <em>=index_table:&lt;table_name&gt;</em> presents internal
- tables. These are mostly of interest for runtime system
- developers.</p>
+ <title>Internal Table Information</title>
+ <p>Under the tags <em>=hash_table:&lt;table_name&gt;</em> and
+ <em>=index_table:&lt;table_name&gt;</em> is shown internal
+ tables. These are mostly of interest for runtime system developers.</p>
</section>
<section>
<marker id="allocated_areas"></marker>
- <title>Allocated areas</title>
- <p>Under the tag <em>=allocated_areas</em> you will find information
- similar to what you can obtain on a living node with
- <seealso marker="erts:erlang#system_info_allocated_areas">erlang:system_info(allocated_areas)</seealso>.</p>
+ <title>Allocated Areas</title>
+ <p>Under the tag <em>=allocated_areas</em> is shown information
+ similar to what can be obtained on a living node with
+ <seealso marker="erts:erlang#system_info_allocated_areas">
+ <c>erlang:system_info(allocated_areas)</c></seealso>.</p>
</section>
<section>
<marker id="allocator"></marker>
<title>Allocator</title>
- <p>Under the tag <em>=allocator:&lt;A&gt;</em> you will find
+ <p>Under the tag <em>=allocator:&lt;A&gt;</em> is shown
various information about allocator &lt;A&gt;. The information
- is similar to what you can obtain on a living node with
- <seealso marker="erts:erlang#system_info_allocator_tuple">erlang:system_info({allocator, &lt;A&gt;})</seealso>.
- For more information see the documentation of
- <seealso marker="erts:erlang#system_info_allocator_tuple">erlang:system_info({allocator, &lt;A&gt;})</seealso>,
- and the
- <seealso marker="erts_alloc">erts_alloc(3)</seealso>
- documentation.</p>
+ is similar to what can be obtained on a living node with
+ <seealso marker="erts:erlang#system_info_allocator_tuple">
+ <c>erlang:system_info({allocator, &lt;A&gt;})</c></seealso>.
+ For more information, see also
+ <seealso marker="erts_alloc"><c>erts_alloc(3)</c></seealso>.</p>
</section>
<section>
<marker id="processes"></marker>
- <title>Process information</title>
+ <title>Process Information</title>
<p>The Erlang crashdump contains a listing of each living Erlang
- process in the system. The process information for one process
- may look like this (line numbers have been added):
- </p>
- <p>The following fields can exist for a process:</p>
+ process in the system. The following fields can exist for a process:</p>
+
<taglist>
<tag><em>=proc:&lt;pid&gt;</em></tag>
- <item>Heading, states the process identifier</item>
+ <item>
+ <p>Heading. States the process identifier.</p>
+ </item>
<tag><em>State</em></tag>
<item>
<p>The state of the process. This can be one of the following:</p>
- <list type="bulleted">
- <item><em>Scheduled</em> - The process was scheduled to run
- but not currently running ("in the run queue").</item>
- <item><em>Waiting</em> - The process was waiting for
- something (in <c><![CDATA[receive]]></c>).</item>
- <item><em>Running</em> - The process was currently
- running. If the BIF <c><![CDATA[erlang:halt/1]]></c> was called, this was
- the process calling it.</item>
- <item><em>Exiting</em> - The process was on its way to
- exit.</item>
- <item><em>Garbing</em> - This is bad luck, the process was
- garbage collecting when the crash dump was written, the rest
- of the information for this process is limited.</item>
- <item><em>Suspended</em> - The process is suspended, either
- by the BIF <c><![CDATA[erlang:suspend_process/1]]></c> or because it is
- trying to write to a busy port.</item>
- </list>
+ <taglist>
+ <tag><em>Scheduled</em></tag>
+ <item>The process was scheduled to run
+ but is currently not running ("in the run queue").</item>
+ <tag><em>Waiting</em></tag>
+ <item>The process was waiting for
+ something (in <c><![CDATA[receive]]></c>).</item>
+ <tag><em>Running</em></tag>
+ <item>The process was currently running.
+ If the BIF <c><![CDATA[erlang:halt/1]]></c> was called, this was
+ the process calling it.</item>
+ <tag><em>Exiting</em></tag>
+ <item>The process was on its way to exit.</item>
+ <tag><em>Garbing</em></tag>
+ <item>This is bad luck, the process was
+ garbage collecting when the crash dump was written. The rest
+ of the information for this process is limited.</item>
+ <tag><em>Suspended</em></tag>
+ <item>The process is suspended, either
+ by the BIF <c><![CDATA[erlang:suspend_process/1]]></c> or because
+ it tries to write to a busy port.</item>
+ </taglist>
</item>
<tag><em>Registered name</em></tag>
- <item>The registered name of the process, if any.</item>
+ <item>
+ <p>The registered name of the process, if any.</p>
+ </item>
<tag><em>Spawned as</em></tag>
- <item>The entry point of the process, i.e., what function was
- referenced in the <c><![CDATA[spawn]]></c> or <c><![CDATA[spawn_link]]></c> call that
- started the process.</item>
+ <item>
+ <p>The entry point of the process, that is, what function was
+ referenced in the <c><![CDATA[spawn]]></c> or
+ <c><![CDATA[spawn_link]]></c> call that
+ started the process.</p>
+ </item>
<tag><em>Last scheduled in for | Current call</em></tag>
- <item>The current function of the process. These fields will not
- always exist.</item>
+ <item>
+ <p>The current function of the process. These fields do not
+ always exist.</p>
+ </item>
<tag><em>Spawned by</em></tag>
- <item>The parent of the process, i.e. the process which executed
- <c><![CDATA[spawn]]></c> or <c><![CDATA[spawn_link]]></c>.</item>
+ <item>
+ <p>The parent of the process, that is, the process that executed
+ <c><![CDATA[spawn]]></c> or <c><![CDATA[spawn_link]]></c>.</p>
+ </item>
<tag><em>Started</em></tag>
- <item>The date and time when the process was started.</item>
+ <item>
+ <p>The date and time when the process was started.</p>
+ </item>
<tag><em>Message queue length</em></tag>
- <item>The number of messages in the process' message queue.</item>
+ <item>
+ <p>The number of messages in the process' message queue.</p>
+ </item>
<tag><em>Number of heap fragments</em></tag>
- <item>The number of allocated heap fragments.</item>
+ <item>
+ <p>The number of allocated heap fragments.</p>
+ </item>
<tag><em>Heap fragment data</em></tag>
- <item>Size of fragmented heap data. This is data either created by
- messages being sent to the process or by the Erlang BIFs. This
- amount depends on so many things that this field is utterly
- uninteresting.</item>
+ <item>
+ <p>Size of fragmented heap data. This is data either created by
+ messages sent to the process or by the Erlang BIFs. This
+ amount depends on so many things that this field is utterly
+ uninteresting.</p>
+ </item>
<tag><em>Link list</em></tag>
- <item>Process id's of processes linked to this one. May also contain
- ports. If process monitoring is used, this field also tells in
- which direction the monitoring is in effect, i.e., a link
- being "to" a process tells you that the "current" process was
- monitoring the other and a link "from" a process tells you
- that the other process was monitoring the current one.</item>
+ <item>
+ <p>Process IDs of processes linked to this one. Can also contain
+ ports. If process monitoring is used, this field also tells in
+ which direction the monitoring is in effect. That is, a link
+ "to" a process tells you that the "current" process was
+ monitoring the other, and a link "from" a process tells you
+ that the other process was monitoring the current one.</p>
+ </item>
<tag><em>Reductions</em></tag>
- <item>The number of reductions consumed by the process.</item>
+ <item>
+ <p>The number of reductions consumed by the process.</p>
+ </item>
<tag><em>Stack+heap</em></tag>
- <item>The size of the stack and heap (they share memory segment)</item>
+ <item>
+ <p>The size of the stack and heap (they share memory segment).</p>
+ </item>
<tag><em>OldHeap</em></tag>
- <item>The size of the "old heap". The Erlang virtual machine uses
- generational garbage collection with two generations. There is
- one heap for new data items and one for the data that have
- survived two garbage collections. The assumption (which is
- almost always correct) is that data that survive two garbage
- collections can be "tenured" to a heap more seldom garbage
- collected, as they will live for a long period. This is a
- quite usual technique in virtual machines. The sum of the
- heaps and stack together constitute most of the process's
- allocated memory.</item>
+ <item>
+ <p>The size of the "old heap". The Erlang virtual machine uses
+ generational garbage collection with two generations. There is
+ one heap for new data items and one for the data that has
+ survived two garbage collections. The assumption (which is
+ almost always correct) is that data surviving two garbage
+ collections can be "tenured" to a heap more seldom garbage
+ collected, as they will live for a long period. This is a
+ usual technique in virtual machines. The sum of the
+ heaps and stack together constitute most of the
+ allocated memory of the process.</p>
+ </item>
<tag><em>Heap unused, OldHeap unused</em></tag>
- <item>The amount of unused memory on each heap. This information is
- usually useless.</item>
- <tag><em>Stack</em></tag>
- <item>If the system uses shared heap, the fields
- <em>Stack+heap</em>, <em>OldHeap</em>, <em>Heap unused</em>
- and <em>OldHeap unused</em> do not exist. Instead this field
- presents the size of the process' stack.</item>
+ <item>
+ <p>The amount of unused memory on each heap. This information is
+ usually useless.</p>
+ </item>
<tag><em>Memory</em></tag>
- <item>The total memory used by this process. This includes call stack,
- heap and internal structures. Same as <seealso marker="erlang#process_info-2">erlang:process_info(Pid,memory)</seealso>.
+ <item>
+ <p>The total memory used by this process. This includes call stack,
+ heap, and internal structures. Same as
+ <seealso marker="erlang#process_info-2">
+ <c>erlang:process_info(Pid,memory)</c></seealso>.</p>
</item>
<tag><em>Program counter</em></tag>
- <item>The current instruction pointer. This is only interesting for
- runtime system developers. The function into which the program
- counter points is the current function of the process.</item>
+ <item>
+ <p>The current instruction pointer. This is only of interest for
+ runtime system developers. The function into which the program
+ counter points is the current function of the process.</p>
+ </item>
<tag><em>CP</em></tag>
- <item>The continuation pointer, i.e. the return address for the
- current call. Usually useless for other than runtime system
- developers. This may be followed by the function into which
- the CP points, which is the function calling the current
- function.</item>
+ <item>
+ <p>The continuation pointer, that is, the return address for the
+ current call. Usually useless for other than runtime system
+ developers. This can be followed by the function into which
+ the CP points, which is the function calling the current
+ function.</p>
+ </item>
<tag><em>Arity</em></tag>
- <item>The number of live argument registers. The argument registers,
- if any are live, will follow. These may contain the arguments
- of the function if they are not yet moved to the stack.</item>
- <item><em>Internal State</em></item>
- <item>A more detailed internal represantation of the state of
- this process.</item>
+ <item>
+ <p>The number of live argument registers. The argument registers
+ if any are live will follow. These can contain the arguments
+ of the function if they are not yet moved to the stack.</p>
+ </item>
+ <tag><em>Internal State</em></tag>
+ <item>
+ <p>A more detailed internal representation of the state of
+ this process.</p>
+ </item>
</taglist>
- <p>See also the section about <seealso marker="#proc_data">process data</seealso>.</p>
+ <p>See also section <seealso marker="#proc_data">Process Data</seealso>.</p>
</section>
<section>
<marker id="ports"></marker>
- <title>Port information</title>
+ <title>Port Information</title>
<p>This section lists the open ports, their owners, any linked
- processed, and the name of their driver or external process.</p>
+ processes, and the name of their driver or external process.</p>
</section>
<section>
<marker id="ets_tables"></marker>
- <title>ETS tables</title>
+ <title>ETS Tables</title>
<p>This section contains information about all the ETS tables in
- the system. The following fields are interesting for each table:</p>
+ the system. The following fields are of interest for each table:</p>
+
<taglist>
<tag><em>=ets:&lt;owner&gt;</em></tag>
- <item>Heading, states the owner of the table (a process identifier)</item>
+ <item>
+ <p>Heading. States the table owner (a process identifier).</p>
+ </item>
<tag><em>Table</em></tag>
- <item>The identifier for the table. If the table is a
- <c><![CDATA[named_table]]></c>, this is the name.</item>
+ <item>
+ <p>The identifier for the table. If the table is a
+ <c><![CDATA[named_table]]></c>, this is the name.</p>
+ </item>
<tag><em>Name</em></tag>
- <item>The name of the table, regardless of whether it is a
- <c><![CDATA[named_table]]></c> or not.</item>
+ <item>
+ <p>The table name, regardless of if it is a
+ <c><![CDATA[named_table]]></c> or not.</p>
+ </item>
<tag><em>Hash table, Buckets</em></tag>
- <item>This occurs if the table is a hash table, i.e. if it is not an
- <c><![CDATA[ordered_set]]></c>.</item>
+ <item>
+ <p>If the table is a hash table, that is, if it is not an
+ <c><![CDATA[ordered_set]]></c>.</p>
+ </item>
<tag><em>Hash table, Chain Length</em></tag>
- <item>Only applicable for hash tables. Contains statistics about the
- hash table, such as the max, min and avg chain length. Having a max much
- larger than the avg, and a std dev much larger that
- the expected std dev is a sign that the hashing of the terms is
- behaving badly for some reason.</item>
+ <item>
+ <p>If the table is a hash table. Contains statistics about the
+ table, such as the maximum, minimum, and average chain length.
+ Having a maximum much larger than the average, and a standard
+ deviation much larger than the expected standard deviation is
+ a sign that the hashing of the terms
+ behaves badly for some reason.</p>
+ </item>
<tag><em>Ordered set (AVL tree), Elements</em></tag>
- <item>This occurs only if the table is an <c><![CDATA[ordered_set]]></c>. (The
- number of elements is the same as the number of objects in the
- table.)</item>
+ <item>
+ <p>If the table is an <c><![CDATA[ordered_set]]></c>. (The
+ number of elements is the same as the number of objects in the
+ table.)</p>
+ </item>
<tag><em>Fixed</em></tag>
- <item>If the table is fixed using ets:safe_fixtable or some internal
- mechanism.</item>
+ <item>
+ <p>If the table is fixed using
+ <seealso marker="stdlib:ets#safe_fixtable/2">
+ <c>ets:safe_fixtable/2</c></seealso> or some internal mechanism.</p>
+ </item>
<tag><em>Objects</em></tag>
- <item>The number of objects in the table</item>
+ <item>
+ <p>The number of objects in the table.</p>
+ </item>
<tag><em>Words</em></tag>
- <item>The number of words (usually 4 bytes/word) allocated to data
- in the table.</item>
+ <item>
+ <p>The number of words (usually 4 bytes/word) allocated to data
+ in the table.</p>
+ </item>
<tag><em>Type</em></tag>
- <item>The type of the table, i.e. <c>set</c>, <c>bag</c>,
- <c>dublicate_bag</c> or <c>ordered_set</c>.</item>
+ <item>
+ <p>The table type, that is, <c>set</c>, <c>bag</c>,
+ <c>dublicate_bag</c>, or <c>ordered_set</c>.</p>
+ </item>
<tag><em>Compressed</em></tag>
- <item>If this table was compressed.</item>
+ <item>
+ <p>If the table was compressed.</p>
+ </item>
<tag><em>Protection</em></tag>
- <item>The protection of this table.</item>
+ <item>
+ <p>The protection of the table.</p>
+ </item>
<tag><em>Write Concurrency</em></tag>
- <item>If write_concurrency was enabled for this table.</item>
+ <item>
+ <p>If <c>write_concurrency</c> was enabled for the table.</p>
+ </item>
<tag><em>Read Concurrency</em></tag>
- <item>If read_concurrency was enabled for this table.</item>
+ <item>
+ <p>If <c>read_concurrency</c> was enabled for the table.</p>
+ </item>
</taglist>
</section>
@@ -435,167 +563,252 @@
<title>Timers</title>
<p>This section contains information about all the timers started
with the BIFs <c><![CDATA[erlang:start_timer/3]]></c> and
- <c><![CDATA[erlang:send_after/3]]></c>. The following fields exists for each
- timer:</p>
+ <c><![CDATA[erlang:send_after/3]]></c>. The following fields exist
+ for each timer:</p>
+
<taglist>
<tag><em>=timer:&lt;owner&gt;</em></tag>
- <item>Heading, states the owner of the timer (a process identifier)
- i.e. the process to receive the message when the timer
- expires.</item>
+ <item>
+ <p>Heading. States the timer owner (a process identifier),
+ that is, the process to receive the message when the timer
+ expires.</p>
+ </item>
<tag><em>Message</em></tag>
- <item>The message to be sent.</item>
+ <item>
+ <p>The message to be sent.</p>
+ </item>
<tag><em>Time left</em></tag>
- <item>Number of milliseconds left until the message would have been
- sent.</item>
+ <item>
+ <p>Number of milliseconds left until the message would have been
+ sent.</p>
+ </item>
</taglist>
</section>
<section>
<marker id="distribution_info"></marker>
- <title>Distribution information</title>
- <p>If the Erlang node was alive, i.e., set up for communicating
+ <title>Distribution Information</title>
+ <p>If the Erlang node was alive, that is, set up for communicating
with other nodes, this section lists the connections that were
active. The following fields can exist:</p>
+
<taglist>
<tag><em>=node:&lt;node_name&gt;</em></tag>
- <item>The name of the node</item>
+ <item>
+ <p>The node name.</p>
+ </item>
<tag><em>no_distribution</em></tag>
- <item>This will only occur if the node was not distributed.</item>
+ <item>
+ <p>If the node was not distributed.</p>
+ </item>
<tag><em>=visible_node:&lt;channel&gt;</em></tag>
- <item>Heading for a visible nodes, i.e. an alive node with a
- connection to the node that crashed. States the channel number
- for the node.</item>
+ <item>
+ <p>Heading for a visible node, that is, an alive node with a
+ connection to the node that crashed. States the channel number
+ for the node.</p>
+ </item>
<tag><em>=hidden_node:&lt;channel&gt;</em></tag>
- <item>Heading for a hidden node. A hidden node is the same as a
- visible node, except that it is started with the "-hidden"
- flag. States the channel number for the node.</item>
+ <item>
+ <p>Heading for a hidden node. A hidden node is the same as a
+ visible node, except that it is started with the <c>"-hidden"</c>
+ flag. States the channel number for the node.</p>
+ </item>
<tag><em>=not_connected:&lt;channel&gt;</em></tag>
- <item>Heading for a node which is has been connected to the crashed
- node earlier. References (i.e. process or port identifiers)
- to the not connected node existed at the time of the crash.
- exist. States the channel number for the node.</item>
+ <item>
+ <p>Heading for a node that was connected to the crashed
+ node earlier. References (that is, process or port identifiers)
+ to the not connected node existed at the time of the crash.
+ States the channel number for the node.</p>
+ </item>
<tag><em>Name</em></tag>
- <item>The name of the remote node.</item>
+ <item>
+ <p>The name of the remote node.</p>
+ </item>
<tag><em>Controller</em></tag>
- <item>The port which controls the communication with the remote node.</item>
+ <item>
+ <p>The port controlling communication with the remote node.</p>
+ </item>
<tag><em>Creation</em></tag>
- <item>An integer (1-3) which together with the node name identifies
- a specific instance of the node.</item>
- <tag><em>Remote monitoring: &lt;local_proc&gt; &lt;remote_proc&gt;</em></tag>
- <item>The local process was monitoring the remote process at the
- time of the crash.</item>
- <tag><em>Remotely monitored by: &lt;local_proc&gt; &lt;remote_proc&gt;</em></tag>
- <item>The remote process was monitoring the local process at the
- time of the crash.</item>
+ <item>
+ <p>An integer (1-3) that together with the node name identifies
+ a specific instance of the node.</p>
+ </item>
+ <tag><em>Remote monitoring: &lt;local_proc&gt; &lt;remote_proc&gt;</em>
+ </tag>
+ <item>
+ <p>The local process was monitoring the remote process at the
+ time of the crash.</p>
+ </item>
+ <tag><em>Remotely monitored by: &lt;local_proc&gt;
+ &lt;remote_proc&gt;</em></tag>
+ <item>
+ <p>The remote process was monitoring the local process at the
+ time of the crash.</p>
+ </item>
<tag><em>Remote link: &lt;local_proc&gt; &lt;remote_proc&gt;</em></tag>
- <item>A link existed between the local process and the remote
- process at the time of the crash.</item>
+ <item>
+ <p>A link existed between the local process and the remote
+ process at the time of the crash.</p>
+ </item>
</taglist>
</section>
<section>
<marker id="loaded_modules"></marker>
- <title>Loaded module information</title>
- <p>This section contains information about all loaded modules.
- First, the memory usage by loaded code is summarized. There is
- one field for "Current code" which is code that is the current
- latest version of the modules. There is also a field for "Old
- code" which is code where there exists a newer version in the
- system, but the old version is not yet purged. The memory usage
- is in bytes.</p>
- <p>All loaded modules are then listed. The following fields exist:</p>
+ <title>Loaded Module Information</title>
+ <p>This section contains information about all loaded modules.</p>
+
+ <p>First, the memory use by the loaded code is summarized:</p>
+
+ <taglist>
+ <tag><em>Current code</em></tag>
+ <item>
+ <p>Code that is the current latest version of the modules.</p>
+ </item>
+ <tag><em>Old code</em></tag>
+ <item>
+ <p>Code where there exists a newer version in the
+ system, but the old version is not yet purged.</p>
+ </item>
+ </taglist>
+
+ <p>The memory use is in bytes.</p>
+
+ <p>Then, all loaded modules are listed. The following fields exist:</p>
+
<taglist>
<tag><em>=mod:&lt;module_name&gt;</em></tag>
- <item>Heading, and the name of the module.</item>
+ <item>
+ <p>Heading. States the module name.</p>
+ </item>
<tag><em>Current size</em></tag>
- <item>Memory usage for the loaded code in bytes</item>
+ <item>
+ <p>Memory use for the loaded code, in bytes.</p>
+ </item>
<tag><em>Old size</em></tag>
- <item>Memory usage for the old code, if any.</item>
+ <item>
+ <p>Memory use for the old code, if any.</p>
+ </item>
<tag><em>Current attributes</em></tag>
- <item>Module attributes for the current code. This field is decoded
- when looked at by the Crashdump Viewer tool.</item>
+ <item>
+ <p>Module attributes for the current code. This field is decoded
+ when looked at by the Crashdump Viewer tool.</p>
+ </item>
<tag><em>Old attributes</em></tag>
- <item>Module attributes for the old code, if any. This field is
- decoded when looked at by the Crashdump Viewer tool.</item>
+ <item>
+ <p>Module attributes for the old code, if any. This field is
+ decoded when looked at by the Crashdump Viewer tool.</p>
+ </item>
<tag><em>Current compilation info</em></tag>
- <item>Compilation information (options) for the current code. This
- field is decoded when looked at by the Crashdump Viewer tool.</item>
+ <item>
+ <p>Compilation information (options) for the current code. This
+ field is decoded when looked at by the Crashdump Viewer tool.</p>
+ </item>
<tag><em>Old compilation info</em></tag>
- <item>Compilation information (options) for the old code, if
- any. This field is decoded when looked at by the Crashdump
- Viewer tool.</item>
+ <item>
+ <p>Compilation information (options) for the old code, if
+ any. This field is decoded when looked at by the Crashdump
+ Viewer tool.</p>
+ </item>
</taglist>
</section>
<section>
<marker id="funs"></marker>
- <title>Fun information</title>
- <p>In this section, all funs are listed. The following fields exist
- for each fun:</p>
+ <title>Fun Information</title>
+ <p>This section lists all funs. The following fields exist for each fun:</p>
+
<taglist>
<tag><em>=fun</em></tag>
- <item>Heading</item>
+ <item>
+ <p>Heading.</p>
+ </item>
<tag><em>Module</em></tag>
- <item>The name of the module where the fun was defined.</item>
+ <item>
+ <p>The name of the module where the fun was defined.</p>
+ </item>
<tag><em>Uniq, Index</em></tag>
- <item>Identifiers</item>
+ <item>
+ <p>Identifiers.</p>
+ </item>
<tag><em>Address</em></tag>
- <item>The address of the fun's code.</item>
+ <item>
+ <p>The address of the fun's code.</p>
+ </item>
<tag><em>Native_address</em></tag>
- <item>The address of the fun's code when HiPE is enabled.</item>
+ <item>
+ <p>The address of the fun's code when HiPE is enabled.</p>
+ </item>
<tag><em>Refc</em></tag>
- <item>The number of references to the fun.</item>
+ <item>
+ <p>The number of references to the fun.</p>
+ </item>
</taglist>
</section>
<section>
<marker id="proc_data"></marker>
<title>Process Data</title>
- <p>For each process there will be at least one <em>=proc_stack</em>
- and one <em>=proc_heap</em> tag followed by the raw memory
+ <p>For each process there is at least one <em>=proc_stack</em>
+ and one <em>=proc_heap</em> tag, followed by the raw memory
information for the stack and heap of the process.</p>
- <p>For each process there will also be a <em>=proc_messages</em>
- tag if the process' message queue is non-empty and a
- <em>=proc_dictionary</em> tag if the process' dictionary (the
- <c><![CDATA[put/2]]></c> and <c><![CDATA[get/1]]></c> thing) is non-empty.</p>
+
+ <p>For each process there is also a <em>=proc_messages</em>
+ tag if the process message queue is non-empty, and a
+ <em>=proc_dictionary</em> tag if the process dictionary (the
+ <c><![CDATA[put/2]]></c> and <c><![CDATA[get/1]]></c> thing) is
+ non-empty.</p>
+
<p>The raw memory information can be decoded by the Crashdump
- Viewer tool. You will then be able to see the stack dump, the
- message queue (if any) and the dictionary (if any).</p>
+ Viewer tool. You can then see the stack dump, the
+ message queue (if any), and the dictionary (if any).</p>
+
<p>The stack dump is a dump of the Erlang process stack. Most of
- the live data (i.e., variables currently in use) are placed on
- the stack; thus this can be quite interesting. One has to
- "guess" what's what, but as the information is symbolic,
- thorough reading of this information can be very useful. As an
- example we can find the state variable of the Erlang primitive
- loader on line <c><![CDATA[(5)]]></c> in the example below:</p>
+ the live data (that is, variables currently in use) are placed on
+ the stack; thus this can be interesting. One has to
+ "guess" what is what, but as the information is symbolic,
+ thorough reading of this information can be useful. As an
+ example, we can find the state variable of the Erlang primitive
+ loader online <c><![CDATA[(5)]]></c> and <c><![CDATA[(6)]]></c>
+ in the following example:</p>
+
<code type="none"><![CDATA[
(1) 3cac44 Return addr 0x13BF58 (<terminate process normally>)
-(2) y(0) ["/view/siri_r10_dev/clearcase/otp/erts/lib/kernel/ebin","/view/siri_r10_dev/
-(3) clearcase/otp/erts/lib/stdlib/ebin"]
+(2) y(0) ["/view/siri_r10_dev/clearcase/otp/erts/lib/kernel/ebin",
+(3) "/view/siri_r10_dev/clearcase/otp/erts/lib/stdlib/ebin"]
(4) y(1) <0.1.0>
-(5) y(2) {state,[],none,#Fun<erl_prim_loader.6.7085890>,undefined,#Fun<erl_prim_loader.7.9000327>,#Fun<erl_prim_loader.8.116480692>,#Port<0.2>,infinity,#Fun<erl_prim_loader.9.10708760>}
-(6) y(3) infinity ]]></code>
+(5) y(2) {state,[],none,#Fun<erl_prim_loader.6.7085890>,undefined,#Fun<erl_prim_loader.7.9000327>,
+(6) #Fun<erl_prim_loader.8.116480692>,#Port<0.2>,infinity,#Fun<erl_prim_loader.9.10708760>}
+(7) y(3) infinity ]]></code>
+
<p>When interpreting the data for a process, it is helpful to know
- that anonymous function objects (funs) are given a name
- constructed from the name of the function in which they are
- created, and a number (starting with 0) indicating the number of
- that fun within that function.</p>
+ that anonymous function objects (funs) are given the following:</p>
+
+ <list type="bulleted">
+ <item>A name constructed from the name of the function in which they are
+ created
+ </item>
+ <item>A number (starting with 0) indicating the number of that fun within
+ that function
+ </item>
+ </list>
</section>
<section>
<marker id="atoms"></marker>
<title>Atoms</title>
- <p>Now all the atoms in the system are written. This is only
- interesting if one suspects that dynamic generation of atoms could
+ <p>This section presents all the atoms in the system. This is only
+ of interest if one suspects that dynamic generation of atoms can
be a problem, otherwise this section can be ignored.</p>
- <p>Note that the last created atom is printed first.</p>
+
+ <p>Notice that the last created atom is shown first.</p>
</section>
<section>
<title>Disclaimer</title>
- <p>The format of the crash dump evolves between releases of
- OTP. Some information here may not apply to your
- version. A description as this will never be complete; it is meant as
+ <p>The format of the crash dump evolves between OTP releases.
+ Some information described here may not apply to your
+ version. A description like this will never be complete; it is meant as
an explanation of the crash dump in general and as a help
when trying to find application errors, not as a complete
specification.</p>
diff --git a/erts/doc/src/driver.xml b/erts/doc/src/driver.xml
index a68e87d3b3..8f31df4cad 100644
--- a/erts/doc/src/driver.xml
+++ b/erts/doc/src/driver.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2001</year><year>2013</year>
+ <year>2001</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -22,103 +22,115 @@
</legalnotice>
- <title>How to implement a driver</title>
+ <title>How to Implement a Driver</title>
<prepared>Jakob C</prepared>
<docno></docno>
<date>2000-11-28</date>
<rev>PA1</rev>
<file>driver.xml</file>
</header>
-
- <note><p>This document was written a long time ago. A lot of it is still
- interesting since it explains important concepts, but it was
- written for an older driver interface so the examples do not
- work anymore. The reader is encouraged to read
- <seealso marker="erl_driver">erl_driver</seealso> and the
- <seealso marker="driver_entry">driver_entry</seealso> documentation.
- </p></note>
+ <note>
+ <p>This section was written a long time ago. Most of it is still
+ valid, as it explains important concepts, but this was
+ written for an older driver interface so the examples do not
+ work anymore. The reader is encouraged to read the
+ <seealso marker="erl_driver"><c>erl_driver</c></seealso> and
+ <seealso marker="driver_entry"><c>driver_entry</c></seealso>
+ documentation also.</p>
+ </note>
<section>
<title>Introduction</title>
- <p>This chapter tells you how to build your own driver for erlang.</p>
- <p>A driver in Erlang is a library written in C, that is linked to
- the Erlang emulator and called from erlang. Drivers can be used
- when C is more suitable than Erlang, to speed things up, or to
- provide access to OS resources not directly accessible from
- Erlang.</p>
+ <p>This section describes how to build your own driver for Erlang.</p>
+
+ <p>A driver in Erlang is a library written in C, which is linked to
+ the Erlang emulator and called from Erlang. Drivers can be used
+ when C is more suitable than Erlang, to speed up things, or to
+ provide access to OS resources not directly accessible from Erlang.</p>
+
<p>A driver can be dynamically loaded, as a shared library (known as
- a DLL on windows), or statically loaded, linked with the emulator
+ a DLL on Windows), or statically loaded, linked with the emulator
when it is compiled and linked. Only dynamically loaded drivers
are described here, statically linked drivers are beyond the scope
- of this chapter.</p>
- <p>When a driver is loaded it is executed in the context of the
- emulator, shares the same memory and the same thread. This means
- that all operations in the driver must be non-blocking, and that
- any crash in the driver will bring the whole emulator down. In
- short: you have to be extremely careful!</p>
- <p></p>
+ of this section.</p>
+
+ <warning>
+ <p>When a driver is loaded it is executed in the context of the
+ emulator, shares the same memory and the same thread. This means
+ that all operations in the driver must be non-blocking, and that
+ any crash in the driver brings the whole emulator down. In short,
+ be careful.</p>
+ </warning>
</section>
<section>
- <title>Sample driver</title>
- <p>This is a simple driver for accessing a postgres
+ <title>Sample Driver</title>
+ <p>This section describes a simple driver for accessing a postgres
database using the libpq C client library. Postgres
- is used because it's free and open source. For information
- on postgres, refer to the website
- <url href="http://www.postgres.org">www.postgres.org</url>.</p>
+ is used because it is free and open source. For information on postgres,
+ see <url href="http://www.postgres.org">www.postgres.org</url>.</p>
+
<p>The driver is synchronous, it uses the synchronous calls of
- the client library. This is only for simplicity, and is
- generally not good, since it will
- halt the emulator while waiting for the database.
- This will be improved on below with an asynchronous
- sample driver.</p>
- <p>The code is quite straight-forward: all
+ the client library. This is only for simplicity, but not good, as it
+ halts the emulator while waiting for the database.
+ This is improved below with an asynchronous sample driver.</p>
+
+ <p>The code is straightforward: all
communication between Erlang and the driver
is done with <c><![CDATA[port_control/3]]></c>, and the
driver returns data back using the <c><![CDATA[rbuf]]></c>.</p>
+
<p>An Erlang driver only exports one function: the driver
entry function. This is defined with a macro,
- <c><![CDATA[DRIVER_INIT]]></c>, and returns a pointer to a
+ <c><![CDATA[DRIVER_INIT]]></c>, which returns a pointer to a
C <c><![CDATA[struct]]></c> containing the entry points that are
called from the emulator. The <c><![CDATA[struct]]></c> defines the
entries that the emulator calls to call the driver, with
a <c><![CDATA[NULL]]></c> pointer for entries that are not defined
and used by the driver.</p>
+
<p>The <c><![CDATA[start]]></c> entry is called when the driver
is opened as a port with <c><![CDATA[open_port/2]]></c>. Here
we allocate memory for a user data structure.
- This user data will be passed every time the emulator
- calls us. First we store the driver handle, because it
- is needed in subsequent calls. We allocate memory for
+ This user data is passed every time the emulator
+ calls us. First we store the driver handle, as it
+ is needed in later calls. We allocate memory for
the connection handle that is used by LibPQ. We also
set the port to return allocated driver binaries, by
- setting the flag <c><![CDATA[PORT_CONTROL_FLAG_BINARY]]></c>, calling
+ setting flag <c><![CDATA[PORT_CONTROL_FLAG_BINARY]]></c>, calling
<c><![CDATA[set_port_control_flags]]></c>. (This is because
- we don't know whether our data will fit in the
- result buffer of <c><![CDATA[control]]></c>, which has a default size
- set up by the emulator, currently 64 bytes.)</p>
- <p>There is an entry <c><![CDATA[init]]></c> which is called when
- the driver is loaded, but we don't use this, since
+ we do not know if our data will fit in the
+ result buffer of <c><![CDATA[control]]></c>, which has a default size,
+ 64 bytes, set up by the emulator.)</p>
+
+ <p>An entry <c><![CDATA[init]]></c> is called when
+ the driver is loaded. However, we do not use this, as
it is executed only once, and we want to have the
possibility of several instances of the driver.</p>
+
<p>The <c><![CDATA[stop]]></c> entry is called when the port
is closed.</p>
+
<p>The <c><![CDATA[control]]></c> entry is called from the emulator
when the Erlang code calls <c><![CDATA[port_control/3]]></c>,
to do the actual work. We have defined a simple set of
- commands: <c><![CDATA[connect]]></c> to login to the database, <c><![CDATA[disconnect]]></c>
- to log out and <c><![CDATA[select]]></c> to send a SQL-query and get the result.
+ commands: <c><![CDATA[connect]]></c> to log in to the database,
+ <c><![CDATA[disconnect]]></c> to log out, and <c><![CDATA[select]]></c>
+ to send a SQL-query and get the result.
All results are returned through <c><![CDATA[rbuf]]></c>.
- The library <c><![CDATA[ei]]></c> in <c><![CDATA[erl_interface]]></c> is used
- to encode data in binary term format. The result is returned
+ The library <c><![CDATA[ei]]></c> in <c><![CDATA[erl_interface]]></c> is
+ used to encode data in binary term format. The result is returned
to the emulator as binary terms, so <c><![CDATA[binary_to_term]]></c>
is called in Erlang to convert the result to term form.</p>
- <p>The code is available in <c><![CDATA[pg_sync.c]]></c> in the <c><![CDATA[sample]]></c>
- directory of <c><![CDATA[erts]]></c>.</p>
+
+ <p>The code is available in <c><![CDATA[pg_sync.c]]></c> in the
+ <c><![CDATA[sample]]></c> directory of <c><![CDATA[erts]]></c>.</p>
+
<p>The driver entry contains the functions that
- will be called by the emulator. In our simple
- example, we only provide <c><![CDATA[start]]></c>, <c><![CDATA[stop]]></c>
- and <c><![CDATA[control]]></c>.</p>
+ will be called by the emulator. In this example,
+ only <c><![CDATA[start]]></c>, <c><![CDATA[stop]]></c>,
+ and <c><![CDATA[control]]></c> are provided:</p>
+
<code type="none"><![CDATA[
/* Driver interface declarations */
static ErlDrvData start(ErlDrvPort port, char *command);
@@ -128,15 +140,15 @@ static int control(ErlDrvData drv_data, unsigned int command, char *buf,
static ErlDrvEntry pq_driver_entry = {
NULL, /* init */
- start,
- stop,
+ start,
+ stop,
NULL, /* output */
NULL, /* ready_input */
- NULL, /* ready_output */
+ NULL, /* ready_output */
"pg_sync", /* the name of the driver */
NULL, /* finish */
NULL, /* handle */
- control,
+ control,
NULL, /* timeout */
NULL, /* outputv */
NULL, /* ready_async */
@@ -145,14 +157,18 @@ static ErlDrvEntry pq_driver_entry = {
NULL /* event */
};
]]></code>
+
<p>We have a structure to store state needed by the driver,
- in this case we only need to keep the database connection.</p>
+ in this case we only need to keep the database connection:</p>
+
<code type="none"><![CDATA[
typedef struct our_data_s {
PGconn* conn;
} our_data_t;
]]></code>
- <p>These are control codes we have defined.</p>
+
+ <p>The control codes that we have defined are as follows:</p>
+
<code type="none"><![CDATA[
/* Keep the following definitions in alignment with the
* defines in erl_pq_sync.erl
@@ -162,10 +178,12 @@ typedef struct our_data_s {
#define DRV_DISCONNECT 'D'
#define DRV_SELECT 'S'
]]></code>
- <p>This just returns the driver structure. The macro
+
+ <p>This returns the driver structure. The macro
<c><![CDATA[DRIVER_INIT]]></c> defines the only exported function.
All the other functions are static, and will not be exported
from the library.</p>
+
<code type="none"><![CDATA[
/* INITIALIZATION AFTER LOADING */
@@ -180,9 +198,11 @@ DRIVER_INIT(pq_drv)
return &pq_driver_entry;
}
]]></code>
- <p>Here we do some initialization, <c><![CDATA[start]]></c> is called from
- <c><![CDATA[open_port]]></c>. The data will be passed to <c><![CDATA[control]]></c>
- and <c><![CDATA[stop]]></c>.</p>
+
+ <p>Here some initialization is done, <c><![CDATA[start]]></c> is called from
+ <c><![CDATA[open_port]]></c>. The data will be passed to
+ <c><![CDATA[control]]></c> and <c><![CDATA[stop]]></c>.</p>
+
<code type="none"><![CDATA[
/* DRIVER INTERFACE */
static ErlDrvData start(ErlDrvPort port, char *command)
@@ -195,8 +215,10 @@ static ErlDrvData start(ErlDrvPort port, char *command)
return (ErlDrvData)data;
}
]]></code>
+
<p>We call disconnect to log out from the database.
(This should have been done from Erlang, but just in case.)</p>
+
<code type="none"><![CDATA[
static int do_disconnect(our_data_t* data, ei_x_buff* x);
@@ -208,22 +230,27 @@ static void stop(ErlDrvData drv_data)
driver_free(data);
}
]]></code>
+
<p>We use the binary format only to return data to the emulator;
- input data is a string paramater for <c><![CDATA[connect]]></c> and
+ input data is a string parameter for <c><![CDATA[connect]]></c> and
<c><![CDATA[select]]></c>. The returned data consists of Erlang terms.</p>
- <p>The functions <c><![CDATA[get_s]]></c> and <c><![CDATA[ei_x_to_new_binary]]></c> are
- utilities that are used to make the code shorter. <c><![CDATA[get_s]]></c>
- duplicates the string and zero-terminates it, since the
+
+ <p>The functions <c><![CDATA[get_s]]></c> and
+ <c><![CDATA[ei_x_to_new_binary]]></c> are utilities that are used to
+ make the code shorter. <c><![CDATA[get_s]]></c>
+ duplicates the string and zero-terminates it, as the
postgres client library wants that. <c><![CDATA[ei_x_to_new_binary]]></c>
- takes an <c><![CDATA[ei_x_buff]]></c> buffer and allocates a binary and
- copies the data there. This binary is returned in <c><![CDATA[*rbuf]]></c>.
- (Note that this binary is freed by the emulator, not by us.)</p>
+ takes an <c><![CDATA[ei_x_buff]]></c> buffer, allocates a binary, and
+ copies the data there. This binary is returned in
+ <c><![CDATA[*rbuf]]></c>.
+ (Notice that this binary is freed by the emulator, not by us.)</p>
+
<code type="none"><![CDATA[
static char* get_s(const char* buf, int len);
static int do_connect(const char *s, our_data_t* data, ei_x_buff* x);
static int do_select(const char* s, our_data_t* data, ei_x_buff* x);
-/* Since we are operating in binary mode, the return value from control
+/* As we are operating in binary mode, the return value from control
* is irrelevant, as long as it is not negative.
*/
static int control(ErlDrvData drv_data, unsigned int command, char *buf,
@@ -246,10 +273,12 @@ static int control(ErlDrvData drv_data, unsigned int command, char *buf,
return r;
}
]]></code>
- <p><c><![CDATA[do_connect]]></c> is where we log in to the database. If the connection
- was successful we store the connection handle in our driver
- data, and return ok. Otherwise, we return the error message
- from postgres, and store <c><![CDATA[NULL]]></c> in the driver data.</p>
+
+ <p><c><![CDATA[do_connect]]></c> is where we log in to the database. If the
+ connection was successful, we store the connection handle in the driver
+ data, and return <c>'ok'</c>. Otherwise, we return the error message
+ from postgres and store <c><![CDATA[NULL]]></c> in the driver data.</p>
+
<code type="none"><![CDATA[
static int do_connect(const char *s, our_data_t* data, ei_x_buff* x)
{
@@ -265,10 +294,13 @@ static int do_connect(const char *s, our_data_t* data, ei_x_buff* x)
return 0;
}
]]></code>
- <p>If we are connected (if the connection handle is not <c><![CDATA[NULL]]></c>),
+
+ <p>If we are connected (and if the connection handle is not
+ <c><![CDATA[NULL]]></c>),
we log out from the database. We need to check if we should
- encode an ok, since we might get here from the <c><![CDATA[stop]]></c>
- function, which doesn't return data to the emulator.</p>
+ encode an <c>'ok'</c>, as we can get here from function
+ <c><![CDATA[stop]]></c>, which does not return data to the emulator:</p>
+
<code type="none"><![CDATA[
static int do_disconnect(our_data_t* data, ei_x_buff* x)
{
@@ -281,9 +313,11 @@ static int do_disconnect(our_data_t* data, ei_x_buff* x)
return 0;
}
]]></code>
- <p>We execute a query and encode the result. Encoding is done
- in another C module, <c><![CDATA[pg_encode.c]]></c> which is also provided
+
+ <p>We execute a query and encode the result. Encoding is done in
+ another C module, <c><![CDATA[pg_encode.c]]></c>, which is also provided
as sample code.</p>
+
<code type="none"><![CDATA[
static int do_select(const char* s, our_data_t* data, ei_x_buff* x)
{
@@ -293,12 +327,14 @@ static int do_select(const char* s, our_data_t* data, ei_x_buff* x)
return 0;
}
]]></code>
- <p>Here we simply check the result from postgres, and
- if it's data we encode it as lists of lists with
+
+ <p>Here we check the result from postgres.
+ If it is data, we encode it as lists of lists with
column data. Everything from postgres is C strings,
- so we just use <c><![CDATA[ei_x_encode_string]]></c> to send
+ so we use <c><![CDATA[ei_x_encode_string]]></c> to send
the result as strings to Erlang. (The head of the list
contains the column names.)</p>
+
<code type="none"><![CDATA[
void encode_result(ei_x_buff* x, PGresult* res, PGconn* conn)
{
@@ -338,33 +374,36 @@ void encode_result(ei_x_buff* x, PGresult* res, PGconn* conn)
</section>
<section>
- <title>Compiling and linking the sample driver</title>
- <p>The driver should be compiled and linked to a shared
- library (DLL on windows). With gcc this is done
- with the link flags <c><![CDATA[-shared]]></c> and <c><![CDATA[-fpic]]></c>.
- Since we use the <c><![CDATA[ei]]></c> library we should include
+ <title>Compiling and Linking the Sample Driver</title>
+ <p>The driver is to be compiled and linked to a shared
+ library (DLL on Windows). With gcc, this is done with
+ link flags <c><![CDATA[-shared]]></c> and <c><![CDATA[-fpic]]></c>.
+ As we use the <c><![CDATA[ei]]></c> library, we should include
it too. There are several versions of <c><![CDATA[ei]]></c>, compiled
for debug or non-debug and multi-threaded or single-threaded.
- In the makefile for the samples the <c><![CDATA[obj]]></c> directory
+ In the makefile for the samples, the <c><![CDATA[obj]]></c> directory
is used for the <c><![CDATA[ei]]></c> library, meaning that we use
the non-debug, single-threaded version.</p>
</section>
<section>
- <title>Calling a driver as a port in Erlang</title>
+ <title>Calling a Driver as a Port in Erlang</title>
<p>Before a driver can be called from Erlang, it must be
loaded and opened. Loading is done using the <c><![CDATA[erl_ddll]]></c>
module (the <c><![CDATA[erl_ddll]]></c> driver that loads dynamic
- driver, is actually a driver itself). If loading is ok
+ driver is actually a driver itself). If loading is successfull,
the port can be opened with <c><![CDATA[open_port/2]]></c>. The port
name must match the name of the shared library and
the name in the driver entry structure.</p>
+
<p>When the port has been opened, the driver can be called. In
- the <c><![CDATA[pg_sync]]></c> example, we don't have any data from
+ the <c><![CDATA[pg_sync]]></c> example, we do not have any data from
the port, only the return value from the
<c><![CDATA[port_control]]></c>.</p>
+
<p>The following code is the Erlang part of the synchronous
- postgres driver, <c><![CDATA[pg_sync.erl]]></c>.</p>
+ postgres driver, <c><![CDATA[pg_sync.erl]]></c>:</p>
+
<code type="none"><![CDATA[
-module(pg_sync).
@@ -394,20 +433,35 @@ disconnect(Port) ->
select(Port, Query) ->
binary_to_term(port_control(Port, ?DRV_SELECT, Query)).
]]></code>
- <p>The API is simple: <c><![CDATA[connect/1]]></c> loads the driver, opens it
- and logs on to the database, returning the Erlang port
- if successful, <c><![CDATA[select/2]]></c> sends a query to the driver,
- and returns the result, <c><![CDATA[disconnect/1]]></c> closes the
- database connection and the driver. (It does not unload it,
- however.) The connection string should be a connection
- string for postgres.</p>
- <p>The driver is loaded with <c><![CDATA[erl_ddll:load_driver/2]]></c>,
- and if this is successful, or if it's already loaded,
+
+ <p>The API is simple:</p>
+
+ <list type="bulleted">
+ <item>
+ <p><c><![CDATA[connect/1]]></c> loads the driver, opens it,
+ and logs on to the database, returning the Erlang port
+ if successful.</p>
+ </item>
+ <item>
+ <p><c><![CDATA[select/2]]></c> sends a query to the driver
+ and returns the result.</p>
+ </item>
+ <item>
+ <p><c><![CDATA[disconnect/1]]></c> closes the database
+ connection and the driver. (However, it does not unload it.)</p>
+ </item>
+ </list>
+
+ <p>The connection string is to be a connection string for postgres.</p>
+
+ <p>The driver is loaded with <c><![CDATA[erl_ddll:load_driver/2]]></c>.
+ If this is successful, or if it is already loaded,
it is opened. This will call the <c><![CDATA[start]]></c> function
in the driver.</p>
+
<p>We use the <c><![CDATA[port_control/3]]></c> function for all
- calls into the driver, the result from the driver is
- returned immediately, and converted to terms by calling
+ calls into the driver. The result from the driver is
+ returned immediately and converted to terms by calling
<c><![CDATA[binary_to_term/1]]></c>. (We trust that the terms returned
from the driver are well-formed, otherwise the
<c><![CDATA[binary_to_term]]></c> calls could be contained in a
@@ -415,16 +469,18 @@ select(Port, Query) ->
</section>
<section>
- <title>Sample asynchronous driver</title>
- <p>Sometimes database queries can take long time to
+ <title>Sample Asynchronous Driver</title>
+ <p>Sometimes database queries can take a long time to
complete, in our <c><![CDATA[pg_sync]]></c> driver, the emulator
halts while the driver is doing its job. This is
- often not acceptable, since no other Erlang process
+ often not acceptable, as no other Erlang process
gets a chance to do anything. To improve on our
- postgres driver, we reimplement it using the asynchronous
+ postgres driver, we re-implement it using the asynchronous
calls in LibPQ.</p>
- <p>The asynchronous version of the driver is in the
- sample files <c><![CDATA[pg_async.c]]></c> and <c><![CDATA[pg_asyng.erl]]></c>.</p>
+
+ <p>The asynchronous version of the driver is in the sample files
+ <c><![CDATA[pg_async.c]]></c> and <c><![CDATA[pg_asyng.erl]]></c>.</p>
+
<code type="none"><![CDATA[
/* Driver interface declarations */
static ErlDrvData start(ErlDrvPort port, char *command);
@@ -459,22 +515,26 @@ typedef struct our_data_t {
int connecting;
} our_data_t;
]]></code>
- <p>Here some things have changed from <c><![CDATA[pg_sync.c]]></c>: we use the
- entry <c><![CDATA[ready_io]]></c> for <c><![CDATA[ready_input]]></c> and
- <c><![CDATA[ready_output]]></c> which will be called from the emulator only
- when there is input to be read from the socket. (Actually, the
+
+ <p>Some things have changed from <c><![CDATA[pg_sync.c]]></c>: we use
+ the entry <c><![CDATA[ready_io]]></c> for <c><![CDATA[ready_input]]></c>
+ and <c><![CDATA[ready_output]]></c>, which is called from the emulator
+ only when there is input to be read from the socket. (Actually, the
socket is used in a <c><![CDATA[select]]></c> function inside
- the emulator, and when the socket is signalled,
- indicating there is data to read, the <c><![CDATA[ready_input]]></c> entry
- is called. More on this below.)</p>
+ the emulator, and when the socket is signaled,
+ indicating there is data to read, the <c><![CDATA[ready_input]]></c>
+ entry is called. More about this below.)</p>
+
<p>Our driver data is also extended, we keep track of the
socket used for communication with postgres, and also
the port, which is needed when we send data to the port with
- <c><![CDATA[driver_output]]></c>. We have a flag <c><![CDATA[connecting]]></c> to tell
+ <c><![CDATA[driver_output]]></c>. We have a flag
+ <c><![CDATA[connecting]]></c> to tell
whether the driver is waiting for a connection or waiting
- for the result of a query. (This is needed since the entry
- <c><![CDATA[ready_io]]></c> will be called both when connecting and
+ for the result of a query. (This is needed, as the entry
+ <c><![CDATA[ready_io]]></c> is called both when connecting and
when there is a query result.)</p>
+
<code type="none"><![CDATA[
static int do_connect(const char *s, our_data_t* data)
{
@@ -498,16 +558,20 @@ static int do_connect(const char *s, our_data_t* data)
return 0;
}
]]></code>
- <p>The <c><![CDATA[connect]]></c> function looks a bit different too. We connect
- using the asynchronous <c><![CDATA[PQconnectStart]]></c> function. After the
- connection is started, we retrieve the socket for the connection
+
+ <p>The <c><![CDATA[connect]]></c> function looks a bit different too. We
+ connect using the asynchronous <c><![CDATA[PQconnectStart]]></c> function.
+ After the connection is started, we retrieve the socket for the connection
with <c><![CDATA[PQsocket]]></c>. This socket is used with the
<c><![CDATA[driver_select]]></c> function to wait for connection. When
- the socket is ready for input or for output, the <c><![CDATA[ready_io]]></c>
- function will be called.</p>
- <p>Note that we only return data (with <c><![CDATA[driver_output]]></c>) if there
+ the socket is ready for input or for output, the
+ <c><![CDATA[ready_io]]></c> function is called.</p>
+
+ <p>Notice that we only return data (with <c><![CDATA[driver_output]]></c>)
+ if there
is an error here, otherwise we wait for the connection to be completed,
- in which case our <c><![CDATA[ready_io]]></c> function will be called.</p>
+ in which case our <c><![CDATA[ready_io]]></c> function is called.</p>
+
<code type="none"><![CDATA[
static int do_select(const char* s, our_data_t* data)
{
@@ -525,9 +589,11 @@ static int do_select(const char* s, our_data_t* data)
return 0;
}
]]></code>
+
<p>The <c><![CDATA[do_select]]></c> function initiates a select, and returns
- if there is no immediate error. The actual result will be returned
+ if there is no immediate error. The result is returned
when <c><![CDATA[ready_io]]></c> is called.</p>
+
<code type="none"><![CDATA[
static void ready_io(ErlDrvData drv_data, ErlDrvEvent event)
{
@@ -566,26 +632,31 @@ static void ready_io(ErlDrvData drv_data, ErlDrvEvent event)
ei_x_free(&x);
}
]]></code>
- <p>The <c><![CDATA[ready_io]]></c> function will be called when the socket
+
+ <p>The <c><![CDATA[ready_io]]></c> function is called when the socket
we got from postgres is ready for input or output. Here
we first check if we are connecting to the database. In that
- case we check connection status and return ok if the
- connection is successful, or error if it's not. If the
- connection is not yet established, we simply return; <c><![CDATA[ready_io]]></c>
- will be called again.</p>
+ case, we check connection status and return OK if the
+ connection is successful, or error if it is not. If the
+ connection is not yet established, we simply return;
+ <c><![CDATA[ready_io]]></c> is called again.</p>
+
<p>If we have a result from a connect, indicated by having data in
the <c><![CDATA[x]]></c> buffer, we no longer need to select on
output (<c><![CDATA[ready_output]]></c>), so we remove this by calling
<c><![CDATA[driver_select]]></c>.</p>
- <p>If we're not connecting, we're waiting for results from a
+
+ <p>If we are not connecting, we wait for results from a
<c><![CDATA[PQsendQuery]]></c>, so we get the result and return it. The
encoding is done with the same functions as in the earlier
example.</p>
- <p>We should add error handling here, for instance checking
- that the socket is still open, but this is just a simple
- example.</p>
+
+ <p>Error handling is to be added here, for example, checking
+ that the socket is still open, but this is only a simple example.</p>
+
<p>The Erlang part of the asynchronous driver consists of the
sample file <c><![CDATA[pg_async.erl]]></c>.</p>
+
<code type="none"><![CDATA[
-module(pg_async).
@@ -626,45 +697,50 @@ return_port_data(Port) ->
binary_to_term(Data)
end.
]]></code>
- <p>The Erlang code is slightly different, this is because we
- don't return the result synchronously from <c><![CDATA[port_control]]></c>,
+
+ <p>The Erlang code is slightly different, as we do not
+ return the result synchronously from <c><![CDATA[port_control]]></c>,
instead we get it from <c><![CDATA[driver_output]]></c> as data in the
message queue. The function <c><![CDATA[return_port_data]]></c> above
- receives data from the port. Since the data is in
+ receives data from the port. As the data is in
binary format, we use <c><![CDATA[binary_to_term/1]]></c> to convert
- it to an Erlang term. Note that the driver is opened in
- binary mode (<c><![CDATA[open_port/2]]></c> is called with the option
+ it to an Erlang term. Notice that the driver is opened in
+ binary mode (<c><![CDATA[open_port/2]]></c> is called with option
<c><![CDATA[[binary]]]></c>). This means that data sent from the driver
- to the emulator is sent as binaries. Without the <c><![CDATA[binary]]></c>
- option, they would have been lists of integers.</p>
+ to the emulator is sent as binaries. Without option
+ <c><![CDATA[binary]]></c>, they would have been lists of integers.</p>
</section>
<section>
- <title>An asynchronous driver using driver_async</title>
- <p>As a final example we demonstrate the use of <c><![CDATA[driver_async]]></c>.
+ <title>An Asynchronous Driver Using driver_async</title>
+ <p>As a final example we demonstrate the use of
+ <c><![CDATA[driver_async]]></c>.
We also use the driver term interface. The driver is written
- in C++. This enables us to use an algorithm from STL. We will
- use the <c><![CDATA[next_permutation]]></c> algorithm to get the next permutation
- of a list of integers. For large lists (more than 100000
- elements), this will take some time, so we will perform this
+ in C++. This enables us to use an algorithm from STL. We use
+ the <c><![CDATA[next_permutation]]></c> algorithm to get the next
+ permutation of a list of integers. For large lists (&gt; 100,000
+ elements), this takes some time, so we perform this
as an asynchronous task.</p>
- <p>The asynchronous API for drivers is quite complicated. First
- of all, the work must be prepared. In our example we do this
- in <c><![CDATA[output]]></c>. We could have used <c><![CDATA[control]]></c> just as well,
- but we want some variation in our examples. In our driver, we allocate
- a structure that contains anything that's needed for the asynchronous task
- to do the work. This is done in the main emulator thread.
+
+ <p>The asynchronous API for drivers is complicated. First,
+ the work must be prepared. In the example, this is done in
+ <c><![CDATA[output]]></c>. We could have used <c><![CDATA[control]]></c>,
+ but we want some variation in the examples. In our driver, we allocate
+ a structure that contains anything that is needed for the asynchronous
+ task to do the work. This is done in the main emulator thread.
Then the asynchronous function is called from a driver thread,
- separate from the main emulator thread. Note that the driver-functions
- are not reentrant, so they shouldn't be used.
+ separate from the main emulator thread. Notice that the driver functions
+ are not re-entrant, so they are not to be used.
Finally, after the function is completed, the driver callback
<c><![CDATA[ready_async]]></c> is called from the main emulator thread,
- this is where we return the result to Erlang. (We can't
- return the result from within the asynchronous function, since
- we can't call the driver-functions.)</p>
- <p>The code below is from the sample file <c><![CDATA[next_perm.cc]]></c>.</p>
- <p>The driver entry looks like before, but also contains the
- call-back <c><![CDATA[ready_async]]></c>.</p>
+ this is where we return the result to Erlang. (We cannot
+ return the result from within the asynchronous function, as
+ we cannot call the driver functions.)</p>
+
+ <p>The following code is from the sample file
+ <c><![CDATA[next_perm.cc]]></c>. The driver entry looks like before,
+ but also contains the callback <c><![CDATA[ready_async]]></c>.</p>
+
<code type="none"><![CDATA[
static ErlDrvEntry next_perm_driver_entry = {
NULL, /* init */
@@ -685,17 +761,21 @@ static ErlDrvEntry next_perm_driver_entry = {
NULL /* event */
};
]]></code>
- <p>The <c><![CDATA[output]]></c> function allocates the work-area of the
- asynchronous function. Since we use C++, we use a struct,
- and stuff the data in it. We have to copy the original data,
+
+ <p>The <c><![CDATA[output]]></c> function allocates the work area of the
+ asynchronous function. As we use C++, we use a struct,
+ and stuff the data in it. We must copy the original data,
it is not valid after we have returned from the <c><![CDATA[output]]></c>
- function, and the <c><![CDATA[do_perm]]></c> function will be called later,
- and from another thread. We return no data here, instead it will
- be sent later from the <c><![CDATA[ready_async]]></c> call-back.</p>
- <p>The <c><![CDATA[async_data]]></c> will be passed to the <c><![CDATA[do_perm]]></c> function.
- We do not use a <c><![CDATA[async_free]]></c> function (the last argument to
- <c><![CDATA[driver_async]]></c>), it's only used if the task is cancelled
+ function, and the <c><![CDATA[do_perm]]></c> function is called
+ later, and from another thread. We return no data here, instead it
+ is sent later from the <c><![CDATA[ready_async]]></c> callback.</p>
+
+ <p>The <c><![CDATA[async_data]]></c> is passed to the
+ <c><![CDATA[do_perm]]></c> function. We do not use a
+ <c><![CDATA[async_free]]></c> function (the last argument to
+ <c><![CDATA[driver_async]]></c>), it is only used if the task is cancelled
programmatically.</p>
+
<code type="none"><![CDATA[
struct our_async_data {
bool prev;
@@ -720,8 +800,10 @@ static void output(ErlDrvData drv_data, char *buf, int len)
driver_async(port, NULL, do_perm, async_data, do_free);
}
]]></code>
- <p>In the <c><![CDATA[do_perm]]></c> we simply do the work, operating
+
+ <p>In the <c><![CDATA[do_perm]]></c> we do the work, operating
on the structure that was allocated in <c><![CDATA[output]]></c>.</p>
+
<code type="none"><![CDATA[
static void do_perm(void* async_data)
{
@@ -732,13 +814,17 @@ static void do_perm(void* async_data)
next_permutation(d->data.begin(), d->data.end());
}
]]></code>
- <p>In the <c><![CDATA[ready_async]]></c> function, the output is sent back to the
+
+ <p>In the <c><![CDATA[ready_async]]></c> function the output is sent back
+ to the
emulator. We use the driver term format instead of <c><![CDATA[ei]]></c>.
- This is the only way to send Erlang terms directly to a driver,
- without having the Erlang code to call <c><![CDATA[binary_to_term/1]]></c>. In
- our simple example this works well, and we don't need to use
+ This is the only way to send Erlang terms directly to a driver, without
+ having the Erlang code to call <c><![CDATA[binary_to_term/1]]></c>. In
+ the simple example this works well, and we do not need to use
<c><![CDATA[ei]]></c> to handle the binary term format.</p>
- <p>When the data is returned we deallocate our data.</p>
+
+ <p>When the data is returned, we deallocate our data.</p>
+
<code type="none"><![CDATA[
static void ready_async(ErlDrvData drv_data, ErlDrvThreadData async_data)
{
@@ -759,12 +845,15 @@ static void ready_async(ErlDrvData drv_data, ErlDrvThreadData async_data)
delete d;
}
]]></code>
- <p>This driver is called like the others from Erlang, however, since
+
+ <p>This driver is called like the others from Erlang. However, as
we use <c><![CDATA[driver_output_term]]></c>, there is no need to call
- binary_to_term. The Erlang code is in the sample file
+ <c>binary_to_term</c>. The Erlang code is in the sample file
<c><![CDATA[next_perm.erl]]></c>.</p>
+
<p>The input is changed into a list of integers and sent to
the driver.</p>
+
<code type="none"><![CDATA[
-module(next_perm).
diff --git a/erts/doc/src/driver_entry.xml b/erts/doc/src/driver_entry.xml
index bad20d6343..2421e0a8d9 100644
--- a/erts/doc/src/driver_entry.xml
+++ b/erts/doc/src/driver_entry.xml
@@ -4,7 +4,7 @@
<cref>
<header>
<copyright>
- <year>2001</year><year>2015</year>
+ <year>2001</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -33,55 +33,64 @@
<file>driver_entry.xml</file>
</header>
<lib>driver_entry</lib>
- <libsummary>The driver-entry structure used by erlang drivers.</libsummary>
+ <libsummary>The driver-entry structure used by Erlang drivers.</libsummary>
<description>
<marker id="WARNING"/>
- <warning><p><em>Use this functionality with extreme care!</em></p>
+ <warning>
+ <p><em>Use this functionality with extreme care.</em></p>
<p>A driver callback is executed as a direct extension of the
- native code of the VM. Execution is not made in a safe environment.
- The VM can <em>not</em> provide the same services as provided when
- executing Erlang code, such as preemptive scheduling or memory
- protection. If the driver callback function doesn't behave well,
- the whole VM will misbehave.</p>
- <list>
- <item><p>A driver callback that crash will crash the whole VM.</p></item>
- <item><p>An erroneously implemented driver callback might cause
- a VM internal state inconsistency which may cause a crash of the VM,
- or miscellaneous misbehaviors of the VM at any point after the call
- to the driver callback.</p></item>
- <item><p>A driver callback that do
- <seealso marker="erl_driver#lengthy_work">lengthy work</seealso>
- before returning will degrade responsiveness of the VM,
- and may cause miscellaneous strange behaviors. Such strange behaviors
- include, but are not limited to, extreme memory usage, and bad load
- balancing between schedulers. Strange behaviors that might occur due
- to lengthy work may also vary between OTP releases.</p></item>
+ native code of the VM. Execution is not made in a safe environment.
+ The VM <em>cannot</em> provide the same services as provided when
+ executing Erlang code, such as pre-emptive scheduling or memory
+ protection. If the driver callback function does not behave well,
+ the whole VM will misbehave.</p>
+ <list type="bulleted">
+ <item>
+ <p>A driver callback that crash will crash the whole VM.</p>
+ </item>
+ <item>
+ <p>An erroneously implemented driver callback can cause a VM
+ internal state inconsistency, which can cause a crash of the VM,
+ or miscellaneous misbehaviors of the VM at any point after the
+ call to the driver callback.</p>
+ </item>
+ <item>
+ <p>A driver callback doing
+ <seealso marker="erl_driver#lengthy_work">lengthy work</seealso>
+ before returning degrades responsiveness of the VM, and can cause
+ miscellaneous strange behaviors. Such strange behaviors
+ include, but are not limited to, extreme memory usage, and bad
+ load balancing between schedulers. Strange behaviors that can
+ occur because of lengthy work can also vary between Erlang/OTP
+ releases.</p>
+ </item>
</list>
- </warning>
- <p>
- As of erts version 5.9 (OTP release R15B) the driver interface
+ </warning>
+
+ <p>As from ERTS 5.9 (Erlang/OTP R15B) the driver interface
has been changed with larger types for the callbacks
- <seealso marker="#output">output</seealso>,
- <seealso marker="#control">control</seealso> and
- <seealso marker="#call">call</seealso>.
+ <seealso marker="#output"><c>output</c></seealso>,
+ <seealso marker="#control"><c>control</c></seealso>, and
+ <seealso marker="#call"><c>call</c></seealso>.
See driver <seealso marker="erl_driver#version_management">
version management</seealso> in
- <seealso marker="erl_driver">erl_driver</seealso>.
- </p>
+ <seealso marker="erl_driver"><c>erl_driver</c></seealso>.</p>
+
<note>
<p>Old drivers (compiled with an <c>erl_driver.h</c> from an
- earlier erts version than 5.9) have to be updated and have
- to use the extended interface (with
- <seealso marker="erl_driver#version_management">version management
- </seealso>).</p>
+ ERTS version earlier than 5.9) must be updated and have
+ to use the extended interface (with
+ <seealso marker="erl_driver#version_management">version management
+ </seealso>).</p>
</note>
- <p>The <c>driver_entry</c> structure is a C struct that all erlang
- drivers define. It contains entry points for the erlang driver
- that are called by the erlang emulator when erlang code accesses
+
+ <p>The <c>driver_entry</c> structure is a C struct that all Erlang
+ drivers define. It contains entry points for the Erlang driver,
+ which are called by the Erlang emulator when Erlang code accesses
the driver.</p>
- <p>
- <marker id="emulator"></marker>
- The <seealso marker="erl_driver">erl_driver</seealso> driver
+
+ <p><marker id="emulator"></marker>
+ The <seealso marker="erl_driver"><c>erl_driver</c></seealso> driver
API functions need a port handle
that identifies the driver instance (and the port in the
emulator). This is only passed to the <c>start</c> function, but
@@ -90,413 +99,466 @@
common practice is to have the <c>start</c> function allocate
some application-defined structure and stash the <c>port</c>
handle in it, to use it later with the driver API functions.</p>
- <p>The driver call-back functions are called synchronously from the
- erlang emulator. If they take too long before completing, they
- can cause timeouts in the emulator. Use the queue or
- asynchronous calls if necessary, since the emulator must be
+
+ <p>The driver callback functions are called synchronously from the
+ Erlang emulator. If they take too long before completing, they
+ can cause time-outs in the emulator. Use the queue or
+ asynchronous calls if necessary, as the emulator must be
responsive.</p>
- <p>The driver structure contains the name of the driver and some
- 15 function pointers. These pointers are called at different
+
+ <p>The driver structure contains the driver name and some
+ 15 function pointers, which are called at different
times by the emulator.</p>
+
<p>The only exported function from the driver is
<c>driver_init</c>. This function returns the <c>driver_entry</c>
structure that points to the other functions in the driver. The
- <c>driver_init</c> function is declared with a macro
- <c>DRIVER_INIT(drivername)</c>. (This is because different OS's
- have different names for it.)</p>
- <p>When writing a driver in C++, the driver entry should be of
- <c>"C"</c> linkage. One way to do this is to put this line
- somewhere before the driver entry:
- <c>extern "C" DRIVER_INIT(drivername);</c>.</p>
+ <c>driver_init</c> function is declared with a macro,
+ <c>DRIVER_INIT(drivername)</c>. (This is because different
+ operating systems have different names for it.)</p>
+
+ <p>When writing a driver in C++, the driver entry is to be of
+ <c>"C"</c> linkage. One way to do this is to put the
+ following line somewhere before the driver entry:</p>
+
+ <pre>
+extern "C" DRIVER_INIT(drivername);</pre>
+
<p>When the driver has passed the <c>driver_entry</c> over to
the emulator, the driver is <em>not</em> allowed to modify the
<c>driver_entry</c>.</p>
- <p>If compiling a driver for static inclusion via --enable-static-drivers you
- have to define STATIC_ERLANG_DRIVER before the DRIVER_INIT declaration.</p>
+
+ <p>If compiling a driver for static inclusion through
+ <c>--enable-static-drivers</c>, you must define
+ <c>STATIC_ERLANG_DRIVER</c> before the <c>DRIVER_INIT</c> declaration.</p>
+
<note>
- <p>Do <em>not</em> declare the <c>driver_entry</c> <c>const</c>. This since the emulator needs to
- modify the <c>handle</c>, and the <c>handle2</c>
- fields. A statically allocated, and <c>const</c>
- declared <c>driver_entry</c> may be located in
- read only memory which will cause the emulator
- to crash.</p>
+ <p>Do <em>not</em> declare the <c>driver_entry</c> <c>const</c>.
+ This because the emulator must
+ modify the <c>handle</c> and the <c>handle2</c>
+ fields. A statically allocated, and <c>const</c>-declared
+ <c>driver_entry</c> can be located in
+ read-only memory, which causes the emulator to crash.</p>
</note>
</description>
<section>
- <title>DATA TYPES</title>
- <taglist>
- <tag><em>ErlDrvEntry</em></tag>
- <item>
- <p/>
+ <title>Data Types</title>
+ <p><c>ErlDrvEntry</c></p>
<code type="none">
typedef struct erl_drv_entry {
- int (*init)(void); /* called at system start up for statically
+ int (*init)(void); /* Called at system startup for statically
linked drivers, and after loading for
- dynamically loaded drivers */
-
+ dynamically loaded drivers */
#ifndef ERL_SYS_DRV
ErlDrvData (*start)(ErlDrvPort port, char *command);
- /* called when open_port/2 is invoked.
- return value -1 means failure. */
+ /* Called when open_port/2 is invoked,
+ return value -1 means failure */
#else
ErlDrvData (*start)(ErlDrvPort port, char *command, SysDriverOpts* opts);
- /* special options, only for system driver */
+ /* Special options, only for system driver */
#endif
void (*stop)(ErlDrvData drv_data);
- /* called when port is closed, and when the
- emulator is halted. */
+ /* Called when port is closed, and when the
+ emulator is halted */
void (*output)(ErlDrvData drv_data, char *buf, ErlDrvSizeT len);
- /* called when we have output from erlang to
+ /* Called when we have output from Erlang to
the port */
void (*ready_input)(ErlDrvData drv_data, ErlDrvEvent event);
- /* called when we have input from one of
+ /* Called when we have input from one of
the driver's handles */
void (*ready_output)(ErlDrvData drv_data, ErlDrvEvent event);
- /* called when output is possible to one of
+ /* Called when output is possible to one of
the driver's handles */
- char *driver_name; /* name supplied as command
- in open_port XXX ? */
- void (*finish)(void); /* called before unloading the driver -
- DYNAMIC DRIVERS ONLY */
- void *handle; /* Reserved -- Used by emulator internally */
+ char *driver_name; /* Name supplied as command in
+ erlang:open_port/2 */
+ void (*finish)(void); /* Called before unloading the driver -
+ dynamic drivers only */
+ void *handle; /* Reserved, used by emulator internally */
ErlDrvSSizeT (*control)(ErlDrvData drv_data, unsigned int command,
char *buf, ErlDrvSizeT len,
char **rbuf, ErlDrvSizeT rlen);
- /* "ioctl" for drivers - invoked by
+ /* "ioctl" for drivers - invoked by
port_control/3 */
- void (*timeout)(ErlDrvData drv_data); /* Handling of timeout in driver */
+ void (*timeout)(ErlDrvData drv_data);
+ /* Handling of time-out in driver */
void (*outputv)(ErlDrvData drv_data, ErlIOVec *ev);
- /* called when we have output from erlang
+ /* Called when we have output from Erlang
to the port */
void (*ready_async)(ErlDrvData drv_data, ErlDrvThreadData thread_data);
void (*flush)(ErlDrvData drv_data);
- /* called when the port is about to be
- closed, and there is data in the
- driver queue that needs to be flushed
+ /* Called when the port is about to be
+ closed, and there is data in the
+ driver queue that must be flushed
before 'stop' can be called */
ErlDrvSSizeT (*call)(ErlDrvData drv_data, unsigned int command,
char *buf, ErlDrvSizeT len,
char **rbuf, ErlDrvSizeT rlen, unsigned int *flags);
/* Works mostly like 'control', a synchronous
- call into the driver. */
+ call into the driver */
void (*event)(ErlDrvData drv_data, ErlDrvEvent event,
ErlDrvEventData event_data);
- /* Called when an event selected by
+ /* Called when an event selected by
driver_event() has occurred */
int extended_marker; /* ERL_DRV_EXTENDED_MARKER */
int major_version; /* ERL_DRV_EXTENDED_MAJOR_VERSION */
int minor_version; /* ERL_DRV_EXTENDED_MINOR_VERSION */
int driver_flags; /* ERL_DRV_FLAGs */
- void *handle2; /* Reserved -- Used by emulator internally */
+ void *handle2; /* Reserved, used by emulator internally */
void (*process_exit)(ErlDrvData drv_data, ErlDrvMonitor *monitor);
/* Called when a process monitor fires */
void (*stop_select)(ErlDrvEvent event, void* reserved);
/* Called to close an event object */
- } ErlDrvEntry;
- </code>
- <p/>
+ } ErlDrvEntry;</code>
<taglist>
- <tag><marker id="init"/>int (*init)(void)</tag>
- <item>
- <p>This is called directly after the driver has been loaded by
- <c>erl_ddll:load_driver/2</c>. (Actually when the driver is
- added to the driver list.) The driver should return 0, or if
- the driver can't initialize, -1.</p>
+ <tag><marker id="init"/><c>int (*init)(void)</c></tag>
+ <item>
+ <p>Called directly after the driver has been loaded by
+ <seealso marker="kernel:erl_ddll#load_driver/2">
+ <c>erl_ddll:load_driver/2</c></seealso> (actually when the driver is
+ added to the driver list). The driver is to return <c>0</c>, or, if
+ the driver cannot initialize, <c>-1</c>.</p>
</item>
- <tag><marker id="start"/>ErlDrvData (*start)(ErlDrvPort port, char* command)</tag>
+ <tag><marker id="start"/>
+ <c>ErlDrvData (*start)(ErlDrvPort port, char* command)</c></tag>
<item>
- <p>This is called when the driver is instantiated, when
- <c>open_port/2</c> is called. The driver should return a
- number &gt;= 0 or a pointer, or if the driver can't be started,
- one of three error codes should be returned:</p>
- <p>ERL_DRV_ERROR_GENERAL - general error, no error code</p>
- <p>ERL_DRV_ERROR_ERRNO - error with error code in <c>errno</c></p>
- <p>ERL_DRV_ERROR_BADARG - error, badarg</p>
- <p>If an error code is returned, the port isn't started.</p>
+ <p>Called when the driver is instantiated, when
+ <seealso marker="erlang#open_port/2">
+ <c>erlang:open_port/2</c></seealso> is called.
+ The driver is to return a number &gt;= 0 or a pointer, or, if the
+ driver cannot be started, one of three error codes:</p>
+ <taglist>
+ <tag><c>ERL_DRV_ERROR_GENERAL</c></tag>
+ <item>General error, no error code</item>
+ <tag><c>ERL_DRV_ERROR_ERRNO</c></tag>
+ <item>Error with error code in <c>errno</c></item>
+ <tag><c>ERL_DRV_ERROR_BADARG</c></tag>
+ <item>Error, <c>badarg</c></item>
+ </taglist>
+ <p>If an error code is returned, the port is not started.</p>
</item>
- <tag><marker id="stop"/>void (*stop)(ErlDrvData drv_data)</tag>
+ <tag><marker id="stop"/><c>void (*stop)(ErlDrvData drv_data)</c></tag>
<item>
- <p>This is called when the port is closed, with
- <c>port_close/1</c> or <c>Port ! {self(), close}</c>. Note
- that terminating the port owner process also closes the
+ <p>Called when the port is closed, with
+ <seealso marker="erlang#port_close/1">
+ <c>erlang:port_close/1</c></seealso> or <c>Port ! {self(), close}</c>.
+ Notice that terminating the port owner process also closes the
port. If <c>drv_data</c> is a pointer to memory allocated in
- <c>start</c>, then <c>stop</c> is the place to deallocate that
- memory.</p>
+ <c>start</c>, then <c>stop</c> is the place to deallocate that
+ memory.</p>
</item>
- <tag><marker id="output"/>void (*output)(ErlDrvData drv_data, char *buf, ErlDrvSizeT len)</tag>
+ <tag><marker id="output"/>
+ <c>void (*output)(ErlDrvData drv_data, char *buf, ErlDrvSizeT len)</c>
+ </tag>
<item>
- <p>This is called when an erlang process has sent data to the
- port. The data is pointed to by <c>buf</c>, and is
- <c>len</c> bytes. Data is sent to the port with <c>Port ! {self(), {command, Data}}</c>, or with
- <c>port_command/2</c>. Depending on how the port was opened,
- it should be either a list of integers 0...255 or a
- binary. See <c>open_port/3</c> and <c>port_command/2</c>.</p>
+ <p>Called when an Erlang process has sent data to the port. The data is
+ pointed to by <c>buf</c>, and is <c>len</c> bytes. Data is sent to
+ the port with <c>Port ! {self(), {command, Data}}</c> or with
+ <c>erlang:port_command/2</c>. Depending on how the port was
+ opened, it is to be either a list of integers <c>0...255</c> or a
+ binary. See <seealso marker="erlang#open_port/2">
+ <c>erlang:open_port/2</c></seealso> and
+ <seealso marker="erlang#port_command/2">
+ <c>erlang:port_command/2</c></seealso>.</p>
</item>
-
- <tag><marker id="ready_input"/>void (*ready_input)(ErlDrvData drv_data, ErlDrvEvent event)</tag>
- <item/>
- <tag><marker id="ready_output"/>void (*ready_output)(ErlDrvData drv_data, ErlDrvEvent event)</tag>
+ <tag><marker id="ready_input"/>
+ <c>void (*ready_input)(ErlDrvData drv_data, ErlDrvEvent event)</c>
+ </tag>
+ <item></item>
+ <tag><marker id="ready_output"/>
+ <c>void (*ready_output)(ErlDrvData drv_data, ErlDrvEvent event)</c>
+ </tag>
<item>
- <p>This is called when a driver event (given in the
- <c>event</c> parameter) is signaled. This is used to help
- asynchronous drivers "wake up" when something happens.</p>
- <p>On unix the <c>event</c> is a pipe or socket handle (or
+ <p>Called when a driver event (specified in parameter
+ <c>event</c>) is signaled. This is used to help
+ asynchronous drivers "wake up" when something occurs.</p>
+ <p>On Unix the <c>event</c> is a pipe or socket handle (or
something that the <c>select</c> system call understands).</p>
- <p>On Windows the <c>event</c> is an Event or Semaphore (or
- something that the <c>WaitForMultipleObjects</c> API
+ <p>On Windows the <c>event</c> is an <c>Event</c> or <c>Semaphore</c>
+ (or something that the <c>WaitForMultipleObjects</c> API
function understands). (Some trickery in the emulator allows
more than the built-in limit of 64 <c>Events</c> to be used.)</p>
- <p>On Enea OSE the <c>event</c> is one or more signals that can
- be retrieved using <seealso marker="ose:ose_erl_driver#erl_drv_ose_get_signal">erl_drv_ose_get_signal</seealso>.</p>
<p>To use this with threads and asynchronous routines, create a
- pipe on unix, an Event on Windows or a unique signal number on
- Enea OSE. When the routine
+ pipe on Unix and an <c>Event</c> on Windows. When the routine
completes, write to the pipe (use <c>SetEvent</c> on
- Windows or send a message to the emulator process on Enea OSE),
- this will make the emulator call
+ Windows), this makes the emulator call
<c>ready_input</c> or <c>ready_output</c>.</p>
- <p>Spurious events may happen. That is, calls to <c>ready_input</c>
- or <c>ready_output</c> even though no real events are signaled. In
- reality it should be rare (and OS dependant), but a robust driver
+ <p>False events can occur. That is, calls to <c>ready_input</c>
+ or <c>ready_output</c> although no real events are signaled. In
+ reality, it is rare (and OS-dependant), but a robust driver
must nevertheless be able to handle such cases.</p>
</item>
- <tag><marker id="driver_name"/>char *driver_name</tag>
+ <tag><marker id="driver_name"/><c>char *driver_name</c></tag>
<item>
- <p>This is the name of the driver, it must correspond to the
- atom used in <c>open_port</c>, and the name of the driver
+ <p>The driver name. It must correspond to the atom used in
+ <seealso marker="erlang#open_port/2">
+ <c>erlang:open_port/2</c></seealso>, and the name of the driver
library file (without the extension).</p>
</item>
- <tag><marker id="finish"/>void (*finish)(void)</tag>
+ <tag><marker id="finish"/><c>void (*finish)(void)</c></tag>
<item>
- <p>This function is called by the <c>erl_ddll</c> driver when the
+ <p>Called by the <c>erl_ddll</c> driver when the
driver is unloaded. (It is only called in dynamic drivers.)</p>
<p>The driver is only unloaded as a result of calling
- <c>unload_driver/1</c>, or when the emulator halts.</p>
+ <seealso marker="kernel:erl_ddll#unload_driver/1">
+ <c>erl_ddll:unload_driver/1</c></seealso>,
+ or when the emulator halts.</p>
</item>
- <tag>void *handle</tag>
+ <tag><c>void *handle</c></tag>
<item>
<p>This field is reserved for the emulator's internal use. The
- emulator will modify this field; therefore, it is important
- that the <c>driver_entry</c> isn't declared <c>const</c>.</p>
+ emulator will modify this field, so it is important
+ that the <c>driver_entry</c> is not declared <c>const</c>.</p>
</item>
- <tag><marker id="control"></marker>ErlDrvSSizeT (*control)(ErlDrvData drv_data, unsigned int command, char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen)</tag>
+ <tag><marker id="control"></marker>
+ <c>ErlDrvSSizeT (*control)(ErlDrvData drv_data, unsigned int command,
+ char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen)</c></tag>
<item>
- <p>This is a special routine invoked with the erlang function
- <c>port_control/3</c>. It works a little like an "ioctl" for
- erlang drivers. The data given to <c>port_control/3</c>
- arrives in <c>buf</c> and <c>len</c>. The driver may send
+ <p>A special routine invoked with
+ <seealso marker="erlang#port_control/3">
+ <c>erlang:port_control/3</c></seealso>.
+ It works a little like an "ioctl" for
+ Erlang drivers. The data specified to <c>port_control/3</c>
+ arrives in <c>buf</c> and <c>len</c>. The driver can send
data back, using <c>*rbuf</c> and <c>rlen</c>.</p>
<p>This is the fastest way of calling a driver and get a
- response. It won't make any context switch in the erlang
- emulator, and requires no message passing. It is suitable
- for calling C function to get faster execution, when erlang
+ response. It makes no context switch in the Erlang
+ emulator and requires no message passing. It is suitable
+ for calling C function to get faster execution, when Erlang
is too slow.</p>
- <p>If the driver wants to return data, it should return it in
+ <p>If the driver wants to return data, it is to return it in
<c>rbuf</c>. When <c>control</c> is called,
<c>*rbuf</c> points to a default buffer of <c>rlen</c> bytes, which
- can be used to return data. Data is returned different depending on
+ can be used to return data. Data is returned differently depending on
the port control flags (those that are set with
- <seealso marker="erl_driver#set_port_control_flags">set_port_control_flags</seealso>).
- </p>
+ <seealso marker="erl_driver#set_port_control_flags">
+ <c>erl_driver:set_port_control_flags</c></seealso>).</p>
<p>If the flag is set to <c>PORT_CONTROL_FLAG_BINARY</c>,
- a binary will be returned. Small binaries can be returned by writing
- the raw data into the default buffer. A binary can also be
- returned by setting <c>*rbuf</c> to point to a binary allocated with
- <seealso marker="erl_driver#driver_alloc_binary">driver_alloc_binary</seealso>.
- This binary will be freed automatically after <c>control</c> has returned.
+ a binary is returned. Small binaries can be returned by writing
+ the raw data into the default buffer. A binary can also be
+ returned by setting <c>*rbuf</c> to point to a binary allocated with
+ <seealso marker="erl_driver#driver_alloc_binary">
+ <c>erl_driver:driver_alloc_binary</c></seealso>.
+ This binary is freed automatically after <c>control</c> has returned.
The driver can retain the binary for <em>read only</em> access with
- <seealso marker="erl_driver#driver_binary_inc_refc">driver_binary_inc_refc</seealso> to be freed later with
- <seealso marker="erl_driver#driver_free_binary">driver_free_binary</seealso>.
- It is never allowed to alter the binary after <c>control</c> has returned.
- If <c>*rbuf</c> is set to NULL, an empty list will be returned.
- </p>
+ <seealso marker="erl_driver#driver_binary_inc_refc">
+ <c>erl_driver:driver_binary_inc_refc</c></seealso> to be freed later
+ with <seealso marker="erl_driver#driver_free_binary">
+ <c>erl_driver:driver_free_binary</c></seealso>.
+ It is never allowed to change the binary after <c>control</c> has
+ returned. If <c>*rbuf</c> is set to <c>NULL</c>, an empty list is
+ returned.</p>
<p>If the flag is set to <c>0</c>, data is returned as a
list of integers. Either use the default buffer or set
<c>*rbuf</c> to point to a larger buffer allocated with
- <seealso marker="erl_driver#driver_alloc">driver_alloc</seealso>.
- The buffer will be freed automatically after <c>control</c> has returned.</p>
+ <seealso marker="erl_driver#driver_alloc">
+ <c>erl_driver:driver_alloc</c></seealso>. The
+ buffer is freed automatically after <c>control</c> has returned.</p>
<p>Using binaries is faster if more than a few bytes are returned.</p>
- <p>The return value is the number of bytes returned in
- <c>*rbuf</c>.</p>
+ <p>The return value is the number of bytes returned in <c>*rbuf</c>.</p>
</item>
-
- <tag><marker id="timeout"/>void (*timeout)(ErlDrvData drv_data)</tag>
+ <tag><marker id="timeout"/><c>void (*timeout)(ErlDrvData drv_data)</c>
+ </tag>
<item>
- <p>This function is called any time after the driver's timer
- reaches 0. The timer is activated with
- <c>driver_set_timer</c>. There are no priorities or ordering
- among drivers, so if several drivers time out at the same
- time, any one of them is called first.</p>
+ <p>Called any time after the driver's timer reaches <c>0</c>.
+ The timer is activated with
+ <seealso marker="erl_driver#driver_set_timer">
+ <c>erl_driver:driver_set_timer</c></seealso>. No priorities or
+ ordering exist among drivers, so if several drivers time out at
+ the same time, anyone of them is called first.</p>
</item>
-
- <tag><marker id="outputv"/>void (*outputv)(ErlDrvData drv_data, ErlIOVec *ev)</tag>
+ <tag><marker id="outputv"/>
+ <c>void (*outputv)(ErlDrvData drv_data, ErlIOVec *ev)</c></tag>
<item>
- <p>This function is called whenever the port is written to. If
+ <p>Called whenever the port is written to. If
it is <c>NULL</c>, the <c>output</c> function is called
- instead. This function is faster than <c>output</c>, because
+ instead. This function is faster than <c>output</c>, as
it takes an <c>ErlIOVec</c> directly, which requires no
- copying of the data. The port should be in binary mode, see
- <c>open_port/2</c>.</p>
- <p>The <c>ErlIOVec</c> contains both a <c>SysIOVec</c>,
+ copying of the data. The port is to be in binary mode, see
+ <seealso marker="erlang#open_port/2">
+ <c>erlang:open_port/2</c></seealso>.</p>
+ <p><c>ErlIOVec</c> contains both a <c>SysIOVec</c>,
suitable for <c>writev</c>, and one or more binaries. If
- these binaries should be retained, when the driver returns
- from <c>outputv</c>, they can be queued (using <seealso marker="erl_driver#driver_enq_bin">driver_enq_bin</seealso>
- for instance), or if they are kept in a static or global
+ these binaries are to be retained when the driver returns
+ from <c>outputv</c>, they can be queued (using, for example,
+ <seealso marker="erl_driver#driver_enq_bin">
+ <c>erl_driver:driver_enq_bin</c></seealso>)
+ or, if they are kept in a static or global
variable, the reference counter can be incremented.</p>
</item>
- <tag><marker id="ready_async"/>void (*ready_async)(ErlDrvData drv_data, ErlDrvThreadData thread_data)</tag>
+ <tag><marker id="ready_async"/>
+ <c>void (*ready_async)(ErlDrvData drv_data, ErlDrvThreadData
+ thread_data)</c></tag>
<item>
- <p>This function is called after an asynchronous call has
- completed. The asynchronous call is started with <seealso marker="erl_driver#driver_async">driver_async</seealso>.
- This function is called from the erlang emulator thread, as
+ <p>Called after an asynchronous call has completed.
+ The asynchronous call is started with
+ <seealso marker="erl_driver#driver_async">
+ <c>erl_driver:driver_async</c></seealso>.
+ This function is called from the Erlang emulator thread, as
opposed to the asynchronous function, which is called in
- some thread (if multithreading is enabled).</p>
+ some thread (if multi-threading is enabled).</p>
</item>
- <tag><marker id="call"/>ErlDrvSSizeT (*call)(ErlDrvData drv_data, unsigned int command, char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen, unsigned int *flags)</tag>
+ <tag><c>void (*flush)(ErlDrvData drv_data)</c></tag>
<item>
- <p>This function is called from <c>erlang:port_call/3</c>. It
- works a lot like the <c>control</c> call-back, but uses the
+ <p>Called when the port is about to be closed,
+ and there is data in the driver queue that must be flushed
+ before 'stop' can be called.</p>
+ </item>
+ <tag><marker id="call"/><c>ErlDrvSSizeT (*call)(ErlDrvData drv_data,
+ unsigned int command, char *buf, ErlDrvSizeT len, char **rbuf,
+ ErlDrvSizeT rlen, unsigned int *flags)</c></tag>
+ <item>
+ <p>Called from <seealso marker="erlang#port_call/3">
+ <c>erlang:port_call/3</c></seealso>.
+ It works a lot like the <c>control</c> callback, but uses the
external term format for input and output.</p>
<p><c>command</c> is an integer, obtained from the call from
- erlang (the second argument to <c>erlang:port_call/3</c>).</p>
+ Erlang (the second argument to <c>erlang:port_call/3</c>).</p>
<p><c>buf</c> and <c>len</c> provide the arguments to the call
(the third argument to <c>erlang:port_call/3</c>). They can
be decoded using <c>ei</c> functions.</p>
<p><c>rbuf</c> points to a return buffer, <c>rlen</c> bytes
- long. The return data should be a valid erlang term in the
- external (binary) format. This is converted to an erlang
+ long. The return data is to be a valid Erlang term in the
+ external (binary) format. This is converted to an Erlang
term and returned by <c>erlang:port_call/3</c> to the
- caller. If more space than <c>rlen</c> bytes is needed to
+ caller. If more space than <c>rlen</c> bytes is needed to
return data, <c>*rbuf</c> can be set to memory allocated with
- <c>driver_alloc</c>. This memory will be freed automatically
- after <c>call</c> has returned.</p>
+ <seealso marker="erl_driver#driver_alloc">
+ <c>erl_driver:driver_alloc</c></seealso>.
+ This memory is freed automatically after <c>call</c> has returned.</p>
<p>The return value is the number of bytes returned in
<c>*rbuf</c>. If <c>ERL_DRV_ERROR_GENERAL</c> is returned
- (or in fact, anything &lt; 0), <c>erlang:port_call/3</c> will
- throw a <c>BAD_ARG</c>.</p>
+ (or in fact, anything &lt; 0), <c>erlang:port_call/3</c>
+ throws a <c>BAD_ARG</c>.</p>
</item>
- <tag>void (*event)(ErlDrvData drv_data, ErlDrvEvent event, ErlDrvEventData event_data)</tag>
+ <tag><c>void (*event)(ErlDrvData drv_data, ErlDrvEvent event,
+ ErlDrvEventData event_data)</c></tag>
<item>
<p>Intentionally left undocumented.</p>
</item>
- <tag><marker id="extended_marker"/>int extended_marker</tag>
+ <tag><marker id="extended_marker"/><c>int extended_marker</c></tag>
<item>
- <p>
- This field should either be equal to <c>ERL_DRV_EXTENDED_MARKER</c>
+ <p>This field is either to be equal to <c>ERL_DRV_EXTENDED_MARKER</c>
or <c>0</c>. An old driver (not aware of the extended driver
- interface) should set this field to <c>0</c>. If this field is
- equal to <c>0</c>, all the fields following this field also
- <em>have</em> to be <c>0</c>, or <c>NULL</c> in case it is a
- pointer field.
- </p>
+ interface) is to set this field to <c>0</c>. If this field is
+ <c>0</c>, all the following fields <em>must</em> also be <c>0</c>,
+ or <c>NULL</c> if it is a pointer field.</p>
</item>
- <tag>int major_version</tag>
+ <tag><c>int major_version</c></tag>
<item>
- <p>This field should equal <c>ERL_DRV_EXTENDED_MAJOR_VERSION</c> if
- the <c>extended_marker</c> field equals
+ <p>This field is to equal <c>ERL_DRV_EXTENDED_MAJOR_VERSION</c> if
+ field <c>extended_marker</c> equals
<c>ERL_DRV_EXTENDED_MARKER</c>.</p>
</item>
- <tag>int minor_version</tag>
+ <tag><c>int minor_version</c></tag>
<item>
- <p>
- This field should equal <c>ERL_DRV_EXTENDED_MINOR_VERSION</c> if
- the <c>extended_marker</c> field equals
- <c>ERL_DRV_EXTENDED_MARKER</c>.
- </p>
+ <p>This field is to equal <c>ERL_DRV_EXTENDED_MINOR_VERSION</c> if
+ field <c>extended_marker</c> equals
+ <c>ERL_DRV_EXTENDED_MARKER</c>.</p>
</item>
-
- <tag><marker id="driver_flags"/>int driver_flags</tag>
+ <tag><marker id="driver_flags"/><c>int driver_flags</c></tag>
<item>
<p>This field is used to pass driver capability and other
- information to the runtime system. If the
- <c>extended_marker</c> field equals <c>ERL_DRV_EXTENDED_MARKER</c>,
- it should contain <c>0</c> or driver flags (<c>ERL_DRV_FLAG_*</c>)
- ored bitwise. Currently the following driver flags exist:
- </p>
+ information to the runtime system. If
+ field <c>extended_marker</c> equals <c>ERL_DRV_EXTENDED_MARKER</c>,
+ it is to contain <c>0</c> or driver flags (<c>ERL_DRV_FLAG_*</c>)
+ OR'ed bitwise. The following driver flags exist:</p>
<taglist>
<tag><c>ERL_DRV_FLAG_USE_PORT_LOCKING</c></tag>
<item>
- The runtime system will use port level locking on
- all ports executing this driver instead of driver
- level locking when the driver is run in a runtime
- system with SMP support. For more information see the
- <seealso marker="erl_driver#smp_support">erl_driver</seealso>
- documentation.
- </item>
+ <p>The runtime system uses port-level locking on
+ all ports executing this driver instead of driver-level
+ locking when the driver is run in a runtime
+ system with SMP support. For more information, see
+ <seealso marker="erl_driver#smp_support">
+ <c>erl_driver</c></seealso>.</p>
+ </item>
<tag><c>ERL_DRV_FLAG_SOFT_BUSY</c></tag>
<item>
- Marks that driver instances can handle being called
- in the <seealso marker="#output">output</seealso> and/or
- <seealso marker="#outputv">outputv</seealso> callbacks even
- though a driver instance has marked itself as busy (see
- <seealso marker="erl_driver#set_busy_port">set_busy_port()</seealso>).
- Since erts version 5.7.4 this flag is required for drivers used
- by the Erlang distribution (the behaviour has always been
- required by drivers used by the distribution).
+ <p>Marks that driver instances can handle being called
+ in the <seealso marker="#output"><c>output</c></seealso> and/or
+ <seealso marker="#outputv"><c>outputv</c></seealso> callbacks
+ although a driver instance has marked itself as busy (see
+ <seealso marker="erl_driver#set_busy_port">
+ <c>erl_driver:set_busy_port</c></seealso>).
+ As from ERTS 5.7.4 this flag is required for drivers used
+ by the Erlang distribution (the behavior has always been
+ required by drivers used by the distribution).</p>
</item>
<tag><c>ERL_DRV_FLAG_NO_BUSY_MSGQ</c></tag>
- <item>Disable busy port message queue functionality. For
- more information, see the documentation of the
- <seealso marker="erl_driver#erl_drv_busy_msgq_limits">erl_drv_busy_msgq_limits()</seealso>
- function.
+ <item>
+ <p>Disables busy port message queue functionality. For
+ more information, see
+ <seealso marker="erl_driver#erl_drv_busy_msgq_limits">
+ <c>erl_driver:erl_drv_busy_msgq_limits</c></seealso>.</p>
+ </item>
+ <tag><c>ERL_DRV_FLAG_USE_INIT_ACK</c></tag>
+ <item>
+ <p>When this flag is specified, the linked-in driver must manually
+ acknowledge that the port has been successfully started using
+ <seealso marker="erl_driver#erl_drv_init_ack">
+ <c>erl_driver:erl_drv_init_ack()</c></seealso>.
+ This allows the implementor to make the
+ <c>erlang:open_port</c> exit with <c>badarg</c> after some
+ initial asynchronous initialization has been done.</p>
</item>
- </taglist>
+ </taglist>
</item>
- <tag>void *handle2</tag>
+ <tag><c>void *handle2</c></tag>
<item>
- <p>
- This field is reserved for the emulator's internal use. The
- emulator will modify this field; therefore, it is important
- that the <c>driver_entry</c> isn't declared <c>const</c>.
- </p>
+ <p>This field is reserved for the emulator's internal use. The
+ emulator modifies this field, so it is important
+ that the <c>driver_entry</c> is not declared <c>const</c>.</p>
</item>
- <tag><marker id="process_exit"/>void (*process_exit)(ErlDrvData drv_data, ErlDrvMonitor *monitor)</tag>
+ <tag><marker id="process_exit"/>
+ <c>void (*process_exit)(ErlDrvData drv_data, ErlDrvMonitor *monitor)</c>
+ </tag>
<item>
- <p>This callback is called when a monitored process exits. The
+ <p>Called when a monitored process exits. The
<c>drv_data</c> is the data associated with the port for which
- the process is monitored (using <seealso marker="erl_driver#driver_monitor_process">driver_monitor_process</seealso>)
- and the <c>monitor</c> corresponds to the <c>ErlDrvMonitor</c>
+ the process is monitored (using
+ <seealso marker="erl_driver#driver_monitor_process">
+ <c>erl_driver:driver_monitor_process</c></seealso>)
+ and the <c>monitor</c> corresponds to the <c>ErlDrvMonitor</c>
structure filled
in when creating the monitor. The driver interface function
- <seealso marker="erl_driver#driver_get_monitored_process">driver_get_monitored_process</seealso>
- can be used to retrieve the process id of the exiting process as
+ <seealso marker="erl_driver#driver_get_monitored_process">
+ <c>erl_driver:driver_get_monitored_process</c></seealso>
+ can be used to retrieve the process ID of the exiting process as
an <c>ErlDrvTermData</c>.</p>
</item>
- <tag><marker id="stop_select"/>void (*stop_select)(ErlDrvEvent event, void* reserved)</tag>
+ <tag><marker id="stop_select"/>
+ <c>void (*stop_select)(ErlDrvEvent event, void* reserved)</c></tag>
<item>
- <p>This function is called on behalf of
- <seealso marker="erl_driver#driver_select">driver_select</seealso>
- when it is safe to close an event object.</p>
+ <p>Called on behalf of
+ <seealso marker="erl_driver#driver_select">
+ <c>erl_driver:driver_select</c></seealso>
+ when it is safe to close an event object.</p>
<p>A typical implementation on Unix is to do
- <c>close((int)event)</c>.</p>
- <p>Argument <c>reserved</c> is intended for future use and should be ignored.</p>
- <p>In contrast to most of the other call-back functions,
- <c>stop_select</c> is called independent of any port. No
- <c>ErlDrvData</c> argument is passed to the function. No
- driver lock or port lock is guaranteed to be held. The port that
- called <c>driver_select</c> might even be closed at the
- time <c>stop_select</c> is called. But it could also be
- the case that <c>stop_select</c> is called directly by
- <c>driver_select</c>.</p>
+ <c>close((int)event)</c>.</p>
+ <p>Argument <c>reserved</c> is intended for future use and is to be
+ ignored.</p>
+ <p>In contrast to most of the other callback functions,
+ <c>stop_select</c> is called independent of any port. No
+ <c>ErlDrvData</c> argument is passed to the function. No
+ driver lock or port lock is guaranteed to be held. The port that
+ called <c>driver_select</c> can even be closed at the
+ time <c>stop_select</c> is called. But it can also be
+ the case that <c>stop_select</c> is called directly by
+ <c>erl_driver:driver_select</c>.</p>
<p>It is not allowed to call any functions in the
- <seealso marker="erl_driver">driver API</seealso> from
- <c>stop_select</c>. This strict limitation is due to the
- volatile context that <c>stop_select</c> may be called.</p>
+ <seealso marker="erl_driver">driver API</seealso> from
+ <c>stop_select</c>. This strict limitation is because the
+ volatile context that <c>stop_select</c> can be called.</p>
</item>
-
- </taglist>
- </item>
-
</taglist>
</section>
<section>
- <title>SEE ALSO</title>
- <p><seealso marker="erl_driver">erl_driver(3)</seealso>,
- <seealso marker="kernel:erl_ddll">erl_ddll(3)</seealso>,
- <seealso marker="erlang">erlang(3)</seealso>,
- kernel(3)</p>
+ <title>See Also</title>
+ <p><seealso marker="erl_driver"><c>erl_driver(3)</c></seealso>,
+ <seealso marker="erlang"><c>erlang(3)</c></seealso>,
+ <seealso marker="kernel:erl_ddll"><c>erl_ddll(3)</c></seealso></p>
</section>
</cref>
diff --git a/erts/doc/src/epmd.xml b/erts/doc/src/epmd.xml
index 7f61804bea..311483022d 100644
--- a/erts/doc/src/epmd.xml
+++ b/erts/doc/src/epmd.xml
@@ -4,7 +4,7 @@
<comref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -28,242 +28,259 @@
<docno>1</docno>
<approved></approved>
<checked></checked>
- <date>98-01-05</date>
+ <date>1998-01-05</date>
<rev>A</rev>
<file>epmd.xml</file>
</header>
<com>epmd</com>
- <comsummary>
- <p>Erlang Port Mapper Daemon</p>
- <taglist>
- <tag><c><![CDATA[epmd [-d|-debug] [DbgExtra...] [-address Addresses] [-port No] [-daemon] [-relaxed_command_check]]]></c></tag>
- <item>
- <p>Starts the port mapper daemon</p>
- </item>
- <tag><c><![CDATA[epmd [-d|-debug] [-port No] [-names|-kill|-stop Name]]]></c></tag>
- <item>
- <p>Communicates with a running port mapper daemon</p>
- </item>
- </taglist>
- </comsummary>
+ <comsummary>Erlang Port Mapper Daemon</comsummary>
+
<description>
- <p>This daemon acts as a name server on all hosts involved in
- distributed Erlang computations. When an Erlang node
- starts, the node has a name and it obtains an address from the host
- OS kernel.
- The name and the address are sent to the
+ <taglist>
+ <tag><c><![CDATA[epmd [-d|-debug] [DbgExtra...] [-address Addresses]
+ [-port No] [-daemon] [-relaxed_command_check]]]></c></tag>
+ <item>
+ <p>Starts the port mapper daemon.</p>
+ </item>
+ <tag><c><![CDATA[epmd [-d|-debug] [-port No]
+ [-names|-kill|-stop Name]]]></c></tag>
+ <item>
+ <p>Communicates with a running port mapper daemon.</p>
+ </item>
+ </taglist>
+
+ <p>This daemon acts as a name server on all hosts involved in
+ distributed Erlang computations. When an Erlang node starts,
+ the node has a name and it obtains an address from the host
+ OS kernel. The name and address are sent to the
<c><![CDATA[epmd]]></c> daemon running on the local host.
In a TCP/IP environment, the address consists
- of the IP address and a port number. The name of the node is
+ of the IP address and a port number. The node name is
an atom on the form of <c><![CDATA[Name@Node]]></c>.
The job of the <c><![CDATA[epmd]]></c> daemon is to keep track of which
node name listens on which address. Hence, <c><![CDATA[epmd]]></c> maps
symbolic node names to machine addresses.</p>
- <p>The TCP/IP <c>epmd</c> daemon actually only keeps track of
+ <p>The TCP/IP <c>epmd</c> daemon only keeps track of
the <c>Name</c> (first) part of an Erlang node name. The <c>Host</c>
part (whatever is after the <c><![CDATA[@]]></c>) is implicit in the
- node name where the <c>epmd</c> daemon was actually contacted,
+ node name where the <c>epmd</c> daemon was contacted,
as is the IP address where the Erlang node can be
reached. Consistent and correct TCP naming services are
therefore required for an Erlang network to function
correctly.</p>
<taglist>
- <tag>Starting the port mapper daemon</tag>
- <item>
-
- <p>The daemon is started automatically by the <c>erl</c>
- command if the node is to be distributed and there is no
- running instance present. If automatically launched,
- environment variables have to be used to alter the behavior of
- the daemon. See the <seealso
- marker="#environment_variables">Environment
- variables</seealso> section below.</p>
-
- <p>If the -daemon argument is not given,
- <c><![CDATA[epmd]]></c> runs as a normal program with the
- controlling terminal of the shell in which it is
- started. Normally, it should run as a daemon.</p>
-
- <p>Regular start-up options are described in the
- <seealso marker="#daemon_flags">Regular options</seealso>
- section below.</p>
-
- <p>The <c>DbgExtra</c> options are described in the
- <seealso marker="#debug_flags">DbgExtra options</seealso>
- section below.</p>
-
- </item>
- <tag>Communicating with a running port mapper daemon</tag>
- <item>
-
- <p>Communicating with the running epmd daemon by means of the
- <c>epmd</c> program is done primarily for debugging
- purposes.</p>
-
- <p>The different queries are described in the <seealso
- marker="#interactive_flags">Interactive options</seealso>
- section below.</p>
-
- </item>
- </taglist>
+ <tag>Starting the port mapper daemon</tag>
+ <item>
+ <p>The daemon is started automatically by command
+ <seealso marker="erl"><c>erl(1)</c></seealso>
+ if the node is to be distributed and no running
+ instance is present. If automatically launched
+ environment variables must be used to change the behavior
+ of the daemon; see section
+ <seealso marker="#environment_variables">Environment
+ Variables</seealso>.</p>
+ <p>If argument <c>-daemon</c> is not specified,
+ <c><![CDATA[epmd]]></c> runs as a normal program with the
+ controlling terminal of the shell in which it is
+ started. Normally, it is to be run as a daemon.</p>
+ <p>Regular startup options are described in section
+ <seealso marker="#daemon_flags">Regular Options</seealso>.</p>
+ <p>The <c>DbgExtra</c> options are described in section
+ <seealso marker="#debug_flags">DbgExtra Options</seealso>.</p>
+ </item>
+ <tag>Communicating with a running port mapper daemon</tag>
+ <item>
+ <p>Communicating with the running <c>epmd</c> daemon by the
+ <c>epmd</c> program is done primarily for debugging purposes.</p>
+ <p>The different queries are described in section <seealso
+ marker="#interactive_flags">Interactive options</seealso>.</p>
+ </item>
+ </taglist>
</description>
+
<section>
<marker id="daemon_flags"></marker>
- <title>Regular options</title>
+ <title>Regular Options</title>
+ <p>These options are available when starting the name server. The name
+ server is normally started automatically by command
+ <seealso marker="erl"><c>erl(1)</c></seealso> (if not already available),
+ but it can also be started at system startup.</p>
- <p>These options are available when starting the actual name server. The name server is normally started automatically by the <c>erl</c> command (if not already available), but it can also be started at i.e. system start-up.</p>
<taglist>
- <tag><c><![CDATA[-address List]]></c></tag>
- <item>
- <p>Let this instance of <c>epmd</c> listen only on the
- comma-separated list of IP addresses and on the loopback address
- (which is implicitly added to the list if it has not been
- specified). This can also be set using the
- <c><![CDATA[ERL_EPMD_ADDRESS]]></c> environment variable. See the
- section <seealso marker="#environment_variables">Environment
- variables</seealso> below.</p>
- </item>
- <tag><c><![CDATA[-port No]]></c></tag>
- <item>
- <p>Let this instance of epmd listen to another TCP port than
- default 4369. This can also be set using the
- <c><![CDATA[ERL_EPMD_PORT]]></c> environment variable. See the
- section <seealso marker="#environment_variables">Environment
- variables</seealso> below</p>
- </item>
- <tag><c><![CDATA[-d | -debug]]></c></tag>
- <item>
-
- <p>Enable debug output. The more <c>-d</c> flags given, the more
- debug output you will get (to a certain limit). This option is
- most useful when the epmd daemon is not started as a daemon.</p>
- </item>
- <tag><c><![CDATA[-daemon]]></c></tag>
- <item>
- <p>Start epmd detached from the controlling terminal. Logging will end up in syslog when available and correctly configured. If the epmd daemon is started at boot, this option should definitely be used. It is also used when the <c>erl</c> command automatically starts <c>epmd</c>.</p>
- </item>
- <tag><c><![CDATA[-relaxed_command_check]]></c></tag>
- <item>
- <p>Start the epmd program with relaxed command checking (mostly for backward compatibility). This affects the following:</p>
- <list type="bulleted">
- <item>
- <p>With relaxed command checking, the <c>epmd</c> daemon can be killed from the localhost with i.e. <c>epmd -kill</c> even if there are active nodes registered. Normally only daemons with an empty node database can be killed with the <c>epmd -kill</c> command.</p>
+ <tag><c><![CDATA[-address List]]></c></tag>
+ <item>
+ <p>Lets this instance of <c>epmd</c> listen only on the
+ comma-separated list of IP addresses and on the loopback address
+ (which is implicitly added to the list if it has not been
+ specified). This can also be set using environment variable
+ <c><![CDATA[ERL_EPMD_ADDRESS]]></c>; see section <seealso
+ marker="#environment_variables">Environment Variables</seealso>.</p>
+ </item>
+ <tag><c><![CDATA[-port No]]></c></tag>
+ <item>
+ <p>Lets this instance of <c>epmd</c> listen to another TCP port than
+ default 4369. This can also be set using environment variable
+ <c><![CDATA[ERL_EPMD_PORT]]></c>; see section <seealso
+ marker="#environment_variables">Environment Variables</seealso>.</p>
+ </item>
+ <tag><c><![CDATA[-d | -debug]]></c></tag>
+ <item>
+ <p>Enables debug output. The more <c>-d</c> flags specified, the more
+ debug output you will get (to a certain limit). This option is most
+ useful when the <c>epmd</c> daemon is not started as a daemon.</p>
+ </item>
+ <tag><c><![CDATA[-daemon]]></c></tag>
+ <item>
+ <p>Starts <c>epmd</c> detached from the controlling terminal. Logging
+ ends up in syslog when available and correctly configured. If the
+ <c>epmd</c> daemon is started at boot, this option is definitely
+ to be used. It is also used when command <c>erl</c> automatically
+ starts <c>epmd</c>.</p>
+ </item>
+ <tag><c><![CDATA[-relaxed_command_check]]></c></tag>
+ <item>
+ <p>Starts the <c>epmd</c> program with relaxed command checking
+ (mostly for backward compatibility). This affects the following:</p>
+ <list type="bulleted">
+ <item>
+ <p>With relaxed command checking, the <c>epmd</c> daemon can be
+ killed from the local host with, for example, command
+ <c>epmd -kill</c> even if active nodes are registered. Normally
+ only daemons with an empty node database can be killed with
+ <c>epmd -kill</c>.</p>
</item>
<item>
- <p>The <c>epmd -stop</c> command (and the corresponding messages to epmd, as can be given using <c>erl_interface/ei</c>) is normally always ignored, as it opens up the possibility of a strange situation where two nodes of the same name can be alive at the same time. A node unregisters itself by just closing the connection to epmd, which is why the <c>stop</c> command was only intended for use in debugging situations.</p>
- <p>With relaxed command checking enabled, you can forcibly unregister live nodes.</p>
+ <p>Command <c>epmd -stop</c> (and the corresponding messages to
+ <c>epmd</c>, as can be specified using <seealso
+ marker="erl_interface:ei"><c>erl_interface:ei(3)</c></seealso>) is
+ normally always ignored. This because it can cause a strange
+ situation where two nodes of the same name can be alive at the
+ same time. A node unregisters itself by only closing the
+ connection to <c>epmd</c>, which is why command <c>stop</c>
+ was only intended for use in debugging situations.</p>
+ <p>With relaxed command checking enabled, you can forcibly
+ unregister live nodes.</p>
</item>
- </list>
- <p>Relaxed command checking can also be enabled by setting the environment variable <c>ERL_EPMD_RELAXED_COMMAND_CHECK</c> prior to starting <c>epmd</c>.</p>
- <p>Only use relaxed command checking on systems with very limited interactive usage.</p>
- </item>
- </taglist>
+ </list>
+ <p>Relaxed command checking can also be enabled by setting environment
+ variable <c>ERL_EPMD_RELAXED_COMMAND_CHECK</c> before starting
+ <c>epmd</c>.</p>
+ <p>Use relaxed command checking only on systems with very limited
+ interactive usage.</p>
+ </item>
+ </taglist>
</section>
<section>
<marker id="debug_flags"></marker>
- <title>DbgExtra options</title>
- <p>These options are purely for debugging and testing epmd clients. They should not be used in normal operation.</p>
+ <title>DbgExtra Options</title>
+ <note>
+ <p>These options are only for debugging and testing <c>epmd</c> clients.
+ They are not to be used in normal operation.</p>
+ </note>
<taglist>
- <tag><c><![CDATA[-packet_timeout Seconds]]></c></tag>
- <item>
- <p>Set the number of seconds a connection can be
- inactive before epmd times out and closes the
- connection (default 60).</p>
- </item>
- <tag><c><![CDATA[-delay_accept Seconds]]></c></tag>
- <item>
- <p>To simulate a busy server you can insert a delay between when epmd
- gets notified that a new connection is requested and
- when the connection gets accepted.</p>
- </item>
- <tag><c><![CDATA[-delay_write Seconds]]></c></tag>
- <item>
- <p>Also a simulation of a busy server. Inserts
- a delay before a reply is sent.</p>
- </item>
+ <tag><c><![CDATA[-packet_timeout Seconds]]></c></tag>
+ <item>
+ <p>Sets the number of seconds a connection can be
+ inactive before <c>epmd</c> times out and closes the
+ connection. Defaults to 60.</p>
+ </item>
+ <tag><c><![CDATA[-delay_accept Seconds]]></c></tag>
+ <item>
+ <p>To simulate a busy server, you can insert a delay between when
+ <c>epmd</c> gets notified that a new connection is requested and
+ when the connection gets accepted.</p>
+ </item>
+ <tag><c><![CDATA[-delay_write Seconds]]></c></tag>
+ <item>
+ <p>Also a simulation of a busy server. Inserts
+ a delay before a reply is sent.</p>
+ </item>
</taglist>
</section>
+
<section>
<marker id="interactive_flags"></marker>
- <title>Interactive options</title>
- <p>These options make <c>epmd</c> run as an interactive command, displaying the results of sending queries to an already running instance of <c>epmd</c>. The epmd contacted is always on the local node, but the <c>-port</c> option can be used to select between instances if several are running using different ports on the host.</p>
- <taglist>
- <tag><c><![CDATA[-port No]]></c></tag>
- <item>
- <p>Contacts the <c>epmd</c> listening on the given TCP port number
- (default 4369). This can also be set using the
- <c><![CDATA[ERL_EPMD_PORT]]></c> environment variable. See the
- section <seealso marker="#environment_variables">Environment
- variables</seealso> below.</p>
- </item>
- <tag><c><![CDATA[-names]]></c></tag>
- <item>
- <p>List names registered with the currently running epmd</p>
- </item>
- <tag><c><![CDATA[-kill]]></c></tag>
- <item>
- <p>Kill the currently running <c>epmd</c>.</p>
+ <title>Interactive Options</title>
+ <p>These options make <c>epmd</c> run as an interactive command,
+ displaying the results of sending queries to an already running
+ instance of <c>epmd</c>. The <c>epmd</c> contacted is always on the
+ local node, but option <c>-port</c> can be used to select between
+ instances if several are running using different ports on the host.</p>
- <p>Killing the running <c>epmd</c> is only allowed if <c>epmd
- -names</c> shows an empty database or
- <c>-relaxed_command_check</c> was given when the running
- instance of <c>epmd</c> was started. Note that
- <c>-relaxed_command_check</c> is given when starting the
- daemon that is to accept killing when it has live nodes
- registered. When running epmd interactively,
- <c>-relaxed_command_check</c> has no effect. A daemon that is
- started without relaxed command checking has to be killed
- using i.e. signals or some other OS specific method if it has
- active clients registered.</p>
- </item>
- <tag><c><![CDATA[-stop Name]]></c></tag>
- <item>
- <p>Forcibly unregister a live node from <c>epmd</c>'s database</p>
-
- <p>This command can only be used when contacting <c>epmd</c>
- instances started with the <c>-relaxed_command_check</c>
- flag. Note that relaxed command checking has to be enabled for
- the <c>epmd</c> daemon contacted. When running epmd
- interactively,
- <c>-relaxed_command_check</c> has no effect.</p>
- </item>
- </taglist>
+ <taglist>
+ <tag><c><![CDATA[-port No]]></c></tag>
+ <item>
+ <p>Contacts the <c>epmd</c> listening on the specified TCP port
+ number (default 4369). This can also be set using environment
+ variable <c><![CDATA[ERL_EPMD_PORT]]></c>; see section <seealso
+ marker="#environment_variables">Environment Variables</seealso>.</p>
+ </item>
+ <tag><c><![CDATA[-names]]></c></tag>
+ <item>
+ <p>Lists names registered with the currently running <c>epmd</c>.</p>
+ </item>
+ <tag><c><![CDATA[-kill]]></c></tag>
+ <item>
+ <p>Kills the currently running <c>epmd</c>.</p>
+ <p>Killing the running <c>epmd</c> is only allowed if
+ <c>epmd -names</c> shows an empty database or if
+ <c>-relaxed_command_check</c> was specified when the running
+ instance of <c>epmd</c> was started.</p>
+ <p>Notice that <c>-relaxed_command_check</c> is specified when
+ starting the daemon that is to accept killing when it has live
+ nodes registered. When running <c>epmd</c> interactively,
+ <c>-relaxed_command_check</c> has no effect. A daemon that is
+ started without relaxed command checking must be killed using,
+ for example, signals or some other OS-specific method if it has
+ active clients registered.</p>
+ </item>
+ <tag><c><![CDATA[-stop Name]]></c></tag>
+ <item>
+ <p>Forcibly unregisters a live node from the <c>epmd</c> database.</p>
+ <p>This command can only be used when contacting <c>epmd</c>
+ instances started with flag <c>-relaxed_command_check</c>.</p>
+ <p>Notice that relaxed command checking must enabled for the
+ <c>epmd</c> daemon contacted. When running <c>epmd</c>
+ interactively, <c>-relaxed_command_check</c> has no effect.</p>
+ </item>
+ </taglist>
</section>
+
<section>
<marker id="environment_variables"></marker>
- <title>Environment variables</title>
+ <title>Environment Variables</title>
<taglist>
<tag><c><![CDATA[ERL_EPMD_ADDRESS]]></c></tag>
<item>
- <p>This environment variable may be set to a comma-separated
- list of IP addresses, in which case the <c>epmd</c> daemon
- will listen only on the specified address(es) and on the
- loopback address (which is implicitly added to the list if it
- has not been specified). The default behaviour is to listen on
- all available IP addresses.</p>
+ <p>Can be set to a comma-separated
+ list of IP addresses, in which case the <c>epmd</c> daemon
+ will listen only on the specified address(es) and on the
+ loopback address (which is implicitly added to the list if it
+ has not been specified). The default behavior is to listen on
+ all available IP addresses.</p>
</item>
<tag><c><![CDATA[ERL_EPMD_PORT]]></c></tag>
<item>
- <p>This environment variable can contain the port number epmd will use.
- The default port will work fine in most cases. A different port can
- be specified to allow several instances of epmd, representing
- independent clusters of nodes, to co-exist on the same host.
- All nodes in a cluster must use the same epmd port number.</p>
+ <p>Can contain the port number <c>epmd</c> will use.
+ The default port will work fine in most cases. A different port can
+ be specified to allow several instances of <c>epmd</c>, representing
+ independent clusters of nodes, to co-exist on the same host.
+ All nodes in a cluster must use the same <c>epmd</c> port number.</p>
</item>
<tag><c><![CDATA[ERL_EPMD_RELAXED_COMMAND_CHECK]]></c></tag>
<item>
- <p>If set prior to start, the <c>epmd</c> daemon will behave
- as if the <c>-relaxed_command_check</c> option was given at
- start-up. Consequently, if this option is set before starting
- the Erlang virtual machine, the automatically started
- <c>epmd</c> will accept the <c>-kill</c> and <c>-stop</c>
- commands without restrictions.</p>
+ <p>If set before start, the <c>epmd</c> daemon behaves
+ as if option <c>-relaxed_command_check</c> was specified at
+ startup. Consequently, if this option is set before starting
+ the Erlang virtual machine, the automatically started
+ <c>epmd</c> accepts the <c>-kill</c> and <c>-stop</c>
+ commands without restrictions.</p>
</item>
</taglist>
</section>
@@ -271,41 +288,45 @@
<section>
<title>Logging</title>
<p>On some operating systems <em>syslog</em> will be used for
- error reporting when epmd runs as an daemon. To enable
- the error logging you have to edit <path unix="" windows="">/etc/syslog.conf</path>
- file and add an entry</p>
+ error reporting when <c>epmd</c> runs as a daemon. To enable
+ the error logging, you must edit the
+ <path unix="" windows="">/etc/syslog.conf</path> file and add an
+ entry:</p>
+
<code type="none"><![CDATA[
- !epmd
- *.*<TABs>/var/log/epmd.log
- ]]></code>
- <p>where &lt;TABs&gt; are at least one real tab character. Spaces will
- silently be ignored.
- </p>
+ !epmd
+ *.*<TABs>/var/log/epmd.log
+]]></code>
+
+ <p>where <c>&lt;TABs&gt;</c> are at least one real tab character.
+ Spaces are silently ignored.</p>
</section>
+
<section>
- <title>Access restrictions</title>
- <p>The <c>epmd</c> daemon accepts messages from both localhost and
- remote hosts. However, only the query commands are answered (and
- acted upon) if the query comes from a remote host. It is always an
- error to try to register a nodename if the client is not a process
- located on the same host as the <c>epmd</c> instance is running on-
- such requests are considered hostile and the connection is
- immediately closed.</p>
+ <title>Access Restrictions</title>
+ <p>The <c>epmd</c> daemon accepts messages from both the local host and
+ remote hosts. However, only the query commands are answered (and
+ acted upon) if the query comes from a remote host. It is always an
+ error to try to register a node name if the client is not a process
+ on the same host as the <c>epmd</c> instance is running on. Such
+ requests are considered hostile and the connection is closed
+ immediately.</p>
- <p>The queries accepted from remote nodes are:</p>
- <list type="bulleted">
- <item>
- <p>Port queries - i.e. on which port does the node with a given
- name listen</p>
- </item>
- <item>
- <p>Name listing - i.e. give a list of all names registered on
+ <p>The following queries are accepted from remote nodes:</p>
+
+ <list type="bulleted">
+ <item>
+ <p>Port queries, that is, on which port the node with a specified
+ name listens</p>
+ </item>
+ <item>
+ <p>Name listing, that is, gives a list of all names registered on
the host</p>
- </item>
- </list>
- <p>To restrict access further, firewall software has to be used.</p>
- </section>
+ </item>
+ </list>
+ <p>To restrict access further, firewall software must be used.</p>
+ </section>
</comref>
diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml
index ed3e7e34c4..8da832ac37 100644
--- a/erts/doc/src/erl.xml
+++ b/erts/doc/src/erl.xml
@@ -4,7 +4,7 @@
<comref>
<header>
<copyright>
- <year>1996</year><year>2015</year>
+ <year>1996</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,86 +30,92 @@
<file>erl.xml</file>
</header>
<com>erl</com>
- <comsummary>The Erlang Emulator</comsummary>
+ <comsummary>The Erlang emulator.</comsummary>
<description>
<p>The <c><![CDATA[erl]]></c> program starts an Erlang runtime system.
- The exact details (for example, whether <c><![CDATA[erl]]></c> is a script or
- a program and which other programs it calls) are system-dependent.</p>
- <p>Windows users probably wants to use the <c><![CDATA[werl]]></c> program
+ The exact details (for example, whether <c><![CDATA[erl]]></c> is a
+ script or a program and which other programs it calls) are
+ system-dependent.</p>
+
+ <p>Windows users probably want to use the <c><![CDATA[werl]]></c> program
instead, which runs in its own window with scrollbars and supports
- command-line editing. The <c><![CDATA[erl]]></c> program on Windows provides
- no line editing in its shell, and on Windows 95 there is no way
- to scroll back to text which has scrolled off the screen.
- The <c><![CDATA[erl]]></c> program must be used, however, in pipelines or if
+ command-line editing. The <c><![CDATA[erl]]></c> program on Windows
+ provides no line editing in its shell, and on Windows 95 there is no way
+ to scroll back to text that has scrolled off the screen. The
+ <c><![CDATA[erl]]></c> program must be used, however, in pipelines or if
you want to redirect standard input or output.</p>
- <note><p>As of ERTS version 5.9 (OTP-R15B) the runtime system will by
- default <em>not</em> bind schedulers to logical processors.
- For more information see documentation of the
- <seealso marker="#+sbt">+sbt</seealso> system flag.
- </p>
- </note>
+
+ <note>
+ <p>As from ERTS 5.9 (Erlang/OTP R15B) the runtime system does by
+ default <em>not</em> bind schedulers to logical processors.
+ For more information, see system flag
+ <seealso marker="#+sbt"><c>+sbt</c></seealso>.</p>
+ </note>
</description>
+
<funcs>
<func>
<name>erl &lt;arguments></name>
- <fsummary>Start an Erlang runtime system</fsummary>
+ <fsummary>Start an Erlang runtime system.</fsummary>
<desc>
- <p>Starts an Erlang runtime system.</p>
- <p>The arguments can be divided into <em>emulator flags</em>,
- <em>flags</em> and <em>plain arguments</em>:</p>
- <list type="bulleted">
- <item>
- <p>Any argument starting with the character <c><![CDATA[+]]></c> is
- interpreted as an <seealso marker="#emu_flags">emulator flag</seealso>.</p>
- <p>As indicated by the name, emulator flags controls
- the behavior of the emulator.</p>
- </item>
- <item>
- <p>Any argument starting with the character <c><![CDATA[-]]></c>
- (hyphen) is interpreted as a
- <seealso marker="#init_flags">flag</seealso> which should
- be passed to the Erlang part of the runtime system, more
- specifically to the <c><![CDATA[init]]></c> system process, see
- <seealso marker="init">init(3)</seealso>.</p>
- <p>The <c><![CDATA[init]]></c> process itself interprets some of these
- flags, the <em>init flags</em>. It also stores any
- remaining flags, the <em>user flags</em>. The latter can
- be retrieved by calling <c><![CDATA[init:get_argument/1]]></c>.</p>
- <p>It can be noted that there are a small number of "-"
- flags which now actually are emulator flags, see
- the description below.</p>
- </item>
- <item>
- <p>Plain arguments are not interpreted in any way. They are
- also stored by the <c><![CDATA[init]]></c> process and can be
- retrieved by calling <c><![CDATA[init:get_plain_arguments/0]]></c>.
- Plain arguments can occur before the first flag, or after
- a <c><![CDATA[--]]></c> flag. Additionally, the flag <c><![CDATA[-extra]]></c>
- causes everything that follows to become plain arguments.</p>
- </item>
- </list>
- <p>Example:</p>
- <pre>
+ <p>Starts an Erlang runtime system.</p>
+ <p>The arguments can be divided into <em>emulator flags</em>,
+ <em>flags</em>, and <em>plain arguments</em>:</p>
+ <list type="bulleted">
+ <item>
+ <p>Any argument starting with character <c><![CDATA[+]]></c> is
+ interpreted as an
+ <seealso marker="#emu_flags">emulator flag</seealso>.</p>
+ <p>As indicated by the name, emulator flags control
+ the behavior of the emulator.</p>
+ </item>
+ <item>
+ <p>Any argument starting with character <c><![CDATA[-]]></c>
+ (hyphen) is interpreted as a
+ <seealso marker="#init_flags">flag</seealso>, which is to
+ be passed to the Erlang part of the runtime system, more
+ specifically to the <c><![CDATA[init]]></c> system process, see
+ <seealso marker="init"><c>init(3)</c></seealso>.</p>
+ <p>The <c><![CDATA[init]]></c> process itself interprets some of
+ these flags, the <em>init flags</em>. It also stores any
+ remaining flags, the <em>user flags</em>. The latter can be
+ retrieved by calling <c><![CDATA[init:get_argument/1]]></c>.</p>
+ <p>A small number of "-" flags exist, which now actually are
+ emulator flags, see the description below.</p>
+ </item>
+ <item>
+ <p>Plain arguments are not interpreted in any way. They are also
+ stored by the <c><![CDATA[init]]></c> process and can be retrieved
+ by calling <c><![CDATA[init:get_plain_arguments/0]]></c>.
+ Plain arguments can occur before the first flag, or after a
+ <c><![CDATA[--]]></c> flag. Also, the <c><![CDATA[-extra]]></c>
+ flag causes everything that follows to become plain arguments.</p>
+ </item>
+ </list>
+ <p><em>Examples:</em></p>
+ <pre>
% <input>erl +W w -sname arnie +R 9 -s my_init -extra +bertie</input>
(arnie@host)1> <input>init:get_argument(sname).</input>
{ok,[["arnie"]]}
(arnie@host)2> <input>init:get_plain_arguments().</input>
["+bertie"]</pre>
- <p>Here <c><![CDATA[+W w]]></c> and <c><![CDATA[+R 9]]></c> are emulator flags.
- <c><![CDATA[-s my_init]]></c> is an init flag, interpreted by <c><![CDATA[init]]></c>.
- <c><![CDATA[-sname arnie]]></c> is a user flag, stored by <c><![CDATA[init]]></c>.
- It is read by Kernel and will cause the Erlang runtime system
- to become distributed. Finally, everything after <c><![CDATA[-extra]]></c>
- (that is, <c><![CDATA[+bertie]]></c>) is considered as plain arguments.</p>
- <pre>
+ <p>Here <c><![CDATA[+W w]]></c> and <c><![CDATA[+R 9]]></c> are
+ emulator flags. <c><![CDATA[-s my_init]]></c> is an init flag,
+ interpreted by <c><![CDATA[init]]></c>.
+ <c><![CDATA[-sname arnie]]></c> is a user flag, stored by
+ <c><![CDATA[init]]></c>. It is read by Kernel and causes the
+ Erlang runtime system to become distributed. Finally, everything after
+ <c><![CDATA[-extra]]></c> (that is, <c><![CDATA[+bertie]]></c>) is
+ considered as plain arguments.</p>
+ <pre>
% <input>erl -myflag 1</input>
1> <input>init:get_argument(myflag).</input>
{ok,[["1"]]}
2> <input>init:get_plain_arguments().</input>
[]</pre>
- <p>Here the user flag <c><![CDATA[-myflag 1]]></c> is passed to and stored
- by the <c><![CDATA[init]]></c> process. It is a user defined flag,
- presumably used by some user defined application.</p>
+ <p>Here the user flag <c><![CDATA[-myflag 1]]></c> is passed to and
+ stored by the <c><![CDATA[init]]></c> process. It is a user-defined
+ flag, presumably used by some user-defined application.</p>
</desc>
</func>
</funcs>
@@ -117,50 +123,54 @@
<section>
<marker id="init_flags"></marker>
<title>Flags</title>
- <p>In the following list, init flags are marked (init flag).
+ <p>In the following list, init flags are marked "(init flag)".
Unless otherwise specified, all other flags are user flags, for
which the values can be retrieved by calling
- <c><![CDATA[init:get_argument/1]]></c>. Note that the list of user flags is
- not exhaustive, there may be additional, application specific
- flags which instead are documented in the corresponding
+ <c><![CDATA[init:get_argument/1]]></c>. Notice that the list of user
+ flags is not exhaustive, there can be more application-specific
+ flags that instead are described in the corresponding
application documentation.</p>
<taglist>
- <tag><c><![CDATA[--]]></c>(init flag)</tag>
+ <tag><c><![CDATA[--]]></c> (init flag)</tag>
<item>
<p>Everything following <c><![CDATA[--]]></c> up to the next flag
- (<c><![CDATA[-flag]]></c> or <c><![CDATA[+flag]]></c>) is considered plain arguments
- and can be retrieved using <c><![CDATA[init:get_plain_arguments/0]]></c>.</p>
+ (<c><![CDATA[-flag]]></c> or <c><![CDATA[+flag]]></c>) is considered
+ plain arguments and can be retrieved using
+ <c><![CDATA[init:get_plain_arguments/0]]></c>.</p>
</item>
<tag><c><![CDATA[-Application Par Val]]></c></tag>
<item>
- <p>Sets the application configuration parameter <c><![CDATA[Par]]></c> to
- the value <c><![CDATA[Val]]></c> for the application <c><![CDATA[Application]]></c>,
- see <seealso marker="kernel:app">app(4)</seealso> and
- <seealso marker="kernel:application">application(3)</seealso>.</p>
+ <p>Sets the application configuration parameter <c><![CDATA[Par]]></c>
+ to the value <c><![CDATA[Val]]></c> for the application
+ <c><![CDATA[Application]]></c>; see
+ <seealso marker="kernel:app"><c>app(4)</c></seealso> and
+ <seealso marker="kernel:application">
+ <c>application(3)</c></seealso>.</p>
</item>
<tag><marker id="args_file"/><c><![CDATA[-args_file FileName]]></c></tag>
<item>
- <p>Command line arguments are read from the file <c><![CDATA[FileName]]></c>.
- The arguments read from the file replace the
- '<c><![CDATA[-args_file FileName]]></c>' flag on the resulting command line.</p>
- <p>The file <c><![CDATA[FileName]]></c> should be a plain text file and may
- contain comments and command line arguments. A comment begins
- with a # character and continues until next end of line character.
- Backslash (\\) is used as quoting character. All command line
- arguments accepted by <c><![CDATA[erl]]></c> are allowed, also the
- <c><![CDATA[-args_file FileName]]></c> flag. Be careful not to cause circular
- dependencies between files containing the <c><![CDATA[-args_file]]></c> flag,
- though.</p>
- <p>The <c><![CDATA[-extra]]></c> flag is treated specially. Its scope ends
- at the end of the file. Arguments following an <c><![CDATA[-extra]]></c>
- flag are moved on the command line into the <c><![CDATA[-extra]]></c> section,
- i.e. the end of the command line following after an <c><![CDATA[-extra]]></c>
- flag.</p>
+ <p>Command-line arguments are read from the file
+ <c><![CDATA[FileName]]></c>. The arguments read from the file replace
+ flag '<c><![CDATA[-args_file FileName]]></c>' on the resulting
+ command line.</p>
+ <p>The file <c><![CDATA[FileName]]></c> is to be a plain text file and
+ can contain comments and command-line arguments. A comment begins
+ with a <c>#</c> character and continues until the next end of line
+ character. Backslash (\\) is used as quoting character. All
+ command-line arguments accepted by <c><![CDATA[erl]]></c> are allowed,
+ also flag <c><![CDATA[-args_file FileName]]></c>. Be careful not to
+ cause circular dependencies between files containing flag
+ <c><![CDATA[-args_file]]></c>, though.</p>
+ <p>The flag <c><![CDATA[-extra]]></c> is treated in special way. Its
+ scope ends at the end of the file. Arguments following an
+ <c><![CDATA[-extra]]></c> flag are moved on the command line into the
+ <c><![CDATA[-extra]]></c> section, that is, the end of the command
+ line following after an <c><![CDATA[-extra]]></c> flag.</p>
</item>
<tag><c><![CDATA[-async_shell_start]]></c></tag>
<item>
<p>The initial Erlang shell does not read user input until
- the system boot procedure has been completed (Erlang 5.4 and
+ the system boot procedure has been completed (Erlang/OTP 5.4 and
later). This flag disables the start synchronization feature
and lets the shell start in parallel with the rest of
the system.</p>
@@ -168,52 +178,56 @@
<tag><c><![CDATA[-boot File]]></c></tag>
<item>
<p>Specifies the name of the boot file, <c><![CDATA[File.boot]]></c>,
- which is used to start the system. See
- <seealso marker="init">init(3)</seealso>. Unless
+ which is used to start the system; see
+ <seealso marker="init"><c>init(3)</c></seealso>. Unless
<c><![CDATA[File]]></c> contains an absolute path, the system searches
- for <c><![CDATA[File.boot]]></c> in the current and <c><![CDATA[$ROOT/bin]]></c>
- directories.</p>
+ for <c><![CDATA[File.boot]]></c> in the current and
+ <c><![CDATA[$ROOT/bin]]></c> directories.</p>
<p>Defaults to <c><![CDATA[$ROOT/bin/start.boot]]></c>.</p>
</item>
<tag><c><![CDATA[-boot_var Var Dir]]></c></tag>
<item>
- <p>If the boot script contains a path variable <c><![CDATA[Var]]></c> other
- than <c><![CDATA[$ROOT]]></c>, this variable is expanded to <c><![CDATA[Dir]]></c>.
- Used when applications are installed in another directory
- than <c><![CDATA[$ROOT/lib]]></c>, see
- <seealso marker="sasl:systools#make_script/1">systools:make_script/1,2</seealso>.</p>
+ <p>If the boot script contains a path variable <c><![CDATA[Var]]></c>
+ other than <c><![CDATA[$ROOT]]></c>, this variable is expanded to
+ <c><![CDATA[Dir]]></c>. Used when applications are installed in
+ another directory than <c><![CDATA[$ROOT/lib]]></c>; see
+ <seealso marker="sasl:systools#make_script/1">
+ <c>systools:make_script/1,2</c></seealso> in SASL.</p>
</item>
<tag><c><![CDATA[-code_path_cache]]></c></tag>
<item>
- <p>Enables the code path cache of the code server, see
- <seealso marker="kernel:code">code(3)</seealso>.</p>
+ <p>Enables the code path cache of the code server; see
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
</item>
<tag><c><![CDATA[-compile Mod1 Mod2 ...]]></c></tag>
<item>
<p>Compiles the specified modules and then terminates (with
non-zero exit code if the compilation of some file did not
- succeed). Implies <c><![CDATA[-noinput]]></c>. Not recommended - use
- <seealso marker="erlc">erlc</seealso> instead.</p>
+ succeed). Implies <c><![CDATA[-noinput]]></c>.</p>
+ <p>Not recommended; use <seealso marker="erlc"><c>erlc</c></seealso>
+ instead.</p>
</item>
<tag><c><![CDATA[-config Config]]></c></tag>
<item>
<p>Specifies the name of a configuration file,
<c><![CDATA[Config.config]]></c>, which is used to configure
- applications. See
- <seealso marker="kernel:app">app(4)</seealso> and
- <seealso marker="kernel:application">application(3)</seealso>.</p>
+ applications; see
+ <seealso marker="kernel:app"><c>app(4)</c></seealso> and
+ <seealso marker="kernel:application">
+ <c>application(3)</c></seealso>.</p>
</item>
<tag><marker id="connect_all"/><c><![CDATA[-connect_all false]]></c></tag>
<item>
- <p>If this flag is present, <c><![CDATA[global]]></c> will not maintain a
- fully connected network of distributed Erlang nodes, and then
- global name registration cannot be used. See
- <seealso marker="kernel:global">global(3)</seealso>.</p>
+ <p>If this flag is present, <c><![CDATA[global]]></c> does not maintain
+ a fully connected network of distributed Erlang nodes, and then
+ global name registration cannot be used; see
+ <seealso marker="kernel:global"><c>global(3)</c></seealso>.</p>
</item>
<tag><c><![CDATA[-cookie Cookie]]></c></tag>
<item>
<p>Obsolete flag without any effect and common misspelling for
- <c><![CDATA[-setcookie]]></c>. Use <c><![CDATA[-setcookie]]></c> instead.</p>
+ <c><![CDATA[-setcookie]]></c>. Use <c><![CDATA[-setcookie]]></c>
+ instead.</p>
</item>
<tag><c><![CDATA[-detached]]></c></tag>
<item>
@@ -223,8 +237,7 @@
</item>
<tag><c><![CDATA[-emu_args]]></c></tag>
<item>
- <p>Useful for debugging. Prints out the actual arguments
- sent to the emulator.</p>
+ <p>Useful for debugging. Prints the arguments sent to the emulator.</p>
</item>
<tag><c><![CDATA[-env Variable Value]]></c></tag>
<item>
@@ -234,14 +247,21 @@
<pre>
% <input>erl -env DISPLAY gin:0</input></pre>
<p>In this example, an Erlang runtime system is started with
- the <c><![CDATA[DISPLAY]]></c> environment variable set to <c><![CDATA[gin:0]]></c>.</p>
+ environment variable <c><![CDATA[DISPLAY]]></c> set to
+ <c><![CDATA[gin:0]]></c>.</p>
+ </item>
+ <tag><c><![CDATA[-epmd_module Module]]></c> (init flag)</tag>
+ <item>
+ <p>Configures the module responsible to communicate to
+ <seealso marker="epmd">epmd</seealso>. Defaults to <c>erl_epmd</c>.</p>
</item>
- <tag><c><![CDATA[-eval Expr]]></c>(init flag)</tag>
+ <tag><c><![CDATA[-eval Expr]]></c> (init flag)</tag>
<item>
- <p>Makes <c><![CDATA[init]]></c> evaluate the expression <c><![CDATA[Expr]]></c>, see
- <seealso marker="init">init(3)</seealso>.</p>
+ <p>Makes <c><![CDATA[init]]></c> evaluate the expression
+ <c><![CDATA[Expr]]></c>; see
+ <seealso marker="init"><c>init(3)</c></seealso>.</p>
</item>
- <tag><c><![CDATA[-extra]]></c>(init flag)</tag>
+ <tag><c><![CDATA[-extra]]></c> (init flag)</tag>
<item>
<p>Everything following <c><![CDATA[-extra]]></c> is considered plain
arguments and can be retrieved using
@@ -249,8 +269,9 @@
</item>
<tag><c><![CDATA[-heart]]></c></tag>
<item>
- <p>Starts heart beat monitoring of the Erlang runtime system.
- See <seealso marker="kernel:heart">heart(3)</seealso>.</p>
+ <p>Starts heartbeat monitoring of the Erlang runtime system;
+ see <seealso marker="kernel:heart">
+ <c>heart(3)</c></seealso>.</p>
</item>
<tag><c><![CDATA[-hidden]]></c></tag>
<item>
@@ -258,90 +279,99 @@
run as a distributed node. Hidden nodes always establish
hidden connections to all other nodes except for nodes in the
same global group. Hidden connections are not published on
- either of the connected nodes, i.e. neither of the connected
- nodes are part of the result from <c><![CDATA[nodes/0]]></c> on the other
- node. See also hidden global groups,
- <seealso marker="kernel:global_group">global_group(3)</seealso>.</p>
+ any of the connected nodes, that is, none of the connected
+ nodes are part of the result from <c><![CDATA[nodes/0]]></c> on the
+ other node. See also hidden global groups;
+ <seealso marker="kernel:global_group">
+ <c>global_group(3)</c></seealso>.</p>
</item>
<tag><c><![CDATA[-hosts Hosts]]></c></tag>
<item>
- <p>Specifies the IP addresses for the hosts on which Erlang
- boot servers are running, see
- <seealso marker="kernel:erl_boot_server">erl_boot_server(3)</seealso>.
- This flag is mandatory if the <c><![CDATA[-loader inet]]></c> flag is
- present.</p>
- <p>The IP addresses must be given in the standard form (four
- decimal numbers separated by periods, for example
- <c><![CDATA["150.236.20.74"]]></c>. Hosts names are not acceptable, but
- a broadcast address (preferably limited to the local network)
+ <p>Specifies the IP addresses for the hosts on which Erlang boot servers
+ are running, see <seealso marker="kernel:erl_boot_server">
+ <c>erl_boot_server(3)</c></seealso>. This flag
+ is mandatory if flag <c><![CDATA[-loader inet]]></c> is present.</p>
+ <p>The IP addresses must be specified in the standard form (four
+ decimal numbers separated by periods, for example,
+ <c><![CDATA["150.236.20.74"]]></c>. Hosts names are not acceptable,
+ but a broadcast address (preferably limited to the local network)
is.</p>
</item>
<tag><c><![CDATA[-id Id]]></c></tag>
<item>
<p>Specifies the identity of the Erlang runtime system. If it is
run as a distributed node, <c><![CDATA[Id]]></c> must be identical to
- the name supplied together with the <c><![CDATA[-sname]]></c> or
- <c><![CDATA[-name]]></c> flag.</p>
+ the name supplied together with flag <c><![CDATA[-sname]]></c> or
+ <c><![CDATA[-name]]></c>.</p>
</item>
<tag><c><![CDATA[-init_debug]]></c></tag>
<item>
<p>Makes <c><![CDATA[init]]></c> write some debug information while
interpreting the boot script.</p>
</item>
- <tag><marker id="instr"/><c><![CDATA[-instr]]></c>(emulator flag)</tag>
+ <tag><marker id="instr"/><c><![CDATA[-instr]]></c> (emulator flag)</tag>
<item>
<p>Selects an instrumented Erlang runtime system (virtual
machine) to run, instead of the ordinary one. When running an
instrumented runtime system, some resource usage data can be
- obtained and analysed using the module <c><![CDATA[instrument]]></c>.
+ obtained and analyzed using the <c><![CDATA[instrument]]></c> module.
Functionally, it behaves exactly like an ordinary Erlang
runtime system.</p>
</item>
<tag><c><![CDATA[-loader Loader]]></c></tag>
<item>
- <p>Specifies the method used by <c><![CDATA[erl_prim_loader]]></c> to load
- Erlang modules into the system. See
- <seealso marker="erl_prim_loader">erl_prim_loader(3)</seealso>.
- Two <c><![CDATA[Loader]]></c> methods are supported, <c><![CDATA[efile]]></c> and
- <c><![CDATA[inet]]></c>. <c><![CDATA[efile]]></c> means use the local file system,
- this is the default. <c><![CDATA[inet]]></c> means use a boot server on
- another machine, and the <c><![CDATA[-id]]></c>, <c><![CDATA[-hosts]]></c> and
- <c><![CDATA[-setcookie]]></c> flags must be specified as well. If
- <c><![CDATA[Loader]]></c> is something else, the user supplied
+ <p>Specifies the method used by <c><![CDATA[erl_prim_loader]]></c> to
+ load Erlang modules into the system; see
+ <seealso marker="erl_prim_loader"><c>erl_prim_loader(3)</c></seealso>.
+ Two <c><![CDATA[Loader]]></c> methods are supported:</p>
+ <list type="bulleted">
+ <item>
+ <p><c><![CDATA[efile]]></c>, which means use the local file system,
+ this is the default.</p>
+ </item>
+ <item>
+ <p><c><![CDATA[inet]]></c>, which means use a boot server on
+ another machine. The flags <c><![CDATA[-id]]></c>,
+ <c><![CDATA[-hosts]]></c> and <c><![CDATA[-setcookie]]></c> must
+ also be specified.</p>
+ </item>
+ </list>
+ <p>If <c><![CDATA[Loader]]></c> is something else, the user-supplied
<c><![CDATA[Loader]]></c> port program is started.</p>
</item>
<tag><c><![CDATA[-make]]></c></tag>
<item>
- <p>Makes the Erlang runtime system invoke <c><![CDATA[make:all()]]></c> in
- the current working directory and then terminate. See
- <seealso marker="tools:make">make(3)</seealso>. Implies
+ <p>Makes the Erlang runtime system invoke <c><![CDATA[make:all()]]></c>
+ in the current working directory and then terminate; see
+ <seealso marker="tools:make"><c>make(3)</c></seealso>. Implies
<c><![CDATA[-noinput]]></c>.</p>
</item>
<tag><c><![CDATA[-man Module]]></c></tag>
<item>
- <p>Displays the manual page for the Erlang module <c><![CDATA[Module]]></c>.
- Only supported on Unix.</p>
+ <p>Displays the manual page for the Erlang module
+ <c><![CDATA[Module]]></c>. Only supported on Unix.</p>
</item>
<tag><c><![CDATA[-mode interactive | embedded]]></c></tag>
<item>
- <p>Indicates if the system should load code dynamically
- (<c><![CDATA[interactive]]></c>), or if all code should be loaded
- during system initialization (<c><![CDATA[embedded]]></c>), see
- <seealso marker="kernel:code">code(3)</seealso>. Defaults to
- <c><![CDATA[interactive]]></c>.</p>
+ <p>Indicates if the system is to load code dynamically
+ (<c><![CDATA[interactive]]></c>), or if all code is to be loaded
+ during system initialization (<c><![CDATA[embedded]]></c>); see
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.
+ Defaults to <c><![CDATA[interactive]]></c>.</p>
</item>
<tag><c><![CDATA[-name Name]]></c></tag>
<item>
<p>Makes the Erlang runtime system into a distributed node.
This flag invokes all network servers necessary for a node to
- become distributed. See
- <seealso marker="kernel:net_kernel">net_kernel(3)</seealso>.
- It is also ensured that <c><![CDATA[epmd]]></c> runs on the current host
- before Erlang is started. See
- <seealso marker="epmd">epmd(1)</seealso>.</p>
- <p>The name of the node will be <c><![CDATA[Name@Host]]></c>, where
- <c><![CDATA[Host]]></c> is the fully qualified host name of the current
- host. For short names, use the <c><![CDATA[-sname]]></c> flag instead.</p>
+ become distributed; see <seealso marker="kernel:net_kernel">
+ <c>net_kernel(3)</c></seealso>. It is also ensured that
+ <c><![CDATA[epmd]]></c> runs on the current host before Erlang is
+ started; see <seealso marker="epmd"><c>epmd(1)</c></seealso>.and the
+ <seealso marker="#start_epmd"><c>-start_epmd</c></seealso> option.</p>
+ <p>The node name will be <c><![CDATA[Name@Host]]></c>, where
+ <c><![CDATA[Host]]></c> is the fully qualified host name of the
+ current host. For short names, use flag <c><![CDATA[-sname]]></c>
+ instead.</p>
</item>
<tag><c><![CDATA[-noinput]]></c></tag>
<item>
@@ -352,132 +382,147 @@
<item>
<p>Starts an Erlang runtime system with no shell. This flag
makes it possible to have the Erlang runtime system as a
- component in a series of UNIX pipes.</p>
+ component in a series of Unix pipes.</p>
</item>
<tag><c><![CDATA[-nostick]]></c></tag>
<item>
<p>Disables the sticky directory facility of the Erlang code
- server, see
- <seealso marker="kernel:code">code(3)</seealso>.</p>
+ server; see
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
</item>
<tag><c><![CDATA[-oldshell]]></c></tag>
<item>
- <p>Invokes the old Erlang shell from Erlang 3.3. The old shell
+ <p>Invokes the old Erlang shell from Erlang/OTP 3.3. The old shell
can still be used.</p>
</item>
<tag><c><![CDATA[-pa Dir1 Dir2 ...]]></c></tag>
<item>
<p>Adds the specified directories to the beginning of the code
- path, similar to <c><![CDATA[code:add_pathsa/1]]></c>. See
- <seealso marker="kernel:code">code(3)</seealso>.
- As an alternative to <c>-pa</c>, if several directories are
+ path, similar to <seealso marker="kernel:code#add_pathsa/1">
+ <c><![CDATA[code:add_pathsa/1]]></c></seealso>. Note that the
+ order of the given directories will be reversed in the
+ resulting path.</p>
+ <p>As an alternative to <c>-pa</c>, if several directories are
to be prepended to the code path and the directories have a
- common parent directory, that parent directory could be
- specified in the <c>ERL_LIBS</c> environment variable.
- See <seealso marker="kernel:code">code(3)</seealso>.</p>
+ common parent directory, that parent directory can be
+ specified in environment variable <c>ERL_LIBS</c>; see
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
</item>
<tag><c><![CDATA[-pz Dir1 Dir2 ...]]></c></tag>
<item>
<p>Adds the specified directories to the end of the code path,
- similar to <c><![CDATA[code:add_pathsz/1]]></c>. See
- <seealso marker="kernel:code">code(3)</seealso>.</p>
+ similar to <c><![CDATA[code:add_pathsz/1]]></c>; see
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
</item>
<tag><c><![CDATA[-path Dir1 Dir2 ...]]></c></tag>
<item>
- <p>Replaces the path specified in the boot script. See
- <seealso marker="sasl:script">script(4)</seealso>.</p>
+ <p>Replaces the path specified in the boot script; see
+ <seealso marker="sasl:script"><c>script(4)</c></seealso>.</p>
</item>
<tag><c><![CDATA[-proto_dist Proto]]></c></tag>
<item>
- <p>Specify a protocol for Erlang distribution.</p>
- <taglist>
- <tag><c>inet_tcp</c></tag>
- <item>
- <p>TCP over IPv4 (the default)</p>
- </item>
- <tag><c>inet_tls</c></tag>
- <item>
- <p>distribution over TLS/SSL</p>
- </item>
- <tag><c>inet6_tcp</c></tag>
- <item>
- <p>TCP over IPv6</p>
- </item>
+ <p>Specifies a protocol for Erlang distribution:</p>
+ <taglist>
+ <tag><c>inet_tcp</c></tag>
+ <item>TCP over IPv4 (the default)</item>
+ <tag><c>inet_tls</c></tag>
+ <item>Distribution over TLS/SSL</item>
+ <tag><c>inet6_tcp</c></tag>
+ <item>TCP over IPv6</item>
</taglist>
<p>For example, to start up IPv6 distributed nodes:</p>
<pre>
-% <input>erl -name [email protected] -proto_dist inet6_tcp</input>
-</pre>
+% <input>erl -name [email protected] -proto_dist inet6_tcp</input></pre>
</item>
<tag><c><![CDATA[-remsh Node]]></c></tag>
<item>
- <p>Starts Erlang with a remote shell connected to <c><![CDATA[Node]]></c>.</p>
+ <p>Starts Erlang with a remote shell connected to
+ <c><![CDATA[Node]]></c>.</p>
</item>
<tag><c><![CDATA[-rsh Program]]></c></tag>
<item>
- <p>Specifies an alternative to <c><![CDATA[rsh]]></c> for starting a slave
- node on a remote host. See
- <seealso marker="stdlib:slave">slave(3)</seealso>.</p>
+ <p>Specifies an alternative to <c><![CDATA[rsh]]></c> for starting a
+ slave node on a remote host; see
+ <seealso marker="stdlib:slave"><c>slave(3)</c></seealso>.</p>
</item>
- <tag><c><![CDATA[-run Mod [Func [Arg1, Arg2, ...]]]]></c>(init flag)</tag>
+ <tag><c><![CDATA[-run Mod [Func [Arg1, Arg2, ...]]]]></c> (init
+ flag)</tag>
<item>
- <p>Makes <c><![CDATA[init]]></c> call the specified function. <c><![CDATA[Func]]></c>
- defaults to <c><![CDATA[start]]></c>. If no arguments are provided,
- the function is assumed to be of arity 0. Otherwise it is
- assumed to be of arity 1, taking the list
- <c><![CDATA[[Arg1,Arg2,...]]]></c> as argument. All arguments are passed
- as strings. See
- <seealso marker="init">init(3)</seealso>.</p>
+ <p>Makes <c><![CDATA[init]]></c> call the specified function.
+ <c><![CDATA[Func]]></c> defaults to <c><![CDATA[start]]></c>.
+ If no arguments are provided, the function is assumed to be of
+ arity 0. Otherwise it is assumed to be of arity 1, taking the list
+ <c><![CDATA[[Arg1,Arg2,...]]]></c> as argument. All arguments are
+ passed as strings. See <seealso marker="init">
+ <c>init(3)</c></seealso>.</p>
</item>
- <tag><c><![CDATA[-s Mod [Func [Arg1, Arg2, ...]]]]></c>(init flag)</tag>
+ <tag><c><![CDATA[-s Mod [Func [Arg1, Arg2, ...]]]]></c> (init flag)</tag>
<item>
- <p>Makes <c><![CDATA[init]]></c> call the specified function. <c><![CDATA[Func]]></c>
- defaults to <c><![CDATA[start]]></c>. If no arguments are provided,
- the function is assumed to be of arity 0. Otherwise it is
- assumed to be of arity 1, taking the list
- <c><![CDATA[[Arg1,Arg2,...]]]></c> as argument. All arguments are passed
- as atoms. See
- <seealso marker="init">init(3)</seealso>.</p>
+ <p>Makes <c><![CDATA[init]]></c> call the specified function.
+ <c><![CDATA[Func]]></c> defaults to <c><![CDATA[start]]></c>.
+ If no arguments are provided, the function is assumed to be of
+ arity 0. Otherwise it is assumed to be of arity 1, taking the list
+ <c><![CDATA[[Arg1,Arg2,...]]]></c> as argument. All arguments are
+ passed as atoms. See <seealso marker="init">
+ <c>init(3)</c></seealso>.</p>
</item>
<tag><c><![CDATA[-setcookie Cookie]]></c></tag>
<item>
- <p>Sets the magic cookie of the node to <c><![CDATA[Cookie]]></c>, see
- <seealso marker="erlang#set_cookie/2">erlang:set_cookie/2</seealso>.</p>
+ <p>Sets the magic cookie of the node to <c><![CDATA[Cookie]]></c>; see
+ <seealso marker="erlang#set_cookie/2">
+ <c>erlang:set_cookie/2</c></seealso>.</p>
</item>
<tag><c><![CDATA[-shutdown_time Time]]></c></tag>
<item>
<p>Specifies how long time (in milliseconds) the <c><![CDATA[init]]></c>
process is allowed to spend shutting down the system. If
- <c><![CDATA[Time]]></c> ms have elapsed, all processes still existing are
- killed. Defaults to <c><![CDATA[infinity]]></c>.</p>
+ <c><![CDATA[Time]]></c> milliseconds have elapsed, all processes still
+ existing are killed. Defaults to <c><![CDATA[infinity]]></c>.</p>
</item>
<tag><c><![CDATA[-sname Name]]></c></tag>
<item>
- <p>Makes the Erlang runtime system into a distributed node,
- similar to <c><![CDATA[-name]]></c>, but the host name portion of the node
+ <p>Makes the Erlang runtime system into a distributed node, similar to
+ <c><![CDATA[-name]]></c>, but the host name portion of the node
name <c><![CDATA[Name@Host]]></c> will be the short name, not fully
qualified.</p>
<p>This is sometimes the only way to run distributed Erlang if
- the DNS (Domain Name System) is not running. There can be no
- communication between nodes running with the <c><![CDATA[-sname]]></c>
- flag and those running with the <c><![CDATA[-name]]></c> flag, as node
+ the Domain Name System (DNS) is not running. No communication can
+ exist between nodes running with flag <c><![CDATA[-sname]]></c>
+ and those running with flag <c><![CDATA[-name]]></c>, as node
names must be unique in distributed Erlang systems.</p>
</item>
+ <tag><marker id="start_epmd"/><c>-start_epmd true | false</c></tag>
+ <item>
+
+ <p>Specifies whether Erlang should start
+ <seealso marker="epmd">epmd</seealso> on startup. By default
+ this is <c>true</c>, but if you prefer to start epmd
+ manually, set this to <c>false</c>.</p>
+
+ <p>This only applies if Erlang is started as a distributed node,
+ i.e. if <c>-name</c> or <c>-sname</c> is specified. Otherwise,
+ epmd is not started even if <c>-start_epmd true</c> is given.</p>
+
+ <p>Note that a distributed node will fail to start if epmd is
+ not running.</p>
+ </item>
<tag><marker id="smp"/><c><![CDATA[-smp [enable|auto|disable]]]></c></tag>
<item>
- <p><c>-smp enable</c> and <c>-smp</c> starts the Erlang runtime
- system with SMP support enabled. This may fail if no runtime
+ <p><c>-smp enable</c> and <c>-smp</c> start the Erlang runtime
+ system with SMP support enabled. This can fail if no runtime
system with SMP support is available. <c>-smp auto</c> starts
the Erlang runtime system with SMP support enabled if it is
- available and more than one logical processor are detected.
+ available and more than one logical processor is detected.
<c>-smp disable</c> starts a runtime system without SMP support.</p>
- <p><em>NOTE</em>: The runtime system with SMP support will not
- be available on all supported platforms. See also the
- <seealso marker="#+S">+S</seealso> flag.</p>
+ <note>
+ <p>The runtime system with SMP support is not available on all
+ supported platforms. See also flag
+ <seealso marker="#+S"><c>+S</c></seealso>.</p>
+ </note>
</item>
- <tag><c><![CDATA[-version]]></c>(emulator flag)</tag>
+ <tag><c><![CDATA[-version]]></c> (emulator flag)</tag>
<item>
- <p>Makes the emulator print out its version number. The same
+ <p>Makes the emulator print its version number. The same
as <c><![CDATA[erl +V]]></c>.</p>
</item>
</taglist>
@@ -489,133 +534,169 @@
<p><c><![CDATA[erl]]></c> invokes the code for the Erlang emulator (virtual
machine), which supports the following flags:</p>
<taglist>
- <tag><marker id="async_thread_stack_size"/><c><![CDATA[+a size]]></c></tag>
+ <tag><marker id="async_thread_stack_size"/>
+ <c><![CDATA[+a size]]></c></tag>
<item>
<p>Suggested stack size, in kilowords, for threads in the
- async-thread pool. Valid range is 16-8192 kilowords. The
- default suggested stack size is 16 kilowords, i.e, 64
+ async thread pool. Valid range is 16-8192 kilowords. The
+ default suggested stack size is 16 kilowords, that is, 64
kilobyte on 32-bit architectures. This small default size
- has been chosen since the amount of async-threads might
- be quite large. The default size is enough for drivers
- delivered with Erlang/OTP, but might not be sufficiently
- large for other dynamically linked in drivers that use the
- <seealso marker="erl_driver#driver_async">driver_async()</seealso>
- functionality. Note that the value passed is only a
- suggestion, and it might even be ignored on some
- platforms.</p>
+ has been chosen because the number of async threads can
+ be large. The default size is enough for drivers
+ delivered with Erlang/OTP, but might not be large
+ enough for other dynamically linked-in drivers that use the
+ <seealso marker="erl_driver#driver_async">
+ <c>driver_async()</c></seealso> functionality.
+ Notice that the value passed is only a suggestion,
+ and it can even be ignored on some platforms.</p>
</item>
<tag><marker id="async_thread_pool_size"/><c><![CDATA[+A size]]></c></tag>
<item>
- <p>Sets the number of threads in async thread pool, valid range
- is 0-1024. If thread support is available, the default is 10.</p>
+ <p>Sets the number of threads in async thread pool. Valid range
+ is 0-1024. Defaults to 10 if thread support is available.</p>
</item>
<tag><c><![CDATA[+B [c | d | i]]]></c></tag>
<item>
- <p>The <c><![CDATA[c]]></c> option makes <c><![CDATA[Ctrl-C]]></c> interrupt the current
- shell instead of invoking the emulator break handler.
- The <c><![CDATA[d]]></c> option (same as specifying <c><![CDATA[+B]]></c> without an
- extra option) disables the break handler. The <c><![CDATA[i]]></c> option
- makes the emulator ignore any break signal.</p>
- <p>If the <c><![CDATA[c]]></c> option is used with <c><![CDATA[oldshell]]></c> on Unix,
- <c><![CDATA[Ctrl-C]]></c> will restart the shell process rather than
- interrupt it.</p>
- <p>Note that on Windows, this flag is only applicable for
- <c><![CDATA[werl]]></c>, not <c><![CDATA[erl]]></c> (<c><![CDATA[oldshell]]></c>). Note also that
- <c><![CDATA[Ctrl-Break]]></c> is used instead of <c><![CDATA[Ctrl-C]]></c> on Windows.</p>
+ <p>Option <c><![CDATA[c]]></c> makes <c><![CDATA[Ctrl-C]]></c>
+ interrupt the current shell instead of invoking the emulator break
+ handler. Option <c><![CDATA[d]]></c> (same as specifying
+ <c><![CDATA[+B]]></c> without an extra option) disables the break
+ handler. Option <c><![CDATA[i]]></c> makes the emulator ignore any
+ break signal.</p>
+ <p>If option <c><![CDATA[c]]></c> is used with
+ <c><![CDATA[oldshell]]></c> on Unix, <c><![CDATA[Ctrl-C]]></c> will
+ restart the shell process rather than interrupt it.</p>
+ <p>Notice that on Windows, this flag is only applicable for
+ <c><![CDATA[werl]]></c>, not <c><![CDATA[erl]]></c>
+ (<c><![CDATA[oldshell]]></c>). Notice also that
+ <c><![CDATA[Ctrl-Break]]></c> is used instead of
+ <c><![CDATA[Ctrl-C]]></c> on Windows.</p>
</item>
<tag><marker id="+c"/><c><![CDATA[+c true | false]]></c></tag>
<item>
- <p>Enable or disable
- <seealso marker="time_correction#Time_Correction">time correction</seealso>:</p>
+ <p>Enables or disables
+ <seealso marker="time_correction#Time_Correction">time
+ correction</seealso>:</p>
<taglist>
<tag><c>true</c></tag>
- <item><p>Enable time correction. This is the default if
- time correction is supported on the specific platform.</p></item>
-
- <tag><c>false</c></tag>
- <item><p>Disable time correction.</p></item>
- </taglist>
- <p>For backwards compatibility, the boolean value can be omitted.
- This is interpreted as <c>+c false</c>.
- </p>
- </item>
- <tag><marker id="+C_"/><c><![CDATA[+C no_time_warp | single_time_warp | multi_time_warp]]></c></tag>
- <item>
- <p>Set
- <seealso marker="time_correction#Time_Warp_Modes">time warp mode</seealso>:
- </p>
- <taglist>
- <tag><c>no_time_warp</c></tag>
- <item><p><seealso marker="time_correction#No_Time_Warp_Mode">No Time Warp Mode</seealso> (the default)</p></item>
- <tag><c>single_time_warp</c></tag>
- <item><p><seealso marker="time_correction#Single_Time_Warp_Mode">Single Time Warp Mode</seealso></p></item>
- <tag><c>multi_time_warp</c></tag>
- <item><p><seealso marker="time_correction#Multi_Time_Warp_Mode">Multi Time Warp Mode</seealso></p></item>
- </taglist>
+ <item>Enables time correction. This is the default if
+ time correction is supported on the specific platform.</item>
+ <tag><c>false</c></tag>
+ <item>Disables time correction.</item>
+ </taglist>
+ <p>For backward compatibility, the boolean value can be omitted.
+ This is interpreted as <c>+c false</c>.</p>
+ </item>
+ <tag><marker id="+C_"/><c><![CDATA[+C no_time_warp | single_time_warp |
+ multi_time_warp]]></c></tag>
+ <item>
+ <p>Sets <seealso marker="time_correction#Time_Warp_Modes">time warp
+ mode</seealso>:</p>
+ <taglist>
+ <tag><c>no_time_warp</c></tag>
+ <item><seealso marker="time_correction#No_Time_Warp_Mode">
+ No time warp mode</seealso> (the default)</item>
+ <tag><c>single_time_warp</c></tag>
+ <item><seealso marker="time_correction#Single_Time_Warp_Mode">
+ Single time warp mode</seealso></item>
+ <tag><c>multi_time_warp</c></tag>
+ <item><seealso marker="time_correction#Multi_Time_Warp_Mode">
+ Multi-time warp mode</seealso></item>
+ </taglist>
</item>
<tag><c><![CDATA[+d]]></c></tag>
<item>
<p>If the emulator detects an internal error (or runs out of memory),
- it will by default generate both a crash dump and a core dump.
- The core dump will, however, not be very useful since the content
- of process heaps is destroyed by the crash dump generation.</p>
-
- <p>The <c>+d</c> option instructs the emulator to only produce a
- core dump and no crash dump if an internal error is detected.</p>
-
- <p>Calling <c>erlang:halt/1</c> with a string argument will still
- produce a crash dump. On Unix systems, sending an emulator process
- a SIGUSR1 signal will also force a crash dump.</p>
+ it, by default, generates both a crash dump and a core dump.
+ The core dump is, however, not very useful as the content
+ of process heaps is destroyed by the crash dump generation.</p>
+ <p>Option <c>+d</c> instructs the emulator to produce only a
+ core dump and no crash dump if an internal error is detected.</p>
+ <p>Calling <seealso marker="erlang:halt/1">
+ <c>erlang:halt/1</c></seealso> with a string argument still
+ produces a crash dump. On Unix systems, sending an emulator process
+ a <c>SIGUSR1</c> signal also forces a crash dump.</p>
</item>
<tag><marker id="+e"/><c><![CDATA[+e Number]]></c></tag>
<item>
- <p>Set max number of ETS tables.</p>
+ <p>Sets the maximum number of ETS tables.</p>
</item>
<tag><c><![CDATA[+ec]]></c></tag>
<item>
- <p>Force the <c>compressed</c> option on all ETS tables.
- Only intended for test and evaluation.</p>
+ <p>Forces option <c>compressed</c> on all ETS tables.
+ Only intended for test and evaluation.</p>
</item>
- <tag><marker id="file_name_encoding"></marker><c><![CDATA[+fnl]]></c></tag>
+ <tag><marker id="file_name_encoding"></marker>
+ <c><![CDATA[+fnl]]></c></tag>
<item>
- <p>The VM works with file names as if they are encoded using the ISO-latin-1 encoding, disallowing Unicode characters with codepoints beyond 255.</p>
- <p>See <seealso marker="stdlib:unicode_usage#unicode_file_names">STDLIB User's Guide</seealso> for more infomation about unicode file names. Note that this value also applies to command-line parameters and environment variables (see <seealso marker="stdlib:unicode_usage#unicode_in_environment_and_parameters">STDLIB User's Guide</seealso>).</p>
+ <p>The virtual machine works with filenames as if they are encoded
+ using the ISO Latin-1 encoding, disallowing Unicode characters with
+ code points &gt; 255.</p>
+ <p>For more information about Unicode filenames, see section
+ <seealso marker="stdlib:unicode_usage#unicode_file_names">Unicode
+ Filenames</seealso> in the STDLIB User's Guide. Notice that
+ this value also applies to command-line parameters and environment
+ variables (see section <seealso
+ marker="stdlib:unicode_usage#unicode_in_environment_and_parameters">
+ Unicode in Enviroment and Parameters</seealso> in the STDLIB
+ User's Guide).</p>
</item>
<tag><c><![CDATA[+fnu[{w|i|e}]]]></c></tag>
<item>
- <p>The VM works with file names as if they are encoded using
- UTF-8 (or some other system specific Unicode encoding). This
- is the default on operating systems that enforce Unicode
- encoding, i.e. Windows and MacOS X.</p>
- <p>The <c>+fnu</c> switch can be followed by <c>w</c>,
- <c>i</c>, or <c>e</c> to control the way wrongly encoded file
- names are to be reported. <c>w</c> means that a warning is
- sent to the <c>error_logger</c> whenever a wrongly encoded
- file name is "skipped" in directory listings, <c>i</c> means
- that those wrongly encoded file names are silently ignored and
- <c>e</c> means that the API function will return an error
- whenever a wrongly encoded file (or directory) name is
- encountered. <c>w</c> is the default. Note that
- <c>file:read_link/1</c> will always return an error if the
- link points to an invalid file name.</p>
- <p>See <seealso marker="stdlib:unicode_usage#unicode_file_names">STDLIB User's Guide</seealso> for more infomation about unicode file names. Note that this value also applies to command-line parameters and environment variables (see <seealso marker="stdlib:unicode_usage#unicode_in_environment_and_parameters">STDLIB User's Guide</seealso>).</p>
+ <p>The virtual machine works with filenames as if they are encoded
+ using UTF-8 (or some other system-specific Unicode encoding). This is
+ the default on operating systems that enforce Unicode encoding, that
+ is, Windows and MacOS X.</p>
+ <p>The <c>+fnu</c> switch can be followed by <c>w</c>, <c>i</c>, or
+ <c>e</c> to control how wrongly encoded filenames are to be
+ reported:</p>
+ <list type="bulleted">
+ <item>
+ <p><c>w</c> means that a warning is sent to the <c>error_logger</c>
+ whenever a wrongly encoded filename is "skipped" in directory
+ listings. This is the default.</p>
+ </item>
+ <item>
+ <p><c>i</c> means that those wrongly encoded filenames are silently
+ ignored.</p>
+ </item>
+ <item>
+ <p><c>e</c> means that the API function returns an error whenever a
+ wrongly encoded filename (or directory name) is encountered.</p>
+ </item>
+ </list>
+ <p>Notice that <seealso marker="kernel:file#read_link/1">
+ <c>file:read_link/1</c></seealso> always returns an error if the link
+ points to an invalid filename.</p>
+ <p>For more information about Unicode filenames, see section
+ <seealso marker="stdlib:unicode_usage#unicode_file_names">Unicode
+ Filenames</seealso> in the STDLIB User's Guide. Notice that
+ this value also applies to command-line parameters and environment
+ variables (see section <seealso
+ marker="stdlib:unicode_usage#unicode_in_environment_and_parameters">
+ Unicode in Enviroment and Parameters</seealso> in the STDLIB
+ User's Guide).</p>
</item>
<tag><c><![CDATA[+fna[{w|i|e}]]]></c></tag>
<item>
<p>Selection between <c>+fnl</c> and <c>+fnu</c> is done based
- on the current locale settings in the OS, meaning that if you
- have set your terminal for UTF-8 encoding, the filesystem is
- expected to use the same encoding for file names. This is
- default on all operating systems except MacOS X and
- Windows.</p>
- <p>The <c>+fna</c> switch can be followed by <c>w</c>,
- <c>i</c>, or <c>e</c>. This will have effect if the locale
- settings cause the behavior of <c>+fnu</c> to be selected.
- See the description of <c>+fnu</c> above. If the locale
- settings cause the behavior of <c>+fnl</c> to be selected,
- then <c>w</c>, <c>i</c>, or <c>e</c> will not have any
- effect.</p>
- <p>See <seealso marker="stdlib:unicode_usage#unicode_file_names">STDLIB User's Guide</seealso> for more infomation about unicode file names. Note that this value also applies to command-line parameters and environment variables (see <seealso marker="stdlib:unicode_usage#unicode_in_environment_and_parameters">STDLIB User's Guide</seealso>).</p>
+ on the current locale settings in the OS. This means that if you
+ have set your terminal for UTF-8 encoding, the filesystem is
+ expected to use the same encoding for filenames. This is
+ default on all operating systems, except MacOS X and Windows.</p>
+ <p>The <c>+fna</c> switch can be followed by <c>w</c>, <c>i</c>, or
+ <c>e</c>. This has effect if the locale settings cause the behavior
+ of <c>+fnu</c> to be selected; see the description of <c>+fnu</c>
+ above. If the locale settings cause the behavior of <c>+fnl</c> to be
+ selected, then <c>w</c>, <c>i</c>, or <c>e</c> have no effect.</p>
+ <p>For more information about Unicode filenames, see section
+ <seealso marker="stdlib:unicode_usage#unicode_file_names">Unicode
+ Filenames</seealso> in the STDLIB User's Guide. Notice that
+ this value also applies to command-line parameters and environment
+ variables (see section <seealso
+ marker="stdlib:unicode_usage#unicode_in_environment_and_parameters">
+ Unicode in Enviroment and Parameters</seealso> in the STDLIB
+ User's Guide).</p>
</item>
<tag><c><![CDATA[+hms Size]]></c></tag>
<item>
@@ -627,92 +708,91 @@
<p>Sets the default binary virtual heap size of processes to the size
<c><![CDATA[Size]]></c>.</p>
</item>
+ <tag><marker id="+hmax"/><c><![CDATA[+hmax Size]]></c></tag>
+ <item>
+ <p>Sets the default maximum heap size of processes to the size
+ <c><![CDATA[Size]]></c>. Defaults to <c>0</c>, which means that no
+ maximum heap size is used. For more information, see
+ <seealso marker="erlang#process_flag_max_heap_size">
+ <c>process_flag(max_heap_size, MaxHeapSize)</c></seealso>.</p>
+ </item>
+ <tag><marker id="+hmaxel"/><c><![CDATA[+hmaxel true|false]]></c></tag>
+ <item>
+ <p>Sets whether to send an error logger message or not for processes
+ reaching the maximum heap size. Defaults to <c>true</c>.
+ For more information, see
+ <seealso marker="erlang#process_flag_max_heap_size">
+ <c>process_flag(max_heap_size, MaxHeapSize)</c></seealso>.</p>
+ </item>
+ <tag><marker id="+hmaxk"/><c><![CDATA[+hmaxk true|false]]></c></tag>
+ <item>
+ <p>Sets whether to kill processes reaching the maximum heap size or not.
+ Default to <c>true</c>. For more information, see
+ <seealso marker="erlang#process_flag_max_heap_size">
+ <c>process_flag(max_heap_size, MaxHeapSize)</c></seealso>.</p>
+ </item>
<tag><c><![CDATA[+hpds Size]]></c></tag>
<item>
<p>Sets the initial process dictionary size of processes to the size
<c><![CDATA[Size]]></c>.</p>
</item>
+ <tag><marker id="+hmqd"/><c>+hmqd off_heap|on_heap</c></tag>
+ <item>
+ <p>Sets the default value for process flag <c>message_queue_data</c>.
+ Defaults to <c>on_heap</c>. If <c>+hmqd</c> is not
+ passed, <c>on_heap</c> will be the default. For more information, see
+ <seealso marker="erlang#process_flag_message_queue_data">
+ <c>process_flag(message_queue_data, MQD)</c></seealso>.</p>
+ </item>
<tag><c><![CDATA[+K true | false]]></c></tag>
<item>
- <p>Enables or disables the kernel poll functionality if
- the emulator supports it. Default is <c><![CDATA[false]]></c> (disabled).
- If the emulator does not support kernel poll, and
- the <c><![CDATA[+K]]></c> flag is passed to the emulator, a warning is
+ <p>Enables or disables the kernel poll functionality if supported by
+ the emulator. Defaults to <c><![CDATA[false]]></c> (disabled).
+ If the emulator does not support kernel poll, and flag
+ <c><![CDATA[+K]]></c> is passed to the emulator, a warning is
issued at startup.</p>
</item>
<tag><c><![CDATA[+l]]></c></tag>
<item>
- <p>Enables auto load tracing, displaying info while loading
+ <p>Enables autoload tracing, displaying information while loading
code.</p>
</item>
<tag><c><![CDATA[+L]]></c></tag>
<item>
- <p>Don't load information about source file names and line numbers.
- This will save some memory, but exceptions will not contain
- information about the file names and line numbers.
- </p>
+ <p>Prevents loading information about source filenames and line
+ numbers. This saves some memory, but exceptions do not contain
+ information about the filenames and line numbers.</p>
</item>
<tag><marker id="erts_alloc"/><c><![CDATA[+MFlag Value]]></c></tag>
<item>
- <p>Memory allocator specific flags, see
- <seealso marker="erts_alloc">erts_alloc(3)</seealso> for
- further information.</p>
- </item>
- <tag><marker id="+n"/><c><![CDATA[+n Behavior]]></c></tag>
- <item>
- <p>Control behavior of signals to ports.</p>
- <p>As of OTP-R16 signals to ports are truly asynchronously
- delivered. Note that signals always have been documented as
- asynchronous. The underlying implementation has, however,
- previously delivered these signals synchronously. Correctly
- written Erlang programs should be able to handle this without
- any issues. Bugs in existing Erlang programs that make false
- assumptions about signals to ports may, however, be tricky to
- find. This switch has been introduced in order to at least
- make it easier to compare behaviors during a transition period.
- Note that <em>this flag is deprecated</em> as of its
- introduction, and is scheduled for removal in OTP-R17.
- <c>Behavior</c> should be one of the following characters:</p>
- <taglist>
- <tag><c>d</c></tag>
- <item>The default. Asynchronous signals. A process that sends
- a signal to a port may continue execution before the signal
- has been delivered to the port.</item>
- <tag><c>s</c></tag>
- <item>Synchronous signals. A processes that sends a signal
- to a port will not continue execution until the signal has
- been delivered. Should <em>only</em> be used for testing and
- debugging.</item>
- <tag><c>a</c></tag>
- <item>Asynchronous signals. As the default, but a processes
- that sends a signal will even more frequently continue
- execution before the signal has been delivered to the
- port. Should <em>only</em> be used for testing and
- debugging.</item>
- </taglist>
- </item>
- <tag><marker id="+pc"/><marker id="printable_character_range"/><c><![CDATA[+pc Range]]></c></tag>
- <item>
- <p>Sets the range of characters that the system will consider printable in heuristic detection of strings. This typically affects the shell, debugger and io:format functions (when ~tp is used in the format string).</p>
- <p>Currently two values for the <c>Range</c> are supported:</p>
- <taglist>
- <tag><c>latin1</c></tag> <item>The default. Only characters
- in the ISO-latin-1 range can be considered printable, which means
- that a character with a code point &gt; 255 will never be
- considered printable and that lists containing such
- characters will be displayed as lists of integers rather
- than text strings by tools.</item>
- <tag><c>unicode</c></tag>
- <item>All printable Unicode characters are considered when
- determining if a list of integers is to be displayed in
- string syntax. This may give unexpected results if for
- example your font does not cover all Unicode
- characters.</item>
- </taglist>
- <p>Se also <seealso marker="stdlib:io#printable_range/0">
- io:printable_range/0</seealso>.</p>
- </item>
- <tag><marker id="+P"/><marker id="max_processes"/><c><![CDATA[+P Number|legacy]]></c></tag>
+ <p>Memory allocator-specific flags. For more information, see
+ <seealso marker="erts_alloc"><c>erts_alloc(3)</c></seealso>.</p>
+ </item>
+ <tag><marker id="+pc"/><marker id="printable_character_range"/>
+ <c><![CDATA[+pc Range]]></c></tag>
+ <item>
+ <p>Sets the range of characters that the system considers printable in
+ heuristic detection of strings. This typically affects the shell,
+ debugger, and <c>io:format</c> functions (when <c>~tp</c> is used in
+ the format string).</p>
+ <p>Two values are supported for <c>Range</c>:</p>
+ <taglist>
+ <tag><c>latin1</c></tag>
+ <item>The default. Only characters in the ISO Latin-1 range can be
+ considered printable. This means that a character with a code point
+ &gt; 255 is never considered printable and that lists containing
+ such characters are displayed as lists of integers rather than text
+ strings by tools.</item>
+ <tag><c>unicode</c></tag>
+ <item>All printable Unicode characters are considered when
+ determining if a list of integers is to be displayed in
+ string syntax. This can give unexpected results if, for
+ example, your font does not cover all Unicode characters.</item>
+ </taglist>
+ <p>See also <seealso marker="stdlib:io#printable_range/0">
+ <c>io:printable_range/0</c></seealso> in STDLIB.</p>
+ </item>
+ <tag><marker id="+P"/><marker id="max_processes"/><c><![CDATA[+P Number]]></c></tag>
<item>
<p>Sets the maximum number of simultaneously existing processes for this
system if a <c>Number</c> is passed as value. Valid range for
@@ -724,15 +804,8 @@
checked by calling
<seealso marker="erlang#system_info_process_limit">erlang:system_info(process_limit)</seealso>.</p>
<p>The default value is <c>262144</c></p>
- <p>If <c>legacy</c> is passed as value, the legacy algorithm for
- allocation of process identifiers will be used. Using the legacy
- algorithm, identifiers will be allocated in a strictly increasing
- fashion until largest possible identifier has been reached. Note that
- this algorithm suffers from performance issues and can under certain
- circumstances be extremely expensive. The legacy algoritm is deprecated,
- and the <c>legacy</c> option is scheduled for removal in OTP-R18.</p>
</item>
- <tag><marker id="+Q"/><marker id="max_ports"/><c><![CDATA[+Q Number|legacy]]></c></tag>
+ <tag><marker id="+Q"/><marker id="max_ports"/><c><![CDATA[+Q Number]]></c></tag>
<item>
<p>Sets the maximum number of simultaneously existing ports for this
system if a Number is passed as value. Valid range for <c>Number</c>
@@ -749,164 +822,179 @@
than <c>65536</c>, the chosen value will increased to a value
larger or equal to the maximum amount of file descriptors that
can be opened.</p>
- <p>On Windows the default value is set to <c>8196</c> because the
+ <p>On Windows the default value is set to <c>8196</c> because the
normal OS limitations are set higher than most machines can handle.</p>
- <p>Previously the environment variable <c>ERL_MAX_PORTS</c> was used
- for setting the maximum number of simultaneously existing ports. This
- environment variable is deprecated, and scheduled for removal in
- OTP-R17, but can still be used.</p>
- <p>If <c>legacy</c> is passed as value, the legacy algorithm for
- allocation of port identifiers will be used. Using the legacy
- algorithm, identifiers will be allocated in a strictly increasing
- fashion until largest possible identifier has been reached. Note that
- this algorithm suffers from performance issues and can under certain
- circumstances be extremely expensive. The legacy algoritm is deprecated,
- and the <c>legacy</c> option is scheduled for removal in OTP-R18.</p>
</item>
<tag><marker id="compat_rel"/><c><![CDATA[+R ReleaseNumber]]></c></tag>
<item>
<p>Sets the compatibility mode.</p>
- <p>The distribution mechanism is not backwards compatible by
- default. This flags sets the emulator in compatibility mode
+ <p>The distribution mechanism is not backward compatible by
+ default. This flag sets the emulator in compatibility mode
with an earlier Erlang/OTP release <c><![CDATA[ReleaseNumber]]></c>.
The release number must be in the range
<c><![CDATA[<current release>-2..<current release>]]></c>. This
- limits the emulator, making it possible for it to communicate
- with Erlang nodes (as well as C- and Java nodes) running that
- earlier release.</p>
- <p>Note: Make sure all nodes (Erlang-, C-, and Java nodes) of
- a distributed Erlang system is of the same Erlang/OTP release,
- or from two different Erlang/OTP releases X and Y, where
- <em>all</em> Y nodes have compatibility mode X.</p>
+ limits the emulator, making it possible for it to communicate
+ with Erlang nodes (as well as C- and Java nodes) running that
+ earlier release.</p>
+ <note>
+ <p>Ensure that all nodes (Erlang-, C-, and Java nodes) of
+ a distributed Erlang system is of the same Erlang/OTP release,
+ or from two different Erlang/OTP releases X and Y, where
+ <em>all</em> Y nodes have compatibility mode X.</p>
+ </note>
</item>
<tag><c><![CDATA[+r]]></c></tag>
<item>
- <p>Force ets memory block to be moved on realloc.</p>
+ <p>Forces ETS memory block to be moved on realloc.</p>
</item>
<tag><marker id="+rg"/><c><![CDATA[+rg ReaderGroupsLimit]]></c></tag>
<item>
- <p>Limits the amount of reader groups used by read/write locks
- optimized for read operations in the Erlang runtime system. By
- default the reader groups limit equals 64.</p>
- <p>When the amount of schedulers is less than or equal to the reader
- groups limit, each scheduler has its own reader group. When the
- amount of schedulers is larger than the reader groups limit,
- schedulers share reader groups. Shared reader groups degrades
- read lock and read unlock performance while a large amount of
- reader groups degrades write lock performance, so the limit is a
- tradeoff between performance for read operations and performance
- for write operations. Each reader group currently consumes 64 byte
- in each read/write lock. Also note that a runtime system using
- shared reader groups benefits from <seealso marker="#+sbt">binding
- schedulers to logical processors</seealso>, since the reader groups
- are distributed better between schedulers.</p>
- </item>
- <tag><marker id="+S"/><c><![CDATA[+S Schedulers:SchedulerOnline]]></c></tag>
- <item>
- <p>Sets the number of scheduler threads to create and scheduler
- threads to set online when SMP support has been enabled. The maximum for
- both values is 1024. If the Erlang runtime system is able to determine the
- amount of logical processors configured and logical processors available,
- <c>Schedulers</c> will default to logical processors configured, and
- <c>SchedulersOnline</c> will default to logical processors available;
- otherwise, the default values will be 1. <c>Schedulers</c> may be omitted
- if <c>:SchedulerOnline</c> is not and vice versa. The number of schedulers
- online can be changed at run time via
- <seealso marker="erlang#system_flag_schedulers_online">erlang:system_flag(schedulers_online, SchedulersOnline)</seealso>.
- </p>
- <p>If <c>Schedulers</c> or <c>SchedulersOnline</c> is specified as a
- negative number, the value is subtracted from the default number of
- logical processors configured or logical processors available, respectively.
- </p>
- <p>Specifying the value 0 for <c>Schedulers</c> or <c>SchedulersOnline</c>
- resets the number of scheduler threads or scheduler threads online respectively
- to its default value.
- </p>
- <p>This option is ignored if the emulator doesn't have
- SMP support enabled (see the <seealso marker="#smp">-smp</seealso>
- flag).</p>
- </item>
- <tag><marker id="+SP"/><c><![CDATA[+SP SchedulersPercentage:SchedulersOnlinePercentage]]></c></tag>
- <item>
- <p>Similar to <seealso marker="#+S">+S</seealso> but uses percentages to set the
- number of scheduler threads to create, based on logical processors configured,
- and scheduler threads to set online, based on logical processors available, when
- SMP support has been enabled. Specified values must be greater than 0. For example,
- <c>+SP 50:25</c> sets the number of scheduler threads to 50% of the logical processors
- configured and the number of scheduler threads online to 25% of the logical processors available.
- <c>SchedulersPercentage</c> may be omitted if <c>:SchedulersOnlinePercentage</c> is
- not and vice versa. The number of schedulers online can be changed at run time via
- <seealso marker="erlang#system_flag_schedulers_online">erlang:system_flag(schedulers_online, SchedulersOnline)</seealso>.
- </p>
- <p>This option interacts with <seealso marker="#+S">+S</seealso> settings.
- For example, on a system with 8 logical cores configured and 8 logical cores
- available, the combination of the options <c>+S 4:4 +SP 50:25</c> (in either order)
- results in 2 scheduler threads (50% of 4) and 1 scheduler thread online (25% of 4).
- </p>
- <p>This option is ignored if the emulator doesn't have
- SMP support enabled (see the <seealso marker="#smp">-smp</seealso>
- flag).</p>
- </item>
- <tag><marker id="+SDcpu"/><c><![CDATA[+SDcpu DirtyCPUSchedulers:DirtyCPUSchedulersOnline]]></c></tag>
- <item>
- <p>Sets the number of dirty CPU scheduler threads to create and dirty
- CPU scheduler threads to set online when threading support has been
- enabled. The maximum for both values is 1024, and each value is further
- limited by the settings for normal schedulers: the number of dirty CPU
- scheduler threads created cannot exceed the number of normal scheduler
- threads created, and the number of dirty CPU scheduler threads online
- cannot exceed the number of normal scheduler threads online (see the
- <seealso marker="#+S">+S</seealso> and <seealso marker="#+SP">+SP</seealso>
- flags for more details). By default, the number of dirty CPU scheduler
- threads created equals the number of normal scheduler threads created,
- and the number of dirty CPU scheduler threads online equals the number
- of normal scheduler threads online. <c>DirtyCPUSchedulers</c> may be
- omitted if <c>:DirtyCPUSchedulersOnline</c> is not and vice versa. The
- number of dirty CPU schedulers online can be changed at run time via
- <seealso marker="erlang#system_flag_dirty_cpu_schedulers_online">erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline)</seealso>.
- </p>
- <p>This option is ignored if the emulator doesn't have threading support
- enabled. Currently, <em>this option is experimental</em> and is supported only
- if the emulator was configured and built with support for dirty schedulers
- enabled (it's disabled by default).
- </p>
- </item>
- <tag><marker id="+SDPcpu"/><c><![CDATA[+SDPcpu DirtyCPUSchedulersPercentage:DirtyCPUSchedulersOnlinePercentage]]></c></tag>
- <item>
- <p>Similar to <seealso marker="#+SDcpu">+SDcpu</seealso> but uses percentages to set the
- number of dirty CPU scheduler threads to create and number of dirty CPU scheduler threads
- to set online when threading support has been enabled. Specified values must be greater
- than 0. For example, <c>+SDPcpu 50:25</c> sets the number of dirty CPU scheduler threads
- to 50% of the logical processors configured and the number of dirty CPU scheduler threads
- online to 25% of the logical processors available. <c>DirtyCPUSchedulersPercentage</c> may
- be omitted if <c>:DirtyCPUSchedulersOnlinePercentage</c> is not and vice versa. The
- number of dirty CPU schedulers online can be changed at run time via
- <seealso marker="erlang#system_flag_dirty_cpu_schedulers_online">erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline)</seealso>.
- </p>
- <p>This option interacts with <seealso marker="#+SDcpu">+SDcpu</seealso> settings.
- For example, on a system with 8 logical cores configured and 8 logical cores available,
- the combination of the options <c>+SDcpu 4:4 +SDPcpu 50:25</c> (in either order) results
- in 2 dirty CPU scheduler threads (50% of 4) and 1 dirty CPU scheduler thread online (25% of 4).
- </p>
- <p>This option is ignored if the emulator doesn't have threading support
- enabled. Currently, <em>this option is experimental</em> and is supported only
- if the emulator was configured and built with support for dirty schedulers
- enabled (it's disabled by default).
- </p>
- </item>
- <tag><marker id="+SDio"/><c><![CDATA[+SDio IOSchedulers]]></c></tag>
- <item>
- <p>Sets the number of dirty I/O scheduler threads to create when threading
- support has been enabled. The valid range is 0-1024. By default, the number
- of dirty I/O scheduler threads created is 10, same as the default number of
- threads in the <seealso marker="#async_thread_pool_size">async thread pool
- </seealso>.
- </p>
- <p>This option is ignored if the emulator doesn't have threading support
- enabled. Currently, <em>this option is experimental</em> and is supported only
- if the emulator was configured and built with support for dirty schedulers
- enabled (it's disabled by default).
- </p>
+ <p>Limits the number of reader groups used by read/write locks
+ optimized for read operations in the Erlang runtime system. By
+ default the reader groups limit is 64.</p>
+ <p>When the number of schedulers is less than or equal to the reader
+ groups limit, each scheduler has its own reader group. When the
+ number of schedulers is larger than the reader groups limit,
+ schedulers share reader groups. Shared reader groups degrade
+ read lock and read unlock performance while many
+ reader groups degrade write lock performance. So, the limit is a
+ tradeoff between performance for read operations and performance
+ for write operations. Each reader group consumes 64 byte
+ in each read/write lock.</p>
+ <p>Notice that a runtime system using shared reader groups benefits from
+ <seealso marker="#+sbt">binding schedulers to logical
+ processors</seealso>, as the reader groups are distributed better
+ between schedulers.</p>
+ </item>
+ <tag><marker id="+S"/>
+ <c><![CDATA[+S Schedulers:SchedulerOnline]]></c></tag>
+ <item>
+ <p>Sets the number of scheduler threads to create and scheduler threads
+ to set online when SMP support has been enabled. The maximum for both
+ values is 1024. If the Erlang runtime system is able to determine the
+ number of logical processors configured and logical processors
+ available, <c>Schedulers</c> defaults to logical processors
+ configured, and <c>SchedulersOnline</c> defaults to logical processors
+ available; otherwise the default values are 1. <c>Schedulers</c> can
+ be omitted if <c>:SchedulerOnline</c> is not and conversely. The
+ number of schedulers online can be changed at runtime through
+ <seealso marker="erlang#system_flag_schedulers_online">
+ <c>erlang:system_flag(schedulers_online,
+ SchedulersOnline)</c></seealso>.</p>
+ <p>If <c>Schedulers</c> or <c>SchedulersOnline</c> is specified as a
+ negative number, the value is subtracted from the default number of
+ logical processors configured or logical processors available,
+ respectively.</p>
+ <p>Specifying value <c>0</c> for <c>Schedulers</c> or
+ <c>SchedulersOnline</c> resets the number of scheduler threads or
+ scheduler threads online, respectively, to its default value.</p>
+ <p>This option is ignored if the emulator does not have SMP support
+ enabled (see flag <seealso marker="#smp"><c>-smp</c></seealso>).</p>
+ </item>
+ <tag><marker id="+SP"/><c><![CDATA[+SP
+ SchedulersPercentage:SchedulersOnlinePercentage]]></c></tag>
+ <item>
+ <p>Similar to <seealso marker="#+S"><c>+S</c></seealso> but uses
+ percentages to set the number of scheduler threads to create, based
+ on logical processors configured, and scheduler threads to set online,
+ based on logical processors available, when SMP support has been
+ enabled. Specified values must be &gt; 0. For example,
+ <c>+SP 50:25</c> sets the number of scheduler threads to 50% of the
+ logical processors configured, and the number of scheduler threads
+ online to 25% of the logical processors available.
+ <c>SchedulersPercentage</c> can be omitted if
+ <c>:SchedulersOnlinePercentage</c> is not and conversely. The number
+ of schedulers online can be changed at runtime through
+ <seealso marker="erlang#system_flag_schedulers_online">
+ <c>erlang:system_flag(schedulers_online,
+ SchedulersOnline)</c></seealso>.</p>
+ <p>This option interacts with <seealso marker="#+S"><c>+S</c></seealso>
+ settings. For example, on a system with 8 logical cores configured
+ and 8 logical cores available, the combination of the options
+ <c>+S 4:4 +SP 50:25</c> (in either order) results in 2 scheduler
+ threads (50% of 4) and 1 scheduler thread online (25% of 4).</p>
+ <p>This option is ignored if the emulator does not have SMP support
+ enabled (see flag <seealso marker="#smp"><c>-smp</c></seealso>).</p>
+ </item>
+ <tag><marker id="+SDcpu"/><c><![CDATA[+SDcpu
+ DirtyCPUSchedulers:DirtyCPUSchedulersOnline]]></c></tag>
+ <item>
+ <p>Sets the number of dirty CPU scheduler threads to create and dirty
+ CPU scheduler threads to set online when threading support has been
+ enabled. The maximum for both values is 1024, and each value is
+ further limited by the settings for normal schedulers:</p>
+ <list type="bulleted">
+ <item>The number of dirty CPU scheduler threads created cannot exceed
+ the number of normal scheduler threads created.</item>
+ <item>The number of dirty CPU scheduler threads online cannot exceed
+ the number of normal scheduler threads online.</item>
+ </list>
+ <p>For details, see the <seealso marker="#+S"><c>+S</c></seealso> and
+ <seealso marker="#+SP"><c>+SP</c></seealso>. By default, the number
+ of dirty CPU scheduler threads created equals the number of normal
+ scheduler threads created, and the number of dirty CPU scheduler
+ threads online equals the number of normal scheduler threads online.
+ <c>DirtyCPUSchedulers</c> can be omitted if
+ <c>:DirtyCPUSchedulersOnline</c> is not and conversely. The number of
+ dirty CPU schedulers online can be changed at runtime through
+ <seealso marker="erlang#system_flag_dirty_cpu_schedulers_online">
+ <c>erlang:system_flag(dirty_cpu_schedulers_online,
+ DirtyCPUSchedulersOnline)</c></seealso>.</p>
+ <p>The amount of dirty CPU schedulers is limited by the amount of
+ normal schedulers in order to limit the effect on processes
+ executing on ordinary schedulers. If the amount of dirty CPU
+ schedulers was allowed to be unlimited, dirty CPU bound jobs would
+ potentially starve normal jobs.</p>
+ <p>This option is ignored if the emulator does not have threading
+ support enabled. <em>This option is experimental</em> and
+ is supported only if the emulator was configured and built with
+ support for dirty schedulers enabled (it is disabled by default).</p>
+ </item>
+ <tag><marker id="+SDPcpu"/><c><![CDATA[+SDPcpu
+ DirtyCPUSchedulersPercentage:DirtyCPUSchedulersOnlinePercentage]]></c></tag>
+ <item>
+ <p>Similar to <seealso marker="#+SDcpu"><c>+SDcpu</c></seealso> but
+ uses percentages to set the number of dirty CPU scheduler threads to
+ create and the number of dirty CPU scheduler threads to set online
+ when threading support has been enabled. Specified values must be
+ &gt; 0. For example, <c>+SDPcpu 50:25</c> sets the number of dirty
+ CPU scheduler threads to 50% of the logical processors configured
+ and the number of dirty CPU scheduler threads online to 25% of the
+ logical processors available. <c>DirtyCPUSchedulersPercentage</c> can
+ be omitted if <c>:DirtyCPUSchedulersOnlinePercentage</c> is not and
+ conversely. The number of dirty CPU schedulers online can be changed
+ at runtime through
+ <seealso marker="erlang#system_flag_dirty_cpu_schedulers_online">
+ <c>erlang:system_flag(dirty_cpu_schedulers_online,
+ DirtyCPUSchedulersOnline)</c></seealso>.</p>
+ <p>This option interacts with <seealso
+ marker="#+SDcpu"><c>+SDcpu</c></seealso> settings. For example, on a
+ system with 8 logical cores configured and 8 logical cores available,
+ the combination of the options <c>+SDcpu 4:4 +SDPcpu 50:25</c> (in
+ either order) results in 2 dirty CPU scheduler threads (50% of 4) and
+ 1 dirty CPU scheduler thread online (25% of 4).</p>
+ <p>This option is ignored if the emulator does not have threading
+ support enabled. <em>This option is experimental</em> and
+ is supported only if the emulator was configured and built with
+ support for dirty schedulers enabled (it is disabled by default).</p>
+ </item>
+ <tag><marker id="+SDio"/><c><![CDATA[+SDio DirtyIOSchedulers]]></c></tag>
+ <item>
+ <p>Sets the number of dirty I/O scheduler threads to create when
+ threading support has been enabled. Valid range is 0-1024. By
+ default, the number of dirty I/O scheduler threads created is 10,
+ same as the default number of threads in the <seealso
+ marker="#async_thread_pool_size">async thread pool</seealso>.</p>
+ <p>The amount of dirty IO schedulers is not limited by the amount of
+ normal schedulers <seealso marker="#+SDcpu">like the amount of
+ dirty CPU schedulers</seealso>. This since only I/O bound work is
+ expected to execute on dirty I/O schedulers. If the user should schedule CPU
+ bound jobs on dirty I/O schedulers, these jobs might starve ordinary
+ jobs executing on ordinary schedulers.</p>
+ <p>This option is ignored if the emulator does not have threading
+ support enabled. <em>This option is experimental</em> and
+ is supported only if the emulator was configured and built with
+ support for dirty schedulers enabled (it is disabled by default).</p>
</item>
<tag><c><![CDATA[+sFlag Value]]></c></tag>
<item>
@@ -914,238 +1002,237 @@
<taglist>
<tag><marker id="+sbt"/><c>+sbt BindType</c></tag>
<item>
- <p>Set scheduler bind type.</p>
- <p>Schedulers can also be bound using the
- <seealso marker="#+stbt">+stbt</seealso> flag. The only difference
- between these two flags is how the following errors are handled:</p>
- <list>
- <item>Binding of schedulers is not supported on the specific
- platform.</item>
- <item>No available CPU topology. That is the runtime system
- was not able to automatically detected the CPU topology, and
- no <seealso marker="#+sct">user defined CPU topology</seealso>
- was set.</item>
- </list>
- <p>If any of these errors occur when <c>+sbt</c> has been passed,
- the runtime system will print an error message, and refuse to
- start. If any of these errors occur when <c>+stbt</c> has been
- passed, the runtime system will silently ignore the error, and
- start up using unbound schedulers.</p>
- <p>Currently valid <c>BindType</c>s:
- </p>
- <taglist>
- <tag><c>u</c></tag>
- <item>
- <p><c>unbound</c> - Schedulers will not be bound to logical
- processors, i.e., the operating system decides where the
- scheduler threads execute, and when to migrate them. This is
- the default.</p>
+ <p>Sets scheduler bind type.</p>
+ <p>Schedulers can also be bound using flag
+ <seealso marker="#+stbt"><c>+stbt</c></seealso>. The only
+ difference between these two flags is how the following errors
+ are handled:</p>
+ <list type="bulleted">
+ <item>Binding of schedulers is not supported on the specific
+ platform.</item>
+ <item>No available CPU topology. That is, the runtime system was
+ not able to detect the CPU topology automatically, and no
+ <seealso marker="#+sct">user-defined CPU topology</seealso>
+ was set.</item>
+ </list>
+ <p>If any of these errors occur when <c>+sbt</c> has been passed,
+ the runtime system prints an error message, and refuses to
+ start. If any of these errors occur when <c>+stbt</c> has been
+ passed, the runtime system silently ignores the error, and
+ start up using unbound schedulers.</p>
+ <p>Valid <c>BindType</c>s:</p>
+ <taglist>
+ <tag><c>u</c></tag>
+ <item><c>unbound</c> - Schedulers are not bound to logical
+ processors, that is, the operating system decides where the
+ scheduler threads execute, and when to migrate them. This is
+ the default.
</item>
- <tag><c>ns</c></tag>
- <item>
- <p><c>no_spread</c> - Schedulers with close scheduler
- identifiers will be bound as close as possible in hardware.</p>
+ <tag><c>ns</c></tag>
+ <item><c>no_spread</c> - Schedulers with close scheduler
+ identifiers are bound as close as possible in hardware.
</item>
- <tag><c>ts</c></tag>
- <item>
- <p><c>thread_spread</c> - Thread refers to hardware threads
- (e.g. Intel's hyper-threads). Schedulers with low scheduler
- identifiers, will be bound to the first hardware thread of
- each core, then schedulers with higher scheduler identifiers
- will be bound to the second hardware thread of each core,
- etc.</p>
+ <tag><c>ts</c></tag>
+ <item><c>thread_spread</c> - Thread refers to hardware threads
+ (such as Intel's hyper-threads). Schedulers with low scheduler
+ identifiers, are bound to the first hardware thread of
+ each core, then schedulers with higher scheduler identifiers
+ are bound to the second hardware thread of each core,and so on.
</item>
- <tag><c>ps</c></tag>
- <item>
- <p><c>processor_spread</c> - Schedulers will be spread like
- <c>thread_spread</c>, but also over physical processor chips.</p>
+ <tag><c>ps</c></tag>
+ <item><c>processor_spread</c> - Schedulers are spread like
+ <c>thread_spread</c>, but also over physical processor chips.
</item>
- <tag><c>s</c></tag>
- <item>
- <p><c>spread</c> - Schedulers will be spread as much as
- possible.</p>
+ <tag><c>s</c></tag>
+ <item><c>spread</c> - Schedulers are spread as much as possible.
</item>
- <tag><c>nnts</c></tag>
- <item>
- <p><c>no_node_thread_spread</c> - Like <c>thread_spread</c>,
- but if multiple NUMA (Non-Uniform Memory Access) nodes exists,
- schedulers will be spread over one NUMA node at a time,
- i.e., all logical processors of one NUMA node will be bound
- to schedulers in sequence.</p>
+ <tag><c>nnts</c></tag>
+ <item><c>no_node_thread_spread</c> - Like <c>thread_spread</c>,
+ but if multiple Non-Uniform Memory Access (NUMA) nodes exist,
+ schedulers are spread over one NUMA node at a time,
+ that is, all logical processors of one NUMA node are bound
+ to schedulers in sequence.
</item>
- <tag><c>nnps</c></tag>
- <item>
- <p><c>no_node_processor_spread</c> - Like
- <c>processor_spread</c>, but if multiple NUMA nodes exists,
- schedulers will be spread over one NUMA node at a time, i.e.,
- all logical processors of one NUMA node will be bound to
- schedulers in sequence.</p>
+ <tag><c>nnps</c></tag>
+ <item><c>no_node_processor_spread</c> - Like
+ <c>processor_spread</c>, but if multiple NUMA nodes exist,
+ schedulers are spread over one NUMA node at a time, that is,
+ all logical processors of one NUMA node are bound to
+ schedulers in sequence.
</item>
- <tag><c>tnnps</c></tag>
- <item>
- <p><c>thread_no_node_processor_spread</c> - A combination of
- <c>thread_spread</c>, and <c>no_node_processor_spread</c>.
- Schedulers will be spread over hardware threads across NUMA
- nodes, but schedulers will only be spread over processors
- internally in one NUMA node at a time.</p>
+ <tag><c>tnnps</c></tag>
+ <item><c>thread_no_node_processor_spread</c> - A combination of
+ <c>thread_spread</c>, and <c>no_node_processor_spread</c>.
+ Schedulers are spread over hardware threads across NUMA
+ nodes, but schedulers are only spread over processors
+ internally in one NUMA node at a time.
</item>
- <tag><c>db</c></tag>
- <item>
- <p><c>default_bind</c> - Binds schedulers the default way.
- Currently the default is <c>thread_no_node_processor_spread</c>
- (which might change in the future).</p>
+ <tag><c>db</c></tag>
+ <item><c>default_bind</c> - Binds schedulers the default way.
+ Defaults to <c>thread_no_node_processor_spread</c>
+ (which can change in the future).
</item>
- </taglist>
- <p>Binding of schedulers is currently only supported on newer
- Linux, Solaris, FreeBSD, and Windows systems.</p>
- <p>If no CPU topology is available when the <c>+sbt</c> flag
- is processed and <c>BindType</c> is any other type than
- <c>u</c>, the runtime system will fail to start. CPU
- topology can be defined using the
- <seealso marker="#+sct">+sct</seealso> flag. Note
- that the <c>+sct</c> flag may have to be passed before the
- <c>+sbt</c> flag on the command line (in case no CPU topology
- has been automatically detected).</p>
- <p>The runtime system will by default <em>not</em> bind schedulers
- to logical processors.
- </p>
- <p><em>NOTE:</em> If the Erlang runtime system is the only operating system
- process that binds threads to logical processors, this
- improves the performance of the runtime system. However,
- if other operating system processes (as for example
- another Erlang runtime system) also bind threads to
- logical processors, there might be a performance penalty
- instead. In some cases this performance penalty might be
- severe. If this is the case, you are advised to not
- bind the schedulers.</p>
+ </taglist>
+ <p>Binding of schedulers is only supported on newer
+ Linux, Solaris, FreeBSD, and Windows systems.</p>
+ <p>If no CPU topology is available when flag <c>+sbt</c>
+ is processed and <c>BindType</c> is any other type than
+ <c>u</c>, the runtime system fails to start. CPU
+ topology can be defined using flag
+ <seealso marker="#+sct"><c>+sct</c></seealso>. Notice
+ that flag <c>+sct</c> can have to be passed before flag
+ <c>+sbt</c> on the command line (if no CPU topology
+ has been automatically detected).</p>
+ <p>The runtime system does by default <em>not</em> bind schedulers
+ to logical processors.</p>
+ <note>
+ <p>If the Erlang runtime system is the only operating system
+ process that binds threads to logical processors, this
+ improves the performance of the runtime system. However,
+ if other operating system processes (for example
+ another Erlang runtime system) also bind threads to
+ logical processors, there can be a performance penalty
+ instead. This performance penalty can sometimes be
+ severe. If so, you are advised not to
+ bind the schedulers.</p>
+ </note>
<p>How schedulers are bound matters. For example, in
- situations when there are fewer running processes than
- schedulers online, the runtime system tries to migrate
- processes to schedulers with low scheduler identifiers.
- The more the schedulers are spread over the hardware,
- the more resources will be available to the runtime
- system in such situations.
- </p>
- <p>
- <em>NOTE:</em> If a scheduler fails to bind, this
- will often be silently ignored. This since it isn't always
- possible to verify valid logical processor identifiers. If
- an error is reported, it will be reported to the
- <c>error_logger</c>. If you want to verify that the
- schedulers actually have bound as requested, call
- <seealso marker="erlang#system_info_scheduler_bindings">erlang:system_info(scheduler_bindings)</seealso>.
- </p>
+ situations when there are fewer running processes than
+ schedulers online, the runtime system tries to migrate
+ processes to schedulers with low scheduler identifiers.
+ The more the schedulers are spread over the hardware,
+ the more resources are available to the runtime
+ system in such situations.</p>
+ <note>
+ <p>If a scheduler fails to bind, this is
+ often silently ignored, as it is not always
+ possible to verify valid logical processor identifiers. If
+ an error is reported, it is reported to the
+ <c>error_logger</c>. If you want to verify that the
+ schedulers have bound as requested, call
+ <seealso marker="erlang#system_info_scheduler_bindings">
+ <c>erlang:system_info(scheduler_bindings)</c></seealso>.</p>
+ </note>
</item>
- <tag><marker id="+sbwt"/><c>+sbwt none|very_short|short|medium|long|very_long</c></tag>
- <item>
- <p>Set scheduler busy wait threshold. Default is <c>medium</c>.
- The threshold determines how long schedulers should busy
- wait when running out of work before going to sleep.
- </p>
- <p><em>NOTE:</em> This flag may be removed or changed at any time
- without prior notice.
- </p>
- </item>
- <tag><marker id="+scl"/><c>+scl true|false</c></tag>
+ <tag><marker id="+sbwt"/>
+ <c>+sbwt none|very_short|short|medium|long|very_long</c></tag>
+ <item>
+ <p>Sets scheduler busy wait threshold. Defaults to <c>medium</c>.
+ The threshold determines how long schedulers are to busy
+ wait when running out of work before going to sleep.</p>
+ <note>
+ <p>This flag can be removed or changed at any time
+ without prior notice.</p>
+ </note>
+ </item>
+<tag><marker id="+scl"/><c>+scl true|false</c></tag>
<item>
- <p>Enable or disable scheduler compaction of load. By default
- scheduler compaction of load is enabled. When enabled, load
- balancing will strive for a load distribution which causes
- as many scheduler threads as possible to be fully loaded (i.e.,
- not run out of work). This is accomplished by migrating load
- (e.g. runnable processes) into a smaller set of schedulers
- when schedulers frequently run out of work. When disabled,
- the frequency with which schedulers run out of work will
- not be taken into account by the load balancing logic.
- <br/>&nbsp;&nbsp;<c>+scl false</c> is similar to
- <seealso marker="#+sub">+sub true</seealso> with the difference
- that <c>+sub true</c> also will balance scheduler utilization
- between schedulers.
- </p>
+ <p>Enables or disables scheduler compaction of load. By default
+ scheduler compaction of load is enabled. When enabled, load
+ balancing strives for a load distribution, which causes
+ as many scheduler threads as possible to be fully loaded (that is,
+ not run out of work). This is accomplished by migrating load
+ (for example, runnable processes) into a smaller set of schedulers
+ when schedulers frequently run out of work. When disabled,
+ the frequency with which schedulers run out of work is
+ not taken into account by the load balancing logic.</p>
+ <p><c>+scl false</c> is similar to
+ <seealso marker="#+sub"><c>+sub true</c></seealso>, but
+ <c>+sub true</c> also balances scheduler utilization
+ between schedulers.</p>
</item>
<tag><marker id="+sct"/><c>+sct CpuTopology</c></tag>
<item>
<list type="bulleted">
- <item><c><![CDATA[<Id> = integer(); when 0 =< <Id> =< 65535]]></c></item>
+ <item><c><![CDATA[<Id> = integer(); when 0 =< <Id> =< 65535]]></c>
+ </item>
<item><c><![CDATA[<IdRange> = <Id>-<Id>]]></c></item>
<item><c><![CDATA[<IdOrIdRange> = <Id> | <IdRange>]]></c></item>
- <item><c><![CDATA[<IdList> = <IdOrIdRange>,<IdOrIdRange> | <IdOrIdRange>]]></c></item>
+ <item><c><![CDATA[<IdList> = <IdOrIdRange>,<IdOrIdRange> |
+ <IdOrIdRange>]]></c></item>
<item><c><![CDATA[<LogicalIds> = L<IdList>]]></c></item>
- <item><c><![CDATA[<ThreadIds> = T<IdList> | t<IdList>]]></c></item>
+ <item><c><![CDATA[<ThreadIds> = T<IdList> | t<IdList>]]></c>
+ </item>
<item><c><![CDATA[<CoreIds> = C<IdList> | c<IdList>]]></c></item>
- <item><c><![CDATA[<ProcessorIds> = P<IdList> | p<IdList>]]></c></item>
+ <item><c><![CDATA[<ProcessorIds> = P<IdList> | p<IdList>]]></c>
+ </item>
<item><c><![CDATA[<NodeIds> = N<IdList> | n<IdList>]]></c></item>
- <item><c><![CDATA[<IdDefs> = <LogicalIds><ThreadIds><CoreIds><ProcessorIds><NodeIds> | <LogicalIds><ThreadIds><CoreIds><NodeIds><ProcessorIds>]]></c></item>
- <item><c><![CDATA[CpuTopology = <IdDefs>:<IdDefs> | <IdDefs>]]></c></item>
+ <item><c><![CDATA[<IdDefs> =
+ <LogicalIds><ThreadIds><CoreIds><ProcessorIds><NodeIds> |
+ <LogicalIds><ThreadIds><CoreIds><NodeIds><ProcessorIds>]]></c>
+ </item>
+ <item><c><![CDATA[CpuTopology = <IdDefs>:<IdDefs> |
+ <IdDefs>]]></c></item>
</list>
- <p>Set a user defined CPU topology. The user defined
- CPU topology will override any automatically detected
- CPU topology. The CPU topology is used when
- <seealso marker="#+sbt">binding schedulers to logical
- processors</seealso>.
- </p>
- <p>Upper-case letters signify real identifiers and lower-case
- letters signify fake identifiers only used for description
- of the topology. Identifiers passed as real identifiers may
- be used by the runtime system when trying to access specific
- hardware and if they are not correct the behavior is
- undefined. Faked logical CPU identifiers are not accepted
- since there is no point in defining the CPU topology without
- real logical CPU identifiers. Thread, core, processor, and
- node identifiers may be left out. If left out, thread id
- defaults to <c>t0</c>, core id defaults to <c>c0</c>,
- processor id defaults to <c>p0</c>, and node id will
- be left undefined. Either each logical processor must
- belong to one and only one NUMA node, or no logical
- processors must belong to any NUMA nodes.
- </p>
- <p>Both increasing and decreasing <c><![CDATA[<IdRange>]]></c>s
- are allowed.</p>
- <p>NUMA node identifiers are system wide. That is, each NUMA
- node on the system have to have a unique identifier. Processor
- identifiers are also system wide. Core identifiers are
- processor wide. Thread identifiers are core wide.</p>
- <p>The order of the identifier types imply the hierarchy of the
- CPU topology. Valid orders are either
- <c><![CDATA[<LogicalIds><ThreadIds><CoreIds><ProcessorIds><NodeIds>]]></c>,
- or
- <c><![CDATA[<LogicalIds><ThreadIds><CoreIds><NodeIds><ProcessorIds>]]></c>.
- That is, thread is part of a core which is part of a processor
- which is part of a NUMA node, or thread is part of a core which
- is part of a NUMA node which is part of a processor. A cpu
- topology can consist of both processor external, and processor
- internal NUMA nodes as long as each logical processor belongs
- to one and only one NUMA node. If <c><![CDATA[<ProcessorIds>]]></c>
- is left out, its default position will be before
- <c><![CDATA[<NodeIds>]]></c>. That is, the default is
- processor external NUMA nodes.
- </p>
- <p>If a list of identifiers is used in an
- <c><![CDATA[<IdDefs>]]></c>:</p>
- <list type="bulleted">
- <item><c><![CDATA[<LogicalIds>]]></c> have to be a list
- of identifiers.</item>
- <item>At least one other identifier type apart from
- <c><![CDATA[<LogicalIds>]]></c> also have to have a
- list of identifiers.</item>
- <item>All lists of identifiers have to produce the
- same amount of identifiers.</item>
- </list>
- <p>A simple example. A single quad core processor may be
- described this way:</p>
+ <p>Sets a user-defined CPU topology. The user-defined
+ CPU topology overrides any automatically detected
+ CPU topology. The CPU topology is used when
+ <seealso marker="#+sbt">binding schedulers to logical
+ processors</seealso>.</p>
+ <p>Uppercase letters signify real identifiers and lowercase
+ letters signify fake identifiers only used for description
+ of the topology. Identifiers passed as real identifiers can
+ be used by the runtime system when trying to access specific
+ hardware; if they are incorrect the behavior is
+ undefined. Faked logical CPU identifiers are not accepted,
+ as there is no point in defining the CPU topology without
+ real logical CPU identifiers. Thread, core, processor, and
+ node identifiers can be omitted. If omitted, the thread ID
+ defaults to <c>t0</c>, the core ID defaults to <c>c0</c>,
+ the processor ID defaults to <c>p0</c>, and the node ID is
+ left undefined. Either each logical processor must
+ belong to only one NUMA node, or no logical
+ processors must belong to any NUMA nodes.</p>
+ <p>Both increasing and decreasing <c><![CDATA[<IdRange>]]></c>s
+ are allowed.</p>
+ <p>NUMA node identifiers are system wide. That is, each NUMA
+ node on the system must have a unique identifier. Processor
+ identifiers are also system wide. Core identifiers are
+ processor wide. Thread identifiers are core wide.</p>
+ <p>The order of the identifier types implies the hierarchy of the
+ CPU topology. The valid orders are as follows:</p>
+ <list type="bulleted">
+ <item>
+ <p><c><![CDATA[<LogicalIds><ThreadIds><CoreIds><ProcessorIds><NodeIds>]]></c>,
+ that is, thread is part of a core that is part of a processor,
+ which is part of a NUMA node.</p>
+ </item>
+ <item>
+ <p><c><![CDATA[<LogicalIds><ThreadIds><CoreIds><NodeIds><ProcessorIds>]]></c>,
+ that is, thread is part of a core that is part of a NUMA node,
+ which is part of a processor.</p>
+ </item>
+ </list>
+ <p>A CPU topology can consist of both processor external, and
+ processor internal NUMA nodes as long as each logical processor
+ belongs to only one NUMA node. If
+ <c><![CDATA[<ProcessorIds>]]></c> is omitted, its default position
+ is before <c><![CDATA[<NodeIds>]]></c>. That is, the default is
+ processor external NUMA nodes.</p>
+ <p>If a list of identifiers is used in an
+ <c><![CDATA[<IdDefs>]]></c>:</p>
+ <list type="bulleted">
+ <item><c><![CDATA[<LogicalIds>]]></c> must be a list
+ of identifiers.</item>
+ <item>At least one other identifier type besides
+ <c><![CDATA[<LogicalIds>]]></c> must also have a
+ list of identifiers.</item>
+ <item>All lists of identifiers must produce the
+ same number of identifiers.</item>
+ </list>
+ <p>A simple example. A single quad core processor can be
+ described as follows:</p>
<pre>
% <input>erl +sct L0-3c0-3</input>
1> <input>erlang:system_info(cpu_topology).</input>
[{processor,[{core,{logical,0}},
{core,{logical,1}},
{core,{logical,2}},
- {core,{logical,3}}]}]
-</pre>
- <p>A little more complicated example. Two quad core
- processors. Each processor in its own NUMA node.
- The ordering of logical processors is a little weird.
- This in order to give a better example of identifier
- lists:</p>
+ {core,{logical,3}}]}]</pre>
+ <p>A more complicated example with two quad core
+ processors, each processor in its own NUMA node.
+ The ordering of logical processors is a bit weird.
+ This to give a better example of identifier lists:</p>
<pre>
% <input>erl +sct L0-1,3-2c0-3p0N0:L7,4,6-5c0-3p1N1</input>
1> <input>erlang:system_info(cpu_topology).</input>
@@ -1156,239 +1243,247 @@
{node,[{processor,[{core,{logical,7}},
{core,{logical,4}},
{core,{logical,6}},
- {core,{logical,5}}]}]}]
-</pre>
- <p>As long as real identifiers are correct it is okay
- to pass a CPU topology that is not a correct
- description of the CPU topology. When used with
- care this can actually be very useful. This in
- order to trick the emulator to bind its schedulers
- as you want. For example, if you want to run multiple
- Erlang runtime systems on the same machine, you
- want to reduce the amount of schedulers used and
- manipulate the CPU topology so that they bind to
- different logical CPUs. An example, with two Erlang
- runtime systems on a quad core machine:</p>
+ {core,{logical,5}}]}]}]</pre>
+ <p>As long as real identifiers are correct, it is OK
+ to pass a CPU topology that is not a correct
+ description of the CPU topology. When used with
+ care this can be very useful. This
+ to trick the emulator to bind its schedulers
+ as you want. For example, if you want to run multiple
+ Erlang runtime systems on the same machine, you
+ want to reduce the number of schedulers used and
+ manipulate the CPU topology so that they bind to
+ different logical CPUs. An example, with two Erlang
+ runtime systems on a quad core machine:</p>
<pre>
% <input>erl +sct L0-3c0-3 +sbt db +S3:2 -detached -noinput -noshell -sname one</input>
-% <input>erl +sct L3-0c0-3 +sbt db +S3:2 -detached -noinput -noshell -sname two</input>
-</pre>
- <p>In this example each runtime system have two
- schedulers each online, and all schedulers online
- will run on different cores. If we change to one
- scheduler online on one runtime system, and three
- schedulers online on the other, all schedulers
- online will still run on different cores.</p>
- <p>Note that a faked CPU topology that does not reflect
- how the real CPU topology looks like is likely to
- decrease the performance of the runtime system.</p>
- <p>For more information, see
- <seealso marker="erlang#system_info_cpu_topology">erlang:system_info(cpu_topology)</seealso>.</p>
+% <input>erl +sct L3-0c0-3 +sbt db +S3:2 -detached -noinput -noshell -sname two</input></pre>
+ <p>In this example, each runtime system have two
+ schedulers each online, and all schedulers online
+ will run on different cores. If we change to one
+ scheduler online on one runtime system, and three
+ schedulers online on the other, all schedulers
+ online will still run on different cores.</p>
+ <p>Notice that a faked CPU topology that does not reflect
+ how the real CPU topology looks like is likely to
+ decrease the performance of the runtime system.</p>
+ <p>For more information, see
+ <seealso marker="erlang#system_info_cpu_topology">
+ <c>erlang:system_info(cpu_topology)</c></seealso>.</p>
</item>
<tag><marker id="+secio"/><c>+secio true|false</c></tag>
<item>
- <p>Enable or disable eager check I/O scheduling. The default
- is currently <c>true</c>. The default was changed from <c>false</c>
- to <c>true</c> as of erts version 7.0. The behaviour before this
- flag was introduced corresponds to <c>+secio false</c>.</p>
- <p>The flag effects when schedulers will check for I/O
- operations possible to execute, and when such I/O operations
- will execute. As the name of the parameter implies,
- schedulers will be more eager to check for I/O when
- <c>true</c> is passed. This however also implies that
- execution of outstanding I/O operation will not be
- prioritized to the same extent as when <c>false</c> is
- passed.</p>
- <p><seealso marker="erlang#system_info_eager_check_io"><c>erlang:system_info(eager_check_io)</c></seealso>
- returns the value of this parameter used when starting the VM.</p>
+ <p>Enables or disables eager check I/O scheduling. Defaults
+ to <c>true</c>. The default was changed from <c>false</c>
+ as from ERTS 7.0. The behavior before this
+ flag was introduced corresponds to <c>+secio false</c>.</p>
+ <p>The flag effects when schedulers will check for I/O
+ operations possible to execute, and when such I/O operations
+ will execute. As the parameter name implies,
+ schedulers are more eager to check for I/O when
+ <c>true</c> is passed. This, however, also implies that
+ execution of outstanding I/O operation is not
+ prioritized to the same extent as when <c>false</c> is
+ passed.</p>
+ <p><seealso marker="erlang#system_info_eager_check_io">
+ <c>erlang:system_info(eager_check_io)</c></seealso>
+ returns the value of this parameter used when starting
+ the virtual machine.</p>
</item>
<tag><marker id="+sfwi"/><c>+sfwi Interval</c></tag>
<item>
- <p>Set scheduler forced wakeup interval. All run queues will
- be scanned each <c>Interval</c> milliseconds. While there are
- sleeping schedulers in the system, one scheduler will be woken
- for each non-empty run queue found. An <c>Interval</c> of zero
- disables this feature, which also is the default.
- </p>
- <p>This feature has been introduced as a temporary workaround
- for long-executing native code, and native code that does not
- bump reductions properly in OTP. When these bugs have be fixed
- the <c>+sfwi</c> flag will be removed.
- </p>
- </item>
+ <p>Sets scheduler-forced wakeup interval. All run queues are
+ scanned each <c>Interval</c> milliseconds. While there are
+ sleeping schedulers in the system, one scheduler is woken
+ for each non-empty run queue found. <c>Interval</c> default
+ to <c>0</c>, meaning this feature is disabled.</p>
+ <note>
+ <p>This feature has been introduced as a temporary workaround
+ for long-executing native code, and native code that does not
+ bump reductions properly in OTP. When these bugs have be fixed,
+ this flag will be removed.</p>
+ </note>
+ </item>
+ <tag><marker id="+spp"/><c>+spp Bool</c></tag>
+ <item>
+ <p>Sets default scheduler hint for port parallelism. If set to
+ <c>true</c>, the virtual machine schedules port tasks when it
+ improves parallelism in the system. If set to <c>false</c>, the
+ virtual machine tries to perform port tasks immediately,
+ improving latency at the expense of parallelism. Default to
+ <c>false</c>. The default used can be inspected in runtime by
+ calling <seealso marker="erlang#system_info_port_parallelism">
+ <c>erlang:system_info(port_parallelism)</c></seealso>.
+ The default can be overridden on port creation by passing option
+ <seealso marker="erlang#open_port_parallelism">
+ <c>parallelism</c></seealso> to
+ <seealso marker="erlang#open_port/2">
+ <c>erlang:open_port/2</c></seealso></p>.
+ </item>
+ <tag><marker id="sched_thread_stack_size"/>
+ <c><![CDATA[+sss size]]></c></tag>
+ <item>
+ <p>Suggested stack size, in kilowords, for scheduler threads.
+ Valid range is 4-8192 kilowords. The default stack size is
+ OS-dependent.</p>
+ </item>
<tag><marker id="+stbt"/><c>+stbt BindType</c></tag>
<item>
- <p>Try to set scheduler bind type. The same as the
- <seealso marker="#+sbt">+sbt</seealso> flag with the exception of
- how some errors are handled. For more information, see the
- documentation of the <seealso marker="#+sbt">+sbt</seealso> flag.
- </p>
- </item>
+ <p>Tries to set the scheduler bind type. The same as flag
+ <seealso marker="#+sbt"><c>+sbt</c></seealso> except
+ how some errors are handled. For more information, see
+ <seealso marker="#+sbt"><c>+sbt</c></seealso>.</p>
+ </item>
<tag><marker id="+sub"/><c>+sub true|false</c></tag>
<item>
- <p>Enable or disable
- <seealso marker="erts:erlang#statistics_scheduler_wall_time">scheduler
- utilization</seealso> balancing of load. By default scheduler
- utilization balancing is disabled and instead scheduler
- compaction of load is enabled which will strive for a load
- distribution which causes as many scheduler threads as possible
- to be fully loaded (i.e., not run out of work). When scheduler
- utilization balancing is enabled the system will instead try to
- balance scheduler utilization between schedulers. That is,
- strive for equal scheduler utilization on all schedulers.
- <br/>&nbsp;&nbsp;&nbsp;<c>+sub true</c> is only supported on
- systems where the runtime system detects and uses a monotonically
- increasing high resolution clock. On other systems, the runtime
- system will fail to start.
- <br/>&nbsp;&nbsp;&nbsp;<c>+sub true</c> implies
- <seealso marker="#+scl">+scl false</seealso>. The difference
- between <c>+sub true</c> and <c>+scl false</c> is that
- <c>+scl false</c> will not try to balance the scheduler
- utilization.
- </p>
+ <p>Enables or disables
+ <seealso marker="erts:erlang#statistics_scheduler_wall_time">
+ scheduler utilization</seealso> balancing of load. By default
+ scheduler utilization balancing is disabled and instead scheduler
+ compaction of load is enabled, which strives for a load
+ distribution that causes as many scheduler threads as possible
+ to be fully loaded (that is, not run out of work). When scheduler
+ utilization balancing is enabled, the system instead tries to
+ balance scheduler utilization between schedulers. That is,
+ strive for equal scheduler utilization on all schedulers.</p>
+ <p><c>+sub true</c> is only supported on systems where the runtime
+ system detects and uses a monotonically increasing high-resolution
+ clock. On other systems, the runtime system fails to start.</p>
+ <p><c>+sub true</c> implies <seealso marker="#+scl">
+ <c>+scl false</c></seealso>. The difference between
+ <c>+sub true</c> and <c>+scl false</c> is that <c>+scl false</c>
+ does not try to balance the scheduler utilization.</p>
</item>
- <tag><marker id="+swct"/><c>+swct very_eager|eager|medium|lazy|very_lazy</c></tag>
- <item>
- <p>
- Set scheduler wake cleanup threshold. Default is <c>medium</c>.
- This flag controls how eager schedulers should be requesting
- wake up due to certain cleanup operations. When a lazy setting
- is used, more outstanding cleanup operations can be left undone
- while a scheduler is idling. When an eager setting is used,
- schedulers will more frequently be woken, potentially increasing
- CPU-utilization.
- </p>
- <p><em>NOTE:</em> This flag may be removed or changed at any time without prior notice.
- </p>
- </item>
- <tag><marker id="+sws"/><c>+sws default|legacy</c></tag>
- <item>
- <p>
- Set scheduler wakeup strategy. Default strategy changed in erts-5.10/OTP-R16A. This strategy was previously known as <c>proposal</c> in OTP-R15. The <c>legacy</c> strategy was used as default from R13 up to and including R15.
- </p>
- <p><em>NOTE:</em> This flag may be removed or changed at any time without prior notice.
- </p>
- </item>
- <tag><marker id="+swt"/><c>+swt very_low|low|medium|high|very_high</c></tag>
- <item>
- <p>Set scheduler wakeup threshold. Default is <c>medium</c>.
- The threshold determines when to wake up sleeping schedulers
- when more work than can be handled by currently awake schedulers
- exist. A low threshold will cause earlier wakeups, and a high
- threshold will cause later wakeups. Early wakeups will
- distribute work over multiple schedulers faster, but work will
- more easily bounce between schedulers.
- </p>
- <p><em>NOTE:</em> This flag may be removed or changed at any time
- without prior notice.
- </p>
- </item>
- <tag><marker id="+spp"/><c>+spp Bool</c></tag>
+ <tag><marker id="+swct"/>
+ <c>+swct very_eager|eager|medium|lazy|very_lazy</c></tag>
<item>
- <p>Set default scheduler hint for port parallelism. If set to
- <c>true</c>, the VM will schedule port tasks when doing so will
- improve parallelism in the system. If set to <c>false</c>, the VM
- will try to perform port tasks immediately, improving latency at the
- expense of parallelism. If this flag has not been passed, the
- default scheduler hint for port parallelism is currently
- <c>false</c>. The default used can be inspected in runtime by
- calling <seealso
- marker="erlang#system_info_port_parallelism">erlang:system_info(port_parallelism)</seealso>.
- The default can be overriden on port creation by passing the
- <seealso marker="erlang#open_port_parallelism">parallelism</seealso>
- option to <seealso
- marker="erlang#open_port/2">open_port/2</seealso></p>.
- </item>
- <tag><marker id="sched_thread_stack_size"/><c><![CDATA[+sss size]]></c></tag>
- <item>
- <p>Suggested stack size, in kilowords, for scheduler threads.
- Valid range is 4-8192 kilowords. The default stack size
- is OS dependent.</p>
- </item>
+ <p>Sets scheduler wake cleanup threshold. Defaults to <c>medium</c>.
+ Controls how eager schedulers are to be requesting
+ wakeup because of certain cleanup operations. When a lazy setting
+ is used, more outstanding cleanup operations can be left undone
+ while a scheduler is idling. When an eager setting is used,
+ schedulers are more frequently woken, potentially increasing
+ CPU-utilization.</p>
+ <note>
+ <p>This flag can be removed or changed at any time without prior
+ notice.</p>
+ </note>
+ </item>
+ <tag><marker id="+sws"/><c>+sws default|legacy</c></tag>
+ <item>
+ <p>Sets scheduler wakeup strategy. Default strategy changed in
+ ERTS 5.10 (Erlang/OTP R16A). This strategy was known as
+ <c>proposal</c> in Erlang/OTP R15. The <c>legacy</c> strategy
+ was used as default from R13 up to and including R15.</p>
+ <note>
+ <p>This flag can be removed or changed at any time without prior
+ notice.</p>
+ </note>
+ </item>
+ <tag><marker id="+swt"/>
+ <c>+swt very_low|low|medium|high|very_high</c></tag>
+ <item>
+ <p>Sets scheduler wakeup threshold. Defaults to <c>medium</c>.
+ The threshold determines when to wake up sleeping schedulers
+ when more work than can be handled by currently awake schedulers
+ exists. A low threshold causes earlier wakeups, and a high
+ threshold causes later wakeups. Early wakeups distribute work
+ over multiple schedulers faster, but work does more easily bounce
+ between schedulers.</p>
+ <note>
+ <p>This flag can be removed or changed at any time without prior
+ notice.</p>
+ </note>
+ </item>
</taglist>
</item>
<tag><marker id="+t"/><c><![CDATA[+t size]]></c></tag>
<item>
- <p>Set the maximum number of atoms the VM can handle. Default is 1048576.</p>
+ <p>Sets the maximum number of atoms the virtual machine can handle.
+ Defaults to 1,048,576.</p>
</item>
<tag><marker id="+T"/><c><![CDATA[+T Level]]></c></tag>
<item>
- <p>Enables modified timing and sets the modified timing level.
- Currently valid range is 0-9. The timing of the runtime system
- will change. A high level usually means a greater change than
- a low level. Changing the timing can be very useful for finding
- timing related bugs.</p>
- <p>Currently, modified timing affects the following:</p>
+ <p>Enables modified timing and sets the modified timing level. Valid
+ range is 0-9. The timing of the runtime system is changed. A high
+ level usually means a greater change than a low level. Changing the
+ timing can be very useful for finding timing-related bugs.</p>
+ <p>Modified timing affects the following:</p>
<taglist>
<tag>Process spawning</tag>
- <item>
- <p>A process calling <c><![CDATA[spawn]]></c>, <c><![CDATA[spawn_link]]></c>,
- <c><![CDATA[spawn_monitor]]></c>, or <c><![CDATA[spawn_opt]]></c> will be scheduled
- out immediately after completing the call. When higher modified
- timing levels are used, the caller will also sleep for a while
- after being scheduled out.</p>
+ <item>A process calling <c><![CDATA[spawn]]></c>,
+ <c><![CDATA[spawn_link]]></c>, <c><![CDATA[spawn_monitor]]></c>,
+ or <c><![CDATA[spawn_opt]]></c> is scheduled out immediately
+ after completing the call. When higher modified timing levels are
+ used, the caller also sleeps for a while after it is scheduled out.
</item>
<tag>Context reductions</tag>
- <item>The amount of reductions a process is a allowed to
- use before being scheduled out is increased or reduced.</item>
+ <item>The number of reductions a process is allowed to use before it
+ is scheduled out is increased or reduced.
+ </item>
<tag>Input reductions</tag>
- <item>The amount of reductions performed before checking I/O
- is increased or reduced.</item>
+ <item>The number of reductions performed before checking I/O is
+ increased or reduced.
+ </item>
</taglist>
- <p><em>NOTE:</em> Performance will suffer when modified timing
- is enabled. This flag is <em>only</em> intended for testing and
- debugging. Also note that <c><![CDATA[return_to]]></c> and <c><![CDATA[return_from]]></c>
- trace messages will be lost when tracing on the spawn BIFs. This
- flag may be removed or changed at any time without prior notice.</p>
- </item>
- <tag><c><![CDATA[+V]]></c></tag>
- <item>
- <p>Makes the emulator print out its version number.</p>
+ <note>
+ <p>Performance suffers when modified timing is enabled. This flag is
+ <em>only</em> intended for testing and debugging.</p>
+ <p><c><![CDATA[return_to]]></c> and <c><![CDATA[return_from]]></c>
+ trace messages are lost when tracing on the spawn BIFs.</p>
+ <p>This flag can be removed or changed at any time without prior
+ notice.</p>
+ </note>
</item>
<tag><c><![CDATA[+v]]></c></tag>
<item>
<p>Verbose.</p>
</item>
+ <tag><c><![CDATA[+V]]></c></tag>
+ <item>
+ <p>Makes the emulator print its version number.</p>
+ </item>
<tag><c><![CDATA[+W w | i | e]]></c></tag>
<item>
- <p>Sets the mapping of warning messages for <c><![CDATA[error_logger]]></c>.
- Messages sent to the error logger using one of the warning
- routines can be mapped either to errors (<c><![CDATA[+W e]]></c>),
- warnings (<c><![CDATA[+W w]]></c>), or info reports
- (<c><![CDATA[+W i]]></c>). The default is warnings.
+ <p>Sets the mapping of warning messages for
+ <c><![CDATA[error_logger]]></c>. Messages sent to the error logger
+ using one of the warning routines can be mapped to errors
+ (<c><![CDATA[+W e]]></c>), warnings (<c><![CDATA[+W w]]></c>), or
+ information reports (<c><![CDATA[+W i]]></c>). Defaults to warnings.
The current mapping can be retrieved using
- <c><![CDATA[error_logger:warning_map/0]]></c>. See
- <seealso marker="kernel:error_logger#warning_map/0">error_logger(3)</seealso>
- for further information.</p>
+ <c><![CDATA[error_logger:warning_map/0]]></c>. For more information,
+ see <seealso marker="kernel:error_logger#warning_map/0">
+ <c>error_logger:warning_map/0</c></seealso> in Kernel.</p>
</item>
<tag><c><![CDATA[+zFlag Value]]></c></tag>
<item>
- <p>Miscellaneous flags.</p>
+ <p>Miscellaneous flags:</p>
<taglist>
<tag><marker id="+zdbbl"/><c>+zdbbl size</c></tag>
<item>
- <p>Set the distribution buffer busy limit
- (<seealso marker="erlang#system_info_dist_buf_busy_limit">dist_buf_busy_limit</seealso>)
- in kilobytes. Valid range is 1-2097151. Default is 1024.</p>
- <p>A larger buffer limit will allow processes to buffer
- more outgoing messages over the distribution. When the
- buffer limit has been reached, sending processes will be
- suspended until the buffer size has shrunk. The buffer
- limit is per distribution channel. A higher limit will
- give lower latency and higher throughput at the expense
- of higher memory usage.</p>
+ <p>Sets the distribution buffer busy limit
+ (<seealso marker="erlang#system_info_dist_buf_busy_limit">
+ <c>dist_buf_busy_limit</c></seealso>)
+ in kilobytes. Valid range is 1-2097151. Defaults to 1024.</p>
+ <p>A larger buffer limit allows processes to buffer
+ more outgoing messages over the distribution. When the
+ buffer limit has been reached, sending processes will be
+ suspended until the buffer size has shrunk. The buffer
+ limit is per distribution channel. A higher limit
+ gives lower latency and higher throughput at the expense
+ of higher memory use.</p>
</item>
<tag><marker id="+zdntgc"/><c>+zdntgc time</c></tag>
<item>
- <p>Set the delayed node table garbage collection time
- (<seealso marker="erlang#system_info_delayed_node_table_gc">delayed_node_table_gc</seealso>)
- in seconds. Valid values are either <c>infinity</c> or
- an integer in the range [0-100000000]. Default is 60.</p>
- <p>Node table entries that are not referred will linger
- in the table for at least the amount of time that this
- parameter determines. The lingering prevents repeated
- deletions and insertions in the tables from occurring.
- </p>
+ <p>Sets the delayed node table garbage collection time
+ (<seealso marker="erlang#system_info_delayed_node_table_gc">
+ <c>delayed_node_table_gc</c></seealso>)
+ in seconds. Valid values are either <c>infinity</c> or
+ an integer in the range 0-100000000. Defaults to 60.</p>
+ <p>Node table entries that are not referred linger
+ in the table for at least the amount of time that this
+ parameter determines. The lingering prevents repeated
+ deletions and insertions in the tables from occurring.</p>
</item>
</taglist>
</item>
@@ -1397,164 +1492,188 @@
<section>
<marker id="environment_variables"></marker>
- <title>Environment variables</title>
+ <title>Environment Variables</title>
<taglist>
<tag><c><![CDATA[ERL_CRASH_DUMP]]></c></tag>
<item>
<p>If the emulator needs to write a crash dump, the value of this
- variable will be the file name of the crash dump file.
- If the variable is not set, the name of the crash dump file will
- be <c><![CDATA[erl_crash.dump]]></c> in the current directory.</p>
+ variable is the filename of the crash dump file.
+ If the variable is not set, the name of the crash dump file is
+ <c><![CDATA[erl_crash.dump]]></c> in the current directory.</p>
</item>
<tag><c><![CDATA[ERL_CRASH_DUMP_NICE]]></c></tag>
<item>
- <p><em>Unix systems</em>: If the emulator needs to write a crash dump,
- it will use the value of this variable to set the nice value
- for the process, thus lowering its priority. The allowable range is
- 1 through 39 (higher values will be replaced with 39). The highest
- value, 39, will give the process the lowest priority.</p>
+ <p><em>Unix systems</em>: If the emulator needs to write a crash dump,
+ it uses the value of this variable to set the nice value
+ for the process, thus lowering its priority. Valid range is
+ 1-39 (higher values are replaced with 39). The highest
+ value, 39, gives the process the lowest priority.</p>
</item>
<tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS]]></c></tag>
<item>
- <p><em>Unix systems</em>: This variable gives the number of seconds that
- the emulator will be allowed to spend writing a crash dump. When
- the given number of seconds have elapsed, the emulator will be
- terminated by a SIGALRM signal.</p>
-
- <p> If the environment variable is <em>not</em> set or it is set to zero seconds, <c><![CDATA[ERL_CRASH_DUMP_SECONDS=0]]></c>,
- the runtime system will not even attempt to write the crash dump file. It will just terminate.
- </p>
- <p> If the environment variable is set to negative valie, e.g. <c><![CDATA[ERL_CRASH_DUMP_SECONDS=-1]]></c>,
- the runtime system will wait indefinitely for the crash dump file to be written.
- </p>
- <p> This environment variable is used in conjuction with
- <seealso marker="kernel:heart"><c>heart</c></seealso> if <c>heart</c> is running:
- </p>
- <taglist>
- <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=0]]></c></tag>
- <item><p>
- Suppresses the writing a crash dump file entirely,
- thus rebooting the runtime system immediately.
- This is the same as not setting the environment variable.
- </p>
- </item>
- <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=-1]]></c></tag>
- <item><p>Setting the environment variable to a negative value will cause the
- termination of the runtime system to wait until the crash dump file
- has been completly written.
- </p>
- </item>
- <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=S]]></c></tag>
- <item><p>
- Will wait for <c>S</c> seconds to complete the crash dump file and
- then terminate the runtime system.
- </p>
- </item>
- </taglist>
+ <p><em>Unix systems</em>: This variable gives the number of seconds
+ that the emulator is allowed to spend writing a crash dump. When the
+ given number of seconds have elapsed, the emulator is terminated by a
+ <c>SIGALRM</c> signal.</p>
+ <p>If the variable is <em>not</em> set or set to <c>0</c> seconds
+ (<c><![CDATA[ERL_CRASH_DUMP_SECONDS=0]]></c>), the runtime system does
+ not even attempt to write the crash dump file. It only terminates.</p>
+ <p>If the variable is set to negative value, such as
+ <c><![CDATA[ERL_CRASH_DUMP_SECONDS=-1]]></c>, the runtime system
+ waits indefinitely for the crash dump file to be written.</p>
+ <p>This variable is used with <seealso marker="kernel:heart">
+ <c>heart(3)</c></seealso> if <c>heart</c> is running:</p>
+ <taglist>
+ <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=0]]></c></tag>
+ <item>Suppresses the writing a crash dump file entirely, thus
+ rebooting the runtime system immediately. This is the same as not
+ setting the environment variable.
+ </item>
+ <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=-1]]></c></tag>
+ <item>Setting the environment variable to a negative value causes the
+ termination of the runtime system to wait until the crash dump file
+ has been completly written.
+ </item>
+ <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=S]]></c></tag>
+ <item>Waits for <c>S</c> seconds to complete the crash dump file and
+ then terminates the runtime system.
+ </item>
+ </taglist>
</item>
- <tag><marker id="ERL_AFLAGS"/><c><![CDATA[ERL_AFLAGS]]></c></tag>
+ <tag><c><![CDATA[ERL_CRASH_DUMP_BYTES]]></c></tag>
<item>
- <p>The content of this environment variable will be added to the
- beginning of the command line for <c><![CDATA[erl]]></c>.</p>
- <p>The <c><![CDATA[-extra]]></c> flag is treated specially. Its scope ends
- at the end of the environment variable content. Arguments
- following an <c><![CDATA[-extra]]></c> flag are moved on the command line into
- the <c><![CDATA[-extra]]></c> section, i.e. the end of the command line
- following after an <c><![CDATA[-extra]]></c> flag.</p>
+ <p>This variable sets the maximum size of a crash dump file in bytes.
+ The crash dump will be truncated if this limit is exceeded. If the
+ variable is not set, no size limit is enforced by default. If the
+ variable is set to <c>0</c>, the runtime system does not even attempt
+ to write a crash dump file.</p>
+ <p>Introduced in ERTS 8.1.2 (Erlang/OTP 19.2).</p>
</item>
- <tag><marker id="ERL_ZFLAGS"/><c><![CDATA[ERL_ZFLAGS]]></c> and <marker id="ERL_FLAGS"/><c><![CDATA[ERL_FLAGS]]></c></tag>
+ <tag><marker id="ERL_AFLAGS"/><c><![CDATA[ERL_AFLAGS]]></c></tag>
<item>
- <p>The content of these environment variables will be added to the
- end of the command line for <c><![CDATA[erl]]></c>.</p>
- <p>The <c><![CDATA[-extra]]></c> flag is treated specially. Its scope ends
- at the end of the environment variable content. Arguments
- following an <c><![CDATA[-extra]]></c> flag are moved on the command line into
- the <c><![CDATA[-extra]]></c> section, i.e. the end of the command line
- following after an <c><![CDATA[-extra]]></c> flag.</p>
+ <p>The content of this variable is added to the beginning of the
+ command line for <c><![CDATA[erl]]></c>.</p>
+ <p>Flag <c><![CDATA[-extra]]></c> is treated in a special way. Its
+ scope ends at the end of the environment variable content. Arguments
+ following an <c><![CDATA[-extra]]></c> flag are moved on the command
+ line into section <c><![CDATA[-extra]]></c>, that is, the end of the
+ command line following an <c><![CDATA[-extra]]></c> flag.</p>
+ </item>
+ <tag><marker id="ERL_ZFLAGS"/><c><![CDATA[ERL_ZFLAGS]]></c> and
+ <marker id="ERL_FLAGS"/><c><![CDATA[ERL_FLAGS]]></c></tag>
+ <item>
+ <p>The content of these variables are added to the end of the command
+ line for <c><![CDATA[erl]]></c>.</p>
+ <p>Flag <c><![CDATA[-extra]]></c> is treated in a special way. Its
+ scope ends at the end of the environment variable content. Arguments
+ following an <c><![CDATA[-extra]]></c> flag are moved on the command
+ line into section <c><![CDATA[-extra]]></c>, that is, the end of the
+ command line following an <c><![CDATA[-extra]]></c> flag.</p>
</item>
<tag><c><![CDATA[ERL_LIBS]]></c></tag>
<item>
- <p>This environment variable contains a list of additional library
- directories that the code server will search for applications and
- add to the code path.
- See <seealso marker="kernel:code">code(3)</seealso>.</p>
+ <p>Contains a list of additional library directories that the code
+ server searches for applications and adds to the code path; see
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
</item>
<tag><c><![CDATA[ERL_EPMD_ADDRESS]]></c></tag>
<item>
- <p>This environment variable may be set to a comma-separated
- list of IP addresses, in which case the
- <seealso marker="epmd">epmd</seealso> daemon
- will listen only on the specified address(es) and on the
- loopback address (which is implicitly added to the list if it
- has not been specified).</p>
+ <p>Can be set to a comma-separated list of IP addresses, in which case
+ the <seealso marker="epmd"><c>epmd</c></seealso> daemon listens only
+ on the specified address(es) and on the loopback address (which is
+ implicitly added to the list if it has not been specified).</p>
</item>
<tag><c><![CDATA[ERL_EPMD_PORT]]></c></tag>
<item>
- <p>This environment variable can contain the port number to use when
- communicating with <seealso marker="epmd">epmd</seealso>. The default
- port will work fine in most cases. A different port can be specified
+ <p>Can contain the port number to use when communicating with
+ <seealso marker="epmd"><c>epmd</c></seealso>. The default port works
+ fine in most cases. A different port can be specified
to allow nodes of independent clusters to co-exist on the same host.
- All nodes in a cluster must use the same epmd port number.</p>
+ All nodes in a cluster must use the same <c>epmd</c> port number.</p>
</item>
</taglist>
</section>
<section>
+ <marker id="signals"></marker>
+ <title>Signals</title>
+ <p>On Unix systems, the Erlang runtime will interpret two types of signals.</p>
+ <taglist>
+ <tag><c>SIGUSR1</c></tag>
+ <item>
+ <p>A <c>SIGUSR1</c> signal forces a crash dump.</p>
+ </item>
+ <tag><c>SIGTERM</c></tag>
+ <item>
+ <p>A <c>SIGTERM</c> will produce a <c>stop</c> message to the <c>init</c> process.
+ This is equivalent to a <c>init:stop/0</c> call.</p>
+ <p>Introduced in ERTS 8.3 (Erlang/OTP 19.3)</p>
+ </item>
+ </taglist>
+ <p>The signal <c>SIGUSR2</c> is reserved for internal usage. No other signals are handled.</p>
+ </section>
+
+ <section>
<marker id="configuration"></marker>
<title>Configuration</title>
- <p>The standard Erlang/OTP system can be re-configured to change the default
- behavior on start-up.</p>
+ <p>The standard Erlang/OTP system can be reconfigured to change the default
+ behavior on startup.</p>
<taglist>
- <tag>The .erlang Start-up File</tag>
- <item>
- <p>When Erlang/OTP is started, the system searches for a file named .erlang
- in the directory where Erlang/OTP is started. If not found, the user's home
- directory is searched for an .erlang file.</p>
- <p>If an .erlang file is found, it is assumed to contain valid Erlang expressions.
- These expressions are evaluated as if they were input to the shell.</p>
- <p>A typical .erlang file contains a set of search paths, for example:</p>
- <code type="none"><![CDATA[
- io:format("executing user profile in HOME/.erlang\n",[]).
- code:add_path("/home/calvin/test/ebin").
- code:add_path("/home/hobbes/bigappl-1.2/ebin").
- io:format(".erlang rc finished\n",[]).
- ]]></code>
- </item>
- <tag>user_default and shell_default</tag>
- <item>
- <p>Functions in the shell which are not prefixed by a module name are assumed
- to be functional objects (Funs), built-in functions (BIFs), or belong to the
- module user_default or shell_default.</p>
- <p>To include private shell commands, define them in a module user_default and
- add the following argument as the first line in the .erlang file.</p>
+ <tag>The <c>.erlang</c> startup file</tag>
+ <item>
+ <p>When Erlang/OTP is started, the system searches for a file named
+ <c>.erlang</c> in the directory where Erlang/OTP is started. If not
+ found, the user's home directory is searched for an <c>.erlang</c>
+ file.</p>
+ <p>If an <c>.erlang</c> file is found, it is assumed to contain valid
+ Erlang expressions. These expressions are evaluated as if they were
+ input to the shell.</p>
+ <p>A typical <c>.erlang</c> file contains a set of search paths, for
+ example:</p>
<code type="none"><![CDATA[
- code:load_abs("..../user_default").
- ]]></code>
- </item>
- <tag>erl</tag>
- <item>
- <p>If the contents of .erlang are changed and a private version of
- user_default is defined, it is possible to customize the Erlang/OTP environment.
- More powerful changes can be made by supplying command line arguments in the
- start-up script erl. Refer to erl(1) and <seealso marker="init">init(3)</seealso>
- for further information.</p>
- </item>
+io:format("executing user profile in HOME/.erlang\n",[]).
+code:add_path("/home/calvin/test/ebin").
+code:add_path("/home/hobbes/bigappl-1.2/ebin").
+io:format(".erlang rc finished\n",[]). ]]></code>
+ </item>
+ <tag>user_default and shell_default</tag>
+ <item>
+ <p>Functions in the shell that are not prefixed by a module name are
+ assumed to be functional objects (funs), built-in functions (BIFs),
+ or belong to the module <c>user_default</c> or
+ <c>shell_default</c>.</p>
+ <p>To include private shell commands, define them in a module
+ <c>user_default</c> and add the following argument as the first line
+ in the <c>.erlang</c> file:</p>
+ <code type="none"><![CDATA[
+code:load_abs("..../user_default"). ]]></code>
+ </item>
+ <tag>erl</tag>
+ <item>
+ <p>If the contents of <c>.erlang</c> are changed and a private version
+ of <c>user_default</c> is defined, the Erlang/OTP environment can be
+ customized. More powerful changes can be made by supplying
+ command-line arguments in the startup script <c>erl</c>. For more
+ information, see <seealso marker="init"><c>init(3)</c></seealso>.</p>
+ </item>
</taglist>
</section>
- <section>
- <title>SEE ALSO</title>
- <p><seealso marker="init">init(3)</seealso>,
- <seealso marker="erl_prim_loader">erl_prim_loader(3)</seealso>,
- <seealso marker="kernel:erl_boot_server">erl_boot_server(3)</seealso>,
- <seealso marker="kernel:code">code(3)</seealso>,
- <seealso marker="kernel:application">application(3)</seealso>,
- <seealso marker="kernel:heart">heart(3)</seealso>,
- <seealso marker="kernel:net_kernel">net_kernel(3)</seealso>,
- <seealso marker="kernel:auth">auth(3)</seealso>,
- <seealso marker="tools:make">make(3)</seealso>,
- <seealso marker="epmd">epmd(1)</seealso>,
- <seealso marker="erts_alloc">erts_alloc(3)</seealso></p>
+ <section>
+ <title>See Also</title>
+ <p><seealso marker="epmd"><c>epmd(1)</c></seealso>,
+ <seealso marker="erl_prim_loader"><c>erl_prim_loader(3)</c></seealso>,
+ <seealso marker="erts_alloc"><c>erts_alloc(3)</c></seealso>,
+ <seealso marker="init"><c>init(3)</c></seealso>,
+ <seealso marker="kernel:application">
+ <c>application(3)</c></seealso>,
+ <seealso marker="kernel:auth"><c>auth(3)</c></seealso>,
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>,
+ <seealso marker="kernel:erl_boot_server">
+ <c>erl_boot_server(3)</c></seealso>,
+ <seealso marker="kernel:heart"><c>heart(3)</c></seealso>,
+ <seealso marker="kernel:net_kernel"><c>net_kernel(3)</c></seealso>,
+ <seealso marker="tools:make"><c>make(3)</c></seealso></p>
</section>
</comref>
diff --git a/erts/doc/src/erl_dist_protocol.xml b/erts/doc/src/erl_dist_protocol.xml
index b435d5c9b4..ee74983730 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>2015</year>
+ <year>2016</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -32,1049 +32,989 @@
<file>erl_dist_protocol.xml</file>
</header>
-<p>
-The description here is far from complete and will therefore be further
-refined in upcoming releases.
-
-The protocols both from Erlang nodes towards
-EPMD (Erlang Port Mapper Daemon) and between Erlang nodes, however, are
-stable since many years.
-</p>
-
-<p>The distribution protocol can be divided into four (4) parts:</p>
-<list>
- <item>
- <p>
- 1. Low level socket connection.
- </p>
- </item>
- <item>
- 2. Handshake, interchange node name and authenticate.
- </item>
- <item>
- 3. Authentication (done by net_kernel).
- </item>
- <item>
- 4. Connected.
- </item>
-</list>
-<p>
- A node fetches the Port number of another node through the EPMD (at the
- other host) in order to initiate a connection request.
-</p>
-<p>
-For each host where a distributed Erlang node is running there should also
-be an EPMD running. The EPMD can be started explicitly or automatically
-as a result of the Erlang node startup.
-</p>
-<p>
-By default EPMD listens on port 4369.
-</p>
-<p>
- 3 and 4 are performed at the same level but the net_kernel disconnects the
- other node if it communicates using an invalid cookie (after one (1) second).
-</p>
-
-<p>The integers in all multi-byte fields are in big-endian order.</p>
+ <p>This description is far from complete. It will be updated if the
+ protocol is updated. However, the protocols, both from Erlang
+ nodes to the Erlang Port Mapper Daemon (EPMD) and between Erlang nodes
+ are stable since many years.</p>
+
+ <p>The distribution protocol can be divided into four parts:</p>
+
+ <list type="bulleted">
+ <item>
+ <p>Low-level socket connection (1)</p>
+ </item>
+ <item>
+ <p>Handshake, interchange node name, and authenticate (2)</p>
+ </item>
+ <item>
+ <p>Authentication (done by <seealso marker="kernel:net_kernel">
+ <c>net_kernel(3)</c></seealso>) (3)</p>
+ </item>
+ <item>
+ <p>Connected (4)</p>
+ </item>
+ </list>
+
+ <p>A node fetches the port number of another node through the EPMD (at the
+ other host) to initiate a connection request.</p>
+
+ <p>For each host, where a distributed Erlang node is running, also an EPMD
+ is to be running. The EPMD can be started explicitly or automatically
+ as a result of the Erlang node startup.</p>
+
+ <p>By default the EPMD listens on port 4369.</p>
+
+ <p>(3) and (4) above are performed at the same level but the <c>net_kernel</c>
+ disconnects the other node if it communicates using an invalid cookie (after
+ 1 second).</p>
+
+ <p>The integers in all multibyte fields are in big-endian order.</p>
<section>
<title>EPMD Protocol</title>
- <p>
- The requests served by the EPMD (Erlang Port Mapper Daemon) are
- summarized in the figure below.
- </p>
-
- <image file="erl_ext_fig.gif">
- <icaption>
- Summary of EPMD requests.
- </icaption>
- </image>
- <p>
- Each request <c>*_REQ</c> is preceded by a two-byte length field.
- Thus, the overall request format is:
- </p>
+ <p>The requests served by the EPMD are summarized in the following
+ figure.</p>
+
+ <image file="erl_ext_fig.gif">
+ <icaption>Summary of EPMD Requests</icaption>
+ </image>
+
+ <p>Each request <c>*_REQ</c> is preceded by a 2 byte length field.
+ Thus, the overall request format is as follows:</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">2</cell>
+ <cell align="center">n</cell>
+ </row>
+ <row>
+ <cell align="center"><c>Length</c></cell>
+ <cell align="center"><c>Request</c></cell>
+ </row>
+ <tcaption>Request Format</tcaption>
+ </table>
+
+ <section>
+ <title>Register a Node in EPMD</title>
+ <p>When a distributed node is started it registers itself in the EPMD.
+ The message <c>ALIVE2_REQ</c> described below is sent from the node to
+ the EPMD. The response from the EPMD is <c>ALIVE2_RESP</c>.</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">2</cell>
+ <cell align="center">1</cell>
+ <cell align="center">1</cell>
+ <cell align="center">2</cell>
+ <cell align="center">2</cell>
+ <cell align="center">2</cell>
+ <cell align="center">Nlen</cell>
+ <cell align="center">2</cell>
+ <cell align="center">Elen</cell>
+ </row>
+ <row>
+ <cell align="center"><c>120</c></cell>
+ <cell align="center"><c>PortNo</c></cell>
+ <cell align="center"><c>NodeType</c></cell>
+ <cell align="center"><c>Protocol</c></cell>
+ <cell align="center"><c>HighestVersion</c></cell>
+ <cell align="center"><c>LowestVersion</c></cell>
+ <cell align="center"><c>Nlen</c></cell>
+ <cell align="center"><c>NodeName</c></cell>
+ <cell align="center"><c>Elen</c></cell>
+ <cell align="center"><c>Extra</c></cell>
+ </row>
+ <tcaption>ALIVE2_REQ (120)</tcaption>
+ </table>
+
+ <taglist>
+ <tag><c>PortNo</c></tag>
+ <item>
+ <p>The port number on which the node accept connection requests.</p>
+ </item>
+ <tag><c>NodeType</c></tag>
+ <item>
+ <p>77 = normal Erlang node, 72 = hidden node (C-node), ...</p>
+ </item>
+ <tag><c>Protocol</c></tag>
+ <item>
+ <p>0 = TCP/IPv4, ...</p>
+ </item>
+ <tag><c>HighestVersion</c></tag>
+ <item>
+ <p>The highest distribution version that this node can handle.
+ The value in Erlang/OTP R6B and later is 5.</p>
+ </item>
+ <tag><c>LowestVersion</c></tag>
+ <item>
+ <p>The lowest distribution version that this node can handle.
+ The value in Erlang/OTP R6B and later is 5.</p>
+ </item>
+ <tag><c>Nlen</c></tag>
+ <item>
+ <p>The length (in bytes) of field <c>NodeName</c>.</p>
+ </item>
+ <tag><c>NodeName</c></tag>
+ <item>
+ <p>The node name as an UTF-8 encoded string of <c>Nlen</c> bytes.</p>
+ </item>
+ <tag><c>Elen</c></tag>
+ <item>
+ <p>The length of field <c>Extra</c>.</p>
+ </item>
+ <tag><c>Extra</c></tag>
+ <item>
+ <p>Extra field of <c>Elen</c> bytes.</p>
+ </item>
+ </taglist>
+
+ <p>The connection created to the EPMD must be kept as long as the
+ node is a distributed node. When the connection is closed,
+ the node is automatically unregistered from the EPMD.</p>
+
+ <p>The response message <c>ALIVE2_RESP</c> is as follows:</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">1</cell>
+ <cell align="center">2</cell>
+ </row>
+ <row>
+ <cell align="center"><c>121</c></cell>
+ <cell align="center"><c>Result</c></cell>
+ <cell align="center"><c>Creation</c></cell>
+ </row>
+ <tcaption>ALIVE2_RESP (121)</tcaption>
+ </table>
+
+ <p>Result = 0 -> ok, result &gt; 0 -> error.</p>
+ </section>
+
+ <section>
+ <title>Unregister a Node from EPMD</title>
+ <p>A node unregisters itself from the EPMD by closing the TCP
+ connection to EPMD established when the node was registered.</p>
+ </section>
+
+ <section>
+ <title>Get the Distribution Port of Another Node</title>
+ <p>When one node wants to connect to another node it starts with
+ a <c>PORT_PLEASE2_REQ</c> request to the EPMD on the host where the
+ node resides to get the distribution port that the node listens to.</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">N</cell>
+ </row>
+ <row>
+ <cell align="center"><c>122</c></cell>
+ <cell align="center"><c>NodeName</c></cell>
+ </row>
+ <tcaption>PORT_PLEASE2_REQ (122)</tcaption>
+ </table>
+
+ <p>where N = <c>Length</c> - 1.</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">1</cell>
+ </row>
+ <row>
+ <cell align="center"><c>119</c></cell>
+ <cell align="center"><c>Result</c></cell>
+ </row>
+ <tcaption>PORT2_RESP (119) Response Indicating Error, Result &gt; 0
+ </tcaption>
+ </table>
+
+ <p>or</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">1</cell>
+ <cell align="center">2</cell>
+ <cell align="center">1</cell>
+ <cell align="center">1</cell>
+ <cell align="center">2</cell>
+ <cell align="center">2</cell>
+ <cell align="center">2</cell>
+ <cell align="center">Nlen</cell>
+ <cell align="center">2</cell>
+ <cell align="center">Elen</cell>
+ </row>
+ <row>
+ <cell align="center"><c>119</c></cell>
+ <cell align="center"><c>Result</c></cell>
+ <cell align="center"><c>PortNo</c></cell>
+ <cell align="center"><c>NodeType</c></cell>
+ <cell align="center"><c>Protocol</c></cell>
+ <cell align="center"><c>HighestVersion</c></cell>
+ <cell align="center"><c>LowestVersion</c></cell>
+ <cell align="center"><c>Nlen</c></cell>
+ <cell align="center"><c>NodeName</c></cell>
+ <cell align="center"><c>Elen</c></cell>
+ <cell align="center">><c>Extra</c></cell>
+ </row>
+ <tcaption>PORT2_RESP, Result = 0</tcaption>
+ </table>
+
+ <p>If <c>Result</c> &gt; 0, the packet only consists of
+ <c>[119, Result]</c>.</p>
+
+ <p>The EPMD closes the socket when it has sent the information.</p>
+ </section>
+
+ <section>
+ <title>Get All Registered Names from EPMD</title>
+ <p>This request is used through the Erlang function
+ <seealso marker="kernel:net_adm#names/1,2">
+ <c>net_adm:names/1,2</c></seealso>. A TCP connection is opened
+ to the EPMD and this request is sent.</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ </row>
+ <row>
+ <cell align="center"><c>110</c></cell>
+ </row>
+ <tcaption>NAMES_REQ (110)</tcaption>
+ </table>
+
+ <p>The response for a <c>NAMES_REQ</c> is as follows:</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">4</cell>
+ <cell align="center">&nbsp;</cell>
+ </row>
+ <row>
+ <cell align="center"><c>EPMDPortNo</c></cell>
+ <cell align="center"><c>NodeInfo*</c></cell>
+ </row>
+ <tcaption>NAMES_RESP</tcaption>
+ </table>
+
+ <p><c>NodeInfo</c> is a string written for each active node.
+ When all <c>NodeInfo</c> has been written the connection is
+ closed by the EPMD.</p>
+
+ <p><c>NodeInfo</c> is, as expressed in Erlang:</p>
+
+ <code>
+io:format("name ~ts at port ~p~n", [NodeName, Port]).</code>
+ </section>
+
+ <section>
+ <title>Dump All Data from EPMD</title>
+ <p>This request is not really used, it is to be regarded as a debug
+ feature.</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ </row>
+ <row>
+ <cell align="center"><c>100</c></cell>
+ </row>
+ <tcaption>DUMP_REQ</tcaption>
+ </table>
+
+ <p>The response for a <c>DUMP_REQ</c> is as follows:</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">4</cell>
+ <cell align="center">&nbsp;</cell>
+ </row>
+ <row>
+ <cell align="center"><c>EPMDPortNo</c></cell>
+ <cell align="center"><c>NodeInfo*</c></cell>
+ </row>
+ <tcaption>DUMP_RESP</tcaption>
+ </table>
+
+ <p><c>NodeInfo</c> is a string written for each node kept in the EPMD.
+ When all <c>NodeInfo</c> has been written the connection is
+ closed by the EPMD.</p>
+
+ <p><c>NodeInfo</c> is, as expressed in Erlang:</p>
+
+ <code>
+io:format("active name ~ts at port ~p, fd = ~p~n",
+ [NodeName, Port, Fd]).</code>
+
+ <p>or</p>
+
+ <code>
+io:format("old/unused name ~ts at port ~p, fd = ~p ~n",
+ [NodeName, Port, Fd]).</code>
+ </section>
+
+ <section>
+ <title>Kill EPMD</title>
+ <p>This request kills the running EPMD. It is almost never used.</p>
<table align="left">
- <row>
- <cell align="center">2</cell>
- <cell align="center">n</cell>
- </row>
- <row>
- <cell align="center">Length</cell>
- <cell align="center">Request</cell>
- </row>
- <tcaption></tcaption></table>
-
- <section>
- <title>Register a node in the EPMD</title>
- <p>
- When a distributed node is started it registers itself in EPMD.
- The message ALIVE2_REQ described below is sent from the node towards
- EPMD. The response from EPMD is ALIVE2_RESP.
- </p>
- <table align="left">
- <row>
- <cell align="center">1</cell>
- <cell align="center">2</cell>
- <cell align="center">1</cell>
- <cell align="center">1</cell>
- <cell align="center">2</cell>
- <cell align="center">2</cell>
- <cell align="center">2</cell>
- <cell align="center">Nlen</cell>
- <cell align="center">2</cell>
- <cell align="center">Elen</cell>
- </row>
- <row>
- <cell align="center">120</cell>
- <cell align="center">PortNo</cell>
- <cell align="center">NodeType</cell>
- <cell align="center">Protocol</cell>
- <cell align="center">HighestVersion</cell>
- <cell align="center">LowestVersion</cell>
- <cell align="center">Nlen</cell>
- <cell align="center">NodeName</cell>
- <cell align="center">Elen</cell>
- <cell align="center">Extra</cell>
- </row>
- <tcaption>ALIVE2_REQ (120)</tcaption></table>
- <taglist>
- <tag><c>PortNo</c></tag>
- <item>
- The port number on which the node accept connection requests.
- </item>
- <tag><c>NodeType</c></tag>
- <item>
- 77 = normal Erlang node, 72 = hidden node (C-node),...
- </item>
- <tag><c>Protocol</c></tag>
- <item>
- 0 = tcp/ip-v4, ...
- </item>
- <tag><c>HighestVersion</c></tag>
- <item>
- The highest distribution version that this node can handle.
- The value in R6B and later is 5.
- </item>
- <tag><c>LowestVersion</c></tag>
- <item>
- The lowest distribution version that this node can handle.
- The value in R6B and later is 5.
- </item>
- <tag><c>Nlen</c></tag>
- <item>
- The length (in bytes) of the <c>NodeName</c> field.
- </item>
- <tag><c>NodeName</c></tag>
- <item>
- The NodeName as an UTF-8 encoded string of
- <c>Nlen</c> bytes.
- </item>
- <tag><c>Elen</c></tag>
- <item>
- The length of the <c>Extra</c> field.
- </item>
- <tag><c>Extra</c></tag>
- <item>
- Extra field of <c>Elen</c> bytes.
- </item>
- </taglist>
- <p>
- The connection created to the EPMD must be kept as long as the
- node is a distributed node. When the connection is closed
- the node is automatically unregistered from the EPMD.
- </p>
- <p>
- The response message ALIVE2_RESP is described below.
- </p>
-
- <table align="left">
- <row>
- <cell align="center">1</cell>
- <cell align="center">1</cell>
- <cell align="center">2</cell>
- </row>
- <row>
- <cell align="center">121</cell>
- <cell align="center">Result</cell>
- <cell align="center">Creation</cell>
- </row>
- <tcaption>ALIVE2_RESP (121)</tcaption></table>
- <p>
- Result = 0 -> ok, Result > 0 -> error
- </p>
- </section>
-
- <section>
- <title>Unregister a node from the EPMD</title>
- <p>
- A node unregisters itself from the EPMD by simply closing the
- TCP connection towards EPMD established when the node was registered.
- </p>
- </section>
-
- <section>
- <title>Get the distribution port of another node</title>
- <p>
- When one node wants to connect to another node it starts with
- a PORT_PLEASE2_REQ request towards EPMD on the host where the
- node resides in order to get the distribution port that the node
- listens to.
- </p>
-
- <table align="left">
- <row>
- <cell align="center">1</cell>
- <cell align="center">N</cell>
- </row>
- <row>
- <cell align="center">122</cell>
- <cell align="center">NodeName</cell>
- </row>
- <tcaption>PORT_PLEASE2_REQ (122)</tcaption></table>
- <p>
- where N = Length - 1
- </p>
-
- <p>
- </p>
+ <row>
+ <cell align="center">1</cell>
+ </row>
+ <row>
+ <cell align="center"><c>107</c></cell>
+ </row>
+ <tcaption>KILL_REQ</tcaption>
+ </table>
+
+ <p>The response for a <c>KILL_REQ</c> is as follows:</p>
+
<table align="left">
- <row>
- <cell align="center">1</cell>
- <cell align="center">1</cell>
- </row>
- <row>
- <cell align="center">119</cell>
- <cell align="center">Result</cell>
- </row>
- <tcaption>
- PORT2_RESP (119) response indicating error, Result > 0.
- </tcaption>
+ <row>
+ <cell align="center">2</cell>
+ </row>
+ <row>
+ <cell align="center"><c>OKString</c></cell>
+ </row>
+ <tcaption>KILL_RESP</tcaption>
</table>
- <p>Or</p>
+
+ <p>where <c>OKString</c> is "OK".</p>
+ </section>
+
+ <section>
+ <title>STOP_REQ (Not Used)</title>
<table align="left">
- <row>
- <cell align="center">1</cell>
- <cell align="center">1</cell>
- <cell align="center">2</cell>
- <cell align="center">1</cell>
- <cell align="center">1</cell>
- <cell align="center">2</cell>
- <cell align="center">2</cell>
- <cell align="center">2</cell>
- <cell align="center">Nlen</cell>
- <cell align="center">2</cell>
- <cell align="center">Elen</cell>
- </row>
- <row>
- <cell align="center">119</cell>
- <cell align="center">Result</cell>
- <cell align="center">PortNo</cell>
- <cell align="center">NodeType</cell>
- <cell align="center">Protocol</cell>
- <cell align="center">HighestVersion</cell>
- <cell align="center">LowestVersion</cell>
- <cell align="center">Nlen</cell>
- <cell align="center">NodeName</cell>
- <cell align="center">Elen</cell>
- <cell align="center">Extra</cell>
- </row>
- <tcaption>PORT2_RESP when Result = 0.</tcaption></table>
-<p>
-If Result > 0, the packet only consists of [119, Result].
-</p>
-
- <p>EPMD will close the socket as soon as it has sent the information.</p>
- </section>
-
- <section>
- <title>Get all registered names from EPMD</title>
- <p>
- This request is used via the Erlang function
- <c>net_adm:names/1,2</c>. A TCP connection is opened
- towards EPMD and this request is sent.
- </p>
- <table align="left">
- <row>
- <cell align="center">1</cell>
- </row>
- <row>
- <cell align="center">110</cell>
- </row>
- <tcaption>NAMES_REQ (110)</tcaption></table>
-
-
- <p>The response for a <c>NAMES_REQ</c> looks like this:</p>
- <table align="left">
- <row>
- <cell align="center">4</cell>
- <cell align="center">&nbsp;</cell>
- </row>
- <row>
- <cell align="center">EPMDPortNo</cell>
- <cell align="center">NodeInfo*</cell>
- </row>
- <tcaption>NAMES_RESP</tcaption></table>
- <p>
- NodeInfo is a string written for each active node.
- When all NodeInfo has been written the connection is
- closed by EPMD.
- </p>
- <p>
- NodeInfo is, as expressed in Erlang:
- </p>
- <code>
- io:format("name ~ts at port ~p~n", [NodeName, Port]).
- </code>
- </section>
-
-
- <section>
- <title>Dump all data from EPMD</title>
- <p>
- This request is not really used, it should be regarded as a debug
- feature.
- </p>
- <table align="left">
- <row>
- <cell align="center">1</cell>
- </row>
- <row>
- <cell align="center">100</cell>
- </row>
- <tcaption>DUMP_REQ</tcaption></table>
-
- <p>The response for a <c>DUMP_REQ</c> looks like this:</p>
- <table align="left">
- <row>
- <cell align="center">4</cell>
- <cell align="center">&nbsp;</cell>
- </row>
- <row>
- <cell align="center">EPMDPortNo</cell>
- <cell align="center">NodeInfo*</cell>
- </row>
- <tcaption>DUMP_RESP</tcaption></table>
- <p>
- NodeInfo is a string written for each node kept in EPMD.
- When all NodeInfo has been written the connection is
- closed by EPMD.
- </p>
- <p>
- NodeInfo is, as expressed in Erlang:
- </p>
- <code>
- io:format("active name ~ts at port ~p, fd = ~p ~n",
- [NodeName, Port, Fd]).
- </code>
- <p>
- or
- </p>
- <code>
- io:format("old/unused name ~ts at port ~p, fd = ~p~n",
- [NodeName, Port, Fd]).
- </code>
-
- </section>
-
- <section>
- <title>Kill the EPMD</title>
- <p>
- This request will kill the running EPMD. It is almost never used.
- </p>
- <table align="left">
- <row>
- <cell align="center">1</cell>
- </row>
- <row>
- <cell align="center">107</cell>
- </row>
- <tcaption>KILL_REQ</tcaption></table>
-
- <p>The response fo a <c>KILL_REQ</c> looks like this:</p>
- <table align="left">
- <row>
- <cell align="center">2</cell>
- </row>
- <row>
- <cell align="center">OKString</cell>
- </row>
- <tcaption>KILL_RESP</tcaption></table>
- <p>
- where <c>OKString</c> is "OK".
- </p>
- </section>
-
- <section>
- <title>STOP_REQ (Not Used)</title>
- <p></p>
- <table align="left">
- <row>
- <cell align="center">1</cell>
- <cell align="center">n</cell>
- </row>
- <row>
- <cell align="center">115</cell>
- <cell align="center">NodeName</cell>
- </row>
- <tcaption>STOP_REQ</tcaption></table>
- <p>
- where n = Length - 1
- </p>
- <p>
- The current implementation of Erlang does not care if the connection
- to the EPMD is broken.
- </p>
- <p>The response for a <c>STOP_REQ</c> looks like this.</p>
- <table align="left">
- <row>
- <cell align="center">7</cell>
- </row>
- <row>
- <cell align="center">OKString</cell>
- </row>
- <tcaption>STOP_RESP</tcaption></table>
- <p>
- where OKString is "STOPPED".
- </p>
- <p>A negative response can look like this.</p>
- <table align="left">
- <row>
- <cell align="center">7</cell>
- </row>
- <row>
- <cell align="center">NOKString</cell>
- </row>
- <tcaption>STOP_NOTOK_RESP</tcaption></table>
- <p>
- where NOKString is "NOEXIST".
- </p>
- </section>
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">n</cell>
+ </row>
+ <row>
+ <cell align="center"><c>115</c></cell>
+ <cell align="center"><c>NodeName</c></cell>
+ </row>
+ <tcaption>STOP_REQ</tcaption>
+ </table>
+
+ <p>where n = <c>Length</c> - 1.</p>
+
+ <p>The current implementation of Erlang does not care if the connection
+ to the EPMD is broken.</p>
+
+ <p>The response for a <c>STOP_REQ</c> is as follows:</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">7</cell>
+ </row>
+ <row>
+ <cell align="center"><c>OKString</c></cell>
+ </row>
+ <tcaption>STOP_RESP</tcaption>
+ </table>
+
+ <p>where <c>OKString</c> is "STOPPED".</p>
+
+ <p>A negative response can look as follows:</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">7</cell>
+ </row>
+ <row>
+ <cell align="center"><c>NOKString</c></cell>
+ </row>
+ <tcaption>STOP_NOTOK_RESP</tcaption>
+ </table>
+
+ <p>where <c>NOKString</c> is "NOEXIST".</p>
+ </section>
<!--
- <section>
- <title>ALIVE_REQ (97)</title>
- <p></p>
-
- <table align="left">
- <row>
- <cell align="center">1</cell>
- <cell align="center">2</cell>
- <cell align="center">n</cell>
- </row>
- <row>
- <cell align="center">97</cell>
- <cell align="center">PortNo</cell>
- <cell align="center">NodeName</cell>
- </row>
- <tcaption></tcaption></table>
-
- <p>
- where n = Length - 3
- </p>
- <p>
- The connection created to the EPMD must be kept until the node is
- not a distributed node any longer.
- </p>
- </section>
-
- <section>
- <title>ALIVE_OK_RESP (89)</title>
- <p></p>
- <table align="left">
- <row>
- <cell align="center">1</cell>
- <cell align="center">2</cell>
- </row>
- <row>
- <cell align="center">89</cell>
- <cell align="center">Creation</cell>
- </row>
- <tcaption></tcaption></table>
- </section>
-
-
- <section>
- <title>ALIVE_NOTOK_RESP ()</title>
- <p>
- EPMD closed the connection.
- </p>
- </section>
-
- <section>
- <title>PORT_PLEASE_REQ (112)</title>
- <p></p>
+ <section>
+ <title>ALIVE_REQ (97)</title>
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">2</cell>
+ <cell align="center">n</cell>
+ </row>
+ <row>
+ <cell align="center"><c>97</c></cell>
+ <cell align="center"><c>PortNo</c></cell>
+ <cell align="center"><c>NodeName</c></cell>
+ </row>
+ <tcaption></tcaption>
+ </table>
+
+ <p>where n = <c>Length</c> - 3.</p>
+
+ <p>The connection created to the EPMD must be kept until the node is
+ not a distributed node any longer.</p>
+ </section>
+
+ <section>
+ <title>ALIVE_OK_RESP (89)</title>
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">2</cell>
+ </row>
+ <row>
+ <cell align="center"><c>89</c></cell>
+ <cell align="center"><c>Creation</c></cell>
+ </row>
+ <tcaption></tcaption>
+ </table>
+ </section>
+
+ <section>
+ <title>ALIVE_NOTOK_RESP ()</title>
+ <p>The EPMD closed the connection.</p>
+ </section>
+
+ <section>
+ <title>PORT_PLEASE_REQ (112)</title>
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">n</cell>
+ </row>
+ <row>
+ <cell align="center"><c>112</c></cell>
+ <cell align="center"><c>NodeName</c></cell>
+ </row>
+ <tcaption></tcaption>
+ </table>
+
+ <p>where n = <c>Length</c> - 1.</p>
+ </section>
+
+ <section>
+ <title>PORT_OK_RESP ()</title>
<table align="left">
- <row>
- <cell align="center">1</cell>
- <cell align="center">n</cell>
- </row>
- <row>
- <cell align="center">112</cell>
- <cell align="center">NodeName</cell>
- </row>
- <tcaption></tcaption></table>
- <p>
- where n = Length - 1
- </p>
- </section>
-
- <section>
- <title>PORT_OK_RESP ()</title>
- <p></p>
- <table align="left">
- <row>
- <cell align="center">2</cell>
- </row>
- <row>
- <cell align="center">PortNo</cell>
- </row>
- <tcaption></tcaption></table>
-
- </section>
-
- <section>
- <title>PORT_NOTOK_RESP ()</title>
- <p>
- EPMD closed the connection.
- </p>
- </section>
-
-
- <section>
- <title>PORT_NOTOK_RESP ()</title>
- <p>
- EPMD closed the connection.
- </p>
- </section>
+ <row>
+ <cell align="center">2</cell>
+ </row>
+ <row>
+ <cell align="center"><c>PortNo</c></cell>
+ </row>
+ <tcaption></tcaption>
+ </table>
+ </section>
+
+ <section>
+ <title>PORT_NOTOK_RESP ()</title>
+ <p>The EPMD closed the connection.</p>
+ </section>
+
+ <section>
+ <title>PORT_NOTOK_RESP ()</title>
+ <p>The EPMD closed the connection.</p>
+ </section>
-->
+ </section>
+
+ <section>
+ <marker id="distribution_handshake"/>
+ <title>Distribution Handshake</title>
+ <p>This section describes the distribution handshake protocol introduced
+ in Erlang/OTP R6. This description was previously located in
+ <c>$ERL_TOP/lib/kernel/internal_doc/distribution_handshake.txt</c> and
+ has more or less been copied and "formatted" here. It has been almost
+ unchanged since 1999, but the handshake has not changed much since then
+ either.</p>
+
+ <section>
+ <title>General</title>
+ <p>The TCP/IP distribution uses a handshake that expects a
+ connection-based protocol, that is, the protocol does not include any
+ authentication after the handshake procedure.</p>
+
+ <p>This is not entirely safe, as it is vulnerable against takeover
+ attacks, but it is a tradeoff between fair safety and performance.</p>
+
+ <p>The cookies are never sent in cleartext and the handshake procedure
+ expects the client (called <c>A</c>) to be the first one to prove that
+ it can generate a sufficient digest. The digest is generated with the
+ MD5 message digest algorithm and the challenges are expected to be
+ random numbers.</p>
+ </section>
+ <section>
+ <title>Definitions</title>
+ <p>A challenge is a 32-bit integer in big-endian order. Below the function
+ <c>gen_challenge()</c> returns a random 32-bit integer used as a
+ challenge.</p>
+
+ <p>A digest is a (16 bytes) MD5 hash of the challenge (as text)
+ concatenated with the cookie (as text). Below, the function
+ <c>gen_digest(Challenge, Cookie)</c> generates a digest as described
+ above.</p>
+
+ <p>An <c>out_cookie</c> is the cookie used in outgoing communication to a
+ certain node, so that <c>A</c>'s <c>out_cookie</c> for <c>B</c> is to
+ correspond with <c>B</c>'s <c>in_cookie</c> for <c>A</c> and conversely.
+ <c>A</c>'s <c>out_cookie</c> for <c>B</c> and <c>A</c>'s
+ <c>in_cookie</c> for <c>B</c> need <em>not</em> be the same. Below the
+ function <c>out_cookie(Node)</c> returns the current node's
+ <c>out_cookie</c> for <c>Node</c>.</p>
+
+ <p>An <c>in_cookie</c> is the cookie expected to be used by another node
+ when communicating with us, so that <c>A</c>'s <c>in_cookie</c> for
+ <c>B</c> corresponds with <c>B</c>'s <c>out_cookie</c> for <c>A</c>.
+ Below the function <c>in_cookie(Node)</c> returns the current node's
+ <c>in_cookie</c> for <c>Node</c>.</p>
+
+ <p>The cookies are text strings that can be viewed as passwords.</p>
+
+ <p>Every message in the handshake starts with a 16-bit big-endian integer,
+ which contains the message length (not counting the two initial bytes).
+ In Erlang this corresponds to option <c>{packet, 2}</c> in
+ <seealso marker="kernel:gen_tcp"><c>gen_tcp(3)</c></seealso>.
+ Notice that after the handshake, the distribution switches to 4 byte
+ packet headers.</p>
</section>
- <section>
- <title>Distribution Handshake</title>
- <p>
- <marker id="distribution_handshake"/>
- This section describes the distribution handshake protocol
- introduced in the OTP-R6 release of Erlang/OTP. This
- description was previously located in
- <c>$ERL_TOP/lib/kernel/internal_doc/distribution_handshake.txt</c>,
- and has more or less been copied and "formatted" here. It has been
- more or less unchanged since the year 1999, but the handshake
- should not have changed much since then either.
- </p>
- <section>
- <title>General</title>
- <p>
- The TCP/IP distribution uses a handshake which expects a
- connection based protocol, i.e. the protocol does not include
- any authentication after the handshake procedure.
- </p>
- <p>
- This is not entirely safe, as it is vulnerable against takeover
- attacks, but it is a tradeoff between fair safety and performance.
- </p>
- <p>
- The cookies are never sent in cleartext and the handshake procedure
- expects the client (called A) to be the first one to prove that it can
- generate a sufficient digest. The digest is generated with the
- MD5 message digest algorithm and the challenges are expected to be very
- random numbers.
- </p>
- </section>
- <section>
- <title>Definitions</title>
- <p>
- A challenge is a 32 bit integer number in big endian order. Below the function
- <c>gen_challenge()</c> returns a random 32 bit integer used as a challenge.
- </p>
- <p>
- A digest is a (16 bytes) MD5 hash of the Challenge (as text) concatenated
- with the cookie (as text). Below, the function <c>gen_digest(Challenge, Cookie)</c>
- generates a digest as described above.
- </p>
- <p>
- An out_cookie is the cookie used in outgoing communication to a certain node,
- so that A's out_cookie for B should correspond with B's in_cookie for A and
- the other way around. A's out_cookie for B and A's in_cookie for B need <em>NOT</em>
- be the same. Below the function <c>out_cookie(Node)</c> returns the current
- node's out_cookie for <c>Node</c>.
- </p>
- <p>
- An in_cookie is the cookie expected to be used by another node when
- communicating with us, so that A's in_cookie for B corresponds with B's
- out_cookie for A. Below the function <c>in_cookie(Node)</c> returns the current
- node's <c>in_cookie</c> for <c>Node</c>.
- </p>
- <p>
- The cookies are text strings that can be viewed as passwords.
- </p>
- <p>
- Every message in the handshake starts with a 16 bit big endian integer
- which contains the length of the message (not counting the two initial bytes).
- In erlang this corresponds to the <c>gen_tcp</c> option <c>{packet, 2}</c>. Note that after
- the handshake, the distribution switches to 4 byte packet headers.
- </p>
-
- </section>
- <section>
- <title>The Handshake in Detail</title>
- <p>
- Imagine two nodes, node A, which initiates the handshake and node B, which
- accepts the connection.
- </p>
- <taglist>
- <tag>1) connect/accept</tag>
- <item><p>A connects to B via TCP/IP and B accepts the connection.</p></item>
- <tag>2) send_name/receive_name</tag>
- <item><p>A sends an initial identification to B. B receives the message.
- The message looks like this (every "square" being one byte and the packet
- header removed):
- </p>
-<pre>
+
+ <section>
+ <title>The Handshake in Detail</title>
+ <p>Imagine two nodes, <c>A</c> that initiates the handshake and <c>B</c>
+ that accepts the connection.</p>
+
+ <taglist>
+ <tag>1) connect/accept</tag>
+ <item>
+ <p><c>A</c> connects to <c>B</c> through TCP/IP and <c>B</c> accepts
+ the connection.</p>
+ </item>
+ <tag>2) <c>send_name</c>/<c>receive_name</c></tag>
+ <item>
+ <p><c>A</c> sends an initial identification to <c>B</c>, which
+ receives the message. The message looks as follows (every "square"
+ is one byte and the packet header is removed):</p>
+ <pre>
+---+--------+--------+-----+-----+-----+-----+-----+-----+-...-+-----+
|'n'|Version0|Version1|Flag0|Flag1|Flag2|Flag3|Name0|Name1| ... |NameN|
-+---+--------+--------+-----+-----+-----+-----+-----+-----+-... +-----+
-</pre>
- <p>
- The 'n' is just a message tag.
- Version0 and Version1 is the distribution version selected by node A,
- based on information from EPMD. (16 bit big endian)
- Flag0 ... Flag3 are capability flags, the capabilities defined in
- <c>$ERL_TOP/lib/kernel/include/dist.hrl</c>.
- (32 bit big endian)
- Name0 ... NameN is the full nodename of A, as a string of bytes (the
- packet length denotes how long it is).
- </p></item>
- <tag>3) recv_status/send_status</tag>
- <item><p>B sends a status message to A, which indicates
- if the connection is allowed. The following status codes are defined:</p>
- <taglist>
- <tag><c>ok</c></tag>
- <item>The handshake will continue.</item>
- <tag><c>ok_simultaneous</c></tag>
- <item>The handshake will continue, but A is informed that B
- has another ongoing connection attempt that will be
- shut down (simultaneous connect where A's name is
- greater than B's name, compared literally).</item>
- <tag><c>nok</c></tag>
- <item>The handshake will not continue, as B already has an ongoing handshake
- which it itself has initiated. (simultaneous connect where B's name is
- greater than A's).</item>
- <tag><c>not_allowed</c></tag>
- <item>The connection is disallowed for some (unspecified) security
- reason.</item>
- <tag><c>alive</c></tag>
- <item>A connection to the node is already active, which either means
- that node A is confused or that the TCP connection breakdown
- of a previous node with this name has not yet reached node B.
- See 3B below.</item>
- </taglist>
- <p>This is the format of the status message:</p>
-<pre>
++---+--------+--------+-----+-----+-----+-----+-----+-----+-... +-----+</pre>
+ <p>'n' is the message tag. 'Version0' and 'Version1' is the
+ distribution version selected by <c>A</c>, based on information
+ from the EPMD. (16-bit big-endian) 'Flag0' ... 'Flag3' are
+ capability flags, the capabilities are defined in
+ <c>$ERL_TOP/lib/kernel/include/dist.hrl</c>. (32-bit big-endian)
+ 'Name0' ... 'NameN' is the full node name of <c>A</c>, as a string
+ of bytes (the packet length denotes how long it is).</p>
+ </item>
+ <tag>3) <c>recv_status</c>/<c>send_status</c></tag>
+ <item>
+ <p><c>B</c> sends a status message to <c>A</c>, which indicates if the
+ connection is allowed. The following status codes are defined:</p>
+ <taglist>
+ <tag><c>ok</c></tag>
+ <item>
+ <p>The handshake will continue.</p>
+ </item>
+ <tag><c>ok_simultaneous</c></tag>
+ <item>
+ <p>The handshake will continue, but <c>A</c> is informed that
+ <c>B</c> has another ongoing connection attempt that will be
+ shut down (simultaneous connect where <c>A</c>'s name is
+ greater than <c>B</c>'s name, compared literally).</p>
+ </item>
+ <tag><c>nok</c></tag>
+ <item>
+ <p>The handshake will not continue, as <c>B</c> already has an
+ ongoing handshake, which it itself has initiated (simultaneous
+ connect where <c>B</c>'s name is greater than <c>A</c>'s).</p>
+ </item>
+ <tag><c>not_allowed</c></tag>
+ <item>
+ <p>The connection is disallowed for some (unspecified) security
+ reason.</p>
+ </item>
+ <tag><c>alive</c></tag>
+ <item>
+ <p>A connection to the node is already active, which either means
+ that node <c>A</c> is confused or that the TCP connection
+ breakdown of a previous node with this name has not yet reached
+ node <c>B</c>. See step 3B below.</p>
+ </item>
+ </taglist>
+ <p>The format of the status message is as follows:</p>
+ <pre>
+---+-------+-------+-...-+-------+
|'s'|Status0|Status1| ... |StatusN|
-+---+-------+-------+-...-+-------+
-</pre>
- <p>
- 's' is the message tag Status0 ... StatusN is the status as a string (not terminated)
- </p>
- </item>
- <tag>3B) send_status/recv_status</tag>
- <item><p>If status was 'alive', node A will answer with
- another status message containing either 'true' which means that the
- connection should continue (The old connection from this node is broken), or
- <c>'false'</c>, which simply means that the connection should be closed, the
- connection attempt was a mistake.</p></item>
- <tag>4) recv_challenge/send_challenge</tag>
- <item><p>If the status was <c>ok</c> or <c>ok_simultaneous</c>,
- The handshake continues with B sending A another message, the challenge.
- The challenge contains the same type of information as the "name" message
- initially sent from A to B, with the addition of a 32 bit challenge:</p>
-<pre>
++---+-------+-------+-...-+-------+</pre>
+ <p>'s' is the message tag. 'Status0' ... 'StatusN' is the status as a
+ string (not terminated).</p>
+ </item>
+ <tag>3B) <c>send_status</c>/<c>recv_status</c></tag>
+ <item>
+ <p>If status was <c>alive</c>, node <c>A</c> answers with another
+ status message containing either <c>true</c>, which means that the
+ connection is to continue (the old connection from this node is
+ broken), or <c>false</c>, which means that the connection is to be
+ closed (the connection attempt was a mistake.</p>
+ </item>
+ <tag>4) <c>recv_challenge</c>/<c>send_challenge</c></tag>
+ <item>
+ <p>If the status was <c>ok</c> or <c>ok_simultaneous</c>, the
+ handshake continues with <c>B</c> sending <c>A</c> another message,
+ the challenge. The challenge contains the same type of information
+ as the "name" message initially sent from <c>A</c> to <c>B</c>, plus
+ a 32-bit challenge:</p>
+ <pre>
+---+--------+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-...-+-----+
|'n'|Version0|Version1|Flag0|Flag1|Flag2|Flag3|Chal0|Chal1|Chal2|Chal3|Name0|Name1| ... |NameN|
-+---+--------+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-... +-----+
-</pre>
- <p>
- Where Chal0 ... Chal3 is the challenge as a 32 bit big endian integer
- and the other fields are B's version, flags and full nodename.
- </p></item>
- <tag>5) send_challenge_reply/recv_challenge_reply</tag>
- <item><p>Now A has generated a digest and its own challenge. Those are
- sent together in a package to B:</p>
-<pre>
++---+--------+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-... +-----+</pre>
+ <p>'Chal0' ... 'Chal3' is the challenge as a 32-bit big-endian integer
+ and the other fields are <c>B</c>'s version, flags, and full node
+ name.</p>
+ </item>
+ <tag>5) <c>send_challenge_reply</c>/<c>recv_challenge_reply</c></tag>
+ <item>
+ <p>Now <c>A</c> has generated a digest and its own challenge. Those
+ are sent together in a package to <c>B</c>:</p>
+ <pre>
+---+-----+-----+-----+-----+-----+-----+-----+-----+-...-+------+
|'r'|Chal0|Chal1|Chal2|Chal3|Dige0|Dige1|Dige2|Dige3| ... |Dige15|
-+---+-----+-----+-----+-----+-----+-----+-----+-----+-...-+------+
-</pre>
- <p>
- Where 'r' is the tag, Chal0 ... Chal3 is A's challenge for B to handle and
- Dige0 ... Dige15 is the digest that A constructed from the challenge B sent
- in the previous step.
- </p></item>
- <tag>6) recv_challenge_ack/send_challenge_ack</tag>
- <item><p>B checks that the digest received from A is correct and generates a
- digest from the challenge received from A. The digest is then sent to A. The
- message looks like this:</p>
-<pre>
++---+-----+-----+-----+-----+-----+-----+-----+-----+-...-+------+</pre>
+ <p>'r' is the tag. 'Chal0' ... 'Chal3' is <c>A</c>'s challenge for
+ <c>B</c> to handle. 'Dige0' ... 'Dige15' is the digest that <c>A</c>
+ constructed from the challenge <c>B</c> sent in the previous
+ step.</p>
+ </item>
+ <tag>6) <c>recv_challenge_ack</c>/<c>send_challenge_ack</c></tag>
+ <item>
+ <p><c>B</c> checks that the digest received from <c>A</c> is correct
+ and generates a digest from the challenge received from <c>A</c>.
+ The digest is then sent to <c>A</c>. The message is as follows:</p>
+ <pre>
+---+-----+-----+-----+-----+-...-+------+
|'a'|Dige0|Dige1|Dige2|Dige3| ... |Dige15|
-+---+-----+-----+-----+-----+-...-+------+
-</pre>
- <p>
- Where 'a' is the tag and Dige0 ... Dige15 is the digest calculated by B
- for A's challenge.</p></item>
- <tag>7)</tag>
- <item><p>A checks the digest from B and the connection is up.</p></item>
- </taglist>
- </section>
- <section>
- <title>Semigraphic View</title>
-<pre>
-A (initiator) B (acceptor)
-
-TCP connect -----------------------------------------&gt;
- TCP accept
-
-send_name -----------------------------------------&gt;
- recv_name
-
- &lt;---------------------------------------- send_status
++---+-----+-----+-----+-----+-...-+------+</pre>
+ <p>'a' is the tag. 'Dige0' ... 'Dige15' is the digest calculated by
+ <c>B</c> for <c>A</c>'s challenge.</p>
+ </item>
+ <tag>7) check</tag>
+ <item>
+ <p><c>A</c> checks the digest from <c>B</c> and the connection is
+ up.</p>
+ </item>
+ </taglist>
+ </section>
+
+ <section>
+ <title>Semigraphic View</title>
+ <pre>
+A (initiator) B (acceptor)
+
+TCP connect ------------------------------------&gt;
+ TCP accept
+
+send_name --------------------------------------&gt;
+ recv_name
+
+ &lt;---------------------------------------------- send_status
recv_status
(if status was 'alive'
- send_status - - - - - - - - - - - - - - - - - - - -&gt;
- recv_status)
- ChB = gen_challenge()
- (ChB)
- &lt;---------------------------------------- send_challenge
+ send_status - - - - - - - - - - - - - - - - - -&gt;
+ recv_status)
+ ChB = gen_challenge()
+ (ChB)
+ &lt;---------------------------------------------- send_challenge
recv_challenge
-
ChA = gen_challenge(),
OCA = out_cookie(B),
-DiA = gen_digest(ChB,OCA)
- (ChA, DiA)
-send_challenge_reply --------------------------------&gt;
- recv_challenge_reply
- ICB = in_cookie(A),
- check:
- DiA == gen_digest
- (ChB, ICB) ?
- - if OK:
- OCB = out_cookie(A),
- DiB = gen_digest
- (DiB) (ChA, OCB)
- &lt;----------------------------------------- send_challenge_ack
-recv_challenge_ack DONE
-ICA = in_cookie(B), - else
-check: CLOSE
-DiB == gen_digest(ChA,ICA) ?
-- if OK
+DiA = gen_digest(ChB, OCA)
+ (ChA, DiA)
+send_challenge_reply ---------------------------&gt;
+ recv_challenge_reply
+ ICB = in_cookie(A),
+ check:
+ DiA == gen_digest (ChB, ICB)?
+ - if OK:
+ OCB = out_cookie(A),
+ DiB = gen_digest (ChA, OCB)
+ (DiB)
+ &lt;----------------------------------------------- send_challenge_ack
+recv_challenge_ack DONE
+ICA = in_cookie(B), - else:
+check: CLOSE
+DiB == gen_digest(ChA, ICA)?
+- if OK:
DONE
-- else
- CLOSE
-</pre>
- </section>
- <marker id="dflags"/>
- <section>
- <title>The Currently Defined Distribution Flags</title>
- <p>
- Currently (OTP-R16) the following capability flags are defined:
- </p>
-<pre>
-%% The node should be published and part of the global namespace
--define(DFLAG_PUBLISHED,1).
-
-%% The node implements an atom cache (obsolete)
--define(DFLAG_ATOM_CACHE,2).
-
-%% The node implements extended (3 * 32 bits) references. This is
-%% required today. If not present connection will be refused.
--define(DFLAG_EXTENDED_REFERENCES,4).
-
-%% The node implements distributed process monitoring.
--define(DFLAG_DIST_MONITOR,8).
-
-%% The node uses separate tag for fun's (lambdas) in the distribution protocol.
--define(DFLAG_FUN_TAGS,16#10).
-
-%% The node implements distributed named process monitoring.
--define(DFLAG_DIST_MONITOR_NAME,16#20).
-
-%% The (hidden) node implements atom cache (obsolete)
--define(DFLAG_HIDDEN_ATOM_CACHE,16#40).
-
-%% The node understand new fun-tags
--define(DFLAG_NEW_FUN_TAGS,16#80).
-
-%% The node is capable of handling extended pids and ports. This is
-%% required today. If not present connection will be refused.
--define(DFLAG_EXTENDED_PIDS_PORTS,16#100).
-
-%%
--define(DFLAG_EXPORT_PTR_TAG,16#200).
-
-%%
--define(DFLAG_BIT_BINARIES,16#400).
-
-%% The node understands new float format
--define(DFLAG_NEW_FLOATS,16#800).
-
-%%
--define(DFLAG_UNICODE_IO,16#1000).
-
-%% The node implements atom cache in distribution header.
--define(DFLAG_DIST_HDR_ATOM_CACHE,16#2000).
-
-%% The node understand the SMALL_ATOM_EXT tag
--define(DFLAG_SMALL_ATOM_TAGS, 16#4000).
-
-%% The node understand UTF-8 encoded atoms
--define(DFLAG_UTF8_ATOMS, 16#10000).
-
-</pre>
- </section>
- </section>
-
- <section>
- <marker id="connected_nodes"/>
- <title>Protocol between connected nodes</title>
- <p>
- As of erts version 5.7.2 the runtime system passes a distribution
- flag in the handshake stage that enables the use of a
- <seealso marker="erl_ext_dist#distribution_header">distribution
- header</seealso> on all messages passed. Messages passed between
- nodes are in this case on the following format:
- </p>
- <table align="left">
- <row>
- <cell align="center">4</cell>
- <cell align="center">d</cell>
- <cell align="center">n</cell>
- <cell align="center">m</cell>
- </row>
- <row>
- <cell align="center"><c>Length</c></cell>
- <cell align="center"><c>DistributionHeader</c></cell>
- <cell align="center"><c>ControlMessage</c></cell>
- <cell align="center"><c>Message</c></cell>
- </row>
- <tcaption></tcaption></table>
- <p>
- where:
- </p>
- <p>
- <c>Length</c> is equal to d + n + m
- </p>
- <p>
- <c>ControlMessage</c> is a tuple passed using the external format of
- Erlang.
- </p>
- <p>
- <c>Message</c> is the message sent to another node using the '!'
- (in external format). Note that <c>Message</c> is only passed in
- combination with a <c>ControlMessage</c> encoding a send ('!').
- </p>
- <p>
- Also note that <seealso marker="erl_ext_dist#overall_format">the
- version number is omitted from the terms that follow a
- distribution header</seealso>.
- </p>
- <p>
- Nodes with an erts version less than 5.7.2 does not pass the
- distribution flag that enables the distribution header. Messages
- passed between nodes are in this case on the following format:
- </p>
- <table align="left">
- <row>
- <cell align="center">4</cell>
- <cell align="center">1</cell>
- <cell align="center">n</cell>
- <cell align="center">m</cell>
- </row>
- <row>
- <cell align="center"><c>Length</c></cell>
- <cell align="center"><c>Type</c></cell>
- <cell align="center"><c>ControlMessage</c></cell>
- <cell align="center"><c>Message</c></cell>
- </row>
- <tcaption></tcaption></table>
- <p>
- where:
- </p>
- <p>
- <c>Length</c> is equal to 1 + n + m
- </p>
- <p>
- Type is: 112 (pass through)
- </p>
- <p>
- <c>ControlMessage</c> is a tuple passed using the external format of
- Erlang.
- </p>
- <p>
- <c>Message</c> is the message sent to another node using the '!'
- (in external format). Note that <c>Message</c> is only passed in
- combination with a <c>ControlMessage</c> encoding a send ('!').
- </p>
- <p>
- The <c>ControlMessage</c> is a tuple, where the first element
- indicates which distributed operation it encodes.
- </p>
- <taglist>
- <tag><c>LINK</c></tag>
- <item>
- <p>
- <c>{1, FromPid, ToPid}</c>
- </p>
- </item>
-
- <tag><c>SEND</c></tag>
- <item>
- <p>
- <c>{2, Cookie, ToPid}</c>
- </p>
- <p>
- <em>Note</em> followed by <c>Message</c>
- </p>
- </item>
-
- <tag><c>EXIT</c></tag>
- <item>
- <p>
- <c>{3, FromPid, ToPid, Reason}</c>
- </p>
- </item>
-
- <tag><c>UNLINK</c></tag>
- <item>
- <p>
- <c>{4, FromPid, ToPid}</c>
- </p>
- </item>
-
- <tag><c>NODE_LINK</c></tag>
- <item>
- <p>
- <c>{5}</c>
- </p>
- </item>
-
- <tag><c>REG_SEND</c></tag>
- <item>
- <p>
- <c>{6, FromPid, Cookie, ToName}</c>
- </p>
- <p>
- <em>Note</em> followed by <c>Message</c>
- </p>
- </item>
-
- <tag><c>GROUP_LEADER</c></tag>
- <item>
- <p>
- <c>{7, FromPid, ToPid}</c>
- </p>
- </item>
-
- <tag><c>EXIT2</c></tag>
- <item>
- <p>
- <c>{8, FromPid, ToPid, Reason}</c>
- </p>
- </item>
- </taglist>
- </section>
-
-
- <section>
- <title>New Ctrlmessages for distrvsn = 1 (OTP R4)</title>
- <taglist>
- <tag><c>SEND_TT</c></tag>
- <item>
- <p>
- <c>{12, Cookie, ToPid, TraceToken}</c>
- </p>
- <p>
- <em>Note</em> followed by <c>Message</c>
- </p>
- </item>
-
- <tag><c>EXIT_TT</c></tag>
- <item>
- <p>
- <c>{13, FromPid, ToPid, TraceToken, Reason}</c>
- </p>
- </item>
-
- <tag><c>REG_SEND_TT</c></tag>
- <item>
- <p>
- <c>{16, FromPid, Cookie, ToName, TraceToken}</c>
- </p>
- <p>
- <em>Note</em> followed by <c>Message</c>
- </p>
- </item>
-
- <tag><c>EXIT2_TT</c></tag>
- <item>
- <p>
- <c>{18, FromPid, ToPid, TraceToken, Reason}</c>
- </p>
- </item>
- </taglist>
- </section>
-
- <section>
- <title>New Ctrlmessages for distrvsn = 2</title>
- <p>
- distrvsn 2 was never used.
- </p>
- </section>
-
- <section>
- <title>New Ctrlmessages for distrvsn = 3 (OTP R5C)</title>
- <p>
- None, but the version number was increased anyway.
- </p>
+- else:
+ CLOSE</pre>
</section>
<section>
- <title>New Ctrlmessages for distrvsn = 4 (OTP R6)</title>
- <p>
- These are only recognized by Erlang nodes, not by hidden nodes.
- </p>
+ <marker id="dflags"/>
+ <title>Distribution Flags</title>
+ <p>The following capability flags are defined:</p>
<taglist>
- <tag><c>MONITOR_P</c></tag>
- <item>
- <p>
- <c>{19, FromPid, ToProc, Ref}</c>
-
- <c>FromPid</c> = monitoring process
- <c>ToProc</c> = monitored process pid or name (atom)
- </p>
- </item>
-
- <tag><c>DEMONITOR_P</c></tag>
- <item>
- <p>
- <c>{20, FromPid, ToProc, Ref}</c>
- We include the FromPid just in case we want to trace this.
-
- <c>FromPid</c> = monitoring process
- <c>ToProc</c> = monitored process pid or name (atom)
- </p>
- </item>
-
- <tag><c>MONITOR_P_EXIT</c></tag>
- <item>
- <p>
- <c>{21, FromProc, ToPid, Ref, Reason}</c>
-
- <c>FromProc</c> = monitored process pid or name (atom)
- <c>ToPid</c> = monitoring process
- <c>Reason</c> = exit reason for the monitored process
- </p>
- </item>
+ <tag><c>-define(DFLAG_PUBLISHED,16#1).</c></tag>
+ <item>
+ <p>The node is to be published and part of the global namespace.</p>
+ </item>
+ <tag><c>-define(DFLAG_ATOM_CACHE,16#2).</c></tag>
+ <item>
+ <p>The node implements an atom cache (obsolete).</p>
+ </item>
+ <tag><c>-define(DFLAG_EXTENDED_REFERENCES,16#4).</c></tag>
+ <item>
+ <p>The node implements extended (3 &times; 32 bits) references. This
+ is required today. If not present, the connection is refused.</p>
+ </item>
+ <tag><c>-define(DFLAG_DIST_MONITOR,16#8).</c></tag>
+ <item>
+ <p>The node implements distributed process monitoring.</p>
+ </item>
+ <tag><c>-define(DFLAG_FUN_TAGS,16#10).</c></tag>
+ <item>
+ <p>The node uses separate tag for funs (lambdas) in the distribution
+ protocol.</p>
+ </item>
+ <tag><c>-define(DFLAG_DIST_MONITOR_NAME,16#20).</c></tag>
+ <item>
+ <p>The node implements distributed named process monitoring.</p>
+ </item>
+ <tag><c>-define(DFLAG_HIDDEN_ATOM_CACHE,16#40).</c></tag>
+ <item>
+ <p>The (hidden) node implements atom cache (obsolete).</p>
+ </item>
+ <tag><c>-define(DFLAG_NEW_FUN_TAGS,16#80).</c></tag>
+ <item>
+ <p>The node understand new fun tags.</p>
+ </item>
+ <tag><c>-define(DFLAG_EXTENDED_PIDS_PORTS,16#100).</c></tag>
+ <item>
+ <p>The node can handle extended pids and ports. This is required
+ today. If not present, the connection is refused.</p>
+ </item>
+ <tag><c>-define(DFLAG_EXPORT_PTR_TAG,16#200).</c></tag>
+ <item>
+ </item>
+ <tag><c>-define(DFLAG_BIT_BINARIES,16#400).</c></tag>
+ <item>
+ </item>
+ <tag><c>-define(DFLAG_NEW_FLOATS,16#800).</c></tag>
+ <item>
+ <p>The node understands new float format.</p>
+ </item>
+ <tag><c>-define(DFLAG_UNICODE_IO,16#1000).</c></tag>
+ <item>
+ </item>
+ <tag><c>-define(DFLAG_DIST_HDR_ATOM_CACHE,16#2000).</c></tag>
+ <item>
+ <p>The node implements atom cache in distribution header.</p>
+ </item>
+ <tag><c>-define(DFLAG_SMALL_ATOM_TAGS, 16#4000).</c></tag>
+ <item>
+ <p>The node understand the <c>SMALL_ATOM_EXT</c> tag.</p>
+ </item>
+ <tag><c>-define(DFLAG_UTF8_ATOMS, 16#10000).</c></tag>
+ <item>
+ <p>The node understand UTF-8 encoded atoms.</p>
+ </item>
</taglist>
</section>
- </chapter>
+ </section>
+
+ <section>
+ <marker id="connected_nodes"/>
+ <title>Protocol between Connected Nodes</title>
+ <p>As from ERTS 5.7.2 the runtime system passes a distribution flag
+ in the handshake stage that enables the use of a
+ <seealso marker="erl_ext_dist#distribution_header">distribution header
+ </seealso> on all messages passed. Messages passed between nodes have in
+ this case the following format:</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">4</cell>
+ <cell align="center">d</cell>
+ <cell align="center">n</cell>
+ <cell align="center">m</cell>
+ </row>
+ <row>
+ <cell align="center"><c>Length</c></cell>
+ <cell align="center"><c>DistributionHeader</c></cell>
+ <cell align="center"><c>ControlMessage</c></cell>
+ <cell align="center"><c>Message</c></cell>
+ </row>
+ <tcaption>Format of Messages Passed between Nodes (as from ERTS 5.7.2)
+ </tcaption>
+ </table>
+
+ <taglist>
+ <tag><c>Length</c></tag>
+ <item>
+ <p>Equal to d + n + m.</p>
+ </item>
+ <tag><c>ControlMessage</c></tag>
+ <item>
+ <p>A tuple passed using the external format of Erlang.</p>
+ </item>
+ <tag><c>Message</c></tag>
+ <item>
+ <p>The message sent to another node using the '!' (in external format).
+ Notice that <c>Message</c> is only passed in combination with a
+ <c>ControlMessage</c> encoding a send ('!').</p>
+ </item>
+ </taglist>
+
+ <p>Notice that <seealso marker="erl_ext_dist#overall_format">the version
+ number is omitted from the terms that follow a distribution header
+ </seealso>.</p>
+
+ <p>Nodes with an ERTS version earlier than 5.7.2 does not pass the
+ distribution flag that enables the distribution header. Messages passed
+ between nodes have in this case the following format:</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">4</cell>
+ <cell align="center">1</cell>
+ <cell align="center">n</cell>
+ <cell align="center">m</cell>
+ </row>
+ <row>
+ <cell align="center"><c>Length</c></cell>
+ <cell align="center"><c>Type</c></cell>
+ <cell align="center"><c>ControlMessage</c></cell>
+ <cell align="center"><c>Message</c></cell>
+ </row>
+ <tcaption>Format of Messages Passed between Nodes (before ERTS 5.7.2)
+ </tcaption>
+ </table>
+
+ <taglist>
+ <tag><c>Length</c></tag>
+ <item>
+ <p>Equal to 1 + n + m.</p>
+ </item>
+ <tag><c>Type</c></tag>
+ <item>
+ <p>Equal to <c>112</c> (pass through).</p>
+ </item>
+ <tag><c>ControlMessage</c></tag>
+ <item>
+ <p>A tuple passed using the external format of Erlang.</p>
+ </item>
+ <tag><c>Message</c></tag>
+ <item>
+ <p>The message sent to another node using the '!' (in external format).
+ Notice that <c>Message</c> is only passed in combination with a
+ <c>ControlMessage</c> encoding a send ('!').</p>
+ </item>
+ </taglist>
+
+ <p>The <c>ControlMessage</c> is a tuple, where the first element indicates
+ which distributed operation it encodes:</p>
+
+ <taglist>
+ <tag><c>LINK</c></tag>
+ <item>
+ <p><c>{1, FromPid, ToPid}</c></p>
+ </item>
+ <tag><c>SEND</c></tag>
+ <item>
+ <p><c>{2, Unused, ToPid}</c></p>
+ <p>Followed by <c>Message</c>.</p>
+ <p><c>Unused</c> is kept for backward compatibility.</p>
+ </item>
+ <tag><c>EXIT</c></tag>
+ <item>
+ <p><c>{3, FromPid, ToPid, Reason}</c></p>
+ </item>
+ <tag><c>UNLINK</c></tag>
+ <item>
+ <p><c>{4, FromPid, ToPid}</c></p>
+ </item>
+ <tag><c>NODE_LINK</c></tag>
+ <item>
+ <p><c>{5}</c></p>
+ </item>
+ <tag><c>REG_SEND</c></tag>
+ <item>
+ <p><c>{6, FromPid, Unused, ToName}</c></p>
+ <p>Followed by <c>Message</c>.</p>
+ <p><c>Unused</c> is kept for backward compatibility.</p>
+ </item>
+ <tag><c>GROUP_LEADER</c></tag>
+ <item>
+ <p><c>{7, FromPid, ToPid}</c></p>
+ </item>
+ <tag><c>EXIT2</c></tag>
+ <item>
+ <p><c>{8, FromPid, ToPid, Reason}</c></p>
+ </item>
+ </taglist>
+ </section>
+
+ <section>
+ <title>New Ctrlmessages for distrvsn = 1 (Erlang/OTP R4)</title>
+ <taglist>
+ <tag><c>SEND_TT</c></tag>
+ <item>
+ <p><c>{12, Unused, ToPid, TraceToken}</c></p>
+ <p>Followed by <c>Message</c>.</p>
+ <p><c>Unused</c> is kept for backward compatibility.</p>
+ </item>
+ <tag><c>EXIT_TT</c></tag>
+ <item>
+ <p><c>{13, FromPid, ToPid, TraceToken, Reason}</c></p>
+ </item>
+ <tag><c>REG_SEND_TT</c></tag>
+ <item>
+ <p><c>{16, FromPid, Unused, ToName, TraceToken}</c></p>
+ <p>Followed by <c>Message</c>.</p>
+ <p><c>Unused</c> is kept for backward compatibility.</p>
+ </item>
+ <tag><c>EXIT2_TT</c></tag>
+ <item>
+ <p><c>{18, FromPid, ToPid, TraceToken, Reason}</c></p>
+ </item>
+ </taglist>
+ </section>
+
+ <section>
+ <title>New Ctrlmessages for distrvsn = 2</title>
+ <p><c>distrvsn</c> 2 was never used.</p>
+ </section>
+
+ <section>
+ <title>New Ctrlmessages for distrvsn = 3 (Erlang/OTP R5C)</title>
+ <p>None, but the version number was increased anyway.</p>
+ </section>
+
+ <section>
+ <title>New Ctrlmessages for distrvsn = 4 (Erlang/OTP R6)</title>
+ <p>These are only recognized by Erlang nodes, not by hidden nodes.</p>
+
+ <taglist>
+ <tag><c>MONITOR_P</c></tag>
+ <item>
+ <p><c>{19, FromPid, ToProc, Ref}</c>, where
+ <c>FromPid</c> = monitoring process and
+ <c>ToProc</c> = monitored process pid or name (atom)</p>
+ </item>
+ <tag><c>DEMONITOR_P</c></tag>
+ <item>
+ <p><c>{20, FromPid, ToProc, Ref}</c>, where
+ <c>FromPid</c> = monitoring process and
+ <c>ToProc</c> = monitored process pid or name (atom)</p>
+ <p>We include <c>FromPid</c> just in case we want to trace this.</p>
+ </item>
+ <tag><c>MONITOR_P_EXIT</c></tag>
+ <item>
+ <p><c>{21, FromProc, ToPid, Ref, Reason}</c>, where
+ <c>FromProc</c> = monitored process pid or name (atom),
+ <c>ToPid</c> = monitoring process, and
+ <c>Reason</c> = exit reason for the monitored process</p>
+ </item>
+ </taglist>
+ </section>
+</chapter>
diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml
index 34dc8af238..5705100ab2 100644
--- a/erts/doc/src/erl_driver.xml
+++ b/erts/doc/src/erl_driver.xml
@@ -4,7 +4,7 @@
<cref>
<header>
<copyright>
- <year>2001</year><year>2015</year>
+ <year>2001</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -21,7 +21,6 @@
limitations under the License.
</legalnotice>
-
<title>erl_driver</title>
<prepared>Jakob Cederlund</prepared>
<responsible>Jakob Cederlund</responsible>
@@ -33,519 +32,545 @@
<file>erl_driver.xml</file>
</header>
<lib>erl_driver</lib>
- <libsummary>API functions for an Erlang driver</libsummary>
+ <libsummary>API functions for an Erlang driver.</libsummary>
<description>
<p>An Erlang driver is a library containing a set of native driver
- callback functions that the Erlang VM calls when certain
- events occur. There may be multiple instances of a driver, each
+ callback functions that the Erlang Virtual Machine calls when certain
+ events occur. There can be multiple instances of a driver, each
instance is associated with an Erlang port.</p>
+
<marker id="WARNING"/>
- <warning><p><em>Use this functionality with extreme care!</em></p>
+ <warning>
+ <p><em>Use this functionality with extreme care.</em></p>
<p>A driver callback is executed as a direct extension of the
- native code of the VM. Execution is not made in a safe environment.
- The VM can <em>not</em> provide the same services as provided when
- executing Erlang code, such as preemptive scheduling or memory
- protection. If the driver callback function doesn't behave well,
- the whole VM will misbehave.</p>
- <list>
- <item><p>A driver callback that crash will crash the whole VM.</p></item>
- <item><p>An erroneously implemented driver callback might cause
- a VM internal state inconsistency which may cause a crash of the VM,
- or miscellaneous misbehaviors of the VM at any point after the call
- to the driver callback.</p></item>
- <item><p>A driver callback that do <seealso marker="#lengthy_work">lengthy
- work</seealso> before returning will degrade responsiveness of the VM,
- and may cause miscellaneous strange behaviors. Such strange behaviors
- include, but are not limited to, extreme memory usage, and bad load
- balancing between schedulers. Strange behaviors that might occur due
- to lengthy work may also vary between OTP releases.</p></item>
+ native code of the VM. Execution is not made in a safe environment.
+ The VM <em>cannot</em> provide the same services as provided when
+ executing Erlang code, such as pre-emptive scheduling or memory
+ protection. If the driver callback function does not behave well,
+ the whole VM will misbehave.</p>
+ <list type="bulleted">
+ <item>
+ <p>A driver callback that crash will crash the whole VM.</p>
+ </item>
+ <item>
+ <p>An erroneously implemented driver callback can cause a VM
+ internal state inconsistency, which can cause a crash of the VM,
+ or miscellaneous misbehaviors of the VM at any point after the
+ call to the driver callback.</p>
+ </item>
+ <item>
+ <p>A driver callback doing
+ <seealso marker="#lengthy_work">lengthy work</seealso> before
+ returning degrades responsiveness of the VM and can cause
+ miscellaneous strange behaviors. Such strange behaviors
+ include, but are not limited to, extreme memory usage and bad
+ load balancing between schedulers. Strange behaviors that can
+ occur because of lengthy work can also vary between Erlang/OTP
+ releases.</p>
+ </item>
</list>
- </warning>
- <p>As of erts version 5.5.3 the driver interface has been extended
- (see <seealso marker="driver_entry#extended_marker">extended marker</seealso>).
- The extended interface introduce
+ </warning>
+
+ <p>As from ERTS 5.5.3 the driver interface has been extended
+ (see <seealso marker="driver_entry#extended_marker">
+ <c>extended marker</c></seealso>). The extended interface introduces
<seealso marker="#version_management">version management</seealso>,
- the possibility to pass capability flags
- (see <seealso marker="driver_entry#driver_flags">driver flags</seealso>)
- to the runtime system at driver initialization, and some new
- driver API functions. </p>
+ the possibility to pass capability flags (see
+ <seealso marker="driver_entry#driver_flags">
+ <c>driver_flags</c></seealso>) to the runtime system at driver
+ initialization, and some new driver API functions.</p>
+
<note>
- <p>As of erts version 5.9 old drivers have to be recompiled
- and have to use the extended interface. They also have to be
- adjusted to the
- <seealso marker="#rewrites_for_64_bits">64-bit capable driver interface.
- </seealso>
- </p>
+ <p>As from ERTS 5.9 old drivers must be recompiled
+ and use the extended interface. They must also be adjusted to the
+ <seealso marker="#rewrites_for_64_bits">
+ 64-bit capable driver interface</seealso>.</p>
</note>
+
<p>The driver calls back to the emulator, using the API
functions declared in <c>erl_driver.h</c>. They are used for
- outputting data from the driver, using timers, etc.</p>
+ outputting data from the driver, using timers, and so on.</p>
+
<p>Each driver instance is associated with a port. Every port
has a port owner process. Communication with the port is normally
done through the port owner process. Most of the functions take
the <c>port</c> handle as an argument. This identifies the driver
- instance. Note that this port handle must be stored by the driver,
+ instance. Notice that this port handle must be stored by the driver,
it is not given when the driver is called from the emulator (see
- <seealso marker="driver_entry#emulator">driver_entry</seealso>).</p>
+ <seealso marker="driver_entry#emulator">
+ <c>driver_entry</c></seealso>).</p>
+
<p>Some of the functions take a parameter of type
- <c>ErlDrvBinary</c>, a driver binary. It should be both
+ <c>ErlDrvBinary</c>, a driver binary. It is to be both
allocated and freed by the caller. Using a binary directly avoids
one extra copying of data.</p>
+
<p>Many of the output functions have a "header buffer", with
<c>hbuf</c> and <c>hlen</c> parameters. This buffer is sent as a
list before the binary (or list, depending on port mode) that is
sent. This is convenient when matching on messages received from
- the port. (Although in the latest versions of Erlang, there is
- the binary syntax, that enables you to match on the beginning of
- a binary.)
- <marker id="smp_support"></marker>
-</p>
- <p>In the runtime system with SMP support, drivers are locked either
- on driver level or port level (driver instance level). By default
- driver level locking will be used, i.e., only one emulator thread
+ the port. (Although in the latest Erlang versions there is
+ the binary syntax, which enables you to match on the beginning of
+ a binary.)</p>
+ <p><marker id="smp_support"></marker>In the runtime system with
+ SMP support, drivers are locked either on driver level
+ or port level (driver instance level). By default
+ driver level locking will be used, that is, only one emulator thread
will execute code in the driver at a time. If port level locking
- is used, multiple emulator threads may execute code in the driver
- at the same time. There will only be one thread at a time calling
- driver call-backs corresponding to the same port, though. In order
- to enable port level locking set the <c>ERL_DRV_FLAG_USE_PORT_LOCKING</c>
+ is used, multiple emulator threads can execute code in the driver
+ at the same time. Only one thread at a time will call
+ driver callbacks corresponding to the same port, though.
+ To enable port level locking, set the <c>ERL_DRV_FLAG_USE_PORT_LOCKING</c>
<seealso marker="driver_entry#driver_flags">driver flag</seealso> in
- the <seealso marker="driver_entry">driver_entry</seealso>
- used by the driver. When port level locking is used it is the
- responsibility of the driver writer to synchronize all accesses
+ the <seealso marker="driver_entry"><c>driver_entry</c></seealso>
+ used by the driver. When port level locking is used,
+ the driver writer is responsible for synchronizing all accesses
to data shared by the ports (driver instances).</p>
+
<p>Most drivers written before the runtime system with SMP
- support existed will be able to run in the runtime system
- with SMP support without being rewritten if driver
+ support existed can run in the runtime system
+ with SMP support, without being rewritten, if driver
level locking is used.</p>
+
<note>
<p>It is assumed that drivers do not access other drivers. If
- drivers should access each other they have to provide their own
- mechanism for thread safe synchronization. Such "inter driver
+ drivers access each other, they must provide their own
+ mechanism for thread-safe synchronization. Such "inter-driver
communication" is strongly discouraged.</p>
</note>
+
<p>Previously, in the runtime system without SMP support,
- specific driver call-backs were always called from the same
+ specific driver callbacks were always called from the same
thread. This is <em>not</em> the case in the runtime system
with SMP support. Regardless of locking scheme used, calls
- to driver call-backs may be made from different threads, e.g.,
- two consecutive calls to exactly the same call-back for exactly
- the same port may be made from two different threads. This
- will for <em>most</em> drivers not be a problem, but it might.
- Drivers that depend on all call-backs being called in the
- same thread, <em>have</em> to be rewritten before being used
+ to driver callbacks can be made from different threads. For example,
+ two consecutive calls to exactly the same callback for exactly
+ the same port can be made from two different threads. This
+ is for <em>most</em> drivers not a problem, but it can be.
+ Drivers that depend on all callbacks that are called in the
+ same thread, <em>must</em> be rewritten before they are used
in the runtime system with SMP support.</p>
+
<note>
<p>Regardless of locking scheme used, calls to driver
- call-backs may be made from different threads.</p>
+ callbacks can be made from different threads.</p>
</note>
- <p>Most functions in this API are <em>not</em> thread-safe, i.e.,
- they may <em>not</em> be called from an arbitrary thread. Functions
- that are not documented as thread-safe may only be called from
- driver call-backs or function calls descending from a driver
- call-back call. Note that driver call-backs may be called from
+
+ <p>Most functions in this API are <em>not</em> thread-safe, that is,
+ they <em>cannot</em> be called from any thread. Functions
+ that are not documented as thread-safe can only be called from
+ driver callbacks or function calls descending from a driver
+ callback call. Notice that driver callbacks can be called from
different threads. This, however, is not a problem for any
- function in this API, since the emulator has control over
+ function in this API, as the emulator has control over
these threads.</p>
+
<warning>
- <p>Functions not explicitly documented as thread safe are
- <em>not</em> thread safe. Also note that some functions
- are <em>only</em> thread safe when used in a runtime
+ <p>Functions not explicitly documented as thread-safe are
+ <em>not</em> thread safe. Also notice that some functions
+ are <em>only</em> thread-safe when used in a runtime
system with SMP support.</p>
- <p>A function not explicitly documented as thread safe may at
- some point in time have a thread safe implementation in the
- runtime system. Such an implementation may however change to
- a thread <em>unsafe</em> implementation at any time <em>without
- any notice</em> at all.
- </p>
- <p><em>Only use functions explicitly documented as thread safe
- from arbitrary threads.</em></p>
+ <p>A function not explicitly documented as thread-safe can, at
+ some point in time, have a thread-safe implementation in the
+ runtime system. Such an implementation can however change to
+ a thread <em>unsafe</em> implementation at any time <em>without
+ any notice</em>.</p>
+ <p><em>Only use functions explicitly documented as thread-safe
+ from arbitrary threads.</em></p>
</warning>
- <p><marker id="lengthy_work"/>
- As mentioned in the <seealso marker="#WARNING">warning</seealso> text at
- the beginning of this document it is of vital importance that a driver callback
- does return relatively fast. It is hard to give an exact maximum amount
- of time that a driver callback is allowed to work, but as a rule of thumb
- a well behaving driver callback should return before a millisecond has
- passed. This can be achieved using different approaches.
- If you have full control over the code that are to execute in the driver
- callback, the best approach is to divide the work into multiple chunks of
- work and trigger multiple calls to the
- <seealso marker="driver_entry#timeout">timeout callback</seealso> using
- zero timeouts. The
- <seealso marker="#erl_drv_consume_timeslice"><c>erl_drv_consume_timeslice()</c></seealso>
- function can be useful in order to determine when to trigger such
- timeout callback calls. It might, however, not always be possible to
- implement it this way, e.g. when calling third party libraries. In this
- case you typically want to dispatch the work to another thread.
- Information about thread primitives can be found below.</p>
+
+ <p><marker id="lengthy_work"/>
+ As mentioned in the <seealso marker="#WARNING">warning</seealso> text at
+ the beginning of this section, it is of vital importance that a driver
+ callback returns relatively fast. It is difficult to give an exact
+ maximum amount of time that a driver callback is allowed to work, but
+ usually a well-behaving driver callback is to return within 1 millisecond.
+ This can be achieved using different approaches.
+ If you have full control over the code to execute in the driver
+ callback, the best approach is to divide the work into multiple chunks of
+ work, and trigger multiple calls to the
+ <seealso marker="driver_entry#timeout">time-out callback</seealso> using
+ zero time-outs. Function <seealso marker="#erl_drv_consume_timeslice">
+ <c>erl_drv_consume_timeslice</c></seealso> can be useful to
+ determine when to trigger such time-out callback calls. However, sometimes
+ it cannot be implemented this way, for example when calling
+ third-party libraries. In this case, you typically want to dispatch the
+ work to another thread. Information about thread primitives is provided
+ below.</p>
</description>
<section>
- <title>FUNCTIONALITY</title>
+ <title>Functionality</title>
<p>All functions that a driver needs to do with Erlang are
- performed through driver API functions. There are functions
+ performed through driver API functions. Functions exist
for the following functionality:</p>
+
<taglist>
<tag>Timer functions</tag>
- <item>Timer functions are used to control the timer that a driver
- may use. The timer will have the emulator call the
- <seealso marker="driver_entry#timeout">timeout</seealso> entry
- function after a specified time. Only one timer is available
- for each driver instance.</item>
+ <item>
+ <p>Control the timer that a driver can use. The timer has the
+ emulator call the <seealso marker="driver_entry#timeout">
+ <c>timeout</c></seealso> entry function after a specified time.
+ Only one timer is available for each driver instance.</p>
+ </item>
<tag>Queue handling</tag>
<item>
<p>Every driver instance has an associated queue. This queue is a
- <c>SysIOVec</c> that works as a buffer. It's mostly used for
- the driver to buffer data that should be written to a device,
+ <c>SysIOVec</c>, which works as a buffer. It is mostly used for
+ the driver to buffer data that is to be written to a device,
it is a byte stream. If the port owner process closes the
- driver, and the queue is not empty, the driver will not be
+ driver, and the queue is not empty, the driver is not
closed. This enables the driver to flush its buffers before
closing.</p>
- <p>The queue can be manipulated from arbitrary threads if
- a port data lock is used. See documentation of the
- <seealso marker="#ErlDrvPDL">ErlDrvPDL</seealso> type for
- more information.</p>
+ <p>The queue can be manipulated from any threads if
+ a port data lock is used. For more information, see
+ <seealso marker="#ErlDrvPDL"><c>ErlDrvPDL</c></seealso>.</p>
</item>
<tag>Output functions</tag>
- <item>With the output functions, the driver sends data back to
- the emulator. They will be received as messages by the port owner
- process, see <c>open_port/2</c>. The vector function and the
- function taking a driver binary are faster, because they avoid
- copying the data buffer. There is also a fast way of sending
- terms from the driver, without going through the binary term
- format.</item>
+ <item>
+ <p>With these functions, the driver sends data back to the emulator.
+ The data is received as messages by the port owner process, see
+ <seealso marker="erlang:open_port/2">
+ <c>erlang:open_port/2</c></seealso>. The vector function and the
+ function taking a driver binary are faster, as they avoid
+ copying the data buffer. There is also a fast way of sending
+ terms from the driver, without going through the binary term
+ format.</p></item>
<tag>Failure</tag>
- <item>The driver can exit and signal errors up to Erlang. This is
- only for severe errors, when the driver can't possibly keep
- open.</item>
+ <item>
+ <p>The driver can exit and signal errors up to Erlang. This is
+ only for severe errors, when the driver cannot possibly keep
+ open.</p>
+ </item>
<tag>Asynchronous calls</tag>
- <item>The latest Erlang versions (R7B and later) has provision for
- asynchronous function calls, using a thread pool provided by
- Erlang. There is also a select call, that can be used for
- asynchronous drivers.</item>
+ <item>
+ <p>Erlang/OTP R7B and later versions have provision for
+ asynchronous function calls, using a thread pool provided by
+ Erlang. There is also a select call, which can be used for
+ asynchronous drivers.</p>
+ </item>
<tag><marker id="multi_threading"/>Multi-threading</tag>
<item>
- <p>A POSIX thread like API for multi-threading is provided. The
- Erlang driver thread API only provide a subset of the functionality
- provided by the POSIX thread API. The subset provided is
- more or less the basic functionality needed for multi-threaded
- programming:
- </p>
- <list>
- <item><seealso marker="#ErlDrvTid">Threads</seealso></item>
- <item><seealso marker="#ErlDrvMutex">Mutexes</seealso></item>
- <item><seealso marker="#ErlDrvCond">Condition variables</seealso></item>
- <item><seealso marker="#ErlDrvRWLock">Read/Write locks</seealso></item>
- <item><seealso marker="#ErlDrvTSDKey">Thread specific data</seealso></item>
- </list>
- <p>The Erlang driver thread API can be used in conjunction with
- the POSIX thread API on UN-ices and with the Windows native thread
- API on Windows. The Erlang driver thread API has the advantage of
- being portable, but there might exist situations where you want to
- use functionality from the POSIX thread API or the Windows
- native thread API.
- </p>
- <p>The Erlang driver thread API only returns error codes when it is
- reasonable to recover from an error condition. If it isn't reasonable
- to recover from an error condition, the whole runtime system is
- terminated. For example, if a create mutex operation fails, an error
- code is returned, but if a lock operation on a mutex fails, the
- whole runtime system is terminated.
- </p>
- <p>Note that there exists no "condition variable wait with timeout" in
- the Erlang driver thread API. This is due to issues with
- <c>pthread_cond_timedwait()</c>. When the system clock suddenly
- is changed, it isn't always guaranteed that you will wake up from
- the call as expected. An Erlang runtime system has to be able to
- cope with sudden changes of the system clock. Therefore, we have
- omitted it from the Erlang driver thread API. In the Erlang driver
- case, timeouts can and should be handled with the timer functionality
- of the Erlang driver API.
- </p>
- <p>In order for the Erlang driver thread API to function, thread
- support has to be enabled in the runtime system. An Erlang driver
- can check if thread support is enabled by use of
- <seealso marker="#driver_system_info">driver_system_info()</seealso>.
- Note that some functions in the Erlang driver API are thread-safe
- only when the runtime system has SMP support, also this
- information can be retrieved via
- <seealso marker="#driver_system_info">driver_system_info()</seealso>.
- Also note that a lot of functions in the Erlang driver API are
- <em>not</em> thread-safe regardless of whether SMP support is
- enabled or not. If a function isn't documented as thread-safe it
- is <em>not</em> thread-safe.
- </p>
- <p><em>NOTE</em>: When executing in an emulator thread, it is
- <em>very important</em> that you unlock <em>all</em> locks you
- have locked before letting the thread out of your control;
- otherwise, you are <em>very likely</em> to deadlock the whole
- emulator. If you need to use thread specific data in an emulator
- thread, only have the thread specific data set while the thread is
- under your control, and clear the thread specific data before
- you let the thread out of your control.
- </p>
- <p>In the future there will probably be debug functionality
- integrated with the Erlang driver thread API. All functions
- that create entities take a <c>name</c> argument. Currently
- the <c>name</c> argument is unused, but it will be used when
- the debug functionality has been implemented. If you name all
- entities created well, the debug functionality will be able
- to give you better error reports.
- </p>
+ <p>A POSIX thread like API for multi-threading is provided. The
+ Erlang driver thread API only provides a subset of the functionality
+ provided by the POSIX thread API. The subset provided is
+ more or less the basic functionality needed for multi-threaded
+ programming:</p>
+ <list>
+ <item><seealso marker="#ErlDrvTid">Threads</seealso></item>
+ <item><seealso marker="#ErlDrvMutex">Mutexes</seealso></item>
+ <item><seealso marker="#ErlDrvCond">
+ Condition variables</seealso></item>
+ <item><seealso marker="#ErlDrvRWLock">
+ Read/write locks</seealso></item>
+ <item><seealso marker="#ErlDrvTSDKey">
+ Thread-specific data</seealso></item>
+ </list>
+ <p>The Erlang driver thread API can be used in conjunction with
+ the POSIX thread API on UN-ices and with the Windows native thread
+ API on Windows. The Erlang driver thread API has the advantage of
+ being portable, but there can exist situations where you want to
+ use functionality from the POSIX thread API or the Windows
+ native thread API.</p>
+ <p>The Erlang driver thread API only returns error codes when it is
+ reasonable to recover from an error condition. If it is not reasonable
+ to recover from an error condition, the whole runtime system is
+ terminated. For example, if a create mutex operation fails, an error
+ code is returned, but if a lock operation on a mutex fails, the
+ whole runtime system is terminated.</p>
+ <p>Notice that there is no "condition variable wait with time-out" in
+ the Erlang driver thread API. This because of issues with
+ <c>pthread_cond_timedwait</c>. When the system clock suddenly
+ is changed, it is not always guaranteed that you will wake up from
+ the call as expected. An Erlang runtime system must be able to
+ cope with sudden changes of the system clock. Therefore, we have
+ omitted it from the Erlang driver thread API. In the Erlang driver
+ case, time-outs can and are to be handled with the timer functionality
+ of the Erlang driver API.</p>
+ <p>In order for the Erlang driver thread API to function, thread
+ support must be enabled in the runtime system. An Erlang driver
+ can check if thread support is enabled by use of
+ <seealso marker="#driver_system_info">
+ <c>driver_system_info</c></seealso>.
+ Notice that some functions in the Erlang driver API are thread-safe
+ only when the runtime system has SMP support, also this
+ information can be retrieved through
+ <seealso marker="#driver_system_info">
+ <c>driver_system_info</c></seealso>.
+ Also notice that many functions in the Erlang driver API are
+ <em>not</em> thread-safe, regardless of whether SMP support is
+ enabled or not. If a function is not documented as thread-safe, it
+ is <em>not</em> thread-safe.</p>
+ <note>
+ <p>When executing in an emulator thread, it is
+ <em>very important</em> that you unlock <em>all</em> locks you
+ have locked before letting the thread out of your control;
+ otherwise you are <em>very likely</em> to deadlock the whole
+ emulator.</p>
+ <p>If you need to use thread-specific data in an emulator
+ thread, only have the thread-specific data set while the thread is
+ under your control, and clear the thread-specific data before
+ you let the thread out of your control.</p>
+ </note>
+ <p>In the future, debug functionality will probably be
+ integrated with the Erlang driver thread API. All functions
+ that create entities take a <c>name</c> argument. Currently
+ the <c>name</c> argument is unused, but it will be used when
+ the debug functionality is implemented. If you name all
+ entities created well, the debug functionality will be able
+ to give you better error reports.</p>
+ </item>
+ <tag>Adding/removing drivers</tag>
+ <item>
+ <p>A driver can add and later remove drivers.</p>
</item>
- <tag>Adding / removing drivers</tag>
- <item><p>A driver can add and later remove drivers.</p></item>
<tag>Monitoring processes</tag>
- <item><p>A driver can monitor a process that does not own a port.</p></item>
+ <item>
+ <p>A driver can monitor a process that does not own a port.</p>
+ </item>
<tag><marker id="version_management"/>Version management</tag>
<item>
<p>Version management is enabled for drivers that have set the
- <seealso marker="driver_entry#extended_marker">extended_marker</seealso>
- field of their
- <seealso marker="driver_entry">driver_entry</seealso>
- to <c>ERL_DRV_EXTENDED_MARKER</c>. <c>erl_driver.h</c> defines
- <c>ERL_DRV_EXTENDED_MARKER</c>,
- <c>ERL_DRV_EXTENDED_MAJOR_VERSION</c>, and
- <c>ERL_DRV_EXTENDED_MINOR_VERSION</c>.
- <c>ERL_DRV_EXTENDED_MAJOR_VERSION</c> will be incremented when
- driver incompatible changes are made to the Erlang runtime
- system. Normally it will suffice to recompile drivers when the
- <c>ERL_DRV_EXTENDED_MAJOR_VERSION</c> has changed, but it
- could, under rare circumstances, mean that drivers have to
- be slightly modified. If so, this will of course be documented.
- <c>ERL_DRV_EXTENDED_MINOR_VERSION</c> will be incremented when
- new features are added. The runtime system uses the minor version
- of the driver to determine what features to use.
- The runtime system will normally refuse to load a driver if the major
+ <seealso marker="driver_entry#extended_marker">
+ <c>extended_marker</c></seealso> field of their
+ <seealso marker="driver_entry"><c>driver_entry</c></seealso>
+ to <c>ERL_DRV_EXTENDED_MARKER</c>. <c>erl_driver.h</c> defines:</p>
+ <list type="bulleted">
+ <item>
+ <p><c>ERL_DRV_EXTENDED_MARKER</c></p>
+ </item>
+ <item>
+ <p><c>ERL_DRV_EXTENDED_MAJOR_VERSION</c>, which is incremented when
+ driver incompatible changes are made to the Erlang runtime
+ system. Normally it suffices to recompile drivers when
+ <c>ERL_DRV_EXTENDED_MAJOR_VERSION</c> has changed, but it
+ can, under rare circumstances, mean that drivers must
+ be slightly modified. If so, this will of course be
+ documented.</p>
+ </item>
+ <item>
+ <p><c>ERL_DRV_EXTENDED_MINOR_VERSION</c>, which is incremented when
+ new features are added. The runtime system uses the minor version
+ of the driver to determine what features to use.</p>
+ </item>
+ </list>
+ <p>The runtime system normally refuses to load a driver if the major
versions differ, or if the major versions are equal and the
minor version used by the driver is greater than the one used
by the runtime system. Old drivers with lower major versions
- will however be allowed after a bump of the major version during
- a transition period of two major releases. Such old drivers might
- however fail if deprecated features are used.</p>
- <p>The emulator will refuse to load a driver that does not use
- the extended driver interface,
- to allow for 64-bit capable drivers,
- since incompatible type changes for the callbacks
- <seealso marker="driver_entry#output">output</seealso>,
- <seealso marker="driver_entry#control">control</seealso> and
- <seealso marker="driver_entry#call">call</seealso>
- were introduced in release R15B. A driver written
- with the old types would compile with warnings and when
- called return garbage sizes to the emulator causing it
- to read random memory and create huge incorrect result blobs.</p>
- <p>Therefore it is not enough to just recompile drivers written with
- version management for pre-R15B types; the types have to be changed
- in the driver suggesting other rewrites especially regarding
- size variables. Investigate all warnings when recompiling!</p>
- <p>Also, the API driver functions <c>driver_output*</c>,
- <c>driver_vec_to_buf</c>, <c>driver_alloc/realloc*</c>
- and the <c>driver_*</c> queue functions were changed to have
- larger length arguments and return values. This is a
- lesser problem since code that passes smaller types
- will get them auto converted in the calls and as long as
- the driver does not handle sizes that overflow an <c>int</c>
- all will work as before.</p>
+ are however allowed after a bump of the major version during
+ a transition period of two major releases. Such old drivers can,
+ however, fail if deprecated features are used.</p>
+ <p>The emulator refuses to load a driver that does not use
+ the extended driver interface, to allow for 64-bit capable drivers,
+ as incompatible type changes for the callbacks
+ <seealso marker="driver_entry#output"><c>output</c></seealso>,
+ <seealso marker="driver_entry#control"><c>control</c></seealso>, and
+ <seealso marker="driver_entry#call"><c>call</c></seealso>
+ were introduced in Erlang/OTP R15B. A driver written
+ with the old types would compile with warnings and when
+ called return garbage sizes to the emulator, causing it
+ to read random memory and create huge incorrect result blobs.</p>
+ <p>Therefore it is not enough to only recompile drivers written with
+ version management for pre R15B types; the types must be changed
+ in the driver suggesting other rewrites, especially regarding size
+ variables. <em>Investigate all warnings when recompiling.</em></p>
+ <p>Also, the API driver functions <c>driver_output*</c> and
+ <c>driver_vec_to_buf</c>, <c>driver_alloc/realloc*</c>, and the
+ <c>driver_*</c> queue functions were changed to have
+ larger length arguments and return values. This is a
+ lesser problem, as code that passes smaller types
+ gets them auto-converted in the calls, and as long as
+ the driver does not handle sizes that overflow an <c>int</c>,
+ all will work as before.</p>
</item>
- <tag><marker id="time_measurement"/>Time Measurement</tag>
- <item><p>Support for time measurement in drivers:
- <list>
- <item><seealso marker="#ErlDrvTime"><c>ErlDrvTime</c></seealso></item>
- <item><seealso marker="#ErlDrvTimeUnit"><c>ErlDrvTimeUnit</c></seealso></item>
- <item><seealso marker="#erl_drv_monotonic_time"><c>erl_drv_monotonic_time()</c></seealso></item>
- <item><seealso marker="#erl_drv_time_offset"><c>erl_drv_time_offset()</c></seealso></item>
- <item><seealso marker="#erl_drv_convert_time_unit"><c>erl_drv_convert_time_unit()</c></seealso></item>
- </list></p>
+ <tag><marker id="time_measurement"/>Time measurement</tag>
+ <item>
+ <p>Support for time measurement in drivers:</p>
+ <list type="bulleted">
+ <item><seealso marker="#ErlDrvTime">
+ <c>ErlDrvTime</c></seealso></item>
+ <item><seealso marker="#ErlDrvTimeUnit">
+ <c>ErlDrvTimeUnit</c></seealso></item>
+ <item><seealso marker="#erl_drv_monotonic_time">
+ <c>erl_drv_monotonic_time</c></seealso></item>
+ <item><seealso marker="#erl_drv_time_offset">
+ <c>erl_drv_time_offset</c></seealso></item>
+ <item><seealso marker="#erl_drv_convert_time_unit">
+ <c>erl_drv_convert_time_unit</c></seealso></item>
+ </list>
</item>
</taglist>
</section>
<section>
<marker id="rewrites_for_64_bits"/>
- <title>
- REWRITES FOR 64-BIT DRIVER INTERFACE
- </title>
- <p>
- For erts-5.9 two new integer types
- <seealso marker="#ErlDrvSizeT">ErlDrvSizeT</seealso> and
- <seealso marker="#ErlDrvSSizeT">ErlDrvSSizeT</seealso>
- were introduced that can hold 64-bit sizes if necessary.
- </p>
- <p>
- To not update a driver and just recompile it probably works
+ <title>Rewrites for 64-Bit Driver Interface</title>
+ <p>ERTS 5.9 introduced two new integer types,
+ <seealso marker="#ErlDrvSizeT"><c>ErlDrvSizeT</c></seealso> and
+ <seealso marker="#ErlDrvSSizeT"><c>ErlDrvSSizeT</c></seealso>,
+ which can hold 64-bit sizes if necessary.</p>
+
+ <p>To not update a driver and only recompile, it probably works
when building for a 32-bit machine creating a false sense of security.
Hopefully that will generate many important warnings.
- But when recompiling the same driver later on for a 64-bit machine
+ But when recompiling the same driver later on for a 64-bit machine,
there <em>will</em> be warnings and almost certainly crashes.
- So it is a BAD idea to postpone updating the driver and
- not fixing the warnings!
- </p>
- <p>
- When recompiling with <c>gcc</c> use the <c>-Wstrict-prototypes</c>
- flag to get better warnings. Try to find a similar flag if you
- are using some other compiler.
- </p>
- <p>
- Here follows a checklist for rewriting a pre erts-5.9 driver,
- most important first.
- </p>
+ So it is a <em>bad</em> idea to postpone updating the driver and
+ not fixing the warnings.</p>
+
+ <p>When recompiling with <c>gcc</c>, use flag <c>-Wstrict-prototypes</c>
+ to get better warnings. Try to find a similar flag if you use
+ another compiler.</p>
+
+ <p>The following is a checklist for rewriting a pre ERTS 5.9 driver,
+ most important first:</p>
+
<taglist>
<tag>Return types for driver callbacks</tag>
<item>
- <p>
- Rewrite driver callback
+ <p>Rrewrite driver callback
<seealso marker="driver_entry#control"><c>control</c></seealso>
- to use return type <c>ErlDrvSSizeT</c> instead of <c>int</c>.
- </p>
- <p>
- Rewrite driver callback
+ to use return type <c>ErlDrvSSizeT</c> instead of <c>int</c>.</p>
+ <p>Rewrite driver callback
<seealso marker="driver_entry#call"><c>call</c></seealso>
- to use return type <c>ErlDrvSSizeT</c> instead of <c>int</c>.
- </p>
+ to use return type <c>ErlDrvSSizeT</c> instead of <c>int</c>.</p>
<note>
- <p>
- These changes are essential to not crash the emulator
+ <p>These changes are essential not to crash the emulator
or worse cause malfunction.
- Without them a driver may return garbage in the high 32 bits
- to the emulator causing it to build a huge result from random
- bytes either crashing on memory allocation or succeeding with
- a random result from the driver call.
- </p>
+ Without them a driver can return garbage in the high 32 bits
+ to the emulator, causing it to build a huge result from random
+ bytes, either crashing on memory allocation or succeeding with
+ a random result from the driver call.</p>
</note>
</item>
<tag>Arguments to driver callbacks</tag>
<item>
- <p>
- Driver callback
+ <p>Driver callback
<seealso marker="driver_entry#output"><c>output</c></seealso>
now gets <c>ErlDrvSizeT</c> as 3rd argument instead
- of previously <c>int</c>.
- </p>
- <p>
- Driver callback
+ of previously <c>int</c>.</p>
+ <p>Driver callback
<seealso marker="driver_entry#control"><c>control</c></seealso>
now gets <c>ErlDrvSizeT</c> as 4th and 6th arguments instead
- of previously <c>int</c>.
- </p>
- <p>
- Driver callback
+ of previously <c>int</c>.</p>
+ <p>Driver callback
<seealso marker="driver_entry#call"><c>call</c></seealso>
now gets <c>ErlDrvSizeT</c> as 4th and 6th arguments instead
- of previously <c>int</c>.
- </p>
- <p>
- Sane compiler's calling conventions probably make these changes
+ of previously <c>int</c>.</p>
+ <p>Sane compiler's calling conventions probably make these changes
necessary only for a driver to handle data chunks that require
- 64-bit size fields (mostly larger than 2 GB since that is what
+ 64-bit size fields (mostly larger than 2 GB, as that is what
an <c>int</c> of 32 bits can hold). But it is possible to think
of non-sane calling conventions that would make the driver
- callbacks mix up the arguments causing malfunction.
- </p>
+ callbacks mix up the arguments causing malfunction.</p>
<note>
- <p>
- The argument type change is from signed to unsigned which
- may cause problems for e.g. loop termination conditions or
- error conditions if you just change the types all over the place.
- </p>
+ <p>The argument type change is from signed to unsigned. This
+ can cause problems for, for example, loop termination conditions or
+ error conditions if you only change the types all over the place.
+ </p>
</note>
</item>
<tag>Larger <c>size</c> field in <c>ErlIOVec</c></tag>
<item>
- <p>
- The <c>size</c> field in
+ <p>The <c>size</c> field in
<seealso marker="#ErlIOVec"><c>ErlIOVec</c></seealso>
has been changed to <c>ErlDrvSizeT</c> from <c>int</c>.
- Check all code that use that field.
- </p>
- <p>
- Automatic type casting probably makes these changes necessary only
- for a driver that encounters sizes larger than 32 bits.
- </p>
+ Check all code that use that field.</p>
+ <p>Automatic type-casting probably makes these changes necessary only
+ for a driver that encounters sizes &gt; 32 bits.</p>
<note>
- <p>
- The <c>size</c> field changed from signed to unsigned which
- may cause problems for e.g. loop termination conditions or
- error conditions if you just change the types all over the place.
- </p>
+ <p>The <c>size</c> field changed from signed to unsigned. This
+ can cause problems for, for example, loop termination conditions or
+ error conditions if you only change the types all over the place.
+ </p>
</note>
</item>
<tag>Arguments and return values in the driver API</tag>
<item>
- <p>
- Many driver API functions have changed argument type
+ <p>Many driver API functions have changed argument type
and/or return value to <c>ErlDrvSizeT</c> from mostly <c>int</c>.
- Automatic type casting probably makes these changes necessary only
- for a driver that encounters sizes larger than 32 bits.
- </p>
+ Automatic type-casting probably makes these changes necessary only
+ for a driver that encounters sizes &gt; 32 bits.</p>
<taglist>
- <tag><seealso marker="#driver_output">driver_output</seealso></tag>
+ <tag><seealso marker="#driver_output">
+ <c>driver_output</c></seealso></tag>
<item>3rd argument</item>
- <tag><seealso marker="#driver_output2">driver_output2</seealso></tag>
+ <tag><seealso marker="#driver_output2">
+ <c>driver_output2</c></seealso></tag>
<item>3rd and 5th arguments</item>
- <tag>
- <seealso marker="#driver_output_binary">driver_output_binary</seealso>
- </tag>
- <item>3rd 5th and 6th arguments</item>
- <tag><seealso marker="#driver_outputv">driver_outputv</seealso></tag>
+ <tag><seealso marker="#driver_output_binary">
+ <c>driver_output_binary</c></seealso></tag>
+ <item>3rd, 5th, and 6th arguments</item>
+ <tag><seealso marker="#driver_outputv">
+ <c>driver_outputv</c></seealso></tag>
<item>3rd and 5th arguments</item>
- <tag>
- <seealso marker="#driver_vec_to_buf">driver_vec_to_buf</seealso>
- </tag>
+ <tag><seealso marker="#driver_vec_to_buf">
+ <c>driver_vec_to_buf</c></seealso></tag>
<item>3rd argument and return value</item>
- <tag><seealso marker="#driver_alloc">driver_alloc</seealso></tag>
+ <tag><seealso marker="#driver_alloc">
+ <c>driver_alloc</c></seealso></tag>
<item>1st argument</item>
- <tag><seealso marker="#driver_realloc">driver_realloc</seealso></tag>
+ <tag><seealso marker="#driver_realloc">
+ <c>driver_realloc</c></seealso></tag>
<item>2nd argument</item>
- <tag>
- <seealso marker="#driver_alloc_binary">driver_alloc_binary</seealso>
- </tag>
+ <tag><seealso marker="#driver_alloc_binary">
+ <c>driver_alloc_binary</c></seealso></tag>
<item>1st argument</item>
- <tag>
- <seealso marker="#driver_realloc_binary">driver_realloc_binary</seealso>
- </tag>
+ <tag><seealso marker="#driver_realloc_binary">
+ <c>driver_realloc_binary</c></seealso></tag>
<item>2nd argument</item>
- <tag><seealso marker="#driver_enq">driver_enq</seealso></tag>
+ <tag><seealso marker="#driver_enq">
+ <c>driver_enq</c></seealso></tag>
<item>3rd argument</item>
- <tag><seealso marker="#driver_pushq">driver_pushq</seealso></tag>
+ <tag><seealso marker="#driver_pushq">
+ <c>driver_pushq</c></seealso></tag>
<item>3rd argument</item>
- <tag><seealso marker="#driver_deq">driver_deq</seealso></tag>
+ <tag><seealso marker="#driver_deq">
+ <c>driver_deq</c></seealso></tag>
<item>2nd argument and return value</item>
- <tag><seealso marker="#driver_sizeq">driver_sizeq</seealso></tag>
- <item>return value</item>
- <tag><seealso marker="#driver_enq_bin">driver_enq_bin</seealso></tag>
- <item>3rd and 4th argument</item>
- <tag><seealso marker="#driver_pushq_bin">driver_pushq_bin</seealso></tag>
- <item>3rd and 4th argument</item>
- <tag><seealso marker="#driver_enqv">driver_enqv</seealso></tag>
+ <tag><seealso marker="#driver_sizeq">
+ <c>driver_sizeq</c></seealso></tag>
+ <item>Return value</item>
+ <tag><seealso marker="#driver_enq_bin">
+ <c>driver_enq_bin</c></seealso></tag>
+ <item>3rd and 4th arguments</item>
+ <tag><seealso marker="#driver_pushq_bin">
+ <c>driver_pushq_bin</c></seealso></tag>
+ <item>3rd and 4th arguments</item>
+ <tag><seealso marker="#driver_enqv">
+ <c>driver_enqv</c></seealso></tag>
<item>3rd argument</item>
- <tag><seealso marker="#driver_pushqv">driver_pushqv</seealso></tag>
+ <tag><seealso marker="#driver_pushqv">
+ <c>driver_pushqv</c></seealso></tag>
<item>3rd argument</item>
- <tag><seealso marker="#driver_peekqv">driver_peekqv</seealso></tag>
- <item>return value</item>
+ <tag><seealso marker="#driver_peekqv">
+ <c>driver_peekqv</c></seealso></tag>
+ <item>Return value</item>
</taglist>
<note>
- <p>
- This is a change from signed to unsigned which
- may cause problems for e.g. loop termination conditions and
- error conditions if you just change the types all over the place.
- </p>
+ <p>This is a change from signed to unsigned. This can cause
+ problems for, for example, loop termination conditions and error
+ conditions if you only change the types all over the place.</p>
</note>
</item>
</taglist>
</section>
<section>
- <title>DATA TYPES</title>
-
+ <title>Data Types</title>
<taglist>
- <tag><marker id="ErlDrvSizeT"/>ErlDrvSizeT</tag>
- <item><p>An unsigned integer type to be used as <c>size_t</c></p></item>
- <tag><marker id="ErlDrvSSizeT"/>ErlDrvSSizeT</tag>
- <item><p>A signed integer type the size of <c>ErlDrvSizeT</c></p></item>
- <tag><marker id="ErlDrvSysInfo"/>ErlDrvSysInfo</tag>
+ <tag><marker id="ErlDrvSizeT"/><c>ErlDrvSizeT</c></tag>
+ <item>
+ <p>An unsigned integer type to be used as <c>size_t</c>.</p>
+ </item>
+ <tag><marker id="ErlDrvSSizeT"/><c>ErlDrvSSizeT</c></tag>
<item>
- <p/>
- <code type="none">
+ <p>A signed integer type, the size of <c>ErlDrvSizeT</c>.</p>
+ </item>
+ <tag><marker id="ErlDrvSysInfo"/><c>ErlDrvSysInfo</c></tag>
+ <item>
+ <code type="none">
typedef struct ErlDrvSysInfo {
int driver_major_version;
int driver_minor_version;
@@ -558,2565 +583,2633 @@ typedef struct ErlDrvSysInfo {
int nif_major_version;
int nif_minor_version;
int dirty_scheduler_support;
-} ErlDrvSysInfo;
- </code>
-
- <p>
- The <c>ErlDrvSysInfo</c> structure is used for storage of
- information about the Erlang runtime system.
- <seealso marker="#driver_system_info">driver_system_info()</seealso>
- will write the system information when passed a reference to
- a <c>ErlDrvSysInfo</c> structure. A description of the
- fields in the structure follows:
- </p>
- <taglist>
- <tag><c>driver_major_version</c></tag>
- <item>The value of
- <seealso marker="#version_management">ERL_DRV_EXTENDED_MAJOR_VERSION</seealso>
- when the runtime system was compiled. This value is the same
- as the value of
- <seealso marker="#version_management">ERL_DRV_EXTENDED_MAJOR_VERSION</seealso>
- used when compiling the driver; otherwise, the runtime system
- would have refused to load the driver.
- </item>
- <tag><c>driver_minor_version</c></tag>
- <item>The value of
- <seealso marker="#version_management">ERL_DRV_EXTENDED_MINOR_VERSION</seealso>
- when the runtime system was compiled. This value might differ
- from the value of
- <seealso marker="#version_management">ERL_DRV_EXTENDED_MINOR_VERSION</seealso>
- used when compiling the driver.
- </item>
- <tag><c>erts_version</c></tag>
- <item>A string containing the version number of the runtime system
- (the same as returned by
- <seealso marker="erlang#system_info_version">erlang:system_info(version)</seealso>).
- </item>
- <tag><c>otp_release</c></tag>
- <item>A string containing the OTP release number
- (the same as returned by
- <seealso marker="erlang#system_info_otp_release">erlang:system_info(otp_release)</seealso>).
- </item>
- <tag><c>thread_support</c></tag>
- <item>A value <c>!= 0</c> if the runtime system has thread support;
- otherwise, <c>0</c>.
- </item>
- <tag><c>smp_support</c></tag>
- <item>A value <c>!= 0</c> if the runtime system has SMP support;
- otherwise, <c>0</c>.
- </item>
- <tag><c>async_threads</c></tag>
- <item>The number of async threads in the async thread pool used
- by <seealso marker="#driver_async">driver_async()</seealso>
- (the same as returned by
- <seealso marker="erlang#system_info_thread_pool_size">erlang:system_info(thread_pool_size)</seealso>).
- </item>
- <tag><c>scheduler_threads</c></tag>
- <item>The number of scheduler threads used by the runtime system
- (the same as returned by
- <seealso marker="erlang#system_info_schedulers">erlang:system_info(schedulers)</seealso>).
- </item>
- <tag><c>nif_major_version</c></tag>
- <item>The value of <c>ERL_NIF_MAJOR_VERSION</c> when the runtime system was compiled.
- </item>
- <tag><c>nif_minor_version</c></tag>
- <item>The value of <c>ERL_NIF_MINOR_VERSION</c> when the runtime system was compiled.
- </item>
- <tag><c>dirty_scheduler_support</c></tag>
- <item>A value <c>!= 0</c> if the runtime system has support for dirty scheduler threads;
- otherwise <c>0</c>.
- </item>
+} ErlDrvSysInfo;</code>
+ <p>The <c>ErlDrvSysInfo</c> structure is used for storage of
+ information about the Erlang runtime system.
+ <seealso marker="#driver_system_info">
+ <c>driver_system_info</c></seealso>
+ writes the system information when passed a reference to
+ a <c>ErlDrvSysInfo</c> structure. The fields in the structure
+ are as follows:</p>
+ <taglist>
+ <tag><c>driver_major_version</c></tag>
+ <item>
+ <p>The value of <seealso marker="#version_management">
+ <c>ERL_DRV_EXTENDED_MAJOR_VERSION</c></seealso>
+ when the runtime system was compiled. This value is the same
+ as the value of <seealso marker="#version_management">
+ <c>ERL_DRV_EXTENDED_MAJOR_VERSION</c></seealso>
+ used when compiling the driver; otherwise the runtime system
+ would have refused to load the driver.</p>
+ </item>
+ <tag><c>driver_minor_version</c></tag>
+ <item>
+ <p>The value of <seealso marker="#version_management">
+ <c>ERL_DRV_EXTENDED_MINOR_VERSION</c></seealso>
+ when the runtime system was compiled. This value can differ
+ from the value of <seealso marker="#version_management">
+ <c>ERL_DRV_EXTENDED_MINOR_VERSION</c></seealso>
+ used when compiling the driver.</p>
+ </item>
+ <tag><c>erts_version</c></tag>
+ <item>
+ <p>A string containing the version number of the runtime system
+ (the same as returned by
+ <seealso marker="erlang#system_info_version">
+ <c>erlang:system_info(version)</c></seealso>).</p>
+ </item>
+ <tag><c>otp_release</c></tag>
+ <item>
+ <p>A string containing the OTP release number
+ (the same as returned by
+ <seealso marker="erlang#system_info_otp_release">
+ <c>erlang:system_info(otp_release)</c></seealso>).</p>
+ </item>
+ <tag><c>thread_support</c></tag>
+ <item>
+ <p>A value <c>!= 0</c> if the runtime system has thread support;
+ otherwise <c>0</c>.</p>
+ </item>
+ <tag><c>smp_support</c></tag>
+ <item>
+ <p>A value <c>!= 0</c> if the runtime system has SMP support;
+ otherwise <c>0</c>.</p>
+ </item>
+ <tag><c>async_threads</c></tag>
+ <item>
+ <p>The number of async threads in the async thread pool used by
+ <seealso marker="#driver_async"><c>driver_async</c></seealso>
+ (the same as returned by
+ <seealso marker="erlang#system_info_thread_pool_size">
+ <c>erlang:system_info(thread_pool_size)</c></seealso>).</p>
+ </item>
+ <tag><c>scheduler_threads</c></tag>
+ <item>
+ <p>The number of scheduler threads used by the runtime system
+ (the same as returned by
+ <seealso marker="erlang#system_info_schedulers">
+ <c>erlang:system_info(schedulers)</c></seealso>).</p>
+ </item>
+ <tag><c>nif_major_version</c></tag>
+ <item>
+ <p>The value of <c>ERL_NIF_MAJOR_VERSION</c> when the runtime
+ system was compiled.</p>
+ </item>
+ <tag><c>nif_minor_version</c></tag>
+ <item>
+ <p>The value of <c>ERL_NIF_MINOR_VERSION</c> when the runtime
+ system was compiled.</p>
+ </item>
+ <tag><c>dirty_scheduler_support</c></tag>
+ <item>
+ <p>A value <c>!= 0</c> if the runtime system has support for dirty
+ scheduler threads; otherwise <c>0</c>.</p>
+ </item>
</taglist>
</item>
- <tag><marker id="ErlDrvBinary"/>ErlDrvBinary</tag>
+ <tag><marker id="ErlDrvBinary"/><c>ErlDrvBinary</c></tag>
<item>
- <p/>
- <code type="none">
+ <code type="none">
typedef struct ErlDrvBinary {
ErlDrvSint orig_size;
char orig_bytes[];
-} ErlDrvBinary;
-</code>
+} ErlDrvBinary;</code>
<p>The <c>ErlDrvBinary</c> structure is a binary, as sent
between the emulator and the driver. All binaries are
reference counted; when <c>driver_binary_free</c> is called,
the reference count is decremented, when it reaches zero,
- the binary is deallocated. The <c>orig_size</c> is the size
- of the binary, and <c>orig_bytes</c> is the buffer. The
- <c>ErlDrvBinary</c> does not have a fixed size, its size is
+ the binary is deallocated. <c>orig_size</c> is the binary size
+ and <c>orig_bytes</c> is the buffer.
+ <c>ErlDrvBinary</c> has not a fixed size, its size is
<c>orig_size + 2 * sizeof(int)</c>.</p>
<note>
<p>The <c>refc</c> field has been removed. The reference count of
an <c>ErlDrvBinary</c> is now stored elsewhere. The
- reference count of an <c>ErlDrvBinary</c> can be accessed via
- <seealso marker="#driver_binary_get_refc">driver_binary_get_refc()</seealso>,
- <seealso marker="#driver_binary_inc_refc">driver_binary_inc_refc()</seealso>,
- and
- <seealso marker="#driver_binary_dec_refc">driver_binary_dec_refc()</seealso>.</p>
+ reference count of an <c>ErlDrvBinary</c> can be accessed through
+ <seealso marker="#driver_binary_get_refc">
+ <c>driver_binary_get_refc</c></seealso>,
+ <seealso marker="#driver_binary_inc_refc">
+ <c>driver_binary_inc_refc</c></seealso>, and
+ <seealso marker="#driver_binary_dec_refc">
+ <c>driver_binary_dec_refc</c></seealso>.</p>
</note>
<p>Some driver calls, such as <c>driver_enq_binary</c>,
increment the driver reference count, and others, such as
<c>driver_deq</c> decrement it.</p>
- <p>Using a driver binary instead of a normal buffer, is often
- faster, since the emulator doesn't need to copy the data,
+ <p>Using a driver binary instead of a normal buffer is often
+ faster, as the emulator needs not to copy the data,
only the pointer is used.</p>
<p>A driver binary allocated in the driver, with
- <c>driver_alloc_binary</c>, should be freed in the driver (unless otherwise stated),
- with <c>driver_free_binary</c>. (Note that this doesn't
+ <c>driver_alloc_binary</c>, is to be freed in the driver
+ (unless otherwise stated)
+ with <c>driver_free_binary</c>. (Notice that this does not
necessarily deallocate it, if the driver is still referred
in the emulator, the ref-count will not go to zero.)</p>
<p>Driver binaries are used in the <c>driver_output2</c> and
<c>driver_outputv</c> calls, and in the queue. Also the
- driver call-back <seealso marker="driver_entry#outputv">outputv</seealso> uses driver
- binaries.</p>
- <p>If the driver for some reason or another, wants to keep a
- driver binary around, in a static variable for instance, the
- reference count should be incremented,
- and the binary can later be freed in the <seealso marker="driver_entry#stop">stop</seealso> call-back, with
- <c>driver_free_binary</c>.</p>
- <p>Note that since a driver binary is shared by the driver and
- the emulator, a binary received from the emulator or sent to
- the emulator, must not be changed by the driver.</p>
- <p>Since erts version 5.5 (OTP release R11B), orig_bytes is
+ driver callback <seealso marker="driver_entry#outputv">
+ <c>outputv</c></seealso> uses driver binaries.</p>
+ <p>If the driver for some reason wants to keep a
+ driver binary around, for example in a static variable, the
+ reference count is to be incremented, and the binary can later
+ be freed in the <seealso marker="driver_entry#stop">
+ <c>stop</c></seealso> callback, with <c>driver_free_binary</c>.</p>
+ <p>Notice that as a driver binary is shared by the driver and
+ the emulator. A binary received from the emulator or sent to
+ the emulator must not be changed by the driver.</p>
+ <p>Since ERTS 5.5 (Erlang/OTP R11B), <c>orig_bytes</c> is
guaranteed to be properly aligned for storage of an array of
doubles (usually 8-byte aligned).</p>
</item>
- <tag>ErlDrvData</tag>
+ <tag><c>ErlDrvData</c></tag>
<item>
- <p>The <c>ErlDrvData</c> is a handle to driver-specific data,
- passed to the driver call-backs. It is a pointer, and is
+ <p>A handle to driver-specific data,
+ passed to the driver callbacks. It is a pointer, and is
most often type cast to a specific pointer in the driver.</p>
</item>
- <tag>SysIOVec</tag>
+ <tag><c>SysIOVec</c></tag>
<item>
- <p>This is a system I/O vector, as used by <c>writev</c> on
- unix and <c>WSASend</c> on Win32. It is used in
+ <p>A system I/O vector, as used by <c>writev</c> on
+ Unix and <c>WSASend</c> on Win32. It is used in
<c>ErlIOVec</c>.</p>
</item>
- <tag><marker id="ErlIOVec"/>ErlIOVec</tag>
+ <tag><marker id="ErlIOVec"/><c>ErlIOVec</c></tag>
<item>
- <p/>
- <code type="none">
+ <code type="none">
typedef struct ErlIOVec {
int vsize;
ErlDrvSizeT size;
SysIOVec* iov;
ErlDrvBinary** binv;
-} ErlIOVec;
-</code>
- <p>The I/O vector used by the emulator and drivers, is a list
+} ErlIOVec;</code>
+ <p>The I/O vector used by the emulator and drivers is a list
of binaries, with a <c>SysIOVec</c> pointing to the buffers
of the binaries. It is used in <c>driver_outputv</c> and the
- <seealso marker="driver_entry#outputv">outputv</seealso>
- driver call-back. Also, the driver queue is an
+ <seealso marker="driver_entry#outputv"><c>outputv</c></seealso>
+ driver callback. Also, the driver queue is an
<c>ErlIOVec</c>.</p>
</item>
-
- <tag>ErlDrvMonitor</tag>
+ <tag><c>ErlDrvMonitor</c></tag>
<item>
<p>When a driver creates a monitor for a process, a
<c>ErlDrvMonitor</c> is filled in. This is an opaque
- data-type which can be assigned to but not compared without
- using the supplied compare function (i.e. it behaves like a struct).</p>
- <p>The driver writer should provide the memory for storing the
- monitor when calling <seealso marker="#driver_monitor_process">driver_monitor_process</seealso>. The
+ data type that can be assigned to, but not compared without
+ using the supplied compare function (that is, it behaves like
+ a struct).</p>
+ <p>The driver writer is to provide the memory for storing the
+ monitor when calling <seealso marker="#driver_monitor_process">
+ <c>driver_monitor_process</c></seealso>. The
address of the data is not stored outside of the driver, so
- the <c>ErlDrvMonitor</c> can be used as any other datum, it
- can be copied, moved in memory, forgotten etc.</p>
+ <c>ErlDrvMonitor</c> can be used as any other data, it
+ can be copied, moved in memory, forgotten, and so on.</p>
</item>
- <tag><marker id="ErlDrvNowData"/>ErlDrvNowData</tag>
+ <tag><marker id="ErlDrvNowData"/><c>ErlDrvNowData</c></tag>
<item>
- <p>The <c>ErlDrvNowData</c> structure holds a timestamp
+ <p>The <c>ErlDrvNowData</c> structure holds a time stamp
consisting of three values measured from some arbitrary
point in the past. The three structure members are:</p>
<taglist>
- <tag>megasecs</tag>
+ <tag><c>megasecs</c></tag>
<item>The number of whole megaseconds elapsed since the arbitrary
- point in time</item>
- <tag>secs</tag>
+ point in time</item>
+ <tag><c>secs</c></tag>
<item>The number of whole seconds elapsed since the arbitrary
- point in time</item>
- <tag>microsecs</tag>
+ point in time</item>
+ <tag><c>microsecs</c></tag>
<item>The number of whole microseconds elapsed since the arbitrary
- point in time</item>
+ point in time</item>
</taglist>
</item>
- <tag><marker id="ErlDrvPDL"/>ErlDrvPDL</tag>
+ <tag><marker id="ErlDrvPDL"/><c>ErlDrvPDL</c></tag>
<item>
- <p>If certain port specific data have to be accessed from other
- threads than those calling the driver call-backs, a port data lock
- can be used in order to synchronize the operations on the data.
- Currently, the only port specific data that the emulator
+ <p>If certain port-specific data must be accessed from other
+ threads than those calling the driver callbacks, a port data lock
+ can be used to synchronize the operations on the data.
+ Currently, the only port-specific data that the emulator
associates with the port data lock is the driver queue.</p>
- <p>Normally a driver instance does not have a port data lock. If
- the driver instance wants to use a port data lock, it has to
+ <p>Normally a driver instance has no port data lock. If
+ the driver instance wants to use a port data lock, it must
create the port data lock by calling
- <seealso marker="#driver_pdl_create">driver_pdl_create()</seealso>.
- <em>NOTE</em>: Once the port data lock has been created, every
- access to data associated with the port data lock has to be done
- while having the port data lock locked. The port data lock is
- locked, and unlocked, respectively, by use of
- <seealso marker="#driver_pdl_lock">driver_pdl_lock()</seealso>, and
- <seealso marker="#driver_pdl_unlock">driver_pdl_unlock()</seealso>.</p>
+ <seealso marker="#driver_pdl_create">
+ <c>driver_pdl_create</c></seealso>.</p>
+ <note>
+ <p>Once the port data lock has been created, every
+ access to data associated with the port data lock must be done
+ while the port data lock is locked. The port data lock is
+ locked and unlocked by
+ <seealso marker="#driver_pdl_lock">
+ <c>driver_pdl_lock</c></seealso>, and
+ <seealso marker="#driver_pdl_unlock">
+ <c>driver_pdl_unlock</c></seealso>, respectively.</p>
+ </note>
<p>A port data lock is reference counted, and when the reference
- count reaches zero, it will be destroyed. The emulator will at
- least increment the reference count once when the lock is
- created and decrement it once when the port associated with
- the lock terminates. The emulator will also increment the
- reference count when an async job is enqueued and decrement
- it after an async job has been invoked. Besides
- this, it is the responsibility of the driver to ensure that
+ count reaches zero, it is destroyed. The emulator at
+ least increments the reference count once when the lock is
+ created and decrements it once the port associated with
+ the lock terminates. The emulator also increments the
+ reference count when an async job is enqueued and decrements
+ it when an async job has been invoked.
+ Also, the driver is responsible for ensuring that
the reference count does not reach zero before the last use
of the lock by the driver has been made. The reference count
- can be read, incremented, and decremented, respectively, by
- use of
- <seealso marker="#driver_pdl_get_refc">driver_pdl_get_refc()</seealso>,
- <seealso marker="#driver_pdl_inc_refc">driver_pdl_inc_refc()</seealso>, and
- <seealso marker="#driver_pdl_dec_refc">driver_pdl_dec_refc()</seealso>.</p>
+ can be read, incremented, and decremented by
+ <seealso marker="#driver_pdl_get_refc">
+ <c>driver_pdl_get_refc</c></seealso>,
+ <seealso marker="#driver_pdl_inc_refc">
+ <c>driver_pdl_inc_refc</c></seealso>, and
+ <seealso marker="#driver_pdl_dec_refc">
+ <c>driver_pdl_dec_refc</c></seealso>, respectively.</p>
</item>
-
- <tag><marker id="ErlDrvTid"/>ErlDrvTid</tag>
+ <tag><marker id="ErlDrvTid"/><c>ErlDrvTid</c></tag>
<item>
<p>Thread identifier.</p>
- <p>See also:
- <seealso marker="#erl_drv_thread_create">erl_drv_thread_create()</seealso>,
- <seealso marker="#erl_drv_thread_exit">erl_drv_thread_exit()</seealso>,
- <seealso marker="#erl_drv_thread_join">erl_drv_thread_join()</seealso>,
- <seealso marker="#erl_drv_thread_self">erl_drv_thread_self()</seealso>,
- and
- <seealso marker="#erl_drv_equal_tids">erl_drv_equal_tids()</seealso>.
- </p>
+ <p>See also <seealso marker="#erl_drv_thread_create">
+ <c>erl_drv_thread_create</c></seealso>,
+ <seealso marker="#erl_drv_thread_exit">
+ <c>erl_drv_thread_exit</c></seealso>,
+ <seealso marker="#erl_drv_thread_join">
+ <c>erl_drv_thread_join</c></seealso>,
+ <seealso marker="#erl_drv_thread_self">
+ <c>erl_drv_thread_self</c></seealso>, and
+ <seealso marker="#erl_drv_equal_tids">
+ <c>erl_drv_equal_tids</c></seealso>.</p>
</item>
- <tag><marker id="ErlDrvThreadOpts"/>ErlDrvThreadOpts</tag>
+ <tag><marker id="ErlDrvThreadOpts"/><c>ErlDrvThreadOpts</c></tag>
<item>
- <p/>
- <code type="none">
- int suggested_stack_size;
- </code>
+ <code type="none">
+int suggested_stack_size;</code>
<p>Thread options structure passed to
- <seealso marker="#erl_drv_thread_create">erl_drv_thread_create()</seealso>.
- Currently the following fields exist:
- </p>
+ <seealso marker="#erl_drv_thread_create">
+ <c>erl_drv_thread_create</c></seealso>.
+ The following fields exists:</p>
<taglist>
- <tag>suggested_stack_size</tag>
- <item>A suggestion, in kilo-words, on how large a stack to use. A value less
- than zero means default size.
+ <tag><c>suggested_stack_size</c></tag>
+ <item>A suggestion, in kilowords, on how large a stack to use.
+ A value &lt; 0 means default size.
</item>
</taglist>
- <p>See also:
- <seealso marker="#erl_drv_thread_opts_create">erl_drv_thread_opts_create()</seealso>,
- <seealso marker="#erl_drv_thread_opts_destroy">erl_drv_thread_opts_destroy()</seealso>,
- and
- <seealso marker="#erl_drv_thread_create">erl_drv_thread_create()</seealso>.
- </p>
+ <p>See also <seealso marker="#erl_drv_thread_opts_create">
+ <c>erl_drv_thread_opts_create</c></seealso>,
+ <seealso marker="#erl_drv_thread_opts_destroy">
+ <c>erl_drv_thread_opts_destroy</c></seealso>, and
+ <seealso marker="#erl_drv_thread_create">
+ <c>erl_drv_thread_create</c></seealso>.</p>
</item>
-
- <tag><marker id="ErlDrvMutex"/>ErlDrvMutex</tag>
+ <tag><marker id="ErlDrvMutex"/><c>ErlDrvMutex</c></tag>
<item>
<p>Mutual exclusion lock. Used for synchronizing access to shared data.
- Only one thread at a time can lock a mutex.
- </p>
- <p>See also:
- <seealso marker="#erl_drv_mutex_create">erl_drv_mutex_create()</seealso>,
- <seealso marker="#erl_drv_mutex_destroy">erl_drv_mutex_destroy()</seealso>,
- <seealso marker="#erl_drv_mutex_lock">erl_drv_mutex_lock()</seealso>,
- <seealso marker="#erl_drv_mutex_trylock">erl_drv_mutex_trylock()</seealso>,
- and
- <seealso marker="#erl_drv_mutex_unlock">erl_drv_mutex_unlock()</seealso>.
- </p>
+ Only one thread at a time can lock a mutex.</p>
+ <p>See also <seealso marker="#erl_drv_mutex_create">
+ <c>erl_drv_mutex_create</c></seealso>,
+ <seealso marker="#erl_drv_mutex_destroy">
+ <c>erl_drv_mutex_destroy</c></seealso>,
+ <seealso marker="#erl_drv_mutex_lock">
+ <c>erl_drv_mutex_lock</c></seealso>,
+ <seealso marker="#erl_drv_mutex_trylock">
+ <c>erl_drv_mutex_trylock</c></seealso>, and
+ <seealso marker="#erl_drv_mutex_unlock">
+ <c>erl_drv_mutex_unlock</c></seealso>.</p>
</item>
- <tag><marker id="ErlDrvCond"/>ErlDrvCond</tag>
+ <tag><marker id="ErlDrvCond"/><c>ErlDrvCond</c></tag>
<item>
- <p>Condition variable. Used when threads need to wait for a specific
- condition to appear before continuing execution. Condition variables
- need to be used with associated mutexes.
- </p>
- <p>See also:
- <seealso marker="#erl_drv_cond_create">erl_drv_cond_create()</seealso>,
- <seealso marker="#erl_drv_cond_destroy">erl_drv_cond_destroy()</seealso>,
- <seealso marker="#erl_drv_cond_signal">erl_drv_cond_signal()</seealso>,
- <seealso marker="#erl_drv_cond_broadcast">erl_drv_cond_broadcast()</seealso>,
- and
- <seealso marker="#erl_drv_cond_wait">erl_drv_cond_wait()</seealso>.
- </p>
+ <p>Condition variable. Used when threads must wait for a specific
+ condition to appear before continuing execution. Condition variables
+ must be used with associated mutexes.</p>
+ <p>See also <seealso marker="#erl_drv_cond_create">
+ <c>erl_drv_cond_create</c></seealso>,
+ <seealso marker="#erl_drv_cond_destroy">
+ <c>erl_drv_cond_destroy</c></seealso>,
+ <seealso marker="#erl_drv_cond_signal">
+ <c>erl_drv_cond_signal</c></seealso>,
+ <seealso marker="#erl_drv_cond_broadcast">
+ <c>erl_drv_cond_broadcast</c></seealso>, and
+ <seealso marker="#erl_drv_cond_wait">
+ <c>erl_drv_cond_wait</c></seealso>.</p>
</item>
- <tag><marker id="ErlDrvRWLock"/>ErlDrvRWLock</tag>
+ <tag><marker id="ErlDrvRWLock"/><c>ErlDrvRWLock</c></tag>
<item>
<p>Read/write lock. Used to allow multiple threads to read shared data
- while only allowing one thread to write the same data. Multiple threads
- can read lock an rwlock at the same time, while only one thread can
- read/write lock an rwlock at a time.
- </p>
- <p>See also:
- <seealso marker="#erl_drv_rwlock_create">erl_drv_rwlock_create()</seealso>,
- <seealso marker="#erl_drv_rwlock_destroy">erl_drv_rwlock_destroy()</seealso>,
- <seealso marker="#erl_drv_rwlock_rlock">erl_drv_rwlock_rlock()</seealso>,
- <seealso marker="#erl_drv_rwlock_tryrlock">erl_drv_rwlock_tryrlock()</seealso>,
- <seealso marker="#erl_drv_rwlock_runlock">erl_drv_rwlock_runlock()</seealso>,
- <seealso marker="#erl_drv_rwlock_rwlock">erl_drv_rwlock_rwlock()</seealso>,
- <seealso marker="#erl_drv_rwlock_tryrwlock">erl_drv_rwlock_tryrwlock()</seealso>,
- and
- <seealso marker="#erl_drv_rwlock_rwunlock">erl_drv_rwlock_rwunlock()</seealso>.
- </p>
+ while only allowing one thread to write the same data. Multiple
+ threads can read lock an rwlock at the same time, while only
+ one thread can read/write lock an rwlock at a time.</p>
+ <p>See also <seealso marker="#erl_drv_rwlock_create">
+ <c>erl_drv_rwlock_create</c></seealso>,
+ <seealso marker="#erl_drv_rwlock_destroy">
+ <c>erl_drv_rwlock_destroy</c></seealso>,
+ <seealso marker="#erl_drv_rwlock_rlock">
+ <c>erl_drv_rwlock_rlock</c></seealso>,
+ <seealso marker="#erl_drv_rwlock_tryrlock">
+ <c>erl_drv_rwlock_tryrlock</c></seealso>,
+ <seealso marker="#erl_drv_rwlock_runlock">
+ <c>erl_drv_rwlock_runlock</c></seealso>,
+ <seealso marker="#erl_drv_rwlock_rwlock">
+ <c>erl_drv_rwlock_rwlock</c></seealso>,
+ <seealso marker="#erl_drv_rwlock_tryrwlock">
+ <c>erl_drv_rwlock_tryrwlock</c></seealso>, and
+ <seealso marker="#erl_drv_rwlock_rwunlock">
+ <c>erl_drv_rwlock_rwunlock</c></seealso>.</p>
</item>
- <tag><marker id="ErlDrvTSDKey"/>ErlDrvTSDKey</tag>
+ <tag><marker id="ErlDrvTSDKey"/><c>ErlDrvTSDKey</c></tag>
<item>
- <p>Key which thread specific data can be associated with.</p>
- <p>See also:
- <seealso marker="#erl_drv_tsd_key_create">erl_drv_tsd_key_create()</seealso>,
- <seealso marker="#erl_drv_tsd_key_destroy">erl_drv_tsd_key_destroy()</seealso>,
- <seealso marker="#erl_drv_tsd_set">erl_drv_tsd_set()</seealso>,
- and
- <seealso marker="#erl_drv_tsd_get">erl_drv_tsd_get()</seealso>.
- </p>
+ <p>Key that thread-specific data can be associated with.</p>
+ <p>See also <seealso marker="#erl_drv_tsd_key_create">
+ <c>erl_drv_tsd_key_create</c></seealso>,
+ <seealso marker="#erl_drv_tsd_key_destroy">
+ <c>erl_drv_tsd_key_destroy</c></seealso>,
+ <seealso marker="#erl_drv_tsd_set">
+ <c>erl_drv_tsd_set</c></seealso>, and
+ <seealso marker="#erl_drv_tsd_get">
+ <c>erl_drv_tsd_get</c></seealso>.</p>
</item>
- <tag><marker id="ErlDrvTime"/>ErlDrvTime</tag>
+ <tag><marker id="ErlDrvTime"/><c>ErlDrvTime</c></tag>
<item>
- <p>A signed 64-bit integer type for representation of time.</p>
+ <p>A signed 64-bit integer type for time representation.</p>
</item>
- <tag><marker id="ErlDrvTimeUnit"/>ErlDrvTimeUnit</tag>
+ <tag><marker id="ErlDrvTimeUnit"/><c>ErlDrvTimeUnit</c></tag>
<item>
<p>An enumeration of time units supported by the driver API:</p>
- <taglist>
+ <taglist>
<tag><c>ERL_DRV_SEC</c></tag>
- <item><p>Seconds</p></item>
+ <item>Seconds</item>
<tag><c>ERL_DRV_MSEC</c></tag>
- <item><p>Milliseconds</p></item>
+ <item>Milliseconds</item>
<tag><c>ERL_DRV_USEC</c></tag>
- <item><p>Microseconds</p></item>
+ <item>Microseconds</item>
<tag><c>ERL_DRV_NSEC</c></tag>
- <item><p>Nanoseconds</p></item>
- </taglist>
+ <item>Nanoseconds</item>
+ </taglist>
</item>
- </taglist>
- </section>
+ </taglist>
+ </section>
<funcs>
<func>
- <name><ret>void</ret><nametext>driver_system_info(ErlDrvSysInfo *sys_info_ptr, size_t size)</nametext></name>
- <fsummary>Get information about the Erlang runtime system</fsummary>
+ <name><ret>void</ret><nametext>add_driver_entry(ErlDrvEntry
+ *de)</nametext></name>
+ <fsummary>Add a driver entry.</fsummary>
<desc>
- <marker id="driver_system_info"></marker>
- <p>This function will write information about the Erlang runtime
- system into the
- <seealso marker="#ErlDrvSysInfo">ErlDrvSysInfo</seealso>
- structure referred to by the first argument. The second
- argument should be the size of the
- <seealso marker="#ErlDrvSysInfo">ErlDrvSysInfo</seealso>
- structure, i.e., <c>sizeof(ErlDrvSysInfo)</c>.</p>
- <p>See the documentation of the
- <seealso marker="#ErlDrvSysInfo">ErlDrvSysInfo</seealso>
- structure for information about specific fields.</p>
+ <marker id="add_driver_entry"></marker>
+ <p>Adds a driver entry to the list of drivers known by Erlang.
+ The <seealso marker="driver_entry#init"><c>init</c></seealso>
+ function of parameter <c>de</c> is called.</p>
+ <note>
+ <p>To use this function for adding drivers residing in
+ dynamically loaded code is dangerous. If the driver code
+ for the added driver resides in the same dynamically
+ loaded module (that is, <c>.so</c> file) as a normal
+ dynamically loaded driver (loaded with the <c>erl_ddll</c>
+ interface), the caller is to call
+ <seealso marker="#driver_lock_driver">
+ <c>driver_lock_driver</c></seealso> before
+ adding driver entries.</p>
+ <p><em>Use of this function is generally deprecated.</em></p>
+ </note>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_output(ErlDrvPort port, char *buf, ErlDrvSizeT len)</nametext></name>
- <fsummary>Send data from driver to port owner</fsummary>
+ <name><ret>void *</ret>
+ <nametext>driver_alloc(ErlDrvSizeT size)</nametext></name>
+ <fsummary>Allocate memory.</fsummary>
<desc>
- <marker id="driver_output"></marker>
- <p>The <c>driver_output</c> function is used to send data from
- the driver up to the emulator. The data will be received as
- terms or binary data, depending on how the driver port was
- opened.</p>
- <p>The data is queued in the port owner process' message
- queue. Note that this does not yield to the emulator. (Since
- the driver and the emulator run in the same thread.)</p>
- <p>The parameter <c>buf</c> points to the data to send, and
- <c>len</c> is the number of bytes.</p>
- <p>The return value for all output functions is 0. (Unless the
- driver is used for distribution, in which case it can fail
- and return -1. For normal use, the output function always
- returns 0.)</p>
+ <marker id="driver_alloc"></marker>
+ <p>Allocates a memory block of the size specified
+ in <c>size</c>, and returns it. This fails only on out of
+ memory, in which case <c>NULL</c> is returned. (This is most
+ often a wrapper for <c>malloc</c>).</p>
+ <p>Memory allocated must be explicitly freed with a corresponding
+ call to <seealso marker="#driver_free"><c>driver_free</c></seealso>
+ (unless otherwise stated).</p>
+ <p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_output2(ErlDrvPort port, char *hbuf, ErlDrvSizeT hlen, char *buf, ErlDrvSizeT len)</nametext></name>
- <fsummary>Send data and binary data to port owner</fsummary>
+ <name><ret>ErlDrvBinary *</ret>
+ <nametext>driver_alloc_binary(ErlDrvSizeT size)</nametext></name>
+ <fsummary>Allocate a driver binary.</fsummary>
<desc>
- <marker id="driver_output2"></marker>
- <p>The <c>driver_output2</c> function first sends <c>hbuf</c>
- (length in <c>hlen</c>) data as a list, regardless of port
- settings. Then <c>buf</c> is sent as a binary or list.
- E.g. if <c>hlen</c> is 3 then the port owner process will
- receive <c>[H1, H2, H3 | T]</c>.</p>
- <p>The point of sending data as a list header, is to facilitate
- matching on the data received.</p>
- <p>The return value is 0 for normal use.</p>
+ <marker id="driver_alloc_binary"></marker>
+ <p>Allocates a driver binary with a memory block
+ of at least <c>size</c> bytes, and returns a pointer to it,
+ or <c>NULL</c> on failure (out of memory). When a driver binary has
+ been sent to the emulator, it must not be changed. Every
+ allocated binary is to be freed by a corresponding call to
+ <seealso marker="#driver_free_binary">
+ <c>driver_free_binary</c></seealso> (unless otherwise stated).</p>
+ <p>Notice that a driver binary has an internal reference counter.
+ This means that calling <c>driver_free_binary</c>, it may not
+ actually dispose of it. If it is sent to the emulator, it can
+ be referenced there.</p>
+ <p>The driver binary has a field, <c>orig_bytes</c>, which
+ marks the start of the data in the binary.</p>
+ <p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_output_binary(ErlDrvPort port, char *hbuf, ErlDrvSizeT hlen, ErlDrvBinary* bin, ErlDrvSizeT offset, ErlDrvSizeT len)</nametext></name>
- <fsummary>Send data from a driver binary to port owner</fsummary>
+ <name><ret>long</ret><nametext>driver_async(ErlDrvPort port, unsigned
+ int* key, void (*async_invoke)(void*), void* async_data, void
+ (*async_free)(void*))</nametext></name>
+ <fsummary>Perform an asynchronous call within a driver.</fsummary>
<desc>
- <marker id="driver_output_binary"></marker>
- <p>This function sends data to port owner process from a
- driver binary, it has a header buffer (<c>hbuf</c>
- and <c>hlen</c>) just like <c>driver_output2</c>. The
- <c>hbuf</c> parameter can be <c>NULL</c>.</p>
- <p>The parameter <c>offset</c> is an offset into the binary and
- <c>len</c> is the number of bytes to send.</p>
- <p>Driver binaries are created with <c>driver_alloc_binary</c>.</p>
- <p>The data in the header is sent as a list and the binary as
- an Erlang binary in the tail of the list.</p>
- <p>E.g. if <c>hlen</c> is 2, then the port owner process will
- receive <c><![CDATA[[H1, H2 | <<T>>]]]></c>.</p>
- <p>The return value is 0 for normal use.</p>
- <p>Note that, using the binary syntax in Erlang, the driver
- application can match the header directly from the binary,
- so the header can be put in the binary, and hlen can be set
- to 0.</p>
+ <marker id="driver_async"></marker>
+ <p>Performs an asynchronous call. The function
+ <c>async_invoke</c> is invoked in a thread separate from the
+ emulator thread. This enables the driver to perform
+ time-consuming, blocking operations without blocking the
+ emulator.</p>
+ <p>The async thread pool size can be set with command-line argument
+ <seealso marker="erl#async_thread_pool_size"><c>+A</c></seealso>
+ in <seealso marker="erl"><c>erl(1)</c></seealso>.
+ If an async thread pool is unavailable, the call is made
+ synchronously in the thread calling <c>driver_async</c>. The
+ current number of async threads in the async thread pool can be
+ retrieved through <seealso marker="#driver_system_info">
+ <c>driver_system_info</c></seealso>.</p>
+ <p>If a thread pool is available, a thread is used.
+ If argument <c>key</c> is <c>NULL</c>, the threads from the
+ pool are used in a round-robin way, each call to
+ <c>driver_async</c> uses the next thread in the pool. With
+ argument <c>key</c> set, this behavior is changed. The two
+ same values of <c>*key</c> always get the same thread.</p>
+ <p>To ensure that a driver instance always uses the same
+ thread, the following call can be used:</p>
+ <code type="none"><![CDATA[
+unsigned int myKey = driver_async_port_key(myPort);
+
+r = driver_async(myPort, &myKey, myData, myFunc); ]]></code>
+ <p>It is enough to initialize <c>myKey</c> once for each
+ driver instance.</p>
+ <p>If a thread is already working, the calls are
+ queued up and executed in order. Using the same thread for
+ each driver instance ensures that the calls are made in sequence.</p>
+ <p>The <c>async_data</c> is the argument to the functions
+ <c>async_invoke</c> and <c>async_free</c>. It is typically a
+ pointer to a structure containing a pipe or event that
+ can be used to signal that the async operation completed.
+ The data is to be freed in <c>async_free</c>.</p>
+ <p>When the async operation is done,
+ <seealso marker="driver_entry#ready_async">
+ <c>ready_async</c></seealso> driver
+ entry function is called. If <c>ready_async</c> is <c>NULL</c> in
+ the driver entry, the <c>async_free</c> function is called
+ instead.</p>
+ <p>The return value is <c>-1</c> if the <c>driver_async</c> call
+ fails.</p>
+ <note>
+ <p>As from ERTS 5.5.4.3 the default stack size for
+ threads in the async-thread pool is 16 kilowords,
+ that is, 64 kilobyte on 32-bit architectures.
+ This small default size has been chosen because the
+ amount of async-threads can be quite large. The
+ default stack size is enough for drivers delivered
+ with Erlang/OTP, but is possibly not sufficiently large
+ for other dynamically linked-in drivers that use the
+ <c>driver_async</c> functionality. A suggested stack size
+ for threads in the async-thread pool can be configured
+ through command-line argument
+ <seealso marker="erl#async_thread_stack_size"><c>+a</c></seealso>
+ in <seealso marker="erl"><c>erl(1)</c></seealso>.</p>
+ </note>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_outputv(ErlDrvPort port, char* hbuf, ErlDrvSizeT hlen, ErlIOVec *ev, ErlDrvSizeT skip)</nametext></name>
- <fsummary>Send vectorized data to port owner</fsummary>
+ <name><ret>unsigned int</ret><nametext>driver_async_port_key(ErlDrvPort
+ port)</nametext></name>
+ <fsummary>Calculate an async key from an ErlDrvPort.</fsummary>
<desc>
- <marker id="driver_outputv"></marker>
- <p>This function sends data from an IO vector, <c>ev</c>, to
- the port owner process. It has a header buffer (<c>hbuf</c>
- and <c>hlen</c>), just like <c>driver_output2</c>.</p>
- <p>The <c>skip</c> parameter is a number of bytes to skip of
- the <c>ev</c> vector from the head.</p>
- <p>You get vectors of <c>ErlIOVec</c> type from the driver
- queue (see below), and the <seealso marker="driver_entry#outputv">outputv</seealso> driver entry
- function. You can also make them yourself, if you want to
- send several <c>ErlDrvBinary</c> buffers at once. Often
- it is faster to use <c>driver_output</c> or
- <c>driver_output_binary</c>.</p>
- <p>E.g. if <c>hlen</c> is 2 and <c>ev</c> points to an array of
- three binaries, the port owner process will receive <c><![CDATA[[H1, H2, <<B1>>, <<B2>> | <<B3>>]]]></c>.</p>
- <p>The return value is 0 for normal use.</p>
- <p>The comment for <c>driver_output_binary</c> applies for
- <c>driver_outputv</c> too.</p>
+ <marker id="driver_async_port_key"></marker>
+ <p>Calculates a key for later use in <seealso
+ marker="#driver_async"><c>driver_async</c></seealso>. The keys are
+ evenly distributed so that a fair mapping between port IDs
+ and async thread IDs is achieved.</p>
+ <note>
+ <p>Before Erlang/OTP R16, the port ID could be used as a key
+ with proper casting, but after the rewrite of the port
+ subsystem, this is no longer the case. With this function, you
+ can achieve the same distribution based on port IDs as before
+ Erlang/OTP R16.</p>
+ </note>
</desc>
</func>
+
<func>
- <name><ret>ErlDrvSizeT</ret><nametext>driver_vec_to_buf(ErlIOVec *ev, char *buf, ErlDrvSizeT len)</nametext></name>
- <fsummary>Collect data segments into a buffer</fsummary>
+ <name><ret>long</ret>
+ <nametext>driver_binary_dec_refc(ErlDrvBinary *bin)</nametext></name>
+ <fsummary>Decrement the reference count of a driver binary.</fsummary>
<desc>
- <marker id="driver_vec_to_buf"></marker>
- <p>This function collects several segments of data, referenced
- by <c>ev</c>, by copying them in order to the buffer
- <c>buf</c>, of the size <c>len</c>.</p>
- <p>If the data is to be sent from the driver to the port owner
- process, it is faster to use <c>driver_outputv</c>.</p>
- <p>The return value is the space left in the buffer, i.e. if
- the <c>ev</c> contains less than <c>len</c> bytes it's the
- difference, and if <c>ev</c> contains <c>len</c> bytes or
- more, it's 0. This is faster if there is more than one header byte,
- since the binary syntax can construct integers directly from
- the binary.</p>
+ <marker id="driver_binary_dec_refc"></marker>
+ <p>Decrements the reference count on <c>bin</c> and returns
+ the reference count reached after the decrement.</p>
+ <p>This function is thread-safe.</p>
+ <note>
+ <p>The reference count of driver binary is normally to be decremented
+ by calling <seealso marker="#driver_free_binary">
+ <c>driver_free_binary</c></seealso>.</p>
+ <p><c>driver_binary_dec_refc</c> does <em>not</em> free
+ the binary if the reference count reaches zero. <em>Only</em>
+ use <c>driver_binary_dec_refc</c> when you are sure
+ <em>not</em> to reach a reference count of zero.</p>
+ </note>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_set_timer(ErlDrvPort port, unsigned long time)</nametext></name>
- <fsummary>Set a timer to call the driver</fsummary>
+ <name><ret>long</ret>
+ <nametext>driver_binary_get_refc(ErlDrvBinary *bin)</nametext></name>
+ <fsummary>Get the reference count of a driver binary.</fsummary>
<desc>
- <marker id="driver_set_timer"></marker>
- <p>This function sets a timer on the driver, which will count
- down and call the driver when it is timed out. The
- <c>time</c> parameter is the time in milliseconds before the
- timer expires.</p>
- <p>When the timer reaches 0 and expires, the driver entry
- function <seealso marker="driver_entry#timeout">timeout</seealso> is called.</p>
- <p>Note that there is only one timer on each driver instance;
- setting a new timer will replace an older one.</p>
- <p>Return value is 0 (-1 only when the <c>timeout</c> driver
- function is <c>NULL</c>).</p>
+ <marker id="driver_binary_get_refc"></marker>
+ <p>Returns the current reference count on <c>bin</c>.</p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>long</ret>
+ <nametext>driver_binary_inc_refc(ErlDrvBinary *bin)</nametext></name>
+ <fsummary>Increment the reference count of a driver binary.</fsummary>
+ <desc>
+ <marker id="driver_binary_inc_refc"></marker>
+ <p>Increments the reference count on <c>bin</c> and returns
+ the reference count reached after the increment.</p>
+ <p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_cancel_timer(ErlDrvPort port)</nametext></name>
- <fsummary>Cancel a previously set timer</fsummary>
+ <name><ret>ErlDrvTermData</ret><nametext>driver_caller(ErlDrvPort
+ port)</nametext></name>
+ <fsummary>Return the process making the driver call.</fsummary>
+ <desc>
+ <marker id="driver_caller"></marker>
+ <p>Returns the process ID of the process that
+ made the current call to the driver. The process ID can be used with
+ <seealso marker="#driver_send_term"><c>driver_send_term</c></seealso>
+ to send back data to the caller.
+ <c>driver_caller</c> only returns valid data
+ when currently executing in one of the following driver callbacks:</p>
+ <taglist>
+ <tag><seealso marker="driver_entry#start">
+ <c>start</c></seealso></tag>
+ <item>Called from <seealso marker="erlang:open_port/2">
+ <c>erlang:open_port/2</c></seealso>.</item>
+ <tag><seealso marker="driver_entry#output">
+ <c>output</c></seealso></tag>
+ <item>Called from <seealso marker="erlang:send/2">
+ <c>erlang:send/2</c></seealso> and
+ <seealso marker="erlang:port_command/2">
+ <c>erlang:port_command/2</c></seealso>.</item>
+ <tag><seealso marker="driver_entry#outputv">
+ <c>outputv</c></seealso></tag>
+ <item>Called from <seealso marker="erlang:send/2">
+ <c>erlang:send/2</c></seealso> and
+ <seealso marker="erlang:port_command/2">
+ <c>erlang:port_command/2</c></seealso>.</item>
+ <tag><seealso marker="driver_entry#control">
+ <c>control</c></seealso></tag>
+ <item>Called from <seealso marker="erlang:port_control/3">
+ <c>erlang:port_control/3</c></seealso>.</item>
+ <tag><seealso marker="driver_entry#call">
+ <c>call</c></seealso></tag>
+ <item>Called from <seealso marker="erlang:port_call/3">
+ <c>erlang:port_call/3</c></seealso>.</item>
+ </taglist>
+ <p>Notice that this function is <em>not</em> thread-safe, not
+ even when the emulator with SMP support is used.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>driver_cancel_timer(ErlDrvPort port)</nametext></name>
+ <fsummary>Cancel a previously set timer.</fsummary>
<desc>
<marker id="driver_cancel_timer"></marker>
- <p>This function cancels a timer set with
- <c>driver_set_timer</c>.</p>
- <p>The return value is 0.</p>
+ <p>Cancels a timer set with
+ <seealso marker="#driver_set_timer">
+ <c>driver_set_timer</c></seealso>.</p>
+ <p>The return value is <c>0</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_read_timer(ErlDrvPort port, unsigned long *time_left)</nametext></name>
- <fsummary>Read the time left before timeout</fsummary>
+ <name><ret>int</ret><nametext>driver_compare_monitors(const ErlDrvMonitor
+ *monitor1, const ErlDrvMonitor *monitor2)</nametext></name>
+ <fsummary>Compare two monitors.</fsummary>
<desc>
- <marker id="driver_read_timer"></marker>
- <p>This function reads the current time of a timer, and places
- the result in <c>time_left</c>. This is the time in
- milliseconds, before the timeout will occur.</p>
- <p>The return value is 0.</p>
+ <marker id="driver_compare_monitors"></marker>
+ <p>Compares two <c>ErlDrvMonitor</c>s.
+ Can also be used to imply some artificial order on monitors,
+ for whatever reason.</p>
+ <p>Returns <c>0</c> if <c>monitor1</c> and <c>monitor2</c> are equal,
+ &lt; <c>0</c> if <c>monitor1</c> &lt; <c>monitor2</c>, and
+ &gt; <c>0</c> if <c>monitor1</c> &gt; <c>monitor2</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_get_now(ErlDrvNowData *now)</nametext></name>
- <fsummary>Read a system timestamp</fsummary>
+ <name><ret>ErlDrvTermData</ret><nametext>driver_connected(ErlDrvPort
+ port)</nametext></name>
+ <fsummary>Return the port owner process.</fsummary>
<desc>
- <marker id="driver_get_now"></marker>
- <warning><p><em>This function is deprecated! Do not use it!</em>
- Use <seealso marker="#erl_drv_monotonic_time"><c>erl_drv_monotonic_time()</c></seealso>
- (perhaps in combination with
- <seealso marker="#erl_drv_time_offset"><c>erl_drv_time_offset()</c></seealso>)
- instead.</p></warning>
- <p>This function reads a timestamp into the memory pointed to by
- the parameter <c>now</c>. See the description of <seealso marker="#ErlDrvNowData">ErlDrvNowData</seealso> for
- specification of its fields. </p>
- <p>The return value is 0 unless the <c>now</c> pointer is not
- valid, in which case it is &lt; 0. </p>
+ <marker id="driver_connected"></marker>
+ <p>Returns the port owner process.</p>
+ <p>Notice that this function is <em>not</em> thread-safe, not
+ even when the emulator with SMP support is used.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_select(ErlDrvPort port, ErlDrvEvent event, int mode, int on)</nametext></name>
- <fsummary>Provide an event for having the emulator call the driver</fsummary>
+ <name><ret>ErlDrvPort</ret><nametext>driver_create_port(ErlDrvPort port,
+ ErlDrvTermData owner_pid, char* name,
+ ErlDrvData drv_data)</nametext></name>
+ <fsummary>Create a new port (driver instance).</fsummary>
<desc>
- <marker id="driver_select"></marker>
- <p>This function is used by drivers to provide the emulator with
- events to check for. This enables the emulator to call the driver
- when something has happened asynchronously.</p>
- <p>The <c>event</c> argument identifies an OS-specific event object.
- On Unix systems, the functions <c>select</c>/<c>poll</c> are used. The
- event object must be a socket or pipe (or other object that
- <c>select</c>/<c>poll</c> can use).
- On windows, the Win32 API function <c>WaitForMultipleObjects</c>
- is used. This places other restrictions on the event object.
- Refer to the Win32 SDK documentation.
- On Enea OSE, the receive function is used. See the <seealso
- marker="ose:ose_erl_driver"></seealso> for more details.</p>
- <p>The <c>on</c> parameter should be <c>1</c> for setting events
- and <c>0</c> for clearing them.</p>
- <p>The <c>mode</c> argument is a bitwise-or combination of
- <c>ERL_DRV_READ</c>, <c>ERL_DRV_WRITE</c> and <c>ERL_DRV_USE</c>.
- The first two specify whether to wait for read events and/or write
- events. A fired read event will call
- <seealso marker="driver_entry#ready_input">ready_input</seealso>
- while a fired write event will call
- <seealso marker="driver_entry#ready_output">ready_output</seealso>.
- </p>
- <note>
- <p>Some OS (Windows and Enea OSE) do not differentiate between read and write events.
- The call-back for a fired event then only depends on the value of <c>mode</c>.</p>
- </note>
- <p><c>ERL_DRV_USE</c> specifies if we are using the event object or if we want to close it.
- On an emulator with SMP support, it is not safe to clear all events
- and then close the event object after <c>driver_select</c> has
- returned. Another thread may still be using the event object
- internally. To safely close an event object call
- <c>driver_select</c> with <c>ERL_DRV_USE</c> and <c>on==0</c>. That
- will clear all events and then call
- <seealso marker="driver_entry#stop_select">stop_select</seealso>
- when it is safe to close the event object.
- <c>ERL_DRV_USE</c> should be set together with the first event
- for an event object. It is harmless to set <c>ERL_DRV_USE</c>
- even though it already has been done. Clearing all events but keeping
- <c>ERL_DRV_USE</c> set will indicate that we are using the event
- object and probably will set events for it again.</p>
- <note>
- <p>ERL_DRV_USE was added in OTP release R13. Old drivers will still work
- as before. But it is recommended to update them to use <c>ERL_DRV_USE</c> and
- <c>stop_select</c> to make sure that event objects are closed in a safe way.</p>
- </note>
- <p>The return value is 0 (failure, -1, only if the
- <c>ready_input</c>/<c>ready_output</c> is
- <c>NULL</c>).</p>
+ <p>Creates a new port executing the same driver
+ code as the port creating the new port.</p>
+ <taglist>
+ <tag><c>port</c></tag>
+ <item>The port handle of the port (driver instance) creating
+ the new port.</item>
+ <tag><c>owner_pid</c></tag>
+ <item>The process ID of the Erlang process to become
+ owner of the new port. This process will be linked
+ to the new port. You usually want to use
+ <c>driver_caller(port)</c> as <c>owner_pid</c>.</item>
+ <tag><c>name</c></tag>
+ <item>The port name of the new port. You usually want to
+ use the same port name as the driver name
+ (<seealso marker="driver_entry#driver_name">
+ <c>driver_name</c></seealso> field of the
+ <seealso marker="driver_entry"><c>driver_entry</c></seealso>).
+ </item>
+ <tag><c>drv_data</c></tag>
+ <item>The driver-defined handle that is passed in later
+ calls to driver callbacks. Notice that the
+ <seealso marker="driver_entry#start">driver start
+ callback</seealso> is not called for this new driver instance.
+ The driver-defined handle is normally created in the
+ <seealso marker="driver_entry#start">driver start callback</seealso>
+ when a port is created through
+ <seealso marker="erlang#open_port/2">
+ <c>erlang:open_port/2</c></seealso>.
+ </item>
+ </taglist>
+ <p>The caller of <c>driver_create_port</c> is allowed to
+ manipulate the newly created port when <c>driver_create_port</c>
+ has returned. When
+ <seealso marker="#smp_support">port level locking</seealso>
+ is used, the creating port is only allowed to
+ manipulate the newly created port until the current driver
+ callback, which was called by the emulator, returns.</p>
</desc>
</func>
+
<func>
- <name><ret>void *</ret><nametext>driver_alloc(ErlDrvSizeT size)</nametext></name>
- <fsummary>Allocate memory</fsummary>
+ <name><ret>int</ret><nametext>driver_demonitor_process(ErlDrvPort port,
+ const ErlDrvMonitor *monitor)</nametext></name>
+ <fsummary>Stop monitoring a process from a driver.</fsummary>
<desc>
- <marker id="driver_alloc"></marker>
- <p>This function allocates a memory block of the size specified
- in <c>size</c>, and returns it. This only fails on out of
- memory, in that case <c>NULL</c> is returned. (This is most
- often a wrapper for <c>malloc</c>).</p>
- <p>Memory allocated must be explicitly freed with a corresponding
- call to <c>driver_free</c> (unless otherwise stated).</p>
- <p>This function is thread-safe.</p>
+ <marker id="driver_demonitor_process"></marker>
+ <p>Cancels a monitor created earlier.</p>
+ <p>Returns <c>0</c> if a monitor was removed and &gt; 0 if the monitor
+ no longer exists.</p>
</desc>
</func>
+
<func>
- <name><ret>void *</ret><nametext>driver_realloc(void *ptr, ErlDrvSizeT size)</nametext></name>
- <fsummary>Resize an allocated memory block</fsummary>
+ <name><ret>ErlDrvSizeT</ret><nametext>driver_deq(ErlDrvPort port,
+ ErlDrvSizeT size)</nametext></name>
+ <fsummary>Dequeue data from the head of the driver queue.</fsummary>
<desc>
- <marker id="driver_realloc"></marker>
- <p>This function resizes a memory block, either in place, or by
- allocating a new block, copying the data and freeing the old
- block. A pointer is returned to the reallocated memory. On
- failure (out of memory), <c>NULL</c> is returned. (This is
- most often a wrapper for <c>realloc</c>.)</p>
- <p>This function is thread-safe.</p>
+ <marker id="driver_deq"></marker>
+ <p>Dequeues data by moving the head pointer
+ forward in the driver queue by <c>size</c> bytes. The data
+ in the queue is deallocated.</p>
+ <p>Returns the number of bytes remaining in the queue on success,
+ otherwise <c>-1</c>.</p>
+ <p>This function can be called from any thread if a
+ <seealso marker="#ErlDrvPDL">port data lock</seealso>
+ associated with the <c>port</c> is locked by the calling
+ thread during the call.</p>
</desc>
</func>
+
<func>
- <name><ret>void</ret><nametext>driver_free(void *ptr)</nametext></name>
- <fsummary>Free an allocated memory block</fsummary>
+ <name><ret>int</ret><nametext>driver_enq(ErlDrvPort port, char* buf,
+ ErlDrvSizeT len)</nametext></name>
+ <fsummary>Enqueue data in the driver queue.</fsummary>
<desc>
- <marker id="driver_free"></marker>
- <p>This function frees the memory pointed to by <c>ptr</c>. The
- memory should have been allocated with
- <c>driver_alloc</c>. All allocated memory should be
- deallocated, just once. There is no garbage collection in
- drivers.</p>
- <p>This function is thread-safe.</p>
+ <marker id="driver_enq"></marker>
+ <p>Enqueues data in the driver queue. The data in
+ <c>buf</c> is copied (<c>len</c> bytes) and placed at the
+ end of the driver queue. The driver queue is normally used
+ in a FIFO way.</p>
+ <p>The driver queue is available to queue output from the
+ emulator to the driver (data from the driver to the emulator
+ is queued by the emulator in normal Erlang message
+ queues). This can be useful if the driver must wait for
+ slow devices, and so on, and wants to yield back to the
+ emulator. The driver queue is implemented as an <c>ErlIOVec</c>.</p>
+ <p>When the queue contains data, the driver does not close until
+ the queue is empty.</p>
+ <p>The return value is <c>0</c>.</p>
+ <p>This function can be called from any thread if a
+ <seealso marker="#ErlDrvPDL">port data lock</seealso>
+ associated with the <c>port</c> is locked by the calling
+ thread during the call.</p>
</desc>
</func>
+
<func>
- <name><ret>ErlDrvBinary *</ret><nametext>driver_alloc_binary(ErlDrvSizeT size)</nametext></name>
- <fsummary>Allocate a driver binary</fsummary>
+ <name><ret>int</ret><nametext>driver_enq_bin(ErlDrvPort port,
+ ErlDrvBinary *bin, ErlDrvSizeT offset, ErlDrvSizeT len)</nametext>
+ </name>
+ <fsummary>Enqueue binary in the driver queue.</fsummary>
<desc>
- <marker id="driver_alloc_binary"></marker>
- <p>This function allocates a driver binary with a memory block
- of at least <c>size</c> bytes, and returns a pointer to it,
- or NULL on failure (out of memory). When a driver binary has
- been sent to the emulator, it must not be altered. Every
- allocated binary should be freed by a corresponding call to
- <c>driver_free_binary</c> (unless otherwise stated).</p>
- <p>Note that a driver binary has an internal reference counter,
- this means that calling <c>driver_free_binary</c> it may not
- actually dispose of it. If it's sent to the emulator, it may
- be referenced there.</p>
- <p>The driver binary has a field, <c>orig_bytes</c>, which
- marks the start of the data in the binary.</p>
- <p>This function is thread-safe.</p>
+ <marker id="driver_enq_bin"></marker>
+ <p>Enqueues a driver binary in the driver
+ queue. The data in <c>bin</c> at <c>offset</c> with length
+ <c>len</c> is placed at the end of the queue. This function
+ is most often faster than
+ <seealso marker="#driver_enq"><c>driver_enq</c></seealso>,
+ because no data must be copied.</p>
+ <p>This function can be called from any thread if a
+ <seealso marker="#ErlDrvPDL">port data lock</seealso>
+ associated with the <c>port</c> is locked by the calling
+ thread during the call.</p>
+ <p>The return value is <c>0</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>ErlDrvBinary *</ret><nametext>driver_realloc_binary(ErlDrvBinary *bin, ErlDrvSizeT size)</nametext></name>
- <fsummary>Resize a driver binary</fsummary>
+ <name><ret>int</ret><nametext>driver_enqv(ErlDrvPort port, ErlIOVec *ev,
+ ErlDrvSizeT skip)</nametext></name>
+ <fsummary>Enqueue vector in the driver queue.</fsummary>
<desc>
- <marker id="driver_realloc_binary"></marker>
- <p>This function resizes a driver binary, while keeping the
- data. The resized driver binary is returned. On failure (out
- of memory), <c>NULL</c> is returned.</p>
- <p>This function is only thread-safe when the emulator with SMP
- support is used.</p>
+ <marker id="driver_enqv"></marker>
+ <p>Enqueues the data in <c>ev</c>, skipping the
+ first <c>skip</c> bytes of it, at the end of the driver
+ queue. It is faster than
+ <seealso marker="#driver_enq"><c>driver_enq</c></seealso>,
+ because no data must be copied.</p>
+ <p>The return value is <c>0</c>.</p>
+ <p>This function can be called from any thread if a
+ <seealso marker="#ErlDrvPDL">port data lock</seealso>
+ associated with the <c>port</c> is locked by the calling
+ thread during the call.</p>
</desc>
</func>
+
<func>
- <name><ret>void</ret><nametext>driver_free_binary(ErlDrvBinary *bin)</nametext></name>
- <fsummary>Free a driver binary</fsummary>
+ <name><ret>int</ret><nametext>driver_failure(ErlDrvPort port, int
+ error)</nametext></name>
+ <name><ret>int</ret><nametext>driver_failure_atom(ErlDrvPort port, char
+ *string)</nametext></name>
+ <name><ret>int</ret><nametext>driver_failure_posix(ErlDrvPort port, int
+ error)</nametext></name>
+ <fsummary>Fail with error.</fsummary>
<desc>
- <marker id="driver_free_binary"></marker>
- <p>This function frees a driver binary <c>bin</c>, allocated
- previously with <c>driver_alloc_binary</c>. Since binaries
- in Erlang are reference counted, the binary may still be
- around.</p>
- <p>This function is only thread-safe when the emulator with SMP
- support is used.</p>
+ <marker id="driver_failure_atom"></marker>
+ <marker id="driver_failure_posix"></marker>
+ <marker id="driver_failure"></marker>
+ <p>Signals to Erlang that the driver has
+ encountered an error and is to be closed. The port is
+ closed and the tuple <c>{'EXIT', error, Err}</c> is sent to
+ the port owner process, where error is an error atom
+ (<c>driver_failure_atom</c> and
+ <c>driver_failure_posix</c>) or an integer
+ (<c>driver_failure</c>).</p>
+ <p>The driver is to fail only when in severe error situations,
+ when the driver cannot possibly keep open, for example,
+ buffer allocation gets out of memory. For normal errors
+ it is more appropriate to send error codes with
+ <seealso marker="#driver_output"><c>driver_output</c></seealso>.</p>
+ <p>The return value is <c>0</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>long</ret><nametext>driver_binary_get_refc(ErlDrvBinary *bin)</nametext></name>
- <fsummary>Get the reference count of a driver binary</fsummary>
+ <name><ret>int</ret><nametext>driver_failure_eof(ErlDrvPort
+ port)</nametext></name>
+ <fsummary>Fail with EOF.</fsummary>
<desc>
- <marker id="driver_binary_get_refc"></marker>
- <p>Returns current reference count on <c>bin</c>.</p>
- <p>This function is only thread-safe when the emulator with SMP
- support is used.</p>
+ <marker id="driver_failure_eof"></marker>
+ <p>Signals to Erlang that the driver has
+ encountered an EOF and is to be closed, unless the port was
+ opened with option <c>eof</c>, in which case <c>eof</c> is sent
+ to the port. Otherwise the port is closed and an
+ <c>'EXIT'</c> message is sent to the port owner process.</p>
+ <p>The return value is <c>0</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>long</ret><nametext>driver_binary_inc_refc(ErlDrvBinary *bin)</nametext></name>
- <fsummary>Increment the reference count of a driver binary</fsummary>
+ <name><ret>void</ret><nametext>driver_free(void *ptr)</nametext></name>
+ <fsummary>Free an allocated memory block.</fsummary>
<desc>
- <marker id="driver_binary_inc_refc"></marker>
- <p>Increments the reference count on <c>bin</c> and returns
- the reference count reached after the increment.</p>
- <p>This function is only thread-safe when the emulator with SMP
- support is used.</p>
+ <marker id="driver_free"></marker>
+ <p>Frees the memory pointed to by <c>ptr</c>. The
+ memory is to have been allocated with
+ <c>driver_alloc</c>. All allocated memory is to be
+ deallocated, only once. There is no garbage collection in
+ drivers.</p>
+ <p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>long</ret><nametext>driver_binary_dec_refc(ErlDrvBinary *bin)</nametext></name>
- <fsummary>Decrement the reference count of a driver binary</fsummary>
+ <name><ret>void</ret>
+ <nametext>driver_free_binary(ErlDrvBinary *bin)</nametext></name>
+ <fsummary>Free a driver binary.</fsummary>
<desc>
- <marker id="driver_binary_dec_refc"></marker>
- <p>Decrements the reference count on <c>bin</c> and returns
- the reference count reached after the decrement.</p>
- <p>This function is only thread-safe when the emulator with SMP
- support is used.</p>
- <note>
- <p>You should normally decrement the reference count of a
- driver binary by calling
- <seealso marker="#driver_free_binary">driver_free_binary()</seealso>.
- <c>driver_binary_dec_refc()</c> does <em>not</em> free
- the binary if the reference count reaches zero. <em>Only</em>
- use <c>driver_binary_dec_refc()</c> when you are sure
- <em>not</em> to reach a reference count of zero.</p>
- </note>
+ <marker id="driver_free_binary"></marker>
+ <p>Frees a driver binary <c>bin</c>, allocated previously with
+ <seealso marker="#driver_alloc_binary">
+ <c>driver_alloc_binary</c></seealso>. As binaries
+ in Erlang are reference counted, the binary can still be around.</p>
+ <p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_enq(ErlDrvPort port, char* buf, ErlDrvSizeT len)</nametext></name>
- <fsummary>Enqueue data in the driver queue</fsummary>
+ <name><ret>ErlDrvTermData</ret>
+ <nametext>driver_get_monitored_process(ErlDrvPort port, const
+ ErlDrvMonitor *monitor)</nametext></name>
+ <fsummary>Retrieve the process ID from a monitor.</fsummary>
<desc>
- <marker id="driver_enq"></marker>
- <p>This function enqueues data in the driver queue. The data in
- <c>buf</c> is copied (<c>len</c> bytes) and placed at the
- end of the driver queue. The driver queue is normally used
- in a FIFO way.</p>
- <p>The driver queue is available to queue output from the
- emulator to the driver (data from the driver to the emulator
- is queued by the emulator in normal erlang message
- queues). This can be useful if the driver has to wait for
- slow devices etc, and wants to yield back to the
- emulator. The driver queue is implemented as an ErlIOVec.</p>
- <p>When the queue contains data, the driver won't close, until
- the queue is empty.</p>
- <p>The return value is 0.</p>
- <p>This function can be called from an arbitrary thread if a
- <seealso marker="#ErlDrvPDL">port data lock</seealso>
- associated with the <c>port</c> is locked by the calling
- thread during the call.</p>
+ <marker id="driver_get_monitored_process"></marker>
+ <p>Returns the process ID associated with a living
+ monitor. It can be used in the
+ <seealso marker="driver_entry#process_exit">
+ <c>process_exit</c></seealso> callback to
+ get the process identification for the exiting process.</p>
+ <p>Returns <c>driver_term_nil</c> if the monitor no longer exists.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_pushq(ErlDrvPort port, char* buf, ErlDrvSizeT len)</nametext></name>
- <fsummary>Push data at the head of the driver queue</fsummary>
+ <name><ret>int</ret>
+ <nametext>driver_get_now(ErlDrvNowData *now)</nametext></name>
+ <fsummary>Read a system time stamp.</fsummary>
<desc>
- <marker id="driver_pushq"></marker>
- <p>This function puts data at the head of the driver queue. The
- data in <c>buf</c> is copied (<c>len</c> bytes) and placed
- at the beginning of the queue.</p>
- <p>The return value is 0.</p>
- <p>This function can be called from an arbitrary thread if a
- <seealso marker="#ErlDrvPDL">port data lock</seealso>
- associated with the <c>port</c> is locked by the calling
- thread during the call.</p>
+ <marker id="driver_get_now"></marker>
+ <warning>
+ <p><em>This function is deprecated. Do not use it.</em> Use
+ <seealso marker="#erl_drv_monotonic_time">
+ <c>erl_drv_monotonic_time</c></seealso> (perhaps in combination with
+ <seealso marker="#erl_drv_time_offset">
+ <c>erl_drv_time_offset</c></seealso>) instead.</p>
+ </warning>
+ <p>Reads a time stamp into the memory pointed to by
+ parameter <c>now</c>. For information about specific fields, see
+ <seealso marker="#ErlDrvNowData"><c>ErlDrvNowData</c></seealso>.</p>
+ <p>The return value is <c>0</c>, unless the <c>now</c> pointer is
+ invalid, in which case it is &lt; <c>0</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>ErlDrvSizeT</ret><nametext>driver_deq(ErlDrvPort port, ErlDrvSizeT size)</nametext></name>
- <fsummary>Dequeue data from the head of the driver queue</fsummary>
+ <name><ret>int</ret><nametext>driver_lock_driver(ErlDrvPort
+ port)</nametext></name>
+ <fsummary>Ensure the driver is never unloaded.</fsummary>
<desc>
- <marker id="driver_deq"></marker>
- <p>This function dequeues data by moving the head pointer
- forward in the driver queue by <c>size</c> bytes. The data
- in the queue will be deallocated.</p>
- <p>The return value is the number of bytes remaining in the queue
- or -1 on failure.</p>
- <p>This function can be called from an arbitrary thread if a
- <seealso marker="#ErlDrvPDL">port data lock</seealso>
- associated with the <c>port</c> is locked by the calling
- thread during the call.</p>
+ <marker id="driver_lock_driver"></marker>
+ <p>Locks the driver used by the port <c>port</c>
+ in memory for the rest of the emulator process'
+ lifetime. After this call, the driver behaves as one of Erlang's
+ statically linked-in drivers.</p>
</desc>
</func>
+
<func>
- <name><ret>ErlDrvSizeT</ret><nametext>driver_sizeq(ErlDrvPort port)</nametext></name>
- <fsummary>Return the size of the driver queue</fsummary>
+ <name><ret>ErlDrvTermData</ret><nametext>driver_mk_atom(char*
+ string)</nametext></name>
+ <fsummary>Make an atom from a name.</fsummary>
<desc>
- <marker id="driver_sizeq"></marker>
- <p>This function returns the number of bytes currently in the
- driver queue.</p>
- <p>This function can be called from an arbitrary thread if a
- <seealso marker="#ErlDrvPDL">port data lock</seealso>
- associated with the <c>port</c> is locked by the calling
- thread during the call.</p>
+ <marker id="driver_mk_atom"></marker>
+ <p>Returns an atom given a name
+ <c>string</c>. The atom is created and does not change, so the
+ return value can be saved and reused, which is faster than
+ looking up the atom several times.</p>
+ <p>Notice that this function is <em>not</em> thread-safe, not
+ even when the emulator with SMP support is used.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_enq_bin(ErlDrvPort port, ErlDrvBinary *bin, ErlDrvSizeT offset, ErlDrvSizeT len)</nametext></name>
- <fsummary>Enqueue binary in the driver queue</fsummary>
+ <name><ret>ErlDrvTermData</ret><nametext>driver_mk_port(ErlDrvPort
+ port)</nametext></name>
+ <fsummary>Make an Erlang term port from a port.</fsummary>
<desc>
- <marker id="driver_enq_bin"></marker>
- <p>This function enqueues a driver binary in the driver
- queue. The data in <c>bin</c> at <c>offset</c> with length
- <c>len</c> is placed at the end of the queue. This function
- is most often faster than <c>driver_enq</c>, because the
- data doesn't have to be copied.</p>
- <p>This function can be called from an arbitrary thread if a
- <seealso marker="#ErlDrvPDL">port data lock</seealso>
- associated with the <c>port</c> is locked by the calling
- thread during the call.</p>
- <p>The return value is 0.</p>
+ <marker id="driver_mk_port"></marker>
+ <p>Converts a port handle to the Erlang term format, usable in
+ <seealso marker="#erl_drv_output_term">
+ <c>erl_drv_output_term</c></seealso> and
+ <seealso marker="#erl_drv_send_term">
+ <c>erl_drv_send_term</c></seealso>.</p>
+ <p>Notice that this function is <em>not</em> thread-safe, not
+ even when the emulator with SMP support is used.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_pushq_bin(ErlDrvPort port, ErlDrvBinary *bin, ErlDrvSizeT offset, ErlDrvSizeT len)</nametext></name>
- <fsummary>Push binary at the head of the driver queue</fsummary>
+ <name><ret>int</ret><nametext>driver_monitor_process(ErlDrvPort port,
+ ErlDrvTermData process, ErlDrvMonitor *monitor)</nametext></name>
+ <fsummary>Monitor a process from a driver.</fsummary>
<desc>
- <marker id="driver_pushq_bin"></marker>
- <p>This function puts data in the binary <c>bin</c>, at
- <c>offset</c> with length <c>len</c> at the head of the
- driver queue. It is most often faster than
- <c>driver_pushq</c>, because the data doesn't have to be
- copied.</p>
- <p>This function can be called from an arbitrary thread if a
- <seealso marker="#ErlDrvPDL">port data lock</seealso>
- associated with the <c>port</c> is locked by the calling
- thread during the call.</p>
- <p>The return value is 0.</p>
+ <marker id="driver_monitor_process"></marker>
+ <p>Starts monitoring a process from a driver. When a process is
+ monitored, a process exit results in a call to the provided
+ <seealso marker="driver_entry#process_exit">
+ <c>process_exit</c></seealso> callback
+ in the <seealso marker="driver_entry"><c>ErlDrvEntry</c></seealso>
+ structure. The <c>ErlDrvMonitor</c> structure is filled in, for later
+ removal or compare.</p>
+ <p>Parameter <c>process</c> is to be the return value of an
+ earlier call to <seealso marker="#driver_caller">
+ <c>driver_caller</c></seealso> or
+ <seealso marker="#driver_connected"><c>driver_connected</c></seealso>
+ call.</p>
+ <p>Returns <c>0</c> on success, &lt; 0 if no callback is
+ provided, and &gt; 0 if the process is no longer alive.</p>
</desc>
</func>
+
<func>
- <name><ret>ErlDrvSizeT</ret><nametext>driver_peekqv(ErlDrvPort port, ErlIOVec *ev)</nametext></name>
- <fsummary>Get the driver queue as an IO vector</fsummary>
+ <name><ret>int</ret><nametext>driver_output(ErlDrvPort port, char *buf,
+ ErlDrvSizeT len)</nametext></name>
+ <fsummary>Send data from driver to port owner.</fsummary>
<desc>
- <marker id="driver_peekqv"></marker>
- <p>
- This function retrieves the driver queue into a supplied
- <c>ErlIOVec</c> <c>ev</c>. It also returns the queue size.
- This is one of two ways to get data out of the queue.
- </p>
- <p>
- If <c>ev</c> is <c>NULL</c> all ones i.e. <c>-1</c> type cast to
- <c>ErlDrvSizeT</c> is returned.
- </p>
- <p>Nothing is removed from the queue by this function, that must be done
- with <c>driver_deq</c>.</p>
- <p>This function can be called from an arbitrary thread if a
- <seealso marker="#ErlDrvPDL">port data lock</seealso>
- associated with the <c>port</c> is locked by the calling
- thread during the call.</p>
+ <marker id="driver_output"></marker>
+ <p>Sends data from the driver up to the emulator. The data is received
+ as terms or binary data, depending on how the driver port was
+ opened.</p>
+ <p>The data is queued in the port owner process' message
+ queue. Notice that this does not yield to the emulator (as
+ the driver and the emulator run in the same thread).</p>
+ <p>Parameter <c>buf</c> points to the data to send, and
+ <c>len</c> is the number of bytes.</p>
+ <p>The return value for all output functions is <c>0</c> for normal use.
+ If the driver is used for distribution, it can fail and return
+ <c>-1</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>SysIOVec *</ret><nametext>driver_peekq(ErlDrvPort port, int *vlen)</nametext></name>
- <fsummary>Get the driver queue as a vector</fsummary>
+ <name><ret>int</ret><nametext>driver_output_binary(ErlDrvPort port, char
+ *hbuf, ErlDrvSizeT hlen, ErlDrvBinary* bin, ErlDrvSizeT offset,
+ ErlDrvSizeT len)</nametext></name>
+ <fsummary>Send data from a driver binary to port owner.</fsummary>
<desc>
- <marker id="driver_peekq"></marker>
- <p>This function retrieves the driver queue as a pointer to an
- array of <c>SysIOVec</c>s. It also returns the number of
- elements in <c>vlen</c>. This is one of two ways to get data
- out of the queue.</p>
- <p>Nothing is removed from the queue by this function, that must be done
- with <c>driver_deq</c>.</p>
- <p>The returned array is suitable to use with the Unix system
- call <c>writev</c>.</p>
- <p>This function can be called from an arbitrary thread if a
- <seealso marker="#ErlDrvPDL">port data lock</seealso>
- associated with the <c>port</c> is locked by the calling
- thread during the call.</p>
+ <marker id="driver_output_binary"></marker>
+ <p>Sends data to a port owner process from a
+ driver binary. It has a header buffer (<c>hbuf</c>
+ and <c>hlen</c>) just like
+ <seealso marker="#driver_output2"><c>driver_output2</c></seealso>.
+ Parameter <c>hbuf</c> can be <c>NULL</c>.</p>
+ <p>Parameter <c>offset</c> is an offset into the binary and
+ <c>len</c> is the number of bytes to send.</p>
+ <p>Driver binaries are created with
+ <seealso marker="#driver_alloc_binary">
+ <c>driver_alloc_binary</c></seealso>.</p>
+ <p>The data in the header is sent as a list and the binary as
+ an Erlang binary in the tail of the list.</p>
+ <p>For example, if <c>hlen</c> is <c>2</c>, the port owner process
+ receives <c><![CDATA[[H1, H2 | <<T>>]]]></c>.</p>
+ <p>The return value is <c>0</c> for normal use.</p>
+ <p>Notice that, using the binary syntax in Erlang, the driver
+ application can match the header directly from the binary,
+ so the header can be put in the binary, and <c>hlen</c> can be set
+ to <c>0</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_enqv(ErlDrvPort port, ErlIOVec *ev, ErlDrvSizeT skip)</nametext></name>
- <fsummary>Enqueue vector in the driver queue</fsummary>
+ <name><ret>int</ret><nametext>driver_output_term(ErlDrvPort port,
+ ErlDrvTermData* term, int n)</nametext></name>
+ <fsummary>Send term data from driver to port owner.</fsummary>
<desc>
- <marker id="driver_enqv"></marker>
- <p>This function enqueues the data in <c>ev</c>, skipping the
- first <c>skip</c> bytes of it, at the end of the driver
- queue. It is faster than <c>driver_enq</c>, because the data
- doesn't have to be copied.</p>
- <p>The return value is 0.</p>
- <p>This function can be called from an arbitrary thread if a
- <seealso marker="#ErlDrvPDL">port data lock</seealso>
- associated with the <c>port</c> is locked by the calling
- thread during the call.</p>
+ <marker id="driver_output_term"></marker>
+ <warning>
+ <p><em>This function is deprecated.</em>
+ Use <seealso marker="#erl_drv_send_term">
+ <c>erl_drv_output_term</c></seealso>instead.</p>
+ </warning>
+ <p>Parameters <c>term</c> and <c>n</c> work as in
+ <seealso marker="#erl_drv_output_term">
+ <c>erl_drv_output_term</c></seealso>.</p>
+ <p>Notice that this function is <em>not</em> thread-safe, not
+ even when the emulator with SMP support is used.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_pushqv(ErlDrvPort port, ErlIOVec *ev, ErlDrvSizeT skip)</nametext></name>
- <fsummary>Push vector at the head of the driver queue</fsummary>
+ <name><ret>int</ret><nametext>driver_output2(ErlDrvPort port, char *hbuf,
+ ErlDrvSizeT hlen, char *buf, ErlDrvSizeT len)</nametext></name>
+ <fsummary>Send data and binary data to port owner.</fsummary>
<desc>
- <marker id="driver_pushqv"></marker>
- <p>This function puts the data in <c>ev</c>, skipping the first
- <c>skip</c> bytes of it, at the head of the driver queue.
- It is faster than <c>driver_pushq</c>, because the data
- doesn't have to be copied.</p>
- <p>The return value is 0.</p>
- <p>This function can be called from an arbitrary thread if a
- <seealso marker="#ErlDrvPDL">port data lock</seealso>
- associated with the <c>port</c> is locked by the calling
- thread during the call.</p>
+ <marker id="driver_output2"></marker>
+ <p>First sends <c>hbuf</c>
+ (length in <c>hlen</c>) data as a list, regardless of port
+ settings. Then sends <c>buf</c> as a binary or list.
+ For example, if <c>hlen</c> is <c>3</c>, the port owner process
+ receives <c>[H1, H2, H3 | T]</c>.</p>
+ <p>The point of sending data as a list header, is to facilitate
+ matching on the data received.</p>
+ <p>The return value is <c>0</c> for normal use.</p>
</desc>
</func>
+
<func>
- <name><ret>ErlDrvPDL</ret><nametext>driver_pdl_create(ErlDrvPort port)</nametext></name>
- <fsummary>Create a port data lock</fsummary>
+ <name><ret>int</ret><nametext>driver_outputv(ErlDrvPort port, char* hbuf,
+ ErlDrvSizeT hlen, ErlIOVec *ev, ErlDrvSizeT skip)</nametext></name>
+ <fsummary>Send vectorized data to port owner.</fsummary>
<desc>
- <marker id="driver_pdl_create"></marker>
- <p>This function creates a port data lock associated with
- the <c>port</c>. <em>NOTE</em>: Once a port data lock has
- been created, it has to be locked during all operations
- on the driver queue of the <c>port</c>.</p>
- <p>On success a newly created port data lock is returned. On
- failure <c>NULL</c> is returned. <c>driver_pdl_create()</c> will
- fail if <c>port</c> is invalid or if a port data lock already has
- been associated with the <c>port</c>.</p>
+ <marker id="driver_outputv"></marker>
+ <p>Sends data from an I/O vector, <c>ev</c>, to
+ the port owner process. It has a header buffer (<c>hbuf</c>
+ and <c>hlen</c>), just like <seealso marker="#driver_output2">
+ <c>driver_output2</c></seealso>.</p>
+ <p>Parameter <c>skip</c> is a number of bytes to skip of
+ the <c>ev</c> vector from the head.</p>
+ <p>You get vectors of <c>ErlIOVec</c> type from the driver
+ queue (see below), and the
+ <seealso marker="driver_entry#outputv"><c>outputv</c></seealso>
+ driver entry function. You can also make them yourself, if you want to
+ send several <c>ErlDrvBinary</c> buffers at once. Often
+ it is faster to use
+ <seealso marker="#driver_output"><c>driver_output</c></seealso> or
+ <seealso marker="#driver_output_binary"></seealso>.</p>
+ <p>For example, if <c>hlen</c> is <c>2</c> and <c>ev</c> points to an
+ array of three binaries, the port owner process receives
+ <c><![CDATA[[H1, H2, <<B1>>, <<B2>> | <<B3>>]]]></c>.</p>
+ <p>The return value is <c>0</c> for normal use.</p>
+ <p>The comment for <c>driver_output_binary</c> also applies for
+ <c>driver_outputv</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>void</ret><nametext>driver_pdl_lock(ErlDrvPDL pdl)</nametext></name>
- <fsummary>Lock port data lock</fsummary>
+ <name><ret>ErlDrvPDL</ret>
+ <nametext>driver_pdl_create(ErlDrvPort port)</nametext></name>
+ <fsummary>Create a port data lock.</fsummary>
<desc>
- <marker id="driver_pdl_lock"></marker>
- <p>This function locks the port data lock passed as argument
- (<c>pdl</c>).</p>
- <p>This function is thread-safe.</p>
+ <marker id="driver_pdl_create"></marker>
+ <p>Creates a port data lock associated with the <c>port</c>.</p>
+ <note>
+ <p>Once a port data lock has been created, it must be locked during
+ all operations on the driver queue of the <c>port</c>.</p>
+ </note>
+ <p>Returns a newly created port data lock on success,
+ otherwise <c>NULL</c>. The function fails
+ if <c>port</c> is invalid or if a port data lock already has
+ been associated with the <c>port</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>void</ret><nametext>driver_pdl_unlock(ErlDrvPDL pdl)</nametext></name>
- <fsummary>Unlock port data lock</fsummary>
+ <name><ret>long</ret><nametext>driver_pdl_dec_refc(ErlDrvPDL
+ pdl)</nametext></name>
+ <fsummary></fsummary>
<desc>
- <marker id="driver_pdl_unlock"></marker>
- <p>This function unlocks the port data lock passed as argument
- (<c>pdl</c>).</p>
+ <marker id="driver_pdl_dec_refc"></marker>
+ <p>Decrements the reference count of
+ the port data lock passed as argument (<c>pdl</c>).</p>
+ <p>The current reference count after the decrement has
+ been performed is returned.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>long</ret><nametext>driver_pdl_get_refc(ErlDrvPDL pdl)</nametext></name>
+ <name><ret>long</ret>
+ <nametext>driver_pdl_get_refc(ErlDrvPDL pdl)</nametext></name>
<fsummary></fsummary>
<desc>
<marker id="driver_pdl_get_refc"></marker>
- <p>This function returns the current reference count of
+ <p>Returns the current reference count of
the port data lock passed as argument (<c>pdl</c>).</p>
<p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>long</ret><nametext>driver_pdl_inc_refc(ErlDrvPDL pdl)</nametext></name>
+ <name><ret>long</ret>
+ <nametext>driver_pdl_inc_refc(ErlDrvPDL pdl)</nametext></name>
<fsummary></fsummary>
<desc>
<marker id="driver_pdl_inc_refc"></marker>
- <p>This function increments the reference count of
+ <p>Increments the reference count of
the port data lock passed as argument (<c>pdl</c>).</p>
<p>The current reference count after the increment has
been performed is returned.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>long</ret><nametext>driver_pdl_dec_refc(ErlDrvPDL pdl)</nametext></name>
- <fsummary></fsummary>
+ <name><ret>void</ret>
+ <nametext>driver_pdl_lock(ErlDrvPDL pdl)</nametext></name>
+ <fsummary>Lock port data lock.</fsummary>
<desc>
- <marker id="driver_pdl_dec_refc"></marker>
- <p>This function decrements the reference count of
- the port data lock passed as argument (<c>pdl</c>).</p>
- <p>The current reference count after the decrement has
- been performed is returned.</p>
+ <marker id="driver_pdl_lock"></marker>
+ <p>Locks the port data lock passed as argument (<c>pdl</c>).</p>
<p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_monitor_process(ErlDrvPort port, ErlDrvTermData process, ErlDrvMonitor *monitor)</nametext></name>
- <fsummary>Monitor a process from a driver</fsummary>
+ <name><ret>void</ret>
+ <nametext>driver_pdl_unlock(ErlDrvPDL pdl)</nametext></name>
+ <fsummary>Unlock port data lock.</fsummary>
<desc>
- <marker id="driver_monitor_process"></marker>
- <p>Start monitoring a process from a driver. When a process is
- monitored, a process exit will result in a call to the
- provided <seealso marker="driver_entry#process_exit">process_exit</seealso> call-back
- in the <seealso marker="driver_entry">ErlDrvEntry</seealso>
- structure. The <c>ErlDrvMonitor</c> structure is filled in, for later
- removal or compare.</p>
- <p>The <c>process</c> parameter should be the return value of an
- earlier call to <seealso marker="#driver_caller">driver_caller</seealso> or <seealso marker="#driver_connected">driver_connected</seealso> call.</p>
- <p>The function returns 0 on success, &lt; 0 if no call-back is
- provided and &gt; 0 if the process is no longer alive.</p>
+ <marker id="driver_pdl_unlock"></marker>
+ <p>Unlocks the port data lock passed as argument (<c>pdl</c>).</p>
+ <p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_demonitor_process(ErlDrvPort port, const ErlDrvMonitor *monitor)</nametext></name>
- <fsummary>Stop monitoring a process from a driver</fsummary>
+ <name><ret>SysIOVec *</ret><nametext>driver_peekq(ErlDrvPort port, int
+ *vlen)</nametext></name>
+ <fsummary>Get the driver queue as a vector.</fsummary>
<desc>
- <marker id="driver_demonitor_process"></marker>
- <p>This function cancels a monitor created earlier. </p>
- <p>The function returns 0 if a monitor was removed and &gt; 0
- if the monitor did no longer exist.</p>
+ <marker id="driver_peekq"></marker>
+ <p>Retrieves the driver queue as a pointer to an
+ array of <c>SysIOVec</c>s. It also returns the number of
+ elements in <c>vlen</c>. This is one of two ways to get data
+ out of the queue.</p>
+ <p>Nothing is removed from the queue by this function, that must be done
+ with <seealso marker="#driver_deq"><c>driver_deq</c></seealso>.</p>
+ <p>The returned array is suitable to use with the Unix system
+ call <c>writev</c>.</p>
+ <p>This function can be called from any thread if a
+ <seealso marker="#ErlDrvPDL">port data lock</seealso>
+ associated with the <c>port</c> is locked by the calling
+ thread during the call.</p>
</desc>
</func>
+
<func>
- <name><ret>ErlDrvTermData</ret><nametext>driver_get_monitored_process(ErlDrvPort port, const ErlDrvMonitor *monitor)</nametext></name>
- <fsummary>Retrieve the process id from a monitor</fsummary>
+ <name><ret>ErlDrvSizeT</ret><nametext>driver_peekqv(ErlDrvPort port,
+ ErlIOVec *ev)</nametext></name>
+ <fsummary>Get the driver queue as an I/O vector.</fsummary>
<desc>
- <marker id="driver_get_monitored_process"></marker>
- <p>The function returns the process id associated with a living
- monitor. It can be used in the <c>process_exit</c> call-back to
- get the process identification for the exiting process.</p>
- <p>The function returns <c>driver_term_nil</c> if the monitor
- no longer exists.</p>
+ <marker id="driver_peekqv"></marker>
+ <p>Retrieves the driver queue into a supplied
+ <c>ErlIOVec</c> <c>ev</c>. It also returns the queue size.
+ This is one of two ways to get data out of the queue.</p>
+ <p>If <c>ev</c> is <c>NULL</c>, all ones that is <c>-1</c> type cast to
+ <c>ErlDrvSizeT</c> are returned.</p>
+ <p>Nothing is removed from the queue by this function, that must be done
+ with <seealso marker="#driver_deq"><c>driver_deq</c></seealso>.</p>
+ <p>This function can be called from any thread if a
+ <seealso marker="#ErlDrvPDL">port data lock</seealso>
+ associated with the <c>port</c> is locked by the calling
+ thread during the call.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_compare_monitors(const ErlDrvMonitor *monitor1, const ErlDrvMonitor *monitor2)</nametext></name>
- <fsummary>Compare two monitors</fsummary>
+ <name><ret>int</ret><nametext>driver_pushq(ErlDrvPort port, char* buf,
+ ErlDrvSizeT len)</nametext></name>
+ <fsummary>Push data at the head of the driver queue.</fsummary>
<desc>
- <marker id="driver_compare_monitors"></marker>
- <p>This function is used to compare two <c>ErlDrvMonitor</c>s. It
- can also be used to imply some artificial order on monitors,
- for whatever reason.</p>
- <p>The function returns 0 if <c>monitor1</c> and
- <c>monitor2</c> are equal, &lt; 0 if <c>monitor1</c> is less
- than <c>monitor2</c> and &gt; 0 if <c>monitor1</c> is greater
- than <c>monitor2</c>.</p>
+ <marker id="driver_pushq"></marker>
+ <p>Puts data at the head of the driver queue. The
+ data in <c>buf</c> is copied (<c>len</c> bytes) and placed
+ at the beginning of the queue.</p>
+ <p>The return value is <c>0</c>.</p>
+ <p>This function can be called from any thread if a
+ <seealso marker="#ErlDrvPDL">port data lock</seealso>
+ associated with the <c>port</c> is locked by the calling
+ thread during the call.</p>
</desc>
</func>
+
<func>
- <name><ret>void</ret><nametext>add_driver_entry(ErlDrvEntry *de)</nametext></name>
- <fsummary>Add a driver entry</fsummary>
+ <name><ret>int</ret><nametext>driver_pushq_bin(ErlDrvPort port,
+ ErlDrvBinary *bin, ErlDrvSizeT offset, ErlDrvSizeT len)</nametext>
+ </name>
+ <fsummary>Push binary at the head of the driver queue.</fsummary>
<desc>
- <marker id="add_driver_entry"></marker>
- <p>This function adds a driver entry to the list of drivers
- known by Erlang. The <seealso marker="driver_entry#init">init</seealso> function of the <c>de</c>
- parameter is called.</p>
- <note>
- <p>To use this function for adding drivers residing in
- dynamically loaded code is dangerous. If the driver code
- for the added driver resides in the same dynamically
- loaded module (i.e. <c>.so</c> file) as a normal
- dynamically loaded driver (loaded with the <c>erl_ddll</c>
- interface), the caller should call <seealso marker="#driver_lock_driver">driver_lock_driver</seealso> before
- adding driver entries.</p>
- <p>Use of this function is generally deprecated.</p>
- </note>
+ <marker id="driver_pushq_bin"></marker>
+ <p>Puts data in the binary <c>bin</c>, at
+ <c>offset</c> with length <c>len</c> at the head of the
+ driver queue. It is most often faster than
+ <seealso marker="#driver_pushq"><c>driver_pushq</c></seealso>,
+ because no data must be copied.</p>
+ <p>This function can be called from any thread if a
+ <seealso marker="#ErlDrvPDL">port data lock</seealso>
+ associated with the <c>port</c> is locked by the calling
+ thread during the call.</p>
+ <p>The return value is <c>0</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>remove_driver_entry(ErlDrvEntry *de)</nametext></name>
- <fsummary>Remove a driver entry</fsummary>
+ <name><ret>int</ret><nametext>driver_pushqv(ErlDrvPort port, ErlIOVec
+ *ev, ErlDrvSizeT skip)</nametext></name>
+ <fsummary>Push vector at the head of the driver queue.</fsummary>
<desc>
- <marker id="remove_driver_entry"></marker>
- <p>This function removes a driver entry <c>de</c> previously
- added with <c>add_driver_entry</c>.</p>
- <p>Driver entries added by the <c>erl_ddll</c> erlang interface can
- not be removed by using this interface.</p>
+ <marker id="driver_pushqv"></marker>
+ <p>Puts the data in <c>ev</c>, skipping the first
+ <c>skip</c> bytes of it, at the head of the driver queue.
+ It is faster than
+ <seealso marker="#driver_pushq"><c>driver_pushq</c></seealso>,
+ because no data must be copied.</p>
+ <p>The return value is <c>0</c>.</p>
+ <p>This function can be called from any thread if a
+ <seealso marker="#ErlDrvPDL">port data lock</seealso>
+ associated with the <c>port</c> is locked by the calling
+ thread during the call.</p>
</desc>
</func>
+
<func>
- <name><ret>char *</ret><nametext>erl_errno_id(int error)</nametext></name>
- <fsummary>Get erlang error atom name from error number</fsummary>
+ <name><ret>int</ret><nametext>driver_read_timer(ErlDrvPort port, unsigned
+ long *time_left)</nametext></name>
+ <fsummary>Read the time left before time-out.</fsummary>
<desc>
- <marker id="erl_errno_id"></marker>
- <p>This function returns the atom name of the erlang error,
- given the error number in <c>error</c>. Error atoms are:
- <c>einval</c>, <c>enoent</c>, etc. It can be used to make
- error terms from the driver.</p>
+ <marker id="driver_read_timer"></marker>
+ <p>Reads the current time of a timer, and places
+ the result in <c>time_left</c>. This is the time in
+ milliseconds, before the time-out occurs.</p>
+ <p>The return value is <c>0</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>void</ret><nametext>erl_drv_busy_msgq_limits(ErlDrvPort port, ErlDrvSizeT *low, ErlDrvSizeT *high)</nametext></name>
- <fsummary>Set and get limits for busy port message queue</fsummary>
- <desc>
- <marker id="erl_drv_busy_msgq_limits"></marker>
- <p>Sets and gets limits that will be used for controling the
- busy state of the port message queue.</p>
- <p>The port message queue will be set into a busy
- state when the amount of command data queued on the
- message queue reaches the <c>high</c> limit. The port
- message queue will be set into a not busy state when the
- amount of command data queued on the message queue falls
- below the <c>low</c> limit. Command data is in this
- context data passed to the port using either
- <c>Port ! {Owner, {command, Data}}</c>, or
- <c>port_command/[2,3]</c>. Note that these limits
- only concerns command data that have not yet reached the
- port. The <seealso marker="#set_busy_port">busy port</seealso>
- feature can be used for data that has reached the port.</p>
-
- <p>Valid limits are values in the range
- <c>[ERL_DRV_BUSY_MSGQ_LIM_MIN, ERL_DRV_BUSY_MSGQ_LIM_MAX]</c>.
- Limits will be automatically adjusted to be sane. That is,
- the system will adjust values so that the low limit used is
- lower than or equal to the high limit used. By default the high
- limit will be 8 kB and the low limit will be 4 kB.</p>
-
- <p>By passing a pointer to an integer variable containing
- the value <c>ERL_DRV_BUSY_MSGQ_READ_ONLY</c>, currently used
- limit will be read and written back to the integer variable.
- A new limit can be set by passing a pointer to an integer
- variable containing a valid limit. The passed value will be
- written to the internal limit. The internal limit will then
- be adjusted. After this the adjusted limit will be written
- back to the integer variable from which the new value was
- read. Values are in bytes.</p>
-
- <p>The busy message queue feature can be disabled either
- by setting the <c>ERL_DRV_FLAG_NO_BUSY_MSGQ</c>
- <seealso marker="driver_entry#driver_flags">driver flag</seealso>
- in the <seealso marker="driver_entry">driver_entry</seealso>
- used by the driver, or by calling this function with
- <c>ERL_DRV_BUSY_MSGQ_DISABLED</c> as a limit (either low or
- high). When this feature has been disabled it cannot be
- enabled again. When reading the limits both of them
- will be <c>ERL_DRV_BUSY_MSGQ_DISABLED</c>, if this
- feature has been disabled.</p>
-
- <p>Processes sending command data to the port will be suspended
- if either the port is busy or if the port message queue is
- busy. Suspended processes will be resumed when neither the
- port is busy, nor the port message queue is busy.</p>
-
- <p>For information about busy port functionality
- see the documentation of the
- <seealso marker="#set_busy_port">set_busy_port()</seealso>
- function.</p>
- </desc>
- </func>
- <func>
- <name><ret>void</ret><nametext>set_busy_port(ErlDrvPort port, int on)</nametext></name>
- <fsummary>Signal or unsignal port as busy</fsummary>
+ <name><ret>void *</ret>
+ <nametext>driver_realloc(void *ptr, ErlDrvSizeT size)</nametext></name>
+ <fsummary>Resize an allocated memory block.</fsummary>
<desc>
- <marker id="set_busy_port"></marker>
- <p>This function set and unset the busy state of the port. If
- <c>on</c> is non-zero, the port is set to busy, if it's zero the port
- is set to not busy. You typically want to combine
- this feature with the <seealso marker="#erl_drv_busy_msgq_limits">busy
- port message queue</seealso> functionality.</p>
- <p>Processes sending command data to the port will be suspended
- if either the port is busy or if the port message queue
- is busy. Suspended processes will be resumed when neither the
- port is busy, nor the port message queue is busy. Command data
- is in this context data passed to the port using either
- <c>Port ! {Owner, {command, Data}}</c>, or
- <c>port_command/[2,3]</c>.</p>
- <p>If the
- <seealso marker="driver_entry#driver_flags"><![CDATA[ERL_DRV_FLAG_SOFT_BUSY]]></seealso>
- has been set in the
- <seealso marker="driver_entry">driver_entry</seealso>,
- data can be forced into the driver via
- <seealso marker="erlang#port_command/3">port_command(Port, Data, [force])</seealso>
- even though the driver has signaled that it is busy.
- </p>
- <p>For information about busy port message queue functionality
- see the documentation of the
- <seealso marker="#erl_drv_busy_msgq_limits">erl_drv_busy_msgq_limits()</seealso>
- function.</p>
- </desc>
- </func>
- <func>
- <name><ret>void</ret><nametext>set_port_control_flags(ErlDrvPort port, int flags)</nametext></name>
- <fsummary>Set flags on how to handle control entry function</fsummary>
- <desc>
- <marker id="set_port_control_flags"></marker>
- <p>This function sets flags for how the <seealso marker="driver_entry#control">control</seealso> driver entry
- function will return data to the port owner process. (The
- <c>control</c> function is called from <c>port_control/3</c>
- in erlang.)</p>
- <p>Currently there are only two meaningful values for
- <c>flags</c>: 0 means that data is returned in a list, and
- <c>PORT_CONTROL_FLAG_BINARY</c> means data is returned as
- a binary from <c>control</c>.</p>
+ <marker id="driver_realloc"></marker>
+ <p>Resizes a memory block, either in place, or by
+ allocating a new block, copying the data, and freeing the old
+ block. A pointer is returned to the reallocated memory. On
+ failure (out of memory), <c>NULL</c> is returned. (This is
+ most often a wrapper for <c>realloc</c>.)</p>
+ <p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_failure_eof(ErlDrvPort port)</nametext></name>
- <fsummary>Fail with EOF</fsummary>
+ <name><ret>ErlDrvBinary *</ret>
+ <nametext>driver_realloc_binary(ErlDrvBinary *bin, ErlDrvSizeT size)
+ </nametext></name>
+ <fsummary>Resize a driver binary.</fsummary>
<desc>
- <marker id="driver_failure_eof"></marker>
- <p>This function signals to erlang that the driver has
- encountered an EOF and should be closed, unless the port was
- opened with the <c>eof</c> option, in that case eof is sent
- to the port. Otherwise, the port is closed and an
- <c>'EXIT'</c> message is sent to the port owner process.</p>
- <p>The return value is 0.</p>
+ <marker id="driver_realloc_binary"></marker>
+ <p>Resizes a driver binary, while keeping the data.</p>
+ <p>Returns the resized driver binary on success. Returns <c>NULL</c>
+ on failure (out of memory).</p>
+ <p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_failure_atom(ErlDrvPort port, char *string)</nametext></name>
- <name><ret>int</ret><nametext>driver_failure_posix(ErlDrvPort port, int error)</nametext></name>
- <name><ret>int</ret><nametext>driver_failure(ErlDrvPort port, int error)</nametext></name>
- <fsummary>Fail with error</fsummary>
+ <name><ret>int</ret><nametext>driver_select(ErlDrvPort port, ErlDrvEvent
+ event, int mode, int on)</nametext></name>
+ <fsummary>Provides an event for having the emulator call the driver.
+ </fsummary>
<desc>
- <marker id="driver_failure_atom"></marker>
- <marker id="driver_failure_posix"></marker>
- <marker id="driver_failure"></marker>
- <p>These functions signal to Erlang that the driver has
- encountered an error and should be closed. The port is
- closed and the tuple <c>{'EXIT', error, Err}</c>, is sent to
- the port owner process, where error is an error atom
- (<c>driver_failure_atom</c> and
- <c>driver_failure_posix</c>), or an integer
- (<c>driver_failure</c>).</p>
- <p>The driver should fail only when in severe error situations,
- when the driver cannot possibly keep open, for instance
- buffer allocation gets out of memory. For normal errors
- it is more appropriate to send error codes with
- <c>driver_output</c>.</p>
- <p>The return value is 0.</p>
+ <marker id="driver_select"></marker>
+ <p>This function is used by drivers to provide the emulator with
+ events to check for. This enables the emulator to call the driver
+ when something has occurred asynchronously.</p>
+ <p>Parameter <c>event</c> identifies an OS-specific event object.
+ On Unix systems, the functions <c>select</c>/<c>poll</c> are used.
+ The event object must be a socket or pipe (or other object that
+ <c>select</c>/<c>poll</c> can use).
+ On Windows, the Win32 API function <c>WaitForMultipleObjects</c>
+ is used. This places other restrictions on the event object;
+ see the Win32 SDK documentation.</p>
+ <p>Parameter <c>on</c> is to be <c>1</c> for setting events
+ and <c>0</c> for clearing them.</p>
+ <p>Parameter <c>mode</c> is a bitwise OR combination of
+ <c>ERL_DRV_READ</c>, <c>ERL_DRV_WRITE</c>, and <c>ERL_DRV_USE</c>.
+ The first two specify whether to wait for read events and/or write
+ events. A fired read event calls
+ <seealso marker="driver_entry#ready_input">
+ <c>ready_input</c></seealso> and a fired write event calls
+ <seealso marker="driver_entry#ready_output">
+ <c>ready_output</c></seealso>.</p>
+ <note>
+ <p>Some OS (Windows) do not differentiate between read and write
+ events. The callback for a fired event then only depends on the
+ value of <c>mode</c>.</p>
+ </note>
+ <p><c>ERL_DRV_USE</c> specifies if we are using the event object or
+ if we want to close it.
+ On an emulator with SMP support, it is not safe to clear all events
+ and then close the event object after <c>driver_select</c> has
+ returned. Another thread can still be using the event object
+ internally. To safely close an event object, call
+ <c>driver_select</c> with <c>ERL_DRV_USE</c> and <c>on==0</c>, which
+ clears all events and then either calls
+ <seealso marker="driver_entry#stop_select"><c>stop_select</c></seealso>
+ or schedules it to be called when it is safe to close the event
+ object. <c>ERL_DRV_USE</c> is to be set together with the first event
+ for an event object. It is harmless to set <c>ERL_DRV_USE</c>
+ even if it already has been done. Clearing all events but keeping
+ <c>ERL_DRV_USE</c> set indicates that we are using the event
+ object and probably will set events for it again.</p>
+ <note>
+ <p><c>ERL_DRV_USE</c> was added in Erlang/OTP R13. Old drivers still
+ work as before, but it is recommended to update them to use
+ <c>ERL_DRV_USE</c> and <c>stop_select</c> to ensure that event
+ objects are closed in a safe way.</p>
+ </note>
+ <p>The return value is <c>0</c>, unless
+ <c>ready_input</c>/<c>ready_output</c> is <c>NULL</c>, in which case
+ it is <c>-1</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>ErlDrvTermData</ret><nametext>driver_connected(ErlDrvPort port)</nametext></name>
- <fsummary>Return the port owner process</fsummary>
+ <name><ret>int</ret><nametext>driver_send_term(ErlDrvPort port,
+ ErlDrvTermData receiver, ErlDrvTermData* term, int n)</nametext></name>
+ <fsummary>Send term data to other process than port owner process.
+ </fsummary>
<desc>
- <marker id="driver_connected"></marker>
- <p>This function returns the port owner process.</p>
- <p>Note that this function is <em>not</em> thread-safe, not
- even when the emulator with SMP support is used.</p>
+ <marker id="driver_send_term"></marker>
+ <warning>
+ <p><em>This function is deprecated.</em>
+ Use <seealso marker="#erl_drv_send_term">
+ <c>erl_drv_send_term</c></seealso> instead.</p>
+ </warning>
+ <note>
+ <p>The parameters of this function
+ cannot be properly checked by the runtime system when
+ executed by arbitrary threads. This can cause the
+ function not to fail when it should.</p>
+ </note>
+ <p>Parameters <c>term</c> and <c>n</c> work as in
+ <seealso marker="#erl_drv_output_term">
+ <c>erl_drv_output_term</c></seealso>.</p>
+ <p>This function is only thread-safe when the emulator with SMP
+ support is used.</p>
</desc>
</func>
+
<func>
- <name><ret>ErlDrvTermData</ret><nametext>driver_caller(ErlDrvPort port)</nametext></name>
- <fsummary>Return the process making the driver call</fsummary>
+ <name><ret>int</ret><nametext>driver_set_timer(ErlDrvPort port, unsigned
+ long time)</nametext></name>
+ <fsummary>Set a timer to call the driver.</fsummary>
<desc>
- <marker id="driver_caller"></marker>
- <p>This function returns the process id of the process that
- made the current call to the driver. The process id can be
- used with <c>driver_send_term</c> to send back data to the
- caller. <c>driver_caller()</c> only returns valid data
- when currently executing in one of the following driver
- callbacks:</p>
- <taglist>
- <tag><seealso marker="driver_entry#start">start</seealso></tag>
- <item>Called from <c>open_port/2</c>.</item>
- <tag><seealso marker="driver_entry#output">output</seealso></tag>
- <item>Called from <c>erlang:send/2</c>, and
- <c>erlang:port_command/2</c></item>
- <tag><seealso marker="driver_entry#outputv">outputv</seealso></tag>
- <item>Called from <c>erlang:send/2</c>, and
- <c>erlang:port_command/2</c></item>
- <tag><seealso marker="driver_entry#control">control</seealso></tag>
- <item>Called from <c>erlang:port_control/3</c></item>
- <tag><seealso marker="driver_entry#call">call</seealso></tag>
- <item>Called from <c>erlang:port_call/3</c></item>
- </taglist>
- <p>Note that this function is <em>not</em> thread-safe, not
- even when the emulator with SMP support is used.</p>
+ <marker id="driver_set_timer"></marker>
+ <p>Sets a timer on the driver, which will count
+ down and call the driver when it is timed out. Parameter
+ <c>time</c> is the time in milliseconds before the timer expires.</p>
+ <p>When the timer reaches <c>0</c> and expires, the driver entry
+ function <seealso marker="driver_entry#timeout">
+ <c>timeout</c></seealso> is called.</p>
+ <p>Notice that only one timer exists on each driver instance;
+ setting a new timer replaces an older one.</p>
+ <p>Return value is <c>0</c>, unless the <c>timeout</c>
+ driver function is <c>NULL</c>, in which case it is <c>-1</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>erl_drv_output_term(ErlDrvTermData port, ErlDrvTermData* term, int n)</nametext></name>
- <fsummary>Send term data from driver to port owner</fsummary>
+ <name><ret>ErlDrvSizeT</ret>
+ <nametext>driver_sizeq(ErlDrvPort port)</nametext></name>
+ <fsummary>Return the size of the driver queue.</fsummary>
<desc>
- <marker id="erl_drv_output_term"></marker>
- <p>This functions sends data in the special driver term
- format to the port owner process. This is a fast way to
- deliver term data from a driver. It also needs no binary
- conversion, so the port owner process receives data as
- normal Erlang terms. The
- <seealso marker="#erl_drv_send_term">erl_drv_send_term()</seealso>
- functions can be used for sending to any arbitrary process
- on the local node.</p>
- <note><p>Note that the <c>port</c> parameter is <em>not</em>
- an ordinary port handle, but a port handle converted using
- <c>driver_mk_port()</c>.</p></note>
- <p>The <c>term</c> parameter points to an array of
- <c>ErlDrvTermData</c>, with <c>n</c> elements. This array
- contains terms described in the driver term format. Every
- term consists of one to four elements in the array. The
- term first has a term type, and then arguments. The
- <c>port</c> parameter specifies the sending port.</p>
- <p>Tuples, maps and lists (with the exception of strings, see below),
- are built in reverse polish notation, so that to build a
- tuple, the elements are given first, and then the tuple
- term, with a count. Likewise for lists and maps.</p>
- <p>A tuple must be specified with the number of elements. (The
- elements precede the <c>ERL_DRV_TUPLE</c> term.)</p>
- <p>A list must be specified with the number of elements,
- including the tail, which is the last term preceding
- <c>ERL_DRV_LIST</c>.</p>
- <p>A map must be specified with the number of key-value pairs <c>N</c>.
- The key-value pairs must precede the <c>ERL_DRV_MAP</c> in this order:
- <c>key1,value1,key2,value2,...,keyN,valueN</c>.
- Duplicate keys are not allowed.</p>
- <p>The special term <c>ERL_DRV_STRING_CONS</c> is used to
- "splice" in a string in a list, a string given this way is
- not a list per se, but the elements are elements of the
- surrounding list.</p>
- <pre>
-Term type Argument(s)
-===========================================
-ERL_DRV_NIL
-ERL_DRV_ATOM ErlDrvTermData atom (from driver_mk_atom(char *string))
-ERL_DRV_INT ErlDrvSInt integer
-ERL_DRV_UINT ErlDrvUInt integer
-ERL_DRV_INT64 ErlDrvSInt64 *integer_ptr
-ERL_DRV_UINT64 ErlDrvUInt64 *integer_ptr
-ERL_DRV_PORT ErlDrvTermData port (from driver_mk_port(ErlDrvPort port))
-ERL_DRV_BINARY ErlDrvBinary *bin, ErlDrvUInt len, ErlDrvUInt offset
-ERL_DRV_BUF2BINARY char *buf, ErlDrvUInt len
-ERL_DRV_STRING char *str, int len
-ERL_DRV_TUPLE int sz
-ERL_DRV_LIST int sz
-ERL_DRV_PID ErlDrvTermData pid (from driver_connected(ErlDrvPort port) or driver_caller(ErlDrvPort port))
-ERL_DRV_STRING_CONS char *str, int len
-ERL_DRV_FLOAT double *dbl
-ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len
-ERL_DRV_MAP int sz
- </pre>
- <p>The unsigned integer data type <c>ErlDrvUInt</c> and the
- signed integer data type <c>ErlDrvSInt</c> are 64 bits wide
- on a 64 bit runtime system and 32 bits wide on a 32 bit
- runtime system. They were introduced in erts version 5.6,
- and replaced some of the <c>int</c> arguments in the list above.
- </p>
- <p>The unsigned integer data type <c>ErlDrvUInt64</c> and the
- signed integer data type <c>ErlDrvSInt64</c> are always 64 bits
- wide. They were introduced in erts version 5.7.4.
- </p>
-
- <p>To build the tuple <c>{tcp, Port, [100 | Binary]}</c>, the
- following call could be made.</p>
- <code type="none"><![CDATA[
- ErlDrvBinary* bin = ...
- ErlDrvPort port = ...
- ErlDrvTermData spec[] = {
- ERL_DRV_ATOM, driver_mk_atom("tcp"),
- ERL_DRV_PORT, driver_mk_port(drvport),
- ERL_DRV_INT, 100,
- ERL_DRV_BINARY, bin, 50, 0,
- ERL_DRV_LIST, 2,
- ERL_DRV_TUPLE, 3,
- };
- erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]));
- ]]>
- </code>
- <p>Where <c>bin</c> is a driver binary of length at least 50
- and <c>drvport</c> is a port handle. Note that the <c>ERL_DRV_LIST</c>
- comes after the elements of the list, likewise the
- <c>ERL_DRV_TUPLE</c>.</p>
- <p>The term <c>ERL_DRV_STRING_CONS</c> is a way to construct
- strings. It works differently from how <c>ERL_DRV_STRING</c>
- works. <c>ERL_DRV_STRING_CONS</c> builds a string list in
- reverse order, (as opposed to how <c>ERL_DRV_LIST</c>
- works), concatenating the strings added to a list. The tail
- must be given before <c>ERL_DRV_STRING_CONS</c>.</p>
- <p>The <c>ERL_DRV_STRING</c> constructs a string, and ends
- it. (So it's the same as <c>ERL_DRV_NIL</c> followed by
- <c>ERL_DRV_STRING_CONS</c>.)</p>
- <code type="none"><![CDATA[
- /* to send [x, "abc", y] to the port: */
- ErlDrvTermData spec[] = {
- ERL_DRV_ATOM, driver_mk_atom("x"),
- ERL_DRV_STRING, (ErlDrvTermData)"abc", 3,
- ERL_DRV_ATOM, driver_mk_atom("y"),
- ERL_DRV_NIL,
- ERL_DRV_LIST, 4
- };
- erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]));
- ]]></code>
- <p></p>
- <code type="none"><![CDATA[
- /* to send "abc123" to the port: */
- ErlDrvTermData spec[] = {
- ERL_DRV_NIL, /* with STRING_CONS, the tail comes first */
- ERL_DRV_STRING_CONS, (ErlDrvTermData)"123", 3,
- ERL_DRV_STRING_CONS, (ErlDrvTermData)"abc", 3,
- };
- erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]));
- ]]></code>
- <p>The <c>ERL_DRV_EXT2TERM</c> term type is used for passing a
- term encoded with the
- <seealso marker="erl_ext_dist">external format</seealso>,
- i.e., a term that has been encoded by
- <seealso marker="erlang#term_to_binary/2">erlang:term_to_binary</seealso>,
- <seealso marker="erl_interface:ei">erl_interface</seealso>, etc.
- For example, if <c>binp</c> is a pointer to an <c>ErlDrvBinary</c>
- that contains the term <c>{17, 4711}</c> encoded with the
- <seealso marker="erl_ext_dist">external format</seealso>
- and you want to wrap it in a two tuple with the tag <c>my_tag</c>,
- i.e., <c>{my_tag, {17, 4711}}</c>, you can do as follows:
- </p>
- <code type="none"><![CDATA[
- ErlDrvTermData spec[] = {
- ERL_DRV_ATOM, driver_mk_atom("my_tag"),
- ERL_DRV_EXT2TERM, (ErlDrvTermData) binp->orig_bytes, binp->orig_size
- ERL_DRV_TUPLE, 2,
- };
- erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]));
- ]]></code>
-
- <p>To build the map <c>#{key1 => 100, key2 => {200, 300}}</c>, the
- following call could be made.</p>
- <code type="none"><![CDATA[
- ErlDrvPort port = ...
- ErlDrvTermData spec[] = {
- ERL_DRV_ATOM, driver_mk_atom("key1"),
- ERL_DRV_INT, 100,
- ERL_DRV_ATOM, driver_mk_atom("key2"),
- ERL_DRV_INT, 200,
- ERL_DRV_INT, 300,
- ERL_DRV_TUPLE, 2,
- ERL_DRV_MAP, 2
- };
- erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]));
- ]]>
- </code>
-
- <p>If you want to pass a binary and don't already have the content
- of the binary in an <c>ErlDrvBinary</c>, you can benefit from using
- <c>ERL_DRV_BUF2BINARY</c> instead of creating an <c>ErlDrvBinary</c>
- via <c>driver_alloc_binary()</c> and then pass the binary via
- <c>ERL_DRV_BINARY</c>. The runtime system will often allocate
- binaries smarter if <c>ERL_DRV_BUF2BINARY</c> is used.
- However, if the content of the binary to pass already resides in
- an <c>ErlDrvBinary</c>, it is normally better to pass the binary
- using <c>ERL_DRV_BINARY</c> and the <c>ErlDrvBinary</c> in question.
- </p>
- <p>The <c>ERL_DRV_UINT</c>, <c>ERL_DRV_BUF2BINARY</c>, and
- <c>ERL_DRV_EXT2TERM</c> term types were introduced in the 5.6
- version of erts.
- </p>
- <p>This function is only thread-safe when the emulator with SMP
- support is used.</p>
+ <marker id="driver_sizeq"></marker>
+ <p>Returns the number of bytes currently in the driver queue.</p>
+ <p>This function can be called from any thread if a
+ <seealso marker="#ErlDrvPDL">port data lock</seealso>
+ associated with the <c>port</c> is locked by the calling
+ thread during the call.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_output_term(ErlDrvPort port, ErlDrvTermData* term, int n)</nametext></name>
- <fsummary>Send term data from driver to port owner</fsummary>
+ <name><ret>void</ret><nametext>driver_system_info(ErlDrvSysInfo
+ *sys_info_ptr, size_t size)</nametext></name>
+ <fsummary>Get information about the Erlang runtime system.</fsummary>
<desc>
- <marker id="driver_output_term"></marker>
- <warning><p><c>driver_output_term()</c> is deprecated and will
- be removed in the OTP-R17 release. Use
- <seealso marker="#erl_drv_send_term">erl_drv_output_term()</seealso>
- instead.</p>
- </warning>
- <p>The parameters <c>term</c> and <c>n</c> do the same thing
- as in <seealso marker="#erl_drv_output_term">erl_drv_output_term()</seealso>.</p>
- <p>Note that this function is <em>not</em> thread-safe, not
- even when the emulator with SMP support is used.</p>
+ <marker id="driver_system_info"></marker>
+ <p>Writes information about the Erlang runtime system into the
+ <seealso marker="#ErlDrvSysInfo"><c>ErlDrvSysInfo</c></seealso>
+ structure referred to by the first argument. The second
+ argument is to be the size of the
+ <seealso marker="#ErlDrvSysInfo"><c>ErlDrvSysInfo</c></seealso>
+ structure, that is, <c>sizeof(ErlDrvSysInfo)</c>.</p>
+ <p>For information about specific fields, see
+ <seealso marker="#ErlDrvSysInfo"><c>ErlDrvSysInfo</c></seealso>.</p>
</desc>
</func>
+
<func>
- <name><ret>ErlDrvTermData</ret><nametext>driver_mk_atom(char* string)</nametext></name>
- <fsummary>Make an atom from a name</fsummary>
+ <name><ret>ErlDrvSizeT</ret><nametext>driver_vec_to_buf(ErlIOVec *ev,
+ char *buf, ErlDrvSizeT len)</nametext></name>
+ <fsummary>Collect data segments into a buffer.</fsummary>
<desc>
- <marker id="driver_mk_atom"></marker>
- <p>This function returns an atom given a name
- <c>string</c>. The atom is created and won't change, so the
- return value may be saved and reused, which is faster than
- looking up the atom several times.</p>
- <p>Note that this function is <em>not</em> thread-safe, not
- even when the emulator with SMP support is used.</p>
+ <marker id="driver_vec_to_buf"></marker>
+ <p>Collects several segments of data, referenced
+ by <c>ev</c>, by copying them in order to the buffer
+ <c>buf</c>, of the size <c>len</c>.</p>
+ <p>If the data is to be sent from the driver to the port owner
+ process, it is faster to use
+ <seealso marker="#driver_outputv"><c>driver_outputv</c></seealso>.</p>
+ <p>The return value is the space left in the buffer, that is, if
+ <c>ev</c> contains less than <c>len</c> bytes it is the
+ difference, and if <c>ev</c> contains <c>len</c> bytes or more,
+ it is <c>0</c>. This is faster if there is more than one header byte,
+ as the binary syntax can construct integers directly from
+ the binary.</p>
</desc>
</func>
+
<func>
- <name><ret>ErlDrvTermData</ret><nametext>driver_mk_port(ErlDrvPort port)</nametext></name>
- <fsummary>Make a erlang term port from a port</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_busy_msgq_limits(ErlDrvPort port,
+ ErlDrvSizeT *low, ErlDrvSizeT *high)</nametext></name>
+ <fsummary>Set and get limits for busy port message queue.</fsummary>
<desc>
- <marker id="driver_mk_port"></marker>
- <p>This function converts a port handle to the erlang term
- format, usable in the <seealso marker="#erl_drv_output_term">erl_drv_output_term()</seealso>, and <seealso marker="#erl_drv_send_term">erl_drv_send_term()</seealso> functions.</p>
- <p>Note that this function is <em>not</em> thread-safe, not
- even when the emulator with SMP support is used.</p>
+ <marker id="erl_drv_busy_msgq_limits"></marker>
+ <p>Sets and gets limits that will be used for controlling the
+ busy state of the port message queue.</p>
+ <p>The port message queue is set into a busy
+ state when the amount of command data queued on the
+ message queue reaches the <c>high</c> limit. The port
+ message queue is set into a not busy state when the
+ amount of command data queued on the message queue falls
+ below the <c>low</c> limit. Command data is in this
+ context data passed to the port using either
+ <c>Port ! {Owner, {command, Data}}</c> or
+ <c>port_command/[2,3]</c>. Notice that these limits
+ only concerns command data that have not yet reached the
+ port. The <seealso marker="#set_busy_port">busy port</seealso>
+ feature can be used for data that has reached the port.</p>
+ <p>Valid limits are values in the range
+ <c>[ERL_DRV_BUSY_MSGQ_LIM_MIN, ERL_DRV_BUSY_MSGQ_LIM_MAX]</c>.
+ Limits are automatically adjusted to be sane. That is,
+ the system adjusts values so that the low limit used is
+ lower than or equal to the high limit used. By default the high
+ limit is 8 kB and the low limit is 4 kB.</p>
+ <p>By passing a pointer to an integer variable containing
+ the value <c>ERL_DRV_BUSY_MSGQ_READ_ONLY</c>, the currently used
+ limit is read and written back to the integer variable.
+ A new limit can be set by passing a pointer to an integer
+ variable containing a valid limit. The passed value is
+ written to the internal limit. The internal limit is then
+ adjusted. After this the adjusted limit is written
+ back to the integer variable from which the new value was
+ read. Values are in bytes.</p>
+ <p>The busy message queue feature can be disabled either
+ by setting the <c>ERL_DRV_FLAG_NO_BUSY_MSGQ</c>
+ <seealso marker="driver_entry#driver_flags">driver flag</seealso>
+ in the <seealso marker="driver_entry"><c>driver_entry</c></seealso>
+ used by the driver, or by calling this function with
+ <c>ERL_DRV_BUSY_MSGQ_DISABLED</c> as a limit (either low or
+ high). When this feature has been disabled, it cannot be
+ enabled again. When reading the limits, both are
+ <c>ERL_DRV_BUSY_MSGQ_DISABLED</c> if this
+ feature has been disabled.</p>
+ <p>Processes sending command data to the port are suspended
+ if either the port is busy or if the port message queue is
+ busy. Suspended processes are resumed when neither the
+ port or the port message queue is busy.</p>
+ <p>For information about busy port functionality, see
+ <seealso marker="#set_busy_port"><c>set_busy_port</c></seealso>.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>erl_drv_send_term(ErlDrvTermData port, ErlDrvTermData receiver, ErlDrvTermData* term, int n)</nametext></name>
- <fsummary>Send term data to other process than port owner process</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_cond_broadcast(ErlDrvCond
+ *cnd)</nametext></name>
+ <fsummary>Broadcast on a condition variable.</fsummary>
<desc>
- <marker id="erl_drv_send_term"></marker>
- <p>This function is the only way for a driver to send data to
- <em>other</em> processes than the port owner process. The
- <c>receiver</c> parameter specifies the process to receive
- the data.</p>
- <note><p>Note that the <c>port</c> parameter is <em>not</em>
- an ordinary port handle, but a port handle converted using
- <c>driver_mk_port()</c>.</p></note>
- <p>The parameters <c>port</c>, <c>term</c> and <c>n</c> do the same thing
- as in <seealso marker="#erl_drv_output_term">erl_drv_output_term()</seealso>.</p>
- <p>This function is only thread-safe when the emulator with SMP
- support is used.</p>
+ <marker id="erl_drv_cond_broadcast"></marker>
+ <p>Broadcasts on a condition variable. That is, if
+ other threads are waiting on the condition variable being
+ broadcast on, <em>all</em> of them are woken.</p>
+ <p><c>cnd</c> is a pointer to a condition variable to broadcast on.</p>
+ <p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_send_term(ErlDrvPort port, ErlDrvTermData receiver, ErlDrvTermData* term, int n)</nametext></name>
- <fsummary>Send term data to other process than port owner process</fsummary>
+ <name><ret>ErlDrvCond *</ret><nametext>erl_drv_cond_create(char
+ *name)</nametext></name>
+ <fsummary>Create a condition variable.</fsummary>
<desc>
- <marker id="driver_send_term"></marker>
- <warning><p><c>driver_send_term()</c> is deprecated and will
- be removed in the OTP-R17 release. Use
- <seealso marker="#erl_drv_send_term">erl_drv_send_term()</seealso>
- instead.</p>
- <p>Also note that parameters of <c>driver_send_term()</c>
- cannot be properly checked by the runtime system when
- executed by arbitrary threads. This may cause the
- <c>driver_send_term()</c> function not to fail when
- it should.</p>
- </warning>
- <p>The parameters <c>term</c> and <c>n</c> do the same thing
- as in <seealso marker="#erl_drv_output_term">erl_drv_output_term()</seealso>.</p>
- <p>This function is only thread-safe when the emulator with SMP
- support is used.</p>
+ <marker id="erl_drv_cond_create"></marker>
+ <p>Creates a condition variable and returns a pointer to it.</p>
+ <p><c>name</c> is a string identifying the created condition variable.
+ It is used to identify the condition variable in planned
+ future debug functionality.</p>
+ <p>Returns <c>NULL</c> on failure. The driver
+ creating the condition variable is responsible for
+ destroying it before the driver is unloaded.</p>
+ <p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>long</ret><nametext>driver_async (ErlDrvPort port, unsigned int* key, void (*async_invoke)(void*), void* async_data, void (*async_free)(void*))</nametext></name>
- <fsummary>Perform an asynchronous call within a driver</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_cond_destroy(ErlDrvCond
+ *cnd)</nametext></name>
+ <fsummary>Destroy a condition variable.</fsummary>
<desc>
- <marker id="driver_async"></marker>
- <p>This function performs an asynchronous call. The function
- <c>async_invoke</c> is invoked in a thread separate from the
- emulator thread. This enables the driver to perform
- time-consuming, blocking operations without blocking the
- emulator.</p>
- <p>The async thread pool size can be set with the
- <seealso marker="erl#async_thread_pool_size">+A</seealso>
- command line argument of <seealso marker="erl">erl(1)</seealso>.
- If no async thread pool is available, the call is made
- synchronously in the thread calling <c>driver_async()</c>. The
- current number of async threads in the async thread pool can be
- retrieved via
- <seealso marker="#driver_system_info">driver_system_info()</seealso>.</p>
- <p>If there is a thread pool available, a thread will be
- used. If the <c>key</c> argument is null, the threads from the
- pool are used in a round-robin way, each call to
- <c>driver_async</c> uses the next thread in the pool. With the
- <c>key</c> argument set, this behaviour is changed. The two
- same values of <c>*key</c> always get the same thread.</p>
- <p>To make sure that a driver instance always uses the same
- thread, the following call can be used:</p>
- <p></p>
- <code type="none"><![CDATA[
- unsigned int myKey = driver_async_port_key(myPort);
+ <marker id="erl_drv_cond_destroy"></marker>
+ <p>Destroys a condition variable previously created by
+ <seealso marker="#erl_drv_cond_create">
+ <c>erl_drv_cond_create</c></seealso>.</p>
+ <p><c>cnd</c> is a pointer to a condition variable to destroy.</p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
- r = driver_async(myPort, &myKey, myData, myFunc);
- ]]></code>
- <p>It is enough to initialize <c>myKey</c> once for each
- driver instance.</p>
- <p>If a thread is already working, the calls will be
- queued up and executed in order. Using the same thread for
- each driver instance ensures that the calls will be made in
- sequence.</p>
- <p>The <c>async_data</c> is the argument to the functions
- <c>async_invoke</c> and <c>async_free</c>. It's typically a
- pointer to a structure that contains a pipe or event that
- can be used to signal that the async operation completed.
- The data should be freed in <c>async_free</c>.</p>
- <p>When the async operation is done, <seealso marker="driver_entry#ready_async">ready_async</seealso> driver
- entry function is called. If <c>ready_async</c> is null in
- the driver entry, the <c>async_free</c> function is called
- instead.</p>
- <p>The return value is -1 if the <c>driver_async</c> call
- fails.</p>
+ <func>
+ <name><ret>char *</ret><nametext>erl_drv_cond_name(ErlDrvCond
+ *cnd)</nametext></name>
+ <fsummary>Get name of driver mutex.</fsummary>
+ <desc>
+ <marker id="erl_drv_cnd_name"></marker>
+ <p>Returns a pointer to the name of the condition.</p>
+ <p><c>cnd</c> is a pointer to an initialized condition.</p>
<note>
- <p>As of erts version 5.5.4.3 the default stack size for
- threads in the async-thread pool is 16 kilowords,
- i.e., 64 kilobyte on 32-bit architectures.
- This small default size has been chosen since the
- amount of async-threads might be quite large. The
- default stack size is enough for drivers delivered
- with Erlang/OTP, but might not be sufficiently large
- for other dynamically linked in drivers that use the
- driver_async() functionality. A suggested stack size
- for threads in the async-thread pool can be configured
- via the
- <seealso marker="erl#async_thread_stack_size">+a</seealso>
- command line argument of
- <seealso marker="erl">erl(1)</seealso>.</p>
+ <p>This function is intended for debugging purposes only.</p>
</note>
</desc>
</func>
+
<func>
- <name><ret>unsigned int</ret><nametext>driver_async_port_key (ErlDrvPort port)</nametext></name>
- <fsummary>Calculate an async key from an ErlDrvPort</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_cond_signal(ErlDrvCond
+ *cnd)</nametext></name>
+ <fsummary>Signal on a condition variable.</fsummary>
<desc>
- <marker id="driver_async_port_key"></marker>
- <p>This function calculates a key for later use in <seealso
- marker="#driver_async">driver_async()</seealso>. The keys are
- evenly distributed so that a fair mapping between port id's
- and async thread id's is achieved.</p>
- <note>
- <p>Before OTP-R16, the actual port id could be used as a key
- with proper casting, but after the rewrite of the port
- subsystem, this is no longer the case. With this function, you
- can achieve the same distribution based on port id's as before
- OTP-R16.</p>
- </note>
+ <marker id="erl_drv_cond_signal"></marker>
+ <p>Signals on a condition variable. That is, if
+ other threads are waiting on the condition variable being
+ signaled, <em>one</em> of them is woken.</p>
+ <p><c>cnd</c> is a pointer to a condition variable to signal on.</p>
+ <p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_lock_driver(ErlDrvPort port)</nametext></name>
- <fsummary>Make sure the driver is never unloaded</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_cond_wait(ErlDrvCond *cnd,
+ ErlDrvMutex *mtx)</nametext></name>
+ <fsummary>Wait on a condition variable.</fsummary>
<desc>
- <marker id="driver_lock_driver"></marker>
- <p>This function locks the driver used by the port <c>port</c>
- in memory for the rest of the emulator process'
- lifetime. After this call, the driver behaves as one of Erlang's
- statically linked in drivers.</p>
+ <marker id="erl_drv_cond_wait"></marker>
+ <p>Waits on a condition variable. The calling
+ thread is blocked until another thread wakes it by signaling
+ or broadcasting on the condition variable. Before the calling
+ thread is blocked, it unlocks the mutex passed as argument.
+ When the calling thread is woken, it locks the same mutex before
+ returning. That is, the mutex currently must be locked by
+ the calling thread when calling this function.</p>
+ <p><c>cnd</c> is a pointer to a condition variable to wait on.
+ <c>mtx</c> is a pointer to a mutex to unlock while waiting.</p>
+ <note>
+ <p><c>erl_drv_cond_wait</c> can return even if
+ no one has signaled or broadcast on the condition
+ variable. Code calling <c>erl_drv_cond_wait</c> is
+ always to be prepared for <c>erl_drv_cond_wait</c>
+ returning even if the condition that the thread was
+ waiting for has not occurred. That is, when returning from
+ <c>erl_drv_cond_wait</c>, always check if the condition
+ has occurred, and if not call <c>erl_drv_cond_wait</c> again.</p>
+ </note>
+ <p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>ErlDrvPort</ret><nametext>driver_create_port(ErlDrvPort port, ErlDrvTermData owner_pid, char* name, ErlDrvData drv_data)</nametext></name>
- <fsummary>Create a new port (driver instance)</fsummary>
+ <name><ret>int</ret><nametext>erl_drv_consume_timeslice(ErlDrvPort port,
+ int percent)</nametext></name>
+ <fsummary>Give the runtime system a hint about how much CPU time the
+ current driver callback call has consumed.</fsummary>
<desc>
- <p>This function creates a new port executing the same driver
- code as the port creating the new port.
- A short description of the arguments:</p>
+ <marker id="erl_drv_consume_timeslice"></marker>
+ <p>Gives the runtime system a hint about how much CPU time the current
+ driver callback call has consumed since the last hint, or since the
+ the start of the callback if no previous hint has been given.</p>
<taglist>
<tag><c>port</c></tag>
- <item>The port handle of the port (driver instance) creating
- the new port.</item>
- <tag><c>owner_pid</c></tag>
- <item>The process id of the Erlang process which will be
- owner of the new port. This process will be linked
- to the new port. You usually want to use
- <c>driver_caller(port)</c> as <c>owner_pid</c>.</item>
- <tag><c>name</c></tag>
- <item>The port name of the new port. You usually want to
- use the same port name as the driver name
- (<seealso marker="driver_entry#driver_name">driver_name</seealso>
- field of the
- <seealso marker="driver_entry">driver_entry</seealso>).</item>
- <tag><c>drv_data</c></tag>
- <item>The driver defined handle that will be passed in subsequent
- calls to driver call-backs. Note, that the
- <seealso marker="driver_entry#start">driver start call-back</seealso>
- will not be called for this new driver instance.
- The driver defined handle is normally created in the
- <seealso marker="driver_entry#start">driver start call-back</seealso>
- when a port is created via
- <seealso marker="erlang#open_port/2">erlang:open_port/2</seealso>. </item>
+ <item>Port handle of the executing port.</item>
+ <tag><c>percent</c></tag>
+ <item>Approximate consumed fraction of a full
+ time-slice in percent.</item>
</taglist>
- <p>The caller of <c>driver_create_port()</c> is allowed to
- manipulate the newly created port when <c>driver_create_port()</c>
- has returned. When
- <seealso marker="#smp_support">port level locking</seealso>
- is used, the creating port is, however, only allowed to
- manipulate the newly created port until the current driver
- call-back that was called by the emulator returns.</p>
- <note>
- <p>When
- <seealso marker="#smp_support">port level locking</seealso>
- is used, the creating port is only allowed to manipulate
- the newly created port until the current driver call-back
- returns.</p>
- </note>
+ <p>The time is specified as a fraction, in percent, of a full time-slice
+ that a port is allowed to execute before it is to surrender the
+ CPU to other runnable ports or processes. Valid range is
+ <c>[1, 100]</c>. The scheduling time-slice is not an exact entity,
+ but can usually be approximated to about 1 millisecond.</p>
+ <p>Notice that it is up to the runtime system to determine if and
+ how to use this information. Implementations on some platforms
+ can use other means to determine the consumed fraction
+ of the time-slice. Lengthy driver callbacks should, regardless of
+ this, frequently call this function to determine if it is allowed
+ to continue execution or not.</p>
+ <p>This function returns a non-zero value
+ if the time-slice has been exhausted, and zero if the callback is
+ allowed to continue execution. If a non-zero value is
+ returned, the driver callback is to return as soon as possible in
+ order for the port to be able to yield.</p>
+ <p>This function is provided to better support co-operative scheduling,
+ improve system responsiveness, and to make it easier to prevent
+ misbehaviors of the VM because of a port monopolizing a scheduler
+ thread. It can be used when dividing lengthy work into some repeated
+ driver callback calls, without the need to use threads.</p>
+ <p>See also the important <seealso marker="#WARNING">warning</seealso>
+ text at the beginning of this manual page.</p>
</desc>
</func>
<func>
- <name><ret>int</ret><nametext>erl_drv_thread_create(char *name,
- ErlDrvTid *tid,
- void * (*func)(void *),
- void *arg,
- ErlDrvThreadOpts *opts)</nametext></name>
- <fsummary>Create a thread</fsummary>
+ <name><ret>ErlDrvTime</ret><nametext>erl_drv_convert_time_unit(ErlDrvTime
+ val, ErlDrvTimeUnit from, ErlDrvTimeUnit to)</nametext></name>
+ <fsummary>Convert time unit of a time value.</fsummary>
<desc>
- <marker id="erl_drv_thread_create"></marker>
- <p>Arguments:</p>
+ <marker id="erl_drv_convert_time_unit"></marker>
+ <p>Converts the <c>val</c> value of time unit <c>from</c> to
+ the corresponding value of time unit <c>to</c>. The result is
+ rounded using the floor function.</p>
<taglist>
- <tag><c>name</c></tag>
- <item>A string identifying the created thread. It will be used
- to identify the thread in planned future debug
- functionality.
- </item>
- <tag><c>tid</c></tag>
- <item>A pointer to a thread identifier variable.</item>
- <tag><c>func</c></tag>
- <item>A pointer to a function to execute in the created thread.</item>
- <tag><c>arg</c></tag>
- <item>A pointer to argument to the <c>func</c> function.</item>
- <tag><c>opts</c></tag>
- <item>A pointer to thread options to use or <c>NULL</c>.</item>
+ <tag><c>val</c></tag>
+ <item>Value to convert time unit for.</item>
+ <tag><c>from</c></tag>
+ <item>Time unit of <c>val</c>.</item>
+ <tag><c>to</c></tag>
+ <item>Time unit of returned value.</item>
</taglist>
- <p>This function creates a new thread. On success <c>0</c> is returned;
- otherwise, an <c>errno</c> value is returned to indicate the error.
- The newly created thread will begin executing in the function pointed
- to by <c>func</c>, and <c>func</c> will be passed <c>arg</c> as
- argument. When <c>erl_drv_thread_create()</c> returns the thread
- identifier of the newly created thread will be available in
- <c>*tid</c>. <c>opts</c> can be either a <c>NULL</c> pointer, or a
- pointer to an
- <seealso marker="#ErlDrvThreadOpts">ErlDrvThreadOpts</seealso>
- structure. If <c>opts</c> is a <c>NULL</c> pointer, default options
- will be used; otherwise, the passed options will be used.
- </p>
- <warning><p>You are not allowed to allocate the
- <seealso marker="#ErlDrvThreadOpts">ErlDrvThreadOpts</seealso>
- structure by yourself. It has to be allocated and
- initialized by
- <seealso marker="#erl_drv_thread_opts_create">erl_drv_thread_opts_create()</seealso>.
- </p></warning>
- <p>The created thread will terminate either when <c>func</c> returns
- or if
- <seealso marker="#erl_drv_thread_exit">erl_drv_thread_exit()</seealso>
- is called by the thread. The exit value of the thread is either
- returned from <c>func</c> or passed as argument to
- <seealso marker="#erl_drv_thread_exit">erl_drv_thread_exit()</seealso>.
- The driver creating the thread has the responsibility of joining the
- thread, via
- <seealso marker="#erl_drv_thread_join">erl_drv_thread_join()</seealso>,
- before the driver is unloaded. It is not possible to create
- "detached" threads, i.e., threads that don't need to be joined.
- </p>
- <warning><p>All created threads need to be joined by the driver before
- it is unloaded. If the driver fails to join all threads
- created before it is unloaded, the runtime system will
- most likely crash when the code of the driver is unloaded.
- </p></warning>
- <p>This function is thread-safe.</p>
+ <p>Returns <c>ERL_DRV_TIME_ERROR</c> if called with an invalid
+ time unit argument.</p>
+ <p>See also <seealso marker="#ErlDrvTime">
+ <c>ErlDrvTime</c></seealso> and
+ <seealso marker="#ErlDrvTimeUnit">
+ <c>ErlDrvTimeUnit</c></seealso>.</p>
</desc>
</func>
<func>
- <name><ret>ErlDrvThreadOpts *</ret><nametext>erl_drv_thread_opts_create(char *name)</nametext></name>
- <fsummary>Create thread options</fsummary>
+ <name><ret>int</ret><nametext>erl_drv_equal_tids(ErlDrvTid tid1,
+ ErlDrvTid tid2)</nametext></name>
+ <fsummary>Compare thread identifiers for equality.</fsummary>
<desc>
- <marker id="erl_drv_thread_opts_create"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>name</c></tag>
- <item>A string identifying the created thread options. It will be used
- to identify the thread options in planned future debug
- functionality.
- </item>
- </taglist>
- <p>This function allocates and initialize a thread option
- structure. On failure <c>NULL</c> is returned. A thread option
- structure is used for passing options to
- <seealso marker="#erl_drv_thread_create">erl_drv_thread_create()</seealso>.
- If the structure isn't modified before it is passed to
- <seealso marker="#erl_drv_thread_create">erl_drv_thread_create()</seealso>,
- the default values will be used.
- </p>
- <warning><p>You are not allowed to allocate the
- <seealso marker="#ErlDrvThreadOpts">ErlDrvThreadOpts</seealso>
- structure by yourself. It has to be allocated and
- initialized by <c>erl_drv_thread_opts_create()</c>.
- </p></warning>
+ <marker id="erl_drv_equal_tids"></marker>
+ <p>Compares two thread identifiers, <c>tid1</c> and <c>tid2</c>,
+ for equality.</p>
+ <p>Returns <c>0</c> it they are not equal, and a value not equal to
+ <c>0</c> if they are equal.</p>
+ <note>
+ <p>A thread identifier can be reused very quickly after
+ a thread has terminated. Therefore, if a thread
+ corresponding to one of the involved thread identifiers
+ has terminated since the thread identifier was saved,
+ the result of <c>erl_drv_equal_tids</c> does possibly not give
+ the expected result.</p>
+ </note>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_thread_opts_destroy(ErlDrvThreadOpts *opts)</nametext></name>
- <fsummary>Destroy thread options</fsummary>
+ <name><ret>int</ret><nametext>erl_drv_getenv(const char *key, char
+ *value, size_t *value_size)</nametext></name>
+ <fsummary>Get the value of an environment variable.</fsummary>
<desc>
- <marker id="erl_drv_thread_opts_destroy"></marker>
- <p>Arguments:</p>
+ <marker id="erl_drv_getenv"></marker>
+ <p>Retrieves the value of an environment variable.</p>
<taglist>
- <tag><c>opts</c></tag>
- <item>A pointer to thread options to destroy.</item>
+ <tag><c>key</c></tag>
+ <item>A <c>NULL</c>-terminated string containing the
+ name of the environment variable.</item>
+ <tag><c>value</c></tag>
+ <item>A pointer to an output buffer.</item>
+ <tag><c>value_size</c></tag>
+ <item>A pointer to an integer. The integer is used both for
+ passing input and output sizes (see below).</item>
</taglist>
- <p>This function destroys thread options previously created by
- <seealso marker="#erl_drv_thread_opts_create">erl_drv_thread_opts_create()</seealso>.
- </p>
+ <p>When this function is called, <c>*value_size</c> is to contain the
+ size of the <c>value</c> buffer.</p>
+ <p>On success, <c>0</c> is returned,
+ the value of the environment variable has been written to
+ the <c>value</c> buffer, and <c>*value_size</c> contains the
+ string length (excluding the terminating <c>NULL</c> character) of
+ the value written to the <c>value</c> buffer.</p>
+ <p>On failure, that is, no such environment variable was found,
+ a value &lt; <c>0</c> is returned. When the size of the <c>value</c>
+ buffer is too small, a value &gt; <c>0</c> is returned and
+ <c>*value_size</c> has been set to the buffer size needed.</p>
+ <warning>
+ <p>Do <em>not</em> use libc's <c>getenv</c> or similar C library
+ interfaces from a driver.</p>
+ </warning>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_thread_exit(void *exit_value)</nametext></name>
- <fsummary>Terminate calling thread</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_init_ack(ErlDrvPort port,
+ ErlDrvData res)</nametext></name>
+ <fsummary>Acknowledge the start of the port.</fsummary>
<desc>
- <marker id="erl_drv_thread_exit"></marker>
- <p>Arguments:</p>
+ <marker id="erl_drv_init_ack"></marker>
+ <p>Acknowledges the start of the port.</p>
<taglist>
- <tag><c>exit_value</c></tag>
- <item>A pointer to an exit value or <c>NULL</c>.</item>
+ <tag><c>port</c></tag>
+ <item>The port handle of the port (driver instance)
+ doing the acknowledgment.
+ </item>
+ <tag><c>res</c></tag>
+ <item>The result of the port initialization. Can be the same
+ values as the return value of <seealso marker="driver_entry#start">
+ <c>start</c></seealso>, that is, any of the error codes or the
+ <c>ErlDrvData</c> that is to be used for this port.
+ </item>
</taglist>
- <p>This function terminates the calling thread with the exit
- value passed as argument. You are only allowed to terminate
- threads created with
- <seealso marker="#erl_drv_thread_create">erl_drv_thread_create()</seealso>.
- The exit value can later be retrieved by another thread via
- <seealso marker="#erl_drv_thread_join">erl_drv_thread_join()</seealso>.
- </p>
+ <p>When this function is called the initiating <c>erlang:open_port</c>
+ call is returned as if the <seealso marker="driver_entry#start">
+ <c>start</c></seealso> function had just been called. It can only be
+ used when flag <seealso marker="driver_entry#driver_flags">
+ <c>ERL_DRV_FLAG_USE_INIT_ACK</c></seealso>
+ has been set on the linked-in driver.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ErlDrvTime</ret>
+ <nametext>erl_drv_monotonic_time(ErlDrvTimeUnit time_unit)</nametext>
+ </name>
+ <fsummary>Get Erlang monotonic time.</fsummary>
+ <desc>
+ <marker id="erl_drv_monotonic_time"></marker>
+ <p>Returns <seealso marker="time_correction#Erlang_Monotonic_Time">
+ Erlang monotonic time</seealso>. Notice that negative values are
+ not uncommon.</p>
+ <p><c>time_unit</c> is time unit of returned value.</p>
+ <p>Returns <c>ERL_DRV_TIME_ERROR</c> if called with an invalid
+ time unit argument, or if called from a thread that is not a
+ scheduler thread.</p>
+ <p>See also <seealso marker="#ErlDrvTime"><c>ErlDrvTime</c></seealso>
+ and <seealso marker="#ErlDrvTimeUnit">
+ <c>ErlDrvTimeUnit</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ErlDrvMutex *</ret><nametext>erl_drv_mutex_create(char
+ *name)</nametext></name>
+ <fsummary>Create a mutex.</fsummary>
+ <desc>
+ <marker id="erl_drv_mutex_create"></marker>
+ <p>Creates a mutex and returns a pointer to it.</p>
+ <p><c>name</c> is a string identifying the created mutex. It is used
+ to identify the mutex in planned future debug functionality.</p>
+ <p>Returns <c>NULL</c> on failure. The driver creating the mutex is
+ responsible for destroying it before the driver is unloaded.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>int</ret><nametext>erl_drv_thread_join(ErlDrvTid tid, void **exit_value)</nametext></name>
- <fsummary>Join with another thread</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_mutex_destroy(ErlDrvMutex
+ *mtx)</nametext></name>
+ <fsummary>Destroy a mutex.</fsummary>
<desc>
- <marker id="erl_drv_thread_join"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>tid</c></tag>
- <item>The thread identifier of the thread to join.</item>
- <tag><c>exit_value</c></tag>
- <item>A pointer to a pointer to an exit value, or <c>NULL</c>.</item>
- </taglist>
- <p>This function joins the calling thread with another thread, i.e.,
- the calling thread is blocked until the thread identified by
- <c>tid</c> has terminated. On success <c>0</c> is returned;
- otherwise, an <c>errno</c> value is returned to indicate the error.
- A thread can only be joined once. The behavior of joining
- more than once is undefined, an emulator crash is likely. If
- <c>exit_value == NULL</c>, the exit value of the terminated thread
- will be ignored; otherwise, the exit value of the terminated thread
- will be stored at <c>*exit_value</c>.
- </p>
+ <marker id="erl_drv_mutex_destroy"></marker>
+ <p>Destroys a mutex previously created by
+ <seealso marker="#erl_drv_mutex_create">
+ <c>erl_drv_mutex_create</c></seealso>.
+ The mutex must be in an unlocked state before it is destroyed.</p>
+ <p><c>mtx</c> is a pointer to a mutex to destroy.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>ErlDrvTid</ret><nametext>erl_drv_thread_self(void)</nametext></name>
- <fsummary>Get the thread identifier of the current thread</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_mutex_lock(ErlDrvMutex
+ *mtx)</nametext></name>
+ <fsummary>Lock a mutex.</fsummary>
<desc>
- <marker id="erl_drv_thread_self"></marker>
- <p>This function returns the thread identifier of the
- calling thread.
- </p>
+ <marker id="erl_drv_mutex_lock"></marker>
+ <p>Locks a mutex. The calling thread is blocked until the mutex has
+ been locked. A thread that has currently locked the mutex
+ <em>cannot</em> lock the same mutex again.</p>
+ <p><c>mtx</c> is a pointer to a mutex to lock.</p>
+ <warning>
+ <p>If you leave a mutex locked in an emulator thread
+ when you let the thread out of your control, you will
+ <em>very likely</em> deadlock the whole emulator.</p>
+ </warning>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>int</ret><nametext>erl_drv_equal_tids(ErlDrvTid tid1, ErlDrvTid tid2)</nametext></name>
- <fsummary>Compare thread identifiers for equality</fsummary>
+ <name><ret>char *</ret><nametext>erl_drv_mutex_name(ErlDrvMutex
+ *mtx)</nametext></name>
+ <fsummary>Get name of driver mutex.</fsummary>
<desc>
- <marker id="erl_drv_equal_tids"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>tid1</c></tag>
- <item>A thread identifier.</item>
- <tag><c>tid2</c></tag>
- <item>A thread identifier.</item>
- </taglist>
- <p>This function compares two thread identifiers for equality,
- and returns <c>0</c> it they aren't equal, and
- a value not equal to <c>0</c> if they are equal.</p>
- <note><p>A Thread identifier may be reused very quickly after
- a thread has terminated. Therefore, if a thread
- corresponding to one of the involved thread identifiers
- has terminated since the thread identifier was saved,
- the result of <c>erl_drv_equal_tids()</c> might not give
- the expected result.
- </p></note>
+ <marker id="erl_drv_mutex_name"></marker>
+ <p>Returns a pointer to the mutex name.</p>
+ <p><c>mtx</c> is a pointer to an initialized mutex.</p>
+ <note>
+ <p>This function is intended for debugging purposes only.</p>
+ </note>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>erl_drv_mutex_trylock(ErlDrvMutex
+ *mtx)</nametext></name>
+ <fsummary>Try lock a mutex.</fsummary>
+ <desc>
+ <marker id="erl_drv_mutex_trylock"></marker>
+ <p>Tries to lock a mutex. A thread that has currently locked the mutex
+ <em>cannot</em> try to lock the same mutex again.</p>
+ <p><c>mtx</c> is a pointer to a mutex to try to lock.</p>
+ <p>Returns <c>0</c> on success, otherwise <c>EBUSY</c>.</p>
+ <warning>
+ <p>If you leave a mutex locked in an emulator thread
+ when you let the thread out of your control, you will
+ <em>very likely</em> deadlock the whole emulator.</p>
+ </warning>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>ErlDrvMutex *</ret><nametext>erl_drv_mutex_create(char *name)</nametext></name>
- <fsummary>Create a mutex</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_mutex_unlock(ErlDrvMutex
+ *mtx)</nametext></name>
+ <fsummary>Unlock a mutex.</fsummary>
<desc>
- <marker id="erl_drv_mutex_create"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>name</c></tag>
- <item>A string identifying the created mutex. It will be used
- to identify the mutex in planned future debug functionality.
- </item>
- </taglist>
- <p>This function creates a mutex and returns a pointer to it. On
- failure <c>NULL</c> is returned. The driver creating the mutex
- has the responsibility of destroying it before the driver is
- unloaded.
- </p>
+ <marker id="erl_drv_mutex_unlock"></marker>
+ <p>Unlocks a mutex. The mutex currently must be
+ locked by the calling thread.</p>
+ <p><c>mtx</c> is a pointer to a mutex to unlock.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_mutex_destroy(ErlDrvMutex *mtx)</nametext></name>
- <fsummary>Destroy a mutex</fsummary>
+ <name><ret>int</ret><nametext>erl_drv_output_term(ErlDrvTermData port,
+ ErlDrvTermData* term, int n)</nametext></name>
+ <fsummary>Send term data from driver to port owner.</fsummary>
<desc>
- <marker id="erl_drv_mutex_destroy"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>mtx</c></tag>
- <item>A pointer to a mutex to destroy.</item>
- </taglist>
- <p>This function destroys a mutex previously created by
- <seealso marker="#erl_drv_mutex_create">erl_drv_mutex_create()</seealso>.
- The mutex has to be in an unlocked state before being
- destroyed.
- </p>
+ <marker id="erl_drv_output_term"></marker>
+ <p>Sends data in the special driver term
+ format to the port owner process. This is a fast way to
+ deliver term data from a driver. It needs no binary
+ conversion, so the port owner process receives data as
+ normal Erlang terms. The <seealso marker="#erl_drv_send_term">
+ <c>erl_drv_send_term</c></seealso>
+ functions can be used for sending to any process
+ on the local node.</p>
+ <note>
+ <p>Parameter <c>port</c> is <em>not</em>
+ an ordinary port handle, but a port handle converted using
+ <seealso marker="#driver_mk_port">
+ <c>driver_mk_port</c></seealso>.</p>
+ </note>
+ <p>Parameter <c>term</c> points to an array of
+ <c>ErlDrvTermData</c> with <c>n</c> elements. This array
+ contains terms described in the driver term format. Every
+ term consists of 1-4 elements in the array. The
+ first term has a term type and then arguments.
+ Parameter <c>port</c> specifies the sending port.</p>
+ <p>Tuples, maps, and lists (except strings, see below)
+ are built in reverse polish notation, so that to build a
+ tuple, the elements are specified first, and then the tuple
+ term, with a count. Likewise for lists and maps.</p>
+ <list type="bulleted">
+ <item>
+ <p>A tuple must be specified with the number of elements. (The
+ elements precede the <c>ERL_DRV_TUPLE</c> term.)</p>
+ </item>
+ <item>
+ <p>A map must be specified with the number of key-value pairs
+ <c>N</c>. The key-value pairs must precede the <c>ERL_DRV_MAP</c>
+ in this order: <c>key1,value1,key2,value2,...,keyN,valueN</c>.
+ Duplicate keys are not allowed.</p>
+ </item>
+ <item>
+ <p>A list must be specified with the number of elements,
+ including the tail, which is the last term preceding
+ <c>ERL_DRV_LIST</c>.</p>
+ </item>
+ </list>
+ <p>The special term <c>ERL_DRV_STRING_CONS</c> is used to
+ "splice" in a string in a list, a string specified this way is
+ not a list in itself, but the elements are elements of the
+ surrounding list.</p>
+ <pre>
+Term type Arguments
+--------- ---------
+ERL_DRV_NIL
+ERL_DRV_ATOM ErlDrvTermData atom (from driver_mk_atom(char *string))
+ERL_DRV_INT ErlDrvSInt integer
+ERL_DRV_UINT ErlDrvUInt integer
+ERL_DRV_INT64 ErlDrvSInt64 *integer_ptr
+ERL_DRV_UINT64 ErlDrvUInt64 *integer_ptr
+ERL_DRV_PORT ErlDrvTermData port (from driver_mk_port(ErlDrvPort port))
+ERL_DRV_BINARY ErlDrvBinary *bin, ErlDrvUInt len, ErlDrvUInt offset
+ERL_DRV_BUF2BINARY char *buf, ErlDrvUInt len
+ERL_DRV_STRING char *str, int len
+ERL_DRV_TUPLE int sz
+ERL_DRV_LIST int sz
+ERL_DRV_PID ErlDrvTermData pid (from driver_connected(ErlDrvPort port)
+ or driver_caller(ErlDrvPort port))
+ERL_DRV_STRING_CONS char *str, int len
+ERL_DRV_FLOAT double *dbl
+ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len
+ERL_DRV_MAP int sz</pre>
+ <p>The unsigned integer data type <c>ErlDrvUInt</c> and the
+ signed integer data type <c>ErlDrvSInt</c> are 64 bits wide
+ on a 64-bit runtime system and 32 bits wide on a 32-bit
+ runtime system. They were introduced in ERTS 5.6
+ and replaced some of the <c>int</c> arguments in the list above.</p>
+ <p>The unsigned integer data type <c>ErlDrvUInt64</c> and the
+ signed integer data type <c>ErlDrvSInt64</c> are always 64 bits
+ wide. They were introduced in ERTS 5.7.4.</p>
+ <p>To build the tuple <c>{tcp, Port, [100 | Binary]}</c>, the
+ following call can be made.</p>
+ <code type="none"><![CDATA[
+ErlDrvBinary* bin = ...
+ErlDrvPort port = ...
+ErlDrvTermData spec[] = {
+ ERL_DRV_ATOM, driver_mk_atom("tcp"),
+ ERL_DRV_PORT, driver_mk_port(drvport),
+ ERL_DRV_INT, 100,
+ ERL_DRV_BINARY, bin, 50, 0,
+ ERL_DRV_LIST, 2,
+ ERL_DRV_TUPLE, 3,
+};
+erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0])); ]]></code>
+ <p>Here <c>bin</c> is a driver binary of length at least 50 and
+ <c>drvport</c> is a port handle. Notice that <c>ERL_DRV_LIST</c>
+ comes after the elements of the list, likewise
+ <c>ERL_DRV_TUPLE</c>.</p>
+ <p>The <c>ERL_DRV_STRING_CONS</c> term is a way to construct
+ strings. It works differently from how <c>ERL_DRV_STRING</c>
+ works. <c>ERL_DRV_STRING_CONS</c> builds a string list in
+ reverse order (as opposed to how <c>ERL_DRV_LIST</c>
+ works), concatenating the strings added to a list. The tail
+ must be specified before <c>ERL_DRV_STRING_CONS</c>.</p>
+ <p><c>ERL_DRV_STRING</c> constructs a string, and ends
+ it. (So it is the same as <c>ERL_DRV_NIL</c> followed by
+ <c>ERL_DRV_STRING_CONS</c>.)</p>
+ <code type="none"><![CDATA[
+/* to send [x, "abc", y] to the port: */
+ErlDrvTermData spec[] = {
+ ERL_DRV_ATOM, driver_mk_atom("x"),
+ ERL_DRV_STRING, (ErlDrvTermData)"abc", 3,
+ ERL_DRV_ATOM, driver_mk_atom("y"),
+ ERL_DRV_NIL,
+ ERL_DRV_LIST, 4
+};
+erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0])); ]]></code>
+ <code type="none"><![CDATA[
+/* to send "abc123" to the port: */
+ErlDrvTermData spec[] = {
+ ERL_DRV_NIL, /* with STRING_CONS, the tail comes first */
+ ERL_DRV_STRING_CONS, (ErlDrvTermData)"123", 3,
+ ERL_DRV_STRING_CONS, (ErlDrvTermData)"abc", 3,
+};
+erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0])); ]]></code>
+ <p>The <c>ERL_DRV_EXT2TERM</c> term type is used for passing a
+ term encoded with the
+ <seealso marker="erl_ext_dist">external format</seealso>,
+ that is, a term that has been encoded by
+ <seealso marker="erlang#term_to_binary/2">
+ <c>erlang:term_to_binary</c></seealso>,
+ <seealso marker="erl_interface:ei"><c>erl_interface:ei(3)</c></seealso>,
+ and so on.
+ For example, if <c>binp</c> is a pointer to an <c>ErlDrvBinary</c>
+ that contains term <c>{17, 4711}</c> encoded with the
+ <seealso marker="erl_ext_dist">external format</seealso>,
+ and you want to wrap it in a two-tuple with the tag <c>my_tag</c>,
+ that is, <c>{my_tag, {17, 4711}}</c>, you can do as follows:</p>
+ <code type="none"><![CDATA[
+ErlDrvTermData spec[] = {
+ ERL_DRV_ATOM, driver_mk_atom("my_tag"),
+ ERL_DRV_EXT2TERM, (ErlDrvTermData) binp->orig_bytes, binp->orig_size
+ ERL_DRV_TUPLE, 2,
+};
+erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0])); ]]></code>
+ <p>To build the map <c>#{key1 => 100, key2 => {200, 300}}</c>, the
+ following call can be made.</p>
+ <code type="none"><![CDATA[
+ErlDrvPort port = ...
+ErlDrvTermData spec[] = {
+ ERL_DRV_ATOM, driver_mk_atom("key1"),
+ ERL_DRV_INT, 100,
+ ERL_DRV_ATOM, driver_mk_atom("key2"),
+ ERL_DRV_INT, 200,
+ ERL_DRV_INT, 300,
+ ERL_DRV_TUPLE, 2,
+ ERL_DRV_MAP, 2
+};
+erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0])); ]]></code>
+ <p>If you want to pass a binary and do not already have the content
+ of the binary in an <c>ErlDrvBinary</c>, you can benefit from using
+ <c>ERL_DRV_BUF2BINARY</c> instead of creating an <c>ErlDrvBinary</c>
+ through <seealso marker="#driver_alloc_binary">
+ <c>driver_alloc_binary</c></seealso> and then pass the binary through
+ <c>ERL_DRV_BINARY</c>. The runtime system often allocates
+ binaries smarter if <c>ERL_DRV_BUF2BINARY</c> is used.
+ However, if the content of the binary to pass already resides in
+ an <c>ErlDrvBinary</c>, it is normally better to pass the binary using
+ <c>ERL_DRV_BINARY</c> and the <c>ErlDrvBinary</c> in question.</p>
+ <p>The <c>ERL_DRV_UINT</c>, <c>ERL_DRV_BUF2BINARY</c>, and
+ <c>ERL_DRV_EXT2TERM</c> term types were introduced in
+ ERTS 5.6.</p>
+ <p>This function is only thread-safe when the emulator with SMP
+ support is used.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>erl_drv_putenv(const char *key, char
+ *value)</nametext></name>
+ <fsummary>Set the value of an environment variable.</fsummary>
+ <desc>
+ <marker id="erl_drv_putenv"></marker>
+ <p>Sets the value of an environment variable.</p>
+ <p><c>key</c> is a <c>NULL</c>-terminated string containing the
+ name of the environment variable.</p>
+ <p><c>value</c> is a <c>NULL</c>-terminated string containing the
+ new value of the environment variable.</p>
+ <p>Returns <c>0</c> on success, otherwise a value <c>!= 0</c>.</p>
+ <note>
+ <p>The result of passing the empty string (<c>""</c>) as a value
+ is platform-dependent. On some platforms the variable value
+ is set to the empty string, on others the
+ environment variable is removed.</p>
+ </note>
+ <warning>
+ <p>Do <em>not</em> use libc's <c>putenv</c> or similar C library
+ interfaces from a driver.</p>
+ </warning>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_mutex_lock(ErlDrvMutex *mtx)</nametext></name>
- <fsummary>Lock a mutex</fsummary>
+ <name><ret>ErlDrvRWLock *</ret><nametext>erl_drv_rwlock_create(char
+ *name)</nametext></name>
+ <fsummary>Create an rwlock.</fsummary>
<desc>
- <marker id="erl_drv_mutex_lock"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>mtx</c></tag>
- <item>A pointer to a mutex to lock.</item>
- </taglist>
- <p>This function locks a mutex. The calling thread will be
- blocked until the mutex has been locked. A thread
- which currently has locked the mutex may <em>not</em> lock
- the same mutex again.
- </p>
- <warning><p>If you leave a mutex locked in an emulator thread
- when you let the thread out of your control, you will
- <em>very likely</em> deadlock the whole emulator.
- </p></warning>
+ <marker id="erl_drv_rwlock_create"></marker>
+ <p>Creates an rwlock and returns a pointer to it.</p>
+ <p><c>name</c> is a string identifying the created rwlock.
+ It is used to identify the rwlock in planned future
+ debug functionality.</p>
+ <p>Returns <c>NULL</c> on failure. The driver creating the rwlock
+ is responsible for destroying it before the driver is unloaded.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>int</ret><nametext>erl_drv_mutex_trylock(ErlDrvMutex *mtx)</nametext></name>
- <fsummary>Try lock a mutex</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_rwlock_destroy(ErlDrvRWLock
+ *rwlck)</nametext></name>
+ <fsummary>Destroy an rwlock.</fsummary>
<desc>
- <marker id="erl_drv_mutex_trylock"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>mtx</c></tag>
- <item>A pointer to a mutex to try to lock.</item>
- </taglist>
- <p>This function tries to lock a mutex. If successful <c>0</c>,
- is returned; otherwise, <c>EBUSY</c> is returned. A thread
- which currently has locked the mutex may <em>not</em> try to
- lock the same mutex again.
- </p>
- <warning><p>If you leave a mutex locked in an emulator thread
- when you let the thread out of your control, you will
- <em>very likely</em> deadlock the whole emulator.
- </p></warning>
+ <marker id="erl_drv_rwlock_destroy"></marker>
+ <p>Destroys an rwlock previously created by
+ <seealso marker="#erl_drv_rwlock_create">
+ <c>erl_drv_rwlock_create</c></seealso>.
+ The rwlock must be in an unlocked state before it is destroyed.</p>
+ <p><c>rwlck</c> is a pointer to an rwlock to destroy.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_mutex_unlock(ErlDrvMutex *mtx)</nametext></name>
- <fsummary>Unlock a mutex</fsummary>
+ <name><ret>char *</ret><nametext>erl_drv_rwlock_name(ErlDrvRWLock
+ *rwlck)</nametext></name>
+ <fsummary>Get name of driver mutex.</fsummary>
<desc>
- <marker id="erl_drv_mutex_unlock"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>mtx</c></tag>
- <item>A pointer to a mutex to unlock.</item>
- </taglist>
- <p>This function unlocks a mutex. The mutex currently has to be
- locked by the calling thread.
- </p>
+ <marker id="erl_drv_rwlock_name"></marker>
+ <p>Returns a pointer to the name of the rwlock.</p>
+ <p><c>rwlck</c> is a pointer to an initialized rwlock.</p>
+ <note>
+ <p>This function is intended for debugging purposes only.</p>
+ </note>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret><nametext>erl_drv_rwlock_rlock(ErlDrvRWLock
+ *rwlck)</nametext></name>
+ <fsummary>Read lock an rwlock.</fsummary>
+ <desc>
+ <marker id="erl_drv_rwlock_rlock"></marker>
+ <p>Read locks an rwlock. The calling thread is
+ blocked until the rwlock has been read locked. A thread
+ that currently has read or read/write locked the rwlock
+ <em>cannot</em> lock the same rwlock again.</p>
+ <p><c>rwlck</c> is a pointer to the rwlock to read lock.</p>
+ <warning>
+ <p>If you leave an rwlock locked in an emulator thread
+ when you let the thread out of your control, you will
+ <em>very likely</em> deadlock the whole emulator.</p>
+ </warning>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>ErlDrvCond *</ret><nametext>erl_drv_cond_create(char *name)</nametext></name>
- <fsummary>Create a condition variable</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_rwlock_runlock(ErlDrvRWLock
+ *rwlck)</nametext></name>
+ <fsummary>Read unlock an rwlock.</fsummary>
<desc>
- <marker id="erl_drv_cond_create"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>name</c></tag>
- <item>A string identifying the created condition variable. It
- will be used to identify the condition variable in planned
- future debug functionality.
- </item>
- </taglist>
- <p>This function creates a condition variable and returns a
- pointer to it. On failure <c>NULL</c> is returned. The driver
- creating the condition variable has the responsibility of
- destroying it before the driver is unloaded.</p>
+ <marker id="erl_drv_rwlock_runlock"></marker>
+ <p>Read unlocks an rwlock. The rwlock currently must
+ be read locked by the calling thread.</p>
+ <p><c>rwlck</c> is a pointer to an rwlock to read unlock.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_cond_destroy(ErlDrvCond *cnd)</nametext></name>
- <fsummary>Destroy a condition variable</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_rwlock_rwlock(ErlDrvRWLock
+ *rwlck)</nametext></name>
+ <fsummary>Read/write lock an rwlock.</fsummary>
<desc>
- <marker id="erl_drv_cond_destroy"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>cnd</c></tag>
- <item>A pointer to a condition variable to destroy.</item>
- </taglist>
- <p>This function destroys a condition variable previously
- created by
- <seealso marker="#erl_drv_cond_create">erl_drv_cond_create()</seealso>.
- </p>
- <p>This function is thread-safe.</p>
+ <marker id="erl_drv_rwlock_rwlock"></marker>
+ <p>Read/write locks an rwlock. The calling thread
+ is blocked until the rwlock has been read/write locked.
+ A thread that currently has read or read/write locked the
+ rwlock <em>cannot</em> lock the same rwlock again.</p>
+ <p><c>rwlck</c> is a pointer to an rwlock to read/write lock.</p>
+ <warning>
+ <p>If you leave an rwlock locked in an emulator thread
+ when you let the thread out of your control, you will
+ <em>very likely</em> deadlock the whole emulator.</p>
+ </warning>
+ <p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_cond_signal(ErlDrvCond *cnd)</nametext></name>
- <fsummary>Signal on a condition variable</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_rwlock_rwunlock(ErlDrvRWLock
+ *rwlck)</nametext></name>
+ <fsummary>Read/write unlock an rwlock.</fsummary>
<desc>
- <marker id="erl_drv_cond_signal"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>cnd</c></tag>
- <item>A pointer to a condition variable to signal on.</item>
- </taglist>
- <p>This function signals on a condition variable. That is, if
- other threads are waiting on the condition variable being
- signaled, <em>one</em> of them will be woken.
- </p>
+ <marker id="erl_drv_rwlock_rwunlock"></marker>
+ <p>Read/write unlocks an rwlock. The rwlock currently must be
+ read/write locked by the calling thread.</p>
+ <p><c>rwlck</c> is a pointer to an rwlock to read/write unlock.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_cond_broadcast(ErlDrvCond *cnd)</nametext></name>
- <fsummary>Broadcast on a condition variable</fsummary>
+ <name><ret>int</ret><nametext>erl_drv_rwlock_tryrlock(ErlDrvRWLock
+ *rwlck)</nametext></name>
+ <fsummary>Try to read lock an rwlock.</fsummary>
<desc>
- <marker id="erl_drv_cond_broadcast"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>cnd</c></tag>
- <item>A pointer to a condition variable to broadcast on.</item>
- </taglist>
- <p>This function broadcasts on a condition variable. That is, if
- other threads are waiting on the condition variable being
- broadcast on, <em>all</em> of them will be woken.
- </p>
+ <marker id="erl_drv_rwlock_tryrlock"></marker>
+ <p>Tries to read lock an rwlock.</p>
+ <p><c>rwlck</c> is a pointer to an rwlock to try to read lock.</p>
+ <p>Returns <c>0</c> on success, otherwise <c>EBUSY</c>.
+ A thread that currently has read or read/write locked the
+ rwlock <em>cannot</em> try to lock the same rwlock again.</p>
+ <warning>
+ <p>If you leave an rwlock locked in an emulator thread
+ when you let the thread out of your control, you will
+ <em>very likely</em> deadlock the whole emulator.</p>
+ </warning>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_cond_wait(ErlDrvCond *cnd, ErlDrvMutex *mtx)</nametext></name>
- <fsummary>Wait on a condition variable</fsummary>
+ <name><ret>int</ret><nametext>erl_drv_rwlock_tryrwlock(ErlDrvRWLock
+ *rwlck)</nametext></name>
+ <fsummary>Try to read/write lock an rwlock.</fsummary>
<desc>
- <marker id="erl_drv_cond_wait"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>cnd</c></tag>
- <item>A pointer to a condition variable to wait on.</item>
- <tag><c>mtx</c></tag>
- <item>A pointer to a mutex to unlock while waiting.</item>
- <tag><c></c></tag>
- <item></item>
- </taglist>
- <p>This function waits on a condition variable. The calling
- thread is blocked until another thread wakes it by signaling
- or broadcasting on the condition variable. Before the calling
- thread is blocked it unlocks the mutex passed as argument, and
- when the calling thread is woken it locks the same mutex before
- returning. That is, the mutex currently has to be locked by
- the calling thread when calling this function.
- </p>
- <note><p><c>erl_drv_cond_wait()</c> might return even though
- no-one has signaled or broadcast on the condition
- variable. Code calling <c>erl_drv_cond_wait()</c> should
- always be prepared for <c>erl_drv_cond_wait()</c>
- returning even though the condition that the thread was
- waiting for hasn't occurred. That is, when returning from
- <c>erl_drv_cond_wait()</c> always check if the condition
- has occurred, and if not call <c>erl_drv_cond_wait()</c>
- again.
- </p></note>
+ <marker id="erl_drv_rwlock_tryrwlock"></marker>
+ <p>Tries to read/write lock an rwlock.
+ A thread that currently has read or read/write locked the
+ rwlock <em>cannot</em> try to lock the same rwlock again.</p>
+ <p><c>rwlck</c>is pointer to an rwlock to try to read/write lock.</p>
+ <p>Returns <c>0</c> on success, otherwise <c>EBUSY</c>.</p>
+ <warning>
+ <p>If you leave an rwlock locked in an emulator thread
+ when you let the thread out of your control, you will
+ <em>very likely</em> deadlock the whole emulator.</p>
+ </warning>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>ErlDrvRWLock *</ret><nametext>erl_drv_rwlock_create(char *name)</nametext></name>
- <fsummary>Create an rwlock</fsummary>
+ <name><ret>int</ret><nametext>erl_drv_send_term(ErlDrvTermData port,
+ ErlDrvTermData receiver, ErlDrvTermData* term, int n)</nametext></name>
+ <fsummary>Send term data to other process than port owner process.
+ </fsummary>
<desc>
- <marker id="erl_drv_rwlock_create"></marker>
- <p>Arguments:</p>
+ <marker id="erl_drv_send_term"></marker>
+ <p>This function is the only way for a driver to send data to
+ <em>other</em> processes than the port owner process. Parameter
+ <c>receiver</c> specifies the process to receive the data.</p>
+ <note>
+ <p>Parameter <c>port</c> is <em>not</em> an ordinary port handle, but
+ a port handle converted using
+ <seealso marker="#driver_mk_port">
+ <c>driver_mk_port</c></seealso>.</p>
+ </note>
+ <p>Parameters <c>port</c>, <c>term</c>, and <c>n</c> work as in
+ <seealso marker="#erl_drv_output_term">
+ <c>erl_drv_output_term</c></seealso>.</p>
+ <p>This function is only thread-safe when the emulator with SMP
+ support is used.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret><nametext>erl_drv_set_os_pid(ErlDrvPort port,
+ ErlDrvSInt pid)</nametext></name>
+ <fsummary>Set the os_pid for the port.</fsummary>
+ <desc>
+ <marker id="erl_drv_set_os_pid"></marker>
+ <p>Sets the <c>os_pid</c> seen when doing
+ <seealso marker="erlang:port_info/2">
+ <c>erlang:port_info/2</c></seealso> on this port.</p>
+ <p><c>port</c> is the port handle of the port (driver instance) to set
+ the pid on. <c>pid</c>is the pid to set.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>erl_drv_thread_create(char *name, ErlDrvTid
+ *tid, void * (*func)(void *), void *arg, ErlDrvThreadOpts
+ *opts)</nametext></name>
+ <fsummary>Create a thread.</fsummary>
+ <desc>
+ <marker id="erl_drv_thread_create"></marker>
+ <p>Creates a new thread.</p>
<taglist>
<tag><c>name</c></tag>
- <item>A string identifying the created rwlock. It will be used to
- identify the rwlock in planned future debug functionality.
- </item>
+ <item>A string identifying the created thread. It is used to
+ identify the thread in planned future debug functionality.
+ </item>
+ <tag><c>tid</c></tag>
+ <item>A pointer to a thread identifier variable.</item>
+ <tag><c>func</c></tag>
+ <item>A pointer to a function to execute in the created thread.</item>
+ <tag><c>arg</c></tag>
+ <item>A pointer to argument to the <c>func</c> function.</item>
+ <tag><c>opts</c></tag>
+ <item>A pointer to thread options to use or <c>NULL</c>.</item>
</taglist>
- <p>This function creates an rwlock and returns a pointer to it. On
- failure <c>NULL</c> is returned. The driver creating the rwlock
- has the responsibility of destroying it before the driver is
- unloaded.
- </p>
+ <p>Returns <c>0</c> on success,
+ otherwise an <c>errno</c> value is returned to indicate the error.
+ The newly created thread begins executing in the function pointed
+ to by <c>func</c>, and <c>func</c> is passed <c>arg</c> as
+ argument. When <c>erl_drv_thread_create</c> returns, the thread
+ identifier of the newly created thread is available in
+ <c>*tid</c>. <c>opts</c> can be either a <c>NULL</c> pointer, or a
+ pointer to an
+ <seealso marker="#ErlDrvThreadOpts"><c>ErlDrvThreadOpts</c></seealso>
+ structure. If <c>opts</c> is a <c>NULL</c> pointer, default options
+ are used, otherwise the passed options are used.</p>
+ <warning>
+ <p>You are not allowed to allocate the
+ <seealso marker="#ErlDrvThreadOpts">
+ <c>ErlDrvThreadOpts</c></seealso> structure by yourself.
+ It must be allocated and initialized by
+ <seealso marker="#erl_drv_thread_opts_create">
+ <c>erl_drv_thread_opts_create</c></seealso>.</p>
+ </warning>
+ <p>The created thread terminates either when <c>func</c> returns or if
+ <seealso marker="#erl_drv_thread_exit">
+ <c>erl_drv_thread_exit</c></seealso>
+ is called by the thread. The exit value of the thread is either
+ returned from <c>func</c> or passed as argument to
+ <seealso marker="#erl_drv_thread_exit">
+ <c>erl_drv_thread_exit</c></seealso>.
+ The driver creating the thread is responsible for joining the
+ thread, through <seealso marker="#erl_drv_thread_join">
+ <c>erl_drv_thread_join</c></seealso>,
+ before the driver is unloaded. "Detached" threads cannot be created,
+ that is, threads that do not need to be joined.</p>
+ <warning>
+ <p>All created threads must be joined by the driver before
+ it is unloaded. If the driver fails to join all threads
+ created before it is unloaded, the runtime system
+ most likely crashes when the driver code is unloaded.</p>
+ </warning>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_rwlock_destroy(ErlDrvRWLock *rwlck)</nametext></name>
- <fsummary>Destroy an rwlock</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_thread_exit(void
+ *exit_value)</nametext></name>
+ <fsummary>Terminate calling thread.</fsummary>
<desc>
- <marker id="erl_drv_rwlock_destroy"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>rwlck</c></tag>
- <item>A pointer to an rwlock to destroy.</item>
- </taglist>
- <p>This function destroys an rwlock previously created by
- <seealso marker="#erl_drv_rwlock_create">erl_drv_rwlock_create()</seealso>.
- The rwlock has to be in an unlocked state before being destroyed.
- </p>
+ <marker id="erl_drv_thread_exit"></marker>
+ <p>Terminates the calling thread with the exit value passed as
+ argument. <c>exit_value</c> is a pointer to an exit value or
+ <c>NULL</c>.</p>
+ <p>You are only allowed to terminate threads created with
+ <seealso marker="#erl_drv_thread_create">
+ <c>erl_drv_thread_create</c></seealso>.</p>
+ <p>The exit value can later be retrieved by another thread through
+ <seealso marker="#erl_drv_thread_join">
+ <c>erl_drv_thread_join</c></seealso>.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_rwlock_rlock(ErlDrvRWLock *rwlck)</nametext></name>
- <fsummary>Read lock an rwlock</fsummary>
+ <name><ret>int</ret><nametext>erl_drv_thread_join(ErlDrvTid tid, void
+ **exit_value)</nametext></name>
+ <fsummary>Join with another thread.</fsummary>
<desc>
- <marker id="erl_drv_rwlock_rlock"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>rwlck</c></tag>
- <item>A pointer to an rwlock to read lock.</item>
- </taglist>
- <p>This function read locks an rwlock. The calling thread will be
- blocked until the rwlock has been read locked. A thread
- which currently has read or read/write locked the rwlock may
- <em>not</em> lock the same rwlock again.
- </p>
- <warning><p>If you leave an rwlock locked in an emulator thread
- when you let the thread out of your control, you will
- <em>very likely</em> deadlock the whole emulator.
- </p></warning>
+ <marker id="erl_drv_thread_join"></marker>
+ <p>Joins the calling thread with another thread, that is,
+ the calling thread is blocked until the thread identified by
+ <c>tid</c> has terminated.</p>
+ <p><c>tid</c> is the thread identifier of the thread to join.
+ <c>exit_value</c> is a pointer to a pointer to an exit value,
+ or <c>NULL</c>.</p>
+ <p>Returns <c>0</c> on success, otherwise an <c>errno</c>
+ value is returned to indicate the error.</p>
+ <p>A thread can only be joined once. The behavior of joining
+ more than once is undefined, an emulator crash is likely. If
+ <c>exit_value == NULL</c>, the exit value of the terminated thread
+ is ignored, otherwise the exit value of the terminated thread
+ is stored at <c>*exit_value</c>.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>int</ret><nametext>erl_drv_rwlock_tryrlock(ErlDrvRWLock *rwlck)</nametext></name>
- <fsummary>Try to read lock an rwlock</fsummary>
+ <name><ret>char *</ret><nametext>erl_drv_thread_name(ErlDrvTid
+ tid)</nametext></name>
+ <fsummary>Get name of driver mutex.</fsummary>
<desc>
- <marker id="erl_drv_rwlock_tryrlock"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>rwlck</c></tag>
- <item>A pointer to an rwlock to try to read lock.</item>
- </taglist>
- <p>This function tries to read lock an rwlock. If successful
- <c>0</c>, is returned; otherwise, <c>EBUSY</c> is returned.
- A thread which currently has read or read/write locked the
- rwlock may <em>not</em> try to lock the same rwlock again.
- </p>
- <warning><p>If you leave an rwlock locked in an emulator thread
- when you let the thread out of your control, you will
- <em>very likely</em> deadlock the whole emulator.
- </p></warning>
- <p>This function is thread-safe.</p>
+ <marker id="erl_drv_rwlock_name"></marker>
+ <p>Returns a pointer to the name of the thread.</p>
+ <p><c>tid</c> is a thread identifier.</p>
+ <note>
+ <p>This function is intended for debugging purposes only.</p>
+ </note>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_rwlock_runlock(ErlDrvRWLock *rwlck)</nametext></name>
- <fsummary>Read unlock an rwlock</fsummary>
+ <name><ret>ErlDrvThreadOpts *</ret>
+ <nametext>erl_drv_thread_opts_create(char *name)</nametext></name>
+ <fsummary>Create thread options.</fsummary>
<desc>
- <marker id="erl_drv_rwlock_runlock"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>rwlck</c></tag>
- <item>A pointer to an rwlock to read unlock.</item>
- </taglist>
- <p>This function read unlocks an rwlock. The rwlock currently
- has to be read locked by the calling thread.
- </p>
+ <marker id="erl_drv_thread_opts_create"></marker>
+ <p>Allocates and initializes a thread option structure.</p>
+ <p><c>name</c> is a string identifying the created thread options.
+ It is used to identify the thread options in planned future debug
+ functionality.</p>
+ <p>Returns <c>NULL</c> on failure. A thread option
+ structure is used for passing options to
+ <seealso marker="#erl_drv_thread_create">
+ <c>erl_drv_thread_create</c></seealso>.
+ If the structure is not modified before it is passed to
+ <seealso marker="#erl_drv_thread_create">
+ <c>erl_drv_thread_create</c></seealso>,
+ the default values are used.</p>
+ <warning>
+ <p>You are not allowed to allocate the
+ <seealso marker="#ErlDrvThreadOpts">
+ <c>ErlDrvThreadOpts</c></seealso>
+ structure by yourself. It must be allocated and initialized by
+ <c>erl_drv_thread_opts_create</c>.</p>
+ </warning>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_rwlock_rwlock(ErlDrvRWLock *rwlck)</nametext></name>
- <fsummary>Read/Write lock an rwlock</fsummary>
+ <name><ret>void</ret>
+ <nametext>erl_drv_thread_opts_destroy(ErlDrvThreadOpts *opts)</nametext>
+ </name>
+ <fsummary>Destroy thread options.</fsummary>
<desc>
- <marker id="erl_drv_rwlock_rwlock"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>rwlck</c></tag>
- <item>A pointer to an rwlock to read/write lock.</item>
- </taglist>
- <p>This function read/write locks an rwlock. The calling thread
- will be blocked until the rwlock has been read/write locked.
- A thread which currently has read or read/write locked the
- rwlock may <em>not</em> lock the same rwlock again.
- </p>
- <warning><p>If you leave an rwlock locked in an emulator thread
- when you let the thread out of your control, you will
- <em>very likely</em> deadlock the whole emulator.
- </p></warning>
+ <marker id="erl_drv_thread_opts_destroy"></marker>
+ <p>Destroys thread options previously created by
+ <seealso marker="#erl_drv_thread_opts_create">
+ <c>erl_drv_thread_opts_create</c></seealso>.</p>
+ <p><c>opts</c> is a pointer to thread options to destroy.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>int</ret><nametext>erl_drv_rwlock_tryrwlock(ErlDrvRWLock *rwlck)</nametext></name>
- <fsummary>Try to read/write lock an rwlock</fsummary>
+ <name><ret>ErlDrvTid</ret>
+ <nametext>erl_drv_thread_self(void)</nametext></name>
+ <fsummary>Get the thread identifier of the current thread.</fsummary>
<desc>
- <marker id="erl_drv_rwlock_tryrwlock"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>rwlck</c></tag>
- <item>A pointer to an rwlock to try to read/write lock.</item>
- </taglist>
- <p>This function tries to read/write lock an rwlock. If successful
- <c>0</c>, is returned; otherwise, <c>EBUSY</c> is returned.
- A thread which currently has read or read/write locked the
- rwlock may <em>not</em> try to lock the same rwlock again.
- </p>
- <warning><p>If you leave an rwlock locked in an emulator thread
- when you let the thread out of your control, you will
- <em>very likely</em> deadlock the whole emulator.
- </p></warning>
+ <marker id="erl_drv_thread_self"></marker>
+ <p>Returns the thread identifier of the calling thread.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_rwlock_rwunlock(ErlDrvRWLock *rwlck)</nametext></name>
- <fsummary>Read/Write unlock an rwlock</fsummary>
+ <name><ret>ErlDrvTime</ret><nametext>erl_drv_time_offset(ErlDrvTimeUnit
+ time_unit)</nametext></name>
+ <fsummary>Get current time offset.</fsummary>
+ <desc>
+ <marker id="erl_drv_time_offset"></marker>
+ <p>Returns the current time offset between
+ <seealso marker="time_correction#Erlang_Monotonic_Time">
+ Erlang monotonic time</seealso> and
+ <seealso marker="time_correction#Erlang_System_Time">
+ Erlang system time</seealso>
+ converted into the <c>time_unit</c> passed as argument.</p>
+ <p><c>time_unit</c> is time unit of returned value.</p>
+ <p>Returns <c>ERL_DRV_TIME_ERROR</c> if called with an invalid
+ time unit argument, or if called from a thread that is not a
+ scheduler thread.</p>
+ <p>See also <seealso marker="#ErlDrvTime">
+ <c>ErlDrvTime</c></seealso> and
+ <seealso marker="#ErlDrvTimeUnit">
+ <c>ErlDrvTimeUnit</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void *</ret><nametext>erl_drv_tsd_get(ErlDrvTSDKey
+ key)</nametext></name>
+ <fsummary>Get thread-specific data.</fsummary>
<desc>
- <marker id="erl_drv_rwlock_rwunlock"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>rwlck</c></tag>
- <item>A pointer to an rwlock to read/write unlock.</item>
- </taglist>
- <p>This function read/write unlocks an rwlock. The rwlock
- currently has to be read/write locked by the calling thread.
- </p>
+ <marker id="erl_drv_tsd_get"></marker>
+ <p>Returns the thread-specific data
+ associated with <c>key</c> for the calling thread.</p>
+ <p><c>key</c> is a thread-specific data key.</p>
+ <p>Returns <c>NULL</c> if no data has been associated
+ with <c>key</c> for the calling thread.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>int</ret><nametext>erl_drv_tsd_key_create(char *name, ErlDrvTSDKey *key)</nametext></name>
- <fsummary>Create a thread specific data key</fsummary>
+ <name><ret>int</ret><nametext>erl_drv_tsd_key_create(char *name,
+ ErlDrvTSDKey *key)</nametext></name>
+ <fsummary>Create a thread-specific data key.</fsummary>
<desc>
<marker id="erl_drv_tsd_key_create"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>name</c></tag>
- <item>A string identifying the created key. It will be used
- to identify the key in planned future debug
- functionality.
- </item>
- <tag><c>key</c></tag>
- <item>A pointer to a thread specific data key variable.</item>
- </taglist>
- <p>This function creates a thread specific data key. On success
- <c>0</c> is returned; otherwise, an <c>errno</c> value is returned
- to indicate the error. The driver creating the key has the
- responsibility of destroying it before the driver is unloaded.
- </p>
+ <p>Creates a thread-specific data key.</p>
+ <p><c>name</c> is a string identifying the created key. It is used
+ to identify the key in planned future debug functionality.</p>
+ <p><c>key</c> is a pointer to a thread-specific data key variable.</p>
+ <p>Returns <c>0</c> on success, otherwise an <c>errno</c> value is
+ returned to indicate the error. The driver creating the key is
+ responsible for destroying it before the driver is unloaded.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_tsd_key_destroy(ErlDrvTSDKey key)</nametext></name>
- <fsummary>Destroy a thread specific data key</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_tsd_key_destroy(ErlDrvTSDKey
+ key)</nametext></name>
+ <fsummary>Destroy a thread-specific data key.</fsummary>
<desc>
<marker id="erl_drv_tsd_key_destroy"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>key</c></tag>
- <item>A thread specific data key to destroy.</item>
- </taglist>
- <p>This function destroys a thread specific data key
- previously created by
- <seealso marker="#erl_drv_tsd_key_create">erl_drv_tsd_key_create()</seealso>.
- All thread specific data using this key in all threads
- have to be cleared (see
- <seealso marker="#erl_drv_tsd_set">erl_drv_tsd_set()</seealso>)
- prior to the call to <c>erl_drv_tsd_key_destroy()</c>.
- </p>
- <warning><p>A destroyed key is very likely to be reused soon.
- Therefore, if you fail to clear the thread specific
- data using this key in a thread prior to destroying
- the key, you will <em>very likely</em> get unexpected
- errors in other parts of the system.
- </p></warning>
+ <p>Destroys a thread-specific data key previously created by
+ <seealso marker="#erl_drv_tsd_key_create">
+ <c>erl_drv_tsd_key_create</c></seealso>.
+ All thread-specific data using this key in all threads
+ must be cleared (see <seealso marker="#erl_drv_tsd_set">
+ <c>erl_drv_tsd_set</c></seealso>)
+ before the call to <c>erl_drv_tsd_key_destroy</c>.</p>
+ <p><c>key</c> is a thread-specific data key to destroy.</p>
+ <warning>
+ <p>A destroyed key is very likely to be reused soon.
+ Therefore, if you fail to clear the thread-specific
+ data using this key in a thread before destroying
+ the key, you will <em>very likely</em> get unexpected
+ errors in other parts of the system.</p>
+ </warning>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_tsd_set(ErlDrvTSDKey key, void *data)</nametext></name>
- <fsummary>Set thread specific data</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_tsd_set(ErlDrvTSDKey key, void
+ *data)</nametext></name>
+ <fsummary>Set thread-specific data.</fsummary>
<desc>
<marker id="erl_drv_tsd_set"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>key</c></tag>
- <item>A thread specific data key.</item>
- <tag><c>data</c></tag>
- <item>A pointer to data to associate with <c>key</c>
- in calling thread.
- </item>
- </taglist>
- <p>This function sets thread specific data associated with
- <c>key</c> for the calling thread. You are only allowed to set
- thread specific data for threads while they are fully under your
- control. For example, if you set thread specific data in a thread
- calling a driver call-back function, it has to be cleared, i.e.
- set to <c>NULL</c>, before returning from the driver call-back
- function.
- </p>
- <warning><p>If you fail to clear thread specific data in an
- emulator thread before letting it out of your control,
- you might not ever be able to clear this data with
- later unexpected errors in other parts of the system as
- a result.
- </p></warning>
+ <p>Sets thread-specific data associated with
+ <c>key</c> for the calling thread. You are only allowed to set
+ thread-specific data for threads while they are fully under your
+ control. For example, if you set thread-specific data in a thread
+ calling a driver callback function, it must be cleared, that is,
+ set to <c>NULL</c>, before returning from the driver callback
+ function.</p>
+ <p><c>key</c> is a thread-specific data key.</p>
+ <p><c>data</c> is a pointer to data to associate with <c>key</c>
+ in the calling thread.</p>
+ <warning>
+ <p>If you fail to clear thread-specific data in an
+ emulator thread before letting it out of your control,
+ you might never be able to clear this data with
+ later unexpected errors in other parts of the system as
+ a result.</p>
+ </warning>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void *</ret><nametext>erl_drv_tsd_get(ErlDrvTSDKey key)</nametext></name>
- <fsummary>Get thread specific data</fsummary>
+ <name><ret>char *</ret><nametext>erl_errno_id(int error)</nametext></name>
+ <fsummary>Get Erlang error atom name from error number.</fsummary>
<desc>
- <marker id="erl_drv_tsd_get"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>key</c></tag>
- <item>A thread specific data key.</item>
- </taglist>
- <p>This function returns the thread specific data
- associated with <c>key</c> for the calling thread.
- If no data has been associated with <c>key</c> for
- the calling thread, <c>NULL</c> is returned.
- </p>
- <p>This function is thread-safe.</p>
+ <marker id="erl_errno_id"></marker>
+ <p>Returns the atom name of the Erlang error,
+ given the error number in <c>error</c>. The error atoms are
+ <c>einval</c>, <c>enoent</c>, and so on. It can be used to make
+ error terms from the driver.</p>
</desc>
</func>
<func>
- <name><ret>int</ret><nametext>erl_drv_putenv(const char *key, char *value)</nametext></name>
- <fsummary>Set the value of an environment variable</fsummary>
+ <name><ret>int</ret><nametext>remove_driver_entry(ErlDrvEntry
+ *de)</nametext></name>
+ <fsummary>Remove a driver entry.</fsummary>
<desc>
- <marker id="erl_drv_putenv"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>key</c></tag>
- <item>A null terminated string containing the
- name of the environment variable.</item>
- <tag><c>value</c></tag>
- <item>A null terminated string containing the
- new value of the environment variable.</item>
- </taglist>
- <p>This function sets the value of an environment variable.
- It returns <c>0</c> on success, and a value <c>!= 0</c> on
- failure.
- </p>
- <note><p>The result of passing the empty string ("") as a value
- is platform dependent. On some platforms the value of the
- variable is set to the empty string, on others, the
- environment variable is removed.</p>
- </note>
- <warning><p>Do <em>not</em> use libc's <c>putenv</c> or similar
- C library interfaces from a driver.
- </p></warning>
- <p>This function is thread-safe.</p>
+ <marker id="remove_driver_entry"></marker>
+ <p>Removes a driver entry <c>de</c> previously added with
+ <seealso marker="#add_driver_entry">
+ <c>add_driver_entry</c></seealso>.</p>
+ <p>Driver entries added by the <c>erl_ddll</c> Erlang interface
+ cannot be removed by using this interface.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>erl_drv_getenv(const char *key, char *value, size_t *value_size)</nametext></name>
- <fsummary>Get the value of an environment variable</fsummary>
+ <name><ret>void</ret><nametext>set_busy_port(ErlDrvPort port, int
+ on)</nametext></name>
+ <fsummary>Signal or unsignal port as busy.</fsummary>
<desc>
- <marker id="erl_drv_getenv"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>key</c></tag>
- <item>A null terminated string containing the
- name of the environment variable.</item>
- <tag><c>value</c></tag>
- <item>A pointer to an output buffer.</item>
- <tag><c>value_size</c></tag>
- <item>A pointer to an integer. The integer is both used for
- passing input and output sizes (see below).
- </item>
- </taglist>
- <p>This function retrieves the value of an environment variable.
- When called, <c>*value_size</c> should contain the size of
- the <c>value</c> buffer. On success <c>0</c> is returned,
- the value of the environment variable has been written to
- the <c>value</c> buffer, and <c>*value_size</c> contains the
- string length (excluding the terminating null character) of
- the value written to the <c>value</c> buffer. On failure,
- i.e., no such environment variable was found, a value less than
- <c>0</c> is returned. When the size of the <c>value</c>
- buffer is too small, a value greater than <c>0</c> is returned
- and <c>*value_size</c> has been set to the buffer size needed.
- </p>
- <warning><p>Do <em>not</em> use libc's <c>getenv</c> or similar
- C library interfaces from a driver.
- </p></warning>
- <p>This function is thread-safe.</p>
+ <marker id="set_busy_port"></marker>
+ <p>Sets and unsets the busy state of the port. If
+ <c>on</c> is non-zero, the port is set to busy. If it is zero,
+ the port is set to not busy. You typically want to combine
+ this feature with the <seealso marker="#erl_drv_busy_msgq_limits">
+ busy port message queue</seealso> functionality.</p>
+ <p>Processes sending command data to the port are suspended
+ if either the port or the port message queue
+ is busy. Suspended processes are resumed when neither the
+ port or the port message queue is busy. Command data
+ is in this context data passed to the port using either
+ <c>Port ! {Owner, {command, Data}}</c> or
+ <c>port_command/[2,3]</c>.</p>
+ <p>If the <seealso marker="driver_entry#driver_flags">
+ <![CDATA[ERL_DRV_FLAG_SOFT_BUSY]]></seealso> has been set in the
+ <seealso marker="driver_entry"><c>driver_entry</c></seealso>,
+ data can be forced into the driver through
+ <seealso marker="erlang#port_command/3">
+ <c>erlang:port_command(Port, Data, [force])</c></seealso>
+ even if the driver has signaled that it is busy.</p>
+ <p>For information about busy port message queue functionality, see
+ <seealso marker="#erl_drv_busy_msgq_limits">
+ <c>erl_drv_busy_msgq_limits</c></seealso>.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>erl_drv_consume_timeslice(ErlDrvPort port, int percent)</nametext></name>
- <fsummary>Give the runtime system a hint about how much CPU time the
- current driver callback call has consumed</fsummary>
+ <name><ret>void</ret><nametext>set_port_control_flags(ErlDrvPort port,
+ int flags)</nametext></name>
+ <fsummary>Set flags on how to handle control entry function.</fsummary>
<desc>
- <marker id="erl_drv_consume_timeslice"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>port</c></tag>
- <item>Port handle of the executing port.</item>
- <tag><c>percent</c></tag>
- <item>Approximate consumed fraction of a full
- time-slice in percent.</item>
- </taglist>
- <p>Give the runtime system a hint about how much CPU time the
- current driver callback call has consumed since last hint, or
- since the start of the callback if no previous hint has been given.
- The time is given as a fraction, in percent, of a full time-slice
- that a port is allowed to execute before it should surrender the
- CPU to other runnable ports or processes. Valid range is
- <c>[1, 100]</c>. The scheduling time-slice is not an exact entity,
- but can usually be approximated to about 1 millisecond.</p>
-
- <p>Note that it is up to the runtime system to determine if and
- how to use this information. Implementations on some platforms
- may use other means in order to determine the consumed fraction
- of the time-slice. Lengthy driver callbacks should regardless of
- this frequently call the <c>erl_drv_consume_timeslice()</c>
- function in order to determine if it is allowed to continue
- execution or not.</p>
-
- <p><c>erl_drv_consume_timeslice()</c> returns a non-zero value
- if the time-slice has been exhausted, and zero if the callback is
- allowed to continue execution. If a non-zero value is
- returned the driver callback should return as soon as possible in
- order for the port to be able to yield.</p>
-
- <p>This function is provided to better support co-operative scheduling,
- improve system responsiveness, and to make it easier to prevent
- misbehaviors of the VM due to a port monopolizing a scheduler thread.
- It can be used when dividing length work into a number of repeated
- driver callback calls without the need to use threads. Also see the
- important <seealso marker="#WARNING">warning</seealso> text at the
- beginning of this document.</p>
- </desc>
- </func>
-
- <func>
- <name><ret>char *</ret><nametext>erl_drv_cond_name(ErlDrvCond *cnd)</nametext></name>
- <fsummary>Get name of driver mutex.</fsummary>
- <desc>
- <marker id="erl_drv_cnd_name"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>cnd</c></tag>
- <item>A pointer to an initialized condition.</item>
- </taglist>
- <p>
- Returns a pointer to the name of the condition.
- </p>
- <note>
- <p>This function is intended for debugging purposes only.</p>
- </note>
- </desc>
- </func>
-
- <func>
- <name><ret>char *</ret><nametext>erl_drv_mutex_name(ErlDrvMutex *mtx)</nametext></name>
- <fsummary>Get name of driver mutex.</fsummary>
- <desc>
- <marker id="erl_drv_mutex_name"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>mtx</c></tag>
- <item>A pointer to an initialized mutex.</item>
- </taglist>
- <p>
- Returns a pointer to the name of the mutex.
- </p>
- <note>
- <p>This function is intended for debugging purposes only.</p>
- </note>
- </desc>
- </func>
-
- <func>
- <name><ret>char *</ret><nametext>erl_drv_rwlock_name(ErlDrvRWLock *rwlck)</nametext></name>
- <fsummary>Get name of driver mutex.</fsummary>
- <desc>
- <marker id="erl_drv_rwlock_name"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>rwlck</c></tag>
- <item>A pointer to an initialized r/w-lock.</item>
- </taglist>
- <p>
- Returns a pointer to the name of the r/w-lock.
- </p>
- <note>
- <p>This function is intended for debugging purposes only.</p>
- </note>
- </desc>
- </func>
-
- <func>
- <name><ret>char *</ret><nametext>erl_drv_thread_name(ErlDrvTid tid)</nametext></name>
- <fsummary>Get name of driver mutex.</fsummary>
- <desc>
- <marker id="erl_drv_rwlock_name"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>tid</c></tag>
- <item>A thread identifier.</item>
- </taglist>
- <p>
- Returns a pointer to the name of the thread.
- </p>
- <note>
- <p>This function is intended for debugging purposes only.</p>
- </note>
- </desc>
- </func>
-
- <func>
- <name><ret>ErlDrvTime</ret><nametext>erl_drv_monotonic_time(ErlDrvTimeUnit time_unit)</nametext></name>
- <fsummary>Get Erlang Monotonic Time</fsummary>
- <desc>
- <marker id="erl_drv_monotonic_time"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>time_unit</c></tag>
- <item>Time unit of returned value.</item>
- </taglist>
- <p>
- Returns
- <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
- monotonic time</seealso>. Note that it is not uncommon with
- negative values.
- </p>
- <p>Returns <c>ERL_DRV_TIME_ERROR</c> if called with an invalid
- time unit argument, or if called from a thread that is not a
- scheduler thread.</p>
- <p>See also:</p>
- <list>
- <item><seealso marker="#ErlDrvTime"><c>ErlDrvTime</c></seealso></item>
- <item><seealso marker="#ErlDrvTimeUnit"><c>ErlDrvTimeUnit</c></seealso></item>
- </list>
- </desc>
- </func>
-
- <func>
- <name><ret>ErlDrvTime</ret><nametext>erl_drv_time_offset(ErlDrvTimeUnit time_unit)</nametext></name>
- <fsummary>Get current Time Offset</fsummary>
- <desc>
- <marker id="erl_drv_time_offset"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>time_unit</c></tag>
- <item>Time unit of returned value.</item>
- </taglist>
- <p>Returns the current time offset between
- <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang monotonic time</seealso>
- and
- <seealso marker="time_correction#Erlang_System_Time">Erlang system time</seealso>
- converted into the <c>time_unit</c> passed as argument.</p>
- <p>Returns <c>ERL_DRV_TIME_ERROR</c> if called with an invalid
- time unit argument, or if called from a thread that is not a
- scheduler thread.</p>
- <p>See also:</p>
- <list>
- <item><seealso marker="#ErlDrvTime"><c>ErlDrvTime</c></seealso></item>
- <item><seealso marker="#ErlDrvTimeUnit"><c>ErlDrvTimeUnit</c></seealso></item>
- </list>
- </desc>
- </func>
-
- <func>
- <name><ret>ErlDrvTime</ret><nametext>erl_drv_convert_time_unit(ErlDrvTime val, ErlDrvTimeUnit from, ErlDrvTimeUnit to)</nametext></name>
- <fsummary>Convert time unit of a time value</fsummary>
- <desc>
- <marker id="erl_drv_convert_time_unit"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>val</c></tag>
- <item>Value to convert time unit for.</item>
- <tag><c>from</c></tag>
- <item>Time unit of <c>val</c>.</item>
- <tag><c>to</c></tag>
- <item>Time unit of returned value.</item>
- </taglist>
- <p>Converts the <c>val</c> value of time unit <c>from</c> to
- the corresponding value of time unit <c>to</c>. The result is
- rounded using the floor function.</p>
- <p>Returns <c>ERL_DRV_TIME_ERROR</c> if called with an invalid
- time unit argument.</p>
- <p>See also:</p>
- <list>
- <item><seealso marker="#ErlDrvTime"><c>ErlDrvTime</c></seealso></item>
- <item><seealso marker="#ErlDrvTimeUnit"><c>ErlDrvTimeUnit</c></seealso></item>
- </list>
- </desc>
- </func>
-
+ <marker id="set_port_control_flags"></marker>
+ <p>Sets flags for how the <seealso marker="driver_entry#control">
+ <c>control</c></seealso> driver entry
+ function will return data to the port owner process.
+ (The <c>control</c> function is called from
+ <seealso marker="erlang:port_control/3">
+ <c>erlang:port_control/3</c></seealso>.)</p>
+ <p>Currently there are only two meaningful values for
+ <c>flags</c>: <c>0</c> means that data is returned in a list,
+ and <c>PORT_CONTROL_FLAG_BINARY</c> means data is returned as
+ a binary from <c>control</c>.</p>
+ </desc>
+ </func>
</funcs>
+
<section>
- <title>SEE ALSO</title>
- <p><seealso marker="driver_entry">driver_entry(3)</seealso>,
- <seealso marker="kernel:erl_ddll">erl_ddll(3)</seealso>,
- <seealso marker="erlang">erlang(3)</seealso></p>
- <p>An Alternative Distribution Driver (ERTS User's
- Guide Ch. 3)</p>
+ <title>See Also</title>
+ <p><seealso marker="driver_entry"><c>driver_entry(3)</c></seealso>,
+ <seealso marker="erlang"><c>erlang(3)</c></seealso>,
+ <seealso marker="kernel:erl_ddll"><c>erl_ddll(3)</c></seealso>,
+ section <seealso marker="alt_dist">How to Implement an Alternative
+ Carrier for the Erlang Distribution></seealso> in the User's Guide</p>
</section>
</cref>
diff --git a/erts/doc/src/erl_ext_dist.xml b/erts/doc/src/erl_ext_dist.xml
index 2ac974f497..4f799f8f34 100644
--- a/erts/doc/src/erl_ext_dist.xml
+++ b/erts/doc/src/erl_ext_dist.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2007</year>
- <year>2015</year>
+ <year>2016</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -35,135 +35,130 @@
<section>
<title>Introduction</title>
<p>
- The external term format is mainly used in the distribution
+ The external term format is mainly used in the distribution
mechanism of Erlang.
</p>
<p>
- Since Erlang has a fixed number of types, there is no need for a
- programmer to define a specification for the external format used
+ As Erlang has a fixed number of types, there is no need for a
+ programmer to define a specification for the external format used
within some application.
- All Erlang terms has an external representation and the interpretation
- of the different terms are application specific.
+ All Erlang terms have an external representation and the interpretation
+ of the different terms is application-specific.
</p>
<p>
- In Erlang the BIF <seealso marker="erts:erlang#term_to_binary/1">term_to_binary/1,2</seealso> is used to convert a
- term into the external format.
- To convert binary data encoding a term the BIF
+ In Erlang the BIF <seealso marker="erts:erlang#term_to_binary/1">
+ <c>erlang:term_to_binary/1,2</c></seealso> is used to convert a
+ term into the external format.
+ To convert binary data encoding to a term, the BIF
<seealso marker="erts:erlang#binary_to_term/1">
- binary_to_term/1
- </seealso>
- is used.
+ <c>erlang:binary_to_term/1</c>c></seealso> is used.
</p>
<p>
- The distribution does this implicitly when sending messages across
+ The distribution does this implicitly when sending messages across
node boundaries.
</p>
<marker id="overall_format"/>
<p>
- The overall format of the term format is:
+ The overall format of the term format is as follows:
</p>
<table align="left">
<row>
- <cell align="center">1</cell>
- <cell align="center">1</cell>
- <cell align="center">N</cell>
+ <cell align="center">1</cell>
+ <cell align="center">1</cell>
+ <cell align="center">N</cell>
</row>
- <row>
- <cell align="center"><c>131</c></cell>
- <cell align="center"><c>Tag</c></cell>
- <cell align="center"><c>Data</c></cell>
- </row>
- <tcaption></tcaption></table>
+ <row>
+ <cell align="center"><c>131</c></cell>
+ <cell align="center"><c>Tag</c></cell>
+ <cell align="center"><c>Data</c></cell>
+ </row>
+ <tcaption>Term Format</tcaption></table>
<note>
- <p>
- When messages are
- <seealso marker="erl_dist_protocol#connected_nodes">passed between
- connected nodes</seealso> and a
- <seealso marker="#distribution_header">distribution
- header</seealso> is used, the first byte containing the version
- number (131) is omitted from the terms that follow the distribution
- header. This since
- the version number is implied by the version number in the
- distribution header.
- </p>
+ <p>
+ When messages are
+ <seealso marker="erl_dist_protocol#connected_nodes">passed between
+ connected nodes</seealso> and a
+ <seealso marker="#distribution_header">distribution
+ header</seealso> is used, the first byte containing the version
+ number (131) is omitted from the terms that follow the distribution
+ header. This is because the version number is implied by the version
+ number in the distribution header.
+ </p>
</note>
<p>
- A compressed term looks like this:
+ The compressed term format is as follows:
</p>
<table align="left">
<row>
- <cell align="center">1</cell>
- <cell align="center">1</cell>
- <cell align="center">4</cell>
- <cell align="center">N</cell>
+ <cell align="center">1</cell>
+ <cell align="center">1</cell>
+ <cell align="center">4</cell>
+ <cell align="center">N</cell>
</row>
<row>
- <cell align="center">131</cell>
- <cell align="center">80</cell>
- <cell align="center">UncompressedSize</cell>
- <cell align="center">Zlib-compressedData</cell>
+ <cell align="center"><c>131</c></cell>
+ <cell align="center"><c>80</c></cell>
+ <cell align="center"><c>UncompressedSize</c></cell>
+ <cell align="center"><c>Zlib-compressedData</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>Compressed Term Format</tcaption></table>
<p>
- Uncompressed Size (unsigned 32 bit integer in big-endian byte order)
+ Uncompressed size (unsigned 32-bit integer in big-endian byte order)
is the size of the data before it was compressed.
- The compressed data has the following format when it has been
- expanded:
+ The compressed data has the following format when it has been expanded:
</p>
<table align="left">
<row>
- <cell align="center">1</cell>
- <cell align="center">Uncompressed Size</cell>
+ <cell align="center">1</cell>
+ <cell align="center">Uncompressed Size</cell>
</row>
<row>
- <cell align="center">Tag</cell>
- <cell align="center">Data</cell>
+ <cell align="center"><c>Tag</c></cell>
+ <cell align="center"><c>Data</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>Compressed Data Format when Expanded</tcaption></table>
<marker id="utf8_atoms"/>
<note>
- <p>As of ERTS version 5.10 (OTP-R16) support
- for UTF-8 encoded atoms has been introduced in the external format.
- However, only characters that can be encoded using Latin1 (ISO-8859-1)
- are currently supported in atoms. The support for UTF-8 encoded atoms
- in the external format has been implemented in order to be able to support
- all Unicode characters in atoms in <em>some future release</em>.
- Until full Unicode support for
- atoms has been introduced, it is an <em>error</em> to pass atoms containing
- characters that cannot be encoded in Latin1, and <em>the behavior is
- undefined</em>.</p>
- <p>When the
- <seealso marker="erl_dist_protocol#dflags"><c>DFLAG_UTF8_ATOMS</c></seealso>
- distribution flag has been exchanged between both nodes in the
- <seealso marker="erl_dist_protocol#distribution_handshake">distribution handshake</seealso>,
- all atoms in the distribution header will be encoded in UTF-8; otherwise,
- all atoms in the distribution header will be encoded in Latin1. The two
- new tags <seealso marker="#ATOM_UTF8_EXT">ATOM_UTF8_EXT</seealso>, and
- <seealso marker="#SMALL_ATOM_UTF8_EXT">SMALL_ATOM_UTF8_EXT</seealso>
- will only be used if the <c>DFLAG_UTF8_ATOMS</c> distribution flag has
- been exchanged between nodes, or if an atom containing characters
- that cannot be encoded in Latin1 is encountered.
- </p>
- <p>The maximum number of allowed characters in an atom is 255. In the
- UTF-8 case each character may need 4 bytes to be encoded.
- </p>
+ <p>As from ERTS 5.10 (OTP R16) support
+ for UTF-8 encoded atoms has been introduced in the external format.
+ However, only characters that can be encoded using Latin-1 (ISO-8859-1)
+ are currently supported in atoms. The support for UTF-8 encoded atoms
+ in the external format has been implemented to be able to support
+ all Unicode characters in atoms in <em>some future release</em>.
+ Until full Unicode support for atoms has been introduced,
+ it is an <em>error</em> to pass atoms containing
+ characters that cannot be encoded in Latin-1, and <em>the behavior is
+ undefined</em>.</p>
+ <p>When distribution flag <seealso marker="erl_dist_protocol#dflags">
+ <c>DFLAG_UTF8_ATOMS</c></seealso> has been exchanged between both nodes
+ in the <seealso marker="erl_dist_protocol#distribution_handshake">
+ distribution handshake</seealso>, all atoms in the distribution header
+ are encoded in UTF-8, otherwise in Latin-1. The two
+ new tags <seealso marker="#ATOM_UTF8_EXT"><c>ATOM_UTF8_EXT</c></seealso>
+ and <seealso marker="#SMALL_ATOM_UTF8_EXT">
+ <c>SMALL_ATOM_UTF8_EXT</c></seealso>
+ are only used if the distribution flag <c>DFLAG_UTF8_ATOMS</c> has
+ been exchanged between nodes, or if an atom containing characters
+ that cannot be encoded in Latin-1 is encountered.</p>
+ <p>The maximum number of allowed characters in an atom is 255. In the
+ UTF-8 case, each character can need 4 bytes to be encoded.</p>
</note>
</section>
<section>
- <title>Distribution header</title>
+ <title>Distribution Header</title>
<p>
<marker id="distribution_header"/>
- As of erts version 5.7.2 the old atom cache protocol was
- dropped and a new one was introduced. This atom cache protocol
- introduced the distribution header. Nodes with erts versions
+ As from ERTS 5.7.2 the old atom cache protocol was
+ dropped and a new one was introduced. This protocol
+ introduced the distribution header. Nodes with an ERTS version
earlier than 5.7.2 can still communicate with new nodes,
- but no distribution header and no atom cache will be used.</p>
+ but no distribution header and no atom cache are used.</p>
<p>
- The distribution header currently only contains an atom cache
- reference section, but could in the future contain more
+ The distribution header only contains an atom cache
+ reference section, but can in the future contain more
information. The distribution header precedes one or more Erlang
- terms on the external format. For more information see the
+ terms on the external format. For more information, see the
documentation of the
<seealso marker="erl_dist_protocol#connected_nodes">protocol between
connected nodes</seealso> in the
@@ -173,37 +168,37 @@
<p>
<seealso marker="#ATOM_CACHE_REF">ATOM_CACHE_REF</seealso>
entries with corresponding <c>AtomCacheReferenceIndex</c> in terms
- encoded on the external format following a distribution header refers
+ encoded on the external format following a distribution header refer
to the atom cache references made in the distribution header. The range
- is 0 &lt;= <c>AtomCacheReferenceIndex</c> &lt; 255, i.e., at most 255
+ is 0 &lt;= <c>AtomCacheReferenceIndex</c> &lt; 255, that is, at most 255
different atom cache references from the following terms can be made.
</p>
<p>
- The distribution header format is:
+ The distribution header format is as follows:
</p>
<table align="left">
<row>
- <cell align="center">1</cell>
- <cell align="center">1</cell>
- <cell align="center">1</cell>
- <cell align="center">NumberOfAtomCacheRefs/2+1 | 0</cell>
- <cell align="center">N | 0</cell>
+ <cell align="center">1</cell>
+ <cell align="center">1</cell>
+ <cell align="center">1</cell>
+ <cell align="center">NumberOfAtomCacheRefs/2+1 | 0</cell>
+ <cell align="center">N | 0</cell>
</row>
<row>
- <cell align="center"><c>131</c></cell>
- <cell align="center"><c>68</c></cell>
- <cell align="center"><c>NumberOfAtomCacheRefs</c></cell>
- <cell align="center"><c>Flags</c></cell>
- <cell align="center"><c>AtomCacheRefs</c></cell>
+ <cell align="center"><c>131</c></cell>
+ <cell align="center"><c>68</c></cell>
+ <cell align="center"><c>NumberOfAtomCacheRefs</c></cell>
+ <cell align="center"><c>Flags</c></cell>
+ <cell align="center"><c>AtomCacheRefs</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>Distribution Header Format</tcaption></table>
<p>
- <c>Flags</c> consists of <c>NumberOfAtomCacheRefs/2+1</c> bytes,
+ <c>Flags</c> consist of <c>NumberOfAtomCacheRefs/2+1</c> bytes,
unless <c>NumberOfAtomCacheRefs</c> is <c>0</c>. If
<c>NumberOfAtomCacheRefs</c> is <c>0</c>, <c>Flags</c> and
- <c>AtomCacheRefs</c> are omitted. Each atom cache reference have
+ <c>AtomCacheRefs</c> are omitted. Each atom cache reference has
a half byte flag field. Flags corresponding to a specific
- <c>AtomCacheReferenceIndex</c>, are located in flag byte number
+ <c>AtomCacheReferenceIndex</c> are located in flag byte number
<c>AtomCacheReferenceIndex/2</c>. Flag byte 0 is the first byte
after the <c>NumberOfAtomCacheRefs</c> byte. Flags for an even
<c>AtomCacheReferenceIndex</c> are located in the least significant
@@ -216,95 +211,97 @@
</p>
<table align="left">
<row>
- <cell align="center">1 bit</cell>
- <cell align="center">3 bits</cell>
+ <cell align="center">1 bit</cell>
+ <cell align="center">3 bits</cell>
</row>
<row>
- <cell align="center"><c>NewCacheEntryFlag</c></cell>
- <cell align="center"><c>SegmentIndex</c></cell>
+ <cell align="center"><c>NewCacheEntryFlag</c></cell>
+ <cell align="center"><c>SegmentIndex</c></cell>
</row>
<tcaption></tcaption></table>
<p>
The most significant bit is the <c>NewCacheEntryFlag</c>. If set,
the corresponding cache reference is new. The three least
significant bits are the <c>SegmentIndex</c> of the corresponding
- atom cache entry. An atom cache consists of 8 segments each of size
- 256, i.e., an atom cache can contain 2048 entries.
+ atom cache entry. An atom cache consists of 8 segments, each of size
+ 256, that is, an atom cache can contain 2048 entries.
</p>
<p>
After flag fields for atom cache references, another half byte flag
- field is located which has the following format:
+ field is located with the following format:
</p>
<table align="left">
<row>
- <cell align="center">3 bits</cell>
- <cell align="center">1 bit</cell>
+ <cell align="center">3 bits</cell>
+ <cell align="center">1 bit</cell>
</row>
<row>
- <cell align="center"><c>CurrentlyUnused</c></cell>
- <cell align="center"><c>LongAtoms</c></cell>
+ <cell align="center"><c>CurrentlyUnused</c></cell>
+ <cell align="center"><c>LongAtoms</c></cell>
</row>
<tcaption></tcaption></table>
<p>
- The least significant bit in that half byte is the <c>LongAtoms</c>
- flag. If it is set, 2 bytes are used for atom lengths instead of
+ The least significant bit in that half byte is flag <c>LongAtoms</c>.
+ If it is set, 2 bytes are used for atom lengths instead of
1 byte in the distribution header.
</p>
<p>
After the <c>Flags</c> field follow the <c>AtomCacheRefs</c>. The
first <c>AtomCacheRef</c> is the one corresponding to
- <c>AtomCacheReferenceIndex</c> 0. Higher indices follows
+ <c>AtomCacheReferenceIndex</c> 0. Higher indices follow
in sequence up to index <c>NumberOfAtomCacheRefs - 1</c>.
</p>
<p>
If the <c>NewCacheEntryFlag</c> for the next <c>AtomCacheRef</c> has
- been set, a <c>NewAtomCacheRef</c> on the following format will follow:
+ been set, a <c>NewAtomCacheRef</c> on the following format follows:
</p>
<table align="left">
<row>
- <cell align="center">1</cell>
- <cell align="center">1 | 2</cell>
- <cell align="center">Length</cell>
+ <cell align="center">1</cell>
+ <cell align="center">1 | 2</cell>
+ <cell align="center">Length</cell>
</row>
<row>
- <cell align="center"><c>InternalSegmentIndex</c></cell>
- <cell align="center"><c>Length</c></cell>
- <cell align="center"><c>AtomText</c></cell>
+ <cell align="center"><c>InternalSegmentIndex</c></cell>
+ <cell align="center"><c>Length</c></cell>
+ <cell align="center"><c>AtomText</c></cell>
</row>
<tcaption></tcaption></table>
<p>
<c>InternalSegmentIndex</c> together with the <c>SegmentIndex</c>
completely identify the location of an atom cache entry in the
- atom cache. <c>Length</c> is number of bytes that <c>AtomText</c>
- consists of. Length is a two byte big endian integer
- if the <c>LongAtoms</c> flag has been set, otherwise a one byte
- integer. When the
- <seealso marker="erl_dist_protocol#dflags"><c>DFLAG_UTF8_ATOMS</c></seealso>
- distribution flag has been exchanged between both nodes in the
- <seealso marker="erl_dist_protocol#distribution_handshake">distribution handshake</seealso>,
- characters in <c>AtomText</c> is encoded in UTF-8; otherwise,
- encoded in Latin1. Subsequent <c>CachedAtomRef</c>s with the same
+ atom cache. <c>Length</c> is the number of bytes that <c>AtomText</c>
+ consists of. Length is a 2 byte big-endian integer
+ if flag <c>LongAtoms</c> has been set, otherwise a 1 byte
+ integer. When distribution flag
+ <seealso marker="erl_dist_protocol#dflags">
+ <c>DFLAG_UTF8_ATOMS</c></seealso>
+ has been exchanged between both nodes in the
+ <seealso marker="erl_dist_protocol#distribution_handshake">
+ distribution handshake</seealso>,
+ characters in <c>AtomText</c> are encoded in UTF-8, otherwise
+ in Latin-1. The following <c>CachedAtomRef</c>s with the same
<c>SegmentIndex</c> and <c>InternalSegmentIndex</c> as this
- <c>NewAtomCacheRef</c> will refer to this atom until a new
+ <c>NewAtomCacheRef</c> refer to this atom until a new
<c>NewAtomCacheRef</c> with the same <c>SegmentIndex</c>
and <c>InternalSegmentIndex</c> appear.
</p>
<p>
- For more information on encoding of atoms, see
+ For more information on encoding of atoms, see the
<seealso marker="#utf8_atoms">note on UTF-8 encoded atoms</seealso>
- in the beginning of this document.
+ in the beginning of this section.
</p>
<p>
If the <c>NewCacheEntryFlag</c> for the next <c>AtomCacheRef</c>
has not been set, a <c>CachedAtomRef</c> on the following format
- will follow:
+ follows:
</p>
<table align="left">
<row>
- <cell align="center">1</cell>
+ <cell align="center">1</cell>
</row>
<row>
- <cell align="center"><c>InternalSegmentIndex</c></cell>
+ <cell align="center"><c>InternalSegmentIndex</c></cell>
</row>
<tcaption></tcaption></table>
<p>
@@ -319,157 +316,147 @@
<section>
<marker id="ATOM_CACHE_REF"/>
<title>ATOM_CACHE_REF</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
- <cell align="center">1</cell>
+ <cell align="center">1</cell>
</row>
<row>
- <cell align="center"><c>82</c></cell>
- <cell align="center"><c>AtomCacheReferenceIndex</c></cell>
+ <cell align="center"><c>82</c></cell>
+ <cell align="center"><c>AtomCacheReferenceIndex</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>ATOM_CACHE_REF</tcaption></table>
<p>
Refers to the atom with <c>AtomCacheReferenceIndex</c> in the
- <seealso marker="#distribution_header">distribution header</seealso>.
+ <seealso marker="#distribution_header">distribution header</seealso>.
</p>
</section>
<section>
<marker id="SMALL_INTEGER_EXT"/>
<title>SMALL_INTEGER_EXT</title>
-
<table align="left">
<row>
- <cell align="center">1</cell>
- <cell align="center">1</cell>
+ <cell align="center">1</cell>
+ <cell align="center">1</cell>
</row>
<row>
- <cell align="center">97</cell>
- <cell align="center">Int</cell>
+ <cell align="center"><c>97</c></cell>
+ <cell align="center"><c>Int</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>SMALL_INTEGER_EXT</tcaption></table>
<p>
- Unsigned 8 bit integer.
+ Unsigned 8-bit integer.
</p>
</section>
<section>
<marker id="INTEGER_EXT"/>
<title>INTEGER_EXT</title>
-
<table align="left">
<row>
- <cell align="center">1</cell>
- <cell align="center">4</cell>
+ <cell align="center">1</cell>
+ <cell align="center">4</cell>
</row>
<row>
- <cell align="center">98</cell>
- <cell align="center">Int</cell>
+ <cell align="center"><c>98</c></cell>
+ <cell align="center"><c>Int</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>INTEGER_EXT</tcaption></table>
<p>
- Signed 32 bit integer in big-endian format (i.e. MSB first)
+ Signed 32-bit integer in big-endian format.
</p>
</section>
<section>
<marker id="FLOAT_EXT"/>
<title>FLOAT_EXT</title>
-
<table align="left">
<row>
- <cell align="center">1</cell>
- <cell align="center">31</cell>
+ <cell align="center">1</cell>
+ <cell align="center">31</cell>
</row>
<row>
- <cell align="center">99</cell>
- <cell align="center">Float String</cell>
+ <cell align="center"><c>99</c></cell>
+ <cell align="center"><c>Float string</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>FLOAT_EXT</tcaption></table>
<p>
- A float is stored in string format. the format used in sprintf to
- format the float is "%.20e"
+ A float is stored in string format. The format used in sprintf to
+ format the float is "%.20e"
(there are more bytes allocated than necessary).
- To unpack the float use sscanf with format "%lf".
+ To unpack the float, use sscanf with format "%lf".
</p>
<p>
- This term is used in minor version 0 of the external format;
- it has been superseded by
- <seealso marker="#NEW_FLOAT_EXT">
- NEW_FLOAT_EXT
- </seealso>.
+ This term is used in minor version 0 of the external format;
+ it has been superseded by
+ <seealso marker="#NEW_FLOAT_EXT"><c>NEW_FLOAT_EXT</c></seealso>.
</p>
</section>
<section>
<marker id="ATOM_EXT"/>
<title>ATOM_EXT</title>
-
<table align="left">
<row>
- <cell align="center">1</cell>
- <cell align="center">2</cell>
- <cell align="center">Len</cell>
+ <cell align="center">1</cell>
+ <cell align="center">2</cell>
+ <cell align="center">Len</cell>
</row>
<row>
- <cell align="center"><c>100</c></cell>
- <cell align="center"><c>Len</c></cell>
- <cell align="center"><c>AtomName</c></cell>
+ <cell align="center"><c>100</c></cell>
+ <cell align="center"><c>Len</c></cell>
+ <cell align="center"><c>AtomName</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>ATOM_EXT</tcaption></table>
<p>
- An atom is stored with a 2 byte unsigned length in big-endian order,
- followed by <c>Len</c> numbers of 8 bit Latin1 characters that forms
- the <c>AtomName</c>.
- <em>Note</em>: The maximum allowed value for <c>Len</c> is 255.
+ An atom is stored with a 2 byte unsigned length in big-endian order,
+ followed by <c>Len</c> numbers of 8-bit Latin-1 characters that forms
+ the <c>AtomName</c>. The maximum allowed value for <c>Len</c> is 255.
</p>
</section>
<section>
<marker id="REFERENCE_EXT"/>
<title>REFERENCE_EXT</title>
-
- <table align="left">
- <row>
- <cell align="center">1</cell>
- <cell align="center">N</cell>
- <cell align="center">4</cell>
- <cell align="center">1</cell>
- </row>
- <row>
- <cell align="center"><c>101</c></cell>
- <cell align="center"><c>Node</c></cell>
- <cell align="center"><c>ID</c></cell>
- <cell align="center"><c>Creation</c></cell>
- </row>
- <tcaption></tcaption></table>
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">N</cell>
+ <cell align="center">4</cell>
+ <cell align="center">1</cell>
+ </row>
+ <row>
+ <cell align="center"><c>101</c></cell>
+ <cell align="center"><c>Node</c></cell>
+ <cell align="center"><c>ID</c></cell>
+ <cell align="center"><c>Creation</c></cell>
+ </row>
+ <tcaption>REFERENCE_EXT</tcaption></table>
<p>
- Encode a reference object (an object generated with <c>make_ref/0</c>).
- The <c>Node</c> term is an encoded atom, i.e.
- <seealso marker="#ATOM_EXT">ATOM_EXT</seealso>,
- <seealso marker="#SMALL_ATOM_EXT">SMALL_ATOM_EXT</seealso> or
- <seealso marker="#ATOM_CACHE_REF">ATOM_CACHE_REF</seealso>.
- The <c>ID</c> field contains a big-endian
- unsigned integer,
- but <em>should be regarded as uninterpreted data</em>
- since this field is node specific.
- <c>Creation</c> is a byte containing a node serial number that
- makes it possible to separate old (crashed) nodes from a new one.
+ Encodes a reference object (an object generated with
+ <seealso marker="erlang:make_ref/0">erlang:make_ref/0</seealso>).
+ The <c>Node</c> term is an encoded atom, that is,
+ <seealso marker="#ATOM_EXT"><c>ATOM_EXT</c></seealso>,
+ <seealso marker="#SMALL_ATOM_EXT"><c>SMALL_ATOM_EXT</c></seealso>, or
+ <seealso marker="#ATOM_CACHE_REF"><c>ATOM_CACHE_REF</c></seealso>.
+ The <c>ID</c> field contains a big-endian unsigned integer,
+ but <em>is to be regarded as uninterpreted data</em>,
+ as this field is node-specific.
+ <c>Creation</c> is a byte containing a node serial number, which
+ makes it possible to separate old (crashed) nodes from a new one.
</p>
<p>
- In <c>ID</c>, only 18 bits are significant; the rest should be 0.
- In <c>Creation</c>, only 2 bits are significant; the rest should be 0.
-
- See <seealso marker="#NEW_REFERENCE_EXT">NEW_REFERENCE_EXT</seealso>.
+ In <c>ID</c>, only 18 bits are significant; the rest are to be 0.
+ In <c>Creation</c>, only two bits are significant; the rest are to be 0.
+ See <seealso marker="#NEW_REFERENCE_EXT">
+ <c>NEW_REFERENCE_EXT</c></seealso>.
</p>
</section>
<section>
<marker id="PORT_EXT"/>
<title>PORT_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -483,20 +470,21 @@
<cell align="center"><c>ID</c></cell>
<cell align="center"><c>Creation</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>PORT_EXT</tcaption></table>
<p>
- Encode a port object (obtained form <c>open_port/2</c>).
- The <c>ID</c> is a node specific identifier for a local port.
+ Encodes a port object (obtained from
+ <seealso marker="erlang:open_port/2">
+ <c>erlang:open_port/2</c></seealso>).
+ The <c>ID</c> is a node-specific identifier for a local port.
Port operations are not allowed across node boundaries.
The <c>Creation</c> works just like in
- <seealso marker="#REFERENCE_EXT">REFERENCE_EXT</seealso>.
+ <seealso marker="#REFERENCE_EXT"><c>REFERENCE_EXT</c></seealso>.
</p>
</section>
<section>
<marker id="PID_EXT"/>
<title>PID_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -512,23 +500,20 @@
<cell align="center"><c>Serial</c></cell>
<cell align="center"><c>Creation</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>PID_EXT</tcaption></table>
<p>
- Encode a process identifier object (obtained from <c>spawn/3</c> or
- friends).
- The <c>ID</c> and <c>Creation</c> fields works just like in
- <seealso marker="#REFERENCE_EXT">REFERENCE_EXT</seealso>, while
- the <c>Serial</c> field is used to improve safety.
-
- In <c>ID</c>, only 15 bits are significant; the rest should be 0.
+ Encodes a process identifier object (obtained from
+ <seealso marker="erlang:spawn/3"><c>erlang:spawn/3</c></seealso> or
+ friends). The <c>ID</c> and <c>Creation</c> fields works just like in
+ <seealso marker="#REFERENCE_EXT"><c>REFERENCE_EXT</c></seealso>, while
+ the <c>Serial</c> field is used to improve safety.
+ In <c>ID</c>, only 15 bits are significant; the rest are to be 0.
</p>
-
</section>
<section>
<marker id="SMALL_TUPLE_EXT"/>
<title>SMALL_TUPLE_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -536,22 +521,21 @@
<cell align="center">N</cell>
</row>
<row>
- <cell align="center">104</cell>
- <cell align="center">Arity</cell>
- <cell align="center">Elements</cell>
+ <cell align="center"><c>104</c></cell>
+ <cell align="center"><c>Arity</c></cell>
+ <cell align="center"><c>Elements</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>SMALL_TUPLE_EXT</tcaption></table>
<p>
- <c>SMALL_TUPLE_EXT</c> encodes a tuple. The <c>Arity</c>
- field is an unsigned byte that determines how many element
- that follows in the <c>Elements</c> section.
+ Encodes a tuple. The <c>Arity</c>
+ field is an unsigned byte that determines how many elements
+ that follows in section <c>Elements</c>.
</p>
</section>
<section>
<marker id="LARGE_TUPLE_EXT"/>
<title>LARGE_TUPLE_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -559,23 +543,22 @@
<cell align="center">N</cell>
</row>
<row>
- <cell align="center">105</cell>
- <cell align="center">Arity</cell>
- <cell align="center">Elements</cell>
+ <cell align="center"><c>105</c></cell>
+ <cell align="center"><c>Arity</c></cell>
+ <cell align="center"><c>Elements</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>LARGE_TUPLE_EXT</tcaption></table>
<p>
- Same as
- <seealso marker="#SMALL_TUPLE_EXT">SMALL_TUPLE_EXT</seealso>
- with the exception that <c>Arity</c> is an
- unsigned 4 byte integer in big endian format.
+ Same as
+ <seealso marker="#SMALL_TUPLE_EXT"><c>SMALL_TUPLE_EXT</c></seealso>
+ except that <c>Arity</c> is an
+ unsigned 4 byte integer in big-endian format.
</p>
</section>
<section>
<marker id="MAP_EXT"/>
<title>MAP_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -583,43 +566,42 @@
<cell align="center">N</cell>
</row>
<row>
- <cell align="center">116</cell>
- <cell align="center">Arity</cell>
- <cell align="center">Pairs</cell>
+ <cell align="center"><c>116</c></cell>
+ <cell align="center"><c>Arity</c></cell>
+ <cell align="center"><c>Pairs</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>MAP_EXT</tcaption></table>
<p>
- <c>MAP_EXT</c> encodes a map. The <c>Arity</c> field is an unsigned
- 4 byte integer in big endian format that determines the number of
+ Encodes a map. The <c>Arity</c> field is an unsigned
+ 4 byte integer in big-endian format that determines the number of
key-value pairs in the map. Key and value pairs (<c>Ki => Vi</c>)
- are encoded in the <c>Pairs</c> section in the following order:
+ are encoded in section <c>Pairs</c> in the following order:
<c>K1, V1, K2, V2,..., Kn, Vn</c>.
Duplicate keys are <em>not allowed</em> within the same map.
</p>
- <p><em>Since: </em>OTP 17.0</p>
+ <p><em>As from </em>Erlang/OTP 17.0</p>
</section>
<section>
<marker id="NIL_EXT"/>
<title>NIL_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
</row>
<row>
- <cell align="center">106</cell>
+ <cell align="center"><c>106</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>NIL_EXT</tcaption></table>
<p>
- The representation for an empty list, i.e. the Erlang syntax <c>[]</c>.
+ The representation for an empty list, that is, the Erlang syntax
+ <c>[]</c>.
</p>
</section>
<section>
<marker id="STRING_EXT"/>
<title>STRING_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -627,27 +609,25 @@
<cell align="center">Len</cell>
</row>
<row>
- <cell align="center">107</cell>
- <cell align="center">Length</cell>
- <cell align="center">Characters</cell>
+ <cell align="center"><c>107</c></cell>
+ <cell align="center"><c>Length</c></cell>
+ <cell align="center"><c>Characters</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>STRING_EXT</tcaption></table>
<p>
- String does NOT have a corresponding Erlang representation,
+ String does <em>not</em> have a corresponding Erlang representation,
but is an optimization for sending lists of bytes (integer in
the range 0-255) more efficiently over the distribution.
- Since the <c>Length</c> field is an unsigned 2 byte integer
- (big endian), implementations must make sure that lists longer than
- 65535 elements are encoded as
- <seealso marker="#LIST_EXT">LIST_EXT</seealso>.
+ As field <c>Length</c> is an unsigned 2 byte integer
+ (big-endian), implementations must ensure that lists longer than
+ 65535 elements are encoded as
+ <seealso marker="#LIST_EXT"><c>LIST_EXT</c></seealso>.
</p>
-
</section>
<section>
<marker id="LIST_EXT"/>
<title>LIST_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -656,27 +636,24 @@
<cell align="center">&nbsp;</cell>
</row>
<row>
- <cell align="center">108</cell>
- <cell align="center">Length</cell>
- <cell align="center">Elements</cell>
- <cell align="center">Tail</cell>
+ <cell align="center"><c>108</c></cell>
+ <cell align="center"><c>Length</c></cell>
+ <cell align="center"><c>Elements</c></cell>
+ <cell align="center"><c>Tail</c></cell>
</row>
- <tcaption></tcaption></table>
-
+ <tcaption>LIST_EXT</tcaption></table>
<p>
- <c>Length</c> is the number of elements that follows in the
- <c>Elements</c> section. <c>Tail</c> is the final tail of
- the list; it is
- <seealso marker="#NIL_EXT">NIL_EXT</seealso>
- for a proper list, but may be anything type if the list is
- improper (for instance <c>[a|b]</c>).
+ <c>Length</c> is the number of elements that follows in section
+ <c>Elements</c>. <c>Tail</c> is the final tail of the list; it is
+ <seealso marker="#NIL_EXT"><c>NIL_EXT</c></seealso>
+ for a proper list, but can be any type if the list is
+ improper (for example, <c>[a|b]</c>).
</p>
</section>
<section>
<marker id="BINARY_EXT"/>
<title>BINARY_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -684,25 +661,26 @@
<cell align="center">Len</cell>
</row>
<row>
- <cell align="center">109</cell>
- <cell align="center">Len</cell>
- <cell align="center">Data</cell>
+ <cell align="center"><c>109</c></cell>
+ <cell align="center"><c>Len</c></cell>
+ <cell align="center"><c>Data</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>BINARY_EXT</tcaption></table>
<p>
Binaries are generated with bit syntax expression or with
- <seealso marker="erts:erlang#list_to_binary/1">list_to_binary/1</seealso>,
- <seealso marker="erts:erlang#term_to_binary/1">term_to_binary/1</seealso>,
+ <seealso marker="erts:erlang#list_to_binary/1">
+ <c>erlang:list_to_binary/1</c></seealso>,
+ <seealso marker="erts:erlang#term_to_binary/1">
+ <c>erlang:term_to_binary/1</c></seealso>,
or as input from binary ports.
- The <c>Len</c> length field is an unsigned 4 byte integer
- (big endian).
+ The <c>Len</c> length field is an unsigned 4 byte integer
+ (big-endian).
</p>
</section>
<section>
<marker id="SMALL_BIG_EXT"/>
<title>SMALL_BIG_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -711,27 +689,26 @@
<cell align="center">n</cell>
</row>
<row>
- <cell align="center">110</cell>
- <cell align="center">n</cell>
- <cell align="center">Sign</cell>
- <cell align="center">d(0) ... d(n-1)</cell>
+ <cell align="center"><c>110</c></cell>
+ <cell align="center"><c>n</c></cell>
+ <cell align="center"><c>Sign</c></cell>
+ <cell align="center"><c>d(0)</c> ... <c>d(n-1)</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>SMALL_BIG_EXT</tcaption></table>
<p>
- Bignums are stored in unary form with a <c>Sign</c> byte
- that is 0 if the binum is positive and 1 if is negative. The
- digits are stored with the LSB byte stored first. To
- calculate the integer the following formula can be used:<br/>
-
- B = 256<br/>
- (d0*B^0 + d1*B^1 + d2*B^2 + ... d(N-1)*B^(n-1))
+ Bignums are stored in unary form with a <c>Sign</c> byte,
+ that is, 0 if the binum is positive and 1 if it is negative. The
+ digits are stored with the least significant byte stored first. To
+ calculate the integer, the following formula can be used:
+ </p>
+ <p><c>B</c> = 256<br/>
+ <c>(d0*B^0 + d1*B^1 + d2*B^2 + ... d(N-1)*B^(n-1))</c>
</p>
</section>
<section>
<marker id="LARGE_BIG_EXT"/>
<title>LARGE_BIG_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -740,24 +717,22 @@
<cell align="center">n</cell>
</row>
<row>
- <cell align="center">111</cell>
- <cell align="center">n</cell>
- <cell align="center">Sign</cell>
- <cell align="center">d(0) ... d(n-1)</cell>
+ <cell align="center"><c>111</c></cell>
+ <cell align="center"><c>n</c></cell>
+ <cell align="center"><c>Sign</c></cell>
+ <cell align="center"><c>d(0)</c> ... <c>d(n-1)</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>LARGE_BIG_EXT</tcaption></table>
<p>
- Same as <seealso marker="#SMALL_BIG_EXT">SMALL_BIG_EXT</seealso>
- with the difference that the length field
- is an unsigned 4 byte integer.
+ Same as <seealso marker="#SMALL_BIG_EXT">
+ <c>SMALL_BIG_EXT</c></seealso>
+ except that the length field is an unsigned 4 byte integer.
</p>
-
</section>
<section>
<marker id="NEW_REFERENCE_EXT"/>
<title>NEW_REFERENCE_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -767,44 +742,43 @@
<cell align="center">N'</cell>
</row>
<row>
- <cell align="center">114</cell>
- <cell align="center">Len</cell>
- <cell align="center">Node</cell>
- <cell align="center">Creation</cell>
- <cell align="center">ID ...</cell>
+ <cell align="center"><c>114</c></cell>
+ <cell align="center"><c>Len</c></cell>
+ <cell align="center"><c>Node</c></cell>
+ <cell align="center"><c>Creation</c></cell>
+ <cell align="center"><c>ID ...</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>NEW_REFERENCE_EXT</tcaption></table>
<p>
- Node and Creation are as in
- <seealso marker="#REFERENCE_EXT">REFERENCE_EXT</seealso>.
+ <c>Node</c> and <c>Creation</c> are as in
+ <seealso marker="#REFERENCE_EXT"><c>REFERENCE_EXT</c></seealso>.
</p>
<p>
- <c>ID</c> contains a sequence of big-endian unsigned integers
- (4 bytes each, so <c>N'</c> is a multiple of 4),
- but should be regarded as uninterpreted data.
+ <c>ID</c> contains a sequence of big-endian unsigned integers
+ (4 bytes each, so <c>N'</c> is a multiple of 4),
+ but is to be regarded as uninterpreted data.
</p>
<p>
<c>N'</c> = 4 * <c>Len</c>.
</p>
<p>
- In the first word (four bytes) of <c>ID</c>, only 18 bits are
- significant, the rest should be 0.
- In <c>Creation</c>, only 2 bits are significant,
- the rest should be 0.
+ In the first word (4 bytes) of <c>ID</c>, only 18 bits are
+ significant, the rest are to be 0.
+ In <c>Creation</c>, only two bits are significant,
+ the rest are to be 0.
</p>
<p>
- NEW_REFERENCE_EXT was introduced with distribution version 4.
- In version 4, <c>N'</c> should be at most 12.
+ <c>NEW_REFERENCE_EXT</c> was introduced with distribution version 4.
+ In version 4, <c>N'</c> is to be at most 12.
</p>
<p>
- See <seealso marker="#REFERENCE_EXT">REFERENCE_EXT</seealso>).
+ See <seealso marker="#REFERENCE_EXT"><c>REFERENCE_EXT</c></seealso>.
</p>
</section>
<section>
<marker id="SMALL_ATOM_EXT"/>
<title>SMALL_ATOM_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -816,24 +790,28 @@
<cell align="center"><c>Len</c></cell>
<cell align="center"><c>AtomName</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>SMALL_ATOM_EXT</tcaption></table>
<p>
- An atom is stored with a 1 byte unsigned length,
- followed by <c>Len</c> numbers of 8 bit Latin1 characters that
+ An atom is stored with a 1 byte unsigned length,
+ followed by <c>Len</c> numbers of 8-bit Latin-1 characters that
forms the <c>AtomName</c>. Longer atoms can be represented
- by <seealso marker="#ATOM_EXT">ATOM_EXT</seealso>. <em>Note</em>
- the <c>SMALL_ATOM_EXT</c> was introduced in erts version 5.7.2 and
- require an exchange of the
- <seealso marker="erl_dist_protocol#dflags"><c>DFLAG_SMALL_ATOM_TAGS</c></seealso>
- distribution flag in the
- <seealso marker="erl_dist_protocol#distribution_handshake">distribution handshake</seealso>.
+ by <seealso marker="#ATOM_EXT"><c>ATOM_EXT</c></seealso>.
</p>
+ <note>
+ <p>
+ <c>SMALL_ATOM_EXT</c> was introduced in ERTS 5.7.2 and
+ require an exchange of distribution flag
+ <seealso marker="erl_dist_protocol#dflags">
+ <c>DFLAG_SMALL_ATOM_TAGS</c></seealso> in the
+ <seealso marker="erl_dist_protocol#distribution_handshake">
+ distribution handshake</seealso>.
+ </p>
+ </note>
</section>
<section>
<marker id="FUN_EXT"/>
<title>FUN_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -845,48 +823,56 @@
<cell align="center">N5</cell>
</row>
<row>
- <cell align="center">117</cell>
- <cell align="center">NumFree</cell>
- <cell align="center">Pid</cell>
- <cell align="center">Module</cell>
- <cell align="center">Index</cell>
- <cell align="center">Uniq</cell>
- <cell align="center">Free vars ...</cell>
+ <cell align="center"><c>117</c></cell>
+ <cell align="center"><c>NumFree</c></cell>
+ <cell align="center"><c>Pid</c></cell>
+ <cell align="center"><c>Module</c></cell>
+ <cell align="center"><c>Index</c></cell>
+ <cell align="center"><c>Uniq</c></cell>
+ <cell align="center"><c>Free vars ...</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>FUN_EXT</tcaption></table>
<taglist>
- <tag><c>Pid</c></tag>
+ <tag><c>Pid</c></tag>
<item>
- is a process identifier as in
- <seealso marker="#PID_EXT">PID_EXT</seealso>.
- It represents the process in which the fun was created.
+ <p>A process identifier as in
+ <seealso marker="#PID_EXT"><c>PID_EXT</c></seealso>.
+ Represents the process in which the fun was created.
+ </p>
</item>
<tag><c>Module</c></tag>
<item>
- is an encoded as an atom, using
- <seealso marker="#ATOM_EXT">ATOM_EXT</seealso>,
- <seealso marker="#SMALL_ATOM_EXT">SMALL_ATOM_EXT</seealso>
- or <seealso marker="#ATOM_CACHE_REF">ATOM_CACHE_REF</seealso>.
- This is the module that the fun is implemented in.
+ <p>Encoded as an atom, using
+ <seealso marker="#ATOM_EXT"><c>ATOM_EXT</c></seealso>,
+ <seealso marker="#SMALL_ATOM_EXT"><c>SMALL_ATOM_EXT</c></seealso>,
+ or <seealso marker="#ATOM_CACHE_REF">
+ <c>ATOM_CACHE_REF</c></seealso>.
+ This is the module that the fun is implemented in.
+ </p>
</item>
<tag><c>Index</c></tag>
<item>
- is an integer encoded using
- <seealso marker="#SMALL_INTEGER_EXT">SMALL_INTEGER_EXT</seealso>
- or <seealso marker="#INTEGER_EXT">INTEGER_EXT</seealso>.
- It is typically a small index into the module's fun table.
+ <p>An integer encoded using
+ <seealso marker="#SMALL_INTEGER_EXT">
+ <c>SMALL_INTEGER_EXT</c></seealso>
+ or <seealso marker="#INTEGER_EXT"><c>INTEGER_EXT</c></seealso>.
+ It is typically a small index into the module's fun table.
+ </p>
</item>
<tag><c>Uniq</c></tag>
<item>
- is an integer encoded using
- <seealso marker="#SMALL_INTEGER_EXT">SMALL_INTEGER_EXT</seealso> or
- <seealso marker="#INTEGER_EXT">INTEGER_EXT</seealso>.
- <c>Uniq</c> is the hash value of the parse for the fun.
+ <p>An integer encoded using
+ <seealso marker="#SMALL_INTEGER_EXT">
+ <c>SMALL_INTEGER_EXT</c></seealso> or
+ <seealso marker="#INTEGER_EXT"><c>INTEGER_EXT</c></seealso>.
+ <c>Uniq</c> is the hash value of the parse for the fun.
+ </p>
</item>
<tag><c>Free vars</c></tag>
<item>
- is <c>NumFree</c> number of terms, each one encoded according
- to its type.
+ <p><c>NumFree</c> number of terms, each one encoded according
+ to its type.
+ </p>
</item>
</taglist>
</section>
@@ -894,7 +880,6 @@
<section>
<marker id="NEW_FUN_EXT"/>
<title>NEW_FUN_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -910,19 +895,19 @@
<cell align="center">N5</cell>
</row>
<row>
- <cell align="center">112</cell>
- <cell align="center">Size</cell>
- <cell align="center">Arity</cell>
- <cell align="center">Uniq</cell>
- <cell align="center">Index</cell>
- <cell align="center">NumFree</cell>
- <cell align="center">Module</cell>
- <cell align="center">OldIndex</cell>
- <cell align="center">OldUniq</cell>
- <cell align="center">Pid</cell>
- <cell align="center">Free Vars</cell>
+ <cell align="center"><c>112</c></cell>
+ <cell align="center"><c>Size</c></cell>
+ <cell align="center"><c>Arity</c></cell>
+ <cell align="center"><c>Uniq</c></cell>
+ <cell align="center"><c>Index</c></cell>
+ <cell align="center"><c>NumFree</c></cell>
+ <cell align="center"><c>Module</c></cell>
+ <cell align="center"><c>OldIndex</c></cell>
+ <cell align="center"><c>OldUniq</c></cell>
+ <cell align="center"><c>Pid</c></cell>
+ <cell align="center"><c>Free Vars</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>NEW_FUN_EXT</tcaption></table>
<p>
This is the new encoding of internal funs: <c>fun F/A</c> and
<c>fun(Arg1,..) -> ... end</c>.
@@ -930,68 +915,73 @@
<taglist>
<tag><c>Size</c></tag>
<item>
- is the total number of bytes, including the <c>Size</c> field.
+ <p>The total number of bytes, including field <c>Size</c>.</p>
</item>
<tag><c>Arity</c></tag>
<item>
- is the arity of the function implementing the fun.
+ <p>The arity of the function implementing the fun.</p>
</item>
<tag><c>Uniq</c></tag>
<item>
- is the 16 bytes MD5 of the significant parts of the Beam file.
+ <p>The 16 bytes MD5 of the significant parts of the Beam file.</p>
</item>
<tag><c>Index</c></tag>
<item>
- is an index number. Each fun within a module has an unique
- index. <c>Index</c> is stored in big-endian byte order.
+ <p>An index number. Each fun within a module has an unique
+ index. <c>Index</c> is stored in big-endian byte order.
+ </p>
</item>
<tag><c>NumFree</c></tag>
<item>
- is the number of free variables.
+ <p>The number of free variables.</p>
</item>
<tag><c>Module</c></tag>
<item>
- is an encoded as an atom, using
- <seealso marker="#ATOM_EXT">ATOM_EXT</seealso>,
- <seealso marker="#SMALL_ATOM_EXT">SMALL_ATOM_EXT</seealso> or
- <seealso marker="#ATOM_CACHE_REF">ATOM_CACHE_REF</seealso>.
- This is the module that the fun is implemented in.
+ <p>Encoded as an atom, using
+ <seealso marker="#ATOM_EXT"><c>ATOM_EXT</c></seealso>,
+ <seealso marker="#SMALL_ATOM_EXT"><c>SMALL_ATOM_EXT</c></seealso>,
+ or <seealso marker="#ATOM_CACHE_REF">
+ <c>ATOM_CACHE_REF</c></seealso>.
+ Is the module that the fun is implemented in.
+ </p>
</item>
<tag><c>OldIndex</c></tag>
<item>
- is an integer encoded using
- <seealso marker="#SMALL_INTEGER_EXT">SMALL_INTEGER_EXT</seealso>
- or <seealso marker="#INTEGER_EXT">INTEGER_EXT</seealso>.
- It is typically a small index into the module's fun table.
+ <p>An integer encoded using
+ <seealso marker="#SMALL_INTEGER_EXT">
+ <c>SMALL_INTEGER_EXT</c></seealso> or
+ <seealso marker="#INTEGER_EXT"><c>INTEGER_EXT</c></seealso>.
+ Is typically a small index into the module's fun table.
+ </p>
</item>
<tag><c>OldUniq</c></tag>
<item>
- is an integer encoded using
- <seealso marker="#SMALL_INTEGER_EXT">SMALL_INTEGER_EXT</seealso>
- or
- <seealso marker="#INTEGER_EXT">INTEGER_EXT</seealso>.
- <c>Uniq</c> is the hash value of the parse tree for the fun.
+ <p>An integer encoded using
+ <seealso marker="#SMALL_INTEGER_EXT">
+ <c>SMALL_INTEGER_EXT</c></seealso> or
+ <seealso marker="#INTEGER_EXT"><c>INTEGER_EXT</c></seealso>.
+ <c>Uniq</c> is the hash value of the parse tree for the fun.
+ </p>
</item>
<tag><c>Pid</c></tag>
<item>
- is a process identifier as in
- <seealso marker="#PID_EXT">PID_EXT</seealso>.
- It represents the process in which
- the fun was created.
+ <p>A process identifier as in
+ <seealso marker="#PID_EXT"><c>PID_EXT</c></seealso>.
+ Represents the process in which the fun was created.
+ </p>
</item>
-
<tag><c>Free vars</c></tag>
<item>
- is <c>NumFree</c> number of terms, each one encoded according
- to its type.
+ <p><c>NumFree</c> number of terms, each one encoded according
+ to its type.
+ </p>
</item>
</taglist>
</section>
<section>
<marker id="EXPORT_EXT"/>
- <title>EXPORT_EXT</title>
-
+ <title>EXPORT_EXT</title>
<table align="left">
<row>
<cell align="center">1</cell>
@@ -1000,32 +990,31 @@
<cell align="center">N3</cell>
</row>
<row>
- <cell align="center">113</cell>
- <cell align="center">Module</cell>
- <cell align="center">Function</cell>
- <cell align="center">Arity</cell>
+ <cell align="center"><c>113</c></cell>
+ <cell align="center"><c>Module</c></cell>
+ <cell align="center"><c>Function</c></cell>
+ <cell align="center"><c>Arity</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>EXPORT_EXT</tcaption></table>
<p>
This term is the encoding for external funs: <c>fun M:F/A</c>.
</p>
<p>
- <c>Module</c> and <c>Function</c> are atoms
- (encoded using <seealso marker="#ATOM_EXT">ATOM_EXT</seealso>,
- <seealso marker="#SMALL_ATOM_EXT">SMALL_ATOM_EXT</seealso> or
- <seealso marker="#ATOM_CACHE_REF">ATOM_CACHE_REF</seealso>).
+ <c>Module</c> and <c>Function</c> are atoms
+ (encoded using <seealso marker="#ATOM_EXT"><c>ATOM_EXT</c></seealso>,
+ <seealso marker="#SMALL_ATOM_EXT"><c>SMALL_ATOM_EXT</c></seealso>, or
+ <seealso marker="#ATOM_CACHE_REF"><c>ATOM_CACHE_REF</c></seealso>).
</p>
<p>
- <c>Arity</c> is an integer encoded using
- <seealso marker="#SMALL_INTEGER_EXT">SMALL_INTEGER_EXT</seealso>.
+ <c>Arity</c> is an integer encoded using
+ <seealso marker="#SMALL_INTEGER_EXT">
+ <c>SMALL_INTEGER_EXT</c></seealso>.
</p>
-
</section>
<section>
<marker id="BIT_BINARY_EXT"/>
<title>BIT_BINARY_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -1034,39 +1023,35 @@
<cell align="center">Len</cell>
</row>
<row>
- <cell align="center">77</cell>
- <cell align="center">Len</cell>
- <cell align="center">Bits</cell>
- <cell align="center">Data</cell>
+ <cell align="center"><c>77</c></cell>
+ <cell align="center"><c>Len</c></cell>
+ <cell align="center"><c>Bits</c></cell>
+ <cell align="center"><c>Data</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>BIT_BINARY_EXT</tcaption></table>
<p>
This term represents a bitstring whose length in bits does
not have to be a multiple of 8.
- The <c>Len</c> field is an unsigned 4 byte integer (big endian).
+ The <c>Len</c> field is an unsigned 4 byte integer (big-endian).
The <c>Bits</c> field is the number of bits (1-8) that are used
- in the last byte in the data field,
- counting from the most significant bit towards the least
- significant.
+ in the last byte in the data field,
+ counting from the most significant bit to the least significant.
</p>
-
-
</section>
<section>
<marker id="NEW_FLOAT_EXT"/>
<title>NEW_FLOAT_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
<cell align="center">8</cell>
</row>
<row>
- <cell align="center">70</cell>
- <cell align="center">IEEE float</cell>
+ <cell align="center"><c>70</c></cell>
+ <cell align="center"><c>IEEE float</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>NEW_FLOAT_EXT</tcaption></table>
<p>
A float is stored as 8 bytes in big-endian IEEE format.
</p>
@@ -1074,10 +1059,10 @@
This term is used in minor version 1 of the external format.
</p>
</section>
+
<section>
<marker id="ATOM_UTF8_EXT"/>
<title>ATOM_UTF8_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -1089,23 +1074,22 @@
<cell align="center"><c>Len</c></cell>
<cell align="center"><c>AtomName</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>ATOM_UTF8_EXT</tcaption></table>
<p>
An atom is stored with a 2 byte unsigned length in big-endian order,
followed by <c>Len</c> bytes containing the <c>AtomName</c> encoded
in UTF-8.
</p>
<p>
- For more information on encoding of atoms, see
+ For more information on encoding of atoms, see the
<seealso marker="#utf8_atoms">note on UTF-8 encoded atoms</seealso>
- in the beginning of this document.
+ in the beginning of this section.
</p>
</section>
<section>
<marker id="SMALL_ATOM_UTF8_EXT"/>
<title>SMALL_ATOM_UTF8_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -1117,20 +1101,19 @@
<cell align="center"><c>Len</c></cell>
<cell align="center"><c>AtomName</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>SMALL_ATOM_UTF8_EXT</tcaption></table>
<p>
- An atom is stored with a 1 byte unsigned length,
+ An atom is stored with a 1 byte unsigned length,
followed by <c>Len</c> bytes containing the <c>AtomName</c> encoded
in UTF-8. Longer atoms encoded in UTF-8 can be represented using
- <seealso marker="#ATOM_UTF8_EXT">ATOM_UTF8_EXT</seealso>.
+ <seealso marker="#ATOM_UTF8_EXT"><c>ATOM_UTF8_EXT</c></seealso>.
</p>
<p>
- For more information on encoding of atoms, see
+ For more information on encoding of atoms, see the
<seealso marker="#utf8_atoms">note on UTF-8 encoded atoms</seealso>
- in the beginning of this document.
+ in the beginning of this section.
</p>
</section>
-
</chapter>
-
+
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml
index 420c9fea38..b5dc9037c4 100644
--- a/erts/doc/src/erl_nif.xml
+++ b/erts/doc/src/erl_nif.xml
@@ -4,7 +4,7 @@
<cref>
<header>
<copyright>
- <year>2001</year><year>2015</year>
+ <year>2001</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -33,43 +33,52 @@
<file>erl_nif.xml</file>
</header>
<lib>erl_nif</lib>
- <libsummary>API functions for an Erlang NIF library</libsummary>
+ <libsummary>API functions for an Erlang NIF library.</libsummary>
<description>
<p>A NIF library contains native implementation of some functions
- of an Erlang module. The native implemented functions (NIFs) are
- called like any other functions without any difference to the
- caller. Each NIF must also have an implementation in Erlang that
- will be invoked if the function is called before the NIF library
- has been successfully loaded. A typical such stub implementation
- is to throw an exception. But it can also be used as a fallback
- implementation if the NIF library is not implemented for some
- architecture.</p>
- <marker id="WARNING"/>
- <warning><p><em>Use this functionality with extreme care!</em></p>
+ of an Erlang module. The native implemented functions (NIFs) are
+ called like any other functions without any difference to the
+ caller. Each NIF must have an implementation in Erlang that
+ is invoked if the function is called before the NIF library
+ is successfully loaded. A typical such stub implementation
+ is to throw an exception. But it can also be used as a fallback
+ implementation if the NIF library is not implemented for some
+ architecture.</p>
+
+ <warning>
+ <marker id="WARNING"/>
+ <p><em>Use this functionality with extreme care.</em></p>
<p>A native function is executed as a direct extension of the
- native code of the VM. Execution is not made in a safe environment.
- The VM can <em>not</em> provide the same services as provided when
- executing Erlang code, such as preemptive scheduling or memory
- protection. If the native function doesn't behave well, the whole
- VM will misbehave.</p>
- <list>
- <item><p>A native function that crash will crash the whole VM.</p></item>
- <item><p>An erroneously implemented native function might cause
- a VM internal state inconsistency which may cause a crash of the VM,
- or miscellaneous misbehaviors of the VM at any point after the call
- to the native function.</p></item>
- <item><p>A native function that do <seealso marker="#lengthy_work">lengthy
- work</seealso> before returning will degrade responsiveness of the VM,
- and may cause miscellaneous strange behaviors. Such strange behaviors
- include, but are not limited to, extreme memory usage, and bad load
- balancing between schedulers. Strange behaviors that might occur due
- to lengthy work may also vary between OTP releases.</p></item>
+ native code of the VM. Execution is not made in a safe environment.
+ The VM <em>cannot</em> provide the same services as provided when
+ executing Erlang code, such as pre-emptive scheduling or memory
+ protection. If the native function does not behave well, the whole
+ VM will misbehave.</p>
+ <list type="bulleted">
+ <item>
+ <p>A native function that crash will crash the whole VM.</p>
+ </item>
+ <item>
+ <p>An erroneously implemented native function can cause a VM
+ internal state inconsistency, which can cause a crash of the VM,
+ or miscellaneous misbehaviors of the VM at any point after the
+ call to the native function.</p>
+ </item>
+ <item>
+ <p>A native function doing <seealso marker="#lengthy_work">lengthy
+ work</seealso> before returning degrades responsiveness of the VM,
+ and can cause miscellaneous strange behaviors. Such strange
+ behaviors include, but are not limited to, extreme memory usage,
+ and bad load balancing between schedulers. Strange behaviors that
+ can occur because of lengthy work can also vary between Erlang/OTP
+ releases.</p>
+ </item>
</list>
- </warning>
+ </warning>
- <p>A minimal example of a NIF library can look like this:</p>
- <p/>
- <code type="none">
+ <p>A minimal example of a NIF library can look as follows:</p>
+
+ <code type="none">
/* niftest.c */
#include "erl_nif.h"
@@ -83,13 +92,11 @@ static ErlNifFunc nif_funcs[] =
{"hello", 0, hello}
};
-ERL_NIF_INIT(niftest,nif_funcs,NULL,NULL,NULL,NULL)
-</code>
+ERL_NIF_INIT(niftest,nif_funcs,NULL,NULL,NULL,NULL)</code>
+
+ <p>The Erlang module can look as follows:</p>
- <p>and the Erlang module would have to look something like
- this:</p>
- <p/>
- <code type="none">
+ <code type="none">
-module(niftest).
-export([init/0, hello/0]).
@@ -98,11 +105,11 @@ init() ->
erlang:load_nif("./niftest", 0).
hello() ->
- "NIF library not loaded".
-</code>
- <p>and compile and test something like this (on Linux):</p>
- <p/>
- <code type="none">
+ "NIF library not loaded".</code>
+
+ <p>Compile and test can look as follows (on Linux):</p>
+
+ <code type="none">
$> gcc -fPIC -shared -o niftest.so niftest.c -I $ERL_ROOT/usr/include/
$> erl
@@ -113,1104 +120,1936 @@ $> erl
3> niftest:init().
ok
4> niftest:hello().
-"Hello world!"
-</code>
-
- <p>A better solution for a real module is to take advantage of
- the new directive <seealso
- marker="doc/reference_manual:code_loading#on_load">on_load</seealso> to automatically
- load the NIF library when the module is loaded.</p>
- <note><p>A NIF does not have to be exported, it can be local to the module.
- Note however that unused local stub functions will be optimized
- away by the compiler causing loading of the NIF library to fail.</p>
+"Hello world!"</code>
+
+ <p>A better solution for a real module is to take advantage of the new
+ directive <c>on load</c> (see section
+ <seealso marker="doc/reference_manual:code_loading#on_load">Running a
+ Function When a Module is Loaded</seealso> in the Erlang Reference
+ Manual) to load the NIF library automatically when the module is
+ loaded.</p>
+
+ <note>
+ <p>A NIF does not have to be exported, it can be local to the module.
+ However, unused local stub functions will be optimized
+ away by the compiler, causing loading of the NIF library to fail.</p>
</note>
- <p>A loaded NIF library is tied to the Erlang module code version
- that loaded it. If the module is upgraded with a new version, the
- new Erlang code will have to load its own NIF library (or maybe choose not
- to). The new code version can however choose to load the exact
- same NIF library as the old code if it wants to. Sharing the same
- dynamic library will mean that static data defined by the library
- will be shared as well. To avoid unintentionally shared static
- data, each Erlang module code can keep its own private data. This
- private data can be set when the NIF library is loaded and
- then retrieved by calling <seealso marker="#enif_priv_data">enif_priv_data</seealso>.</p>
- <p>There is no way to explicitly unload a NIF library. A library will be
- automatically unloaded when the module code that it belongs to is purged
- by the code server.</p>
-
- <p><marker id="lengthy_work"/>
- As mentioned in the <seealso marker="#WARNING">warning</seealso> text at
- the beginning of this document it is of vital importance that a native function
- return relatively quickly. It is hard to give an exact maximum amount
- of time that a native function is allowed to work, but as a rule of thumb
- a well-behaving native function should return to its caller before a
- millisecond has passed. This can be achieved using different approaches.
- If you have full control over the code to execute in the native
- function, the best approach is to divide the work into multiple chunks of
- work and call the native function multiple times, either directly from Erlang code
- or by having a native function schedule a future NIF call via the
- <seealso marker="#enif_schedule_nif"> enif_schedule_nif</seealso> function. Function
- <seealso marker="#enif_consume_timeslice">enif_consume_timeslice</seealso> can be
- used to help with such work division. In some cases, however, this might not
- be possible, e.g. when calling third-party libraries. Then you typically want
- to dispatch the work to another thread, return
- from the native function, and wait for the result. The thread can send
- the result back to the calling thread using message passing. Information
- about thread primitives can be found below. If you have built your system
- with <em>the currently experimental</em> support for dirty schedulers,
- you may want to try out this functionality by dispatching the work to a
- <seealso marker="#dirty_nifs">dirty NIF</seealso>,
- which does not have the same duration restriction as a normal NIF.</p>
+
+ <p>A loaded NIF library is tied to the Erlang module code version
+ that loaded it. If the module is upgraded with a new version, the
+ new Erlang code need to load its own NIF library (or maybe choose not
+ to). The new code version can, however, choose to load the
+ same NIF library as the old code if it wants to. Sharing the
+ dynamic library means that static data defined by the library
+ is shared as well. To avoid unintentionally shared static
+ data, each Erlang module code can keep its own private data. This
+ private data can be set when the NIF library is loaded and
+ then retrieved by calling <seealso marker="#enif_priv_data">
+ <c>enif_priv_data</c></seealso>.</p>
+
+ <p>A NIF library cannot be loaded explicitly. A library is
+ automatically unloaded when the module code that it belongs to is purged
+ by the code server.</p>
</description>
+
<section>
- <title>FUNCTIONALITY</title>
- <p>All functions that a NIF library needs to do with Erlang are
- performed through the NIF API functions. There are functions
+ <title>Functionality</title>
+ <p>All functions that a NIF library needs to do with Erlang are
+ performed through the NIF API functions. Functions exist
for the following functionality:</p>
+
<taglist>
<tag>Read and write Erlang terms</tag>
- <item><p>Any Erlang terms can be passed to a NIF as function arguments and
- be returned as function return values. The terms are of C-type
- <seealso marker="#ERL_NIF_TERM">ERL_NIF_TERM</seealso>
- and can only be read or written using API functions. Most functions to read
- the content of a term are prefixed <c>enif_get_</c> and usually return
- true (or false) if the term was of the expected type (or not).
- The functions to write terms are all prefixed <c>enif_make_</c> and usually
- return the created <c>ERL_NIF_TERM</c>. There are also some functions
- to query terms, like <c>enif_is_atom</c>, <c>enif_is_identical</c> and
- <c>enif_compare</c>.</p>
- <p>All terms of type <c>ERL_NIF_TERM</c> belong to an environment of type
- <seealso marker="#ErlNifEnv">ErlNifEnv</seealso>. The lifetime of a term is
- controlled by the lifetime of its environment object. All API functions that read
- or write terms has the environment, that the term belongs to, as the first
- function argument.</p></item>
+ <item>
+ <p>Any Erlang terms can be passed to a NIF as function arguments and
+ be returned as function return values. The terms are of C-type
+ <seealso marker="#ERL_NIF_TERM"><c>ERL_NIF_TERM</c></seealso> and can
+ only be read or written using API functions. Most functions to read
+ the content of a term are prefixed <c>enif_get_</c> and usually return
+ <c>true</c> (or <c>false</c>) if the term is of the expected type (or
+ not). The functions to write terms are all prefixed <c>enif_make_</c>
+ and usually
+ return the created <c>ERL_NIF_TERM</c>. There are also some functions
+ to query terms, like <c>enif_is_atom</c>, <c>enif_is_identical</c>,
+ and <c>enif_compare</c>.</p>
+ <p>All terms of type <c>ERL_NIF_TERM</c> belong to an environment of
+ type <seealso marker="#ErlNifEnv"><c>ErlNifEnv</c></seealso>. The
+ lifetime of a term is controlled by the lifetime of its environment
+ object. All API functions that read or write terms has the
+ environment that the term belongs to as the first function
+ argument.</p>
+ </item>
<tag>Binaries</tag>
- <item><p>Terms of type binary are accessed with the help of the struct type
- <seealso marker="#ErlNifBinary">ErlNifBinary</seealso>
- that contains a pointer (<c>data</c>) to the raw binary data and the length
- (<c>size</c>) of the data in bytes. Both <c>data</c> and <c>size</c> are
- read-only and should only be written using calls to API functions.
- Instances of <c>ErlNifBinary</c> are however always allocated by the user
- (usually as local variables).</p>
- <p>The raw data pointed to by <c>data</c> is only mutable after a call to
- <seealso marker="#enif_alloc_binary">enif_alloc_binary</seealso> or
- <seealso marker="#enif_realloc_binary">enif_realloc_binary</seealso>.
- All other functions that operates on a binary will leave the data as read-only.
- A mutable binary must in the end either be freed with
- <seealso marker="#enif_release_binary">enif_release_binary</seealso>
- or made read-only by transferring it to an Erlang term with
- <seealso marker="#enif_make_binary">enif_make_binary</seealso>.
- But it does not have to happen in the same NIF call. Read-only binaries
- do not have to be released.</p>
- <p><seealso marker="#enif_make_new_binary">enif_make_new_binary</seealso>
- can be used as a shortcut to allocate and return a binary in the same NIF call.</p>
- <p>Binaries are sequences of whole bytes. Bitstrings with an arbitrary
- bit length have no support yet.</p>
- </item>
+ <item>
+ <p>Terms of type binary are accessed with the help of struct type
+ <seealso marker="#ErlNifBinary"><c>ErlNifBinary</c></seealso>,
+ which contains a pointer (<c>data</c>) to the raw binary data and the
+ length (<c>size</c>) of the data in bytes. Both <c>data</c> and
+ <c>size</c> are read-only and are only to be written using calls to
+ API functions. Instances of <c>ErlNifBinary</c> are, however, always
+ allocated by the user (usually as local variables).</p>
+ <p>The raw data pointed to by <c>data</c> is only mutable after a call
+ to <seealso marker="#enif_alloc_binary">
+ <c>enif_alloc_binary</c></seealso> or
+ <seealso marker="#enif_realloc_binary">
+ <c>enif_realloc_binary</c></seealso>. All other functions that
+ operate on a binary leave the data as read-only.
+ A mutable binary must in the end either be freed with
+ <seealso marker="#enif_release_binary">
+ <c>enif_release_binary</c></seealso>
+ or made read-only by transferring it to an Erlang term with
+ <seealso marker="#enif_make_binary"><c>enif_make_binary</c></seealso>.
+ However, it does not have to occur in the same NIF call. Read-only
+ binaries do not have to be released.</p>
+ <p><seealso marker="#enif_make_new_binary">
+ <c>enif_make_new_binary</c></seealso> can be used as a shortcut to
+ allocate and return a binary in the same NIF call.</p>
+ <p>Binaries are sequences of whole bytes. Bitstrings with an arbitrary
+ bit length have no support yet.</p>
+ </item>
<tag>Resource objects</tag>
- <item><p>The use of resource objects is a safe way to return pointers to
- native data structures from a NIF. A resource object is
- just a block of memory allocated with
- <seealso marker="#enif_alloc_resource">enif_alloc_resource</seealso>.
- A handle ("safe pointer") to this memory block can then be returned to Erlang by the use of
- <seealso marker="#enif_make_resource">enif_make_resource</seealso>.
- The term returned by <c>enif_make_resource</c>
- is totally opaque in nature. It can be stored and passed between processes
- on the same node, but the only real end usage is to pass it back as an argument to a NIF.
- The NIF can then call <seealso marker="#enif_get_resource">enif_get_resource</seealso>
- and get back a pointer to the memory block that is guaranteed to still be
- valid. A resource object will not be deallocated until the last handle term
- has been garbage collected by the VM and the resource has been
- released with <seealso marker="#enif_release_resource">enif_release_resource</seealso>
- (not necessarily in that order).</p>
- <p>All resource objects are created as instances of some <em>resource type</em>.
- This makes resources from different modules to be distinguishable.
- A resource type is created by calling
- <seealso marker="#enif_open_resource_type">enif_open_resource_type</seealso>
- when a library is loaded. Objects of that resource type can then later be allocated
- and <c>enif_get_resource</c> verifies that the resource is of the expected type.
- A resource type can have a user supplied destructor function that is
- automatically called when resources of that type are released (by either
- the garbage collector or <c>enif_release_resource</c>). Resource types
- are uniquely identified by a supplied name string and the name of the
- implementing module.</p>
- <marker id="enif_resource_example"/><p>Here is a template example of how to create and return a resource object.</p>
- <p/>
- <code type="none">
- ERL_NIF_TERM term;
- MyStruct* obj = enif_alloc_resource(my_resource_type, sizeof(MyStruct));
-
- /* initialize struct ... */
-
- term = enif_make_resource(env, obj);
-
- if (keep_a_reference_of_our_own) {
- /* store 'obj' in static variable, private data or other resource object */
- }
- else {
- enif_release_resource(obj);
- /* resource now only owned by "Erlang" */
- }
- return term;
- </code>
- <p>Note that once <c>enif_make_resource</c> creates the term to
- return to Erlang, the code can choose to either keep its own
- native pointer to the allocated struct and release it later, or
- release it immediately and rely solely on the garbage collector
- to eventually deallocate the resource object when it collects
- the term.</p>
- <p>Another usage of resource objects is to create binary terms with
- user defined memory management.
- <seealso marker="#enif_make_resource_binary">enif_make_resource_binary</seealso>
- will create a binary term that is connected to a resource object. The
- destructor of the resource will be called when the binary is garbage
- collected, at which time the binary data can be released. An example of
- this can be a binary term consisting of data from a <c>mmap</c>'ed file.
- The destructor can then do <c>munmap</c> to release the memory
- region.</p>
- <p>Resource types support upgrade in runtime by allowing a loaded NIF
- library to takeover an already existing resource type and thereby
- "inherit" all existing objects of that type. The destructor of the new
- library will thereafter be called for the inherited objects and the
- library with the old destructor function can be safely unloaded. Existing
- resource objects, of a module that is upgraded, must either be deleted
- or taken over by the new NIF library. The unloading of a library will be
- postponed as long as there exist resource objects with a destructor
- function in the library.
- </p>
+ <item>
+ <p>The use of resource objects is a safe way to return pointers to
+ native data structures from a NIF. A resource object is
+ only a block of memory allocated with
+ <seealso marker="#enif_alloc_resource">
+ <c>enif_alloc_resource</c></seealso>.
+ A handle ("safe pointer") to this memory block can then be returned
+ to Erlang by the use of
+ <seealso marker="#enif_make_resource">
+ <c>enif_make_resource</c></seealso>.
+ The term returned by <c>enif_make_resource</c> is opaque in nature.
+ It can be stored and passed between processes on the same node, but
+ the only real end usage is to pass it back as an argument to a NIF.
+ The NIF can then call <seealso marker="#enif_get_resource">
+ <c>enif_get_resource</c></seealso> and get back a pointer to the
+ memory block, which is guaranteed to still be valid. A resource
+ object is not deallocated until the last handle term
+ is garbage collected by the VM and the resource is released with
+ <seealso marker="#enif_release_resource">
+ <c>enif_release_resource</c></seealso>
+ (not necessarily in that order).</p>
+ <p>All resource objects are created as instances of some <em>resource
+ type</em>. This makes resources from different modules to be
+ distinguishable. A resource type is created by calling
+ <seealso marker="#enif_open_resource_type">
+ <c>enif_open_resource_type</c></seealso> when a library is loaded.
+ Objects of that resource type can then later be allocated and
+ <c>enif_get_resource</c> verifies that the resource is of the
+ expected type. A resource type can have a user-supplied destructor
+ function, which is automatically called when resources of that type
+ are released (by either the garbage collector or
+ <c>enif_release_resource</c>). Resource types are uniquely identified
+ by a supplied name string and the name of the implementing module.</p>
+ <marker id="enif_resource_example"/>
+ <p>The following is a template example of how to create and return a
+ resource object.</p>
+ <code type="none">
+ERL_NIF_TERM term;
+MyStruct* obj = enif_alloc_resource(my_resource_type, sizeof(MyStruct));
+
+/* initialize struct ... */
+
+term = enif_make_resource(env, obj);
+
+if (keep_a_reference_of_our_own) {
+ /* store 'obj' in static variable, private data or other resource object */
+}
+else {
+ enif_release_resource(obj);
+ /* resource now only owned by "Erlang" */
+}
+return term;</code>
+ <p>Notice that once <c>enif_make_resource</c> creates the term to
+ return to Erlang, the code can choose to either keep its own
+ native pointer to the allocated struct and release it later, or
+ release it immediately and rely only on the garbage collector
+ to deallocate the resource object eventually when it collects
+ the term.</p>
+ <p>Another use of resource objects is to create binary terms with
+ user-defined memory management.
+ <seealso marker="#enif_make_resource_binary">
+ <c>enif_make_resource_binary</c></seealso>
+ creates a binary term that is connected to a resource object. The
+ destructor of the resource is called when the binary is garbage
+ collected, at which time the binary data can be released. An example
+ of this can be a binary term consisting of data from a <c>mmap</c>'ed
+ file. The destructor can then do <c>munmap</c> to release the memory
+ region.</p>
+ <p>Resource types support upgrade in runtime by allowing a loaded NIF
+ library to take over an already existing resource type and by that
+ "inherit" all existing objects of that type. The destructor of the
+ new library is thereafter called for the inherited objects and the
+ library with the old destructor function can be safely unloaded.
+ Existing resource objects, of a module that is upgraded, must either
+ be deleted or taken over by the new NIF library. The unloading of a
+ library is postponed as long as there exist resource objects with a
+ destructor function in the library.</p>
</item>
<tag>Threads and concurrency</tag>
- <item><p>A NIF is thread-safe without any explicit synchronization as
- long as it acts as a pure function and only reads the supplied
- arguments. As soon as you write towards a shared state either through
- static variables or <seealso marker="#enif_priv_data">enif_priv_data</seealso>
- you need to supply your own explicit synchronization. This includes terms
- in process independent environments that are shared between threads.
- Resource objects will also require synchronization if you treat them as
- mutable.</p>
- <p>The library initialization callbacks <c>load</c>, <c>reload</c> and
- <c>upgrade</c> are all thread-safe even for shared state data.</p>
+ <item>
+ <p>A NIF is thread-safe without any explicit synchronization as
+ long as it acts as a pure function and only reads the supplied
+ arguments. When you write to a shared state either through
+ static variables or <seealso marker="#enif_priv_data">
+ <c>enif_priv_data</c></seealso>, you need to supply your own explicit
+ synchronization. This includes terms in process-independent
+ environments that are shared between threads. Resource objects also
+ require synchronization if you treat them as mutable.</p>
+ <p>The library initialization callbacks <c>load</c>, <c>reload</c>, and
+ <c>upgrade</c> are all thread-safe even for shared state data.</p>
</item>
-
<tag><marker id="version_management"/>Version Management</tag>
- <item><p>
- When a NIF library is built, information about NIF API version
- is compiled into the library. When a NIF library is loaded the
- runtime system verifies that the library is of a compatible version.
- <c>erl_nif.h</c> defines <c>ERL_NIF_MAJOR_VERSION</c>, and
- <c>ERL_NIF_MINOR_VERSION</c>. <c>ERL_NIF_MAJOR_VERSION</c> will be
- incremented when NIF library incompatible changes are made to the
- Erlang runtime system. Normally it will suffice to recompile the NIF
- library when the <c>ERL_NIF_MAJOR_VERSION</c> has changed, but it
- could, under rare circumstances, mean that NIF libraries have to
- be slightly modified. If so, this will of course be documented.
- <c>ERL_NIF_MINOR_VERSION</c> will be incremented when
- new features are added. The runtime system uses the minor version
- to determine what features to use.
- </p><p>
- The runtime system will normally refuse to load a NIF library if
- the major versions differ, or if the major versions are equal and
- the minor version used by the NIF library is greater than the one
- used by the runtime system. Old NIF libraries with lower major
- versions will however be allowed after a bump of the major version
- during a transition period of two major releases. Such old NIF
- libraries might however fail if deprecated features are used.
- </p></item>
-
+ <item>
+ <p>When a NIF library is built, information about the NIF API version
+ is compiled into the library. When a NIF library is loaded, the
+ runtime system verifies that the library is of a compatible version.
+ <c>erl_nif.h</c> defines the following:</p>
+ <taglist>
+ <tag><c>ERL_NIF_MAJOR_VERSION</c></tag>
+ <item>
+ <p>Incremented when NIF library incompatible changes are made to the
+ Erlang runtime system. Normally it suffices to recompile the NIF
+ library when the <c>ERL_NIF_MAJOR_VERSION</c> has changed, but it
+ can, under rare circumstances, mean that NIF libraries must be
+ slightly modified. If so, this will of course be documented.</p>
+ </item>
+ <tag><c>ERL_NIF_MINOR_VERSION</c></tag>
+ <item>
+ <p>Incremented when new features are added. The runtime system uses
+ the minor version to determine what features to use.</p>
+ </item>
+ </taglist>
+ <p>The runtime system normally refuses to load a NIF library if
+ the major versions differ, or if the major versions are equal and
+ the minor version used by the NIF library is greater than the one
+ used by the runtime system. Old NIF libraries with lower major
+ versions are, however, allowed after a bump of the major version
+ during a transition period of two major releases. Such old NIF
+ libraries can however fail if deprecated features are used.</p>
+ </item>
<tag><marker id="time_measurement"/>Time Measurement</tag>
- <item><p>Support for time measurement in NIF libraries:
- <list>
- <item><seealso marker="#ErlNifTime"><c>ErlNifTime</c></seealso></item>
- <item><seealso marker="#ErlNifTimeUnit"><c>ErlNifTimeUnit</c></seealso></item>
- <item><seealso marker="#enif_monotonic_time"><c>enif_monotonic_time()</c></seealso></item>
- <item><seealso marker="#enif_time_offset"><c>enif_time_offset()</c></seealso></item>
- <item><seealso marker="#enif_convert_time_unit"><c>enif_convert_time_unit()</c></seealso></item>
- </list></p>
+ <item>
+ <p>Support for time measurement in NIF libraries:</p>
+ <list type="bulleted">
+ <item><seealso marker="#ErlNifTime">
+ <c>ErlNifTime</c></seealso></item>
+ <item><seealso marker="#ErlNifTimeUnit">
+ <c>ErlNifTimeUnit</c></seealso></item>
+ <item><seealso marker="#enif_monotonic_time">
+ <c>enif_monotonic_time()</c></seealso></item>
+ <item><seealso marker="#enif_time_offset">
+ <c>enif_time_offset()</c></seealso></item>
+ <item><seealso marker="#enif_convert_time_unit">
+ <c>enif_convert_time_unit()</c></seealso></item>
+ </list>
</item>
+ <tag><marker id="lengthy_work"/>Long-running NIFs</tag>
+ <item>
+ <p>As mentioned in the <seealso marker="#WARNING">warning</seealso> text
+ at the beginning of this manual page, it is of <em>vital
+ importance</em> that a native function returns relatively fast. It is
+ difficult to give an exact maximum amount of time that a native
+ function is allowed to work, but usually a well-behaving native
+ function is to return to its caller within 1 millisecond. This can be
+ achieved using different approaches. If you have full control over the
+ code to execute in the native function, the best approach is to
+ divide the work into multiple chunks of work and call the native
+ function multiple times. This is, however, not always possible, for
+ example when calling third-party libraries.</p>
+ <p>The <seealso marker="#enif_consume_timeslice">
+ <c>enif_consume_timeslice()</c></seealso> function can be used to
+ inform the runtime system about the length of the NIF call.
+ It is typically always to be used unless the NIF executes very
+ fast.</p>
+ <p>If the NIF call is too lengthy, this must be handled in one of
+ the following ways to avoid degraded responsiveness, scheduler load
+ balancing problems, and other strange behaviors:</p>
+ <taglist>
+ <tag>Yielding NIF</tag>
+ <item>
+ <p>If the functionality of a long-running NIF can be split so that
+ its work can be achieved through a series of shorter NIF calls,
+ the application has two options:</p>
+ <list type="bulleted">
+ <item>
+ <p>Make that series of NIF calls from the Erlang level.</p>
+ </item>
+ <item>
+ <p>Call a NIF that first performs a chunk of the work, then
+ invokes the <seealso marker="#enif_schedule_nif">
+ <c>enif_schedule_nif</c></seealso> function to schedule
+ another NIF call to perform the next chunk. The final call
+ scheduled in this manner can then return the overall
+ result.</p>
+ </item>
+ </list>
+ <p>Breaking up a long-running function in this manner enables the
+ VM to regain control between calls to the NIFs.</p>
+ <p>This approach is always preferred over the other alternatives
+ described below. This both from a performance perspective and
+ a system characteristics perspective.</p>
+ </item>
+ <tag>Threaded NIF</tag>
+ <item>
+ <p>This is accomplished by dispatching the work to another thread
+ managed by the NIF library, return from the NIF, and wait for
+ the result. The thread can send the result back to the Erlang
+ process using <seealso marker="#enif_send">
+ <c>enif_send</c></seealso>.
+ Information about thread primitives is provided below.</p>
+ </item>
+ <tag><marker id="dirty_nifs"/>Dirty NIF</tag>
+ <item>
+ <note>
+ <p><em>The dirty NIF functionality described here
+ is experimental</em>. Dirty NIF support is available only when
+ the emulator is configured with dirty schedulers enabled. This
+ feature is disabled by default. The Erlang runtime
+ without SMP support does not support dirty schedulers even when
+ the dirty scheduler support is enabled. To check at runtime for
+ the presence of dirty scheduler threads, code can use the
+ <seealso marker="#enif_system_info">
+ <c>enif_system_info()</c></seealso> API function.</p>
+ </note>
+ <p>A NIF that cannot be split and cannot execute in a millisecond
+ or less is called a "dirty NIF", as it performs work that the
+ ordinary schedulers of the Erlang runtime system cannot handle cleanly.
+ Applications that make use of such functions must indicate to the
+ runtime that the functions are dirty so they can be handled
+ specially. This is handled by executing dirty jobs on a separate
+ set of schedulers called dirty schedulers. A dirty NIF executing
+ on a dirty scheduler does not have the same duration restriction
+ as a normal NIF.
+ </p>
+
+ <p>
+ It is important to classify the dirty job correct. An I/O bound
+ job should be classified as such, and a CPU bound job should be
+ classified as such. If you should classify CPU bound jobs
+ as I/O bound jobs, dirty I/O schedulers might starve ordinary
+ schedulers. I/O bound jobs are expected to either block waiting
+ for I/O, and/or spend a limited amount of time moving data.
+ </p>
- <tag>Long-running NIFs</tag>
- <item><p><marker id="dirty_nifs"/>Native functions
- <seealso marker="#lengthy_work">
- must normally run quickly</seealso>, as explained earlier in this document. They
- generally should execute for no more than a millisecond. But not all native functions
- can execute so quickly; for example, functions that encrypt large blocks of data or
- perform lengthy file system operations can often run for tens of seconds or more.</p>
- <p>If the functionality of a long-running NIF can be split so that its work can be
- achieved through a series of shorter NIF calls, the application can either make that series
- of NIF calls from the Erlang level, or it can call a NIF that first performs a chunk of the
- work, then invokes the <seealso marker="#enif_schedule_nif">enif_schedule_nif</seealso>
- function to schedule another NIF call to perform the next chunk. The final call scheduled
- in this manner can then return the overall result. Breaking up a long-running function in
- this manner enables the VM to regain control between calls to the NIFs, thereby avoiding
- degraded responsiveness, scheduler load balancing problems, and other strange behaviours.</p>
- <p>A NIF that cannot be split and cannot execute in a millisecond or less is called a "dirty NIF"
- because it performs work that the Erlang runtime cannot handle cleanly.
- <em>Note that the dirty NIF functionality described here is experimental</em> and that you have to
- enable support for dirty schedulers when building OTP in order to try the functionality out.
- Applications that make use of such functions must indicate to the runtime that the functions are
- dirty so they can be handled specially. To schedule a dirty NIF for execution, the
- appropriate flags value can be set for the NIF in its <seealso marker="#ErlNifFunc">ErlNifFunc</seealso>
- entry, or the application can call <seealso marker="#enif_schedule_nif">enif_schedule_nif</seealso>,
- passing to it a pointer to the dirty NIF to be executed and indicating with the <c>flags</c>
- argument whether it expects the operation to be CPU-bound or I/O-bound.</p>
- <note><p>Dirty NIF support is available only when the emulator is configured with dirty
- schedulers enabled. This feature is currently disabled by default. To determine whether
- the dirty NIF API is available, native code can check to see if the C preprocessor macro
- <c>ERL_NIF_DIRTY_SCHEDULER_SUPPORT</c> is defined. Also, if the Erlang runtime was built
- without threading support, dirty schedulers are disabled. To check at runtime for the presence
- of dirty scheduler threads, code can use the <seealso marker="#enif_system_info"><c>
- enif_system_info()</c></seealso> API function.</p></note>
+ <p>
+ To schedule a dirty NIF for execution, the application has two options:</p>
+ <list type="bulleted">
+ <item>
+ <p>Set the appropriate flags value for the dirty NIF in its
+ <seealso marker="#ErlNifFunc"> <c>ErlNifFunc</c></seealso>
+ entry.</p>
+ </item>
+ <item>
+ <p>Call <seealso marker="#enif_schedule_nif">
+ <c>enif_schedule_nif</c></seealso>, pass to it a pointer
+ to the dirty NIF to be executed, and indicate with argument
+ <c>flags</c> whether it expects the operation to be CPU-bound
+ or I/O-bound.</p>
+ </item>
+ </list>
+ <p>A job that alternates between I/O bound and CPU bound can be
+ reclassified and rescheduled using <c>enif_schedule_nif</c> so
+ that it executes on the correct type of dirty scheduler at all
+ times. For more information see the documentation of the
+ <c>erl(1)</c> command line arguments
+ <seealso marker="erl#+SDcpu"><c>+SDcpu</c></seealso>,
+ and <seealso marker="erl#+SDio"><c>+SDio</c></seealso>.</p>
+ <p>While a process executes a dirty NIF, some operations that
+ communicate with it can take a very long time to complete.
+ Suspend or garbage collection of a process executing a dirty
+ NIF cannot be done until the dirty NIF has returned. Thus, other
+ processes waiting for such operations to complete might
+ have to wait for a very long time. Blocking multi-scheduling, that
+ is, calling <seealso marker="erlang#system_flag_multi_scheduling">
+ <c>erlang:system_flag(multi_scheduling, block)</c></seealso>, can
+ also take a very long time to complete. This becaue all ongoing
+ dirty operations on all dirty schedulers must complete before
+ the block operation can complete.</p>
+ <p>Many operations communicating with a process executing a
+ dirty NIF can, however, complete while it executes the
+ dirty NIF. For example, retrieving information about it through
+ <seealso marker="erlang:process_info/1">
+ <c>erlang:process_info</c></seealso>, setting its group leader,
+ register/unregister its name, and so on.</p>
+ <p>Termination of a process executing a dirty NIF can only be
+ completed up to a certain point while it executes the dirty NIF.
+ All Erlang resources, such as its registered name and its ETS
+ tables, are released. All links and monitors are triggered. The
+ execution of the NIF is, however, <em>not</em> stopped. The NIF
+ can safely continue execution, allocate heap memory, and so on,
+ but it is of course better to stop executing as soon as possible.
+ The NIF can check whether a current process is alive using
+ <seealso marker="#enif_is_current_process_alive">
+ <c>enif_is_current_process_alive</c></seealso>. Communication
+ using <seealso marker="#enif_send"><c>enif_send</c></seealso> and
+ <seealso marker="#enif_port_command">
+ <c>enif_port_command</c></seealso> is also dropped when the
+ sending process is not alive. Deallocation of certain internal
+ resources, such as process heap and process control block, is
+ delayed until the dirty NIF has completed.</p>
+ </item>
+ </taglist>
</item>
</taglist>
</section>
+
<section>
- <title>INITIALIZATION</title>
+ <title>Initialization</title>
<taglist>
- <tag><marker id="ERL_NIF_INIT"/>ERL_NIF_INIT(MODULE, ErlNifFunc funcs[], load, reload, upgrade, unload)</tag>
- <item><p>This is the magic macro to initialize a NIF library. It
- should be evaluated in global file scope.</p>
- <p><c>MODULE</c> is the name of the Erlang module as an
- identifier without string quotations. It will be stringified by
- the macro.</p>
- <p><c>funcs</c> is a static array of function descriptors for
- all the implemented NIFs in this library.</p>
- <p><c>load</c>, <c>reload</c>, <c>upgrade</c> and <c>unload</c>
- are pointers to functions. One of <c>load</c>, <c>reload</c> or
- <c>upgrade</c> will be called to initialize the library.
- <c>unload</c> is called to release the library. They are all
- described individually below.</p>
- <p>If compiling a nif for static inclusion via --enable-static-nifs you
- have to define STATIC_ERLANG_NIF before the ERL_NIF_INIT declaration.</p>
+ <tag><marker id="ERL_NIF_INIT"/><c>ERL_NIF_INIT(MODULE,
+ ErlNifFunc funcs[], load, reload, upgrade, unload)</c></tag>
+ <item>
+ <p>This is the magic macro to initialize a NIF library. It
+ is to be evaluated in global file scope.</p>
+ <p><c>MODULE</c> is the name of the Erlang module as an
+ identifier without string quotations. It is stringified by
+ the macro.</p>
+ <p><c>funcs</c> is a static array of function descriptors for
+ all the implemented NIFs in this library.</p>
+ <p><c>load</c>, <c>reload</c>, <c>upgrade</c> and <c>unload</c>
+ are pointers to functions. One of <c>load</c>, <c>reload</c>, or
+ <c>upgrade</c> is called to initialize the library.
+ <c>unload</c> is called to release the library. All are
+ described individually below.</p>
+ <p>If compiling a NIF for static inclusion through
+ <c>--enable-static-nifs</c>, you must define <c>STATIC_ERLANG_NIF</c>
+ before the <c>ERL_NIF_INIT</c> declaration.</p>
</item>
-
- <tag><marker id="load"/>int (*load)(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)</tag>
- <item><p><c>load</c> is called when the NIF library is loaded
- and there is no previously loaded library for this module.</p>
+ <tag><marker id="load"/><c>int (*load)(ErlNifEnv* env, void** priv_data,
+ ERL_NIF_TERM load_info)</c></tag>
+ <item>
+ <p><c>load</c> is called when the NIF library is loaded
+ and no previously loaded library exists for this module.</p>
<p><c>*priv_data</c> can be set to point to some private data
- that the library needs in order to keep a state between NIF
- calls. <c>enif_priv_data</c> will return this pointer.
- <c>*priv_data</c> will be initialized to NULL when <c>load</c> is
- called.</p>
+ that the library needs to keep a state between NIF
+ calls. <c>enif_priv_data</c> returns this pointer.
+ <c>*priv_data</c> is initialized to <c>NULL</c> when <c>load</c> is
+ called.</p>
<p><c>load_info</c> is the second argument to <seealso
- marker="erlang#load_nif-2">erlang:load_nif/2</seealso>.</p>
- <p>The library will fail to load if <c>load</c> returns
- anything other than 0. <c>load</c> can be NULL in case no
- initialization is needed.</p>
- </item>
-
- <tag><marker id="upgrade"/>int (*upgrade)(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info)</tag>
- <item><p><c>upgrade</c> is called when the NIF library is loaded
- and there is old code of this module with a loaded NIF library.</p>
- <p>Works the same as <c>load</c>. The only difference is that
- <c>*old_priv_data</c> already contains the value set by the
- last call to <c>load</c> or <c>reload</c> for the old module
- code. <c>*priv_data</c> will be initialized to NULL when <c>upgrade</c>
- is called. It is allowed to write to both *priv_data and *old_priv_data.</p>
- <p>The library will fail to load if <c>upgrade</c> returns
- anything other than 0 or if <c>upgrade</c> is NULL.</p>
+ marker="erlang#load_nif-2"><c>erlang:load_nif/2</c></seealso>.</p>
+ <p>The library fails to load if <c>load</c> returns
+ anything other than <c>0</c>. <c>load</c> can be <c>NULL</c> if
+ initialization is not needed.</p>
</item>
-
- <tag><marker id="unload"/>void (*unload)(ErlNifEnv* env, void* priv_data)</tag>
- <item><p><c>unload</c> is called when the module code that
- the NIF library belongs to is purged as old. New code
- of the same module may or may not exist. Note that <c>unload</c> is not
- called for a replaced library as a consequence of <c>reload</c>.</p>
+ <tag><marker id="upgrade"/><c>int (*upgrade)(ErlNifEnv* env, void**
+ priv_data, void** old_priv_data, ERL_NIF_TERM load_info)</c></tag>
+ <item>
+ <p><c>upgrade</c> is called when the NIF library is loaded
+ and there is old code of this module with a loaded NIF library.</p>
+ <p>Works as <c>load</c>, except that <c>*old_priv_data</c> already
+ contains the value set by the last call to <c>load</c> or
+ <c>reload</c> for the old module code. <c>*priv_data</c> is
+ initialized to <c>NULL</c> when <c>upgrade</c> is called. It is
+ allowed to write to both <c>*priv_data</c> and
+ <c>*old_priv_data.</c></p>
+ <p>The library fails to load if <c>upgrade</c> returns
+ anything other than <c>0</c> or if <c>upgrade</c> is <c>NULL</c>.</p>
</item>
-
- <tag><marker id="reload"/>int (*reload)(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)</tag>
- <note><p>The reload mechanism is <em>deprecated</em>. It was only intended
- as a development feature. Do not use it as an upgrade method for
- live production systems. It might be removed in future releases. Be sure
- to pass <c>reload</c> as <c>NULL</c> to <seealso marker="#ERL_NIF_INIT">ERL_NIF_INIT</seealso>
- to disable it when not used.</p>
- </note>
- <item><p><c>reload</c> is called when the NIF library is loaded
- and there is already a previously loaded library for this
- module code.</p>
- <p>Works the same as <c>load</c>. The only difference is that
- <c>*priv_data</c> already contains the value set by the
- previous call to <c>load</c> or <c>reload</c>.</p>
- <p>The library will fail to load if <c>reload</c> returns
- anything other than 0 or if <c>reload</c> is NULL.</p>
+ <tag><marker id="unload"/><c>void (*unload)(ErlNifEnv* env, void*
+ priv_data)</c></tag>
+ <item>
+ <p><c>unload</c> is called when the module code that
+ the NIF library belongs to is purged as old. New code of the same
+ module may or may not exist. Notice that <c>unload</c> is not
+ called for a replaced library as a consequence of <c>reload</c>.</p>
+ </item>
+ <tag><marker id="reload"/><c>int (*reload)(ErlNifEnv* env, void**
+ priv_data, ERL_NIF_TERM load_info)</c></tag>
+ <item>
+ <note>
+ <p><em>The reload mechanism is deprecated.</em> It was only intended
+ as a development feature. Do not use it as an upgrade method for
+ live production systems. It can be removed in future releases.
+ Ensure to pass <c>reload</c> as <c>NULL</c> to
+ <seealso marker="#ERL_NIF_INIT"><c>ERL_NIF_INIT</c></seealso>
+ to disable it when not used.</p>
+ </note>
+ <p><c>reload</c> is called when the NIF library is loaded and a
+ previously loaded library already exists for this module code.</p>
+ <p>Works as <c>load</c>, except that
+ <c>*priv_data</c> already contains the value set by the
+ previous call to <c>load</c> or <c>reload</c>.</p>
+ <p>The library fails to load if <c>reload</c> returns
+ anything other than <c>0</c> or if <c>reload</c> is <c>NULL</c>.</p>
</item>
-
</taglist>
</section>
<section>
- <title>DATA TYPES</title>
-
+ <title>Data Types</title>
<taglist>
- <tag><marker id="ERL_NIF_TERM"/>ERL_NIF_TERM</tag>
- <item>
+ <tag><marker id="ERL_NIF_TERM"/><c>ERL_NIF_TERM</c></tag>
+ <item>
<p>Variables of type <c>ERL_NIF_TERM</c> can refer to any Erlang term.
- This is an opaque type and values of it can only by used either as
- arguments to API functions or as return values from NIFs. All
- <c>ERL_NIF_TERM</c>'s belong to an environment
- (<seealso marker="#ErlNifEnv">ErlNifEnv</seealso>). A term can not be
- destructed individually, it is valid until its environment is destructed.</p>
+ This is an opaque type and values of it can only by used either as
+ arguments to API functions or as return values from NIFs. All
+ <c>ERL_NIF_TERM</c>s belong to an environment
+ (<seealso marker="#ErlNifEnv"><c>ErlNifEnv</c></seealso>).
+ A term cannot be destructed individually, it is valid until its
+ environment is destructed.</p>
</item>
- <tag><marker id="ErlNifEnv"/>ErlNifEnv</tag>
+ <tag><marker id="ErlNifEnv"/><c>ErlNifEnv</c></tag>
<item>
- <p><c>ErlNifEnv</c> represents an environment that can host Erlang terms.
- All terms in an environment are valid as long as the environment is valid.
- <c>ErlNifEnv</c> is an opaque type and pointers to it can only be passed
- on to API functions. There are two types of environments; process
- bound and process independent.</p>
- <p>A <em>process bound environment</em> is passed as the first argument to all NIFs.
- All function arguments passed to a NIF will belong to that environment.
- The return value from a NIF must also be a term belonging to the same
- environment.
- In addition a process bound environment contains transient information
- about the calling Erlang process. The environment is only valid in the
- thread where it was supplied as argument until the NIF returns. It is
- thus useless and dangerous to store pointers to process bound
- environments between NIF calls. </p>
- <p>A <em>process independent environment</em> is created by calling
- <seealso marker="#enif_alloc_env">enif_alloc_env</seealso>. It can be
- used to store terms between NIF calls and to send terms with
- <seealso marker="#enif_send">enif_send</seealso>. A process
- independent environment with all its terms is valid until you explicitly
- invalidates it with <seealso marker="#enif_free_env">enif_free_env</seealso>
- or <c>enif_send</c>.</p>
+ <p><c>ErlNifEnv</c> represents an environment that can host Erlang
+ terms. All terms in an environment are valid as long as the
+ environment is valid. <c>ErlNifEnv</c> is an opaque type; pointers to
+ it can only be passed on to API functions. Two types of environments
+ exist:</p>
+ <taglist>
+ <tag>Process-bound environment</tag>
+ <item>
+ <p>Passed as the first argument to all NIFs. All function arguments
+ passed to a NIF belong to that environment. The return value from
+ a NIF must also be a term belonging to the same environment.</p>
+ <p>A process-bound environment contains transient information
+ about the calling Erlang process. The environment is only valid
+ in the thread where it was supplied as argument until the NIF
+ returns. It is thus useless and dangerous to store pointers to
+ process-bound environments between NIF calls.</p>
+ </item>
+ <tag>Process-independent environment</tag>
+ <item>
+ <p>Created by calling <seealso marker="#enif_alloc_env">
+ <c>enif_alloc_env</c></seealso>. This environment can be
+ used to store terms between NIF calls and to send terms with
+ <seealso marker="#enif_send"><c>enif_send</c></seealso>. A
+ process-independent environment with all its terms is valid until
+ you explicitly invalidate it with
+ <seealso marker="#enif_free_env"><c>enif_free_env</c></seealso>
+ or <c>enif_send</c>.</p>
+ </item>
+ </taglist>
<p>All contained terms of a list/tuple/map must belong to the same
- environment as the list/tuple/map itself. Terms can be copied between
- environments with
- <seealso marker="#enif_make_copy">enif_make_copy</seealso>.</p>
+ environment as the list/tuple/map itself. Terms can be copied between
+ environments with
+ <seealso marker="#enif_make_copy"><c>enif_make_copy</c></seealso>.</p>
</item>
- <tag><marker id="ErlNifFunc"/>ErlNifFunc</tag>
- <item>
- <p/>
- <code type="none">
+ <tag><marker id="ErlNifFunc"/><c>ErlNifFunc</c></tag>
+ <item>
+ <code type="none">
typedef struct {
- const char* <em>name</em>;
- unsigned <em>arity</em>;
- ERL_NIF_TERM (*<em>fptr</em>)(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+ const char* name;
+ unsigned arity;
+ ERL_NIF_TERM (*fptr)(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
unsigned flags;
-} ErlNifFunc;
-</code>
- <p>Describes a NIF by its name, arity and implementation.
- <c>fptr</c> is a pointer to the function that implements the
- NIF. The argument <c>argv</c> of a NIF will contain the
- function arguments passed to the NIF and <c>argc</c> is the
- length of the array, i.e. the function arity. <c>argv[N-1]</c>
- will thus denote the Nth argument to the NIF. Note that the
- <c>argc</c> argument allows for the same C function to
- implement several Erlang functions with different arity (but
- same name probably). For a regular NIF, <c>flags</c> is 0 (and
- so its value can be omitted for statically initialized <c>ErlNifFunc</c>
- instances), or it can be used to indicate that the NIF is a <seealso
- marker="#dirty_nifs">dirty NIF</seealso> that should be executed
- on a dirty scheduler thread (<em>note that the dirty NIF functionality
- described here is experimental</em> and that you have to enable
- support for dirty schedulers when building OTP in order to try the
- functionality out). If the dirty NIF is expected to be
- CPU-bound, its <c>flags</c> field should be set to
- <c>ERL_NIF_DIRTY_JOB_CPU_BOUND</c>, or for I/O-bound jobs,
- <c>ERL_NIF_DIRTY_JOB_IO_BOUND</c>.</p>
+} ErlNifFunc;</code>
+ <p>Describes a NIF by its name, arity, and implementation.</p>
+ <taglist>
+ <tag><c>fptr</c></tag>
+ <item>
+ <p>A pointer to the function that implements the NIF.</p>
+ </item>
+ <tag><c>argv</c></tag>
+ <item>
+ <p>Contains the function arguments passed to the NIF.</p>
+ </item>
+ <tag><c>argc</c></tag>
+ <item>
+ <p>The array length, that is, the function arity. <c>argv[N-1]</c>
+ thus denotes the Nth argument to the NIF. Notice that the argument
+ <c>argc</c> allows for the same C function to implement several
+ Erlang functions with different arity (but probably with the same
+ name).</p>
+ </item>
+ <tag><c>flags</c></tag>
+ <item>
+ <p>Is <c>0</c> for a regular NIF (and so its value can be omitted
+ for statically initialized <c>ErlNifFunc</c> instances).</p>
+ <p><c>flags</c> can be used to indicate that the NIF is a
+ <seealso marker="#dirty_nifs">dirty NIF</seealso> that is to be
+ executed on a dirty scheduler thread.</p>
+ <p><em>The dirty NIF functionality described here is
+ experimental.</em> You have to enable support for dirty
+ schedulers when building OTP to try out the functionality.</p>
+ <p>If the dirty NIF is expected to be CPU-bound, its <c>flags</c>
+ field is to be set to <c>ERL_NIF_DIRTY_JOB_CPU_BOUND</c> or
+ <c>ERL_NIF_DIRTY_JOB_IO_BOUND</c>.</p>
+ <note>
+ <p>If one of the <c>ERL_NIF_DIRTY_JOB_*_BOUND</c> flags is set,
+ and the runtime system has no support for dirty schedulers,
+ the runtime system refuses to load the NIF library.</p>
+ </note>
+ </item>
+ </taglist>
</item>
- <tag><marker id="ErlNifBinary"/>ErlNifBinary</tag>
- <item>
- <p/>
- <code type="none">
+ <tag><marker id="ErlNifBinary"/><c>ErlNifBinary</c></tag>
+ <item>
+ <code type="none">
typedef struct {
- unsigned <em>size</em>;
- unsigned char* <em>data</em>;
-} ErlNifBinary;
-</code>
+ unsigned size;
+ unsigned char* data;
+} ErlNifBinary;</code>
<p><c>ErlNifBinary</c> contains transient information about an
inspected binary term. <c>data</c> is a pointer to a buffer
of <c>size</c> bytes with the raw content of the binary.</p>
- <p>Note that <c>ErlNifBinary</c> is a semi-opaque type and you are
+ <p>Notice that <c>ErlNifBinary</c> is a semi-opaque type and you are
only allowed to read fields <c>size</c> and <c>data</c>.</p>
</item>
- <tag><marker id="ErlNifPid"/>ErlNifPid</tag>
- <item>
- <p><c>ErlNifPid</c> is a process identifier (pid). In contrast to
- pid terms (instances of <c>ERL_NIF_TERM</c>), <c>ErlNifPid</c>'s are self
- contained and not bound to any
- <seealso marker="#ErlNifEnv">environment</seealso>. <c>ErlNifPid</c>
- is an opaque type.</p>
- </item>
-
- <tag><marker id="ErlNifResourceType"/>ErlNifResourceType</tag>
- <item>
- <p>Each instance of <c>ErlNifResourceType</c> represent a class of
- memory managed resource objects that can be garbage collected.
+ <tag><marker id="ErlNifBinaryToTerm"/><c>ErlNifBinaryToTerm</c></tag>
+ <item>
+ <p>An enumeration of the options that can be specified to
+ <seealso marker="#enif_binary_to_term">
+ <c>enif_binary_to_term</c></seealso>.
+ For default behavior, use value <c>0</c>.</p>
+ <p>When receiving data from untrusted sources, use option
+ <c>ERL_NIF_BIN2TERM_SAFE</c>.</p>
+ </item>
+ <tag><marker id="ErlNifPid"/><c>ErlNifPid</c></tag>
+ <item>
+ <p>A process identifier (pid). In contrast to pid terms (instances of
+ <c>ERL_NIF_TERM</c>), <c>ErlNifPid</c>s are self-contained and not
+ bound to any <seealso marker="#ErlNifEnv">environment</seealso>.
+ <c>ErlNifPid</c> is an opaque type.</p>
+ </item>
+ <tag><marker id="ErlNifPort"/><c>ErlNifPort</c></tag>
+ <item>
+ <p>A port identifier. In contrast to port ID terms (instances of
+ <c>ERL_NIF_TERM</c>), <c>ErlNifPort</c>s are self-contained and not
+ bound to any <seealso marker="#ErlNifEnv">environment</seealso>.
+ <c>ErlNifPort</c> is an opaque type.</p>
+ </item>
+ <tag><marker id="ErlNifResourceType"/><c>ErlNifResourceType</c></tag>
+ <item>
+ <p>Each instance of <c>ErlNifResourceType</c> represents a class of
+ memory-managed resource objects that can be garbage collected.
Each resource type has a unique name and a destructor function that
is called when objects of its type are released.</p>
- </item>
- <tag><marker id="ErlNifResourceDtor"/>ErlNifResourceDtor</tag>
- <item>
- <p/>
- <code type="none">
-typedef void ErlNifResourceDtor(ErlNifEnv* env, void* obj);
-</code>
- <p>The function prototype of a resource destructor function.
- A destructor function is not allowed to call any term-making functions.</p>
- </item>
- <tag><marker id="ErlNifCharEncoding"/>ErlNifCharEncoding</tag>
- <item>
- <p/>
- <code type="none">
+ </item>
+ <tag><marker id="ErlNifResourceDtor"/><c>ErlNifResourceDtor</c></tag>
+ <item>
+ <code type="none">
+typedef void ErlNifResourceDtor(ErlNifEnv* env, void* obj);</code>
+ <p>The function prototype of a resource destructor function.</p>
+ </item>
+ <tag><marker id="ErlNifCharEncoding"/><c>ErlNifCharEncoding</c></tag>
+ <item>
+ <code type="none">
typedef enum {
ERL_NIF_LATIN1
-}ErlNifCharEncoding;
-</code>
- <p>The character encoding used in strings and atoms. The only
- supported encoding is currently <c>ERL_NIF_LATIN1</c> for
- iso-latin-1 (8-bit ascii).</p>
- </item>
- <tag><marker id="ErlNifSysInfo"/>ErlNifSysInfo</tag>
- <item>
- <p>Used by <seealso marker="#enif_system_info">enif_system_info</seealso>
- to return information about the runtime system. Contains currently
- the exact same content as <seealso marker="erl_driver#ErlDrvSysInfo">ErlDrvSysInfo</seealso>.</p>
- </item>
- <tag><marker id="ErlNifSInt64"/>ErlNifSInt64</tag>
- <item><p>A native signed 64-bit integer type.</p></item>
- <tag><marker id="ErlNifUInt64"/>ErlNifUInt64</tag>
- <item><p>A native unsigned 64-bit integer type.</p></item>
-
- <tag><marker id="ErlNifTime"/>ErlNifTime</tag>
+}ErlNifCharEncoding;</code>
+ <p>The character encoding used in strings and atoms. The only
+ supported encoding is <c>ERL_NIF_LATIN1</c> for
+ ISO Latin-1 (8-bit ASCII).</p>
+ </item>
+ <tag><marker id="ErlNifSysInfo"/><c>ErlNifSysInfo</c></tag>
+ <item>
+ <p>Used by <seealso marker="#enif_system_info">
+ <c>enif_system_info</c></seealso> to return information about the
+ runtime system. Contains the same content as
+ <seealso marker="erl_driver#ErlDrvSysInfo">
+ <c>ErlDrvSysInfo</c></seealso>.</p>
+ </item>
+ <tag><marker id="ErlNifSInt64"/><c>ErlNifSInt64</c></tag>
+ <item>
+ <p>A native signed 64-bit integer type.</p>
+ </item>
+ <tag><marker id="ErlNifUInt64"/><c>ErlNifUInt64</c></tag>
+ <item>
+ <p>A native unsigned 64-bit integer type.</p>
+ </item>
+ <tag><marker id="ErlNifTime"/><c>ErlNifTime</c></tag>
<item>
<p>A signed 64-bit integer type for representation of time.</p>
</item>
- <tag><marker id="ErlNifTimeUnit"/>ErlNifTimeUnit</tag>
+ <tag><marker id="ErlNifTimeUnit"/><c>ErlNifTimeUnit</c></tag>
<item>
<p>An enumeration of time units supported by the NIF API:</p>
- <taglist>
- <tag><c>ERL_NIF_SEC</c></tag>
- <item><p>Seconds</p></item>
- <tag><c>ERL_NIF_MSEC</c></tag>
- <item><p>Milliseconds</p></item>
- <tag><c>ERL_NIF_USEC</c></tag>
- <item><p>Microseconds</p></item>
- <tag><c>ERL_NIF_NSEC</c></tag>
- <item><p>Nanoseconds</p></item>
- </taglist>
+ <taglist>
+ <tag><c>ERL_NIF_SEC</c></tag>
+ <item>Seconds</item>
+ <tag><c>ERL_NIF_MSEC</c></tag>
+ <item>Milliseconds</item>
+ <tag><c>ERL_NIF_USEC</c></tag>
+ <item>Microseconds</item>
+ <tag><c>ERL_NIF_NSEC</c></tag>
+ <item>Nanoseconds</item>
+ </taglist>
+ </item>
+ <tag><marker id="ErlNifUniqueInteger"/><c>ErlNifUniqueInteger</c></tag>
+ <item>
+ <p>An enumeration of the properties that can be requested from
+ <seealso marker="#enif_make_unique_integer">
+ <c>enif_unique_integer</c></seealso>.
+ For default properties, use value <c>0</c>.</p>
+ <taglist>
+ <tag><c>ERL_NIF_UNIQUE_POSITIVE</c></tag>
+ <item>
+ <p>Return only positive integers.</p>
+ </item>
+ <tag><c>ERL_NIF_UNIQUE_MONOTONIC</c></tag>
+ <item>
+ <p>Return only <seealso
+ marker="time_correction#Strictly_Monotonically_Increasing">
+ strictly monotonically increasing</seealso> integer corresponding
+ to creation time.</p>
+ </item>
+ </taglist>
</item>
-
</taglist>
</section>
<funcs>
- <func><name><ret>void *</ret><nametext>enif_alloc(size_t size)</nametext></name>
- <fsummary>Allocate dynamic memory</fsummary>
- <desc><p>Allocate memory of <c>size</c> bytes. Return NULL if allocation failed.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_alloc_binary(size_t size, ErlNifBinary* bin)</nametext></name>
- <fsummary>Create a new binary</fsummary>
- <desc><p>Allocate a new binary of size <c>size</c>
- bytes. Initialize the structure pointed to by <c>bin</c> to
- refer to the allocated binary. The binary must either be released by
- <seealso marker="#enif_release_binary">enif_release_binary</seealso>
- or ownership transferred to an Erlang term with
- <seealso marker="#enif_make_binary">enif_make_binary</seealso>.
- An allocated (and owned) <c>ErlNifBinary</c> can be kept between NIF
- calls.</p>
- <p>Return true on success or false if allocation failed.</p>
- </desc>
- </func>
- <func><name><ret>ErlNifEnv *</ret><nametext>enif_alloc_env()</nametext></name>
- <fsummary>Create a new environment</fsummary>
- <desc><p>Allocate a new process independent environment. The environment can
- be used to hold terms that is not bound to any process. Such terms can
- later be copied to a process environment with
- <seealso marker="#enif_make_copy">enif_make_copy</seealso>
- or be sent to a process as a message with <seealso marker="#enif_send">enif_send</seealso>.</p>
- <p>Return pointer to the new environment.</p>
- </desc>
- </func>
- <func><name><ret>void *</ret><nametext>enif_alloc_resource(ErlNifResourceType* type, unsigned size)</nametext></name>
- <fsummary>Allocate a memory managed resource object</fsummary>
- <desc><p>Allocate a memory managed resource object of type <c>type</c> and size <c>size</c> bytes.</p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_clear_env(ErlNifEnv* env)</nametext></name>
- <fsummary>Clear an environment for reuse</fsummary>
- <desc><p>Free all terms in an environment and clear it for reuse. The environment must
- have been allocated with <seealso marker="#enif_alloc_env">enif_alloc_env</seealso>.
- </p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_compare(ERL_NIF_TERM lhs, ERL_NIF_TERM rhs)</nametext></name>
- <fsummary>Compare two terms</fsummary>
- <desc><p>Return an integer less than, equal to, or greater than
- zero if <c>lhs</c> is found, respectively, to be less than,
- equal, or greater than <c>rhs</c>. Corresponds to the Erlang
- operators <c>==</c>, <c>/=</c>, <c>=&lt;</c>, <c>&lt;</c>,
- <c>&gt;=</c> and <c>&gt;</c> (but <em>not</em> <c>=:=</c> or <c>=/=</c>).</p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_cond_broadcast(ErlNifCond *cnd)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_cond_broadcast">erl_drv_cond_broadcast</seealso>.
- </p></desc>
- </func>
- <func><name><ret>ErlNifCond *</ret><nametext>enif_cond_create(char *name)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_cond_create">erl_drv_cond_create</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_cond_destroy(ErlNifCond *cnd)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_cond_destroy">erl_drv_cond_destroy</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_cond_signal(ErlNifCond *cnd)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_cond_signal">erl_drv_cond_signal</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_cond_wait(ErlNifCond *cnd, ErlNifMutex *mtx)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_cond_wait">erl_drv_cond_wait</seealso>.
- </p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_consume_timeslice(ErlNifEnv *env, int percent)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Give the runtime system a hint about how much CPU time the current NIF call has consumed
- since last hint, or since the start of the NIF if no previous hint has been given.
- The time is given as a <c>percent</c> of the timeslice that a process is allowed to execute Erlang
- code until it may be suspended to give time for other runnable processes.
- The scheduling timeslice is not an exact entity, but can usually be
- approximated to about 1 millisecond.</p>
- <p>Note that it is up to the runtime system to determine if and how to use this information.
- Implementations on some platforms may use other means in order to determine consumed
- CPU time. Lengthy NIFs should regardless of this frequently call <c>enif_consume_timeslice</c>
- in order to determine if it is allowed to continue execution or not.</p>
-
- <p>Returns 1 if the timeslice is exhausted, or 0 otherwise. If 1 is returned the NIF should return
- as soon as possible in order for the process to yield.</p>
- <p>Argument <c>percent</c> must be an integer between 1 and 100. This function
- must only be called from a NIF-calling thread and argument <c>env</c> must be
- the environment of the calling process.</p>
- <p>This function is provided to better support co-operative scheduling, improve system responsiveness,
- and make it easier to prevent misbehaviors of the VM due to a NIF monopolizing a scheduler thread.
- It can be used to divide <seealso marker="#lengthy_work">length work</seealso> into
- a number of repeated NIF-calls without the need to create threads.
- See also the <seealso marker="#WARNING">warning</seealso> text at the beginning of this document.</p>
- </desc>
+ <func>
+ <name><ret>void *</ret><nametext>enif_alloc(size_t size)</nametext></name>
+ <fsummary>Allocate dynamic memory.</fsummary>
+ <desc>
+ <p>Allocates memory of <c>size</c> bytes.</p>
+ <p>Returns <c>NULL</c> if the allocation fails.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_alloc_binary(size_t size, ErlNifBinary* bin)</nametext>
+ </name>
+ <fsummary>Create a new binary.</fsummary>
+ <desc>
+ <p>Allocates a new binary of size <c>size</c> bytes.
+ Initializes the structure pointed to by <c>bin</c> to
+ refer to the allocated binary. The binary must either be released by
+ <seealso marker="#enif_release_binary">
+ <c>enif_release_binary</c></seealso>
+ or ownership transferred to an Erlang term with
+ <seealso marker="#enif_make_binary"><c>enif_make_binary</c></seealso>.
+ An allocated (and owned) <c>ErlNifBinary</c> can be kept between NIF
+ calls.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if allocation
+ fails.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ErlNifEnv *</ret><nametext>enif_alloc_env()</nametext></name>
+ <fsummary>Create a new environment.</fsummary>
+ <desc>
+ <p>Allocates a new process-independent environment. The environment can
+ be used to hold terms that are not bound to any process. Such terms
+ can later be copied to a process environment with
+ <seealso marker="#enif_make_copy"><c>enif_make_copy</c></seealso> or
+ be sent to a process as a message with <seealso marker="#enif_send">
+ <c>enif_send</c></seealso>.</p>
+ <p>Returns pointer to the new environment.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void *</ret><nametext>enif_alloc_resource(ErlNifResourceType*
+ type, unsigned size)</nametext></name>
+ <fsummary>Allocate a memory-managed resource object.</fsummary>
+ <desc>
+ <p>Allocates a memory-managed resource object of type <c>type</c> and
+ size <c>size</c> bytes.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>size_t</ret><nametext>enif_binary_to_term(ErlNifEnv *env,
+ const unsigned char* data, size_t size, ERL_NIF_TERM *term,
+ ErlNifBinaryToTerm opts)</nametext></name>
+ <fsummary>Create a term from the external format.</fsummary>
+ <desc>
+ <p>Creates a term that is the result of decoding the binary data at
+ <c>data</c>, which must be encoded according to the Erlang external
+ term format. No more than <c>size</c> bytes are read from <c>data</c>.
+ Argument <c>opts</c> corresponds to the second argument to
+ <seealso marker="erlang#binary_to_term-2">
+ <c>erlang:binary_to_term/2</c></seealso> and must be either <c>0</c>
+ or <c>ERL_NIF_BIN2TERM_SAFE</c>.</p>
+ <p>On success, stores the resulting term at <c>*term</c> and returns
+ the number of bytes read. Returns <c>0</c> if decoding fails or if
+ <c>opts</c> is invalid.</p>
+ <p>See also <seealso marker="#ErlNifBinaryToTerm">
+ <c>ErlNifBinaryToTerm</c></seealso>,
+ <seealso marker="erlang#binary_to_term-2">
+ <c>erlang:binary_to_term/2</c></seealso>, and
+ <seealso marker="#enif_term_to_binary">
+ <c>enif_term_to_binary</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret><nametext>enif_clear_env(ErlNifEnv* env)</nametext>
+ </name>
+ <fsummary>Clear an environment for reuse.</fsummary>
+ <desc>
+ <p>Frees all terms in an environment and clears it for reuse.
+ The environment must have been allocated with
+ <seealso marker="#enif_alloc_env"><c>enif_alloc_env</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_compare(ERL_NIF_TERM lhs, ERL_NIF_TERM rhs)</nametext>
+ </name>
+ <fsummary>Compare two terms.</fsummary>
+ <desc>
+ <p>Returns an integer &lt; <c>0</c> if <c>lhs</c> &lt; <c>rhs</c>,
+ <c>0</c> if <c>lhs</c> = <c>rhs</c>, and &gt; <c>0</c> if
+ <c>lhs</c> &gt; <c>rhs</c>. Corresponds to the Erlang
+ operators <c>==</c>, <c>/=</c>, <c>=&lt;</c>, <c>&lt;</c>,
+ <c>&gt;=</c>, and <c>&gt;</c> (but <em>not</em> <c>=:=</c> or
+ <c>=/=</c>).</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_cond_broadcast(ErlNifCond *cnd)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_cond_broadcast">
+ <c>erl_drv_cond_broadcast</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ErlNifCond *</ret>
+ <nametext>enif_cond_create(char *name)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_cond_create">
+ <c>erl_drv_cond_create</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_cond_destroy(ErlNifCond *cnd)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_cond_destroy">
+ <c>erl_drv_cond_destroy</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_cond_signal(ErlNifCond *cnd)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_cond_signal">
+ <c>erl_drv_cond_signal</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_cond_wait(ErlNifCond *cnd, ErlNifMutex *mtx)</nametext>
+ </name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_cond_wait">
+ <c>erl_drv_cond_wait</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_consume_timeslice(ErlNifEnv *env, int percent)</nametext>
+ </name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Gives the runtime system a hint about how much CPU time the current
+ NIF call has consumed since the last hint, or since the start of the
+ NIF if no previous hint has been specified. The time is specified as a
+ percent of the timeslice that a process is allowed to execute
+ Erlang code until it can be suspended to give time for other runnable
+ processes. The scheduling timeslice is not an exact entity, but can
+ usually be approximated to about 1 millisecond.</p>
+ <p>Notice that it is up to the runtime system to determine if and how
+ to use this information. Implementations on some platforms can use
+ other means to determine consumed CPU time. Lengthy NIFs should
+ regardless of this frequently call <c>enif_consume_timeslice</c> to
+ determine if it is allowed to continue execution.</p>
+ <p>Argument <c>percent</c> must be an integer between 1 and 100. This
+ function must only be called from a NIF-calling thread, and argument
+ <c>env</c> must be the environment of the calling process.</p>
+ <p>Returns <c>1</c> if the timeslice is exhausted, otherwise <c>0</c>.
+ If <c>1</c> is returned, the NIF is to return as soon as possible in
+ order for the process to yield.</p>
+ <p>This function is provided to better support co-operative scheduling,
+ improve system responsiveness, and make it easier to prevent
+ misbehaviors of the VM because of a NIF monopolizing a scheduler
+ thread. It can be used to divide <seealso marker="#lengthy_work">
+ length work</seealso> into a number of repeated NIF calls without the
+ need to create threads.</p>
+ <p>See also the <seealso marker="#WARNING">warning</seealso> text at
+ the beginning of this manual page.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ErlNifTime</ret><nametext>enif_convert_time_unit(ErlNifTime
+ val, ErlNifTimeUnit from, ErlNifTimeUnit to)</nametext></name>
+ <fsummary>Convert time unit of a time value.</fsummary>
+ <desc>
+ <marker id="enif_convert_time_unit"></marker>
+ <p>Converts the <c>val</c> value of time unit <c>from</c> to
+ the corresponding value of time unit <c>to</c>. The result is
+ rounded using the floor function.</p>
+ <taglist>
+ <tag><c>val</c></tag>
+ <item>Value to convert time unit for.</item>
+ <tag><c>from</c></tag>
+ <item>Time unit of <c>val</c>.</item>
+ <tag><c>to</c></tag>
+ <item>Time unit of returned value.</item>
+ </taglist>
+ <p>Returns <c>ERL_NIF_TIME_ERROR</c> if called with an invalid
+ time unit argument.</p>
+ <p>See also <seealso marker="#ErlNifTime"><c>ErlNifTime</c></seealso>
+ and
+ <seealso marker="#ErlNifTimeUnit"><c>ErlNifTimeUnit</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_cpu_time(ErlNifEnv *)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Returns the CPU time in the same format as
+ <seealso marker="erlang#timestamp-0">
+ <c>erlang:timestamp()</c></seealso>.
+ The CPU time is the time the current logical CPU has spent executing
+ since some arbitrary point in the past. If the OS does not support
+ fetching this value, <c>enif_cpu_time</c> invokes
+ <seealso marker="#enif_make_badarg">
+ <c>enif_make_badarg</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_equal_tids(ErlNifTid tid1, ErlNifTid tid2)</nametext>
+ </name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_equal_tids">
+ <c>erl_drv_equal_tids</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret><nametext>enif_free(void* ptr)</nametext></name>
+ <fsummary>Free dynamic memory.</fsummary>
+ <desc>
+ <p>Frees memory allocated by
+ <seealso marker="#enif_alloc"><c>enif_alloc</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_free_env(ErlNifEnv* env)</nametext></name>
+ <fsummary>Free an environment allocated with enif_alloc_env.</fsummary>
+ <desc>
+ <p>Frees an environment allocated with
+ <seealso marker="#enif_alloc_env"><c>enif_alloc_env</c></seealso>.
+ All terms created in the environment are freed as well.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_atom(ErlNifEnv* env, ERL_NIF_TERM
+ term, char* buf, unsigned size, ErlNifCharEncoding encode)</nametext>
+ </name>
+ <fsummary>Get the text representation of an atom term.</fsummary>
+ <desc>
+ <p>Writes a <c>NULL</c>-terminated string in the buffer pointed to by
+ <c>buf</c> of size <c>size</c>, consisting of the string
+ representation of the atom <c>term</c> with encoding
+ <seealso marker="#ErlNifCharEncoding">encode</seealso>.</p>
+ <p>Returns the number of bytes written (including terminating
+ <c>NULL</c> character) or <c>0</c> if <c>term</c> is not an atom with
+ maximum length of <c>size-1</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_atom_length(ErlNifEnv* env,
+ ERL_NIF_TERM term, unsigned* len, ErlNifCharEncoding encode)</nametext>
+ </name>
+ <fsummary>Get the length of atom <c>term</c>.</fsummary>
+ <desc>
+ <p>Sets <c>*len</c> to the length (number of bytes excluding
+ terminating <c>NULL</c> character) of the atom <c>term</c> with
+ encoding <c>encode</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is not
+ an atom.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_double(ErlNifEnv* env,
+ ERL_NIF_TERM term, double* dp)</nametext></name>
+ <fsummary>Read a floating-point number term.</fsummary>
+ <desc>
+ <p>Sets <c>*dp</c> to the floating-point value of <c>term</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is not
+ a float.</p>
+ </desc>
</func>
- <func><name><ret>int</ret><nametext>enif_equal_tids(ErlNifTid tid1, ErlNifTid tid2)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_equal_tids">erl_drv_equal_tids</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_free(void* ptr)</nametext></name>
- <fsummary>Free dynamic memory</fsummary>
- <desc><p>Free memory allocated by <c>enif_alloc</c>.</p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_free_env(ErlNifEnv* env)</nametext></name>
- <fsummary>Free an environment allocated with enif_alloc_env</fsummary>
- <desc><p>Free an environment allocated with <seealso marker="#enif_alloc_env">enif_alloc_env</seealso>.
- All terms created in the environment will be freed as well.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_atom(ErlNifEnv* env, ERL_NIF_TERM term, char* buf, unsigned size, ErlNifCharEncoding encode)</nametext></name>
- <fsummary>Get the text representation of an atom term</fsummary>
- <desc><p>Write a null-terminated string, in the buffer pointed to by
- <c>buf</c> of size <c>size</c>, consisting of the string
- representation of the atom <c>term</c> with encoding
- <seealso marker="#ErlNifCharEncoding">encode</seealso>. Return
- the number of bytes written (including terminating null character) or 0 if
- <c>term</c> is not an atom with maximum length of
- <c>size-1</c>.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_atom_length(ErlNifEnv* env, ERL_NIF_TERM term, unsigned* len, ErlNifCharEncoding encode)</nametext></name>
- <fsummary>Get the length of atom <c>term</c></fsummary>
- <desc><p>Set <c>*len</c> to the length (number of bytes excluding
- terminating null character) of the atom <c>term</c> with encoding
- <c>encode</c>. Return true on success or false if <c>term</c> is not an
- atom.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_double(ErlNifEnv* env, ERL_NIF_TERM term, double* dp)</nametext></name>
- <fsummary>Read a floating-point number term</fsummary>
- <desc><p>Set <c>*dp</c> to the floating point value of
- <c>term</c>. Return true on success or false if <c>term</c> is not a float.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_int(ErlNifEnv* env, ERL_NIF_TERM term, int* ip)</nametext></name>
- <fsummary>Read an integer term</fsummary>
- <desc><p>Set <c>*ip</c> to the integer value of
- <c>term</c>. Return true on success or false if <c>term</c> is not an
- integer or is outside the bounds of type <c>int</c>.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_int64(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifSInt64* ip)</nametext></name>
- <fsummary>Read a 64-bit integer term</fsummary>
- <desc><p>Set <c>*ip</c> to the integer value of
- <c>term</c>. Return true on success or false if <c>term</c> is not an
- integer or is outside the bounds of a signed 64-bit integer.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_local_pid(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifPid* pid)</nametext></name>
- <fsummary>Read an local pid term</fsummary>
- <desc><p>If <c>term</c> is the pid of a node local process, initialize the
- pid variable <c>*pid</c> from it and return true. Otherwise return false.
- No check if the process is alive is done.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_list_cell(ErlNifEnv* env, ERL_NIF_TERM list, ERL_NIF_TERM* head, ERL_NIF_TERM* tail)</nametext></name>
- <fsummary>Get head and tail from a list</fsummary>
- <desc><p>Set <c>*head</c> and <c>*tail</c> from
- <c>list</c> and return true, or return false if <c>list</c> is not a
- non-empty list.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_list_length(ErlNifEnv* env, ERL_NIF_TERM term, unsigned* len)</nametext></name>
- <fsummary>Get the length of list <c>term</c></fsummary>
- <desc><p>Set <c>*len</c> to the length of list <c>term</c> and return true,
- or return false if <c>term</c> is not a list.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_long(ErlNifEnv* env, ERL_NIF_TERM term, long int* ip)</nametext></name>
- <fsummary>Read an long integer term</fsummary>
- <desc><p>Set <c>*ip</c> to the long integer value of <c>term</c> and
- return true, or return false if <c>term</c> is not an integer or is
- outside the bounds of type <c>long int</c>.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_map_size(ErlNifEnv* env, ERL_NIF_TERM term, size_t *size)</nametext></name>
- <fsummary>Read the size of a map term</fsummary>
- <desc><p>Set <c>*size</c> to the number of key-value pairs in the map <c>term</c> and
- return true, or return false if <c>term</c> is not a map.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_map_value(ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value)</nametext></name>
- <fsummary>Get the value of a key in a map</fsummary>
- <desc><p>Set <c>*value</c> to the value associated with <c>key</c> in the
- map <c>map</c> and return true. Return false if <c>map</c> is not a map
- or if <c>map</c> does not contain <c>key</c>.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_resource(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifResourceType* type, void** objp)</nametext></name>
- <fsummary>Get the pointer to a resource object</fsummary>
- <desc><p>Set <c>*objp</c> to point to the resource object referred to by <c>term</c>.</p>
- <p>Return true on success or false if <c>term</c> is not a handle to a resource object
- of type <c>type</c>.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_string(ErlNifEnv* env,
- ERL_NIF_TERM list, char* buf, unsigned size,
- ErlNifCharEncoding encode)</nametext></name>
- <fsummary>Get a C-string from a list</fsummary>
- <desc><p>Write a null-terminated string, in the buffer pointed to by
- <c>buf</c> with size <c>size</c>, consisting of the characters
- in the string <c>list</c>. The characters are written using encoding
- <seealso marker="#ErlNifCharEncoding">encode</seealso>.
- Return the number of bytes written (including terminating null
- character), or <c>-size</c> if the string was truncated due to
- buffer space, or 0 if <c>list</c> is not a string that can be
- encoded with <c>encode</c> or if <c>size</c> was less than 1.
- The written string is always null-terminated unless buffer
- <c>size</c> is less than 1.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_tuple(ErlNifEnv* env, ERL_NIF_TERM term, int* arity, const ERL_NIF_TERM** array)</nametext></name>
- <fsummary>Inspect the elements of a tuple</fsummary>
- <desc><p>If <c>term</c> is a tuple, set <c>*array</c> to point
- to an array containing the elements of the tuple and set
- <c>*arity</c> to the number of elements. Note that the array
- is read-only and <c>(*array)[N-1]</c> will be the Nth element of
- the tuple. <c>*array</c> is undefined if the arity of the tuple
- is zero.</p><p>Return true on success or false if <c>term</c> is not a
- tuple.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_uint(ErlNifEnv* env, ERL_NIF_TERM term, unsigned int* ip)</nametext></name>
- <fsummary>Read an unsigned integer term</fsummary>
- <desc><p>Set <c>*ip</c> to the unsigned integer value of <c>term</c> and
- return true, or return false if <c>term</c> is not an unsigned integer or
- is outside the bounds of type <c>unsigned int</c>.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_uint64(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifUInt64* ip)</nametext></name>
- <fsummary>Read an unsigned 64-bit integer term</fsummary>
- <desc><p>Set <c>*ip</c> to the unsigned integer value of <c>term</c> and
- return true, or return false if <c>term</c> is not an unsigned integer or
- is outside the bounds of an unsigned 64-bit integer.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_ulong(ErlNifEnv* env, ERL_NIF_TERM term, unsigned long* ip)</nametext></name>
- <fsummary>Read an unsigned integer term</fsummary>
- <desc><p>Set <c>*ip</c> to the unsigned long integer value of <c>term</c>
- and return true, or return false if <c>term</c> is not an unsigned integer or is
- outside the bounds of type <c>unsigned long</c>.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_getenv(const char* key, char* value, size_t *value_size)</nametext></name>
- <fsummary>Get the value of an environment variable</fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_getenv">erl_drv_getenv</seealso>.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_has_pending_exception(ErlNifEnv* env, ERL_NIF_TERM* reason)</nametext></name>
- <fsummary>Check if an exception has been raised</fsummary>
- <desc><p>Return true if a pending exception is associated
- with the environment <c>env</c>. If <c>reason</c> is a null pointer, ignore it.
- Otherwise, if there's a pending exception associated with <c>env</c>, set the ERL_NIF_TERM
- to which <c>reason</c> points to the value of the exception's term. For example, if
- <seealso marker="#enif_make_badarg">enif_make_badarg</seealso> is called to set a
- pending <c>badarg</c> exception, a subsequent call to <c>enif_has_pending_exception(env, &amp;reason)</c>
- will set <c>reason</c> to the atom <c>badarg</c>, then return true.</p>
- <p>See also: <seealso marker="#enif_make_badarg">enif_make_badarg</seealso>
- and <seealso marker="#enif_raise_exception">enif_raise_exception</seealso>.</p>
- </desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_inspect_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term, ErlNifBinary* bin)</nametext></name>
- <fsummary>Inspect the content of a binary</fsummary>
- <desc><p>Initialize the structure pointed to by <c>bin</c> with
- information about the binary term
- <c>bin_term</c>. Return true on success or false if <c>bin_term</c> is not a binary.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_inspect_iolist_as_binary(ErlNifEnv*
- env, ERL_NIF_TERM term, ErlNifBinary* bin)
- </nametext></name>
- <fsummary>Inspect the content of an iolist</fsummary>
- <desc><p>Initialize the structure pointed to by <c>bin</c> with one
- continuous buffer with the same byte content as <c>iolist</c>. As with
- inspect_binary, the data pointed to by <c>bin</c> is transient and does
- not need to be released. Return true on success or false if <c>iolist</c> is not an
- iolist.</p>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_int(ErlNifEnv* env, ERL_NIF_TERM
+ term, int* ip)</nametext></name>
+ <fsummary>Read an integer term.</fsummary>
+ <desc>
+ <p>Sets <c>*ip</c> to the integer value of <c>term</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is not
+ an integer or is outside the bounds of type <c>int</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_int64(ErlNifEnv* env, ERL_NIF_TERM
+ term, ErlNifSInt64* ip)</nametext></name>
+ <fsummary>Read a 64-bit integer term.</fsummary>
+ <desc>
+ <p>Sets <c>*ip</c> to the integer value of <c>term</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is not
+ an integer or is outside the bounds of a signed 64-bit integer.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_local_pid(ErlNifEnv* env,
+ ERL_NIF_TERM term, ErlNifPid* pid)</nametext></name>
+ <fsummary>Read a local pid term.</fsummary>
+ <desc>
+ <p>If <c>term</c> is the pid of a node local process, this function
+ initializes the pid variable <c>*pid</c> from it and returns
+ <c>true</c>. Otherwise returns <c>false</c>. No check is done to see
+ if the process is alive.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_local_port(ErlNifEnv* env,
+ ERL_NIF_TERM term, ErlNifPort* port_id)</nametext></name>
+ <fsummary>Read a local port term.</fsummary>
+ <desc>
+ <p>If <c>term</c> identifies a node local port, this function
+ initializes the port variable <c>*port_id</c> from it and returns
+ <c>true</c>. Otherwise returns <c>false</c>. No check is done to see
+ if the port is alive.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_list_cell(ErlNifEnv* env,
+ ERL_NIF_TERM list, ERL_NIF_TERM* head, ERL_NIF_TERM* tail)</nametext>
+ </name>
+ <fsummary>Get head and tail from a list.</fsummary>
+ <desc>
+ <p>Sets <c>*head</c> and <c>*tail</c> from list <c>list</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if it is
+ not a list or the list is empty.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_list_length(ErlNifEnv* env,
+ ERL_NIF_TERM term, unsigned* len)</nametext></name>
+ <fsummary>Get the length of list <c>term</c>.</fsummary>
+ <desc>
+ <p>Sets <c>*len</c> to the length of list <c>term</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is
+ not a proper list.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_long(ErlNifEnv* env, ERL_NIF_TERM
+ term, long int* ip)</nametext></name>
+ <fsummary>Read a long integer term.</fsummary>
+ <desc>
+ <p>Sets <c>*ip</c> to the long integer value of <c>term</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is
+ not an integer or is outside the bounds of type <c>long int</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_map_size(ErlNifEnv* env,
+ ERL_NIF_TERM term, size_t *size)</nametext></name>
+ <fsummary>Read the size of a map term.</fsummary>
+ <desc>
+ <p>Sets <c>*size</c> to the number of key-value pairs in the map
+ <c>term</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is
+ not a map.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_map_value(ErlNifEnv* env,
+ ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value)</nametext>
+ </name>
+ <fsummary>Get the value of a key in a map.</fsummary>
+ <desc>
+ <p>Sets <c>*value</c> to the value associated with <c>key</c> in the
+ map <c>map</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>map</c> is not
+ a map or if <c>map</c> does not contain <c>key</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_resource(ErlNifEnv* env,
+ ERL_NIF_TERM term, ErlNifResourceType* type, void** objp)</nametext>
+ </name>
+ <fsummary>Get the pointer to a resource object.</fsummary>
+ <desc>
+ <p>Sets <c>*objp</c> to point to the resource object referred to by
+ <c>term</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is
+ not a handle to a resource object of type <c>type</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_string(ErlNifEnv* env,
+ ERL_NIF_TERM list, char* buf, unsigned size,
+ ErlNifCharEncoding encode)</nametext></name>
+ <fsummary>Get a C-string from a list.</fsummary>
+ <desc>
+ <p>Writes a <c>NULL</c>-terminated string in the buffer pointed to by
+ <c>buf</c> with size <c>size</c>, consisting of the characters
+ in the string <c>list</c>. The characters are written using encoding
+ <seealso marker="#ErlNifCharEncoding">encode</seealso>.</p>
+ <p>Returns one of the following:</p>
+ <list type="bulleted">
+ <item>The number of bytes written (including terminating <c>NULL</c>
+ character)</item>
+ <item><c>-size</c> if the string was truncated because of buffer
+ space</item>
+ <item><c>0</c> if <c>list</c> is not a string that can be encoded
+ with <c>encode</c> or if <c>size</c> was &lt; <c>1</c>.</item>
+ </list>
+ <p>The written string is always <c>NULL</c>-terminated, unless buffer
+ <c>size</c> is &lt; <c>1</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_tuple(ErlNifEnv* env, ERL_NIF_TERM
+ term, int* arity, const ERL_NIF_TERM** array)</nametext></name>
+ <fsummary>Inspect the elements of a tuple.</fsummary>
+ <desc>
+ <p>If <c>term</c> is a tuple, this function sets <c>*array</c> to point
+ to an array containing the elements of the tuple, and sets
+ <c>*arity</c> to the number of elements. Notice that the array
+ is read-only and <c>(*array)[N-1]</c> is the Nth element of
+ the tuple. <c>*array</c> is undefined if the arity of the tuple
+ is zero.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is
+ not a tuple.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_uint(ErlNifEnv* env, ERL_NIF_TERM
+ term, unsigned int* ip)</nametext></name>
+ <fsummary>Read an unsigned integer term.</fsummary>
+ <desc>
+ <p>Sets <c>*ip</c> to the unsigned integer value of <c>term</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is
+ not an unsigned integer or is outside the bounds of type
+ <c>unsigned int</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_uint64(ErlNifEnv* env,
+ ERL_NIF_TERM term, ErlNifUInt64* ip)</nametext></name>
+ <fsummary>Read an unsigned 64-bit integer term.</fsummary>
+ <desc>
+ <p>Sets <c>*ip</c> to the unsigned integer value of <c>term</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is
+ not an unsigned integer or is outside the bounds of an unsigned
+ 64-bit integer.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_ulong(ErlNifEnv* env, ERL_NIF_TERM
+ term, unsigned long* ip)</nametext></name>
+ <fsummary>Read an unsigned integer term.</fsummary>
+ <desc>
+ <p>Sets <c>*ip</c> to the unsigned long integer value of
+ <c>term</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is
+ not an unsigned integer or is outside the bounds of type
+ <c>unsigned long</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_getenv(const char* key, char* value,
+ size_t *value_size)</nametext></name>
+ <fsummary>Get the value of an environment variable.</fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_getenv">
+ <c>erl_drv_getenv</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_has_pending_exception(ErlNifEnv* env,
+ ERL_NIF_TERM* reason)</nametext></name>
+ <fsummary>Check if an exception has been raised.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if a pending exception is associated with the
+ environment <c>env</c>. If <c>reason</c> is a <c>NULL</c> pointer,
+ ignore it. Otherwise, if a pending exception associated with
+ <c>env</c> exists, set <c>ERL_NIF_TERM</c> to which <c>reason</c>
+ points to the value of the exception's term. For example, if
+ <seealso marker="#enif_make_badarg">
+ <c>enif_make_badarg</c></seealso> is called to set a pending
+ <c>badarg</c> exception, a later call to
+ <c>enif_has_pending_exception(env, &amp;reason)</c> sets
+ <c>reason</c> to the atom <c>badarg</c>, then return <c>true</c>.</p>
+ <p>See also <seealso marker="#enif_make_badarg">
+ <c>enif_make_badarg</c></seealso> and
+ <seealso marker="#enif_raise_exception">
+ <c>enif_raise_exception</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_inspect_binary(ErlNifEnv* env,
+ ERL_NIF_TERM bin_term, ErlNifBinary* bin)</nametext></name>
+ <fsummary>Inspect the content of a binary.</fsummary>
+ <desc>
+ <p>Initializes the structure pointed to by <c>bin</c> with information
+ about binary term <c>bin_term</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>bin_term</c>
+ is not a binary.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_inspect_iolist_as_binary(ErlNifEnv*
+ env, ERL_NIF_TERM term, ErlNifBinary* bin)</nametext></name>
+ <fsummary>Inspect the content of an iolist.</fsummary>
+ <desc>
+ <p>Initializes the structure pointed to by <c>bin</c> with a
+ continuous buffer with the same byte content as <c>iolist</c>. As
+ with <c>inspect_binary</c>, the data pointed to by <c>bin</c> is
+ transient and does not need to be released.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>iolist</c> is
+ not an iolist.</p>
</desc>
</func>
- <func><name><ret>int</ret><nametext>enif_is_atom(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name>
- <fsummary>Determine if a term is an atom</fsummary>
- <desc><p>Return true if <c>term</c> is an atom.</p></desc>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_is_atom(ErlNifEnv* env, ERL_NIF_TERM term)</nametext>
+ </name>
+ <fsummary>Determine if a term is an atom.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c>term</c> is an atom.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_is_binary(ErlNifEnv* env, ERL_NIF_TERM term)</nametext>
+ </name>
+ <fsummary>Determine if a term is a binary.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c>term</c> is a binary.</p>
+ </desc>
</func>
- <func><name><ret>int</ret><nametext>enif_is_binary(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name>
- <fsummary>Determine if a term is a binary</fsummary>
- <desc><p>Return true if <c>term</c> is a binary</p></desc>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_is_current_process_alive(ErlNifEnv* env)</nametext>
+ </name>
+ <fsummary>Determine if currently executing process is alive.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if the currently executing process is currently
+ alive, otherwise <c>false</c>.</p>
+ <p>This function can only be used from a NIF-calling thread, and with
+ an environment corresponding to currently executing processes.</p>
+ </desc>
</func>
- <func><name><ret>int</ret><nametext>enif_is_empty_list(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name>
- <fsummary>Determine if a term is an empty list</fsummary>
- <desc><p>Return true if <c>term</c> is an empty list.</p></desc>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_is_empty_list(ErlNifEnv* env,
+ ERL_NIF_TERM term)</nametext></name>
+ <fsummary>Determine if a term is an empty list.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c>term</c> is an empty list.</p>
+ </desc>
</func>
- <func><name><ret>int</ret><nametext>enif_is_exception(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name>
- <fsummary>Determine if a term is an exception</fsummary>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_is_exception(ErlNifEnv* env,
+ ERL_NIF_TERM term)</nametext></name>
+ <fsummary>Determine if a term is an exception.</fsummary>
<desc><marker id="enif_is_exception"/>
- <p>Return true if <c>term</c> is an exception.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_is_map(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name>
- <fsummary>Determine if a term is a map</fsummary>
- <desc><p>Return true if <c>term</c> is a map, false otherwise.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_is_number(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name>
- <fsummary>Determine if a term is a number (integer or float)</fsummary>
- <desc><p>Return true if <c>term</c> is a number.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_is_fun(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name>
- <fsummary>Determine if a term is a fun</fsummary>
- <desc><p>Return true if <c>term</c> is a fun.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_is_identical(ERL_NIF_TERM lhs, ERL_NIF_TERM rhs)</nametext></name>
- <fsummary>Erlang operator =:=</fsummary>
- <desc><p>Return true if the two terms are identical. Corresponds to the
- Erlang operators <c>=:=</c> and
- <c>=/=</c>.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_is_on_dirty_scheduler(ErlNifEnv* env)</nametext></name>
- <fsummary>Check to see if executing on a dirty scheduler thread</fsummary>
- <desc>
- <p>Check to see if the current NIF is executing on a dirty scheduler thread. If the
- emulator is built with threading support, calling <c>enif_is_on_dirty_scheduler</c>
- from within a dirty NIF returns true. It returns false when the calling NIF is a regular
- NIF running on a normal scheduler thread, or when the emulator is built without threading
- support.</p>
- <note><p>This function is available only when the emulator is configured with dirty
- schedulers enabled. This feature is currently disabled by default. To determine whether
- the dirty NIF API is available, native code can check to see if the C preprocessor macro
- <c>ERL_NIF_DIRTY_SCHEDULER_SUPPORT</c> is defined.</p></note>
- </desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_is_pid(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name>
- <fsummary>Determine if a term is a pid</fsummary>
- <desc><p>Return true if <c>term</c> is a pid.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_is_port(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name>
- <fsummary>Determine if a term is a port</fsummary>
- <desc><p>Return true if <c>term</c> is a port.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_is_ref(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name>
- <fsummary>Determine if a term is a reference</fsummary>
- <desc><p>Return true if <c>term</c> is a reference.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_is_tuple(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name>
- <fsummary>Determine if a term is a tuple</fsummary>
- <desc><p>Return true if <c>term</c> is a tuple.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_is_list(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name>
- <fsummary>Determine if a term is a list</fsummary>
- <desc><p>Return true if <c>term</c> is a list.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_keep_resource(void* obj)</nametext></name>
- <fsummary>Add a reference to a resource object</fsummary>
- <desc><p>Add a reference to resource object <c>obj</c> obtained from
- <seealso marker="#enif_alloc_resource">enif_alloc_resource</seealso>.
- Each call to <c>enif_keep_resource</c> for an object must be balanced by
- a call to <seealso marker="#enif_release_resource">enif_release_resource</seealso>
- before the object will be destructed.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_atom(ErlNifEnv* env, const char* name)</nametext></name>
- <fsummary>Create an atom term</fsummary>
- <desc><p>Create an atom term from the null-terminated C-string <c>name</c>
- with iso-latin-1 encoding. If the length of <c>name</c> exceeds the maximum length
- allowed for an atom (255 characters), <c>enif_make_atom</c> invokes
- <seealso marker="#enif_make_badarg">enif_make_badarg</seealso>.
- </p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_atom_len(ErlNifEnv* env, const char* name, size_t len)</nametext></name>
- <fsummary>Create an atom term</fsummary>
- <desc><p>Create an atom term from the string <c>name</c> with length <c>len</c>.
- Null-characters are treated as any other characters. If <c>len</c> is greater than the maximum length
- allowed for an atom (255 characters), <c>enif_make_atom</c> invokes
- <seealso marker="#enif_make_badarg">enif_make_badarg</seealso>.
- </p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_badarg(ErlNifEnv* env)</nametext></name>
- <fsummary>Make a badarg exception</fsummary>
- <desc><p>Make a badarg exception to be returned from a NIF, and associate
- it with the environment <c>env</c>. Once a NIF or any function
- it calls invokes <c>enif_make_badarg</c>, the runtime ensures that a
- <c>badarg</c> exception is raised when the NIF returns, even if the NIF
- attempts to return a non-exception term instead.
- The return value from <c>enif_make_badarg</c> may be used only as the
- return value from the NIF that invoked it (directly or indirectly)
- or be passed to
- <seealso marker="#enif_is_exception">enif_is_exception</seealso>, but
- not to any other NIF API function.</p>
- <p>See also: <seealso marker="#enif_has_pending_exception">enif_has_pending_exception</seealso>
- and <seealso marker="#enif_raise_exception">enif_raise_exception</seealso>
- </p>
- <note><p>In earlier versions (older than erts-7.0, OTP 18) the return value
- from <c>enif_make_badarg</c> had to be returned from the NIF. This
- requirement is now lifted as the return value from the NIF is ignored
- if <c>enif_make_badarg</c> has been invoked.</p></note></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_binary(ErlNifEnv* env, ErlNifBinary* bin)</nametext></name>
- <fsummary>Make a binary term</fsummary>
- <desc><p>Make a binary term from <c>bin</c>. Any ownership of
- the binary data will be transferred to the created term and
- <c>bin</c> should be considered read-only for the rest of the NIF
- call and then as released.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_copy(ErlNifEnv* dst_env, ERL_NIF_TERM src_term)</nametext></name>
- <fsummary>Make a copy of a term</fsummary>
- <desc><p>Make a copy of term <c>src_term</c>. The copy will be created in
- environment <c>dst_env</c>. The source term may be located in any
- environment.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_double(ErlNifEnv* env, double d)</nametext></name>
- <fsummary>Create a floating-point term</fsummary>
- <desc><p>Create a floating-point term from a <c>double</c>. If the <c>double</c> argument is
- not finite or is NaN, <c>enif_make_double</c> invokes
- <seealso marker="#enif_make_badarg">enif_make_badarg</seealso>.
- </p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_make_existing_atom(ErlNifEnv* env, const char* name, ERL_NIF_TERM* atom, ErlNifCharEncoding encode)</nametext></name>
- <fsummary>Create an existing atom term</fsummary>
- <desc><p>Try to create the term of an already existing atom from
- the null-terminated C-string <c>name</c> with encoding
- <seealso marker="#ErlNifCharEncoding">encode</seealso>. If the atom
- already exists store the term in <c>*atom</c> and return true, otherwise
- return false. If the length of <c>name</c> exceeds the maximum length
- allowed for an atom (255 characters), <c>enif_make_existing_atom</c>
- returns false.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_make_existing_atom_len(ErlNifEnv* env, const char* name, size_t len, ERL_NIF_TERM* atom, ErlNifCharEncoding encoding)</nametext></name>
- <fsummary>Create an existing atom term</fsummary>
- <desc><p>Try to create the term of an already existing atom from the
- string <c>name</c> with length <c>len</c> and encoding
- <seealso marker="#ErlNifCharEncoding">encode</seealso>. Null-characters
- are treated as any other characters. If the atom already exists store the term
- in <c>*atom</c> and return true, otherwise return false. If <c>len</c> is greater
- than the maximum length allowed for an atom (255 characters),
- <c>enif_make_existing_atom_len</c> returns false.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_int(ErlNifEnv* env, int i)</nametext></name>
- <fsummary>Create an integer term</fsummary>
- <desc><p>Create an integer term.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_int64(ErlNifEnv* env, ErlNifSInt64 i)</nametext></name>
- <fsummary>Create an integer term</fsummary>
- <desc><p>Create an integer term from a signed 64-bit integer.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list(ErlNifEnv* env, unsigned cnt, ...)</nametext></name>
- <fsummary>Create a list term</fsummary>
- <desc><p>Create an ordinary list term of length <c>cnt</c>. Expects
- <c>cnt</c> number of arguments (after <c>cnt</c>) of type ERL_NIF_TERM as the
- elements of the list. An empty list is returned if <c>cnt</c> is 0.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list1(ErlNifEnv* env, ERL_NIF_TERM e1)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list2(ErlNifEnv* env, ERL_NIF_TERM e1, ERL_NIF_TERM e2)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list3(ErlNifEnv* env, ERL_NIF_TERM e1, ERL_NIF_TERM e2, ERL_NIF_TERM e3)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list4(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e4)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list5(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e5)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list6(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e6)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list7(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e7)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list8(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e8)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list9(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e9)</nametext></name>
- <fsummary>Create a list term</fsummary>
- <desc><p>Create an ordinary list term with length indicated by the
- function name. Prefer these functions (macros) over the variadic
- <c>enif_make_list</c> to get a compile time error if the number of
- arguments does not match.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list_cell(ErlNifEnv* env, ERL_NIF_TERM head, ERL_NIF_TERM tail)</nametext></name>
- <fsummary>Create a list cell</fsummary>
- <desc><p>Create a list cell <c>[head | tail]</c>.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list_from_array(ErlNifEnv* env, const ERL_NIF_TERM arr[], unsigned cnt)</nametext></name>
- <fsummary>Create a list term from an array</fsummary>
- <desc><p>Create an ordinary list containing the elements of array <c>arr</c>
- of length <c>cnt</c>. An empty list is returned if <c>cnt</c> is 0.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_long(ErlNifEnv* env, long int i)</nametext></name>
- <fsummary>Create an integer term from a long int</fsummary>
- <desc><p>Create an integer term from a <c>long int</c>.</p></desc>
- </func>
- <func><name><ret>unsigned char *</ret><nametext>enif_make_new_binary(ErlNifEnv* env, size_t size, ERL_NIF_TERM* termp)</nametext></name>
- <fsummary>Allocate and create a new binary term</fsummary>
- <desc><p>Allocate a binary of size <c>size</c> bytes and create an owning
- term. The binary data is mutable until the calling NIF returns. This is a
- quick way to create a new binary without having to use
- <seealso marker="#ErlNifBinary">ErlNifBinary</seealso>. The drawbacks are
- that the binary can not be kept between NIF calls and it can not be
- reallocated.</p><p>Return a pointer to the raw binary data and set
- <c>*termp</c> to the binary term.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_new_map(ErlNifEnv* env)</nametext></name>
- <fsummary>Make an empty map term</fsummary>
- <desc><p>Make an empty map term.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_make_map_put(ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)</nametext></name>
- <fsummary>Insert key-value pair in map</fsummary>
- <desc><p>Make a copy of map <c>map_in</c> and insert <c>key</c> with
- <c>value</c>. If <c>key</c> already exists in <c>map_in</c>, the old
- associated value is replaced by <c>value</c>. If successful set
- <c>*map_out</c> to the new map and return true. Return false if
- <c>map_in</c> is not a map.</p>
- <p>The <c>map_in</c> term must belong to the environment <c>env</c>.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_make_map_update(ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM new_value, ERL_NIF_TERM* map_out)</nametext></name>
- <fsummary>Replace value for key in map</fsummary>
- <desc><p>Make a copy of map <c>map_in</c> and replace the old associated
- value for <c>key</c> with <c>new_value</c>. If successful set
- <c>*map_out</c> to the new map and return true. Return false if
- <c>map_in</c> is not a map or if it does no contain <c>key</c>.</p>
- <p>The <c>map_in</c> term must belong to the environment <c>env</c>.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_make_map_remove(ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM* map_out)</nametext></name>
- <fsummary>Remove key from map</fsummary>
- <desc><p>If map <c>map_in</c> contains <c>key</c>, make a copy of
- <c>map_in</c> in <c>*map_out</c> and remove <c>key</c> and associated
- value. If map <c>map_in</c> does not contain <c>key</c>, set
- <c>*map_out</c> to <c>map_in</c>. Return true for success or false if
- <c>map_in</c> is not a map.</p>
- <p>The <c>map_in</c> term must belong to the environment <c>env</c>.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_pid(ErlNifEnv* env, const ErlNifPid* pid)</nametext></name>
- <fsummary>Make a pid term</fsummary>
- <desc><p>Make a pid term from <c>*pid</c>.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_ref(ErlNifEnv* env)</nametext></name>
- <fsummary>Create a reference</fsummary>
- <desc><p>Create a reference like <seealso marker="erlang#make_ref-0">erlang:make_ref/0</seealso>.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_resource(ErlNifEnv* env, void* obj)</nametext></name>
- <fsummary>Create an opaque handle to a resource object</fsummary>
- <desc><p>Create an opaque handle to a memory managed resource object
- obtained by <seealso marker="#enif_alloc_resource">enif_alloc_resource</seealso>.
- No ownership transfer is done, as the resource object still needs to be released by
- <seealso marker="#enif_release_resource">enif_release_resource</seealso>,
- but note that the call to <c>enif_release_resource</c> can occur
- immediately after obtaining the term from <c>enif_make_resource</c>,
- in which case the resource object will be deallocated when the
- term is garbage collected. See the
- <seealso marker="#enif_resource_example">example of creating and
- returning a resource object</seealso> for more details.</p>
- <p>Note that the only defined behaviour of using a resource term in
- an Erlang program is to store it and send it between processes on the
- same node. Other operations such as matching or <c>term_to_binary</c>
- will have unpredictable (but harmless) results.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_resource_binary(ErlNifEnv* env, void* obj, const void* data, size_t size)</nametext></name>
- <fsummary>Create a custom binary term</fsummary>
- <desc><p>Create a binary term that is memory managed by a resource object
- <c>obj</c> obtained by <seealso marker="#enif_alloc_resource">enif_alloc_resource</seealso>.
- The returned binary term will consist of <c>size</c> bytes pointed to
- by <c>data</c>. This raw binary data must be kept readable and unchanged
- until the destructor of the resource is called. The binary data may be
- stored external to the resource object in which case it is the responsibility
- of the destructor to release the data.</p>
- <p>Several binary terms may be managed by the same resource object. The
- destructor will not be called until the last binary is garbage collected.
- This can be useful as a way to return different parts of a larger binary
- buffer.</p>
- <p>As with <seealso marker="#enif_make_resource">enif_make_resource</seealso>,
- no ownership transfer is done. The resource still needs to be released with
- <seealso marker="#enif_release_resource">enif_release_resource</seealso>.</p>
- </desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_make_reverse_list(ErlNifEnv* env, ERL_NIF_TERM list_in, ERL_NIF_TERM *list_out)</nametext></name>
- <fsummary>Create the reverse of a list</fsummary>
- <desc><p>Set <c>*list_out</c> to the reverse list of the list <c>list_in</c> and return true,
- or return false if <c>list_in</c> is not a list. This function should only be used on
- short lists as a copy will be created of the list which will not be released until after the
- nif returns.</p>
- <p>The <c>list_in</c> term must belong to the environment <c>env</c>.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_string(ErlNifEnv* env, const char* string, ErlNifCharEncoding encoding)</nametext></name>
- <fsummary>Create a string</fsummary>
- <desc><p>Create a list containing the characters of the
- null-terminated string <c>string</c> with encoding <seealso marker="#ErlNifCharEncoding">encoding</seealso>.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_string_len(ErlNifEnv* env, const char* string, size_t len, ErlNifCharEncoding encoding)</nametext></name>
- <fsummary>Create a string</fsummary>
- <desc><p>Create a list containing the characters of the string <c>string</c> with
- length <c>len</c> and encoding <seealso marker="#ErlNifCharEncoding">encoding</seealso>.
- Null-characters are treated as any other characters.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_sub_binary(ErlNifEnv*
- env, ERL_NIF_TERM bin_term, size_t pos, size_t size)</nametext></name>
- <fsummary>Make a subbinary term</fsummary>
- <desc><p>Make a subbinary of binary <c>bin_term</c>, starting at
- zero-based position <c>pos</c> with a length of <c>size</c> bytes.
- <c>bin_term</c> must be a binary or bitstring and
- <c>pos+size</c> must be less or equal to the number of whole
- bytes in <c>bin_term</c>.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple(ErlNifEnv* env, unsigned cnt, ...)</nametext></name>
- <fsummary>Create a tuple term</fsummary>
- <desc><p>Create a tuple term of arity <c>cnt</c>. Expects
- <c>cnt</c> number of arguments (after <c>cnt</c>) of type ERL_NIF_TERM as the
- elements of the tuple.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple1(ErlNifEnv* env, ERL_NIF_TERM e1)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple2(ErlNifEnv* env, ERL_NIF_TERM e1, ERL_NIF_TERM e2)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple3(ErlNifEnv* env, ERL_NIF_TERM e1, ERL_NIF_TERM e2, ERL_NIF_TERM e3)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple4(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e4)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple5(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e5)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple6(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e6)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple7(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e7)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple8(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e8)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple9(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e9)</nametext></name>
- <fsummary>Create a tuple term</fsummary>
- <desc><p>Create a tuple term with length indicated by the
- function name. Prefer these functions (macros) over the variadic
- <c>enif_make_tuple</c> to get a compile time error if the number of
- arguments does not match.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple_from_array(ErlNifEnv* env, const ERL_NIF_TERM arr[], unsigned cnt)</nametext></name>
- <fsummary>Create a tuple term from an array</fsummary>
- <desc><p>Create a tuple containing the elements of array <c>arr</c>
- of length <c>cnt</c>.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_uint(ErlNifEnv* env, unsigned int i)</nametext></name>
- <fsummary>Create an unsigned integer term</fsummary>
- <desc><p>Create an integer term from an <c>unsigned int</c>.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_uint64(ErlNifEnv* env, ErlNifUInt64 i)</nametext></name>
- <fsummary>Create an unsigned integer term</fsummary>
- <desc><p>Create an integer term from an unsigned 64-bit integer.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_ulong(ErlNifEnv* env, unsigned long i)</nametext></name>
- <fsummary>Create an integer term from an unsigned long int</fsummary>
- <desc><p>Create an integer term from an <c>unsigned long int</c>.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_map_iterator_create(ErlNifEnv *env, ERL_NIF_TERM map, ErlNifMapIterator *iter, ErlNifMapIteratorEntry entry)</nametext></name>
- <fsummary>Create a map iterator</fsummary>
- <desc><p>Create an iterator for the map <c>map</c> by initializing the
- structure pointed to by <c>iter</c>. The <c>entry</c> argument determines
- the start position of the iterator: <c>ERL_NIF_MAP_ITERATOR_FIRST</c> or
- <c>ERL_NIF_MAP_ITERATOR_LAST</c>. Return true on success or false if
- <c>map</c> is not a map.</p>
- <p>A map iterator is only useful during the lifetime of the environment
- <c>env</c> that the <c>map</c> belongs to. The iterator must be destroyed by
- calling <seealso marker="#enif_map_iterator_destroy">
- enif_map_iterator_destroy</seealso>.</p>
- <code type="none">
+ <p>Return true if <c>term</c> is an exception.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_is_fun(ErlNifEnv* env, ERL_NIF_TERM
+ term)</nametext></name>
+ <fsummary>Determine if a term is a fun.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c>term</c> is a fun.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_is_identical(ERL_NIF_TERM lhs,
+ ERL_NIF_TERM rhs)</nametext></name>
+ <fsummary>Erlang operator =:=.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if the two terms are identical. Corresponds to
+ the Erlang operators <c>=:=</c> and <c>=/=</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_is_list(ErlNifEnv* env, ERL_NIF_TERM term)</nametext>
+ </name>
+ <fsummary>Determine if a term is a list.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c>term</c> is a list.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_is_map(ErlNifEnv* env, ERL_NIF_TERM
+ term)</nametext></name>
+ <fsummary>Determine if a term is a map.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c>term</c> is a map, otherwise
+ <c>false</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_is_number(ErlNifEnv* env, ERL_NIF_TERM
+ term)</nametext></name>
+ <fsummary>Determine if a term is a number (integer or float).</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c>term</c> is a number.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_is_pid(ErlNifEnv* env, ERL_NIF_TERM term)</nametext>
+ </name>
+ <fsummary>Determine if a term is a pid.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c>term</c> is a pid.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_is_port(ErlNifEnv* env, ERL_NIF_TERM term)</nametext>
+ </name>
+ <fsummary>Determine if a term is a port.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c>term</c> is a port.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_is_port_alive(ErlNifEnv* env,
+ ErlNifPort *port_id)</nametext></name>
+ <fsummary>Determine if a local port is alive.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c>port_id</c> is alive.</p>
+ <p>This function is only thread-safe when the emulator with SMP support
+ is used. It can only be used in a non-SMP emulator from a NIF-calling
+ thread.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_is_process_alive(ErlNifEnv* env,
+ ErlNifPid *pid)</nametext></name>
+ <fsummary>Determine if a local process is alive.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c>pid</c> is alive.</p>
+ <p>This function is only thread-safe when the emulator with SMP support
+ is used. It can only be used in a non-SMP emulator from a NIF-calling
+ thread.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_is_ref(ErlNifEnv* env, ERL_NIF_TERM term)</nametext>
+ </name>
+ <fsummary>Determine if a term is a reference.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c>term</c> is a reference.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_is_tuple(ErlNifEnv* env, ERL_NIF_TERM term)</nametext>
+ </name>
+ <fsummary>Determine if a term is a tuple.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c>term</c> is a tuple.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_keep_resource(void* obj)</nametext>
+ </name>
+ <fsummary>Add a reference to a resource object.</fsummary>
+ <desc>
+ <p>Adds a reference to resource object <c>obj</c> obtained from
+ <seealso marker="#enif_alloc_resource">
+ <c>enif_alloc_resource</c></seealso>. Each call to
+ <c>enif_keep_resource</c> for an object must be balanced by a call to
+ <seealso marker="#enif_release_resource">
+ <c>enif_release_resource</c></seealso>
+ before the object is destructed.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_atom(ErlNifEnv* env, const char* name)</nametext>
+ </name>
+ <fsummary>Create an atom term.</fsummary>
+ <desc>
+ <p>Creates an atom term from the <c>NULL</c>-terminated C-string
+ <c>name</c> with ISO Latin-1 encoding. If the length of <c>name</c>
+ exceeds the maximum length allowed for an atom (255 characters),
+ <c>enif_make_atom</c> invokes <seealso marker="#enif_make_badarg">
+ <c>enif_make_badarg</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_atom_len(ErlNifEnv* env,
+ const char* name, size_t len)</nametext></name>
+ <fsummary>Create an atom term.</fsummary>
+ <desc>
+ <p>Create an atom term from the string <c>name</c> with length
+ <c>len</c>. <c>NULL</c> characters are treated as any other
+ characters. If <c>len</c> exceeds the maximum length
+ allowed for an atom (255 characters), <c>enif_make_atom</c> invokes
+ <seealso marker="#enif_make_badarg">
+ <c>enif_make_badarg</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_badarg(ErlNifEnv* env)</nametext></name>
+ <fsummary>Make a badarg exception.</fsummary>
+ <desc>
+ <p>Makes a <c>badarg</c> exception to be returned from a NIF, and
+ associates it with environment <c>env</c>. Once a NIF or any function
+ it calls invokes <c>enif_make_badarg</c>, the runtime ensures that a
+ <c>badarg</c> exception is raised when the NIF returns, even if the
+ NIF attempts to return a non-exception term instead.</p>
+ <p>The return value from <c>enif_make_badarg</c> can be used only as
+ the return value from the NIF that invoked it (directly or indirectly)
+ or be passed to <seealso marker="#enif_is_exception">
+ <c>enif_is_exception</c></seealso>, but not to any other NIF API
+ function.</p>
+ <p>See also <seealso marker="#enif_has_pending_exception">
+ <c>enif_has_pending_exception</c></seealso> and
+ <seealso marker="#enif_raise_exception">
+ <c>enif_raise_exception</c></seealso>.</p>
+ <note>
+ <p>Before ERTS 7.0 (Erlang/OTP 18), the return value
+ from <c>enif_make_badarg</c> had to be returned from the NIF. This
+ requirement is now lifted as the return value from the NIF is
+ ignored if <c>enif_make_badarg</c> has been invoked.</p>
+ </note>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_binary(ErlNifEnv* env, ErlNifBinary* bin)</nametext>
+ </name>
+ <fsummary>Make a binary term.</fsummary>
+ <desc>
+ <p>Makes a binary term from <c>bin</c>. Any ownership of
+ the binary data is transferred to the created term and
+ <c>bin</c> is to be considered read-only for the rest of the NIF
+ call and then as released.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_copy(ErlNifEnv* dst_env,
+ ERL_NIF_TERM src_term)</nametext></name>
+ <fsummary>Make a copy of a term.</fsummary>
+ <desc>
+ <p>Makes a copy of term <c>src_term</c>. The copy is created in
+ environment <c>dst_env</c>. The source term can be located in any
+ environment.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_double(ErlNifEnv* env, double d)</nametext></name>
+ <fsummary>Create a floating-point term.</fsummary>
+ <desc>
+ <p>Creates a floating-point term from a <c>double</c>. If argument
+ <c>double</c> is not finite or is NaN, <c>enif_make_double</c>
+ invokes <seealso marker="#enif_make_badarg">
+ <c>enif_make_badarg</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_make_existing_atom(ErlNifEnv* env,
+ const char* name, ERL_NIF_TERM* atom, ErlNifCharEncoding
+ encode)</nametext></name>
+ <fsummary>Create an existing atom term.</fsummary>
+ <desc>
+ <p>Tries to create the term of an already existing atom from
+ the <c>NULL</c>-terminated C-string <c>name</c> with encoding
+ <seealso marker="#ErlNifCharEncoding">encode</seealso>.</p>
+ <p>If the atom already exists, this function stores the term in
+ <c>*atom</c> and returns <c>true</c>, otherwise <c>false</c>.
+ Also returns <c>false</c> if the length of <c>name</c> exceeds the
+ maximum length allowed for an atom (255 characters).</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_make_existing_atom_len(ErlNifEnv* env,
+ const char* name, size_t len, ERL_NIF_TERM* atom, ErlNifCharEncoding
+ encoding)</nametext></name>
+ <fsummary>Create an existing atom term.</fsummary>
+ <desc>
+ <p>Tries to create the term of an already existing atom from the
+ string <c>name</c> with length <c>len</c> and encoding
+ <seealso marker="#ErlNifCharEncoding">encode</seealso>. <c>NULL</c>
+ characters are treated as any other characters.</p>
+ <p>If the atom already exists, this function stores the term in
+ <c>*atom</c> and returns <c>true</c>, otherwise <c>false</c>.
+ Also returns <c>false</c> if <c>len</c> exceeds the maximum length
+ allowed for an atom (255 characters).</p>
+ </desc>
+ </func>
+
+ <func><name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_int(ErlNifEnv* env, int i)</nametext></name>
+ <fsummary>Create an integer term.</fsummary>
+ <desc>
+ <p>Creates an integer term.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_int64(ErlNifEnv* env, ErlNifSInt64 i)</nametext>
+ </name>
+ <fsummary>Create an integer term.</fsummary>
+ <desc>
+ <p>Creates an integer term from a signed 64-bit integer.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_list(ErlNifEnv* env, unsigned cnt, ...)</nametext>
+ </name>
+ <fsummary>Create a list term.</fsummary>
+ <desc>
+ <p>Creates an ordinary list term of length <c>cnt</c>. Expects
+ <c>cnt</c> number of arguments (after <c>cnt</c>) of type
+ <c>ERL_NIF_TERM</c> as the elements of the list.</p>
+ <p>Returns an empty list if <c>cnt</c> is 0.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_list1(ErlNifEnv* env, ERL_NIF_TERM e1)</nametext>
+ </name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list2(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ERL_NIF_TERM e2)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list3(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ERL_NIF_TERM e2, ERL_NIF_TERM e3)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list4(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ..., ERL_NIF_TERM e4)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list5(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ..., ERL_NIF_TERM e5)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list6(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ..., ERL_NIF_TERM e6)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list7(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ..., ERL_NIF_TERM e7)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list8(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ..., ERL_NIF_TERM e8)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list9(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ..., ERL_NIF_TERM e9)</nametext></name>
+ <fsummary>Create a list term.</fsummary>
+ <desc>
+ <p>Creates an ordinary list term with length indicated by the
+ function name. Prefer these functions (macros) over the variadic
+ <c>enif_make_list</c> to get a compile-time error if the number of
+ arguments does not match.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list_cell(ErlNifEnv*
+ env, ERL_NIF_TERM head, ERL_NIF_TERM tail)</nametext></name>
+ <fsummary>Create a list cell.</fsummary>
+ <desc>
+ <p>Creates a list cell <c>[head | tail]</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_list_from_array(ErlNifEnv* env, const ERL_NIF_TERM
+ arr[], unsigned cnt)</nametext></name>
+ <fsummary>Create a list term from an array.</fsummary>
+ <desc>
+ <p>Creates an ordinary list containing the elements of array <c>arr</c>
+ of length <c>cnt</c>.</p>
+ <p>Returns an empty list if <c>cnt</c> is 0.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_long(ErlNifEnv* env, long int i)</nametext></name>
+ <fsummary>Create an integer term from a long int.</fsummary>
+ <desc>
+ <p>Creates an integer term from a <c>long int</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_make_map_put(ErlNifEnv* env,
+ ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value,
+ ERL_NIF_TERM* map_out)</nametext></name>
+ <fsummary>Insert key-value pair in map.</fsummary>
+ <desc>
+ <p>Makes a copy of map <c>map_in</c> and inserts <c>key</c> with
+ <c>value</c>. If <c>key</c> already exists in <c>map_in</c>, the old
+ associated value is replaced by <c>value</c>.</p>
+ <p>If successful, this function sets <c>*map_out</c> to the new map and
+ returns <c>true</c>. Returns <c>false</c> if <c>map_in</c> is not a
+ map.</p>
+ <p>The <c>map_in</c> term must belong to environment <c>env</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_make_map_remove(ErlNifEnv* env,
+ ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM* map_out)</nametext>
+ </name>
+ <fsummary>Remove key from map.</fsummary>
+ <desc>
+ <p>If map <c>map_in</c> contains <c>key</c>, this function makes a copy
+ of <c>map_in</c> in <c>*map_out</c>, and removes <c>key</c> and the
+ associated value. If map <c>map_in</c> does not contain <c>key</c>,
+ <c>*map_out</c> is set to <c>map_in</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>map_in</c> is
+ not a map.</p>
+ <p>The <c>map_in</c> term must belong to environment <c>env</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_make_map_update(ErlNifEnv* env,
+ ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM new_value,
+ ERL_NIF_TERM* map_out)</nametext></name>
+ <fsummary>Replace value for key in map.</fsummary>
+ <desc>
+ <p>Makes a copy of map <c>map_in</c> and replace the old associated
+ value for <c>key</c> with <c>new_value</c>.</p>
+ <p>If successful, this function sets <c>*map_out</c> to the new map and
+ returns <c>true</c>. Returns <c>false</c> if <c>map_in</c> is not a
+ map or if it does not contain <c>key</c>.</p>
+ <p>The <c>map_in</c> term must belong to environment <c>env</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>unsigned char *</ret><nametext>enif_make_new_binary(ErlNifEnv*
+ env, size_t size, ERL_NIF_TERM* termp)</nametext></name>
+ <fsummary>Allocate and create a new binary term.</fsummary>
+ <desc>
+ <p>Allocates a binary of size <c>size</c> bytes and creates an owning
+ term. The binary data is mutable until the calling NIF returns.
+ This is a quick way to create a new binary without having to use
+ <seealso marker="#ErlNifBinary"><c>ErlNifBinary</c></seealso>.
+ The drawbacks are that the binary cannot be kept between NIF calls
+ and it cannot be reallocated.</p>
+ <p>Returns a pointer to the raw binary data and sets
+ <c>*termp</c> to the binary term.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_new_map(ErlNifEnv* env)</nametext></name>
+ <fsummary>Make an empty map term.</fsummary>
+ <desc>
+ <p>Makes an empty map term.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_pid(ErlNifEnv* env, const ErlNifPid* pid)</nametext>
+ </name>
+ <fsummary>Make a pid term.</fsummary>
+ <desc>
+ <p>Makes a pid term from <c>*pid</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_ref(ErlNifEnv* env)</nametext></name>
+ <fsummary>Create a reference.</fsummary>
+ <desc>
+ <p>Creates a reference like <seealso marker="erlang#make_ref-0">
+ <c>erlang:make_ref/0</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_resource(ErlNifEnv* env, void* obj)</nametext>
+ </name>
+ <fsummary>Create an opaque handle to a resource object.</fsummary>
+ <desc>
+ <p>Creates an opaque handle to a memory-managed resource object
+ obtained by <seealso marker="#enif_alloc_resource">
+ <c>enif_alloc_resource</c></seealso>. No ownership transfer is done,
+ as the resource object still needs to be released by
+ <seealso marker="#enif_release_resource">
+ <c>enif_release_resource</c></seealso>. However, notice that the call
+ to <c>enif_release_resource</c> can occur immediately after obtaining
+ the term from <c>enif_make_resource</c>, in which case the resource
+ object is deallocated when the term is garbage collected. For more
+ details, see the <seealso marker="#enif_resource_example">example of
+ creating and returning a resource object</seealso> in the User's
+ Guide.</p>
+ <p>Notice that the only defined behavior of using a resource term in
+ an Erlang program is to store it and send it between processes on the
+ same node. Other operations, such as matching or
+ <c>term_to_binary</c>, have unpredictable (but harmless) results.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_resource_binary(ErlNifEnv* env, void* obj, const
+ void* data, size_t size)</nametext></name>
+ <fsummary>Create a custom binary term.</fsummary>
+ <desc>
+ <p>Creates a binary term that is memory-managed by a resource object
+ <c>obj</c> obtained by <seealso marker="#enif_alloc_resource">
+ <c>enif_alloc_resource</c></seealso>. The returned binary term
+ consists of <c>size</c> bytes pointed to by <c>data</c>. This raw
+ binary data must be kept readable and unchanged until the destructor
+ of the resource is called. The binary data can be stored external to
+ the resource object, in which case the destructor is responsible
+ for releasing the data.</p>
+ <p>Several binary terms can be managed by the same resource object. The
+ destructor is not called until the last binary is garbage collected.
+ This can be useful to return different parts of a larger binary
+ buffer.</p>
+ <p>As with <seealso marker="#enif_make_resource">
+ <c>enif_make_resource</c></seealso>, no ownership transfer is done.
+ The resource still needs to be released with
+ <seealso marker="#enif_release_resource">
+ <c>enif_release_resource</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_make_reverse_list(ErlNifEnv* env, ERL_NIF_TERM list_in,
+ ERL_NIF_TERM *list_out)</nametext></name>
+ <fsummary>Create the reverse of a list.</fsummary>
+ <desc>
+ <p>Sets <c>*list_out</c> to the reverse list of the list <c>list_in</c>
+ and returns <c>true</c>, or returns <c>false</c> if <c>list_in</c> is
+ not a list.</p>
+ <p>This function is only to be used on short lists, as a copy is
+ created of the list, which is not released until after the NIF
+ returns.</p>
+ <p>The <c>list_in</c> term must belong to environment <c>env</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_string(ErlNifEnv* env,
+ const char* string, ErlNifCharEncoding encoding)</nametext></name>
+ <fsummary>Create a string.</fsummary>
+ <desc>
+ <p>Creates a list containing the characters of the
+ <c>NULL</c>-terminated string <c>string</c> with encoding
+ <seealso marker="#ErlNifCharEncoding">encoding</seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_string_len(ErlNifEnv*
+ env, const char* string, size_t len, ErlNifCharEncoding
+ encoding)</nametext></name>
+ <fsummary>Create a string.</fsummary>
+ <desc>
+ <p>Creates a list containing the characters of the string <c>string</c>
+ with length <c>len</c> and encoding
+ <seealso marker="#ErlNifCharEncoding">encoding</seealso>.
+ <c>NULL</c> characters are treated as any other characters.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_sub_binary(ErlNifEnv*
+ env, ERL_NIF_TERM bin_term, size_t pos, size_t size)</nametext></name>
+ <fsummary>Make a subbinary term.</fsummary>
+ <desc>
+ <p>Makes a subbinary of binary <c>bin_term</c>, starting at
+ zero-based position <c>pos</c> with a length of <c>size</c> bytes.
+ <c>bin_term</c> must be a binary or bitstring. <c>pos+size</c> must
+ be less or equal to the number of whole bytes in <c>bin_term</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple(ErlNifEnv* env,
+ unsigned cnt, ...)</nametext></name>
+ <fsummary>Creates a tuple term.</fsummary>
+ <desc>
+ <p>Creates a tuple term of arity <c>cnt</c>. Expects <c>cnt</c> number
+ of arguments (after <c>cnt</c>) of type <c>ERL_NIF_TERM</c> as the
+ elements of the tuple.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple1(ErlNifEnv* env,
+ ERL_NIF_TERM e1)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple2(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ERL_NIF_TERM e2)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple3(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ERL_NIF_TERM e2, ERL_NIF_TERM e3)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple4(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ..., ERL_NIF_TERM e4)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple5(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ..., ERL_NIF_TERM e5)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple6(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ..., ERL_NIF_TERM e6)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple7(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ..., ERL_NIF_TERM e7)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple8(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ..., ERL_NIF_TERM e8)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple9(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ..., ERL_NIF_TERM e9)</nametext></name>
+ <fsummary>Create a tuple term.</fsummary>
+ <desc>
+ <p>Creates a tuple term with length indicated by the
+ function name. Prefer these functions (macros) over the variadic
+ <c>enif_make_tuple</c> to get a compile-time error if the number of
+ arguments does not match.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_tuple_from_array(ErlNifEnv* env, const ERL_NIF_TERM
+ arr[], unsigned cnt)</nametext></name>
+ <fsummary>Create a tuple term from an array.</fsummary>
+ <desc>
+ <p>Creates a tuple containing the elements of array <c>arr</c>
+ of length <c>cnt</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_uint(ErlNifEnv* env, unsigned int i)</nametext>
+ </name>
+ <fsummary>Create an unsigned integer term.</fsummary>
+ <desc>
+ <p>Creates an integer term from an <c>unsigned int</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_uint64(ErlNifEnv* env, ErlNifUInt64 i)</nametext>
+ </name>
+ <fsummary>Create an unsigned integer term.</fsummary>
+ <desc>
+ <p>Creates an integer term from an unsigned 64-bit integer.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_ulong(ErlNifEnv* env, unsigned long i)</nametext>
+ </name>
+ <fsummary>Create an integer term from an unsigned long int.</fsummary>
+ <desc>
+ <p>Creates an integer term from an <c>unsigned long int</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_unique_integer(ErlNifEnv
+ *env, ErlNifUniqueInteger properties)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Returns a unique integer with the same properties as specified by
+ <seealso marker="erlang#unique_integer-1">
+ <c>erlang:unique_integer/1</c></seealso>.</p>
+ <p><c>env</c> is the environment to create the integer in.</p>
+ <p><c>ERL_NIF_UNIQUE_POSITIVE</c> and <c>ERL_NIF_UNIQUE_MONOTONIC</c>
+ can be passed as the second argument to change the properties of the
+ integer returned. They can be combined by OR:ing the two values
+ together.</p>
+ <p>See also <seealso marker="#ErlNifUniqueInteger">
+ <c>ErlNifUniqueInteger</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_map_iterator_create(ErlNifEnv *env,
+ ERL_NIF_TERM map, ErlNifMapIterator *iter, ErlNifMapIteratorEntry
+ entry)</nametext></name>
+ <fsummary>Create a map iterator.</fsummary>
+ <desc>
+ <p>Creates an iterator for the map <c>map</c> by initializing the
+ structure pointed to by <c>iter</c>. Argument <c>entry</c> determines
+ the start position of the iterator: <c>ERL_NIF_MAP_ITERATOR_FIRST</c>
+ or <c>ERL_NIF_MAP_ITERATOR_LAST</c>.</p>
+ <p>Returns <c>true</c> on success, or false if <c>map</c> is not a
+ map.</p>
+ <p>A map iterator is only useful during the lifetime of environment
+ <c>env</c> that the <c>map</c> belongs to. The iterator must be
+ destroyed by calling <seealso marker="#enif_map_iterator_destroy">
+ <c>enif_map_iterator_destroy</c></seealso>:</p>
+ <code type="none">
ERL_NIF_TERM key, value;
ErlNifMapIterator iter;
enif_map_iterator_create(env, my_map, &amp;iter, ERL_NIF_MAP_ITERATOR_FIRST);
@@ -1219,388 +2058,714 @@ while (enif_map_iterator_get_pair(env, &amp;iter, &amp;key, &amp;value)) {
do_something(key,value);
enif_map_iterator_next(env, &amp;iter);
}
-enif_map_iterator_destroy(env, &amp;iter);
- </code>
- <note><p>The key-value pairs of a map have no defined iteration
- order. The only guarantee is that the iteration order of a single map
- instance is preserved during the lifetime of the environment that the map
- belongs to.</p>
- </note>
+enif_map_iterator_destroy(env, &amp;iter);</code>
+ <note>
+ <p>The key-value pairs of a map have no defined iteration order.
+ The only guarantee is that the iteration order of a single map
+ instance is preserved during the lifetime of the environment that
+ the map belongs to.</p>
+ </note>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret><nametext>enif_map_iterator_destroy(ErlNifEnv *env,
+ ErlNifMapIterator *iter)</nametext></name>
+ <fsummary>Destroy a map iterator.</fsummary>
+ <desc>
+ <p>Destroys a map iterator created by
+ <seealso marker="#enif_map_iterator_create">
+ <c>enif_map_iterator_create</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_map_iterator_get_pair(ErlNifEnv *env,
+ ErlNifMapIterator *iter, ERL_NIF_TERM *key, ERL_NIF_TERM
+ *value)</nametext></name>
+ <fsummary>Get key and value at current map iterator position.</fsummary>
+ <desc>
+ <p>Gets key and value terms at the current map iterator position.</p>
+ <p>On success, sets <c>*key</c> and <c>*value</c> and returns
+ <c>true</c>. Returns <c>false</c> if the iterator is positioned at
+ head (before first entry) or tail (beyond last entry).</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_map_iterator_is_head(ErlNifEnv *env,
+ ErlNifMapIterator *iter)</nametext></name>
+ <fsummary>Check if map iterator is positioned before first.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if map iterator <c>iter</c> is positioned
+ before the first entry.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_map_iterator_is_tail(ErlNifEnv *env,
+ ErlNifMapIterator *iter)</nametext></name>
+ <fsummary>Check if map iterator is positioned after last.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if map iterator <c>iter</c> is positioned
+ after the last entry.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_map_iterator_next(ErlNifEnv *env,
+ ErlNifMapIterator *iter)</nametext></name>
+ <fsummary>Increment map iterator to point to next entry.</fsummary>
+ <desc>
+ <p>Increments map iterator to point to the next key-value entry.</p>
+ <p>Returns <c>true</c> if the iterator is now positioned at a valid
+ key-value entry, or <c>false</c> if the iterator is positioned at
+ the tail (beyond the last entry).</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_map_iterator_prev(ErlNifEnv *env,
+ ErlNifMapIterator *iter)</nametext></name>
+ <fsummary>Decrement map iterator to point to previous entry.</fsummary>
+ <desc>
+ <p>Decrements map iterator to point to the previous key-value entry.</p>
+ <p>Returns <c>true</c> if the iterator is now positioned at a valid
+ key-value entry, or <c>false</c> if the iterator is positioned at
+ the head (before the first entry).</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ErlNifTime</ret>
+ <nametext>enif_monotonic_time(ErlNifTimeUnit time_unit)</nametext>
+ </name>
+ <fsummary>Get Erlang monotonic time.</fsummary>
+ <desc>
+ <marker id="enif_monotonic_time"></marker>
+ <p>Returns the current
+ <seealso marker="time_correction#Erlang_Monotonic_Time">
+ Erlang monotonic time</seealso>. Notice that it is not uncommon with
+ negative values.</p>
+ <p><c>time_unit</c> is the time unit of the returned value.</p>
+ <p>Returns <c>ERL_NIF_TIME_ERROR</c> if called with an invalid time
+ unit argument, or if called from a thread that is not a scheduler
+ thread.</p>
+ <p>See also <seealso marker="#ErlNifTime"><c>ErlNifTime</c></seealso>
+ and <seealso marker="#ErlNifTimeUnit"><c>ErlNifTimeUnit</c></seealso>.
+ </p>
</desc>
</func>
- <func><name><ret>void</ret><nametext>enif_map_iterator_destroy(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name>
- <fsummary>Destroy a map iterator</fsummary>
- <desc><p>Destroy a map iterator created by
- <seealso marker="#enif_map_iterator_create">enif_map_iterator_create</seealso>.
- </p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_map_iterator_get_pair(ErlNifEnv *env, ErlNifMapIterator *iter, ERL_NIF_TERM *key, ERL_NIF_TERM *value)</nametext></name>
- <fsummary>Get key and value at current map iterator position</fsummary>
- <desc><p>Get key and value terms at current map iterator position.
- On success set <c>*key</c> and <c>*value</c> and return true.
- Return false if the iterator is positioned at head (before first entry)
- or tail (beyond last entry).</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name>
- <fsummary>Check if map iterator is positioned before first</fsummary>
- <desc><p>Return true if map iterator <c>iter</c> is positioned
- before first entry.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name>
- <fsummary>Check if map iterator is positioned after last</fsummary>
- <desc><p>Return true if map iterator <c>iter</c> is positioned
- after last entry.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name>
- <fsummary>Increment map iterator to point to next entry</fsummary>
- <desc><p>Increment map iterator to point to next key-value entry.
- Return true if the iterator is now positioned at a valid key-value entry,
- or false if the iterator is positioned at the tail (beyond the last
- entry).</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name>
- <fsummary>Decrement map iterator to point to previous entry</fsummary>
- <desc><p>Decrement map iterator to point to previous key-value entry.
- Return true if the iterator is now positioned at a valid key-value entry,
- or false if the iterator is positioned at the head (before the first
- entry).</p></desc>
- </func>
- <func><name><ret>ErlNifMutex *</ret><nametext>enif_mutex_create(char *name)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_mutex_create">erl_drv_mutex_create</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_mutex_destroy(ErlNifMutex *mtx)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_mutex_destroy">erl_drv_mutex_destroy</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_mutex_lock(ErlNifMutex *mtx)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_mutex_lock">erl_drv_mutex_lock</seealso>.
- </p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_mutex_trylock(ErlNifMutex *mtx)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_mutex_trylock">erl_drv_mutex_trylock</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_mutex_unlock(ErlNifMutex *mtx)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_mutex_unlock">erl_drv_mutex_unlock</seealso>.
- </p></desc>
- </func>
- <func><name><ret>ErlNifResourceType *</ret><nametext>enif_open_resource_type(ErlNifEnv* env,
- const char* module_str, const char* name,
- ErlNifResourceDtor* dtor, ErlNifResourceFlags flags, ErlNifResourceFlags* tried)</nametext></name>
- <fsummary>Create or takeover a resource type</fsummary>
- <desc><p>Create or takeover a resource type identified by the string
- <c>name</c> and give it the destructor function pointed to by <seealso marker="#ErlNifResourceDtor">dtor</seealso>.
- Argument <c>flags</c> can have the following values:</p>
- <taglist>
- <tag><c>ERL_NIF_RT_CREATE</c></tag>
- <item>Create a new resource type that does not already exist.</item>
- <tag><c>ERL_NIF_RT_TAKEOVER</c></tag>
- <item>Open an existing resource type and take over ownership of all its instances.
- The supplied destructor <c>dtor</c> will be called both for existing instances
- as well as new instances not yet created by the calling NIF library.</item>
- </taglist>
- <p>The two flag values can be combined with bitwise-or. The name of the
- resource type is local to the calling module. Argument <c>module_str</c>
- is not (yet) used and must be NULL. The <c>dtor</c> may be <c>NULL</c>
- in case no destructor is needed.</p>
- <p>On success, return a pointer to the resource type and <c>*tried</c>
- will be set to either <c>ERL_NIF_RT_CREATE</c> or
- <c>ERL_NIF_RT_TAKEOVER</c> to indicate what was actually done.
- On failure, return <c>NULL</c> and set <c>*tried</c> to <c>flags</c>.
- It is allowed to set <c>tried</c> to <c>NULL</c>.</p>
- <p>Note that <c>enif_open_resource_type</c> is only allowed to be called in the three callbacks
- <seealso marker="#load">load</seealso>, <seealso marker="#reload">reload</seealso>
- and <seealso marker="#upgrade">upgrade</seealso>.</p>
- </desc>
- </func>
- <func><name><ret>void *</ret><nametext>enif_priv_data(ErlNifEnv* env)</nametext></name>
- <fsummary>Get the private data of a NIF library</fsummary>
- <desc><p>Return the pointer to the private data that was set by <c>load</c>,
- <c>reload</c> or <c>upgrade</c>.</p>
- <p>Was previously named <c>enif_get_data</c>.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_raise_exception(ErlNifEnv* env, ERL_NIF_TERM reason)</nametext></name>
- <fsummary>Raise a NIF error exception</fsummary>
- <desc><p>Create an error exception with the term <c>reason</c> to be returned from a NIF,
- and associate it with the environment <c>env</c>. Once a NIF or any function it calls
- invokes <c>enif_raise_exception</c>, the runtime ensures that the exception it creates
- is raised when the NIF returns, even if the NIF attempts to return a non-exception
- term instead. The return value from <c>enif_raise_exception</c> may be used only as
- the return value from the NIF that invoked it (directly or indirectly) or be passed
- to <seealso marker="#enif_is_exception">enif_is_exception</seealso>, but
- not to any other NIF API function.</p>
- <p>See also: <seealso marker="#enif_has_pending_exception">enif_has_pending_exception</seealso>
- and <seealso marker="#enif_make_badarg">enif_make_badarg</seealso>.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_realloc_binary(ErlNifBinary* bin, size_t size)</nametext></name>
- <fsummary>Change the size of a binary</fsummary>
- <desc><p>Change the size of a binary <c>bin</c>. The source binary
- may be read-only, in which case it will be left untouched and
- a mutable copy is allocated and assigned to <c>*bin</c>. Return true on success,
- false if memory allocation failed.</p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_release_binary(ErlNifBinary* bin)</nametext></name>
- <fsummary>Release a binary</fsummary>
- <desc><p>Release a binary obtained from <c>enif_alloc_binary</c>.</p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_release_resource(void* obj)</nametext></name>
- <fsummary>Release a resource object</fsummary>
- <desc><p>Remove a reference to resource object <c>obj</c>obtained from
- <seealso marker="#enif_alloc_resource">enif_alloc_resource</seealso>.
- The resource object will be destructed when the last reference is removed.
- Each call to <c>enif_release_resource</c> must correspond to a previous
- call to <c>enif_alloc_resource</c> or
- <seealso marker="#enif_keep_resource">enif_keep_resource</seealso>.
- References made by <seealso marker="#enif_make_resource">enif_make_resource</seealso>
- can only be removed by the garbage collector.</p></desc>
- </func>
- <func><name><ret>ErlNifRWLock *</ret><nametext>enif_rwlock_create(char *name)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_create">erl_drv_rwlock_create</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_rwlock_destroy(ErlNifRWLock *rwlck)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_destroy">erl_drv_rwlock_destroy</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_rwlock_rlock(ErlNifRWLock *rwlck)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_rlock">erl_drv_rwlock_rlock</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_rwlock_runlock(ErlNifRWLock *rwlck)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_runlock">erl_drv_rwlock_runlock</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_rwlock_rwlock(ErlNifRWLock *rwlck)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_rwlock">erl_drv_rwlock_rwlock</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_rwlock_rwunlock(ErlNifRWLock *rwlck)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_rwunlock">erl_drv_rwlock_rwunlock</seealso>.
- </p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_rwlock_tryrlock(ErlNifRWLock *rwlck)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_tryrlock">erl_drv_rwlock_tryrlock</seealso>.
- </p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_rwlock_tryrwlock(ErlNifRWLock *rwlck)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_tryrwlock">erl_drv_rwlock_tryrwlock</seealso>.
- </p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_schedule_nif(ErlNifEnv* env, const char* fun_name, int flags, ERL_NIF_TERM (*fp)(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]), int argc, const ERL_NIF_TERM argv[])</nametext></name>
- <fsummary>Schedule a NIF for execution</fsummary>
- <desc>
- <p>Schedule NIF <c>fp</c> to execute. This function allows an application to break up long-running
- work into multiple regular NIF calls or to schedule a <seealso marker="#dirty_nifs">dirty NIF</seealso>
- to execute on a dirty scheduler thread (<em>note that the dirty NIF functionality described here is
- experimental</em> and that you have to enable support for dirty schedulers when building OTP in
- order to try the functionality out).</p>
- <p>The <c>fun_name</c> argument provides a name for the NIF being scheduled for execution. If it cannot
- be converted to an atom, <c>enif_schedule_nif</c> returns a <c>badarg</c> exception.</p>
- <p>The <c>flags</c> argument must be set to 0 for a regular NIF, or if the emulator was built the
- experimental dirty scheduler support enabled, <c>flags</c> can be set to either <c>ERL_NIF_DIRTY_JOB_CPU_BOUND</c>
- if the job is expected to be primarily CPU-bound, or <c>ERL_NIF_DIRTY_JOB_IO_BOUND</c> for jobs that will
- be I/O-bound. If dirty scheduler threads are not available in the emulator, a try to schedule such a job
- will result in a <c>badarg</c> exception.</p>
-
- <p>The <c>argc</c> and <c>argv</c> arguments can either be the originals passed into the calling NIF, or
- they can be values created by the calling NIF.</p>
- <p>The calling NIF must use the return value of <c>enif_schedule_nif</c> as its own return value.</p>
- <p>Be aware that <c>enif_schedule_nif</c>, as its name implies, only schedules the
- NIF for future execution. The calling NIF does not block waiting for the scheduled NIF to
- execute and return, which means that the calling NIF can't expect to receive the scheduled NIF
+
+ <func>
+ <name><ret>ErlNifMutex *</ret>
+ <nametext>enif_mutex_create(char *name)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_mutex_create">
+ <c>erl_drv_mutex_create</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_mutex_destroy(ErlNifMutex *mtx)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_mutex_destroy">
+ <c>erl_drv_mutex_destroy</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_mutex_lock(ErlNifMutex *mtx)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_mutex_lock">
+ <c>erl_drv_mutex_lock</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_mutex_trylock(ErlNifMutex *mtx)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_mutex_trylock">
+ <c>erl_drv_mutex_trylock</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_mutex_unlock(ErlNifMutex *mtx)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_mutex_unlock">
+ <c>erl_drv_mutex_unlock</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_now_time(ErlNifEnv *env)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Returns an <seealso marker="erlang#now-0">
+ <c>erlang:now()</c></seealso> time stamp.</p>
+ <p><em>This function is deprecated.</em></p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ErlNifResourceType *</ret>
+ <nametext>enif_open_resource_type(ErlNifEnv* env, const char*
+ module_str, const char* name, ErlNifResourceDtor* dtor,
+ ErlNifResourceFlags flags, ErlNifResourceFlags* tried)</nametext>
+ </name>
+ <fsummary>Create or takeover a resource type.</fsummary>
+ <desc>
+ <p>Creates or takes over a resource type identified by the string
+ <c>name</c> and gives it the destructor function pointed to by
+ <seealso marker="#ErlNifResourceDtor"><c>dtor</c></seealso>.
+ Argument <c>flags</c> can have the following values:</p>
+ <taglist>
+ <tag><c>ERL_NIF_RT_CREATE</c></tag>
+ <item>Creates a new resource type that does not already exist.</item>
+ <tag><c>ERL_NIF_RT_TAKEOVER</c></tag>
+ <item>Opens an existing resource type and takes over ownership of all
+ its instances. The supplied destructor <c>dtor</c> is called both
+ for existing instances and new instances not yet created by the
+ calling NIF library.</item>
+ </taglist>
+ <p>The two flag values can be combined with bitwise OR. The resource
+ type name is local to the calling module. Argument <c>module_str</c>
+ is not (yet) used and must be <c>NULL</c>. <c>dtor</c> can be
+ <c>NULL</c> if no destructor is needed.</p>
+ <p>On success, the function returns a pointer to the resource type and
+ <c>*tried</c> is set to either <c>ERL_NIF_RT_CREATE</c> or
+ <c>ERL_NIF_RT_TAKEOVER</c> to indicate what was done. On failure,
+ returns <c>NULL</c> and sets <c>*tried</c> to <c>flags</c>.
+ It is allowed to set <c>tried</c> to <c>NULL</c>.</p>
+ <p>Notice that <c>enif_open_resource_type</c> is only allowed to be
+ called in the three callbacks
+ <seealso marker="#load"><c>load</c></seealso>,
+ <seealso marker="#reload"><c>reload</c></seealso>, and
+ <seealso marker="#upgrade"><c>upgrade</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_port_command(ErlNifEnv* env, const
+ ErlNifPort* to_port, ErlNifEnv *msg_env, ERL_NIF_TERM msg)</nametext>
+ </name>
+ <fsummary>Send a port_command to to_port.</fsummary>
+ <desc>
+ <p>Works as <seealso marker="erlang#port_command-2">
+ <c>erlang:port_command/2</c></seealso>,
+ except that it is always completely asynchronous.</p>
+ <taglist>
+ <tag><c>env</c></tag>
+ <item>The environment of the calling process. Must not be
+ <c>NULL</c>.</item>
+ <tag><c>*to_port</c></tag>
+ <item>The port ID of the receiving port. The port ID is to refer to a
+ port on the local node.</item>
+ <tag><c>msg_env</c></tag>
+ <item>The environment of the message term. Can be a process-independent
+ environment allocated with <seealso marker="#enif_alloc_env">
+ <c>enif_alloc_env</c></seealso> or <c>NULL</c>.</item>
+ <tag><c>msg</c></tag>
+ <item>The message term to send. The same limitations apply as on the
+ payload to <seealso marker="erlang#port_command-2">
+ <c>erlang:port_command/2</c></seealso>.</item>
+ </taglist>
+ <p>Using a <c>msg_env</c> of <c>NULL</c> is an optimization, which
+ groups together calls to <c>enif_alloc_env</c>, <c>enif_make_copy</c>,
+ <c>enif_port_command</c>, and <c>enif_free_env</c> into one call.
+ This optimization is only useful when a majority of the terms are to
+ be copied from <c>env</c> to <c>msg_env</c>.</p>
+ <p>Returns <c>true</c> if the command is successfully sent. Returns
+ <c>false</c> if the command fails, for example:</p>
+ <list type="bulleted">
+ <item><c>*to_port</c> does not refer to a local port.</item>
+ <item>The currently executing process (that is, the sender) is not
+ alive.</item>
+ <item><c>msg</c> is invalid.</item>
+ </list>
+ <p>See also <seealso marker="#enif_get_local_port">
+ <c>enif_get_local_port</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void *</ret>
+ <nametext>enif_priv_data(ErlNifEnv* env)</nametext></name>
+ <fsummary>Get the private data of a NIF library.</fsummary>
+ <desc>
+ <p>Returns the pointer to the private data that was set by
+ <seealso marker="#load"><c>load</c></seealso>,
+ <seealso marker="#reload"><c>reload</c></seealso>, or
+ <seealso marker="#upgrade"><c>upgrade</c></seealso>.</p>
+ <p>Was previously named <c>enif_get_data</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_raise_exception(ErlNifEnv*
+ env, ERL_NIF_TERM reason)</nametext></name>
+ <fsummary>Raise a NIF error exception.</fsummary>
+ <desc>
+ <p>Creates an error exception with the term <c>reason</c> to be
+ returned from a NIF, and associates it with environment <c>env</c>.
+ Once a NIF or any function it calls invokes
+ <c>enif_raise_exception</c>, the runtime ensures that the exception
+ it creates is raised when the NIF returns, even if the NIF attempts
+ to return a non-exception term instead.</p>
+ <p>The return value from <c>enif_raise_exception</c> can only be used
+ as the return value from the NIF that invoked it (directly or
+ indirectly) or be passed to <seealso marker="#enif_is_exception">
+ <c>enif_is_exception</c></seealso>, but not to any other NIF API
+ function.</p>
+ <p>See also <seealso marker="#enif_has_pending_exception">
+ <c>enif_has_pending_exception</c></seealso> and
+ <seealso marker="#enif_make_badarg">
+ <c>enif_make_badarg</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_realloc_binary(ErlNifBinary* bin, size_t size)</nametext>
+ </name>
+ <fsummary>Change the size of a binary.</fsummary>
+ <desc>
+ <p>Changes the size of a binary <c>bin</c>. The source binary
+ can be read-only, in which case it is left untouched and
+ a mutable copy is allocated and assigned to <c>*bin</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if memory allocation
+ failed.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_release_binary(ErlNifBinary* bin)</nametext></name>
+ <fsummary>Release a binary.</fsummary>
+ <desc>
+ <p>Releases a binary obtained from
+ <seealso marker="#enif_alloc_binary">
+ <c>enif_alloc_binary</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_release_resource(void* obj)</nametext></name>
+ <fsummary>Release a resource object.</fsummary>
+ <desc>
+ <p>Removes a reference to resource object <c>obj</c>obtained from
+ <seealso marker="#enif_alloc_resource">
+ <c>enif_alloc_resource</c></seealso>.
+ The resource object is destructed when the last reference is removed.
+ Each call to <c>enif_release_resource</c> must correspond to a
+ previous call to <c>enif_alloc_resource</c> or
+ <seealso marker="#enif_keep_resource">
+ <c>enif_keep_resource</c></seealso>.
+ References made by <seealso marker="#enif_make_resource">
+ <c>enif_make_resource</c></seealso>
+ can only be removed by the garbage collector.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ErlNifRWLock *</ret>
+ <nametext>enif_rwlock_create(char *name)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_rwlock_create">
+ <c>erl_drv_rwlock_create</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_rwlock_destroy(ErlNifRWLock *rwlck)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_rwlock_destroy">
+ <c>erl_drv_rwlock_destroy</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_rwlock_rlock(ErlNifRWLock *rwlck)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_rwlock_rlock">
+ <c>erl_drv_rwlock_rlock</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_rwlock_runlock(ErlNifRWLock *rwlck)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_rwlock_runlock">
+ <c>erl_drv_rwlock_runlock</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_rwlock_rwlock(ErlNifRWLock *rwlck)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_rwlock_rwlock">
+ <c>erl_drv_rwlock_rwlock</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_rwlock_rwunlock(ErlNifRWLock *rwlck)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_rwlock_rwunlock">
+ <c>erl_drv_rwlock_rwunlock</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_rwlock_tryrlock(ErlNifRWLock *rwlck)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_rwlock_tryrlock">
+ <c>erl_drv_rwlock_tryrlock</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_rwlock_tryrwlock(ErlNifRWLock *rwlck)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_rwlock_tryrwlock">
+ <c>erl_drv_rwlock_tryrwlock</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_schedule_nif(ErlNifEnv* env,
+ const char* fun_name, int flags, ERL_NIF_TERM (*fp)(ErlNifEnv* env, int
+ argc, const ERL_NIF_TERM argv[]), int argc, const ERL_NIF_TERM
+ argv[])</nametext></name>
+ <fsummary>Schedule a NIF for execution.</fsummary>
+ <desc>
+ <p>Schedules NIF <c>fp</c> to execute. This function allows an
+ application to break up long-running work into multiple regular NIF
+ calls or to schedule a <seealso marker="#dirty_nifs">
+ dirty NIF</seealso> to execute on a dirty scheduler thread.</p>
+ <p><em>The dirty NIF functionality described here is
+ experimental.</em> You have to enable support for dirty
+ schedulers when building OTP to try out the functionality.</p>
+ <taglist>
+ <tag><c>fun_name</c></tag>
+ <item>
+ <p>Provides a name for the NIF that is scheduled for execution.
+ If it cannot be converted to an atom, <c>enif_schedule_nif</c>
+ returns a <c>badarg</c> exception.</p>
+ </item>
+ <tag><c>flags</c></tag>
+ <item>
+ <p>Must be set to <c>0</c> for a regular NIF. If the emulator was
+ built with the experimental dirty scheduler support enabled,
+ <c>flags</c> can be set to either
+ <c>ERL_NIF_DIRTY_JOB_CPU_BOUND</c> if the job is expected to be
+ CPU-bound, or <c>ERL_NIF_DIRTY_JOB_IO_BOUND</c> for
+ jobs that will be I/O-bound. If dirty scheduler threads are not
+ available in the emulator, an attempt to schedule such a job
+ results in a <c>badarg</c> exception.</p>
+ </item>
+ <tag><c>argc</c> and <c>argv</c></tag>
+ <item>
+ <p>Can either be the originals passed into the calling NIF,
+ or can be values created by the calling NIF.</p>
+ </item>
+ </taglist>
+ <p>The calling NIF must use the return value of
+ <c>enif_schedule_nif</c> as its own return value.</p>
+ <p>Be aware that <c>enif_schedule_nif</c>, as its name implies, only
+ schedules the NIF for future execution. The calling NIF does not
+ block waiting for the scheduled NIF to execute and return. This means
+ that the calling NIF cannot expect to receive the scheduled NIF
return value and use it for further operations.</p>
</desc>
</func>
- <func><name><ret>ErlNifPid *</ret><nametext>enif_self(ErlNifEnv* caller_env, ErlNifPid* pid)</nametext></name>
- <fsummary>Get the pid of the calling process</fsummary>
- <desc><p>Initialize the pid variable <c>*pid</c> to represent the
- calling process. Return <c>pid</c>.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_send(ErlNifEnv* env, ErlNifPid* to_pid, ErlNifEnv* msg_env, ERL_NIF_TERM msg)</nametext></name>
- <fsummary>Send a message to a process</fsummary>
- <desc><p>Send a message to a process.</p>
- <taglist>
- <tag><c>env</c></tag>
- <item>The environment of the calling process. Must be NULL if and
- only if calling from a created thread.</item>
- <tag><c>*to_pid</c></tag>
- <item>The pid of the receiving process. The pid should refer to a process on the local node.</item>
- <tag><c>msg_env</c></tag>
- <item>The environment of the message term. Must be a process
- independent environment allocated with
- <seealso marker="#enif_alloc_env">enif_alloc_env</seealso>.</item>
- <tag><c>msg</c></tag>
- <item>The message term to send.</item>
- </taglist>
- <p>Return true on success, or false if <c>*to_pid</c> does not refer to an alive local process.</p>
- <p>The message environment <c>msg_env</c> with all its terms (including
- <c>msg</c>) will be invalidated by a successful call to <c>enif_send</c>. The environment
- should either be freed with <seealso marker="#enif_free_env">enif_free_env</seealso>
- of cleared for reuse with <seealso marker="#enif_clear_env">enif_clear_env</seealso>.</p>
- <p>This function is only thread-safe when the emulator with SMP support is used.
- It can only be used in a non-SMP emulator from a NIF-calling thread.</p>
- </desc>
- </func>
- <func><name><ret>unsigned</ret><nametext>enif_sizeof_resource(void* obj)</nametext></name>
- <fsummary>Get the byte size of a resource object</fsummary>
- <desc><p>Get the byte size of a resource object <c>obj</c> obtained by
- <seealso marker="#enif_alloc_resource">enif_alloc_resource</seealso>.</p></desc>
- </func>
- <func>
- <name><ret>void</ret><nametext>enif_system_info(ErlNifSysInfo *sys_info_ptr, size_t size)</nametext></name>
- <fsummary>Get information about the Erlang runtime system</fsummary>
- <desc><p>Same as <seealso marker="erl_driver#driver_system_info">driver_system_info</seealso>.
- </p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_thread_create(char *name,ErlNifTid *tid,void * (*func)(void *),void *args,ErlNifThreadOpts *opts)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_thread_create">erl_drv_thread_create</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_thread_exit(void *resp)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_thread_exit">erl_drv_thread_exit</seealso>.
- </p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_thread_join(ErlNifTid, void **respp)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_thread_join">erl_drv_thread_join </seealso>.
- </p></desc>
- </func>
- <func><name><ret>ErlNifThreadOpts *</ret><nametext>enif_thread_opts_create(char *name)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_thread_opts_create">erl_drv_thread_opts_create</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_thread_opts_destroy(ErlNifThreadOpts *opts)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_thread_opts_destroy">erl_drv_thread_opts_destroy</seealso>.
- </p></desc>
- </func>
- <func><name><ret>ErlNifTid</ret><nametext>enif_thread_self(void)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_thread_self">erl_drv_thread_self</seealso>.
- </p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_tsd_key_create(char *name, ErlNifTSDKey *key)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_tsd_key_create">erl_drv_tsd_key_create</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_tsd_key_destroy(ErlNifTSDKey key)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_tsd_key_destroy">erl_drv_tsd_key_destroy</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void *</ret><nametext>enif_tsd_get(ErlNifTSDKey key)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_tsd_get">erl_drv_tsd_get</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_tsd_set(ErlNifTSDKey key, void *data)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_tsd_set">erl_drv_tsd_set</seealso>.
- </p></desc>
- </func>
-
-
- <func>
- <name><ret>ErlNifTime</ret><nametext>enif_monotonic_time(ErlNifTimeUnit time_unit)</nametext></name>
- <fsummary>Get Erlang Monotonic Time</fsummary>
- <desc>
- <marker id="enif_monotonic_time"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>time_unit</c></tag>
- <item>Time unit of returned value.</item>
- </taglist>
- <p>
- Returns
- <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
- monotonic time</seealso>. Note that it is not uncommon with
- negative values.
- </p>
- <p>Returns <c>ERL_NIF_TIME_ERROR</c> if called with an invalid
- time unit argument, or if called from a thread that is not a
- scheduler thread.</p>
- <p>See also:</p>
- <list>
- <item><seealso marker="#ErlNifTime"><c>ErlNifTime</c></seealso></item>
- <item><seealso marker="#ErlNifTimeUnit"><c>ErlNifTimeUnit</c></seealso></item>
- </list>
- </desc>
- </func>
-
- <func>
- <name><ret>ErlNifTime</ret><nametext>enif_time_offset(ErlNifTimeUnit time_unit)</nametext></name>
- <fsummary>Get current Time Offset</fsummary>
- <desc>
- <marker id="enif_time_offset"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>time_unit</c></tag>
- <item>Time unit of returned value.</item>
- </taglist>
- <p>Returns the current time offset between
- <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang monotonic time</seealso>
- and
- <seealso marker="time_correction#Erlang_System_Time">Erlang system time</seealso>
- converted into the <c>time_unit</c> passed as argument.</p>
- <p>Returns <c>ERL_NIF_TIME_ERROR</c> if called with an invalid
- time unit argument, or if called from a thread that is not a
- scheduler thread.</p>
- <p>See also:</p>
- <list>
- <item><seealso marker="#ErlNifTime"><c>ErlNifTime</c></seealso></item>
- <item><seealso marker="#ErlNifTimeUnit"><c>ErlNifTimeUnit</c></seealso></item>
- </list>
- </desc>
- </func>
-
- <func>
- <name><ret>ErlNifTime</ret><nametext>enif_convert_time_unit(ErlNifTime val, ErlNifTimeUnit from, ErlNifTimeUnit to)</nametext></name>
- <fsummary>Convert time unit of a time value</fsummary>
- <desc>
- <marker id="enif_convert_time_unit"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>val</c></tag>
- <item>Value to convert time unit for.</item>
- <tag><c>from</c></tag>
- <item>Time unit of <c>val</c>.</item>
- <tag><c>to</c></tag>
- <item>Time unit of returned value.</item>
- </taglist>
- <p>Converts the <c>val</c> value of time unit <c>from</c> to
- the corresponding value of time unit <c>to</c>. The result is
- rounded using the floor function.</p>
- <p>Returns <c>ERL_NIF_TIME_ERROR</c> if called with an invalid
- time unit argument.</p>
- <p>See also:</p>
- <list>
- <item><seealso marker="#ErlNifTime"><c>ErlNifTime</c></seealso></item>
- <item><seealso marker="#ErlNifTimeUnit"><c>ErlNifTimeUnit</c></seealso></item>
- </list>
+
+ <func>
+ <name><ret>ErlNifPid *</ret>
+ <nametext>enif_self(ErlNifEnv* caller_env, ErlNifPid* pid)</nametext>
+ </name>
+ <fsummary>Get the pid of the calling process.</fsummary>
+ <desc>
+ <p>Initializes the pid variable <c>*pid</c> to represent the
+ calling process.</p>
+ <p>Returns <c>pid</c>.</p>
</desc>
</func>
+ <func>
+ <name><ret>int</ret><nametext>enif_send(ErlNifEnv* env, ErlNifPid* to_pid,
+ ErlNifEnv* msg_env, ERL_NIF_TERM msg)</nametext></name>
+ <fsummary>Send a message to a process.</fsummary>
+ <desc>
+ <p>Sends a message to a process.</p>
+ <taglist>
+ <tag><c>env</c></tag>
+ <item>The environment of the calling process. Must be <c>NULL</c>
+ only if calling from a created thread.</item>
+ <tag><c>*to_pid</c></tag>
+ <item>The pid of the receiving process. The pid is to refer to a
+ process on the local node.</item>
+ <tag><c>msg_env</c></tag>
+ <item>The environment of the message term. Must be a
+ process-independent environment allocated with
+ <seealso marker="#enif_alloc_env"><c>enif_alloc_env</c></seealso>
+ or NULL.</item>
+ <tag><c>msg</c></tag>
+ <item>The message term to send.</item>
+ </taglist>
+ <p>Returns <c>true</c> if the message is successfully sent. Returns
+ <c>false</c> if the send operation fails, that is:</p>
+ <list type="bulleted">
+ <item><c>*to_pid</c> does not refer to an alive local process.</item>
+ <item>The currently executing process (that is, the sender) is not
+ alive.</item>
+ </list>
+ <p>The message environment <c>msg_env</c> with all its terms (including
+ <c>msg</c>) is invalidated by a successful call to <c>enif_send</c>.
+ The environment is to either be freed with
+ <seealso marker="#enif_free_env">
+ <c>enif_free_env</c></seealso> of cleared for reuse with
+ <seealso marker="#enif_clear_env"><c>enif_clear_env</c></seealso>.</p>
+ <p>If <c>msg_env</c> is set to <c>NULL</c>, the <c>msg</c> term is
+ copied and the original term and its environemt is still valid after
+ the call.</p>
+ <p>This function is only thread-safe when the emulator with SMP support
+ is used. It can only be used in a non-SMP emulator from a NIF-calling
+ thread.</p>
+ <note>
+ <p>Passing <c>msg_env</c> as <c>NULL</c> is only supported as from
+ ERTS 8.0 (Erlang/OTP 19).</p>
+ </note>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>unsigned</ret>
+ <nametext>enif_sizeof_resource(void* obj)</nametext></name>
+ <fsummary>Get the byte size of a resource object.</fsummary>
+ <desc>
+ <p>Gets the byte size of resource object <c>obj</c> obtained by
+ <seealso marker="#enif_alloc_resource">
+ <c>enif_alloc_resource</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_snprintf(char *str, size_t size, const
+ char *format, ...)</nametext></name>
+ <fsummary>Format strings and Erlang terms.</fsummary>
+ <desc>
+ <p>Similar to <c>snprintf</c> but this format string also accepts
+ <c>"%T"</c>, which formats Erlang terms.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret><nametext>enif_system_info(ErlNifSysInfo
+ *sys_info_ptr, size_t size)</nametext></name>
+ <fsummary>Get information about the Erlang runtime system.</fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#driver_system_info">
+ <c>driver_system_info</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_term_to_binary(ErlNifEnv *env,
+ ERL_NIF_TERM term, ErlNifBinary *bin)</nametext></name>
+ <fsummary>Convert a term to the external format.</fsummary>
+ <desc>
+ <p>Allocates a new binary with <seealso marker="#enif_alloc_binary">
+ <c>enif_alloc_binary</c></seealso> and stores the result of encoding
+ <c>term</c> according to the Erlang external term format.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if the allocation
+ fails.</p>
+ <p>See also <seealso marker="erlang#term_to_binary-1">
+ <c>erlang:term_to_binary/1</c></seealso> and
+ <seealso marker="#enif_binary_to_term">
+ <c>enif_binary_to_term</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_thread_create(char *name,ErlNifTid
+ *tid,void * (*func)(void *),void *args,ErlNifThreadOpts
+ *opts)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_thread_create">
+ <c>erl_drv_thread_create</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_thread_exit(void *resp)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_thread_exit">
+ <c>erl_drv_thread_exit</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_thread_join(ErlNifTid, void **respp)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_thread_join">
+ <c>erl_drv_thread_join</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ErlNifThreadOpts *</ret>
+ <nametext>enif_thread_opts_create(char *name)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_thread_opts_create">
+ <c>erl_drv_thread_opts_create</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_thread_opts_destroy(ErlNifThreadOpts *opts)</nametext>
+ </name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_thread_opts_destroy">
+ <c>erl_drv_thread_opts_destroy</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ErlNifTid</ret>
+ <nametext>enif_thread_self(void)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_thread_self">
+ <c>erl_drv_thread_self</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_thread_type(void)</nametext></name>
+ <fsummary>Determine type of current thread</fsummary>
+ <desc>
+ <p>Determine the type of currently executing thread. A positive value
+ indicates a scheduler thread while a negative value or zero indicates
+ another type of thread. Currently the following specific types exist
+ (which may be extended in the future):</p>
+ <taglist>
+ <tag><c>ERL_NIF_THR_UNDEFINED</c></tag>
+ <item><p>Undefined thread that is not a scheduler thread.</p></item>
+ <tag><c>ERL_NIF_THR_NORMAL_SCHEDULER</c></tag>
+ <item><p>A normal scheduler thread.</p></item>
+ <tag><c>ERL_NIF_THR_DIRTY_CPU_SCHEDULER</c></tag>
+ <item><p>A dirty CPU scheduler thread.</p></item>
+ <tag><c>ERL_NIF_THR_DIRTY_IO_SCHEDULER</c></tag>
+ <item><p>A dirty I/O scheduler thread.</p></item>
+ </taglist>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ErlNifTime</ret>
+ <nametext>enif_time_offset(ErlNifTimeUnit time_unit)</nametext></name>
+ <fsummary>Get current time offset.</fsummary>
+ <desc>
+ <marker id="enif_time_offset"></marker>
+ <p>Returns the current time offset between
+ <seealso marker="time_correction#Erlang_Monotonic_Time">
+ Erlang monotonic time</seealso> and
+ <seealso marker="time_correction#Erlang_System_Time">
+ Erlang system time</seealso>
+ converted into the <c>time_unit</c> passed as argument.</p>
+ <p><c>time_unit</c> is the time unit of the returned value.</p>
+ <p>Returns <c>ERL_NIF_TIME_ERROR</c> if called with an invalid
+ time unit argument or if called from a thread that is not a
+ scheduler thread.</p>
+ <p>See also <seealso marker="#ErlNifTime"><c>ErlNifTime</c></seealso>
+ and
+ <seealso marker="#ErlNifTimeUnit"><c>ErlNifTimeUnit</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void *</ret>
+ <nametext>enif_tsd_get(ErlNifTSDKey key)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_tsd_get">
+ <c>erl_drv_tsd_get</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_tsd_key_create(char *name, ErlNifTSDKey *key)</nametext>
+ </name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_tsd_key_create">
+ <c>erl_drv_tsd_key_create</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_tsd_key_destroy(ErlNifTSDKey key)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_tsd_key_destroy">
+ <c>erl_drv_tsd_key_destroy</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_tsd_set(ErlNifTSDKey key, void *data)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_tsd_set">
+ <c>erl_drv_tsd_set</c></seealso>.</p>
+ </desc>
+ </func>
</funcs>
+
<section>
- <title>SEE ALSO</title>
- <p><seealso marker="erlang#load_nif-2">erlang:load_nif/2</seealso></p>
+ <title>See Also</title>
+ <p><seealso marker="erlang#load_nif-2">
+ <c>erlang:load_nif/2</c></seealso></p>
</section>
</cref>
diff --git a/erts/doc/src/erl_prim_loader.xml b/erts/doc/src/erl_prim_loader.xml
index db4f132609..286bac6c93 100644
--- a/erts/doc/src/erl_prim_loader.xml
+++ b/erts/doc/src/erl_prim_loader.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,99 +30,70 @@
<file>erl_prim_loader.xml</file>
</header>
<module>erl_prim_loader</module>
- <modulesummary>Low Level Erlang Loader</modulesummary>
+ <modulesummary>Low-level Erlang loader.</modulesummary>
<description>
- <p><c>erl_prim_loader</c> is used to load all Erlang modules into
- the system. The start script is also fetched with this low level
+ <p>This module is used to load all Erlang modules into
+ the system. The start script is also fetched with this low-level
loader.</p>
+
<p><c>erl_prim_loader</c> knows about the environment and how to
fetch modules.</p>
- <p>The <c>-loader Loader</c> command line flag can be used to
- choose the method used by the <c>erl_prim_loader</c>. Two
+
+ <p>Command-line flag <c>-loader Loader</c> can be used to
+ choose the method used by <c>erl_prim_loader</c>. Two
<c>Loader</c> methods are supported by the Erlang runtime system:
<c>efile</c> and <c>inet</c>.</p>
-
- <warning><p>The support for loading of code from archive files is
- experimental. The sole purpose of releasing it before it is ready
- is to obtain early feedback. The file format, semantics,
- interfaces etc. may be changed in a future release. The functions
- <c>list_dir/1</c> and <c>read_file_info/1</c> as well as the flag
- <c>-loader_debug</c> are also experimental</p></warning>
-
</description>
- <datatypes>
- <datatype>
- <name name="host"/>
- </datatype>
- </datatypes>
<funcs>
<func>
- <name name="start" arity="3"/>
- <fsummary>Start the Erlang low level loader</fsummary>
- <desc>
- <p>Starts the Erlang low level loader. This function is called
- by the <c>init</c> process (and module). The <c>init</c>
- process reads the command line flags <c>-id <anno>Id</anno></c>,
- <c>-loader <anno>Loader</anno></c>, and <c>-hosts <anno>Hosts</anno></c>. These are
- the arguments supplied to the <c>start/3</c> function.</p>
- <p>If <c>-loader</c> is not given, the default loader is
- <c>efile</c> which tells the system to read from the file
- system.</p>
- <p>If <c>-loader</c> is <c>inet</c>, the <c>-id <anno>Id</anno></c>,
- <c>-hosts <anno>Hosts</anno></c>, and <c>-setcookie Cookie</c> flags must
- also be supplied. <c><anno>Hosts</anno></c> identifies hosts which this
- node can contact in order to load modules. One Erlang
- runtime system with a <c>erl_boot_server</c> process must be
- started on each of hosts given in <c><anno>Hosts</anno></c> in order to
- answer the requests. See <seealso
- marker="kernel:erl_boot_server">erl_boot_server(3)</seealso>.</p>
- </desc>
- </func>
- <func>
<name name="get_file" arity="1"/>
- <fsummary>Get a file</fsummary>
+ <fsummary>Get a file.</fsummary>
<desc>
- <p>This function fetches a file using the low level loader.
- <c><anno>Filename</anno></c> is either an absolute file name or just the name
- of the file, for example <c>"lists.beam"</c>. If an internal
+ <p>Fetches a file using the low-level loader.
+ <c><anno>Filename</anno></c> is either an absolute filename or only
+ the name of the file, for example, <c>"lists.beam"</c>. If an internal
path is set to the loader, this path is used to find the file.
- If a user supplied loader is used, the path can be stripped
- off if it is obsolete, and the loader does not use a path.
<c><anno>FullName</anno></c> is the complete name of the fetched file.
<c><anno>Bin</anno></c> is the contents of the file as a binary.</p>
-
- <p>The <c><anno>Filename</anno></c> can also be a file in an archive. For example
+ <p><c><anno>Filename</anno></c> can also be a file in an archive,
+ for example,
<c>$OTPROOT/lib/</c><c>mnesia-4.4.7.ez/mnesia-4.4.7/ebin/</c><c>mnesia.beam</c>.
- See <seealso marker="kernel:code">code(3)</seealso> about archive files.</p>
+ For information about archive files, see
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
</desc>
</func>
+
<func>
<name name="get_path" arity="0"/>
- <fsummary>Get the path set in the loader</fsummary>
+ <fsummary>Get the path set in the loader.</fsummary>
<desc>
- <p>This function gets the path set in the loader. The path is
- set by the <c>init</c> process according to information found
- in the start script.</p>
+ <p>Gets the path set in the loader. The path is
+ set by the <seealso marker="init"><c>init(3)</c></seealso>
+ process according to information found in the start script.</p>
</desc>
</func>
+
<func>
<name name="list_dir" arity="1"/>
- <fsummary>List files in a directory</fsummary>
+ <fsummary>List files in a directory.</fsummary>
<desc>
<p>Lists all the files in a directory. Returns
- <c>{ok, <anno>Filenames</anno>}</c> if successful. Otherwise, it returns
+ <c>{ok, <anno>Filenames</anno>}</c> if successful, otherwise
<c>error</c>. <c><anno>Filenames</anno></c> is a list of
the names of all the files in the directory. The names are
not sorted.</p>
- <p>The <c><anno>Dir</anno></c> can also be a directory in an archive. For example
+ <p><c><anno>Dir</anno></c> can also be a directory in an archive,
+ for example,
<c>$OTPROOT/lib/</c><c>mnesia-4.4.7.ez/mnesia-4.4.7/ebin</c>.
- See <seealso marker="kernel:code">code(3)</seealso> about archive files.</p>
+ For information about archive files, see
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
</desc>
</func>
+
<func>
<name name="read_file_info" arity="1"/>
- <fsummary>Get information about a file</fsummary>
+ <fsummary>Get information about a file.</fsummary>
<desc>
<p>Retrieves information about a file. Returns
<c>{ok, <anno>FileInfo</anno>}</c> if successful, otherwise
@@ -132,22 +103,25 @@
from which the function is called:</p>
<code type="none">
-include_lib("kernel/include/file.hrl").</code>
- <p>See <seealso marker="kernel:file">file(3)</seealso> for more info about
- the record <c>file_info</c>.</p>
- <p>The <c><anno>Filename</anno></c> can also be a file in an archive. For example
+ <p>For more information about the record <c>file_info</c>, see
+ <seealso marker="kernel:file"><c>file(3)</c></seealso>.</p>
+ <p><c><anno>Filename</anno></c> can also be a file in an archive,
+ for example,
<c>$OTPROOT/lib/</c><c>mnesia-4.4.7.ez/mnesia-4.4.7/ebin/</c><c>mnesia</c>.
- See <seealso marker="kernel:code">code(3)</seealso> about archive files.</p>
+ For information about archive files, see
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
</desc>
</func>
+
<func>
<name name="read_link_info" arity="1"/>
- <fsummary>Get information about a link or file</fsummary>
+ <fsummary>Get information about a link or file.</fsummary>
<desc>
- <p>This function works like
- <seealso marker="#read_file_info/1">read_file_info/1</seealso>
+ <p>Works like
+ <seealso marker="#read_file_info/1"><c>read_file_info/1</c></seealso>
except that if <c><anno>Filename</anno></c> is a symbolic link,
- information about the link will be returned in the <c>file_info</c>
- record and the <c>type</c> field of the record will be set to
+ information about the link is returned in the <c>file_info</c>
+ record and the <c>type</c> field of the record is set to
<c>symlink</c>.</p>
<p>If <c><anno>Filename</anno></c> is not a symbolic link, this function
returns exactly the same result as <c>read_file_info/1</c>.
@@ -155,20 +129,23 @@
is always equivalent to <c>read_file_info/1</c>.</p>
</desc>
</func>
+
<func>
<name name="set_path" arity="1"/>
- <fsummary>Set the path of the loader</fsummary>
+ <fsummary>Set the path of the loader.</fsummary>
<desc>
- <p>This function sets the path of the loader if <c>init</c>
+ <p>Sets the path of the loader if
+ <seealso marker="init"><c>init(3)</c></seealso>
interprets a <c>path</c> command in the start script.</p>
</desc>
</func>
</funcs>
<section>
- <title>Command Line Flags</title>
+ <title>Command-Line Flags</title>
<p>The <c>erl_prim_loader</c> module interprets the following
- command line flags:</p>
+ command-line flags:</p>
+
<taglist>
<tag><c>-loader Loader</c></tag>
<item>
@@ -176,42 +153,38 @@
<c>erl_prim_loader</c>. <c>Loader</c> can be <c>efile</c>
(use the local file system) or <c>inet</c> (load using
the <c>boot_server</c> on another Erlang node).</p>
- <p>If the <c>-loader</c> flag is omitted, it defaults to
+ <p>If flag <c>-loader</c> is omitted, it defaults to
<c>efile</c>.</p>
</item>
<tag><c>-loader_debug</c></tag>
<item>
<p>Makes the <c>efile</c> loader write some debug information,
- such as the reason for failures, while it handles files.</p>
+ such as the reason for failures, while it handles files.</p>
</item>
<tag><c>-hosts Hosts</c></tag>
<item>
<p>Specifies which other Erlang nodes the <c>inet</c> loader
- can use. This flag is mandatory if the <c>-loader inet</c>
- flag is present. On each host, there must be on Erlang node
- with the <c>erl_boot_server</c> which handles the load
- requests. <c>Hosts</c> is a list of IP addresses (hostnames
+ can use. This flag is mandatory if flag <c>-loader inet</c>
+ is present. On each host, there must be on Erlang node
+ with the <seealso marker="kernel:erl_boot_server">
+ <c>erl_boot_server(3)</c></seealso>,
+ which handles the load requests.
+ <c>Hosts</c> is a list of IP addresses (hostnames
are not acceptable).</p>
</item>
- <tag><c>-id Id</c></tag>
- <item>
- <p>Specifies the identity of the Erlang runtime system. If
- the system runs as a distributed node, <c>Id</c> must be
- identical to the name supplied with the <c>-sname</c> or
- <c>-name</c> distribution flags.</p>
- </item>
<tag><c>-setcookie Cookie</c></tag>
<item>
<p>Specifies the cookie of the Erlang runtime system. This flag
- is mandatory if the <c>-loader inet</c> flag is present.</p>
+ is mandatory if flag <c>-loader inet</c> is present.</p>
</item>
</taglist>
</section>
<section>
- <title>SEE ALSO</title>
- <p><seealso marker="init">init(3)</seealso>,
- <seealso marker="kernel:erl_boot_server">erl_boot_server(3)</seealso></p>
+ <title>See Also</title>
+ <p><seealso marker="init"><c>init(3)</c></seealso>,
+ <seealso marker="kernel:erl_boot_server">
+ <c>erl_boot_server(3)</c></seealso></p>
</section>
</erlref>
diff --git a/erts/doc/src/erl_tracer.xml b/erts/doc/src/erl_tracer.xml
new file mode 100644
index 0000000000..83eef374ca
--- /dev/null
+++ b/erts/doc/src/erl_tracer.xml
@@ -0,0 +1,778 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>2016</year><year>2016</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ </legalnotice>
+
+ <title>erl_tracer</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ </header>
+ <module>erl_tracer</module>
+ <modulesummary>Erlang tracer behavior.</modulesummary>
+ <description>
+ <p>This behavior module implements the back end of the Erlang
+ tracing system. The functions in this module are called whenever
+ a trace probe is triggered. Both the <c>enabled</c> and <c>trace</c>
+ functions are called in the context of the entity that triggered the
+ trace probe.
+ This means that the overhead by having the tracing enabled is
+ greatly effected by how much time is spent in these functions. So, do as
+ little work as possible in these functions.</p>
+
+ <note>
+ <p>All functions in this behavior must be implemented as NIFs.
+ This limitation can be removed in a future releases.
+ An <seealso marker="#example">example tracer module NIF</seealso>
+ implementation is provided at the end of this page.</p>
+ </note>
+
+ <warning>
+ <p>Do not send messages or issue port commands to the <c>Tracee</c>
+ in any of the callbacks. This is not allowed and can cause all
+ sorts of strange behavior, including, but not limited to, infinite
+ recursions.</p>
+ </warning>
+ </description>
+
+ <datatypes>
+ <datatype>
+ <name name="trace_tag_call"/>
+ </datatype>
+ <datatype>
+ <name name="trace_tag_gc"/>
+ </datatype>
+ <datatype>
+ <name name="trace_tag_ports"/>
+ </datatype>
+ <datatype>
+ <name name="trace_tag_procs"/>
+ </datatype>
+ <datatype>
+ <name name="trace_tag_receive"/>
+ </datatype>
+ <datatype>
+ <name name="trace_tag_running_ports"/>
+ </datatype>
+ <datatype>
+ <name name="trace_tag_running_procs"/>
+ </datatype>
+ <datatype>
+ <name name="trace_tag_send"/>
+ </datatype>
+ <datatype>
+ <name name="trace_tag"/>
+ <desc>
+ <p>The different trace tags that the tracer is called with.
+ Each trace tag is described in detail in
+ <seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso>.</p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="tracee"/>
+ <desc>
+ <p>The process or port that the trace belongs to.</p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="trace_opts"/>
+ <desc>
+ <p>The options for the tracee:</p>
+ <taglist>
+ <tag><c>timestamp</c></tag>
+ <item>If set the tracer has been requested to include a
+ time stamp.</item>
+ <tag><c>extra</c></tag>
+ <item>If set the tracepoint has included additonal data about
+ the trace event. What the additional data is depends on which
+ <c>TraceTag</c> has been triggered. The <c>extra</c> trace data
+ corresponds to the fifth element in the trace tuples described in
+ <seealso marker="erlang#trace_3_trace_messages">
+ erlang:trace/3</seealso>.</item>
+ <tag><c>match_spec_result</c></tag>
+ <item>If set the tracer has been requested to include the output
+ of a match specification that was run.</item>
+ <tag><c>scheduler_id</c></tag>
+ <item>If set the scheduler id is to be included by the tracer.</item>
+ </taglist>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="tracer_state"/>
+ <desc>
+ <p>The state specified when calling
+ <seealso marker="erlang#trace-3">
+ <c>erlang:trace(PidPortSpec,true,[{tracer,Module,TracerState}])</c></seealso>.
+ The tracer state is an immutable value that is passed to
+ <c>erl_tracer</c> callbacks and is to
+ contain all the data that is needed to generate the trace event.</p>
+ </desc>
+ </datatype>
+ </datatypes>
+
+ <section>
+ <title>Callback Functions</title>
+ <p>The following functions are to be exported from an <c>erl_tracer</c>
+ callback module:</p>
+
+ <taglist>
+ <tag><seealso marker="#Module:enabled/3">
+ <c>Module:enabled/3</c></seealso></tag>
+ <item>Mandatory</item>
+ <tag><seealso marker="#Module:trace/5">
+ <c>Module:trace/5</c></seealso></tag>
+ <item>Mandatory</item>
+ <tag><seealso marker="#Module:enabled_call/3">
+ <c>Module:enabled_call/3</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:trace_call/5">
+ <c>Module:trace_call/5</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:enabled_garbage_collection/3">
+ <c>Module:enabled_garbage_collection/3</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:trace_garbage_collection/5">
+ <c>Module:trace_garbage_collection/5</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:enabled_ports/3">
+ <c>Module:enabled_ports/3</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:trace_ports/5">
+ <c>Module:trace_ports/5</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:enabled_procs/3">
+ <c>Module:enabled_procs/3</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:trace_procs/5">
+ <c>Module:trace_procs/5</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:enabled_receive/3">
+ <c>Module:enabled_receive/3</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:trace_receive/5">
+ <c>Module:trace_receive/5</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:enabled_running_ports/3">
+ <c>Module:enabled_running_ports/3</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:trace_running_ports/5">
+ <c>Module:trace_running_ports/5</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:enabled_running_procs/3">
+ <c>Module:enabled_running_procs/3</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:trace_running_procs/5">
+ <c>Module:trace_running_procs/5</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:enabled_send/3">
+ <c>Module:enabled_send/3</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:trace_send/5">
+ <c>Module:trace_send/5</c></seealso></tag>
+ <item>Optional</item>
+ </taglist>
+ </section>
+
+ <funcs>
+ <func>
+ <name>Module:enabled(TraceTag, TracerState, Tracee) -> Result</name>
+ <fsummary>Check if a trace event is to be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag">
+ trace_tag()</seealso> | trace_status</v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>Result = trace | discard | remove</v>
+ </type>
+ <desc>
+ <p>This callback is called whenever a tracepoint is triggered. It
+ allows the tracer to decide whether a trace is to be generated or not.
+ This check is made as early as possible to limit the amount of
+ overhead associated with tracing. If <c>trace</c> is returned, the
+ necessary trace data is created and the trace callback of the tracer
+ is called. If <c>discard</c> is returned, this trace call is
+ discarded and no call to trace is done.</p>
+ <p><c>trace_status</c> is a special type of <c>TraceTag</c>, which is
+ used to check if the tracer is still to be active. It is called in
+ multiple scenarios, but most significantly it is used when tracing
+ is started using this tracer. If <c>remove</c> is returned when the
+ <c>trace_status</c> is checked, the tracer is removed from the
+ tracee.</p>
+ <p>This function can be called multiple times per tracepoint, so it
+ is important that it is both fast and without side effects.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:enabled_call(TraceTag, TracerState, Tracee) -> Result</name>
+ <fsummary>Check if a trace event is to be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_call">
+ trace_tag_call()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>Result = trace | discard | remove</v>
+ </type>
+ <desc>
+ <p>This callback is called whenever a tracepoint with trace flag
+ <seealso marker="erlang#trace-3"><c>call | return_to</c></seealso>
+ is triggered.</p>
+ <p>If <c>enabled_call/3</c> is undefined,
+ <seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso>
+ is called instead.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:enabled_garbage_collection(TraceTag, TracerState, Tracee) -> Result</name>
+ <fsummary>Check if a trace event is to be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_gc">
+ trace_tag_gc()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>Result = trace | discard | remove</v>
+ </type>
+ <desc>
+ <p>This callback is called whenever a tracepoint with trace flag
+ <seealso marker="erlang#trace-3"><c>garbage_collection</c></seealso>
+ is triggered.</p>
+ <p>If <c>enabled_garbage_collection/3</c> is undefined,
+ <seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso>
+ is called instead.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:enabled_ports(TraceTag, TracerState, Tracee) -> Result</name>
+ <fsummary>Check if a trace event is to be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_ports">
+ trace_tag_ports()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>Result = trace | discard | remove</v>
+ </type>
+ <desc>
+ <p>This callback is called whenever a tracepoint with trace flag
+ <seealso marker="erlang#trace-3"><c>ports</c></seealso>
+ is triggered.</p>
+ <p>If <c>enabled_ports/3</c> is undefined,
+ <seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso>
+ is called instead.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:enabled_procs(TraceTag, TracerState, Tracee) -> Result</name>
+ <fsummary>Check if a trace event is to be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_procs">
+ trace_tag_procs()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>Result = trace | discard | remove</v>
+ </type>
+ <desc>
+ <p>This callback is called whenever a tracepoint with trace flag
+ <seealso marker="erlang#trace-3"><c>procs</c></seealso>
+ is triggered.</p>
+ <p>If <c>enabled_procs/3</c> is undefined,
+ <seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso>
+ is called instead.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:enabled_receive(TraceTag, TracerState, Tracee) -> Result
+ </name>
+ <fsummary>Check if a trace event is to be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_receive">
+ trace_tag_receive()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>Result = trace | discard | remove</v>
+ </type>
+ <desc>
+ <p>This callback is called whenever a tracepoint with trace flag
+ <seealso marker="erlang#trace-3"><c>'receive'</c></seealso>
+ is triggered.</p>
+ <p>If <c>enabled_receive/3</c> is undefined,
+ <seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso>
+ is called instead.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:enabled_running_ports(TraceTag, TracerState, Tracee) ->
+ Result</name>
+ <fsummary>Check if a trace event is to be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_running_ports">
+ trace_tag_running_ports()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>Result = trace | discard | remove</v>
+ </type>
+ <desc>
+ <p>This callback is called whenever a tracepoint with trace flag
+ <seealso marker="erlang#trace-3"><c>running_ports</c></seealso>
+ is triggered.</p>
+ <p>If <c>enabled_running_ports/3</c> is undefined,
+ <seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso>
+ is called instead.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:enabled_running_procs(TraceTag, TracerState, Tracee) ->
+ Result</name>
+ <fsummary>Check if a trace event is to be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_running_procs">
+ trace_tag_running_procs()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>Result = trace | discard | remove</v>
+ </type>
+ <desc>
+ <p>This callback is called whenever a tracepoint with trace flag
+ <seealso marker="erlang#trace-3">
+ <c>running_procs | running</c></seealso>
+ is triggered.</p>
+ <p>If <c>enabled_running_procs/3</c> is undefined,
+ <seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso>
+ is called instead.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:enabled_send(TraceTag, TracerState, Tracee) -> Result</name>
+ <fsummary>Check if a trace event is to be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_send">
+ trace_tag_send()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>Result = trace | discard | remove</v>
+ </type>
+ <desc>
+ <p>This callback is called whenever a tracepoint with trace flag
+ <seealso marker="erlang#trace-3"><c>send</c></seealso>
+ is triggered.</p>
+ <p>If <c>enabled_send/3</c> is undefined,
+ <seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso>
+ is called instead.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:trace(TraceTag, TracerState, Tracee, TraceTerm,
+ Opts) -> Result</name>
+ <fsummary>Check if a trace event is to be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag">
+ trace_tag()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>TraceTerm = term()</v>
+ <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
+ <v>Result = ok</v>
+ </type>
+ <desc>
+ <p>This callback is called when a tracepoint is triggered and the
+ <seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso>
+ callback returned <c>trace</c>. In it any side effects needed by
+ the tracer are to be done. The tracepoint payload is located in
+ the <c>TraceTerm</c>. The content of the <c>TraceTerm</c>
+ depends on which <c>TraceTag</c> is triggered.
+ <c>TraceTerm</c> corresponds to the
+ fourth element in the trace tuples described in
+ <seealso marker="erlang#trace_3_trace_messages">
+ <c>erlang:trace/3</c></seealso>.</p>
+ <p>If the trace tuple has five elements, the fifth element
+ will be sent as the <c>extra</c> value in the <c>Opts</c> maps.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="trace">Module:trace(seq_trace, TracerState, Label,
+ SeqTraceInfo, Opts) -> Result</name>
+ <fsummary>Check if a sequence trace event is to be generated.</fsummary>
+ <type>
+ <v>TracerState = term()</v>
+ <v>Label = term()</v>
+ <v>SeqTraceInfo = term()</v>
+ <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
+ <v>Result = ok</v>
+ </type>
+ <desc>
+ <p>The <c>TraceTag</c> <c>seq_trace</c> is handled slightly
+ differently. There is no <c>Tracee</c> for <c>seq_trace</c>, instead
+ the <c>Label</c> associated with the <c>seq_trace</c> event is
+ specified.</p>
+ <p>For more information on what <c>Label</c> and <c>SeqTraceInfo</c>
+ can be, see <seealso marker="kernel:seq_trace">
+ <c>seq_trace(3)</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:trace_call(TraceTag, TracerState, Tracee, TraceTerm,
+ Opts) -> Result</name>
+ <fsummary>Check if a trace event is to be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_call">
+ trace_tag_call()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>TraceTerm = term()</v>
+ <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
+ <v>Result = ok</v>
+ </type>
+ <desc>
+ <p>This callback is called when a tracepoint is triggered and the
+ <seealso marker="#Module:enabled_call/3">
+ <c>Module:enabled_call/3</c></seealso>
+ callback returned <c>trace</c>.</p>
+ <p>If <c>trace_call/5</c> is undefined,
+ <seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso>
+ is called instead.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:trace_garbage_collection(TraceTag, TracerState, Tracee,
+ TraceTerm, Opts) -> Result</name>
+ <fsummary>Check if a trace event is to be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_gc">
+ trace_tag_gc()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>TraceTerm = term()</v>
+ <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
+ <v>Result = ok</v>
+ </type>
+ <desc>
+ <p>This callback is called when a tracepoint is triggered and the
+ <seealso marker="#Module:enabled_garbage_collection/3">
+ <c>Module:enabled_garbage_collection/3</c></seealso>
+ callback returned <c>trace</c>.</p>
+ <p>If <c>trace_garbage_collection/5</c> is undefined,
+ <seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso>
+ is called instead.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:trace_ports(TraceTag, TracerState, Tracee, TraceTerm,
+ Opts) -> Result</name>
+ <fsummary>Check if a trace event is to be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_ports">
+ trace_tag()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>TraceTerm = term()</v>
+ <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
+ <v>Result = ok</v>
+ </type>
+ <desc>
+ <p>This callback is called when a tracepoint is triggered and the
+ <seealso marker="#Module:enabled_ports/3">
+ <c>Module:enabled_ports/3</c></seealso>
+ callback returned <c>trace</c>.</p>
+ <p>If <c>trace_ports/5</c> is undefined,
+ <seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso>
+ is called instead.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:trace_procs(TraceTag, TracerState, Tracee, TraceTerm,
+ Opts) -> Result</name>
+ <fsummary>Check if a trace event is to be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_procs">
+ trace_tag()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>TraceTerm = term()</v>
+ <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
+ <v>Result = ok</v>
+ </type>
+ <desc>
+ <p>This callback is called when a tracepoint is triggered and the
+ <seealso marker="#Module:enabled_procs/3">
+ <c>Module:enabled_procs/3</c></seealso>
+ callback returned <c>trace</c>.</p>
+ <p>If <c>trace_procs/5</c> is undefined,
+ <seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso>
+ is called instead.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:trace_receive(TraceTag, TracerState, Tracee, TraceTerm,
+ Opts) -> Result</name>
+ <fsummary>Check if a trace event is to be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_receive">
+ trace_tag_receive()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>TraceTerm = term()</v>
+ <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
+ <v>Result = ok</v>
+ </type>
+ <desc>
+ <p>This callback is called when a tracepoint is triggered and the
+ <seealso marker="#Module:enabled_receive/3">
+ <c>Module:enabled_receive/3</c></seealso>
+ callback returned <c>trace</c>.</p>
+ <p>If <c>trace_receive/5</c> is undefined,
+ <seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso>
+ is called instead.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:trace_running_ports(TraceTag, TracerState, Tracee,
+ TraceTerm, Opts) -> Result</name>
+ <fsummary>Check if a trace event is to be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_running_ports">
+ trace_tag_running_ports()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>TraceTerm = term()</v>
+ <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
+ <v>Result = ok</v>
+ </type>
+ <desc>
+ <p>This callback is called when a tracepoint is triggered and the
+ <seealso marker="#Module:enabled_running_ports/3">
+ <c>Module:enabled_running_ports/3</c></seealso>
+ callback returned <c>trace</c>.</p>
+ <p>If <c>trace_running_ports/5</c> is undefined,
+ <seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso>
+ is called instead.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:trace_running_procs(TraceTag, TracerState, Tracee,
+ TraceTerm, Opts) -> Result</name>
+ <fsummary>Check if a trace event is to be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_running_procs">
+ trace_tag_running_procs()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>TraceTerm = term()</v>
+ <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
+ <v>Result = ok</v>
+ </type>
+ <desc>
+ <p>This callback is called when a tracepoint is triggered and the
+ <seealso marker="#Module:enabled_running_procs/3">
+ <c>Module:enabled_running_procs/3</c></seealso>
+ callback returned <c>trace</c>.</p>
+ <p>If <c>trace_running_procs/5</c> is undefined,
+ <seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso>
+ is called instead.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:trace_send(TraceTag, TracerState, Tracee, TraceTerm,
+ Opts) -> Result</name>
+ <fsummary>Check if a trace event is to be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_send">
+ trace_tag_send()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>TraceTerm = term()</v>
+ <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
+ <v>Result = ok</v>
+ </type>
+ <desc>
+ <p>This callback is called when a tracepoint is triggered and the
+ <seealso marker="#Module:enabled_send/3">
+ <c>Module:enabled_send/3</c></seealso>
+ callback returned <c>trace</c>.</p>
+ <p>If <c>trace_send/5</c> is undefined,
+ <seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso>
+ is called instead.</p>
+ </desc>
+ </func>
+ </funcs>
+
+ <section>
+ <marker id="example"></marker>
+ <title>Erl Tracer Module Example</title>
+ <p>In this example, a tracer module with a NIF back end sends a
+ message for each <c>send</c> trace tag containing only the sender and
+ receiver. Using this tracer module, a much more lightweight message
+ tracer is used, which only records who sent messages to who.</p>
+
+ <p>The following is an example session using it on Linux:</p>
+
+ <pre>
+$ gcc -I erts-8.0/include/ -fPIC -shared -o erl_msg_tracer.so erl_msg_tracer.c
+$ erl
+Erlang/OTP 19 [DEVELOPMENT] [erts-8.0] [source-ed2b56b] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false]
+
+Eshell V8.0 (abort with ^G)
+1&gt; c(erl_msg_tracer), erl_msg_tracer:load().
+ok
+2&gt; Tracer = spawn(fun F() -&gt; receive M -&gt; io:format("~p~n",[M]), F() end end).
+&lt;0.37.0&gt;
+3&gt; erlang:trace(new, true, [send,{tracer, erl_msg_tracer, Tracer}]).
+0
+{&lt;0.39.0&gt;,&lt;0.27.0&gt;}
+4&gt; {ok, D} = file:open("/tmp/tmp.data",[write]).
+{trace,#Port&lt;0.486&gt;,&lt;0.40.0&gt;}
+{trace,&lt;0.40.0&gt;,&lt;0.21.0&gt;}
+{trace,#Port&lt;0.487&gt;,&lt;0.4.0&gt;}
+{trace,#Port&lt;0.488&gt;,&lt;0.4.0&gt;}
+{trace,#Port&lt;0.489&gt;,&lt;0.4.0&gt;}
+{trace,#Port&lt;0.490&gt;,&lt;0.4.0&gt;}
+{ok,&lt;0.40.0&gt;}
+{trace,&lt;0.41.0&gt;,&lt;0.27.0&gt;}
+5&gt;</pre>
+
+ <p><c>erl_msg_tracer.erl</c>:</p>
+
+ <pre>
+-module(erl_msg_tracer).
+
+-export([enabled/3, trace/5, load/0]).
+
+load() ->
+ erlang:load_nif("erl_msg_tracer", []).
+
+enabled(_, _, _) ->
+ error.
+
+trace(_, _, _, _, _) ->
+ error.</pre>
+
+ <p><c>erl_msg_tracer.c</c>:</p>
+
+ <pre>
+#include "erl_nif.h"
+
+/* NIF interface declarations */
+static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info);
+static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info);
+static void unload(ErlNifEnv* env, void* priv_data);
+
+/* The NIFs: */
+static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+
+static ErlNifFunc nif_funcs[] = {
+ {"enabled", 3, enabled},
+ {"trace", 5, trace}
+};
+
+ERL_NIF_INIT(erl_msg_tracer, nif_funcs, load, NULL, upgrade, unload)
+
+static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
+{
+ *priv_data = NULL;
+ return 0;
+}
+
+static void unload(ErlNifEnv* env, void* priv_data)
+{
+
+}
+
+static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data,
+ ERL_NIF_TERM load_info)
+{
+ if (*old_priv_data != NULL || *priv_data != NULL) {
+ return -1; /* Don't know how to do that */
+ }
+ if (load(env, priv_data, load_info)) {
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * argv[0]: TraceTag
+ * argv[1]: TracerState
+ * argv[2]: Tracee
+ */
+static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifPid to_pid;
+ if (enif_get_local_pid(env, argv[1], &amp;to_pid))
+ if (!enif_is_process_alive(env, &amp;to_pid))
+ if (enif_is_identical(enif_make_atom(env, "trace_status"), argv[0]))
+ /* tracer is dead so we should remove this tracepoint */
+ return enif_make_atom(env, "remove");
+ else
+ return enif_make_atom(env, "discard");
+
+ /* Only generate trace for when tracer != tracee */
+ if (enif_is_identical(argv[1], argv[2]))
+ return enif_make_atom(env, "discard");
+
+ /* Only trigger trace messages on 'send' */
+ if (enif_is_identical(enif_make_atom(env, "send"), argv[0]))
+ return enif_make_atom(env, "trace");
+
+ /* Have to answer trace_status */
+ if (enif_is_identical(enif_make_atom(env, "trace_status"), argv[0]))
+ return enif_make_atom(env, "trace");
+
+ return enif_make_atom(env, "discard");
+}
+
+/*
+ * argv[0]: TraceTag, should only be 'send'
+ * argv[1]: TracerState, process to send {argv[2], argv[4]} to
+ * argv[2]: Tracee
+ * argv[3]: Recipient
+ * argv[4]: Options, ignored
+ */
+static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifPid to_pid;
+
+ if (enif_get_local_pid(env, argv[1], &amp;to_pid)) {
+ ERL_NIF_TERM msg = enif_make_tuple3(env, enif_make_atom(env, "trace"), argv[2], argv[4]);
+ enif_send(env, &amp;to_pid, NULL, msg);
+ }
+
+ return enif_make_atom(env, "ok");
+}</pre>
+ </section>
+</erlref>
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index 30e6751f41..c3b0bc0d74 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2015</year>
+ <year>1996</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -32,18 +32,21 @@
<module>erlang</module>
<modulesummary>The Erlang BIFs.</modulesummary>
<description>
- <p>By convention, most Built-In Functions (BIFs) are seen as being
+ <p>By convention, most Built-In Functions (BIFs) are included
in this module. Some of the BIFs are viewed more
or less as part of the Erlang programming language and are
<em>auto-imported</em>. Thus, it is not necessary to specify the
module name. For example, the calls <c>atom_to_list(Erlang)</c>
and <c>erlang:atom_to_list(Erlang)</c> are identical.</p>
+
<p>Auto-imported BIFs are listed without module prefix.
BIFs listed with module prefix are not auto-imported.</p>
+
<p>BIFs can fail for various reasons. All BIFs fail with
reason <c>badarg</c> if they are called with arguments of an
incorrect type. The other reasons are described in the
description of each individual BIF.</p>
+
<p>Some BIFs can be used in guard tests and are marked with
"Allowed in guard tests".</p>
</description>
@@ -52,85 +55,130 @@
<datatype>
<name>ext_binary()</name>
<desc>
- <marker id="type-ext_binary"></marker>
<p>A binary data object, structured according to
- the Erlang external term format.</p>
+ the Erlang external term format.</p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="message_queue_data"></name>
+ <desc>
+ <p>See <seealso marker="#process_flag_message_queue_data">
+ <c>process_flag(message_queue_data, MQD)</c></seealso>.</p>
</desc>
</datatype>
-
<datatype>
<name name="timestamp"></name>
- <desc><p>See <seealso marker="#timestamp/0">erlang:timestamp/0</seealso>.</p>
+ <desc>
+ <p>See <seealso marker="#timestamp/0">
+ <c>erlang:timestamp/0</c></seealso>.</p>
</desc>
</datatype>
<datatype>
<name name="time_unit"></name>
- <desc><p><marker id="type_time_unit"/>
- Supported time unit representations:</p>
+ <desc>
+ <marker id="type_time_unit"/>
+ <p>Supported time unit representations:</p>
<taglist>
- <tag><c>PartsPerSecond :: integer() >= 1</c></tag>
- <item><p>Time unit expressed in parts per second. That is,
- the time unit equals <c>1/PartsPerSecond</c> second.</p></item>
-
+ <tag><c>PartsPerSecond :: integer() >= 1</c></tag>
+ <item>
+ <p>Time unit expressed in parts per second. That is,
+ the time unit equals <c>1/PartsPerSecond</c> second.</p>
+ </item>
+ <tag><c>second</c></tag>
+ <item>
+ <p>Symbolic representation of the time unit
+ represented by the integer <c>1</c>.</p>
+ </item>
+ <tag><c>millisecond</c></tag>
+ <item>
+ <p>Symbolic representation of the time unit
+ represented by the integer <c>1000</c>.</p>
+ </item>
+ <tag><c>microsecond</c></tag>
+ <item>
+ <p>Symbolic representation of the time unit
+ represented by the integer <c>1000000</c>.</p>
+ </item>
+ <tag><c>nanosecond</c></tag>
+ <item>
+ <p>Symbolic representation of the time unit
+ represented by the integer <c>1000000000</c>.</p>
+ </item>
+ <tag><c>native</c></tag>
+ <item>
+ <p>Symbolic representation of the native time unit
+ used by the Erlang runtime system.</p>
+ <p>The <c>native</c> time unit is determined at
+ runtime system start, and remains the same until
+ the runtime system terminates. If a runtime system
+ is stopped and then started again (even on the same
+ machine), the <c>native</c> time unit of the new
+ runtime system instance can differ from the
+ <c>native</c> time unit of the old runtime system
+ instance.</p>
+ <p>One can get an approximation of the <c>native</c>
+ time unit by calling
+ <seealso marker="erlang:convert_time_unit/3">
+ <c>erlang:convert_time_unit(1, second, native)</c></seealso>.
+ The result equals the number
+ of whole <c>native</c> time units per second. If
+ the number of <c>native</c> time units per second does not
+ add up to a whole number, the result is rounded downwards.</p>
+ <note>
+ <p>The value of the <c>native</c> time unit gives
+ you more or less no information about the
+ quality of time values. It sets a limit for the
+ <seealso marker="time_correction#Time_Resolution">
+ resolution</seealso> and for the
+ <seealso marker="time_correction#Time_Precision">
+ precision</seealso> of time values,
+ but it gives no information about the
+ <seealso marker="time_correction#Time_Accuracy">
+ accuracy</seealso> of time values. The resolution of
+ the <c>native</c> time unit and the resolution of time
+ values can differ significantly.</p>
+ </note>
+ </item>
+ <tag><c>perf_counter</c></tag>
+ <item>
+ <p>Symbolic representation of the performance counter
+ time unit used by the Erlang runtime system.</p>
+ <p>The <c>perf_counter</c> time unit behaves much in the same way
+ as the <c>native</c> time unit. That is, it can differ between
+ runtime restarts. To get values of this type, call
+ <seealso marker="kernel:os#perf_counter/0">
+ <c>os:perf_counter/0</c></seealso>.</p>
+ </item>
+ <tag><seealso marker="#type_deprecated_time_unit"><c>deprecated_time_unit()</c></seealso></tag>
+ <item><p>
+ Deprecated symbolic representations kept for backwards-compatibility.
+ </p></item>
+ </taglist>
+ <p>The <c>time_unit/0</c> type can be extended.
+ To convert time values between time units, use
+ <seealso marker="#convert_time_unit/3">
+ <c>erlang:convert_time_unit/3</c></seealso>.</p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="deprecated_time_unit"></name>
+ <desc><marker id="type_deprecated_time_unit"/>
+ <p>The <seealso marker="#type_time_unit"><c>time_unit()</c></seealso>
+ type also consist of the following <em>deprecated</em> symbolic
+ time units:</p>
+ <taglist>
<tag><c>seconds</c></tag>
- <item><p>Symbolic representation of the time unit
- represented by the integer <c>1</c>.</p></item>
+ <item><p>Same as <seealso marker="#type_time_unit"><c>second</c></seealso>.</p></item>
<tag><c>milli_seconds</c></tag>
- <item><p>Symbolic representation of the time unit
- represented by the integer <c>1000</c>.</p></item>
+ <item><p>Same as <seealso marker="#type_time_unit"><c>millisecond</c></seealso>.</p></item>
<tag><c>micro_seconds</c></tag>
- <item><p>Symbolic representation of the time unit
- represented by the integer <c>1000000</c>.</p></item>
+ <item><p>Same as <seealso marker="#type_time_unit"><c>microsecond</c></seealso>.</p></item>
<tag><c>nano_seconds</c></tag>
- <item><p>Symbolic representation of the time unit
- represented by the integer <c>1000000000</c>.</p></item>
-
- <tag><c>native</c></tag>
- <item><p>Symbolic representation of the native time unit
- used by the Erlang runtime system.</p>
-
- <p>The <c>native</c> time unit is determined at
- runtime system start, and remains the same until
- the runtime system terminates. If a runtime system
- is stopped and then started again (even on the same
- machine), the <c>native</c> time unit of the new
- runtime system instance can differ from the
- <c>native</c> time unit of the old runtime system
- instance.</p>
-
- <p>One can get an approximation of the <c>native</c>
- time unit by calling <c>erlang:convert_time_unit(1,
- seconds, native)</c>. The result equals the number
- of whole <c>native</c> time units per second. In case
- the number of <c>native</c> time units per second does
- not add up to a whole number, the result is rounded downwards.</p>
-
- <note>
- <p>The value of the <c>native</c> time unit gives
- you more or less no information at all about the
- quality of time values. It sets a limit for
- the
- <seealso marker="time_correction#Time_Resolution">resolution</seealso>
- as well as for the
- <seealso marker="time_correction#Time_Precision">precision</seealso>
- of time values,
- but it gives absolutely no information at all about the
- <seealso marker="time_correction#Time_Accuracy">accuracy</seealso>
- of time values. The resolution of the <c>native</c> time
- unit and the resolution of time values can differ
- significantly.</p>
- </note>
- </item>
-
+ <item><p>Same as <seealso marker="#type_time_unit"><c>nanosecond</c></seealso>.</p></item>
</taglist>
-
- <p>The <c>time_unit/0</c> type may be extended. Use
- <seealso marker="#convert_time_unit/3"><c>erlang:convert_time_unit/3</c></seealso>
- in order to convert time values between time units.</p>
-
</desc>
</datatype>
</datatypes>
@@ -159,61 +207,60 @@
<func>
<name name="adler32" arity="1"/>
- <fsummary>Computes adler32 checksum.</fsummary>
+ <fsummary>Compute adler32 checksum.</fsummary>
<desc>
<p>Computes and returns the adler32 checksum for
- <c><anno>Data</anno></c>.</p>
+ <c><anno>Data</anno></c>.</p>
</desc>
</func>
<func>
<name name="adler32" arity="2"/>
- <fsummary>Computes adler32 checksum.</fsummary>
+ <fsummary>Compute adler32 checksum.</fsummary>
<desc>
<p>Continues computing the adler32 checksum by combining
- the previous checksum, <c><anno>OldAdler</anno></c>, with
- the checksum of <c><anno>Data</anno></c>.</p>
+ the previous checksum, <c><anno>OldAdler</anno></c>, with
+ the checksum of <c><anno>Data</anno></c>.</p>
<p>The following code:</p>
<code>
- X = erlang:adler32(Data1),
- Y = erlang:adler32(X,Data2).</code>
- <p>assigns the same value to <c>Y</c> as this:</p>
+X = erlang:adler32(Data1),
+Y = erlang:adler32(X,Data2).</code>
+ <p>assigns the same value to <c>Y</c> as this:</p>
<code>
- Y = erlang:adler32([Data1,Data2]).</code>
+Y = erlang:adler32([Data1,Data2]).</code>
</desc>
</func>
<func>
<name name="adler32_combine" arity="3"/>
- <fsummary>Combines two adler32 checksums.</fsummary>
+ <fsummary>Combine two adler32 checksums.</fsummary>
<desc>
<p>Combines two previously computed adler32 checksums.
- This computation requires the size of the data object for
- the second checksum to be known.</p>
+ This computation requires the size of the data object for
+ the second checksum to be known.</p>
<p>The following code:</p>
<code>
- Y = erlang:adler32(Data1),
- Z = erlang:adler32(Y,Data2).</code>
+Y = erlang:adler32(Data1),
+Z = erlang:adler32(Y,Data2).</code>
<p>assigns the same value to <c>Z</c> as this:</p>
<code>
- X = erlang:adler32(Data1),
- Y = erlang:adler32(Data2),
- Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code>
+X = erlang:adler32(Data1),
+Y = erlang:adler32(Data2),
+Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code>
</desc>
</func>
<func>
<name name="append_element" arity="2"/>
- <fsummary>Appends an extra element to a tuple.</fsummary>
+ <fsummary>Append an extra element to a tuple.</fsummary>
<desc>
<p>Returns a new tuple that has one element more than
<c><anno>Tuple1</anno></c>, and contains the elements in
- <c><anno>Tuple1</anno></c>
+ <c><anno>Tuple1</anno></c>
followed by <c><anno>Term</anno></c> as the last element.
- Semantically equivalent to
+ Semantically equivalent to
<c>list_to_tuple(tuple_to_list(<anno>Tuple1</anno>) ++
- [<anno>Term</anno>])</c>, but much faster.</p>
- <p>Example:</p>
+ [<anno>Term</anno>])</c>, but much faster. Example:</p>
<pre>
> <input>erlang:append_element({one, two}, three).</input>
{one,two,three}</pre>
@@ -222,32 +269,31 @@
<func>
<name name="apply" arity="2"/>
- <fsummary>Applies a function to an argument list.</fsummary>
+ <fsummary>Apply a function to an argument list.</fsummary>
<desc>
<p>Calls a fun, passing the elements in <c><anno>Args</anno></c>
- as arguments.</p>
+ as arguments.</p>
<p>If the number of elements in the arguments are known at
compile time, the call is better written as
<c><anno>Fun</anno>(Arg1, Arg2, ... ArgN)</c>.</p>
<warning>
- <p>Earlier, <c><anno>Fun</anno></c> could also be given as
+ <p>Earlier, <c><anno>Fun</anno></c> could also be specified as
<c>{Module, Function}</c>, equivalent to
- <c>apply(Module, Function, Args)</c>. This use is
- deprecated and will stop working in a future release.</p>
+ <c>apply(Module, Function, Args)</c>. <em>This use is
+ deprecated and will stop working in a future release.</em></p>
</warning>
</desc>
</func>
<func>
<name name="apply" arity="3"/>
- <fsummary>Applies a function to an argument list.</fsummary>
+ <fsummary>Apply a function to an argument list.</fsummary>
<desc>
<p>Returns the result of applying <c>Function</c> in
<c><anno>Module</anno></c> to <c><anno>Args</anno></c>.
- The applied function must
+ The applied function must
be exported from <c>Module</c>. The arity of the function is
- the length of <c>Args</c>.</p>
- <p>Example:</p>
+ the length of <c>Args</c>. Example:</p>
<pre>
> <input>apply(lists, reverse, [[a, b, c]]).</input>
[c,b,a]
@@ -255,39 +301,43 @@
"Erlang"</pre>
<p>If the number of arguments are known at compile time,
the call is better written as
- <c><anno>Module</anno>:<anno>Function</anno>(Arg1, Arg2, ..., ArgN)</c>.</p>
- <p>Failure: <c>error_handler:undefined_function/3</c> is called
+ <c><anno>Module</anno>:<anno>Function</anno>(Arg1, Arg2, ...,
+ ArgN)</c>.</p>
+ <p>Failure: <seealso marker="kernel:error_handler#undefined_function/3">
+ <c>error_handler:undefined_function/3</c></seealso> is called
if the applied function is not exported. The error handler
can be redefined (see
- <seealso marker="#process_flag/2">process_flag/2</seealso>).
+ <seealso marker="#process_flag/2"><c>process_flag/2</c></seealso>).
If <c>error_handler</c> is undefined, or if the user has
redefined the default <c>error_handler</c> so the replacement
- module is undefined, an error with the reason <c>undef</c>
+ module is undefined, an error with reason <c>undef</c>
is generated.</p>
</desc>
</func>
<func>
<name name="atom_to_binary" arity="2"/>
- <fsummary>Returns the binary representation of an atom.</fsummary>
+ <fsummary>Return the binary representation of an atom.</fsummary>
<desc>
<p>Returns a binary corresponding to the text
representation of <c><anno>Atom</anno></c>.
If <c><anno>Encoding</anno></c>
- is <c>latin1</c>, there is one byte for each character
+ is <c>latin1</c>, one byte exists for each character
in the text representation. If <c><anno>Encoding</anno></c> is
<c>utf8</c> or
<c>unicode</c>, the characters are encoded using UTF-8
(that is, characters from 128 through 255 are
encoded in two bytes).</p>
- <note><p><c>atom_to_binary(<anno>Atom</anno>, latin1)</c> never
- fails because the text representation of an atom can only
- contain characters from 0 through 255. In a future release,
- the text representation
- of atoms can be allowed to contain any Unicode character and
- <c>atom_to_binary(<anno>Atom</anno>, latin1)</c> will then fail if the
- text representation for <c><anno>Atom</anno></c> contains a Unicode
- character greater than 255.</p></note>
+ <note>
+ <p><c>atom_to_binary(<anno>Atom</anno>, latin1)</c> never
+ fails, as the text representation of an atom can only
+ contain characters from 0 through 255. In a future release,
+ the text representation
+ of atoms can be allowed to contain any Unicode character and
+ <c>atom_to_binary(<anno>Atom</anno>, latin1)</c> then fails if the
+ text representation for <c><anno>Atom</anno></c> contains a Unicode
+ character &gt; 255.</p>
+ </note>
<p>Example:</p>
<pre>
> <input>atom_to_binary('Erlang', latin1).</input>
@@ -309,12 +359,12 @@
<func>
<name name="binary_part" arity="2"/>
- <fsummary>Extracts a part of a binary.</fsummary>
+ <fsummary>Extract a part of a binary.</fsummary>
<desc>
<p>Extracts the part of the binary described by
<c><anno>PosLen</anno></c>.</p>
<p>Negative length can be used to extract bytes at the end
- of a binary, for example:</p>
+ of a binary, for example:</p>
<code>
1> Bin = &lt;&lt;1,2,3,4,5,6,7,8,9,10&gt;&gt;.
2> binary_part(Bin,{byte_size(Bin), -5}).
@@ -326,43 +376,44 @@
1> Bin = &lt;&lt;1,2,3&gt;&gt;
2> binary_part(Bin,{0,2}).
&lt;&lt;1,2&gt;&gt;</code>
- <p>For details about the <c><anno>PosLen</anno></c> semantics, see the
- <seealso marker="stdlib:binary">binary</seealso>
- manual page in <c>STDLIB</c>.</p>
+ <p>For details about the <c><anno>PosLen</anno></c> semantics, see
+ <seealso marker="stdlib:binary"><c>binary(3)</c></seealso>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
<name name="binary_part" arity="3"/>
- <fsummary>Extracts a part of a binary.</fsummary>
+ <fsummary>Extract a part of a binary.</fsummary>
<desc>
<p>The same as <c>binary_part(<anno>Subject</anno>,
- {<anno>Start</anno>, <anno>Length</anno>})</c>.</p>
+ {<anno>Start</anno>, <anno>Length</anno>})</c>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
<name name="binary_to_atom" arity="2"/>
- <fsummary>Converts from text representation to an atom.</fsummary>
+ <fsummary>Convert from text representation to an atom.</fsummary>
<desc>
<p>Returns the atom whose text representation is
- <c><anno>Binary</anno></c>.
- If <c><anno>Encoding</anno></c> is <c>latin1</c>, no
- translation of bytes in the binary is done.
- If <c><anno>Encoding</anno></c>
- is <c>utf8</c> or <c>unicode</c>, the binary must contain
- valid UTF-8 sequences. Only Unicode characters up
- to 255 are allowed.</p>
- <note><p><c>binary_to_atom(<anno>Binary</anno>, utf8)</c> fails if
- the binary contains Unicode characters greater than 255.
- In a future release, such Unicode characters can be allowed
- and <c>binary_to_atom(<anno>Binary</anno>, utf8)</c> does then not fail.
- For more information on Unicode support in atoms, see the
- <seealso marker="erl_ext_dist#utf8_atoms">note on UTF-8
- encoded atoms</seealso>
- in Section "External Term Format" in the User's Guide.</p></note>
+ <c><anno>Binary</anno></c>.
+ If <c><anno>Encoding</anno></c> is <c>latin1</c>, no
+ translation of bytes in the binary is done.
+ If <c><anno>Encoding</anno></c>
+ is <c>utf8</c> or <c>unicode</c>, the binary must contain
+ valid UTF-8 sequences. Only Unicode characters up
+ to 255 are allowed.</p>
+ <note>
+ <p><c>binary_to_atom(<anno>Binary</anno>, utf8)</c> fails if
+ the binary contains Unicode characters &gt; 255.
+ In a future release, such Unicode characters can be allowed and
+ <c>binary_to_atom(<anno>Binary</anno>, utf8)</c> does then not fail.
+ For more information about Unicode support in atoms, see the
+ <seealso marker="erl_ext_dist#utf8_atoms">note on UTF-8
+ encoded atoms</seealso>
+ in section "External Term Format" in the User's Guide.</p>
+ </note>
<p>Examples:</p>
<pre>
> <input>binary_to_atom(&lt;&lt;"Erlang"&gt;&gt;, latin1).</input>
@@ -376,10 +427,10 @@
<func>
<name name="binary_to_existing_atom" arity="2"/>
- <fsummary>Converts from text representation to an atom.</fsummary>
+ <fsummary>Convert from text representation to an atom.</fsummary>
<desc>
<p>As
- <seealso marker="#binary_to_atom/2">binary_to_atom/2</seealso>,
+ <seealso marker="#binary_to_atom/2"><c>binary_to_atom/2</c></seealso>,
but the atom must exist.</p>
<p>Failure: <c>badarg</c> if the atom does not exist.</p>
</desc>
@@ -387,7 +438,7 @@
<func>
<name name="binary_to_float" arity="1"/>
- <fsummary>Converts from text representation to a float.</fsummary>
+ <fsummary>Convert from text representation to a float.</fsummary>
<desc>
<p>Returns the float whose text representation is
<c><anno>Binary</anno></c>, for example:</p>
@@ -401,7 +452,7 @@
<func>
<name name="binary_to_integer" arity="1"/>
- <fsummary>Converts from text representation to an integer.</fsummary>
+ <fsummary>Convert from text representation to an integer.</fsummary>
<desc>
<p>Returns an integer whose text representation is
<c><anno>Binary</anno></c>, for example:</p>
@@ -415,10 +466,11 @@
<func>
<name name="binary_to_integer" arity="2"/>
- <fsummary>Converts from text representation to an integer.</fsummary>
+ <fsummary>Convert from text representation to an integer.</fsummary>
<desc>
<p>Returns an integer whose text representation in base
- <c><anno>Base</anno></c> is <c><anno>Binary</anno></c>, for example:</p>
+ <c><anno>Base</anno></c> is <c><anno>Binary</anno></c>, for
+ example:</p>
<pre>
> <input>binary_to_integer(&lt;&lt;"3FF"&gt;&gt;, 16).</input>
1023</pre>
@@ -429,7 +481,7 @@
<func>
<name name="binary_to_list" arity="1"/>
- <fsummary>Converts a binary to a list.</fsummary>
+ <fsummary>Convert a binary to a list.</fsummary>
<desc>
<p>Returns a list of integers corresponding to the bytes of
<c><anno>Binary</anno></c>.</p>
@@ -438,86 +490,98 @@
<func>
<name name="binary_to_list" arity="3"/>
- <fsummary>Converts part of a binary to a list.</fsummary>
- <type_desc variable="Start">1..byte_size(<c><anno>Binary</anno></c>)</type_desc>
+ <fsummary>Convert part of a binary to a list.</fsummary>
+ <type_desc variable="Start">1..byte_size(<c><anno>Binary</anno></c>)
+ </type_desc>
<desc>
<p>As <c>binary_to_list/1</c>, but returns a list of integers
corresponding to the bytes from position <c><anno>Start</anno></c> to
position <c><anno>Stop</anno></c> in <c><anno>Binary</anno></c>.
- The positions in the
+ The positions in the
binary are numbered starting from 1.</p>
- <note><p>The one-based indexing for binaries used by
- this function is deprecated. New code is to use
- <seealso marker="stdlib:binary#bin_to_list/3">binary:bin_to_list/3</seealso>
- in <c>STDLIB</c> instead. All functions in module
- <c>binary</c> consistently use zero-based indexing.</p></note>
- </desc>
- </func>
-
- <func>
- <name name="bitstring_to_list" arity="1"/>
- <fsummary>Converts a bitstring to a list.</fsummary>
- <desc>
- <p>Returns a list of integers corresponding to the bytes of
- <c><anno>Bitstring</anno></c>. If the number of bits in the binary
- is not divisible by 8, the last element of the list is a bitstring
- containing the remaining 1-7 bits.</p>
+ <note>
+ <p><em>The one-based indexing for binaries used by
+ this function is deprecated.</em> New code is to use
+ <seealso marker="stdlib:binary#bin_to_list/3">
+ <c>binary:bin_to_list/3</c></seealso>
+ in STDLIB instead. All functions in module
+ <c>binary</c> consistently use zero-based indexing.</p>
+ </note>
</desc>
</func>
<func>
<name name="binary_to_term" arity="1"/>
- <fsummary>Decodes an Erlang external term format binary.</fsummary>
+ <fsummary>Decode an Erlang external term format binary.</fsummary>
<desc>
<p>Returns an Erlang term that is the result of decoding
binary object <c><anno>Binary</anno></c>, which must be encoded
- according to the Erlang external term format.</p>
- <warning><p>When decoding binaries from untrusted sources,
- consider using <c>binary_to_term/2</c> to prevent Denial
- of Service attacks.</p></warning>
+ according to the <seealso marker="erts:erl_ext_dist">
+ Erlang external term format</seealso>.</p>
+ <pre>
+> <input>Bin = term_to_binary(hello).</input>
+&lt;&lt;131,100,0,5,104,101,108,108,111>>
+> <input>hello = binary_to_term(Bin).</input>
+hello
+</pre>
+ <warning>
+ <p>When decoding binaries from untrusted sources,
+ consider using <c>binary_to_term/2</c> to prevent Denial
+ of Service attacks.</p>
+ </warning>
<p>See also
- <seealso marker="#term_to_binary/1">term_to_binary/1</seealso>
- and
- <seealso marker="#binary_to_term/2">binary_to_term/2</seealso>.</p>
+ <seealso marker="#term_to_binary/1"><c>term_to_binary/1</c></seealso>
+ and <seealso marker="#binary_to_term/2">
+ <c>binary_to_term/2</c></seealso>.</p>
</desc>
</func>
<func>
<name name="binary_to_term" arity="2"/>
- <fsummary>Decodes an Erlang external term format binary.</fsummary>
+ <fsummary>Decode an Erlang external term format binary.</fsummary>
<desc>
<p>As <c>binary_to_term/1</c>, but takes options that affect decoding
- of the binary.</p>
+ of the binary.</p>
+ <p>Option:</p>
<taglist>
<tag><c>safe</c></tag>
<item>
<p>Use this option when receiving binaries from an untrusted
- source.</p>
+ source.</p>
<p>When enabled, it prevents decoding data that can be used to
- attack the Erlang system. In the event of receiving unsafe
- data, decoding fails with a <c>badarg</c> error.</p>
+ attack the Erlang system. In the event of receiving unsafe
+ data, decoding fails with a <c>badarg</c> error.</p>
<p>This prevents creation of new atoms directly,
- creation of new atoms indirectly (as they are embedded in
- certain structures, such as process identifiers,
- refs, and funs), and
- creation of new external function references.
- None of those resources are garbage collected, so unchecked
- creation of them can exhaust available memory.</p>
+ creation of new atoms indirectly (as they are embedded in
+ certain structures, such as process identifiers,
+ refs, and funs), and
+ creation of new external function references.
+ None of those resources are garbage collected, so unchecked
+ creation of them can exhaust available memory.</p>
</item>
</taglist>
<p>Failure: <c>badarg</c> if <c>safe</c> is specified and unsafe
- data is decoded.</p>
+ data is decoded.</p>
+ <pre>
+> <input>binary_to_term(&lt;&lt;131,100,0,5,104,101,108,108,111>>, [safe]).</input>
+** exception error: bad argument
+> <input>hello.</input>
+hello
+> <input>binary_to_term(&lt;&lt;131,100,0,5,104,101,108,108,111>>, [safe]).</input>
+hello
+</pre>
<p>See also
- <seealso marker="#term_to_binary/1">term_to_binary/1</seealso>,
- <seealso marker="#binary_to_term/1">binary_to_term/1</seealso>,
- and
- <seealso marker="#list_to_existing_atom/1">list_to_existing_atom/1</seealso>.</p>
+ <seealso marker="#term_to_binary/1"><c>term_to_binary/1</c></seealso>,
+ <seealso marker="#binary_to_term/1">
+ <c>binary_to_term/1</c></seealso>, and
+ <seealso marker="#list_to_existing_atom/1">
+ <c>list_to_existing_atom/1</c></seealso>.</p>
</desc>
</func>
<func>
<name name="bit_size" arity="1"/>
- <fsummary>Returns the size of a bitstring.</fsummary>
+ <fsummary>Return the size of a bitstring.</fsummary>
<desc>
<p>Returns an integer that is the size in bits of
<c><anno>Bitstring</anno></c>, for example:</p>
@@ -531,15 +595,26 @@
</func>
<func>
+ <name name="bitstring_to_list" arity="1"/>
+ <fsummary>Convert a bitstring to a list.</fsummary>
+ <desc>
+ <p>Returns a list of integers corresponding to the bytes of
+ <c><anno>Bitstring</anno></c>. If the number of bits in the binary
+ is not divisible by 8, the last element of the list is a bitstring
+ containing the remaining 1-7 bits.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="bump_reductions" arity="1"/>
- <fsummary>Increments the reduction counter.</fsummary>
+ <fsummary>Increment the reduction counter.</fsummary>
<desc>
<p>This implementation-dependent function increments
the reduction counter for the calling process. In the Beam
emulator, the reduction counter is normally incremented by
one for each function and BIF call. A context switch is
forced when the counter reaches the maximum number of
- reductions for a process (2000 reductions in OTP R12B).</p>
+ reductions for a process (2000 reductions in Erlang/OTP R12B).</p>
<warning>
<p>This BIF can be removed in a future version of the Beam
machine without prior warning. It is unlikely to be
@@ -550,13 +625,12 @@
<func>
<name name="byte_size" arity="1"/>
- <fsummary>Returns the size of a bitstring (or binary).</fsummary>
+ <fsummary>Return the size of a bitstring (or binary).</fsummary>
<desc>
<p>Returns an integer that is the number of bytes needed to
contain <c><anno>Bitstring</anno></c>. That is, if the number of bits
in <c><anno>Bitstring</anno></c> is not divisible by 8, the resulting
- number of bytes is rounded <em>up</em>.</p>
- <p>Examples:</p>
+ number of bytes is rounded <em>up</em>. Examples:</p>
<pre>
> <input>byte_size(&lt;&lt;433:16,3:3&gt;&gt;).</input>
3
@@ -567,134 +641,127 @@
</func>
<func>
- <name name="cancel_timer" arity="2"/>
- <fsummary>Cancels a timer.</fsummary>
+ <name name="cancel_timer" arity="1"/>
+ <fsummary>Cancel a timer.</fsummary>
<desc>
- <p>
- Cancels a timer that has been created by
- <seealso marker="#start_timer/4"><c>erlang:start_timer()</c></seealso>,
- or <seealso marker="#send_after/4"><c>erlang:send_after()</c></seealso>.
- <c><anno>TimerRef</anno></c> identifies the timer, and
- was returned by the BIF that created the timer.
- </p>
- <p>Available <c><anno>Option</anno></c>s:</p>
+ <p>Cancels a timer. The same as calling
+ <seealso marker="#cancel_timer/2">
+ <c>erlang:cancel_timer(TimerRef, [])</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="cancel_timer" arity="2"/>
+ <fsummary>Cancel a timer.</fsummary>
+ <desc>
+ <p>Cancels a timer that has been created by
+ <seealso marker="#start_timer/4">
+ <c>erlang:start_timer</c></seealso> or
+ <seealso marker="#send_after/4"><c>erlang:send_after</c></seealso>.
+ <c><anno>TimerRef</anno></c> identifies the timer, and
+ was returned by the BIF that created the timer.</p>
+ <p><c><anno>Option</anno></c>s:</p>
<taglist>
<tag><c>{async, Async}</c></tag>
<item>
- <p>
- Asynchronous request for cancellation. <c>Async</c>
- defaults to <c>false</c> which will cause the
- cancellation to be performed synchronously. When
- <c>Async</c> is set to <c>true</c>, the cancel
- operation is performed asynchronously. That is,
- <c>erlang:cancel_timer()</c> will send an asynchronous
- request for cancellation to the timer service that
- manages the timer, and then return <c>ok</c>.
- </p>
- </item>
+ <p>Asynchronous request for cancellation. <c>Async</c>
+ defaults to <c>false</c>, which causes the
+ cancellation to be performed synchronously. When
+ <c>Async</c> is set to <c>true</c>, the cancel
+ operation is performed asynchronously. That is,
+ <c>cancel_timer()</c> sends an asynchronous
+ request for cancellation to the timer service that
+ manages the timer, and then returns <c>ok</c>.</p>
+ </item>
<tag><c>{info, Info}</c></tag>
<item>
- <p>
- Request information about the <c><anno>Result</anno></c>
- of the cancellation. <c>Info</c> defaults to <c>true</c>
- which means the <c><anno>Result</anno></c> is
- given. When <c>Info</c> is set to <c>false</c>, no
- information about the result of the cancellation
- is given. When the operation is performed</p>
- <taglist>
- <tag>synchronously</tag>
- <item>
- <p>
- If <c>Info</c> is <c>true</c>, the <c>Result</c> is
- returned by <c>erlang:cancel_timer()</c>; otherwise,
- <c>ok</c> is returned.
- </p>
- </item>
- <tag>asynchronously</tag>
- <item>
- <p>
- If <c>Info</c> is <c>true</c>, a message on the form
- <c>{cancel_timer, <anno>TimerRef</anno>,
- <anno>Result</anno>}</c> is sent to the
- caller of <c>erlang:cancel_timer()</c> when the
- cancellation operation has been performed; otherwise,
- no message is sent.
- </p>
- </item>
- </taglist>
- </item>
- </taglist>
- <p>
- More <c><anno>Option</anno></c>s may be added in the future.
- </p>
- <p>If <c><anno>Result</anno></c> is an integer, it represents
- the time in milli-seconds left until the canceled timer would
- have expired.</p>
- <p>
- If <c><anno>Result</anno></c> is <c>false</c>, a
- timer corresponding to <c><anno>TimerRef</anno></c> could not
- be found. This can be either because the timer had expired,
- already had been canceled, or because <c><anno>TimerRef</anno></c>
- never corresponded to a timer. Even if the timer had expired,
- it does not tell you whether or not the timeout message has
- arrived at its destination yet.
- </p>
- <note>
- <p>
- The timer service that manages the timer may be co-located
- with another scheduler than the scheduler that the calling
- process is executing on. If this is the case, communication
- with the timer service takes much longer time than if it
- is located locally. If the calling process is in critical
- path, and can do other things while waiting for the result
- of this operation, or is not interested in the result of
- the operation, you want to use option <c>{async, true}</c>.
- If using option <c>{async, false}</c>, the calling
- process blocks until the operation has been performed.
- </p>
- </note>
- <p>See also
+ <p>Requests information about the <c><anno>Result</anno></c>
+ of the cancellation. <c>Info</c> defaults to <c>true</c>,
+ which means the <c><anno>Result</anno></c> is
+ given. When <c>Info</c> is set to <c>false</c>, no
+ information about the result of the cancellation
+ is given.</p>
+ <list type="bulleted">
+ <item>
+ <p>When <c>Async</c> is <c>false</c>:
+ if <c>Info</c> is <c>true</c>, the <c>Result</c> is
+ returned by <c>erlang:cancel_timer()</c>. otherwise
+ <c>ok</c> is returned.</p>
+ </item>
+ <item>
+ <p>When <c>Async</c> is <c>true</c>:
+ if <c>Info</c> is <c>true</c>, a message on the form
+ <c>{cancel_timer, <anno>TimerRef</anno>,
+ <anno>Result</anno>}</c> is sent to the
+ caller of <c>erlang:cancel_timer()</c> when the
+ cancellation operation has been performed, otherwise
+ no message is sent.</p>
+ </item>
+ </list>
+ </item>
+ </taglist>
+ <p>More <c><anno>Option</anno></c>s may be added in the future.</p>
+ <p>If <c><anno>Result</anno></c> is an integer, it represents
+ the time in milliseconds left until the canceled timer would
+ have expired.</p>
+ <p>If <c><anno>Result</anno></c> is <c>false</c>, a
+ timer corresponding to <c><anno>TimerRef</anno></c> could not
+ be found. This can be either because the timer had expired,
+ already had been canceled, or because <c><anno>TimerRef</anno></c>
+ never corresponded to a timer. Even if the timer had expired,
+ it does not tell you if the time-out message has
+ arrived at its destination yet.</p>
+ <note>
+ <p>The timer service that manages the timer can be co-located
+ with another scheduler than the scheduler that the calling
+ process is executing on. If so, communication
+ with the timer service takes much longer time than if it
+ is located locally. If the calling process is in critical
+ path, and can do other things while waiting for the result
+ of this operation, or is not interested in the result of
+ the operation, you want to use option <c>{async, true}</c>.
+ If using option <c>{async, false}</c>, the calling
+ process blocks until the operation has been performed.</p>
+ </note>
+ <p>See also
<seealso marker="#send_after/4"><c>erlang:send_after/4</c></seealso>,
- <seealso marker="#start_timer/4"><c>erlang:start_timer/4</c></seealso>,
- and
- <seealso marker="#read_timer/2"><c>erlang:read_timer/2</c></seealso>.</p>
- </desc>
- </func>
- <func>
- <name name="cancel_timer" arity="1"/>
- <fsummary>Cancels a timer.</fsummary>
- <desc>
- <p>Cancels a timer. The same as calling
- <seealso marker="#cancel_timer/2"><c>erlang:cancel_timer(TimerRef,
- [])</c></seealso>.</p>
+ <seealso marker="#start_timer/4">
+ <c>erlang:start_timer/4</c></seealso>, and
+ <seealso marker="#read_timer/2">
+ <c>erlang:read_timer/2</c></seealso>.</p>
</desc>
</func>
+
<func>
<name name="check_old_code" arity="1"/>
- <fsummary>Checks if a module has old code.</fsummary>
+ <fsummary>Check if a module has old code.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Module</anno></c> has old code,
- otherwise <c>false</c>.</p>
- <p>See also <seealso marker="kernel:code">code(3)</seealso>.</p>
+ otherwise <c>false</c>.</p>
+ <p>See also <seealso marker="kernel:code">
+ <c>code(3)</c></seealso>.</p>
</desc>
</func>
<func>
<name name="check_process_code" arity="2"/>
- <fsummary>Checks if a process executes old code for a module.</fsummary>
+ <fsummary>Check if a process executes old code for a module.</fsummary>
<desc>
<p>The same as
- <seealso marker="#check_process_code/3"><c>erlang:check_process_code(<anno>Pid</anno>, <anno>Module</anno>, [])</c></seealso>.</p>
+ <seealso marker="#check_process_code/3">
+ <c>check_process_code(<anno>Pid</anno>, <anno>Module</anno>, [])</c>
+ </seealso>.</p>
</desc>
</func>
<func>
<name name="check_process_code" arity="3"/>
- <fsummary>Checks if a process executes old code for a module.</fsummary>
+ <fsummary>Check if a process executes old code for a module.</fsummary>
<desc>
- <p>Checks if the node local process identified by <c><anno>Pid</anno></c>
- executes old code for <c><anno>Module</anno></c>.</p>
- <p>The available <c><anno>Option</anno></c>s are as follows:</p>
+ <p>Checks if the node local process identified by
+ <c><anno>Pid</anno></c>
+ executes old code for <c><anno>Module</anno></c>.</p>
+ <p><c><anno>Option</anno></c>s:</p>
<taglist>
<tag><c>{allow_gc, boolean()}</c></tag>
<item>
@@ -702,30 +769,30 @@
the operation. If <c>{allow_gc, false}</c> is passed, and
a garbage collection is needed to determine the
result of the operation, the operation is aborted (see
- information on <c><anno>CheckResult</anno></c> in the following).
+ information on <c><anno>CheckResult</anno></c> below).
The default is to allow garbage collection, that is,
<c>{allow_gc, true}</c>.</p>
- </item>
+ </item>
<tag><c>{async, RequestId}</c></tag>
<item>
<p>The function <c>check_process_code/3</c> returns
the value <c>async</c> immediately after the request
has been sent. When the request has been processed, the
process that called this function is passed a
- message on the form
- <c>{check_process_code, <anno>RequestId</anno>, <anno>CheckResult</anno>}</c>.</p>
- </item>
+ message on the form <c>{check_process_code, <anno>RequestId</anno>,
+ <anno>CheckResult</anno>}</c>.</p>
+ </item>
</taglist>
<p>If <c><anno>Pid</anno></c> equals <c>self()</c>, and
- no <c>async</c> option has been passed, the operation
- is performed at once. Otherwise a request for
- the operation is sent to the process identified by
- <c><anno>Pid</anno></c>, and is handled when
- appropriate. If no <c>async</c> option has been passed,
- the caller blocks until <c><anno>CheckResult</anno></c>
- is available and can be returned.</p>
+ no <c>async</c> option has been passed, the operation
+ is performed at once. Otherwise a request for
+ the operation is sent to the process identified by
+ <c><anno>Pid</anno></c>, and is handled when
+ appropriate. If no <c>async</c> option has been passed,
+ the caller blocks until <c><anno>CheckResult</anno></c>
+ is available and can be returned.</p>
<p><c><anno>CheckResult</anno></c> informs about the result of
- the request as follows:</p>
+ the request as follows:</p>
<taglist>
<tag><c>true</c></tag>
<item>
@@ -735,54 +802,82 @@
code for this module, or the process has references
to old code for this module, or the process contains
funs that references old code for this module.</p>
- </item>
+ </item>
<tag><c>false</c></tag>
<item>
<p>The process identified by <c><anno>Pid</anno></c> does
not execute old code for <c><anno>Module</anno></c>.</p>
- </item>
+ </item>
<tag><c>aborted</c></tag>
<item>
<p>The operation was aborted, as the process needed to
be garbage collected to determine the operation result,
and the operation was requested
- by passing option <c>{allow_gc, false}</c>.</p></item>
+ by passing option <c>{allow_gc, false}</c>.</p>
+ </item>
</taglist>
- <p>See also <seealso marker="kernel:code">code(3)</seealso>.</p>
+ <note>
+ <p>
+ Up until ERTS version 8.*, the check process code operation
+ checks for all types of references to the old code. That is,
+ direct references (e.g. return addresses on the process
+ stack), indirect references (<c>fun</c>s in process
+ context), and references to literals in the code.
+ </p>
+ <p>
+ As of ERTS version 9.0, the check process code operation
+ only checks for direct references to the code. Indirect
+ references via <c>fun</c>s will be ignored. If such
+ <c>fun</c>s exist and are used after a purge of the old
+ code, an exception will be raised upon usage (same as
+ the case when the <c>fun</c> is received by the process
+ after the purge). Literals will be taken care of (copied)
+ at a later stage. This behavior can as of ERTS version
+ 8.1 be enabled when
+ <seealso marker="doc/installation_guide:INSTALL#Advanced-configuration-and-build-of-ErlangOTP_Configuring">building OTP</seealso>,
+ and will automatically be enabled if dirty scheduler
+ support is enabled.
+ </p>
+ </note>
+ <p>See also <seealso marker="kernel:code">
+ <c>code(3)</c></seealso>.</p>
<p>Failures:</p>
<taglist>
<tag><c>badarg</c></tag>
- <item>If <c><anno>Pid</anno></c> is not a node local process identifier.
- </item>
+ <item>If <c><anno>Pid</anno></c> is not a node local process
+ identifier.
+ </item>
<tag><c>badarg</c></tag>
<item>If <c><anno>Module</anno></c> is not an atom.
- </item>
+ </item>
<tag><c>badarg</c></tag>
<item>If <c><anno>OptionList</anno></c> is an invalid list of options.
- </item>
+ </item>
</taglist>
</desc>
</func>
<func>
<name name="convert_time_unit" arity="3"/>
- <fsummary>Converts time unit of a time value.</fsummary>
+ <fsummary>Convert time unit of a time value.</fsummary>
<desc>
- <p>Converts the <c><anno>Time</anno></c> value of time unit
- <c><anno>FromUnit</anno></c> to the corresponding
- <c><anno>ConvertedTime</anno></c> value of time unit
- <c><anno>ToUnit</anno></c>. The result is rounded
- using the floor function.</p>
-
- <warning><p>You may lose accuracy and precision when converting
- between time units. In order to minimize such loss, collect all
- data at <c>native</c> time unit and do the conversion on the end
- result.</p></warning>
+ <p>Converts the <c><anno>Time</anno></c> value of time unit
+ <c><anno>FromUnit</anno></c> to the corresponding
+ <c><anno>ConvertedTime</anno></c> value of time unit
+ <c><anno>ToUnit</anno></c>. The result is rounded
+ using the floor function.</p>
+ <warning>
+ <p>You can lose accuracy and precision when converting
+ between time units. To minimize such loss, collect all
+ data at <c>native</c> time unit and do the conversion on the end
+ result.</p>
+ </warning>
</desc>
</func>
+
<func>
<name name="crc32" arity="1"/>
- <fsummary>Computes crc32 (IEEE 802.3) checksum.</fsummary>
+ <fsummary>Compute crc32 (IEEE 802.3) checksum.</fsummary>
<desc>
<p>Computes and returns the crc32 (IEEE 802.3 style) checksum
for <c><anno>Data</anno></c>.</p>
@@ -791,37 +886,37 @@
<func>
<name name="crc32" arity="2"/>
- <fsummary>Computes crc32 (IEEE 802.3) checksum.</fsummary>
+ <fsummary>Compute crc32 (IEEE 802.3) checksum.</fsummary>
<desc>
<p>Continues computing the crc32 checksum by combining
- the previous checksum, <c><anno>OldCrc</anno></c>, with the checksum of
- <c><anno>Data</anno></c>.</p>
+ the previous checksum, <c><anno>OldCrc</anno></c>, with the checksum
+ of <c><anno>Data</anno></c>.</p>
<p>The following code:</p>
<code>
- X = erlang:crc32(Data1),
- Y = erlang:crc32(X,Data2).</code>
+X = erlang:crc32(Data1),
+Y = erlang:crc32(X,Data2).</code>
<p>assigns the same value to <c>Y</c> as this:</p>
<code>
- Y = erlang:crc32([Data1,Data2]).</code>
+Y = erlang:crc32([Data1,Data2]).</code>
</desc>
</func>
<func>
<name name="crc32_combine" arity="3"/>
- <fsummary>Combines two crc32 (IEEE 802.3) checksums.</fsummary>
+ <fsummary>Combine two crc32 (IEEE 802.3) checksums.</fsummary>
<desc>
<p>Combines two previously computed crc32 checksums.
- This computation requires the size of the data object for
- the second checksum to be known.</p>
+ This computation requires the size of the data object for
+ the second checksum to be known.</p>
<p>The following code:</p>
<code>
- Y = erlang:crc32(Data1),
- Z = erlang:crc32(Y,Data2).</code>
+Y = erlang:crc32(Data1),
+Z = erlang:crc32(Y,Data2).</code>
<p>assigns the same value to <c>Z</c> as this:</p>
<code>
- X = erlang:crc32(Data1),
- Y = erlang:crc32(Data2),
- Z = erlang:crc32_combine(X,Y,iolist_size(Data2)).</code>
+X = erlang:crc32(Data1),
+Y = erlang:crc32(Data2),
+Z = erlang:crc32_combine(X,Y,iolist_size(Data2)).</code>
</desc>
</func>
@@ -831,8 +926,7 @@
<desc>
<p>Returns the current date as <c>{Year, Month, Day}</c>.</p>
<p>The time zone and Daylight Saving Time correction depend on
- the underlying OS.</p>
- <p>Example:</p>
+ the underlying OS. Example:</p>
<pre>
> <input>date().</input>
{1995,2,19}</pre>
@@ -841,28 +935,29 @@
<func>
<name name="decode_packet" arity="3"/>
- <fsummary>Extracts a protocol packet from a binary.</fsummary>
+ <fsummary>Extract a protocol packet from a binary.</fsummary>
<desc>
<p>Decodes the binary <c><anno>Bin</anno></c> according to the packet
- protocol specified by <c><anno>Type</anno></c>. Similar to the packet
- handling done by sockets with option {packet,<anno>Type</anno>}.</p>
+ protocol specified by <c><anno>Type</anno></c>. Similar to the packet
+ handling done by sockets with option
+ <c>{packet,<anno>Type</anno>}.</c></p>
<p>If an entire packet is contained in <c><anno>Bin</anno></c>, it is
- returned together with the remainder of the binary as
- <c>{ok,<anno>Packet</anno>,<anno>Rest</anno>}</c>.</p>
+ returned together with the remainder of the binary as
+ <c>{ok,<anno>Packet</anno>,<anno>Rest</anno>}</c>.</p>
<p>If <c><anno>Bin</anno></c> does not contain the entire packet,
- <c>{more,<anno>Length</anno>}</c> is returned.
- <c><anno>Length</anno></c> is either the
- expected <em>total size</em> of the packet, or <c>undefined</c>
- if the expected packet size is unknown. <c>decode_packet</c>
- can then be called again with more data added.</p>
+ <c>{more,<anno>Length</anno>}</c> is returned.
+ <c><anno>Length</anno></c> is either the
+ expected <em>total size</em> of the packet, or <c>undefined</c>
+ if the expected packet size is unknown. <c>decode_packet</c>
+ can then be called again with more data added.</p>
<p>If the packet does not conform to the protocol format,
- <c>{error,<anno>Reason</anno>}</c> is returned.</p>
- <p>The following <c>Type</c>s are valid:</p>
+ <c>{error,<anno>Reason</anno>}</c> is returned.</p>
+ <p><c>Type</c>s:</p>
<taglist>
<tag><c>raw | 0</c></tag>
<item>
<p>No packet handling is done. The entire binary is
- returned unless it is empty.</p>
+ returned unless it is empty.</p>
</item>
<tag><c>1 | 2 | 4</c></tag>
<item>
@@ -874,10 +969,10 @@
</item>
<tag><c>line</c></tag>
<item>
- <p>A packet is a line terminated by a delimiter byte,
- default is the latin1 newline character. The delimiter
- byte is included in the returned packet unless the line
- was truncated according to option <c>line_length</c>.</p>
+ <p>A packet is a line-terminated by a delimiter byte,
+ default is the latin-1 newline character. The delimiter
+ byte is included in the returned packet unless the line
+ was truncated according to option <c>line_length</c>.</p>
</item>
<tag><c>asn1 | cdr | sunrm | fcgi | tpkt</c></tag>
<item>
@@ -894,50 +989,51 @@
<tag><c>http | httph | http_bin | httph_bin</c></tag>
<item>
<p>The Hypertext Transfer Protocol. The packets
- are returned with the format according to
- <c><anno>HttpPacket</anno></c> described earlier.
- A packet is either a
- request, a response, a header, or an end of header
- mark. Invalid lines are returned as
- <c><anno>HttpError</anno></c>.</p>
+ are returned with the format according to
+ <c><anno>HttpPacket</anno></c> described earlier.
+ A packet is either a
+ request, a response, a header, or an end of header
+ mark. Invalid lines are returned as
+ <c><anno>HttpError</anno></c>.</p>
<p>Recognized request methods and header fields are returned
- as atoms. Others are returned as strings. Strings of
- unrecognized header fields are formatted with only
- capital letters first and after hyphen characters, for
- example, <c>"Sec-Websocket-Key"</c>.</p>
+ as atoms. Others are returned as strings. Strings of
+ unrecognized header fields are formatted with only
+ capital letters first and after hyphen characters, for
+ example, <c>"Sec-Websocket-Key"</c>.</p>
<p>The protocol type <c>http</c> is only to be used for
- the first line when an <c><anno>HttpRequest</anno></c> or an
- <c><anno>HttpResponse</anno></c> is expected.
- The following calls are to use <c>httph</c> to get
- <c><anno>HttpHeader</anno></c>s until
- <c>http_eoh</c> is returned, which marks the end of the
- headers and the beginning of any following message body.</p>
+ the first line when an <c><anno>HttpRequest</anno></c> or an
+ <c><anno>HttpResponse</anno></c> is expected.
+ The following calls are to use <c>httph</c> to get
+ <c><anno>HttpHeader</anno></c>s until
+ <c>http_eoh</c> is returned, which marks the end of the
+ headers and the beginning of any following message body.</p>
<p>The variants <c>http_bin</c> and <c>httph_bin</c> return
- strings (<c>HttpString</c>) as binaries instead of lists.</p>
+ strings (<c>HttpString</c>) as binaries instead of lists.</p>
</item>
</taglist>
- <p>The following options are available:</p>
- <taglist>
- <tag><c>{packet_size, integer() >= 0}</c></tag>
- <item><p>Sets the maximum allowed size of the packet body.
- If the packet header indicates that the length of the
- packet is longer than the maximum allowed length, the
- packet is considered invalid. Default is 0, which means
- no size limit.</p>
- </item>
- <tag><c>{line_length, integer() >= 0}</c></tag>
- <item><p>For packet type <c>line</c>, lines longer than
+ <p>Options:</p>
+ <taglist>
+ <tag><c>{packet_size, integer() >= 0}</c></tag>
+ <item><p>Sets the maximum allowed size of the packet body.
+ If the packet header indicates that the length of the
+ packet is longer than the maximum allowed length, the
+ packet is considered invalid. Defaults to 0, which means
+ no size limit.</p>
+ </item>
+ <tag><c>{line_length, integer() >= 0}</c></tag>
+ <item>
+ <p>For packet type <c>line</c>, lines longer than
the indicated length are truncated.</p>
- <p>Option <c>line_length</c> also applies to <c>http*</c>
- packet types as an alias for option <c>packet_size</c>
- if <c>packet_size</c> itself is not set. This use is
- only intended for backward compatibility.</p>
- </item>
- <tag><c>{line_delimiter, 0 =&lt; byte() =&lt; 255}</c></tag>
- <item><p>For packet type <c>line</c>, sets the delimiting byte.
- Default is the latin1 character <c>$\n</c>.</p>
- </item>
- </taglist>
+ <p>Option <c>line_length</c> also applies to <c>http*</c>
+ packet types as an alias for option <c>packet_size</c>
+ if <c>packet_size</c> itself is not set. This use is
+ only intended for backward compatibility.</p>
+ </item>
+ <tag><c>{line_delimiter, 0 =&lt; byte() =&lt; 255}</c></tag>
+ <item><p>For packet type <c>line</c>, sets the delimiting byte.
+ Default is the latin-1 character <c>$\n</c>.</p>
+ </item>
+ </taglist>
<p>Examples:</p>
<pre>
> <input>erlang:decode_packet(1,&lt;&lt;3,"abcd"&gt;&gt;,[]).</input>
@@ -949,7 +1045,7 @@
<func>
<name name="delete_element" arity="2"/>
- <fsummary>Deletes element at index in a tuple.</fsummary>
+ <fsummary>Delete element at index in a tuple.</fsummary>
<type_desc variable="Index">1..tuple_size(<anno>Tuple1</anno>)</type_desc>
<desc>
<p>Returns a new tuple with element at <c><anno>Index</anno></c>
@@ -962,16 +1058,16 @@
<func>
<name name="delete_module" arity="1"/>
- <fsummary>Makes the current code for a module old.</fsummary>
+ <fsummary>Make the current code for a module old.</fsummary>
<desc>
- <p>Makes the current code for <c><anno>Module</anno></c> become old code,
- and deletes all references for this module from the export table.
+ <p>Makes the current code for <c><anno>Module</anno></c> become old
+ code and deletes all references for this module from the export table.
Returns <c>undefined</c> if the module does not exist,
otherwise <c>true</c>.</p>
<warning>
<p>This BIF is intended for the code server (see
- <seealso marker="kernel:code">code(3)</seealso>) and is not
- to be used elsewhere.</p>
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>)
+ and is not to be used elsewhere.</p>
</warning>
<p>Failure: <c>badarg</c> if there already is an old version of
<c>Module</c>.</p>
@@ -980,54 +1076,56 @@
<func>
<name name="demonitor" arity="1"/>
- <fsummary>Stops monitoring.</fsummary>
+ <fsummary>Stop monitoring.</fsummary>
<desc>
<p>If <c><anno>MonitorRef</anno></c> is a reference that the
calling process obtained by calling
- <seealso marker="#monitor/2">monitor/2</seealso>,
+ <seealso marker="#monitor/2"><c>monitor/2</c></seealso>,
this monitoring is turned off. If the monitoring is already
turned off, nothing happens.</p>
<p>Once <c>demonitor(<anno>MonitorRef</anno>)</c> has returned, it is
guaranteed that no <c>{'DOWN',
<anno>MonitorRef</anno>, _, _, _}</c> message,
because of the monitor, will be placed in the caller message queue
- in the future. A <c>{'DOWN',
+ in the future. However, a <c>{'DOWN',
<anno>MonitorRef</anno>, _, _, _}</c> message
can have been placed in the caller message queue before
- the call, though. It is therefore usually advisable
+ the call. It is therefore usually advisable
to remove such a <c>'DOWN'</c> message from the message queue
after monitoring has been stopped.
- <seealso marker="#demonitor/2"><c>demonitor(<anno>MonitorRef</anno>, [flush])</c></seealso>
- can be used instead of
- <c>demonitor(<anno>MonitorRef</anno>)</c> if this cleanup is wanted.</p>
+ <seealso marker="#demonitor/2">
+ <c>demonitor(<anno>MonitorRef</anno>, [flush])</c></seealso>
+ can be used instead of <c>demonitor(<anno>MonitorRef</anno>)</c>
+ if this cleanup is wanted.</p>
<note>
- <p>Prior to OTP release R11B (ERTS version 5.5) <c>demonitor/1</c>
- behaved completely asynchronously, i.e., the monitor was active
+ <p>Before Erlang/OTP R11B (ERTS 5.5) <c>demonitor/1</c>
+ behaved completely asynchronously, that is, the monitor was active
until the "demonitor signal" reached the monitored entity. This
had one undesirable effect. You could never know when
you were guaranteed <em>not</em> to receive a <c>DOWN</c> message
- due to the monitor.</p>
- <p>Current behavior can be viewed as two combined operations:
+ because of the monitor.</p>
+ <p>The current behavior can be viewed as two combined operations:
asynchronously send a "demonitor signal" to the monitored entity
- and ignore any future results of the monitor. </p>
+ and ignore any future results of the monitor.</p>
</note>
<p>Failure: It is an error if <c><anno>MonitorRef</anno></c> refers to a
monitoring started by another process. Not all such cases are
cheap to check. If checking is cheap, the call fails with
- <c>badarg</c> for example, if <c><anno>MonitorRef</anno></c> is a
+ <c>badarg</c>, for example if <c><anno>MonitorRef</anno></c> is a
remote reference.</p>
</desc>
</func>
<func>
<name name="demonitor" arity="2"/>
- <fsummary>Stops monitoring.</fsummary>
+ <fsummary>Stop monitoring.</fsummary>
<desc>
<p>The returned value is <c>true</c> unless <c>info</c> is part
of <c><anno>OptionList</anno></c>.</p>
<p><c>demonitor(<anno>MonitorRef</anno>, [])</c> is equivalent to
- <seealso marker="#demonitor/1"><c>demonitor(<anno>MonitorRef</anno>)</c></seealso>.</p>
- <p>The available <c><anno>Option</anno></c>s are as follows:</p>
+ <seealso marker="#demonitor/1">
+ <c>demonitor(<anno>MonitorRef</anno>)</c></seealso>.</p>
+ <p><c><anno>Option</anno></c>s:</p>
<taglist>
<tag><c>flush</c></tag>
<item>
@@ -1038,28 +1136,28 @@
<p>Calling <c>demonitor(<anno>MonitorRef</anno>, [flush])</c>
is equivalent to the following, but more efficient:</p>
<code type="none">
- demonitor(MonitorRef),
- receive
- {_, MonitorRef, _, _, _} ->
- true
- after 0 ->
- true
- end</code>
+demonitor(MonitorRef),
+receive
+ {_, MonitorRef, _, _, _} ->
+ true
+after 0 ->
+ true
+end</code>
</item>
<tag><c>info</c></tag>
<item>
<p>The returned value is one of the following:</p>
<taglist>
<tag><c>true</c></tag>
- <item>The monitor was found and removed. In this case,
+ <item><p>The monitor was found and removed. In this case,
no <c>'DOWN'</c> message corresponding to this
- monitor has been delivered and will not be delivered.
+ monitor has been delivered and will not be delivered.</p>
</item>
<tag><c>false</c></tag>
- <item>The monitor was not found and could not be removed.
+ <item><p>The monitor was not found and could not be removed.
This probably because someone already has placed a
<c>'DOWN'</c> message corresponding to this monitor
- in the caller message queue.
+ in the caller message queue.</p>
</item>
</taglist>
<p>If option <c>info</c> is combined with option <c>flush</c>,
@@ -1074,21 +1172,21 @@
<taglist>
<tag><c>badarg</c></tag>
<item>If <c><anno>OptionList</anno></c> is not a list.
- </item>
+ </item>
<tag><c>badarg</c></tag>
<item>If <c><anno>Option</anno></c> is an invalid option.
- </item>
+ </item>
<tag><c>badarg</c></tag>
<item>The same failure as for
- <seealso marker="#demonitor/1">demonitor/1</seealso>.
- </item>
+ <seealso marker="#demonitor/1"><c>demonitor/1</c></seealso>.
+ </item>
</taglist>
</desc>
</func>
<func>
<name name="disconnect_node" arity="1"/>
- <fsummary>Forces the disconnection of a node.</fsummary>
+ <fsummary>Force the disconnection of a node.</fsummary>
<desc>
<p>Forces the disconnection of a node. This appears to
the node <c><anno>Node</anno></c> as if the local node has crashed.
@@ -1102,10 +1200,10 @@
<func>
<name name="display" arity="1"/>
- <fsummary>Prints a term on standard output.</fsummary>
+ <fsummary>Print a term on standard output.</fsummary>
<desc>
<p>Prints a text representation of <c><anno>Term</anno></c> on the
- standard output. On OSE, the term is printed to the ramlog.</p>
+ standard output.</p>
<warning>
<p>This BIF is intended for debugging only.</p>
</warning>
@@ -1114,7 +1212,7 @@
<func>
<name name="element" arity="2"/>
- <fsummary>Returns the Nth element of a tuple.</fsummary>
+ <fsummary>Return the Nth element of a tuple.</fsummary>
<type_desc variable="N">1..tuple_size(<anno>Tuple</anno>)</type_desc>
<desc>
<p>Returns the <c><anno>N</anno></c>th element (numbering from 1) of
@@ -1128,7 +1226,7 @@ b</pre>
<func>
<name name="erase" arity="0"/>
- <fsummary>Returns and deletes the process dictionary.</fsummary>
+ <fsummary>Return and delete the process dictionary.</fsummary>
<desc>
<p>Returns the process dictionary and deletes it, for
example:</p>
@@ -1142,13 +1240,13 @@ b</pre>
<func>
<name name="erase" arity="1"/>
- <fsummary>Returns and deletes a value from the process dictionary.</fsummary>
+ <fsummary>Return and delete a value from the process dictionary.
+ </fsummary>
<desc>
<p>Returns the value <c><anno>Val</anno></c> associated with
<c><anno>Key</anno></c> and deletes it from the process dictionary.
Returns <c>undefined</c> if no value is associated with
- <c><anno>Key</anno></c>.</p>
- <p>Example:</p>
+ <c><anno>Key</anno></c>. Example:</p>
<pre>
> <input>put(key1, {merry, lambs, are, playing}),</input>
<input>X = erase(key1),</input>
@@ -1159,16 +1257,15 @@ b</pre>
<func>
<name name="error" arity="1"/>
- <fsummary>Stops execution with a given reason.</fsummary>
+ <fsummary>Stop execution with a specified reason.</fsummary>
<desc>
<p>Stops the execution of the calling process with the reason
<c><anno>Reason</anno></c>, where <c><anno>Reason</anno></c>
is any term. The exit reason is
<c>{<anno>Reason</anno>, Where}</c>, where <c>Where</c>
is a list of the functions most recently called (the current
- function first). Since evaluating this function causes
- the process to terminate, it has no return value.</p>
- <p>Example:</p>
+ function first). As evaluating this function causes
+ the process to terminate, it has no return value. Example:</p>
<pre>
> <input>catch error(foobar).</input>
{'EXIT',{foobar,[{erl_eval,do_apply,5},
@@ -1181,7 +1278,7 @@ b</pre>
<func>
<name name="error" arity="2"/>
- <fsummary>Stops execution with a given reason.</fsummary>
+ <fsummary>Stop execution with a specified reason.</fsummary>
<desc>
<p>Stops the execution of the calling process with the reason
<c><anno>Reason</anno></c>, where <c><anno>Reason</anno></c>
@@ -1191,21 +1288,20 @@ b</pre>
function first). <c><anno>Args</anno></c> is expected to be the
list of arguments for the current function; in Beam it is used
to provide the arguments for the current function in
- the term <c>Where</c>. Since evaluating this function causes
+ the term <c>Where</c>. As evaluating this function causes
the process to terminate, it has no return value.</p>
</desc>
</func>
<func>
<name name="exit" arity="1"/>
- <fsummary>Stops execution with a given reason.</fsummary>
+ <fsummary>Stop execution with a specified reason.</fsummary>
<desc>
<p>Stops the execution of the calling process with exit reason
<c><anno>Reason</anno></c>, where <c><anno>Reason</anno></c>
- is any term. Since
+ is any term. As
evaluating this function causes the process to terminate, it
- has no return value.</p>
- <p>Example:</p>
+ has no return value. Example:</p>
<pre>
> <input>exit(foobar).</input>
** exception exit: foobar
@@ -1216,43 +1312,44 @@ b</pre>
<func>
<name name="exit" arity="2"/>
- <fsummary>Sends an exit signal to a process or a port.</fsummary>
+ <fsummary>Send an exit signal to a process or a port.</fsummary>
<desc>
<p>Sends an exit signal with exit reason <c><anno>Reason</anno></c> to
the process or port identified by <c><anno>Pid</anno></c>.</p>
<p>The following behavior applies if <c><anno>Reason</anno></c>
is any term, except <c>normal</c> or <c>kill</c>:</p>
- <list type="bulleted">
- <item>If <c><anno>Pid</anno></c> is not trapping exits,
- <c><anno>Pid</anno></c>
- itself exits with exit reason <c><anno>Reason</anno></c>.
- </item>
- <item>If <c><anno>Pid</anno></c> is trapping exits, the exit
- signal is transformed into a message
- <c>{'EXIT', From, <anno>Reason</anno>}</c>
- and delivered to the message queue of <c><anno>Pid</anno></c>.
- </item>
- <item><c>From</c> is the process identifier of the process
- that sent the exit signal. See also
- <seealso marker="#process_flag/2">process_flag/2</seealso>.
- </item>
- </list>
- <p>If <c><anno>Reason</anno></c> is the atom <c>normal</c>,
+ <list type="bulleted">
+ <item><p>If <c><anno>Pid</anno></c> is not trapping exits,
<c><anno>Pid</anno></c>
- does not exit. If it is trapping exits, the exit signal is
- transformed into a message <c>{'EXIT', From, normal}</c>
- and delivered to its message queue.</p>
- <p>If <c><anno>Reason</anno></c> is the atom <c>kill</c>,
- that is, if <c>exit(<anno>Pid</anno>, kill)</c> is called,
- an untrappable exit signal is sent to <c><anno>Pid</anno></c>,
- which unconditionally exits with exit reason <c>killed</c>.
- </p>
+ itself exits with exit reason <c><anno>Reason</anno></c>.</p>
+ </item>
+ <item><p>If <c><anno>Pid</anno></c> is trapping exits, the exit
+ signal is transformed into a message
+ <c>{'EXIT', From, <anno>Reason</anno>}</c>
+ and delivered to the message queue of <c><anno>Pid</anno></c>.</p>
+ </item>
+ <item><p><c>From</c> is the process identifier of the process
+ that sent the exit signal. See also
+ <seealso marker="#process_flag/2">
+ <c>process_flag/2</c></seealso>.</p>
+ </item>
+ </list>
+ <p>If <c><anno>Reason</anno></c> is the atom <c>normal</c>,
+ <c><anno>Pid</anno></c>
+ does not exit. If it is trapping exits, the exit signal is
+ transformed into a message <c>{'EXIT', From, normal}</c>
+ and delivered to its message queue.</p>
+ <p>If <c><anno>Reason</anno></c> is the atom <c>kill</c>,
+ that is, if <c>exit(<anno>Pid</anno>, kill)</c> is called,
+ an untrappable exit signal is sent to <c><anno>Pid</anno></c>,
+ which unconditionally exits with exit reason <c>killed</c>.</p>
</desc>
</func>
<func>
<name name="external_size" arity="1"/>
- <fsummary>Calculates the maximum size for a term encoded in the Erlang external term format.</fsummary>
+ <fsummary>Calculate the maximum size for a term encoded in the Erlang
+ external term format.</fsummary>
<desc>
<p>Calculates, without doing the encoding, the maximum byte size for
a term encoded in the Erlang external term format. The following
@@ -1263,13 +1360,15 @@ b</pre>
> <input>true = Size1 =&lt; Size2.</input>
true</pre>
<p>This is equivalent to a call to:</p>
-<code>erlang:external_size(<anno>Term</anno>, [])</code>
+<code>
+erlang:external_size(<anno>Term</anno>, [])</code>
</desc>
</func>
<func>
<name name="external_size" arity="2"/>
- <fsummary>Calculates the maximum size for a term encoded in the Erlang external term format.</fsummary>
+ <fsummary>Calculate the maximum size for a term encoded in the Erlang
+ external term format.</fsummary>
<desc>
<p>Calculates, without doing the encoding, the maximum byte size for
a term encoded in the Erlang external term format. The following
@@ -1281,13 +1380,14 @@ true</pre>
true</pre>
<p>Option <c>{minor_version, <anno>Version</anno>}</c> specifies how
floats are encoded. For a detailed description, see
- <seealso marker="#term_to_binary/2">term_to_binary/2</seealso>.</p>
+ <seealso marker="#term_to_binary/2">
+ <c>term_to_binary/2</c></seealso>.</p>
</desc>
</func>
<func>
<name name="float" arity="1"/>
- <fsummary>Converts a number to a float.</fsummary>
+ <fsummary>Convert a number to a float.</fsummary>
<desc>
<p>Returns a float by converting <c><anno>Number</anno></c> to a float,
for example:</p>
@@ -1298,7 +1398,8 @@ true</pre>
<note>
<p>If used on the top level in a guard, it tests whether the
argument is a floating point number; for clarity, use
- <seealso marker="#is_float/1">is_float/1</seealso> instead.</p>
+ <seealso marker="#is_float/1"><c>is_float/1</c></seealso>
+ instead.</p>
<p>When <c>float/1</c> is used in an expression in a guard,
such as '<c>float(A) == 4.0</c>', it converts a number as
described earlier.</p>
@@ -1317,13 +1418,14 @@ true</pre>
<func>
<name name="float_to_binary" arity="2"/>
- <fsummary>Text representation of a float formatted using given options.</fsummary>
+ <fsummary>Text representation of a float formatted using specified
+ options.</fsummary>
<desc>
<p>Returns a binary corresponding to the text
representation of <c><anno>Float</anno></c> using fixed decimal
point formatting. <c><anno>Options</anno></c> behaves in the same
- way as <seealso marker="#float_to_list/2">float_to_list/2</seealso>.</p>
- <p>Examples:</p>
+ way as <seealso marker="#float_to_list/2">
+ <c>float_to_list/2</c></seealso>. Examples:</p>
<pre>
> <input>float_to_binary(7.12, [{decimals, 4}]).</input>
&lt;&lt;"7.1200">>
@@ -1343,27 +1445,29 @@ true</pre>
<func>
<name name="float_to_list" arity="2"/>
- <fsummary>Text representation of a float formatted using given options.</fsummary>
+ <fsummary>Text representation of a float formatted using specified
+ options.</fsummary>
<desc>
<p>Returns a string corresponding to the text representation
- of <c>Float</c> using fixed decimal point formatting. The
- options are as follows:</p>
+ of <c>Float</c> using fixed decimal point formatting.</p>
+ <p>Available options:</p>
<list type="bulleted">
- <item>If option <c>decimals</c> is specified, the returned value
+ <item><p>If option <c>decimals</c> is specified, the returned value
contains at most <c>Decimals</c> number of digits past the
- decimal point. If the number does not fit in the internal
- static buffer of 256 bytes, the function throws <c>badarg</c>.
+ decimal point. If the number does not fit in the internal
+ static buffer of 256 bytes, the function throws <c>badarg</c>.</p>
</item>
- <item>If option <c>compact</c> is provided, the trailing zeros
+ <item><p>If option <c>compact</c> is specified, the trailing zeros
at the end of the list are truncated. This option is only
- meaningful together with option <c>decimals</c>.
+ meaningful together with option <c>decimals</c>.</p>
</item>
- <item>If option <c>scientific</c> is provided, the float is
+ <item><p>If option <c>scientific</c> is specified, the float is
formatted using scientific notation with <c>Decimals</c>
- digits of precision.
+ digits of precision.</p>
</item>
- <item>If <c>Options</c> is <c>[]</c>, the function behaves as
- <seealso marker="#float_to_list/1">float_to_list/1</seealso>.
+ <item><p>If <c>Options</c> is <c>[]</c>, the function behaves as
+ <seealso marker="#float_to_list/1">
+ <c>float_to_list/1</c></seealso>.</p>
</item>
</list>
<p>Examples:</p>
@@ -1390,16 +1494,16 @@ true</pre>
</warning>
<p>Two types of funs have slightly different semantics:</p>
<list type="bulleted">
- <item>A fun created by <c>fun M:F/A</c> is called an
+ <item><p>A fun created by <c>fun M:F/A</c> is called an
<em>external</em> fun. Calling it will always call the
function <c>F</c> with arity <c>A</c> in the latest code for
module <c>M</c>. Notice that module <c>M</c> does not even
- need to be loaded when the fun <c>fun M:F/A</c> is created.
+ need to be loaded when the fun <c>fun M:F/A</c> is created.</p>
</item>
- <item>All other funs are called <em>local</em>. When a local fun
+ <item><p>All other funs are called <em>local</em>. When a local fun
is called, the same version of the code that created the fun
is called (even if a newer version of the module has been
- loaded).
+ loaded).</p>
</item>
</list>
<p>The following elements are always present in the list
@@ -1463,14 +1567,14 @@ true</pre>
<tag><c>{new_uniq, Uniq}</c></tag>
<item>
<p><c>Uniq</c> (a binary) is a unique value for this fun. It
- is calculated from the compiled code for the entire module.</p>
+ is calculated from the compiled code for the entire module.</p>
</item>
<tag><c>{uniq, Uniq}</c></tag>
<item>
<p><c>Uniq</c> (an integer) is a unique value for this fun.
- As from OTP R15, this integer is calculated from the
- compiled code for the entire module. Before OTP R15, this
- integer was based on only the body of the fun.</p>
+ As from Erlang/OTP R15, this integer is calculated from the
+ compiled code for the entire module. Before Erlang/OTP R15, this
+ integer was based on only the body of the fun.</p>
</item>
</taglist>
</desc>
@@ -1492,7 +1596,7 @@ true</pre>
<c>uniq</c>, and <c>pid</c>. For an external fun, the value
of any of these items is always the atom <c>undefined</c>.</p>
<p>See
- <seealso marker="#fun_info/1">erlang:fun_info/1</seealso>.</p>
+ <seealso marker="#fun_info/1"><c>erlang:fun_info/1</c></seealso>.</p>
</desc>
</func>
@@ -1507,20 +1611,24 @@ true</pre>
<func>
<name name="function_exported" arity="3"/>
- <fsummary>Checks if a function is exported and loaded.</fsummary>
+ <fsummary>Check if a function is exported and loaded.</fsummary>
<desc>
- <p>Returns <c>true</c> if the module <c><anno>Module</anno></c> is loaded
- and contains an exported function <c><anno>Function</anno>/<anno>Arity</anno></c>,
- or if there is a BIF (a built-in function implemented in C)
- with the given name, otherwise returns <c>false</c>.</p>
- <note><p>This function used to return false for built-in
- functions before the 18.0 release.</p></note>
+ <p>Returns <c>true</c> if the module <c><anno>Module</anno></c> is
+ loaded and contains an exported function
+ <c><anno>Function</anno>/<anno>Arity</anno></c>,
+ or if there is a BIF (a built-in function implemented in C)
+ with the specified name, otherwise returns <c>false</c>.</p>
+ <note>
+ <p>This function used to return <c>false</c> for BIFs
+ before Erlang/OTP 18.0.</p>
+ </note>
</desc>
</func>
<func>
<name name="garbage_collect" arity="0"/>
- <fsummary>Forces an immediate garbage collection of the calling process.</fsummary>
+ <fsummary>Force an immediate garbage collection of the calling process.
+ </fsummary>
<desc>
<p>Forces an immediate garbage collection of the
executing process. The function is not to be used unless
@@ -1535,73 +1643,76 @@ true</pre>
<func>
<name name="garbage_collect" arity="1"/>
- <fsummary>Garbage collects a process.</fsummary>
+ <fsummary>Garbage collect a process.</fsummary>
<desc>
<p>The same as
- <seealso marker="#garbage_collect/2"><c>garbage_collect(<anno>Pid</anno>, [])</c></seealso>.</p>
+ <seealso marker="#garbage_collect/2">
+ <c>garbage_collect(<anno>Pid</anno>, [])</c></seealso>.</p>
</desc>
</func>
<func>
<name name="garbage_collect" arity="2"/>
- <fsummary>Garbage collects a process.</fsummary>
+ <fsummary>Garbage collect a process.</fsummary>
<desc>
<p>Garbage collects the node local process identified by
- <c><anno>Pid</anno></c>.</p>
- <p>The available <c><anno>Option</anno></c>s are as follows:</p>
+ <c><anno>Pid</anno></c>.</p>
+ <p><c><anno>Option</anno></c>:</p>
<taglist>
<tag><c>{async, RequestId}</c></tag>
<item>The function <c>garbage_collect/2</c> returns
- the value <c>async</c> immediately after the request
- has been sent. When the request has been processed, the
- process that called this function is passed a message on
+ the value <c>async</c> immediately after the request
+ has been sent. When the request has been processed, the
+ process that called this function is passed a message on
the form <c>{garbage_collect,
<anno>RequestId</anno>, <anno>GCResult</anno>}</c>.
</item>
</taglist>
- <p>If <c><anno>Pid</anno></c> equals <c>self()</c>, and
- no <c>async</c> option has been passed, the garbage
- collection is performed at once, that is, the same as calling
- <seealso marker="#garbage_collect/0">garbage_collect/0</seealso>.
- Otherwise a request for garbage collection
- is sent to the process identified by <c><anno>Pid</anno></c>,
- and will be handled when appropriate. If no <c>async</c>
- option has been passed, the caller blocks until
- <c><anno>GCResult</anno></c> is available and can be returned.</p>
- <p><c><anno>GCResult</anno></c> informs about the result of
- the garbage collection request as follows:</p>
+ <p>If <c><anno>Pid</anno></c> equals <c>self()</c>, and
+ no <c>async</c> option has been passed, the garbage
+ collection is performed at once, that is, the same as calling
+ <seealso marker="#garbage_collect/0">
+ <c>garbage_collect/0</c></seealso>.
+ Otherwise a request for garbage collection
+ is sent to the process identified by <c><anno>Pid</anno></c>,
+ and will be handled when appropriate. If no <c>async</c>
+ option has been passed, the caller blocks until
+ <c><anno>GCResult</anno></c> is available and can be returned.</p>
+ <p><c><anno>GCResult</anno></c> informs about the result of
+ the garbage collection request as follows:</p>
<taglist>
<tag><c>true</c></tag>
<item>
- The process identified by <c><anno>Pid</anno></c> has
- been garbage collected.
- </item>
+ The process identified by <c><anno>Pid</anno></c> has
+ been garbage collected.
+ </item>
<tag><c>false</c></tag>
<item>
- No garbage collection was performed, as
- the process identified by <c><anno>Pid</anno></c>
- terminated before the request could be satisfied.
- </item>
+ No garbage collection was performed, as
+ the process identified by <c><anno>Pid</anno></c>
+ terminated before the request could be satisfied.
+ </item>
</taglist>
<p>Notice that the same caveats apply as for
- <seealso marker="#garbage_collect/0">garbage_collect/0</seealso>.</p>
+ <seealso marker="#garbage_collect/0">
+ <c>garbage_collect/0</c></seealso>.</p>
<p>Failures:</p>
<taglist>
<tag><c>badarg</c></tag>
<item>
- If <c><anno>Pid</anno></c> is not a node local process identifier.
- </item>
+ If <c><anno>Pid</anno></c> is not a node local process identifier.
+ </item>
<tag><c>badarg</c></tag>
<item>
- If <c><anno>OptionList</anno></c> is an invalid list of options.
- </item>
+ If <c><anno>OptionList</anno></c> is an invalid list of options.
+ </item>
</taglist>
</desc>
</func>
<func>
<name name="get" arity="0"/>
- <fsummary>Returns the process dictionary.</fsummary>
+ <fsummary>Return the process dictionary.</fsummary>
<desc>
<p>Returns the process dictionary as a list of
<c>{<anno>Key</anno>, <anno>Val</anno>}</c> tuples, for example:</p>
@@ -1616,12 +1727,11 @@ true</pre>
<func>
<name name="get" arity="1"/>
- <fsummary>Returns a value from the process dictionary.</fsummary>
+ <fsummary>Return a value from the process dictionary.</fsummary>
<desc>
- <p>Returns the value <c><anno>Val</anno></c> associated with <c><anno>Key</anno></c> in
- the process dictionary, or <c>undefined</c> if <c><anno>Key</anno></c>
- does not exist.</p>
- <p>Example:</p>
+ <p>Returns the value <c><anno>Val</anno></c> associated with
+ <c><anno>Key</anno></c> in the process dictionary, or <c>undefined</c>
+ if <c><anno>Key</anno></c> does not exist. Example:</p>
<pre>
> <input>put(key1, merry),</input>
<input>put(key2, lambs),</input>
@@ -1633,7 +1743,7 @@ true</pre>
<func>
<name name="get_cookie" arity="0"/>
- <fsummary>Gets the magic cookie of the local node.</fsummary>
+ <fsummary>Get the magic cookie of the local node.</fsummary>
<desc>
<p>Returns the magic cookie of the local node if the node is
alive, otherwise the atom <c>nocookie</c>.</p>
@@ -1642,9 +1752,11 @@ true</pre>
<func>
<name name="get_keys" arity="0"/>
- <fsummary>Return a list of all keys from the process dictionary</fsummary>
+ <fsummary>Return a list of all keys from the process dictionary.
+ </fsummary>
<desc>
- <p>Returns a list of keys all keys present in the process dictionary.</p>
+ <p>Returns a list of all keys present in the process dictionary,
+ for example:</p>
<pre>
> <input>put(dog, {animal,1}),</input>
<input>put(cow, {animal,2}),</input>
@@ -1653,9 +1765,10 @@ true</pre>
[dog,cow,lamb]</pre>
</desc>
</func>
+
<func>
<name name="get_keys" arity="1"/>
- <fsummary>Returns a list of keys from the process dictionary.</fsummary>
+ <fsummary>Return a list of keys from the process dictionary.</fsummary>
<desc>
<p>Returns a list of keys that are associated with the value
<c><anno>Val</anno></c> in the process dictionary, for example:</p>
@@ -1673,48 +1786,49 @@ true</pre>
<func>
<name name="get_stacktrace" arity="0"/>
- <fsummary>Gets the call stack back-trace of the last exception.</fsummary>
+ <fsummary>Get the call stack back-trace of the last exception.</fsummary>
<type name="stack_item"/>
<desc>
<p>Gets the call stack back-trace (<em>stacktrace</em>) of the
last exception in the calling process as a list of
- <c>{<anno>Module</anno>,<anno>Function</anno>,<anno>Arity</anno>,<anno>Location</anno>}</c> tuples.
- Field <c><anno>Arity</anno></c> in the first tuple can be the
+ <c>{<anno>Module</anno>,<anno>Function</anno>,<anno>Arity</anno>,<anno>Location</anno>}</c>
+ tuples. Field <c><anno>Arity</anno></c> in the first tuple can be the
argument list of that function call instead of an arity integer,
depending on the exception.</p>
<p>If there has not been any exceptions in a process, the
stacktrace is <c>[]</c>. After a code change for the process,
the stacktrace can also be reset to <c>[]</c>.</p>
- <p>The stacktrace is the same data as the <c>catch</c> operator
+ <p>The stacktrace is the same data as operator <c>catch</c>
returns, for example:</p>
- <p><c>{'EXIT',{badarg,Stacktrace}} = catch abs(x)</c></p>
- <p><c><anno>Location</anno></c> is a (possibly empty) list
+ <pre>
+{'EXIT',{badarg,Stacktrace}} = catch abs(x)</pre>
+ <p><c><anno>Location</anno></c> is a (possibly empty) list
of two-tuples that
- can indicate the location in the source code of the function.
- The first element is an atom describing the type of
- information in the second element. The following
- items can occur:</p>
- <taglist>
- <tag><c>file</c></tag>
- <item>The second element of the tuple is a string (list of
- characters) representing the file name of the source file
- of the function.
- </item>
- <tag><c>line</c></tag>
- <item>The second element of the tuple is the line number
- (an integer greater than zero) in the source file
- where the exception occurred or the function was called.
- </item>
- </taglist>
+ can indicate the location in the source code of the function.
+ The first element is an atom describing the type of
+ information in the second element. The following
+ items can occur:</p>
+ <taglist>
+ <tag><c>file</c></tag>
+ <item>The second element of the tuple is a string (list of
+ characters) representing the filename of the source file
+ of the function.
+ </item>
+ <tag><c>line</c></tag>
+ <item>The second element of the tuple is the line number
+ (an integer &gt; 0) in the source file
+ where the exception occurred or the function was called.
+ </item>
+ </taglist>
<p>See also
- <seealso marker="#error/1">erlang:error/1</seealso> and
- <seealso marker="#error/2">erlang:error/2</seealso>.</p>
+ <seealso marker="#error/1"><c>error/1</c></seealso> and
+ <seealso marker="#error/2"><c>error/2</c></seealso>.</p>
</desc>
</func>
<func>
<name name="group_leader" arity="0"/>
- <fsummary>Gets the group leader for the calling process.</fsummary>
+ <fsummary>Get the group leader for the calling process.</fsummary>
<desc>
<p>Returns the process identifier of the group leader for the
process evaluating the function.</p>
@@ -1722,14 +1836,14 @@ true</pre>
groups have a <em>group leader</em>. All I/O from the group
is channeled to the group leader. When a new process is
spawned, it gets the same group leader as the spawning
- process. Initially, at system start-up, <c>init</c> is both
+ process. Initially, at system startup, <c>init</c> is both
its own group leader and the group leader of all processes.</p>
</desc>
</func>
<func>
<name name="group_leader" arity="2"/>
- <fsummary>Sets the group leader for a process.</fsummary>
+ <fsummary>Set the group leader for a process.</fsummary>
<desc>
<p>Sets the group leader of <c><anno>Pid</anno></c>
to <c><anno>GroupLeader</anno></c>.
@@ -1737,70 +1851,72 @@ true</pre>
certain shell is to have another group leader than
<c>init</c>.</p>
<p>See also
- <seealso marker="#group_leader/0">group_leader/0</seealso>.</p>
+ <seealso marker="#group_leader/0"><c>group_leader/0</c></seealso>.</p>
</desc>
</func>
<func>
<name name="halt" arity="0"/>
- <fsummary>Halts the Erlang runtime system and indicates normal exit to the calling environment.</fsummary>
+ <fsummary>Halt the Erlang runtime system and indicate normal exit to
+ the calling environment.</fsummary>
<desc>
- <p>The same as
- <seealso marker="#halt/2"><c>halt(0, [])</c></seealso>.</p>
- <p>Example:</p>
+ <p>The same as
+ <seealso marker="#halt/2"><c>halt(0, [])</c></seealso>. Example:</p>
<pre>
> <input>halt().</input>
-os_prompt% </pre>
+os_prompt%</pre>
</desc>
</func>
<func>
<name name="halt" arity="1"/>
- <fsummary>Halts the Erlang runtime system.</fsummary>
+ <fsummary>Halt the Erlang runtime system.</fsummary>
<desc>
- <p>The same as
- <seealso marker="#halt/2"><c>halt(<anno>Status</anno>, [])</c></seealso>.</p>
- <p>Example:</p>
+ <p>The same as <seealso marker="#halt/2">
+ <c>halt(<anno>Status</anno>, [])</c></seealso>. Example:</p>
<pre>
> <input>halt(17).</input>
os_prompt% <input>echo $?</input>
17
-os_prompt% </pre>
+os_prompt%</pre>
</desc>
</func>
<func>
<name name="halt" arity="2"/>
- <fsummary>Halts the Erlang runtime system.</fsummary>
+ <fsummary>Halt the Erlang runtime system.</fsummary>
<desc>
<p><c><anno>Status</anno></c> must be a non-negative integer, a string,
- or the atom <c>abort</c>.
- Halts the Erlang runtime system. Has no return value.
- Depending on <c><anno>Status</anno></c>, the following occurs:</p>
- <taglist>
- <tag>integer()</tag>
- <item>The runtime system exits with integer value
- <c><anno>Status</anno></c>
- as status code to the calling environment (OS).
- </item>
- <tag>string()</tag>
- <item>An Erlang crash dump is produced with <c><anno>Status</anno></c>
- as slogan. Then the runtime system exits with status code <c>1</c>.
- </item>
- <tag><c>abort</c></tag>
- <item>
- The runtime system aborts producing a core dump, if that is
- enabled in the OS.
- </item>
- </taglist>
- <note><p>On many platforms, the OS supports only status
- codes 0-255. A too large status code will be truncated by clearing
- the high bits.</p></note>
- <p>For integer <c><anno>Status</anno></c>, the Erlang runtime system
+ or the atom <c>abort</c>.
+ Halts the Erlang runtime system. Has no return value.
+ Depending on <c><anno>Status</anno></c>, the following occurs:</p>
+ <taglist>
+ <tag>integer()</tag>
+ <item>The runtime system exits with integer value
+ <c><anno>Status</anno></c>
+ as status code to the calling environment (OS).
+ </item>
+ <tag>string()</tag>
+ <item>An Erlang crash dump is produced with <c><anno>Status</anno></c>
+ as slogan. Then the runtime system exits with status code <c>1</c>.
+ Note that only code points in the range 0-255 may be used
+ and the string will be truncated if longer than 200 characters.
+ </item>
+ <tag><c>abort</c></tag>
+ <item>The runtime system aborts producing a core dump, if that is
+ enabled in the OS.
+ </item>
+ </taglist>
+ <note>
+ <p>On many platforms, the OS supports only status
+ codes 0-255. A too large status code is truncated by clearing
+ the high bits.</p>
+ </note>
+ <p>For integer <c><anno>Status</anno></c>, the Erlang runtime system
closes all ports and allows async threads to finish their
operations before exiting. To exit without such flushing, use
- <c><anno>Option</anno></c> as <c>{flush,false}</c>.</p>
- <p>For statuses <c>string()</c> and <c>abort</c>, option
+ <c><anno>Option</anno></c> as <c>{flush,false}</c>.</p>
+ <p>For statuses <c>string()</c> and <c>abort</c>, option
<c>flush</c> is ignored and flushing is <em>not</em> done.</p>
</desc>
</func>
@@ -1812,13 +1928,15 @@ os_prompt% </pre>
<p>Returns a hash value for <c><anno>Term</anno></c> within the range
<c>1..<anno>Range</anno></c>. The maximum range is 1..2^27-1.</p>
<warning>
- <p>This BIF is deprecated, as the hash value can differ on
- different architectures. The hash values for integer
- terms higher than 2^27 and large binaries are
+ <p><em>This BIF is deprecated, as the hash value can differ on
+ different architectures.</em> The hash values for integer
+ terms &gt; 2^27 and large binaries are
poor. The BIF is retained for backward compatibility
reasons (it can have been used to hash records into a file),
but all new code is to use one of the BIFs
- <c>erlang:phash/2</c> or <c>erlang:phash2/1,2</c> instead.</p>
+ <seealso marker="#phash/2"><c>erlang:phash/2</c></seealso> or
+ <seealso marker="#phash2/1"><c>erlang:phash2/1,2</c></seealso>
+ instead.</p>
</warning>
</desc>
</func>
@@ -1840,7 +1958,7 @@ os_prompt% </pre>
<func>
<name name="hibernate" arity="3"/>
- <fsummary>Hibernates a process until a message is sent to it.</fsummary>
+ <fsummary>Hibernate a process until a message is sent to it.</fsummary>
<desc>
<p>Puts the calling process into a wait state where its memory
allocation has been reduced as much as possible. This is
@@ -1848,15 +1966,15 @@ os_prompt% </pre>
soon.</p>
<p>The process is awaken when a message is sent to it, and control
resumes in <c><anno>Module</anno>:<anno>Function</anno></c> with
- the arguments given by <c><anno>Args</anno></c> with the call
+ the arguments specified by <c><anno>Args</anno></c> with the call
stack emptied, meaning that the process terminates when that
function returns. Thus <c>erlang:hibernate/3</c> never
returns to its caller.</p>
<p>If the process has any message in its message queue,
the process is awakened immediately in the same way as
described earlier.</p>
- <p>In more technical terms, what <c>erlang:hibernate/3</c> does
- is the following. It discards the call stack for the process,
+ <p>In more technical terms, <c>erlang:hibernate/3</c>
+ discards the call stack for the process,
and then garbage collects the process. After this,
all live data is in one continuous heap. The heap
is then shrunken to the exact same size as the live data
@@ -1868,11 +1986,12 @@ os_prompt% </pre>
size is changed to a size not smaller than the minimum heap
size.</p>
<p>Notice that emptying the call stack means that any surrounding
- <c>catch</c> is removed and must be reinserted after
+ <c>catch</c> is removed and must be re-inserted after
hibernation. One effect of this is that processes started
using <c>proc_lib</c> (also indirectly, such as
<c>gen_server</c> processes), are to use
- <seealso marker="stdlib:proc_lib#hibernate/3">proc_lib:hibernate/3</seealso>
+ <seealso marker="stdlib:proc_lib#hibernate/3">
+ <c>proc_lib:hibernate/3</c></seealso>
instead, to ensure that the exception handler continues to work
when the process wakes up.</p>
</desc>
@@ -1880,15 +1999,16 @@ os_prompt% </pre>
<func>
<name name="insert_element" arity="3"/>
- <fsummary>Inserts an element at index in a tuple.</fsummary>
- <type_desc variable="Index">1..tuple_size(<anno>Tuple1</anno>) + 1</type_desc>
+ <fsummary>Insert an element at index in a tuple.</fsummary>
+ <type_desc variable="Index">1..tuple_size(<anno>Tuple1</anno>)
+ + 1</type_desc>
<desc>
<p>Returns a new tuple with element <c><anno>Term</anno></c>
- inserted at position
- <c><anno>Index</anno></c> in tuple <c><anno>Tuple1</anno></c>.
- All elements from position <c><anno>Index</anno></c> and upwards are
- pushed one step higher in the new tuple <c><anno>Tuple2</anno></c>.</p>
- <p>Example:</p>
+ inserted at position
+ <c><anno>Index</anno></c> in tuple <c><anno>Tuple1</anno></c>.
+ All elements from position <c><anno>Index</anno></c> and upwards are
+ pushed one step higher in the new tuple <c><anno>Tuple2</anno></c>.
+ Example:</p>
<pre>
> <input>erlang:insert_element(2, {one, two, three}, new).</input>
{one,new,two,three}</pre>
@@ -1912,8 +2032,8 @@ os_prompt% </pre>
<fsummary>Text representation of an integer.</fsummary>
<desc>
<p>Returns a binary corresponding to the text
- representation of <c><anno>Integer</anno></c> in base
- <c><anno>Base</anno></c>, for example:</p>
+ representation of <c><anno>Integer</anno></c> in base
+ <c><anno>Base</anno></c>, for example:</p>
<pre>
> <input>integer_to_binary(1023, 16).</input>
&lt;&lt;"3FF">></pre>
@@ -1946,8 +2066,21 @@ os_prompt% </pre>
</func>
<func>
+ <name name="iolist_size" arity="1"/>
+ <fsummary>Size of an iolist.</fsummary>
+ <desc>
+ <p>Returns an integer, that is the size in bytes,
+ of the binary that would be the result of
+ <c>iolist_to_binary(<anno>Item</anno>)</c>, for example:</p>
+ <pre>
+> <input>iolist_size([1,2|&lt;&lt;3,4>>]).</input>
+4</pre>
+ </desc>
+ </func>
+
+ <func>
<name name="iolist_to_binary" arity="1"/>
- <fsummary>Converts an iolist to a binary.</fsummary>
+ <fsummary>Convert an iolist to a binary.</fsummary>
<desc>
<p>Returns a binary that is made from the integers and
binaries in <c><anno>IoListOrBinary</anno></c>, for example:</p>
@@ -1964,21 +2097,8 @@ os_prompt% </pre>
</func>
<func>
- <name name="iolist_size" arity="1"/>
- <fsummary>Size of an iolist.</fsummary>
- <desc>
- <p>Returns an integer that is the size in bytes
- of the binary that would be the result of
- <c>iolist_to_binary(<anno>Item</anno>)</c>, for example:</p>
- <pre>
-> <input>iolist_size([1,2|&lt;&lt;3,4>>]).</input>
-4</pre>
- </desc>
- </func>
-
- <func>
<name name="is_alive" arity="0"/>
- <fsummary>Checks whether the local node is alive.</fsummary>
+ <fsummary>Check whether the local node is alive.</fsummary>
<desc>
<p>Returns <c>true</c> if the local node is alive (that is, if
the node can be part of a distributed system), otherwise
@@ -1988,7 +2108,7 @@ os_prompt% </pre>
<func>
<name name="is_atom" arity="1"/>
- <fsummary>Checks whether a term is an atom.</fsummary>
+ <fsummary>Check whether a term is an atom.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is an atom,
otherwise <c>false</c>.</p>
@@ -1998,18 +2118,18 @@ os_prompt% </pre>
<func>
<name name="is_binary" arity="1"/>
- <fsummary>Checks whether a term is a binary.</fsummary>
+ <fsummary>Check whether a term is a binary.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is a binary,
otherwise <c>false</c>.</p>
- <p>A binary always contains a complete number of bytes.</p>
+ <p>A binary always contains a complete number of bytes.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
<name name="is_bitstring" arity="1"/>
- <fsummary>Checks whether a term is a bitstring.</fsummary>
+ <fsummary>Check whether a term is a bitstring.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is a
bitstring (including a binary), otherwise <c>false</c>.</p>
@@ -2019,7 +2139,7 @@ os_prompt% </pre>
<func>
<name name="is_boolean" arity="1"/>
- <fsummary>Checks whether a term is a boolean.</fsummary>
+ <fsummary>Check whether a term is a boolean.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is the
atom <c>true</c> or the atom <c>false</c> (that is, a boolean).
@@ -2030,18 +2150,18 @@ os_prompt% </pre>
<func>
<name name="is_builtin" arity="3"/>
- <fsummary>Checks if a function is a BIF implemented in C.</fsummary>
+ <fsummary>Check if a function is a BIF implemented in C.</fsummary>
<desc>
<p>This BIF is useful for builders of cross-reference tools.</p>
<p>Returns <c>true</c> if
<c><anno>Module</anno>:<anno>Function</anno>/<anno>Arity</anno></c>
- is a BIF implemented in C, otherwise <c>false</c>.</p>
+ is a BIF implemented in C, otherwise <c>false</c>.</p>
</desc>
</func>
<func>
<name name="is_float" arity="1"/>
- <fsummary>Checks whether a term is a float.</fsummary>
+ <fsummary>Check whether a term is a float.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is a floating point
number, otherwise <c>false</c>.</p>
@@ -2051,7 +2171,7 @@ os_prompt% </pre>
<func>
<name name="is_function" arity="1"/>
- <fsummary>Checks whether a term is a fun.</fsummary>
+ <fsummary>Check whether a term is a fun.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is a fun, otherwise
<c>false</c>.</p>
@@ -2061,7 +2181,8 @@ os_prompt% </pre>
<func>
<name name="is_function" arity="2"/>
- <fsummary>Checks whether a term is a fun with a given arity.</fsummary>
+ <fsummary>Check whether a term is a fun with a specified given arity.
+ </fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is a fun that can be
applied with <c><anno>Arity</anno></c> number of arguments, otherwise
@@ -2072,7 +2193,7 @@ os_prompt% </pre>
<func>
<name name="is_integer" arity="1"/>
- <fsummary>Checks whether a term is an integer.</fsummary>
+ <fsummary>Check whether a term is an integer.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is an integer,
otherwise <c>false</c>.</p>
@@ -2082,7 +2203,7 @@ os_prompt% </pre>
<func>
<name name="is_list" arity="1"/>
- <fsummary>Checks whether a term is a list.</fsummary>
+ <fsummary>Check whether a term is a list.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is a list with
zero or more elements, otherwise <c>false</c>.</p>
@@ -2092,7 +2213,7 @@ os_prompt% </pre>
<func>
<name name="is_map" arity="1"/>
- <fsummary>Checks whether a term is a map.</fsummary>
+ <fsummary>Check whether a term is a map.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is a map,
otherwise <c>false</c>.</p>
@@ -2102,7 +2223,7 @@ os_prompt% </pre>
<func>
<name name="is_number" arity="1"/>
- <fsummary>Checks whether a term is a number.</fsummary>
+ <fsummary>Check whether a term is a number.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is an integer or a
floating point number. Otherwise returns <c>false</c>.</p>
@@ -2112,7 +2233,7 @@ os_prompt% </pre>
<func>
<name name="is_pid" arity="1"/>
- <fsummary>Checks whether a term is a process identifier.</fsummary>
+ <fsummary>Check whether a term is a process identifier.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is a process
identifier, otherwise <c>false</c>.</p>
@@ -2122,7 +2243,7 @@ os_prompt% </pre>
<func>
<name name="is_port" arity="1"/>
- <fsummary>Checks whether a term is a port.</fsummary>
+ <fsummary>Check whether a term is a port.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is a port identifier,
otherwise <c>false</c>.</p>
@@ -2132,26 +2253,26 @@ os_prompt% </pre>
<func>
<name name="is_process_alive" arity="1"/>
- <fsummary>Checks whether a process is alive.</fsummary>
+ <fsummary>Check whether a process is alive.</fsummary>
<desc>
- <p><c><anno>Pid</anno></c> must refer to a process at the local node.</p>
+ <p><c><anno>Pid</anno></c> must refer to a process at the local
+ node.</p>
<p>Returns <c>true</c> if the process exists and is alive, that
is, is not exiting and has not exited. Otherwise returns
- <c>false</c>.
- </p>
+ <c>false</c>.</p>
</desc>
</func>
<func>
<name name="is_record" arity="2"/>
- <fsummary>Checks whether a term appears to be a record.</fsummary>
+ <fsummary>Check whether a term appears to be a record.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is a tuple and its
first element is <c><anno>RecordTag</anno></c>.
Otherwise returns <c>false</c>.</p>
<note>
<p>Normally the compiler treats calls to <c>is_record/2</c>
- specially. It emits code to verify that <c><anno>Term</anno></c>
+ especially. It emits code to verify that <c><anno>Term</anno></c>
is a tuple, that its first element is
<c><anno>RecordTag</anno></c>, and that the
size is correct. However, if <c><anno>RecordTag</anno></c> is
@@ -2165,7 +2286,7 @@ os_prompt% </pre>
<func>
<name name="is_record" arity="3"/>
- <fsummary>Checks whether a term appears to be a record.</fsummary>
+ <fsummary>Check whether a term appears to be a record.</fsummary>
<desc>
<p><c><anno>RecordTag</anno></c> must be an atom.</p>
<p>Returns <c>true</c> if
@@ -2184,7 +2305,7 @@ os_prompt% </pre>
<func>
<name name="is_reference" arity="1"/>
- <fsummary>Checks whether a term is a reference.</fsummary>
+ <fsummary>Check whether a term is a reference.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is a reference,
otherwise <c>false</c>.</p>
@@ -2194,7 +2315,7 @@ os_prompt% </pre>
<func>
<name name="is_tuple" arity="1"/>
- <fsummary>Checks whether a term is a tuple.</fsummary>
+ <fsummary>Check whether a term is a tuple.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is a tuple,
otherwise <c>false</c>.</p>
@@ -2216,7 +2337,7 @@ os_prompt% </pre>
<func>
<name name="link" arity="1"/>
- <fsummary>Creates a link to another process (or port).</fsummary>
+ <fsummary>Create a link to another process (or port).</fsummary>
<desc>
<p>Creates a link between the calling process and another
process (or port) <c><anno>PidOrPort</anno></c>, if there is
@@ -2226,34 +2347,34 @@ os_prompt% </pre>
<p>If <c><anno>PidOrPort</anno></c> does not exist, the behavior
of the BIF
depends on if the calling process is trapping exits or not (see
- <seealso marker="#process_flag/2">process_flag/2</seealso>):</p>
+ <seealso marker="#process_flag/2">
+ <c>process_flag/2</c></seealso>):</p>
<list type="bulleted">
- <item>If the calling process is not trapping exits, and
- checking <c><anno>PidOrPort</anno></c> is cheap
- (that is, if <c><anno>PidOrPort</anno></c>
- is local), <c>link/1</c> fails with reason <c>noproc</c>.</item>
- <item>Otherwise, if the calling process is trapping exits,
- and/or <c><anno>PidOrPort</anno></c> is remote, <c>link/1</c>
- returns <c>true</c>, but an exit signal with reason <c>noproc</c>
- is sent to the calling process.</item>
+ <item><p>If the calling process is not trapping exits, and
+ checking <c><anno>PidOrPort</anno></c> is cheap
+ (that is, if <c><anno>PidOrPort</anno></c>
+ is local), <c>link/1</c> fails with reason <c>noproc</c>.</p></item>
+ <item><p>Otherwise, if the calling process is trapping exits,
+ and/or <c><anno>PidOrPort</anno></c> is remote, <c>link/1</c>
+ returns <c>true</c>, but an exit signal with reason <c>noproc</c>
+ is sent to the calling process.</p></item>
</list>
</desc>
</func>
<func>
<name name="list_to_atom" arity="1"/>
- <fsummary>Converts from text representation to an atom.</fsummary>
+ <fsummary>Convert from text representation to an atom.</fsummary>
<desc>
<p>Returns the atom whose text representation is
<c><anno>String</anno></c>.</p>
- <p><c><anno>String</anno></c> can only contain ISO-latin-1
- characters (that is,
- numbers less than 256) as the implementation does not
- allow unicode characters equal to or above 256 in atoms.
- For more information on Unicode support in atoms, see
- <seealso marker="erl_ext_dist#utf8_atoms">note on UTF-8
+ <p><c><anno>String</anno></c> can only contain ISO-latin-1
+ characters (that is, numbers &lt; 256) as the implementation does not
+ allow Unicode characters equal to or above 256 in atoms.
+ For more information on Unicode support in atoms, see
+ <seealso marker="erl_ext_dist#utf8_atoms">note on UTF-8
encoded atoms</seealso>
- in Section "External Term Format" in the User's Guide.</p>
+ in section "External Term Format" in the User's Guide.</p>
<p>Example:</p>
<pre>
> <input>list_to_atom("Erlang").</input>
@@ -2263,7 +2384,7 @@ os_prompt% </pre>
<func>
<name name="list_to_binary" arity="1"/>
- <fsummary>Converts a list to a binary.</fsummary>
+ <fsummary>Convert a list to a binary.</fsummary>
<desc>
<p>Returns a binary that is made from the integers and
binaries in <c><anno>IoList</anno></c>, for example:</p>
@@ -2281,13 +2402,13 @@ os_prompt% </pre>
<func>
<name name="list_to_bitstring" arity="1"/>
- <fsummary>Converts a list to a bitstring.</fsummary>
+ <fsummary>Convert a list to a bitstring.</fsummary>
<type name="bitstring_list"/>
<desc>
<p>Returns a bitstring that is made from the integers and
bitstrings in <c><anno>BitstringList</anno></c>. (The last tail in
- <c><anno>BitstringList</anno></c> is allowed to be a bitstring.)</p>
- <p>Example:</p>
+ <c><anno>BitstringList</anno></c> is allowed to be a bitstring.)
+ Example:</p>
<pre>
> <input>Bin1 = &lt;&lt;1,2,3&gt;&gt;.</input>
&lt;&lt;1,2,3&gt;&gt;
@@ -2302,7 +2423,7 @@ os_prompt% </pre>
<func>
<name name="list_to_existing_atom" arity="1"/>
- <fsummary>Converts from text representation to an atom.</fsummary>
+ <fsummary>Convert from text representation to an atom.</fsummary>
<desc>
<p>Returns the atom whose text representation is
<c><anno>String</anno></c>,
@@ -2314,7 +2435,7 @@ os_prompt% </pre>
<func>
<name name="list_to_float" arity="1"/>
- <fsummary>Converts from text representation to a float.</fsummary>
+ <fsummary>Convert from text representation to a float.</fsummary>
<desc>
<p>Returns the float whose text representation is
<c><anno>String</anno></c>, for example:</p>
@@ -2328,7 +2449,7 @@ os_prompt% </pre>
<func>
<name name="list_to_integer" arity="1"/>
- <fsummary>Converts from text representation to an integer.</fsummary>
+ <fsummary>Convert from text representation to an integer.</fsummary>
<desc>
<p>Returns an integer whose text representation is
<c><anno>String</anno></c>, for example:</p>
@@ -2342,7 +2463,7 @@ os_prompt% </pre>
<func>
<name name="list_to_integer" arity="2"/>
- <fsummary>Converts from text representation to an integer.</fsummary>
+ <fsummary>Convert from text representation to an integer.</fsummary>
<desc>
<p>Returns an integer whose text representation in base
<c><anno>Base</anno></c> is <c><anno>String</anno></c>,
@@ -2357,7 +2478,7 @@ os_prompt% </pre>
<func>
<name name="list_to_pid" arity="1"/>
- <fsummary>Converts from text representation to a pid.</fsummary>
+ <fsummary>Convert from text representation to a pid.</fsummary>
<desc>
<p>Returns a process identifier whose text representation is a
<c><anno>String</anno></c>, for example:</p>
@@ -2368,14 +2489,14 @@ os_prompt% </pre>
representation of a process identifier.</p>
<warning>
<p>This BIF is intended for debugging and is not to be used
- in application programs.</p>
+ in application programs.</p>
</warning>
</desc>
</func>
<func>
<name name="list_to_tuple" arity="1"/>
- <fsummary>Converts a list to a tuple.</fsummary>
+ <fsummary>Convert a list to a tuple.</fsummary>
<desc>
<p>Returns a tuple corresponding to <c><anno>List</anno></c>,
for example</p>
@@ -2388,7 +2509,7 @@ os_prompt% </pre>
<func>
<name name="load_module" arity="2"/>
- <fsummary>Loads object code for a module.</fsummary>
+ <fsummary>Load object code for a module.</fsummary>
<desc>
<p>If <c><anno>Binary</anno></c> contains the object code for module
<c><anno>Module</anno></c>, this BIF loads that object code. If
@@ -2399,23 +2520,21 @@ os_prompt% </pre>
that code.</p>
<p>Returns either <c>{module, <anno>Module</anno>}</c>, or
<c>{error, <anno>Reason</anno>}</c> if loading fails.
- <c><anno>Reason</anno></c> is any of the following:</p>
+ <c><anno>Reason</anno></c> is one of the following:</p>
<taglist>
<tag><c>badfile</c></tag>
- <item>
- <p>The object code in <c><anno>Binary</anno></c> has an
- incorrect format <em>or</em> the object code contains code
- for another module than <c><anno>Module</anno></c>.</p>
+ <item>The object code in <c><anno>Binary</anno></c> has an
+ incorrect format <em>or</em> the object code contains code
+ for another module than <c><anno>Module</anno></c>.
</item>
<tag><c>not_purged</c></tag>
- <item>
- <p><c><anno>Binary</anno></c> contains a module that cannot be
- loaded because old code for this module already exists.</p>
+ <item><c><anno>Binary</anno></c> contains a module that cannot be
+ loaded because old code for this module already exists.
</item>
</taglist>
<warning>
<p>This BIF is intended for the code server (see
- <seealso marker="kernel:code">code(3)</seealso>)
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>)
and is not to be used elsewhere.</p>
</warning>
</desc>
@@ -2423,33 +2542,36 @@ os_prompt% </pre>
<func>
<name name="load_nif" arity="2"/>
- <fsummary>Loads NIF library.</fsummary>
+ <fsummary>Load NIF library.</fsummary>
<desc>
<note>
- <p>Before OTP R14B, NIFs were an
- experimental feature. Versions before OTP R14B can
- have different and possibly incompatible NIF semantics and
- interfaces. For example, in OTP R13B03 the return value on
- failure was <c>{error,Reason,Text}</c>.</p>
+ <p>Before Erlang/OTP R14B, NIFs were an
+ experimental feature. Versions before Erlang/OTP R14B can
+ have different and possibly incompatible NIF semantics and
+ interfaces. For example, in Erlang/OTP R13B03 the return value on
+ failure was <c>{error,Reason,Text}</c>.</p>
</note>
<p>Loads and links a dynamic library containing native
- implemented functions (NIFs) for a module. <c><anno>Path</anno></c>
- is a file path to the shareable object/dynamic library file minus
- the OS-dependent file extension (<c>.so</c> for Unix and
- <c>.dll</c> for Windows. For information on how to
- implement a NIF library, see
- <seealso marker="erl_nif">erl_nif</seealso>.</p>
+ implemented functions (NIFs) for a module. <c><anno>Path</anno></c>
+ is a file path to the shareable object/dynamic library file minus
+ the OS-dependent file extension (<c>.so</c> for Unix and
+ <c>.dll</c> for Windows). Notice that on most OSs the library has
+ to have a different name on disc when an upgrade of the nif is
+ done. If the name is the same, but the contents differ, the
+ old library may be loaded instead. For information on how to
+ implement a NIF library, see
+ <seealso marker="erl_nif"><c>erl_nif(3)</c></seealso>.</p>
<p><c><anno>LoadInfo</anno></c> can be any term. It is passed on to
- the library as part of the initialization. A good practice is
- to include a module version number to support future code
- upgrade scenarios.</p>
+ the library as part of the initialization. A good practice is
+ to include a module version number to support future code
+ upgrade scenarios.</p>
<p>The call to <c>load_nif/2</c> must be made
- <em>directly</em> from the Erlang code of the module that the
- NIF library belongs to. It returns either <c>ok</c>, or
- <c>{error,{<anno>Reason</anno>,Text}}</c> if loading fails.
- <c><anno>Reason</anno></c> is one of the following atoms
- while <c><anno>Text</anno></c> is a human readable string that
- can give more information about the failure:</p>
+ <em>directly</em> from the Erlang code of the module that the
+ NIF library belongs to. It returns either <c>ok</c>, or
+ <c>{error,{<anno>Reason</anno>,Text}}</c> if loading fails.
+ <c><anno>Reason</anno></c> is one of the following atoms
+ while <c><anno>Text</anno></c> is a human readable string that
+ can give more information about the failure:</p>
<taglist>
<tag><c>load_failed</c></tag>
<item>The OS failed to load the NIF library.
@@ -2472,11 +2594,12 @@ os_prompt% </pre>
<func>
<name name="loaded" arity="0"/>
- <fsummary>Lists all loaded modules.</fsummary>
+ <fsummary>List all loaded modules.</fsummary>
<desc>
<p>Returns a list of all loaded Erlang modules (current and
old code), including preloaded modules.</p>
- <p>See also <seealso marker="kernel:code">code(3)</seealso>.</p>
+ <p>See also <seealso marker="kernel:code">
+ <c>code(3)</c></seealso>.</p>
</desc>
</func>
@@ -2497,13 +2620,13 @@ os_prompt% </pre>
<func>
<name name="localtime_to_universaltime" arity="1"/>
- <fsummary>Converts from local to Universal Time Coordinated (UTC) date and time.</fsummary>
+ <fsummary>Convert from local to Universal Time Coordinated (UTC) date
+ and time.</fsummary>
<desc>
<p>Converts local date and time to Universal Time Coordinated
(UTC), if supported by the underlying OS. Otherwise
no conversion is done and <c><anno>Localtime</anno></c>
- is returned.</p>
- <p>Example:</p>
+ is returned. Example:</p>
<pre>
> <input>erlang:localtime_to_universaltime({{1996,11,6},{14,45,17}}).</input>
{{1996,11,6},{13,45,17}}</pre>
@@ -2514,15 +2637,16 @@ os_prompt% </pre>
<func>
<name name="localtime_to_universaltime" arity="2"/>
- <fsummary>Converts from local to Universal Time Coordinated (UTC) date and time.</fsummary>
+ <fsummary>Convert from local to Universal Time Coordinated (UTC) date
+ and time.</fsummary>
<desc>
<p>Converts local date and time to Universal Time Coordinated
(UTC) as <c>erlang:localtime_to_universaltime/1</c>,
but the caller decides if Daylight Saving Time is active.</p>
- <p>If <c><anno>IsDst</anno> == true</c>, <c><anno>Localtime</anno></c> is
- during Daylight Saving Time, if <c><anno>IsDst</anno> == false</c> it is
- not. If <c><anno>IsDst</anno> == undefined</c>, the underlying OS can
- guess, which is the same as calling
+ <p>If <c><anno>IsDst</anno> == true</c>, <c><anno>Localtime</anno></c>
+ is during Daylight Saving Time, if <c><anno>IsDst</anno> == false</c>
+ it is not. If <c><anno>IsDst</anno> == undefined</c>, the underlying
+ OS can guess, which is the same as calling
<c>erlang:localtime_to_universaltime(<anno>Localtime</anno>)</c>.</p>
<p>Examples:</p>
<pre>
@@ -2539,24 +2663,27 @@ os_prompt% </pre>
<func>
<name name="make_ref" arity="0"/>
- <fsummary>Returns a unique reference.</fsummary>
+ <fsummary>Return a unique reference.</fsummary>
<desc>
- <p>Returns a <seealso marker="doc/efficiency_guide:advanced#unique_references">unique
- reference</seealso>. The reference is unique among
- connected nodes.</p>
- <warning><p>Known issue: When a node is restarted multiple
- times with the same node name, references created
- on a newer node can be mistaken for a reference
- created on an older node with the same node name.</p></warning>
+ <p>Returns a
+ <seealso marker="doc/efficiency_guide:advanced#unique_references">
+ unique reference</seealso>. The reference is unique among
+ connected nodes.</p>
+ <warning>
+ <p>Known issue: When a node is restarted multiple
+ times with the same node name, references created
+ on a newer node can be mistaken for a reference
+ created on an older node with the same node name.</p>
+ </warning>
</desc>
</func>
<func>
<name name="make_tuple" arity="2"/>
- <fsummary>Creates a new tuple of a given arity.</fsummary>
+ <fsummary>Create a new tuple of a specified arity.</fsummary>
<desc>
- <p>Creates a new tuple of the given <c><anno>Arity</anno></c>, where all
- elements are <c><anno>InitialValue</anno></c>, for example:</p>
+ <p>Creates a new tuple of the specified <c><anno>Arity</anno></c>, where
+ all elements are <c><anno>InitialValue</anno></c>, for example:</p>
<pre>
> <input>erlang:make_tuple(4, []).</input>
{[],[],[],[]}</pre>
@@ -2565,26 +2692,25 @@ os_prompt% </pre>
<func>
<name name="make_tuple" arity="3"/>
- <fsummary>Creates a new tuple with given arity and contents.</fsummary>
+ <fsummary>Create a new tuple with specifed arity and contents.</fsummary>
<desc>
<p>Creates a tuple of size <c><anno>Arity</anno></c>, where each element
has value <c><anno>DefaultValue</anno></c>, and then fills in
- values from <c><anno>InitList</anno></c>.
+ values from <c><anno>InitList</anno></c>.
Each list element in <c><anno>InitList</anno></c>
- must be a two-tuple, where the first element is a position in the
- newly created tuple and the second element is any term. If a
- position occurs more than once in the list, the term corresponding
- to the last occurrence is used.</p>
- <p>Example:</p>
+ must be a two-tuple, where the first element is a position in the
+ newly created tuple and the second element is any term. If a
+ position occurs more than once in the list, the term corresponding
+ to the last occurrence is used. Example:</p>
<pre>
> <input>erlang:make_tuple(5, [], [{2,ignored},{5,zz},{2,aa}]).</input>
-{{[],aa,[],[],zz}</pre>
+{[],aa,[],[],zz}</pre>
</desc>
</func>
<func>
<name name="map_size" arity="1"/>
- <fsummary>Returns the size of a map.</fsummary>
+ <fsummary>Return the size of a map.</fsummary>
<desc>
<p>Returns an integer, which is the number of key-value pairs
in <c><anno>Map</anno></c>, for example:</p>
@@ -2596,33 +2722,75 @@ os_prompt% </pre>
</func>
<func>
+ <name name="match_spec_test" arity="3"/>
+ <fsummary>Test that a match specification works.</fsummary>
+ <desc>
+ <p>Tests a match specification used in calls to
+ <seealso marker="stdlib:ets#select/2"><c>ets:select/2</c></seealso>
+ and <seealso marker="#trace_pattern/3">
+ <c>erlang:trace_pattern/3</c></seealso>.
+ The function tests both a match specification for "syntactic"
+ correctness and runs the match specification against the object. If
+ the match specification contains errors, the tuple <c>{error,
+ Errors}</c> is returned, where <c>Errors</c> is a list of natural
+ language descriptions of what was wrong with the match
+ specification.</p>
+ <p>If <c><anno>Type</anno></c> is <c>table</c>, the object to match
+ against is to be a tuple. The function then returns
+ <c>{ok,Result,[],Warnings}</c>, where <c>Result</c> is what would
+ have been the result in a real <c>ets:select/2</c> call, or
+ <c>false</c> if the match specification does not match the object
+ tuple.</p>
+ <p>If <c><anno>Type</anno></c> is <c>trace</c>, the object to match
+ against is to be a list. The function returns
+ <c>{ok, Result, Flags, Warnings}</c>, where <c>Result</c> is one of
+ the following:</p>
+ <list type="bulleted">
+ <item><c>true</c> if a trace message is to be emitted</item>
+ <item><c>false</c> if a trace message is not to be emitted</item>
+ <item>The message term to be appended to the trace message</item>
+ </list>
+ <p><c>Flags</c> is a list containing all the trace flags to be enabled,
+ currently this is only <c>return_trace</c>.</p>
+ <p>This is a useful debugging and test tool, especially when writing
+ complicated match specifications.</p>
+ <p>See also
+ <seealso marker="stdlib:ets#test_ms/2"><c>ets:test_ms/2</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="max" arity="2"/>
- <fsummary>Returns the largest of two terms.</fsummary>
+ <fsummary>Return the largest of two terms.</fsummary>
<desc>
<p>Returns the largest of <c><anno>Term1</anno></c> and
<c><anno>Term2</anno></c>.
- If the terms are equal, <c><anno>Term1</anno></c> is returned.</p>
+ If the terms are equal, <c><anno>Term1</anno></c> is returned.</p>
</desc>
</func>
<func>
<name name="md5" arity="1"/>
- <fsummary>Computes an MD5 message digest.</fsummary>
+ <fsummary>Compute an MD5 message digest.</fsummary>
<desc>
<p>Computes an MD5 message digest from <c><anno>Data</anno></c>, where
the length of the digest is 128 bits (16 bytes).
<c><anno>Data</anno></c>
is a binary or a list of small integers and binaries.</p>
- <p>For more information about MD5, see RFC 1321 - The
- MD5 Message-Digest Algorithm.</p>
- <warning><p>The MD5 Message-Digest Algorithm is <em>not</em> considered
- safe for code-signing or software-integrity purposes.</p></warning>
+ <p>For more information about MD5, see
+ <url href="https://www.ietf.org/rfc/rfc1321.txt">
+ RFC 1321 - The MD5 Message-Digest Algorithm</url>.</p>
+ <warning>
+ <p>The MD5 Message-Digest Algorithm is <em>not</em> considered
+ safe for code-signing or software-integrity purposes.</p>
+ </warning>
</desc>
</func>
<func>
<name name="md5_final" arity="1"/>
- <fsummary>Finishes the update of an MD5 context and returns the computed MD5 message digest.</fsummary>
+ <fsummary>Finish the update of an MD5 context and return the computed
+ MD5 message digest.</fsummary>
<desc>
<p>Finishes the update of an MD5 <c><anno>Context</anno></c> and returns
the computed <c>MD5</c> message digest.</p>
@@ -2631,18 +2799,19 @@ os_prompt% </pre>
<func>
<name name="md5_init" arity="0"/>
- <fsummary>Creates an MD5 context.</fsummary>
+ <fsummary>Create an MD5 context.</fsummary>
<desc>
- <p>Creates an MD5 context, to be used in subsequent calls to
+ <p>Creates an MD5 context, to be used in the following calls to
<c>md5_update/2</c>.</p>
</desc>
</func>
<func>
<name name="md5_update" arity="2"/>
- <fsummary>Updates an MD5 context with data and returns a new context.</fsummary>
+ <fsummary>Update an MD5 context with data and return a new context.
+ </fsummary>
<desc>
- <p>Updates an MD5 <c><anno>Context</anno></c> with
+ <p>Update an MD5 <c><anno>Context</anno></c> with
<c><anno>Data</anno></c> and returns a
<c><anno>NewContext</anno></c>.</p>
</desc>
@@ -2658,7 +2827,7 @@ os_prompt% </pre>
element is a tuple <c>{Type, Size}</c>. The first element
<c><anno>Type</anno></c> is an atom describing memory type. The second
element <c><anno>Size</anno></c> is the memory size in bytes.</p>
- <p>The memory types are as follows:</p>
+ <p>Memory types:</p>
<taglist>
<tag><c>total</c></tag>
<item>
@@ -2710,7 +2879,7 @@ os_prompt% </pre>
</item>
<tag><c>ets</c></tag>
<item>
- <p>The total amount of memory currently allocated for ets
+ <p>The total amount of memory currently allocated for ETS
tables. This memory is part of the memory presented as
<c>system</c> memory.</p>
</item>
@@ -2718,9 +2887,9 @@ os_prompt% </pre>
<item>
<p>Only on 64-bit halfword emulator.
The total amount of memory allocated in low memory areas
- that are restricted to less than 4 GB, although
- the system can have more memory.</p>
- <p>Can be removed in a future release of the halfword
+ that are restricted to &lt; 4 GB, although
+ the system can have more memory.</p>
+ <p>Can be removed in a future release of the halfword
emulator.</p>
</item>
<tag><c>maximum</c></tag>
@@ -2730,8 +2899,9 @@ os_prompt% </pre>
when the emulator is run with instrumentation.</p>
<p>For information on how to run the emulator with
instrumentation, see
- <seealso marker="tools:instrument">instrument(3)</seealso>
- and/or <seealso marker="erts:erl">erl(1)</seealso>.</p>
+ <seealso marker="tools:instrument">
+ <c>instrument(3)</c></seealso>
+ and/or <seealso marker="erl"><c>erl(1)</c></seealso>.</p>
</item>
</taglist>
<note>
@@ -2748,20 +2918,20 @@ os_prompt% </pre>
<p>As the <c>total</c> value is the sum of <c>processes</c>
and <c>system</c>, the error in <c>system</c> propagates
to the <c>total</c> value.</p>
- <p>The different amounts of memory that are summed are
- <em>not</em> gathered atomically, which introduces
- an error in the result.</p>
+ <p>The different amounts of memory that are summed are
+ <em>not</em> gathered atomically, which introduces
+ an error in the result.</p>
</note>
<p>The different values have the following relation to each
other. Values beginning with an uppercase letter is not part
of the result.</p>
<code type="none">
- total = processes + system
- processes = processes_used + ProcessesNotUsed
- system = atom + binary + code + ets + OtherSystem
- atom = atom_used + AtomNotUsed
- RealTotal = processes + RealSystem
- RealSystem = system + MissedSystem</code>
+total = processes + system
+processes = processes_used + ProcessesNotUsed
+system = atom + binary + code + ets + OtherSystem
+atom = atom_used + AtomNotUsed
+RealTotal = processes + RealSystem
+RealSystem = system + MissedSystem</code>
<p>More tuples in the returned list can be added in a
future release.</p>
<note>
@@ -2771,20 +2941,20 @@ os_prompt% </pre>
the emulator stacks are not supposed to be included. That
is, the <c>total</c> value is <em>not</em> supposed to be
equal to the total size of all pages mapped to the emulator.</p>
- <p>Furthermore, because of fragmentation and prereservation of
+ <p>Also, because of fragmentation and prereservation of
memory areas, the size of the memory segments containing
the dynamically allocated memory blocks can be much
larger than the total size of the dynamically allocated
memory blocks.</p>
</note>
- <note>
- <p>As from <c>ERTS</c> 5.6.4, <c>erlang:memory/0</c> requires that
- all <seealso marker="erts:erts_alloc">erts_alloc(3)</seealso>
- allocators are enabled (default behavior).</p>
- </note>
+ <note>
+ <p>As from ERTS 5.6.4, <c>erlang:memory/0</c> requires that
+ all <seealso marker="erts:erts_alloc"><c>erts_alloc(3)</c></seealso>
+ allocators are enabled (default behavior).</p>
+ </note>
<p>Failure: <c>notsup</c> if an
- <seealso marker="erts:erts_alloc">erts_alloc(3)</seealso>
- allocator has been disabled.</p>
+ <seealso marker="erts:erts_alloc"><c>erts_alloc(3)</c></seealso>
+ allocator has been disabled.</p>
</desc>
</func>
@@ -2794,61 +2964,61 @@ os_prompt% </pre>
<fsummary>Information about dynamically allocated memory.</fsummary>
<type name="memory_type"/>
<desc>
- <p>Returns the memory size in bytes allocated for memory of
- type <c><anno>Type</anno></c>. The argument can also be given as a list
+ <p>Returns the memory size in bytes allocated for memory of type
+ <c><anno>Type</anno></c>. The argument can also be specified as a list
of <c>memory_type()</c> atoms, in which case a corresponding list of
<c>{memory_type(), Size :: integer >= 0}</c> tuples is returned.</p>
- <note>
- <p>As from <c>ERTS</c> version 5.6.4,
+ <note>
+ <p>As from ERTS 5.6.4,
<c>erlang:memory/1</c> requires that
- all <seealso marker="erts:erts_alloc">erts_alloc(3)</seealso>
- allocators are enabled (default behavior).</p>
- </note>
+ all <seealso marker="erts_alloc"><c>erts_alloc(3)</c></seealso>
+ allocators are enabled (default behavior).</p>
+ </note>
<p>Failures:</p>
<taglist>
<tag><c>badarg</c></tag>
<item>
- If <c><anno>Type</anno></c> is not one of the memory types
+ If <c><anno>Type</anno></c> is not one of the memory types
listed in the description of
- <seealso marker="#memory/0">erlang:memory/0</seealso>.
- </item>
+ <seealso marker="#memory/0"><c>erlang:memory/0</c></seealso>.
+ </item>
<tag><c>badarg</c></tag>
<item>
- If <c>maximum</c> is passed as <c><anno>Type</anno></c> and
+ If <c>maximum</c> is passed as <c><anno>Type</anno></c> and
the emulator is not run in instrumented mode.
- </item>
+ </item>
<tag><c>notsup</c></tag>
<item>
- If an <seealso marker="erts:erts_alloc">erts_alloc(3)</seealso>
- allocator has been disabled.
- </item>
- </taglist>
+ If an <seealso marker="erts_alloc"><c>erts_alloc(3)</c></seealso>
+ allocator has been disabled.
+ </item>
+ </taglist>
<p>See also
- <seealso marker="#memory/0">erlang:memory/0</seealso>.</p>
+ <seealso marker="#memory/0"><c>erlang:memory/0</c></seealso>.</p>
</desc>
</func>
<func>
<name name="min" arity="2"/>
- <fsummary>Returns the smallest of two terms.</fsummary>
+ <fsummary>Return the smallest of two terms.</fsummary>
<desc>
<p>Returns the smallest of <c><anno>Term1</anno></c> and
<c><anno>Term2</anno></c>.
- If the terms are equal, <c><anno>Term1</anno></c> is returned.</p>
+ If the terms are equal, <c><anno>Term1</anno></c> is returned.</p>
</desc>
</func>
<func>
<name name="module_loaded" arity="1"/>
- <fsummary>Checks if a module is loaded.</fsummary>
+ <fsummary>Check if a module is loaded.</fsummary>
<desc>
<p>Returns <c>true</c> if the module <c><anno>Module</anno></c>
is loaded, otherwise <c>false</c>. It does not attempt to load
the module.</p>
<warning>
<p>This BIF is intended for the code server (see
- <seealso marker="kernel:code">code(3)</seealso>) and is not to be
- used elsewhere.</p>
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>)
+ and is not to be used elsewhere.</p>
</warning>
</desc>
</func>
@@ -2856,184 +3026,189 @@ os_prompt% </pre>
<func>
<name name="monitor" arity="2" clause_i="1"/>
<name name="monitor" arity="2" clause_i="2"/>
- <fsummary>Starts monitoring.</fsummary>
+ <name name="monitor" arity="2" clause_i="3"/>
+ <fsummary>Start monitoring.</fsummary>
<type name="registered_name"/>
<type name="registered_process_identifier"/>
<type name="monitor_process_identifier"/>
+ <type name="monitor_port_identifier"/>
<desc>
- <p>Send a monitor request of type <c><anno>Type</anno></c> to the
- entity identified by <c><anno>Item</anno></c>. The caller of
- <c>monitor/2</c> will later be notified by a monitor message on the
- following format if the monitored state is changed:</p>
- <code type="none">{Tag, <anno>MonitorRef</anno>, <anno>Type</anno>, Object, Info}</code>
- <note><p>The monitor request is an asynchronous signal. That is, it
- takes time before the signal reaches its destination.</p></note>
- <p>Valid <c><anno>Type</anno></c>s:</p>
- <taglist>
- <tag><marker id="monitor_process"/><c>process</c></tag>
- <item>
- <p>Monitor the existence of the process identified by
- <c><anno>Item</anno></c>. Valid
- <c><anno>Item</anno></c>s in combination with the
- <c>process <anno>Type</anno></c> can be any of the following:</p>
- <taglist>
- <tag><c>pid()</c></tag>
- <item>
- <p>The process identifier of the process to monitor.</p>
- </item>
- <tag><c>{RegisteredName, Node}</c></tag>
- <item>
- <p>A tuple consisting of a registered name of a process and
- a node name. The process residing on the node <c>Node</c>
- with the registered name <c>{RegisteredName, Node}</c> will
- be monitored.</p>
- </item>
- <tag><c>RegisteredName</c></tag>
- <item>
- <p>The process locally registered as <c>RegisteredName</c>
- will become monitored.</p>
- </item>
- </taglist>
- <note><p>When a registered name is used, the
- process that has the registered name when the
- monitor request reach its destination will be monitored.
- The monitor is not effected if the registered name is
- unregistered, or unregistered and later registered on another
- process.</p></note>
- <p>The monitor is triggered either when the monitored process
- terminates, is non existing, or if the connection to it is
- lost. In the case the connection to it is lost, we do not know
- if it still exist or not. After this type of monitor has been
- triggered, the monitor is automatically removed.</p>
- <p>When the monitor is triggered a <c>'DOWN'</c> message is
- sent to the monitoring process. A <c>'DOWN'</c> message has
- the following pattern:</p>
- <code type="none">{'DOWN', MonitorRef, Type, Object, Info}</code>
- <p>Here <c>MonitorRef</c> and <c>Type</c> are the same as
- described earlier, and:</p>
- <taglist>
- <tag><c>Object</c></tag>
- <item>
- <p>equals:</p>
- <taglist>
- <tag><c><anno>Item</anno></c></tag>
- <item>If <c><anno>Item</anno></c> is specified by a
- process identifier.</item>
- <tag><c>{RegisteredName, Node}</c></tag>
- <item>If <c><anno>Item</anno></c> is specified as
- <c>RegisteredName</c>, or <c>{RegisteredName, Node}</c>
- where <c>Node</c> corresponds to the node that the
- monitored process resides on.</item>
- </taglist>
- </item>
- <tag><c>Info</c></tag>
- <item>
- <p>Either the exit reason of the process, <c>noproc</c>
- (non-existing process), or <c>noconnection</c> (no
- connection to the node where the monitored process
- resides).</p></item>
- </taglist>
- <p>The monitoring is turned off when the <c>'DOWN'</c>
- message is sent or when
- <seealso marker="#demonitor/1">demonitor/1</seealso>
- is called.</p>
- <p>If an attempt is made to monitor a process on an older node
- (where remote process monitoring is not implemented or
- where remote process monitoring by registered name is not
- implemented), the call fails with <c>badarg</c>.</p>
- <note>
- <p>The format of the <c>'DOWN'</c> message changed in ERTS
- version 5.2 (OTP R9B) for monitoring
- <em>by registered name</em>. Element <c>Object</c> of
- the <c>'DOWN'</c> message could in earlier versions
- sometimes be the process identifier of the monitored process and sometimes
- be the registered name. Now element <c>Object</c> is
- always a tuple consisting of the registered name and
- the node name. Processes on new nodes (ERTS version 5.2
- or higher) always get <c>'DOWN'</c> messages on
- the new format even if they are monitoring processes on old
- nodes. Processes on old nodes always get <c>'DOWN'</c>
- messages on the old format.</p>
- </note>
- </item>
- <tag><marker id="monitor_time_offset"/><c>time_offset</c></tag>
+ <p>Sends a monitor request of type <c><anno>Type</anno></c> to the
+ entity identified by <c><anno>Item</anno></c>. If the monitored entity
+ does not exist or it changes monitored state, the caller of
+ <c>monitor/2</c> is notified by a message on the following format:</p>
+ <code type="none">
+{Tag, <anno>MonitorRef</anno>, <anno>Type</anno>, Object, Info}</code>
+ <note>
+ <p>The monitor request is an asynchronous signal. That is, it
+ takes time before the signal reaches its destination.</p>
+ </note>
+
+ <p><c><anno>Type</anno></c> can be one of the following atoms:
+ <c>process</c>, <c>port</c> or <c>time_offset</c>.</p>
+
+ <p>A <c>process</c> or <c>port</c> monitor is triggered only once,
+ after that it is removed from both monitoring process and
+ the monitored entity. Monitors are fired when the monitored process
+ or port terminates, does not exist at the moment of creation,
+ or if the connection to it is lost. If the connection to it is lost,
+ we do not know if it still exists. The monitoring is also turned off
+ when <seealso marker="#demonitor/1">demonitor/1</seealso> is
+ called.</p>
+
+ <p>A <c>process</c> or <c>port</c> monitor by name
+ resolves the <c>RegisteredName</c> to <c>pid()</c> or <c>port()</c>
+ only once at the moment of monitor instantiation, later changes to
+ the name registration will not affect the existing monitor.</p>
+
+ <p>When a <c>process</c> or <c>port</c> monitor is triggered,
+ a <c>'DOWN'</c> message is sent that has the following pattern:</p>
+ <code type="none">
+{'DOWN', MonitorRef, Type, Object, Info}</code>
+
+ <p>In the monitor message <c>MonitorRef</c> and <c>Type</c> are the
+ same as described earlier, and:</p>
+ <taglist>
+ <tag><c>Object</c></tag>
+ <item>
+ <p>The monitored entity, which triggered the event. When monitoring
+ a local process or port, <c>Object</c> will be equal to the
+ <c>pid()</c> or <c>port()</c> that was being monitored. When
+ monitoring process or port by name, <c>Object</c> will have format
+ <c>{RegisteredName, Node}</c> where <c>RegisteredName</c> is the
+ name which has been used with <c>monitor/2</c> call and
+ <c>Node</c> is local or remote node name (for ports monitored by
+ name, <c>Node</c> is always local node name).</p>
+ </item>
+ <tag><c>Info</c></tag>
+ <item>
+ <p>Either the exit reason of the process, <c>noproc</c>
+ (process or port did not exist at the time of monitor creation),
+ or <c>noconnection</c> (no connection to the node where the
+ monitored process resides). </p></item>
+ </taglist>
+
+ <p>If an attempt is made to monitor a process on an older node
+ (where remote process monitoring is not implemented or
+ where remote process monitoring by registered name is not
+ implemented), the call fails with <c>badarg</c>.</p>
+ <note>
+ <p>The format of the <c>'DOWN'</c> message changed in ERTS
+ 5.2 (Erlang/OTP R9B) for monitoring
+ <em>by registered name</em>. Element <c>Object</c> of
+ the <c>'DOWN'</c> message could in earlier versions
+ sometimes be the process identifier of the monitored process and sometimes
+ be the registered name. Now element <c>Object</c> is
+ always a tuple consisting of the registered name and
+ the node name. Processes on new nodes (ERTS 5.2
+ or higher versions) always get <c>'DOWN'</c> messages on
+ the new format even if they are monitoring processes on old
+ nodes. Processes on old nodes always get <c>'DOWN'</c>
+ messages on the old format.</p>
+ </note>
+
+ <taglist>
+ <tag>Monitoring a <marker id="monitor_process"/><c>process</c></tag>
+ <item>
+ <p>Creates monitor between the current process and another
+ process identified by <c><anno>Item</anno></c>, which can be a
+ <c>pid()</c> (local or remote), an atom <c>RegisteredName</c> or
+ a tuple <c>{RegisteredName, Node}</c> for a registered process,
+ located elsewhere.</p>
+ </item>
+
+ <tag>Monitoring a <marker id="monitor_port"/><c>port</c></tag>
+ <item>
+ <p>Creates monitor between the current process and a port
+ identified by <c><anno>Item</anno></c>, which can be a
+ <c>port()</c> (only local), an atom <c>RegisteredName</c> or
+ a tuple <c>{RegisteredName, Node}</c> for a registered port,
+ located on this node. Note, that attempt to monitor a remote port
+ will result in <c>badarg</c>.</p>
+ </item>
+
+ <tag>Monitoring a
+ <marker id="monitor_time_offset"/><c>time_offset</c></tag>
<item>
- <p>Monitor changes in
- <seealso marker="#time_offset/0">time offset</seealso>
- between
- <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
- monotonic time</seealso> and
- <seealso marker="time_correction#Erlang_System_Time">Erlang
- system time</seealso>. There is only one valid
- <c><anno>Item</anno></c> in combination with the
- <c>time_offset <anno>Type</anno></c>, namely the atom
- <c>clock_service</c>. Note that the atom <c>clock_service</c> is
- <em>not</em> the registered name of a process. In this specific
- case it serves as an identifier of the runtime system internal
- clock service at current runtime system instance.</p>
+ <p>Monitors changes in
+ <seealso marker="#time_offset/0"><c>time offset</c></seealso>
+ between
+ <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
+ monotonic time</seealso> and
+ <seealso marker="time_correction#Erlang_System_Time">Erlang
+ system time</seealso>. One valid <c><anno>Item</anno></c>
+ exists in combination with the
+ <c>time_offset <anno>Type</anno></c>, namely the atom
+ <c>clock_service</c>. Notice that the atom <c>clock_service</c> is
+ <em>not</em> the registered name of a process. In this
+ case it serves as an identifier of the runtime system internal
+ clock service at current runtime system instance.</p>
<p>The monitor is triggered when the time offset is changed.
- This either if the time offset value is changed, or if the
- offset is changed from preliminary to final during
- <seealso marker="#system_flag_time_offset">finalization
- of the time offset</seealso> when the
- <seealso marker="time_correction#Single_Time_Warp_Mode">single
- time warp mode</seealso> is used. When a change from preliminary
- to final time offset is made, the monitor will be triggered once
- regardless of whether the time offset value was actually changed
- or not.</p>
+ This either if the time offset value is changed, or if the
+ offset is changed from preliminary to final during
+ <seealso marker="#system_flag_time_offset">finalization
+ of the time offset</seealso> when the
+ <seealso marker="time_correction#Single_Time_Warp_Mode">single
+ time warp mode</seealso> is used. When a change from preliminary
+ to final time offset is made, the monitor is triggered once
+ regardless of whether the time offset value was changed
+ or not.</p>
<p>If the runtime system is in
- <seealso marker="time_correction#Multi_Time_Warp_Mode">multi
- time warp mode</seealso>, the time offset will be changed when
- the runtime system detects that the
- <seealso marker="time_correction#OS_System_Time">OS system
- time</seealso> has changed. The runtime system will, however,
- not detect this immediately when it happens. A task checking
- the time offset is scheduled to execute at least once a minute,
- so under normal operation this should be detected within a
- minute, but during heavy load it might take longer time.</p>
-
- <p>The monitor will <em>not</em> be automatically removed
- after it has been triggered. That is, repeated changes of
- the time offset will trigger the monitor repeatedly.</p>
-
- <p>When the monitor is triggered a <c>'CHANGE'</c> message will
- be sent to the monitoring process. A <c>'CHANGE'</c> message has
- the following pattern:</p>
- <code type="none">{'CHANGE', MonitorRef, Type, Item, NewTimeOffset}</code>
- <p>where <c>MonitorRef</c>, <c><anno>Type</anno></c>, and
- <c><anno>Item</anno></c> are the same as described above, and
- <c>NewTimeOffset</c> is the new time offset.</p>
+ <seealso marker="time_correction#Multi_Time_Warp_Mode">multi
+ time warp mode</seealso>, the time offset is changed when
+ the runtime system detects that the
+ <seealso marker="time_correction#OS_System_Time">OS system
+ time</seealso> has changed. The runtime system does, however,
+ not detect this immediately when it occurs. A task checking
+ the time offset is scheduled to execute at least once a minute,
+ so under normal operation this is to be detected within a
+ minute, but during heavy load it can take longer time.</p>
+
+ <p>The monitor is <em>not</em> automatically removed
+ after it has been triggered. That is, repeated changes of
+ the time offset trigger the monitor repeatedly.</p>
+
+ <p>When the monitor is triggered a <c>'CHANGE'</c> message is
+ sent to the monitoring process. A <c>'CHANGE'</c> message has
+ the following pattern:</p>
+ <code type="none">
+{'CHANGE', MonitorRef, Type, Item, NewTimeOffset}</code>
+ <p>where <c>MonitorRef</c>, <c><anno>Type</anno></c>, and
+ <c><anno>Item</anno></c> are the same as described above, and
+ <c>NewTimeOffset</c> is the new time offset.</p>
<p>When the <c>'CHANGE'</c> message has been received you are
- guaranteed not to retrieve the old time offset when calling
- <seealso marker="#time_offset/0"><c>erlang:time_offset()</c></seealso>.
- Note that you can observe the change of the time offset
- when calling <c>erlang:time_offset()</c> before you
- get the <c>'CHANGE'</c> message.</p>
-
+ guaranteed not to retrieve the old time offset when calling
+ <seealso marker="#time_offset/0">
+ <c>erlang:time_offset()</c></seealso>.
+ Notice that you can observe the change of the time offset
+ when calling <c>erlang:time_offset()</c> before you
+ get the <c>'CHANGE'</c> message.</p>
</item>
</taglist>
+
<p>Making several calls to <c>monitor/2</c> for the same
- <c><anno>Item</anno></c> and/or <c><anno>Type</anno></c> is not
- an error; it results in as many independent monitoring instances.</p>
+ <c><anno>Item</anno></c> and/or <c><anno>Type</anno></c> is not
+ an error; it results in as many independent monitoring instances.</p>
+
<p>The monitor functionality is expected to be extended. That is,
- other <c><anno>Type</anno></c>s and <c><anno>Item</anno></c>s
- are expected to be supported in a future release.</p>
+ other <c><anno>Type</anno></c>s and <c><anno>Item</anno></c>s
+ are expected to be supported in a future release.</p>
<note>
<p>If or when <c>monitor/2</c> is extended, other
- possible values for <c>Tag</c>, <c>Object</c> and
- <c>Info</c> in the monitor message will be introduced.</p>
+ possible values for <c>Tag</c>, <c>Object</c>, and
+ <c>Info</c> in the monitor message will be introduced.</p>
</note>
</desc>
</func>
<func>
<name name="monitor_node" arity="2"/>
- <fsummary>Monitors the status of a node.</fsummary>
+ <fsummary>Monitor the status of a node.</fsummary>
<desc>
- <p>Monitors the status of the node <c><anno>Node</anno></c>.
+ <p>Monitor the status of the node <c><anno>Node</anno></c>.
If <c><anno>Flag</anno></c>
is <c>true</c>, monitoring is turned on. If <c><anno>Flag</anno></c>
is <c>false</c>, monitoring is turned off.</p>
@@ -3055,23 +3230,23 @@ os_prompt% </pre>
<func>
<name name="monitor_node" arity="3"/>
- <fsummary>Monitors the status of a node.</fsummary>
+ <fsummary>Monitor the status of a node.</fsummary>
<desc>
<p>Behaves as
- <seealso marker="#monitor_node/2">monitor_node/2</seealso>
+ <seealso marker="#monitor_node/2"><c>monitor_node/2</c></seealso>
except that it allows an
- extra option to be given, namely <c>allow_passive_connect</c>.
+ extra option to be specified, namely <c>allow_passive_connect</c>.
This option allows the BIF to wait the normal network connection
time-out for the <em>monitored node</em> to connect itself,
even if it cannot be actively connected from this node
(that is, it is blocked). The state where this can be useful
- can only be achieved by using the <c>Kernel</c> option
+ can only be achieved by using the Kernel option
<c>dist_auto_connect once</c>. If that option is not
used, option <c>allow_passive_connect</c> has no effect.</p>
<note>
<p>Option <c>allow_passive_connect</c> is used
internally and is seldom needed in applications where the
- network topology and the <c>Kernel</c> options in effect
+ network topology and the Kernel options in effect
are known in advance.</p>
</note>
<p>Failure: <c>badarg</c> if the local node is not alive or the
@@ -3083,70 +3258,77 @@ os_prompt% </pre>
<name name="monotonic_time" arity="0"/>
<fsummary>Current Erlang monotonic time.</fsummary>
<desc>
- <p>Returns the current
- <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
- monotonic time</seealso> in <c>native</c>
- <seealso marker="#type_time_unit">time unit</seealso>. This
- is a monotonically increasing time since some unspecified point in
- time.</p>
-
- <note><p>This is a
- <seealso marker="time_correction#Monotonically_Increasing">monotonically increasing</seealso> time, but <em>not</em> a
- <seealso marker="time_correction#Strictly_Monotonically_Increasing">strictly monotonically increasing</seealso>
- time. That is, consecutive calls to
- <c>erlang:monotonic_time/0</c> can produce the same result.</p>
-
- <p>Different runtime system instances will use different
- unspecified points in time as base for their Erlang monotonic clocks.
- That is, it is <em>pointless</em> comparing monotonic times from
- different runtime system instances. Different runtime system instances
- may also place this unspecified point in time different relative
- runtime system start. It may be placed in the future (time at start
- is a negative value), the past (time at start is a
- positive value), or the runtime system start (time at start is
- zero). The monotonic time at runtime system start can be
- retrieved by calling
- <seealso marker="#system_info_start_time"><c>erlang:system_info(start_time)</c></seealso>.</p></note>
+ <p>Returns the current
+ <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
+ monotonic time</seealso> in <c>native</c>
+ <seealso marker="#type_time_unit">time unit</seealso>. This
+ is a monotonically increasing time since some unspecified point in
+ time.</p>
+ <note>
+ <p>This is a
+ <seealso marker="time_correction#Monotonically_Increasing">
+ monotonically increasing</seealso> time, but <em>not</em> a
+ <seealso marker="time_correction#Strictly_Monotonically_Increasing">
+ strictly monotonically increasing</seealso>
+ time. That is, consecutive calls to
+ <c>erlang:monotonic_time/0</c> can produce the same result.</p>
+ <p>Different runtime system instances will use different unspecified
+ points in time as base for their Erlang monotonic clocks.
+ That is, it is <em>pointless</em> comparing monotonic times from
+ different runtime system instances. Different runtime system
+ instances can also place this unspecified point in time different
+ relative runtime system start. It can be placed in the future (time
+ at start is a negative value), the past (time at start is a
+ positive value), or the runtime system start (time at start is
+ zero). The monotonic time at runtime system start can be
+ retrieved by calling
+ <seealso marker="#system_info_start_time">
+ <c>erlang:system_info(start_time)</c></seealso>.</p>
+ </note>
</desc>
</func>
+
<func>
<name name="monotonic_time" arity="1"/>
- <fsummary>Current Erlang monotonic time</fsummary>
+ <fsummary>Current Erlang monotonic time.</fsummary>
<desc>
- <p>Returns the current
- <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
- monotonic time</seealso> converted
- into the <c><anno>Unit</anno></c> passed as argument.</p>
-
- <p>Same as calling
- <seealso marker="#convert_time_unit/3"><c>erlang:convert_time_unit</c></seealso><c>(</c><seealso marker="#monotonic_time/0"><c>erlang:monotonic_time()</c></seealso><c>,
- native, <anno>Unit</anno>)</c>
- however optimized for commonly used <c><anno>Unit</anno></c>s.</p>
+ <p>Returns the current
+ <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
+ monotonic time</seealso> converted
+ into the <c><anno>Unit</anno></c> passed as argument.</p>
+ <p>Same as calling
+ <seealso marker="#convert_time_unit/3">
+ <c>erlang:convert_time_unit</c></seealso><c>(</c><seealso
+ marker="#monotonic_time/0">
+ <c>erlang:monotonic_time()</c></seealso><c>,
+ native, <anno>Unit</anno>)</c>,
+ however optimized for commonly used <c><anno>Unit</anno></c>s.</p>
</desc>
</func>
+
<func>
<name name="nif_error" arity="1"/>
- <fsummary>Stops execution with a given reason.</fsummary>
+ <fsummary>Stop execution with a specified reason.</fsummary>
<desc>
<p>Works exactly like
- <seealso marker="#error/1">erlang:error/1</seealso>, but
- <c>Dialyzer</c> thinks that this BIF will return an arbitrary
- term. When used in a stub function for a NIF to generate an
- exception when the NIF library is not loaded, <c>Dialyzer</c>
- does not generate false warnings.</p>
+ <seealso marker="#error/1"><c>error/1</c></seealso>, but
+ Dialyzer thinks that this BIF will return an arbitrary
+ term. When used in a stub function for a NIF to generate an
+ exception when the NIF library is not loaded, Dialyzer
+ does not generate false warnings.</p>
</desc>
</func>
<func>
<name name="nif_error" arity="2"/>
- <fsummary>Stops execution with a given reason.</fsummary>
+ <fsummary>Stop execution with a specified reason.</fsummary>
<desc>
<p>Works exactly like
- <seealso marker="#error/2">erlang:error/2</seealso>, but
- <c>Dialyzer</c> thinks that this BIF will return an arbitrary
- term. When used in a stub function for a NIF to generate an
- exception when the NIF library is not loaded, <c>Dialyzer</c>
- does not generate false warnings.</p>
+ <seealso marker="#error/2"><c>error/2</c></seealso>, but
+ Dialyzer thinks that this BIF will return an arbitrary
+ term. When used in a stub function for a NIF to generate an
+ exception when the NIF library is not loaded, Dialyzer
+ does not generate false warnings.</p>
</desc>
</func>
@@ -3186,10 +3368,10 @@ os_prompt% </pre>
<name name="nodes" arity="1"/>
<fsummary>All nodes of a certain type in the system.</fsummary>
<desc>
- <p>Returns a list of nodes according to the argument given.
- The returned result when the argument is a list, is the list
+ <p>Returns a list of nodes according to the argument specified.
+ The returned result, when the argument is a list, is the list
of nodes satisfying the disjunction(s) of the list elements.</p>
- <p><c><anno>NodeType</anno></c> can be any of the following:</p>
+ <p><c><anno>NodeType</anno></c>s:</p>
<taglist>
<tag><c>visible</c></tag>
<item>
@@ -3210,13 +3392,13 @@ os_prompt% </pre>
<tag><c>known</c></tag>
<item>
<p>Nodes that are known to this node. That is, connected
- nodes and nodes referred to by process identifiers, port
- identifiers and references located on this node.
- The set of known nodes is garbage collected. Notice that
- this garbage collection can be delayed. For more
- information, see
- <seealso marker="erlang#system_info_delayed_node_table_gc">delayed_node_table_gc</seealso>.
- </p>
+ nodes and nodes referred to by process identifiers, port
+ identifiers, and references located on this node.
+ The set of known nodes is garbage collected. Notice that
+ this garbage collection can be delayed. For more
+ information, see
+ <seealso marker="erlang#system_info_delayed_node_table_gc">
+ <c>erlang:system_info(delayed_node_table_gc)</c></seealso>.</p>
</item>
</taglist>
<p>Some equalities: <c>[node()] = nodes(this)</c>,
@@ -3230,20 +3412,22 @@ os_prompt% </pre>
<fsummary>Elapsed time since 00:00 GMT.</fsummary>
<type name="timestamp"/>
<desc>
- <warning><p><em>This function is deprecated! Do not use it!</em>
- See the users guide chapter
- <seealso marker="time_correction">Time and Time Correction</seealso>
- for more information. Specifically the
- <seealso marker="time_correction#Dos_and_Donts">Dos and Dont's</seealso>
- section for information on what to use instead of <c>erlang:now/0</c>.
- </p></warning>
- <p>Returns the tuple <c>{MegaSecs, Secs, MicroSecs}</c> which is
+ <warning>
+ <p><em>This function is deprecated. Do not use it.</em></p>
+ <p>For more information, see section
+ <seealso marker="time_correction">Time and Time Correction</seealso>
+ in the User's Guide. Specifically, section
+ <seealso marker="time_correction#Dos_and_Donts">
+ Dos and Dont's</seealso> describes what to use instead of
+ <c>erlang:now/0</c>.</p>
+ </warning>
+ <p>Returns the tuple <c>{MegaSecs, Secs, MicroSecs}</c>, which is
the elapsed time since 00:00 GMT, January 1, 1970 (zero hour),
- on the assumption that the underlying OS supports this.
+ if provided by the underlying OS.
Otherwise some other point in time is chosen. It is also
- guaranteed that subsequent calls to this BIF return
+ guaranteed that the following calls to this BIF return
continuously increasing values. Hence, the return value from
- <c>now()</c> can be used to generate unique time-stamps.
+ <c>erlang:now/0</c> can be used to generate unique time stamps.
If it is called in a tight loop on a fast machine,
the time of the node can become skewed.</p>
<p>Can only be used to check the local time of day if
@@ -3254,28 +3438,31 @@ os_prompt% </pre>
<func>
<name name="open_port" arity="2"/>
- <fsummary>Opens a port.</fsummary>
+ <fsummary>Open a port.</fsummary>
<desc>
<p>Returns a port identifier as the result of opening a
new Erlang port. A port can be seen as an external Erlang
process.</p>
- <p>The name of the executable as well as the arguments
- given in <c>cd</c>, <c>env</c>, <c>args</c>, and <c>arg0</c> are
- subject to Unicode file name translation if the system is running
- in Unicode file name mode. To avoid
- translation or to force, for example UTF-8, supply the executable
- and/or arguments as a binary in the correct
- encoding. For details, see the module
- <seealso marker="kernel:file">file</seealso>, the function
- <seealso marker="kernel:file#native_name_encoding/0">file:native_name_encoding/0</seealso>, and the
- <seealso marker="stdlib:unicode_usage">STDLIB </seealso>
- User's Guide.</p>
- <note><p>The characters in the name (if given as a list) can
- only be higher than 255 if the Erlang Virtual Machine is started
- in Unicode file name translation mode. Otherwise the name
- of the executable is limited to the ISO-latin-1
- character set.</p></note>
- <p><c><anno>PortName</anno></c> can be any of the following:</p>
+ <p>The name of the executable as well as the arguments
+ specifed in <c>cd</c>, <c>env</c>, <c>args</c>, and <c>arg0</c> are
+ subject to Unicode filename translation if the system is running
+ in Unicode filename mode. To avoid
+ translation or to force, for example UTF-8, supply the executable
+ and/or arguments as a binary in the correct
+ encoding. For details, see the module
+ <seealso marker="kernel:file"><c>file(3)</c></seealso>, the
+ function <seealso marker="kernel:file#native_name_encoding/0">
+ <c>file:native_name_encoding/0</c></seealso> in Kernel, and
+ the <seealso marker="stdlib:unicode_usage">
+ <c>Using Unicode in Erlang</c></seealso> User's Guide.</p>
+ <note>
+ <p>The characters in the name (if specified as a list) can
+ only be &gt; 255 if the Erlang virtual machine is started
+ in Unicode filename translation mode. Otherwise the name
+ of the executable is limited to the ISO Latin-1
+ character set.</p>
+ </note>
+ <p><c><anno>PortName</anno></c>s:</p>
<taglist>
<tag><c>{spawn, <anno>Command</anno>}</c></tag>
<item>
@@ -3294,55 +3481,57 @@ os_prompt% </pre>
<c>vfork</c>, setting environment variable
<c>ERL_NO_VFORK</c> to any value causes <c>fork</c>
to be used instead.</p>
- <p>For external programs, <c>PATH</c> is searched
- (or an equivalent method is used to find programs,
- depending on OS). This is done by invoking
- the shell on certain platforms. The first space-separated
- token of the command is considered as the
- name of the executable (or driver). This (among other
- things) makes this option unsuitable for running
- programs having spaces in file names or directory names.
- If spaces in executable file names are desired, use
- <c>{spawn_executable, <anno>Command</anno>}</c> instead.</p>
+ <p>For external programs, <c>PATH</c> is searched
+ (or an equivalent method is used to find programs,
+ depending on the OS). This is done by invoking
+ the shell on certain platforms. The first space-separated
+ token of the command is considered as the
+ name of the executable (or driver). This (among other
+ things) makes this option unsuitable for running
+ programs with spaces in filenames or directory names.
+ If spaces in executable filenames are desired, use
+ <c>{spawn_executable, <anno>Command</anno>}</c> instead.</p>
</item>
<tag><c>{spawn_driver, <anno>Command</anno>}</c></tag>
<item>
- <p>Works like <c>{spawn, <anno>Command</anno>}</c>, but demands the
- first (space-separated) token of the command to be the name of a
- loaded driver. If no driver with that name is loaded, a
- <c>badarg</c> error is raised.</p>
+ <p>Works like <c>{spawn, <anno>Command</anno>}</c>, but demands
+ the first (space-separated) token of the command to be the name
+ of a loaded driver. If no driver with that name is loaded, a
+ <c>badarg</c> error is raised.</p>
</item>
<tag><c>{spawn_executable, <anno>FileName</anno>}</c></tag>
<item>
- <p>Works like <c>{spawn, <anno>FileName</anno>}</c>, but only runs
- external executables. <c><anno>FileName</anno></c> in its whole
- is used as the name of the executable, including any spaces.
- If arguments are to be passed, the <c><anno>PortSettings</anno></c>
- <c>args</c> and <c>arg0</c> can be used.</p>
- <p>The shell is usually not invoked to start the
- program, it is executed directly. <c>PATH</c> (or
+ <p>Works like <c>{spawn, <anno>FileName</anno>}</c>, but only runs
+ external executables. <c><anno>FileName</anno></c> in its whole
+ is used as the name of the executable, including any spaces.
+ If arguments are to be passed, the
+ <c><anno>PortSettings</anno></c>
+ <c>args</c> and <c>arg0</c> can be used.</p>
+ <p>The shell is usually not invoked to start the
+ program, it is executed directly. <c>PATH</c> (or
equivalent) is not searched. To find a program
- in <c>PATH</c> to execute, use
- <seealso marker="kernel:os#find_executable/1">os:find_executable/1</seealso>.</p>
- <p>Only if a shell script or <c>.bat</c> file is
- executed, the appropriate command interpreter is
- invoked implicitly, but there is still no
- command argument expansion or implicit <c>PATH</c> search.</p>
- <p>If <c><anno>FileName</anno></c> cannot be run, an error
- exception is raised, with the POSIX error code as the reason.
- The error reason can differ between OSs.
- Typically the error <c>enoent</c> is raised when an
- attempt is made to run a program that is not found and
- <c>eacces</c> is raised when the given file is not
- executable.</p>
+ in <c>PATH</c> to execute, use
+ <seealso marker="kernel:os#find_executable/1">
+ <c>os:find_executable/1</c></seealso>.</p>
+ <p>Only if a shell script or <c>.bat</c> file is
+ executed, the appropriate command interpreter is
+ invoked implicitly, but there is still no
+ command-argument expansion or implicit <c>PATH</c> search.</p>
+ <p>If <c><anno>FileName</anno></c> cannot be run, an error
+ exception is raised, with the POSIX error code as the reason.
+ The error reason can differ between OSs.
+ Typically the error <c>enoent</c> is raised when an
+ attempt is made to run a program that is not found and
+ <c>eacces</c> is raised when the specified file is not
+ executable.</p>
</item>
<tag><c>{fd, <anno>In</anno>, <anno>Out</anno>}</c></tag>
<item>
<p>Allows an Erlang process to access any currently opened
file descriptors used by Erlang. The file descriptor
- <c><anno>In</anno></c> can be used for standard input, and the file
- descriptor <c><anno>Out</anno></c> for standard output. It is only
- used for various servers in the Erlang OS (<c>shell</c>
+ <c><anno>In</anno></c> can be used for standard input, and the
+ file descriptor <c><anno>Out</anno></c> for standard output.
+ It is only used for various servers in the Erlang OS (<c>shell</c>
and <c>user</c>). Hence, its use is limited.</p>
</item>
</taglist>
@@ -3351,7 +3540,8 @@ os_prompt% </pre>
<taglist>
<tag><c>{packet, <anno>N</anno>}</c></tag>
<item>
- <p>Messages are preceded by their length, sent in <c><anno>N</anno></c>
+ <p>Messages are preceded by their length, sent in
+ <c><anno>N</anno></c>
bytes, with the most significant byte first. The valid values
for <c>N</c> are 1, 2, and 4.</p>
</item>
@@ -3364,16 +3554,16 @@ os_prompt% </pre>
<tag><c>{line, <anno>L</anno>}</c></tag>
<item>
<p>Messages are delivered on a per line basis. Each line
- (delimited by the OS-dependent new line sequence) is
+ (delimited by the OS-dependent newline sequence) is
delivered in a single message. The message data format
is <c>{Flag, Line}</c>, where <c>Flag</c> is
<c>eol</c> or <c>noeol</c>, and <c>Line</c> is the
- data delivered (without the new line sequence).</p>
+ data delivered (without the newline sequence).</p>
<p><c><anno>L</anno></c> specifies the maximum line length in bytes.
Lines longer than this are delivered in more than one
message, with <c>Flag</c> set to <c>noeol</c> for all
but the last message. If end of file is encountered
- anywhere else than immediately following a new line
+ anywhere else than immediately following a newline
sequence, the last line is also delivered with
<c>Flag</c> set to <c>noeol</c>. Otherwise
lines are delivered with <c>Flag</c> set to <c>eol</c>.</p>
@@ -3383,14 +3573,14 @@ os_prompt% </pre>
<tag><c>{cd, <anno>Dir</anno>}</c></tag>
<item>
<p>Only valid for <c>{spawn, <anno>Command</anno>}</c> and
- <c>{spawn_executable, <anno>FileName</anno>}</c>.
+ <c>{spawn_executable, <anno>FileName</anno>}</c>.
The external program starts using <c><anno>Dir</anno></c> as its
working directory. <c><anno>Dir</anno></c> must be a string.</p>
</item>
<tag><c>{env, <anno>Env</anno>}</c></tag>
<item>
- <p>Only valid for <c>{spawn, <anno>Command</anno>}</c> and
- <c>{spawn_executable, <anno>FileName</anno>}</c>.
+ <p>Only valid for <c>{spawn, <anno>Command</anno>}</c>, and
+ <c>{spawn_executable, <anno>FileName</anno>}</c>.
The environment of the started process is extended using
the environment specifications in <c><anno>Env</anno></c>.</p>
<p><c><anno>Env</anno></c> is to be a list of tuples
@@ -3401,56 +3591,58 @@ os_prompt% </pre>
port process. Both <c><anno>Name</anno></c> and
<c><anno>Val</anno></c> must be strings. The one
exception is <c><anno>Val</anno></c> being the atom
- <c>false</c> (in analogy with <c>os:getenv/1</c>), which
- removes the environment variable.</p>
- </item>
- <tag><c>{args, [ string() | binary() ]}</c></tag>
- <item>
- <p>Only valid for <c>{spawn_executable, <anno>FileName</anno>}</c>
- and specifies arguments to the executable. Each argument
- is given as a separate string and (on Unix) eventually
- ends up as one element each in the argument vector. On
- other platforms, a similar behavior is mimicked.</p>
- <p>The arguments are not expanded by the shell before
- being supplied to the executable. Most notably this
- means that file wild card expansion does not happen.
- To expand wild cards for the arguments, use
- <seealso marker="stdlib:filelib#wildcard/1">filelib:wildcard/1</seealso>.
- Notice that even if
- the program is a Unix shell script, meaning that the
- shell ultimately is invoked, wild card expansion
- does not happen, and the script is provided with the
- untouched arguments. On Windows, wild card expansion
- is always up to the program itself, therefore this is
- not an issue issue.</p>
- <p>The executable name (also known as <c>argv[0]</c>)
- is not to be given in this list. The proper executable name
- is automatically used as argv[0], where applicable.</p>
- <p>If you explicitly want to set the
- program name in the argument vector, option <c>arg0</c>
- can be used.</p>
- </item>
- <tag><c>{arg0, string() | binary()}</c></tag>
- <item>
- <p>Only valid for <c>{spawn_executable, <anno>FileName</anno>}</c>
- and explicitly specifies the program name argument when
- running an executable. This can in some circumstances,
- on some OSs, be desirable. How the program
- responds to this is highly system-dependent and no specific
- effect is guaranteed.</p>
- </item>
+ <c>false</c> (in analogy with
+ <seealso marker="kernel:os#getenv/1"><c>os:getenv/1</c></seealso>,
+ which removes the environment variable.</p>
+ </item>
+ <tag><c>{args, [ string() | binary() ]}</c></tag>
+ <item>
+ <p>Only valid for <c>{spawn_executable, <anno>FileName</anno>}</c>
+ and specifies arguments to the executable. Each argument
+ is specified as a separate string and (on Unix) eventually
+ ends up as one element each in the argument vector. On
+ other platforms, a similar behavior is mimicked.</p>
+ <p>The arguments are not expanded by the shell before
+ they are supplied to the executable. Most notably this
+ means that file wildcard expansion does not occur.
+ To expand wildcards for the arguments, use
+ <seealso marker="stdlib:filelib#wildcard/1">
+ <c>filelib:wildcard/1</c></seealso>.
+ Notice that even if
+ the program is a Unix shell script, meaning that the
+ shell ultimately is invoked, wildcard expansion
+ does not occur, and the script is provided with the
+ untouched arguments. On Windows, wildcard expansion
+ is always up to the program itself, therefore this is
+ not an issue.</p>
+ <p>The executable name (also known as <c>argv[0]</c>)
+ is not to be specified in this list. The proper executable name
+ is automatically used as <c>argv[0]</c>, where applicable.</p>
+ <p>If you explicitly want to set the
+ program name in the argument vector, option <c>arg0</c>
+ can be used.</p>
+ </item>
+ <tag><c>{arg0, string() | binary()}</c></tag>
+ <item>
+ <p>Only valid for <c>{spawn_executable, <anno>FileName</anno>}</c>
+ and explicitly specifies the program name argument when
+ running an executable. This can in some circumstances,
+ on some OSs, be desirable. How the program
+ responds to this is highly system-dependent and no specific
+ effect is guaranteed.</p>
+ </item>
<tag><c>exit_status</c></tag>
<item>
<p>Only valid for <c>{spawn, <anno>Command</anno>}</c>, where
<c><anno>Command</anno></c> refers to an external program, and
- for <c>{spawn_executable, <anno>FileName</anno>}</c>.</p>
+ for <c>{spawn_executable, <anno>FileName</anno>}</c>.</p>
<p>When the external process connected to the port exits, a
message of the form <c>{Port,{exit_status,Status}}</c> is
sent to the connected process, where <c>Status</c> is the
exit status of the external process. If the program
aborts on Unix, the same convention is used as the shells
do (that is, 128+signal).</p>
- <p>If option <c>eof</c> is also given, the messages <c>eof</c>
+ <p>If option <c>eof</c> is specified also, the messages <c>eof</c>
and <c>exit_status</c> appear in an unspecified order.</p>
<p>If the port program closes its <c>stdout</c> without exiting,
option <c>exit_status</c> does not work.</p>
@@ -3458,7 +3650,7 @@ os_prompt% </pre>
<tag><c>use_stdio</c></tag>
<item>
<p>Only valid for <c>{spawn, <anno>Command</anno>}</c> and
- <c>{spawn_executable, <anno>FileName</anno>}</c>. It
+ <c>{spawn_executable, <anno>FileName</anno>}</c>. It
allows the standard input and output (file descriptors 0
and 1) of the spawned (Unix) process for communication
with Erlang.</p>
@@ -3478,14 +3670,14 @@ os_prompt% </pre>
<tag><c>overlapped_io</c></tag>
<item>
<p>Affects ports to external programs on Windows only. The
- standard input and standard output handles of the port program
- are, if this option is supplied, opened with flag
- <c>FILE_FLAG_OVERLAPPED</c>, so that the port program can
- (and must) do
- overlapped I/O on its standard handles. This is not normally
- the case for simple port programs, but an option of value for the
- experienced Windows programmer. <em>On all other platforms, this
- option is silently discarded.</em></p>
+ standard input and standard output handles of the port program
+ are, if this option is supplied, opened with flag
+ <c>FILE_FLAG_OVERLAPPED</c>, so that the port program can
+ (and must) do
+ overlapped I/O on its standard handles. This is not normally
+ the case for simple port programs, but an option of value for the
+ experienced Windows programmer. <em>On all other platforms, this
+ option is silently discarded.</em></p>
</item>
<tag><c>in</c></tag>
<item>
@@ -3510,27 +3702,28 @@ os_prompt% </pre>
<tag><c>hide</c></tag>
<item>
<p>When running on Windows, suppresses creation of a new
- console window when spawning the port program.
- (This option has no effect on other platforms.)</p>
+ console window when spawning the port program.
+ (This option has no effect on other platforms.)</p>
</item>
<tag><c>{parallelism, Boolean}</c></tag>
<item>
- <marker id="open_port_parallelism"></marker>
+ <marker id="open_port_parallelism"></marker>
<p>Sets scheduler hint for port parallelism. If set to
- <c>true</c>, the Virtual Machine schedules port tasks;
- when doing so, it improves parallelism in the system. If set
- to <c>false</c>, the Virtual Machine tries to
- perform port tasks immediately, improving latency at the
- expense of parallelism. The default can be set at system startup
- by passing command-line argument
- <seealso marker="erl#+spp">+spp</seealso> to <c>erl(1)</c>.</p>
+ <c>true</c>, the virtual machine schedules port tasks;
+ when doing so, it improves parallelism in the system. If set
+ to <c>false</c>, the virtual machine tries to
+ perform port tasks immediately, improving latency at the
+ expense of parallelism. The default can be set at system startup
+ by passing command-line argument
+ <seealso marker="erl#+spp"><c>+spp</c></seealso> to
+ <c>erl(1)</c>.</p>
</item>
</taglist>
<p>Default is <c>stream</c> for all port types and
<c>use_stdio</c> for spawned ports.</p>
- <p>Failure: If the port cannot be opened, the exit reason is
- <c>badarg</c>, <c>system_limit</c>, or the POSIX error code that
- most closely describes the error, or <c>einval</c> if no POSIX
+ <p>Failure: if the port cannot be opened, the exit reason is
+ <c>badarg</c>, <c>system_limit</c>, or the POSIX error code that
+ most closely describes the error, or <c>einval</c> if no POSIX
code is appropriate:</p>
<taglist>
<tag><c>badarg</c></tag>
@@ -3556,11 +3749,11 @@ os_prompt% </pre>
<item>Full file table (for the entire OS).
</item>
<tag><c>eacces</c></tag>
- <item><c>Command</c> given in <c>{spawn_executable, Command}</c>
+ <item><c>Command</c> specified in <c>{spawn_executable, Command}</c>
does not point out an executable file.
</item>
<tag><c>enoent</c></tag>
- <item><c><anno>FileName</anno></c> given in
+ <item><c><anno>FileName</anno></c> specified in
<c>{spawn_executable, <anno>FileName</anno>}</c>
does not point out an existing file.
</item>
@@ -3570,13 +3763,12 @@ os_prompt% </pre>
errors arising when sending messages to it are reported to
the owning process using signals of the form
<c>{'EXIT', Port, PosixCode}</c>. For the possible values of
- <c>PosixCode</c>, see the
- <seealso marker="kernel:file">file(3)</seealso>
- manual page in <c>Kernel</c>.</p>
- <p>The maximum number of ports that can be open at the same
+ <c>PosixCode</c>, see
+ <seealso marker="kernel:file"><c>file(3)</c></seealso>.</p>
+ <p>The maximum number of ports that can be open at the same
time can be configured by passing command-line flag
- <seealso marker="erl#max_ports"><c>+Q</c></seealso> to
- <c>erl(1)</c>.</p>
+ <seealso marker="erl#max_ports"><c>+Q</c></seealso> to
+ <c>erl(1)</c>.</p>
</desc>
</func>
@@ -3587,11 +3779,11 @@ os_prompt% </pre>
<desc>
<p>Portable hash function that gives the same hash for
the same Erlang term regardless of machine architecture and
- <c>ERTS</c> version (the BIF was introduced in <c>ERTS</c> 4.9.1.1).
+ ERTS version (the BIF was introduced in ERTS 4.9.1.1).
The function returns a hash value for
<c><anno>Term</anno></c> within the range
<c>1..<anno>Range</anno></c>. The maximum value for
- <c><anno>Range</anno></c> is 2^32.</p>
+ <c><anno>Range</anno></c> is 2^32.</p>
<p>This BIF can be used instead of the old deprecated BIF
<c>erlang:hash/2</c>, as it calculates better hashes for
all data types, but consider using <c>phash2/1,2</c> instead.</p>
@@ -3607,11 +3799,11 @@ os_prompt% </pre>
<desc>
<p>Portable hash function that gives the same hash for
the same Erlang term regardless of machine architecture and
- <c>ERTS</c> version (the BIF was introduced in <c>ERTS</c> 5.2).
+ ERTS version (the BIF was introduced in ERTS 5.2).
The function returns a hash value for
- <c><anno>Term</anno></c> within the range
- <c>0..<anno>Range</anno>-1</c>. The maximum value for
- <c><anno>Range</anno></c> is 2^32. When without argument
+ <c><anno>Term</anno></c> within the range
+ <c>0..<anno>Range</anno>-1</c>. The maximum value for
+ <c><anno>Range</anno></c> is 2^32. When without argument
<c><anno>Range</anno></c>, a value in the range
0..2^27-1 is returned.</p>
<p>This BIF is always to be used for hashing terms. It
@@ -3631,66 +3823,109 @@ os_prompt% </pre>
representation of <c><anno>Pid</anno></c>.</p>
<warning>
<p>This BIF is intended for debugging and is not to be used
- in application programs.</p>
+ in application programs.</p>
</warning>
</desc>
</func>
<func>
+ <name name="port_call" arity="3"/>
+ <fsummary>Perform a synchronous call to a port with term data.</fsummary>
+ <desc>
+ <p>Performs a synchronous call to a port. The meaning of
+ <c><anno>Operation</anno></c> and <c><anno>Data</anno></c>
+ depends on the port, that is,
+ on the port driver. Not all port drivers support this feature.</p>
+ <p><c><anno>Port</anno></c> is a port identifier,
+ referring to a driver.</p>
+ <p><c><anno>Operation</anno></c> is an integer, which is passed on to
+ the driver.</p>
+ <p><c><anno>Data</anno></c> is any Erlang term. This data is converted
+ to binary term format and sent to the port.</p>
+ <p>Returns a term from the driver. The meaning of the returned
+ data also depends on the port driver.</p>
+ <p>Failures:</p>
+ <taglist>
+ <tag><c>badarg</c></tag>
+ <item>
+ If <c><anno>Port</anno></c> is not an identifier of an open port,
+ or the registered name of an open port. If the calling
+ process was previously linked to the closed port,
+ identified by <c><anno>Port</anno></c>, the exit signal
+ from the port is guaranteed to be delivered before this
+ <c>badarg</c> exception occurs.
+ </item>
+ <tag><c>badarg</c></tag>
+ <item>
+ If <c><anno>Operation</anno></c> does not fit in a 32-bit integer.
+ </item>
+ <tag><c>badarg</c></tag>
+ <item>
+ If the port driver does not support synchronous control operations.
+ </item>
+ <tag><c>badarg</c></tag>
+ <item>
+ If the port driver so decides for any reason (probably
+ something wrong with <c><anno>Operation</anno></c>
+ or <c><anno>Data</anno></c>).
+ </item>
+ </taglist>
+ </desc>
+ </func>
+
+ <func>
<name name="port_close" arity="1"/>
- <fsummary>Closes an open port.</fsummary>
- <desc>
- <p>Closes an open port. Roughly the same as
- <c><anno>Port</anno> ! {self(), close}</c> except for the error behavior
- (see the following), being synchronous, and that the port does
- <em>not</em> reply with <c>{Port, closed}</c>. Any process can
- close a port with <c>port_close/1</c>, not only the port owner
- (the connected process). If the calling process is linked to
+ <fsummary>Close an open port.</fsummary>
+ <desc>
+ <p>Closes an open port. Roughly the same as <c><anno>Port</anno> !
+ {self(), close}</c> except for the error behavior
+ (see below), being synchronous, and that the port does
+ <em>not</em> reply with <c>{Port, closed}</c>. Any process can
+ close a port with <c>port_close/1</c>, not only the port owner
+ (the connected process). If the calling process is linked to
the port identified by <c><anno>Port</anno></c>, the exit
- signal from the port is guaranteed to be delivered before
- <c>port_close/1</c> returns.</p>
+ signal from the port is guaranteed to be delivered before
+ <c>port_close/1</c> returns.</p>
<p>For comparison: <c><anno>Port</anno> ! {self(), close}</c>
- only fails with <c>badarg</c> if <c><anno>Port</anno></c> does
- not refer to a port or a process. If <c><anno>Port</anno></c>
- is a closed port, nothing happens. If <c><anno>Port</anno></c>
+ only fails with <c>badarg</c> if <c><anno>Port</anno></c> does
+ not refer to a port or a process. If <c><anno>Port</anno></c>
+ is a closed port, nothing happens. If <c><anno>Port</anno></c>
is an open port and the calling process is the port owner,
- the port replies with <c>{Port, closed}</c> when all buffers
- have been flushed and the port really closes. If the calling
- process is not the port owner, the <em>port owner</em> fails
- with <c>badsig</c>.</p>
+ the port replies with <c>{Port, closed}</c> when all buffers
+ have been flushed and the port really closes. If the calling
+ process is not the port owner, the <em>port owner</em> fails
+ with <c>badsig</c>.</p>
<p>Notice that any process can close a port using
<c><anno>Port</anno> ! {PortOwner, close}</c> as if it itself was
the port owner, but the reply always goes to the port owner.</p>
- <p>As from OTP R16, <c><anno>Port</anno> ! {PortOwner, close}</c> is truly
- asynchronous. Notice that this operation has always been
- documented as an asynchronous operation, while the underlying
- implementation has been synchronous. <c>port_close/1</c> is
- however still fully synchronous. This because of its error
- behavior.</p>
- <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not an identifier
- of an open port, or the registered name of an open port.
- If the calling process was previously linked to the closed
- port, identified by <c><anno>Port</anno></c>, the exit
- signal from the port is guaranteed to be delivered before
- this <c>badarg</c> exception occurs.</p>
+ <p>As from Erlang/OTP R16,
+ <c><anno>Port</anno> ! {PortOwner, close}</c> is truly
+ asynchronous. Notice that this operation has always been
+ documented as an asynchronous operation, while the underlying
+ implementation has been synchronous. <c>port_close/1</c> is
+ however still fully synchronous because of its error behavior.</p>
+ <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not an
+ identifier of an open port, or the registered name of an open port.
+ If the calling process was previously linked to the closed
+ port, identified by <c><anno>Port</anno></c>, the exit
+ signal from the port is guaranteed to be delivered before
+ this <c>badarg</c> exception occurs.</p>
</desc>
</func>
<func>
<name name="port_command" arity="2"/>
- <fsummary>Sends data to a port.</fsummary>
+ <fsummary>Send data to a port.</fsummary>
<desc>
<p>Sends data to a port. Same as
- <c><anno>Port</anno> ! {PortOwner, {command, Data}}</c> except
- for the error
- behavior and being synchronous (see the following). Any process
- can send data to a port with <c>port_command/2</c>, not only the
- port owner (the connected process).</p>
- <p>For comparison: <c><anno>Port</anno> ! {PortOwner, {command, Data}}</c>
- only fails with <c>badarg</c> if <c><anno>Port</anno></c>
- does not refer to a port or a process. If
- <c><anno>Port</anno></c> is a closed port, the data message
- disappears
+ <c><anno>Port</anno> ! {PortOwner, {command, Data}}</c> except for
+ the error behavior and being synchronous (see below). Any process
+ can send data to a port with <c>port_command/2</c>, not only the
+ port owner (the connected process).</p>
+ <p>For comparison: <c><anno>Port</anno> ! {PortOwner, {command,
+ Data}}</c> only fails with <c>badarg</c> if <c><anno>Port</anno></c>
+ does not refer to a port or a process. If <c><anno>Port</anno></c> is
+ a closed port, the data message disappears
without a sound. If <c><anno>Port</anno></c> is open and the calling
process is not the port owner, the <em>port owner</em> fails
with <c>badsig</c>. The port owner fails with <c>badsig</c>
@@ -3698,57 +3933,58 @@ os_prompt% </pre>
<p>Notice that any process can send to a port using
<c><anno>Port</anno> ! {PortOwner, {command, <anno>Data</anno>}}</c>
as if it itself was the port owner.</p>
- <p>If the port is busy, the calling process is suspended
- until the port is not busy any more.</p>
- <p>As from OTP-R16, <c><anno>Port</anno> ! {PortOwner, {command, Data}}</c>
- is truly asynchronous. Notice that this operation has always been
- documented as an asynchronous operation, while the underlying
- implementation has been synchronous. <c>port_command/2</c> is
- however still fully synchronous. This because of its error
- behavior.</p>
+ <p>If the port is busy, the calling process is suspended
+ until the port is not busy any more.</p>
+ <p>As from Erlang/OTP R16,
+ <c><anno>Port</anno> ! {PortOwner, {command, Data}}</c>
+ is truly asynchronous. Notice that this operation has always been
+ documented as an asynchronous operation, while the underlying
+ implementation has been synchronous. <c>port_command/2</c> is
+ however still fully synchronous because of its error behavior.</p>
<p>Failures:</p>
<taglist>
<tag><c>badarg</c></tag>
<item>
- If <c><anno>Port</anno></c> is not an identifier of an open
- port, or the registered name of an open port. If the
- calling process was previously linked to the closed port,
- identified by <c><anno>Port</anno></c>, the exit signal
- from the port is guaranteed to be delivered before this
- <c>badarg</c> exception occurs.
- </item>
+ <p>If <c><anno>Port</anno></c> is not an identifier of an open
+ port, or the registered name of an open port. If the
+ calling process was previously linked to the closed port,
+ identified by <c><anno>Port</anno></c>, the exit signal
+ from the port is guaranteed to be delivered before this
+ <c>badarg</c> exception occurs.</p>
+ </item>
<tag><c>badarg</c></tag>
<item>
- If <c><anno>Data</anno></c> is an invalid I/O list.
- </item>
- </taglist>
+ <p>If <c><anno>Data</anno></c> is an invalid I/O list.</p>
+ </item>
+ </taglist>
</desc>
</func>
<func>
<name name="port_command" arity="3"/>
- <fsummary>Sends data to a port.</fsummary>
+ <fsummary>Send data to a port.</fsummary>
<desc>
<p>Sends data to a port. <c>port_command(Port, Data, [])</c>
- equals <c>port_command(Port, Data)</c>.</p>
- <p>If the port command is aborted, <c>false</c> is returned,
- otherwise <c>true</c>.</p>
- <p>If the port is busy, the calling process is suspended
- until the port is not busy any more.</p>
- <p>The following <c><anno>Option</anno></c>s are valid:</p>
+ equals <c>port_command(Port, Data)</c>.</p>
+ <p>If the port command is aborted, <c>false</c> is returned,
+ otherwise <c>true</c>.</p>
+ <p>If the port is busy, the calling process is suspended
+ until the port is not busy anymore.</p>
+ <p><c><anno>Option</anno></c>s:</p>
<taglist>
<tag><c>force</c></tag>
<item>The calling process is not suspended if the port is
- busy, instead the port command is forced through. The
- call fails with a <c>notsup</c> exception if the
- driver of the port does not support this. For more
- information, see driver flag
- <seealso marker="driver_entry#driver_flags"><![CDATA[ERL_DRV_FLAG_SOFT_BUSY]]></seealso>.
+ busy, instead the port command is forced through. The
+ call fails with a <c>notsup</c> exception if the
+ driver of the port does not support this. For more
+ information, see driver flag
+ <seealso marker="driver_entry#driver_flags">
+ <c>![CDATA[ERL_DRV_FLAG_SOFT_BUSY]]</c></seealso>.
</item>
<tag><c>nosuspend</c></tag>
<item>The calling process is not suspended if the port is
- busy, instead the port command is aborted and
- <c>false</c> is returned.
+ busy, instead the port command is aborted and
+ <c>false</c> is returned.
</item>
</taglist>
<note>
@@ -3758,34 +3994,34 @@ os_prompt% </pre>
<taglist>
<tag><c>badarg</c></tag>
<item>
- If <c><anno>Port</anno></c> is not an identifier of an open
- port, or the registered name of an open port. If the
- calling process was previously linked to the closed port,
- identified by <c><anno>Port</anno></c>, the exit signal
- from the port is guaranteed to be delivered before this
- <c>badarg</c> exception occurs.
- </item>
+ If <c><anno>Port</anno></c> is not an identifier of an open
+ port, or the registered name of an open port. If the
+ calling process was previously linked to the closed port,
+ identified by <c><anno>Port</anno></c>, the exit signal
+ from the port is guaranteed to be delivered before this
+ <c>badarg</c> exception occurs.
+ </item>
<tag><c>badarg</c></tag>
<item>
- If <c><anno>Data</anno></c> is an invalid I/O list.
- </item>
+ If <c><anno>Data</anno></c> is an invalid I/O list.
+ </item>
<tag><c>badarg</c></tag>
<item>
- If <c><anno>OptionList</anno></c> is an invalid option list.
- </item>
+ If <c><anno>OptionList</anno></c> is an invalid option list.
+ </item>
<tag><c>notsup</c></tag>
<item>
- If option <c>force</c> has been passed, but the
- driver of the port does not allow forcing through
- a busy port.
- </item>
- </taglist>
+ If option <c>force</c> has been passed, but the
+ driver of the port does not allow forcing through
+ a busy port.
+ </item>
+ </taglist>
</desc>
</func>
<func>
<name name="port_connect" arity="2"/>
- <fsummary>Sets the owner of a port.</fsummary>
+ <fsummary>Set the owner of a port.</fsummary>
<desc>
<p>Sets the port owner (the connected port) to <c><anno>Pid</anno></c>.
Roughly the same as
@@ -3793,14 +4029,14 @@ os_prompt% </pre>
except for the following:</p>
<list type="bulleted">
<item>
- <p>The error behavior differs, see the following.</p>
+ <p>The error behavior differs, see below.</p>
</item>
<item>
<p>The port does <em>not</em> reply with
<c>{Port,connected}</c>.</p>
</item>
<item>
- <p><c>port_connect/1</c> is synchronous, see the following.</p>
+ <p><c>port_connect/1</c> is synchronous, see below.</p>
</item>
<item>
<p>The new port owner gets linked to the port.</p>
@@ -3812,7 +4048,7 @@ os_prompt% </pre>
<c>port_connect/2</c>.</p>
<p>For comparison:
<c><anno>Port</anno> ! {self(), {connect, <anno>Pid</anno>}}</c>
- only fails with <c>badarg</c> if <c><anno>Port</anno></c>
+ only fails with <c>badarg</c> if <c><anno>Port</anno></c>
does not refer to a port or a process. If
<c><anno>Port</anno></c> is a closed port, nothing happens.
If <c><anno>Port</anno></c>
@@ -3822,40 +4058,39 @@ os_prompt% </pre>
the port, while the new is not. If <c><anno>Port</anno></c> is an open
port and the calling process is not the port owner,
the <em>port owner</em> fails with <c>badsig</c>. The port
- owner fails with <c>badsig</c> also if <c><anno>Pid</anno></c> is not an
- existing local process identifier.</p>
+ owner fails with <c>badsig</c> also if <c><anno>Pid</anno></c> is not
+ an existing local process identifier.</p>
<p>Notice that any process can set the port owner using
<c><anno>Port</anno> ! {PortOwner, {connect, <anno>Pid</anno>}}</c>
as if it itself was the port owner, but the reply always goes to
the port owner.</p>
- <p>As from OTP-R16,
- <c><anno>Port</anno> ! {PortOwner, {connect, <anno>Pid</anno>}}</c> is
- truly asynchronous. Notice that this operation has always been
- documented as an asynchronous operation, while the underlying
- implementation has been synchronous. <c>port_connect/2</c> is
- however still fully synchronous. This because of its error
- behavior.</p>
+ <p>As from Erlang/OTP R16,
+ <c><anno>Port</anno> ! {PortOwner, {connect, <anno>Pid</anno>}}</c>
+ is truly asynchronous. Notice that this operation has always been
+ documented as an asynchronous operation, while the underlying
+ implementation has been synchronous. <c>port_connect/2</c> is
+ however still fully synchronous because of its error behavior.</p>
<p>Failures:</p>
<taglist>
<tag><c>badarg</c></tag>
<item>
- If <c><anno>Port</anno></c> is not an identifier of an open port, or
- the registered name of an open port. If the calling
- process was previously linked to the closed port,
- identified by <c><anno>Port</anno></c>, the exit signal
- from the port is guaranteed to be delivered before this
- <c>badarg</c> exception occurs.
- </item>
+ If <c><anno>Port</anno></c> is not an identifier of an open port,
+ or the registered name of an open port. If the calling
+ process was previously linked to the closed port,
+ identified by <c><anno>Port</anno></c>, the exit signal
+ from the port is guaranteed to be delivered before this
+ <c>badarg</c> exception occurs.
+ </item>
<tag><c>badarg</c></tag>
- <item>If process identified by <c>Pid</c> is not an existing
- local process.</item>
- </taglist>
+ <item>If the process identified by <c>Pid</c> is not an existing
+ local process.</item>
+ </taglist>
</desc>
</func>
<func>
<name name="port_control" arity="3"/>
- <fsummary>Performs a synchronous control operation on a port.</fsummary>
+ <fsummary>Perform a synchronous control operation on a port.</fsummary>
<desc>
<p>Performs a synchronous control operation on a port.
The meaning of <c><anno>Operation</anno></c> and
@@ -3869,71 +4104,24 @@ os_prompt% </pre>
<taglist>
<tag><c>badarg</c></tag>
<item>
- If <c><anno>Port</anno></c> is not an open port or the registered
- name of an open port.
- </item>
+ If <c><anno>Port</anno></c> is not an open port or the registered
+ name of an open port.
+ </item>
<tag><c>badarg</c></tag>
<item>
- If <c><anno>Operation</anno></c> cannot fit in a 32-bit integer.
- </item>
+ If <c><anno>Operation</anno></c> cannot fit in a 32-bit integer.
+ </item>
<tag><c>badarg</c></tag>
<item>
- If the port driver does not support synchronous control
- operations.
- </item>
+ If the port driver does not support synchronous control operations.
+ </item>
<tag><c>badarg</c></tag>
<item>
- If the port driver so decides for any reason (probably
+ If the port driver so decides for any reason (probably
something wrong with <c><anno>Operation</anno></c> or
<c><anno>Data</anno></c>).
- </item>
- </taglist>
- </desc>
- </func>
-
- <func>
- <name name="port_call" arity="3"/>
- <fsummary>Performs a synchronous call to a port with term data.</fsummary>
- <desc>
- <p>Performs a synchronous call to a port. The meaning of
- <c><anno>Operation</anno></c> and <c><anno>Data</anno></c>
- depends on the port, that is,
- on the port driver. Not all port drivers support this feature.</p>
- <p><c><anno>Port</anno></c> is a port identifier,
- referring to a driver.</p>
- <p><c><anno>Operation</anno></c> is an integer, which is passed on to
- the driver.</p>
- <p><c><anno>Data</anno></c> is any Erlang term. This data is converted
- to binary term format and sent to the port.</p>
- <p>Returns a term from the driver. The meaning of the returned
- data also depends on the port driver.</p>
- <p>Failures:</p>
- <taglist>
- <tag><c>badarg</c></tag>
- <item>
- If <c><anno>Port</anno></c> is not an identifier of an open port,
- or the registered name of an open port. If the calling
- process was previously linked to the closed port,
- identified by <c><anno>Port</anno></c>, the exit signal
- from the port is guaranteed to be delivered before this
- <c>badarg</c> exception occurs.
- </item>
- <tag><c>badarg</c></tag>
- <item>
- If <c><anno>Operation</anno></c> does not fit in a 32-bit integer.
- </item>
- <tag><c>badarg</c></tag>
- <item>
- If the port driver does not support synchronous control
- operations.
- </item>
- <tag><c>badarg</c></tag>
- <item>
- If the port driver so decides for any reason (probably
- something wrong with <c><anno>Operation</anno></c>
- or <c><anno>Data</anno></c>).
- </item>
- </taglist>
+ </item>
+ </taglist>
</desc>
</func>
@@ -3945,11 +4133,11 @@ os_prompt% </pre>
<c><anno>Port</anno></c>, or <c>undefined</c> if the port is not open.
The order of the tuples is undefined, and all the
tuples are not mandatory.
- If the port is closed and the calling process
- was previously linked to the port, the exit signal from the
- port is guaranteed to be delivered before <c>port_info/1</c>
- returns <c>undefined</c>.</p>
- <p>The result contains information about the following
+ If the port is closed and the calling process
+ was previously linked to the port, the exit signal from the
+ port is guaranteed to be delivered before <c>port_info/1</c>
+ returns <c>undefined</c>.</p>
+ <p>The result contains information about the following
<c>Item</c>s:</p>
<list type="bulleted">
<item><c>registered_name</c> (if the port has a registered
@@ -3962,9 +4150,9 @@ os_prompt% </pre>
<item><c>output</c></item>
</list>
<p>For more information about the different <c>Item</c>s, see
- <seealso marker="#port_info/2">port_info/2</seealso>.</p>
+ <seealso marker="#port_info/2"><c>port_info/2</c></seealso>.</p>
<p>Failure: <c>badarg</c> if <c>Port</c> is not a local port
- identifier, or an atom.</p>
+ identifier, or an atom.</p>
</desc>
</func>
@@ -3972,15 +4160,15 @@ os_prompt% </pre>
<name name="port_info" arity="2" clause_i="1"/>
<fsummary>Information about the connected process of a port.</fsummary>
<desc>
- <p><c><anno>Pid</anno></c> is the process identifier of the process
- connected to the port.</p>
- <p>If the port identified by <c><anno>Port</anno></c> is not open,
- <c>undefined</c> is returned. If the port is closed and the
- calling process was previously linked to the port, the exit
- signal from the port is guaranteed to be delivered before
- <c>port_info/2</c> returns <c>undefined</c>.</p>
+ <p><c><anno>Pid</anno></c> is the process identifier of the process
+ connected to the port.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned. If the port is closed and the
+ calling process was previously linked to the port, the exit
+ signal from the port is guaranteed to be delivered before
+ <c>port_info/2</c> returns <c>undefined</c>.</p>
<p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
- port identifier, or an atom.</p>
+ port identifier, or an atom.</p>
</desc>
</func>
@@ -3988,15 +4176,15 @@ os_prompt% </pre>
<name name="port_info" arity="2" clause_i="2"/>
<fsummary>Information about the internal index of a port.</fsummary>
<desc>
- <p><c><anno>Index</anno></c> is the internal index of the port. This
- index can be used to separate ports.</p>
- <p>If the port identified by <c><anno>Port</anno></c> is not open,
- <c>undefined</c> is returned. If the port is closed and the
- calling process was previously linked to the port, the exit
- signal from the port is guaranteed to be delivered before
- <c>port_info/2</c> returns <c>undefined</c>.</p>
+ <p><c><anno>Index</anno></c> is the internal index of the port. This
+ index can be used to separate ports.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned. If the port is closed and the
+ calling process was previously linked to the port, the exit
+ signal from the port is guaranteed to be delivered before
+ <c>port_info/2</c> returns <c>undefined</c>.</p>
<p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
- port identifier, or an atom.</p>
+ port identifier, or an atom.</p>
</desc>
</func>
@@ -4004,15 +4192,15 @@ os_prompt% </pre>
<name name="port_info" arity="2" clause_i="3"/>
<fsummary>Information about the input of a port.</fsummary>
<desc>
- <p><c><anno>Bytes</anno></c> is the total number of bytes
- read from the port.</p>
- <p>If the port identified by <c><anno>Port</anno></c> is not open,
- <c>undefined</c> is returned. If the port is closed and the
- calling process was previously linked to the port, the exit
- signal from the port is guaranteed to be delivered before
- <c>port_info/2</c> returns <c>undefined</c>.</p>
+ <p><c><anno>Bytes</anno></c> is the total number of bytes
+ read from the port.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned. If the port is closed and the
+ calling process was previously linked to the port, the exit
+ signal from the port is guaranteed to be delivered before
+ <c>port_info/2</c> returns <c>undefined</c>.</p>
<p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
- port identifier, or an atom.</p>
+ port identifier, or an atom.</p>
</desc>
</func>
@@ -4020,15 +4208,15 @@ os_prompt% </pre>
<name name="port_info" arity="2" clause_i="4"/>
<fsummary>Information about the links of a port.</fsummary>
<desc>
- <p><c><anno>Pids</anno></c> is a list of the process identifiers
- of the processes that the port is linked to.</p>
- <p>If the port identified by <c><anno>Port</anno></c> is not open,
- <c>undefined</c> is returned. If the port is closed and the
- calling process was previously linked to the port, the exit
- signal from the port is guaranteed to be delivered before
- <c>port_info/2</c> returns <c>undefined</c>.</p>
+ <p><c><anno>Pids</anno></c> is a list of the process identifiers
+ of the processes that the port is linked to.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned. If the port is closed and the
+ calling process was previously linked to the port, the exit
+ signal from the port is guaranteed to be delivered before
+ <c>port_info/2</c> returns <c>undefined</c>.</p>
<p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
- port identifier, or an atom.</p>
+ port identifier, or an atom.</p>
</desc>
</func>
@@ -4036,7 +4224,7 @@ os_prompt% </pre>
<name name="port_info" arity="2" clause_i="5"/>
<fsummary>Information about the locking of a port.</fsummary>
<desc>
- <p><c><anno>Locking</anno></c> is one of the following:</p>
+ <p><c><anno>Locking</anno></c> is one of the following:</p>
<list type="bulleted">
<item><c>false</c> (emulator without SMP support)</item>
<item><c>port_level</c> (port-specific locking)</item>
@@ -4044,13 +4232,13 @@ os_prompt% </pre>
</list>
<p>Notice that these results are highly implementation-specific
and can change in a future release.</p>
- <p>If the port identified by <c><anno>Port</anno></c> is not open,
- <c>undefined</c> is returned. If the port is closed and the
- calling process was previously linked to the port, the exit
- signal from the port is guaranteed to be delivered before
- <c>port_info/2</c> returns <c>undefined</c>.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned. If the port is closed and the
+ calling process was previously linked to the port, the exit
+ signal from the port is guaranteed to be delivered before
+ <c>port_info/2</c> returns <c>undefined</c>.</p>
<p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
- port identifier, or an atom.</p>
+ port identifier, or an atom.</p>
</desc>
</func>
@@ -4058,17 +4246,17 @@ os_prompt% </pre>
<name name="port_info" arity="2" clause_i="6"/>
<fsummary>Information about the memory size of a port.</fsummary>
<desc>
- <p><c><anno>Bytes</anno></c> is the total number of
- bytes allocated for this port by the runtime system. The
- port itself can have allocated memory that is not
- included in <c><anno>Bytes</anno></c>.</p>
- <p>If the port identified by <c><anno>Port</anno></c> is not open,
- <c>undefined</c> is returned. If the port is closed and the
- calling process was previously linked to the port, the exit
- signal from the port is guaranteed to be delivered before
- <c>port_info/2</c> returns <c>undefined</c>.</p>
+ <p><c><anno>Bytes</anno></c> is the total number of
+ bytes allocated for this port by the runtime system. The
+ port itself can have allocated memory that is not
+ included in <c><anno>Bytes</anno></c>.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned. If the port is closed and the
+ calling process was previously linked to the port, the exit
+ signal from the port is guaranteed to be delivered before
+ <c>port_info/2</c> returns <c>undefined</c>.</p>
<p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
- port identifier, or an atom.</p>
+ port identifier, or an atom.</p>
</desc>
</func>
@@ -4076,113 +4264,130 @@ os_prompt% </pre>
<name name="port_info" arity="2" clause_i="7"/>
<fsummary>Information about the monitors of a port.</fsummary>
<desc>
- <p><c><anno>Monitors</anno></c> represent processes that this port
- monitors.</p>
- <p>If the port identified by <c><anno>Port</anno></c> is not open,
- <c>undefined</c> is returned. If the port is closed and the
- calling process was previously linked to the port, the exit
- signal from the port is guaranteed to be delivered before
- <c>port_info/2</c> returns <c>undefined</c>.</p>
+ <p><c><anno>Monitors</anno></c> represent processes monitored by
+ this port.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned. If the port is closed and the
+ calling process was previously linked to the port, the exit
+ signal from the port is guaranteed to be delivered before
+ <c>port_info/2</c> returns <c>undefined</c>.</p>
<p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
- port identifier, or an atom.</p>
+ port identifier, or an atom.</p>
</desc>
</func>
<func>
<name name="port_info" arity="2" clause_i="8"/>
+ <fsummary>Which processes are monitoring this port.</fsummary>
+ <desc>
+ <p>Returns list of pids that are monitoring given port at the
+ moment.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned. If the port is closed and the
+ calling process was previously linked to the port, the exit
+ signal from the port is guaranteed to be delivered before
+ <c>port_info/2</c> returns <c>undefined</c>.</p>
+ <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
+ port identifier, or an atom.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="port_info" arity="2" clause_i="9"/>
<fsummary>Information about the name of a port.</fsummary>
<desc>
- <p><c><anno>Name</anno></c> is the command name set by
- <seealso marker="#open_port/2">open_port/2</seealso>.</p>
- <p>If the port identified by <c><anno>Port</anno></c> is not open,
- <c>undefined</c> is returned. If the port is closed and the
- calling process was previously linked to the port, the exit
- signal from the port is guaranteed to be delivered before
- <c>port_info/2</c> returns <c>undefined</c>.</p>
+ <p><c><anno>Name</anno></c> is the command name set by
+ <seealso marker="#open_port/2"><c>open_port/2</c></seealso>.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned. If the port is closed and the
+ calling process was previously linked to the port, the exit
+ signal from the port is guaranteed to be delivered before
+ <c>port_info/2</c> returns <c>undefined</c>.</p>
<p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
- port identifier, or an atom.</p>
+ port identifier, or an atom.</p>
</desc>
</func>
<func>
- <name name="port_info" arity="2" clause_i="9"/>
+ <name name="port_info" arity="2" clause_i="10"/>
<fsummary>Information about the OS pid of a port.</fsummary>
<desc>
- <p><c><anno>OsPid</anno></c> is the process identifier (or equivalent)
- of an OS process created with
- <seealso marker="#open_port/2">open_port({spawn | spawn_executable,
- Command}, Options)</seealso>. If the port is not the result of spawning
- an OS process, the value is <c>undefined</c>.</p>
- <p>If the port identified by <c><anno>Port</anno></c> is not open,
- <c>undefined</c> is returned. If the port is closed and the
- calling process was previously linked to the port, the exit
- signal from the port is guaranteed to be delivered before
- <c>port_info/2</c> returns <c>undefined</c>.</p>
+ <p><c><anno>OsPid</anno></c> is the process identifier (or equivalent)
+ of an OS process created with
+ <seealso marker="#open_port/2"><c>open_port({spawn | spawn_executable,
+ Command}, Options)</c></seealso>. If the port is not the result of
+ spawning an OS process, the value is <c>undefined</c>.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned. If the port is closed and the
+ calling process was previously linked to the port, the exit
+ signal from the port is guaranteed to be delivered before
+ <c>port_info/2</c> returns <c>undefined</c>.</p>
<p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
- port identifier, or an atom.</p>
+ port identifier, or an atom.</p>
</desc>
</func>
<func>
- <name name="port_info" arity="2" clause_i="10"/>
+ <name name="port_info" arity="2" clause_i="11"/>
<fsummary>Information about the output of a port.</fsummary>
<desc>
- <p><c><anno>Bytes</anno></c> is the total number of bytes written
- to the port from Erlang processes using
- <seealso marker="#port_command/2">port_command/2</seealso>,
- <seealso marker="#port_command/3">port_command/3</seealso>,
- or <c><anno>Port</anno> ! {Owner, {command, Data}</c>.</p>
- <p>If the port identified by <c><anno>Port</anno></c> is not open,
- <c>undefined</c> is returned. If the port is closed and the
- calling process was previously linked to the port, the exit
- signal from the port is guaranteed to be delivered before
- <c>port_info/2</c> returns <c>undefined</c>.</p>
+ <p><c><anno>Bytes</anno></c> is the total number of bytes written
+ to the port from Erlang processes using
+ <seealso marker="#port_command/2"><c>port_command/2</c></seealso>,
+ <seealso marker="#port_command/3"><c>port_command/3</c></seealso>,
+ or <c><anno>Port</anno> ! {Owner, {command, Data}</c>.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned. If the port is closed and the
+ calling process was previously linked to the port, the exit
+ signal from the port is guaranteed to be delivered before
+ <c>port_info/2</c> returns <c>undefined</c>.</p>
<p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
- port identifier, or an atom.</p>
+ port identifier, or an atom.</p>
</desc>
</func>
<func>
- <name name="port_info" arity="2" clause_i="11"/>
+ <name name="port_info" arity="2" clause_i="12"/>
<fsummary>Information about the parallelism hint of a port.</fsummary>
<desc>
- <p><c><anno>Boolean</anno></c> corresponds to the port parallelism
- hint being used by this port. For more information, see option
- <seealso marker="#open_port_parallelism">parallelism</seealso>
- of <seealso marker="#open_port/2">open_port/2</seealso>.</p>
+ <p><c><anno>Boolean</anno></c> corresponds to the port parallelism
+ hint used by this port. For more information, see option
+ <seealso marker="#open_port_parallelism"><c>parallelism</c></seealso>
+ of <seealso marker="#open_port/2"><c>open_port/2</c></seealso>.</p>
</desc>
</func>
<func>
- <name name="port_info" arity="2" clause_i="12"/>
+ <name name="port_info" arity="2" clause_i="13"/>
<fsummary>Information about the queue size of a port.</fsummary>
<desc>
- <p><c><anno>Bytes</anno></c> is the total number
- of bytes queued by the port using the <c>ERTS</c> driver queue
- implementation.</p>
- <p>If the port identified by <c><anno>Port</anno></c> is not open,
- <c>undefined</c> is returned. If the port is closed and the
- calling process was previously linked to the port, the exit
- signal from the port is guaranteed to be delivered before
- <c>port_info/2</c> returns <c>undefined</c>.</p>
+ <p><c><anno>Bytes</anno></c> is the total number
+ of bytes queued by the port using the ERTS driver queue
+ implementation.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned. If the port is closed and the
+ calling process was previously linked to the port, the exit
+ signal from the port is guaranteed to be delivered before
+ <c>port_info/2</c> returns <c>undefined</c>.</p>
<p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
- port identifier, or an atom.</p>
+ port identifier, or an atom.</p>
</desc>
</func>
<func>
- <name name="port_info" arity="2" clause_i="13"/>
+ <name name="port_info" arity="2" clause_i="14"/>
<fsummary>Information about the registered name of a port.</fsummary>
<desc>
- <p><c><anno>RegisteredName</anno></c> is the registered name of
- the port. If the port has no registered name, <c>[]</c> is returned.</p>
- <p>If the port identified by <c><anno>Port</anno></c> is not open,
- <c>undefined</c> is returned. If the port is closed and the
- calling process was previously linked to the port, the exit
- signal from the port is guaranteed to be delivered before
- <c>port_info/2</c> returns <c>undefined</c>.</p>
+ <p><c><anno>RegisteredName</anno></c> is the registered name of
+ the port. If the port has no registered name, <c>[]</c> is
+ returned.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned. If the port is closed and the
+ calling process was previously linked to the port, the exit
+ signal from the port is guaranteed to be delivered before
+ <c>port_info/2</c> returns <c>undefined</c>.</p>
<p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
- port identifier, or an atom.</p>
+ port identifier, or an atom.</p>
</desc>
</func>
@@ -4194,24 +4399,24 @@ os_prompt% </pre>
representation of the port identifier <c><anno>Port</anno></c>.</p>
<warning>
<p>This BIF is intended for debugging. It is not to be used
- in application programs.</p>
+ in application programs.</p>
</warning>
</desc>
</func>
<func>
<name name="ports" arity="0"/>
- <fsummary>Lists all existing ports.</fsummary>
+ <fsummary>List all existing ports.</fsummary>
<desc>
- <p>Returns a list of port identifiers corresponding to all the
- ports existing on the local node.</p>
- <p>Notice that an exiting port exists, but is not open.</p>
+ <p>Returns a list of port identifiers corresponding to all the
+ ports existing on the local node.</p>
+ <p>Notice that an exiting port exists, but is not open.</p>
</desc>
</func>
<func>
<name name="pre_loaded" arity="0"/>
- <fsummary>Lists all pre-loaded modules.</fsummary>
+ <fsummary>List all preloaded modules.</fsummary>
<desc>
<p>Returns a list of Erlang modules that are preloaded in
the system. As all loading of code is done through the file
@@ -4222,12 +4427,13 @@ os_prompt% </pre>
<func>
<name name="process_display" arity="2"/>
- <fsummary>Writes information about a local process on standard error.</fsummary>
+ <fsummary>Write information about a local process on standard error.
+ </fsummary>
<desc>
<p>Writes information about the local process <c><anno>Pid</anno></c> on
standard error. The only allowed value for the atom
- <c><anno>Type</anno></c> is <c>backtrace</c>, which shows the contents of
- the call stack, including information about the call chain, with
+ <c><anno>Type</anno></c> is <c>backtrace</c>, which shows the contents
+ of the call stack, including information about the call chain, with
the current function printed first. The format of the output
is not further defined.</p>
</desc>
@@ -4235,7 +4441,7 @@ os_prompt% </pre>
<func>
<name name="process_flag" arity="2" clause_i="1"/>
- <fsummary>Sets process flag <c>trap_exit</c> for the calling process.</fsummary>
+ <fsummary>Set process flag trap_exit for the calling process.</fsummary>
<desc>
<p>When <c>trap_exit</c> is set to <c>true</c>, exit signals
arriving to a process are converted to <c>{'EXIT', From, Reason}</c>
@@ -4246,13 +4452,14 @@ os_prompt% </pre>
linked processes. Application processes are normally
not to trap exits.</p>
<p>Returns the old value of the flag.</p>
- <p>See also <seealso marker="#exit/2">exit/2</seealso>.</p>
+ <p>See also <seealso marker="#exit/2"><c>exit/2</c></seealso>.</p>
</desc>
</func>
<func>
<name name="process_flag" arity="2" clause_i="2"/>
- <fsummary>Sets process flag <c>error_handler</c> for the calling process.</fsummary>
+ <fsummary>Set process flag error_handler for the calling process.
+ </fsummary>
<desc>
<p>Used by a process to redefine the error handler
for undefined function calls and undefined registered
@@ -4265,8 +4472,10 @@ os_prompt% </pre>
<func>
<name name="process_flag" arity="2" clause_i="3"/>
- <fsummary>Sets process flag <c>min_heap_size</c> for the calling process.</fsummary>
+ <fsummary>Set process flag min_heap_size for the calling process.
+ </fsummary>
<desc>
+ <marker id="process_flag_min_heap_size"/>
<p>Changes the minimum heap size for the calling process.</p>
<p>Returns the old value of the flag.</p>
</desc>
@@ -4274,7 +4483,8 @@ os_prompt% </pre>
<func>
<name name="process_flag" arity="2" clause_i="4"/>
- <fsummary>Sets process flag <c>min_bin_vheap_size</c> for the calling process.</fsummary>
+ <fsummary>Set process flag min_bin_vheap_size for the calling process.
+ </fsummary>
<desc>
<p>Changes the minimum binary virtual heap size for the calling
process.</p>
@@ -4284,12 +4494,128 @@ os_prompt% </pre>
<func>
<name name="process_flag" arity="2" clause_i="5"/>
- <fsummary>Sets process flag <c>priority</c> for the calling process.</fsummary>
+ <fsummary>Set process flag max_heap_size for the calling process.
+ </fsummary>
+ <type name="max_heap_size"/>
+ <desc>
+ <marker id="process_flag_max_heap_size"/>
+ <p>This flag sets the maximum heap size for the calling process.
+ If <c><anno>MaxHeapSize</anno></c> is an integer, the system default
+ values for <c>kill</c> and <c>error_logger</c> are used.
+ </p>
+ <taglist>
+ <tag><c>size</c></tag>
+ <item>
+ <p>The maximum size in words of the process. If set to zero, the
+ heap size limit is disabled. <c>badarg</c> is be thrown if the
+ value is smaller than <seealso marker="#process_flag_min_heap_size">
+ <c>min_heap_size</c></seealso>. The size check is only done when
+ a garbage collection is triggered.</p>
+ <p><c>size</c> is the entire heap of the process when garbage collection
+ is triggered. This includes all generational heaps, the process stack,
+ any <seealso marker="#process_flag_message_queue_data">
+ messages that are considered to be part of the heap</seealso>, and any
+ extra memory that the garbage collector needs during collection.</p>
+ <p><c>size</c> is the same as can be retrieved using
+ <seealso marker="#process_info_total_heap_size">
+ <c>erlang:process_info(Pid, total_heap_size)</c></seealso>,
+ or by adding <c>heap_block_size</c>, <c>old_heap_block_size</c>
+ and <c>mbuf_size</c> from <seealso marker="#process_info_garbage_collection_info">
+ <c>erlang:process_info(Pid, garbage_collection_info)</c></seealso>.</p>
+ </item>
+ <tag><c>kill</c></tag>
+ <item>
+ <p>When set to <c>true</c>, the runtime system sends an
+ untrappable exit signal with reason <c>kill</c> to the process
+ if the maximum heap size is reached. The garbage collection
+ that triggered the <c>kill</c> is not completed, instead the
+ process exits as soon as possible. When set to <c>false</c>,
+ no exit signal is sent to the process, instead it continues
+ executing.</p>
+ <p>If <c>kill</c> is not defined in the map,
+ the system default will be used. The default system default
+ is <c>true</c>. It can be changed by either option
+ <seealso marker="erl#+hmaxk">+hmaxk</seealso> in <c>erl(1)</c>,
+ or <seealso marker="#system_flag_max_heap_size">
+ <c>erlang:system_flag(max_heap_size, MaxHeapSize)</c></seealso>.</p>
+ </item>
+ <tag><c>error_logger</c></tag>
+ <item>
+ <p>When set to <c>true</c>, the runtime system sends a
+ message to the current <seealso marker="kernel:error_logger">
+ <c>error_logger</c></seealso>
+ containing details about the process when the maximum
+ heap size is reached. One <c>error_logger</c> report is sent
+ each time the limit is reached.</p>
+ <p>If <c>error_logger</c> is not defined in the map, the system
+ default is used. The default system default is <c>true</c>.
+ It can be changed by either the option
+ <seealso marker="erl#+hmaxel">+hmaxel</seealso> int <c>erl(1)</c>,
+ or <seealso marker="#system_flag_max_heap_size">
+ <c>erlang:system_flag(max_heap_size, MaxHeapSize)</c></seealso>.</p>
+ </item>
+ </taglist>
+ <p>The heap size of a process is quite hard to predict, especially the
+ amount of memory that is used during the garbage collection. When
+ contemplating using this option, it is recommended to first run
+ it in production with <c>kill</c> set to <c>false</c> and inspect
+ the <c>error_logger</c> reports to see what the normal peak sizes
+ of the processes in the system is and then tune the value
+ accordingly.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="process_flag" arity="2" clause_i="6"/>
+ <fsummary>Set process flag message_queue_data for the calling process.
+ </fsummary>
+ <type name="message_queue_data"/>
+ <desc>
+ <marker id="process_flag_message_queue_data"/>
+ <p>This flag determines how messages in the message queue
+ are stored, as follows:</p>
+ <taglist>
+ <tag><c>off_heap</c></tag>
+ <item>
+ <p><em>All</em> messages in the message queue will be stored
+ outside of the process heap. This implies that <em>no</em>
+ messages in the message queue will be part of a garbage
+ collection of the process.</p>
+ </item>
+ <tag><c>on_heap</c></tag>
+ <item>
+ <p>All messages in the message queue will eventually be
+ placed on heap. They can however temporarily be stored
+ off heap. This is how messages always have been stored
+ up until ERTS 8.0.</p>
+ </item>
+ </taglist>
+ <p>The default <c>message_queue_data</c> process flag is determined
+ by command-line argument <seealso marker="erl#+hmqd">
+ <c>+hmqd</c></seealso> in <c>erl(1)</c>.</p>
+ <p>If the process potentially can get many messages,
+ you are advised to set the flag to <c>off_heap</c>. This
+ because a garbage collection with many messages placed on
+ the heap can become extremly expensive and the process can
+ consume large amounts of memory. Performance of the
+ actual message passing is however generally better when not
+ using flag <c>off_heap</c>.</p>
+ <p>When changing this flag messages will be moved. This work
+ has been initiated but not completed when this function
+ call returns.</p>
+ <p>Returns the old value of the flag.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="process_flag" arity="2" clause_i="7"/>
+ <fsummary>Set process flag priority for the calling process.</fsummary>
<type name="priority_level"/>
<desc>
<p><marker id="process_flag_priority"></marker>
Sets the process priority. <c><anno>Level</anno></c> is an atom.
- There are four priority levels: <c>low</c>,
+ Four priority levels exist: <c>low</c>,
<c>normal</c>, <c>high</c>, and <c>max</c>. Default
is <c>normal</c>.</p>
<note>
@@ -4303,23 +4629,23 @@ os_prompt% </pre>
<c>low</c> are interleaved. Processes on priority
<c>low</c> are selected for execution less
frequently than processes on priority <c>normal</c>.</p>
- <p>When there are runnable processes on priority <c>high</c>,
+ <p>When runnable processes on priority <c>high</c> exist,
no processes on priority <c>low</c> or <c>normal</c> are
- selected for execution. Notice however, that this does
+ selected for execution. Notice however that this does
<em>not</em> mean that no processes on priority <c>low</c>
- or <c>normal</c> can run when there are processes
- running on priority <c>high</c>. On the runtime
+ or <c>normal</c> can run when processes
+ are running on priority <c>high</c>. On the runtime
system with SMP support, more processes can be running
- in parallel than processes on priority <c>high</c>, that is,
+ in parallel than processes on priority <c>high</c>. That is,
a <c>low</c> and a <c>high</c> priority process can
execute at the same time.</p>
- <p>When there are runnable processes on priority <c>max</c>,
+ <p>When runnable processes on priority <c>max</c> exist,
no processes on priority <c>low</c>, <c>normal</c>, or
<c>high</c> are selected for execution. As with priority
<c>high</c>, processes on lower priorities can
execute in parallel with processes on priority <c>max</c>.</p>
- <p>Scheduling is preemptive. Regardless of priority, a process
- is preempted when it has consumed more than a certain number
+ <p>Scheduling is pre-emptive. Regardless of priority, a process
+ is pre-empted when it has consumed more than a certain number
of reductions since the last time it was selected for
execution.</p>
<note>
@@ -4335,7 +4661,7 @@ os_prompt% </pre>
take this into account and handle such scenarios by
yourself.</p>
<p>Making calls from a <c>high</c> priority process into code
- that you have no control over can cause the <c>high</c>
+ that you has no control over can cause the <c>high</c>
priority process to wait for a process with lower
priority. That is, effectively decreasing the priority of the
<c>high</c> priority process during the call. Even if this
@@ -4349,19 +4675,19 @@ os_prompt% </pre>
<em>especially</em> priority <c>high</c>. A
process on priority <c>high</c> is only
to perform work for short periods. Busy looping for
- long periods in a <c>high</c> priority process does
- most likely cause problems, as important OTP servers
+ long periods in a <c>high</c> priority process causes
+ most likely problems, as important OTP servers
run on priority <c>normal</c>.</p>
<p>Returns the old value of the flag.</p>
</desc>
</func>
<func>
- <name name="process_flag" arity="2" clause_i="6"/>
- <fsummary>Sets process flag <c>save_calls</c> for the calling process.</fsummary>
+ <name name="process_flag" arity="2" clause_i="8"/>
+ <fsummary>Set process flag save_calls for the calling process.</fsummary>
<desc>
<p><c><anno>N</anno></c> must be an integer in the interval 0..10000.
- If <c><anno>N</anno></c> is greater than 0, call saving is made
+ If <c><anno>N</anno></c> &gt; 0, call saving is made
active for the
process. This means that information about the <c><anno>N</anno></c>
most recent global function calls, BIF calls, sends, and
@@ -4372,12 +4698,12 @@ os_prompt% </pre>
explicitly mentioned. Only a fixed amount of information
is saved, as follows:</p>
<list type="bulleted">
- <item>A tuple <c>{Module, Function, Arity}</c> for
- function calls</item>
- <item> The atoms <c>send</c>, <c>'receive'</c>, and
+ <item><p>A tuple <c>{Module, Function, Arity}</c> for
+ function calls</p></item>
+ <item><p>The atoms <c>send</c>, <c>'receive'</c>, and
<c>timeout</c> for sends and receives (<c>'receive'</c>
when a message is received and <c>timeout</c> when a
- receive times out)</item>
+ receive times out)</p></item>
</list>
<p>If <c>N</c> = 0,
call saving is disabled for the process, which is the
@@ -4388,8 +4714,8 @@ os_prompt% </pre>
</func>
<func>
- <name name="process_flag" arity="2" clause_i="7"/>
- <fsummary>Sets process flag <c>sensitive</c> for the calling process.</fsummary>
+ <name name="process_flag" arity="2" clause_i="9"/>
+ <fsummary>Set process flag sensitive for the calling process.</fsummary>
<desc>
<p>Sets or clears flag <c>sensitive</c> for the current process.
When a process has been marked as sensitive by calling
@@ -4399,13 +4725,13 @@ os_prompt% </pre>
<p>Features that are disabled include (but are not limited to)
the following:</p>
<list type="bulleted">
- <item>Tracing: Trace flags can still be set for the process,
+ <item><p>Tracing. Trace flags can still be set for the process,
but no trace messages of any kind are generated. (If flag
<c>sensitive</c> is turned off, trace messages are again
- generated if any trace flags are set.)</item>
- <item>Sequential tracing: The sequential trace token is
+ generated if any trace flags are set.)</p></item>
+ <item><p>Sequential tracing. The sequential trace token is
propagated as usual, but no sequential trace messages are
- generated.</item>
+ generated.</p></item>
</list>
<p><c>process_info/1,2</c> cannot be used to read out the
message queue or the process dictionary (both are returned
@@ -4415,19 +4741,19 @@ os_prompt% </pre>
are omitted.</p>
<p>If <c>{save_calls,N}</c> has been set for the process, no
function calls are saved to the call saving list.
- (The call saving list is not cleared. Furthermore, send, receive,
- and timeout events are still added to the list.)</p>
+ (The call saving list is not cleared. Also, send, receive,
+ and time-out events are still added to the list.)</p>
<p>Returns the old value of the flag.</p>
</desc>
</func>
<func>
<name name="process_flag" arity="3"/>
- <fsummary>Sets process flags for a process.</fsummary>
+ <fsummary>Set process flags for a process.</fsummary>
<desc>
<p>Sets certain flags for the process <c><anno>Pid</anno></c>,
in the same manner as
- <seealso marker="#process_flag/2">process_flag/2</seealso>.
+ <seealso marker="#process_flag/2"><c>process_flag/2</c></seealso>.
Returns the old value of the flag. The valid values for
<c><anno>Flag</anno></c> are only a subset of those allowed in
<c>process_flag/2</c>, namely <c>save_calls</c>.</p>
@@ -4442,44 +4768,46 @@ os_prompt% </pre>
<type name="process_info_result_item"/>
<type name="priority_level"/>
<type name="stack_item"/>
+ <type name="max_heap_size"/>
+ <type name="message_queue_data"/>
<desc>
<p>Returns a list containing <c><anno>InfoTuple</anno></c>s with
- miscellaneous information about the process identified by
- <c>Pid</c>, or <c>undefined</c> if the process is not alive.</p>
- <p>The order of the <c><anno>InfoTuple</anno></c>s is undefined and
- all <c><anno>InfoTuple</anno></c>s are not mandatory.
+ miscellaneous information about the process identified by
+ <c>Pid</c>, or <c>undefined</c> if the process is not alive.</p>
+ <p>The order of the <c><anno>InfoTuple</anno></c>s is undefined and
+ all <c><anno>InfoTuple</anno></c>s are not mandatory.
The <c><anno>InfoTuple</anno></c>s
- part of the result can be changed without prior notice.</p>
- <p>The <c><anno>InfoTuple</anno></c>s with the following items
- are part of the result:</p>
+ part of the result can be changed without prior notice.</p>
+ <p>The <c><anno>InfoTuple</anno></c>s with the following items
+ are part of the result:</p>
<list type="bulleted">
<item><c>current_function</c></item>
<item><c>initial_call</c></item>
<item><c>status</c></item>
- <item><c>message_queue_len</c></item>
+ <item><c>message_queue_len</c></item>
<item><c>messages</c></item>
<item><c>links</c></item>
- <item><c>dictionary</c></item>
+ <item><c>dictionary</c></item>
<item><c>trap_exit</c></item>
<item><c>error_handler</c></item>
- <item><c>priority</c></item>
+ <item><c>priority</c></item>
<item><c>group_leader</c></item>
<item><c>total_heap_size</c></item>
- <item><c>heap_size</c></item>
+ <item><c>heap_size</c></item>
<item><c>stack_size</c></item>
<item><c>reductions</c></item>
- <item><c>garbage_collection</c></item>
+ <item><c>garbage_collection</c></item>
</list>
<p>If the process identified by <c><anno>Pid</anno></c> has a
registered name,
- also an <c><anno>InfoTuple</anno></c> with item <c>registered_name</c>
- appears.</p>
- <p>For information about specific <c><anno>InfoTuple</anno></c>s, see
- <seealso marker="#process_info/2">process_info/2</seealso>.</p>
+ also an <c><anno>InfoTuple</anno></c> with item <c>registered_name</c>
+ is included.</p>
+ <p>For information about specific <c><anno>InfoTuple</anno></c>s, see
+ <seealso marker="#process_info/2"><c>process_info/2</c></seealso>.</p>
<warning>
<p>This BIF is intended for <em>debugging only</em>. For
- all other purposes, use
- <seealso marker="#process_info/2">process_info/2</seealso>.</p>
+ all other purposes, use <seealso marker="#process_info/2">
+ <c>process_info/2</c></seealso>.</p>
</warning>
<p>Failure: <c>badarg</c> if <c><anno>Pid</anno></c> is not a
local process.</p>
@@ -4494,36 +4822,39 @@ os_prompt% </pre>
<type name="process_info_result_item"/>
<type name="stack_item"/>
<type name="priority_level"/>
+ <type name="max_heap_size"/>
+ <type name="message_queue_data"/>
<desc>
<p>Returns information about the process identified by
- <c><anno>Pid</anno></c>, as specified by
- <c><anno>Item</anno></c> or <c><anno>ItemList</anno></c>.
- Returns <c>undefined</c> if the process is not alive.</p>
- <p>If the process is alive and a single <c><anno>Item</anno></c>
- is given, the returned value is the corresponding
- <c><anno>InfoTuple</anno></c>, unless <c>Item =:= registered_name</c>
- and the process has no registered name. In this case,
- <c>[]</c> is returned. This strange behavior is because of
- historical reasons, and is kept for backward compatibility.</p>
- <p>If <c><anno>ItemList</anno></c> is given, the result is
- <c><anno>InfoTupleList</anno></c>.
- The <c><anno>InfoTuple</anno></c>s in
- <c><anno>InfoTupleList</anno></c> appear with the corresponding
- <c><anno>Item</anno></c>s in the same order as the
- <c><anno>Item</anno></c>s appeared
- in <c><anno>ItemList</anno></c>. Valid <c><anno>Item</anno></c>s can
- appear multiple times in <c><anno>ItemList</anno></c>.</p>
- <note><p>If <c>registered_name</c> is part of <c><anno>ItemList</anno></c>
- and the process has no name registered a
- <c>{registered_name, []}</c>, <c><anno>InfoTuple</anno></c>
- <em>will</em> appear in the resulting
- <c><anno>InfoTupleList</anno></c>. This
- behavior is different when a single
- <c>Item =:= registered_name</c> is given, and when
- <c>process_info/1</c> is used.</p>
- </note>
- <p>The following <c><anno>InfoTuple</anno></c>s with corresponding
- <c><anno>Item</anno></c>s are valid:</p>
+ <c><anno>Pid</anno></c>, as specified by
+ <c><anno>Item</anno></c> or <c><anno>ItemList</anno></c>.
+ Returns <c>undefined</c> if the process is not alive.</p>
+ <p>If the process is alive and a single <c><anno>Item</anno></c>
+ is specified, the returned value is the corresponding
+ <c><anno>InfoTuple</anno></c>, unless <c>Item =:= registered_name</c>
+ and the process has no registered name. In this case,
+ <c>[]</c> is returned. This strange behavior is because of
+ historical reasons, and is kept for backward compatibility.</p>
+ <p>If <c><anno>ItemList</anno></c> is specified, the result is
+ <c><anno>InfoTupleList</anno></c>.
+ The <c><anno>InfoTuple</anno></c>s in
+ <c><anno>InfoTupleList</anno></c> are included with the corresponding
+ <c><anno>Item</anno></c>s in the same order as the
+ <c><anno>Item</anno></c>s were included
+ in <c><anno>ItemList</anno></c>. Valid <c><anno>Item</anno></c>s can
+ be included multiple times in <c><anno>ItemList</anno></c>.</p>
+ <note>
+ <p>If <c>registered_name</c> is part of <c><anno>ItemList</anno></c>
+ and the process has no name registered, a
+ <c>{registered_name, []}</c>, <c><anno>InfoTuple</anno></c>
+ <em>will</em> be included in the resulting
+ <c><anno>InfoTupleList</anno></c>. This
+ behavior is different when a single
+ <c>Item =:= registered_name</c> is specified, and when
+ <c>process_info/1</c> is used.</p>
+ </note>
+ <p>Valid <c><anno>InfoTuple</anno></c>s with corresponding
+ <c><anno>Item</anno></c>s:</p>
<taglist>
<tag><c>{backtrace, <anno>Bin</anno>}</c></tag>
<item>
@@ -4536,15 +4867,17 @@ os_prompt% </pre>
<tag><c>{binary, <anno>BinInfo</anno>}</c></tag>
<item>
<p><c><anno>BinInfo</anno></c> is a list containing miscellaneous
- information about binaries currently being referred to by this
+ information about binaries currently referred to by this
process. This <c><anno>InfoTuple</anno></c> can be changed or
- removed without prior notice.</p>
+ removed without prior notice. In the current implementation
+ <c><anno>BinInfo</anno></c> is a list of tuples. The tuples
+ contain; <c>BinaryId</c>, <c>BinarySize</c>, <c>BinaryRefcCount</c>.</p>
</item>
<tag><c>{catchlevel, <anno>CatchLevel</anno>}</c></tag>
<item>
<p><c><anno>CatchLevel</anno></c> is the number of currently active
- catches in this process. This <c><anno>InfoTuple</anno></c> can be
- changed or removed without prior notice.</p>
+ catches in this process. This <c><anno>InfoTuple</anno></c> can be
+ changed or removed without prior notice.</p>
</item>
<tag><c>{current_function, {<anno>Module</anno>,
<anno>Function</anno>, Arity}}</c></tag>
@@ -4560,14 +4893,17 @@ os_prompt% </pre>
<p><c><anno>Module</anno></c>, <c><anno>Function</anno></c>,
<c><anno>Arity</anno></c> is
the current function call of the process.
- <c><anno>Location</anno></c> is a list of two-tuples describing the
- location in the source code.</p>
+ <c><anno>Location</anno></c> is a list of two-tuples describing
+ the location in the source code.</p>
</item>
<tag><c>{current_stacktrace, <anno>Stack</anno>}</c></tag>
<item>
<p>Returns the current call stack back-trace (<em>stacktrace</em>)
of the process. The stack has the same format as returned by
- <seealso marker="#get_stacktrace/0">erlang:get_stacktrace/0</seealso>.</p>
+ <seealso marker="#get_stacktrace/0">
+ <c>erlang:get_stacktrace/0</c></seealso>. The depth of the
+ stacktrace is truncated according to the <c>backtrace_depth</c>
+ system flag setting.</p>
</item>
<tag><c>{dictionary, <anno>Dictionary</anno>}</c></tag>
<item>
@@ -4581,20 +4917,32 @@ os_prompt% </pre>
<tag><c>{garbage_collection, <anno>GCInfo</anno>}</c></tag>
<item>
<p><c><anno>GCInfo</anno></c> is a list containing miscellaneous
- information about garbage collection for this process.
- The content of <c><anno>GCInfo</anno></c> can be changed without
- prior notice.</p>
+ information about garbage collection for this process.
+ The content of <c><anno>GCInfo</anno></c> can be changed without
+ prior notice.</p>
+ </item>
+ <tag>
+ <marker id="process_info_garbage_collection_info"/>
+ <c>{garbage_collection_info, <anno>GCInfo</anno>}</c>
+ </tag>
+ <item>
+ <p><c><anno>GCInfo</anno></c> is a list containing miscellaneous
+ detailed information about garbage collection for this process.
+ The content of <c><anno>GCInfo</anno></c> can be changed without
+ prior notice. For details about the meaning of each item, see
+ <seealso marker="#gc_minor_start"><c>gc_minor_start</c></seealso>
+ in <seealso marker="#trace/3"><c>erlang:trace/3</c></seealso>.</p>
</item>
<tag><c>{group_leader, <anno>GroupLeader</anno>}</c></tag>
<item>
- <p><c><anno>GroupLeader</anno></c> is group leader for the I/O of
- the process.</p>
+ <p><c><anno>GroupLeader</anno></c> is the group leader for the I/O
+ of the process.</p>
</item>
<tag><c>{heap_size, <anno>Size</anno>}</c></tag>
<item>
- <p><c><anno>Size</anno></c> is the size in words of the youngest heap
- generation of the process. This generation includes
- the process stack. This information is highly
+ <p><c><anno>Size</anno></c> is the size in words of the youngest
+ heap generation of the process. This generation includes
+ the process stack. This information is highly
implementation-dependent, and can change if the
implementation changes.</p>
</item>
@@ -4608,29 +4956,29 @@ os_prompt% </pre>
</item>
<tag><c>{links, <anno>PidsAndPorts</anno>}</c></tag>
<item>
- <p><c><anno>PidsAndPorts</anno></c> is a list of process identifiers
- and port identifiers, with processes or ports to which the process
- has a link.</p>
+ <p><c><anno>PidsAndPorts</anno></c> is a list of process identifiers
+ and port identifiers, with processes or ports to which the process
+ has a link.</p>
</item>
<tag><c>{last_calls, false|Calls}</c></tag>
<item>
<p>The value is <c>false</c> if call saving is not active
- for the process (see
- <seealso marker="#process_flag/3">process_flag/3</seealso>).
+ for the process (see <seealso marker="#process_flag/3">
+ <c>process_flag/3</c></seealso>).
If call saving is active, a list is returned, in which
the last element is the most recent called.</p>
</item>
<tag><c>{memory, <anno>Size</anno>}</c></tag>
<item>
- <p><c><anno>Size</anno></c> is the size in bytes of the process. This
- includes call stack, heap, and internal structures.</p>
+ <p><c><anno>Size</anno></c> is the size in bytes of the process.
+ This includes call stack, heap, and internal structures.</p>
</item>
<tag><c>{message_queue_len, <anno>MessageQueueLen</anno>}</c></tag>
<item>
<p><c><anno>MessageQueueLen</anno></c> is the number of messages
- currently in the message queue of the process. This is
- the length of the list <c><anno>MessageQueue</anno></c> returned as
- the information item <c>messages</c> (see the following).</p>
+ currently in the message queue of the process. This is the
+ length of the list <c><anno>MessageQueue</anno></c> returned as
+ the information item <c>messages</c> (see below).</p>
</item>
<tag><c>{messages, <anno>MessageQueue</anno>}</c></tag>
<item>
@@ -4657,34 +5005,53 @@ os_prompt% </pre>
<p>A list of monitors (started by <c>monitor/2</c>)
that are active for the process. For a local process
monitor or a remote process monitor by a process
- identifier, the list item is <c>{process, <anno>Pid</anno>}</c>.
- For a remote process
- monitor by name, the list item is
- <c>{process, {<anno>RegName</anno>, <anno>Node</anno>}}</c>.</p>
+ identifier, the list consists of:</p>
+ <taglist>
+ <tag><c>{process, <anno>Pid</anno>}</c></tag>
+ <item>Process is monitored by pid.</item>
+ <tag><c>{process, {<anno>RegName</anno>, <anno>Node</anno>}}</c></tag>
+ <item>Local or remote process is monitored by name.</item>
+ <tag><c>{port, PortId}</c></tag>
+ <item>Local port is monitored by port id.</item>
+ <tag><c>{port, {<anno>RegName</anno>, <anno>Node</anno>}}</c></tag>
+ <item>Local port is monitored by name. Please note, that
+ remote port monitors are not supported, so <c>Node</c> will
+ always be the local node name.</item>
+ </taglist>
+ </item>
+ <tag><c>{message_queue_data, <anno>MQD</anno>}</c></tag>
+ <item>
+ <p>Returns the current state of process flag
+ <c>message_queue_data</c>. <c><anno>MQD</anno></c> is either
+ <c>off_heap</c> or <c>on_heap</c>. For more
+ information, see the documentation of
+ <seealso marker="#process_flag_message_queue_data">
+ <c>process_flag(message_queue_data, MQD)</c></seealso>.</p>
</item>
<tag><c>{priority, <anno>Level</anno>}</c></tag>
<item>
<p><c><anno>Level</anno></c> is the current priority level for
- the process. For more information on priorities, see
- <seealso marker="#process_flag_priority">process_flag(priority,
- Level)</seealso>.</p>
+ the process. For more information on priorities, see
+ <seealso marker="#process_flag_priority">
+ <c>process_flag(priority, Level)</c></seealso>.</p>
</item>
<tag><c>{reductions, <anno>Number</anno>}</c></tag>
<item>
- <p><c><anno>Number</anno></c> is the number of reductions executed by
- the process.</p>
+ <p><c><anno>Number</anno></c> is the number of reductions executed
+ by the process.</p>
</item>
<tag><c>{registered_name, <anno>Atom</anno>}</c></tag>
<item>
- <p><c><anno>Atom</anno></c> is the registered name of the process. If
- the process has no registered name, this tuple is not
+ <p><c><anno>Atom</anno></c> is the registered process name.
+ If the process has no registered name, this tuple is not
present in the list.</p>
</item>
- <tag><c>{sequential_trace_token, [] | <anno>SequentialTraceToken</anno>}</c></tag>
+ <tag><c>{sequential_trace_token, [] |
+ <anno>SequentialTraceToken</anno>}</c></tag>
<item>
<p><c><anno>SequentialTraceToken</anno></c> is the sequential trace
- token for the process. This <c><anno>InfoTuple</anno></c> can be
- changed or removed without prior notice.</p>
+ token for the process. This <c><anno>InfoTuple</anno></c> can be
+ changed or removed without prior notice.</p>
</item>
<tag><c>{stack_size, <anno>Size</anno>}</c></tag>
<item>
@@ -4693,9 +5060,9 @@ os_prompt% </pre>
</item>
<tag><c>{status, <anno>Status</anno>}</c></tag>
<item>
- <p><c><anno>Status</anno></c> is the status of the process and is one
- of the following:</p>
- <list type="bulleted">
+ <p><c><anno>Status</anno></c> is the status of the process and is
+ one of the following:</p>
+ <list type="bulleted">
<item><c>exiting</c></item>
<item><c>garbage_collecting</c></item>
<item><c>waiting</c> (for a message)</item>
@@ -4703,63 +5070,66 @@ os_prompt% </pre>
<item><c>runnable</c> (ready to run, but another process is
running)</item>
<item><c>suspended</c> (suspended on a "busy" port
- or by the BIF <c>erlang:suspend_process/[1,2]</c>)</item>
+ or by the BIF <c>erlang:suspend_process/1,2</c>)</item>
</list>
</item>
<tag><c>{suspending, <anno>SuspendeeList</anno>}</c></tag>
<item>
<p><c><anno>SuspendeeList</anno></c> is a list of
- <c>{<anno>Suspendee</anno>, <anno>ActiveSuspendCount</anno>,
- <anno>OutstandingSuspendCount</anno>}</c> tuples.
- <c><anno>Suspendee</anno></c> is the process identifier of a
- process that has been, or is to be,
- suspended by the process identified by <c><anno>Pid</anno></c>
- through one of the following BIFs:</p>
+ <c>{<anno>Suspendee</anno>, <anno>ActiveSuspendCount</anno>,
+ <anno>OutstandingSuspendCount</anno>}</c> tuples.
+ <c><anno>Suspendee</anno></c> is the process identifier of a
+ process that has been, or is to be,
+ suspended by the process identified by <c><anno>Pid</anno></c>
+ through the BIF <seealso marker="#suspend_process/2">
+ <c>erlang:suspend_process/2</c></seealso> or
+ <seealso marker="#suspend_process/1">
+ <c>erlang:suspend_process/1</c></seealso>.</p>
+ <p><c><anno>ActiveSuspendCount</anno></c> is the number of
+ times <c><anno>Suspendee</anno></c> has been suspended by
+ <c><anno>Pid</anno></c>.
+ <c><anno>OutstandingSuspendCount</anno></c> is the number of not
+ yet completed suspend requests sent by <c><anno>Pid</anno></c>,
+ that is:</p>
<list type="bulleted">
<item>
- <seealso marker="#suspend_process/2">erlang:suspend_process/2</seealso>
- </item>
- <item>
- <seealso marker="#suspend_process/1">erlang:suspend_process/1</seealso>
+ <p>If <c><anno>ActiveSuspendCount</anno> =/= 0</c>,
+ <c><anno>Suspendee</anno></c> is
+ currently in the suspended state.</p>
</item>
- </list>
- <p><c><anno>ActiveSuspendCount</anno></c> is the number of
- times <c><anno>Suspendee</anno></c> has been suspended by
- <c><anno>Pid</anno></c>.
- <c><anno>OutstandingSuspendCount</anno></c> is the number of not yet
- completed suspend requests sent by <c><anno>Pid</anno></c>, that is:</p>
- <list type="bulleted">
- <item>If <c><anno>ActiveSuspendCount</anno> =/= 0</c>,
- <c><anno>Suspendee</anno></c> is
- currently in the suspended state.
- </item>
- <item>If <c><anno>OutstandingSuspendCount</anno> =/= 0</c>, option
- <c>asynchronous</c> of <c>erlang:suspend_process/2</c>
- has been used and the suspendee has not yet been
- suspended by <c><anno>Pid</anno></c>.
+ <item>
+ <p>If <c><anno>OutstandingSuspendCount</anno> =/= 0</c>,
+ option <c>asynchronous</c> of <c>erlang:suspend_process/2</c>
+ has been used and the suspendee has not yet been
+ suspended by <c><anno>Pid</anno></c>.</p>
</item>
</list>
- <p>Notice that <c><anno>ActiveSuspendCount</anno></c> and
- <c><anno>OutstandingSuspendCount</anno></c> are not the
- total suspend count on <c><anno>Suspendee</anno></c>,
- only the parts contributed by <c><anno>Pid</anno></c>.</p>
+ <p>Notice that <c><anno>ActiveSuspendCount</anno></c> and
+ <c><anno>OutstandingSuspendCount</anno></c> are not the
+ total suspend count on <c><anno>Suspendee</anno></c>,
+ only the parts contributed by <c><anno>Pid</anno></c>.</p>
</item>
- <tag><c>{total_heap_size, <anno>Size</anno>}</c></tag>
+ <tag>
+ <marker id="process_info_total_heap_size"/>
+ <c>{total_heap_size, <anno>Size</anno>}</c>
+ </tag>
<item>
<p><c><anno>Size</anno></c> is the total size, in words, of all heap
- fragments of the process. This includes the process stack.</p>
+ fragments of the process. This includes the process stack and
+ any unreceived messages that are considered to be part of the
+ heap.</p>
</item>
<tag><c>{trace, <anno>InternalTraceFlags</anno>}</c></tag>
<item>
<p><c><anno>InternalTraceFlags</anno></c> is an integer
- representing the internal trace flag for this process.
- This <c><anno>InfoTuple</anno></c>
- can be changed or removed without prior notice.</p>
+ representing the internal trace flag for this process.
+ This <c><anno>InfoTuple</anno></c>
+ can be changed or removed without prior notice.</p>
</item>
<tag><c>{trap_exit, <anno>Boolean</anno>}</c></tag>
<item>
<p><c><anno>Boolean</anno></c> is <c>true</c> if the process
- is trapping exits, otherwise <c>false</c>.</p>
+ is trapping exits, otherwise <c>false</c>.</p>
</item>
</taglist>
<p>Notice that not all implementations support all
@@ -4767,9 +5137,9 @@ os_prompt% </pre>
<p>Failures:</p>
<taglist>
<tag><c>badarg</c></tag>
- <item>If <c><anno>Pid</anno></c> is not a local process.</item>
+ <item>If <c><anno>Pid</anno></c> is not a local process.</item>
<tag><c>badarg</c></tag>
- <item>If <c><anno>Item</anno></c> is an invalid item.</item>
+ <item>If <c><anno>Item</anno></c> is an invalid item.</item>
</taglist>
</desc>
</func>
@@ -4779,11 +5149,11 @@ os_prompt% </pre>
<fsummary>All processes.</fsummary>
<desc>
<p>Returns a list of process identifiers corresponding to
- all the processes currently existing on the local node.</p>
- <p>Notice that an exiting process exists, but is not alive.
- That is, <c>is_process_alive/1</c> returns <c>false</c>
- for an exiting process, but its process identifier is part
- of the result returned from <c>processes/0</c>.</p>
+ all the processes currently existing on the local node.</p>
+ <p>Notice that an exiting process exists, but is not alive.
+ That is, <c>is_process_alive/1</c> returns <c>false</c>
+ for an exiting process, but its process identifier is part
+ of the result returned from <c>processes/0</c>.</p>
<p>Example:</p>
<pre>
> <input>processes().</input>
@@ -4793,17 +5163,24 @@ os_prompt% </pre>
<func>
<name name="purge_module" arity="1"/>
- <fsummary>Removes old code for a module.</fsummary>
+ <fsummary>Remove old code for a module.</fsummary>
<desc>
<p>Removes old code for <c><anno>Module</anno></c>.
Before this BIF is used,
- <c>erlang:check_process_code/2</c> is to be called to check
+ <seealso marker="#check_process_code/2">
+ <c>check_process_code/2</c></seealso>is to be called to check
that no processes execute old code in the module.</p>
<warning>
<p>This BIF is intended for the code server (see
- <seealso marker="kernel:code">code(3)</seealso>)
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>)
and is not to be used elsewhere.</p>
</warning>
+ <note>
+ <p>As from ERTS 8.0 (Erlang/OTP 19), any lingering processes
+ that still execute the old code is killed by this function.
+ In earlier versions, such incorrect use could cause much
+ more fatal failures, like emulator crash.</p>
+ </note>
<p>Failure: <c>badarg</c> if there is no old code for
<c><anno>Module</anno></c>.</p>
</desc>
@@ -4811,14 +5188,13 @@ os_prompt% </pre>
<func>
<name name="put" arity="2"/>
- <fsummary>Adds a new value to the process dictionary.</fsummary>
+ <fsummary>Add a new value to the process dictionary.</fsummary>
<desc>
<p>Adds a new <c><anno>Key</anno></c> to the process dictionary,
associated with the value <c><anno>Val</anno></c>, and returns
<c>undefined</c>. If <c><anno>Key</anno></c> exists, the old
value is deleted and replaced by <c><anno>Val</anno></c>, and
- the function returns the old value.</p>
- <p>Example:</p>
+ the function returns the old value. Example:</p>
<pre>
> <input>X = put(name, walrus), Y = put(name, carpenter),</input>
<input>Z = get(name),</input>
@@ -4834,21 +5210,18 @@ os_prompt% </pre>
<func>
<name name="raise" arity="3"/>
- <fsummary>Stops execution with an exception of given class, reason, and call stack backtrace.</fsummary>
+ <fsummary>Stop execution with an exception of specified class, reason,
+ and call stack backtrace.</fsummary>
<type name="raise_stacktrace"/>
<desc>
<p>Stops the execution of the calling process with an
- exception of given class, reason, and call stack backtrace
+ exception of the specified class, reason, and call stack backtrace
(<em>stacktrace</em>).</p>
- <warning>
- <p>This BIF is intended for debugging. Avoid to use it in applications,
- unless you really know what you are doing.</p>
- </warning>
<p><c><anno>Class</anno></c> is <c>error</c>, <c>exit</c>, or
<c>throw</c>. So, if it were not for the stacktrace,
<c>erlang:raise(<anno>Class</anno>, <anno>Reason</anno>,
- <anno>Stacktrace</anno>)</c> is
- equivalent to <c>erlang:<anno>Class</anno>(<anno>Reason</anno>)</c>.</p>
+ <anno>Stacktrace</anno>)</c> is equivalent to
+ <c>erlang:<anno>Class</anno>(<anno>Reason</anno>)</c>.</p>
<p><c><anno>Reason</anno></c> is any term.
<c><anno>Stacktrace</anno></c> is a list as
returned from <c>get_stacktrace()</c>, that is, a list of
@@ -4858,12 +5231,12 @@ os_prompt% </pre>
argument list. The stacktrace can also contain <c>{Fun,
Args, Location}</c> tuples, where <c>Fun</c> is a local
fun and <c>Args</c> is an argument list.</p>
- <p>Element <c>Location</c> at the end is optional.
- Omitting it is equivalent to specifying an empty list.</p>
+ <p>Element <c>Location</c> at the end is optional.
+ Omitting it is equivalent to specifying an empty list.</p>
<p>The stacktrace is used as the exception stacktrace for the
calling process; it is truncated to the current
maximum stacktrace depth.</p>
- <p>Since evaluating this function causes the process to
+ <p>As evaluating this function causes the process to
terminate, it has no return value unless the arguments are
invalid, in which case the function <em>returns the error
reason</em> <c>badarg</c>. If you want to be
@@ -4875,78 +5248,68 @@ os_prompt% </pre>
</func>
<func>
- <name name="read_timer" arity="2"/>
- <fsummary>Reads the state of a timer.</fsummary>
+ <name name="read_timer" arity="1"/>
+ <fsummary>Read the state of a timer.</fsummary>
<desc>
- <p>
- Read the state of a timer that has been created by either
- <seealso marker="#start_timer/4"><c>erlang:start_timer()</c></seealso>,
- or <seealso marker="#send_after/4"><c>erlang:send_after()</c></seealso>.
- <c><anno>TimerRef</anno></c> identifies the timer, and
- was returned by the BIF that created the timer.
- </p>
- <p>Available <c><anno>Option</anno>s</c>:</p>
+ <p>Reads the state of a timer. The same as calling
+ <seealso marker="#read_timer/2"><c>erlang:read_timer(TimerRef,
+ [])</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="read_timer" arity="2"/>
+ <fsummary>Read the state of a timer.</fsummary>
+ <desc>
+ <p>Reads the state of a timer that has been created by either
+ <seealso marker="#start_timer/4"><c>erlang:start_timer</c></seealso>
+ or <seealso marker="#send_after/4"><c>erlang:send_after</c></seealso>.
+ <c><anno>TimerRef</anno></c> identifies the timer, and
+ was returned by the BIF that created the timer.</p>
+ <p><c><anno>Option</anno>s</c>:</p>
<taglist>
- <tag><c>{async, Async}</c></tag>
- <item>
- <p>
- Asynchronous request for state information. <c>Async</c>
- defaults to <c>false</c> which will cause the operation
- to be performed synchronously. In this case, the <c>Result</c>
- is returned by <c>erlang:read_timer()</c>. When
- <c>Async</c> is <c>true</c>, <c>erlang:read_timer()</c>
- sends an asynchronous request for the state information
- to the timer service that manages the timer, and then returns
- <c>ok</c>. A message on the format <c>{read_timer,
- <anno>TimerRef</anno>, <anno>Result</anno>}</c> is
- sent to the caller of <c>erlang:read_timer()</c> when the
- operation has been processed.
- </p>
- </item>
+ <tag><c>{async, Async}</c></tag>
+ <item>
+ <p>Asynchronous request for state information. <c>Async</c>
+ defaults to <c>false</c>, which causes the operation
+ to be performed synchronously. In this case, the <c>Result</c>
+ is returned by <c>erlang:read_timer</c>. When
+ <c>Async</c> is <c>true</c>, <c>erlang:read_timer</c>
+ sends an asynchronous request for the state information
+ to the timer service that manages the timer, and then returns
+ <c>ok</c>. A message on the format <c>{read_timer,
+ <anno>TimerRef</anno>, <anno>Result</anno>}</c> is
+ sent to the caller of <c>erlang:read_timer</c> when the
+ operation has been processed.</p>
+ </item>
</taglist>
- <p>
- More <c><anno>Option</anno></c>s may be added in the future.
- </p>
- <p>
- If <c><anno>Result</anno></c> is an integer, it represents the
- time in milli-seconds left until the timer expires.</p>
- <p>
- If <c><anno>Result</anno></c> is <c>false</c>, a
- timer corresponding to <c><anno>TimerRef</anno></c> could not
- be found. This can be because the timer had expired,
- it had been canceled, or because <c><anno>TimerRef</anno></c>
- never has corresponded to a timer. Even if the timer has expired,
- it does not tell you whether or not the timeout message has
- arrived at its destination yet.
- </p>
- <note>
- <p>
- The timer service that manages the timer may be co-located
- with another scheduler than the scheduler that the calling
- process is executing on. If this is the case, communication
- with the timer service takes much longer time than if it
- is located locally. If the calling process is in critical
- path, and can do other things while waiting for the result
- of this operation, you want to use option <c>{async, true}</c>.
- If using option <c>{async, false}</c>, the calling
- process will be blocked until the operation has been
- performed.
- </p>
- </note>
+ <p>More <c><anno>Option</anno></c>s can be added in the future.</p>
+ <p>If <c><anno>Result</anno></c> is an integer, it represents the
+ time in milliseconds left until the timer expires.</p>
+ <p>If <c><anno>Result</anno></c> is <c>false</c>, a
+ timer corresponding to <c><anno>TimerRef</anno></c> could not
+ be found. This because the timer had expired,
+ or been canceled, or because <c><anno>TimerRef</anno></c>
+ never has corresponded to a timer. Even if the timer has expired,
+ it does not tell you whether or not the time-out message has
+ arrived at its destination yet.</p>
+ <note>
+ <p>The timer service that manages the timer can be co-located
+ with another scheduler than the scheduler that the calling
+ process is executing on. If so, communication
+ with the timer service takes much longer time than if it
+ is located locally. If the calling process is in a critical
+ path, and can do other things while waiting for the result
+ of this operation, you want to use option <c>{async, true}</c>.
+ If using option <c>{async, false}</c>, the calling
+ process is blocked until the operation has been performed.</p>
+ </note>
<p>See also
<seealso marker="#send_after/4"><c>erlang:send_after/4</c></seealso>,
- <seealso marker="#start_timer/4"><c>erlang:start_timer/4</c></seealso>,
- and
- <seealso marker="#cancel_timer/2"><c>erlang:cancel_timer/2</c></seealso>.</p>
- </desc>
- </func>
- <func>
- <name name="read_timer" arity="1"/>
- <fsummary>Reads the state of a timer.</fsummary>
- <desc>
- <p>Read the state of a timer. The same as calling
- <seealso marker="#read_timer/2"><c>erlang:read_timer(TimerRef,
- [])</c></seealso>.</p>
+ <seealso marker="#start_timer/4">
+ <c>erlang:start_timer/4</c></seealso>, and
+ <seealso marker="#cancel_timer/2">
+ <c>erlang:cancel_timer/2</c></seealso>.</p>
</desc>
</func>
@@ -4958,21 +5321,20 @@ os_prompt% </pre>
representation of <c><anno>Ref</anno></c>.</p>
<warning>
<p>This BIF is intended for debugging and is not to be used
- in application programs.</p>
+ in application programs.</p>
</warning>
</desc>
</func>
<func>
<name name="register" arity="2"/>
- <fsummary>Registers a name for a pid (or port).</fsummary>
+ <fsummary>Register a name for a pid (or port).</fsummary>
<desc>
<p>Associates the name <c><anno>RegName</anno></c> with a process
identifier (pid) or a port identifier.
<c><anno>RegName</anno></c>, which must be an atom, can be used
instead of the pid or port identifier in send operator
- (<c><anno>RegName</anno> ! Message</c>).</p>
- <p>Example:</p>
+ (<c><anno>RegName</anno> ! Message</c>). Example:</p>
<pre>
> <input>register(db, Pid).</input>
true</pre>
@@ -4998,7 +5360,7 @@ true</pre>
<fsummary>All registered names.</fsummary>
<desc>
<p>Returns a list of names that have been registered using
- <seealso marker="#register/2">register/2</seealso>, for
+ <seealso marker="#register/2"><c>register/2</c></seealso>, for
example:</p>
<pre>
> <input>registered().</input>
@@ -5008,20 +5370,21 @@ true</pre>
<func>
<name name="resume_process" arity="1"/>
- <fsummary>Resumes a suspended process.</fsummary>
+ <fsummary>Resume a suspended process.</fsummary>
<desc>
<p>Decreases the suspend count on the process identified by
- <c><anno>Suspendee</anno></c>. <c><anno>Suspendee</anno></c>
- is previously to have been suspended through
- <seealso marker="#suspend_process/2">erlang:suspend_process/2</seealso>
- or
- <seealso marker="#suspend_process/1">erlang:suspend_process/1</seealso>
- by the process calling
- <c>erlang:resume_process(<anno>Suspendee</anno>)</c>. When the
- suspend count on <c><anno>Suspendee</anno></c> reaches zero,
- <c><anno>Suspendee</anno></c> is resumed, that is, its state
- is changed from suspended into the state it had before it was
- suspended.</p>
+ <c><anno>Suspendee</anno></c>. <c><anno>Suspendee</anno></c>
+ is previously to have been suspended through
+ <seealso marker="#suspend_process/2">
+ <c>erlang:suspend_process/2</c></seealso> or
+ <seealso marker="#suspend_process/1">
+ <c>erlang:suspend_process/1</c></seealso>
+ by the process calling
+ <c>erlang:resume_process(<anno>Suspendee</anno>)</c>. When the
+ suspend count on <c><anno>Suspendee</anno></c> reaches zero,
+ <c><anno>Suspendee</anno></c> is resumed, that is, its state
+ is changed from suspended into the state it had before it was
+ suspended.</p>
<warning>
<p>This BIF is intended for debugging only.</p>
</warning>
@@ -5029,29 +5392,29 @@ true</pre>
<taglist>
<tag><c>badarg</c></tag>
<item>
- If <c><anno>Suspendee</anno></c> is not a process identifier.
- </item>
+ If <c><anno>Suspendee</anno></c> is not a process identifier.
+ </item>
<tag><c>badarg</c></tag>
<item>
- If the process calling <c>erlang:resume_process/1</c> had
- not previously increased the suspend count on the process
- identified by <c><anno>Suspendee</anno></c>.
- </item>
+ If the process calling <c>erlang:resume_process/1</c> had
+ not previously increased the suspend count on the process
+ identified by <c><anno>Suspendee</anno></c>.
+ </item>
<tag><c>badarg</c></tag>
<item>
- If the process identified by <c><anno>Suspendee</anno></c>
- is not alive.
- </item>
+ If the process identified by <c><anno>Suspendee</anno></c>
+ is not alive.
+ </item>
</taglist>
</desc>
</func>
<func>
<name name="round" arity="1"/>
- <fsummary>Returns an integer by rounding a number.</fsummary>
+ <fsummary>Return an integer by rounding a number.</fsummary>
<desc>
<p>Returns an integer by rounding <c><anno>Number</anno></c>,
- for example:</p>
+ for example:</p>
<pre>
<input>round(5.5).</input>
6</pre>
@@ -5061,7 +5424,7 @@ true</pre>
<func>
<name name="self" arity="0"/>
- <fsummary>Returns pid of the calling process.</fsummary>
+ <fsummary>Return pid of the calling process.</fsummary>
<desc>
<p>Returns the process identifier of the calling process, for
example:</p>
@@ -5074,7 +5437,7 @@ true</pre>
<func>
<name name="send" arity="2"/>
- <fsummary>Sends a message.</fsummary>
+ <fsummary>Send a message.</fsummary>
<type name="dst"/>
<desc>
<p>Sends a message and returns <c><anno>Msg</anno></c>. This
@@ -5088,27 +5451,27 @@ true</pre>
<func>
<name name="send" arity="3"/>
- <fsummary>Sends a message conditionally.</fsummary>
+ <fsummary>Send a message conditionally.</fsummary>
<type name="dst"/>
<desc>
<p>Either sends a message and returns <c>ok</c>, or does not send
- the message but returns something else (see the following).
+ the message but returns something else (see below).
Otherwise the same as
- <seealso marker="#send/2">erlang:send/2</seealso>.
+ <seealso marker="#send/2"><c>erlang:send/2</c></seealso>.
For more detailed explanation and warnings, see
- <seealso marker="#send_nosuspend/2">erlang:send_nosuspend/2,3</seealso>.</p>
- <p>The options are as follows:</p>
+ <seealso marker="#send_nosuspend/2">
+ <c>erlang:send_nosuspend/2,3</c></seealso>.</p>
+ <p>Options:</p>
<taglist>
<tag><c>nosuspend</c></tag>
- <item>
- <p>If the sender would have to be suspended to do the send,
- <c>nosuspend</c> is returned instead.</p>
+ <item>If the sender would have to be suspended to do the send,
+ <c>nosuspend</c> is returned instead.
</item>
<tag><c>noconnect</c></tag>
<item>
- <p>If the destination node would have to be auto-connected
- to do the send, <c>noconnect</c> is returned
- instead.</p>
+ If the destination node would have to be auto-connected
+ to do the send, <c>noconnect</c> is returned
+ instead.
</item>
</taglist>
<warning>
@@ -5119,36 +5482,37 @@ true</pre>
</func>
<func>
- <name name="send_after" arity="4"/>
- <fsummary>Start a timer</fsummary>
+ <name name="send_after" arity="3"/>
+ <fsummary>Start a timer.</fsummary>
<desc>
- <p>
- Starts a timer. When the timer expires, the message
- <c><anno>Msg</anno></c> is sent to the process
- identified by <c><anno>Dest</anno></c>. Apart from
- the format of the timeout message,
- <c>erlang:send_after/4</c> works exactly as
- <seealso marker="#start_timer/4"><c>erlang:start_timer/4</c></seealso>.</p>
+ <p>Starts a timer. The same as calling
+ <seealso marker="#send_after/4">
+ <c>erlang:send_after(<anno>Time</anno>, <anno>Dest</anno>,
+ <anno>Msg</anno>, [])</c></seealso>.</p>
</desc>
</func>
+
<func>
- <name name="send_after" arity="3"/>
- <fsummary>Starts a timer.</fsummary>
+ <name name="send_after" arity="4"/>
+ <fsummary>Start a timer.</fsummary>
<desc>
- <p>Starts a timer. The same as calling
- <seealso marker="#send_after/4"><c>erlang:send_after(<anno>Time</anno>,
- <anno>Dest</anno>, <anno>Msg</anno>, [])</c></seealso>.</p>
+ <p>Starts a timer. When the timer expires, the message
+ <c><anno>Msg</anno></c> is sent to the process
+ identified by <c><anno>Dest</anno></c>. Apart from
+ the format of the time-out message, this function works exactly as
+ <seealso marker="#start_timer/4">
+ <c>erlang:start_timer/4</c></seealso>.</p>
</desc>
</func>
<func>
<name name="send_nosuspend" arity="2"/>
- <fsummary>Tries to send a message without ever blocking.</fsummary>
+ <fsummary>Try to send a message without ever blocking.</fsummary>
<type name="dst"/>
<desc>
<p>The same as
- <seealso marker="#send/3">erlang:send(<anno>Dest</anno>,
- <anno>Msg</anno>, [nosuspend])</seealso>,
+ <seealso marker="#send/3"><c>erlang:send(<anno>Dest</anno>,
+ <anno>Msg</anno>, [nosuspend])</c></seealso>,
but returns <c>true</c> if
the message was sent and <c>false</c> if the message was not
sent because the sender would have had to be suspended.</p>
@@ -5174,12 +5538,12 @@ true</pre>
contradictory to the Erlang programming model. The message is
<em>not</em> sent if this function returns <c>false</c>.</p>
<p>In many systems, transient states of
- overloaded queues are normal. The fact that this function
+ overloaded queues are normal. Although this function
returns <c>false</c> does not mean that the other
node is guaranteed to be non-responsive, it could be a
temporary overload. Also, a return value of <c>true</c> does
only mean that the message can be sent on the (TCP) channel
- without blocking, the message is not guaranteed to
+ without blocking; the message is not guaranteed to
arrive at the remote node. For a disconnected
non-responsive node, the return value is <c>true</c> (mimics
the behavior of operator <c>!</c>). The expected
@@ -5193,15 +5557,16 @@ true</pre>
<func>
<name name="send_nosuspend" arity="3"/>
- <fsummary>Tries to send a message without ever blocking.</fsummary>
+ <fsummary>Try to send a message without ever blocking.</fsummary>
<type name="dst"/>
<desc>
<p>The same as
- <seealso marker="#send/3">erlang:send(<anno>Dest</anno>,
- <anno>Msg</anno>, [nosuspend | <anno>Options</anno>])</seealso>,
+ <seealso marker="#send/3"><c>erlang:send(<anno>Dest</anno>,
+ <anno>Msg</anno>, [nosuspend | <anno>Options</anno>])</c></seealso>,
but with a Boolean return value.</p>
<p>This function behaves like
- <seealso marker="#send_nosuspend/2">erlang:send_nosuspend/2</seealso>,
+ <seealso marker="#send_nosuspend/2">
+ <c>erlang:send_nosuspend/2</c></seealso>,
but takes a third parameter, a list of options.
The only option is <c>noconnect</c>, which
makes the function return <c>false</c> if
@@ -5225,14 +5590,15 @@ true</pre>
<func>
<name name="set_cookie" arity="2"/>
- <fsummary>Sets the magic cookie of a node.</fsummary>
+ <fsummary>Set the magic cookie of a node.</fsummary>
<desc>
<p>Sets the magic cookie of <c><anno>Node</anno></c> to the atom
<c><anno>Cookie</anno></c>. If <c><anno>Node</anno></c> is the
local node, the function
also sets the cookie of all other unknown nodes to
- <c><anno>Cookie</anno></c> (see Section
- <seealso marker="doc/reference_manual:distributed">Distributed Erlang</seealso>
+ <c><anno>Cookie</anno></c> (see section
+ <seealso marker="doc/reference_manual:distributed">
+ Distributed Erlang</seealso>
in the Erlang Reference Manual in System Documentation).</p>
<p>Failure: <c>function_clause</c> if the local node is not
alive.</p>
@@ -5241,12 +5607,12 @@ true</pre>
<func>
<name name="setelement" arity="3"/>
- <fsummary>Sets the Nth element of a tuple.</fsummary>
+ <fsummary>Set the Nth element of a tuple.</fsummary>
<type_desc variable="Index">1..tuple_size(<anno>Tuple1</anno></type_desc>
<desc>
<p>Returns a tuple that is a copy of argument
<c><anno>Tuple1</anno></c>
- with the element given by integer argument
+ with the element specified by integer argument
<c><anno>Index</anno></c>
(the first element is the element with index 1) replaced by
argument <c><anno>Value</anno></c>, for example:</p>
@@ -5261,52 +5627,52 @@ true</pre>
<fsummary>Size of a tuple or binary.</fsummary>
<desc>
<p>Returns the number of elements in a tuple or the number of
- bytes in a binary or bitstring, for example:</p>
+ bytes in a binary or bitstring, for example:</p>
<pre>
> <input>size({morni, mulle, bwange}).</input>
3
> <input>size(&lt;&lt;11, 22, 33&gt;&gt;).</input>
-3
-</pre>
- <p>For bitstrings the number of whole bytes is returned. That is, if the number of bits
+3</pre>
+ <p>For bitstrings, the number of whole bytes is returned.
+ That is, if the number of bits
in the bitstring is not divisible by 8, the resulting
number of bytes is rounded <em>down</em>.</p>
<p>Allowed in guard tests.</p>
<p>See also
<seealso marker="#tuple_size/1"><c>tuple_size/1</c></seealso>,
- <seealso marker="#byte_size/1"><c>byte_size/1</c></seealso>
- and
+ <seealso marker="#byte_size/1"><c>byte_size/1</c></seealso>, and
<seealso marker="#bit_size/1"><c>bit_size/1</c></seealso>.</p>
</desc>
</func>
<func>
<name name="spawn" arity="1"/>
- <fsummary>Creates a new process with a fun as entry point.</fsummary>
+ <fsummary>Create a new process with a fun as entry point.</fsummary>
<desc>
<p>Returns the process identifier of a new process started by the
application of <c><anno>Fun</anno></c> to the empty list
<c>[]</c>. Otherwise
- works like <seealso marker="#spawn/3">spawn/3</seealso>.</p>
+ works like <seealso marker="#spawn/3"><c>spawn/3</c></seealso>.</p>
</desc>
</func>
<func>
<name name="spawn" arity="2"/>
- <fsummary>Creates a new process with a fun as entry point on a given node.</fsummary>
+ <fsummary>Create a new process with a fun as entry point on a specified
+ node.</fsummary>
<desc>
<p>Returns the process identifier of a new process started
by the application of <c><anno>Fun</anno></c> to the
empty list <c>[]</c> on <c><anno>Node</anno></c>. If
<c><anno>Node</anno></c> does not exist, a useless pid is
returned. Otherwise works like
- <seealso marker="#spawn/3">spawn/3</seealso>.</p>
+ <seealso marker="#spawn/3"><c>spawn/3</c></seealso>.</p>
</desc>
</func>
<func>
<name name="spawn" arity="3"/>
- <fsummary>Creates a new process with a function as entry point.</fsummary>
+ <fsummary>Create a new process with a function as entry point.</fsummary>
<desc>
<p>Returns the process identifier of a new process started by
the application of <c><anno>Module</anno>:<anno>Function</anno></c>
@@ -5318,7 +5684,7 @@ true</pre>
does not exist (where <c>Arity</c> is the length of
<c><anno>Args</anno></c>). The error handler
can be redefined (see
- <seealso marker="#process_flag/2">process_flag/2</seealso>).
+ <seealso marker="#process_flag/2"><c>process_flag/2</c></seealso>).
If <c>error_handler</c> is undefined, or the user has
redefined the default <c>error_handler</c> and its replacement is
undefined, a failure with reason <c>undef</c> occurs.</p>
@@ -5331,7 +5697,8 @@ true</pre>
<func>
<name name="spawn" arity="4"/>
- <fsummary>Creates a new process with a function as entry point on a given node.</fsummary>
+ <fsummary>Create a new process with a function as entry point on a
+ specified node.</fsummary>
<desc>
<p>Returns the process identifier (pid) of a new process started
by the application
@@ -5339,26 +5706,28 @@ true</pre>
to <c><anno>Args</anno></c> on <c><anno>Node</anno></c>. If
<c><anno>Node</anno></c> does not exist, a useless pid is returned.
Otherwise works like
- <seealso marker="#spawn/3">spawn/3</seealso>.</p>
+ <seealso marker="#spawn/3"><c>spawn/3</c></seealso>.</p>
</desc>
</func>
<func>
<name name="spawn_link" arity="1"/>
- <fsummary>Creates and links to a new process with a fun as entry point.</fsummary>
+ <fsummary>Create and link to a new process with a fun as entry point.
+ </fsummary>
<desc>
<p>Returns the process identifier of a new process started by
the application of <c><anno>Fun</anno></c> to the empty list
<c>[]</c>. A link is created between
the calling process and the new process, atomically.
Otherwise works like
- <seealso marker="#spawn/3">spawn/3</seealso>.</p>
+ <seealso marker="#spawn/3"><c>spawn/3</c></seealso>.</p>
</desc>
</func>
<func>
<name name="spawn_link" arity="2"/>
- <fsummary>Creates and links to a new process with a fun as entry point on a specified node.</fsummary>
+ <fsummary>Create and link to a new process with a fun as entry point on
+ a specified node.</fsummary>
<desc>
<p>Returns the process identifier (pid) of a new process started
by the application of <c><anno>Fun</anno></c> to the empty
@@ -5367,26 +5736,29 @@ true</pre>
atomically. If <c><anno>Node</anno></c> does not exist,
a useless pid is returned and an exit signal with
reason <c>noconnection</c> is sent to the calling
- process. Otherwise works like <seealso marker="#spawn/3">spawn/3</seealso>.</p>
+ process. Otherwise works like <seealso marker="#spawn/3">
+ <c>spawn/3</c></seealso>.</p>
</desc>
</func>
<func>
<name name="spawn_link" arity="3"/>
- <fsummary>Creates and links to a new process with a function as entry point.</fsummary>
+ <fsummary>Create and link to a new process with a function as entry point.
+ </fsummary>
<desc>
<p>Returns the process identifier of a new process started by
the application of <c><anno>Module</anno>:<anno>Function</anno></c>
to <c><anno>Args</anno></c>. A link is created
between the calling process and the new process, atomically.
Otherwise works like
- <seealso marker="#spawn/3">spawn/3</seealso>.</p>
+ <seealso marker="#spawn/3"><c>spawn/3</c></seealso>.</p>
</desc>
</func>
<func>
<name name="spawn_link" arity="4"/>
- <fsummary>Creates and links to a new process with a function as entry point on a given node.</fsummary>
+ <fsummary>Create and link to a new process with a function as entry point
+ on a specified node.</fsummary>
<desc>
<p>Returns the process identifier (pid) of a new process
started by the application
@@ -5396,100 +5768,113 @@ true</pre>
process, atomically. If <c><anno>Node</anno></c> does
not exist, a useless pid is returned and an exit signal with
reason <c>noconnection</c> is sent to the calling
- process. Otherwise works like <seealso marker="#spawn/3">spawn/3</seealso>.</p>
+ process. Otherwise works like <seealso marker="#spawn/3">
+ <c>spawn/3</c></seealso>.</p>
</desc>
</func>
<func>
<name name="spawn_monitor" arity="1"/>
- <fsummary>Creates and monitors a new process with a fun as entry point.</fsummary>
+ <fsummary>Create and monitor a new process with a fun as entry point.
+ </fsummary>
<desc>
<p>Returns the process identifier of a new process, started by
the application of <c><anno>Fun</anno></c> to the empty list
<c>[]</c>,
and a reference for a monitor created to the new process.
Otherwise works like
- <seealso marker="#spawn/3">spawn/3</seealso>.</p>
+ <seealso marker="#spawn/3"><c>spawn/3</c></seealso>.</p>
</desc>
</func>
<func>
<name name="spawn_monitor" arity="3"/>
- <fsummary>Creates and monitors a new process with a function as entry point.</fsummary>
+ <fsummary>Create and monitor a new process with a function as entry point.
+ </fsummary>
<desc>
<p>A new process is started by the application
of <c><anno>Module</anno>:<anno>Function</anno></c>
to <c><anno>Args</anno></c>. The process is
monitored at the same time. Returns the process identifier
and a reference for the monitor. Otherwise works like
- <seealso marker="#spawn/3">spawn/3</seealso>.</p>
+ <seealso marker="#spawn/3"><c>spawn/3</c></seealso>.</p>
</desc>
</func>
<func>
<name name="spawn_opt" arity="2"/>
- <fsummary>Creates a new process with a fun as entry point.</fsummary>
+ <fsummary>Create a new process with a fun as entry point.</fsummary>
<type name="priority_level"/>
+ <type name="max_heap_size"/>
+ <type name="message_queue_data"/>
+ <type name="spawn_opt_option"/>
<desc>
<p>Returns the process identifier (pid) of a new process
started by the application of <c><anno>Fun</anno></c>
to the empty list <c>[]</c>. Otherwise works like
- <seealso marker="#spawn_opt/4">spawn_opt/4</seealso>.</p>
- <p>If option <c>monitor</c> is given, the newly created
+ <seealso marker="#spawn_opt/4"><c>spawn_opt/4</c></seealso>.</p>
+ <p>If option <c>monitor</c> is specified, the newly created
process is monitored, and both the pid and reference for
- the monitor is returned.</p>
+ the monitor are returned.</p>
</desc>
</func>
<func>
<name name="spawn_opt" arity="3"/>
- <fsummary>Creates a new process with a fun as entry point on a given node.</fsummary>
+ <fsummary>Create a new process with a fun as entry point on a specified
+ node.</fsummary>
<type name="priority_level"/>
+ <type name="max_heap_size"/>
+ <type name="message_queue_data"/>
+ <type name="spawn_opt_option"/>
<desc>
<p>Returns the process identifier (pid) of a new process started
by the application of <c><anno>Fun</anno></c> to the
empty list <c>[]</c> on <c><anno>Node</anno></c>. If
<c><anno>Node</anno></c> does not exist, a useless pid is
returned. Otherwise works like
- <seealso marker="#spawn_opt/4">spawn_opt/4</seealso>.</p>
+ <seealso marker="#spawn_opt/4"><c>spawn_opt/4</c></seealso>.</p>
</desc>
</func>
<func>
<name name="spawn_opt" arity="4"/>
- <fsummary>Creates a new process with a function as entry point.</fsummary>
+ <fsummary>Create a new process with a function as entry point.</fsummary>
<type name="priority_level"/>
+ <type name="max_heap_size"/>
+ <type name="message_queue_data"/>
+ <type name="spawn_opt_option"/>
<desc>
<p>Works as
- <seealso marker="#spawn/3">spawn/3</seealso>, except that an
- extra option list is given when creating the process.</p>
- <p>If option <c>monitor</c> is given, the newly created
+ <seealso marker="#spawn/3"><c>spawn/3</c></seealso>, except that an
+ extra option list is specified when creating the process.</p>
+ <p>If option <c>monitor</c> is specified, the newly created
process is monitored, and both the pid and reference for
- the monitor is returned.</p>
- <p>The options are as follows:</p>
+ the monitor are returned.</p>
+ <p>Options:</p>
<taglist>
<tag><c>link</c></tag>
<item>
<p>Sets a link to the parent process (like
- <c>spawn_link/3</c> does).</p>
+ <seealso marker="#spawn_link/3"><c>spawn_link/3</c></seealso>
+ does).</p>
</item>
<tag><c>monitor</c></tag>
<item>
<p>Monitors the new process (like
- <seealso marker="#monitor/2">monitor/2</seealso> does).</p>
+ <seealso marker="#monitor/2"><c>monitor/2</c></seealso> does).</p>
</item>
<tag><c>{priority, <anno>Level</anno></c></tag>
<item>
<p>Sets the priority of the new process. Equivalent to
- executing
- <seealso marker="#process_flag_priority">process_flag(priority,
- <anno>Level</anno>)</seealso>
+ executing <seealso marker="#process_flag_priority">
+ <c>process_flag(priority, <anno>Level</anno>)</c></seealso>
in the start function of the new process,
except that the priority is set before the process is
selected for execution for the first time. For more
information on priorities, see
- <seealso marker="#process_flag_priority">process_flag(priority,
- <anno>Level</anno>)</seealso>.</p>
+ <seealso marker="#process_flag_priority">
+ <c>process_flag(priority, <anno>Level</anno>)</c></seealso>.</p>
</item>
<tag><c>{fullsweep_after, <anno>Number</anno>}</c></tag>
<item>
@@ -5512,21 +5897,22 @@ true</pre>
<p>A few cases when it can be useful to change
<c>fullsweep_after</c>:</p>
<list type="bulleted">
- <item>If binaries that are no longer used are to be
+ <item><p>If binaries that are no longer used are to be
thrown away as soon as possible. (Set
- <c><anno>Number</anno></c> to zero.)
+ <c><anno>Number</anno></c> to zero.)</p>
</item>
- <item>A process that mostly have short-lived data is
+ <item><p>A process that mostly have short-lived data is
fullsweeped seldom or never, that is, the old heap
contains mostly garbage. To ensure a fullsweep
occasionally, set <c><anno>Number</anno></c> to a
- suitable value, such as 10 or 20.
+ suitable value, such as 10 or 20.</p>
</item>
<item>In embedded systems with a limited amount of RAM
and no virtual memory, you might want to preserve memory
by setting <c><anno>Number</anno></c> to zero.
(The value can be set globally, see
- <seealso marker="#system_flag/2">erlang:system_flag/2</seealso>.)
+ <seealso marker="#system_flag/2">
+ <c>erlang:system_flag/2</c></seealso>.)
</item>
</list>
</item>
@@ -5551,7 +5937,7 @@ true</pre>
option unless you know that there is problem with
execution times or memory consumption, and
ensure that the option improves matters.</p>
- <p>Gives a minimum binary virtual heap size, in words.
+ <p>Gives a minimum binary virtual heap size, in words.
Setting this value
higher than the system default can speed up some
processes because less garbage collection is done.
@@ -5560,14 +5946,41 @@ true</pre>
fine-tuning an application and to measure the execution
time with various <c><anno>VSize</anno></c> values.</p>
</item>
+ <tag><c>{max_heap_size, <anno>Size</anno>}</c></tag>
+ <item>
+ <p>Sets the <c>max_heap_size</c> process flag. The default
+ <c>max_heap_size</c> is determined by command-line argument
+ <seealso marker="erl#+hmax"><c>+hmax</c></seealso>
+ in <c>erl(1)</c>. For more information, see the
+ documentation of <seealso marker="#process_flag_max_heap_size">
+ <c>process_flag(max_heap_size, <anno>Size</anno>)</c></seealso>.
+ </p>
+ </item>
+ <tag><c>{message_queue_data, <anno>MQD</anno>}</c></tag>
+ <item>
+ <p>Sets the state of the <c>message_queue_data</c> process
+ flag. <c><anno>MQD</anno></c> is to be either <c>off_heap</c>
+ or <c>on_heap</c>. The default
+ <c>message_queue_data</c> process flag is determined by
+ command-line argument <seealso marker="erl#+hmqd">
+ <c>+hmqd</c></seealso> in <c>erl(1)</c>.
+ For more information, see the documentation of
+ <seealso marker="#process_flag_message_queue_data">
+ <c>process_flag(message_queue_data,
+ <anno>MQD</anno>)</c></seealso>.</p>
+ </item>
</taglist>
</desc>
</func>
<func>
<name name="spawn_opt" arity="5"/>
- <fsummary>Creates a new process with a function as entry point on a given node.</fsummary>
+ <fsummary>Create a new process with a function as entry point on a
+ specified node.</fsummary>
<type name="priority_level"/>
+ <type name="max_heap_size"/>
+ <type name="message_queue_data"/>
+ <type name="spawn_opt_option"/>
<desc>
<p>Returns the process identifier (pid) of a new process started
by the application
@@ -5575,23 +5988,24 @@ true</pre>
<c><anno>Args</anno></c> on <c><anno>Node</anno></c>. If
<c><anno>Node</anno></c> does not exist, a useless pid is returned.
Otherwise works like
- <seealso marker="#spawn_opt/4">spawn_opt/4</seealso>.</p>
- <note><p>Option <c>monitor</c> is not supported by
- <c>spawn_opt/5</c>.</p></note>
+ <seealso marker="#spawn_opt/4"><c>spawn_opt/4</c></seealso>.</p>
+ <note>
+ <p>Option <c>monitor</c> is not supported by
+ <c>spawn_opt/5</c>.</p>
+ </note>
</desc>
</func>
<func>
<name name="split_binary" arity="2"/>
- <fsummary>Splits a binary into two.</fsummary>
+ <fsummary>Split a binary into two.</fsummary>
<type_desc variable="Pos">0..byte_size(Bin)</type_desc>
<desc>
<p>Returns a tuple containing the binaries that are the result
of splitting <c><anno>Bin</anno></c> into two parts at
position <c><anno>Pos</anno></c>.
This is not a destructive operation. After the operation,
- there are three binaries altogether.</p>
- <p>Example:</p>
+ there are three binaries altogether. Example:</p>
<pre>
> <input>B = list_to_binary("0123456789").</input>
&lt;&lt;"0123456789">>
@@ -5607,104 +6021,96 @@ true</pre>
</func>
<func>
+ <name name="start_timer" arity="3"/>
+ <fsummary>Start a timer.</fsummary>
+ <desc>
+ <p>Starts a timer. The same as calling
+ <seealso marker="#start_timer/4">
+ <c>erlang:start_timer(<anno>Time</anno>,
+ <anno>Dest</anno>, <anno>Msg</anno>, [])</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="start_timer" arity="4"/>
- <fsummary>Starts a timer.</fsummary>
+ <fsummary>Start a timer.</fsummary>
<desc>
- <p>
- Starts a timer. When the timer expires, the message
+ <p>Starts a timer. When the timer expires, the message
<c>{timeout, <anno>TimerRef</anno>, <anno>Msg</anno>}</c>
- is sent to the process identified by
- <c><anno>Dest</anno></c>.
- </p>
- <p>Available <c><anno>Option</anno></c>s:</p>
+ is sent to the process identified by <c><anno>Dest</anno></c>.</p>
+ <p><c><anno>Option</anno></c>s:</p>
<taglist>
<tag><c>{abs, false}</c></tag>
<item>
- <p>
- This is the default. It means the
- <c><anno>Time</anno></c> value is interpreted
- as a time in milli-seconds <em>relative</em> current
- <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
- monotonic time</seealso>.
- </p>
- </item>
+ <p>This is the default. It means the
+ <c><anno>Time</anno></c> value is interpreted
+ as a time in milliseconds <em>relative</em> current
+ <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
+ monotonic time</seealso>.</p>
+ </item>
<tag><c>{abs, true}</c></tag>
<item>
- <p>
- Absolute <c><anno>Time</anno></c> value. The
- <c><anno>Time</anno></c> value is interpreted as an
- absolute Erlang monotonic time in milli-seconds.
- </p>
- </item>
+ <p>Absolute <c><anno>Time</anno></c> value. The
+ <c><anno>Time</anno></c> value is interpreted as an
+ absolute Erlang monotonic time in milliseconds.</p>
+ </item>
</taglist>
- <p>
- More <c><anno>Option</anno></c>s may be added in the future.
- </p>
- <p>
- The absolute point in time, the timer is set to expire on,
- has to be in the interval
- <c>[</c><seealso marker="#system_info_start_time"><c>erlang:system_info(start_time)</c></seealso><c>,
- </c><seealso marker="#system_info_end_time"><c>erlang:system_info(end_time)</c></seealso><c>]</c>.
- Further, if a relative time is specified, the <c><anno>Time</anno></c> value
- is not allowed to be negative.
- </p>
- <p>
- If <c><anno>Dest</anno></c> is a <c>pid()</c>, it must
- be a <c>pid()</c> of a process created on the current
- runtime system instance. This process may or may not
- have terminated. If <c><anno>Dest</anno></c> is an
- <c>atom()</c>, it is interpreted as the name of a
- locally registered process. The process referred to by the
- name is looked up at the time of timer expiration. No error
- is given if the name does not refer to a process.
- </p>
- <p>
- If <c><anno>Dest</anno></c> is a <c>pid()</c>, the timer is
- automatically canceled if the process referred to by the
- <c>pid()</c> is not alive, or if the process exits. This
- feature was introduced in ERTS version 5.4.11. Notice that
- timers are not automatically canceled when
- <c><anno>Dest</anno></c> is an <c>atom()</c>.
- </p>
+ <p>More <c><anno>Option</anno></c>s can be added in the future.</p>
+ <p>The absolute point in time, the timer is set to expire on,
+ must be in the interval
+ <c>[</c><seealso marker="#system_info_start_time">
+ <c>erlang:system_info(start_time)</c></seealso><c>,
+ </c><seealso marker="#system_info_end_time">
+ <c>erlang:system_info(end_time)</c></seealso><c>]</c>.
+ If a relative time is specified, the <c><anno>Time</anno></c>
+ value is not allowed to be negative.</p>
+ <p>If <c><anno>Dest</anno></c> is a <c>pid()</c>, it must
+ be a <c>pid()</c> of a process created on the current
+ runtime system instance. This process has either terminated
+ or not. If <c><anno>Dest</anno></c> is an
+ <c>atom()</c>, it is interpreted as the name of a
+ locally registered process. The process referred to by the
+ name is looked up at the time of timer expiration. No error
+ is returned if the name does not refer to a process.</p>
+ <p>If <c><anno>Dest</anno></c> is a <c>pid()</c>, the timer is
+ automatically canceled if the process referred to by the
+ <c>pid()</c> is not alive, or if the process exits. This
+ feature was introduced in ERTS 5.4.11. Notice that
+ timers are not automatically canceled when
+ <c><anno>Dest</anno></c> is an <c>atom()</c>.</p>
<p>See also
<seealso marker="#send_after/4"><c>erlang:send_after/4</c></seealso>,
- <seealso marker="#cancel_timer/2"><c>erlang:cancel_timer/2</c></seealso>,
- and
- <seealso marker="#read_timer/2"><c>erlang:read_timer/2</c></seealso>.</p>
+ <seealso marker="#cancel_timer/2">
+ <c>erlang:cancel_timer/2</c></seealso>, and
+ <seealso marker="#read_timer/2">
+ <c>erlang:read_timer/2</c></seealso>.</p>
<p>Failure: <c>badarg</c> if the arguments do not satisfy
the requirements specified here.</p>
</desc>
</func>
<func>
- <name name="start_timer" arity="3"/>
- <fsummary>Starts a timer.</fsummary>
- <desc>
- <p>Starts a timer. The same as calling
- <seealso marker="#start_timer/4"><c>erlang:start_timer(<anno>Time</anno>,
- <anno>Dest</anno>, <anno>Msg</anno>, [])</c></seealso>.</p>
- </desc>
- </func>
-
- <func>
<name name="statistics" arity="1" clause_i="1"/>
<fsummary>Information about active processes and ports.</fsummary>
- <desc><marker id="statistics_active_tasks"></marker>
- <p>
- Returns a list where each element represents the amount
- of active processes and ports on each run queue and its
- associated scheduler. That is, the number of processes and
- ports that are ready to run, or are currently running. The
- element location in the list corresponds to the scheduler
- and its run queue. The first element corresponds to scheduler
- number 1 and so on. The information is <em>not</em> gathered
- atomically. That is, the result is not necessarily a
- consistent snapshot of the state, but instead quite
- efficiently gathered. See also,
- <seealso marker="#statistics_total_active_tasks"><c>statistics(total_active_tasks)</c></seealso>,
- <seealso marker="#statistics_run_queue_lengths"><c>statistics(run_queue_lengths)</c></seealso>, and
- <seealso marker="#statistics_total_run_queue_lengths"><c>statistics(total_run_queue_lengths)</c></seealso>.
- </p>
+ <desc>
+ <marker id="statistics_active_tasks"></marker>
+ <p>Returns a list where each element represents the amount
+ of active processes and ports on each run queue and its
+ associated scheduler. That is, the number of processes and
+ ports that are ready to run, or are currently running. The
+ element location in the list corresponds to the scheduler
+ and its run queue. The first element corresponds to scheduler
+ number 1 and so on. The information is <em>not</em> gathered
+ atomically. That is, the result is not necessarily a
+ consistent snapshot of the state, but instead quite
+ efficiently gathered.</p>
+ <p>See also
+ <seealso marker="#statistics_total_active_tasks">
+ <c>statistics(total_active_tasks)</c></seealso>,
+ <seealso marker="#statistics_run_queue_lengths">
+ <c>statistics(run_queue_lengths)</c></seealso>, and
+ <seealso marker="#statistics_total_run_queue_lengths">
+ <c>statistics(total_run_queue_lengths)</c></seealso>.</p>
</desc>
</func>
@@ -5723,11 +6129,13 @@ true</pre>
<desc>
<marker id="statistics_exact_reductions"></marker>
<p>Returns the number of exact reductions.</p>
- <note><p><c>statistics(exact_reductions)</c> is
- a more expensive operation than
- <seealso marker="#statistics_reductions">statistics(reductions)</seealso>,
- especially on an Erlang machine with SMP support.</p>
- </note>
+ <note>
+ <p><c>statistics(exact_reductions)</c> is
+ a more expensive operation than
+ <seealso marker="#statistics_reductions">
+ statistics(reductions)</seealso>,
+ especially on an Erlang machine with SMP support.</p>
+ </note>
</desc>
</func>
@@ -5736,7 +6144,7 @@ true</pre>
<fsummary>Information about garbage collection.</fsummary>
<desc>
<p>Returns information about garbage collection, for example:</p>
- <pre>
+ <pre>
> <input>statistics(garbage_collection).</input>
{85,23961,0}</pre>
<p>This information can be invalid for some implementations.</p>
@@ -5748,14 +6156,150 @@ true</pre>
<fsummary>Information about I/O.</fsummary>
<desc>
<p>Returns <c><anno>Input</anno></c>,
- which is the total number of bytes
- received through ports, and <c><anno>Output</anno></c>,
- which is the total number of bytes output to ports.</p>
+ which is the total number of bytes
+ received through ports, and <c><anno>Output</anno></c>,
+ which is the total number of bytes output to ports.</p>
</desc>
</func>
<func>
<name name="statistics" arity="1" clause_i="6"/>
+ <fsummary>Information about microstate accounting.</fsummary>
+ <desc>
+ <marker id="statistics_microstate_accounting"></marker>
+ <p>Microstate accounting can be used to measure how much time the Erlang
+ runtime system spends doing various tasks. It is designed to be as
+ lightweight as possible, but some overhead exists when this
+ is enabled. Microstate accounting is meant to be a profiling tool
+ to help finding performance bottlenecks.
+ To <c>start</c>/<c>stop</c>/<c>reset</c> microstate accounting, use
+ system flag <seealso marker="#system_flag_microstate_accounting">
+ <c>microstate_accounting</c></seealso>.</p>
+ <p><c>statistics(microstate_accounting)</c> returns a list of maps
+ representing some of the OS threads within ERTS. Each map
+ contains <c>type</c> and <c>id</c> fields that can be used to
+ identify what
+ thread it is, and also a counters field that contains data about how
+ much time has been spent in the various states.</p>
+ <p>Example:</p>
+ <pre>
+> <input>erlang:statistics(microstate_accounting).</input>
+[#{counters => #{aux => 1899182914,
+ check_io => 2605863602,
+ emulator => 45731880463,
+ gc => 1512206910,
+ other => 5421338456,
+ port => 221631,
+ sleep => 5150294100},
+ id => 1,
+ type => scheduler}|...]</pre>
+ <p>The time unit is the same as returned by
+ <seealso marker="kernel:os#perf_counter/0">
+ <c>os:perf_counter/0</c></seealso>.
+ So, to convert it to milliseconds, you can do something like this:</p>
+ <pre>
+lists:map(
+ fun(#{ counters := Cnt } = M) ->
+ MsCnt = maps:map(fun(_K, PerfCount) ->
+ erlang:convert_time_unit(PerfCount, perf_counter, 1000)
+ end, Cnt),
+ M#{ counters := MsCnt }
+ end, erlang:statistics(microstate_accounting)).</pre>
+ <p>Notice that these values are not guaranteed to be
+ the exact time spent in each state. This is because of various
+ optimisation done to keep the overhead as small as possible.</p>
+ <p><c><anno>MSAcc_Thread_Type</anno></c>s:</p>
+ <taglist>
+ <tag><c>scheduler</c></tag>
+ <item>The main execution threads that do most of the work.</item>
+ <tag><c>dirty_cpu_scheduler</c></tag>
+ <item>The threads for long running cpu intensive work.</item>
+ <tag><c>dirty_io_scheduler</c></tag>
+ <item>The threads for long running I/O work.</item>
+ <tag><c>async</c></tag>
+ <item>Async threads are used by various linked-in drivers (mainly the
+ file drivers) do offload non-CPU intensive work.</item>
+ <tag><c>aux</c></tag>
+ <item>Takes care of any work that is not
+ specifically assigned to a scheduler.</item>
+ </taglist>
+ <p>The following <c><anno>MSAcc_Thread_State</anno></c>s are available.
+ All states are exclusive, meaning that a thread cannot be in two
+ states at once. So, if you add the numbers of all counters in a
+ thread, you get the total runtime for that thread.</p>
+ <taglist>
+ <tag><c>aux</c></tag>
+ <item>Time spent handling auxiliary jobs.</item>
+ <tag><c>check_io</c></tag>
+ <item>Time spent checking for new I/O events.</item>
+ <tag><c>emulator</c></tag>
+ <item>Time spent executing Erlang processes.</item>
+ <tag><c>gc</c></tag>
+ <item>Time spent doing garbage collection. When extra states are
+ enabled this is the time spent doing non-fullsweep garbage
+ collections.</item>
+ <tag><c>other</c></tag>
+ <item>Time spent doing unaccounted things.</item>
+ <tag><c>port</c></tag>
+ <item>Time spent executing ports.</item>
+ <tag><c>sleep</c></tag>
+ <item>Time spent sleeping.</item>
+ </taglist>
+ <p>More fine-grained <c><anno>MSAcc_Thread_State</anno></c>s can
+ be added through configure (such as
+ <c>./configure --with-microstate-accounting=extra</c>).
+ Enabling these states causes performance degradation when
+ microstate accounting is turned off and increases the overhead when
+ it is turned on.</p>
+ <taglist>
+ <tag><c>alloc</c></tag>
+ <item>Time spent managing memory. Without extra states this time is
+ spread out over all other states.</item>
+ <tag><c>bif</c></tag>
+ <item>Time spent in BIFs. Without extra states this time is part of
+ the <c>emulator</c> state.</item>
+ <tag><c>busy_wait</c></tag>
+ <item>Time spent busy waiting. This is also the state where a
+ scheduler no longer reports that it is active when using
+ <seealso marker="#statistics_scheduler_wall_time">
+ <c>statistics(scheduler_wall_time)</c></seealso>. So, if you add
+ all other states but this and sleep, and then divide that by all
+ time in the thread, you should get something very similar to the
+ <c>scheduler_wall_time</c> fraction. Without extra states this
+ time is part of the <c>other</c> state.</item>
+ <tag><c>ets</c></tag>
+ <item>Time spent executing ETS BIFs. Without extra states
+ this time is part of the <c>emulator</c> state.</item>
+ <tag><c>gc_full</c></tag>
+ <item>Time spent doing fullsweep garbage collection. Without extra
+ states this time is part of the <c>gc</c> state.</item>
+ <tag><c>nif</c></tag>
+ <item>Time spent in NIFs. Without extra states this time is part of
+ the <c>emulator</c> state.</item>
+ <tag><c>send</c></tag>
+ <item>Time spent sending messages (processes only). Without extra
+ states this time is part of the <c>emulator</c> state.</item>
+ <tag><c>timers</c></tag>
+ <item>Time spent managing timers. Without extra states this time is
+ part of the <c>other</c> state.</item>
+ </taglist>
+ <p>The utility module
+ <seealso marker="runtime_tools:msacc"><c>msacc(3)</c></seealso>
+ can be used to more easily analyse these statistics.</p>
+ <p>Returns <c>undefined</c> if system flag
+ <seealso marker="#system_flag_microstate_accounting">
+ <c>microstate_accounting</c></seealso> is turned off.</p>
+ <p>The list of thread information is unsorted and can appear in
+ different order between calls.</p>
+ <note>
+ <p>The threads and states are subject to change without any
+ prior notice.</p>
+ </note>
+ </desc>
+ </func>
+
+ <func>
+ <name name="statistics" arity="1" clause_i="7"/>
<fsummary>Information about reductions.</fsummary>
<desc>
<marker id="statistics_reductions"></marker>
@@ -5763,59 +6307,61 @@ true</pre>
<pre>
> <input>statistics(reductions).</input>
{2046,11}</pre>
- <note><p>As from <c>ERTS</c> 5.5 (OTP R11B),
+ <note><p>As from ERTS 5.5 (Erlang/OTP R11B),
this value does not include reductions performed in current
time slices of currently scheduled processes. If an
exact value is wanted, use
- <seealso marker="#statistics_exact_reductions">statistics(exact_reductions)</seealso>.</p>
+ <seealso marker="#statistics_exact_reductions">
+ <c>statistics(exact_reductions)</c></seealso>.</p>
</note>
</desc>
</func>
<func>
- <name name="statistics" arity="1" clause_i="7"/>
+ <name name="statistics" arity="1" clause_i="8"/>
<fsummary>Information about the run-queues.</fsummary>
<desc><marker id="statistics_run_queue"></marker>
- <p>
- Returns the total length of the run-queues. That is, the number
+ <p>Returns the total length of the run-queues. That is, the number
of processes and ports that are ready to run on all available
- run-queues. The information is gathered atomically. That
- is, the result is a consistent snapshot of the state, but
- this operation is much more expensive compared to
- <seealso marker="#statistics_total_run_queue_lengths"><c>statistics(total_run_queue_lengths)</c></seealso>.
- This especially when a large amount of schedulers is used.
- </p>
+ run-queues. The information is gathered atomically. That
+ is, the result is a consistent snapshot of the state, but
+ this operation is much more expensive compared to
+ <seealso marker="#statistics_total_run_queue_lengths">
+ <c>statistics(total_run_queue_lengths)</c></seealso>,
+ especially when a large amount of schedulers is used.</p>
</desc>
</func>
<func>
- <name name="statistics" arity="1" clause_i="8"/>
+ <name name="statistics" arity="1" clause_i="9"/>
<fsummary>Information about the run-queue lengths.</fsummary>
<desc><marker id="statistics_run_queue_lengths"></marker>
- <p>
- Returns a list where each element represents the amount
- of processes and ports ready to run for each run queue. The
- element location in the list corresponds to the run queue
- of a scheduler. The first element corresponds to the run
- queue of scheduler number 1 and so on. The information is
- <em>not</em> gathered atomically. That is, the result is
- not necessarily a consistent snapshot of the state, but
- instead quite efficiently gathered. See also,
- <seealso marker="#statistics_total_run_queue_lengths"><c>statistics(total_run_queue_lengths)</c></seealso>,
- <seealso marker="#statistics_active_tasks"><c>statistics(active_tasks)</c></seealso>, and
- <seealso marker="#statistics_total_active_tasks"><c>statistics(total_active_tasks)</c></seealso>.
- </p>
+ <p>Returns a list where each element represents the amount
+ of processes and ports ready to run for each run queue. The
+ element location in the list corresponds to the run queue
+ of a scheduler. The first element corresponds to the run
+ queue of scheduler number 1 and so on. The information is
+ <em>not</em> gathered atomically. That is, the result is
+ not necessarily a consistent snapshot of the state, but
+ instead quite efficiently gathered.</p>
+ <p>See also
+ <seealso marker="#statistics_total_run_queue_lengths">
+ <c>statistics(total_run_queue_lengths)</c></seealso>,
+ <seealso marker="#statistics_active_tasks">
+ <c>statistics(active_tasks)</c></seealso>, and
+ <seealso marker="#statistics_total_active_tasks">
+ <c>statistics(total_active_tasks)</c></seealso>.</p>
</desc>
</func>
<func>
- <name name="statistics" arity="1" clause_i="9"/>
+ <name name="statistics" arity="1" clause_i="10"/>
<fsummary>Information about runtime.</fsummary>
<desc>
<p>Returns information about runtime, in milliseconds.</p>
<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>
+ in the Erlang runtime system and can therefore be greater
+ than the wall clock time.</p>
<p>Example:</p>
<pre>
> <input>statistics(runtime).</input>
@@ -5824,10 +6370,10 @@ true</pre>
</func>
<func>
- <name name="statistics" arity="1" clause_i="10"/>
+ <name name="statistics" arity="1" clause_i="11"/>
<fsummary>Information about each schedulers work time.</fsummary>
<desc>
- <marker id="statistics_scheduler_wall_time"></marker>
+ <marker id="statistics_scheduler_wall_time"></marker>
<p>Returns a list of tuples with
<c>{<anno>SchedulerId</anno>, <anno>ActiveTime</anno>,
<anno>TotalTime</anno>}</c>, where
@@ -5835,7 +6381,8 @@ true</pre>
<c><anno>ActiveTime</anno></c> is
the duration the scheduler has been busy, and
<c><anno>TotalTime</anno></c> is the total time duration since
- <seealso marker="#system_flag_scheduler_wall_time">scheduler_wall_time</seealso>
+ <seealso marker="#system_flag_scheduler_wall_time">
+ <c>scheduler_wall_time</c></seealso>
activation. The time unit is undefined and can be subject
to change between releases, OSs, and system restarts.
<c>scheduler_wall_time</c> is only to be used to
@@ -5847,28 +6394,28 @@ true</pre>
that is:</p>
<list type="bulleted">
<item>Executing process code</item>
- <item>Executing linked-in-driver or NIF code</item>
- <item>Executing built-in-functions, or any other runtime
- handling</item>
+ <item>Executing linked-in driver or NIF code</item>
+ <item>Executing BIFs, or any other runtime handling</item>
<item>Garbage collecting</item>
<item>Handling any other memory management</item>
</list>
- <p>Notice that a scheduler can also be busy even if the
- OS has scheduled out the scheduler thread.</p>
+ <p>Notice that a scheduler can also be busy even if the
+ OS has scheduled out the scheduler thread.</p>
<p>Returns <c>undefined</c> if system flag
- <seealso marker="#system_flag_scheduler_wall_time">scheduler_wall_time</seealso>
- is turned off.</p>
+ <seealso marker="#system_flag_scheduler_wall_time">
+ <c>scheduler_wall_time</c></seealso> is turned off.</p>
<p>The list of scheduler information is unsorted and can
appear in different order between calls.</p>
- <p>Using <c>scheduler_wall_time</c> to calculate scheduler-utilization:</p>
-<pre>
+ <p>Using <c>scheduler_wall_time</c> to calculate
+ scheduler-utilization:</p>
+ <pre>
> <input>erlang:system_flag(scheduler_wall_time, true).</input>
false
> <input>Ts0 = lists:sort(erlang:statistics(scheduler_wall_time)), ok.</input>
ok</pre>
<p>Some time later the user takes another snapshot and calculates
scheduler-utilization per scheduler, for example:</p>
-<pre>
+ <pre>
> <input>Ts1 = lists:sort(erlang:statistics(scheduler_wall_time)), ok.</input>
ok
> <input>lists:map(fun({{I, A0, T0}, {I, A1, T1}}) ->
@@ -5881,8 +6428,9 @@ ok
{6,0.9739235846420741},
{7,0.973237033077876},
{8,0.9741297293248656}]</pre>
- <p>Using the same snapshots to calculate a total scheduler-utilization:</p>
-<pre>
+ <p>Using the same snapshots to calculate a total
+ scheduler-utilization:</p>
+ <pre>
> <input>{A, T} = lists:foldl(fun({{_, A0, T0}, {_, A1, T1}}, {Ai,Ti}) ->
{Ai + (A1 - A0), Ti + (T1 - T0)} end, {0, 0}, lists:zip(Ts0,Ts1)), A/T.</input>
0.9769136803764825</pre>
@@ -5895,44 +6443,47 @@ ok
</func>
<func>
- <name name="statistics" arity="1" clause_i="11"/>
+ <name name="statistics" arity="1" clause_i="12"/>
<fsummary>Information about active processes and ports.</fsummary>
<desc><marker id="statistics_total_active_tasks"></marker>
- <p>
- Returns the total amount of active processes and ports in
- the system. That is, the number of processes and ports that
- are ready to run, or are currently running. The information
- is <em>not</em> gathered atomically. That is, the result
- is not necessarily a consistent snapshot of the state, but
- instead quite efficiently gathered. See also,
- <seealso marker="#statistics_active_tasks"><c>statistics(active_tasks)</c></seealso>,
- <seealso marker="#statistics_run_queue_lengths"><c>statistics(run_queue_lengths)</c></seealso>, and
- <seealso marker="#statistics_total_run_queue_lengths"><c>statistics(total_run_queue_lengths)</c></seealso>.
- </p>
+ <p>Returns the total amount of active processes and ports in
+ the system. That is, the number of processes and ports that
+ are ready to run, or are currently running. The information
+ is <em>not</em> gathered atomically. That is, the result
+ is not necessarily a consistent snapshot of the state, but
+ instead quite efficiently gathered.</p>
+ <p>See also
+ <seealso marker="#statistics_active_tasks">
+ <c>statistics(active_tasks)</c></seealso>,
+ <seealso marker="#statistics_run_queue_lengths">
+ <c>statistics(run_queue_lengths)</c></seealso>, and
+ <seealso marker="#statistics_total_run_queue_lengths">
+ <c>statistics(total_run_queue_lengths)</c></seealso>.</p>
</desc>
</func>
<func>
- <name name="statistics" arity="1" clause_i="12"/>
+ <name name="statistics" arity="1" clause_i="13"/>
<fsummary>Information about the run-queue lengths.</fsummary>
<desc><marker id="statistics_total_run_queue_lengths"></marker>
- <p>
- Returns the total length of the run-queues. That is, the number
+ <p>Returns the total length of the run queues. That is, the number
of processes and ports that are ready to run on all available
- run-queues. The information is <em>not</em> gathered atomically.
- That is, the result is not necessarily a consistent snapshot of
- the state, but much more efficiently gathered compared to
- <seealso marker="#statistics_run_queue"><c>statistics(run_queue)</c></seealso>.
- See also,
- <seealso marker="#statistics_run_queue_lengths"><c>statistics(run_queue_lengths)</c></seealso>,
- <seealso marker="#statistics_total_active_tasks"><c>statistics(total_active_tasks)</c></seealso>, and
- <seealso marker="#statistics_active_tasks"><c>statistics(active_tasks)</c></seealso>.
- </p>
+ run queues. The information is <em>not</em> gathered atomically.
+ That is, the result is not necessarily a consistent snapshot of
+ the state, but much more efficiently gathered compared to
+ <seealso marker="#statistics_run_queue">
+ <c>statistics(run_queue)</c></seealso>.</p>
+ <p>See also <seealso marker="#statistics_run_queue_lengths">
+ <c>statistics(run_queue_lengths)</c></seealso>,
+ <seealso marker="#statistics_total_active_tasks">
+ <c>statistics(total_active_tasks)</c></seealso>, and
+ <seealso marker="#statistics_active_tasks">
+ <c>statistics(active_tasks)</c></seealso>.</p>
</desc>
</func>
<func>
- <name name="statistics" arity="1" clause_i="13"/>
+ <name name="statistics" arity="1" clause_i="14"/>
<fsummary>Information about wall clock.</fsummary>
<desc>
<p>Returns information about wall clock. <c>wall_clock</c> can
@@ -5943,64 +6494,80 @@ ok
</func>
<func>
+ <name name="suspend_process" arity="1"/>
+ <fsummary>Suspend a process.</fsummary>
+ <desc>
+ <p>Suspends the process identified by
+ <c><anno>Suspendee</anno></c>. The same as calling
+ <seealso marker="#suspend_process/2">
+ <c>erlang:suspend_process(<anno>Suspendee</anno>,
+ [])</c></seealso>.</p>
+ <warning>
+ <p>This BIF is intended for debugging only.</p>
+ </warning>
+ </desc>
+ </func>
+
+ <func>
<name name="suspend_process" arity="2"/>
- <fsummary>Suspends a process.</fsummary>
+ <fsummary>Suspend a process.</fsummary>
<desc>
<p>Increases the suspend count on the process identified by
- <c><anno>Suspendee</anno></c> and puts it in the suspended
- state if it is not
- already in that state. A suspended process will not be
- scheduled for execution until the process has been resumed.</p>
- <p>A process can be suspended by multiple processes and can
- be suspended multiple times by a single process. A suspended
- process does not leave the suspended state until its suspend
- count reaches zero. The suspend count of
- <c><anno>Suspendee</anno></c> is decreased when
- <seealso marker="#resume_process/1">erlang:resume_process(<anno>Suspendee</anno>)</seealso>
- is called by the same process that called
- <c>erlang:suspend_process(<anno>Suspendee</anno>)</c>.
- All increased suspend
- counts on other processes acquired by a process are automatically
- decreased when the process terminates.</p>
- <p>The options (<c><anno>Opt</anno></c>s) are as follows:</p>
+ <c><anno>Suspendee</anno></c> and puts it in the suspended
+ state if it is not
+ already in that state. A suspended process is not
+ scheduled for execution until the process has been resumed.</p>
+ <p>A process can be suspended by multiple processes and can
+ be suspended multiple times by a single process. A suspended
+ process does not leave the suspended state until its suspend
+ count reaches zero. The suspend count of
+ <c><anno>Suspendee</anno></c> is decreased when
+ <seealso marker="#resume_process/1">
+ <c>erlang:resume_process(<anno>Suspendee</anno>)</c></seealso>
+ is called by the same process that called
+ <c>erlang:suspend_process(<anno>Suspendee</anno>)</c>.
+ All increased suspend
+ counts on other processes acquired by a process are automatically
+ decreased when the process terminates.</p>
+ <p>Options (<c><anno>Opt</anno></c>s):</p>
<taglist>
<tag><c>asynchronous</c></tag>
<item>
- A suspend request is sent to the process identified by
- <c><anno>Suspendee</anno></c>. <c><anno>Suspendee</anno></c>
- eventually suspends
- unless it is resumed before it could suspend. The caller
- of <c>erlang:suspend_process/2</c> returns immediately,
- regardless of whether <c><anno>Suspendee</anno></c> has
- suspended yet or not. The point in time when
- <c><anno>Suspendee</anno></c> suspends cannot be deduced
- from other events in the system. It is only guaranteed that
- <c><anno>Suspendee</anno></c> <em>eventually</em> suspends
- (unless it
- is resumed). If option <c>asynchronous</c> has <em>not</em>
- been passed, the caller of <c>erlang:suspend_process/2</c> is
- blocked until <c><anno>Suspendee</anno></c> has suspended.
- </item>
+ <p>A suspend request is sent to the process identified by
+ <c><anno>Suspendee</anno></c>. <c><anno>Suspendee</anno></c>
+ eventually suspends
+ unless it is resumed before it could suspend. The caller
+ of <c>erlang:suspend_process/2</c> returns immediately,
+ regardless of whether <c><anno>Suspendee</anno></c> has
+ suspended yet or not. The point in time when
+ <c><anno>Suspendee</anno></c> suspends cannot be deduced
+ from other events in the system. It is only guaranteed that
+ <c><anno>Suspendee</anno></c> <em>eventually</em> suspends
+ (unless it
+ is resumed). If option <c>asynchronous</c> has <em>not</em>
+ been passed, the caller of <c>erlang:suspend_process/2</c> is
+ blocked until <c><anno>Suspendee</anno></c> has suspended.</p>
+ </item>
<tag><c>unless_suspending</c></tag>
<item>
- The process identified by <c><anno>Suspendee</anno></c> is
- suspended unless the calling process already is suspending
- <c><anno>Suspendee</anno></c>.
- If <c>unless_suspending</c> is combined
- with option <c>asynchronous</c>, a suspend request is
- sent unless the calling process already is suspending
- <c><anno>Suspendee</anno></c> or if a suspend request
- already has been sent and is in transit. If the calling
- process already is suspending <c><anno>Suspendee</anno></c>,
- or if combined with option <c>asynchronous</c>
- and a send request already is in transit,
- <c>false</c> is returned and the suspend count on
- <c><anno>Suspendee</anno></c> remains unchanged.
- </item>
+ <p>The process identified by <c><anno>Suspendee</anno></c> is
+ suspended unless the calling process already is suspending
+ <c><anno>Suspendee</anno></c>.
+ If <c>unless_suspending</c> is combined
+ with option <c>asynchronous</c>, a suspend request is
+ sent unless the calling process already is suspending
+ <c><anno>Suspendee</anno></c> or if a suspend request
+ already has been sent and is in transit. If the calling
+ process already is suspending <c><anno>Suspendee</anno></c>,
+ or if combined with option <c>asynchronous</c>
+ and a send request already is in transit,
+ <c>false</c> is returned and the suspend count on
+ <c><anno>Suspendee</anno></c> remains unchanged.</p>
+ </item>
</taglist>
- <p>If the suspend count on the process identified by
- <c><anno>Suspendee</anno></c> is increased, <c>true</c>
- is returned, otherwise <c>false</c>.</p>
+ <p>If the suspend count on the process identified by
+ <c><anno>Suspendee</anno></c> is increased, <c>true</c>
+ is returned, otherwise <c>false</c>.</p>
<warning>
<p>This BIF is intended for debugging only.</p>
</warning>
@@ -6008,68 +6575,56 @@ ok
<taglist>
<tag><c>badarg</c></tag>
<item>
- If <c><anno>Suspendee</anno></c> is not a process identifier.
- </item>
+ If <c><anno>Suspendee</anno></c> is not a process identifier.
+ </item>
<tag><c>badarg</c></tag>
<item>
- If the process identified by <c><anno>Suspendee</anno></c>
- is the same process
- as the process calling <c>erlang:suspend_process/2</c>.
- </item>
+ If the process identified by <c><anno>Suspendee</anno></c>
+ is the same process
+ as the process calling <c>erlang:suspend_process/2</c>.
+ </item>
<tag><c>badarg</c></tag>
<item>
- If the process identified by <c><anno>Suspendee</anno></c>
- is not alive.
- </item>
+ If the process identified by <c><anno>Suspendee</anno></c>
+ is not alive.
+ </item>
<tag><c>badarg</c></tag>
<item>
- If the process identified by <c><anno>Suspendee</anno></c>
- resides on another node.
- </item>
+ If the process identified by <c><anno>Suspendee</anno></c>
+ resides on another node.
+ </item>
<tag><c>badarg</c></tag>
<item>
- If <c><anno>OptList</anno></c> is not a proper list of valid
- <c><anno>Opt</anno></c>s.
- </item>
+ If <c><anno>OptList</anno></c> is not a proper list of valid
+ <c><anno>Opt</anno></c>s.
+ </item>
<tag><c>system_limit</c></tag>
<item>
- If the process identified by <c><anno>Suspendee</anno></c>
- has been suspended
- more times by the calling process than can be represented by the
- currently used internal data structures. The system limit is
- higher than 2,000,000,000 suspends and will never be lower.
- </item>
+ If the process identified by <c><anno>Suspendee</anno></c>
+ has been suspended
+ more times by the calling process than can be represented by the
+ currently used internal data structures. The system limit is
+ &gt; 2,000,000,000 suspends and will never be lower.
+ </item>
</taglist>
</desc>
</func>
<func>
- <name name="suspend_process" arity="1"/>
- <fsummary>Suspends a process.</fsummary>
- <desc>
- <p>Suspends the process identified by
- <c><anno>Suspendee</anno></c>. The same as calling
- <seealso marker="#suspend_process/2">erlang:suspend_process(<anno>Suspendee</anno>,
- [])</seealso>.</p>
- <warning>
- <p>This BIF is intended for debugging only.</p>
- </warning>
- </desc>
- </func>
-
- <func>
<name name="system_flag" arity="2" clause_i="1"/>
- <fsummary>Sets system flag <c>backtrace_depth</c>.</fsummary>
+ <fsummary>Set system flag <c>backtrace_depth</c>.</fsummary>
<desc>
<p>Sets the maximum depth of call stack back-traces in the
- exit reason element of <c>'EXIT'</c> tuples.</p>
+ exit reason element of <c>'EXIT'</c> tuples. The flag
+ also limits the stacktrace depth returned by <c>process_info</c>
+ item <c>current_stacktrace.</c></p>
<p>Returns the old value of the flag.</p>
</desc>
</func>
<func>
<name name="system_flag" arity="2" clause_i="2"/>
- <fsummary>Sets system flag <c>cpu_topology</c>.</fsummary>
+ <fsummary>Set system flag <c>cpu_topology</c>.</fsummary>
<type name="cpu_topology"/>
<type name="level_entry"/>
<type name="level_tag"/>
@@ -6078,52 +6633,52 @@ ok
<desc>
<warning>
<p><marker id="system_flag_cpu_topology"></marker>
- This argument is <em>deprecated</em> and scheduled for
- removal in <c>ERTS</c> 5.10/OTP R16. Instead of using this
- argument, use command-line argument
- <seealso marker="erts:erl#+sct">+sct</seealso> in
- <c>erl(1)</c>.</p>
+ <em>This argument is deprecated.</em>
+ Instead of using this argument, use command-line argument
+ <seealso marker="erts:erl#+sct"><c>+sct</c></seealso> in
+ <c>erl(1)</c>.</p>
<p>When this argument is removed, a final CPU topology
- to use is determined at emulator boot time.</p>
+ to use is determined at emulator boot time.</p>
</warning>
<p>Sets the user-defined <c><anno>CpuTopology</anno></c>.
- The user-defined
- CPU topology overrides any automatically detected
- CPU topology. By passing <c>undefined</c> as
- <c><anno>CpuTopology</anno></c>,
- the system reverts to the CPU topology automatically
- detected. The returned value equals the value returned
- from <c>erlang:system_info(cpu_topology)</c> before the
- change was made.</p>
+ The user-defined
+ CPU topology overrides any automatically detected
+ CPU topology. By passing <c>undefined</c> as
+ <c><anno>CpuTopology</anno></c>,
+ the system reverts to the CPU topology automatically
+ detected. The returned value equals the value returned
+ from <c>erlang:system_info(cpu_topology)</c> before the
+ change was made.</p>
<p>Returns the old value of the flag.</p>
<p>The CPU topology is used when binding schedulers to logical
- processors. If schedulers are already bound when the CPU
- topology is changed, the schedulers are sent a request
- to rebind according to the new CPU topology.</p>
+ processors. If schedulers are already bound when the CPU
+ topology is changed, the schedulers are sent a request
+ to rebind according to the new CPU topology.</p>
<p>The user-defined CPU topology can also be set by passing
- command-line argument
- <seealso marker="erts:erl#+sct">+sct</seealso> to
- <c>erl(1)</c>.</p>
+ command-line argument
+ <seealso marker="erts:erl#+sct"><c>+sct</c></seealso> to
+ <c>erl(1)</c>.</p>
<p>For information on type <c><anno>CpuTopology</anno></c>
- and more, see
- <seealso marker="#system_info_cpu_topology">erlang:system_info(cpu_topology)</seealso>
- as well as the command-line flags
- <seealso marker="erts:erl#+sct">+sct</seealso> and
- <seealso marker="erts:erl#+sbt">+sbt</seealso> in
- <c>erl(1)</c>.</p>
+ and more, see
+ <seealso marker="#system_info_cpu_topology">
+ <c>erlang:system_info(cpu_topology)</c></seealso>
+ as well as command-line flags
+ <seealso marker="erts:erl#+sct"><c>+sct</c></seealso> and
+ <seealso marker="erts:erl#+sbt"><c>+sbt</c></seealso> in
+ <c>erl(1)</c>.</p>
</desc>
</func>
<func>
<name name="system_flag" arity="2" clause_i="3"/>
- <fsummary>Sets <c>system_flag_dirty_cpu_schedulers_online</c>.</fsummary>
+ <fsummary>Set system_flag_dirty_cpu_schedulers_online.</fsummary>
<desc>
<p><marker id="system_flag_dirty_cpu_schedulers_online"></marker>
- Sets the number of dirty CPU schedulers online. Range is
- <![CDATA[1 <= DirtyCPUSchedulersOnline <= N]]>, where <c>N</c>
- is the smallest of the return values of
- <c>erlang:system_info(dirty_cpu_schedulers)</c> and
- <c>erlang:system_info(schedulers_online)</c>.</p>
+ Sets the number of dirty CPU schedulers online. Range is
+ <c><![CDATA[1 <= DirtyCPUSchedulersOnline <= N]]></c>, where <c>N</c>
+ is the smallest of the return values of
+ <c>erlang:system_info(dirty_cpu_schedulers)</c> and
+ <c>erlang:system_info(schedulers_online)</c>.</p>
<p>Returns the old value of the flag.</p>
<p>The number of dirty CPU schedulers online can change if the
number of schedulers online changes. For example, if 12
@@ -6134,20 +6689,22 @@ ok
down to 3. Similarly, the number of dirty CPU schedulers
online increases proportionally to increases in the number of
schedulers online.</p>
- <note><p>The dirty schedulers functionality is experimental.
- Enable support for dirty schedulers when building OTP to
- try out the functionality.</p>
+ <note>
+ <p>The dirty schedulers functionality is experimental.
+ Enable support for dirty schedulers when building OTP to
+ try out the functionality.</p>
</note>
<p>For more information, see
- <seealso marker="#system_info_dirty_cpu_schedulers">erlang:system_info(dirty_cpu_schedulers)</seealso>
- and
- <seealso marker="#system_info_dirty_cpu_schedulers_online">erlang:system_info(dirty_cpu_schedulers_online)</seealso>.</p>
+ <seealso marker="#system_info_dirty_cpu_schedulers">
+ <c>erlang:system_info(dirty_cpu_schedulers)</c></seealso> and
+ <seealso marker="#system_info_dirty_cpu_schedulers_online">
+ <c>erlang:system_info(dirty_cpu_schedulers_online)</c></seealso>.</p>
</desc>
</func>
<func>
<name name="system_flag" arity="2" clause_i="4"/>
- <fsummary>Sets system flag <c>fullsweep_after</c>.</fsummary>
+ <fsummary>Set system flag fullsweep_after.</fsummary>
<desc>
<p>Sets system flag <c>fullsweep_after</c>.
<c><anno>Number</anno></c> is a non-negative integer indicating
@@ -6166,266 +6723,328 @@ ok
<func>
<name name="system_flag" arity="2" clause_i="5"/>
- <fsummary>Sets system flag <c>min_heap_size</c>.</fsummary>
+ <fsummary>Set system flag microstate_accounting.</fsummary>
+ <desc>
+ <p><marker id="system_flag_microstate_accounting"></marker>
+ Turns on/off microstate accounting measurements. When passing reset,
+ all counters are reset to 0.</p>
+ <p>For more information see
+ <seealso marker="#statistics_microstate_accounting">
+ <c>statistics(microstate_accounting)</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="system_flag" arity="2" clause_i="6"/>
+ <fsummary>Set system flag min_heap_size.</fsummary>
<desc>
<p>Sets the default minimum heap size for processes. The size
- is given in words. The new <c>min_heap_size</c> effects
+ is specified in words. The new <c>min_heap_size</c> effects
only processes spawned after the change of
<c>min_heap_size</c> has been made. <c>min_heap_size</c>
can be set for individual processes by using
- <seealso marker="#spawn_opt/4">spawn_opt/N</seealso> or
- <seealso marker="#process_flag/2">process_flag/2</seealso>.</p>
+ <seealso marker="#spawn_opt/4"><c>spawn_opt/4</c></seealso> or
+ <seealso marker="#process_flag/2"><c>process_flag/2</c></seealso>.</p>
<p>Returns the old value of the flag.</p>
</desc>
</func>
<func>
- <name name="system_flag" arity="2" clause_i="6"/>
- <fsummary>Sets system flag <c>min_bin_vheap_size</c>.</fsummary>
+ <name name="system_flag" arity="2" clause_i="7"/>
+ <fsummary>Set system flag min_bin_vheap_size.</fsummary>
<desc>
<p>Sets the default minimum binary virtual heap size for
- processes. The size is given in words.
+ processes. The size is specified in words.
The new <c>min_bin_vhheap_size</c> effects only
processes spawned after the change of
- <c>min_bin_vhheap_size</c> has been made.
+ <c>min_bin_vheap_size</c> has been made.
<c>min_bin_vheap_size</c> can be set for individual
processes by using
- <seealso marker="#spawn_opt/4">spawn_opt/N</seealso> or
- <seealso marker="#process_flag/2">process_flag/2</seealso>.</p>
+ <seealso marker="#spawn_opt/4"><c>spawn_opt/2,3,4</c></seealso> or
+ <seealso marker="#process_flag/2"><c>process_flag/2</c></seealso>.</p>
<p>Returns the old value of the flag.</p>
</desc>
</func>
<func>
- <name name="system_flag" arity="2" clause_i="7"/>
- <fsummary>Sets system flag <c>multi_scheduling</c>.</fsummary>
+ <name name="system_flag" arity="2" clause_i="8"/>
+ <fsummary>Set system flag max_heap_size.</fsummary>
+ <type name="max_heap_size"/>
+ <desc>
+ <marker id="system_flag_max_heap_size"></marker>
+ <p>
+ Sets the default maximum heap size settings for processes.
+ The size is specified in words. The new <c>max_heap_size</c>
+ effects only processes spawned efter the change has been made.
+ <c>max_heap_size</c> can be set for individual processes using
+ <seealso marker="#spawn_opt/4"><c>spawn_opt/2,3,4</c></seealso> or
+ <seealso marker="#process_flag_message_queue_data">
+ <c>process_flag/2</c></seealso>.</p>
+ <p>Returns the old value of the flag.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="system_flag" arity="2" clause_i="9"/>
+ <fsummary>Set system flag multi_scheduling.</fsummary>
<desc>
<p><marker id="system_flag_multi_scheduling"></marker>
If multi-scheduling is enabled, more than one scheduler
thread is used by the emulator. Multi-scheduling can be
- blocked. When multi-scheduling is blocked, only
- one scheduler thread schedules Erlang processes.</p>
+ blocked in two different ways. Either all schedulers but
+ one is blocked, or all <em>normal</em> schedulers but
+ one is blocked. When only normal schedulers are blocked,
+ dirty schedulers are free to continue to schedule
+ processes.</p>
<p>If <c><anno>BlockState</anno> =:= block</c>, multi-scheduling is
- blocked. If <c><anno>BlockState</anno> =:= unblock</c> and no one
+ blocked. That is, one and only one scheduler thread will
+ execute. If <c><anno>BlockState</anno> =:= unblock</c> and no one
else blocks multi-scheduling, and this process has
blocked only once, multi-scheduling is unblocked.</p>
- <p>One process can block multi-scheduling multiple times.
- If a process has blocked multiple times, it must
- unblock exactly as many times as it has blocked before it
- has released its multi-scheduling block. If a process that
- has blocked multi-scheduling exits, it releases its
- blocking of multi-scheduling.</p>
+ <p>If <c><anno>BlockState</anno> =:= block_normal</c>, normal
+ multi-scheduling is blocked. That is, only one normal scheduler
+ thread will execute, but multiple dirty schedulers can execute.
+ If <c><anno>BlockState</anno> =:= unblock_normal</c> and no one
+ else blocks normal multi-scheduling, and this process has
+ blocked only once, normal multi-scheduling is unblocked.</p>
+ <p>One process can block multi-scheduling and normal
+ multi-scheduling multiple times. If a process has blocked
+ multiple times, it must unblock exactly as many times as it
+ has blocked before it has released its multi-scheduling
+ block. If a process that has blocked multi-scheduling or normal
+ multi-scheduling exits, it automatically releases its blocking
+ of multi-scheduling and normal multi-scheduling.</p>
<p>The return values are <c>disabled</c>, <c>blocked</c>,
- or <c>enabled</c>. The returned value describes the
- state just after the call to
+ <c>blocked_normal</c>, or <c>enabled</c>. The returned value
+ describes the state just after the call to
<c>erlang:system_flag(multi_scheduling, <anno>BlockState</anno>)</c>
has been made. For information about the return values, see
- <seealso marker="#system_info_multi_scheduling">erlang:system_info(multi_scheduling)</seealso>.</p>
- <note><p>Blocking of multi-scheduling is normally not needed.
- If you feel that you need to block multi-scheduling,
- consider it a few more times again. Blocking multi-scheduling
- is only to be used as a last resort, as it is most likely
- a <em>very inefficient</em> way to solve the problem.</p>
+ <seealso marker="#system_info_multi_scheduling">
+ <c>erlang:system_info(multi_scheduling)</c></seealso>.</p>
+ <note><p>Blocking of multi-scheduling and normal multi-scheduling
+ is normally not needed. If you feel that you need to use these
+ features, consider it a few more times again. Blocking
+ multi-scheduling is only to be used as a last resort, as it is
+ most likely a <em>very inefficient</em> way to solve the problem.</p>
</note>
<p>See also
- <seealso marker="#system_info_multi_scheduling">erlang:system_info(multi_scheduling)</seealso>,
- <seealso marker="#system_info_multi_scheduling_blockers">erlang:system_info(multi_scheduling_blockers)</seealso>, and
- <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>.</p>
+ <seealso marker="#system_info_multi_scheduling">
+ <c>erlang:system_info(multi_scheduling)</c></seealso>,
+ <seealso marker="#system_info_normal_multi_scheduling_blockers">
+ <c>erlang:system_info(normal_multi_scheduling_blockers)</c></seealso>,
+ <seealso marker="#system_info_multi_scheduling_blockers">
+ <c>erlang:system_info(multi_scheduling_blockers)</c></seealso>, and
+ <seealso marker="#system_info_schedulers">
+ <c>erlang:system_info(schedulers)</c></seealso>.</p>
</desc>
</func>
<func>
- <name name="system_flag" arity="2" clause_i="8"/>
- <fsummary>Sets system flag <c>scheduler_bind_type</c>.</fsummary>
+ <name name="system_flag" arity="2" clause_i="10"/>
+ <fsummary>Set system flag scheduler_bind_type.</fsummary>
<type name="scheduler_bind_type"/>
<desc>
<warning>
<p><marker id="system_flag_scheduler_bind_type"></marker>
- This argument is <em>deprecated</em> and scheduled for
- removal in <c>ERTS</c> 5.10/OTP R16. Instead of using this
- argument, use command-line argument
- <seealso marker="erts:erl#+sbt">+sbt</seealso> in <c>erl(1)</c>.
- When this argument is removed, a final scheduler bind
- type to use is determined at emulator boot time.</p>
+ <em>This argument is deprecated.</em>
+ Instead of using this argument, use command-line argument
+ <seealso marker="erts:erl#+sbt"><c>+sbt</c></seealso> in
+ <c>erl(1)</c>. When this argument is removed, a final scheduler bind
+ type to use is determined at emulator boot time.</p>
</warning>
<p>Controls if and how schedulers are bound to logical
- processors.</p>
+ processors.</p>
<p>When <c>erlang:system_flag(scheduler_bind_type, <anno>How</anno>)</c>
- is called, an asynchronous signal is sent to all schedulers
- online, causing them to try to bind or unbind as requested.</p>
+ is called, an asynchronous signal is sent to all schedulers
+ online, causing them to try to bind or unbind as requested.</p>
<note><p>If a scheduler fails to bind, this is often silently
- ignored, as it is not always possible to verify valid
- logical processor identifiers. If an error is reported,
- it is reported to <c>error_logger</c>. To verify that the
- schedulers have bound as requested, call
- <seealso marker="#system_info_scheduler_bindings">erlang:system_info(scheduler_bindings)</seealso>.</p>
+ ignored, as it is not always possible to verify valid
+ logical processor identifiers. If an error is reported,
+ it is reported to <c>error_logger</c>. To verify that the
+ schedulers have bound as requested, call
+ <seealso marker="#system_info_scheduler_bindings">
+ <c>erlang:system_info(scheduler_bindings)</c></seealso>.</p>
</note>
<p>Schedulers can be bound on newer Linux,
- Solaris, FreeBSD, and Windows systems, but more systems will be
- supported in future releases.</p>
+ Solaris, FreeBSD, and Windows systems, but more systems will be
+ supported in future releases.</p>
<p>In order for the runtime system to be able to bind schedulers,
- the CPU topology must be known. If the runtime system fails
- to detect the CPU topology automatically, it can be defined.
- For more information on how to define the CPU topology, see
- command-line flag <seealso marker="erts:erl#+sct">+sct</seealso>
- in <c>erl(1)</c>.</p>
+ the CPU topology must be known. If the runtime system fails
+ to detect the CPU topology automatically, it can be defined.
+ For more information on how to define the CPU topology, see
+ command-line flag <seealso marker="erts:erl#+sct">
+ <c>+sct</c></seealso> in <c>erl(1)</c>.</p>
<p>The runtime system does by default <em>not</em> bind schedulers
- to logical processors.</p>
+ to logical processors.</p>
<note><p>If the Erlang runtime system is the only OS
- process binding threads to logical processors, this
- improves the performance of the runtime system. However,
- if other OS processes (for example, another Erlang
- runtime system) also bind threads to logical processors,
- there can be a performance penalty instead. Sometimes this
- performance penalty can be severe. If so, it is recommended
- to not bind the schedulers.</p>
+ process binding threads to logical processors, this
+ improves the performance of the runtime system. However,
+ if other OS processes (for example, another Erlang
+ runtime system) also bind threads to logical processors,
+ there can be a performance penalty instead. Sometimes this
+ performance penalty can be severe. If so, it is recommended
+ to not bind the schedulers.</p>
</note>
<p>Schedulers can be bound in different ways. Argument
- <c><anno>How</anno></c> determines how schedulers are
- bound and can be any of the following:</p>
+ <c><anno>How</anno></c> determines how schedulers are
+ bound and can be any of the following:</p>
<taglist>
<tag><c>unbound</c></tag>
- <item><p>Same as command-line argument
- <seealso marker="erts:erl#+sbt">+sbt u</seealso> in <c>erl(1)</c>.
- </p></item>
+ <item>Same as command-line argument
+ <seealso marker="erts:erl#+sbt"><c>+sbt u</c></seealso> in
+ <c>erl(1)</c>.
+ </item>
<tag><c>no_spread</c></tag>
- <item><p>Same as command-line argument
- <seealso marker="erts:erl#+sbt">+sbt ns</seealso> in <c>erl(1)</c>.
- </p></item>
+ <item>Same as command-line argument
+ <seealso marker="erts:erl#+sbt"><c>+sbt ns</c></seealso>
+ in <c>erl(1)</c>.
+ </item>
<tag><c>thread_spread</c></tag>
- <item><p>Same as command-line argument
- <seealso marker="erts:erl#+sbt">+sbt ts</seealso> in <c>erl(1)</c>.
- </p></item>
+ <item>Same as command-line argument
+ <seealso marker="erts:erl#+sbt"><c>+sbt ts</c></seealso>
+ in <c>erl(1)</c>.
+ </item>
<tag><c>processor_spread</c></tag>
- <item><p>Same as command-line argument
- <seealso marker="erts:erl#+sbt">+sbt ps</seealso> in <c>erl(1)</c>.
- </p></item>
+ <item>Same as command-line argument
+ <seealso marker="erts:erl#+sbt"><c>+sbt ps</c></seealso>
+ in <c>erl(1)</c>.
+ </item>
<tag><c>spread</c></tag>
- <item><p>Same as command-line argument
- <seealso marker="erts:erl#+sbt">+sbt s</seealso> in <c>erl(1)</c>.
- </p></item>
+ <item>Same as command-line argument
+ <seealso marker="erts:erl#+sbt"><c>+sbt s</c></seealso>
+ in <c>erl(1)</c>.
+ </item>
<tag><c>no_node_thread_spread</c></tag>
- <item><p>Same as command-line argument
- <seealso marker="erts:erl#+sbt">+sbt nnts</seealso> in <c>erl(1)</c>.
- </p></item>
+ <item>Same as command-line argument
+ <seealso marker="erts:erl#+sbt"><c>+sbt nnts</c></seealso>
+ in <c>erl(1)</c>.
+ </item>
<tag><c>no_node_processor_spread</c></tag>
- <item><p>Same as command-line argument
- <seealso marker="erts:erl#+sbt">+sbt nnps</seealso> in <c>erl(1)</c>.
- </p></item>
+ <item>Same as command-line argument
+ <seealso marker="erts:erl#+sbt"><c>+sbt nnps</c></seealso>
+ in <c>erl(1)</c>.
+ </item>
<tag><c>thread_no_node_processor_spread</c></tag>
- <item><p>Same as command-line argument
- <seealso marker="erts:erl#+sbt">+sbt tnnps</seealso> in <c>erl(1)</c>.
- </p></item>
+ <item>Same as command-line argument
+ <seealso marker="erts:erl#+sbt"><c>+sbt tnnps</c></seealso>
+ in <c>erl(1)</c>.
+ </item>
<tag><c>default_bind</c></tag>
- <item><p>Same as command-line argument
- <seealso marker="erts:erl#+sbt">+sbt db</seealso> in <c>erl(1)</c>.
- </p></item>
+ <item>Same as command-line argument
+ <seealso marker="erts:erl#+sbt"><c>+sbt db</c></seealso>
+ in <c>erl(1)</c>.
+ </item>
</taglist>
<p>The returned value equals <c><anno>How</anno></c> before flag
<c>scheduler_bind_type</c> was changed.</p>
<p>Failures:</p>
<taglist>
<tag><c>notsup</c></tag>
- <item>
- <p>If binding of schedulers is not supported.</p>
+ <item>If binding of schedulers is not supported.
</item>
<tag><c>badarg</c></tag>
- <item>
- <p>If <c><anno>How</anno></c> is not one of the documented
- alternatives.</p>
+ <item>If <c><anno>How</anno></c> is not one of the documented
+ alternatives.
</item>
<tag><c>badarg</c></tag>
- <item>
- <p>If CPU topology information is unavailable.</p>
+ <item>If CPU topology information is unavailable.
</item>
</taglist>
- <p>The scheduler bind type can also be set by passing
- command-line argument
- <seealso marker="erts:erl#+sbt">+sbt</seealso> to <c>erl(1)</c>.</p>
+ <p>The scheduler bind type can also be set by passing command-line
+ argument <seealso marker="erts:erl#+sbt">
+ <c>+sbt</c></seealso> to <c>erl(1)</c>.</p>
<p>For more information, see
- <seealso marker="#system_info_scheduler_bind_type">erlang:system_info(scheduler_bind_type)</seealso>,
- <seealso marker="#system_info_scheduler_bindings">erlang:system_info(scheduler_bindings)</seealso>,
- as well as command-line flags
- <seealso marker="erts:erl#+sbt">+sbt</seealso>
- and <seealso marker="erts:erl#+sct">+sct</seealso>
- in <c>erl(1)</c>.</p>
+ <seealso marker="#system_info_scheduler_bind_type">
+ <c>erlang:system_info(scheduler_bind_type)</c></seealso>,
+ <seealso marker="#system_info_scheduler_bindings">
+ <c>erlang:system_info(scheduler_bindings)</c></seealso>,
+ as well as command-line flags
+ <seealso marker="erts:erl#+sbt"><c>+sbt</c></seealso>
+ and <seealso marker="erts:erl#+sct"><c>+sct</c></seealso>
+ in <c>erl(1)</c>.</p>
</desc>
</func>
<func>
- <name name="system_flag" arity="2" clause_i="9"/>
- <fsummary>Sets system flag <c>scheduler_wall_time</c>.</fsummary>
- <desc><p><marker id="system_flag_scheduler_wall_time"></marker>
- Turns on or off scheduler wall time measurements.</p>
- <p>For more information, see
- <seealso marker="#statistics_scheduler_wall_time">erlang:statistics(scheduler_wall_time)</seealso>.</p>
+ <name name="system_flag" arity="2" clause_i="11"/>
+ <fsummary>Set system flag scheduler_wall_time.</fsummary>
+ <desc>
+ <p><marker id="system_flag_scheduler_wall_time"></marker>
+ Turns on or off scheduler wall time measurements.</p>
+ <p>For more information, see
+ <seealso marker="#statistics_scheduler_wall_time">
+ <c>statistics(scheduler_wall_time)</c></seealso>.</p>
</desc>
</func>
<func>
- <name name="system_flag" arity="2" clause_i="10"/>
- <fsummary>Sets system flag <c>schedulers_online</c>.</fsummary>
+ <name name="system_flag" arity="2" clause_i="12"/>
+ <fsummary>Set system flag schedulers_online.</fsummary>
<desc>
<p><marker id="system_flag_schedulers_online"></marker>
- Sets the number of schedulers online. Range is
- <![CDATA[1 <= SchedulersOnline <= erlang:system_info(schedulers)]]>.</p>
+ Sets the number of schedulers online. Range is
+ <c><![CDATA[1 <= SchedulersOnline <=
+ erlang:system_info(schedulers)]]></c>.</p>
<p>Returns the old value of the flag.</p>
<p>If the emulator was built with support for
- <seealso marker="#system_flag_dirty_cpu_schedulers_online">dirty schedulers</seealso>,
- changing the number of schedulers online can also change the
- number of dirty CPU schedulers online. For example, if 12
- schedulers and 6 dirty CPU schedulers are online, and
- <c>system_flag/2</c> is used to set the number of schedulers
- online to 6, then the number of dirty CPU schedulers online
- is automatically decreased by half as well, down to 3.
- Similarly, the number of dirty CPU schedulers online increases
- proportionally to increases in the number of schedulers online.</p>
+ <seealso marker="#system_flag_dirty_cpu_schedulers_online">
+ dirty schedulers</seealso>,
+ changing the number of schedulers online can also change the
+ number of dirty CPU schedulers online. For example, if 12
+ schedulers and 6 dirty CPU schedulers are online, and
+ <c>system_flag/2</c> is used to set the number of schedulers
+ online to 6, then the number of dirty CPU schedulers online
+ is automatically decreased by half as well, down to 3.
+ Similarly, the number of dirty CPU schedulers online increases
+ proportionally to increases in the number of schedulers online.</p>
<p>For more information, see
- <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>
- and
- <seealso marker="#system_info_schedulers_online">erlang:system_info(schedulers_online)</seealso>.</p>
+ <seealso marker="#system_info_schedulers">
+ <c>erlang:system_info(schedulers)</c></seealso> and
+ <seealso marker="#system_info_schedulers_online">
+ <c>erlang:system_info(schedulers_online)</c></seealso>.</p>
</desc>
</func>
<func>
- <name name="system_flag" arity="2" clause_i="11"/>
- <fsummary>Sets system flag <c>trace_control_word</c>.</fsummary>
+ <name name="system_flag" arity="2" clause_i="13"/>
+ <fsummary>Set system flag trace_control_word.</fsummary>
<desc>
<p>Sets the value of the node trace control word to
<c><anno>TCW</anno></c>, which is to be an unsigned integer.
- For more information, see the function
- <seealso marker="erts:match_spec#set_tcw">set_tcw</seealso>
- in Section "Match Specifications in Erlang" in the
+ For more information, see function
+ <seealso marker="erts:match_spec#set_tcw"><c>set_tcw</c></seealso>
+ in section "Match Specifications in Erlang" in the
User's Guide.</p>
<p>Returns the old value of the flag.</p>
</desc>
</func>
<func>
- <name name="system_flag" arity="2" clause_i="12"/>
- <fsummary>Finalize the Time Offset</fsummary>
+ <name name="system_flag" arity="2" clause_i="14"/>
+ <fsummary>Finalize the time offset.</fsummary>
<desc>
<p><marker id="system_flag_time_offset"></marker>
- Finalizes the <seealso marker="#time_offset/0">time offset</seealso>
- when <seealso marker="time_correction#Single_Time_Warp_Mode">single
- time warp mode</seealso> is used. If another time warp mode
- is used, the time offset state is left unchanged.</p>
- <p>Returns the old state identifier. That is:</p>
- <list>
- <item><p>If <c>preliminary</c> is returned, finalization was
- performed and the time offset is now final.</p></item>
-
- <item><p>If <c>final</c> is returned, the time offset was
- already in the final state. This either because another
- <c>erlang:system_flag(time_offset, finalize)</c> call, or
- because <seealso marker="time_correction#No_Time_Warp_Mode">no
- time warp mode</seealso> is used.</p></item>
-
- <item><p>If <c>volatile</c> is returned, the time offset
- cannot be finalized because
- <seealso marker="time_correction#Multi_Time_Warp_Mode">multi
- time warp mode</seealso> is used.</p></item>
- </list>
+ Finalizes the <seealso marker="#time_offset/0">time offset</seealso>
+ when <seealso marker="time_correction#Single_Time_Warp_Mode">single
+ time warp mode</seealso> is used. If another time warp mode
+ is used, the time offset state is left unchanged.</p>
+ <p>Returns the old state identifier, that is:</p>
+ <list>
+ <item><p>If <c>preliminary</c> is returned, finalization was
+ performed and the time offset is now final.</p>
+ </item>
+ <item><p>If <c>final</c> is returned, the time offset was
+ already in the final state. This either because another
+ <c>erlang:system_flag(time_offset, finalize)</c> call or
+ because <seealso marker="time_correction#No_Time_Warp_Mode">no
+ time warp mode</seealso> is used.</p>
+ </item>
+ <item><p>If <c>volatile</c> is returned, the time offset
+ cannot be finalized because
+ <seealso marker="time_correction#Multi_Time_Warp_Mode">multi-time
+ warp mode</seealso> is used.</p>
+ </item>
+ </list>
</desc>
</func>
@@ -6442,7 +7061,7 @@ ok
<type variable="Settings" name_i="2"/>
<type variable="Alloc" name_i="3"/>
<desc>
- <marker id="system_info_allocator_tags"></marker>
+ <marker id="system_info_allocator_tags"></marker>
<p>Returns various information about the allocators of the
current system (emulator) as specified by
<c><anno>Item</anno></c>:</p>
@@ -6461,17 +7080,18 @@ ok
<p><c>erlang:system_info(allocated_areas)</c> is intended
for debugging, and the content is highly
implementation-dependent. The content of the results
- therefore changes when needed without prior notice.</p>
+ therefore changes when needed without prior notice.</p>
<p>Notice that the sum of these values is <em>not</em>
the total amount of memory allocated by the emulator.
Some values are part of other values, and some memory
areas are not part of the result. For information about
the total amount of memory allocated by the emulator, see
- <seealso marker="#memory/0">erlang:memory/0,1</seealso>.</p>
+ <seealso marker="#memory/0">
+ <c>erlang:memory/0,1</c></seealso>.</p>
</item>
<tag><c>allocator</c></tag>
<item>
- <marker id="system_info_allocator"></marker>
+ <marker id="system_info_allocator"></marker>
<p>Returns <c>{<anno>Allocator</anno>, <anno>Version</anno>,
<anno>Features</anno>, <anno>Settings</anno></c>, where:</p>
<list type="bulleted">
@@ -6501,31 +7121,30 @@ ok
</item>
</list>
<p>See also "System Flags Effecting erts_alloc" in
- <seealso marker="erts:erts_alloc#flags">erts_alloc(3)</seealso>.</p>
+ <seealso marker="erts:erts_alloc#flags">
+ <c>erts_alloc(3)</c></seealso>.</p>
</item>
<tag><c>alloc_util_allocators</c></tag>
<item>
- <marker id="system_info_alloc_util_allocators"></marker>
- <p>Returns a list of the names of all allocators using
- the <c>ERTS</c> internal <c>alloc_util</c> framework
- as atoms. For more information, see Section
- <seealso marker="erts:erts_alloc#alloc_util">"The
- alloc_util framework" in erts_alloc(3)</seealso>.</p>
+ <marker id="system_info_alloc_util_allocators"></marker>
+ <p>Returns a list of the names of all allocators using
+ the ERTS internal <c>alloc_util</c> framework
+ as atoms. For more information, see section
+ <seealso marker="erts:erts_alloc#alloc_util">The
+ alloc_util framework</seealso>
+ in <c>erts_alloc(3)</c>.</p>
</item>
<tag><c>{allocator, <anno>Alloc</anno>}</c></tag>
<item>
- <marker id="system_info_allocator_tuple"></marker>
+ <marker id="system_info_allocator_tuple"></marker>
<p>Returns information about the specified allocator.
- As from <c>ERTS</c> 5.6.1, the return value is a list
- of <c>{instance, InstanceNo, InstanceInfo}</c> tuples,
- where <c>InstanceInfo</c> contains information about
- a specific instance of the allocator. As from
- <c>ERTS</c> 5.10.4, the returned list when calling
- <c>erlang:system_info({allocator, mseg_alloc})</c> also
- includes an <c>{erts_mmap, _}</c> tuple as one element
- in the list. If <c><anno>Alloc</anno></c> is not a
- recognized allocator, <c>undefined</c> is returned.
- If <c><anno>Alloc</anno></c> is disabled,
+ As from ERTS 5.6.1, the return value is a list
+ of <c>{instance, InstanceNo, InstanceInfo}</c> tuples,
+ where <c>InstanceInfo</c> contains information about
+ a specific instance of the allocator.
+ If <c><anno>Alloc</anno></c> is not a
+ recognized allocator, <c>undefined</c> is returned.
+ If <c><anno>Alloc</anno></c> is disabled,
<c>false</c> is returned.</p>
<p>Notice that the information returned is highly
implementation-dependent and can be changed or removed
@@ -6534,9 +7153,14 @@ ok
as it can be of interest for others it has been
briefly documented.</p>
<p>The recognized allocators are listed in
- <seealso marker="erts:erts_alloc">erts_alloc(3)</seealso>.
- After reading the <c>erts_alloc(3)</c> documentation,
- the returned information
+ <seealso marker="erts:erts_alloc"><c>erts_alloc(3)</c></seealso>.
+ Information about super carriers can be obtained from
+ ERTS 8.0 with <c>{allocator, erts_mmap}</c> or from
+ ERTS 5.10.4; the returned list when calling with
+ <c>{allocator, mseg_alloc}</c> also includes an
+ <c>{erts_mmap, _}</c> tuple as one element in the list.</p>
+ <p>After reading the <c>erts_alloc(3)</c> documentation,
+ the returned information
more or less speaks for itself, but it can be worth
explaining some things. Call counts are presented by two
values, the first value is giga calls, and the second
@@ -6552,19 +7176,20 @@ ok
<item>The third is the maximum value since the emulator
was started.</item>
</list>
- <p>If only one value is present, it is the current value.
+ <p>If only one value is present, it is the current value.
<c>fix_alloc</c> memory block types are presented by two
values. The first value is the memory pool size and
the second value is the used memory size.</p>
</item>
<tag><c>{allocator_sizes, <anno>Alloc</anno>}</c></tag>
<item>
- <marker id="system_info_allocator_sizes"></marker>
- <p>Returns various size information for the specified
- allocator. The information returned is a subset of the
- information returned by
- <seealso marker="#system_info_allocator_tuple"><c>erlang:system_info({allocator, <anno>Alloc</anno>})</c></seealso>.
- </p>
+ <marker id="system_info_allocator_sizes"></marker>
+ <p>Returns various size information for the specified
+ allocator. The information returned is a subset of the
+ information returned by
+ <seealso marker="#system_info_allocator_tuple">
+ <c>erlang:system_info({allocator,
+ <anno>Alloc</anno>})</c></seealso>.</p>
</item>
</taglist>
</desc>
@@ -6598,69 +7223,152 @@ ok
The <c>info_list()</c> can be extended in a future release.
</type_desc>
<desc>
- <marker id="system_info_cpu_topology_tags"></marker>
- <marker id="system_info_cpu_topology"></marker>
+ <marker id="system_info_cpu_topology_tags"></marker>
+ <marker id="system_info_cpu_topology"></marker>
<p>Returns various information about the CPU topology of
the current system (emulator) as specified by
<c><anno>Item</anno></c>:</p>
<taglist>
<tag><c>cpu_topology</c></tag>
<item>
- <p>Returns the <c><anno>CpuTopology</anno></c> currently used by
- the emulator. The CPU topology is used when binding schedulers
- to logical processors. The CPU topology used is the
- <seealso marker="erlang#system_info_cpu_topology_defined">user-defined CPU topology</seealso>,
- if such exists, otherwise the
- <seealso marker="erlang#system_info_cpu_topology_detected">automatically detected CPU topology</seealso>,
- if such exists. If no CPU topology
- exists, <c>undefined</c> is returned.</p>
- <p><c>node</c> refers to Non-Uniform Memory Access (NUMA)
- nodes. <c>thread</c> refers to hardware threads
- (for example, Intel hyper-threads).</p>
+ <p>Returns the <c><anno>CpuTopology</anno></c> currently used by
+ the emulator. The CPU topology is used when binding schedulers
+ to logical processors. The CPU topology used is the
+ <seealso marker="erlang#system_info_cpu_topology_defined">
+ user-defined CPU topology</seealso>,
+ if such exists, otherwise the
+ <seealso marker="erlang#system_info_cpu_topology_detected">
+ automatically detected CPU topology</seealso>,
+ if such exists. If no CPU topology
+ exists, <c>undefined</c> is returned.</p>
+ <p><c>node</c> refers to Non-Uniform Memory Access (NUMA)
+ nodes. <c>thread</c> refers to hardware threads
+ (for example, Intel hyper-threads).</p>
<p>A level in term <c><anno>CpuTopology</anno></c> can be
- omitted if only one entry exists and
- <c><anno>InfoList</anno></c> is empty.</p>
- <p><c>thread</c> can only be a sub level to <c>core</c>.
- <c>core</c> can be a sub level to <c>processor</c>
- or <c>node</c>. <c>processor</c> can be on the
- top level or a sub level to <c>node</c>. <c>node</c>
- can be on the top level or a sub level to
- <c>processor</c>. That is, NUMA nodes can be processor
- internal or processor external. A CPU topology can
- consist of a mix of processor internal and external
- NUMA nodes, as long as each logical CPU belongs to
- <em>one</em> NUMA node. Cache hierarchy is not part of
- the <c><anno>CpuTopology</anno></c> type, but will be in a
- future release. Other things can also make it into the CPU
- topology in a future release. In other words, expect the
- <c><anno>CpuTopology</anno></c> type to change.</p>
+ omitted if only one entry exists and
+ <c><anno>InfoList</anno></c> is empty.</p>
+ <p><c>thread</c> can only be a sublevel to <c>core</c>.
+ <c>core</c> can be a sublevel to <c>processor</c>
+ or <c>node</c>. <c>processor</c> can be on the
+ top level or a sublevel to <c>node</c>. <c>node</c>
+ can be on the top level or a sublevel to
+ <c>processor</c>. That is, NUMA nodes can be processor
+ internal or processor external. A CPU topology can
+ consist of a mix of processor internal and external
+ NUMA nodes, as long as each logical CPU belongs to
+ <em>one</em> NUMA node. Cache hierarchy is not part of
+ the <c><anno>CpuTopology</anno></c> type, but will be in a
+ future release. Other things can also make it into the CPU
+ topology in a future release. So, expect the
+ <c><anno>CpuTopology</anno></c> type to change.</p>
</item>
<tag><c>{cpu_topology, defined}</c></tag>
<item>
- <marker id="system_info_cpu_topology_defined"></marker>
+ <marker id="system_info_cpu_topology_defined"></marker>
<p>Returns the user-defined <c><anno>CpuTopology</anno></c>.
- For more information, see command-line flag
- <seealso marker="erts:erl#+sct">+sct</seealso> in
- <c>erl(1)</c> and argument
- <seealso marker="#system_info_cpu_topology">cpu_topology</seealso>.</p>
+ For more information, see command-line flag
+ <seealso marker="erts:erl#+sct"><c>+sct</c></seealso> in
+ <c>erl(1)</c> and argument
+ <seealso marker="#system_info_cpu_topology">
+ <c>cpu_topology</c></seealso>.</p>
</item>
<tag><c>{cpu_topology, detected}</c></tag>
<item>
- <marker id="system_info_cpu_topology_detected"></marker>
+ <marker id="system_info_cpu_topology_detected"></marker>
<p>Returns the automatically detected
- <c><anno>CpuTopology</anno>y</c>. The
- emulator detects the CPU topology on some newer
- Linux, Solaris, FreeBSD, and Windows systems.
- On Windows system with more than 32 logical processors,
- the CPU topology is not detected.</p>
+ <c><anno>CpuTopology</anno>y</c>. The
+ emulator detects the CPU topology on some newer
+ Linux, Solaris, FreeBSD, and Windows systems.
+ On Windows system with more than 32 logical processors,
+ the CPU topology is not detected.</p>
<p>For more information, see argument
- <seealso marker="#system_info_cpu_topology">cpu_topology</seealso>.</p>
+ <seealso marker="#system_info_cpu_topology">
+ <c>cpu_topology</c></seealso>.</p>
</item>
<tag><c>{cpu_topology, used}</c></tag>
<item>
<p>Returns <c><anno>CpuTopology</anno></c> used by the emulator.
For more information, see argument
- <seealso marker="#system_info_cpu_topology">cpu_topology</seealso>.</p>
+ <seealso marker="#system_info_cpu_topology">
+ <c>cpu_topology</c></seealso>.</p>
+ </item>
+ </taglist>
+ </desc>
+ </func>
+
+ <func>
+ <name name="system_info" arity="1" clause_i="27"/>
+ <name name="system_info" arity="1" clause_i="28"/>
+ <name name="system_info" arity="1" clause_i="36"/>
+ <name name="system_info" arity="1" clause_i="37"/>
+ <name name="system_info" arity="1" clause_i="38"/>
+ <name name="system_info" arity="1" clause_i="39"/>
+ <fsummary>Information about the default process heap settings.</fsummary>
+ <type name="message_queue_data"/>
+ <type name="max_heap_size"/>
+ <desc>
+ <p>Returns information about the default process heap settings:</p>
+ <taglist>
+ <tag><c>fullsweep_after</c></tag>
+ <item>
+ <p>Returns <c>{fullsweep_after, integer() >= 0}</c>, which is
+ the <c>fullsweep_after</c> garbage collection setting used
+ by default. For more information, see
+ <c>garbage_collection</c> described below.</p>
+ </item>
+ <tag><c>garbage_collection</c></tag>
+ <item>
+ <p>Returns a list describing the default garbage collection
+ settings. A process spawned on the local node by a
+ <c>spawn</c> or <c>spawn_link</c> uses these
+ garbage collection settings. The default settings can be
+ changed by using
+ <seealso marker="#system_flag/2">
+ <c>erlang:system_flag/2</c></seealso>.
+ <seealso marker="#spawn_opt/4"><c>spawn_opt/2,3,4</c></seealso>
+ can spawn a process that does not use the default
+ settings.</p>
+ </item>
+ <tag><c>max_heap_size</c></tag>
+ <item>
+ <p>Returns <c>{max_heap_size, <anno>MaxHeapSize</anno>}</c>,
+ where <c><anno>MaxHeapSize</anno></c> is the current
+ system-wide maximum heap size settings for spawned processes.
+ This setting can be set using the command-line flags
+ <seealso marker="erl#+hmax"><c>+hmax</c></seealso>,
+ <seealso marker="erl#+hmaxk"><c>+hmaxk</c></seealso> and
+ <seealso marker="erl#+hmaxel"><c>+hmaxel</c></seealso> in
+ <c>erl(1)</c>. It can also be changed at runtime using
+ <seealso marker="#system_flag_max_heap_size">
+ <c>erlang:system_flag(max_heap_size, MaxHeapSize)</c></seealso>.
+ For more details about the <c>max_heap_size</c> process flag,
+ see <seealso marker="#process_flag_max_heap_size">
+ <c>process_flag(max_heap_size, MaxHeapSize)</c></seealso>.</p>
+ </item>
+ <tag><c>min_heap_size</c></tag>
+ <item>
+ <p>Returns <c>{min_heap_size, <anno>MinHeapSize</anno>}</c>,
+ where <c><anno>MinHeapSize</anno></c> is the current
+ system-wide minimum heap size for spawned processes.</p>
+ </item>
+ <tag><marker id="system_info_message_queue_data"/>
+ <c>message_queue_data</c></tag>
+ <item>
+ <p>Returns the default value of the <c>message_queue_data</c>
+ process flag, which is either <c>off_heap</c> or <c>on_heap</c>.
+ This default is set by command-line argument
+ <seealso marker="erl#+hmqd"><c>+hmqd</c></seealso> in
+ <c>erl(1)</c>. For more information on the
+ <c>message_queue_data</c> process flag, see documentation of
+ <seealso marker="#process_flag_message_queue_data">
+ <c>process_flag(message_queue_data, MQD)</c></seealso>.</p>
+ </item>
+ <tag><c>min_bin_vheap_size</c></tag>
+ <item>
+ <p>Returns <c>{min_bin_vheap_size,
+ <anno>MinBinVHeapSize</anno>}</c>, where
+ <c><anno>MinBinVHeapSize</anno></c> is the current system-wide
+ minimum binary virtual heap size for spawned processes.</p>
</item>
</taglist>
</desc>
@@ -6686,8 +7394,6 @@ ok
<name name="system_info" arity="1" clause_i="24"/>
<name name="system_info" arity="1" clause_i="25"/>
<name name="system_info" arity="1" clause_i="26"/>
- <name name="system_info" arity="1" clause_i="27"/>
- <name name="system_info" arity="1" clause_i="28"/>
<name name="system_info" arity="1" clause_i="29"/>
<name name="system_info" arity="1" clause_i="30"/>
<name name="system_info" arity="1" clause_i="31"/>
@@ -6695,10 +7401,6 @@ ok
<name name="system_info" arity="1" clause_i="33"/>
<name name="system_info" arity="1" clause_i="34"/>
<name name="system_info" arity="1" clause_i="35"/>
- <name name="system_info" arity="1" clause_i="36"/>
- <name name="system_info" arity="1" clause_i="37"/>
- <name name="system_info" arity="1" clause_i="38"/>
- <name name="system_info" arity="1" clause_i="39"/>
<name name="system_info" arity="1" clause_i="40"/>
<name name="system_info" arity="1" clause_i="41"/>
<name name="system_info" arity="1" clause_i="42"/>
@@ -6727,6 +7429,8 @@ ok
<name name="system_info" arity="1" clause_i="65"/>
<name name="system_info" arity="1" clause_i="66"/>
<name name="system_info" arity="1" clause_i="67"/>
+ <name name="system_info" arity="1" clause_i="68"/>
+ <name name="system_info" arity="1" clause_i="69"/>
<fsummary>Information about the system.</fsummary>
<desc>
<p>Returns various information about the current system
@@ -6735,24 +7439,25 @@ ok
<tag><c>allocated_areas</c>, <c>allocator</c>,
<c>alloc_util_allocators</c>, <c>allocator_sizes</c></tag>
<item>
- <p>See <seealso marker="#system_info_allocator_tags">above</seealso>.</p>
+ <p>See <seealso marker="#system_info_allocator_tags">
+ above</seealso>.</p>
</item>
<tag><c>build_type</c></tag>
<item>
<p>Returns an atom describing the build type of the runtime
- system. This is normally the atom <c>opt</c> for optimized.
- Other possible return values are <c>debug</c>, <c>purify</c>,
- <c>quantify</c>, <c>purecov</c>, <c>gcov</c>, <c>valgrind</c>,
- <c>gprof</c>, and <c>lcnt</c>. Possible return values
- can be added or removed at any time without prior notice.</p>
+ system. This is normally the atom <c>opt</c> for optimized.
+ Other possible return values are <c>debug</c>, <c>purify</c>,
+ <c>quantify</c>, <c>purecov</c>, <c>gcov</c>, <c>valgrind</c>,
+ <c>gprof</c>, and <c>lcnt</c>. Possible return values
+ can be added or removed at any time without prior notice.</p>
</item>
<tag><c>c_compiler_used</c></tag>
<item>
<p>Returns a two-tuple describing the C compiler used when
- compiling the runtime system. The first element is an
- atom describing the name of the compiler, or <c>undefined</c>
- if unknown. The second element is a term describing the
- version of the compiler, or <c>undefined</c> if unknown.</p>
+ compiling the runtime system. The first element is an
+ atom describing the name of the compiler, or <c>undefined</c>
+ if unknown. The second element is a term describing the
+ version of the compiler, or <c>undefined</c> if unknown.</p>
</item>
<tag><c>check_io</c></tag>
<item>
@@ -6769,12 +7474,13 @@ ok
Erlang/OTP release that the current emulator has been
set to be backward compatible with. The compatibility
mode can be configured at startup by using command-line flag
- <seealso marker="erts:erl#compat_rel">+R</seealso> in
+ <seealso marker="erts:erl#compat_rel"><c>+R</c></seealso> in
<c>erl(1)</c>.</p>
</item>
<tag><c>cpu_topology</c></tag>
<item>
- <p>See <seealso marker="#system_info_cpu_topology_tags">above</seealso>.</p>
+ <p>See <seealso
+ marker="#system_info_cpu_topology_tags">above</seealso>.</p>
</item>
<tag><c>creation</c></tag>
<item>
@@ -6790,22 +7496,22 @@ ok
</item>
<tag><c>debug_compiled</c></tag>
<item>
- <p>Returns <c>true</c> if the emulator has been debug
- compiled, otherwise <c>false</c>.</p>
+ <p>Returns <c>true</c> if the emulator has been
+ debug-compiled, otherwise <c>false</c>.</p>
</item>
<tag><c>delayed_node_table_gc</c></tag>
<item>
<marker id="system_info_delayed_node_table_gc"></marker>
<p>Returns the amount of time in seconds garbage collection
- of an entry in a node table is delayed. This limit can be set
- on startup by passing the command line flag
- <seealso marker="erts:erl#+zdntgc">+zdntgc</seealso>
- to <c>erl</c>. For more information see the documentation of the
- command line flag.</p>
+ of an entry in a node table is delayed. This limit can be set
+ on startup by passing command-line flag
+ <seealso marker="erts:erl#+zdntgc"><c>+zdntgc</c></seealso>
+ to <c>erl(1)</c>. For more information, see the documentation of
+ the command-line flag.</p>
</item>
<tag><c>dirty_cpu_schedulers</c></tag>
<item>
- <marker id="system_info_dirty_cpu_schedulers"></marker>
+ <marker id="system_info_dirty_cpu_schedulers"></marker>
<p>Returns the number of dirty CPU scheduler threads used by
the emulator. Dirty CPU schedulers execute CPU-bound
native functions, such as NIFs, linked-in driver code,
@@ -6817,23 +7523,31 @@ ok
can be changed at any time. The number of dirty CPU
schedulers can be set at startup by passing
command-line flag
- <seealso marker="erts:erl#+SDcpu">+SDcpu</seealso> or
- <seealso marker="erts:erl#+SDPcpu">+SDPcpu</seealso> in
+ <seealso marker="erts:erl#+SDcpu"><c>+SDcpu</c></seealso> or
+ <seealso marker="erts:erl#+SDPcpu"><c>+SDPcpu</c></seealso> in
<c>erl(1)</c>.</p>
- <p>Notice that the dirty schedulers functionality is
+ <p>Notice that the dirty schedulers functionality is
experimental. Enable support for dirty schedulers when
building OTP to try out the functionality.</p>
<p>See also
- <seealso marker="#system_flag_dirty_cpu_schedulers_online">erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline)</seealso>,
- <seealso marker="#system_info_dirty_cpu_schedulers_online">erlang:system_info(dirty_cpu_schedulers_online)</seealso>,
- <seealso marker="#system_info_dirty_io_schedulers">erlang:system_info(dirty_io_schedulers)</seealso>,
- <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>,
- <seealso marker="#system_info_schedulers_online">erlang:system_info(schedulers_online)</seealso>, and
- <seealso marker="#system_flag_schedulers_online">erlang:system_flag(schedulers_online, SchedulersOnline)</seealso>.</p>
+ <seealso marker="#system_flag_dirty_cpu_schedulers_online">
+ <c>erlang:system_flag(dirty_cpu_schedulers_online,
+ DirtyCPUSchedulersOnline)</c></seealso>,
+ <seealso marker="#system_info_dirty_cpu_schedulers_online">
+ <c>erlang:system_info(dirty_cpu_schedulers_online)</c></seealso>,
+ <seealso marker="#system_info_dirty_io_schedulers">
+ <c>erlang:system_info(dirty_io_schedulers)</c></seealso>,
+ <seealso marker="#system_info_schedulers">
+ <c>erlang:system_info(schedulers)</c></seealso>,
+ <seealso marker="#system_info_schedulers_online">
+ <c>erlang:system_info(schedulers_online)</c></seealso>, and
+ <seealso marker="#system_flag_schedulers_online">
+ <c>erlang:system_flag(schedulers_online,
+ SchedulersOnline)</c></seealso>.</p>
</item>
<tag><c>dirty_cpu_schedulers_online</c></tag>
<item>
- <marker id="system_info_dirty_cpu_schedulers_online"></marker>
+ <marker id="system_info_dirty_cpu_schedulers_online"></marker>
<p>Returns the number of dirty CPU schedulers online.
The return value satisfies
<c><![CDATA[1 <= DirtyCPUSchedulersOnline <= N]]></c>,
@@ -6842,51 +7556,60 @@ ok
<c>erlang:system_info(schedulers_online)</c>.</p>
<p>The number of dirty CPU schedulers online can be set at
startup by passing command-line flag
- <seealso marker="erts:erl#+SDcpu">+SDcpu</seealso> in
+ <seealso marker="erts:erl#+SDcpu"><c>+SDcpu</c></seealso> in
<c>erl(1)</c>.</p>
<p>Notice that the dirty schedulers functionality is
experimental. Enable support for dirty schedulers when
building OTP to try out the functionality.</p>
<p>For more information, see
- <seealso marker="#system_info_dirty_cpu_schedulers">erlang:system_info(dirty_cpu_schedulers)</seealso>,
- <seealso marker="#system_info_dirty_io_schedulers">erlang:system_info(dirty_io_schedulers)</seealso>,
- <seealso marker="#system_info_schedulers_online">erlang:system_info(schedulers_online)</seealso>, and
- <seealso marker="#system_flag_dirty_cpu_schedulers_online">erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline)</seealso>.</p>
+ <seealso marker="#system_info_dirty_cpu_schedulers">
+ <c>erlang:system_info(dirty_cpu_schedulers)</c></seealso>,
+ <seealso marker="#system_info_dirty_io_schedulers">
+ <c>erlang:system_info(dirty_io_schedulers)</c></seealso>,
+ <seealso marker="#system_info_schedulers_online">
+ <c>erlang:system_info(schedulers_online)</c></seealso>, and
+ <seealso marker="#system_flag_dirty_cpu_schedulers_online">
+ <c>erlang:system_flag(dirty_cpu_schedulers_online,
+ DirtyCPUSchedulersOnline)</c></seealso>.</p>
</item>
<tag><c>dirty_io_schedulers</c></tag>
<item>
- <marker id="system_info_dirty_io_schedulers"></marker>
+ <marker id="system_info_dirty_io_schedulers"></marker>
<p>Returns the number of dirty I/O schedulers as an integer.
Dirty I/O schedulers execute I/O-bound native functions,
such as NIFs and linked-in driver code, which cannot be
managed cleanly by the normal emulator schedulers.</p>
<p>This value can be set at startup by passing command-line
- argument <seealso marker="erts:erl#+SDio">+SDio</seealso>
+ argument <seealso marker="erts:erl#+SDio"><c>+SDio</c></seealso>
in <c>erl(1)</c>.</p>
- <p>Notice that the dirty schedulers functionality is
+ <p>Notice that the dirty schedulers functionality is
experimental. Enable support for dirty schedulers when
building OTP to try out the functionality.</p>
<p>For more information, see
- <seealso marker="#system_info_dirty_cpu_schedulers">erlang:system_info(dirty_cpu_schedulers)</seealso>,
- <seealso marker="#system_info_dirty_cpu_schedulers_online">erlang:system_info(dirty_cpu_schedulers_online)</seealso>, and
- <seealso marker="#system_flag_dirty_cpu_schedulers_online">erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline)</seealso>.</p>
+ <seealso marker="#system_info_dirty_cpu_schedulers">
+ <c>erlang:system_info(dirty_cpu_schedulers)</c></seealso>,
+ <seealso marker="#system_info_dirty_cpu_schedulers_online">
+ <c>erlang:system_info(dirty_cpu_schedulers_online)</c></seealso>,
+ and <seealso marker="#system_flag_dirty_cpu_schedulers_online">
+ <c>erlang:system_flag(dirty_cpu_schedulers_online,
+ DirtyCPUSchedulersOnline)</c></seealso>.</p>
</item>
<tag><c>dist</c></tag>
<item>
<p>Returns a binary containing a string of distribution
information formatted as in Erlang crash dumps. For more
- information, see Section
- <seealso marker="erts:crash_dump">"How to interpret the Erlang crash dumps"</seealso>
+ information, see section <seealso marker="erts:crash_dump">
+ How to interpret the Erlang crash dumps</seealso>
in the User's Guide.</p>
</item>
<tag><c>dist_buf_busy_limit</c></tag>
<item>
- <marker id="system_info_dist_buf_busy_limit"></marker>
+ <marker id="system_info_dist_buf_busy_limit"></marker>
<p>Returns the value of the distribution buffer busy limit
- in bytes. This limit can be set at startup by passing
- command-line flag
- <seealso marker="erts:erl#+zdbbl">+zdbbl</seealso>
- to <c>erl</c>.</p>
+ in bytes. This limit can be set at startup by passing
+ command-line flag
+ <seealso marker="erts:erl#+zdbbl"><c>+zdbbl</c></seealso>
+ to <c>erl(1)</c>.</p>
</item>
<tag><c>dist_ctrl</c></tag>
<item>
@@ -6904,84 +7627,67 @@ ok
<item>
<p>Returns a string containing the Erlang driver version
used by the runtime system. It has the form
- <seealso marker="erts:erl_driver#version_management">"&lt;major ver&gt;.&lt;minor ver&gt;"</seealso>.</p>
+ <seealso marker="erts:erl_driver#version_management">
+ "&lt;major ver&gt;.&lt;minor ver&gt;"</seealso>.</p>
</item>
<tag><c>dynamic_trace</c></tag>
<item>
<p>Returns an atom describing the dynamic trace framework
- compiled into the virtual machine. It can be
- <c>dtrace</c>, <c>systemtap</c>, or <c>none</c>. For a
- commercial or standard build, it is always <c>none</c>.
- The other return values indicate a custom configuration
- (for example, <c>./configure --with-dynamic-trace=dtrace</c>).
- For more information about dynamic tracing, see the
- <seealso marker="runtime_tools:dyntrace">dyntrace</seealso>
- manual page and the
- <c>README.dtrace</c>/<c>README.systemtap</c> files in the
- Erlang source code top directory.</p>
+ compiled into the virtual machine. It can be
+ <c>dtrace</c>, <c>systemtap</c>, or <c>none</c>. For a
+ commercial or standard build, it is always <c>none</c>.
+ The other return values indicate a custom configuration
+ (for example, <c>./configure --with-dynamic-trace=dtrace</c>).
+ For more information about dynamic tracing, see
+ <seealso marker="runtime_tools:dyntrace">
+ <c>dyntrace(3)</c></seealso> manual page and the
+ <c>README.dtrace</c>/<c>README.systemtap</c> files in the
+ Erlang source code top directory.</p>
</item>
<tag><c>dynamic_trace_probes</c></tag>
<item>
<p>Returns a <c>boolean()</c> indicating if dynamic trace
- probes (<c>dtrace</c> or <c>systemtap</c>) are built into
- the emulator. This can only be <c>true</c> if the Virtual
- Machine was built for dynamic tracing (that is,
- <c>system_info(dynamic_trace)</c> returns
- <c>dtrace</c> or <c>systemtap</c>).</p>
- </item>
- <tag><marker id="system_info_end_time"/><c>end_time</c></tag>
- <item><p>The last <seealso marker="#monotonic_time/0">Erlang monotonic
- time</seealso> in <c>native</c>
- <seealso marker="#type_time_unit">time unit</seealso> that
- can be represented internally in the current Erlang runtime system
- instance. The time between the
- <seealso marker="#system_info_start_time">start time</seealso> and
- the end time is at least a quarter of a millennium.</p></item>
+ probes (<c>dtrace</c> or <c>systemtap</c>) are built into
+ the emulator. This can only be <c>true</c> if the virtual
+ machine was built for dynamic tracing (that is,
+ <c>system_info(dynamic_trace)</c> returns
+ <c>dtrace</c> or <c>systemtap</c>).</p>
+ </item>
+ <tag><marker id="system_info_end_time"/><c>end_time</c></tag>
+ <item>
+ <p>The last <seealso marker="#monotonic_time/0">Erlang monotonic
+ time</seealso> in <c>native</c>
+ <seealso marker="#type_time_unit">time unit</seealso> that
+ can be represented internally in the current Erlang runtime system
+ instance. The time between the
+ <seealso marker="#system_info_start_time">start time</seealso> and
+ the end time is at least a quarter of a millennium.</p>
+ </item>
<tag><c>elib_malloc</c></tag>
<item>
<p>This option will be removed in a future release.
- The return value will always be <c>false</c>, as the
- <c>elib_malloc</c> allocator has been removed.</p>
+ The return value will always be <c>false</c>, as the
+ <c>elib_malloc</c> allocator has been removed.</p>
</item>
- <tag><marker id="system_info_eager_check_io"/><c>eager_check_io</c></tag>
+ <tag><marker id="system_info_eager_check_io"/>
+ <c>eager_check_io</c></tag>
<item>
- <p>
- Returns the value of the <c>erl</c> command line flag
- <seealso marker="erl#+secio">+secio</seealso>
- which is either <c>true</c> or <c>false</c>. See the
- documentation of the command line flag for information about
- the different values.
- </p>
+ <p>Returns the value of command-line flag
+ <seealso marker="erl#+secio"><c>+secio</c></seealso> in
+ <c>erl(1)</c>, which is either <c>true</c> or <c>false</c>.
+ For information about the different values, see the
+ documentation of the command-line flag.</p>
</item>
<tag><c>ets_limit</c></tag>
<item>
<p>Returns the maximum number of ETS tables allowed. This
limit can be increased at startup by passing
command-line flag
- <seealso marker="erts:erl#+e">+e</seealso> to
+ <seealso marker="erts:erl#+e"><c>+e</c></seealso> to
<c>erl(1)</c> or by setting environment variable
<c>ERL_MAX_ETS_TABLES</c> before starting the Erlang
runtime system.</p>
</item>
- <tag><c>fullsweep_after</c></tag>
- <item>
- <p>Returns <c>{fullsweep_after, integer() >= 0}</c>, which is
- the <c>fullsweep_after</c> garbage collection setting used
- by default. For more information, see
- <c>garbage_collection</c> described in the following.</p>
- </item>
- <tag><c>garbage_collection</c></tag>
- <item>
- <p>Returns a list describing the default garbage collection
- settings. A process spawned on the local node by a
- <c>spawn</c> or <c>spawn_link</c> uses these
- garbage collection settings. The default settings can be
- changed by using
- <seealso marker="#system_flag/2">system_flag/2</seealso>.
- <seealso marker="#spawn_opt/4">spawn_opt/4</seealso>
- can spawn a process that does not use the default
- settings.</p>
- </item>
<tag><c>heap_sizes</c></tag>
<item>
<p>Returns a list of integers representing valid heap sizes
@@ -6995,10 +7701,10 @@ ok
<taglist>
<tag><c>private</c></tag>
<item>
- <p>Each process has a heap reserved for its use and no
- references between heaps of different processes are
- allowed. Messages passed between processes are copied
- between heaps.</p>
+ Each process has a heap reserved for its use and no
+ references between heaps of different processes are
+ allowed. Messages passed between processes are copied
+ between heaps.
</item>
</taglist>
</item>
@@ -7006,8 +7712,9 @@ ok
<item>
<p>Returns a binary containing a string of miscellaneous
system information formatted as in Erlang crash dumps.
- For more information, see Section
- <seealso marker="erts:crash_dump">"How to interpret the Erlang crash dumps"</seealso>
+ For more information, see section
+ <seealso marker="erts:crash_dump">
+ How to interpret the Erlang crash dumps</seealso>
in the User's Guide.</p>
</item>
<tag><c>kernel_poll</c></tag>
@@ -7019,69 +7726,57 @@ ok
<item>
<p>Returns a binary containing a string of loaded module
information formatted as in Erlang crash dumps. For more
- information, see Section
- <seealso marker="erts:crash_dump">"How to interpret the Erlang crash dumps"</seealso>
- in the User's Guide.</p>
+ information, see section
+ <seealso marker="erts:crash_dump">How to interpret the Erlang
+ crash dumps</seealso> in the User's Guide.</p>
</item>
- <tag><c>logical_processors</c></tag>
+ <tag><c>logical_processors</c></tag>
<item>
- <marker id="logical_processors"></marker>
+ <marker id="logical_processors"></marker>
<p>Returns the detected number of logical processors configured
- in the system. The return value is either an integer, or
- the atom <c>unknown</c> if the emulator cannot
- detect the configured logical processors.</p>
+ in the system. The return value is either an integer, or
+ the atom <c>unknown</c> if the emulator cannot
+ detect the configured logical processors.</p>
</item>
- <tag><c>logical_processors_available</c></tag>
+ <tag><c>logical_processors_available</c></tag>
<item>
- <marker id="logical_processors_available"></marker>
+ <marker id="logical_processors_available"></marker>
<p>Returns the detected number of logical processors available
- to the Erlang runtime system. The return value is either an
- integer, or the atom <c>unknown</c> if the emulator
- cannot detect the available logical processors. The number
- of available logical processors is less than or equal to
- the number of
- <seealso marker="#logical_processors_online">logical processors online</seealso>.</p>
+ to the Erlang runtime system. The return value is either an
+ integer, or the atom <c>unknown</c> if the emulator
+ cannot detect the available logical processors. The number
+ of available logical processors is less than or equal to
+ the number of <seealso marker="#logical_processors_online">
+ logical processors online</seealso>.</p>
</item>
- <tag><c>logical_processors_online</c></tag>
+ <tag><c>logical_processors_online</c></tag>
<item>
- <marker id="logical_processors_online"></marker>
+ <marker id="logical_processors_online"></marker>
<p>Returns the detected number of logical processors online on
- the system. The return value is either an integer,
- or the atom <c>unknown</c> if the emulator cannot
- detect logical processors online. The number of logical
- processors online is less than or equal to the number of
- <seealso marker="#logical_processors">logical processors configured</seealso>.</p>
+ the system. The return value is either an integer,
+ or the atom <c>unknown</c> if the emulator cannot
+ detect logical processors online. The number of logical
+ processors online is less than or equal to the number of
+ <seealso marker="#logical_processors">logical processors
+ configured</seealso>.</p>
</item>
<tag><c>machine</c></tag>
<item>
<p>Returns a string containing the Erlang machine name.</p>
</item>
- <tag><c>min_heap_size</c></tag>
- <item>
- <p>Returns <c>{min_heap_size, <anno>MinHeapSize</anno>}</c>,
- where <c><anno>MinHeapSize</anno></c> is the current
- system-wide minimum heap size for spawned processes.</p>
- </item>
- <tag><c>min_bin_vheap_size</c></tag>
- <item>
- <p>Returns <c>{min_bin_vheap_size,
- <anno>MinBinVHeapSize</anno>}</c>, where
- <c><anno>MinBinVHeapSize</anno></c> is the current system-wide
- minimum binary virtual heap size for spawned processes.</p>
- </item>
<tag><c>modified_timing_level</c></tag>
<item>
<p>Returns the modified timing-level (an integer) if
- modified timing is enabled, otherwise, <c>undefined</c>.
+ modified timing is enabled, otherwise <c>undefined</c>.
For more information about modified timing, see
command-line flag
- <seealso marker="erts:erl#+T">+T</seealso>
+ <seealso marker="erts:erl#+T"><c>+T</c></seealso>
in <c>erl(1)</c></p>
</item>
<tag><c>multi_scheduling</c></tag>
<item>
- <marker id="system_info_multi_scheduling"></marker>
- <p>Returns <c>disabled</c>, <c>blocked</c>, or <c>enabled</c>:</p>
+ <marker id="system_info_multi_scheduling"></marker>
+ <p>Returns one of the following:</p>
<taglist>
<tag><c>disabled</c></tag>
<item>
@@ -7092,181 +7787,225 @@ ok
<tag><c>blocked</c></tag>
<item>
<p>The emulator has more than one scheduler thread,
- but all scheduler threads except one are blocked,
- that is, only one scheduler thread schedules
+ but all scheduler threads except one are blocked.
+ That is, only one scheduler thread schedules
Erlang processes and executes Erlang code.</p>
</item>
+ <tag><c>blocked_normal</c></tag>
+ <item>
+ <p>The emulator has more than one scheduler thread,
+ but all normal scheduler threads except one are
+ blocked. Notice that dirty schedulers are not
+ blocked, and can schedule Erlang processes and
+ execute native code.</p>
+ </item>
<tag><c>enabled</c></tag>
<item>
<p>The emulator has more than one scheduler thread,
- and no scheduler threads are blocked, that is,
+ and no scheduler threads are blocked. That is,
all available scheduler threads schedule
Erlang processes and execute Erlang code.</p>
</item>
</taglist>
<p>See also
- <seealso marker="#system_flag_multi_scheduling">erlang:system_flag(multi_scheduling, BlockState)</seealso>,
- <seealso marker="#system_info_multi_scheduling_blockers">erlang:system_info(multi_scheduling_blockers)</seealso>,
- and
- <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>.</p>
+ <seealso marker="#system_flag_multi_scheduling">
+ <c>erlang:system_flag(multi_scheduling, BlockState)</c></seealso>,
+ <seealso marker="#system_info_multi_scheduling_blockers">
+ <c>erlang:system_info(multi_scheduling_blockers)</c></seealso>,
+ <seealso marker="#system_info_normal_multi_scheduling_blockers">
+ <c>erlang:system_info(normal_multi_scheduling_blockers)</c></seealso>,
+ and <seealso marker="#system_info_schedulers">
+ <c>erlang:system_info(schedulers)</c></seealso>.</p>
</item>
<tag><c>multi_scheduling_blockers</c></tag>
<item>
- <marker id="system_info_multi_scheduling_blockers"></marker>
+ <marker id="system_info_multi_scheduling_blockers"></marker>
<p>Returns a list of <c><anno>Pid</anno></c>s when
multi-scheduling is blocked, otherwise the empty list is
returned. The <c><anno>Pid</anno></c>s in the list
- represent all the processes currently
+ represent all the processes currently
blocking multi-scheduling. A <c><anno>Pid</anno></c> occurs
only once in the list, even if the corresponding
process has blocked multiple times.</p>
<p>See also
- <seealso marker="#system_flag_multi_scheduling">erlang:system_flag(multi_scheduling, BlockState)</seealso>,
- <seealso marker="#system_info_multi_scheduling">erlang:system_info(multi_scheduling)</seealso>,
- and
- <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>.</p>
+ <seealso marker="#system_flag_multi_scheduling">
+ <c>erlang:system_flag(multi_scheduling, BlockState)</c></seealso>,
+ <seealso marker="#system_info_multi_scheduling">
+ <c>erlang:system_info(multi_scheduling)</c></seealso>,
+ <seealso marker="#system_info_normal_multi_scheduling_blockers">
+ <c>erlang:system_info(normal_multi_scheduling_blockers)</c></seealso>,
+ and <seealso marker="#system_info_schedulers">
+ <c>erlang:system_info(schedulers)</c></seealso>.</p>
</item>
<tag><c>nif_version</c></tag>
<item>
- <p>Returns a string containing the version of the Erlang NIF interface
- used by the runtime system. It is on the form
- "&lt;major ver&gt;.&lt;minor ver&gt;".</p>
+ <p>Returns a string containing the version of the Erlang NIF
+ interface used by the runtime system. It is on the form
+ "&lt;major ver&gt;.&lt;minor ver&gt;".</p>
</item>
- <tag><c>otp_release</c></tag>
+ <tag><c>normal_multi_scheduling_blockers</c></tag>
<item>
- <marker id="system_info_otp_release"></marker>
+ <marker id="system_info_normal_multi_scheduling_blockers"></marker>
+ <p>Returns a list of <c><anno>Pid</anno></c>s when
+ normal multi-scheduling is blocked (that is, all normal schedulers
+ but one is blocked), otherwise the empty list is returned.
+ The <c><anno>Pid</anno></c>s in the list represent all the
+ processes currently blocking normal multi-scheduling.
+ A <c><anno>Pid</anno></c> occurs only once in the list, even if
+ the corresponding process has blocked multiple times.</p>
+ <p>See also
+ <seealso marker="#system_flag_multi_scheduling">
+ <c>erlang:system_flag(multi_scheduling, BlockState)</c></seealso>,
+ <seealso marker="#system_info_multi_scheduling">
+ <c>erlang:system_info(multi_scheduling)</c></seealso>,
+ <seealso marker="#system_info_multi_scheduling_blockers">
+ <c>erlang:system_info(multi_scheduling_blockers)</c></seealso>,
+ and <seealso marker="#system_info_schedulers">
+ <c>erlang:system_info(schedulers)</c></seealso>.</p>
+ </item>
+ <tag><marker id="system_info_otp_release"/>
+ <c>otp_release</c></tag>
+ <item>
+ <marker id="system_info_otp_release"></marker>
<p>Returns a string containing the OTP release number of the
- OTP release that the currently executing <c>ERTS</c> application is
- part of.</p>
- <p>As from OTP 17, the OTP release number corresponds to
- the major OTP version number. No
- <c>erlang:system_info()</c> argument gives the exact OTP
- version. This is because the exact OTP version in the general case
- is difficult to determine. For more information, see the description
- of versions in <seealso marker="doc/system_principles:versions">
- System principles</seealso> in System Documentation.</p>
- </item>
- <tag><marker id="system_info_os_monotonic_time_source"/><c>os_monotonic_time_source</c></tag>
+ OTP release that the currently executing ERTS application
+ is part of.</p>
+ <p>As from Erlang/OTP 17, the OTP release number corresponds to
+ the major OTP version number. No
+ <c>erlang:system_info()</c> argument gives the exact OTP
+ version. This is because the exact OTP version in the general case
+ is difficult to determine. For more information, see the
+ description of versions in
+ <seealso marker="doc/system_principles:versions">
+ System principles</seealso> in System Documentation.</p>
+ </item>
+ <tag><marker id="system_info_os_monotonic_time_source"/>
+ <c>os_monotonic_time_source</c></tag>
<item>
<p>Returns a list containing information about the source of
- <seealso marker="erts:time_correction#OS_Monotonic_Time">OS
- monotonic time</seealso> that is used by the runtime system.</p>
- <p>If <c>[]</c> is returned, no OS monotonic time is
- available. The list contains two-tuples with <c>Key</c>s
- as first element, and <c>Value</c>s as second element. The
- order of these tuples is undefined. The following
- tuples can be part of the list, but more tuples can be
- introduced in the future:</p>
- <taglist>
- <tag><c>{function, Function}</c></tag>
- <item><p><c>Function</c> is the name of the function
- used. This tuple always exist if OS monotonic time is
- available to the runtime system.</p></item>
-
- <tag><c>{clock_id, ClockId}</c></tag>
- <item><p>This tuple only exist if <c>Function</c>
- can be used with different clocks. <c>ClockId</c>
- corresponds to the clock identifier used when calling
- <c>Function</c>.</p></item>
-
- <tag><c>{resolution, OsMonotonicTimeResolution}</c></tag>
- <item><p>Highest possible
- <seealso marker="time_correction#Time_Resolution">resolution</seealso>
- of current OS monotonic time source as parts per
- second. If no resolution information can be retrieved
- from the OS, <c>OsMonotonicTimeResolution</c> is
- set to the resolution of the time unit of
- <c>Function</c>s return value. That is, the actual
- resolution can be lower than
- <c>OsMonotonicTimeResolution</c>. Also note that
- the resolution does not say anything about the
- <seealso marker="time_correction#Time_Accuracy">accuracy</seealso>,
- and whether the
- <seealso marker="time_correction#Time_Precision">precision</seealso>
- do align with the resolution. You do,
- however, know that the precision is not better than
- <c>OsMonotonicTimeResolution</c>.</p></item>
-
- <tag><c>{extended, Extended}</c></tag>
- <item><p><c>Extended</c> equals <c>yes</c> if
- the range of time values has been extended;
- otherwise, <c>Extended</c> equals <c>no</c>. The
- range needs to be extended if <c>Function</c>
- returns values that wrap fast. This typically
- is the case when the return value is a 32-bit
- value.</p></item>
-
- <tag><c>{parallel, Parallel}</c></tag>
- <item><p><c>Parallel</c> equals <c>yes</c> if
- <c>Function</c> is called in parallel from multiple
- threads. If it is not called in parallel, because
- calls needs to be serialized, <c>Parallel</c> equals
- <c>no</c>.</p></item>
-
- <tag><c>{time, OsMonotonicTime}</c></tag>
- <item><p><c>OsMonotonicTime</c> equals current OS
- monotonic time in <c>native</c>
- <seealso marker="#type_time_unit">time unit</seealso>.</p></item>
- </taglist>
- </item>
- <tag><marker id="system_info_os_system_time_source"/><c>os_system_time_source</c></tag>
+ <seealso marker="erts:time_correction#OS_Monotonic_Time">OS
+ monotonic time</seealso> that is used by the runtime system.</p>
+ <p>If <c>[]</c> is returned, no OS monotonic time is
+ available. The list contains two-tuples with <c>Key</c>s
+ as first element, and <c>Value</c>s as second element. The
+ order of these tuples is undefined. The following
+ tuples can be part of the list, but more tuples can be
+ introduced in the future:</p>
+ <taglist>
+ <tag><c>{function, Function}</c></tag>
+ <item><p><c>Function</c> is the name of the function
+ used. This tuple always exists if OS monotonic time is
+ available to the runtime system.</p>
+ </item>
+ <tag><c>{clock_id, ClockId}</c></tag>
+ <item><p>This tuple only exists if <c>Function</c>
+ can be used with different clocks. <c>ClockId</c>
+ corresponds to the clock identifier used when calling
+ <c>Function</c>.</p>
+ </item>
+ <tag><c>{resolution, OsMonotonicTimeResolution}</c></tag>
+ <item><p>Highest possible
+ <seealso marker="time_correction#Time_Resolution">
+ resolution</seealso>
+ of current OS monotonic time source as parts per
+ second. If no resolution information can be retrieved
+ from the OS, <c>OsMonotonicTimeResolution</c> is
+ set to the resolution of the time unit of
+ <c>Function</c>s return value. That is, the actual
+ resolution can be lower than
+ <c>OsMonotonicTimeResolution</c>. Notice that
+ the resolution does not say anything about the
+ <seealso marker="time_correction#Time_Accuracy">
+ accuracy</seealso> or whether the
+ <seealso marker="time_correction#Time_Precision">
+ precision</seealso> aligns with the resolution. You do,
+ however, know that the precision is not better than
+ <c>OsMonotonicTimeResolution</c>.</p>
+ </item>
+ <tag><c>{extended, Extended}</c></tag>
+ <item><p><c>Extended</c> equals <c>yes</c> if
+ the range of time values has been extended;
+ otherwise <c>Extended</c> equals <c>no</c>. The
+ range must be extended if <c>Function</c>
+ returns values that wrap fast. This typically
+ is the case when the return value is a 32-bit value.</p>
+ </item>
+ <tag><c>{parallel, Parallel}</c></tag>
+ <item><p><c>Parallel</c> equals <c>yes</c> if
+ <c>Function</c> is called in parallel from multiple
+ threads. If it is not called in parallel, because
+ calls must be serialized, <c>Parallel</c> equals
+ <c>no</c>.</p>
+ </item>
+ <tag><c>{time, OsMonotonicTime}</c></tag>
+ <item><p><c>OsMonotonicTime</c> equals current OS
+ monotonic time in <c>native</c>
+ <seealso marker="#type_time_unit">time unit</seealso>.</p>
+ </item>
+ </taglist>
+ </item>
+ <tag><marker id="system_info_os_system_time_source"/>
+ <c>os_system_time_source</c></tag>
<item>
<p>Returns a list containing information about the source of
- <seealso marker="erts:time_correction#OS_System_Time">OS
- system time</seealso> that is used by the runtime system.</p>
- <p>The list contains two-tuples with <c>Key</c>s
- as first element, and <c>Value</c>s as second element. The
- order if these tuples is undefined. The following
- tuples can be part of the list, but more tuples can be
- introduced in the future:</p>
- <taglist>
- <tag><c>{function, Function}</c></tag>
- <item><p><c>Function</c> is the name of the funcion
- used.</p></item>
-
- <tag><c>{clock_id, ClockId}</c></tag>
- <item><p>This tuple only exist if <c>Function</c>
- can be used with different clocks. <c>ClockId</c>
- corresponds to the clock identifier used when calling
- <c>Function</c>.</p></item>
-
- <tag><c>{resolution, OsSystemTimeResolution}</c></tag>
- <item><p>Highest possible
- <seealso marker="time_correction#Time_Resolution">resolution</seealso>
- of current OS system time source as parts per
- second. If no resolution information can be retrieved
- from the OS, <c>OsSystemTimeResolution</c> is
- set to the resolution of the time unit of
- <c>Function</c>s return value. That is, the actual
- resolution may be lower than
- <c>OsSystemTimeResolution</c>. Also note that
- the resolution does not say anything about the
- <seealso marker="time_correction#Time_Accuracy">accuracy</seealso>,
- and whether the
- <seealso marker="time_correction#Time_Precision">precision</seealso>
- do align with the resolution. You do,
- however, know that the precision is not better than
- <c>OsSystemTimeResolution</c>.</p></item>
-
- <tag><c>{parallel, Parallel}</c></tag>
- <item><p><c>Parallel</c> equals <c>yes</c> if
- <c>Function</c> is called in parallel from multiple
- threads. If it is not called in parallel, because
- calls needs to be serialized, <c>Parallel</c> equals
- <c>no</c>.</p></item>
-
- <tag><c>{time, OsSystemTime}</c></tag>
- <item><p><c>OsSystemTime</c> equals current OS
- system time in <c>native</c>
- <seealso marker="#type_time_unit">time unit</seealso>.</p></item>
- </taglist>
- </item>
- <tag><c>port_parallelism</c></tag>
- <item>
- <marker id="system_info_port_parallelism"></marker>
- <p>Returns the default port parallelism scheduling hint used.
- For more information, see command-line argument
- <seealso marker="erl#+spp">+spp</seealso> in <c>erl(1)</c>.</p></item>
+ <seealso marker="erts:time_correction#OS_System_Time">OS
+ system time</seealso> that is used by the runtime system.</p>
+ <p>The list contains two-tuples with <c>Key</c>s
+ as first element, and <c>Value</c>s as second element. The
+ order if these tuples is undefined. The following
+ tuples can be part of the list, but more tuples can be
+ introduced in the future:</p>
+ <taglist>
+ <tag><c>{function, Function}</c></tag>
+ <item><p><c>Function</c> is the name of the funcion used.</p>
+ </item>
+ <tag><c>{clock_id, ClockId}</c></tag>
+ <item><p>Exists only if <c>Function</c>
+ can be used with different clocks. <c>ClockId</c>
+ corresponds to the clock identifier used when calling
+ <c>Function</c>.</p>
+ </item>
+ <tag><c>{resolution, OsSystemTimeResolution}</c></tag>
+ <item><p>Highest possible
+ <seealso marker="time_correction#Time_Resolution">
+ resolution</seealso>
+ of current OS system time source as parts per
+ second. If no resolution information can be retrieved
+ from the OS, <c>OsSystemTimeResolution</c> is
+ set to the resolution of the time unit of
+ <c>Function</c>s return value. That is, the actual
+ resolution can be lower than
+ <c>OsSystemTimeResolution</c>. Notice that
+ the resolution does not say anything about the
+ <seealso marker="time_correction#Time_Accuracy">
+ accuracy</seealso> or whether the
+ <seealso marker="time_correction#Time_Precision">
+ precision</seealso> do align with the resolution. You do,
+ however, know that the precision is not better than
+ <c>OsSystemTimeResolution</c>.</p>
+ </item>
+ <tag><c>{parallel, Parallel}</c></tag>
+ <item><p><c>Parallel</c> equals <c>yes</c> if
+ <c>Function</c> is called in parallel from multiple
+ threads. If it is not called in parallel, because
+ calls needs to be serialized, <c>Parallel</c> equals
+ <c>no</c>.</p>
+ </item>
+ <tag><c>{time, OsSystemTime}</c></tag>
+ <item><p><c>OsSystemTime</c> equals current OS
+ system time in <c>native</c>
+ <seealso marker="#type_time_unit">time unit</seealso>.</p>
+ </item>
+ </taglist>
+ </item>
+ <tag><c>port_parallelism</c></tag>
+ <item>
+ <marker id="system_info_port_parallelism"></marker>
+ <p>Returns the default port parallelism scheduling hint used.
+ For more information, see command-line argument
+ <seealso marker="erl#+spp"><c>+spp</c></seealso>
+ in <c>erl(1)</c>.</p>
+ </item>
<tag><marker id="system_info_port_count"/><c>port_count</c></tag>
<item>
<p>Returns the number of ports currently existing at the
@@ -7280,9 +8019,10 @@ ok
<p>Returns the maximum number of simultaneously existing
ports at the local node as an integer. This limit can be
configured at startup by using command-line flag
- <seealso marker="erl#+Q">+Q</seealso> in <c>erl(1)</c>.</p>
+ <seealso marker="erl#+Q"><c>+Q</c></seealso> in <c>erl(1)</c>.</p>
</item>
- <tag><marker id="system_info_process_count"/><c>process_count</c></tag>
+ <tag><marker id="system_info_process_count"/>
+ <c>process_count</c></tag>
<item>
<p>Returns the number of processes currently existing at the
local node. The value is given as an integer. This is
@@ -7291,74 +8031,78 @@ ok
</item>
<tag><c>process_limit</c></tag>
<item>
- <marker id="system_info_process_limit"></marker>
+ <marker id="system_info_process_limit"></marker>
<p>Returns the maximum number of simultaneously existing
processes at the local node. The value is given as an
integer. This limit can be configured at startup by using
- command-line flag <seealso marker="erl#+P">+P</seealso>
- in <c>erl(1)</c>.</p>
+ command-line flag <seealso marker="erl#+P"><c>+P</c></seealso>
+ in <c>erl(1)</c>.</p>
</item>
<tag><c>procs</c></tag>
<item>
<p>Returns a binary containing a string of process and port
information formatted as in Erlang crash dumps. For more
- information, see Section
- <seealso marker="erts:crash_dump">"How to interpret the Erlang crash dumps"</seealso>
+ information, see section <seealso marker="erts:crash_dump">
+ How to interpret the Erlang crash dumps</seealso>
in the User's Guide.</p>
</item>
<tag><c>scheduler_bind_type</c></tag>
<item>
- <marker id="system_info_scheduler_bind_type"></marker>
- <p>Returns information about how the user has requested
- schedulers to be bound or not bound.</p>
- <p>Notice that even though a user has requested
- schedulers to be bound, they can silently have failed
- to bind. To inspect the scheduler bindings, call
- <seealso marker="#system_info_scheduler_bindings">erlang:system_info(scheduler_bindings)</seealso>.</p>
- <p>For more information, see command-line argument
- <seealso marker="erts:erl#+sbt">+sbt</seealso>
- in <c>erl(1)</c> and
- <seealso marker="#system_info_scheduler_bindings">erlang:system_info(scheduler_bindings)</seealso>.</p>
+ <marker id="system_info_scheduler_bind_type"></marker>
+ <p>Returns information about how the user has requested
+ schedulers to be bound or not bound.</p>
+ <p>Notice that although a user has requested
+ schedulers to be bound, they can silently have failed
+ to bind. To inspect the scheduler bindings, call
+ <seealso marker="#system_info_scheduler_bindings">
+ <c>erlang:system_info(scheduler_bindings)</c></seealso>.</p>
+ <p>For more information, see command-line argument
+ <seealso marker="erts:erl#+sbt"><c>+sbt</c></seealso>
+ in <c>erl(1)</c> and
+ <seealso marker="#system_info_scheduler_bindings">
+ <c>erlang:system_info(scheduler_bindings)</c></seealso>.</p>
</item>
<tag><c>scheduler_bindings</c></tag>
<item>
- <marker id="system_info_scheduler_bindings"></marker>
- <p>Returns information about the currently used scheduler
- bindings.</p>
- <p>A tuple of a size equal to
- <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>
- is returned. The tuple elements are integers
- or the atom <c>unbound</c>. Logical processor identifiers
- are represented as integers. The <c>N</c>th
- element of the tuple equals the current binding for
- the scheduler with the scheduler identifier equal to
- <c>N</c>. For example, if the schedulers are bound,
- <c>element(erlang:system_info(scheduler_id),
- erlang:system_info(scheduler_bindings))</c> returns
- the identifier of the logical processor that the calling
- process is executing on.</p>
- <p>Notice that only schedulers online can be bound to logical
- processors.</p>
- <p>For more information, see command-line argument
- <seealso marker="erts:erl#+sbt">+sbt</seealso>
- in <c>erl(1)</c> and
- <seealso marker="#system_info_schedulers_online">erlang:system_info(schedulers_online)</seealso>.
- </p>
+ <marker id="system_info_scheduler_bindings"></marker>
+ <p>Returns information about the currently used scheduler
+ bindings.</p>
+ <p>A tuple of a size equal to
+ <seealso marker="#system_info_schedulers">
+ <c>erlang:system_info(schedulers)</c></seealso>
+ is returned. The tuple elements are integers
+ or the atom <c>unbound</c>. Logical processor identifiers
+ are represented as integers. The <c>N</c>th
+ element of the tuple equals the current binding for
+ the scheduler with the scheduler identifier equal to
+ <c>N</c>. For example, if the schedulers are bound,
+ <c>element(erlang:system_info(scheduler_id),
+ erlang:system_info(scheduler_bindings))</c> returns
+ the identifier of the logical processor that the calling
+ process is executing on.</p>
+ <p>Notice that only schedulers online can be bound to logical
+ processors.</p>
+ <p>For more information, see command-line argument
+ <seealso marker="erts:erl#+sbt"><c>+sbt</c></seealso>
+ in <c>erl(1)</c> and
+ <seealso marker="#system_info_schedulers_online">
+ <c>erlang:system_info(schedulers_online)</c></seealso>.</p>
</item>
<tag><c>scheduler_id</c></tag>
<item>
- <marker id="system_info_scheduler_id"></marker>
+ <marker id="system_info_scheduler_id"></marker>
<p>Returns the scheduler ID (<c>SchedulerId</c>) of the
scheduler thread that the calling process is executing
on. <c><anno>SchedulerId</anno></c> is a positive integer,
- where
- <c><![CDATA[1 <= SchedulerId <= erlang:system_info(schedulers)]]></c>.
- See also
- <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>.</p>
+ where <c><![CDATA[1 <= SchedulerId <=
+ erlang:system_info(schedulers)]]></c>.</p>
+ <p>See also
+ <seealso marker="#system_info_schedulers">
+ <c>erlang:system_info(schedulers)</c></seealso>.</p>
</item>
<tag><c>schedulers</c></tag>
<item>
- <marker id="system_info_schedulers"></marker>
+ <marker id="system_info_schedulers"></marker>
<p>Returns the number of scheduler threads used by
the emulator. Scheduler threads online schedules Erlang
processes and Erlang ports, and execute Erlang code
@@ -7366,43 +8110,57 @@ ok
<p>The number of scheduler threads is determined at
emulator boot time and cannot be changed later.
However, the number of schedulers online can
- be changed at any time.</p>
+ be changed at any time.</p>
<p>See also
- <seealso marker="#system_flag_schedulers_online">erlang:system_flag(schedulers_online, SchedulersOnline)</seealso>,
- <seealso marker="#system_info_schedulers_online">erlang:system_info(schedulers_online)</seealso>,
- <seealso marker="#system_info_scheduler_id">erlang:system_info(scheduler_id)</seealso>,
- <seealso marker="#system_flag_multi_scheduling">erlang:system_flag(multi_scheduling, BlockState)</seealso>,
- <seealso marker="#system_info_multi_scheduling">erlang:system_info(multi_scheduling)</seealso>,
- and
- <seealso marker="#system_info_multi_scheduling_blockers">erlang:system_info(multi_scheduling_blockers)</seealso>.</p>
+ <seealso marker="#system_flag_schedulers_online">
+ <c>erlang:system_flag(schedulers_online,
+ SchedulersOnline)</c></seealso>,
+ <seealso marker="#system_info_schedulers_online">
+ <c>erlang:system_info(schedulers_online)</c></seealso>,
+ <seealso marker="#system_info_scheduler_id">
+ <c>erlang:system_info(scheduler_id)</c></seealso>,
+ <seealso marker="#system_flag_multi_scheduling">
+ <c>erlang:system_flag(multi_scheduling, BlockState)</c></seealso>,
+ <seealso marker="#system_info_multi_scheduling">
+ <c>erlang:system_info(multi_scheduling)</c></seealso>,
+ <seealso marker="#system_info_normal_multi_scheduling_blockers">
+ <c>erlang:system_info(normal_multi_scheduling_blockers)</c></seealso>
+ and <seealso marker="#system_info_multi_scheduling_blockers">
+ <c>erlang:system_info(multi_scheduling_blockers)</c></seealso>.
+ </p>
</item>
<tag><c>schedulers_online</c></tag>
<item>
- <marker id="system_info_schedulers_online"></marker>
+ <marker id="system_info_schedulers_online"></marker>
<p>Returns the number of schedulers online. The scheduler
- identifiers of schedulers online satisfy the relationship
- <c><![CDATA[1 <= SchedulerId <= erlang:system_info(schedulers_online)]]></c>.</p>
- <p>For more information, see
- <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>
- and
- <seealso marker="#system_flag_schedulers_online">erlang:system_flag(schedulers_online, SchedulersOnline)</seealso>.</p>
+ identifiers of schedulers online satisfy the relationship
+ <c><![CDATA[1 <= SchedulerId <=
+ erlang:system_info(schedulers_online)]]></c>.</p>
+ <p>For more information, see
+ <seealso marker="#system_info_schedulers">
+ <c>erlang:system_info(schedulers)</c></seealso> and
+ <seealso marker="#system_flag_schedulers_online">
+ <c>erlang:system_flag(schedulers_online,
+ SchedulersOnline)</c></seealso>.</p>
</item>
<tag><c>smp_support</c></tag>
<item>
<p>Returns <c>true</c> if the emulator has been compiled
with SMP support, otherwise <c>false</c> is returned.</p>
</item>
- <tag><marker id="system_info_start_time"/><c>start_time</c></tag>
- <item><p>The <seealso marker="#monotonic_time/0">Erlang monotonic
- time</seealso> in <c>native</c>
- <seealso marker="#type_time_unit">time unit</seealso> at the
- time when current Erlang runtime system instance started. See also
- <seealso marker="#system_info_end_time"><c>erlang:system_info(end_time)</c></seealso>.
- </p></item>
+ <tag><marker id="system_info_start_time"/><c>start_time</c></tag>
+ <item>
+ <p>The <seealso marker="#monotonic_time/0">Erlang monotonic
+ time</seealso> in <c>native</c>
+ <seealso marker="#type_time_unit">time unit</seealso> at the
+ time when current Erlang runtime system instance started.</p>
+ <p>See also <seealso marker="#system_info_end_time">
+ <c>erlang:system_info(end_time)</c></seealso>.</p>
+ </item>
<tag><c>system_version</c></tag>
<item>
<p>Returns a string containing version number and
- some important properties, such as the number of schedulers.</p>
+ some important properties, such as the number of schedulers.</p>
</item>
<tag><c>system_architecture</c></tag>
<item>
@@ -7416,105 +8174,114 @@ ok
</item>
<tag><c>thread_pool_size</c></tag>
<item>
- <marker id="system_info_thread_pool_size"></marker>
+ <marker id="system_info_thread_pool_size"></marker>
<p>Returns the number of async threads in the async thread
pool used for asynchronous driver calls
- (<seealso marker="erts:erl_driver#driver_async">driver_async()</seealso>).
+ (<seealso marker="erts:erl_driver#driver_async">
+ <c>erl_driver:driver_async()</c></seealso>).
The value is given as an integer.</p>
</item>
-
- <tag><c>time_correction</c></tag>
- <item>
- <marker id="system_info_time_correction"></marker>
- <p>Returns a boolean value indicating whether
- <seealso marker="time_correction#Time_Correction">time correction</seealso>
- is enabled or not.
- </p></item>
- <tag><c>time_offset</c></tag>
- <item>
- <marker id="system_info_time_offset"></marker>
- <p>Returns the state of the time offset:</p>
- <taglist>
- <tag><c>preliminary</c></tag>
- <item><p>The time offset is preliminary, and will be changed
- at a later time when being finalized. The preliminary time offset
- is used during the preliminary phase of the
- <seealso marker="time_correction#Single_Time_Warp_Mode">single
- time warp mode</seealso>.</p></item>
-
- <tag><c>final</c></tag>
- <item><p>The time offset is final. This either because
- <seealso marker="time_correction#No_Time_Warp_Mode">no
- time warp mode</seealso> is used, or because the time
- offset have been finalized when
- <seealso marker="time_correction#Single_Time_Warp_Mode">single
- time warp mode</seealso> is used.</p></item>
-
- <tag><c>volatile</c></tag>
- <item><p>The time offset is volatile. That is, it can
- change at any time. This is because
- <seealso marker="time_correction#Multi_Time_Warp_Mode">multi
- time warp mode</seealso> is used.</p></item>
- </taglist>
- </item>
- <tag><marker id="system_info_time_warp_mode"/><c>time_warp_mode</c></tag>
- <item><p>Returns a value identifying the
- <seealso marker="time_correction#Time_Warp_Modes">time warp
- mode</seealso> being used:</p>
- <taglist>
- <tag><c>no_time_warp</c></tag>
- <item><p>The <seealso marker="time_correction#No_Time_Warp_Mode">no
- time warp mode</seealso> is used.</p></item>
-
- <tag><c>single_time_warp</c></tag>
- <item><p>The <seealso marker="time_correction#Single_Time_Warp_Mode">single
- time warp mode</seealso> is used.</p></item>
-
- <tag><c>multi_time_warp</c></tag>
- <item><p>The <seealso marker="time_correction#Multi_Time_Warp_Mode">multi
- time warp mode</seealso> is used.</p></item>
- </taglist>
- </item>
+ <tag><c>time_correction</c></tag>
+ <item>
+ <marker id="system_info_time_correction"></marker>
+ <p>Returns a boolean value indicating whether
+ <seealso marker="time_correction#Time_Correction">
+ time correction</seealso> is enabled or not.</p>
+ </item>
+ <tag><c>time_offset</c></tag>
+ <item>
+ <marker id="system_info_time_offset"></marker>
+ <p>Returns the state of the time offset:</p>
+ <taglist>
+ <tag><c>preliminary</c></tag>
+ <item>
+ <p>The time offset is preliminary, and will be changed
+ and finalized later. The preliminary time offset
+ is used during the preliminary phase of the
+ <seealso marker="time_correction#Single_Time_Warp_Mode">
+ single time warp mode</seealso>.</p>
+ </item>
+ <tag><c>final</c></tag>
+ <item>
+ <p>The time offset is final. This either because
+ <seealso marker="time_correction#No_Time_Warp_Mode">
+ no time warp mode</seealso> is used, or because the time
+ offset have been finalized when
+ <seealso marker="time_correction#Single_Time_Warp_Mode">
+ single time warp mode</seealso> is used.</p>
+ </item>
+ <tag><c>volatile</c></tag>
+ <item>
+ <p>The time offset is volatile. That is, it can
+ change at any time. This is because
+ <seealso marker="time_correction#Multi_Time_Warp_Mode">
+ multi-time warp mode</seealso> is used.</p>
+ </item>
+ </taglist>
+ </item>
+ <tag><marker id="system_info_time_warp_mode"/>
+ <c>time_warp_mode</c></tag>
+ <item>
+ <p>Returns a value identifying the
+ <seealso marker="time_correction#Time_Warp_Modes">
+ time warp mode</seealso> that is used:</p>
+ <taglist>
+ <tag><c>no_time_warp</c></tag>
+ <item>The <seealso marker="time_correction#No_Time_Warp_Mode">
+ no time warp mode</seealso> is used.
+ </item>
+ <tag><c>single_time_warp</c></tag>
+ <item>The <seealso marker="time_correction#Single_Time_Warp_Mode">
+ single time warp mode</seealso> is used.
+ </item>
+ <tag><c>multi_time_warp</c></tag>
+ <item>The <seealso marker="time_correction#Multi_Time_Warp_Mode">
+ multi-time warp mode</seealso> is used.
+ </item>
+ </taglist>
+ </item>
<tag><c>tolerant_timeofday</c></tag>
<item>
- <marker id="system_info_tolerant_timeofday"></marker>
- <p>Returns whether a pre erts-7.0 backwards compatible compensation
- for sudden changes of system time is <c>enabled</c> or <c>disabled</c>.
- Such compensation is <c>enabled</c> when the
- <seealso marker="#system_info_time_offset">time offset</seealso> is
- <c>final</c>, and
- <seealso marker="#system_info_time_correction">time correction</seealso>
- is enabled.</p>
+ <marker id="system_info_tolerant_timeofday"></marker>
+ <p>Returns whether a pre ERTS 7.0 backwards compatible
+ compensation for sudden changes of system time is <c>enabled</c>
+ or <c>disabled</c>. Such compensation is <c>enabled</c> when the
+ <seealso marker="#system_info_time_offset">time offset</seealso>
+ is <c>final</c>, and
+ <seealso marker="#system_info_time_correction">
+ time correction</seealso> is enabled.</p>
</item>
<tag><c>trace_control_word</c></tag>
<item>
<p>Returns the value of the node trace control word. For
- more information, see function <c>get_tcw</c> in Section
- <seealso marker="erts:match_spec#get_tcw">Match Specifications in Erlang</seealso> in the User's Guide.</p>
+ more information, see function <c>get_tcw</c> in section
+ <seealso marker="erts:match_spec#get_tcw">
+ Match Specifications in Erlang</seealso> in the User's Guide.</p>
</item>
<tag><c>update_cpu_info</c></tag>
<item>
- <marker id="update_cpu_info"></marker>
+ <marker id="update_cpu_info"></marker>
<p>The runtime system rereads the CPU information available
- and updates its internally stored information about the
- <seealso marker="#system_info_cpu_topology_detected">detected
- CPU topology</seealso> and the number of logical processors
- <seealso marker="#logical_processors">configured</seealso>,
- <seealso marker="#logical_processors_online">online</seealso>, and
- <seealso marker="#logical_processors_available">available</seealso>.</p>
- <p>If the CPU information has changed since the last time
- it was read, the atom <c>changed</c> is returned, otherwise
- the atom <c>unchanged</c>. If the CPU information has changed,
- you probably want to
- <seealso marker="#system_flag_schedulers_online">adjust the
- number of schedulers online</seealso>. You typically want
- to have as many schedulers online as
- <seealso marker="#logical_processors_available">logical
- processors available</seealso>.</p>
+ and updates its internally stored information about the
+ <seealso marker="#system_info_cpu_topology_detected">detected
+ CPU topology</seealso> and the number of logical processors
+ <seealso marker="#logical_processors">configured</seealso>,
+ <seealso marker="#logical_processors_online">online</seealso>,
+ and <seealso marker="#logical_processors_available">
+ available</seealso>.</p>
+ <p>If the CPU information has changed since the last time
+ it was read, the atom <c>changed</c> is returned, otherwise
+ the atom <c>unchanged</c>. If the CPU information has changed,
+ you probably want to
+ <seealso marker="#system_flag_schedulers_online">adjust the
+ number of schedulers online</seealso>. You typically want
+ to have as many schedulers online as
+ <seealso marker="#logical_processors_available">logical
+ processors available</seealso>.</p>
</item>
<tag><c>version</c></tag>
<item>
- <marker id="system_info_version"></marker>
+ <marker id="system_info_version"></marker>
<p>Returns a string containing the version number of the
emulator.</p>
</item>
@@ -7533,19 +8300,19 @@ ok
</item>
<tag><c>{wordsize, external}</c></tag>
<item>
- <p>Returns the true word size of the emulator, that is,
- the size of a pointer. The value is given in bytes
- as an integer. On a pure 32-bit architecture, 4 is
- returned. On both a half word and on a pure
- 64-bit architecture, 8 is returned.</p>
+ <p>Returns the true word size of the emulator, that is,
+ the size of a pointer. The value is given in bytes
+ as an integer. On a pure 32-bit architecture, 4 is
+ returned. On both a half word and on a pure
+ 64-bit architecture, 8 is returned.</p>
</item>
</taglist>
<note>
<p>Argument <c>scheduler</c> has changed name to
<c>scheduler_id</c> to avoid mix up with argument
<c>schedulers</c>. Argument <c>scheduler</c> was
- introduced in <c>ERTS</c> 5.5 and renamed in
- <c>ERTS</c> 5.5.1.</p>
+ introduced in ERTS 5.5 and renamed in
+ ERTS 5.5.1.</p>
</note>
</desc>
</func>
@@ -7556,32 +8323,35 @@ ok
<type name="system_monitor_option"/>
<desc>
<p>Returns the current system monitoring settings set by
- <seealso marker="#system_monitor/2">erlang:system_monitor/2</seealso>
+ <seealso marker="#system_monitor/2">
+ <c>erlang:system_monitor/2</c></seealso>
as <c>{<anno>MonitorPid</anno>, <anno>Options</anno>}</c>,
- or <c>undefined</c> if there
- are no settings. The order of the options can be different
- from the one that was set.</p>
+ or <c>undefined</c> if no settings exist. The order of the
+ options can be different from the one that was set.</p>
</desc>
</func>
<func>
<name name="system_monitor" arity="1"/>
- <fsummary>Sets or clears system performance monitoring options.</fsummary>
+ <fsummary>Set or clear system performance monitoring options.</fsummary>
<type name="system_monitor_option"/>
<desc>
<p>When called with argument <c>undefined</c>, all
system performance monitoring settings are cleared.</p>
<p>Calling the function with <c>{<anno>MonitorPid</anno>,
<anno>Options</anno>}</c> as argument is the same as calling
- <seealso marker="#system_monitor/2"><c>erlang:system_monitor(<anno>MonitorPid</anno>, <anno>Options</anno>)</c></seealso>.</p>
+ <seealso marker="#system_monitor/2">
+ <c>erlang:system_monitor(<anno>MonitorPid</anno>,
+ <anno>Options</anno>)</c></seealso>.</p>
<p>Returns the previous system monitor settings just like
- <seealso marker="#system_monitor/0">erlang:system_monitor/0</seealso>.</p>
+ <seealso marker="#system_monitor/0">
+ <c>erlang:system_monitor/0</c></seealso>.</p>
</desc>
</func>
<func>
<name name="system_monitor" arity="2"/>
- <fsummary>Sets system performance monitoring options.</fsummary>
+ <fsummary>Set system performance monitoring options.</fsummary>
<type name="system_monitor_option"/>
<desc>
<p>Sets the system performance monitoring options.
@@ -7600,12 +8370,12 @@ ok
<p>One of the tuples is <c>{timeout, GcTime}</c>, where
<c>GcTime</c> is the time for the garbage
collection in milliseconds. The other tuples are
- tagged with <c>heap_size</c>, <c>heap_block_size</c>
+ tagged with <c>heap_size</c>, <c>heap_block_size</c>,
<c>stack_size</c>, <c>mbuf_size</c>, <c>old_heap_size</c>,
and <c>old_heap_block_size</c>. These tuples are
explained in the description of trace message
- <seealso marker="#gc_start">gc_start</seealso> (see
- <seealso marker="#trace/3">erlang:trace/3</seealso>).
+ <seealso marker="#gc_minor_start"><c>gc_minor_start</c></seealso>
+ (see <seealso marker="#trace/3"><c>erlang:trace/3</c></seealso>).
New tuples can be added, and the order of the tuples in
the <c>Info</c> list can be changed at any time without
prior notice.</p>
@@ -7636,7 +8406,7 @@ ok
<c>timeout</c>, <c>ready_input</c>, <c>ready_output</c>,
<c>event</c>, and <c>outputv</c> (when the port
is used by distribution). Value <c>Millis</c> in
- the <c>timeout</c> tuple informs about the
+ tuple <c>timeout</c> informs about the
uninterrupted execution time of the process or port, which
always is equal to or higher than the <c>Time</c> value
supplied when starting the trace. New tuples can be
@@ -7647,7 +8417,7 @@ ok
drivers that take too long to execute. 1 ms is
considered a good maximum time for a driver callback
or a NIF. However, a time-sharing system is usually to
- consider everything below 100 ms as "possible" and
+ consider everything &lt; 100 ms as "possible" and
fairly "normal". However, longer schedule times can
indicate swapping or a misbehaving NIF/driver.
Misbehaving NIFs and drivers can cause bad resource
@@ -7662,12 +8432,14 @@ ok
<c>GcPid</c> and <c>Info</c>
are the same as for <c>long_gc</c> earlier, except that
the tuple tagged with <c>timeout</c> is not present.</p>
- <p>As of <c>ERTS</c> 5.6, the monitor message is sent
- if the sum of the sizes of all memory blocks allocated
- for all heap generations is equal to or higher than <c>Size</c>.
- Previously the monitor message was sent if the memory block
- allocated for the youngest generation was equal to or higher
- than <c>Size</c>.</p>
+ <p>The monitor message is sent if the sum of the sizes of
+ all memory blocks allocated for all heap generations after
+ a garbage collection is equal to or higher than <c>Size</c>.</p>
+ <p>When a process is killed by
+ <seealso marker="#process_flag_max_heap_size">
+ <c>max_heap_size</c></seealso>, it is killed before the
+ garbage collection is complete and thus no large heap message
+ is sent.</p>
</item>
<tag><c>busy_port</c></tag>
<item>
@@ -7689,7 +8461,8 @@ ok
</item>
</taglist>
<p>Returns the previous system monitor settings just like
- <seealso marker="#system_monitor/0">erlang:system_monitor/0</seealso>.</p>
+ <seealso marker="#system_monitor/0">
+ <c>erlang:system_monitor/0</c></seealso>.</p>
<note>
<p>If a monitoring process gets so large that it itself
starts to cause system monitor messages when garbage
@@ -7714,7 +8487,8 @@ ok
<type name="system_profile_option"/>
<desc>
<p>Returns the current system profiling settings set by
- <seealso marker="#system_profile/2">erlang:system_profile/2</seealso>
+ <seealso marker="#system_profile/2">
+ <c>erlang:system_profile/2</c></seealso>
as <c>{<anno>ProfilerPid</anno>, <anno>Options</anno>}</c>,
or <c>undefined</c> if there
are no settings. The order of the options can be different
@@ -7734,172 +8508,190 @@ ok
<taglist>
<tag><c>exclusive</c></tag>
<item>
- <p>If a synchronous call to a port from a process is done, the
- calling process is considered not runnable during the call
- runtime to the port. The calling process is notified as
- <c>inactive</c>, and later <c>active</c> when the port
- callback returns.</p>
+ <p>If a synchronous call to a port from a process is done, the
+ calling process is considered not runnable during the call
+ runtime to the port. The calling process is notified as
+ <c>inactive</c>, and later <c>active</c> when the port
+ callback returns.</p>
</item>
<tag><c>monotonic_timestamp</c></tag>
<item>
- <p>Timestamps in profile messages will use
- <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
- monotonic time</seealso>. The time-stamp (Ts) has the same
- format and value as produced by
- <c>erlang:monotonic_time(nano_seconds)</c>.</p>
+ <p>Time stamps in profile messages use
+ <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
+ monotonic time</seealso>. The time stamp (Ts) has the same
+ format and value as produced by
+ <c>erlang:monotonic_time(nanosecond)</c>.</p>
</item>
<tag><c>runnable_procs</c></tag>
<item>
- <p>If a process is put into or removed from the run queue, a
- message, <c>{profile, Pid, State, Mfa, Ts}</c>, is sent to
- <c><anno>ProfilerPid</anno></c>. Running processes that
- are reinserted into the run queue after having been
- preempted do not trigger this message.</p>
+ <p>If a process is put into or removed from the run queue, a
+ message, <c>{profile, Pid, State, Mfa, Ts}</c>, is sent to
+ <c><anno>ProfilerPid</anno></c>. Running processes that
+ are reinserted into the run queue after having been
+ pre-empted do not trigger this message.</p>
</item>
<tag><c>runnable_ports</c></tag>
<item>
- <p>If a port is put into or removed from the run queue, a
- message, <c>{profile, Port, State, 0, Ts}</c>, is sent to
- <c><anno>ProfilerPid</anno></c>.</p>
+ <p>If a port is put into or removed from the run queue, a
+ message, <c>{profile, Port, State, 0, Ts}</c>, is sent to
+ <c><anno>ProfilerPid</anno></c>.</p>
</item>
<tag><c>scheduler</c></tag>
<item>
- <p>If a scheduler is put to sleep or awoken, a message,
- <c>{profile, scheduler, Id, State, NoScheds, Ts}</c>, is
- sent to <c><anno>ProfilerPid</anno></c>.</p>
+ <p>If a scheduler is put to sleep or awoken, a message,
+ <c>{profile, scheduler, Id, State, NoScheds, Ts}</c>, is
+ sent to <c><anno>ProfilerPid</anno></c>.</p>
</item>
<tag><c>strict_monotonic_timestamp</c></tag>
<item>
- <p>Timestamps in profile messages will consisting of
- <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
- monotonic time</seealso> and a monotonically increasing
- integer. The time-stamp (Ts) has the same format and value
- as produced by <c>{erlang:monotonic_time(nano_seconds),
- erlang:unique_integer([monotonic])}</c>.</p>
+ <p>Time stamps in profile messages consist of
+ <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
+ monotonic time</seealso> and a monotonically increasing
+ integer. The time stamp (Ts) has the same format and value
+ as produced by <c>{erlang:monotonic_time(nanosecond),
+ erlang:unique_integer([monotonic])}</c>.</p>
</item>
<tag><c>timestamp</c></tag>
<item>
- <p>Timestamps in profile messages will include a
- time-stamp (Ts) that has the same form as returned by
- <c>erlang:now()</c>. This is also the default if no
- timestamp flag is given. If <c>cpu_timestamp</c> has
- been enabled via <c>erlang:trace/3</c>, this will also
- effect the timestamp produced in profiling messages
- when <c>timestamp</c> flag is enabled.</p>
+ <p>Time stamps in profile messages include a
+ time stamp (Ts) that has the same form as returned by
+ <c>erlang:now()</c>. This is also the default if no
+ time stamp flag is specified. If <c>cpu_timestamp</c> has
+ been enabled through
+ <seealso marker="erlang:trace/3"><c>erlang:trace/3</c></seealso>,
+ this also effects the time stamp produced in profiling messages
+ when flag <c>timestamp</c> is enabled.</p>
</item>
</taglist>
- <note><p><c>erlang:system_profile</c> is considered experimental
- and its behavior can change in a future release.</p>
+ <note>
+ <p><c>erlang:system_profile</c> behavior can change
+ in a future release.</p>
</note>
</desc>
</func>
+
<func>
<name name="system_time" arity="0"/>
- <fsummary>Current Erlang system time</fsummary>
- <desc>
- <p>Returns current
- <seealso marker="time_correction#Erlang_System_Time">Erlang system time</seealso>
- in <c>native</c>
- <seealso marker="#type_time_unit">time unit</seealso>.</p>
-
- <p>Calling <c>erlang:system_time()</c> is equivalent to:
- <seealso marker="#monotonic_time/0"><c>erlang:monotonic_time()</c></seealso><c>
- +
- </c><seealso marker="#time_offset/0"><c>erlang:time_offset()</c></seealso>.</p>
-
- <note><p>This time is <em>not</em> a monotonically increasing time
- in the general case. For more information, see the documentation of
- <seealso marker="time_correction#Time_Warp_Modes">time warp modes</seealso> in the
- ERTS User's Guide.</p></note>
+ <fsummary>Current Erlang system time.</fsummary>
+ <desc>
+ <p>Returns current
+ <seealso marker="time_correction#Erlang_System_Time">
+ Erlang system time</seealso> in <c>native</c>
+ <seealso marker="#type_time_unit">time unit</seealso>.</p>
+ <p>Calling <c>erlang:system_time()</c> is equivalent to
+ <seealso marker="#monotonic_time/0">
+ <c>erlang:monotonic_time()</c></seealso><c> +
+ </c><seealso marker="#time_offset/0">
+ <c>erlang:time_offset()</c></seealso>.</p>
+ <note>
+ <p>This time is <em>not</em> a monotonically increasing time
+ in the general case. For more information, see the documentation of
+ <seealso marker="time_correction#Time_Warp_Modes">
+ time warp modes</seealso> in the User's Guide.</p>
+ </note>
</desc>
</func>
+
<func>
<name name="system_time" arity="1"/>
- <fsummary>Current Erlang system time</fsummary>
- <desc>
- <p>Returns current
- <seealso marker="time_correction#Erlang_System_Time">Erlang system time</seealso>
- converted into the <c><anno>Unit</anno></c> passed as argument.</p>
-
- <p>Calling <c>erlang:system_time(<anno>Unit</anno>)</c> is equivalent to:
- <seealso marker="#convert_time_unit/3"><c>erlang:convert_time_unit</c></seealso><c>(</c><seealso marker="#system_time/0"><c>erlang:system_time()</c></seealso><c>,
- native, <anno>Unit</anno>)</c>.</p>
-
- <note><p>This time is <em>not</em> a monotonically increasing time
- in the general case. For more information, see the documentation of
- <seealso marker="time_correction#Time_Warp_Modes">time warp modes</seealso> in the
- ERTS User's Guide.</p></note>
+ <fsummary>Current Erlang system time.</fsummary>
+ <desc>
+ <p>Returns current
+ <seealso marker="time_correction#Erlang_System_Time">
+ Erlang system time</seealso>
+ converted into the <c><anno>Unit</anno></c> passed as argument.</p>
+ <p>Calling <c>erlang:system_time(<anno>Unit</anno>)</c> is equivalent
+ to <seealso marker="#convert_time_unit/3">
+ <c>erlang:convert_time_unit</c></seealso><c>(</c><seealso
+ marker="#system_time/0"><c>erlang:system_time()</c></seealso><c>,
+ native, <anno>Unit</anno>)</c>.</p>
+ <note>
+ <p>This time is <em>not</em> a monotonically increasing time
+ in the general case. For more information, see the documentation of
+ <seealso marker="time_correction#Time_Warp_Modes">
+ time warp modes</seealso> in the User's Guide.</p>
+ </note>
</desc>
</func>
+
<func>
<name name="term_to_binary" arity="1"/>
- <fsummary>Encodes a term to an Erlang external term format binary.</fsummary>
+ <fsummary>Encode a term to an Erlang external term format binary.
+ </fsummary>
<desc>
<p>Returns a binary data object that is the result of encoding
- <c><anno>Term</anno></c> according to the Erlang external
- term format.</p>
+ <c><anno>Term</anno></c> according to the
+ <seealso marker="erts:erl_ext_dist">Erlang external
+ term format.</seealso></p>
<p>This can be used for various purposes, for example,
writing a term to a file in an efficient way, or sending an
Erlang term to some type of communications channel not
supported by distributed Erlang.</p>
- <p>See also
- <seealso marker="#binary_to_term/1">binary_to_term/1</seealso>.</p>
+ <pre>
+> <input>Bin = term_to_binary(hello).</input>
+&lt;&lt;131,100,0,5,104,101,108,108,111>>
+> <input>hello = binary_to_term(Bin).</input>
+hello
+</pre>
+ <p>See also <seealso marker="#binary_to_term/1">
+ <c>binary_to_term/1</c></seealso>.</p>
</desc>
</func>
<func>
<name name="term_to_binary" arity="2"/>
- <fsummary>Encodes a term to en Erlang external term format binary.</fsummary>
+ <fsummary>Encode a term to en Erlang external term format binary.
+ </fsummary>
<desc>
<p>Returns a binary data object that is the result of encoding
<c><anno>Term</anno></c> according to the Erlang external
term format.</p>
<p>If option <c>compressed</c> is provided, the external term
format is compressed. The compressed format is automatically
- recognized by <c>binary_to_term/1</c> as from Erlang R7B.</p>
+ recognized by <c>binary_to_term/1</c> as from Erlang/OTP R7B.</p>
<p>A compression level can be specified by giving option
<c>{compressed, <anno>Level</anno>}</c>.
<c><anno>Level</anno></c> is an integer
with range 0..9, where:</p>
<list type="bulleted">
- <item><c>0</c> - No compression is done (it is the same as
- giving no <c>compressed</c> option).</item>
- <item><c>1</c> - Takes least time but may not compress
- as well as the higher levels.</item>
- <item><c>6</c> - Default level when option <c>compressed</c>
- is provided.</item>
- <item><c>9</c> - Takes most time and tries to produce a smaller
+ <item><p><c>0</c> - No compression is done (it is the same as
+ giving no <c>compressed</c> option).</p></item>
+ <item><p><c>1</c> - Takes least time but may not compress
+ as well as the higher levels.</p></item>
+ <item><p><c>6</c> - Default level when option <c>compressed</c>
+ is provided.</p></item>
+ <item><p><c>9</c> - Takes most time and tries to produce a smaller
result. Notice "tries" in the preceding sentence; depending
on the input term, level 9 compression either does or does
- not produce a smaller result than level 1 compression.</item>
+ not produce a smaller result than level 1 compression.</p></item>
</list>
<p>Option <c>{minor_version, <anno>Version</anno>}</c>
- can be used to control
- some encoding details. This option was introduced in OTP R11B-4.
+ can be used to control some
+ encoding details. This option was introduced in Erlang/OTP R11B-4.
The valid values for <c><anno>Version</anno></c> are
<c>0</c> and <c>1</c>.</p>
- <p>As from OTP 17.0, <c>{minor_version, 1}</c> is the default. It
+ <p>As from Erlang/OTP 17.0, <c>{minor_version, 1}</c> is the default. It
forces any floats in the term to be encoded in a more
space-efficient and exact way (namely in the 64-bit IEEE format,
rather than converted to a textual representation).</p>
- <p>As from OTP R11B-4, <c>binary_to_term/1</c> can decode this
+ <p>As from Erlang/OTP R11B-4, <c>binary_to_term/1</c> can decode this
representation.</p>
<p><c>{minor_version, 0}</c> means that floats are encoded
using a textual representation. This option is useful to
- ensure that releases before OTP R11B-4 can decode resulting
+ ensure that releases before Erlang/OTP R11B-4 can decode resulting
binary.</p>
- <p>See also
- <seealso marker="#binary_to_term/1">binary_to_term/1</seealso>.</p>
+ <p>See also <seealso marker="#binary_to_term/1">
+ <c>binary_to_term/1</c></seealso>.</p>
</desc>
</func>
<func>
<name name="throw" arity="1"/>
- <fsummary>Throws an exception.</fsummary>
+ <fsummary>Throw an exception.</fsummary>
<desc>
<p>A non-local return from a function. If evaluated within a
- <c>catch</c>, <c>catch</c> returns value <c><anno>Any</anno></c>.</p>
- <p>Example:</p>
+ <c>catch</c>, <c>catch</c> returns value <c><anno>Any</anno></c>.
+ Example:</p>
<pre>
> <input>catch throw({hello, there}).</input>
{hello,there}</pre>
@@ -7913,8 +8705,7 @@ ok
<desc>
<p>Returns the current time as <c>{Hour, Minute, Second}</c>.</p>
<p>The time zone and Daylight Saving Time correction depend on
- the underlying OS.</p>
- <p>Example:</p>
+ the underlying OS. Example:</p>
<pre>
> <input>time().</input>
{9,42,44}</pre>
@@ -7923,86 +8714,94 @@ ok
<func>
<name name="time_offset" arity="0"/>
- <fsummary>Current time offset</fsummary>
- <desc>
- <p>Returns the current time offset between
- <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang monotonic time</seealso>
- and
- <seealso marker="time_correction#Erlang_System_Time">Erlang system time</seealso> in
- <c>native</c> <seealso marker="#type_time_unit">time unit</seealso>.
- Current time offset added to an Erlang monotonic time gives
- corresponding Erlang system time.</p>
-
- <p>The time offset may or may not change during operation depending
- on the <seealso marker="time_correction#Time_Warp_Modes">time
- warp mode</seealso> used.</p>
-
- <note>
- <p>A change in time offset may be observed at slightly
- different points in time by different processes.</p>
-
- <p>If the runtime system is in
- <seealso marker="time_correction#Multi_Time_Warp_Mode">multi
- time warp mode</seealso>, the time offset will be changed when
- the runtime system detects that the
- <seealso marker="time_correction#OS_System_Time">OS system
- time</seealso> has changed. The runtime system will, however,
- not detect this immediately when it happens. A task checking
- the time offset is scheduled to execute at least once a minute,
- so under normal operation this should be detected within a
- minute, but during heavy load it might take longer time.</p>
- </note>
+ <fsummary>Current time offset.</fsummary>
+ <desc>
+ <p>Returns the current time offset between
+ <seealso marker="time_correction#Erlang_Monotonic_Time">
+ Erlang monotonic time</seealso> and
+ <seealso marker="time_correction#Erlang_System_Time">
+ Erlang system time</seealso> in
+ <c>native</c> <seealso marker="#type_time_unit">time unit</seealso>.
+ Current time offset added to an Erlang monotonic time gives
+ corresponding Erlang system time.</p>
+ <p>The time offset may or may not change during operation depending
+ on the <seealso marker="time_correction#Time_Warp_Modes">time
+ warp mode</seealso> used.</p>
+ <note>
+ <p>A change in time offset can be observed at slightly
+ different points in time by different processes.</p>
+ <p>If the runtime system is in
+ <seealso marker="time_correction#Multi_Time_Warp_Mode">multi-time
+ warp mode</seealso>, the time offset is changed when
+ the runtime system detects that the
+ <seealso marker="time_correction#OS_System_Time">OS system
+ time</seealso> has changed. The runtime system will, however,
+ not detect this immediately when it occurs. A task checking
+ the time offset is scheduled to execute at least once a minute;
+ so, under normal operation this is to be detected within a
+ minute, but during heavy load it can take longer time.</p>
+ </note>
</desc>
</func>
+
<func>
<name name="time_offset" arity="1"/>
- <fsummary>Current time offset</fsummary>
+ <fsummary>Current time offset.</fsummary>
<desc>
- <p>Returns the current time offset between
- <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang monotonic time</seealso>
- and
- <seealso marker="time_correction#Erlang_System_Time">Erlang system time</seealso>
- converted into the <c><anno>Unit</anno></c> passed as argument.</p>
-
- <p>Same as calling
- <seealso marker="#convert_time_unit/3"><c>erlang:convert_time_unit</c></seealso><c>(</c><seealso marker="#time_offset/0"><c>erlang:time_offset()</c></seealso><c>, native, <anno>Unit</anno>)</c>
- however optimized for commonly used <c><anno>Unit</anno></c>s.</p>
+ <p>Returns the current time offset between
+ <seealso marker="time_correction#Erlang_Monotonic_Time">
+ Erlang monotonic time</seealso> and
+ <seealso marker="time_correction#Erlang_System_Time">
+ Erlang system time</seealso>
+ converted into the <c><anno>Unit</anno></c> passed as argument.</p>
+ <p>Same as calling
+ <seealso marker="#convert_time_unit/3">
+ <c>erlang:convert_time_unit</c></seealso><c>(</c><seealso marker="#time_offset/0">
+ <c>erlang:time_offset()</c></seealso><c>, native,
+ <anno>Unit</anno>)</c>
+ however optimized for commonly used <c><anno>Unit</anno></c>s.</p>
</desc>
</func>
+
<func>
<name name="timestamp" arity="0"/>
- <fsummary>Current Erlang System time</fsummary>
+ <fsummary>Current Erlang System time.</fsummary>
<type name="timestamp"/>
<desc>
- <p>Returns current
- <seealso marker="time_correction#Erlang_System_Time">Erlang system time</seealso>
- on the format <c>{MegaSecs, Secs, MicroSecs}</c>. This format is
- the same as <seealso marker="kernel:os#timestamp/0"><c>os:timestamp/0</c></seealso>
- and the deprecated <seealso marker="#now/0"><c>erlang:now/0</c></seealso>
- uses. The reason for the existence of <c>erlang:timestamp()</c> is
- purely to simplify usage for existing code that assumes this timestamp
- format. Current Erlang system time can more efficiently be retrieved in
- the time unit of your choice using
- <seealso marker="#system_time/1"><c>erlang:system_time/1</c></seealso>.</p>
-
- <p>The <c>erlang:timestamp()</c> BIF is equivalent to:</p><code type="none">
+ <p>Returns current
+ <seealso marker="time_correction#Erlang_System_Time">
+ Erlang system time</seealso>
+ on the format <c>{MegaSecs, Secs, MicroSecs}</c>. This format is
+ the same as <seealso marker="kernel:os#timestamp/0">
+ <c>os:timestamp/0</c></seealso>
+ and the deprecated <seealso marker="#now/0">
+ <c>erlang:now/0</c></seealso>
+ use. The reason for the existence of <c>erlang:timestamp()</c> is
+ purely to simplify use for existing code that assumes this time stamp
+ format. Current Erlang system time can more efficiently be retrieved
+ in the time unit of your choice using
+ <seealso marker="#system_time/1">
+ <c>erlang:system_time/1</c></seealso>.</p>
+ <p>The <c>erlang:timestamp()</c> BIF is equivalent to:</p>
+<code type="none">
timestamp() ->
- ErlangSystemTime = erlang:system_time(micro_seconds),
+ ErlangSystemTime = erlang:system_time(microsecond),
MegaSecs = ErlangSystemTime div 1000000000000,
Secs = ErlangSystemTime div 1000000 - MegaSecs*1000000,
MicroSecs = ErlangSystemTime rem 1000000,
{MegaSecs, Secs, MicroSecs}.</code>
- <p>It, however, uses a native implementation which does
- not build garbage on the heap and with slightly better
- performance.</p>
-
- <note><p>This time is <em>not</em> a monotonically increasing time
- in the general case. For more information, see the documentation of
- <seealso marker="time_correction#Time_Warp_Modes">time warp modes</seealso> in the
- ERTS User's Guide.</p></note>
+ <p>It, however, uses a native implementation that does
+ not build garbage on the heap and with slightly better
+ performance.</p>
+ <note>
+ <p>This time is <em>not</em> a monotonically increasing time
+ in the general case. For more information, see the documentation of
+ <seealso marker="time_correction#Time_Warp_Modes">
+ time warp modes</seealso> in the User's Guide.</p>
+ </note>
</desc>
-
</func>
+
<func>
<name name="tl" arity="1"/>
<fsummary>Tail of a list.</fsummary>
@@ -8020,66 +8819,86 @@ timestamp() ->
<func>
<name name="trace" arity="3"/>
- <fsummary>Sets trace flags for a process or processes.</fsummary>
+ <fsummary>Set trace flags for a process or processes.</fsummary>
<type name="trace_flag"/>
<desc>
<p>Turns on (if <c><anno>How</anno> == true</c>) or off (if
<c><anno>How</anno> == false</c>) the trace flags in
<c><anno>FlagList</anno></c> for
the process or processes represented by
- <c><anno>PidSpec</anno></c>.</p>
- <p><c><anno>PidSpec</anno></c> is either a process identifier
- (pid) for a local process, or one of the following atoms:</p>
+ <c><anno>PidPortSpec</anno></c>.</p>
+ <p><c><anno>PidPortSpec</anno></c> is either a process identifier
+ (pid) for a local process, a port identifier,
+ or one of the following atoms:</p>
<taglist>
+ <tag><c>all</c></tag>
+ <item>All currently existing processes and ports and all that
+ will be created in the future.
+ </item>
+ <tag><c>processes</c></tag>
+ <item>All currently existing processes and all that will be created
+ in the future.
+ </item>
+ <tag><c>ports</c></tag>
+ <item>All currently existing ports and all that will be created in
+ the future.
+ </item>
<tag><c>existing</c></tag>
- <item>
- <p>All currently existing processes.</p>
+ <item>All currently existing processes and ports.
+ </item>
+ <tag><c>existing_processes</c></tag>
+ <item>All currently existing processes.
+ </item>
+ <tag><c>existing_ports</c></tag>
+ <item>All currently existing ports.
</item>
<tag><c>new</c></tag>
- <item>
- <p>All processes that are created in the future.</p>
+ <item>All processes and ports that will be created in the future.
</item>
- <tag><c>all</c></tag>
- <item>
- <p>All currently existing processes and all processes that
- are created in the future.</p>
+ <tag><c>new_processes</c></tag>
+ <item>All processes that will be created in the future.
+ </item>
+ <tag><c>new_ports</c></tag>
+ <item>All ports that will be created in the future.
</item>
</taglist>
<p><c><anno>FlagList</anno></c> can contain any number of the
following flags (the "message tags" refers to the list of
- <seealso marker="#trace_3_trace_messages">trace messages</seealso>):</p>
+ <seealso marker="#trace_3_trace_messages">
+ <c>trace messages</c></seealso>):</p>
<taglist>
<tag><c>all</c></tag>
<item>
- <p>Sets all trace flags except <c>{tracer, Tracer}</c> and
+ <p>Sets all trace flags except <c>tracer</c> and
<c>cpu_timestamp</c>, which are in their nature different
than the others.</p>
</item>
<tag><c>send</c></tag>
<item>
<p>Traces sending of messages.</p>
- <p>Message tags: <c>send</c> and
- <c>send_to_non_existing_process</c>.</p>
+ <p>Message tags:
+ <seealso marker="#trace_3_trace_messages_send">
+ <c>send</c></seealso> and
+ <seealso marker="#trace_3_trace_messages_send_to_non_existing_process">
+ <c>send_to_non_existing_process</c></seealso>.</p>
</item>
<tag><c>'receive'</c></tag>
<item>
<p>Traces receiving of messages.</p>
- <p>Message tags: <c>'receive'</c>.</p>
- </item>
- <tag><c>procs</c></tag>
- <item>
- <p>Traces process-related events.</p>
- <p>Message tags: <c>spawn</c>, <c>exit</c>,
- <c>register</c>, <c>unregister</c>, <c>link</c>,
- <c>unlink</c>, <c>getting_linked</c>, and
- <c>getting_unlinked</c>.</p>
+ <p>Message tags:
+ <seealso marker="#trace_3_trace_messages_receive">
+ <c>'receive'</c></seealso>.</p>
</item>
<tag><c>call</c></tag>
<item>
<p>Traces certain function calls. Specify which function
- calls to trace by calling
- <seealso marker="#trace_pattern/3">erlang:trace_pattern/3</seealso>.</p>
- <p>Message tags: <c>call</c> and <c>return_from</c>.</p>
+ calls to trace by calling <seealso marker="#trace_pattern/3">
+ <c>erlang:trace_pattern/3</c></seealso>.</p>
+ <p>Message tags:
+ <seealso marker="#trace_3_trace_messages_call">
+ <c>call</c></seealso> and
+ <seealso marker="#trace_3_trace_messages_return_from">
+ <c>return_from</c></seealso>.</p>
</item>
<tag><c>silent</c></tag>
<item>
@@ -8097,16 +8916,21 @@ timestamp() ->
specification function <c>{silent,Bool}</c>, giving
a high degree of control of which functions with which
arguments that trigger the trace.</p>
- <p>Message tags: <c>call</c>, <c>return_from</c>, and
- <c>return_to</c>. Or rather, the absence of.</p>
+ <p>Message tags:
+ <seealso marker="#trace_3_trace_messages_call">
+ <c>call</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_return_from">
+ <c>return_from</c></seealso>, and
+ <seealso marker="#trace_3_trace_messages_return_to">
+ <c>return_to</c></seealso>. Or rather, the absence of.</p>
</item>
<tag><c>return_to</c></tag>
<item>
<p>Used with the <c>call</c> trace flag.
Traces the return from a traced function back to
its caller. Only works for functions traced with
- option <c>local</c> to
- <seealso marker="#trace_pattern/3">erlang:trace_pattern/3</seealso>.</p>
+ option <c>local</c> to <seealso marker="#trace_pattern/3">
+ <c>erlang:trace_pattern/3</c></seealso>.</p>
<p>The semantics is that a trace message is sent when a
call traced function returns, that is, when a
chain of tail recursive calls ends. Only one trace
@@ -8119,63 +8943,144 @@ timestamp() ->
<p>To get trace messages containing return values from
functions, use the <c>{return_trace}</c> match
specification action instead.</p>
- <p>Message tags: <c>return_to</c>.</p>
+ <p>Message tags:
+ <seealso marker="#trace_3_trace_messages_return_to">
+ <c>return_to</c></seealso>.</p>
+ </item>
+ <tag><c>procs</c></tag>
+ <item>
+ <p>Traces process-related events.</p>
+ <p>Message tags:
+ <seealso marker="#trace_3_trace_messages_spawn">
+ <c>spawn</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_spawned">
+ <c>spawned</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_exit">
+ <c>exit</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_register">
+ <c>register</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_unregister">
+ <c>unregister</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_link">
+ <c>link</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_unlink">
+ <c>unlink</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_getting_linked">
+ <c>getting_linked</c></seealso>, and
+ <seealso marker="#trace_3_trace_messages_getting_unlinked">
+ <c>getting_unlinked</c></seealso>.</p>
+ </item>
+ <tag><c>ports</c></tag>
+ <item>
+ <p>Traces port-related events.</p>
+ <p>Message tags:
+ <seealso marker="#trace_3_trace_messages_open">
+ <c>open</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_closed">
+ <c>closed</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_register">
+ <c>register</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_unregister">
+ <c>unregister</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_getting_linked">
+ <c>getting_linked</c></seealso>, and
+ <seealso marker="#trace_3_trace_messages_getting_unlinked">
+ <c>getting_unlinked</c></seealso>.</p>
</item>
<tag><c>running</c></tag>
<item>
<p>Traces scheduling of processes.</p>
- <p>Message tags: <c>in</c> and <c>out</c>.</p>
+ <p>Message tags:
+ <seealso marker="#trace_3_trace_messages_in_proc">
+ <c>in</c></seealso> and
+ <seealso marker="#trace_3_trace_messages_out_proc">
+ <c>out</c></seealso>.</p>
</item>
<tag><c>exiting</c></tag>
<item>
<p>Traces scheduling of exiting processes.</p>
- <p>Message tags: <c>in_exiting</c>, <c>out_exiting</c>, and
- <c>out_exited</c>.</p>
+ <p>Message tags:
+ <seealso marker="#trace_3_trace_messages_in_exiting_proc">
+ <c>in_exiting</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_out_exiting_proc">
+ <c>out_exiting</c></seealso>, and
+ <seealso marker="#trace_3_trace_messages_out_exited_proc">
+ <c>out_exited</c></seealso>.</p>
+ </item>
+ <tag><c>running_procs</c></tag>
+ <item>
+ <p>Traces scheduling of processes just like <c>running</c>.
+ However, this option also includes schedule events when the
+ process executes within the context of a port without
+ being scheduled out itself.</p>
+ <p>Message tags:
+ <seealso marker="#trace_3_trace_messages_in_proc">
+ <c>in</c></seealso> and
+ <seealso marker="#trace_3_trace_messages_out_proc">
+ <c>out</c></seealso>.</p>
+ </item>
+ <tag><c>running_ports</c></tag>
+ <item>
+ <p>Traces scheduling of ports.</p>
+ <p>Message tags:
+ <seealso marker="#trace_3_trace_messages_in_port">
+ <c>in</c></seealso> and
+ <seealso marker="#trace_3_trace_messages_out_port">
+ <c>out</c></seealso>.</p>
</item>
<tag><c>garbage_collection</c></tag>
<item>
<p>Traces garbage collections of processes.</p>
- <p>Message tags: <c>gc_start</c> and <c>gc_end</c>.</p>
+ <p>Message tags:
+ <seealso marker="#trace_3_trace_messages_gc_minor_start">
+ <c>gc_minor_start</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_gc_max_heap_size">
+ <c>gc_max_heap_size</c></seealso>, and
+ <seealso marker="#trace_3_trace_messages_gc_minor_end">
+ <c>gc_minor_end</c></seealso>.</p>
</item>
<tag><c>timestamp</c></tag>
<item>
- <p>Includes a time-stamp in all trace messages. The
- time-stamp (Ts) has the same form as returned by
+ <p>Includes a time stamp in all trace messages. The
+ time stamp (Ts) has the same form as returned by
<c>erlang:now()</c>.</p>
</item>
<tag><c>cpu_timestamp</c></tag>
<item>
<p>A global trace flag for the Erlang node that makes all
- trace time-stamps using the <c>timestamp</c> flag to be
- in CPU time, not wall clock time. That is, <c>cpu_timestamp</c>
- will not be used if <c>monotonic_timestamp</c>, or
- <c>strict_monotonic_timestamp</c> is enabled.
- Only allowed with <c>PidSpec==all</c>. If the host
- machine OS does not support high-resolution
+ trace time stamps using flag <c>timestamp</c> to be
+ in CPU time, not wall clock time. That is, <c>cpu_timestamp</c>
+ is not be used if <c>monotonic_timestamp</c> or
+ <c>strict_monotonic_timestamp</c> is enabled.
+ Only allowed with <c><anno>PidPortSpec</anno>==all</c>. If the
+ host machine OS does not support high-resolution
CPU time measurements, <c>trace/3</c> exits with
<c>badarg</c>. Notice that most OS do
not synchronize this value across cores, so be prepared
- that time might seem to go backwards when using this option.</p>
+ that time can seem to go backwards when using this option.</p>
</item>
<tag><c>monotonic_timestamp</c></tag>
<item>
<p>Includes an
- <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
- monotonic time</seealso> time-stamp in all trace messages. The
- time-stamp (Ts) has the same format and value as produced by
- <c>erlang:monotonic_time(nano_seconds)</c>. This flag overrides
- the <c>cpu_timestamp</c> flag.</p>
+ <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
+ monotonic time</seealso> time stamp in all trace messages. The
+ time stamp (Ts) has the same format and value as produced by
+ <seealso marker="#monotonic_time-1">
+ <c>erlang:monotonic_time(nanosecond)</c></seealso>.
+ This flag overrides flag <c>cpu_timestamp</c>.</p>
</item>
<tag><c>strict_monotonic_timestamp</c></tag>
<item>
- <p>Includes an timestamp consisting of
- <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
- monotonic time</seealso> and a monotonically increasing
- integer in all trace messages. The time-stamp (Ts) has the
- same format and value as produced by
- <c>{erlang:monotonic_time(nano_seconds),
- erlang:unique_integer([monotonic])}</c>. This flag overrides
- the <c>cpu_timestamp</c> flag.</p>
+ <p>Includes an time stamp consisting of
+ <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
+ monotonic time</seealso> and a monotonically increasing
+ integer in all trace messages. The time stamp (Ts) has the
+ same format and value as produced by <c>{</c>
+ <seealso marker="#monotonic_time-1">
+ <c>erlang:monotonic_time(nanosecond)</c></seealso><c>,</c>
+ <seealso marker="#unique_integer-1">
+ <c>erlang:unique_integer([monotonic])</c></seealso><c>}</c>.
+ This flag overrides flag <c>cpu_timestamp</c>.</p>
</item>
<tag><c>arity</c></tag>
<item>
@@ -8209,47 +9114,70 @@ timestamp() ->
<item>
<p>Specifies where to send the trace messages. <c>Tracer</c>
must be the process identifier of a local process
- or the port identifier
- of a local port. If this flag is not given, trace
- messages are sent to the process that called
- <c>erlang:trace/3</c>.</p>
+ or the port identifier of a local port.</p>
+ </item>
+ <tag><c>{tracer, TracerModule, TracerState}</c></tag>
+ <item>
+ <p>Specifies that a tracer module is to be called
+ instead of sending a trace message. The tracer module
+ can then ignore or change the trace message. For more details
+ on how to write a tracer module, see
+ <seealso marker="erts:erl_tracer"><c>erl_tracer(3)</c></seealso>.</p>
</item>
</taglist>
+ <p>If no <c>tracer</c> is specified, the calling process
+ receives all the trace messages.</p>
<p>The effect of combining <c>set_on_first_link</c> with
- <c>set_on_link</c> is the same as having
+ <c>set_on_link</c> is the same as
<c>set_on_first_link</c> alone. Likewise for
<c>set_on_spawn</c> and <c>set_on_first_spawn</c>.</p>
<p>The tracing process receives the <em>trace messages</em> described
- in the following list. <c>Pid</c> is the process identifier of the
- traced process in which the traced event has occurred. The
- third tuple element is the message tag.</p>
+ in the following list. <c>Pid</c> is the process identifier of the
+ traced process in which the traced event has occurred. The
+ third tuple element is the message tag.</p>
<p>If flag <c>timestamp</c>, <c>strict_monotonic_timestamp</c>, or
- <c>monotonic_timestamp</c> is given, the first tuple
- element is <c>trace_ts</c> instead, and the time-stamp
- is added as an extra element last in the message tuple. If
- multiple timestamp flags are passed, <c>timestamp</c> has
- precedence over <c>strict_monotonic_timestamp</c> which
- in turn has precedence over <c>monotonic_timestamp</c>. All
- timestamp flags are remembered, so if two are passed
- and the one with highest precedence later is disabled
- the other one will become active.</p>
+ <c>monotonic_timestamp</c> is specified, the first tuple
+ element is <c>trace_ts</c> instead, and the time stamp
+ is added as an extra element last in the message tuple. If
+ multiple time stamp flags are passed, <c>timestamp</c> has
+ precedence over <c>strict_monotonic_timestamp</c>, which
+ in turn has precedence over <c>monotonic_timestamp</c>. All
+ time stamp flags are remembered, so if two are passed
+ and the one with highest precedence later is disabled,
+ the other one becomes active.</p>
+ <p>Trace messages:</p>
<marker id="trace_3_trace_messages"></marker>
<taglist>
- <tag><c>{trace, Pid, 'receive', Msg}</c></tag>
- <item>
- <p>When <c>Pid</c> receives message <c>Msg</c>.</p>
- </item>
- <tag><c>{trace, Pid, send, Msg, To}</c></tag>
+ <tag>
+ <marker id="trace_3_trace_messages_send"></marker>
+ <c>{trace, PidPort, send, Msg, To}</c>
+ </tag>
<item>
- <p>When <c>Pid</c> sends message <c>Msg</c> to
+ <p>When <c>PidPort</c> sends message <c>Msg</c> to
process <c>To</c>.</p>
</item>
- <tag><c>{trace, Pid, send_to_non_existing_process, Msg, To}</c></tag>
+ <tag>
+ <marker id="trace_3_trace_messages_send_to_non_existing_process"/>
+ <c>{trace, PidPort, send_to_non_existing_process, Msg, To}</c>
+ </tag>
<item>
- <p>When <c>Pid</c> sends message <c>Msg</c> to
+ <p>When <c>PidPort</c> sends message <c>Msg</c> to
the non-existing process <c>To</c>.</p>
</item>
- <tag><c>{trace, Pid, call, {M, F, Args}}</c></tag>
+ <tag>
+ <marker id="trace_3_trace_messages_receive"></marker>
+ <c>{trace, PidPort, 'receive', Msg}</c>
+ </tag>
+ <item>
+ <p>When <c>PidPort</c> receives message <c>Msg</c>.
+ If <c>Msg</c> is set to time-out, a receive
+ statement can have timed out, or the process received
+ a message with the payload <c>timeout</c>.</p>
+ </item>
+ <tag>
+ <marker id="trace_3_trace_messages_call"></marker>
+ <c>{trace, Pid, call, {M, F, Args}}</c>
+ </tag>
<item>
<p>When <c>Pid</c> calls a traced function. The return
values of calls are never supplied, only the call and its
@@ -8258,7 +9186,10 @@ timestamp() ->
change the contents of this message, so that <c>Arity</c>
is specified instead of <c>Args</c>.</p>
</item>
- <tag><c>{trace, Pid, return_to, {M, F, Arity}}</c></tag>
+ <tag>
+ <marker id="trace_3_trace_messages_return_to"></marker>
+ <c>{trace, Pid, return_to, {M, F, Arity}}</c>
+ </tag>
<item>
<p>When <c>Pid</c> returns <em>to</em> the specified
function. This trace message is sent if both
@@ -8270,76 +9201,177 @@ timestamp() ->
(that is, the functions match specification matched, and
<c>{message, false}</c> was not an action).</p>
</item>
- <tag><c>{trace, Pid, return_from, {M, F, Arity}, ReturnValue}</c></tag>
+ <tag>
+ <marker id="trace_3_trace_messages_return_from"></marker>
+ <c>{trace, Pid, return_from, {M, F, Arity}, ReturnValue}</c>
+ </tag>
<item>
<p>When <c>Pid</c> returns <em>from</em> the specified
function. This trace message is sent if flag <c>call</c>
is set, and the function has a match specification
with a <c>return_trace</c> or <c>exception_trace</c> action.</p>
</item>
- <tag><c>{trace, Pid, exception_from, {M, F, Arity}, {Class, Value}}</c></tag>
+ <tag>
+ <marker id="trace_3_trace_messages_exception_from"></marker>
+ <c>{trace, Pid, exception_from, {M, F, Arity}, {Class, Value}}</c>
+ </tag>
<item>
<p>When <c>Pid</c> exits <em>from</em> the specified
function because of an exception. This trace message is
sent if flag <c>call</c> is set, and the function has
a match specification with an <c>exception_trace</c> action.</p>
</item>
- <tag><c>{trace, Pid, spawn, Pid2, {M, F, Args}}</c></tag>
+ <tag>
+ <marker id="trace_3_trace_messages_spawn"></marker>
+ <c>{trace, Pid, spawn, Pid2, {M, F, Args}}</c>
+ </tag>
<item>
<p>When <c>Pid</c> spawns a new process <c>Pid2</c> with
the specified function call as entry point.</p>
<p><c>Args</c> is supposed to be the argument list,
but can be any term if the spawn is erroneous.</p>
</item>
- <tag><c>{trace, Pid, exit, Reason}</c></tag>
+ <tag>
+ <marker id="trace_3_trace_messages_spawned"></marker>
+ <c>{trace, Pid, spawned, Pid2, {M, F, Args}}</c>
+ </tag>
+ <item>
+ <p>When <c>Pid</c> is spawned by process <c>Pid2</c> with
+ the specified function call as entry point.</p>
+ <p><c>Args</c> is supposed to be the argument list,
+ but can be any term if the spawn is erroneous.</p>
+ </item>
+ <tag>
+ <marker id="trace_3_trace_messages_exit"></marker>
+ <c>{trace, Pid, exit, Reason}</c>
+ </tag>
<item>
<p>When <c>Pid</c> exits with reason <c>Reason</c>.</p>
</item>
- <tag><c>{trace, Pid, link, Pid2}</c></tag>
+ <tag>
+ <marker id="trace_3_trace_messages_register"></marker>
+ <c>{trace, PidPort, register, RegName}</c>
+ </tag>
+ <item>
+ <p>When <c>PidPort</c> gets the name <c>RegName</c> registered.</p>
+ </item>
+ <tag>
+ <marker id="trace_3_trace_messages_unregister"></marker>
+ <c>{trace, PidPort, unregister, RegName}</c>
+ </tag>
+ <item>
+ <p>When <c>PidPort</c> gets the name <c>RegName</c> unregistered.
+ This is done automatically when a registered
+ process or port exits.</p>
+ </item>
+ <tag>
+ <marker id="trace_3_trace_messages_link"></marker>
+ <c>{trace, Pid, link, Pid2}</c>
+ </tag>
<item>
<p>When <c>Pid</c> links to a process <c>Pid2</c>.</p>
</item>
- <tag><c>{trace, Pid, unlink, Pid2}</c></tag>
+ <tag>
+ <marker id="trace_3_trace_messages_unlink"></marker>
+ <c>{trace, Pid, unlink, Pid2}</c>
+ </tag>
<item>
<p>When <c>Pid</c> removes the link from a process
<c>Pid2</c>.</p>
</item>
- <tag><c>{trace, Pid, getting_linked, Pid2}</c></tag>
+ <tag>
+ <marker id="trace_3_trace_messages_getting_linked"></marker>
+ <c>{trace, PidPort, getting_linked, Pid2}</c>
+ </tag>
+ <item>
+ <p>When <c>PidPort</c> gets linked to a process <c>Pid2</c>.</p>
+ </item>
+ <tag>
+ <marker id="trace_3_trace_messages_getting_unlinked"></marker>
+ <c>{trace, PidPort, getting_unlinked, Pid2}</c>
+ </tag>
<item>
- <p>When <c>Pid</c> gets linked to a process <c>Pid2</c>.</p>
+ <p>When <c>PidPort</c> gets unlinked from a process <c>Pid2</c>.</p>
</item>
- <tag><c>{trace, Pid, getting_unlinked, Pid2}</c></tag>
+ <tag>
+ <marker id="trace_3_trace_messages_exit"></marker>
+ <c>{trace, Pid, exit, Reason}</c>
+ </tag>
<item>
- <p>When <c>Pid</c> gets unlinked from a process <c>Pid2</c>.</p>
+ <p>When <c>Pid</c> exits with reason <c>Reason</c>.</p>
</item>
- <tag><c>{trace, Pid, register, RegName}</c></tag>
+ <tag>
+ <marker id="trace_3_trace_messages_open"></marker>
+ <c>{trace, Port, open, Pid, Driver}</c>
+ </tag>
<item>
- <p>When <c>Pid</c> gets the name <c>RegName</c> registered.</p>
+ <p>When <c>Pid</c> opens a new port <c>Port</c> with
+ the running <c>Driver</c>.</p>
+ <p><c>Driver</c> is the name of the driver as an atom.</p>
</item>
- <tag><c>{trace, Pid, unregister, RegName}</c></tag>
+ <tag>
+ <marker id="trace_3_trace_messages_closed"></marker>
+ <c>{trace, Port, closed, Reason}</c>
+ </tag>
<item>
- <p>When <c>Pid</c> gets the name <c>RegName</c> unregistered.
- This is done automatically when a registered
- process exits.</p>
+ <p>When <c>Port</c> closes with <c>Reason</c>.</p>
</item>
- <tag><c>{trace, Pid, in, {M, F, Arity} | 0}</c></tag>
+ <tag>
+ <marker id="trace_3_trace_messages_in_proc"></marker>
+ <marker id="trace_3_trace_messages_in_exiting_proc"></marker>
+ <c>{trace, Pid, in | in_exiting, {M, F, Arity} | 0}</c>
+ </tag>
<item>
<p>When <c>Pid</c> is scheduled to run. The process
runs in function <c>{M, F, Arity}</c>. On some rare
occasions, the current function cannot be determined,
then the last element is <c>0</c>.</p>
</item>
- <tag><c>{trace, Pid, out, {M, F, Arity} | 0}</c></tag>
+ <tag>
+ <marker id="trace_3_trace_messages_out_proc"></marker>
+ <marker id="trace_3_trace_messages_out_exiting_proc"></marker>
+ <marker id="trace_3_trace_messages_out_exited_proc"></marker>
+ <c>{trace, Pid, out | out_exiting | out_exited, {M, F, Arity}
+ | 0}</c>
+ </tag>
<item>
<p>When <c>Pid</c> is scheduled out. The process was
running in function {M, F, Arity}. On some rare occasions,
the current function cannot be determined, then the last
element is <c>0</c>.</p>
</item>
- <tag><c>{trace, Pid, gc_start, Info}</c></tag>
+ <tag>
+ <marker id="trace_3_trace_messages_in_port"></marker>
+ <c>{trace, Port, in, Command | 0}</c>
+ </tag>
+ <item>
+ <p>When <c>Port</c> is scheduled to run. <c>Command</c> is the
+ first thing the port will execute, it can however run several
+ commands before being scheduled out. On some rare
+ occasions, the current function cannot be determined,
+ then the last element is <c>0</c>.</p>
+ <p>The possible commands are <c>call</c>, <c>close</c>,
+ <c>command</c>, <c>connect</c>, <c>control</c>, <c>flush</c>,
+ <c>info</c>, <c>link</c>, <c>open</c>, and <c>unlink</c>.</p>
+ </item>
+ <tag>
+ <marker id="trace_3_trace_messages_out_port"></marker>
+ <c>{trace, Port, out, Command | 0}</c>
+ </tag>
+ <item>
+ <p>When <c>Port</c> is scheduled out. The last command run
+ was <c>Command</c>. On some rare occasions,
+ the current function cannot be determined, then the last
+ element is <c>0</c>. <c>Command</c> can contain the same
+ commands as <c>in</c></p>
+ </item>
+ <tag>
+ <marker id="trace_3_trace_messages_gc_minor_start"></marker>
+ <c>{trace, Pid, gc_minor_start, Info}</c>
+ </tag>
<item>
- <marker id="gc_start"></marker>
- <p>Sent when garbage collection is about to be started.
+ <marker id="gc_minor_start"></marker>
+ <p>Sent when a young garbage collection is about to be started.
<c>Info</c> is a list of two-element tuples, where
the first element is a key, and the second is the value.
Do not depend on any order of the tuples.
@@ -8347,13 +9379,13 @@ timestamp() ->
<taglist>
<tag><c>heap_size</c></tag>
<item>The size of the used part of the heap.</item>
- <tag><c>heap_block_size</c></tag>
- <item>The size of the memory block used for storing
- the heap and the stack.</item>
+ <tag><c>heap_block_size</c></tag>
+ <item>The size of the memory block used for storing
+ the heap and the stack.</item>
<tag><c>old_heap_size</c></tag>
<item>The size of the used part of the old heap.</item>
- <tag><c>old_heap_block_size</c></tag>
- <item>The size of the memory block used for storing
+ <tag><c>old_heap_block_size</c></tag>
+ <item>The size of the memory block used for storing
the old heap.</item>
<tag><c>stack_size</c></tag>
<item>The size of the stack.</item>
@@ -8367,37 +9399,73 @@ timestamp() ->
<item>The total size of unique off-heap binaries referenced
from the process heap.</item>
<tag><c>bin_vheap_block_size</c></tag>
- <item>The total size of binaries allowed in the virtual
- heap in the process before doing a garbage collection.</item>
+ <item>The total size of binaries allowed in the virtual
+ heap in the process before doing a garbage collection.</item>
<tag><c>bin_old_vheap_size</c></tag>
<item>The total size of unique off-heap binaries referenced
from the process old heap.</item>
<tag><c>bin_old_vheap_block_size</c></tag>
- <item>The total size of binaries allowed in the virtual
- old heap in the process before doing a garbage collection.</item>
+ <item>The total size of binaries allowed in the virtual
+ old heap in the process before doing a garbage
+ collection.</item>
</taglist>
<p>All sizes are in words.</p>
</item>
- <tag><c>{trace, Pid, gc_end, Info}</c></tag>
- <item>
- <p>Sent when garbage collection is finished. <c>Info</c>
- contains the same kind of list as in message <c>gc_start</c>,
+ <tag>
+ <marker id="trace_3_trace_messages_gc_max_heap_size"></marker>
+ <c>{trace, Pid, gc_max_heap_size, Info}</c>
+ </tag>
+ <item>
+ <p>Sent when the <seealso marker="#process_flag_max_heap_size">
+ <c>max_heap_size</c></seealso>
+ is reached during garbage collection. <c>Info</c> contains the
+ same kind of list as in message <c>gc_start</c>,
+ but the sizes reflect the sizes that triggered
+ <c>max_heap_size</c> to be reached.</p>
+ </item>
+ <tag>
+ <marker id="trace_3_trace_messages_gc_minor_end"></marker>
+ <c>{trace, Pid, gc_minor_end, Info}</c>
+ </tag>
+ <item>
+ <p>Sent when young garbage collection is finished. <c>Info</c>
+ contains the same kind of list as in message
+ <c>gc_minor_start</c>,
but the sizes reflect the new sizes after
garbage collection.</p>
</item>
+ <tag>
+ <marker id="trace_3_trace_messages_gc_major_start"></marker>
+ <c>{trace, Pid, gc_major_start, Info}</c>
+ </tag>
+ <item>
+ <p>Sent when fullsweep garbage collection is about to be started.
+ <c>Info</c> contains the same kind of list as in message
+ <c>gc_minor_start</c>.</p>
+ </item>
+ <tag>
+ <marker id="trace_3_trace_messages_gc_major_end"></marker>
+ <c>{trace, Pid, gc_major_end, Info}</c>
+ </tag>
+ <item>
+ <p>Sent when fullsweep garbage collection is finished. <c>Info</c>
+ contains the same kind of list as in message
+ <c>gc_minor_start</c>, but the sizes reflect the new sizes after
+ a fullsweep garbage collection.</p>
+ </item>
</taglist>
- <p>If the tracing process dies, the flags are silently
- removed.</p>
- <p>Only one process can trace a particular process. Therefore,
+ <p>If the tracing process/port dies or the tracer module returns
+ <c>remove</c>, the flags are silently removed.</p>
+ <p>Each process can only be traced by one tracer. Therefore,
attempts to trace an already traced process fail.</p>
- <p>Returns: A number indicating the number of processes that
- matched <c><anno>PidSpec</anno></c>.
- If <c><anno>PidSpec</anno></c> is a process
+ <p>Returns a number indicating the number of processes that
+ matched <c><anno>PidPortSpec</anno></c>.
+ If <c><anno>PidPortSpec</anno></c> is a process
identifier, the return value is <c>1</c>.
- If <c><anno>PidSpec</anno></c>
+ If <c><anno>PidPortSpec</anno></c>
is <c>all</c> or <c>existing</c>, the return value is
- the number of processes running, excluding tracer processes.
- If <c><anno>PidSpec</anno></c> is <c>new</c>, the return value is
+ the number of processes running.
+ If <c><anno>PidPortSpec</anno></c> is <c>new</c>, the return value is
<c>0</c>.</p>
<p>Failure: <c>badarg</c> if the specified arguments are
not supported. For example, <c>cpu_timestamp</c> is not
@@ -8409,19 +9477,25 @@ timestamp() ->
<name name="trace_delivered" arity="1"/>
<fsummary>Notification when trace has been delivered.</fsummary>
<desc>
- <p>The delivery of trace messages is dislocated on the time-line
+ <p>The delivery of trace messages (generated by
+ <seealso marker="#trace/3"><c>erlang:trace/3</c></seealso>,
+ <seealso marker="kernel:seq_trace"><c>seq_trace(3)</c></seealso>,
+ or <seealso marker="#system_profile/2">
+ <c>erlang:system_profile/2</c></seealso>)
+ is dislocated on the time-line
compared to other events in the system. If you know that
<c><anno>Tracee</anno></c> has passed some specific point
in its execution,
and you want to know when at least all trace messages
corresponding to events up to this point have reached the
- tracer, use <c>erlang:trace_delivered(<anno>Tracee</anno>)</c>. A
- <c>{trace_delivered, <anno>Tracee</anno>, <anno>Ref</anno>}</c> message is sent to
- the caller of <c>erlang:trace_delivered(<anno>Tracee</anno>)</c> when it
- is guaranteed that all trace messages are delivered to
+ tracer, use <c>erlang:trace_delivered(<anno>Tracee</anno>)</c>.</p>
+ <p>When it is guaranteed that all trace messages are delivered to
the tracer up to the point that <c><anno>Tracee</anno></c> reached
at the time of the call to
- <c>erlang:trace_delivered(<anno>Tracee</anno>)</c>.</p>
+ <c>erlang:trace_delivered(<anno>Tracee</anno>)</c>, then a
+ <c>{trace_delivered, <anno>Tracee</anno>, <anno>Ref</anno>}</c>
+ message is sent to the caller of
+ <c>erlang:trace_delivered(<anno>Tracee</anno>)</c> .</p>
<p>Notice that message <c>trace_delivered</c> does <em>not</em>
imply that trace messages have been delivered.
Instead it implies that all trace messages that
@@ -8430,20 +9504,23 @@ timestamp() ->
has not been traced by someone, but if this is the case,
<em>no</em> trace messages have been delivered when the
<c>trace_delivered</c> message arrives.</p>
- <p>Notice that that <c><anno>Tracee</anno></c> must refer
- to a process currently,
+ <p>Notice that <c><anno>Tracee</anno></c> must refer
+ to a process currently
or previously existing on the same node as the caller of
<c>erlang:trace_delivered(<anno>Tracee</anno>)</c> resides on.
The special <c><anno>Tracee</anno></c> atom <c>all</c>
- denotes all processes
- that currently are traced in the node.</p>
+ denotes all processes that currently are traced in the node.</p>
+ <p>When used together with a <seealso marker="erts:erl_tracer">
+ Tracer Module</seealso>, any message sent in the trace callback
+ is guaranteed to have reached its recipient before the
+ <c>trace_delivered</c> message is sent.</p>
<p>Example: Process <c>A</c> is <c><anno>Tracee</anno></c>,
- port <c>B</c> is tracer, and process <c>C</c> is the port
- owner of <c>B</c>. <c>C</c> wants to close <c>B</c> when
- <c>A</c> exits. To ensure that the trace is not truncated,
- <c>C</c> can call <c>erlang:trace_delivered(A)</c>, when
- <c>A</c> exits, and wait for message <c>{trace_delivered, A,
- <anno>Ref</anno>}</c> before closing <c>B</c>.</p>
+ port <c>B</c> is tracer, and process <c>C</c> is the port
+ owner of <c>B</c>. <c>C</c> wants to close <c>B</c> when
+ <c>A</c> exits. To ensure that the trace is not truncated,
+ <c>C</c> can call <c>erlang:trace_delivered(A)</c> when
+ <c>A</c> exits, and wait for message <c>{trace_delivered, A,
+ <anno>Ref</anno>}</c> before closing <c>B</c>.</p>
<p>Failure: <c>badarg</c> if <c><anno>Tracee</anno></c>
does not refer to a
process (dead or alive) on the same node as the caller of
@@ -8453,19 +9530,27 @@ timestamp() ->
<func>
<name name="trace_info" arity="2"/>
- <fsummary>Trace information about a process or function.</fsummary>
+ <fsummary>Trace information about a process or function.</fsummary>
<type name="trace_info_return"/>
<type name="trace_info_item_result"/>
<type name="trace_info_flag"/>
<type name="trace_match_spec"/>
+ <type name="match_variable"/>
+ <type_desc name="match_variable">
+ Approximation of '$1' | '$2' | '$3' | ...
+ </type_desc>
<desc>
- <p>Returns trace information about a process or function.</p>
- <p>To get information about a process,
- <c><anno>PidOrFunc</anno></c> is to
- be a process identifier (pid) or the atom <c>new</c>.
- The atom <c>new</c> means that the default trace state for
- processes to be created is returned.</p>
- <p>The following <c>Item</c>s are valid:</p>
+ <p>Returns trace information about a port, process, function, or
+ event.</p>
+ <p><em>To get information about a port or process</em>,
+ <c><anno>PidPortFuncEvent</anno></c> is to
+ be a process identifier (pid), port identifier, or one of
+ the atoms <c>new</c>, <c>new_processes</c>, or <c>new_ports</c>. The
+ atom <c>new</c> or <c>new_processes</c> means that the default trace
+ state for processes to be created is returned. The atom
+ <c>new_ports</c> means that the default trace state for ports to be
+ created is returned.</p>
+ <p>Valid <c>Item</c>s for ports and processes:</p>
<taglist>
<tag><c>flags</c></tag>
<item>
@@ -8474,24 +9559,32 @@ timestamp() ->
traces are enabled, and one or more of the followings
atoms if traces are enabled: <c>send</c>,
<c>'receive'</c>, <c>set_on_spawn</c>, <c>call</c>,
- <c>return_to</c>, <c>procs</c>, <c>set_on_first_spawn</c>,
- <c>set_on_link</c>, <c>running</c>,
+ <c>return_to</c>, <c>procs</c>, <c>ports</c>,
+ <c>set_on_first_spawn</c>,
+ <c>set_on_link</c>, <c>running</c>, <c>running_procs</c>,
+ <c>running_ports</c>, <c>silent</c>, <c>exiting</c>,
+ <c>monotonic_timestamp</c>, <c>strict_monotonic_timestamp</c>,
<c>garbage_collection</c>, <c>timestamp</c>, and
<c>arity</c>. The order is arbitrary.</p>
</item>
<tag><c>tracer</c></tag>
<item>
- <p>Returns the identifier for process or port tracing this
- process. If this process is not being traced, the return
+ <p>Returns the identifier for process, port, or a tuple containing
+ the tracer module and tracer state tracing this
+ process. If this process is not traced, the return
value is <c>[]</c>.</p>
</item>
</taglist>
- <p>To get information about a function, <c>PidOrFunc</c> is to
+ <p><em>To get information about a function</em>,
+ <c><anno>PidPortFuncEvent</anno></c> is to
be the three-element tuple <c>{Module, Function, Arity}</c> or
- the atom <c>on_load</c>. No wild cards are allowed. Returns
+ the atom <c>on_load</c>. No wildcards are allowed. Returns
<c>undefined</c> if the function does not exist, or
- <c>false</c> if the function is not traced.</p>
- <p>The following <c>Item</c>s are valid::</p>
+ <c>false</c> if the function is not traced.
+ If <c><anno>PidPortFuncEvent</anno></c>
+ is <c>on_load</c>, the information returned refers to
+ the default value for code that will be loaded.</p>
+ <p>Valid <c>Item</c>s for functions:</p>
<taglist>
<tag><c>traced</c></tag>
<item>
@@ -8510,11 +9603,12 @@ timestamp() ->
</item>
<tag><c>meta</c></tag>
<item>
- <p>Returns the meta-trace tracer process or port for this
- function, if it has one. If the function is not
+ <p>Returns the meta-trace tracer process, port, or trace module
+ for this function, if it has one. If the function is not
meta-traced, the returned value is <c>false</c>. If
the function is meta-traced but has once detected that
- the tracer process is invalid, the returned value is [].</p>
+ the tracer process is invalid, the returned value is
+ <c>[]</c>.</p>
</item>
<tag><c>meta_match_spec</c></tag>
<item>
@@ -8527,21 +9621,22 @@ timestamp() ->
<item>
<p>Returns the call count value for this function or
<c>true</c> for the pseudo function <c>on_load</c> if call
- count tracing is active. Otherwise <c>false</c> is returned.
- See also
- <seealso marker="#trace_pattern/3">erlang:trace_pattern/3</seealso>.</p>
+ count tracing is active. Otherwise <c>false</c> is returned.</p>
+ <p>See also <seealso marker="#trace_pattern/3">
+ <c>erlang:trace_pattern/3</c></seealso>.</p>
</item>
<tag><c>call_time</c></tag>
<item>
- <p>Returns the call time values for this function or
+ <p>Returns the call time values for this function or
<c>true</c> for the pseudo function <c>on_load</c> if call
- time tracing is active. Otherwise <c>false</c> is returned.
- The call time values returned, <c>[{Pid, Count, S, Us}]</c>,
- is a list of each process that executed the function
- and its specific counters. See also
- <seealso marker="#trace_pattern/3">erlang:trace_pattern/3</seealso>.</p>
+ time tracing is active. Otherwise <c>false</c> is returned.
+ The call time values returned, <c>[{Pid, Count, S, Us}]</c>,
+ is a list of each process that executed the function
+ and its specific counters.</p>
+ <p>See also
+ <seealso marker="#trace_pattern/3">
+ <c>erlang:trace_pattern/3</c></seealso>.</p>
</item>
-
<tag><c>all</c></tag>
<item>
<p>Returns a list containing the
@@ -8550,47 +9645,210 @@ timestamp() ->
is active for this function.</p>
</item>
</taglist>
+ <p><em>To get information about an event</em>,
+ <c><anno>PidPortFuncEvent</anno></c> is to
+ be one of the atoms <c>send</c> or <c>'receive'</c>.</p>
+ <p>One valid <c>Item</c> for events exists:</p>
+ <taglist>
+ <tag><c>match_spec</c></tag>
+ <item>
+ <p>Returns the match specification for this event, if it
+ has one, or <c>true</c> if no match specification has been
+ set.</p>
+ </item>
+ </taglist>
<p>The return value is <c>{<anno>Item</anno>, Value}</c>, where
<c>Value</c> is the requested information as described earlier.
- If a pid for a dead process was given, or the name of a
+ If a pid for a dead process was specified, or the name of a
non-existing function, <c>Value</c> is <c>undefined</c>.</p>
- <p>If <c><anno>PidOrFunc</anno></c> is <c>on_load</c>, the information
- returned refers to the default value for code that will be
- loaded.</p>
</desc>
</func>
<func>
<name name="trace_pattern" arity="2" clause_i="1"/>
- <fsummary>Sets trace patterns for global call tracing.</fsummary>
+ <fsummary>Set trace patterns for call, send, or 'receive' tracing.
+ </fsummary>
<type name="trace_pattern_mfa"/>
<type name="trace_match_spec"/>
+ <type_desc name="match_variable">
+ Approximation of '$1' | '$2' | '$3' | ...
+ </type_desc>
+ <type name="match_variable"/>
<desc>
<p>The same as
- <seealso marker="#trace_pattern/3">erlang:trace_pattern(MFA, MatchSpec, [])</seealso>,
+ <seealso marker="#trace_pattern/3">
+ <c>erlang:trace_pattern(Event, MatchSpec, [])</c></seealso>,
retained for backward compatibility.</p>
</desc>
</func>
<func>
- <name name="trace_pattern" arity="3"/>
- <fsummary>Sets trace patterns for tracing of function calls.</fsummary>
+ <name name="trace_pattern" arity="3" clause_i="1"/>
+ <fsummary>Set trace pattern for message sending.</fsummary>
+ <type name="trace_match_spec"/>
+ <type name="match_variable"/>
+ <type_desc name="match_variable">
+ Approximation of '$1' | '$2' | '$3' | ...
+ </type_desc>
+ <desc>
+ <p>Sets trace pattern for <em>message sending</em>.
+ Must be combined with
+ <seealso marker="#trace/3"><c>erlang:trace/3</c></seealso>
+ to set the <c>send</c> trace flag for one or more processes.
+ By default all messages sent from <c>send</c> traced processes
+ are traced. To limit
+ traced send events based on the message content, the sender
+ and/or the receiver, use <c>erlang:trace_pattern/3</c>.</p>
+ <p>Argument <c><anno>MatchSpec</anno></c> can take the
+ following forms:</p>
+ <taglist>
+ <tag><c><anno>MatchSpecList</anno></c></tag>
+ <item>
+ <p>A list of match specifications. The matching is done
+ on the list <c>[Receiver, Msg]</c>. <c>Receiver</c>
+ is the process or port identity of the receiver and
+ <c>Msg</c> is the message term. The pid of the sending
+ process can be accessed with the guard function
+ <c>self/0</c>. An empty list is the same as <c>true</c>.
+ For more information, see section
+ <seealso marker="erts:match_spec">
+ Match Specifications in Erlang</seealso> in the User's Guide.</p>
+ </item>
+ <tag><c>true</c></tag>
+ <item>
+ <p>Enables tracing for all sent messages (from <c>send</c>
+ traced processes). Any match specification is
+ removed. <em>This is the default</em>.</p>
+ </item>
+ <tag><c>false</c></tag>
+ <item>
+ <p>Disables tracing for all sent messages.
+ Any match specification is removed.</p>
+ </item>
+ </taglist>
+ <p>Argument <c><anno>FlagList</anno></c> must be <c>[]</c>
+ for send tracing.</p>
+ <p>The return value is always <c>1</c>.</p>
+ <p>Examples:</p>
+ <p>Only trace messages to a specific process <c>Pid</c>:</p>
+ <pre>
+> <input>erlang:trace_pattern(send, [{[Pid, '_'],[],[]}], []).</input>
+1</pre>
+ <p>Only trace messages matching <c>{reply, _}</c>:</p>
+ <pre>
+> <input>erlang:trace_pattern(send, [{['_', {reply,'_'}],[],[]}], []).</input>
+1</pre>
+ <p>Only trace messages sent to the sender itself:</p>
+ <pre>
+> <input>erlang:trace_pattern(send, [{['$1', '_'],[{'=:=','$1',{self}}],[]}], []).</input>
+1</pre>
+ <p>Only trace messages sent to other nodes:</p>
+ <pre>
+> <input>erlang:trace_pattern(send, [{['$1', '_'],[{'=/=',{node,'$1'},{node}}],[]}], []).</input>
+1</pre>
+ <note>
+ <p>A match specification for <c>send</c> trace can use
+ all guard and body functions except <c>caller</c>.</p>
+ </note>
+ </desc>
+ </func>
+
+ <func>
+ <name name="trace_pattern" arity="3" clause_i="2"/>
+ <fsummary>Set trace pattern for tracing of message receiving.</fsummary>
+ <type name="trace_match_spec"/>
+ <type name="match_variable"/>
+ <type_desc name="match_variable">
+ Approximation of '$1' | '$2' | '$3' | ...
+ </type_desc>
+ <desc>
+ <p>Sets trace pattern for <em>message receiving</em>.
+ Must be combined with
+ <seealso marker="#trace/3"><c>erlang:trace/3</c></seealso>
+ to set the <c>'receive'</c> trace flag for one or more processes.
+ By default all messages received by <c>'receive'</c> traced
+ processes are traced. To limit
+ traced receive events based on the message content, the sender
+ and/or the receiver, use <c>erlang:trace_pattern/3</c>.</p>
+ <p>Argument <c><anno>MatchSpec</anno></c> can take the
+ following forms:</p>
+ <taglist>
+ <tag><c><anno>MatchSpecList</anno></c></tag>
+ <item>
+ <p>A list of match specifications. The matching is done
+ on the list <c>[Node, Sender, Msg]</c>. <c>Node</c>
+ is the node name of the sender. <c>Sender</c> is the
+ process or port identity of the sender, or the atom
+ <c>undefined</c> if the sender is not known (which can
+ be the case for remote senders). <c>Msg</c> is the
+ message term. The pid of the receiving process can be
+ accessed with the guard function <c>self/0</c>. An empty
+ list is the same as <c>true</c>. For more information, see
+ section <seealso marker="erts:match_spec">
+ Match Specifications in Erlang</seealso> in the User's Guide.</p>
+ </item>
+ <tag><c>true</c></tag>
+ <item>
+ <p>Enables tracing for all received messages (to <c>'receive'</c>
+ traced processes). Any match specification is
+ removed. <em>This is the default</em>.</p>
+ </item>
+ <tag><c>false</c></tag>
+ <item>
+ <p>Disables tracing for all received messages.
+ Any match specification is removed.</p>
+ </item>
+ </taglist>
+ <p>Argument <c><anno>FlagList</anno></c> must be <c>[]</c>
+ for receive tracing.</p>
+ <p>The return value is always <c>1</c>.</p>
+ <p>Examples:</p>
+ <p>Only trace messages from a specific process <c>Pid</c>:</p>
+ <pre>
+> <input>erlang:trace_pattern('receive', [{['_',Pid, '_'],[],[]}], []).</input>
+1</pre>
+ <p>Only trace messages matching <c>{reply, _}</c>:</p>
+ <pre>
+> <input>erlang:trace_pattern('receive', [{['_','_', {reply,'_'}],[],[]}], []).</input>
+1</pre>
+ <p>Only trace messages from other nodes:</p>
+ <pre>
+> <input>erlang:trace_pattern('receive', [{['$1', '_', '_'],[{'=/=','$1',{node}}],[]}], []).</input>
+1</pre>
+ <note>
+ <p>A match specification for <c>'receive'</c> trace can
+ use all guard and body functions except <c>caller</c>,
+ <c>is_seq_trace</c>, <c>get_seq_token</c>, <c>set_seq_token</c>,
+ <c>enable_trace</c>, <c>disable_trace</c>, <c>trace</c>,
+ <c>silent</c>, and <c>process_dump</c>.</p>
+ </note>
+ </desc>
+ </func>
+
+ <func>
+ <name name="trace_pattern" arity="3" clause_i="3"/>
+ <fsummary>Set trace patterns for tracing of function calls.</fsummary>
<type name="trace_pattern_mfa"/>
<type name="trace_match_spec"/>
<type name="trace_pattern_flag"/>
+ <type name="match_variable"/>
+ <type_desc name="match_variable">
+ Approximation of '$1' | '$2' | '$3' | ...
+ </type_desc>
<desc>
- <p>Enables or disables call tracing for
- one or more functions. Must be combined with
- <seealso marker="#trace/3">erlang:trace/3</seealso>
- to set the <c>call</c> trace flag for one or more processes.</p>
+ <p>Enables or disables <em>call tracing</em> for one or more functions.
+ Must be combined with
+ <seealso marker="#trace/3"><c>erlang:trace/3</c></seealso>
+ to set the <c>call</c> trace flag
+ for one or more processes.</p>
<p>Conceptually, call tracing works as follows. Inside
- the Erlang Virtual Machine, a set of processes and
+ the Erlang virtual machine, a set of processes and
a set of functions are to be traced. If a traced process
calls a traced function, the trace action is taken.
Otherwise, nothing happens.</p>
<p>To add or remove one or more processes to the set of traced
processes, use
- <seealso marker="#trace/3">erlang:trace/3</seealso>.</p>
+ <seealso marker="#trace/3"><c>erlang:trace/3</c></seealso>.</p>
<p>To add or remove functions to the set of traced
functions, use <c>erlang:trace_pattern/3</c>.</p>
<p>The BIF <c>erlang:trace_pattern/3</c> can also add match
@@ -8602,10 +9860,10 @@ timestamp() ->
fails, the action is not executed.</p>
<p>Argument <c><anno>MFA</anno></c> is to be a tuple, such as
<c>{Module, Function, Arity}</c>, or the atom <c>on_load</c>
- (described in the following). It can be the module, function,
+ (described below). It can be the module, function,
and arity for a function (or a BIF in any module).
- The atom <c>'_'</c> can be used as a wild card in any of the
- following ways:</p>
+ The atom <c>'_'</c> can be used as a wildcard in any of the
+ following ways:</p>
<taglist>
<tag><c>{Module,Function,'_'}</c></tag>
<item>
@@ -8622,7 +9880,7 @@ timestamp() ->
</item>
</taglist>
<p>Other combinations, such as <c>{Module,'_',Arity}</c>, are
- not allowed. Local functions match wild cards only if
+ not allowed. Local functions match wildcards only if
option <c>local</c> is in <c><anno>FlagList</anno></c>.</p>
<p>If argument <c><anno>MFA</anno></c> is the atom <c>on_load</c>,
the match specification and flag list are used on all
@@ -8637,13 +9895,15 @@ timestamp() ->
</item>
<tag><c>true</c></tag>
<item>
- <p>Enables tracing for the matching functions.</p>
+ <p>Enables tracing for the matching functions.
+ Any match specification is removed.</p>
</item>
<tag><c><anno>MatchSpecList</anno></c></tag>
<item>
<p>A list of match specifications. An empty list is
equivalent to <c>true</c>. For a description of match
- specifications, see the User's Guide.</p>
+ specifications, see section <seealso marker="erts:match_spec">
+ Match Specifications in Erlang</seealso> in the User's Guide.</p>
</item>
<tag><c>restart</c></tag>
<item>
@@ -8654,7 +9914,7 @@ timestamp() ->
</item>
<tag><c>pause</c></tag>
<item>
- <p>For the <c><anno>FlagList</anno></c> options
+ <p>For the <c><anno>FlagList</anno></c> options
<c>call_count</c> and <c>call_time</c>: pauses
the existing counters. The behavior is undefined for
other <c><anno>FlagList</anno></c> options.</p>
@@ -8679,21 +9939,21 @@ timestamp() ->
the process, a <c>return_to</c> message is also sent
when this function returns to its caller.</p>
</item>
- <tag><c>meta | {meta, <anno>Pid</anno>}</c></tag>
+ <tag><c>meta | {meta, <anno>Pid</anno>} |
+ {meta, <anno>TracerModule</anno>, <anno>TracerState</anno>}</c>
+ </tag>
<item>
<p>Turns on or off meta-tracing for all types of function
- calls. Trace messages are sent to the tracer process
- or port <c><anno>Pid</anno></c> whenever any of the specified
- functions are called, regardless of how they are called.
- If no <c><anno>Pid</anno></c> is specified,
+ calls. Trace messages are sent to the tracer whenever any of
+ the specified functions are called. If no tracer is specified,
<c>self()</c> is used as a default tracer process.</p>
<p>Meta-tracing traces all processes and does not care
- about the process trace flags set by <c>trace/3</c>,
+ about the process trace flags set by <c>erlang:trace/3</c>,
the trace flags are instead fixed to
<c>[call, timestamp]</c>.</p>
<p>The match specification function <c>{return_trace}</c>
works with meta-trace and sends its trace message to the
- same tracer process.</p>
+ same tracer.</p>
</item>
<tag><c>call_count</c></tag>
<item>
@@ -8709,7 +9969,8 @@ timestamp() ->
Paused and running counters can be restarted from zero with
<c><anno>MatchSpec</anno> == restart</c>.</p>
<p>To read the counter value, use
- <seealso marker="#trace_info/2">erlang:trace_info/2</seealso>.</p>
+ <seealso marker="#trace_info/2">
+ <c>erlang:trace_info/2</c></seealso>.</p>
</item>
<tag><c>call_time</c></tag>
<item>
@@ -8717,17 +9978,18 @@ timestamp() ->
(<c><anno>MatchSpec</anno> == false</c>) call time
tracing for all
types of function calls. For every function, a counter is
- incremented when the function is called.
+ incremented when the function is called.
Time spent in the function is accumulated in
two other counters, seconds and microseconds.
- The counters are stored for each call traced process.</p>
+ The counters are stored for each call traced process.</p>
<p>If call time tracing is started while already running,
- the count and time is restarted from zero. To pause
+ the count and time restart from zero. To pause
running counters, use <c><anno>MatchSpec</anno> == pause</c>.
Paused and running counters can be restarted from zero with
<c><anno>MatchSpec</anno> == restart</c>.</p>
<p>To read the counter value, use
- <seealso marker="#trace_info/2">erlang:trace_info/2</seealso>.</p>
+ <seealso marker="#trace_info/2">
+ <c>erlang:trace_info/2</c></seealso>.</p>
</item>
</taglist>
<p>The options <c>global</c> and <c>local</c> are mutually
@@ -8742,12 +10004,12 @@ timestamp() ->
<p>When disabling trace, the option must match the type of trace
set on the function. That is, local tracing must be
disabled with option <c>local</c> and global tracing with
- option <c>global</c> (or no option), and so forth.</p>
+ option <c>global</c> (or no option), and so on.</p>
<p>Part of a match specification list cannot be changed directly.
If a function has a match specification, it can be replaced
with a new one. To change an existing match specification,
use the BIF
- <seealso marker="#trace_info/2">erlang:trace_info/2</seealso>
+ <seealso marker="#trace_info/2"><c>erlang:trace_info/2</c></seealso>
to retrieve the existing match specification.</p>
<p>Returns the number of functions matching
argument <c><anno>MFA</anno></c>. This is zero if none matched.</p>
@@ -8756,7 +10018,7 @@ timestamp() ->
<func>
<name name="trunc" arity="1"/>
- <fsummary>Returns an integer by truncating a number</fsummary>
+ <fsummary>Return an integer by truncating a number.</fsummary>
<desc>
<p>Returns an integer by truncating <c><anno>Number</anno></c>,
for example:</p>
@@ -8769,7 +10031,7 @@ timestamp() ->
<func>
<name name="tuple_size" arity="1"/>
- <fsummary>Returns the size of a tuple.</fsummary>
+ <fsummary>Return the size of a tuple.</fsummary>
<desc>
<p>Returns an integer that is the number of elements in
<c><anno>Tuple</anno></c>, for example:</p>
@@ -8782,11 +10044,11 @@ timestamp() ->
<func>
<name name="tuple_to_list" arity="1"/>
- <fsummary>Converts a tuple to a list.</fsummary>
+ <fsummary>Convert a tuple to a list.</fsummary>
<desc>
<p>Returns a list corresponding to <c><anno>Tuple</anno></c>.
- <c><anno>Tuple</anno></c> can contain any Erlang terms.</p>
- <p>Example:</p>
+ <c><anno>Tuple</anno></c> can contain any Erlang terms.
+ Example:</p>
<pre>
> <input>tuple_to_list({share, {'Ericsson_B', 163}}).</input>
[share,{'Ericsson_B',163}]</pre>
@@ -8794,16 +10056,111 @@ timestamp() ->
</func>
<func>
+ <name name="unique_integer" arity="0"/>
+ <fsummary>Get a unique integer value.</fsummary>
+ <desc>
+ <p>Generates and returns an
+ <seealso marker="doc/efficiency_guide:advanced#unique_integers">
+ integer unique on current runtime system instance</seealso>.
+ The same as calling
+ <seealso marker="#unique_integer/1">
+ <c>erlang:unique_integer([])</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="unique_integer" arity="1"/>
+ <fsummary>Get a unique integer value.</fsummary>
+ <desc>
+ <p>Generates and returns an
+ <seealso marker="doc/efficiency_guide:advanced#unique_integers">
+ integer unique on current runtime system
+ instance</seealso>. The integer is unique in the
+ sense that this BIF, using the same set of
+ modifiers, does not return the same integer more
+ than once on the current runtime system instance.
+ Each integer value can of course be constructed
+ by other means.</p>
+ <p>By default, when <c>[]</c> is passed as
+ <c><anno>ModifierList</anno></c>, both negative and
+ positive integers can be returned. This
+ to use the range of integers that do
+ not need heap memory allocation as much as possible.
+ By default the returned integers are also only
+ guaranteed to be unique, that is, any returned integer
+ can be smaller or larger than previously
+ returned integers.</p>
+ <p><c><anno>Modifier</anno></c>s:</p>
+ <taglist>
+ <tag>positive</tag>
+ <item>
+ <p>Returns only positive integers.</p>
+ <p>Notice that by passing the <c>positive</c> modifier
+ you will get heap allocated integers (bignums) quicker.</p>
+ </item>
+ <tag>monotonic</tag>
+ <item>
+ <p>Returns <seealso
+ marker="time_correction#Strictly_Monotonically_Increasing">
+ strictly monotonically increasing</seealso> integers
+ corresponding to creation time. That is, the integer
+ returned is always larger than previously
+ returned integers on the current runtime system
+ instance.</p>
+ <p>These values can be used to determine order between events
+ on the runtime system instance. That is, if both
+ <c>X = erlang:unique_integer([monotonic])</c> and
+ <c>Y = erlang:unique_integer([monotonic])</c> are
+ executed by different processes (or the same
+ process) on the same runtime system instance and
+ <c>X &lt; Y</c>, we know that <c>X</c> was created
+ before <c>Y</c>.</p>
+ <warning>
+ <p>Strictly monotonically increasing values
+ are inherently quite expensive to generate and scales
+ poorly. This is because the values need to be synchronized
+ between CPU cores. That is, do not pass the <c>monotonic</c>
+ modifier unless you really need strictly monotonically
+ increasing values.</p>
+ </warning>
+ </item>
+ </taglist>
+ <p>All valid <c><anno>Modifier</anno></c>s
+ can be combined. Repeated (valid)
+ <c><anno>Modifier</anno></c>s in the <c>ModifierList</c>
+ are ignored.</p>
+ <note>
+ <p>The set of integers returned by
+ <c>erlang:unique_integer/1</c> using different sets of
+ <c><anno>Modifier</anno></c>s <em>will overlap</em>.
+ For example, by calling <c>unique_integer([monotonic])</c>,
+ and <c>unique_integer([positive, monotonic])</c>
+ repeatedly, you will eventually see some integers that are
+ returned by both calls.</p>
+ </note>
+ <p>Failures:</p>
+ <taglist>
+ <tag><c>badarg</c></tag>
+ <item>if <c><anno>ModifierList</anno></c> is not a
+ proper list.</item>
+ <tag><c>badarg</c></tag>
+ <item>if <c><anno>Modifier</anno></c> is not a
+ valid modifier.</item>
+ </taglist>
+ </desc>
+ </func>
+
+ <func>
<name name="universaltime" arity="0"/>
- <fsummary>Current date and time according to Universal Time Coordinated (UTC).</fsummary>
+ <fsummary>Current date and time according to Universal Time Coordinated
+ (UTC).</fsummary>
<desc>
<p>Returns the current date and time according to Universal
Time Coordinated (UTC) in the form
<c>{{Year, Month, Day}, {Hour, Minute, Second}}</c> if
supported by the underlying OS.
Otherwise <c>erlang:universaltime()</c> is equivalent to
- <c>erlang:localtime()</c>.</p>
- <p>Example:</p>
+ <c>erlang:localtime()</c>. Example:</p>
<pre>
> <input>erlang:universaltime().</input>
{{1996,11,6},{14,18,43}}</pre>
@@ -8812,15 +10169,15 @@ timestamp() ->
<func>
<name name="universaltime_to_localtime" arity="1"/>
- <fsummary>Converts from Universal Time Coordinated (UTC) to local date and time.</fsummary>
+ <fsummary>Convert from Universal Time Coordinated (UTC) to local date
+ and time.</fsummary>
<desc>
<p>Converts Universal Time Coordinated (UTC) date and time to
local date and time in the form
<c>{{Year, Month, Day}, {Hour, Minute, Second}}</c> if
supported by the underlying OS.
Otherwise no conversion is done, and
- <c><anno>Universaltime</anno></c> is returned.</p>
- <p>Example:</p>
+ <c><anno>Universaltime</anno></c> is returned. Example:</p>
<pre>
> <input>erlang:universaltime_to_localtime({{1996,11,6},{14,18,43}}).</input>
{{1996,11,7},{15,18,43}}</pre>
@@ -8830,102 +10187,8 @@ timestamp() ->
</func>
<func>
- <name name="unique_integer" arity="0"/>
- <fsummary>Get a unique integer value</fsummary>
- <desc>
- <p>Generates and returns an
- <seealso marker="doc/efficiency_guide:advanced#unique_integers">integer
- unique on current runtime system instance</seealso>. The same as calling
- <seealso marker="#unique_integer/1"><c>erlang:unique_integer([])</c></seealso>.</p>
- </desc>
- </func>
- <func>
- <name name="unique_integer" arity="1"/>
- <fsummary>Get a unique integer value</fsummary>
- <desc>
- <p>Generates and returns an
- <seealso marker="doc/efficiency_guide:advanced#unique_integers">integer
- unique on current runtime system
- instance</seealso>. The integer is unique in the
- sense that this BIF, using the same set of
- modifiers, will not return the same integer more
- than once on the current runtime system instance.
- Each integer value can of course be constructed
- by other means.</p>
-
- <p>By default, when <c>[]</c> is passed as
- <c><anno>ModifierList</anno></c>, both negative and
- positive integers can be returned. This in order
- to utilize the range of integers that do
- not need heap memory allocation as much as possible.
- By default the returned integers are also only
- guaranteed to be unique, that is, any returned integer
- can be smaller or larger than previously
- returned integers.</p>
-
- <p>Valid <c><anno>Modifier</anno></c>s:</p>
- <taglist>
-
- <tag>positive</tag>
- <item><p>Return only positive integers.</p>
- <p>Note that by passing the <c>positive</c> modifier
- you will get heap allocated integers (bignums)
- quicker.</p>
- </item>
-
- <tag>monotonic</tag>
- <item><p>Return
- <seealso marker="time_correction#Strictly_Monotonically_Increasing">strictly
- monotonically increasing</seealso> integers
- corresponding to creation time. That is, the integer
- returned will always be larger than previously
- returned integers on the current runtime system
- instance.</p>
- <p>These values can be used to determine order between events
- on the runtime system instance. That is, if both
- <c>X = erlang:unique_integer([monotonic])</c> and
- <c>Y = erlang:unique_integer([monotonic])</c> are
- executed by different processes (or the same
- process) on the same runtime system instance and
- <c>X &lt; Y</c> we know that <c>X</c> was created
- before <c>Y</c>.</p>
- <warning><p>Strictly monotonically increasing values
- are inherently quite expensive to generate and scales
- poorly. This is because the values need to be
- synchronized between cpu cores. That is, do not pass the <c>monotonic</c>
- modifier unless you really need strictly monotonically
- increasing values.</p></warning>
- </item>
-
- </taglist>
-
- <p>All valid <c><anno>Modifier</anno></c>s
- can be combined. Repeated (valid)
- <c><anno>Modifier</anno></c>s in the <c>ModifierList</c>
- are ignored.</p>
-
- <note><p>Note that the set of integers returned by
- <c>unique_integer/1</c> using different sets of
- <c><anno>Modifier</anno></c>s <em>will overlap</em>.
- For example, by calling <c>unique_integer([monotonic])</c>,
- and <c>unique_integer([positive, monotonic])</c>
- repeatedly, you will eventually see some integers being
- returned by both calls.</p></note>
-
- <p>Failures:</p>
- <taglist>
- <tag><c>badarg</c></tag>
- <item>if <c><anno>ModifierList</anno></c> is not a
- proper list.</item>
- <tag><c>badarg</c></tag>
- <item>if <c><anno>Modifier</anno></c> is not a
- valid modifier.</item>
- </taglist>
- </desc>
- </func>
- <func>
<name name="unlink" arity="1"/>
- <fsummary>Removes a link to another process or port.</fsummary>
+ <fsummary>Remove a link to another process or port.</fsummary>
<desc>
<p>Removes the link, if there is one, between the calling
process and the process or port referred to by
@@ -8940,8 +10203,8 @@ timestamp() ->
in the future (unless
the link is setup again). If the caller is trapping exits, an
<c>{'EXIT', <anno>Id</anno>, _}</c> message from the link
- can have been placed in the caller's message queue before
- the call.</p>
+ can have been placed in the caller's message queue before
+ the call.</p>
<p>Notice that the <c>{'EXIT', <anno>Id</anno>, _}</c>
message can be the
result of the link, but can also be the result of <c>Id</c>
@@ -8949,16 +10212,16 @@ timestamp() ->
appropriate to clean up the message queue when trapping exits
after the call to <c>unlink(<anno>Id</anno>)</c>, as follows:</p>
<code type="none">
- unlink(Id),
- receive
- {'EXIT', Id, _} ->
- true
- after 0 ->
- true
- end</code>
+unlink(Id),
+receive
+ {'EXIT', Id, _} ->
+ true
+after 0 ->
+ true
+end</code>
<note>
- <p>Prior to OTP release R11B (ERTS version 5.5) <c>unlink/1</c>
- behaved completely asynchronously, i.e., the link was active
+ <p>Before Erlang/OTP R11B (ERTS 5.5) <c>unlink/1</c>
+ behaved completely asynchronously, that is, the link was active
until the "unlink signal" reached the linked entity. This
had an undesirable effect, as you could never know when
you were guaranteed <em>not</em> to be effected by the link.</p>
@@ -8971,7 +10234,7 @@ timestamp() ->
<func>
<name name="unregister" arity="1"/>
- <fsummary>Removes the registered name for a process (or port).</fsummary>
+ <fsummary>Remove the registered name for a process (or port).</fsummary>
<desc>
<p>Removes the registered name <c><anno>RegName</anno></c>
associated with a
@@ -8987,12 +10250,12 @@ true</pre>
<func>
<name name="whereis" arity="1"/>
- <fsummary>Gets the pid (or port) with a given registered name.</fsummary>
+ <fsummary>Get the pid (or port) with a specified registered name.
+ </fsummary>
<desc>
<p>Returns the process identifier or port identifier with
the registered name <c>RegName</c>. Returns <c>undefined</c>
- if the name is not registered.</p>
- <p>Example:</p>
+ if the name is not registered. Example:</p>
<pre>
> <input>whereis(db).</input>
&lt;0.43.0></pre>
@@ -9001,17 +10264,19 @@ true</pre>
<func>
<name name="yield" arity="0"/>
- <fsummary>Lets other processes get a chance to execute.</fsummary>
+ <fsummary>Let other processes get a chance to execute.</fsummary>
<desc>
<p>Voluntarily lets other processes (if any) get a chance to
- execute. Using <c>erlang:yield()</c> is similar to
+ execute. Using this function is similar to
<c>receive after 1 -> ok end</c>, except that <c>yield()</c>
is faster.</p>
- <warning><p>There is seldom or never any need to use this BIF,
- especially in the SMP emulator, as other processes have a
- chance to run in another scheduler thread anyway.
- Using this BIF without a thorough grasp of how the scheduler
- works can cause performance degradation.</p></warning>
+ <warning>
+ <p>There is seldom or never any need to use this BIF,
+ especially in the SMP emulator, as other processes have a
+ chance to run in another scheduler thread anyway.
+ Using this BIF without a thorough grasp of how the scheduler
+ works can cause performance degradation.</p>
+ </warning>
</desc>
</func>
</funcs>
diff --git a/erts/doc/src/erlc.xml b/erts/doc/src/erlc.xml
index 9fc5864413..7355be488b 100644
--- a/erts/doc/src/erlc.xml
+++ b/erts/doc/src/erlc.xml
@@ -4,7 +4,7 @@
<comref>
<header>
<copyright>
- <year>1997</year><year>2013</year>
+ <year>1997</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -28,7 +28,7 @@
<docno>1</docno>
<approved>Bjarne D&auml;cker</approved>
<checked></checked>
- <date>97-03-24</date>
+ <date>1997-03-24</date>
<rev>A</rev>
<file>erlc.xml</file>
</header>
@@ -38,167 +38,162 @@
<p>The <c><![CDATA[erlc]]></c> program provides a common way to run
all compilers in the Erlang system.
Depending on the extension of each input file, <c><![CDATA[erlc]]></c>
- will invoke the appropriate compiler.
- Regardless of which compiler is used, the same flags are used to provide parameters such as include paths and output directory.</p>
- <p>The current working directory, <c>"."</c>, will not be included
- in the code path when running the compiler (to avoid loading
- Beam files from the current working directory that could potentially
- be in conflict with the compiler or Erlang/OTP system used by the
- compiler).</p>
+ invokes the appropriate compiler.
+ Regardless of which compiler is used, the same flags are used to provide
+ parameters, such as include paths and output directory.</p>
+ <p>The current working directory, <c>"."</c>, is not included
+ in the code path when running the compiler. This to avoid loading
+ Beam files from the current working directory that could potentially
+ be in conflict with the compiler or the Erlang/OTP system used by the
+ compiler.</p>
</description>
+
<funcs>
<func>
<name>erlc flags file1.ext file2.ext...</name>
- <fsummary>Compile files</fsummary>
+ <fsummary>Compile files.</fsummary>
<desc>
- <p><c><![CDATA[Erlc]]></c> compiles one or more files.
- The files must include the extension, for example <c><![CDATA[.erl]]></c>
- for Erlang source code, or <c><![CDATA[.yrl]]></c> for Yecc source code.
- <c><![CDATA[Erlc]]></c> uses the extension to invoke the correct compiler.</p>
+ <p>Compiles one or more files. The files must include the extension,
+ for example, <c><![CDATA[.erl]]></c> for Erlang source code, or
+ <c><![CDATA[.yrl]]></c> for Yecc source code.
+ <c><![CDATA[Erlc]]></c> uses the extension to invoke the correct
+ compiler.</p>
</desc>
</func>
</funcs>
<section>
<title>Generally Useful Flags</title>
- <p>The following flags are supported:
- </p>
+ <p>The following flags are supported:</p>
<taglist>
- <tag>-I <em>directory</em></tag>
+ <tag><c>-I &lt;Directory&gt;</c></tag>
<item>
<p>Instructs the compiler to search for include files in
- the specified directory. When encountering an
- <c><![CDATA[-include]]></c> or <c><![CDATA[-include_lib]]></c> directive, the
- compiler searches for header files in the following
+ the <c>Directory</c>. When encountering an
+ <c><![CDATA[-include]]></c> or <c><![CDATA[-include_lib]]></c>
+ directive, the compiler searches for header files in the following
directories:</p>
- <list type="ordered">
+ <list type="bulleted">
<item>
<p><c><![CDATA["."]]></c>, the current working directory of the
- file server;</p>
+ file server</p>
</item>
<item>
- <p>the base name of the compiled file;</p>
+ <p>The base name of the compiled file</p>
</item>
<item>
- <p>the directories specified using the <c><![CDATA[-I]]></c> option.
- The directory specified last is searched first.</p>
+ <p>The directories specified using option <c><![CDATA[-I]]></c>;
+ the directory specified last is searched first</p>
</item>
</list>
</item>
- <tag>-o <em>directory</em></tag>
+ <tag><c>-o &lt;Directory&gt;</c></tag>
<item>
- <p>The directory where the compiler should place the output files.
- If not specified, output files will be placed in the current working
- directory.</p>
+ <p>The directory where the compiler is to place the output files.
+ Defaults to the current working directory.</p>
</item>
- <tag>-D<em>name</em></tag>
+ <tag><c>-D&lt;Name&gt;</c></tag>
<item>
<p>Defines a macro.</p>
</item>
- <tag>-D<em>name</em>=<em>value</em></tag>
+ <tag><c>-D&lt;Name&gt;=&lt;Value&gt;</c></tag>
<item>
- <p>Defines a macro with the given value.
+ <p>Defines a macro with the specified value.
The value can be any Erlang term.
Depending on the platform, the value may need to be
quoted if the shell itself interprets certain characters.
- On Unix, terms which contain tuples and list
- must be quoted. Terms which contain spaces
+ On Unix, terms containing tuples and lists
+ must be quoted. Terms containing spaces
must be quoted on all platforms.</p>
</item>
- <tag>-W<em>error</em></tag>
+ <tag><c>-W&lt;Error&gt;</c></tag>
<item>
<p>Makes all warnings into errors.</p>
</item>
- <tag>-W<em>number</em></tag>
+ <tag><c>-W&lt;Number&gt;</c></tag>
<item>
- <p>Sets warning level to <em>number</em>. Default is <c><![CDATA[1]]></c>.
- Use <c><![CDATA[-W0]]></c> to turn off warnings.</p>
+ <p>Sets warning level to <c>Number</c>. Defaults to
+ <c><![CDATA[1]]></c>. To turn off warnings,
+ use <c><![CDATA[-W0]]></c>.</p>
</item>
- <tag>-W</tag>
+ <tag><c>-W</c></tag>
<item>
<p>Same as <c><![CDATA[-W1]]></c>. Default.</p>
</item>
- <tag>-v</tag>
+ <tag><c>-v</c></tag>
<item>
<p>Enables verbose output.</p>
</item>
- <tag>-b <em>output-type</em></tag>
+ <tag><c>-b &lt;Output_type&gt;</c></tag>
<item>
<p>Specifies the type of output file.
- Generally, <em>output-type</em> is the same as the file extension
- of the output file but without the period.
- This option will be ignored by compilers that have a
+ <c>Output_type</c> is the same as the file extension
+ of the output file, but without the period.
+ This option is ignored by compilers that have
a single output format.</p>
</item>
- <tag>-smp</tag>
+ <tag><c>-smp</c></tag>
<item>
- <p>Compile using the SMP emulator. This is mainly useful
- for compiling native code, which needs to be compiled with the same
- run-time system that it should be run on.</p>
+ <p>Compiles using the SMP emulator. This is mainly useful
+ for compiling native code, which must be compiled with the same
+ runtime system that it is to be run on.</p>
</item>
- <tag>-M</tag>
+ <tag><c>-M</c></tag>
<item>
- <p>Produces a Makefile rule to track headers dependencies. The
- rule is sent to stdout. No object file is produced.
- </p>
+ <p>Produces a Makefile rule to track header dependencies. The
+ rule is sent to <c>stdout</c>. No object file is produced.</p>
</item>
- <tag>-MF <em>Makefile</em></tag>
+ <tag><c>-MF &lt;Makefile&gt;</c></tag>
<item>
- <p>Like the <c><![CDATA[-M]]></c> option above, except that the
- Makefile is written to <em>Makefile</em>. No object
- file is produced.
- </p>
+ <p>As option <c><![CDATA[-M]]></c>, except that the
+ Makefile is written to <c>Makefile</c>. No object
+ file is produced.</p>
</item>
- <tag>-MD</tag>
+ <tag><c>-MD</c></tag>
<item>
- <p>Same as <c><![CDATA[-M -MF <File>.Pbeam]]></c>.
- </p>
+ <p>Same as <c><![CDATA[-M -MF <File>.Pbeam]]></c>.</p>
</item>
- <tag>-MT <em>Target</em></tag>
+ <tag><c>-MT &lt;Target&gt;</c></tag>
<item>
- <p>In conjunction with <c><![CDATA[-M]]></c> or
- <c><![CDATA[-MF]]></c>, change the name of the rule emitted
- to <em>Target</em>.
- </p>
+ <p>In conjunction with option <c><![CDATA[-M]]></c> or
+ <c><![CDATA[-MF]]></c>, changes the name of the rule emitted
+ to <c>Target</c>.</p>
</item>
- <tag>-MQ <em>Target</em></tag>
+ <tag><c>-MQ &lt;Target&gt;</c></tag>
<item>
- <p>Like the <c><![CDATA[-MT]]></c> option above, except that
- characters special to make(1) are quoted.
- </p>
+ <p>As option <c><![CDATA[-MT]]></c>, except that characters special to
+ <c>make/1</c> are quoted.</p>
</item>
- <tag>-MP</tag>
+ <tag><c>-MP</c></tag>
<item>
- <p>In conjunction with <c><![CDATA[-M]]></c> or
- <c><![CDATA[-MF]]></c>, add a phony target for each dependency.
- </p>
+ <p>In conjunction with option <c><![CDATA[-M]]></c> or
+ <c><![CDATA[-MF]]></c>, adds a phony target for each dependency.</p>
</item>
- <tag>-MG</tag>
+ <tag><c>-MG</c></tag>
<item>
- <p>In conjunction with <c><![CDATA[-M]]></c> or
- <c><![CDATA[-MF]]></c>, consider missing headers as generated
- files and add them to the dependencies.
- </p>
+ <p>In conjunction with option <c><![CDATA[-M]]></c> or
+ <c><![CDATA[-MF]]></c>, considers missing headers as generated
+ files and adds them to the dependencies.</p>
</item>
- <tag>--</tag>
+ <tag><c>--</c></tag>
<item>
<p>Signals that no more options will follow.
- The rest of the arguments will be treated as file names,
+ The rest of the arguments is treated as filenames,
even if they start with hyphens.</p>
</item>
- <tag>+<em>term</em></tag>
+ <tag><c>+&lt;Term&gt;</c></tag>
<item>
- <p>A flag starting with a plus ('<em>+</em>') rather than a hyphen
- will be converted to an Erlang term and passed unchanged to
+ <p>A flag starting with a plus (<c>+</c>) rather than a hyphen
+ is converted to an Erlang term and passed unchanged to
the compiler.
- For instance, the <c><![CDATA[export_all]]></c> option for the Erlang
+ For example, option <c><![CDATA[export_all]]></c> for the Erlang
compiler can be specified as follows:</p>
<pre>
erlc +export_all file.erl</pre>
<p>Depending on the platform, the value may need to be
quoted if the shell itself interprets certain characters.
- On Unix, terms which contain tuples and list
- must be quoted. Terms which contain spaces
+ On Unix, terms containing tuples and lists
+ must be quoted. Terms containing spaces
must be quoted on all platforms.</p>
</item>
</taglist>
@@ -206,19 +201,19 @@ erlc +export_all file.erl</pre>
<section>
<title>Special Flags</title>
- <p>The flags in this section are useful in special situations
- such as re-building the OTP system.</p>
+ <p>The following flags are useful in special situations,
+ such as rebuilding the OTP system:</p>
<taglist>
- <tag>-pa <em>directory</em></tag>
+ <tag><c>-pa &lt;Directory&gt;</c></tag>
<item>
- <p>Appends <em>directory</em> to the front of the code path in
+ <p>Appends <c>Directory</c> to the front of the code path in
the invoked Erlang emulator.
This can be used to invoke another
compiler than the default one.</p>
</item>
- <tag>-pz <em>directory</em></tag>
+ <tag><c>-pz &lt;Directory&gt;</c></tag>
<item>
- <p>Appends <em>directory</em> to the code path in
+ <p>Appends <c>Directory</c> to the code path in
the invoked Erlang emulator.</p>
</item>
</taglist>
@@ -226,63 +221,70 @@ erlc +export_all file.erl</pre>
<section>
<title>Supported Compilers</title>
+ <p>The following compilers are supported:</p>
<taglist>
- <tag>.erl</tag>
+ <tag><c>.erl</c></tag>
<item>
<p>Erlang source code. It generates a <c><![CDATA[.beam]]></c> file.</p>
- <p>The options -P, -E, and -S are equivalent to +'P',
- +'E', and +'S', except that it is not necessary to include the single quotes to protect them
- from the shell.</p>
- <p>Supported options: -I, -o, -D, -v, -W, -b.</p>
+ <p>Options <c>-P</c>, <c>-E</c>, and <c>-S</c> are equivalent to
+ <c>+'P'</c>, <c>+'E'</c>, and <c>+'S'</c>, except that it is not
+ necessary to include the single quotes to protect them from the
+ shell.</p>
+ <p>Supported options: <c>-I</c>, <c>-o</c>, <c>-D</c>, <c>-v</c>,
+ <c>-W</c>, <c>-b</c>.</p>
</item>
- <tag>.S</tag>
+ <tag><c>.S</c></tag>
<item>
- <p>Erlang assembler source code. It generates a <c><![CDATA[.beam]]></c> file.</p>
- <p>Supported options: same as for .erl.</p>
+ <p>Erlang assembler source code. It generates a <c><![CDATA[.beam]]></c>
+ file.</p>
+ <p>Supported options: same as for <c>.erl</c>.</p>
</item>
- <tag>.core</tag>
+ <tag><c>.core</c></tag>
<item>
- <p>Erlang core source code. It generates a <c><![CDATA[.beam]]></c> file.</p>
- <p>Supported options: same as for .erl.</p>
+ <p>Erlang core source code. It generates a <c><![CDATA[.beam]]></c>
+ file.</p>
+ <p>Supported options: same as for <c>.erl</c>.</p>
</item>
- <tag>.yrl</tag>
+ <tag><c>.yrl</c></tag>
<item>
<p>Yecc source code. It generates an <c><![CDATA[.erl]]></c> file.</p>
- <p>Use the -I option with the name of a file to use that file
- as a customized prologue file (the <c><![CDATA[includefile]]></c> option).</p>
- <p>Supported options: -o, -v, -I, -W (see above).</p>
+ <p>Use option <c>-I</c> with the name of a file to use that file
+ as a customized prologue file (option
+ <c><![CDATA[includefile]]></c>).</p>
+ <p>Supported options: <c>-o</c>, <c>-v</c>, <c>-I</c>, <c>-W</c>.</p>
</item>
- <tag>.mib</tag>
+ <tag><c>.mib</c></tag>
<item>
<p>MIB for SNMP. It generates a <c><![CDATA[.bin]]></c> file.</p>
- <p>Supported options: -I, -o, -W.</p>
+ <p>Supported options: <c>-I</c>, <c>-o</c>, <c>-W</c>.</p>
</item>
- <tag>.bin</tag>
+ <tag><c>.bin</c></tag>
<item>
- <p>A compiled MIB for SNMP. It generates a <c><![CDATA[.hrl]]></c> file.</p>
- <p>Supported options: -o, -v.</p>
+ <p>A compiled MIB for SNMP. It generates a <c><![CDATA[.hrl]]></c>
+ file.</p>
+ <p>Supported options: <c>-o</c>, <c>-v</c>.</p>
</item>
- <tag>.rel</tag>
+ <tag><c>.rel</c></tag>
<item>
<p>Script file. It generates a boot file.</p>
- <p>Use the -I to name directories to be searched for application
- files (equivalent to the <c><![CDATA[path]]></c> in the option list for
- <c><![CDATA[systools:make_script/2]]></c>).</p>
- <p>Supported options: -o.</p>
+ <p>Use option <c>-I</c> to name directories to be searched for
+ application files (equivalent to the <c><![CDATA[path]]></c> in the
+ option list for <c><![CDATA[systools:make_script/2]]></c>).</p>
+ <p>Supported option: <c>-o</c>.</p>
</item>
- <tag>.asn1</tag>
+ <tag><c>.asn1</c></tag>
<item>
- <p>ASN1 file.</p>
- <p>Creates an <c><![CDATA[.erl]]></c>, <c><![CDATA[.hrl]]></c>, and <c><![CDATA[.asn1db]]></c> file from
- an <c><![CDATA[.asn1]]></c> file. Also compiles the <c><![CDATA[.erl]]></c> using the Erlang
- compiler unless the <c><![CDATA[+noobj]]></c> options is given.</p>
- <p>Supported options: -I, -o, -b, -W.</p>
+ <p>ASN1 file. It creates an <c><![CDATA[.erl]]></c>,
+ <c><![CDATA[.hrl]]></c>, and <c><![CDATA[.asn1db]]></c> file from
+ an <c><![CDATA[.asn1]]></c> file. Also compiles the
+ <c><![CDATA[.erl]]></c> using the Erlang compiler unless option
+ <c><![CDATA[+noobj]]></c> is specified.</p>
+ <p>Supported options: <c>-I</c>, <c>-o</c>, <c>-b</c>, <c>-W</c>.</p>
</item>
- <tag>.idl</tag>
+ <tag><c>.idl</c></tag>
<item>
- <p>IC file.</p>
- <p>Runs the IDL compiler.</p>
- <p>Supported options: -I, -o.</p>
+ <p>IC file. It runs the IDL compiler.</p>
+ <p>Supported options: <c>-I</c>, <c>-o</c>.</p>
</item>
</taglist>
</section>
@@ -290,20 +292,20 @@ erlc +export_all file.erl</pre>
<section>
<title>Environment Variables</title>
<taglist>
- <tag>ERLC_EMULATOR</tag>
- <item>The command for starting the emulator.
- Default is <em>erl</em> in the same directory as the <em>erlc</em> program
- itself, or if it doesn't exist, <em>erl</em> in any of the directories
- given in the <em>PATH</em> environment variable.</item>
+ <tag><c>ERLC_EMULATOR</c></tag>
+ <item>The command for starting the emulator. Defaults to <c>erl</c>
+ in the same directory as the <c>erlc</c> program itself,
+ or, if it does not exist, <c>erl</c> in any of the directories
+ specified in environment variable <c>PATH</c>.</item>
</taglist>
</section>
<section>
- <title>SEE ALSO</title>
- <p><seealso marker="erl">erl(1)</seealso>,
- <seealso marker="compiler:compile">compile(3)</seealso>,
- <seealso marker="parsetools:yecc">yecc(3)</seealso>,
- <seealso marker="snmp:snmp">snmp(3)</seealso></p>
+ <title>See Also</title>
+ <p><seealso marker="erl"><c>erl(1)</c></seealso>,
+ <seealso marker="compiler:compile"><c>compile(3)</c></seealso>,
+ <seealso marker="parsetools:yecc"><c>yecc(3)</c></seealso>,
+ <seealso marker="snmp:snmp"><c>snmp(3)</c></seealso></p>
</section>
</comref>
diff --git a/erts/doc/src/erlsrv.xml b/erts/doc/src/erlsrv.xml
index ccb8b2dd76..6c08b25220 100644
--- a/erts/doc/src/erlsrv.xml
+++ b/erts/doc/src/erlsrv.xml
@@ -4,7 +4,7 @@
<comref>
<header>
<copyright>
- <year>1998</year><year>2013</year>
+ <year>1998</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -28,343 +28,447 @@
<docno></docno>
<approved></approved>
<checked></checked>
- <date>98-04-29</date>
+ <date>1998-04-29</date>
<rev></rev>
<file>erlsrv.xml</file>
</header>
<com>erlsrv</com>
- <comsummary>Run the Erlang emulator as a service on Windows NT&reg;</comsummary>
+ <comsummary>Run the Erlang emulator as a service on Windows</comsummary>
<description>
- <p>This utility is specific to Windows NT/2000/XP&reg; (and subsequent versions of Windows) It allows Erlang
+ <p>This utility is specific to Windows NT/2000/XP (and later
+ versions of Windows). It allows Erlang
emulators to run as services on the Windows system, allowing embedded
- systems to start without any user needing to log in. The
+ systems to start without any user needing to log on. The
emulator started in this way can be manipulated through the
- Windows&reg; services applet in a manner similar to other
- services.</p>
- <p>Note that erlsrv is not a general service utility for Windows, but designed for embedded Erlang systems.</p>
- <p>As well as being the actual service, erlsrv also provides a
- command line interface for registering, changing, starting and
- stopping services.</p>
- <p>To manipulate services, the logged in user should have
- Administrator privileges on the machine. The Erlang machine
+ Windows services applet in a manner similar to other services.</p>
+
+ <p>Notice that <c>erlsrv</c> is not a general service utility for Windows,
+ but designed for embedded Erlang systems.</p>
+
+ <p><c>erlsrv</c> also provides a command-line interface for registering,
+ changing, starting, and stopping services.</p>
+
+ <p>To manipulate services, the logged on user is to have
+ administrator privileges on the machine. The Erlang machine
itself is (default) run as the local administrator. This can be
- changed with the Services applet in Windows &reg;.</p>
+ changed with the Services applet in Windows.</p>
+
<p>The processes created by the service can, as opposed to normal
- services, be "killed" with the task manager. Killing a emulator
- that is started by a service will trigger the "OnFail" action
- specified for that service, which may be a reboot.</p>
- <p>The following parameters may be specified for each Erlang
- service:</p>
- <list type="bulleted">
+ services, be "killed" with the task manager. Killing an emulator
+ that is started by a service triggers the "OnFail" action
+ specified for that service, which can be a reboot.</p>
+
+ <p>The following parameters can be specified for each Erlang service:</p>
+
+ <taglist>
+ <tag><c><![CDATA[StopAction]]></c></tag>
<item>
- <p><c><![CDATA[StopAction]]></c>: This tells <c><![CDATA[erlsrv]]></c> how to stop
+ <p>Tells <c><![CDATA[erlsrv]]></c> how to stop
the Erlang emulator. Default is to kill it (Win32
TerminateProcess), but this action can specify any Erlang
shell command that will be executed in the emulator to make
it stop. The emulator is expected to stop within 30 seconds
after the command is issued in the shell. If the emulator is
- not stopped, it will report a running state to the service
+ not stopped, it reports a running state to the service
manager.</p>
</item>
+ <tag><c><![CDATA[OnFail]]></c></tag>
<item>
- <p><c><![CDATA[OnFail]]></c>: This can be either of <c><![CDATA[reboot]]></c>,
- <c><![CDATA[restart]]></c>, <c><![CDATA[restart_always]]></c> or <c><![CDATA[ignore]]></c> (the
- default). In case of <c><![CDATA[reboot]]></c>, the NT system is
- rebooted whenever the emulator stops (a more simple form of
- watchdog), this could be useful for less critical systems,
- otherwise use the heart functionality to accomplish
- this. The restart value makes the Erlang emulator be
- restarted (with whatever parameters are registered for the
- service at the occasion) when it stops. If the emulator
- stops again within 10 seconds, it is not restarted to avoid
- an infinite loop which could completely hang the NT
- system. <c><![CDATA[restart_always]]></c> is similar to restart, but
- does not try to detect cyclic restarts, it is expected that
- some other mechanism is present to avoid the problem. The
- default (ignore) just reports the service as stopped to the
- service manager whenever it fails, it has to be manually
- restarted.</p>
- <p>On a system where release handling is
- used, this should always be set to <c><![CDATA[ignore]]></c>. Use
- <c><![CDATA[heart]]></c> to restart the service on failure instead.</p>
+ <p>Can be one of the following:</p>
+ <taglist>
+ <tag><c><![CDATA[reboot]]></c></tag>
+ <item>
+ <p>The Windows system is rebooted whenever the emulator stops
+ (a more simple form of watchdog). This can be useful for
+ less critical systems, otherwise use the heart functionality
+ to accomplish this.</p>
+ </item>
+ <tag><c><![CDATA[restart]]></c></tag>
+ <item>
+ <p>Makes the Erlang emulator be
+ restarted (with whatever parameters are registered for the
+ service at the occasion) when it stops. If the emulator
+ stops again within 10 seconds, it is not restarted to avoid
+ an infinite loop, which could hang the Windows system.</p>
+ </item>
+ <tag><c><![CDATA[restart_always]]></c></tag>
+ <item>
+ <p>Similar to <c><![CDATA[restart]]></c>, but does
+ not try to detect cyclic restarts; it is expected that
+ some other mechanism is present to avoid the problem.</p>
+ </item>
+ <tag><c><![CDATA[ignore]]></c> (the default)</tag>
+ <item>
+ <p>Reports the service as stopped to the service manager
+ whenever it fails; it must be manually restarted.</p>
+ </item>
+ </taglist>
+ <p>On a system where release handling is used,
+ this is always to be set to <c><![CDATA[ignore]]></c>. Use
+ <c><![CDATA[heart]]></c> to restart the service on failure
+ instead.</p>
</item>
+ <tag><c><![CDATA[Machine]]></c></tag>
<item>
- <p><c><![CDATA[Machine]]></c>: The location of the Erlang
- emulator. The default is the <c><![CDATA[erl.exe]]></c> located in the
- same directory as erlsrv.exe. Do not specify <c><![CDATA[werl.exe]]></c>
- as this emulator, it will not work.</p>
- <p>If the system
- uses release handling, this should be set to a program
- similar to <c><![CDATA[start_erl.exe]]></c>.</p>
+ <p>The location of the Erlang emulator.
+ The default is the <c><![CDATA[erl.exe]]></c> located in the same
+ directory as <c>erlsrv.exe</c>. Do not specify
+ <c><![CDATA[werl.exe]]></c> as this emulator, it will not work.</p>
+ <p>If the system uses release handling, this is to be set to a
+ program similar to <c><![CDATA[start_erl.exe]]></c>.</p>
</item>
+ <tag><c><![CDATA[Env]]></c></tag>
<item>
- <p><c><![CDATA[Env]]></c>: Specifies an <em>additional</em> environment
+ <p>Specifies an <em>extra</em> environment
for the emulator. The environment variables specified
- here are added to the system wide environment block that is
+ here are added to the system-wide environment block that is
normally present when a service starts up. Variables present
- in both the system wide environment and in the service
+ in both the system-wide environment and in the service
environment specification will be set to the value specified
in the service.</p>
</item>
+ <tag><c><![CDATA[WorkDir]]></c></tag>
<item>
- <p><c><![CDATA[WorkDir]]></c>: The working directory for the Erlang
- emulator, has to be on a local drive (there are no network
- drives mounted when a service starts). Default working
- directory for services is <c><![CDATA[%SystemDrive%%SystemPath%]]></c>.
+ <p>The working directory for the Erlang emulator.
+ Must be on a local drive (no network drives are mounted when a
+ service starts). Default working directory for services is
+ <c><![CDATA[%SystemDrive%%SystemPath%]]></c>.
Debug log files will be placed in this directory.</p>
</item>
+ <tag><c><![CDATA[Priority]]></c></tag>
<item>
- <p><c><![CDATA[Priority]]></c>: The process priority of the emulator,
- this can be one of <c><![CDATA[realtime]]></c>, <c><![CDATA[high]]></c>, <c><![CDATA[low]]></c>
- or <c><![CDATA[default]]></c> (the default). Real-time priority is not
- recommended, the machine will possibly be inaccessible to
- interactive users. High priority could be used if two Erlang
- nodes should reside on one dedicated system and one should
- have precedence over the other. Low process priority may be
- used if interactive performance should not be affected by
- the emulator process.</p>
+ <p>The process priority of the emulator. Can be one of the
+ following:</p>
+ <taglist>
+ <tag><c><![CDATA[realtime]]></c></tag>
+ <item>
+ <p>Not recommended, as the machine will possibly be
+ inaccessible to interactive users.</p>
+ </item>
+ <tag><c><![CDATA[high]]></c></tag>
+ <item>
+ <p>Can be used if two Erlang nodes are to reside on one dedicated
+ system and one is to have precedence over the other.</p>
+ </item>
+ <tag><c><![CDATA[low]]></c></tag>
+ <item>
+ <p>Can be used if interactive performance is not to be affected
+ by the emulator process.</p>
+ </item>
+ <tag><c><![CDATA[default]]></c> (the default></tag>
+ <item>
+ </item>
+ </taglist>
</item>
+ <tag><c><![CDATA[SName or Name]]></c></tag>
<item>
- <p><c><![CDATA[SName or Name]]></c>: Specifies the short or long
- node-name of the Erlang emulator. The Erlang services are
- always distributed, default is to use the service name as
- (short) node-name.</p>
+ <p>Specifies the short or long
+ node name of the Erlang emulator. The Erlang services are
+ always distributed. Default is to use the service name as
+ (short) nodename.</p>
</item>
+ <tag><c><![CDATA[DebugType]]></c></tag>
<item>
- <p><c><![CDATA[DebugType]]></c>: Can be one of <c><![CDATA[none]]></c> (default),
- <c><![CDATA[new]]></c>, <c><![CDATA[reuse]]></c> or <c><![CDATA[console]]></c>.
- Specifies that output from the Erlang shell should be
+ <p>Specifies that output from the Erlang shell is to be
sent to a "debug log". The log file is named
&lt;servicename&gt;<c><![CDATA[.debug]]></c> or
- &lt;servicename&gt;<c><![CDATA[.debug.]]></c>&lt;N&gt;, where &lt;N&gt; is
- an integer between 1 and 99. The log-file is placed in the
- working directory of the service (as specified in WorkDir). The
- <c><![CDATA[reuse]]></c> option always reuses the same log file
- (&lt;servicename&gt;<c><![CDATA[.debug]]></c>) and the <c><![CDATA[new]]></c> option
- uses a separate log file for every invocation of the service
- (&lt;servicename&gt;<c><![CDATA[.debug.]]></c>&lt;N&gt;). The <c><![CDATA[console]]></c>
- option opens an interactive Windows&reg; console window for
- the Erlang shell of the service. The <c><![CDATA[console]]></c> option
- automatically
- disables the <c><![CDATA[StopAction]]></c> and a service started with an
- interactive console window will not survive logouts,
- <c><![CDATA[OnFail]]></c> actions do not work with debug-consoles either.
- If no <c><![CDATA[DebugType]]></c> is specified (<c><![CDATA[none]]></c>), the
- output of the Erlang shell is discarded.</p>
- <p>The <c><![CDATA[console]]></c><c><![CDATA[DebugType]]></c> is <em>not in any way</em>
- intended for production. It is <em>only</em> a convenient way to
- debug Erlang services during development. The <c><![CDATA[new]]></c> and
- <c><![CDATA[reuse]]></c> options might seem convenient to have in a
- production system, but one has to take into account that the
- logs will grow indefinitely during the systems lifetime and
- there is no way, short of restarting the service, to
- truncate those logs. In short, the <c><![CDATA[DebugType]]></c> is
- intended for debugging only. Logs during production are
- better produced with the standard Erlang logging
- facilities.</p>
+ &lt;servicename&gt;<c><![CDATA[.debug.]]></c>&lt;N&gt;,
+ where &lt;N&gt; is an integer from 1 through 99.
+ The log file is placed in the working directory of the
+ service (as specified in <c>WorkDir</c>).</p>
+ <p>Can be one of the following:</p>
+ <taglist>
+ <tag><c><![CDATA[new]]></c></tag>
+ <item>
+ <p>Uses a separate log file for every invocation of the service
+ (&lt;servicename&gt;<c><![CDATA[.debug.]]></c>&lt;N&gt;).</p>
+ </item>
+ <tag><c><![CDATA[reuse]]></c></tag>
+ <item>
+ <p>Reuses the same log file
+ (&lt;servicename&gt;<c><![CDATA[.debug]]></c>).</p>
+ </item>
+ <tag><c><![CDATA[console]]></c></tag>
+ <item>
+ <p>Opens an interactive Windows console window for the Erlang
+ shell of the service. Automatically disables the
+ <c><![CDATA[StopAction]]></c>. A service started with an
+ interactive console window does not survive logouts.
+ <c><![CDATA[OnFail]]></c> actions do not work with
+ debug consoles either.</p>
+ </item>
+ <tag><c><![CDATA[none]]></c> (the default)</tag>
+ <item>
+ <p>The output of the Erlang shell is discarded.</p>
+ </item>
+ </taglist>
+ <note>
+ <p>The <c><![CDATA[console]]></c> option is <em>not</em> intended
+ for production. It is <em>only</em> a convenient way to debug
+ Erlang services during development.</p>
+ <p>The <c><![CDATA[new]]></c> and <c><![CDATA[reuse]]></c> options
+ might seem convenient in a production system, but consider that
+ the logs grow indefinitely during the system lifetime and cannot
+ be truncated, except if the service is restarted.</p>
+ <p>In short, the <c><![CDATA[DebugType]]></c> is
+ intended for debugging only. Logs during production are
+ better produced with the standard Erlang logging facilities.</p>
+ </note>
</item>
+ <tag><c><![CDATA[Args]]></c></tag>
<item>
- <p><c><![CDATA[Args]]></c>: Additional arguments passed to the
- emulator startup program <c><![CDATA[erl.exe]]></c> (or
- <c><![CDATA[start_erl.exe]]></c>). Arguments that cannot be specified
- here are <c><![CDATA[-noinput]]></c> (StopActions would not work),
- <c><![CDATA[-name]]></c> and <c><![CDATA[-sname]]></c> (they are specified in any
- way. The most common use is for specifying cookies and flags
- to be passed to init:boot() (<c><![CDATA[-s]]></c>).</p>
+ <p>Passes extra arguments to the emulator startup program
+ <c><![CDATA[erl.exe]]></c> (or <c><![CDATA[start_erl.exe]]></c>).
+ Arguments that cannot be specified here are
+ <c><![CDATA[-noinput]]></c> (<c>StopActions</c> would not work),
+ <c><![CDATA[-name]]></c>, and <c><![CDATA[-sname]]></c> (they are
+ specified in any way). The most common use is for specifying cookies
+ and flags to be passed to <c>init:boot()</c>
+ (<c><![CDATA[-s]]></c>).</p>
</item>
+ <tag><c><![CDATA[InternalServiceName]]></c></tag>
<item>
- <p><c><![CDATA[InternalServiceName]]></c>: Specifies the Windows&reg; internal service name (not the display name, which is the one <c>erlsrv</c> uses to identify the service).</p>
- <p>This internal name can not be changed, it is fixed even if the service is renamed. <c>Erlsrv</c> generates a unique internal name when a service is created, it is recommended to keep to the defaut if release-handling is to be used for the application.</p>
- <p>The internal service name can be seen in the Windows&reg; service manager if viewing <c>Properties</c> for an erlang service.</p>
+ <p>Specifies the Windows-internal service name (not the display name,
+ which is the one <c>erlsrv</c> uses to identify the service).</p>
+ <p>This internal name cannot be changed, it is fixed even if the
+ service is renamed. <c>erlsrv</c> generates a unique internal name
+ when a service is created. It is recommended to keep to the default
+ if release handling is to be used for the application.</p>
+ <p>The internal service name can be seen in the Windows service
+ manager if viewing <c>Properties</c> for an Erlang service.</p>
</item>
+ <tag><c><![CDATA[Comment]]></c></tag>
<item>
- <p><c><![CDATA[Comment]]></c>: A textual comment describing the service. Not mandatory, but shows up as the service description in the Windows&reg; service manager.</p>
+ <p>A textual comment describing the service. Not mandatory, but shows
+ up as the service description in the Windows service manager.</p>
</item>
- </list>
- <p> <marker id="001"></marker>
- The naming of the service in a system that
- uses release handling has to follow the convention
+ </taglist>
+
+ <p><marker id="001"></marker>
+ The naming of the service in a system that
+ uses release handling must follow the convention
<em>NodeName</em>_<em>Release</em>, where <em>NodeName</em> is
- the first part of the Erlang nodename (up to, but not including
+ the first part of the Erlang node name (up to, but not including
the "@") and <em>Release</em> is the current release of the
application.</p>
</description>
+
<funcs>
<func>
<name>erlsrv {set | add} &lt;service-name> [&lt;service options>]</name>
- <fsummary>Add or modify an Erlang service</fsummary>
+ <fsummary>Add or modify an Erlang service.</fsummary>
<desc>
- <p>The set and add commands adds or modifies a Erlang service
- respectively. The simplest form of an add command would be
- completely without options in which case all default values
+ <p>The <c>set</c> and <c>add</c> commands modifies or adds an Erlang
+ service, respectively. The simplest form of an <c>add</c> command is
+ without any options in which case all default values
(described above) apply. The service name is mandatory.</p>
- <p>Every option can be given without parameters, in which case
- the default value is applied. Values to the options are
- supplied <em>only</em> when the default should not be used
- (i.e. <c><![CDATA[erlsrv set myservice -prio -arg]]></c> sets the
- default priority and removes all arguments).</p>
- <p>The following service options are currently available:</p>
+ <p>Every option can be specified without parameters, the
+ default value is then applied. Values to the options are
+ supplied <em>only</em> when the default is not to be used.
+ For example, <c><![CDATA[erlsrv set myservice -prio -arg]]></c>
+ sets the default priority and removes all arguments.</p>
+ <p>Service options:</p>
<taglist>
- <tag>-st[opaction] [&lt;erlang shell command&gt;]</tag>
- <item>Defines the StopAction, the command given to the Erlang
- shell when the service is stopped. Default is none.</item>
- <tag>-on[fail] [{reboot | restart | restart_always}]</tag>
- <item>Specifies the action to take when the Erlang emulator
- stops unexpectedly. Default is to ignore.</item>
- <tag>-m[achine] [&lt;erl-command&gt;]</tag>
- <item>The complete path to the Erlang emulator, never use the
- werl program for this. Default is the <c><![CDATA[erl.exe]]></c> in the
- same directory as <c><![CDATA[erlsrv.exe]]></c>. When release handling
- is used, this should be set to a program similar to
- <c><![CDATA[start_erl.exe]]></c>.</item>
- <tag>-e[nv] [&lt;variable&gt;[=&lt;value&gt;]] ...</tag>
- <item>Edits the environment block for the service. Every
- environment variable specified will add to the system
- environment block. If a variable specified here has the same
- name as a system wide environment variable, the specified
- value overrides the system wide. Environment variables are
- added to this list by specifying
- &lt;variable&gt;=&lt;value&gt; and deleted from the list by
- specifying &lt;variable&gt; alone. The environment block is
- automatically sorted. Any number of <c><![CDATA[-env]]></c> options can
- be specified in one command. Default is to use the system
- environment block unmodified (except for two additions, see
- <seealso marker="#002">below</seealso>).</item>
- <tag>-w[orkdir] [&lt;directory&gt;]</tag>
- <item>The initial working directory of the Erlang
- emulator. Default is the system directory.</item>
- <tag>-p[riority] [{low|high|realtime}]</tag>
- <item>The priority of the Erlang emulator. The default is the
- Windows&reg; default priority.</item>
- <tag>{-sn[ame] | -n[ame]} [&lt;node-name&gt;]</tag>
- <item>The node-name of the Erlang machine, distribution is
- mandatory. Default is <c><![CDATA[-sname <service name>]]></c>.
+ <tag><c>-st[opaction] [&lt;erlang shell command&gt;]</c></tag>
+ <item>
+ <p>Defines the <c><![CDATA[StopAction]]></c>, the command given
+ to the Erlang shell when the service is stopped.
+ Default is none.</p>
+ </item>
+ <tag><c>-on[fail] [{reboot | restart | restart_always}]</c></tag>
+ <item>
+ <p>The action to take when the Erlang emulator
+ stops unexpectedly. Default is to ignore.</p>
+ </item>
+ <tag><c>-m[achine] [&lt;erl-command&gt;]</c></tag>
+ <item>
+ <p>The complete path to the Erlang emulator. Never use the
+ <c>werl</c> program for this. Defaults to the
+ <c><![CDATA[erl.exe]]></c> in the same directory as
+ <c><![CDATA[erlsrv.exe]]></c>. When release handling
+ is used, this is to be set to a program similar to
+ <c><![CDATA[start_erl.exe]]></c>.</p>
+ </item>
+ <tag><c>-e[nv] [&lt;variable&gt;[=&lt;value&gt;]] ...</c></tag>
+ <item>
+ <p>Edits the environment block for the service. Every
+ environment variable specified is added to the system
+ environment block. If a variable specified here has the same
+ name as a system-wide environment variable, the specified
+ value overrides the system-wide. Environment variables are
+ added to this list by specifying
+ &lt;variable&gt;=&lt;value&gt; and deleted from the list by
+ specifying &lt;variable&gt; alone. The environment block is
+ automatically sorted. Any number of <c><![CDATA[-env]]></c>
+ options can be specified in one command. Default is to use the
+ system environment block unmodified (except for two additions,
+ see section <seealso marker="#002">Environment</seealso>
+ below).</p>
+ </item>
+ <tag><c>-w[orkdir] [&lt;directory&gt;]</c></tag>
+ <item>
+ <p>The initial working directory of the Erlang
+ emulator. Defaults to the system directory.</p>
+ </item>
+ <tag><c>-p[riority] [{low|high|realtime}]</c></tag>
+ <item>
+ <p>The priority of the Erlang emulator. Default to the
+ Windows default priority.</p>
+ </item>
+ <tag><c>{-sn[ame] | -n[ame]} [&lt;node-name&gt;]</c></tag>
+ <item>
+ <p>The node name of the Erlang machine. Distribution is mandatory.
+ Defaults to <c><![CDATA[-sname <service name>]]></c>.</p>
+ </item>
+ <tag><c>-d[ebugtype] [{new|reuse|console}]</c></tag>
+ <item>
+ <p>Specifies where shell output is to be sent.
+ Default is that shell output is discarded.
+ To be used only for debugging.</p>
+ </item>
+ <tag><c>-ar[gs] [&lt;limited erl arguments&gt;]</c></tag>
+ <item>
+ <p>Extra arguments to the Erlang emulator. Avoid
+ <c><![CDATA[-noinput]]></c>, <c><![CDATA[-noshell]]></c>, and
+ <c><![CDATA[-sname]]></c>/<c><![CDATA[-name]]></c>. Default is
+ no extra arguments. Remember that the services cookie file is not
+ necessarily the same as the interactive users. The service
+ runs as the local administrator. Specify all arguments
+ together in one string, use double quotes (") to specify an
+ argument string containing spaces, and use quoted quotes (\")
+ to specify a quote within the argument string if necessary.</p>
+ </item>
+ <tag><c>-i[nternalservicename] [&lt;internal name&gt;]</c></tag>
+ <item>
+ <p><em>Only</em> allowed for <c>add</c>. Specifies a
+ Windows-internal service name for the service, which by
+ default is set to something unique (prefixed with the
+ original service name) by <c>erlsrv</c> when adding a new
+ service. Specifying this is a purely cosmethic action and is
+ <em>not</em> recommended if release handling is to be
+ performed. The internal service name cannot be changed once
+ the service is created. The internal name is <em>not</em> to
+ be confused with the ordinary service name, which is the name
+ used to identify a service to <c>erlsrv</c>.</p>
+ </item>
+ <tag><c>-c[omment] [&lt;short description&gt;]</c></tag>
+ <item>
+ <p>Specifies a textual comment describing the
+ service. This comment shows up as the service description
+ in the Windows service manager.</p>
</item>
- <tag>-d[ebugtype] [{new|reuse|console}]</tag>
- <item>Specifies where shell output should be sent,
- default is that shell output is discarded.
- To be used only for debugging.</item>
- <tag>-ar[gs] [&lt;limited erl arguments&gt;]</tag>
- <item>Additional arguments to the Erlang emulator, avoid
- <c><![CDATA[-noinput]]></c>, <c><![CDATA[-noshell]]></c> and
- <c><![CDATA[-sname]]></c>/<c><![CDATA[-name]]></c>. Default is no additional
- arguments. Remember that the services cookie file is not
- necessarily the same as the interactive users. The service
- runs as the local administrator. All arguments should be given
- together in one string, use double quotes (") to give an
- argument string containing spaces and use quoted quotes (\")
- to give an quote within the argument string if
- necessary.</item>
- <tag>-i[nternalservicename] [&lt;internal name&gt;]</tag>
- <item><em>Only</em> allowed for <c>add</c>. Specifies a
- Windows&reg; internal service name for the service, which by
- default is set to something unique (prefixed with the
- original service name) by erlsrv when adding a new
- service. Specifying this is a purely cosmethic action and is
- <em>not</em> recommended if release handling is to be
- performed. The internal service name cannot be changed once
- the service is created. The internal name is <em>not</em> to
- be confused with the ordinary service name, which is the name
- used to identify a service to erlsrv.</item>
- <tag>-c[omment] [&lt;short description&gt;]</tag>
- <item>Specifies a textual comment describing the
- service. This comment will show upp as the service description
- in the Windows&reg; service manager.</item>
</taglist>
</desc>
</func>
+
<func>
- <name>erlsrv {start | start_disabled | stop | disable | enable} &lt;service-name></name>
+ <name>erlsrv {start | start_disabled | stop | disable |
+ enable} &lt;service-name></name>
<fsummary>Manipulate the current service status.</fsummary>
<desc>
<p>These commands are only added for convenience, the normal
way to manipulate the state of a service is through the
- control panels services applet. The <c><![CDATA[start]]></c> and
+ control panels services applet.</p>
+ <p>The <c><![CDATA[start]]></c> and
<c><![CDATA[stop]]></c> commands communicates
- with the service manager for stopping and starting a
- service. The commands wait until the service is actually
- stopped or started. When disabling a service, it is not
- stopped, the disabled state will not take effect until the
- service actually is stopped. Enabling a service sets it in
- automatic mode, that is started at boot. This command cannot
- set the service to manual. </p>
-
- <p>The <c>start_disabled</c> command operates on a service
- regardless of if it's enabled/disabled or started/stopped. It
- does this by first enabling it (regardless of if it's enabled
- or not), then starting it (if it's not already started) and
- then disabling it. The result will be a disabled but started
- service, regardless of its earlier state. This is useful for
- starting services temporarily during a release upgrade. The
- difference between using <c>start_disabled</c> and the
- sequence <c>enable</c>, <c>start</c> and <c>disable</c> is
- that all other <c>erlsrv</c> commands are locked out during
- the sequence of operations in <c>start_disable</c>, making the
- operation atomic from an <c>erlsrv</c> user's point of
- view.</p>
-
+ with the service manager for starting and stopping a
+ service. The commands wait until the service is
+ started or stopped. When disabling a service, it is not
+ stopped, the disabled state does not take effect until the
+ service is stopped. Enabling a service sets it in
+ automatic mode, which is started at boot. This command cannot
+ set the service to manual.</p>
+ <p>The <c>start_disabled</c> command operates on a service
+ regardless of if it is enabled/disabled or started/stopped. It
+ does this by first enabling it (regardless of if it is enabled
+ or not), then starting it (if not already started), and
+ then disabling it. The result is a disabled but started
+ service, regardless of its earlier state. This is useful for
+ starting services temporarily during a release upgrade. The
+ difference between using <c>start_disabled</c> and the
+ sequence <c>enable</c>, <c>start</c>, and <c>disable</c> is
+ that all other <c>erlsrv</c> commands are locked out during
+ the sequence of operations in <c>start_disable</c>, making the
+ operation atomic from an <c>erlsrv</c> user's point of view.</p>
</desc>
</func>
+
<func>
<name>erlsrv remove &lt;service-name&gt;</name>
<fsummary>Remove the service.</fsummary>
<desc>
- <p>This command removes the service completely with all its registered
- options. It will be stopped before it is removed.</p>
+ <p>Removes the service completely with all its registered
+ options. It is stopped before it is removed.</p>
</desc>
</func>
+
<func>
<name>erlsrv list [&lt;service-name&gt;]</name>
- <fsummary>List all Erlang services or all options for one service.</fsummary>
+ <fsummary>List all Erlang services or all options for one service.
+ </fsummary>
<desc>
- <p>If no service name is supplied, a brief listing of all Erlang services
- is presented. If a service-name is supplied, all options for that
- service are presented.</p>
+ <p>If no service name is specified, a brief listing of all Erlang
+ services is presented. If a service name is supplied, all options
+ for that service are presented.</p>
</desc>
</func>
+
<func>
<name>erlsrv help</name>
- <fsummary>Display a brief help text</fsummary>
+ <fsummary>Display a brief help text.</fsummary>
+ <desc>
+ <p>Displays a brief help text.</p>
+ </desc>
</func>
</funcs>
<section>
- <title>ENVIRONMENT</title>
- <p> <marker id="002"></marker>
-The environment of an Erlang machine started
- as a service will contain two special variables,
- <c><![CDATA[ERLSRV_SERVICE_NAME]]></c>, which is the name of the service that
- started the machine and <c><![CDATA[ERLSRV_EXECUTABLE]]></c> which is the
- full path to the <c><![CDATA[erlsrv.exe]]></c> that can be used to manipulate
- the service. This will come in handy when defining a heart command for
- your service. A command file for restarting a service will
- simply look like this:</p>
+ <title>Environment</title>
+ <p><marker id="002"></marker>
+ The environment of an Erlang machine started
+ as a service contains two special variables:</p>
+
+ <taglist>
+ <tag><c><![CDATA[ERLSRV_SERVICE_NAME]]></c></tag>
+ <item>The name of the service that started the machine.</item>
+ <tag><c><![CDATA[ERLSRV_EXECUTABLE]]></c></tag>
+ <item>The full path to the <c><![CDATA[erlsrv.exe]]></c>, which can be
+ used to manipulate the service. This comes in handy when defining a
+ heart command for your service.</item>
+ </taglist>
+
+ <p>A command file for restarting a service looks as follows:</p>
+
<code type="none"><![CDATA[
@echo off
%ERLSRV_EXECUTABLE% stop %ERLSRV_SERVICE_NAME%
%ERLSRV_EXECUTABLE% start %ERLSRV_SERVICE_NAME% ]]></code>
+
<p>This command file is then set as heart command.</p>
+
<p>The environment variables can also be used to detect that we
are running as a service and make port programs react correctly
- to the control events generated on logout (see below).</p>
+ to the control events generated on logout (see the next section).</p>
</section>
<section>
- <title>PORT PROGRAMS</title>
+ <title>Port Programs</title>
<p>When a program runs in
- the service context, it has to handle the control events that is
+ the service context, it must handle the control events that are
sent to every program in the system when the interactive user
logs off. This is done in different ways for programs running in
the console subsystem and programs running as window
- applications. An application which runs in the console subsystem
+ applications. An application running in the console subsystem
(normal for port programs) uses the win32 function
<c><![CDATA[SetConsoleCtrlHandler]]></c> to register a control handler
- that returns TRUE in answer to the <c><![CDATA[CTRL_LOGOFF_EVENT]]></c>
+ that returns <c>true</c> in answer to the
+ <c><![CDATA[CTRL_LOGOFF_EVENT]]></c>
and <c><![CDATA[CTRL_SHUTDOWN_EVENT]]></c> events. Other applications
- just forward <c><![CDATA[WM_ENDSESSION]]></c> and
- <c><![CDATA[WM_QUERYENDSESSION]]></c> to the default window procedure.
- Here is a brief example in C of how to set the console control
- handler:</p>
+ only forward <c><![CDATA[WM_ENDSESSION]]></c> and
+ <c><![CDATA[WM_QUERYENDSESSION]]></c> to the default window procedure.</p>
+
+ <p>A brief example in C of how to set the console control handler:</p>
+
<code type="none"><![CDATA[
#include <windows.h>
/*
@@ -383,7 +487,7 @@ void initialize_handler(void){
char buffer[2];
/*
* We assume we are running as a service if this
- * environment variable is defined
+ * environment variable is defined.
*/
if(GetEnvironmentVariable("ERLSRV_SERVICE_NAME",buffer,
(DWORD) 2)){
@@ -396,29 +500,34 @@ void initialize_handler(void){
</section>
<section>
- <title>NOTES</title>
- <p>Even though the options are described in a Unix-like format, the case of
- the options or commands is not relevant, and the "/" character for options
- can be used as well as the "-" character. </p>
- <p>Note that the program resides in the emulators
- <c><![CDATA[bin]]></c>-directory, not in the <c><![CDATA[bin]]></c>-directory directly under
+ <title>Notes</title>
+ <p>Although the options are described in a Unix-like format, the case of
+ the options or commands is not relevant, and both character "/" and "-"
+ can be used for options.</p>
+
+ <p>Notice that the program resides in the emulator's <c><![CDATA[bin]]></c>
+ directory, not in the <c><![CDATA[bin]]></c> directory directly under
the Erlang root. The reasons for this are the subtle problem of
upgrading the emulator on a running system, where a new version of
the runtime system should not need to overwrite existing (and probably
used) executables.</p>
- <p>To easily manipulate the Erlang services, put
+
+ <p>To manipulate the Erlang services easily, put
the <c><![CDATA[<erlang_root>\erts-<version>\bin]]></c> directory in
- the path instead of <c><![CDATA[<erlang_root>\bin]]></c>. The erlsrv program
- can be found from inside Erlang by using the
+ the path instead of <c><![CDATA[<erlang_root>\bin]]></c>. The
+ <c>erlsrv</c> program can be found from inside Erlang by using the
<c><![CDATA[os:find_executable/1]]></c> Erlang function.</p>
- <p>For release handling to work, use <c><![CDATA[start_erl]]></c> as the Erlang
- machine. It is also worth mentioning again that the name of the
- service is significant (see <seealso marker="#001">above</seealso>).</p>
+
+ <p>For release handling to work, use <c><![CDATA[start_erl]]></c> as the
+ Erlang machine. As stated <seealso marker="#001">above</seealso>,
+ the service name is significant.</p>
</section>
<section>
- <title>SEE ALSO</title>
- <p>start_erl(1), release_handler(3)</p>
+ <title>See Also</title>
+ <p><seealso marker="start_erl"><c>start_erl(1)</c></seealso>,
+ <seealso marker="sasl:release_handler">
+ <c>release_handler(3)</c></seealso></p>
</section>
</comref>
diff --git a/erts/doc/src/erts_alloc.xml b/erts/doc/src/erts_alloc.xml
index 15b78ffa10..8ab35851c1 100644
--- a/erts/doc/src/erts_alloc.xml
+++ b/erts/doc/src/erts_alloc.xml
@@ -4,7 +4,7 @@
<cref>
<header>
<copyright>
- <year>2002</year><year>2015</year>
+ <year>2002</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -25,68 +25,80 @@
<title>erts_alloc</title>
<prepared>Rickard Green</prepared>
<docno>1</docno>
- <date>03-06-11</date>
+ <date>2003-06-11</date>
<rev>1</rev>
<file>erts_alloc.xml</file>
</header>
<lib>erts_alloc</lib>
- <libsummary>An Erlang Run-Time System internal memory allocator library.</libsummary>
+ <libsummary>An Erlang runtime system internal memory allocator library.
+ </libsummary>
<description>
- <p><c>erts_alloc</c> is an Erlang Run-Time System internal memory
+ <p><c>erts_alloc</c> is an Erlang runtime system internal memory
allocator library. <c>erts_alloc</c> provides the Erlang
- Run-Time System with a number of memory allocators.</p>
+ runtime system with a number of memory allocators.</p>
</description>
<section>
<title>Allocators</title>
<marker id="allocators"></marker>
- <p>Currently the following allocators are present:</p>
+ <p>The following allocators are present:</p>
+
<taglist>
<tag><c>temp_alloc</c></tag>
<item>Allocator used for temporary allocations.</item>
<tag><c>eheap_alloc</c></tag>
- <item>Allocator used for Erlang heap data, such as Erlang process heaps.</item>
+ <item>Allocator used for Erlang heap data, such as Erlang process heaps.
+ </item>
<tag><c>binary_alloc</c></tag>
<item>Allocator used for Erlang binary data.</item>
<tag><c>ets_alloc</c></tag>
- <item>Allocator used for ETS data.</item>
+ <item>Allocator used for <c>ets</c> data.</item>
<tag><c>driver_alloc</c></tag>
<item>Allocator used for driver data.</item>
+ <tag><c>literal_alloc</c></tag>
+ <item>Allocator used for constant terms in Erlang code.</item>
<tag><c>sl_alloc</c></tag>
<item>Allocator used for memory blocks that are expected to be
- short-lived.</item>
+ short-lived.</item>
<tag><c>ll_alloc</c></tag>
<item>Allocator used for memory blocks that are expected to be
- long-lived, for example Erlang code.</item>
+ long-lived, for example, Erlang code.</item>
<tag><c>fix_alloc</c></tag>
<item>A fast allocator used for some frequently used
fixed size data types.</item>
+ <tag><c>exec_alloc</c></tag>
+ <item>Allocator used by the <seealso marker="hipe:HiPE_app"><c>HiPE</c></seealso>
+ application for native executable code on specific architectures
+ (x86_64).</item>
<tag><c>std_alloc</c></tag>
- <item>Allocator used for most memory blocks not allocated via any of
- the other allocators described above.</item>
+ <item>Allocator used for most memory blocks not allocated through any of
+ the other allocators described above.</item>
<tag><c>sys_alloc</c></tag>
<item>This is normally the default <c>malloc</c> implementation
- used on the specific OS.</item>
+ used on the specific OS.</item>
<tag><c>mseg_alloc</c></tag>
- <item>A memory segment allocator. <c>mseg_alloc</c> is used by other
- allocators for allocating memory segments and is currently only
- available on systems that have the <c>mmap</c> system
- call. Memory segments that are deallocated are kept for a
- while in a segment cache before they are destroyed. When
- segments are allocated, cached segments are used if possible
- instead of creating new segments. This in order to reduce
- the number of system calls made.</item>
+ <item>A memory segment allocator. It is used by other
+ allocators for allocating memory segments and is only
+ available on systems that have the <c>mmap</c> system
+ call. Memory segments that are deallocated are kept for a
+ while in a segment cache before they are destroyed. When
+ segments are allocated, cached segments are used if possible
+ instead of creating new segments. This to reduce
+ the number of system calls made.</item>
</taglist>
- <p><c>sys_alloc</c> is always enabled and
- cannot be disabled. <c>mseg_alloc</c> is always enabled if it is
+
+ <p><c>sys_alloc</c> and <c>literal_alloc</c> are always enabled and
+ cannot be disabled. <c>exec_alloc</c> is only available if it is needed
+ and cannot be disabled. <c>mseg_alloc</c> is always enabled if it is
available and an allocator that uses it is enabled. All other
allocators can be <seealso marker="#M_e">enabled or disabled</seealso>.
By default all allocators are enabled.
When an allocator is disabled, <c>sys_alloc</c> is used instead of
the disabled allocator.</p>
+
<p>The main idea with the <c>erts_alloc</c> library is to separate
memory blocks that are used differently into different memory
- areas, and by this achieving less memory fragmentation. By
+ areas, to achieve less memory fragmentation. By
putting less effort in finding a good fit for memory blocks that
are frequently allocated than for those less frequently
allocated, a performance gain can be achieved.</p>
@@ -94,61 +106,85 @@
<section>
<marker id="alloc_util"></marker>
- <title>The alloc_util framework</title>
+ <title>The alloc_util Framework</title>
<p>Internally a framework called <c>alloc_util</c> is used for
- implementing allocators. <c>sys_alloc</c>, and
- <c>mseg_alloc</c> do not use this framework; hence, the
+ implementing allocators. <c>sys_alloc</c> and
+ <c>mseg_alloc</c> do not use this framework, so the
following does <em>not</em> apply to them.</p>
+
<p>An allocator manages multiple areas, called carriers, in which
memory blocks are placed. A carrier is either placed in a
- separate memory segment (allocated via <c>mseg_alloc</c>), or in
- the heap segment (allocated via <c>sys_alloc</c>). Multiblock
- carriers are used for storage of several blocks. Singleblock
- carriers are used for storage of one block. Blocks that are
- larger than the value of the singleblock carrier threshold
- (<seealso marker="#M_sbct">sbct</seealso>) parameter are placed
- in singleblock carriers. Blocks that are smaller than the value
- of the <c>sbct</c> parameter are placed in multiblock
- carriers. Normally an allocator creates a "main multiblock
+ separate memory segment (allocated through <c>mseg_alloc</c>), or in
+ the heap segment (allocated through <c>sys_alloc</c>).</p>
+
+ <list type="bulleted">
+ <item>
+ <p>Multiblock carriers are used for storage of several blocks.</p>
+ </item>
+ <item>
+ <p>Singleblock carriers are used for storage of one block.</p>
+ </item>
+ <item>
+ <p>Blocks that are larger than the value of the singleblock carrier
+ threshold (<seealso marker="#M_sbct"><c>sbct</c></seealso>) parameter
+ are placed in singleblock carriers.</p>
+ </item>
+ <item>
+ <p>Blocks that are smaller than the value of parameter <c>sbct</c>
+ are placed in multiblock carriers.</p></item>
+ </list>
+
+ <p>Normally an allocator creates a "main multiblock
carrier". Main multiblock carriers are never deallocated. The
- size of the main multiblock carrier is determined by the value
- of the <seealso marker="#M_mmbcs">mmbcs</seealso> parameter.</p>
+ size of the main multiblock carrier is determined by the value of
+ parameter <seealso marker="#M_mmbcs"><c>mmbcs</c></seealso>.</p>
+
<p><marker id="mseg_mbc_sizes"></marker>Sizes of multiblock carriers
- allocated via <c>mseg_alloc</c> are
- decided based on the values of the largest multiblock carrier
- size (<seealso marker="#M_lmbcs">lmbcs</seealso>), the smallest
- multiblock carrier size (<seealso marker="#M_smbcs">smbcs</seealso>),
- and the multiblock carrier growth stages
- (<seealso marker="#M_mbcgs">mbcgs</seealso>) parameters. If
- <c>nc</c> is the current number of multiblock carriers (the main
+ allocated through <c>mseg_alloc</c> are decided based on the
+ following parameters:</p>
+
+ <list type="bulleted">
+ <item>The values of the largest multiblock carrier size
+ (<seealso marker="#M_lmbcs"><c>lmbcs</c></seealso>)</item>
+ <item>The smallest multiblock carrier size
+ (<seealso marker="#M_smbcs"><c>smbcs</c></seealso>)</item>
+ <item>The multiblock carrier growth stages
+ (<seealso marker="#M_mbcgs"><c>mbcgs</c></seealso>)</item>
+ </list>
+
+ <p>If <c>nc</c> is the current number of multiblock carriers (the main
multiblock carrier excluded) managed by an allocator, the size
of the next <c>mseg_alloc</c> multiblock carrier allocated by
- this allocator will roughly be
+ this allocator is roughly
<c><![CDATA[smbcs+nc*(lmbcs-smbcs)/mbcgs]]></c> when
<c><![CDATA[nc <= mbcgs]]></c>,
- and <c>lmbcs</c> when <c><![CDATA[nc > mbcgs]]></c>. If the value of the
- <c>sbct</c> parameter should be larger than the value of the
- <c>lmbcs</c> parameter, the allocator may have to create
- multiblock carriers that are larger than the value of the
- <c>lmbcs</c> parameter, though.
- Singleblock carriers allocated via <c>mseg_alloc</c> are sized
+ and <c>lmbcs</c> when <c><![CDATA[nc > mbcgs]]></c>. If the value of
+ parameter <c>sbct</c> is larger than the value of parameter
+ <c>lmbcs</c>, the allocator may have to create
+ multiblock carriers that are larger than the value of
+ parameter <c>lmbcs</c>, though.
+ Singleblock carriers allocated through <c>mseg_alloc</c> are sized
to whole pages.</p>
- <p>Sizes of carriers allocated via <c>sys_alloc</c> are
+
+ <p>Sizes of carriers allocated through <c>sys_alloc</c> are
decided based on the value of the <c>sys_alloc</c> carrier size
- (<seealso marker="#Muycs">ycs</seealso>) parameter. The size of
- a carrier is the least number of multiples of the value of the
- <c>ycs</c> parameter that satisfies the request.</p>
+ (<seealso marker="#Muycs"><c>ycs</c></seealso>) parameter. The size of
+ a carrier is the least number of multiples of the value of
+ parameter <c>ycs</c> satisfying the request.</p>
+
<p>Coalescing of free blocks are always performed immediately.
- Boundary tags (headers and footers) in free blocks are used
+ Boundary tags (headers and footers) in free blocks are used,
which makes the time complexity for coalescing constant.</p>
+
<p><marker id="strategy"></marker>The memory allocation strategy
- used for multiblock carriers by an
- allocator is configurable via the <seealso marker="#M_as">as</seealso>
- parameter. Currently the following strategies are available:</p>
+ used for multiblock carriers by an allocator can be
+ configured using parameter <seealso marker="#M_as"><c>as</c></seealso>.
+ The following strategies are available:</p>
+
<taglist>
<tag>Best fit</tag>
<item>
- <p>Strategy: Find the smallest block that satisfies the
+ <p>Strategy: Find the smallest block satisfying the
requested block size.</p>
<p>Implementation: A balanced binary search tree is
used. The time complexity is proportional to log N, where
@@ -156,7 +192,7 @@
</item>
<tag>Address order best fit</tag>
<item>
- <p>Strategy: Find the smallest block that satisfies the
+ <p>Strategy: Find the smallest block satisfying the
requested block size. If multiple blocks are found, choose
the one with the lowest address.</p>
<p>Implementation: A balanced binary search tree is
@@ -165,7 +201,7 @@
</item>
<tag>Address order first fit</tag>
<item>
- <p>Strategy: Find the block with the lowest address that satisfies the
+ <p>Strategy: Find the block with the lowest address satisfying the
requested block size.</p>
<p>Implementation: A balanced binary search tree is
used. The time complexity is proportional to log N, where
@@ -174,8 +210,8 @@
<tag>Address order first fit carrier best fit</tag>
<item>
<p>Strategy: Find the <em>carrier</em> with the lowest address that
- can satisfy the requested block size, then find a block within
- that carrier using the "best fit" strategy.</p>
+ can satisfy the requested block size, then find a block within
+ that carrier using the "best fit" strategy.</p>
<p>Implementation: Balanced binary search trees are
used. The time complexity is proportional to log N, where
N is the number of free blocks.</p>
@@ -183,8 +219,8 @@
<tag>Address order first fit carrier address order best fit</tag>
<item>
<p>Strategy: Find the <em>carrier</em> with the lowest address that
- can satisfy the requested block size, then find a block within
- that carrier using the "adress order best fit" strategy.</p>
+ can satisfy the requested block size, then find a block within
+ that carrier using the "address order best fit" strategy.</p>
<p>Implementation: Balanced binary search trees are
used. The time complexity is proportional to log N, where
N is the number of free blocks.</p>
@@ -194,12 +230,12 @@
<p>Strategy: Try to find the best fit, but settle for the best fit
found during a limited search.</p>
<p>Implementation: The implementation uses segregated free
- lists with a maximum block search depth (in each list) in
- order to find a good fit fast. When the maximum block
- search depth is small (by default 3) this implementation
+ lists with a maximum block search depth (in each list)
+ to find a good fit fast. When the maximum block
+ search depth is small (by default 3), this implementation
has a time complexity that is constant. The maximum block
- search depth is configurable via the
- <seealso marker="#M_mbsd">mbsd</seealso> parameter.</p>
+ search depth can be configured using parameter
+ <seealso marker="#M_mbsd"><c>mbsd</c></seealso>.</p>
</item>
<tag>A fit</tag>
<item>
@@ -207,452 +243,557 @@
block to see if it satisfies the request. This strategy is
only intended to be used for temporary allocations.</p>
<p>Implementation: Inspect the first block in a free-list.
- If it satisfies the request, it is used; otherwise, a new
+ If it satisfies the request, it is used, otherwise a new
carrier is created. The implementation has a time
complexity that is constant.</p>
- <p>As of erts version 5.6.1 the emulator will refuse to
- use this strategy on other allocators than <c>temp_alloc</c>.
- This since it will only cause problems for other allocators.</p>
+ <p>As from ERTS 5.6.1 the emulator refuses to
+ use this strategy on other allocators than <c>temp_alloc</c>.
+ This because it only causes problems for other allocators.</p>
</item>
</taglist>
- <p>Apart from the ordinary allocators described above a number of
- pre-allocators are used for some specific data types. These
- pre-allocators pre-allocate a fixed amount of memory for certain data
- types when the run-time system starts. As long as pre-allocated memory
- is available, it will be used. When no pre-allocated memory is
- available, memory will be allocated in ordinary allocators. These
- pre-allocators are typically much faster than the ordinary allocators,
- but can only satisfy a limited amount of requests.</p>
+
+ <p>Apart from the ordinary allocators described above, some
+ pre-allocators are used for some specific data types. These
+ pre-allocators pre-allocate a fixed amount of memory for certain data
+ types when the runtime system starts. As long as pre-allocated memory
+ is available, it is used. When no pre-allocated memory is
+ available, memory is allocated in ordinary allocators. These
+ pre-allocators are typically much faster than the ordinary allocators,
+ but can only satisfy a limited number of requests.</p>
</section>
<section>
<marker id="flags"></marker>
<title>System Flags Effecting erts_alloc</title>
<warning>
- <p>Only use these flags if you are absolutely sure what you are
- doing. Unsuitable settings may cause serious performance
+ <p>Only use these flags if you are sure what you are
+ doing. Unsuitable settings can cause serious performance
degradation and even a system crash at any time during
operation.</p>
</warning>
+
<p>Memory allocator system flags have the following syntax:
- <c><![CDATA[+M<S><P> <V>]]></c>
+ <c><![CDATA[+M<S><P> <V>]]></c>,
where <c><![CDATA[<S>]]></c> is a letter identifying a subsystem,
<c><![CDATA[<P>]]></c> is a parameter, and <c><![CDATA[<V>]]></c> is the
value to use. The flags can be passed to the Erlang emulator
- (<seealso marker="erl">erl</seealso>) as command line
+ (<seealso marker="erl"><c>erl(1)</c></seealso>) as command-line
arguments.</p>
- <p>System flags effecting specific allocators have an upper-case
+
+ <p>System flags effecting specific allocators have an uppercase
letter as <c><![CDATA[<S>]]></c>. The following letters are used for
- the currently present allocators:</p>
+ the allocators:</p>
+
<list type="bulleted">
<item><c>B: binary_alloc</c></item>
<item><c>D: std_alloc</c></item>
<item><c>E: ets_alloc</c></item>
<item><c>F: fix_alloc</c></item>
<item><c>H: eheap_alloc</c></item>
+ <item><c>I: literal_alloc</c></item>
<item><c>L: ll_alloc</c></item>
<item><c>M: mseg_alloc</c></item>
<item><c>R: driver_alloc</c></item>
<item><c>S: sl_alloc</c></item>
<item><c>T: temp_alloc</c></item>
+ <item><c>X: exec_alloc</c></item>
<item><c>Y: sys_alloc</c></item>
</list>
- <p>The following flags are available for configuration of
- <c>mseg_alloc</c>:</p>
- <taglist>
- <tag><marker id="MMamcbf"/><c><![CDATA[+MMamcbf <size>]]></c></tag>
- <item>
- Absolute max cache bad fit (in kilobytes). A segment in the
- memory segment cache is not reused if its size exceeds the
- requested size with more than the value of this
- parameter. Default value is 4096. </item>
- <tag><marker id="MMrmcbf"/><c><![CDATA[+MMrmcbf <ratio>]]></c></tag>
- <item>
- Relative max cache bad fit (in percent). A segment in the
- memory segment cache is not reused if its size exceeds the
- requested size with more than relative max cache bad fit
- percent of the requested size. Default value is 20.</item>
- <tag><marker id="MMsco"/><c><![CDATA[+MMsco true|false]]></c></tag>
- <item>
- Set <seealso marker="#MMscs">super carrier</seealso> only flag. This
- flag defaults to <c>true</c>. When a super carrier is used and this
- flag is <c>true</c>, <c>mseg_alloc</c> will only create carriers
- in the super carrier. Note that the <c>alloc_util</c> framework may
- create <c>sys_alloc</c> carriers, so if you want all carriers to
- be created in the super carrier, you therefore want to disable use
- of <c>sys_alloc</c> carriers by also passing
- <seealso marker="#Musac"><c>+Musac false</c></seealso>. When the flag
- is <c>false</c>, <c>mseg_alloc</c> will try to create carriers outside
- of the super carrier when the super carrier is full.
- <br/><br/>
- <em>NOTE</em>: Setting this flag to <c>false</c> may not be supported
- on all systems. This flag will in that case be ignored.
- <br/><br/>
- <em>NOTE</em>: The super carrier cannot be enabled nor
- disabled on halfword heap systems. This flag will be
- ignored on halfword heap systems.
- </item>
- <tag><marker id="MMscrfsd"/><c><![CDATA[+MMscrfsd <amount>]]></c></tag>
- <item>
- Set <seealso marker="#MMscs">super carrier</seealso> reserved
- free segment descriptors. This parameter defaults to <c>65536</c>.
- This parameter determines the amount of memory to reserve for
- free segment descriptors used by the super carrier. If the system
- runs out of reserved memory for free segment descriptors, other
- memory will be used. This may however cause fragmentation issues,
- so you want to ensure that this never happens. The maximum amount
- of free segment descriptors used can be retrieved from the
- <c>erts_mmap</c> tuple part of the result from calling
- <seealso marker="erts:erlang#system_info_allocator_tuple">erlang:system_info({allocator, mseg_alloc})</seealso>.
- </item>
- <tag><marker id="MMscrpm"/><c><![CDATA[+MMscrpm true|false]]></c></tag>
- <item>
- Set <seealso marker="#MMscs">super carrier</seealso> reserve physical
- memory flag. This flag defaults to <c>true</c>. When this flag is
- <c>true</c>, physical memory will be reserved for the whole super
- carrier at once when it is created. The reservation will after that
- be left unchanged. When this flag is set to <c>false</c> only virtual
- address space will be reserved for the super carrier upon creation.
- The system will attempt to reserve physical memory upon carrier
- creations in the super carrier, and attempt to unreserve physical
- memory upon carrier destructions in the super carrier.
- <br/><br/>
- <em>NOTE</em>: What reservation of physical memory actually means
- highly depends on the operating system, and how it is configured. For
- example, different memory overcommit settings on Linux drastically
- change the behaviour. Also note, setting this flag to <c>false</c>
- may not be supported on all systems. This flag will in that case
- be ignored.
- <br/><br/>
- <em>NOTE</em>: The super carrier cannot be enabled nor
- disabled on halfword heap systems. This flag will be
- ignored on halfword heap systems.
- </item>
- <tag><marker id="MMscs"/><c><![CDATA[+MMscs <size in MB>]]></c></tag>
- <item>
- Set super carrier size (in MB). The super carrier size defaults to
- zero; i.e, the super carrier is by default disabled. The super
- carrier is a large continuous area in the virtual address space.
- <c>mseg_alloc</c> will always try to create new carriers in the super
- carrier if it exists. Note that the <c>alloc_util</c> framework may
- create <c>sys_alloc</c> carriers. For more information on this, see the
- documentation of the <seealso marker="#MMsco"><c>+MMsco</c></seealso>
- flag.
- <br/><br/>
- <em>NOTE</em>: The super carrier cannot be enabled nor
- disabled on halfword heap systems. This flag will be
- ignored on halfword heap systems.
- </item>
- <tag><marker id="MMmcs"/><c><![CDATA[+MMmcs <amount>]]></c></tag>
- <item>
- Max cached segments. The maximum number of memory segments
- stored in the memory segment cache. Valid range is
- 0-30. Default value is 10.</item>
- </taglist>
- <p>The following flags are available for configuration of
- <c>sys_alloc</c>:</p>
- <taglist>
- <tag><marker id="MYe"/><c>+MYe true</c></tag>
- <item>
- Enable <c>sys_alloc</c>. Note: <c>sys_alloc</c> cannot be disabled.</item>
- <tag><marker id="MYm"/><c>+MYm libc</c></tag>
- <item>
- <c>malloc</c> library to use. Currently only
- <c>libc</c> is available. <c>libc</c> enables the standard
- <c>libc</c> malloc implementation. By default <c>libc</c> is used.</item>
- <tag><marker id="MYtt"/><c><![CDATA[+MYtt <size>]]></c></tag>
- <item>
- Trim threshold size (in kilobytes). This is the maximum amount
- of free memory at the top of the heap (allocated by
- <c>sbrk</c>) that will be kept by <c>malloc</c> (not
- released to the operating system). When the amount of free
- memory at the top of the heap exceeds the trim threshold,
- <c>malloc</c> will release it (by calling
- <c>sbrk</c>). Trim threshold is given in kilobytes. Default
- trim threshold is 128. <em>Note:</em> This flag will
- only have any effect when the emulator has been linked with
- the GNU C library, and uses its <c>malloc</c> implementation.</item>
- <tag><marker id="MYtp"/><c><![CDATA[+MYtp <size>]]></c></tag>
- <item>
- Top pad size (in kilobytes). This is the amount of extra
- memory that will be allocated by <c>malloc</c> when
- <c>sbrk</c> is called to get more memory from the operating
- system. Default top pad size is 0. <em>Note:</em> This flag
- will only have any effect when the emulator has been linked
- with the GNU C library, and uses its <c>malloc</c>
- implementation.</item>
- </taglist>
- <p>The following flags are available for configuration of allocators
- based on <c>alloc_util</c>. If <c>u</c> is used as subsystem
- identifier (i.e., <c><![CDATA[<S> = u]]></c>) all allocators based on
- <c>alloc_util</c> will be effected. If <c>B</c>, <c>D</c>, <c>E</c>,
- <c>F</c>, <c>H</c>, <c>L</c>, <c>R</c>, <c>S</c>, or <c>T</c> is used as
- subsystem identifier, only the specific allocator identified will be
- effected:</p>
- <taglist>
- <tag><marker id="M_acul"/><c><![CDATA[+M<S>acul <utilization>|de]]></c></tag>
- <item>
- Abandon carrier utilization limit. A valid
- <c><![CDATA[<utilization>]]></c> is an integer in the range
- <c>[0, 100]</c> representing utilization in percent. When a
- utilization value larger than zero is used, allocator instances
- are allowed to abandon multiblock carriers. If <c>de</c> (default
- enabled) is passed instead of a <c><![CDATA[<utilization>]]></c>,
- a recomended non zero utilization value will be used. The actual
- value chosen depend on allocator type and may be changed between
- ERTS versions. Currently the default equals <c>de</c>, but this
- may be changed in the future. Carriers will be abandoned when
- memory utilization in the allocator instance falls below the
- utilization value used. Once a carrier has been abandoned, no new
- allocations will be made in it. When an allocator instance gets an
- increased multiblock carrier need, it will first try to fetch an
- abandoned carrier from an allocator instances of the same
- allocator type. If no abandoned carrier could be fetched, it will
- create a new empty carrier. When an abandoned carrier has been
- fetched it will function as an ordinary carrier. This feature has
- special requirements on the
- <seealso marker="#M_as">allocation strategy</seealso> used. Currently
- only the strategies <c>aoff</c>, <c>aoffcbf</c> and <c>aoffcaobf</c> support
- abandoned carriers. This feature also requires
- <seealso marker="#M_t">multiple thread specific instances</seealso>
- to be enabled. When enabling this feature, multiple thread specific
- instances will be enabled if not already enabled, and the
- <c>aoffcbf</c> strategy will be enabled if current strategy does not
- support abandoned carriers. This feature can be enabled on all
- allocators based on the <c>alloc_util</c> framework with the
- exception of <c>temp_alloc</c> (which would be pointless).
- </item>
- <tag><marker id="M_as"/><c><![CDATA[+M<S>as bf|aobf|aoff|aoffcbf|aoffcaobf|gf|af]]></c></tag>
- <item>
- Allocation strategy. Valid strategies are <c>bf</c> (best fit),
- <c>aobf</c> (address order best fit), <c>aoff</c> (address order first fit),
- <c>aoffcbf</c> (address order first fit carrier best fit),
- <c>aoffcaobf</c> (address order first fit carrier address order best fit),
- <c>gf</c> (good fit), and <c>af</c> (a fit). See
- <seealso marker="#strategy">the description of allocation strategies</seealso> in "the <c>alloc_util</c> framework" section.</item>
- <tag><marker id="M_asbcst"/><c><![CDATA[+M<S>asbcst <size>]]></c></tag>
- <item>
- Absolute singleblock carrier shrink threshold (in
- kilobytes). When a block located in an
- <c>mseg_alloc</c> singleblock carrier is shrunk, the carrier
- will be left unchanged if the amount of unused memory is less
- than this threshold; otherwise, the carrier will be shrunk.
- See also <seealso marker="#M_rsbcst">rsbcst</seealso>.</item>
- <tag><marker id="M_e"/><c><![CDATA[+M<S>e true|false]]></c></tag>
- <item>
- Enable allocator <c><![CDATA[<S>]]></c>.</item>
- <tag><marker id="M_lmbcs"/><c><![CDATA[+M<S>lmbcs <size>]]></c></tag>
- <item>
- Largest (<c>mseg_alloc</c>) multiblock carrier size (in
- kilobytes). See <seealso marker="#mseg_mbc_sizes">the description
- on how sizes for mseg_alloc multiblock carriers are decided</seealso>
- in "the <c>alloc_util</c> framework" section. On 32-bit Unix style OS
- this limit can not be set higher than 128 megabyte.</item>
- <tag><marker id="M_mbcgs"/><c><![CDATA[+M<S>mbcgs <ratio>]]></c></tag>
- <item>
- (<c>mseg_alloc</c>) multiblock carrier growth stages. See
- <seealso marker="#mseg_mbc_sizes">the description on how sizes for
- mseg_alloc multiblock carriers are decided</seealso>
- in "the <c>alloc_util</c> framework" section.</item>
- <tag><marker id="M_mbsd"/><c><![CDATA[+M<S>mbsd <depth>]]></c></tag>
- <item>
- Max block search depth. This flag has effect only if the
- good fit strategy has been selected for allocator
- <c><![CDATA[<S>]]></c>. When the good fit strategy is used, free
- blocks are placed in segregated free-lists. Each free list
- contains blocks of sizes in a specific range. The max block
- search depth sets a limit on the maximum number of blocks to
- inspect in a free list during a search for suitable block
- satisfying the request.</item>
- <tag><marker id="M_mmbcs"/><c><![CDATA[+M<S>mmbcs <size>]]></c></tag>
- <item>
- Main multiblock carrier size. Sets the size of the main
- multiblock carrier for allocator <c><![CDATA[<S>]]></c>. The main
- multiblock carrier is allocated via <c><![CDATA[sys_alloc]]></c> and is
- never deallocated.</item>
- <tag><marker id="M_mmmbc"/><c><![CDATA[+M<S>mmmbc <amount>]]></c></tag>
- <item>
- Max <c>mseg_alloc</c> multiblock carriers. Maximum number of
- multiblock carriers allocated via <c>mseg_alloc</c> by
- allocator <c><![CDATA[<S>]]></c>. When this limit has been reached,
- new multiblock carriers will be allocated via
- <c>sys_alloc</c>.</item>
- <tag><marker id="M_mmsbc"/><c><![CDATA[+M<S>mmsbc <amount>]]></c></tag>
- <item>
- Max <c>mseg_alloc</c> singleblock carriers. Maximum number of
- singleblock carriers allocated via <c>mseg_alloc</c> by
- allocator <c><![CDATA[<S>]]></c>. When this limit has been reached,
- new singleblock carriers will be allocated via
- <c>sys_alloc</c>.</item>
- <tag><marker id="M_ramv"/><c><![CDATA[+M<S>ramv <bool>]]></c></tag>
- <item>
- Realloc always moves. When enabled, reallocate operations will
- more or less be translated into an allocate, copy, free sequence.
- This often reduce memory fragmentation, but costs performance.
- </item>
- <tag><marker id="M_rmbcmt"/><c><![CDATA[+M<S>rmbcmt <ratio>]]></c></tag>
- <item>
- Relative multiblock carrier move threshold (in percent). When
- a block located in a multiblock carrier is shrunk,
- the block will be moved if the ratio of the size of the returned
- memory compared to the previous size is more than this threshold;
- otherwise, the block will be shrunk at current location.</item>
- <tag><marker id="M_rsbcmt"/><c><![CDATA[+M<S>rsbcmt <ratio>]]></c></tag>
- <item>
- Relative singleblock carrier move threshold (in percent). When
- a block located in a singleblock carrier is shrunk to
- a size smaller than the value of the
- <seealso marker="#M_sbct">sbct</seealso> parameter,
- the block will be left unchanged in the singleblock carrier if
- the ratio of unused memory is less than this threshold;
- otherwise, it will be moved into a multiblock carrier. </item>
- <tag><marker id="M_rsbcst"/><c><![CDATA[+M<S>rsbcst <ratio>]]></c></tag>
- <item>
- Relative singleblock carrier shrink threshold (in
- percent). When a block located in an <c>mseg_alloc</c>
- singleblock carrier is shrunk, the carrier will be left
- unchanged if the ratio of unused memory is less than this
- threshold; otherwise, the carrier will be shrunk.
- See also <seealso marker="#M_asbcst">asbcst</seealso>.</item>
- <tag><marker id="M_sbct"/><c><![CDATA[+M<S>sbct <size>]]></c></tag>
- <item>
- Singleblock carrier threshold. Blocks larger than this
- threshold will be placed in singleblock carriers. Blocks
- smaller than this threshold will be placed in multiblock
- carriers. On 32-bit Unix style OS this threshold can not be set higher
- than 8 megabytes.</item>
- <tag><marker id="M_smbcs"/><c><![CDATA[+M<S>smbcs <size>]]></c></tag>
- <item>
- Smallest (<c>mseg_alloc</c>) multiblock carrier size (in
- kilobytes). See <seealso marker="#mseg_mbc_sizes">the description
- on how sizes for mseg_alloc multiblock carriers are decided</seealso>
- in "the <c>alloc_util</c> framework" section.</item>
- <tag><marker id="M_t"/><c><![CDATA[+M<S>t true|false]]></c></tag>
- <item>
- <p>Multiple, thread specific instances of the allocator.
- This option will only have any effect on the runtime system
- with SMP support. Default behaviour on the runtime system with
- SMP support is <c>NoSchedulers+1</c> instances. Each scheduler will use
- a lock-free instance of its own and other threads will use
- a common instance.</p>
- <p>It was previously (before ERTS version 5.9) possible to configure
- a smaller amount of thread specific instances than schedulers.
- This is, however, not possible any more.</p>
- </item>
- </taglist>
- <p>Currently the following flags are available for configuration of
- <c>alloc_util</c>, i.e. all allocators based on <c>alloc_util</c>
- will be effected:</p>
- <taglist>
- <tag><marker id="Muycs"/><c><![CDATA[+Muycs <size>]]></c></tag>
- <item>
- <c>sys_alloc</c> carrier size. Carriers allocated via
- <c>sys_alloc</c> will be allocated in sizes which are
- multiples of the <c>sys_alloc</c> carrier size. This is not
- true for main multiblock carriers and carriers allocated
- during a memory shortage, though.</item>
- <tag><marker id="Mummc"/><c><![CDATA[+Mummc <amount>]]></c></tag>
- <item>
- Max <c>mseg_alloc</c> carriers. Maximum number of carriers
- placed in separate memory segments. When this limit has been
- reached, new carriers will be placed in memory retrieved from
- <c>sys_alloc</c>.</item>
- <tag><marker id="Musac"/><c><![CDATA[+Musac <bool>]]></c></tag>
- <item>
- Allow <c>sys_alloc</c> carriers. By default <c>true</c>. If
- set to <c>false</c>, <c>sys_alloc</c> carriers will never be
- created by allocators using the <c>alloc_util</c> framework.</item>
- </taglist>
- <p>Instrumentation flags:</p>
- <taglist>
- <tag><marker id="Mim"/><c>+Mim true|false</c></tag>
- <item>
- A map over current allocations is kept by the emulator. The
- allocation map can be retrieved via the <c>instrument</c>
- module. <c>+Mim true</c> implies <c>+Mis true</c>.
- <c>+Mim true</c> is the same as
- <seealso marker="erl#instr">-instr</seealso>.</item>
- <tag><marker id="Mis"/><c>+Mis true|false</c></tag>
- <item>
- Status over allocated memory is kept by the emulator. The
- allocation status can be retrieved via the <c>instrument</c>
- module.</item>
- <tag><marker id="Mit"/><c>+Mit X</c></tag>
- <item>
- Reserved for future use. Do <em>not</em> use this flag.</item>
- </taglist>
- <note>
- <p>When instrumentation of the emulator is enabled, the emulator
- uses more memory and runs slower.</p>
- </note>
- <p>Other flags:</p>
- <taglist>
- <tag><marker id="Mea"/><c>+Mea min|max|r9c|r10b|r11b|config</c></tag>
- <item>
+
+ <section>
+ <title>Flags for Configuration of mseg_alloc</title>
<taglist>
- <tag><c>min</c></tag>
+ <tag><marker id="MMamcbf"/><c><![CDATA[+MMamcbf <size>]]></c></tag>
+ <item>
+ <p>Absolute maximum cache bad fit (in kilobytes). A segment in the
+ memory segment cache is not reused if its size exceeds the
+ requested size with more than the value of this
+ parameter. Defaults to <c>4096</c>.</p>
+ </item>
+ <tag><marker id="MMrmcbf"/><c><![CDATA[+MMrmcbf <ratio>]]></c></tag>
+ <item>
+ <p>Relative maximum cache bad fit (in percent). A segment in the
+ memory segment cache is not reused if its size exceeds the
+ requested size with more than relative maximum cache bad fit
+ percent of the requested size. Defaults to <c>20</c>.</p>
+ </item>
+ <tag><marker id="MMsco"/><c><![CDATA[+MMsco true|false]]></c></tag>
+ <item>
+ <p>Sets <seealso marker="#MMscs">super carrier</seealso> only flag.
+ Defaults to <c>true</c>. When a super carrier is used and this
+ flag is <c>true</c>, <c>mseg_alloc</c> only creates carriers in
+ the super carrier. Notice that the <c>alloc_util</c> framework can
+ create <c>sys_alloc</c> carriers, so if you want all carriers to
+ be created in the super carrier, you therefore want to disable use
+ of <c>sys_alloc</c> carriers by also passing
+ <seealso marker="#Musac"><c>+Musac false</c></seealso>. When
+ the flag is <c>false</c>, <c>mseg_alloc</c> tries to create carriers
+ outside of the super carrier when the super carrier is full.</p>
+ <note>
+ <p>Setting this flag to <c>false</c> is not supported
+ on all systems. The flag is then ignored.</p>
+ </note>
+ </item>
+ <tag><marker id="MMscrfsd"/><c><![CDATA[+MMscrfsd <amount>]]></c></tag>
+ <item>
+ <p>Sets <seealso marker="#MMscs">super carrier</seealso> reserved
+ free segment descriptors. Defaults to <c>65536</c>.
+ This parameter determines the amount of memory to reserve for
+ free segment descriptors used by the super carrier. If the system
+ runs out of reserved memory for free segment descriptors, other
+ memory is used. This can however cause fragmentation issues,
+ so you want to ensure that this never happens. The maximum amount
+ of free segment descriptors used can be retrieved from the
+ <c>erts_mmap</c> tuple part of the result from calling
+ <seealso marker="erts:erlang#system_info_allocator_tuple">
+ <c>erlang:system_info({allocator, mseg_alloc})</c></seealso>.</p>
+ </item>
+ <tag><marker id="MMscrpm"/><c><![CDATA[+MMscrpm true|false]]></c></tag>
<item>
- Disables all allocators that can be disabled.
- </item>
+ <p>Sets <seealso marker="#MMscs">super carrier</seealso> reserve
+ physical memory flag. Defaults to <c>true</c>. When this flag is
+ <c>true</c>, physical memory is reserved for the whole super
+ carrier at once when it is created. The reservation is after that
+ left unchanged. When this flag is set to <c>false</c>, only virtual
+ address space is reserved for the super carrier upon creation.
+ The system attempts to reserve physical memory upon carrier
+ creations in the super carrier, and attempt to unreserve physical
+ memory upon carrier destructions in the super carrier.</p>
+ <note>
+ <p>What reservation of physical memory means, highly
+ depends on the operating system, and how it is configured. For
+ example, different memory overcommit settings on Linux drastically
+ change the behavior.</p>
+ <p>Setting this flag to <c>false</c> is possibly not supported on
+ all systems. The flag is then ignored.</p>
+ </note>
+ </item>
+ <tag><marker id="MMscs"/><c><![CDATA[+MMscs <size in MB>]]></c></tag>
+ <item>
+ <p>Sets super carrier size (in MB). Defaults to <c>0</c>, that is,
+ the super carrier is by default disabled. The super
+ carrier is a large continuous area in the virtual address space.
+ <c>mseg_alloc</c> always tries to create new carriers in the super
+ carrier if it exists. Notice that the <c>alloc_util</c> framework
+ can create <c>sys_alloc</c> carriers. For more information, see
+ <seealso marker="#MMsco"><c>+MMsco</c></seealso>.</p>
+ </item>
+ <tag><marker id="MMmcs"/><c><![CDATA[+MMmcs <amount>]]></c></tag>
+ <item>
+ <p>Maximum cached segments. The maximum number of memory segments
+ stored in the memory segment cache. Valid range is <c>[0, 30]</c>.
+ Defaults to <c>10</c>.</p>
+ </item>
+ </taglist>
+ </section>
+
+ <section>
+ <title>Flags for Configuration of sys_alloc</title>
+ <taglist>
+ <tag><marker id="MYe"/><c>+MYe true</c></tag>
+ <item>
+ <p>Enables <c>sys_alloc</c>.</p>
+ <note>
+ <p><c>sys_alloc</c> cannot be disabled.</p>
+ </note>
+ </item>
+ <tag><marker id="MYm"/><c>+MYm libc</c></tag>
+ <item>
+ <p><c>malloc</c> library to use. Only
+ <c>libc</c> is available. <c>libc</c> enables the standard
+ <c>libc</c> <c>malloc</c> implementation. By default <c>libc</c>
+ is used.</p>
+ </item>
+ <tag><marker id="MYtt"/><c><![CDATA[+MYtt <size>]]></c></tag>
+ <item>
+ <p>Trim threshold size (in kilobytes). This is the maximum amount
+ of free memory at the top of the heap (allocated by
+ <c>sbrk</c>) that is kept by <c>malloc</c> (not
+ released to the operating system). When the amount of free
+ memory at the top of the heap exceeds the trim threshold,
+ <c>malloc</c> releases it (by calling <c>sbrk</c>).
+ Trim threshold is specified in kilobytes.
+ Defaults to <c>128</c>.</p>
+ <note>
+ <p>This flag has effect only when the emulator is linked with
+ the GNU C library, and uses its <c>malloc</c> implementation.</p>
+ </note>
+ </item>
+ <tag><marker id="MYtp"/><c><![CDATA[+MYtp <size>]]></c></tag>
+ <item>
+ <p>Top pad size (in kilobytes). This is the amount of extra
+ memory that is allocated by <c>malloc</c> when
+ <c>sbrk</c> is called to get more memory from the operating
+ system. Defaults to <c>0</c>.</p>
+ <note>
+ <p>This flag has effect only when the emulator is linked with
+ the GNU C library, and uses its <c>malloc</c> implementation.</p>
+ </note>
+ </item>
+ </taglist>
+ </section>
+
+ <section>
+ <title>Flags for Configuration of Allocators Based on alloc_util</title>
+ <p>If <c>u</c> is used as subsystem identifier (that is,
+ <c><![CDATA[<S> = u]]></c>), all allocators based on
+ <c>alloc_util</c> are effected. If <c>B</c>, <c>D</c>, <c>E</c>,
+ <c>F</c>, <c>H</c>, <c>L</c>, <c>R</c>, <c>S</c>, or <c>T</c> is used
+ as subsystem identifier, only the specific allocator identifier is
+ effected.</p>
+
+ <taglist>
+ <tag><marker id="M_acul"/><c><![CDATA[+M<S>acul <utilization>|de]]></c>
+ </tag>
+ <item>
+ <p>Abandon carrier utilization limit. A valid
+ <c><![CDATA[<utilization>]]></c> is an integer in the range
+ <c>[0, 100]</c> representing utilization in percent. When a
+ utilization value &gt; 0 is used, allocator instances
+ are allowed to abandon multiblock carriers. If <c>de</c> (default
+ enabled) is passed instead of a <c><![CDATA[<utilization>]]></c>,
+ a recomended non-zero utilization value is used. The value
+ chosen depends on the allocator type and can be changed between
+ ERTS versions. Defaults to <c>de</c>, but this
+ can be changed in the future.</p>
+ <p>Carriers are abandoned when
+ memory utilization in the allocator instance falls below the
+ utilization value used. Once a carrier is abandoned, no new
+ allocations are made in it. When an allocator instance gets an
+ increased multiblock carrier need, it first tries to fetch an
+ abandoned carrier from an allocator instance of the same
+ allocator type. If no abandoned carrier can be fetched, it
+ creates a new empty carrier. When an abandoned carrier has been
+ fetched, it will function as an ordinary carrier. This feature has
+ special requirements on the
+ <seealso marker="#M_as">allocation strategy</seealso> used. Only
+ the strategies <c>aoff</c>, <c>aoffcbf</c>, and <c>aoffcaobf</c>
+ support abandoned carriers.</p>
+ <p>This feature also requires
+ <seealso marker="#M_t">multiple thread specific instances</seealso>
+ to be enabled. When enabling this feature, multiple thread-specific
+ instances are enabled if not already enabled, and the
+ <c>aoffcbf</c> strategy is enabled if the current strategy does not
+ support abandoned carriers. This feature can be enabled on all
+ allocators based on the <c>alloc_util</c> framework, except
+ <c>temp_alloc</c> (which would be pointless).</p>
+ </item>
+ <tag><marker id="M_as"/>
+ <c><![CDATA[+M<S>as bf|aobf|aoff|aoffcbf|aoffcaobf|gf|af]]></c></tag>
+ <item>
+ <p>Allocation strategy. The following strategies are valid:</p>
+ <list type="bulleted">
+ <item><c>bf</c> (best fit)</item>
+ <item><c>aobf</c> (address order best fit)</item>
+ <item><c>aoff</c> (address order first fit)</item>
+ <item><c>aoffcbf</c> (address order first fit carrier best fit)
+ </item>
+ <item><c>aoffcaobf</c> (address order first fit carrier address
+ order best fit)</item>
+ <item><c>gf</c> (good fit)</item>
+ <item><c>af</c> (a fit)</item>
+ </list>
+ <p>See the description of allocation strategies in section
+ <seealso marker="#strategy">The alloc_util Framework</seealso>.</p>
+ </item>
+ <tag><marker id="M_asbcst"/><c><![CDATA[+M<S>asbcst <size>]]></c></tag>
+ <item>
+ <p>Absolute singleblock carrier shrink threshold (in
+ kilobytes). When a block located in an
+ <c>mseg_alloc</c> singleblock carrier is shrunk, the carrier
+ is left unchanged if the amount of unused memory is less
+ than this threshold, otherwise the carrier is shrunk.
+ See also <seealso marker="#M_rsbcst"><c>rsbcst</c></seealso>.</p>
+ </item>
+ <tag><marker id="M_e"/><c><![CDATA[+M<S>e true|false]]></c></tag>
+ <item>
+ <p>Enables allocator <c><![CDATA[<S>]]></c>.</p>
+ </item>
+ <tag><marker id="M_lmbcs"/><c><![CDATA[+M<S>lmbcs <size>]]></c></tag>
+ <item>
+ <p>Largest (<c>mseg_alloc</c>) multiblock carrier size (in kilobytes).
+ See the description on how sizes for <c>mseg_alloc</c> multiblock
+ carriers are decided in section
+ <seealso marker="#mseg_mbc_sizes">
+ The alloc_util Framework</seealso>. On
+ 32-bit Unix style OS this limit cannot be set &gt; 128 MB.</p>
+ </item>
+ <tag><marker id="M_mbcgs"/><c><![CDATA[+M<S>mbcgs <ratio>]]></c></tag>
+ <item>
+ <p>(<c>mseg_alloc</c>) multiblock carrier growth stages.
+ See the description on how sizes for <c>mseg_alloc</c> multiblock
+ carriers are decided in section
+ <seealso marker="#mseg_mbc_sizes">
+ The alloc_util Framework</seealso>.</p>
+ </item>
+ <tag><marker id="M_mbsd"/><c><![CDATA[+M<S>mbsd <depth>]]></c></tag>
+ <item>
+ <p>Maximum block search depth. This flag has effect only if the
+ good fit strategy is selected for allocator
+ <c><![CDATA[<S>]]></c>. When the good fit strategy is used, free
+ blocks are placed in segregated free-lists. Each free-list
+ contains blocks of sizes in a specific range. The maxiumum block
+ search depth sets a limit on the maximum number of blocks to
+ inspect in a free-list during a search for suitable block
+ satisfying the request.</p>
+ </item>
+ <tag><marker id="M_mmbcs"/><c><![CDATA[+M<S>mmbcs <size>]]></c></tag>
+ <item>
+ <p>Main multiblock carrier size. Sets the size of the main
+ multiblock carrier for allocator <c><![CDATA[<S>]]></c>. The main
+ multiblock carrier is allocated through <c><![CDATA[sys_alloc]]></c>
+ and is never deallocated.</p>
+ </item>
+ <tag><marker id="M_mmmbc"/><c><![CDATA[+M<S>mmmbc <amount>]]></c></tag>
+ <item>
+ <p>Maximum <c>mseg_alloc</c> multiblock carriers. Maximum number of
+ multiblock carriers allocated through <c>mseg_alloc</c> by
+ allocator <c><![CDATA[<S>]]></c>. When this limit is reached,
+ new multiblock carriers are allocated through
+ <c>sys_alloc</c>.</p>
+ </item>
+ <tag><marker id="M_mmsbc"/><c><![CDATA[+M<S>mmsbc <amount>]]></c></tag>
+ <item>
+ <p>Maximum <c>mseg_alloc</c> singleblock carriers. Maximum number of
+ singleblock carriers allocated through <c>mseg_alloc</c> by
+ allocator <c><![CDATA[<S>]]></c>. When this limit is reached,
+ new singleblock carriers are allocated through
+ <c>sys_alloc</c>.</p>
+ </item>
+ <tag><marker id="M_ramv"/><c><![CDATA[+M<S>ramv <bool>]]></c></tag>
+ <item>
+ <p>Realloc always moves. When enabled, reallocate operations are
+ more or less translated into an allocate, copy, free sequence.
+ This often reduces memory fragmentation, but costs performance.</p>
+ </item>
+ <tag><marker id="M_rmbcmt"/><c><![CDATA[+M<S>rmbcmt <ratio>]]></c></tag>
+ <item>
+ <p>Relative multiblock carrier move threshold (in percent). When
+ a block located in a multiblock carrier is shrunk,
+ the block is moved if the ratio of the size of the returned
+ memory compared to the previous size is more than this threshold,
+ otherwise the block is shrunk at the current location.</p>
+ </item>
+ <tag><marker id="M_rsbcmt"/><c><![CDATA[+M<S>rsbcmt <ratio>]]></c></tag>
+ <item>
+ <p>Relative singleblock carrier move threshold (in percent). When
+ a block located in a singleblock carrier is shrunk to
+ a size smaller than the value of parameter
+ <seealso marker="#M_sbct"><c>sbct</c></seealso>,
+ the block is left unchanged in the singleblock carrier if
+ the ratio of unused memory is less than this threshold,
+ otherwise it is moved into a multiblock carrier.</p>
+ </item>
+ <tag><marker id="M_rsbcst"/><c><![CDATA[+M<S>rsbcst <ratio>]]></c></tag>
+ <item>
+ <p>Relative singleblock carrier shrink threshold (in
+ percent). When a block located in an <c>mseg_alloc</c>
+ singleblock carrier is shrunk, the carrier is left
+ unchanged if the ratio of unused memory is less than this
+ threshold, otherwise the carrier is shrunk.
+ See also <seealso marker="#M_asbcst"><c>asbcst</c></seealso>.</p>
+ </item>
+ <tag><marker id="M_sbct"/><c><![CDATA[+M<S>sbct <size>]]></c></tag>
+ <item>
+ <p>Singleblock carrier threshold. Blocks larger than this
+ threshold are placed in singleblock carriers. Blocks
+ smaller than this threshold are placed in multiblock
+ carriers. On 32-bit Unix style OS this threshold cannot be set
+ &gt; 8 MB.</p>
+ </item>
+ <tag><marker id="M_smbcs"/><c><![CDATA[+M<S>smbcs <size>]]></c></tag>
+ <item>
+ <p>Smallest (<c>mseg_alloc</c>) multiblock carrier size (in
+ kilobytes). See the description on how sizes for <c>mseg_alloc</c>
+ multiblock carriers are decided in section
+ <seealso marker="#mseg_mbc_sizes">
+ The alloc_util Framework</seealso>.</p>
+ </item>
+ <tag><marker id="M_t"/><c><![CDATA[+M<S>t true|false]]></c></tag>
+ <item>
+ <p>Multiple, thread-specific instances of the allocator.
+ This option has only effect on the runtime system
+ with SMP support. Default behavior on the runtime system with
+ SMP support is <c>NoSchedulers+1</c> instances. Each scheduler
+ uses a lock-free instance of its own and other threads use
+ a common instance.</p>
+ <p>Before ERTS 5.9 it was possible to configure
+ a smaller number of thread-specific instances than schedulers.
+ This is, however, not possible anymore.</p>
+ </item>
+ </taglist>
+ </section>
- <tag><c>max</c></tag>
+ <section>
+ <title>Flags for Configuration of alloc_util</title>
+ <p>All allocators based on <c>alloc_util</c> are effected.</p>
+
+ <taglist>
+ <tag><marker id="Muycs"/><c><![CDATA[+Muycs <size>]]></c></tag>
<item>
- Enables all allocators (currently default).
- </item>
+ <p><c>sys_alloc</c> carrier size. Carriers allocated through
+ <c>sys_alloc</c> are allocated in sizes that are
+ multiples of the <c>sys_alloc</c> carrier size. This is not
+ true for main multiblock carriers and carriers allocated
+ during a memory shortage, though.</p>
+ </item>
+ <tag><marker id="Mummc"/><c><![CDATA[+Mummc <amount>]]></c></tag>
+ <item>
+ <p>Maximum <c>mseg_alloc</c> carriers. Maximum number of carriers
+ placed in separate memory segments. When this limit is
+ reached, new carriers are placed in memory retrieved from
+ <c>sys_alloc</c>.</p>
+ </item>
+ <tag><marker id="Musac"/><c><![CDATA[+Musac <bool>]]></c></tag>
+ <item>
+ <p>Allow <c>sys_alloc</c> carriers. Defaults to <c>true</c>.
+ If set to <c>false</c>, <c>sys_alloc</c> carriers are never
+ created by allocators using the <c>alloc_util</c> framework.</p>
+ </item>
+ </taglist>
+ </section>
- <tag><c>r9c|r10b|r11b</c></tag>
+ <section>
+ <title>Special Flag for literal_alloc</title>
+ <taglist>
+ <tag><marker id="MIscs"/><c><![CDATA[+MIscs <size in MB>]]></c></tag>
<item>
- Configures all allocators as they were configured in respective
- OTP release. These will eventually be removed.
- </item>
+ <p><c>literal_alloc</c> super carrier size (in MB). The amount of
+ <em>virtual</em> address space reserved for literal terms in
+ Erlang code on 64-bit architectures. Defaults to <c>1024</c>
+ (that is, 1 GB), which is usually sufficient.
+ The flag is ignored on 32-bit architectures.</p>
+ </item>
+ </taglist>
+ </section>
- <tag><c>config</c></tag>
+ <section>
+ <title>Special Flag for exec_alloc</title>
+ <taglist>
+ <tag><marker id="MXscs"/><c><![CDATA[+MXscs <size in MB>]]></c></tag>
<item>
- Disables features that cannot be enabled while creating an
- allocator configuration with
- <seealso marker="runtime_tools:erts_alloc_config">erts_alloc_config(3)</seealso>.
- Note, this option should only be used while running
- <c>erts_alloc_config</c>, <em>not</em> when using the created
- configuration.
+ <p><c>exec_alloc</c> super carrier size (in MB). The amount of
+ <em>virtual</em> address space reserved for native executable code
+ used by the <seealso marker="hipe:HiPE_app"><c>HiPE</c></seealso> application
+ on specific architectures (x86_64). Defaults to <c>512</c>.</p>
</item>
</taglist>
- </item>
- <tag><marker id="Mlpm"/><c>+Mlpm all|no</c></tag>
- <item>Lock physical memory. The default value is <c>no</c>, i.e.,
- no physical memory will be locked. If set to <c>all</c>, all
- memory mappings made by the runtime system, will be locked into
- physical memory. If set to <c>all</c>, the runtime system will fail
- to start if this feature is not supported, the user has not got enough
- privileges, or the user is not allowed to lock enough physical memory.
- The runtime system will also fail with an out of memory condition
- if the user limit on the amount of locked memory is reached.
- </item>
- </taglist>
- <p>Only some default values have been presented
- here.
- <seealso marker="erts:erlang#system_info_allocator">erlang:system_info(allocator)</seealso>,
- and
- <seealso marker="erts:erlang#system_info_allocator_tuple">erlang:system_info({allocator, Alloc})</seealso>
- can be used in order to obtain currently used settings and current
- status of the allocators.</p>
+ </section>
+
+ <section>
+ <title>Instrumentation Flags</title>
+ <taglist>
+ <tag><marker id="Mim"/><c>+Mim true|false</c></tag>
+ <item>
+ <p>A map over current allocations is kept by the emulator.
+ The allocation map can be retrieved through module
+ <seealso marker="tools:instrument">
+ <c>instrument(3)</c></seealso>. <c>+Mim true</c>
+ implies <c>+Mis true</c>. <c>+Mim true</c> is the same as flag
+ <seealso marker="erl#instr"><c>-instr</c></seealso> in
+ <c>erl(1)</c>.</p>
+ </item>
+ <tag><marker id="Mis"/><c>+Mis true|false</c></tag>
+ <item>
+ <p>Status over allocated memory is kept by the emulator.
+ The allocation status can be retrieved through module
+ <seealso marker="tools:instrument">
+ <c>instrument(3)</c></seealso>.</p>
+ </item>
+ <tag><marker id="Mit"/><c>+Mit X</c></tag>
+ <item>
+ <p>Reserved for future use. Do <em>not</em> use this flag.</p>
+ </item>
+ </taglist>
+
+ <note>
+ <p>When instrumentation of the emulator is enabled, the emulator
+ uses more memory and runs slower.</p>
+ </note>
+ </section>
+
+ <section>
+ <title>Other Flags</title>
+ <taglist>
+ <tag><marker id="Mea"/><c>+Mea min|max|r9c|r10b|r11b|config</c></tag>
+ <item>
+ <p>Options:</p>
+ <taglist>
+ <tag><c>min</c></tag>
+ <item>
+ <p>Disables all allocators that can be disabled.</p>
+ </item>
+ <tag><c>max</c></tag>
+ <item>
+ <p>Enables all allocators (default).</p>
+ </item>
+ <tag><c>r9c|r10b|r11b</c></tag>
+ <item>
+ <p>Configures all allocators as they were configured in respective
+ Erlang/OTP release. These will eventually be removed.</p>
+ </item>
+ <tag><c>config</c></tag>
+ <item>
+ <p>Disables features that cannot be enabled while creating an
+ allocator configuration with
+ <seealso marker="runtime_tools:erts_alloc_config">
+ <c>erts_alloc_config(3)</c></seealso>.</p>
+ <note>
+ <p>This option is to be used only while running
+ <c>erts_alloc_config(3)</c>, <em>not</em> when
+ using the created configuration.</p>
+ </note>
+ </item>
+ </taglist>
+ </item>
+ <tag><marker id="Mlpm"/><c>+Mlpm all|no</c></tag>
+ <item>
+ <p>Lock physical memory. Defaults to <c>no</c>, that is,
+ no physical memory is locked. If set to <c>all</c>, all
+ memory mappings made by the runtime system are locked into
+ physical memory. If set to <c>all</c>, the runtime system fails to
+ start if this feature is not supported, the user has not got enough
+ privileges, or the user is not allowed to lock enough physical
+ memory. The runtime system also fails with an out of memory
+ condition if the user limit on the amount of locked memory is
+ reached.</p>
+ </item>
+ </taglist>
+ </section>
+ </section>
+
+ <section>
+ <title>Notes</title>
+ <p>Only some default values have been presented here. For information
+ about the currently used settings and the current status of the
+ allocators, see
+ <seealso marker="erts:erlang#system_info_allocator">
+ <c>erlang:system_info(allocator)</c></seealso> and
+ <seealso marker="erts:erlang#system_info_allocator_tuple">
+ <c>erlang:system_info({allocator, Alloc})</c></seealso>.</p>
+
<note>
- <p>Most of these flags are highly implementation dependent, and they
- may be changed or removed without prior notice.</p>
+ <p>Most of these flags are highly implementation-dependent and
+ can be changed or removed without prior notice.</p>
<p><c>erts_alloc</c> is not obliged to strictly use the settings that
- have been passed to it (it may even ignore them).</p>
+ have been passed to it (it can even ignore them).</p>
</note>
- <p><seealso marker="runtime_tools:erts_alloc_config">erts_alloc_config(3)</seealso>
- is a tool that can be used to aid creation of an
+
+ <p>The <seealso marker="runtime_tools:erts_alloc_config">
+ <c>erts_alloc_config(3)</c></seealso>
+ tool can be used to aid creation of an
<c>erts_alloc</c> configuration that is suitable for a limited
number of runtime scenarios.</p>
</section>
<section>
- <title>SEE ALSO</title>
- <p><seealso marker="runtime_tools:erts_alloc_config">erts_alloc_config(3)</seealso>,
- <seealso marker="erl">erl(1)</seealso>,
- <seealso marker="tools:instrument">instrument(3)</seealso>,
- <seealso marker="erts:erlang">erlang(3)</seealso></p>
+ <title>See Also</title>
+ <p><seealso marker="erl"><c>erl(1)</c></seealso>,
+ <seealso marker="erlang"><c>erlang(3)</c></seealso>,
+ <seealso marker="runtime_tools:erts_alloc_config">
+ <c>erts_alloc_config(3)</c></seealso>,
+ <seealso marker="tools:instrument">
+ <c>instrument(3)</c></seealso></p>
</section>
</cref>
diff --git a/erts/doc/src/escript.xml b/erts/doc/src/escript.xml
index f12f76890c..1d5d280338 100644
--- a/erts/doc/src/escript.xml
+++ b/erts/doc/src/escript.xml
@@ -4,7 +4,7 @@
<comref>
<header>
<copyright>
- <year>2007</year><year>2015</year>
+ <year>2007</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -33,17 +33,18 @@
<comsummary>Erlang scripting support</comsummary>
<description>
<p><c>escript</c> provides support for running short Erlang programs
- without having to compile them first and an easy way to retrieve the
- command line arguments.</p>
+ without having to compile them first, and an easy way to retrieve the
+ command-line arguments.</p>
</description>
+
<funcs>
<func>
<name>script-name script-arg1 script-arg2...</name>
<name>escript escript-flags script-name script-arg1 script-arg2...</name>
- <fsummary>Run a script written in Erlang</fsummary>
+ <fsummary>Run a script written in Erlang.</fsummary>
<desc>
<p><c>escript</c> runs a script written in Erlang.</p>
- <p>Here follows an example.</p>
+ <p>Example:</p>
<pre>
$ <input>chmod u+x factorial</input>
$ <input>cat factorial</input>
@@ -73,195 +74,183 @@ factorial 5 = 120
$ <input>./factorial</input>
usage: factorial integer
$ <input>./factorial five</input>
-usage: factorial integer
- </pre>
+usage: factorial integer</pre>
<p>The header of the Erlang script in the example differs from
- a normal Erlang module. The first line is intended to be the
- interpreter line, which invokes <c>escript</c>. However if you
- invoke the <c>escript</c> like this</p>
+ a normal Erlang module. The first line is intended to be the
+ interpreter line, which invokes <c>escript</c>.</p>
+ <p>However, if you invoke the <c>escript</c> as follows,
+ the contents of the first line does not matter, but it
+ cannot contain Erlang code as it will be ignored:</p>
<pre>
-$ <input>escript factorial 5</input> </pre>
- <p>the contents of the first line does not matter, but it
- cannot contain Erlang code as it will be ignored.</p>
- <p>The second line in the example, contains an optional
- directive to the <c>Emacs</c> editor which causes it to
+$ <input>escript factorial 5</input></pre>
+ <p>The second line in the example contains an optional
+ directive to the <c>Emacs</c> editor, which causes it to
enter the major mode for editing Erlang source files. If the
- directive is present it must be located on the second
+ directive is present, it must be located on the second
line.</p>
-
- <p>If there is a comment selecting the <seealso
- marker="stdlib:epp#encoding">encoding</seealso> it can be
+ <p>If a comment selecting the <seealso
+ marker="stdlib:epp#encoding">encoding</seealso> exists, it can be
located on the second line.</p>
-
- <note><p>
- The encoding specified by the above mentioned comment
- applies to the script itself. The encoding of the
- I/O-server, however, has to be set explicitly like this:</p>
-<code>io:setopts([{encoding, unicode}])</code>
+ <note>
+ <p>The encoding specified by the above mentioned comment
+ applies to the script itself. The encoding of the
+ I/O-server, however, must be set explicitly as follows:</p>
+ <code>
+io:setopts([{encoding, unicode}])</code>
<p>The default encoding of the I/O-server for <c>standard_io</c>
- is <c>latin1</c>
- since the script runs in a non-interactive terminal
- (see <seealso marker="stdlib:unicode_usage#unicode_options_summary">
- Using Unicode in Erlang</seealso>).
- </p></note>
-
- <p>On the third line (or second line depending on the presence
- of the Emacs directive), it is possible to give arguments to
- the emulator, such as </p>
+ is <c>latin1</c>, as the script runs in a non-interactive terminal
+ (see section
+ <seealso marker="stdlib:unicode_usage#unicode_options_summary">
+ Summary of Options</seealso>) in the STDLIB User's Guide.</p>
+ </note>
+ <p>On the third line (or second line depending on the presence
+ of the Emacs directive), arguments can be specified to
+ the emulator, for example:</p>
<pre>
%%! -smp enable -sname factorial -mnesia debug verbose</pre>
<p>Such an argument line must start with <c>%%!</c> and the
- rest of the line will interpreted as arguments to the emulator.</p>
+ remaining line is interpreted as arguments to the emulator.</p>
<p>If you know the location of the <c>escript</c> executable, the first
- line can directly give the path to <c>escript</c>. For instance:</p>
+ line can directly give the path to <c>escript</c>, for example:</p>
<pre>
-#!/usr/local/bin/escript </pre>
- <p>As any other kind of scripts, Erlang scripts will not work on
+#!/usr/local/bin/escript</pre>
+ <p>As any other type of scripts, Erlang scripts do not work on
Unix platforms if the execution bit for the script file is not set.
- (Use <c>chmod +x script-name</c> to turn on the execution bit.)
- </p>
-
- <p>The rest of the Erlang script file may either contain
- Erlang <c>source code</c>, an <c>inlined beam file</c> or an
- <c>inlined archive file</c>.</p>
-
- <p>An Erlang script file must always contain the function
- <em>main/1</em>. When the script is run, the
- <c>main/1</c> function will be called with a list
- of strings representing the arguments given to the script (not
- changed or interpreted in any way).</p>
-
+ (To turn on the execution bit, use <c>chmod +x script-name</c>.)</p>
+ <p>The remaining Erlang script file can either contain
+ Erlang <em>source code</em>, an <em>inlined beam file</em>, or an
+ <em>inlined archive file</em>.</p>
+ <p>An Erlang script file must always contain the <c>main/1</c>
+ function. When the script is run, the
+ <c>main/1</c> function is called with a list
+ of strings representing the arguments specified to the script (not
+ changed or interpreted in any way).</p>
<p>If the <c>main/1</c> function in the script returns successfully,
- the exit status for the script will be 0. If an exception is generated
- during execution, a short message will be printed and the script terminated
- with exit status 127.</p>
-
- <p>To return your own non-zero exit code, call <c>halt(ExitCode)</c>;
- for instance:</p>
+ the exit status for the script is <c>0</c>. If an exception is
+ generated during execution, a short message is printed and the script
+ terminates with exit status <c>127</c>.</p>
+ <p>To return your own non-zero exit code, call <c>halt(ExitCode)</c>,
+ for example:</p>
<pre>
halt(1).</pre>
-
- <p>Call <seealso marker="#script_name_0">escript:script_name()</seealso>
- from your to script to retrieve the pathname of the script
- (the pathname is usually, but not always, absolute).</p>
-
+ <p>To retrieve the pathname of the script, call
+ <seealso marker="#script_name_0">
+ <c>escript:script_name()</c></seealso> from your script
+ (the pathname is usually, but not always, absolute).</p>
<p>If the file contains source code (as in the example above),
- it will be processed by the preprocessor <c>epp</c>. This
- means that you for example may use pre-defined macros (such as
- <c>?MODULE</c>) as well as include directives like
- the <c>-include_lib</c> directive. For instance, use</p>
+ it is processed by the
+ <seealso marker="stdlib:epp"><c>epp</c></seealso> preprocessor.
+ This means that you, for example, can use predefined macros
+ (such as <c>?MODULE</c>) and include directives like
+ the <c>-include_lib</c> directive. For example, use</p>
<pre>
-include_lib("kernel/include/file.hrl").</pre>
- <p>to include the record definitions for the records used by the
- <c>file:read_link_info/1</c> function. You can also select
- encoding by including a encoding comment here, but if there
- is a valid encoding comment on the second line it takes
+ <p>to include the record definitions for the records used by function
+ <seealso marker="kernel:file#read_link_info/1">
+ <c>file:read_link_info/1</c></seealso>. You can also select
+ encoding by including an encoding comment here, but if
+ a valid encoding comment exists on the second line, it takes
precedence.</p>
-
- <p>The script will be checked for syntactic and semantic
- correctness before being run. If there are warnings (such as
- unused variables), they will be printed and the script will
- still be run. If there are errors, they will be printed and
- the script will not be run and its exit status will be
- 127.</p>
-
+ <p>The script is checked for syntactic and semantic
+ correctness before it is run. If there are warnings (such as
+ unused variables), they are printed and the script will
+ still be run. If there are errors, they are printed and
+ the script will not be run and its exit status is
+ <c>127</c>.</p>
<p>Both the module declaration and the export declaration of
the <c>main/1</c> function are optional.</p>
-
<p>By default, the script will be interpreted. You can force
it to be compiled by including the following line somewhere
- in the script file:</p><pre>
+ in the script file:</p>
+ <pre>
-mode(compile).</pre>
-
<p>Execution of interpreted code is slower than compiled code.
- If much of the execution takes place in interpreted code it
- may be worthwhile to compile it, even though the compilation
- itself will take a little while. It is also possible to supply
- <c>native</c> instead of <c>compile</c>, this will compile the script
- using the native flag, again depending on the characteristics
- of the escript this could or could not be worth while.</p>
-
- <p>As mentioned earlier, it is possible to have a script which
+ If much of the execution takes place in interpreted code, it
+ can be worthwhile to compile it, although the compilation
+ itself takes a little while. Also, <c>native</c> can be supplied
+ instead of <c>compile</c>. This compiles the script
+ using the native flag and may or may not be worthwhile
+ depending on the escript characteristics.</p>
+ <p>As mentioned earlier, a script can
contains precompiled <c>beam</c> code. In a precompiled
- script, the interpretation of the script header is exactly
- the same as in a script containing source code. That means
+ script, the interpretation of the script header is
+ the same as in a script containing source code. This means
that you can make a <c>beam</c> file executable by
prepending the file with the lines starting with <c>#!</c>
and <c>%%!</c> mentioned above. In a precompiled script, the
- function
- <c>main/1</c> must be exported.</p>
-
- <p>As yet another option it is possible to have an entire
- Erlang archive in the script. In a archive script, the
- interpretation of the script header is exactly the same as
- in a script containing source code. That means that you can
+ <c>main/1</c> function must be exported.</p>
+ <p>Another option is to have an entire
+ Erlang archive in the script. In an archive script, the
+ interpretation of the script header is the same as
+ in a script containing source code. This means that you can
make an archive file executable by prepending the file with
the lines starting with <c>#!</c> and <c>%%!</c> mentioned
- above. In an archive script, the function <c>main/1</c> must
+ above. In an archive script, the <c>main/1</c> function must
be exported. By default the <c>main/1</c> function in the
module with the same name as the basename of the
- <c>escript</c> file will be invoked. This behavior can be
- overridden by setting the flag <c>-escript main Module</c>
- as one of the emulator flags. The <c>Module</c> must be the
- name of a module which has an exported <c>main/1</c>
- function. See <seealso marker="kernel:code">code(3)</seealso>
- for more information about archives and code loading.</p>
-
- <p>In many cases it is very convenient to have a header in
- the escript, especially on Unix platforms. But the header is
- in fact optional. This means that you directly can "execute"
- an Erlang module, beam file or archive file without adding
- any header to them. But then you have to invoke the script
- like this:</p>
- <pre>
+ <c>escript</c> file is invoked. This behavior can be
+ overridden by setting flag <c>-escript main Module</c>
+ as one of the emulator flags. <c>Module</c> must be the
+ name of a module that has an exported <c>main/1</c>
+ function. For more information about archives and code loading, see
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
+ <p>It is often very convenient to have a header in
+ the escript, especially on Unix platforms. However, the header
+ is optional, so you directly can "execute"
+ an Erlang module, Beam file, or archive file without adding
+ any header to them. But then you have to invoke the script
+ as follows:</p>
+ <pre>
$ <input>escript factorial.erl 5</input>
factorial 5 = 120
$ <input>escript factorial.beam 5</input>
factorial 5 = 120
$ <input>escript factorial.zip 5</input>
-factorial 5 = 120
-</pre>
+factorial 5 = 120</pre>
</desc>
</func>
+
<func>
- <name>escript:create(FileOrBin, Sections) -> ok | {ok, binary()} | {error, term()}</name>
- <fsummary>Create an escript</fsummary>
+ <name>escript:create(FileOrBin, Sections) -> ok | {ok, binary()} |
+ {error, term()}</name>
+ <fsummary>Create an escript.</fsummary>
<type>
<v>FileOrBin = filename() | 'binary'</v>
<v>Sections = [Header] Body | Body</v>
<v>Header = shebang | {shebang, Shebang}
- | comment | {comment, Comment}
- | {emu_args, EmuArgs}</v>
+ &nbsp;&nbsp;&nbsp;| comment | {comment, Comment}
+ &nbsp;&nbsp;&nbsp;| {emu_args, EmuArgs}</v>
<v>Shebang = string() | 'default' | 'undefined'</v>
<v>Comment = string() | 'default' | 'undefined'</v>
<v>EmuArgs = string() | 'undefined'</v>
- <v>Body = {source, SourceCode}
- | {beam, BeamCode}
- | {archive, ZipArchive}
- | {archive, ZipFiles, ZipOptions}</v>
+ <v>Body = {source, SourceCode} | {beam, BeamCode}
+ &nbsp;&nbsp;&nbsp;| {archive, ZipArchive}
+ &nbsp;&nbsp;&nbsp;| {archive, ZipFiles, ZipOptions}</v>
<v>SourceCode = BeamCode = file:filename() | binary()</v>
- <v>ZipArchive = <seealso marker="stdlib:zip#type-filename">zip:filename()</seealso> | binary()</v>
+ <v>ZipArchive = <seealso marker="stdlib:zip#type-filename">
+ zip:filename()</seealso> | binary()</v>
<v>ZipFiles = [ZipFile]</v>
- <v>ZipFile = file:filename() | {file:filename(), binary()} | {file:filename(), binary(), file:file_info()}</v>
- <v>ZipOptions = [<seealso marker="stdlib:zip#type-create_option">zip:create_option()</seealso>]</v>
+ <v>ZipFile = file:filename()
+ &nbsp;&nbsp;&nbsp;| {file:filename(), binary()}
+ &nbsp;&nbsp;&nbsp;| {file:filename(), binary(), file:file_info()}</v>
+ <v>ZipOptions = [<seealso marker="stdlib:zip#type-create_option">
+ zip:create_option()</seealso>]</v>
</type>
<desc>
- <p>The <marker id="create_2"></marker> <c>create/2</c>
- function creates an escript from a list of sections. The
- sections can be given in any order. An escript begins with an
- optional <c>Header</c> followed by a mandatory <c>Body</c>. If
- the header is present, it does always begin with a
- <c>shebang</c>, possibly followed by a <c>comment</c> and
- <c>emu_args</c>. The <c>shebang</c> defaults to
- <c>"/usr/bin/env escript"</c>. The comment defaults to
- <c>"This is an -*- erlang -*- file"</c>. The created escript
- can either be returned as a binary or written to file.</p>
-
- <p>As an example of how the function can be used, we create an
- interpreted escript which uses <c>emu_args</c> to set some emulator
- flag. In this case it happens to disable the smp_support. We
- do also extract the different sections from the newly created
- script:</p>
+ <p><marker id="create_2"></marker>
+ Creates an escript from a list of sections. The
+ sections can be specified in any order. An escript begins with an
+ optional <c>Header</c> followed by a mandatory <c>Body</c>. If
+ the header is present, it does always begin with a
+ <c>shebang</c>, possibly followed by a <c>comment</c> and
+ <c>emu_args</c>. The <c>shebang</c> defaults to
+ <c>"/usr/bin/env escript"</c>. The <c>comment</c> defaults to
+ <c>"This is an -*- erlang -*- file"</c>. The created escript
+ can either be returned as a binary or written to file.</p>
+ <p>As an example of how the function can be used, we create an
+ interpreted escript that uses <c>emu_args</c> to set some emulator
+ flag. In this case, it happens to disable the <c>smp_support</c>. We
+ also extract the different sections from the newly created script:</p>
<pre>
&gt; <input>Source = "%% Demo\nmain(_Args) ->\n io:format(erlang:system_info(smp_support)).\n".</input>
"%% Demo\nmain(_Args) ->\n io:format(erlang:system_info(smp_support)).\n"
@@ -280,11 +269,9 @@ ok
"false"
&gt; <input>escript:extract("demo.escript", []).</input>
{ok,[{shebang,default}, {comment,default}, {emu_args,"-smp disable"},
- {source,&lt;&lt;"%% Demo\nmain(_Args) ->\n io:format(erlang:system_info(smp_su"...&gt;&gt;}]}
- </pre>
-
- <p>An escript without header can be created like this:</p>
-<pre>
+ {source,&lt;&lt;"%% Demo\nmain(_Args) ->\n io:format(erlang:system_info(smp_su"...&gt;&gt;}]}</pre>
+ <p>An escript without header can be created as follows:</p>
+ <pre>
&gt; <input>file:write_file("demo.erl",
["%% demo.erl\n-module(demo).\n-export([main/1]).\n\n", Source]).</input>
ok
@@ -299,14 +286,12 @@ ok
{beam,&lt;&lt;70,79,82,49,0,0,3,68,66,69,65,77,65,116,
111,109,0,0,0,83,0,0,0,9,...&gt;&gt;}]}
&gt; <input>os:cmd("escript demo.beam").</input>
-"true"
-</pre>
- <p>Here we create an archive script containing both Erlang
- code as well as beam code. Then we iterate over all files in
- the archive and collect their contents and some info about
- them.
- </p>
-<pre>
+"true"</pre>
+ <p>Here we create an archive script containing both Erlang
+ code and Beam code, then we iterate over all files in
+ the archive and collect their contents and some information about
+ them:</p>
+ <pre>
&gt; <input>{ok, SourceCode} = file:read_file("demo.erl").</input>
{ok,&lt;&lt;"%% demo.erl\n-module(demo).\n-export([main/1]).\n\n%% Demo\nmain(_Arg"...&gt;&gt;}
&gt; <input>escript:create("demo.escript",
@@ -339,43 +324,43 @@ ok
&lt;&lt;"%% demo.erl\n-module(demo).\n-export([main/1]).\n\n%% Demo\nmain(_Arg"...&gt;&gt;}]}</pre>
</desc>
</func>
+
<func>
- <name>escript:extract(File, Options) -> {ok, Sections} | {error, term()}</name>
- <fsummary>Parses an escript and extracts its sections</fsummary>
+ <name>escript:extract(File, Options) -> {ok, Sections} |
+ {error, term()}</name>
+ <fsummary>Parse an escript and extract its sections.</fsummary>
<type>
<v>File = filename()</v>
<v>Options = [] | [compile_source]</v>
<v>Sections = Headers Body</v>
<v>Headers = {shebang, Shebang}
- {comment, Comment}
- {emu_args, EmuArgs}</v>
+ {comment, Comment}
+ {emu_args, EmuArgs}</v>
<v>Shebang = string() | 'default' | 'undefined'</v>
<v>Comment = string() | 'default' | 'undefined'</v>
<v>EmuArgs = string() | 'undefined'</v>
<v>Body = {source, SourceCode}
- | {source, BeamCode}
- | {beam, BeamCode}
- | {archive, ZipArchive}</v>
+ &nbsp;&nbsp;&nbsp;| {source, BeamCode}
+ &nbsp;&nbsp;&nbsp;| {beam, BeamCode}
+ &nbsp;&nbsp;&nbsp;| {archive, ZipArchive}</v>
<v>SourceCode = BeamCode = ZipArchive = binary()</v>
</type>
<desc>
- <p>The <marker id="extract_2"></marker> <c>extract/2</c>
- function parses an escript and extracts its sections. This is
- the reverse of <c>create/2</c>.</p>
-
- <p>All sections are returned even if they do not exist in the
- escript. If a particular section happens to have the same
- value as the default value, the extracted value is set to the
- atom <c>default</c>. If a section is missing, the extracted
- value is set to the atom <c>undefined</c>. </p>
-
- <p>The <c>compile_source</c> option only affects the result if
- the escript contains <c>source</c> code. In that case the
- Erlang code is automatically compiled and <c>{source,
- BeamCode}</c> is returned instead of <c>{source,
- SourceCode}</c>.</p>
-
- <pre>
+ <p><marker id="extract_2"></marker>
+ Parses an escript and extracts its sections. This is the reverse
+ of <seealso marker="#create_2"><c>create/2</c></seealso>.</p>
+ <p>All sections are returned even if they do not exist in the
+ escript. If a particular section happens to have the same
+ value as the default value, the extracted value is set to the
+ atom <c>default</c>. If a section is missing, the extracted
+ value is set to the atom <c>undefined</c>.</p>
+ <p>Option <c>compile_source</c> only affects the result if
+ the escript contains <c>source</c> code. In this case the
+ Erlang code is automatically compiled and <c>{source,
+ BeamCode}</c> is returned instead of <c>{source,
+ SourceCode}</c>.</p>
+ <p>Example:</p>
+ <pre>
&gt; <input>escript:create("demo.escript",
[shebang, {archive, [{"demo.erl", SourceCode},
{"demo.beam", BeamCode}], []}]).</input>
@@ -385,52 +370,51 @@ ok
escript:extract("demo.escript", []).</input>
{ok,[{{archive,&lt;&lt;80,75,3,4,20,0,0,0,8,0,118,7,98,60,105,
152,61,93,107,0,0,0,118,0,...&gt;&gt;}
- {emu_args,undefined}]}
- </pre>
+ {emu_args,undefined}]}</pre>
</desc>
</func>
+
<func>
<name>escript:script_name() -> File</name>
- <fsummary>Returns the name of an escript</fsummary>
+ <fsummary>Return the name of an escript.</fsummary>
<type>
<v>File = filename()</v>
</type>
<desc>
- <p>The <marker id="script_name_0"></marker>
- <c>script_name/0</c> function returns the name of the escript
- being executed. If the function is invoked outside the context
- of an escript, the behavior is undefined.</p>
+ <p><marker id="script_name_0"></marker>
+ Returns the name of the escript that is executed.
+ If the function is invoked outside the context
+ of an escript, the behavior is undefined.</p>
</desc>
</func>
</funcs>
<section>
- <title>Options accepted by escript</title>
+ <title>Options Accepted By escript</title>
<taglist>
- <tag>-c</tag>
- <item>Compile the escript regardless of the value of the mode attribute.
+ <tag><c>-c</c></tag>
+ <item>Compiles the escript regardless of the value of the mode attribute.
</item>
-
- <tag>-d</tag>
- <item>Debug the escript. Starts the debugger, loads the module
- containing the <c>main/1</c> function into the debugger, sets a
- breakpoint in <c>main/1</c> and invokes <c>main/1</c>. If the
- module is precompiled, it must be explicitly compiled with the
- <c>debug_info</c> option.
+ <tag><c>-d</c></tag>
+ <item>Debugs the escript. Starts the debugger, loads the module
+ containing the <c>main/1</c> function into the debugger, sets a
+ breakpoint in <c>main/1</c>, and invokes <c>main/1</c>. If the
+ module is precompiled, it must be explicitly compiled with option
+ <c>debug_info</c>.
</item>
-
- <tag>-i</tag>
- <item>Interpret the escript regardless of the value of the mode attribute.
+ <tag><c>-i</c></tag>
+ <item>Interprets the escript regardless of the value of the mode
+ attribute.
+ </item>
+ <tag><c>-s</c></tag>
+ <item>Performs a syntactic and semantic check of the script file.
+ Warnings and errors (if any) are written to the standard output, but
+ the script will not be run. The exit status is <c>0</c> if any errors
+ are found, otherwise <c>127</c>.
+ </item>
+ <tag><c>-n</c></tag>
+ <item>Compiles the escript using flag <c>+native</c>.
</item>
-
- <tag>-s</tag>
- <item>Only perform a syntactic and semantic check of the script file.
- Warnings and errors (if any) are written to the standard output, but
- the script will not be run. The exit status will be 0 if there were
- no errors, and 127 otherwise.</item>
-
- <tag>-n</tag>
- <item>Compile the escript using the +native flag.</item>
</taglist>
</section>
</comref>
diff --git a/erts/doc/src/inet_cfg.xml b/erts/doc/src/inet_cfg.xml
index 5caf232a62..0cfcc7905d 100644
--- a/erts/doc/src/inet_cfg.xml
+++ b/erts/doc/src/inet_cfg.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2013</year>
+ <year>2004</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -22,7 +22,7 @@
</legalnotice>
- <title>Inet configuration</title>
+ <title>Inet Configuration</title>
<prepared>Peter Andersson</prepared>
<docno></docno>
<date>2004-03-02</date>
@@ -32,374 +32,341 @@
<section>
<title>Introduction</title>
- <p>This chapter tells you how the Erlang runtime system is configured
- for IP communication. It also explains how you may configure it
- for your own particular needs by means of a configuration file.
- The information here is mainly intended for users with special
- configuration needs or problems. There should normally be no need
- for specific settings for Erlang to function properly on a correctly
- IP configured platform. </p>
- <p>When Erlang starts up it will read the kernel variable
- <c><![CDATA[inetrc]]></c> which, if defined, should specify the location and
- name of a user configuration file. Example:</p>
- <p><c><![CDATA[% erl -kernel inetrc '"./cfg_files/erl_inetrc"']]></c></p>
- <p>Note that the usage of a <c><![CDATA[.inetrc]]></c> file, which was
- supported in earlier Erlang versions, is now obsolete.</p>
- <p>A second way to specify the configuration file is to set the
- environment variable <c><![CDATA[ERL_INETRC]]></c> to the full name of the file. Example (bash):</p>
- <p><c><![CDATA[% export ERL_INETRC=./cfg_files/erl_inetrc]]></c></p>
- <p>Note that the kernel variable <c><![CDATA[inetrc]]></c> overrides this environment variable.</p>
+ <p>This section describes how the Erlang runtime system is configured
+ for IP communication. It also explains how you can configure it
+ for your needs by a configuration file.
+ The information is primarily intended for users with special
+ configuration needs or problems. There is normally no need
+ for specific settings for Erlang to function properly on a correctly
+ IP-configured platform.</p>
+
+ <p>When Erlang starts up it reads the Kernel variable
+ <c><![CDATA[inetrc]]></c>, which, if defined, is to specify the location
+ and name of a user configuration file. Example:</p>
+
+ <code type="none"><![CDATA[
+% erl -kernel inetrc '"./cfg_files/erl_inetrc"']]></code>
+
+ <p>Notice that the use of an <c><![CDATA[.inetrc]]></c> file, which was
+ supported in earlier Erlang/OTP versions, is now obsolete.</p>
+
+ <p>A second way to specify the configuration file is to set
+ environment variable <c><![CDATA[ERL_INETRC]]></c> to the full name of
+ the file. Example (bash):</p>
+
+ <code type="none"><![CDATA[
+% export ERL_INETRC=./cfg_files/erl_inetrc]]></code>
+
+ <p>Notice that the Kernel variable <c><![CDATA[inetrc]]></c>
+ overrides this environment variable.</p>
+
<p>If no user configuration file is specified and Erlang is started
- in non-distributed or short name distributed mode, Erlang will use
- default configuration settings and a native lookup method that should
- work correctly under most circumstances. Erlang
- will not read any information from system inet configuration files
- (like /etc/host.conf, /etc/nsswitch.conf, etc) in these modes,
- except for /etc/resolv.conf and /etc/hosts that is read and monitored
- for changes on Unix platforms for the internal DNS client
- <seealso marker="kernel:inet_res">inet_res</seealso>.</p>
+ in non-distributed or short name distributed mode, Erlang uses
+ default configuration settings and a native lookup method that
+ works correctly under most circumstances. Erlang reads no
+ information from system <c>inet</c> configuration files (such as
+ <c>/etc/host.conf</c> and <c>/etc/nsswitch.conf</c>) in these modes,
+ except for <c>/etc/resolv.conf</c> and <c>/etc/hosts</c> that is read and
+ monitored for changes on Unix platforms for the internal DNS client
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>.</p>
+
<p>If Erlang is started in long name distributed mode, it needs to
- get the domain name from somewhere and will read system inet
+ get the domain name from somewhere and reads system <c>inet</c>
configuration files for this information. Any hosts and resolver
- information found then is also recorded, but not
- used as long as Erlang is configured for native lookups. (The
+ information found is also recorded, but not
+ used as long as Erlang is configured for native lookups. The
information becomes useful if the lookup method is changed to
- <c><![CDATA['file']]></c> or <c><![CDATA['dns']]></c>, see below).</p>
- <p>Native lookup (system calls) is always the default resolver method. This
- is true for all platforms except VxWorks and OSE Delta where <c><![CDATA['file']]></c>
- or <c><![CDATA['dns']]></c> is used (in that order of priority).</p>
- <p>On Windows platforms, Erlang will search the system registry rather than
- look for configuration files when started in long name distributed mode. </p>
+ <c><![CDATA['file']]></c> or <c><![CDATA['dns']]></c>, see below.</p>
+
+ <p>Native lookup (system calls) is always the default resolver method.
+ This is true for all platforms, except VxWorks and OSE Delta where
+ <c><![CDATA['file']]></c> or <c><![CDATA['dns']]></c> is used (in that
+ priority order).</p>
+
+ <p>On Windows platforms, Erlang searches the system registry rather than
+ looks for configuration files when started in long name distributed
+ mode.</p>
</section>
<section>
<title>Configuration Data</title>
<p>Erlang records the following data in a local database if found in system
- inet configuration files (or system registry):</p>
- <list type="bulleted">
- <item>Host names and addresses</item>
+ <c>inet</c> configuration files (or system registry):</p>
+
+ <list type="bulleted">
+ <item>Hostnames and host addresses</item>
<item>Domain name</item>
<item>Nameservers</item>
<item>Search domains</item>
<item>Lookup method</item>
</list>
- <p>This data may also be specified explicitly in the user
- configuration file. The configuration file should contain lines
- of configuration parameters (each terminated with a full
- stop). Some parameters add data to the configuration (e.g. host
+
+ <p>This data can also be specified explicitly in the user
+ configuration file. This file is to contain lines
+ of configuration parameters (each terminated with a full stop).
+ Some parameters add data to the configuration (such as host
and nameserver), others overwrite any previous settings
- (e.g. domain and lookup). The user configuration file is always
+ (such as domain and lookup). The user configuration file is always
examined last in the configuration process, making it possible
for the user to override any default values or previously made
settings. Call <c><![CDATA[inet:get_rc()]]></c> to view the state of the
- inet configuration database.</p>
- <p>These are the valid configuration parameters:</p>
- <p></p>
+ <c>inet</c> configuration database.</p>
+
+ <p>The valid configuration parameters are as follows:</p>
+
<taglist>
- <tag><em><c><![CDATA[{file, Format, File}.]]></c></em></tag>
+ <tag><c><![CDATA[{file, Format, File}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[Format = atom()]]></c></p>
<p><c><![CDATA[File = string()]]></c></p>
- <p></p>
- <p>Specify a system file that Erlang should read configuration
- data from. <c><![CDATA[Format]]></c> tells the parser how the file should be
- interpreted: <c><![CDATA[resolv]]></c> (Unix resolv.conf), <c><![CDATA[host_conf_freebsd]]></c>
- (FreeBSD host.conf), <c><![CDATA[host_conf_bsdos]]></c> (BSDOS host.conf),
- <c><![CDATA[host_conf_linux]]></c> (Linux host.conf), <c><![CDATA[nsswitch_conf]]></c>
- (Unix nsswitch.conf) or <c><![CDATA[hosts]]></c> (Unix hosts). <c><![CDATA[File]]></c> should
- specify the name of the file with full path.</p>
- <p></p>
+ <p>Specify a system file that Erlang is to read configuration data from.
+ <c><![CDATA[Format]]></c> tells the parser how the file is to be
+ interpreted:</p>
+ <list type="bulleted">
+ <item><c><![CDATA[resolv]]></c> (Unix resolv.conf)</item>
+ <item><c><![CDATA[host_conf_freebsd]]></c> (FreeBSD host.conf)</item>
+ <item><c><![CDATA[host_conf_bsdos]]></c> (BSDOS host.conf)</item>
+ <item><c><![CDATA[host_conf_linux]]></c> (Linux host.conf)</item>
+ <item><c><![CDATA[nsswitch_conf]]></c> (Unix nsswitch.conf)</item>
+ <item><c><![CDATA[hosts]]></c> (Unix hosts)</item>
+ </list>
+ <p><c><![CDATA[File]]></c> is to specify the filename with full
+ path.</p>
</item>
- <tag><em><c><![CDATA[{resolv_conf, File}.]]></c></em></tag>
+ <tag><c><![CDATA[{resolv_conf, File}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[File = string()]]></c></p>
- <p></p>
- <p>Specify a system file that Erlang should read resolver
+ <p>Specify a system file that Erlang is to read resolver
configuration from for the internal DNS client
- <seealso marker="kernel:inet_res">inet_res</seealso>,
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>,
and monitor for changes, even if it does not exist.
The path must be absolute.</p>
- <p>This may override the configuration parameters
+ <p>This can override the configuration parameters
<c><![CDATA[nameserver]]></c> and
<c><![CDATA[search]]></c> depending on the contents
- of the specified file. They may also change any time in the future
+ of the specified file. They can also change any time in the future
reflecting the file contents.</p>
- <p>If the file is specified as an empty string "",
- no file is read nor monitored in the future. This emulates
- the old behaviour of not configuring the DNS client when
+ <p>If the file is specified as an empty string <c>""</c>,
+ no file is read or monitored in the future. This emulates
+ the old behavior of not configuring the DNS client when
the node is started in short name distributed mode.</p>
- <p>If this parameter is not specified it defaults to
- <c><![CDATA[/etc/resolv.conf]]></c> unless the environment variable
- <c><![CDATA[ERL_INET_ETC_DIR]]></c> is set which defines
+ <p>If this parameter is not specified, it defaults to
+ <c><![CDATA[/etc/resolv.conf]]></c> unless environment variable
+ <c><![CDATA[ERL_INET_ETC_DIR]]></c> is set, which defines
the directory for this file to some maybe other than
<c><![CDATA[/etc]]></c>.</p>
- <p></p>
</item>
- <tag><em><c><![CDATA[{hosts_file, File}.]]></c></em></tag>
+ <tag><c><![CDATA[{hosts_file, File}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[File = string()]]></c></p>
- <p></p>
- <p>Specify a system file that Erlang should read resolver
- configuration from for the internal hosts file resolver
+ <p>Specify a system file that Erlang is to read resolver
+ configuration from for the internal hosts file resolver,
and monitor for changes, even if it does not exist.
The path must be absolute.</p>
<p>These host entries are searched after all added with
<c>{file, hosts, File}</c> above or
- <c>{host, IP, Aliases}</c> below when the lookup option
+ <c>{host, IP, Aliases}</c> below when lookup option
<c>file</c> is used.</p>
- <p>If the file is specified as an empty string "",
- no file is read nor monitored in the future. This emulates
- the old behaviour of not configuring the DNS client when
+ <p>If the file is specified as an empty string <c>""</c>,
+ no file is read or monitored in the future. This emulates
+ the old behavior of not configuring the DNS client when
the node is started in short name distributed mode.</p>
- <p>If this parameter is not specified it defaults to
- <c><![CDATA[/etc/hosts]]></c> unless the environment variable
- <c><![CDATA[ERL_INET_ETC_DIR]]></c> is set which defines
+ <p>If this parameter is not specified, it defaults to
+ <c><![CDATA[/etc/hosts]]></c> unless environment variable
+ <c><![CDATA[ERL_INET_ETC_DIR]]></c> is set, which defines
the directory for this file to some maybe other than
<c><![CDATA[/etc]]></c>.</p>
- <p></p>
</item>
- <tag><em><c><![CDATA[{registry, Type}.]]></c></em></tag>
+ <tag><c><![CDATA[{registry, Type}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[Type = atom()]]></c></p>
- <p></p>
- <p>Specify a system registry that Erlang should read configuration
- data from. Currently, <c><![CDATA[win32]]></c> is the only valid option.</p>
- <p></p>
+ <p>Specify a system registry that Erlang is to read configuration
+ data from. <c><![CDATA[win32]]></c> is the only valid option.</p>
</item>
- <tag><em><c><![CDATA[{host, IP, Aliases}.]]></c></em></tag>
+ <tag><c><![CDATA[{host, IP, Aliases}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[IP = tuple()]]></c></p>
<p><c><![CDATA[Aliases = [string()]]]></c></p>
- <p></p>
<p>Add host entry to the hosts table.</p>
- <p></p>
</item>
- <tag><em><c><![CDATA[{domain, Domain}.]]></c></em></tag>
+ <tag><c><![CDATA[{domain, Domain}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[Domain = string()]]></c></p>
- <p></p>
<p>Set domain name.</p>
- <p></p>
</item>
- <tag><em><c><![CDATA[{nameserver, IP [,Port]}.]]></c></em></tag>
+ <tag><c><![CDATA[{nameserver, IP [,Port]}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[IP = tuple()]]></c></p>
<p><c><![CDATA[Port = integer()]]></c></p>
- <p></p>
- <p>Add address (and port, if other than default) of primary
+ <p>Add address (and port, if other than default) of the primary
nameserver to use for
- <seealso marker="kernel:inet_res">inet_res</seealso>.</p>
- <p></p>
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>.
+ </p>
</item>
- <tag><em><c><![CDATA[{alt_nameserver, IP [,Port]}.]]></c></em></tag>
+ <tag><c><![CDATA[{alt_nameserver, IP [,Port]}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[IP = tuple()]]></c></p>
<p><c><![CDATA[Port = integer()]]></c></p>
- <p></p>
- <p>Add address (and port, if other than default) of secondary
+ <p>Add address (and port, if other than default) of the secondary
nameserver for
- <seealso marker="kernel:inet_res">inet_res</seealso>.</p>
- <p></p>
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>.
+ </p>
</item>
- <tag><em><c><![CDATA[{search, Domains}.]]></c></em></tag>
+ <tag><c><![CDATA[{search, Domains}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[Domains = [string()]]]></c></p>
- <p></p>
<p>Add search domains for
- <seealso marker="kernel:inet_res">inet_res</seealso>.</p>
- <p></p>
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>.
+ </p>
</item>
- <tag><em><c><![CDATA[{lookup, Methods}.]]></c></em></tag>
+ <tag><c><![CDATA[{lookup, Methods}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[Methods = [atom()]]]></c></p>
- <p></p>
- <p>Specify lookup methods and in which order to try them.
- The valid methods are: <c><![CDATA[native]]></c> (use system calls),
- <c><![CDATA[file]]></c> (use host data retrieved from
- system configuration files and/or
- the user configuration file) or <c><![CDATA[dns]]></c>
- (use the Erlang DNS client
- <seealso marker="kernel:inet_res">inet_res</seealso>
- for nameserver queries).</p>
+ <p>Specify lookup methods and in which order to try them.
+ The valid methods are as follows:</p>
+ <list type="bulleted">
+ <item><c><![CDATA[native]]></c> (use system calls)</item>
+ <item><c><![CDATA[file]]></c> (use host data retrieved from system
+ configuration files and/or the user configuration file)</item>
+ <item><c><![CDATA[dns]]></c> (use the Erlang DNS client
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>
+ for nameserver queries)</item>
+ </list>
<p>The lookup method <c><![CDATA[string]]></c> tries to
- parse the hostname as a IPv4 or IPv6 string and return
+ parse the hostname as an IPv4 or IPv6 string and return
the resulting IP address. It is automatically tried
first when <c><![CDATA[native]]></c> is <em>not</em>
- in the <c><![CDATA[Methods]]></c> list. To skip it in this case
+ in the <c><![CDATA[Methods]]></c> list. To skip it in this case,
the pseudo lookup method <c><![CDATA[nostring]]></c> can be
inserted anywhere in the <c><![CDATA[Methods]]></c> list.</p>
- <p></p>
</item>
- <tag><em><c><![CDATA[{cache_size, Size}.]]></c></em></tag>
+ <tag><c><![CDATA[{cache_size, Size}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[Size = integer()]]></c></p>
- <p></p>
- <p>Set size of resolver cache. Default is 100 DNS records.</p>
- <p></p>
+ <p>Set the resolver cache size. Defaults to 100 DNS records.</p>
</item>
- <tag><em><c><![CDATA[{cache_refresh, Time}.]]></c></em></tag>
- <item>
- <p></p>
+ <tag><c><![CDATA[{cache_refresh, Time}.]]></c></tag>
+ <item>
<p><c><![CDATA[Time = integer()]]></c></p>
- <p></p>
- <p>Set how often (in millisec)
- the resolver cache for
- <seealso marker="kernel:inet_res">inet_res</seealso>.
- is refreshed (i.e. expired DNS records are deleted).
- Default is 1 h.</p>
- <p></p>
+ <p>Set how often (in milliseconds) the resolver cache for
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>
+ is refreshed (that is, expired DNS records are deleted).
+ Defaults to 1 hour.</p>
</item>
- <tag><em><c><![CDATA[{timeout, Time}.]]></c></em></tag>
+ <tag><c><![CDATA[{timeout, Time}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[Time = integer()]]></c></p>
- <p></p>
- <p>Set the time to wait until retry (in millisec) for DNS queries
+ <p>Set the time to wait until retry (in milliseconds) for DNS queries
made by
- <seealso marker="kernel:inet_res">inet_res</seealso>.
- Default is 2 sec.</p>
- <p></p>
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>.
+ Defaults to 2 seconds.</p>
</item>
- <tag><em><c><![CDATA[{retry, N}.]]></c></em></tag>
+ <tag><c><![CDATA[{retry, N}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[N = integer()]]></c></p>
- <p></p>
<p>Set the number of DNS queries
- <seealso marker="kernel:inet_res">inet_res</seealso>
- will try before giving up.
- Default is 3.</p>
- <p></p>
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>
+ will try before giving up. Defaults to 3.</p>
</item>
- <tag><em><c><![CDATA[{inet6, Bool}.]]></c></em></tag>
+ <tag><c><![CDATA[{inet6, Bool}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[Bool = true | false]]></c></p>
- <p></p>
- <p>Tells the DNS client
- <seealso marker="kernel:inet_res">inet_res</seealso>
- to look up IPv6 addresses. Default is false.</p>
- <p></p>
+ <p>Tells the DNS client
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>
+ to look up IPv6 addresses. Defaults to <c>false</c>.</p>
</item>
- <tag><em><c><![CDATA[{usevc, Bool}.]]></c></em></tag>
+ <tag><c><![CDATA[{usevc, Bool}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[Bool = true | false]]></c></p>
- <p></p>
- <p>Tells the DNS client
- <seealso marker="kernel:inet_res">inet_res</seealso>
- to use TCP (Virtual Circuit) instead of UDP. Default is false.</p>
- <p></p>
+ <p>Tells the DNS client
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>
+ to use TCP (Virtual Circuit) instead of UDP. Defaults to
+ <c>false</c>.</p>
</item>
- <tag><em><c><![CDATA[{edns, Version}.]]></c></em></tag>
+ <tag><c><![CDATA[{edns, Version}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[Version = false | 0]]></c></p>
- <p></p>
<p>Sets the EDNS version that
- <seealso marker="kernel:inet_res">inet_res</seealso>
- will use. The only allowed is zero. Default is false
- which means to not use EDNS.</p>
- <p></p>
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>
+ will use. The only allowed version is zero. Defaults to <c>false</c>,
+ which means not to use EDNS.</p>
</item>
- <tag><em><c><![CDATA[{udp_payload_size, Size}.]]></c></em></tag>
+ <tag><c><![CDATA[{udp_payload_size, Size}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[N = integer()]]></c></p>
- <p></p>
<p>Sets the allowed UDP payload size
- <seealso marker="kernel:inet_res">inet_res</seealso>
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>
will advertise in EDNS queries. Also sets the limit
when the DNS query will be deemed too large for UDP
- forcing a TCP query instead, which is not entirely
- correct since the advertised UDP payload size of the
- individual nameserver is what should be used,
+ forcing a TCP query instead; this is not entirely
+ correct, as the advertised UDP payload size of the
+ individual nameserver is what is to be used,
but this simple strategy will do until a more intelligent
- (probing, caching) algorithm need be implemented.
- The default is 1280 which stems from the
- standard Ethernet MTU size.</p>
- <p></p>
+ (probing, caching) algorithm needs to be implemented.
+ Default to 1280, which stems from the standard Ethernet MTU size.</p>
</item>
- <tag><em><c><![CDATA[{udp, Module}.]]></c></em></tag>
+ <tag><c><![CDATA[{udp, Module}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[Module = atom()]]></c></p>
- <p></p>
- <p>Tell Erlang to use other primitive UDP module than inet_udp.</p>
- <p></p>
+ <p>Tell Erlang to use another primitive UDP module than
+ <c>inet_udp</c>.</p>
</item>
- <tag><em><c><![CDATA[{tcp, Module}.]]></c></em></tag>
+ <tag><c><![CDATA[{tcp, Module}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[Module = atom()]]></c></p>
- <p></p>
- <p>Tell Erlang to use other primitive TCP module than inet_tcp.</p>
- <p></p>
+ <p>Tell Erlang to use another primitive TCP module than
+ <c>inet_tcp</c>.</p>
</item>
- <tag><em><c><![CDATA[clear_hosts.]]></c></em></tag>
+ <tag><c><![CDATA[clear_hosts.]]></c></tag>
<item>
- <p></p>
<p>Clear the hosts table.</p>
- <p></p>
</item>
- <tag><em><c><![CDATA[clear_ns.]]></c></em></tag>
+ <tag><c><![CDATA[clear_ns.]]></c></tag>
<item>
- <p></p>
<p>Clear the list of recorded nameservers (primary and secondary).</p>
- <p></p>
</item>
- <tag><em><c><![CDATA[clear_search.]]></c></em></tag>
+ <tag><c><![CDATA[clear_search.]]></c></tag>
<item>
- <p></p>
<p>Clear the list of search domains.</p>
- <p></p>
</item>
</taglist>
</section>
<section>
<title>User Configuration Example</title>
- <p>Here follows a user configuration example.</p>
- <p>Assume a user does not want Erlang to use the native lookup method,
- but wants Erlang to read all information necessary from start and use
- that for resolving names and addresses. In case lookup fails, Erlang
- should request the data from a nameserver (using the Erlang
+ <p>Assume that a user does not want Erlang to use the native lookup method,
+ but wants Erlang to read all information necessary from start and use
+ that for resolving names and addresses. If lookup fails, Erlang
+ is to request the data from a nameserver (using the Erlang
DNS client, set to use EDNS allowing larger responses).
- The resolver configuration will be updated when
- its configuration file changes, furthermore, DNS records
- should never be cached. The user configuration file
+ The resolver configuration is updated when
+ its configuration file changes. Also, DNS records
+ are never to be cached. The user configuration file
(in this example named <c><![CDATA[erl_inetrc]]></c>, stored
- in directory <c><![CDATA[./cfg_files]]></c>) could then look like this
+ in directory <c><![CDATA[./cfg_files]]></c>) can then look as follows
(Unix):</p>
+
<pre>
- %% -- ERLANG INET CONFIGURATION FILE --
- %% read the hosts file
- {file, hosts, "/etc/hosts"}.
- %% add a particular host
- {host, {134,138,177,105}, ["finwe"]}.
- %% do not monitor the hosts file
- {hosts_file, ""}.
- %% read and monitor nameserver config from here
- {resolv_conf, "/usr/local/etc/resolv.conf"}.
- %% enable EDNS
- {edns,0}.
- %% disable caching
- {cache_size, 0}.
- %% specify lookup method
- {lookup, [file, dns]}.</pre>
- <p>And Erlang could, for example, be started like this:</p>
- <p><c><![CDATA[% erl -sname my_node -kernel inetrc '"./cfg_files/erl_inetrc"']]></c></p>
+%% -- ERLANG INET CONFIGURATION FILE --
+%% read the hosts file
+{file, hosts, "/etc/hosts"}.
+%% add a particular host
+{host, {134,138,177,105}, ["finwe"]}.
+%% do not monitor the hosts file
+{hosts_file, ""}.
+%% read and monitor nameserver config from here
+{resolv_conf, "/usr/local/etc/resolv.conf"}.
+%% enable EDNS
+{edns,0}.
+%% disable caching
+{cache_size, 0}.
+%% specify lookup method
+{lookup, [file, dns]}.</pre>
+
+ <p>And Erlang can, for example, be started as follows:</p>
+
+ <code type="none"><![CDATA[
+% erl -sname my_node -kernel inetrc '"./cfg_files/erl_inetrc"']]></code>
</section>
</chapter>
diff --git a/erts/doc/src/init.xml b/erts/doc/src/init.xml
index fe26df61f7..c14f0a558d 100644
--- a/erts/doc/src/init.xml
+++ b/erts/doc/src/init.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -19,7 +19,7 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-
+
</legalnotice>
<title>init</title>
@@ -30,128 +30,140 @@
<file>init.xml</file>
</header>
<module>init</module>
- <modulesummary>Coordination of System Startup</modulesummary>
+ <modulesummary>Coordination of system startup.</modulesummary>
<description>
- <p>The <c>init</c> module is pre-loaded and contains the code for
- the <c>init</c> system process which coordinates the start-up of
- the system. The first function evaluated at start-up is
- <c>boot(BootArgs)</c>, where <c>BootArgs</c> is a list of command
- line arguments supplied to the Erlang runtime system from
- the local operating system. See
- <seealso marker="erts:erl">erl(1)</seealso>.</p>
- <p><c>init</c> reads the boot script which contains instructions on
- how to initiate the system. See
- <seealso marker="sasl:script">script(4)</seealso> for more
- information about boot scripts.</p>
+ <p>This module is preloaded and contains the code for
+ the <c>init</c> system process that coordinates the startup of
+ the system. The first function evaluated at startup is
+ <c>boot(BootArgs)</c>, where <c>BootArgs</c> is a list of
+ command-line arguments supplied to the Erlang runtime system from
+ the local operating system; see
+ <seealso marker="erts:erl"><c>erl(1)</c></seealso>.</p>
+
+ <p><c>init</c> reads the boot script, which contains instructions on
+ how to initiate the system. For more information about boot scripts, see
+ <seealso marker="sasl:script"><c>script(4)</c></seealso>.</p>
+
<p><c>init</c> also contains functions to restart, reboot, and stop
the system.</p>
</description>
+
<funcs>
<func>
<name name="boot" arity="1"/>
- <fsummary>Start the Erlang runtime system</fsummary>
+ <fsummary>Start the Erlang runtime system.</fsummary>
<desc>
<p>Starts the Erlang runtime system. This function is called
- when the emulator is started and coordinates system start-up.</p>
- <p><c><anno>BootArgs</anno></c> are all command line arguments except
- the emulator flags, that is, flags and plain arguments. See
- <seealso marker="erts:erl">erl(1)</seealso>.</p>
- <p><c>init</c> itself interprets some of the flags, see
- <seealso marker="#flags">Command Line Flags</seealso> below.
+ when the emulator is started and coordinates system startup.</p>
+ <p><c><anno>BootArgs</anno></c> are all command-line arguments except
+ the emulator flags, that is, flags and plain arguments; see
+ <seealso marker="erts:erl"><c>erl(1)</c></seealso>.</p>
+ <p><c>init</c> interprets some of the flags, see section
+ <seealso marker="#flags">Command-Line Flags</seealso> below.
The remaining flags ("user flags") and plain arguments are
passed to the <c>init</c> loop and can be retrieved by calling
- <c>get_arguments/0</c> and <c>get_plain_arguments/0</c>,
- respectively.</p>
+ <seealso marker="#get_arguments/0"><c>get_arguments/0</c></seealso>
+ and <seealso marker="#get_plain_arguments/0">
+ <c>get_plain_arguments/0</c></seealso>, respectively.</p>
</desc>
</func>
+
<func>
<name name="get_argument" arity="1"/>
- <fsummary>Get the values associated with a command line user flag</fsummary>
+ <fsummary>Get the values associated with a command-line user flag.
+ </fsummary>
<desc>
- <p>Returns all values associated with the command line user flag
- <c><anno>Flag</anno></c>. If <c><anno>Flag</anno></c> is provided several times, each
- <c><anno>Values</anno></c> is returned in preserved order.</p>
+ <p>Returns all values associated with the command-line user flag
+ <c><anno>Flag</anno></c>. If <c><anno>Flag</anno></c> is provided
+ several times, each <c><anno>Values</anno></c> is returned in
+ preserved order. Example:</p>
<pre>
% <input>erl -a b c -a d</input>
...
1> <input>init:get_argument(a).</input>
{ok,[["b","c"],["d"]]}</pre>
- <p>There are also a number of flags, which are defined
+ <p>The following flags are defined
automatically and can be retrieved using this function:</p>
<taglist>
<tag><c>root</c></tag>
<item>
- <p>The installation directory of Erlang/OTP, <c>$ROOT</c>.</p>
+ <p>The installation directory of Erlang/OTP, <c>$ROOT</c>:</p>
<pre>
2> <input>init:get_argument(root).</input>
{ok,[["/usr/local/otp/releases/otp_beam_solaris8_r10b_patched"]]}</pre>
</item>
<tag><c>progname</c></tag>
<item>
- <p>The name of the program which started Erlang.</p>
+ <p>The name of the program which started Erlang:</p>
<pre>
3> <input>init:get_argument(progname).</input>
{ok,[["erl"]]}</pre>
</item>
<tag><c>home</c></tag>
<item>
- <p>The home directory.</p>
+ <p>The home directory:</p>
<pre>
4> <input>init:get_argument(home).</input>
{ok,[["/home/harry"]]}</pre>
</item>
</taglist>
- <p>Returns <c>error</c> if there is no value associated with
- <c>Flag</c>.</p>
+ <p>Returns <c>error</c> if no value is associated with <c>Flag</c>.</p>
</desc>
</func>
+
<func>
<name name="get_arguments" arity="0"/>
- <fsummary>Get all command line user flags</fsummary>
+ <fsummary>Get all command-line user flags.</fsummary>
<desc>
- <p>Returns all command line flags, as well as the system
- defined flags, see <c>get_argument/1</c>.</p>
+ <p>Returns all command-line flags and the system-defined flags, see
+ <seealso marker="#get_argument/1"><c>get_argument/1</c></seealso>.</p>
</desc>
</func>
+
<func>
<name name="get_plain_arguments" arity="0"/>
- <fsummary>Get all non-flag command line arguments</fsummary>
+ <fsummary>Get all non-flag command-line arguments.</fsummary>
<desc>
- <p>Returns any plain command line arguments as a list of strings
+ <p>Returns any plain command-line arguments as a list of strings
(possibly empty).</p>
</desc>
</func>
+
<func>
<name name="get_status" arity="0"/>
- <fsummary>Get system status information</fsummary>
+ <fsummary>Get system status information.</fsummary>
<type name="internal_status"/>
<desc>
<p>The current status of the <c>init</c> process can be
inspected. During system startup (initialization),
<c><anno>InternalStatus</anno></c> is <c>starting</c>, and
- <c><anno>ProvidedStatus</anno></c> indicates how far the boot script has
- been interpreted. Each <c>{progress, Info}</c> term
- interpreted in the boot script affects <c><anno>ProvidedStatus</anno></c>,
- that is, <c><anno>ProvidedStatus</anno></c> gets the value of <c>Info</c>.</p>
+ <c><anno>ProvidedStatus</anno></c> indicates how far the boot
+ script has been interpreted. Each <c>{progress, Info}</c> term
+ interpreted in the boot script affects
+ <c><anno>ProvidedStatus</anno></c>, that is,
+ <c><anno>ProvidedStatus</anno></c> gets the value of <c>Info</c>.</p>
</desc>
</func>
+
<func>
<name name="reboot" arity="0"/>
- <fsummary>Take down and restart an Erlang node smoothly</fsummary>
+ <fsummary>Take down and restart an Erlang node smoothly.</fsummary>
<desc>
<p>All applications are taken down smoothly, all code is
unloaded, and all ports are closed before the system
- terminates. If the <c>-heart</c> command line flag was given,
- the <c>heart</c> program will try to reboot the system. Refer
- to <c>heart(3)</c> for more information.</p>
+ terminates. If command-line flag <c>-heart</c> was specified,
+ the <c>heart</c> program tries to reboot the system. For more
+ information, see
+ <seealso marker="kernel:heart"><c>heart(3)</c></seealso>.</p>
<p>To limit the shutdown time, the time <c>init</c> is allowed
- to spend taking down applications, the <c>-shutdown_time</c>
- command line flag should be used.</p>
+ to spend taking down applications, command-line flag
+ <c>-shutdown_time</c> is to be used.</p>
</desc>
</func>
+
<func>
<name name="restart" arity="0"/>
- <fsummary>Restart the running Erlang node</fsummary>
+ <fsummary>Restart the running Erlang node.</fsummary>
<desc>
<p>The system is restarted <em>inside</em> the running Erlang
node, which means that the emulator is not restarted. All
@@ -160,97 +172,104 @@
the same way as initially started. The same <c>BootArgs</c>
are used again.</p>
<p>To limit the shutdown time, the time <c>init</c> is allowed
- to spend taking down applications, the <c>-shutdown_time</c>
- command line flag should be used.</p>
+ to spend taking down applications, command-line flag
+ <c>-shutdown_time</c> is to be used.</p>
</desc>
</func>
+
<func>
<name name="script_id" arity="0"/>
- <fsummary>Get the identity of the used boot script</fsummary>
+ <fsummary>Get the identity of the used boot script.</fsummary>
<desc>
- <p>Get the identity of the boot script used to boot the system.
+ <p>Gets the identity of the boot script used to boot the system.
<c><anno>Id</anno></c> can be any Erlang term. In the delivered boot
- scripts, <c><anno>Id</anno></c> is <c>{Name, Vsn}</c>. <c>Name</c> and
- <c>Vsn</c> are strings.</p>
+ scripts, <c><anno>Id</anno></c> is <c>{Name, Vsn}</c>. <c>Name</c>
+ and <c>Vsn</c> are strings.</p>
</desc>
</func>
+
<func>
<name name="stop" arity="0"/>
- <fsummary>Take down an Erlang node smoothly</fsummary>
+ <fsummary>Take down an Erlang node smoothly.</fsummary>
<desc>
- <p>All applications are taken down smoothly, all code is
- unloaded, and all ports are closed before the system
- terminates. If the <c>-heart</c> command line flag was given,
- the <c>heart</c> program is terminated before the Erlang node
- terminates. Refer to <c>heart(3)</c> for more information.</p>
- <p>To limit the shutdown time, the time <c>init</c> is allowed
- to spend taking down applications, the <c>-shutdown_time</c>
- command line flag should be used.</p>
+ <p>The same as
+ <seealso marker="#stop/1"><c>stop(0)</c></seealso>.</p>
</desc>
</func>
+
<func>
<name name="stop" arity="1"/>
- <fsummary>Take down an Erlang node smoothly</fsummary>
+ <fsummary>Take down an Erlang node smoothly.</fsummary>
<desc>
<p>All applications are taken down smoothly, all code is
unloaded, and all ports are closed before the system
- terminates by calling <c>halt(<anno>Status</anno>)</c>. If the
- <c>-heart</c> command line flag was given, the <c>heart</c>
- program is terminated before the Erlang node
- terminates. Refer to <c>heart(3)</c> for more
- information.</p>
+ terminates by calling <c>halt(<anno>Status</anno>)</c>. If
+ command-line flag <c>-heart</c> was specified, the <c>heart</c>
+ program is terminated before the Erlang node terminates.
+ For more information, see
+ <seealso marker="kernel:heart"><c>heart(3)</c></seealso>.</p>
<p>To limit the shutdown time, the time <c>init</c> is allowed
- to spend taking down applications, the <c>-shutdown_time</c>
- command line flag should be used.</p>
+ to spend taking down applications, command-line flag
+ <c>-shutdown_time</c> is to be used.</p>
</desc>
</func>
</funcs>
<section>
<marker id="flags"></marker>
- <title>Command Line Flags</title>
- <warning><p>The support for loading of code from archive files is
- experimental. The sole purpose of releasing it before it is ready
- is to obtain early feedback. The file format, semantics,
- interfaces etc. may be changed in a future release. The
- <c>-code_path_choice</c> flag is also experimental.</p></warning>
+ <title>Command-Line Flags</title>
+ <warning>
+ <p>The support for loading of code from archive files is
+ experimental. The only purpose of releasing it before it is ready
+ is to obtain early feedback. The file format, semantics,
+ interfaces, and so on, can be changed in a future release. The
+ <c>-code_path_choice</c> flag is also experimental.</p>
+ </warning>
- <p>The <c>init</c> module interprets the following command line
- flags:</p>
+ <p>The <c>init</c> module interprets the following command-line flags:</p>
<taglist>
<tag><c>--</c></tag>
<item>
<p>Everything following <c>--</c> up to the next flag is
considered plain arguments and can be retrieved using
- <c>get_plain_arguments/0</c>.</p>
+ <seealso marker="#get_plain_arguments/0">
+ <c>get_plain_arguments/0</c></seealso>.</p>
</item>
<tag><c>-code_path_choice Choice</c></tag>
<item>
- <p>This flag can be set to <c>strict</c> or <c>relaxed</c>. It
- controls whether each directory in the code path should be
- interpreted strictly as it appears in the <c>boot script</c> or if
- <c>init</c> should be more relaxed and try to find a suitable
- directory if it can choose from a regular ebin directory and
- an ebin directory in an archive file. This flag is particular
- useful when you want to elaborate with code loading from
- archives without editing the <c>boot script</c>. See <seealso
- marker="sasl:script">script(4)</seealso> for more information
- about interpretation of boot scripts. The flag does also have
- a similar affect on how the code server works. See <seealso
- marker="kernel:code">code(3)</seealso>.</p>
-
+ <p>Can be set to <c>strict</c> or <c>relaxed</c>. It controls how each
+ directory in the code path is to be interpreted:</p>
+ <list type="bulleted">
+ <item>
+ <p>Strictly as it appears in the <c>boot script</c>, or</p>
+ </item>
+ <item>
+ <p><c>init</c> is to be more relaxed and try to find a suitable
+ directory if it can choose from a regular <c>ebin</c> directory
+ and an <c>ebin</c> directory in an archive file.</p>
+ </item>
+ </list>
+ <p>This flag is particular
+ useful when you want to elaborate with code loading from
+ archives without editing the <c>boot script</c>. For more
+ information about interpretation of boot scripts, see
+ <seealso marker="sasl:script"><c>script(4)</c></seealso>.
+ The flag has also a similar effect on how the code server works; see
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
+ </item>
+ <tag><c>-epmd_module Module</c></tag>
+ <item>
+ <p>Specifies the module to use for registration and lookup of
+ node names. Defaults to <c>erl_epmd</c>.</p>
</item>
<tag><c>-eval Expr</c></tag>
<item>
- <p>Scans, parses and evaluates an arbitrary expression
+ <p>Scans, parses, and evaluates an arbitrary expression
<c>Expr</c> during system initialization. If any of these
- steps fail (syntax error, parse error or exception during
- evaluation), Erlang stops with an error message. Here is an
- example that seeds the random number generator:</p>
- <pre>
-% <input>erl -eval '{X,Y,Z} = now(), random:seed(X,Y,Z).'</input></pre>
- <p>This example uses Erlang as a hexadecimal calculator:</p>
+ steps fail (syntax error, parse error, or exception during
+ evaluation), Erlang stops with an error message. In the following
+ example Erlang is used as a hexadecimal calculator:</p>
<pre>
% <input>erl -noshell -eval 'R = 16#1F+16#A0, io:format("~.16B~n", [R])' \\</input>
<input>-s erlang halt</input>
@@ -260,14 +279,15 @@ BF</pre>
<c>-eval</c> expressions are evaluated sequentially with
<c>-s</c> and <c>-run</c> function calls (this also in
the order specified). As with <c>-s</c> and <c>-run</c>, an
- evaluation that does not terminate, blocks the system
+ evaluation that does not terminate blocks the system
initialization process.</p>
</item>
<tag><c>-extra</c></tag>
<item>
<p>Everything following <c>-extra</c> is considered plain
arguments and can be retrieved using
- <c>get_plain_arguments/0</c>.</p>
+ <seealso marker="#get_plain_arguments/0">
+ <c>get_plain_arguments/0</c></seealso>.</p>
</item>
<tag><c>-run Mod [Func [Arg1, Arg2, ...]]</c></tag>
<item>
@@ -289,8 +309,8 @@ foo:bar()
foo:bar(["baz", "1", "2"]).</code>
<p>The functions are executed sequentially in an initialization
process, which then terminates normally and passes control to
- the user. This means that a <c>-run</c> call which does not
- return will block further processing; to avoid this, use
+ the user. This means that a <c>-run</c> call that does not
+ return blocks further processing; to avoid this, use
some variant of <c>spawn</c> in such cases.</p>
</item>
<tag><c>-s Mod [Func [Arg1, Arg2, ...]]</c></tag>
@@ -313,11 +333,11 @@ foo:bar()
foo:bar([baz, '1', '2']).</code>
<p>The functions are executed sequentially in an initialization
process, which then terminates normally and passes control to
- the user. This means that a <c>-s</c> call which does not
- return will block further processing; to avoid this, use
+ the user. This means that a <c>-s</c> call that does not
+ return blocks further processing; to avoid this, use
some variant of <c>spawn</c> in such cases.</p>
- <p>Due to the limited length of atoms, it is recommended that
- <c>-run</c> be used instead.</p>
+ <p>Because of the limited length of atoms, it is recommended to
+ use <c>-run</c> instead.</p>
</item>
</taglist>
</section>
@@ -339,9 +359,9 @@ error</pre>
</section>
<section>
- <title>SEE ALSO</title>
- <p><seealso marker="erl_prim_loader">erl_prim_loader(3)</seealso>,
- <seealso marker="kernel:heart">heart(3)</seealso></p>
+ <title>See Also</title>
+ <p><seealso marker="erl_prim_loader"><c>erl_prim_loader(3)</c></seealso>,
+ <seealso marker="kernel:heart"><c>heart(3)</c></seealso></p>
</section>
</erlref>
diff --git a/erts/doc/src/introduction.xml b/erts/doc/src/introduction.xml
new file mode 100644
index 0000000000..790e24f9f3
--- /dev/null
+++ b/erts/doc/src/introduction.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2012</year><year>2016</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ </legalnotice>
+
+ <title>Introduction</title>
+ <prepared></prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date>2016-05-22</date>
+ <rev>PA1</rev>
+ <file>introduction.xml</file>
+ </header>
+ <section>
+ <title>Scope</title>
+ <p>The Erlang Runtime System Application, ERTS, contains
+ functionality necessary to run the Erlang system.</p>
+ <note>
+ <p>By default, <c><![CDATA[ERTS]]></c> is only guaranteed to be
+ compatible with other Erlang/OTP components from the same release as
+ <c><![CDATA[ERTS]]></c> itself.</p>
+ <p>For information on how to communicate with Erlang/OTP components
+ from earlier releases, see the documentation of system flag
+ <seealso marker="erl#compat_rel"><c>+R</c></seealso> in <c>erl(1)</c>.
+ </p>
+ </note>
+ </section>
+
+ <section>
+ <title>Prerequisites</title>
+ <p>It is assumed that the reader is familiar with the Erlang programming
+ language.</p>
+ </section>
+</chapter>
+
diff --git a/erts/doc/src/match_spec.xml b/erts/doc/src/match_spec.xml
index 08dad8cc10..2a14f1e47b 100644
--- a/erts/doc/src/match_spec.xml
+++ b/erts/doc/src/match_spec.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1999</year><year>2013</year>
+ <year>1999</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -22,7 +22,7 @@
</legalnotice>
- <title>Match specifications in Erlang</title>
+ <title>Match Specifications in Erlang</title>
<prepared>Patrik Nyblom</prepared>
<responsible></responsible>
<docno></docno>
@@ -32,49 +32,54 @@
<rev>PA1</rev>
<file>match_spec.xml</file>
</header>
- <p>A "match specification" (match_spec) is an Erlang term describing a
- small "program" that will try to match something (either the
- parameters to a function as used in the <c><![CDATA[erlang:trace_pattern/2]]></c>
- BIF, or the objects in an ETS table.).
- The match_spec in many ways works like a small function in Erlang, but is
- interpreted/compiled by the Erlang runtime system to something much more
- efficient than calling an Erlang function. The match_spec is also
+ <p>A "match specification" (<c>match_spec</c>) is an Erlang term describing a
+ small "program" that tries to match something. It can be used
+ to either control tracing with
+ <seealso marker="erlang#trace_pattern/3">erlang:trace_pattern/3</seealso>
+ or to search for objects in an ETS table with for example
+ <seealso marker="stdlib:ets#select/2">ets:select/2</seealso>.
+ The match specification in many ways works like a small function in Erlang,
+ but is interpreted/compiled by the Erlang runtime system to something much more
+ efficient than calling an Erlang function. The match specification is also
very limited compared to the expressiveness of real Erlang functions.</p>
- <p>Match specifications are given to the BIF <c><![CDATA[erlang:trace_pattern/2]]></c> to
- execute matching of function arguments as well as to define some actions
- to be taken when the match succeeds (the <c><![CDATA[MatchBody]]></c> part). Match
- specifications can also be used in ETS, to specify objects to be
- returned from an <c><![CDATA[ets:select/2]]></c> call (or other select
- calls). The semantics and restrictions differ slightly when using
- match specifications for tracing and in ETS, the differences are
- defined in a separate paragraph below.</p>
- <p>The most notable difference between a match_spec and an Erlang fun is
- of course the syntax. Match specifications are Erlang terms, not
- Erlang code. A match_spec also has a somewhat strange concept of
- exceptions. An exception (e.g., <c><![CDATA[badarg]]></c>) in the <c><![CDATA[MatchCondition]]></c>
- part,
- which resembles an Erlang guard, will generate immediate failure,
- while an exception in the <c><![CDATA[MatchBody]]></c> part, which resembles the body of an
- Erlang function, is implicitly caught and results in the single atom
- <c><![CDATA['EXIT']]></c>.
- </p>
+ <p>The most notable difference between a match specification and an Erlang
+ fun is the syntax. Match specifications are Erlang terms, not Erlang code.
+ Also, a match specification has a strange concept of exceptions:</p>
+
+ <list type="bulleted">
+ <item>
+ <p>An exception (such as <c><![CDATA[badarg]]></c>) in the
+ <c><![CDATA[MatchCondition]]></c> part, which resembles an Erlang guard,
+ generates immediate failure.</p>
+ </item>
+ <item>
+ <p>An exception in the <c><![CDATA[MatchBody]]></c> part, which resembles
+ the body of an Erlang function, is implicitly caught and results in the
+ single atom <c><![CDATA['EXIT']]></c>.</p>
+ </item>
+ </list>
<section>
<title>Grammar</title>
- <p>A match_spec used in tracing can be described in this <em>informal</em> grammar:</p>
+ <p>A match specification used in tracing can be described in the following
+ <em>informal</em> grammar:</p>
+
<list type="bulleted">
<item>MatchExpression ::= [ MatchFunction, ... ]
</item>
<item>MatchFunction ::= { MatchHead, MatchConditions, MatchBody }
</item>
- <item>MatchHead ::= MatchVariable | <c><![CDATA['_']]></c> | [ MatchHeadPart, ... ]
+ <item>MatchHead ::= MatchVariable | <c><![CDATA['_']]></c> |
+ [ MatchHeadPart, ... ]
+ </item>
+ <item>MatchHeadPart ::= term() | MatchVariable | <c><![CDATA['_']]></c>
</item>
- <item>MatchHeadPart ::= term() | MatchVariable | <c><![CDATA['_']]></c></item>
<item>MatchVariable ::= '$&lt;number&gt;'
</item>
- <item>MatchConditions ::= [ MatchCondition, ...] | <c><![CDATA[[]]]></c></item>
- <item>MatchCondition ::= { GuardFunction } |
- { GuardFunction, ConditionExpression, ... }
+ <item>MatchConditions ::= [ MatchCondition, ...] | <c><![CDATA[[]]]></c>
+ </item>
+ <item>MatchCondition ::= { GuardFunction } | { GuardFunction,
+ ConditionExpression, ... }
</item>
<item>BoolFunction ::= <c><![CDATA[is_atom]]></c> |
<c><![CDATA[is_float]]></c> | <c><![CDATA[is_integer]]></c> |
@@ -85,58 +90,77 @@
<c><![CDATA[is_function]]></c> | <c><![CDATA[is_record]]></c> |
<c><![CDATA[is_seq_trace]]></c> | <c><![CDATA['and']]></c> |
<c><![CDATA['or']]></c> | <c><![CDATA['not']]></c> |
- <c><![CDATA['xor']]></c> | <c><![CDATA[andalso]]></c> |
- <c><![CDATA[orelse]]></c></item>
+ <c><![CDATA['xor']]></c> | <c><![CDATA['andalso']]></c> |
+ <c><![CDATA['orelse']]></c>
+ </item>
<item>ConditionExpression ::= ExprMatchVariable | { GuardFunction } |
- { GuardFunction, ConditionExpression, ... } | TermConstruct
+ { GuardFunction, ConditionExpression, ... } | TermConstruct
+ </item>
+ <item>ExprMatchVariable ::= MatchVariable (bound in the MatchHead) |
+ <c><![CDATA['$_']]></c> | <c><![CDATA['$$']]></c>
</item>
- <item>ExprMatchVariable ::= MatchVariable (bound in the MatchHead) |
- <c><![CDATA['$_']]></c> | <c><![CDATA['$$']]></c></item>
<item>TermConstruct = {{}} | {{ ConditionExpression, ... }} |
<c><![CDATA[[]]]></c> | [ConditionExpression, ...] |
<c><![CDATA[#{}]]></c> | #{term() => ConditionExpression, ...} |
- NonCompositeTerm | Constant</item>
- <item>NonCompositeTerm ::= term() (not list or tuple or map)</item>
+ NonCompositeTerm | Constant
+ </item>
+ <item>NonCompositeTerm ::= term() (not list or tuple or map)
+ </item>
<item>Constant ::= {<c><![CDATA[const]]></c>, term()}
</item>
<item>GuardFunction ::= BoolFunction | <c><![CDATA[abs]]></c> |
- <c><![CDATA[element]]></c> | <c><![CDATA[hd]]></c> | <c><![CDATA[length]]></c> | <c><![CDATA[node]]></c> |
- <c><![CDATA[round]]></c> | <c><![CDATA[size]]></c> | <c><![CDATA[tl]]></c> | <c><![CDATA[trunc]]></c> |
- <c><![CDATA['+']]></c> | <c><![CDATA['-']]></c> | <c><![CDATA['*']]></c> | <c><![CDATA['div']]></c> |
- <c><![CDATA['rem']]></c> | <c><![CDATA['band']]></c> | <c><![CDATA['bor']]></c> | <c><![CDATA['bxor']]></c> |
- <c><![CDATA['bnot']]></c> | <c><![CDATA['bsl']]></c> | <c><![CDATA['bsr']]></c> | <c><![CDATA['>']]></c> |
- <c><![CDATA['>=']]></c> | <c><![CDATA['<']]></c> | <c><![CDATA['=<']]></c> | <c><![CDATA['=:=']]></c> |
- <c><![CDATA['==']]></c> | <c><![CDATA['=/=']]></c> | <c><![CDATA['/=']]></c> | <c><![CDATA[self]]></c> |
- <c><![CDATA[get_tcw]]></c></item>
+ <c><![CDATA[element]]></c> | <c><![CDATA[hd]]></c> |
+ <c><![CDATA[length]]></c> | <c><![CDATA[node]]></c> |
+ <c><![CDATA[round]]></c> | <c><![CDATA[size]]></c> |
+ <c><![CDATA[tl]]></c> | <c><![CDATA[trunc]]></c> |
+ <c><![CDATA['+']]></c> | <c><![CDATA['-']]></c> |
+ <c><![CDATA['*']]></c> | <c><![CDATA['div']]></c> |
+ <c><![CDATA['rem']]></c> | <c><![CDATA['band']]></c> |
+ <c><![CDATA['bor']]></c> | <c><![CDATA['bxor']]></c> |
+ <c><![CDATA['bnot']]></c> | <c><![CDATA['bsl']]></c> |
+ <c><![CDATA['bsr']]></c> | <c><![CDATA['>']]></c> |
+ <c><![CDATA['>=']]></c> | <c><![CDATA['<']]></c> |
+ <c><![CDATA['=<']]></c> | <c><![CDATA['=:=']]></c> |
+ <c><![CDATA['==']]></c> | <c><![CDATA['=/=']]></c> |
+ <c><![CDATA['/=']]></c> | <c><![CDATA[self]]></c> |
+ <c><![CDATA[get_tcw]]></c>
+ </item>
<item>MatchBody ::= [ ActionTerm ]
</item>
<item>ActionTerm ::= ConditionExpression | ActionCall
</item>
- <item>ActionCall ::= {ActionFunction} |
- {ActionFunction, ActionTerm, ...}
+ <item>ActionCall ::= {ActionFunction} | {ActionFunction, ActionTerm, ...}
</item>
<item>ActionFunction ::= <c><![CDATA[set_seq_token]]></c> |
- <c><![CDATA[get_seq_token]]></c> | <c><![CDATA[message]]></c> |
- <c><![CDATA[return_trace]]></c> | <c><![CDATA[exception_trace]]></c> | <c><![CDATA[process_dump]]></c> |
- <c><![CDATA[enable_trace]]></c> | <c><![CDATA[disable_trace]]></c> | <c><![CDATA[trace]]></c> |
- <c><![CDATA[display]]></c> | <c><![CDATA[caller]]></c> | <c><![CDATA[set_tcw]]></c> |
- <c><![CDATA[silent]]></c></item>
+ <c><![CDATA[get_seq_token]]></c> | <c><![CDATA[message]]></c> |
+ <c><![CDATA[return_trace]]></c> | <c><![CDATA[exception_trace]]></c> |
+ <c><![CDATA[process_dump]]></c> | <c><![CDATA[enable_trace]]></c> |
+ <c><![CDATA[disable_trace]]></c> | <c><![CDATA[trace]]></c> |
+ <c><![CDATA[display]]></c> | <c><![CDATA[caller]]></c> |
+ <c><![CDATA[set_tcw]]></c> | <c><![CDATA[silent]]></c>
+ </item>
</list>
- <p>A match_spec used in ets can be described in this <em>informal</em> grammar:</p>
+ <p>A match specification used in
+ <seealso marker="stdlib:ets"><c>ets(3)</c></seealso>
+ can be described in the following <em>informal</em> grammar:</p>
+
<list type="bulleted">
<item>MatchExpression ::= [ MatchFunction, ... ]
</item>
<item>MatchFunction ::= { MatchHead, MatchConditions, MatchBody }
</item>
- <item>MatchHead ::= MatchVariable | <c><![CDATA['_']]></c> | { MatchHeadPart, ... }
+ <item>MatchHead ::= MatchVariable | <c><![CDATA['_']]></c> |
+ { MatchHeadPart, ... }
+ </item>
+ <item>MatchHeadPart ::= term() | MatchVariable | <c><![CDATA['_']]></c>
</item>
- <item>MatchHeadPart ::= term() | MatchVariable | <c><![CDATA['_']]></c></item>
<item>MatchVariable ::= '$&lt;number&gt;'
</item>
- <item>MatchConditions ::= [ MatchCondition, ...] | <c><![CDATA[[]]]></c></item>
+ <item>MatchConditions ::= [ MatchCondition, ...] | <c><![CDATA[[]]]></c>
+ </item>
<item>MatchCondition ::= { GuardFunction } |
- { GuardFunction, ConditionExpression, ... }
+ { GuardFunction, ConditionExpression, ... }
</item>
<item>BoolFunction ::= <c><![CDATA[is_atom]]></c> |
<c><![CDATA[is_float]]></c> | <c><![CDATA[is_integer]]></c> |
@@ -147,464 +171,688 @@
<c><![CDATA[is_function]]></c> | <c><![CDATA[is_record]]></c> |
<c><![CDATA[is_seq_trace]]></c> | <c><![CDATA['and']]></c> |
<c><![CDATA['or']]></c> | <c><![CDATA['not']]></c> |
- <c><![CDATA['xor']]></c> | <c><![CDATA[andalso]]></c> |
- <c><![CDATA[orelse]]></c></item>
+ <c><![CDATA['xor']]></c> | <c><![CDATA['andalso']]></c> |
+ <c><![CDATA['orelse']]></c>
+ </item>
<item>ConditionExpression ::= ExprMatchVariable | { GuardFunction } |
- { GuardFunction, ConditionExpression, ... } | TermConstruct
+ { GuardFunction, ConditionExpression, ... } | TermConstruct
</item>
<item>ExprMatchVariable ::= MatchVariable (bound in the MatchHead) |
- <c><![CDATA['$_']]></c> | <c><![CDATA['$$']]></c></item>
+ <c><![CDATA['$_']]></c> | <c><![CDATA['$$']]></c>
+ </item>
<item>TermConstruct = {{}} | {{ ConditionExpression, ... }} |
<c><![CDATA[[]]]></c> | [ConditionExpression, ...] | #{} |
- #{term() => ConditionExpression, ...} | NonCompositeTerm |
- Constant</item>
- <item>NonCompositeTerm ::= term() (not list or tuple or map)</item>
+ #{term() => ConditionExpression, ...} | NonCompositeTerm | Constant
+ </item>
+ <item>NonCompositeTerm ::= term() (not list or tuple or map)
+ </item>
<item>Constant ::= {<c><![CDATA[const]]></c>, term()}
</item>
<item>GuardFunction ::= BoolFunction | <c><![CDATA[abs]]></c> |
- <c><![CDATA[element]]></c> | <c><![CDATA[hd]]></c> | <c><![CDATA[length]]></c> | <c><![CDATA[node]]></c> |
- <c><![CDATA[round]]></c> | <c><![CDATA[size]]></c> | <c><![CDATA[tl]]></c> | <c><![CDATA[trunc]]></c> |
- <c><![CDATA['+']]></c> | <c><![CDATA['-']]></c> | <c><![CDATA['*']]></c> | <c><![CDATA['div']]></c> |
- <c><![CDATA['rem']]></c> | <c><![CDATA['band']]></c> | <c><![CDATA['bor']]></c> | <c><![CDATA['bxor']]></c> |
- <c><![CDATA['bnot']]></c> | <c><![CDATA['bsl']]></c> | <c><![CDATA['bsr']]></c> | <c><![CDATA['>']]></c> |
- <c><![CDATA['>=']]></c> | <c><![CDATA['<']]></c> | <c><![CDATA['=<']]></c> | <c><![CDATA['=:=']]></c> |
- <c><![CDATA['==']]></c> | <c><![CDATA['=/=']]></c> | <c><![CDATA['/=']]></c> | <c><![CDATA[self]]></c> |
- <c><![CDATA[get_tcw]]></c></item>
- <item>MatchBody ::= [ ConditionExpression, ... ]</item>
+ <c><![CDATA[element]]></c> | <c><![CDATA[hd]]></c> |
+ <c><![CDATA[length]]></c> | <c><![CDATA[node]]></c> |
+ <c><![CDATA[round]]></c> | <c><![CDATA[size]]></c> |
+ <c><![CDATA[tl]]></c> | <c><![CDATA[trunc]]></c> |
+ <c><![CDATA['+']]></c> | <c><![CDATA['-']]></c> |
+ <c><![CDATA['*']]></c> | <c><![CDATA['div']]></c> |
+ <c><![CDATA['rem']]></c> | <c><![CDATA['band']]></c> |
+ <c><![CDATA['bor']]></c> | <c><![CDATA['bxor']]></c> |
+ <c><![CDATA['bnot']]></c> | <c><![CDATA['bsl']]></c> |
+ <c><![CDATA['bsr']]></c> | <c><![CDATA['>']]></c> |
+ <c><![CDATA['>=']]></c> | <c><![CDATA['<']]></c> |
+ <c><![CDATA['=<']]></c> | <c><![CDATA['=:=']]></c> |
+ <c><![CDATA['==']]></c> | <c><![CDATA['=/=']]></c> |
+ <c><![CDATA['/=']]></c> | <c><![CDATA[self]]></c> |
+ <c><![CDATA[get_tcw]]></c>
+ </item>
+ <item>MatchBody ::= [ ConditionExpression, ... ]
+ </item>
</list>
</section>
<section>
- <title>Function descriptions</title>
-
+ <title>Function Descriptions</title>
<section>
- <title>Functions allowed in all types of match specifications</title>
- <p>The different functions allowed in <c><![CDATA[match_spec]]></c> work like this:
- </p>
- <p><em>is_atom, is_float, is_integer, is_list, is_number, is_pid, is_port,
- is_reference, is_tuple, is_map, is_binary, is_function:</em> Like the
- corresponding guard tests in Erlang, return <c><![CDATA[true]]></c> or
- <c><![CDATA[false]]></c>.</p>
- <p><em>is_record: </em>Takes an additional parameter, which SHALL
- be the result of <c><![CDATA[record_info(size, <record_type>)]]></c>,
- like in <c><![CDATA[{is_record, '$1', rectype, record_info(size, rectype)}]]></c>.
- </p>
- <p><em>'not': </em>Negates its single argument (anything other
- than <c><![CDATA[false]]></c> gives <c><![CDATA[false]]></c>).
- </p>
- <p><em>'and': </em>Returns <c><![CDATA[true]]></c> if all its arguments
- (variable length argument list) evaluate to <c><![CDATA[true]]></c>, else
- <c><![CDATA[false]]></c>. Evaluation order is undefined.
- </p>
- <p><em>'or': </em>Returns <c><![CDATA[true]]></c> if any of its arguments
- evaluates to <c><![CDATA[true]]></c>. Variable length argument
- list. Evaluation order is undefined.
- </p>
- <p><em>andalso: </em>Like <c><![CDATA['and']]></c>, but quits evaluating its
- arguments as soon as one argument evaluates to something else
- than true. Arguments are evaluated left to right.
- </p>
- <p><em>orelse: </em>Like <c><![CDATA['or']]></c>, but quits evaluating as soon
- as one of its arguments evaluates to <c><![CDATA[true]]></c>. Arguments are
- evaluated left to right.
- </p>
- <p><em>'xor': </em>Only two arguments, of which one has to be true
- and the other false to return <c><![CDATA[true]]></c>; otherwise
- <c><![CDATA['xor']]></c> returns false.
- </p>
- <p><em>abs, element, hd, length, node, round, size, tl, trunc, '+', '-', '*', 'div', 'rem', 'band', 'bor', 'bxor', 'bnot', 'bsl', 'bsr', '>', '>=', '&lt;', '=&lt;', '=:=', '==', '=/=', '/=', self: </em>Work as the corresponding Erlang bif's (or
- operators). In case of bad arguments, the result depends on
- the context. In the <c><![CDATA[MatchConditions]]></c> part of the
- expression, the test fails immediately (like in an Erlang
- guard), but in the <c><![CDATA[MatchBody]]></c>, exceptions are implicitly
- caught and the call results in the atom <c><![CDATA['EXIT']]></c>.</p>
+ <title>Functions Allowed in All Types of Match Specifications</title>
+ <p>The functions allowed in <c><![CDATA[match_spec]]></c> work as
+ follows:</p>
+
+ <taglist>
+ <tag><c>is_atom</c>, <c>is_float</c>, <c>is_integer</c>, <c>is_list</c>,
+ <c>is_number</c>, <c>is_pid</c>, <c>is_port</c>, <c>is_reference</c>,
+ <c>is_tuple</c>, <c>is_map</c>, <c>is_binary</c>, <c>is_function</c>
+ </tag>
+ <item>
+ <p>Same as the corresponding guard tests in Erlang, return
+ <c><![CDATA[true]]></c> or <c><![CDATA[false]]></c>.</p>
+ </item>
+ <tag><c>is_record</c></tag>
+ <item>
+ <p>Takes an additional parameter, which <em>must</em> be the result
+ of <c><![CDATA[record_info(size, <record_type>)]]></c>, like in
+ <c><![CDATA[{is_record, '$1', rectype, record_info(size,
+ rectype)}]]></c>.</p>
+ </item>
+ <tag><c>'not'</c></tag>
+ <item>
+ <p>Negates its single argument (anything other
+ than <c><![CDATA[false]]></c> gives <c><![CDATA[false]]></c>).</p>
+ </item>
+ <tag><c>'and'</c></tag>
+ <item>
+ <p>Returns <c><![CDATA[true]]></c> if all its arguments (variable
+ length argument list) evaluate to <c><![CDATA[true]]></c>, otherwise
+ <c><![CDATA[false]]></c>. Evaluation order is undefined.</p>
+ </item>
+ <tag><c>'or'</c></tag>
+ <item>
+ <p>Returns <c><![CDATA[true]]></c> if any of its arguments
+ evaluates to <c><![CDATA[true]]></c>. Variable length argument
+ list. Evaluation order is undefined.</p>
+ </item>
+ <tag><c>'andalso'</c></tag>
+ <item>
+ <p>Works as <c><![CDATA['and']]></c>, but quits evaluating its
+ arguments when one argument evaluates to something else
+ than <c>true</c>. Arguments are evaluated left to right.</p>
+ </item>
+ <tag><c>'orelse'</c></tag>
+ <item>
+ <p>Works as <c><![CDATA['or']]></c>, but quits evaluating as soon
+ as one of its arguments evaluates to <c><![CDATA[true]]></c>.
+ Arguments are evaluated left to right.</p>
+ </item>
+ <tag><c>'xor'</c></tag>
+ <item>
+ <p>Only two arguments, of which one must be <c>true</c> and the
+ other <c>false</c> to return <c><![CDATA[true]]></c>; otherwise
+ <c><![CDATA['xor']]></c> returns false.</p>
+ </item>
+ <tag><c>abs</c>, <c>element</c>, <c>hd</c>, <c>length</c>, <c>node</c>,
+ <c>round</c>, <c>size</c>, <c>tl</c>, <c>trunc</c>, <c>'+'</c>,
+ <c>'-'</c>, <c>'*'</c>, <c>'div'</c>, <c>'rem'</c>, <c>'band'</c>,
+ <c>'bor'</c>, <c>'bxor'</c>, <c>'bnot'</c>, <c>'bsl'</c>,
+ <c>'bsr'</c>, <c>'>'</c>, <c>'>='</c>, <c>'&lt;'</c>, <c>'=&lt;'</c>,
+ <c>'=:='</c>, <c>'=='</c>, <c>'=/='</c>, <c>'/='</c>,
+ <c>self</c></tag>
+ <item>
+ <p>Same as the corresponding Erlang BIFs (or operators). In case of
+ bad arguments, the result depends on the context. In the
+ <c><![CDATA[MatchConditions]]></c> part of the expression, the test
+ fails immediately (like in an Erlang guard). In the
+ <c><![CDATA[MatchBody]]></c> part, exceptions are implicitly caught
+ and the call results in the atom <c><![CDATA['EXIT']]></c>.</p>
+ </item>
+ </taglist>
</section>
<section>
- <title>Functions allowed only for tracing</title>
- <p><em>is_seq_trace: </em>Returns <c><![CDATA[true]]></c> if a sequential
- trace token is set for the current process, otherwise <c><![CDATA[false]]></c>.
- </p>
- <p><em>set_seq_token:</em> Works like
- <c><![CDATA[seq_trace:set_token/2]]></c>, but returns <c><![CDATA[true]]></c> on success
- and <c><![CDATA['EXIT']]></c> on error or bad argument. Only allowed in the
- <c><![CDATA[MatchBody]]></c> part and only allowed when tracing.
- </p>
- <p><em>get_seq_token:</em> Works just like
- <c><![CDATA[seq_trace:get_token/0]]></c>, and is only allowed in the
- <c><![CDATA[MatchBody]]></c> part when tracing.
- </p>
- <p><em>message:</em> Sets an additional message appended to the
- trace message sent. One can only set one additional message in
- the body; subsequent calls will replace the appended message. As
- a special case, <c><![CDATA[{message, false}]]></c> disables sending of
- trace messages ('call' and 'return_to')
- for this function call, just like if the match_spec had not matched,
- which can be useful if only the side effects of
- the <c><![CDATA[MatchBody]]></c> are desired.
- Another special case is <c><![CDATA[{message, true}]]></c> which
- sets the default behavior, as if the function had no match_spec,
- trace message is sent with no extra
- information (if no other calls to <c><![CDATA[message]]></c> are placed
- before <c><![CDATA[{message, true}]]></c>, it is in fact a "noop").
- </p>
- <p>Takes one argument, the message. Returns <c><![CDATA[true]]></c> and can
- only be used in the <c><![CDATA[MatchBody]]></c> part and when tracing.
- </p>
- <p><em>return_trace:</em> Causes a <c><![CDATA[return_from]]></c> trace
- message to be sent upon return from the current function.
- Takes no arguments, returns <c><![CDATA[true]]></c> and can only be used
- in the <c><![CDATA[MatchBody]]></c> part when tracing.
- If the process trace flag <c><![CDATA[silent]]></c>
- is active the <c><![CDATA[return_from]]></c> trace message is inhibited.
- </p>
- <p>NOTE! If the traced function is tail recursive, this match
- spec function destroys that property.
- Hence, if a match spec executing this function is used on a
- perpetual server process, it may only be active for a limited
- time, or the emulator will eventually use all memory in the host
- machine and crash. If this match_spec function is inhibited
- using the <c><![CDATA[silent]]></c> process trace flag
- tail recursiveness still remains.
- </p>
- <p><em>exception_trace:</em> Same as <em>return_trace</em>,
- plus; if the traced function exits due to an exception,
- an <c><![CDATA[exception_from]]></c> trace message is generated,
- whether the exception is caught or not.
- </p>
- <p><em>process_dump:</em> Returns some textual information about
- the current process as a binary. Takes no arguments and is only
- allowed in the <c><![CDATA[MatchBody]]></c> part when tracing.
- </p>
- <p><em>enable_trace:</em> With one parameter this function turns
- on tracing like the Erlang call <c><![CDATA[erlang:trace(self(), true, [P2])]]></c>, where <c><![CDATA[P2]]></c> is the parameter to
- <c><![CDATA[enable_trace]]></c>. With two parameters, the first parameter
- should be either a process identifier or the registered name of
- a process. In this case tracing is turned on for the designated
- process in the same way as in the Erlang call <c><![CDATA[erlang:trace(P1, true, [P2])]]></c>, where P1 is the first and P2 is the second
- argument. The process <c><![CDATA[P1]]></c> gets its trace messages sent to the same
- tracer as the process executing the statement uses. <c><![CDATA[P1]]></c>
- can <em>not</em> be one of the atoms <c><![CDATA[all]]></c>, <c><![CDATA[new]]></c> or
- <c><![CDATA[existing]]></c> (unless, of course, they are registered names).
- <c><![CDATA[P2]]></c> can <em>not</em> be <c><![CDATA[cpu_timestamp]]></c> nor
- <c><![CDATA[{tracer,_}]]></c>.
- Returns <c><![CDATA[true]]></c> and may only be used in
- the <c><![CDATA[MatchBody]]></c> part when tracing.
- </p>
- <p><em>disable_trace:</em> With one parameter this function
- disables tracing like the Erlang call <c><![CDATA[erlang:trace(self(), false, [P2])]]></c>, where <c><![CDATA[P2]]></c> is the parameter to
- <c><![CDATA[disable_trace]]></c>. With two parameters it works like the
- Erlang call <c><![CDATA[erlang:trace(P1, false, [P2])]]></c>, where P1 can
- be either a process identifier or a registered name and is given
- as the first argument to the match_spec function.
- <c><![CDATA[P2]]></c> can <em>not</em> be <c><![CDATA[cpu_timestamp]]></c> nor
- <c><![CDATA[{tracer,_}]]></c>. Returns
- <c><![CDATA[true]]></c> and may only be used in the <c><![CDATA[MatchBody]]></c> part
- when tracing.
- </p>
- <p><em>trace:</em> With two parameters this function takes a list
- of trace flags to disable as first parameter and a list
- of trace flags to enable as second parameter. Logically, the
- disable list is applied first, but effectively all changes
- are applied atomically. The trace flags
- are the same as for <c><![CDATA[erlang:trace/3]]></c> not including
- <c><![CDATA[cpu_timestamp]]></c> but including <c><![CDATA[{tracer,_}]]></c>. If a
- tracer is specified in both lists, the tracer in the
- enable list takes precedence. If no tracer is specified the
- same tracer as the process executing the match spec is
- used. With three parameters to this function the first is
- either a process identifier or the registered name of a
- process to set trace flags on, the second is the disable
- list, and the third is the enable list. Returns
- <c><![CDATA[true]]></c> if any trace property was changed for the
- trace target process or <c><![CDATA[false]]></c> if not. It may only
- be used in the <c><![CDATA[MatchBody]]></c> part when tracing.
- </p>
- <p><em>caller:</em>
- Returns the calling function as a tuple {Module,
- Function, Arity} or the atom <c><![CDATA[undefined]]></c> if the calling
- function cannot be determined. May only be used in the
- <c><![CDATA[MatchBody]]></c> part when tracing.
- </p>
- <p>Note that if a "technically built in function" (i.e. a
- function not written in Erlang) is traced, the <c><![CDATA[caller]]></c>
- function will sometimes return the atom <c><![CDATA[undefined]]></c>. The calling
- Erlang function is not available during such calls.
- </p>
- <p><em>display:</em> For debugging purposes only; displays the
- single argument as an Erlang term on stdout, which is seldom
- what is wanted. Returns <c><![CDATA[true]]></c> and may only be used in the
- <c><![CDATA[MatchBody]]></c> part when tracing.
- </p>
- <p> <marker id="get_tcw"></marker>
-<em>get_tcw:</em>
- Takes no argument and returns the value of the node's trace
- control word. The same is done by
- <c><![CDATA[erlang:system_info(trace_control_word)]]></c>.
- </p>
- <p>The trace control word is a 32-bit unsigned integer intended for
- generic trace control. The trace control word can be tested and
- set both from within trace match specifications and with BIFs.
- This call is only allowed when tracing.
- </p>
- <p> <marker id="set_tcw"></marker>
-<em>set_tcw:</em>
- Takes one unsigned integer argument, sets the value of
- the node's trace control word to the value of the argument
- and returns the previous value. The same is done by
- <c><![CDATA[erlang:system_flag(trace_control_word, Value)]]></c>. It is only
- allowed to use <c><![CDATA[set_tcw]]></c> in the <c><![CDATA[MatchBody]]></c> part
- when tracing.
- </p>
- <p><em>silent:</em>
- Takes one argument. If the argument is <c><![CDATA[true]]></c>, the call
- trace message mode for the current process is set to silent
- for this call and all subsequent, i.e call trace messages
- are inhibited even if <c><![CDATA[{message, true}]]></c> is called in the
- <c><![CDATA[MatchBody]]></c> part for a traced function.
- </p>
- <p>This mode can also be activated with the <c><![CDATA[silent]]></c> flag
- to <c><![CDATA[erlang:trace/3]]></c>.
- </p>
- <p>If the argument is <c><![CDATA[false]]></c>, the call trace message mode
- for the current process is set to normal (non-silent) for
- this call and all subsequent.
- </p>
- <p>If the argument is neither <c><![CDATA[true]]></c> nor <c><![CDATA[false]]></c>,
- the call trace message mode is unaffected.</p>
+ <title>Functions Allowed Only for Tracing</title>
+ <p>The functions allowed only for tracing work as follows:</p>
+
+ <taglist>
+ <tag><c>is_seq_trace</c></tag>
+ <item>
+ <p>Returns <c><![CDATA[true]]></c> if a sequential trace token is set
+ for the current process, otherwise <c><![CDATA[false]]></c>.</p>
+ </item>
+ <tag><c>set_seq_token</c></tag>
+ <item>
+ <p>Works as <c><![CDATA[seq_trace:set_token/2]]></c>, but returns
+ <c><![CDATA[true]]></c> on success, and <c><![CDATA['EXIT']]></c>
+ on error or bad argument. Only allowed in the
+ <c><![CDATA[MatchBody]]></c> part and only allowed when tracing.</p>
+ </item>
+ <tag><c>get_seq_token</c></tag>
+ <item>
+ <p>Same as <c><![CDATA[seq_trace:get_token/0]]></c> and only
+ allowed in the <c><![CDATA[MatchBody]]></c> part when tracing.</p>
+ </item>
+ <tag><c>message</c></tag>
+ <item>
+ <p>Sets an additional message appended to the
+ trace message sent. One can only set one additional message in
+ the body. Later calls replace the appended message.</p>
+ <p>As a special case, <c><![CDATA[{message, false}]]></c> disables
+ sending of trace messages ('call' and 'return_to') for this function
+ call, just like if the match specification had not matched.
+ This can be useful if only the side effects of
+ the <c><![CDATA[MatchBody]]></c> part are desired.</p>
+ <p>Another special case is <c><![CDATA[{message, true}]]></c>, which
+ sets the default behavior, as if the function had no match
+ specification; trace message is sent with no extra information
+ (if no other calls to <c><![CDATA[message]]></c> are placed before
+ <c><![CDATA[{message, true}]]></c>, it is in fact a "noop").</p>
+ <p>Takes one argument: the message. Returns <c><![CDATA[true]]></c>
+ and can only be used in the <c><![CDATA[MatchBody]]></c> part and
+ when tracing.</p>
+ </item>
+ <tag><c>return_trace</c></tag>
+ <item>
+ <p>Causes a <c><![CDATA[return_from]]></c> trace message to be sent
+ upon return from the current function. Takes no arguments, returns
+ <c><![CDATA[true]]></c> and can only be used in the
+ <c><![CDATA[MatchBody]]></c> part when tracing.
+ If the process trace flag <c><![CDATA[silent]]></c> is active, the
+ <c><![CDATA[return_from]]></c> trace message is inhibited.</p>
+ <p><em>Warning:</em> If the traced function is tail-recursive, this
+ match specification function destroys that property. Hence, if a
+ match specification executing this function is used on a
+ perpetual server process, it can only be active for a limited
+ period of time, or the emulator will eventually use all memory in
+ the host machine and crash. If this match specification function is
+ inhibited using process trace flag <c><![CDATA[silent]]></c>,
+ tail-recursiveness still remains.</p>
+ </item>
+ <tag><c>exception_trace</c></tag>
+ <item>
+ <p>Works as <c>return_trace</c> plus; if the traced function exits
+ because of an exception,
+ an <c><![CDATA[exception_from]]></c> trace message is generated,
+ regardless of the exception is caught or not.</p>
+ </item>
+ <tag><c>process_dump</c></tag>
+ <item>
+ <p>Returns some textual information about
+ the current process as a binary. Takes no arguments and is only
+ allowed in the <c><![CDATA[MatchBody]]></c> part when tracing.</p>
+ </item>
+ <tag><c>enable_trace</c></tag>
+ <item>
+ <p>With one parameter this function turns on tracing like the Erlang
+ call <c><![CDATA[erlang:trace(self(), true, [P2])]]></c>, where
+ <c><![CDATA[P2]]></c> is the parameter to
+ <c><![CDATA[enable_trace]]></c>.</p>
+ <p>With two parameters, the first parameter is to be either a process
+ identifier or the registered name of a process. In this case tracing
+ is turned on for the designated process in the same way as in the
+ Erlang call <c><![CDATA[erlang:trace(P1, true, [P2])]]></c>, where
+ <c>P1</c> is the first and <c>P2</c> is the second argument. The
+ process <c><![CDATA[P1]]></c> gets its trace messages sent to the
+ same tracer as the process executing the statement uses.
+ <c><![CDATA[P1]]></c> <em>cannot</em> be one of the atoms
+ <c><![CDATA[all]]></c>, <c><![CDATA[new]]></c> or
+ <c><![CDATA[existing]]></c> (unless they are registered names).
+ <c><![CDATA[P2]]></c> <em>cannot</em> be
+ <c><![CDATA[cpu_timestamp]]></c> or <c><![CDATA[tracer]]></c>.</p>
+ <p>Returns <c><![CDATA[true]]></c> and can only be used in
+ the <c><![CDATA[MatchBody]]></c> part when tracing.</p>
+ </item>
+ <tag><c>disable_trace</c></tag>
+ <item>
+ <p>With one parameter this function disables tracing like the Erlang
+ call <c><![CDATA[erlang:trace(self(), false, [P2])]]></c>, where
+ <c><![CDATA[P2]]></c> is the parameter to
+ <c><![CDATA[disable_trace]]></c>.</p>
+ <p>With two parameters this function works as the Erlang call
+ <c><![CDATA[erlang:trace(P1, false, [P2])]]></c>, where <c>P1</c>
+ can be either a process identifier or a registered name and is
+ specified as the first argument to the match specification function.
+ <c><![CDATA[P2]]></c> <em>cannot</em> be
+ <c><![CDATA[cpu_timestamp]]></c> or <c><![CDATA[tracer]]></c>.</p>
+ <p>Returns <c><![CDATA[true]]></c> and can only be used in the
+ <c><![CDATA[MatchBody]]></c> part when tracing.</p>
+ </item>
+ <tag><c>trace</c></tag>
+ <item>
+ <p>With two parameters this function takes a list
+ of trace flags to disable as first parameter and a list
+ of trace flags to enable as second parameter. Logically, the
+ disable list is applied first, but effectively all changes
+ are applied atomically. The trace flags
+ are the same as for <c><![CDATA[erlang:trace/3]]></c>,
+ not including <c><![CDATA[cpu_timestamp]]></c>, but including
+ <c><![CDATA[tracer]]></c>.</p>
+ <p>If a tracer is specified in both lists, the tracer in the
+ enable list takes precedence. If no tracer is specified, the same
+ tracer as the process executing the match specification is used.</p>
+ <p>When using a <seealso marker="erl_tracer">tracer module</seealso>,
+ the module must be loaded before the match specification is
+ executed. If it is not loaded, the match fails.</p>
+ <p>With three parameters to this function, the first is
+ either a process identifier or the registered name of a
+ process to set trace flags on, the second is the disable
+ list, and the third is the enable list.</p>
+ <p>Returns <c><![CDATA[true]]></c> if any trace property was changed
+ for the trace target process, otherwise <c><![CDATA[false]]></c>.
+ Can only be used in the <c><![CDATA[MatchBody]]></c> part when
+ tracing.</p>
+ </item>
+ <tag><c>caller</c></tag>
+ <item>
+ <p>Returns the calling function as a tuple <c>{Module, Function,
+ Arity}</c> or the atom <c><![CDATA[undefined]]></c> if the calling
+ function cannot be determined. Can only be used in the
+ <c><![CDATA[MatchBody]]></c> part when tracing.</p>
+ <p>Notice that if a "technically built in function" (that is, a
+ function not written in Erlang) is traced, the
+ <c><![CDATA[caller]]></c> function sometimes returns the atom
+ <c><![CDATA[undefined]]></c>. The calling
+ Erlang function is not available during such calls.</p>
+ </item>
+ <tag><c>display</c></tag>
+ <item>
+ <p>For debugging purposes only. Displays the single argument as an
+ Erlang term on <c>stdout</c>, which is seldom what is wanted.
+ Returns <c><![CDATA[true]]></c> and can only be used in the
+ <c><![CDATA[MatchBody]]></c> part when tracing.</p>
+ </item>
+ <tag><marker id="get_tcw"/><c>get_tcw</c></tag>
+ <item>
+ <p>Takes no argument and returns the value of the node's trace
+ control word. The same is done by
+ <c><![CDATA[erlang:system_info(trace_control_word)]]></c>.</p>
+ <p>The trace control word is a 32-bit unsigned integer intended for
+ generic trace control. The trace control word can be tested and
+ set both from within trace match specifications and with BIFs.
+ This call is only allowed when tracing.</p>
+ </item>
+ <tag><marker id="set_tcw"/><c>set_tcw</c></tag>
+ <item>
+ <p>Takes one unsigned integer argument, sets the value of
+ the node's trace control word to the value of the argument,
+ and returns the previous value. The same is done by
+ <c><![CDATA[erlang:system_flag(trace_control_word, Value)]]></c>.
+ It is only allowed to use <c><![CDATA[set_tcw]]></c> in the
+ <c><![CDATA[MatchBody]]></c> part when tracing.</p>
+ </item>
+ <tag><c>silent</c></tag>
+ <item>
+ <p>Takes one argument. If the argument is <c><![CDATA[true]]></c>,
+ the call trace message mode for the current process is set to
+ silent for this call and all later calls, that is, call trace
+ messages are inhibited even if
+ <c><![CDATA[{message, true}]]></c> is called in the
+ <c><![CDATA[MatchBody]]></c> part for a traced function.</p>
+ <p>This mode can also be activated with flag
+ <c><![CDATA[silent]]></c> to
+ <c><![CDATA[erlang:trace/3]]></c>.</p>
+ <p>If the argument is <c><![CDATA[false]]></c>, the call trace
+ message mode for the current process is set to normal
+ (non-silent) for this call and all later calls.</p>
+ <p>If the argument is not <c><![CDATA[true]]></c> or
+ <c><![CDATA[false]]></c>, the call trace message mode is
+ unaffected.</p>
+ </item>
+ </taglist>
</section>
- <p><em>Note</em> that all "function calls" have to be tuples,
- even if they take no arguments. The value of <c><![CDATA[self]]></c> is
- the atom() <c><![CDATA[self]]></c>, but the value of <c><![CDATA[{self}]]></c> is
- the pid() of the current process.</p>
+
+ <note>
+ <p>All "function calls" must be tuples, even if they take no arguments.
+ The value of <c><![CDATA[self]]></c> is the atom()
+ <c><![CDATA[self]]></c>, but the value of <c><![CDATA[{self}]]></c> is
+ the pid() of the current process.</p>
+ </note>
</section>
<section>
- <title>Variables and literals</title>
- <p>Variables take the form <c><![CDATA['$<number>']]></c> where
- <c><![CDATA[<number>]]></c> is an integer between 0 (zero) and
- 100000000 (1e+8), the behavior if the number is outside these
- limits is <em>undefined</em>. In the <c><![CDATA[MatchHead]]></c> part, the special
- variable <c><![CDATA['_']]></c> matches anything, and never gets bound (like
- <c><![CDATA[_]]></c> in Erlang). In the <c><![CDATA[MatchCondition/MatchBody]]></c>
- parts, no unbound variables are allowed, why <c><![CDATA['_']]></c> is
- interpreted as itself (an atom). Variables can only be bound in
- the <c><![CDATA[MatchHead]]></c> part. In the <c><![CDATA[MatchBody]]></c> and
- <c><![CDATA[MatchCondition]]></c> parts, only variables bound previously may
- be used. As a special case, in the
- <c><![CDATA[MatchCondition/MatchBody]]></c> parts, the variable <c><![CDATA['$_']]></c>
- expands to the whole expression which matched the
- <c><![CDATA[MatchHead]]></c> (i.e., the whole parameter list to the possibly
- traced function or the whole matching object in the ets table)
- and the variable <c><![CDATA['$$']]></c> expands to a list
- of the values of all bound variables in order
- (i.e. <c><![CDATA[['$1','$2', ...]]]></c>).
- </p>
- <p>In the <c><![CDATA[MatchHead]]></c> part, all literals (except the variables
- noted above) are interpreted as is. In the
- <c><![CDATA[MatchCondition/MatchBody]]></c> parts, however, the
- interpretation is in some ways different. Literals in the
- <c><![CDATA[MatchCondition/MatchBody]]></c> can either be written as is,
- which works for all literals except tuples, or by using the
- special form <c><![CDATA[{const, T}]]></c>, where <c><![CDATA[T]]></c> is any Erlang
- term. For tuple literals in the match_spec, one can also use
- double tuple parentheses, i.e., construct them as a tuple of
+ <marker id="match_target"/>
+ <title>Match target</title>
+ <p>Each execution of a match specification is done against
+ a match target term. The format and content of the target term
+ depends on the context in which the match is done. The match
+ target for ETS is always a full table tuple. The match target
+ for call trace is always a list of all function arguments. The
+ match target for event trace depends on the event type, see
+ table below.</p>
+ <table>
+ <row>
+ <cell align="left" valign="middle">Context</cell>
+ <cell align="left" valign="middle">Type</cell>
+ <cell align="left" valign="middle">Match target</cell>
+ <cell align="left" valign="middle">Description</cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">ETS</cell>
+ <cell align="left" valign="middle"></cell>
+ <cell align="left" valign="middle">{Key, Value1, Value2, ...}</cell>
+ <cell align="left" valign="middle">A table object</cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">Trace</cell>
+ <cell align="left" valign="middle">call</cell>
+ <cell align="left" valign="middle">[Arg1, Arg2, ...]</cell>
+ <cell align="left" valign="middle">Function arguments</cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">Trace</cell>
+ <cell align="left" valign="middle">send</cell>
+ <cell align="left" valign="middle">[Receiver, Message]</cell>
+ <cell align="left" valign="middle">Receiving process/port and message term</cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">Trace</cell>
+ <cell align="left" valign="middle">'receive'</cell>
+ <cell align="left" valign="middle">[Node, Sender, Message]</cell>
+ <cell align="left" valign="middle">Sending node, process/port and message term</cell>
+ </row>
+ <tcaption>Match target depending on context</tcaption>
+ </table>
+ </section>
+
+ <section>
+ <title>Variables and Literals</title>
+ <p>Variables take the form <c><![CDATA['$<number>']]></c>, where
+ <c><![CDATA[<number>]]></c> is an integer between 0 and
+ 100,000,000 (1e+8). The behavior if the number is outside these limits
+ is <em>undefined</em>. In the <c><![CDATA[MatchHead]]></c> part, the
+ special variable <c><![CDATA['_']]></c> matches anything, and never gets
+ bound (like <c><![CDATA[_]]></c> in Erlang).</p>
+
+ <list type="bulleted">
+ <item>
+ <p>In the <c><![CDATA[MatchCondition/MatchBody]]></c> parts,
+ no unbound variables are allowed, so <c><![CDATA['_']]></c> is
+ interpreted as itself (an atom). Variables can only be bound in the
+ <c><![CDATA[MatchHead]]></c> part.</p>
+ </item>
+ <item>
+ <p>In the <c><![CDATA[MatchBody]]></c> and
+ <c><![CDATA[MatchCondition]]></c> parts, only variables bound
+ previously can be used.</p>
+ </item>
+ <item>
+ <p>As a special case, the following apply in the
+ <c><![CDATA[MatchCondition/MatchBody]]></c> parts:</p>
+ <list type="bulleted">
+ <item>
+ <p>The variable <c><![CDATA['$_']]></c> expands to the whole
+ <seealso marker="#match_target">match target</seealso> term.
+ </p>
+ </item>
+ <item>
+ <p>The variable <c><![CDATA['$$']]></c> expands to a list of the
+ values of all bound variables in order (that is,
+ <c><![CDATA[['$1','$2', ...]]]></c>).</p>
+ </item>
+ </list>
+ </item>
+ </list>
+
+ <p>In the <c><![CDATA[MatchHead]]></c> part, all literals (except the
+ variables above) are interpreted "as is".</p>
+
+ <p>In the <c><![CDATA[MatchCondition/MatchBody]]></c> parts, the
+ interpretation is in some ways different. Literals in these parts
+ can either be written "as is", which works for all literals except
+ tuples, or by using the special form <c><![CDATA[{const, T}]]></c>,
+ where <c><![CDATA[T]]></c> is any Erlang term.</p>
+
+ <p>For tuple literals in the match specification, double tuple parentheses
+ can also be used, that is, construct them as a tuple of
arity one containing a single tuple, which is the one to be
constructed. The "double tuple parenthesis" syntax is useful to
construct tuples from already bound variables, like in
- <c><![CDATA[{{'$1', [a,b,'$2']}}]]></c>. Some examples may be needed:
- </p>
+ <c><![CDATA[{{'$1', [a,b,'$2']}}]]></c>. Examples:</p>
+
<table>
<row>
- <cell align="left" valign="middle">Expression </cell>
- <cell align="left" valign="middle">Variable bindings </cell>
- <cell align="left" valign="middle">Result </cell>
+ <cell align="left" valign="middle"><em>Expression</em></cell>
+ <cell align="left" valign="middle"><em>Variable Bindings</em></cell>
+ <cell align="left" valign="middle"><em>Result</em></cell>
</row>
<row>
- <cell align="left" valign="middle">{{'$1','$2'}} </cell>
+ <cell align="left" valign="middle">{{'$1','$2'}}</cell>
<cell align="left" valign="middle">'$1' = a, '$2' = b</cell>
<cell align="left" valign="middle">{a,b}</cell>
</row>
<row>
- <cell align="left" valign="middle">{const, {'$1', '$2'}} </cell>
- <cell align="left" valign="middle">doesn't matter</cell>
+ <cell align="left" valign="middle">{const, {'$1', '$2'}}</cell>
+ <cell align="left" valign="middle">Irrelevant</cell>
<cell align="left" valign="middle">{'$1', '$2'}</cell>
</row>
<row>
- <cell align="left" valign="middle">a </cell>
- <cell align="left" valign="middle">doesn't matter </cell>
+ <cell align="left" valign="middle">a</cell>
+ <cell align="left" valign="middle">Irrelevant</cell>
<cell align="left" valign="middle">a</cell>
</row>
<row>
- <cell align="left" valign="middle">'$1' </cell>
- <cell align="left" valign="middle">'$1' = [] </cell>
+ <cell align="left" valign="middle">'$1'</cell>
+ <cell align="left" valign="middle">'$1' = []</cell>
<cell align="left" valign="middle">[]</cell>
</row>
<row>
- <cell align="left" valign="middle">['$1'] </cell>
- <cell align="left" valign="middle">'$1' = [] </cell>
+ <cell align="left" valign="middle">['$1']</cell>
+ <cell align="left" valign="middle">'$1' = []</cell>
<cell align="left" valign="middle">[[]]</cell>
</row>
<row>
- <cell align="left" valign="middle">[{{a}}] </cell>
- <cell align="left" valign="middle">doesn't matter</cell>
+ <cell align="left" valign="middle">[{{a}}]</cell>
+ <cell align="left" valign="middle">Irrelevant</cell>
<cell align="left" valign="middle">[{a}]</cell>
</row>
<row>
- <cell align="left" valign="middle">42 </cell>
- <cell align="left" valign="middle">doesn't matter</cell>
+ <cell align="left" valign="middle">42</cell>
+ <cell align="left" valign="middle">Irrelevant</cell>
<cell align="left" valign="middle">42</cell>
</row>
<row>
- <cell align="left" valign="middle">"hello" </cell>
- <cell align="left" valign="middle">doesn't matter</cell>
+ <cell align="left" valign="middle">"hello"</cell>
+ <cell align="left" valign="middle">Irrelevant</cell>
<cell align="left" valign="middle">"hello"</cell>
</row>
<row>
- <cell align="left" valign="middle">$1 </cell>
- <cell align="left" valign="middle">doesn't matter</cell>
- <cell align="left" valign="middle">49 (the ASCII value for the character '1')</cell>
+ <cell align="left" valign="middle">$1</cell>
+ <cell align="left" valign="middle">Irrelevant</cell>
+ <cell align="left" valign="middle">49 (the ASCII value for
+ character '1')</cell>
</row>
- <tcaption>Literals in the MatchCondition/MatchBody parts of a match_spec</tcaption>
+ <tcaption>Literals in MatchCondition/MatchBody Parts of a Match
+ Specification</tcaption>
</table>
</section>
<section>
- <title>Execution of the match</title>
+ <title>Execution of the Match</title>
<p>The execution of the match expression, when the runtime system
- decides whether a trace message should be sent, goes as follows:
- </p>
- <p>For each tuple in the <c><![CDATA[MatchExpression]]></c> list and while no
- match has succeeded:</p>
- <list type="bulleted">
- <item>Match the <c><![CDATA[MatchHead]]></c> part against the arguments to the
- function,
- binding the <c><![CDATA['$<number>']]></c> variables (much like in
- <c><![CDATA[ets:match/2]]></c>).
- If the <c><![CDATA[MatchHead]]></c> cannot match the arguments, the match fails.
- </item>
- <item>Evaluate each <c><![CDATA[MatchCondition]]></c> (where only
- <c><![CDATA['$<number>']]></c> variables previously bound in the
- <c><![CDATA[MatchHead]]></c> can occur) and expect it to return the atom
- <c><![CDATA[true]]></c>. As soon as a condition does not evaluate to
- <c><![CDATA[true]]></c>, the match fails. If any BIF call generates an
- exception, also fail.
+ decides whether a trace message is to be sent, is as follows:</p>
+
+ <p>For each tuple in the <c><![CDATA[MatchExpression]]></c> list and while
+ no match has succeeded:</p>
+
+ <list type="ordered">
+ <item>
+ <p>Match the <c><![CDATA[MatchHead]]></c> part against the match target
+ term, binding the <c><![CDATA['$<number>']]></c> variables
+ (much like in <c><![CDATA[ets:match/2]]></c>). If the
+ <c><![CDATA[MatchHead]]></c> part cannot match the arguments, the
+ match fails.</p>
</item>
<item>
+ <p>Evaluate each <c><![CDATA[MatchCondition]]></c> (where only
+ <c><![CDATA['$<number>']]></c> variables previously bound in the
+ <c><![CDATA[MatchHead]]></c> part can occur) and expect it to return
+ the atom <c><![CDATA[true]]></c>. When a condition does not evaluate
+ to <c><![CDATA[true]]></c>, the match fails. If any BIF call
+ generates an exception, the match also fails.</p>
+ </item>
+ <item>
+ <p>Two cases can occur:</p>
<list type="bulleted">
- <item><em>If the match_spec is executing when tracing:</em><br></br>
- Evaluate each <c><![CDATA[ActionTerm]]></c> in the same way as the
- <c><![CDATA[MatchConditions]]></c>, but completely ignore the return
- values. Regardless of what happens in this part, the match has
- succeeded.</item>
- <item><em>If the match_spec is executed when selecting objects from an ETS table:</em><br></br>
- Evaluate the expressions in order and return the value of
- the last expression (typically there is only one expression
- in this context)</item>
+ <item>
+ <p>If the match specification is executing when tracing:</p>
+ <p>Evaluate each <c><![CDATA[ActionTerm]]></c> in the same way as
+ the <c><![CDATA[MatchConditions]]></c>, but ignore the return
+ values. Regardless of what happens in this part, the match has
+ succeeded.</p>
+ </item>
+ <item>
+ <p>If the match specification is executed when selecting objects
+ from an ETS table:</p>
+ <p>Evaluate the expressions in order and return the value of
+ the last expression (typically there is only one expression
+ in this context).</p>
+ </item>
</list>
</item>
</list>
</section>
<section>
- <title>Differences between match specifications in ETS and tracing</title>
- <p>ETS match specifications are there to produce a return
- value. Usually the <c><![CDATA[MatchBody]]></c> contains one single
- <c><![CDATA[ConditionExpression]]></c> which defines the return value without having
- any side effects. Calls with side effects are not allowed in the
- ETS context.</p>
+ <marker id="differences_ets_tracing"/>
+ <title>Differences between Match Specifications in ETS and Tracing</title>
+ <p>ETS match specifications produce a return value.
+ Usually the <c><![CDATA[MatchBody]]></c> contains one single
+ <c><![CDATA[ConditionExpression]]></c> that defines the return value
+ without any side effects. Calls with side effects are not allowed in
+ the ETS context.</p>
+
<p>When tracing there is no return value to produce, the
- match specification either matches or doesn't. The effect when the
- expression matches is a trace message rather then a returned
- term. The <c><![CDATA[ActionTerm]]></c>'s are executed as in an imperative
- language, i.e. for their side effects. Functions with side effects
+ match specification either matches or does not. The effect when the
+ expression matches is a trace message rather than a returned
+ term. The <c><![CDATA[ActionTerm]]></c>s are executed as in an imperative
+ language, that is, for their side effects. Functions with side effects
are also allowed when tracing.</p>
- <p>In ETS the match head is a <c><![CDATA[tuple()]]></c> (or a single match
- variable) while it is a list (or a single match variable) when
- tracing.</p>
</section>
<section>
- <title>Examples</title>
- <p>Match an argument list of three where the first and third arguments
+ <title>Tracing Examples</title>
+ <p>Match an argument list of three, where the first and third arguments
are equal:</p>
+
<code type="none"><![CDATA[
[{['$1', '_', '$1'],
[],
[]}]
]]></code>
- <p>Match an argument list of three where the second argument is
- a number greater than three:</p>
+
+ <p>Match an argument list of three, where the second argument is
+ a number &gt; 3:</p>
+
<code type="none"><![CDATA[
[{['_', '$1', '_'],
[{ '>', '$1', 3}],
[]}]
]]></code>
- <p>Match an argument list of three, where the third argument
- is a tuple containing argument one and two <em>or</em> a list
- beginning with argument one and two (i. e. <c><![CDATA[[a,b,[a,b,c]]]]></c> or
- <c><![CDATA[[a,b,{a,b}]]]></c>):
- </p>
+
+ <p>Match an argument list of three, where the third argument is
+ either a tuple containing argument one and two, <em>or</em> a list
+ beginning with argument one and two (that is,
+ <c><![CDATA[[a,b,[a,b,c]]]]></c> or <c><![CDATA[[a,b,{a,b}]]]></c>):</p>
+
<code type="none"><![CDATA[
[{['$1', '$2', '$3'],
- [{orelse,
+ [{'orelse',
{'=:=', '$3', {{'$1','$2'}}},
{'and',
{'=:=', '$1', {hd, '$3'}},
{'=:=', '$2', {hd, {tl, '$3'}}}}}],
[]}]
]]></code>
- <p>The above problem may also be solved like this:</p>
+
+ <p>The above problem can also be solved as follows:</p>
+
<code type="none"><![CDATA[
[{['$1', '$2', {'$1', '$2}], [], []},
{['$1', '$2', ['$1', '$2' | '_']], [], []}]
]]></code>
- <p>Match two arguments where the first is a tuple beginning with
- a list which in turn begins with the second argument times
- two (i. e. [{[4,x],y},2] or [{[8], y, z},4])</p>
+
+ <p>Match two arguments, where the first is a tuple beginning with
+ a list that in turn begins with the second argument times
+ two (that is, <c>[{[4,x],y},2]</c> or <c>[{[8], y, z},4])</c>:</p>
+
<code type="none"><![CDATA[
[{['$1', '$2'],[{'=:=', {'*', 2, '$2'}, {hd, {element, 1, '$1'}}}],
[]}]
]]></code>
+
<p>Match three arguments. When all three are equal and are
- numbers, append the process dump to the trace message, else
- let the trace message be as is, but set the sequential trace
- token label to 4711.</p>
+ numbers, append the process dump to the trace message, otherwise
+ let the trace message be "as is", but set the sequential trace
+ token label to 4711:</p>
+
<code type="none"><![CDATA[
[{['$1', '$1', '$1'],
[{is_number, '$1'}],
[{message, {process_dump}}]},
{'_', [], [{set_seq_token, label, 4711}]}]
]]></code>
- <p>As can be noted above, the parameter list can be matched
- against a single <c><![CDATA[MatchVariable]]></c> or an <c><![CDATA['_']]></c>. To replace the
- whole
- parameter list with a single variable is a special case. In all
- other cases the <c><![CDATA[MatchHead]]></c> has to be a <em>proper</em> list.
- </p>
- <p>Match all objects in an ets table where the first element is
- the atom 'strider' and the tuple arity is 3 and return the whole
- object.</p>
+
+ <p>As can be noted above, the parameter list can be matched against a
+ single <c><![CDATA[MatchVariable]]></c> or an <c><![CDATA['_']]></c>.
+ To replace the whole parameter list with a single variable is a special
+ case. In all other cases the <c><![CDATA[MatchHead]]></c> must be a
+ <em>proper</em> list.</p>
+
+ <p>Generate a trace message only if the trace control word is set to 1:</p>
+
+ <code type="none"><![CDATA[
+[{'_',
+ [{'==',{get_tcw},{const, 1}}],
+ []}]
+ ]]></code>
+
+ <p>Generate a trace message only if there is a <c>seq_trace</c> token:</p>
+
+ <code type="none"><![CDATA[
+[{'_',
+ [{'==',{is_seq_trace},{const, 1}}],
+ []}]
+ ]]></code>
+
+ <p>Remove the <c>'silent'</c> trace flag when the first argument is
+ <c>'verbose'</c>, and add it when it is <c>'silent':</c></p>
+
+ <code type="none"><![CDATA[
+[{'$1',
+ [{'==',{hd, '$1'},verbose}],
+ [{trace, [silent],[]}]},
+ {'$1',
+ [{'==',{hd, '$1'},silent}],
+ [{trace, [],[silent]}]}]
+ ]]></code>
+
+ <p>Add a <c>return_trace</c> message if the function is of arity 3:</p>
+
+ <code type="none"><![CDATA[
+[{'$1',
+ [{'==',{length, '$1'},3}],
+ [{return_trace}]},
+ {'_',[],[]}]
+ ]]></code>
+
+ <p>Generate a trace message only if the function is of arity 3 and the
+ first argument is <c>'trace'</c>:</p>
+
+ <code type="none"><![CDATA[
+[{['trace','$2','$3'],
+ [],
+ []},
+ {'_',[],[]}]
+ ]]></code>
+ </section>
+
+ <section>
+ <title>ETS Examples</title>
+ <p>Match all objects in an ETS table, where the first element is
+ the atom <c>'strider'</c> and the tuple arity is 3, and return the whole
+ object:</p>
+
<code type="none"><![CDATA[
[{{strider,'_','_'},
[],
['$_']}]
]]></code>
- <p>Match all objects in an ets table with arity &gt; 1 and the first
- element is 'gandalf', return element 2.</p>
+
+ <p>Match all objects in an ETS table with arity &gt; 1 and the first
+ element is 'gandalf', and return element 2:</p>
+
<code type="none"><![CDATA[
[{'$1',
[{'==', gandalf, {element, 1, '$1'}},{'>=',{size, '$1'},2}],
[{element,2,'$1'}]}]
]]></code>
- <p>In the above example, if the first element had been the key,
- it's much more efficient to match that key in the <c><![CDATA[MatchHead]]></c>
- part than in the <c><![CDATA[MatchConditions]]></c> part. The search space of
- the tables is restricted with regards to the <c><![CDATA[MatchHead]]></c> so
- that only objects with the matching key are searched.
- </p>
- <p>Match tuples of 3 elements where the second element is either
- 'merry' or 'pippin', return the whole objects.</p>
+
+ <p>In this example, if the first element had been the key, it is
+ much more efficient to match that key in the <c><![CDATA[MatchHead]]></c>
+ part than in the <c><![CDATA[MatchConditions]]></c> part.
+ The search space of the tables is restricted with regards to the
+ <c><![CDATA[MatchHead]]></c> so
+ that only objects with the matching key are searched.</p>
+
+ <p>Match tuples of three elements, where the second element is either
+ <c>'merry'</c> or <c>'pippin'</c>, and return the whole objects:</p>
+
<code type="none"><![CDATA[
[{{'_',merry,'_'},
[],
@@ -613,8 +861,9 @@
[],
['$_']}]
]]></code>
- <p>The function <c><![CDATA[ets:test_ms/2]]></c> can be useful for testing
- complicated ets matches.</p>
+
+ <p>Function <seealso marker="stdlib:ets#test_ms/2"><c>ets:test_ms/2></c></seealso>
+ can be useful for testing complicated ETS matches.</p>
</section>
</chapter>
diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml
index 7ccddf4ff0..02b4de404e 100644
--- a/erts/doc/src/notes.xml
+++ b/erts/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2015</year>
+ <year>2004</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -32,6 +32,1696 @@
<p>This document describes the changes made to the ERTS application.</p>
+<section><title>Erts 8.3.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <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>
+ A code purge operation could under certain circumstances
+ expand the size of hibernated processes.</p>
+ <p>
+ Own Id: OTP-14444 Aux Id: ERIERL-24 </p>
+ </item>
+ <item>
+ <p>
+ Fix so that the ERL_ZZ_SIGTERM_KILL introduced in
+ erts-8.3.4 works.</p>
+ <p>
+ Own Id: OTP-14451</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.3.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Add option to make SIGTERM trigger the OS default
+ behaviour instead of doing a gracefull shutdown. To
+ activate this bahviour the environment variable
+ ERL_ZZ_SIGTERM_KILL should be set to "true". This option
+ only works in OTP 19 as OTP 20 will have a different way
+ to deal with SIGTERM.</p>
+ <p>
+ Own Id: OTP-14418 Aux Id: ERIERL-15 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.3.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <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 release note for OTP-14290 in ERTS version 8.3.1. It
+ was erroneously placed under "Known Bugs and Problems".</p>
+ <p>
+ Own Id: OTP-14363 Aux Id: OTP-14290 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.3.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The <c>+Bi</c> command line argument of <c>erl</c>
+ erroneously caused <c>SIGTERM</c> to be ignored by the VM
+ as well as of all its child processes. This bug was
+ introduced in erts version 8.3.</p>
+ <p>
+ Own Id: OTP-14358 Aux Id: OTP-14085 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.3.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Invoking <c>init:stop/0</c> via the SIGTERM signal, in a
+ non-SMP BEAM, could cause BEAM to terminate with fatal
+ error. This has now been fixed and the BEAM will
+ terminate normally when SIGTERM is received.</p>
+ <p>
+ Own Id: OTP-14290</p>
+ </item>
+ <item>
+ <p>
+ Trying to open a directory with file:read_file/1 on Unix
+ leaked a file descriptor. This bug has now been fixed.</p>
+ <p>
+ Own Id: OTP-14308 Aux Id: ERL-383 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Fixed a number of bugs that caused faulty stack-traces
+ to be generated. The faulty stack traces were generated
+ either when applying the following functions or tracing
+ the following functions:</p> <list>
+ <item><c>erlang:error/1</c></item>
+ <item><c>erlang:error/2</c></item>
+ <item><c>erlang:exit/1</c></item>
+ <item><c>erlang:throw/1</c></item> </list>
+ <p>
+ Own Id: OTP-14055</p>
+ </item>
+ <item>
+ <p>
+ Corrected documentation about memory footprint for maps.</p>
+ <p>
+ Own Id: OTP-14118</p>
+ </item>
+ <item>
+ <p>
+ Fix <c>process_info(Pid, current_stacktrace)</c> to use
+ stack depth limit set by
+ <c>system_flag(backtrace_depth)</c>. The old behavior was
+ a hard coded depth limit of 8.</p>
+ <p>
+ Own Id: OTP-14119 Aux Id: PR-1263 </p>
+ </item>
+ <item>
+ <p>
+ A process calling <seealso
+ marker="erlang#system_flag_multi_scheduling"><c>erlang:system_flag(multi_scheduling,
+ block)</c></seealso> could end up hanging forever in the
+ call.</p>
+ <p>
+ Own Id: OTP-14121</p>
+ </item>
+ <item>
+ <p>Dirty scheduler bug fixes:</p> <list> <item><p>Fixed
+ call time tracing of process being scheduled on dirty
+ scheduler.</p></item> <item><p>GC info from dirty
+ schedulers.</p></item> <item><p>Multi scheduling block
+ with dirty schedulers could crash the runtime
+ system.</p></item> <item><p>Process structures could be
+ removed prematurely.</p></item> <item><p>GC on dirty
+ scheduler could crash the runtime system.</p></item>
+ <item><p>Termination of a process executing on a dirty
+ scheduler could cause a runtime system crash.</p></item>
+ </list>
+ <p>
+ Own Id: OTP-14122</p>
+ </item>
+ <item>
+ <p>
+ Fixed crash that occurred when writing timer data to a
+ crash dump.</p>
+ <p>
+ Own Id: OTP-14133</p>
+ </item>
+ <item>
+ <p>
+ A literal area could be removed while still referred from
+ processes.</p>
+ <p>
+ Own Id: OTP-14134</p>
+ </item>
+ <item>
+ <p>
+ Fixed a bug in the garbage collector that could crash the
+ runtime system.</p>
+ <p>
+ Own Id: OTP-14135</p>
+ </item>
+ <item>
+ <p>
+ Fixed a bug in call-time trace for NIFs which caused
+ tracing to erroneously be started multiple times for one
+ call.</p>
+ <p>
+ Own Id: OTP-14136</p>
+ </item>
+ <item>
+ <p>
+ Remove a debug printout and an unnecessary garbage
+ collection when handling exceptions in hipe compiled
+ code.</p>
+ <p>
+ Own Id: OTP-14153</p>
+ </item>
+ <item>
+ <p>
+ Fix bug in tracing of garbage collection that could cause
+ VM crash. Bug exists since OTP 19.0.</p>
+ <p>
+ Own Id: OTP-14154</p>
+ </item>
+ <item>
+ <p>
+ Fix bug in <c>binary_to_term</c> for binaries created by
+ <c>term_to_binary </c> with option <c>compressed</c>. The
+ bug can cause <c>badarg</c> exception for a valid binary
+ when Erlang VM is linked against a <c>zlib</c> library of
+ version 1.2.9 or newer. Bug exists since OTP 17.0.</p>
+ <p>
+ Own Id: OTP-14159 Aux Id: ERL-340 </p>
+ </item>
+ <item>
+ <p>
+ Fix suspension of schedulers when generating a crashdump.</p>
+ <p>
+ Own Id: OTP-14164</p>
+ </item>
+ <item>
+ <p>NIF resources was not handled in a thread-safe manner
+ in the runtime system without SMP support.</p>
+ <p>As a consequence of this fix, the following driver
+ functions are now thread-safe also in the runtime system
+ without SMP support:</p> <list>
+ <item><p><c>driver_free_binary()</c></p></item>
+ <item><p><c>driver_realloc_binary()</c></p></item>
+ <item><p><c>driver_binary_get_refc()</c></p></item>
+ <item><p><c>driver_binary_inc_refc()</c></p></item>
+ <item><p><c>driver_binary_dec_refc()</c></p></item>
+ </list>
+ <p>
+ Own Id: OTP-14202</p>
+ </item>
+ <item>
+ <p>
+ Fix <c>erlang:round/1</c> for large floating point
+ numbers with an odd absolute value between <c>(1 bsl
+ 52)</c> and <c>(1 bsl 53)</c>. The result was falsely
+ calculated as the next higher even number even though all
+ integer values up to <c>(1 bsl 53)</c> can be represented
+ as floats with full precision.</p>
+ <p>
+ Own Id: OTP-14227</p>
+ </item>
+ <item>
+ <p>
+ Add size of literals to module code size in crash dump
+ and <c>(l)oaded</c> command in break menu like it used to
+ be before OTP-19.0.</p>
+ <p>
+ Own Id: OTP-14228</p>
+ </item>
+ <item>
+ <p>
+ Fix potential bug in <c>enif_send</c> when called without
+ a process context and with argument <c>msg_env</c> as
+ <c>NULL</c>.</p>
+ <p>
+ Own Id: OTP-14229</p>
+ </item>
+ <item>
+ <p>
+ Fix bug where passing an appendable binary to
+ <c>erlang:port_control()</c> could crash the emulator.</p>
+ <p>
+ Own Id: OTP-14231</p>
+ </item>
+ <item>
+ <p>
+ Receive expressions with timeout in the Erlang shell
+ could cause a VM crash.</p>
+ <p>
+ Own Id: OTP-14241 Aux Id: ERL-365 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ A received SIGTERM signal to beam will generate a
+ <c>'stop'</c> message to the <c>init</c> process and
+ terminate the Erlang VM nicely. This is equivalent to
+ calling <c>init:stop/0</c>.</p>
+ <p>
+ Own Id: OTP-14085</p>
+ </item>
+ <item>
+ <p>
+ Workaround for buggy Android implementation of
+ <c>PTHREAD_STACK_MIN</c> causing build of runtime system
+ to crash on undeclared <c>PAGE_SIZE</c>.</p>
+ <p>
+ Own Id: OTP-14165 Aux Id: ERL-319 </p>
+ </item>
+ <item>
+ <p>
+ Add configure option --without-thread-names that removes
+ the naming of individual emulator threads.</p>
+ <p>
+ Own Id: OTP-14234</p>
+ </item>
+ <item>
+ <p>
+ Add warning in documentation of <c>zlib:deflateInit/6</c>
+ about option <c>WindowsBits</c> values 8 and -8.</p>
+ <p>
+ Own Id: OTP-14254 Aux Id: ERL-362 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.2.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix bug in <c>binary_to_term</c> for binaries created by
+ <c>term_to_binary </c> with option <c>compressed</c>. The
+ bug can cause <c>badarg</c> exception for a valid binary
+ when Erlang VM is linked against a <c>zlib</c> library of
+ version 1.2.9 or newer. Bug exists since OTP 17.0.</p>
+ <p>
+ Own Id: OTP-14159 Aux Id: ERL-340 </p>
+ </item>
+ <item>
+ <p>
+ The driver efile_drv when opening a file now use fstat()
+ on the open file instead of stat() before opening, if
+ fstat() exists. This avoids a race when the file happens
+ to change between stat() and open().</p>
+ <p>
+ Own Id: OTP-14184 Aux Id: seq-13266 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.2.1</title>
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix a quite rare bug causing VM crash during code loading
+ and the use of export funs (fun M:F/A) of not yet loaded
+ modules. Requires a very specfic timing of concurrent
+ scheduler threads. Has been seen on ARM but can probably
+ also occure on other architectures. Bug has existed since
+ OTP R16.</p>
+ <p>
+ Own Id: OTP-14144 Aux Id: seq13242 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed <c>configure</c> failures on MacOSX. Most important
+ <c>clock_gettime()</c> was detected when building for
+ MacOSX - El Capitan using XCode 8 despite it is not
+ available until MacOSX - Sierra.</p>
+ <p>
+ Own Id: OTP-13904 Aux Id: ERL-256 </p>
+ </item>
+ <item>
+ <p>
+ <c>code:add_pathsa/1</c> and command line option
+ <c>-pa</c> both revert the given list of directories when
+ adding it at the beginning of the code path. This is now
+ documented.</p>
+ <p>
+ Own Id: OTP-13920 Aux Id: ERL-267 </p>
+ </item>
+ <item>
+ <p>
+ Fix a compilation error of erts in OpenBSD related to the
+ usage of the __errno variable.</p>
+ <p>
+ Own Id: OTP-13927</p>
+ </item>
+ <item>
+ <p>
+ Fixed so that when enabling tracing on a process that had
+ an invalid tracer associated with it, the new tracer
+ overwrites the old tracer. Before this fix, calling
+ erlang:trace/3 would behave as if the tracer was still
+ alive and not apply the new trace.</p>
+ <p>
+ This fault was introduced in ERTS 8.0.</p>
+ <p>
+ Own Id: OTP-13928</p>
+ </item>
+ <item>
+ <p>
+ Fix parsing of <c>-profile_boot 'true' | 'false'</c></p>
+ <p>
+ Own Id: OTP-13955 Aux Id: ERL-280 </p>
+ </item>
+ <item>
+ <p>
+ A slight improvement of <c>erlang:get_stacktrace/0</c>
+ for exceptions raised in hipe compiled code. Beam
+ compiled functions in such stack trace was earlier
+ replaced by some unrelated function. They are now instead
+ omitted. This is an attempt to reduce the confusion in
+ the absence of a complete and correct stack trace for
+ mixed beam and hipe functions.</p>
+ <p>
+ Own Id: OTP-13992</p>
+ </item>
+ <item>
+ <p> Correct type declaration of match specification head.
+ </p>
+ <p>
+ Own Id: OTP-13996</p>
+ </item>
+ <item>
+ <p>
+ HiPE code loading failed for x86_64 if gcc was configured
+ with <c>--enable-default-pie</c>. Fixed by disabling PIE,
+ if needed for HiPE, when building the VM.</p>
+ <p>
+ Own Id: OTP-14031 Aux Id: ERL-294, PR-1239 </p>
+ </item>
+ <item>
+ <p>
+ Faulty arguments could be presented on exception from a
+ NIF that had rescheduled itself using
+ <c>enif_schedule_nif()</c>.</p>
+ <p>
+ Own Id: OTP-14048</p>
+ </item>
+ <item>
+ <p>
+ The runtime system could crash if a garbage collection on
+ a process was performed immediately after a NIF had been
+ rescheduled using <c>enif_schedule_nif()</c>.</p>
+ <p>
+ Own Id: OTP-14049</p>
+ </item>
+ <item>
+ <p>
+ A reference to purged code could be left undetected by
+ the purge operation if a process just had rescheduled a
+ NIF call using <c>enif_schedule_nif()</c> when the
+ process was checked. This could cause a runtime system
+ crash.</p>
+ <p>
+ Own Id: OTP-14050</p>
+ </item>
+ <item>
+ <p>Fixed a number of dirty scheduler related bugs:</p>
+ <list> <item><p>Process priority was not handled correct
+ when scheduling on a dirty scheduler.</p></item>
+ <item><p>The runtime system could crash when an exit
+ signal with a compound exit reason was sent to a process
+ executing on a dirty scheduler.</p></item> <item><p>The
+ runtime system crashed when call tracing a process
+ executing on a dirty scheduler.</p></item> <item><p>A
+ code purge operation could end up hanging forever when a
+ process executed on a dirty scheduler</p></item> </list>
+ <p>
+ Own Id: OTP-14051</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Fix minor soft purge race bug that could incorrectly
+ trigger code_server to load new code for the module if
+ the soft purge failed and no current version of the
+ module existed.</p>
+ <p>
+ Own Id: OTP-13925</p>
+ </item>
+ <item>
+ <p>
+ To ease troubleshooting, <c>erlang:load_nif/2</c> now
+ includes the return value from a failed call to
+ load/reload/upgrade in the text part of the error tuple.
+ The <c>crypto</c> NIF makes use of this feature by
+ returning the source line where/if the initialization
+ fails.</p>
+ <p>
+ Own Id: OTP-13951</p>
+ </item>
+ <item>
+ <p>
+ New environment variable <c>ERL_CRASH_DUMP_BYTES</c> can
+ be used to limit the size of crash dumps. If the limit is
+ reached, crash dump generation is aborted and the
+ generated file will be truncated.</p>
+ <p>
+ Own Id: OTP-14046</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The emulator got a dynamic library dependency towards
+ libsctp, which on Linux was not intended since the
+ emulator there loads and resolves the needed sctp
+ functions in runtime. This has been fixed and a configure
+ switch --enable-sctp=lib has been added for those who
+ want such a library dependency.</p>
+ <p>
+ Own Id: OTP-13956 Aux Id: ERL-262, ERL-133 </p>
+ </item>
+ <item>
+ <p>
+ Fix SIGUSR1 crashdump generation</p>
+ <p>
+ Do not generate a core when a crashdump is asked for.</p>
+ <p>
+ Own Id: OTP-13997</p>
+ </item>
+ <item>
+ <p>The new functions in <c>code</c> that allows loading
+ of many modules at once had a performance problem. While
+ executing a helper function in the <c>erl_prim_loader</c>
+ process, garbage messages were produced. The garbages
+ messages were ignored and ultimately discarded, but there
+ would be a negative impact on performance and memory
+ usage. The number of garbage message depended on both the
+ number of modules to be loaded and the length of the code
+ path.</p>
+ <p>The functions affected of this problem were:
+ <c>atomic_load/1</c>, <c>ensure_modules_loaded/1</c>, and
+ <c>prepare_loading/1</c>.</p>
+ <p>
+ Own Id: OTP-14009</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix bug for calls from hipe code to BIFs that disable GC
+ while yielding. Has been causing Dialyzer crashes on ARM
+ (and presumably all other non-intel platforms).</p>
+ <p>
+ Own Id: OTP-13724 Aux Id: PR-1116 </p>
+ </item>
+ <item>
+ <p>
+ Fix a bug where changing the current working directory of
+ the VM would not change the current working directory of
+ programs spawned using <c>erlang:open_port({spawn,""},
+ ...)</c>.</p>
+ <p>
+ Own Id: OTP-13733 Aux Id: ERL-175 </p>
+ </item>
+ <item>
+ <p>
+ Fix a bug where disabling tracing from a process that had
+ return_to tracing enabled and was tracing on
+ <c>erlang:trace/3</c> would cause a segmentation fault.</p>
+ <p>
+ Own Id: OTP-13734</p>
+ </item>
+ <item>
+ <p>
+ Update all erts documentation to use simpler English, use
+ consistent terminology and be easier to navigate.</p>
+ <p>
+ Own Id: OTP-13740</p>
+ </item>
+ <item>
+ <p>
+ Add dirty schedulers to the microstate accounting
+ statistics.</p>
+ <p>
+ Own Id: OTP-13744</p>
+ </item>
+ <item>
+ <p>
+ Fixed dirty scheduler build support on 32-bit windows.</p>
+ <p>
+ Own Id: OTP-13759</p>
+ </item>
+ <item>
+ <p>
+ inet:getstat(Socket) on an SCTP socket returned 0 for
+ send stats. This bug has now been corrected. Reported by
+ systra as issue ERL-102 on bugs.erlang.org.</p>
+ <p>
+ Own Id: OTP-13773 Aux Id: ERL-102 </p>
+ </item>
+ <item>
+ <p>
+ AF_UNSPEC and unknown address families were misread by
+ UDP receive in prim_inet resulting in an exception. This
+ bug has now been corrected.</p>
+ <p>
+ Own Id: OTP-13775</p>
+ </item>
+ <item>
+ <p>
+ Sweep HiPE stack for literals during code purge.</p>
+ <p>
+ Own Id: OTP-13777 Aux Id: PR-1122 </p>
+ </item>
+ <item>
+ <p>
+ Fix bug in run_erl for OpenBSD that could cause it on
+ rare occations to exit without starting the program (erl)
+ at all.</p>
+ <p>
+ Own Id: OTP-13795</p>
+ </item>
+ <item>
+ <p>
+ Update build scripts to not make assumtions about where
+ env, cp and perl are located.</p>
+ <p>
+ Own Id: OTP-13800</p>
+ </item>
+ <item>
+ <p>
+ Fixed a bug where init:stop could deadlock if a process
+ with infinite shutdown timeout (e.g. a supervisor)
+ attempted to load code while terminating.</p>
+ <p>
+ Own Id: OTP-13802</p>
+ </item>
+ <item>
+ <p>
+ Fixed a segmentation fault on sparc CPUs when free'ing a
+ tracer module's state.</p>
+ <p>
+ Own Id: OTP-13803</p>
+ </item>
+ <item>
+ <p>
+ <c>fun</c>s was not properly handled during purge of a
+ module. This could cause a crash of the VM after a purge
+ of a module.</p>
+ <p>
+ Own Id: OTP-13809</p>
+ </item>
+ <item>
+ <p>
+ Fixed a memory leak when the process monitoring a port
+ crashed.</p>
+ <p>
+ Own Id: OTP-13818</p>
+ </item>
+ <item>
+ <p>
+ Fixed multiple dirty scheduler related tracing bugs.</p>
+ <p>
+ Own Id: OTP-13822</p>
+ </item>
+ <item>
+ <p>
+ Fix error handling in beam code runtime loader for a
+ number of cases when index and size fields got corrupted
+ (negative) values.</p>
+ <p>
+ Own Id: OTP-13848 Aux Id: ERL-216 </p>
+ </item>
+ <item>
+ <p>
+ Minor fix of dirty scheduler implementation.</p>
+ <p>
+ Own Id: OTP-13852</p>
+ </item>
+ <item>
+ <p>
+ Calls to <c>erl_drv_send_term()</c> or
+ <c>erl_drv_output_term()</c> from a non-scheduler thread
+ while the corresponding port was invalid caused the
+ emulator to enter an inconsistent state which eventually
+ caused an emulator crash.</p>
+ <p>
+ Own Id: OTP-13866</p>
+ </item>
+ <item>
+ <p>
+ Fix a rare race condition in <c>erlang:open_port({spawn,
+ ""}, ...)</c> that would result in the erl_child_setup
+ program aborting and cause the emulator to exit.</p>
+ <p>
+ Own Id: OTP-13868</p>
+ </item>
+ <item>
+ <p>Driver and NIF operations accessing processes or ports
+ could cause an emulator crash when used from
+ non-scheduler threads. Those operations are:</p> <list>
+ <item><c>erl_drv_send_term()</c></item>
+ <item><c>driver_send_term()</c></item>
+ <item><c>erl_drv_output_term()</c></item>
+ <item><c>driver_output_term()</c></item>
+ <item><c>enif_send()</c></item>
+ <item><c>enif_port_command()</c></item> </list>
+ <p>
+ Own Id: OTP-13869</p>
+ </item>
+ <item>
+ <p>
+ Fix start scripts generation dependency in Makefile</p>
+ <p>
+ Own Id: OTP-13871 Aux Id: ERL-241 </p>
+ </item>
+ <item>
+ <p>
+ The VM could crash if <c>erlang:get_stacktrace()</c> was
+ called after a rescheduled NIF had thrown an exception.</p>
+ <p>
+ Own Id: OTP-13877</p>
+ </item>
+ <item>
+ <p>Calling <c>code:delete/1</c> before a loading a module
+ with an on_load function, the old code would be
+ overwritten, causing a memory or a crash if NIFs were
+ involved. (Thanks to vans163 for reporting this bug.)</p>
+ <p>
+ Own Id: OTP-13893 Aux Id: ERL-240 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Improved accuracy of timeouts on MacOS X. This by setting
+ premature timeouts followed by a short actual timeout
+ during scheduler wait.</p>
+ <p>
+ Own Id: OTP-13698</p>
+ </item>
+ <item>
+ <p>Added the following symbolic time unit representations
+ to the <seealso
+ marker="erlang#type-time_unit"><c>erlang:time_unit()</c></seealso>
+ type:</p> <list> <item><c>second</c></item>
+ <item><c>millisecond</c></item>
+ <item><c>microsecond</c></item>
+ <item><c>nanosecond</c></item> </list> <p>The following
+ symbolic time unit representations are now
+ <em>deprecated</em>, but still part of the
+ <c>erlang:time_unit()</c> type:</p> <list>
+ <item><c>seconds</c></item>
+ <item><c>milli_seconds</c></item>
+ <item><c>micro_seconds</c></item>
+ <item><c>nano_seconds</c></item> </list>
+ <p>
+ Own Id: OTP-13735</p>
+ </item>
+ <item>
+ <p>
+ Fix maps hashing entropy of maps with maps keys.</p>
+ <p>
+ Own Id: OTP-13763 Aux Id: ERL-199 </p>
+ </item>
+ <item>
+ <p>
+ Improved dirty scheduler support. A purge of a module can
+ now be performed without having to wait for completion of
+ all ongoing dirty NIF calls.</p>
+ <p>
+ Note that when enabling support for dirty schedulers, a
+ new purge strategy will as of ERTS version 8.1 be
+ enabled. This new strategy is not fully backwards
+ compatible with the strategy used by default. 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>
+ Own Id: OTP-13808 Aux Id: OTP-13833 </p>
+ </item>
+ <item>
+ <p>
+ A new purge strategy has been introduced. The new
+ strategy will by default be disabled during the OTP 19
+ release, but will be the only strategy available as of
+ the OTP 20 release.</p>
+ <p>
+ The new strategy is slightly incompatible with the
+ strategy being used by default in OTP 19. Using the
+ default strategy, processes holding <c>fun</c>s that
+ refer to the module being purged either fail a soft
+ purge, or will be 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>
+ The new strategy can optionally be enabled when building
+ OTP during OTP 19, and will automatically be enabled if
+ the runtime system is built with support for dirty
+ schedulers.</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>
+ Own Id: OTP-13833</p>
+ </item>
+ <item>
+ <p>
+ Fixed unnecessary overestimation of heap size need during
+ garbage collection.</p>
+ <p>
+ Own Id: OTP-13851</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.0.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed a VM crash that occured in a garbage collection of
+ a process when it had received binaries. This bug was
+ introduced in ERTS version 8.0 (OTP 19.0).</p>
+ <p>
+ Own Id: OTP-13890</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.0.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed a VM crash that occured in garbage collection of a
+ process when it had received maps over the distribution.
+ This bug was introduced in ERTS version 8.0 (OTP 19.0).</p>
+ <p>
+ Own Id: OTP-13889</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.0.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed a race that could cause a lost wakeup of a process
+ that timed out in a <c>receive ... after</c>. This bug
+ was introduced in ERTS version 7.0.</p>
+ <p>
+ Own Id: OTP-13798 Aux Id: OTP-11997 </p>
+ </item>
+ <item>
+ <p>
+ Fixed segfault after writing an erl crash dump.</p>
+ <p>
+ Own Id: OTP-13799</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.0.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix scheduler deadlock bug in <c>ets:update_counter/4</c>
+ when key is not found and inserting the default object
+ causes the table to grow.</p>
+ <p>
+ Own Id: OTP-13731 Aux Id: ERL-188 </p>
+ </item>
+ <item>
+ <p>
+ Fix VM abort "Overrun stack and heap" in garbage
+ collection triggered by a <c>bsl</c> operation and some
+ very specific heap conditions.</p>
+ <p>
+ Own Id: OTP-13732 Aux Id: seq13142 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A memory allocation bug in <c>group_leader/2</c> could
+ cause an emulator crash when garbage collecting a process
+ that had been assigned a remote group leader. This bug
+ was introduced in ERTS version 8.0.</p>
+ <p>
+ Own Id: OTP-13716</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.0</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>The handling of <c>on_load</c> functions has been
+ improved. The major improvement is that if a code upgrade
+ fails because the <c>on_load</c> function fails, the
+ previous version of the module will now be retained.</p>
+ <p>
+ Own Id: OTP-12593</p>
+ </item>
+ <item>
+ <p><c>is_builtin(erlang, apply, 3)</c> will now return
+ <c>true</c>.</p>
+ <p>
+ Own Id: OTP-13034</p>
+ </item>
+ <item>
+ <p>
+ Fix <c>enif_get_list_length</c> to return false if list
+ is improper or have length larger than <c>UINT_MAX</c>
+ (did return true and an incorrect length value).</p>
+ <p>
+ Own Id: OTP-13288 Aux Id: PR913 </p>
+ </item>
+ <item>
+ <p>
+ Cleanup hipe signal handling code for x86 and make it
+ more portable.</p>
+ <p>
+ Own Id: OTP-13341 Aux Id: PR951 </p>
+ </item>
+ <item>
+ <p>
+ Make file:datasync use fsync instead of fdatasync on Mac
+ OSX.</p>
+ <p>
+ Own Id: OTP-13411</p>
+ </item>
+ <item>
+ <p>
+ Make sure to create a crash dump when running out of
+ memory. This was accidentally removed in the erts-7.3
+ release.</p>
+ <p>
+ Own Id: OTP-13419</p>
+ </item>
+ <item>
+ <p>
+ A bug has been fixed where if erlang was started +B on a
+ unix platform it would be killed by a SIGUSR2 signal when
+ creating a crash dump.</p>
+ <p>
+ Own Id: OTP-13425</p>
+ </item>
+ <item>
+ <p>
+ Fix race between <c>process_flag(trap_exit,true)</c> and
+ a received exit signal.</p>
+ <p>
+ A process could terminate due to exit signal even though
+ <c>process_flag(trap_exit,true)</c> had returned. A very
+ specific timing between call to <c>process_flag/2</c> and
+ exit signal from another scheduler was required for this
+ to happen.</p>
+ <p>
+ Own Id: OTP-13452</p>
+ </item>
+ <item>
+ <p>Don't search for non-existing Map keys twice</p>
+ <p>For <c>maps:get/2,3</c> and <c>maps:find/2</c>,
+ searching for an immediate key, e.g. an atom, in a small
+ map, the search was performed twice if the key did not
+ exist.</p>
+ <p>
+ Own Id: OTP-13459</p>
+ </item>
+ <item>
+ <p>
+ When an abnormally large distribution message is about to
+ be sent, the VM has been changed to create a crash dump
+ instead of a core dump.</p>
+ <p>
+ Own Id: OTP-13474</p>
+ </item>
+ <item>
+ <p>
+ Fix <c>erlang:process_info/2</c> type specification</p>
+ <p>
+ Own Id: OTP-13485 Aux Id: ERL-123 </p>
+ </item>
+ <item>
+ <p>
+ Fix bug in <c>open_port/2</c> with option <c>{args,
+ List}</c>. A vm crash could be caused by an improper
+ <c>List</c>.</p>
+ <p>
+ Own Id: OTP-13489 Aux Id: ERL-127 </p>
+ </item>
+ <item>
+ <p>
+ Fixed a race-condition bug where the emulator could crash
+ when <c>erlang:system_profile/1,2</c> was enabled and a
+ process had to be re-scheduled during termination.</p>
+ <p>
+ Own Id: OTP-13494 Aux Id: ERL-126 </p>
+ </item>
+ <item>
+ <p>
+ Fixed bugs where the reduction counter was not handled
+ correct.</p>
+ <p>
+ Own Id: OTP-13512</p>
+ </item>
+ <item>
+ <p>
+ Fixed typo in description of the <c>EPMD_DUMP_REQ</c>
+ response.</p>
+ <p>
+ Own Id: OTP-13517</p>
+ </item>
+ <item>
+ <p>
+ Fixed a bug where a process flagged as sensitive would
+ sometimes record its save_calls when it shouldn't.</p>
+ <p>
+ Own Id: OTP-13540</p>
+ </item>
+ <item>
+ <p>
+ Update configure scripts to not use hard-coded path for
+ /bin/pwd and /bin/rm.</p>
+ <p>
+ Own Id: OTP-13562</p>
+ </item>
+ <item>
+ <p>
+ When passing a larger binary than the outputv callback of
+ a linked-in driver can handle in one io vector slot, the
+ binary is now split into multiple slots in the io vector.
+ This change only effects system where the max size of an
+ io vector slot is smaller then the word size of the
+ system (e.g. Windows).</p>
+ <p>
+ This change means that it is now possible on Windows to
+ send binaries that are larger than 4GB to port_command,
+ which is what is used for file:write, gen_tcp:send etc.</p>
+ <p>
+ Own Id: OTP-13628</p>
+ </item>
+ <item>
+ <p>
+ Workaround of Maps output in crashdumps. Currently the
+ atom 'undefined' is generated instead of Map data if a
+ Map type is encountered during crash.</p>
+ <p>
+ Own Id: OTP-13657</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The tracing support has been extended to allow a <seealso
+ marker="erl_tracer">tracer module</seealso> to be the
+ trace event handler instead of a process or port. The
+ <seealso marker="erl_tracer">tracer module</seealso>
+ makes it possible for trace tools to filter or manipulate
+ trace event data without the trace event first having to
+ be copied from the traced process or port.</p>
+ <p>
+ With the introduction of this feature,
+ <c>erlang:trace(all|existing, _, _)</c> now also returns
+ the tracer process as part of the number of processes on
+ which tracing is enabled. The is incompatible with the
+ previous releases.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-10267</p>
+ </item>
+ <item>
+ <p>
+ Introduce LTTng tracing of Erlang Runtime System</p>
+ <p>
+ For LTTng to be enabled OTP needs to be built with
+ configure option <c>--with-dynamic-trace=lttng</c>.</p>
+ <p>
+ This feature introduces tracepoints for schedulers,
+ drivers, memory carriers, memory and async thread pool.</p>
+ <p> For a list of all tracepoints, see <seealso
+ marker="runtime_tools:LTTng">Runtime Tools User's
+ Guide</seealso> .</p>
+ <p>
+ Own Id: OTP-10282</p>
+ </item>
+ <item>
+ <p>
+ Make it possible to monitor/demonitor ports using the
+ <seealso
+ marker="erlang#monitor/2">erlang:monitor/2</seealso> API.
+ The process and port information functions have also been
+ updated to include information about monitors from
+ processes to ports.</p>
+ <p>
+ Own Id: OTP-11384</p>
+ </item>
+ <item>
+ <p>
+ Add microstate accounting</p>
+ <p>
+ Microstate accounting is a way to track which state the
+ different threads within ERTS are in. The main usage area
+ is to pin point performance bottlenecks by checking which
+ states the threads are in and then from there figuring
+ out why and where to optimize.</p>
+ <p>
+ Since checking whether microstate accounting is on or off
+ is relatively expensive only a few of the states are
+ enabled by default and more states can be enabled through
+ configure.</p>
+ <p>
+ There is a convenience module called msacc that has been
+ added to runtime_tools that can assist in gathering and
+ interpreting the data from Microstate accounting.</p>
+ <p>
+ For more information see <seealso
+ marker="erts:erlang#statistics_microstate_accounting">erlang:statistics(microstate_accounting,
+ _)</seealso> and the <seealso
+ marker="runtime_tools:msacc">msacc</seealso> module in
+ runtime_tools.</p>
+ <p>
+ Own Id: OTP-12345</p>
+ </item>
+ <item>
+ <p>
+ The port of Erlang/OTP to the real-time operating system
+ OSE has been removed.</p>
+ <p>
+ Own Id: OTP-12573</p>
+ </item>
+ <item>
+ <p>
+ Sharing preserved copy for messages and exit signals</p>
+ <p>
+ Enable sharing preserved copy with configure option
+ <c>--enable-sharing-preserving</c>. This will preserve
+ sharing, within the process, when communication with
+ other processes in the Erlang node. There is a trade-off,
+ the copy is more costly but this cost can be reclaimed if
+ there is a lot of sharing in the message. In addition
+ literals will not be copied in a send except during a
+ purge phase of the module where the literals are located.
+ This feature is considered experimental in 19.0.</p>
+ <p>
+ Own Id: OTP-12590 Aux Id: OTP-10251 </p>
+ </item>
+ <item>
+ <p>
+ Halfword BEAM has been removed.</p>
+ <p>
+ Own Id: OTP-12883</p>
+ </item>
+ <item>
+ <p>
+ Added <seealso
+ marker="kernel:os#perf_counter/1">os:perf_counter/1</seealso>.</p>
+ <p>
+ The perf_counter is a very very cheap and high resolution
+ timer that can be used to timestamp system events. It
+ does not have monoticity guarantees, but should on most
+ OS's expose a monotonous time.</p>
+ <p>
+ Own Id: OTP-12908</p>
+ </item>
+ <item>
+ <p>
+ Support for a fragmented young heap generation. That is,
+ the young heap generation can consist of multiple non
+ continuous memory areas. The main reason for this change
+ is to avoid extra copying of messages that could not be
+ allocated directly on the receivers heap.</p>
+ <p>
+ Own Id: OTP-13047</p>
+ </item>
+ <item>
+ <p>
+ Erlang linked-in driver can now force the call to
+ open_port to block until a call to erl_drv_init_ack is
+ made inside the driver. This is useful when you want to
+ do some asynchronous initialization, for example getting
+ configuration from a pipe, and you want the initial
+ open_port call to fail if the configuration is incomplete
+ or wrong. See the erl_driver documentation for more
+ details on the API.</p>
+ <p>
+ Own Id: OTP-13086</p>
+ </item>
+ <item>
+ <p>
+ Erlang linked-in drivers can now set their own pids as
+ seen in <c>erlang:port_info/1</c> by using the
+ <c>erl_drv_set_pid</c> function. For more details see the
+ erl_driver documentation.</p>
+ <p>
+ Own Id: OTP-13087</p>
+ </item>
+ <item>
+ <p>
+ The functionality behind <c>erlang:open_port/2</c> when
+ called with spawn or spawn_executable has been redone so
+ that the forking of the new program is done in a separate
+ process called erl_child_setup. This allows for a much
+ more robust implementation that uses less memory and does
+ not block the entire emulator if the program to be
+ started is on an un-accessible NFS. Benchmarks have shown
+ this approach to be about 3-5 times as fast as the old
+ approach where the fork/vfork was done by erts. This is a
+ pure stability and performance fix, however some error
+ messages may have changed, which is why it is marked as a
+ backwards incompatible change.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-13088</p>
+ </item>
+ <item>
+ <p>Improved yielding strategy in the implementation of
+ the following native functions:</p> <list>
+ <item><c>erlang:binary_to_list/1</c></item>
+ <item><c>erlang:binary_to_list/3</c></item>
+ <item><c>erlang:bitstring_to_list/1</c></item>
+ <item><c>erlang:list_to_binary/1</c></item>
+ <item><c>erlang:iolist_to_binary/1</c></item>
+ <item><c>erlang:list_to_bitstring/1</c></item>
+ <item><c>binary:list_to_bin/1</c></item> </list> <p>This
+ in order to improve performance of these functions.</p>
+ <p>
+ Own Id: OTP-13096</p>
+ </item>
+ <item>
+ <p>
+ All garbage collections of processes now bump reductions.
+ Also the amount of reductions bumped when garbage
+ collecting has been adjusted. It now better corresponds
+ to the amount of work performed. This in order to improve
+ the real time characteristics of the system.</p>
+ <p>
+ Own Id: OTP-13097</p>
+ </item>
+ <item>
+ <p>New functions that can load multiple modules at once
+ have been added to the '<c>code</c>' module. The
+ functions are <c>code:atomic_load/1</c>,
+ <c>code:prepare_loading/1</c>,
+ <c>code:finish_loading/1</c>, and
+ <c>code:ensure_modules_loaded/1</c>.</p>
+ <p>
+ Own Id: OTP-13111</p>
+ </item>
+ <item>
+ <p>The <c>-boot_var</c> option for <c>erl</c> now only
+ supports a single key and single value (as documented).
+ The option used to allow multiple key/value pairs, but
+ that behavior was undocumented.</p>
+ <p>The function <c>erl_prim_loader:start/3</c> has been
+ removed. Its documentation has also been removed.</p>
+ <p>The undocumented and unsupported function
+ <c>erl_prim_loader:get_files/2</c> has been removed.</p>
+ <p>
+ Own Id: OTP-13112</p>
+ </item>
+ <item>
+ <p>
+ Low level BIF <c>erlang:purge_module/1</c> is made more
+ robust against incorrect use. Lingering processes that
+ still refer the old code are now killed before the module
+ is purged to prevent fatal VM behavior.</p>
+ <p>
+ Own Id: OTP-13122</p>
+ </item>
+ <item>
+ <p>Improved dirty scheduler implementation. For more
+ information see the <seealso
+ marker="erl_nif#dirty_nifs">NIF
+ documentation</seealso>.</p> <note><list> <item><p>The
+ dirty scheduler support is still
+ <em>experimental</em>.</p></item> <item><p>The support
+ for determining whether dirty NIF support exist or not at
+ compile time using the C preprocessor macro
+ <c>ERL_NIF_DIRTY_SCHEDULER_SUPPORT</c> has been
+ removed.</p></item> <item><p>The
+ <c>enif_is_on_dirty_scheduler()</c> function has been
+ removed. Use <seealso
+ marker="erl_nif#enif_thread_type"><c>enif_thread_type()</c></seealso>
+ instead.</p></item> </list></note>
+ <p>
+ Own Id: OTP-13123</p>
+ </item>
+ <item>
+ <p>
+ Various optimizations done to process dictionary access.</p>
+ <p>
+ Own Id: OTP-13167</p>
+ </item>
+ <item>
+ <p>
+ Added max_heap_size process flag. max_heap_size allows
+ the user to limit the maximum heap used by a process. See
+ <seealso
+ marker="erlang#process_flag/2">erlang:process_flag</seealso>
+ for more details.</p>
+ <p>
+ Own Id: OTP-13174</p>
+ </item>
+ <item>
+ <p>
+ Allow dynamic drivers and NIF libraries to be built with
+ gcc option <c>-fvisibility=hidden</c> for faster loading
+ and more optimized code.</p>
+ <p>
+ Own Id: OTP-13227</p>
+ </item>
+ <item>
+ <p>
+ Add <c>erlang:process_info(Pid,
+ garbage_collection_info)</c> which returns extended
+ garbage_collection information. For more details see the
+ documentation.</p>
+ <p>
+ Own Id: OTP-13265</p>
+ </item>
+ <item>
+ <p>
+ The functions <c>erlang:list_to_integer/1</c> and
+ <c>string:to_integer/1</c> have been optimized for large
+ inputs.</p>
+ <p>
+ Own Id: OTP-13293</p>
+ </item>
+ <item>
+ <p>
+ Improved memory allocation strategy for hipe native code
+ on x86_64 (amd64) architectures by reserving enough low
+ virtual address space needed for the HiPE/AMD64 small
+ code model. The default virtual address area for hipe
+ code is set to 512Mb, but can be changed with emulator
+ flag <c>+MXscs</c>.</p>
+ <p>
+ Own Id: OTP-13359</p>
+ </item>
+ <item>
+ <p>
+ Introduction of configurable management of data referred
+ to by the message queue of a process. Each process can be
+ configured individually.</p>
+ <p>
+ It is now possible to configure the message queue of a
+ process, so that all data referred by it will be kept
+ outside of the heap, and by this prevent this data from
+ being part of garbage collections.</p>
+ <p>
+ For more information see the documentation of <seealso
+ marker="erlang#process_flag_message_queue_data"><c>process_flag(message_queue_data,
+ MQD)</c></seealso>.</p>
+ <p>
+ Own Id: OTP-13366 Aux Id: OTP-13047 </p>
+ </item>
+ <item>
+ <p>
+ Processes now yield when scanning large message queues
+ and not finding a matching message. This in order to
+ improve real time characteristics.</p>
+ <p>
+ Own Id: OTP-13401</p>
+ </item>
+ <item>
+ <p>
+ Optimized an erts internal function that is used to
+ traverse erlang terms. The internal function was mainly
+ used by term_to_binary and comparison of terms.
+ Benchmarks have shown up to a 10% performance increase in
+ those functions after the optimization.</p>
+ <p>
+ Own Id: OTP-13440</p>
+ </item>
+ <item>
+ <p>Add the following NIF API functions:</p>
+ <list> <item><seealso
+ marker="erl_nif#enif_cpu_time"><c>enif_cpu_time</c></seealso></item>
+ <item><seealso
+ marker="erl_nif#enif_now_time"><c>enif_now_time</c></seealso></item>
+ <item><seealso
+ marker="erl_nif#enif_make_unique_integer"><c>enif_make_unique_integer</c></seealso></item>
+ <item><seealso
+ marker="erl_nif#enif_is_process_alive"><c>enif_is_process_alive</c></seealso></item>
+ <item><seealso
+ marker="erl_nif#enif_is_port_alive"><c>enif_is_port_alive</c></seealso></item>
+ <item><seealso
+ marker="erl_nif#enif_term_to_binary"><c>enif_term_to_binary</c></seealso></item>
+ <item><seealso
+ marker="erl_nif#enif_binary_to_term"><c>enif_binary_to_term</c></seealso></item>
+ <item><seealso
+ marker="erl_nif#enif_port_command"><c>enif_port_command</c></seealso></item>
+ </list>
+ <p>
+ For details of what each function does, see the erl_nif
+ documentation.</p>
+ <p>
+ Own Id: OTP-13442</p>
+ </item>
+ <item>
+ <p>
+ Optimize <c>'++'</c> operator and <c>lists:append/2</c>
+ by using a single pass to build a new list while checking
+ for properness.</p>
+ <p>
+ Own Id: OTP-13487</p>
+ </item>
+ <item>
+ <p>
+ Handle terms (pids,ports and refs) from nodes with a
+ 'creation' value larger than 3. This is a preparation of
+ the distribution protocol to allow OTP 19 nodes to
+ correctly communicate with future nodes (20 or higher).
+ The 'creation' value differentiates different
+ incarnations of the same node (name).</p>
+ <p>
+ Own Id: OTP-13488</p>
+ </item>
+ <item>
+ <p>
+ Don't send unasked for systemd notifications in epmd</p>
+ <p>
+ Own Id: OTP-13493 Aux Id: PR-999 </p>
+ </item>
+ <item>
+ <p>
+ The enif_send API has been extended to allow NULL to be
+ used as the message environment. When used this way, a
+ message environment is implicitly created and the given
+ term is copied into that environment before sending. This
+ can be an optimization if many small messages are being
+ sent by the nif.</p>
+ <p>
+ Own Id: OTP-13495</p>
+ </item>
+ <item>
+ <p>
+ The tracing support has been extended to allow tracing on
+ ports. Ports can be traced on using the 'ports', 'send'
+ and 'receive' trace flags.</p>
+ <p>
+ The first argument of <seealso
+ marker="erts:erlang#trace/3">erlang:trace/3</seealso> has
+ been extended so that <c>'all'</c>, <c>'existing'</c> and
+ <c>'new'</c> now include both processes and ports. New
+ <c>Tracee</c> variants, <c>'all_processes'</c>,
+ <c>'all_ports'</c>, <c>'existing_processes'</c> etc have
+ been added to specify only processes or ports.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-13496</p>
+ </item>
+ <item>
+ <p>
+ When the <c>'procs'</c> trace flag is enabled, a
+ <c>'spawned'</c> trace event is now also generated by a
+ newly created process. The previous event <c>'spawn'</c>
+ remains, but as it is generated by the process that did
+ the spawn, it is not guaranteed that it is ordered with
+ other trace events from the newly spawned process. So
+ when tracking the lifetime of a process this new event
+ should be used as the creation event.</p>
+ <p>
+ This new trace event is marked as an incompatibility
+ because tools that expect certain trace events when
+ enabling 'procs' will have to updated.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-13497</p>
+ </item>
+ <item>
+ <p>
+ Add the <seealso
+ marker="erts:erlang#match_spec_test/3">erlang:match_spec_test/3</seealso>
+ function. The functions allows the testing of match
+ specifications for both tracing and ets tables. It can be
+ used to test that a match specification does the expected
+ filtering on specific data. It also returns more verbose
+ error reasons for incorrectly constructed match
+ specifications.</p>
+ <p>
+ Own Id: OTP-13501</p>
+ </item>
+ <item>
+ <p>
+ The erts internal tracing support has been changed to
+ have much less overhead and be more scalable.</p>
+ <p>
+ This rewrite does not break any backwards
+ incompatibilities, but it does change the ordering of
+ some trace messages when compared to previous releases.
+ It should be noted that this only applies to trace
+ messages sent to processes or ports, it does not apply to
+ the new tracer module. However in future releases they
+ may also be effected by this.</p>
+ <p>
+ Trace messages are only guaranteed to be ordered from one
+ traced process or port. In previous releases this was not
+ visible as a <c>'send'</c> trace message would always
+ arrive before the corresponding <c>'receive'</c> trace
+ message that is no longer always the case. This also
+ means that timestamped trace messages may seem to arrive
+ out of order as the timestamp is taken when the event is
+ triggered and not when it is put in the queue of the
+ tracer.</p>
+ <p>
+ Own Id: OTP-13503</p>
+ </item>
+ <item>
+ <p>
+ Add possibility to filter <c>send</c> and <c>receive</c>
+ trace with match specifications.</p>
+ <p>
+ Own Id: OTP-13507</p>
+ </item>
+ <item>
+ <p>
+ Add <c>maps:update_with/3,4</c> and <c>maps:take/2</c></p>
+ <p>
+ Own Id: OTP-13522 Aux Id: PR-1025 </p>
+ </item>
+ <item>
+ <p>
+ Introduce LTTng tracing via Erlang tracing.</p>
+ <p>
+ For LTTng to be enabled OTP needs to be built with
+ configure option <c>--with-dynamic-trace=lttng</c>.</p>
+ <p>The dynamic trace module <c>dyntrace</c> is now
+ capable to be used as a LTTng sink for Erlang tracing.
+ For a list of all tracepoints, see <seealso
+ marker="runtime_tools:LTTng">Runtime Tools User's
+ Guide</seealso> .</p>
+ <p>This feature also introduces an incompatible change in
+ trace tags. The trace tags <c>gc_start</c> and
+ <c>gc_end</c> has been split into <c>gc_minor_start</c>,
+ <c>gc_minor_end</c> and <c>gc_major_start</c>,
+ <c>gc_major_end</c>.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-13532</p>
+ </item>
+ <item>
+ <p>
+ Print heap pointers for garbing processes during
+ crashdump</p>
+ <p>
+ Own Id: OTP-13541 Aux Id: PR-1026 </p>
+ </item>
+ <item>
+ <p>
+ Changed and improved low level memory statistics returned
+ by <c>erlang:system_info/1</c>. The info for
+ <c>erts_mmap</c> has been moved from <c>mseg_alloc</c> to
+ its own section returned by <c>{allocator,
+ erts_mmap}</c>.</p>
+ <p>
+ Own Id: OTP-13560</p>
+ </item>
+ <item>
+ <p>
+ Add enif_snprintf to the NIF API</p>
+ <p>
+ The function <c>enif_snprintf</c> is similar to
+ <c>snprintf</c> call but can handle formatting of Erlang
+ terms via <c>%T</c> format specifier.</p>
+ <p>
+ Own Id: OTP-13580</p>
+ </item>
+ <item>
+ <p>The warning in the documentation for
+ <c>erlang:raise/3</c> has been removed. It is now
+ officially perfectly fine to use raise/3 in production
+ code.</p>
+ <p>
+ Own Id: OTP-13599</p>
+ </item>
+ <item>
+ <p>
+ Fix bugs caused by the VM sometimes truncating object
+ sizes or offsets to 32 bits on 64-bit hosts. These bugs
+ were mainly found when working with large unicode strings
+ and nifs environments.</p>
+ <p>
+ Own Id: OTP-13606</p>
+ </item>
+ <item>
+ <p>
+ Add <c>-start_epmd</c> command line option, this lets you
+ disable automatic starting of epmd when starting a
+ distributed node.</p>
+ <p>
+ Add <c>-epmd_module</c> command line option, this lets
+ you specify a module to register and look-up node names
+ in. The default module is <c>erl_epmd</c>.</p>
+ <p>
+ Own Id: OTP-13627</p>
+ </item>
+ <item>
+ <p>
+ <c>erlang:halt</c> now truncates strings longer than 200
+ characters instead of failing with <c>badarg</c>.</p>
+ <p>
+ Own Id: OTP-13630</p>
+ </item>
+ <item>
+ <p>
+ Fix possible race in poller wake up on windows</p>
+ <p>
+ Own Id: OTP-13634</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erts 7.3.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -8580,9 +10270,9 @@
dynamically linking against <c>libssl.so</c> and
<c>libcrypto.so</c>. The runtime library search path has
also been extended. </item><item> The <c>configure</c>
- scripts of <c>erl_interface</c> and <c>odbc</c> now
+ scripts of Erl_interface and ODBC now
search for thread libraries and thread library quirks the
- same way as <c>erts</c> do. </item><item> The
+ same way as ERTS do. </item><item> The
<c>configure</c> script of the <c>odbc</c> application
now also looks for odbc libraries in <c>lib64</c> and
<c>lib/64</c> directories when building on a 64-bit
@@ -9698,7 +11388,7 @@
</item>
<item>
<p>
- A corrected bug in <c>ets</c> for <c>bag</c> and
+ A corrected bug in ETS for <c>bag</c> and
<c>duplicate_bag</c>. A <c>delete/2</c> or
<c>lookup_element/3</c> could miss objects in a fixed
table if one or more objects with the same key had
@@ -10140,7 +11830,7 @@
<list>
<item>
<p>
- A corrected bug in <c>ets</c> for <c>bag</c> and
+ A corrected bug in ETS for <c>bag</c> and
<c>duplicate_bag</c>. A <c>delete/2</c> or
<c>lookup_element/3</c> could miss objects in a fixed
table if one or more objects with the same key had
diff --git a/erts/doc/src/notes_history.xml b/erts/doc/src/notes_history.xml
index 0886ae4039..0bc2ab1383 100644
--- a/erts/doc/src/notes_history.xml
+++ b/erts/doc/src/notes_history.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2006</year><year>2013</year>
+ <year>2006</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/erts/doc/src/part.xml b/erts/doc/src/part.xml
index 2f5eca93db..d583b873a0 100644
--- a/erts/doc/src/part.xml
+++ b/erts/doc/src/part.xml
@@ -4,7 +4,7 @@
<part xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,8 +30,8 @@
<file>part.xml</file>
</header>
<description>
- <p>The Erlang Runtime System Application <em>ERTS</em>.</p>
</description>
+ <xi:include href="introduction.xml"/>
<xi:include href="communication.xml"/>
<xi:include href="time_correction.xml"/>
<xi:include href="match_spec.xml"/>
diff --git a/erts/doc/src/part_notes.xml b/erts/doc/src/part_notes.xml
index 83bb479715..e579b7635d 100644
--- a/erts/doc/src/part_notes.xml
+++ b/erts/doc/src/part_notes.xml
@@ -4,7 +4,7 @@
<part xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>2004</year><year>2013</year>
+ <year>2004</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/erts/doc/src/part_notes_history.xml b/erts/doc/src/part_notes_history.xml
index 055d1681d5..277683a2b5 100644
--- a/erts/doc/src/part_notes_history.xml
+++ b/erts/doc/src/part_notes_history.xml
@@ -4,7 +4,7 @@
<part xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>2006</year><year>2013</year>
+ <year>2006</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/erts/doc/src/ref_man.xml b/erts/doc/src/ref_man.xml
index ac589f8cb5..0617463a7b 100644
--- a/erts/doc/src/ref_man.xml
+++ b/erts/doc/src/ref_man.xml
@@ -4,7 +4,7 @@
<application xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,14 +30,6 @@
<file>application.xml</file>
</header>
<description>
- <p>The Erlang Runtime System Application <em>ERTS</em>.</p>
- <note>
- <p>By default, the <c><![CDATA[erts]]></c> is only guaranteed to be compatible
- with other Erlang/OTP components from the same release as
- the <c><![CDATA[erts]]></c> itself. See the documentation of the system flag
- <seealso marker="erl#compat_rel">+R</seealso> on how to communicate
- with Erlang/OTP components from earlier releases.</p>
- </note>
</description>
<xi:include href="erl_prim_loader.xml"/>
<xi:include href="erlang.xml"/>
@@ -56,5 +48,6 @@
<xi:include href="driver_entry.xml"/>
<xi:include href="erts_alloc.xml"/>
<xi:include href="erl_nif.xml"/>
+ <xi:include href="erl_tracer.xml"/>
</application>
diff --git a/erts/doc/src/run_erl.xml b/erts/doc/src/run_erl.xml
index 0a5b2c6136..a9b6a7e2c6 100644
--- a/erts/doc/src/run_erl.xml
+++ b/erts/doc/src/run_erl.xml
@@ -4,7 +4,7 @@
<comref>
<header>
<copyright>
- <year>1999</year><year>2013</year>
+ <year>1999</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -28,137 +28,183 @@
<docno></docno>
<approved></approved>
<checked></checked>
- <date>99-12-15</date>
+ <date>1999-12-15</date>
<rev></rev>
<file>run_erl.xml</file>
</header>
<com>run_erl</com>
- <comsummary>Redirect Erlang input and output streams on Solaris&reg;</comsummary>
+ <comsummary>Redirect Erlang input and output streams on Unix systems.</comsummary>
<description>
- <p>This describes the <c><![CDATA[run_erl]]></c> program specific to
- Solaris/Linux. This program redirect the standard input and standard
- output streams so that all output can be logged. It also let the
- program <c><![CDATA[to_erl]]></c> connect to the Erlang console making it
- possible to monitor and debug an embedded system remotely.</p>
- <p>You can read more about the use in the <c><![CDATA[Embedded System User's Guide]]></c>.</p>
+ <p>The <c><![CDATA[run_erl]]></c> program is specific to Unix systems.
+ This program redirects the standard input and standard
+ output streams so that all output can be logged. It also lets the
+ program <c><![CDATA[to_erl]]></c> connect to the Erlang console, making
+ it possible to monitor and debug an embedded system remotely.</p>
+
+ <p>For more information about the use, see the
+ <seealso marker="doc/embedded:embedded_solaris">
+ Embedded System User's Guide</seealso> in System Documentation.</p>
</description>
+
<funcs>
<func>
- <name>run_erl [-daemon] pipe_dir/ log_dir "exec command [command_arguments]"</name>
- <fsummary>Start the Erlang emulator without attached terminal</fsummary>
+ <name>run_erl [-daemon] pipe_dir/ log_dir "exec command
+ arg1 arg2 ..."</name>
+ <fsummary>Start the Erlang emulator without attached terminal.</fsummary>
<desc>
- <p>The <c><![CDATA[run_erl]]></c> program arguments are:</p>
+ <p>Arguments:</p>
<taglist>
- <tag>-daemon</tag>
- <item>This option is highly recommended. It makes run_erl run in
- the background completely detached from any controlling
- terminal and the command returns to the caller immediately.
- Without this option, run_erl must be started using several
- tricks in the shell to detach it completely from the
- terminal in use when starting it. The option must be the
- first argument to run_erl on the command line.</item>
- <tag>pipe_dir</tag>
- <item>This is where to put the named pipe, usually
- <c><![CDATA[/tmp/]]></c> on Unix or <c><![CDATA[/pipe/]]></c> on OSE. It shall be suffixed by a <c><![CDATA[/]]></c> (slash),
- i.e. not <c><![CDATA[/tmp/epipies]]></c>, but <c><![CDATA[/tmp/epipes/]]></c>. </item>
- <tag>log_dir</tag>
- <item>This is where the log files are written. There will be one
- log file, <c><![CDATA[run_erl.log]]></c> that log progress and
- warnings from the <c><![CDATA[run_erl]]></c> program itself and there
- will be up to five log files at maximum 100KB each (both
- number of logs and sizes can be
- changed by environment variables, see below) with
- the content of the standard streams from and to the
- command. When the logs are full <c><![CDATA[run_erl]]></c> will delete
- and reuse the oldest log file.</item>
- <tag>"exec command [command_arguments]"</tag>
- <item>In the third argument <c><![CDATA[command]]></c> is the to execute
- where everything written to stdin and stdout is logged to
- <c><![CDATA[log_dir]]></c>.</item>
+ <tag><c>-daemon</c></tag>
+ <item>
+ <p>This option is highly recommended. It makes <c>run_erl</c> run
+ in the background completely detached from any controlling
+ terminal and the command returns to the caller immediately.
+ Without this option, <c>run_erl</c> must be started using several
+ tricks in the shell to detach it completely from the
+ terminal in use when starting it. The option must be the
+ first argument to <c>run_erl</c> on the command line.</p>
+ </item>
+ <tag><c>pipe_dir</c></tag>
+ <item>
+ <p>The named pipe, usually <c><![CDATA[/tmp/]]></c>. It must be
+ suffixed by a <c><![CDATA[/]]></c> (slash), that is,
+ <c><![CDATA[/tmp/epipes/]]></c>, not
+ <c><![CDATA[/tmp/epipes]]></c>.</p>
+ </item>
+ <tag><c>log_dir</c></tag>
+ <item>
+ <p>The log files, that is:</p>
+ <list type="bulleted">
+ <item>
+ <p>One log file, <c><![CDATA[run_erl.log]]></c>, which logs
+ progress and warnings from the <c><![CDATA[run_erl]]></c>
+ program itself.</p>
+ </item>
+ <item>
+ <p>Up to five log files at maximum 100 KB each with the content
+ of the standard streams from and to the command. (Both the
+ number of logs and sizes can be changed by environment
+ variables, see section <seealso
+ marker="#environment_variables">Environment Variables</seealso>
+ below.)</p>
+ <p>When the logs are full, <c><![CDATA[run_erl]]></c> deletes
+ and reuses the oldest log file.</p>
+ </item>
+ </list>
+ </item>
+ <tag><c>"exec command arg1 arg2 ..."</c></tag>
+ <item>
+ <p>A space-separated string specifying the program to be executed.
+ The second field is typically a command name such as <c>erl</c>.</p>
+ </item>
</taglist>
</desc>
</func>
</funcs>
<section>
- <title>Notes concerning the log files</title>
- <p>While running, run_erl (as stated earlier) sends all output,
- uninterpreted, to a log file. The file is called
- <c><![CDATA[erlang.log.N]]></c>, where N is a number. When the log is "full",
- default after 100KB, run_erl starts to log in file
- <c><![CDATA[erlang.log.(N+1)]]></c>, until N reaches a certain number (default
- 5), where after N starts at 1 again and the oldest files start
- getting overwritten. If no output comes from the erlang shell, but
- the erlang machine still seems to be alive, an "ALIVE" message is
- written to the log, it is a timestamp and is written, by default,
- after 15 minutes of inactivity. Also, if output from erlang is
- logged but it's been more than 5 minutes (default) since last time
- we got anything from erlang, a timestamp is written in the
- log. The "ALIVE" messages look like this:</p>
+ <title>Notes concerning the Log Files</title>
+ <p>While running, <c>run_erl</c> sends all output,
+ uninterpreted, to a log file. The file is named
+ <c><![CDATA[erlang.log.N]]></c>, where <c>N</c> is an integer. When the
+ log is "full" (default log size is 100 KB), <c>run_erl</c> starts to log
+ in file <c><![CDATA[erlang.log.(N+1)]]></c>, until <c>N</c> reaches a
+ certain number (default 5), whereupon <c>N</c> starts at 1 again and
+ the oldest files start getting overwritten.</p>
+
+ <p>If no output comes from the Erlang shell, but
+ the Erlang machine still seems to be alive, an "ALIVE" message is
+ written to the log; it is a time stamp and is written, by default,
+ after 15 minutes of inactivity. Also, if output from Erlang is
+ logged, but more than 5 minutes (default) has passed since last time
+ we got anything from Erlang, a time stamp is written in the
+ log. The "ALIVE" messages look as follows:</p>
+
<code type="none"><![CDATA[
- ===== ALIVE <date-time-string>
- ]]></code>
- <p>while the other timestamps look like this:</p>
+===== ALIVE <date-time-string> ]]></code>
+
+ <p>The other time stamps look as follows:</p>
+
<code type="none"><![CDATA[
- ===== <date-time-string>
- ]]></code>
- <p>The <c><![CDATA[date-time-string]]></c> is the date and time the message is
- written, default in local time (can be changed to GMT if one wants
- to) and is formatted with the ANSI-C function <c><![CDATA[strftime]]></c>
- using the format string <c><![CDATA[%a %b %e %T %Z %Y]]></c>, which produces
- messages on the line of <c><![CDATA[===== ALIVE Thu May 15 10:13:36 MEST 2003]]></c>, this can be changed, see below.</p>
+===== <date-time-string> ]]></code>
+
+ <p><c><![CDATA[date-time-string]]></c> is the date and time the message is
+ written, default in local time (can be changed to UTC if needed).
+ It is formatted with the ANSI-C function <c><![CDATA[strftime]]></c>
+ using the format string <c><![CDATA[%a %b %e %T %Z %Y]]></c>, which
+ produces messages like
+ <c><![CDATA[===== ALIVE Thu May 15 10:13:36 MEST 2003]]></c>; this can
+ be changed, see the next section.</p>
</section>
<section>
- <title>Environment variables</title>
- <p>The following environment variables are recognized by run_erl
- and change the logging behavior. Also see the notes above to get
- more info on how the log behaves.</p>
+ <marker id="environment_variables"/>
+ <title>Environment Variables</title>
+ <p>The following environment variables are recognized by <c>run_erl</c>
+ and change the logging behavior. For more information, see the previous
+ section.</p>
+
<taglist>
- <tag>RUN_ERL_LOG_ALIVE_MINUTES</tag>
- <item>How long to wait for output (in minutes) before writing an
- "ALIVE" message to the log. Default is 15, can never be less
- than 1.</item>
- <tag>RUN_ERL_LOG_ACTIVITY_MINUTES</tag>
- <item>How long erlang need to be inactive before output will be
- preceded with a timestamp. Default is
- RUN_ERL_LOG_ALIVE_MINUTES div 3, but never less than 1.</item>
- <tag>RUN_ERL_LOG_ALIVE_FORMAT</tag>
- <item>Specifies another format string to be used in the strftime
- C library call. i.e specifying this to <c><![CDATA["%e-%b-%Y, %T %Z"]]></c>
- will give log messages with timestamps looking like
- <c><![CDATA[15-May-2003, 10:23:04 MET]]></c> etc. See the documentation
- for the C library function strftime for more
- information. Default is <c><![CDATA["%a %b %e %T %Z %Y"]]></c>.</item>
- <tag>RUN_ERL_LOG_ALIVE_IN_UTC</tag>
- <item>If set to anything else than "0", it will make all
- times displayed by run_erl to be in UTC (GMT,CET,MET, without
- DST), rather than
- in local time. This does not affect data coming from erlang,
- only the logs output directly by run_erl. The application
- <c><![CDATA[sasl]]></c> can be modified accordingly by setting the erlang
- application variable <c><![CDATA[utc_log]]></c> to <c><![CDATA[true]]></c>.</item>
- <tag>RUN_ERL_LOG_GENERATIONS</tag>
- <item>Controls the number of log files written before older
- files are being reused. Default is 5, minimum is 2, maximum is 1000.</item>
- <tag>RUN_ERL_LOG_MAXSIZE</tag>
- <item>The size (in bytes) of a log file before switching to a
- new log file. Default is 100000, minimum is 1000 and maximum is
- approximately 2^30.</item>
- <tag>RUN_ERL_DISABLE_FLOWCNTRL</tag>
- <item>If defined, disables input and output flow control for the pty opend by run_erl.
- Useful if you want to remove any risk of accidentally blocking the flow control
- by hit Ctrl-S (instead of Ctrl-D to detach).
- Which may result in blocking of the entire beam process
- and in the case of running heart as supervisor
- even the heart process will be blocked when writing log message to terminal.
- Leaving the heart process unable to do its work.</item>
+ <tag><c>RUN_ERL_LOG_ALIVE_MINUTES</c></tag>
+ <item>
+ <p>How long to wait for output (in minutes) before writing an
+ "ALIVE" message to the log. Defaults to 15, minimum is 1.</p>
+ </item>
+ <tag><c>RUN_ERL_LOG_ACTIVITY_MINUTES</c></tag>
+ <item>
+ <p>How long Erlang needs to be inactive before output is
+ preceded with a time stamp. Defaults to
+ <c>RUN_ERL_LOG_ALIVE_MINUTES div 3</c>, minimum is 1.</p>
+ </item>
+ <tag><c>RUN_ERL_LOG_ALIVE_FORMAT</c></tag>
+ <item>
+ <p>Specifies another format string to be used in the <c>strftime</c>
+ C library call. That is, specifying this to
+ <c><![CDATA["%e-%b-%Y, %T %Z"]]></c> gives
+ log messages with time stamps like
+ <c><![CDATA[15-May-2003, 10:23:04 MET]]></c>. For more information,
+ see the documentation for the C library function <c>strftime</c>.
+ Defaults to <c><![CDATA["%a %b %e %T %Z %Y"]]></c>.</p>
+ </item>
+ <tag><c>RUN_ERL_LOG_ALIVE_IN_UTC</c></tag>
+ <item>
+ <p>If set to anything else than <c>0</c>, it makes all
+ times displayed by <c>run_erl</c> to be in UTC (GMT, CET, MET,
+ without Daylight Saving Time), rather than in local time.
+ This does not affect data coming from Erlang,
+ only the logs output directly by <c>run_erl</c>. Application
+ SASL can be modified accordingly by setting the Erlang
+ application variable <c><![CDATA[utc_log]]></c> to
+ <c><![CDATA[true]]></c>.</p>
+ </item>
+ <tag><c>RUN_ERL_LOG_GENERATIONS</c></tag>
+ <item>
+ <p>Controls the number of log files written before older
+ files are reused. Defaults to 5, minimum is 2, maximum is 1000.</p>
+ </item>
+ <tag><c>RUN_ERL_LOG_MAXSIZE</c></tag>
+ <item>
+ <p>The size, in bytes, of a log file before switching to a
+ new log file. Defaults to 100000, minimum is 1000, maximum is
+ about 2^30.</p>
+ </item>
+ <tag><c>RUN_ERL_DISABLE_FLOWCNTRL</c></tag>
+ <item>
+ <p>If defined, disables input and output flow control for the pty
+ opend by <c>run_erl</c>. Useful if you want to remove any risk of
+ accidentally blocking the flow control by using Ctrl-S (instead of
+ Ctrl-D to detach), which can result in blocking of the entire Beam
+ process, and in the case of running heart as supervisor even the
+ heart process becomes blocked when writing log message to terminal,
+ leaving the heart process unable to do its work.</p>
+ </item>
</taglist>
</section>
<section>
- <title>SEE ALSO</title>
- <p>start(1), start_erl(1)</p>
+ <title>See Also</title>
+ <p><seealso marker="start"><c>start(1)</c></seealso>,
+ <seealso marker="start_erl"><c>start_erl(1)</c></seealso></p>
</section>
</comref>
diff --git a/erts/doc/src/specs.xml b/erts/doc/src/specs.xml
index 41a3984659..ed6be650e5 100644
--- a/erts/doc/src/specs.xml
+++ b/erts/doc/src/specs.xml
@@ -2,6 +2,7 @@
<specs xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="../specs/specs_erl_prim_loader.xml"/>
<xi:include href="../specs/specs_erlang.xml"/>
+ <xi:include href="../specs/specs_erl_tracer.xml"/>
<xi:include href="../specs/specs_init.xml"/>
<xi:include href="../specs/specs_zlib.xml"/>
</specs>
diff --git a/erts/doc/src/start.xml b/erts/doc/src/start.xml
index 386fbe6e88..6eac47fe94 100644
--- a/erts/doc/src/start.xml
+++ b/erts/doc/src/start.xml
@@ -4,7 +4,7 @@
<comref>
<header>
<copyright>
- <year>1999</year><year>2013</year>
+ <year>1999</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -28,38 +28,46 @@
<docno></docno>
<approved></approved>
<checked></checked>
- <date>99-12-15</date>
+ <date>1999-12-15</date>
<rev></rev>
<file>start.xml</file>
</header>
<com>start</com>
- <comsummary>OTP start script example for Unix</comsummary>
+ <comsummary>OTP start script example for Unix.</comsummary>
<description>
- <p>This describes the <c><![CDATA[start]]></c> script that is an example script on
- how to startup the Erlang system in embedded mode on Unix.</p>
- <p>You can read more about the use in the <c><![CDATA[Embedded System User's Guide]]></c>.</p>
+ <p>The <c><![CDATA[start]]></c> script is an example script on
+ how to start up the Erlang system in embedded mode on Unix.</p>
+
+ <p>For more information about the use, see the
+ <seealso marker="doc/embedded:embedded_solaris">
+ Embedded System User's Guide</seealso> in System Documentation.</p>
</description>
+
<funcs>
<func>
<name>start [ data_file ]</name>
- <fsummary>This is an example script on how to startup the Erlang system in embedded mode on Unix.</fsummary>
+ <fsummary>Example script on how to start up the Erlang system in embedded
+ mode on Unix.</fsummary>
<desc>
- <p>In the example there is one argument</p>
+ <p>Argument:</p>
<taglist>
- <tag>data_file</tag>
- <item>Optional, specifies what <c><![CDATA[start_erl.data]]></c> file
- to use.</item>
+ <tag><c>data_file</c></tag>
+ <item>
+ <p>Optional. Specifies what <c><![CDATA[start_erl.data]]></c> file
+ to use.</p>
+ </item>
</taglist>
- <p>There is also an environment variable <c><![CDATA[RELDIR]]></c> that can
- be set prior to calling this example that set the directory
+ <p>Environment variable <c><![CDATA[RELDIR]]></c> can
+ be set before calling this example, which sets the directory
where to find the release files.</p>
</desc>
</func>
</funcs>
<section>
- <title>SEE ALSO</title>
- <p>run_erl(1), start_erl(1)</p>
+ <title>See Also</title>
+ <p><seealso marker="run_erl"><c>run_erl(1)</c></seealso>,
+ <seealso marker="start_erl"><c>start_erl(1)</c></seealso></p>
</section>
</comref>
diff --git a/erts/doc/src/start_erl.xml b/erts/doc/src/start_erl.xml
index 62610b43b0..4887d4606e 100644
--- a/erts/doc/src/start_erl.xml
+++ b/erts/doc/src/start_erl.xml
@@ -4,7 +4,7 @@
<comref>
<header>
<copyright>
- <year>1998</year><year>2013</year>
+ <year>1998</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -28,117 +28,142 @@
<docno></docno>
<approved></approved>
<checked></checked>
- <date>98-08-05</date>
+ <date>1998-08-05</date>
<rev></rev>
<file>start_erl.xml</file>
</header>
<com>start_erl</com>
- <comsummary>Start Erlang for embedded systems on Windows NT&reg;</comsummary>
+ <comsummary>Start Erlang for embedded systems on Windows systems.</comsummary>
<description>
- <p>This describes the <c><![CDATA[start_erl]]></c> program specific to Windows
- NT. Although there exists programs with the same name on other
- platforms, their functionality is not the same.</p>
- <p>The <c><![CDATA[start_erl]]></c> program is distributed both in compiled
+ <p>The <c><![CDATA[start_erl]]></c> program is specific to
+ Windows NT/2000/XP (and later versions of Windows).
+ Although there are programs with the same name on other
+ platforms, their functionality is different.</p>
+
+ <p>This program is distributed both in compiled
form (under &lt;Erlang root&gt;\\erts-&lt;version&gt;\\bin) and
- in source form (under &lt;Erlang
- root&gt;\\erts-&lt;version&gt;\\src).
- The purpose of the source code is to make it possible to easily
- customize the program for local needs, such as cyclic restart
- detection etc. There is also a "make"-file, written for the
- <c><![CDATA[nmake]]></c> program distributed with Microsoft&reg; Visual
- C++&reg;. The program can however be compiled with
- any Win32 C compiler (possibly with slight modifications).</p>
- <p>The purpose of the program is to aid release handling on
- Windows NT&reg;. The program should be called by the
+ in source form (under &lt;Erlang root&gt;\\erts-&lt;version&gt;\\src).
+ The purpose of the source code is to ease customization of the
+ program for local needs, such as cyclic restart
+ detection. There is also a "make"-file, written for the
+ <c><![CDATA[nmake]]></c> program distributed with Microsoft Visual
+ C++. This program can, however, be compiled with
+ any Win32 C compiler (possibly with minor modifications).</p>
+
+ <p>This program aids release handling on Windows systems.
+ The program is to be called by the
<c><![CDATA[erlsrv]]></c> program, read up the release data file
- start_erl.data and start Erlang. Certain options to start_erl
- are added and removed by the release handler during upgrade with
- emulator restart (more specifically the <c><![CDATA[-data]]></c> option).</p>
+ <c>start_erl.data</c>, and start Erlang. Some options to
+ <c>start_erl</c> are added and removed by the release handler
+ during upgrade with emulator restart (more specifically option
+ <c><![CDATA[-data]]></c>).</p>
</description>
+
<funcs>
<func>
<name>start_erl [&lt;erl options>] ++ [&lt;start_erl options>]</name>
- <fsummary>Start the Erlang emulator with the correct release data</fsummary>
+ <fsummary>Start the Erlang emulator with the correct release data.
+ </fsummary>
<desc>
- <p>The <c><![CDATA[start_erl]]></c> program in its original form
+ <p>The <c><![CDATA[start_erl]]></c> program in its original form
recognizes the following options:</p>
<taglist>
- <tag>++</tag>
- <item>Mandatory, delimits start_erl options from normal Erlang
- options. Everything on the command line <em>before</em> the
- <c><![CDATA[++]]></c> is interpreted as options to be sent to the
- <c><![CDATA[erl]]></c> program. Everything <em>after</em><c><![CDATA[++]]></c> is
- interpreted as options to <c><![CDATA[start_erl]]></c> itself.</item>
- <tag>-reldir &lt;release root&gt;</tag>
-
- <item>Mandatory if the environment variable
- <c><![CDATA[RELDIR]]></c> is not specified and no
- <c>-rootdir</c> option is given. Tells start_erl where the
- root of the release tree is placed in the file-system (typically
- &lt;Erlang root&gt;\\releases). The
- <c><![CDATA[start_erl.data]]></c> file is expected to be
- placed in this directory (if not otherwise specified). If
- only the <c>-rootdir</c> option is given, the directory is
- assumed to be &lt;Erlang root&gt;\\releases.</item>
-
- <tag>-rootdir &lt;Erlang root directory&gt;</tag>
-
- <item>Mandatory if <c>-reldir</c> is not given and there is
- no <c><![CDATA[RELDIR]]></c> in the environment. This
- specifies the Erlang installation root directory (under
- which the <c>lib</c>, <c>releases</c> and
- <c>erts-&lt;Version&gt;</c> directories are placed). If only
- <c>-reldir</c> (or the environment variable
- <c><![CDATA[RELDIR]]></c>) is given, the Erlang root is assumed to
- be the directory exactly one level above the release
- directory.</item>
-
- <tag>-data &lt;data file name&gt;</tag>
- <item>Optional, specifies another data file than start_erl.data
- in the &lt;release root&gt;. It is specified relative to the
- &lt;release root&gt; or absolute (including drive letter
- etc.). This option is used by the release handler during
- upgrade and should not be used during normal
- operation. The release data file should not normally be
- named differently.</item>
- <tag>-bootflags &lt;boot flags file name&gt;</tag>
- <item>Optional, specifies a file name relative to actual release
- directory (that is the subdirectory of &lt;release
- root&gt; where the <c><![CDATA[.boot]]></c> file etc. are placed).
- The contents of this file is appended to the command line
- when Erlang is started. This makes it easy to start the
- emulator with different options for different releases.</item>
+ <tag><c>++</c></tag>
+ <item>
+ <p>Mandatory. Delimits <c>start_erl</c> options from normal Erlang
+ options. Everything on the command line <em>before</em>
+ <c><![CDATA[++]]></c> is interpreted as options to be sent to the
+ <c><![CDATA[erl]]></c> program. Everything <em>after</em>
+ <c><![CDATA[++]]></c> is interpreted as options to
+ <c><![CDATA[start_erl]]></c> itself.</p>
+ </item>
+ <tag><c>-reldir &lt;release root&gt;</c></tag>
+ <item>
+ <p>Mandatory if environment variable
+ <c><![CDATA[RELDIR]]></c> is not specified and no
+ <c>-rootdir</c> option is specified. Tells <c>start_erl</c> where
+ the root of the release tree is located in the file system
+ (typically &lt;Erlang root&gt;\\releases). The
+ <c><![CDATA[start_erl.data]]></c> file is expected to be
+ located in this directory (unless otherwise specified). If
+ only option <c>-rootdir</c> is specified, the directory is
+ assumed to be &lt;Erlang root&gt;\\releases.</p>
+ </item>
+ <tag><c>-rootdir &lt;Erlang root directory&gt;</c></tag>
+ <item>
+ <p>Mandatory if <c>-reldir</c> is not specified and no
+ <c><![CDATA[RELDIR]]></c> exists in the environment. This
+ specifies the Erlang installation root directory (under
+ which the <c>lib</c>, <c>releases</c>, and
+ <c>erts-&lt;Version&gt;</c> directories are located). If only
+ <c>-reldir</c> (or environment variable <c><![CDATA[RELDIR]]></c>)
+ is specified, the Erlang root is assumed to
+ be the directory exactly one level above the release
+ directory.</p>
+ </item>
+ <tag><c>-data &lt;data file name&gt;</c></tag>
+ <item>
+ <p>Optional. Specifies another data file than <c>start_erl.data</c>
+ in the &lt;release root&gt;. It is specified relative to the
+ &lt;release root&gt; or absolute (including drive letter, and so
+ on). This option is used by the release handler during
+ upgrade and is not to be used during normal
+ operation. Normally the release data file is not to be
+ named differently.</p>
+ </item>
+ <tag><c>-bootflags &lt;boot flags file name&gt;</c></tag>
+ <item>
+ <p>Optional. Specifies a file name relative to the release
+ directory (that is, the subdirectory of &lt;release root&gt;
+ where the <c><![CDATA[.boot]]></c> file and others are located).
+ The contents of this file is appended to the command line
+ when Erlang is started. This makes it easy to start the
+ emulator with different options for different releases.</p>
+ </item>
</taglist>
</desc>
</func>
</funcs>
<section>
- <title>NOTES</title>
- <p>As the source code is distributed, it can easily be modified to
- accept other options. The program must still accept the
- <c><![CDATA[-data]]></c> option with the semantics described above for the
- release handler to work correctly.</p>
- <p>The Erlang emulator is found by examining the registry keys for
- the emulator version specified in the release data file. The new
- emulator needs to be properly installed before the upgrade for
- this to work.</p>
- <p>Although the program is located together with files specific to
- emulator version, it is not expected to be specific to the
- emulator version. The release handler does <em>not</em> change the
- <c><![CDATA[-machine]]></c> option to <c><![CDATA[erlsrv]]></c> during emulator restart.
- Place the (possibly customized) <c><![CDATA[start_erl]]></c> program so that
- it is not overwritten during upgrade. </p>
- <p>The <c><![CDATA[erlsrv]]></c> program's default options are not
- sufficient for release handling. The machine <c><![CDATA[erlsrv]]></c>
- starts should be specified as the <c><![CDATA[start_erl]]></c> program and
- the arguments should contain the <c><![CDATA[++]]></c> followed by desired
- options.</p>
+ <title>Notes</title>
+ <list type="bulleted">
+ <item>
+ <p>As the source code is distributed, it can easily be modified to
+ accept other options. The program must still accept option
+ <c><![CDATA[-data]]></c> with the semantics described above for the
+ release handler to work correctly.</p>
+ </item>
+ <item>
+ <p>The Erlang emulator is found by examining the registry keys for
+ the emulator version specified in the release data file. The new
+ emulator must be properly installed before the upgrade for
+ this to work.</p>
+ </item>
+ <item>
+ <p>Although the program is located together with files specific to the
+ emulator version, it is not expected to be specific to the
+ emulator version. The release handler does <em>not</em> change option
+ <c><![CDATA[-machine]]></c> to <c><![CDATA[erlsrv]]></c> during
+ emulator restart. Locate the (possibly customized)
+ <c><![CDATA[start_erl]]></c> program so that it is not overwritten
+ during upgrade.</p>
+ </item>
+ <item>
+ <p>The default options of the <c><![CDATA[erlsrv]]></c> program are not
+ sufficient for release handling. The machine started by
+ <c><![CDATA[erlsrv]]></c> is be specified as the
+ <c><![CDATA[start_erl]]></c> program and the arguments are to contain
+ <c><![CDATA[++]]></c> followed by the desired options.</p>
+ </item>
+ </list>
</section>
<section>
- <title>SEE ALSO</title>
- <p>erlsrv(1), release_handler(3)</p>
+ <title>See Also</title>
+ <p><seealso marker="erlsrv"><c>erlsrv(1)</c></seealso>,
+ <seealso marker="sasl:release_handler">
+ <c>release_handler(3)</c></seealso></p>
</section>
</comref>
diff --git a/erts/doc/src/time_correction.xml b/erts/doc/src/time_correction.xml
index 236fe679cb..77e7a40529 100644
--- a/erts/doc/src/time_correction.xml
+++ b/erts/doc/src/time_correction.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1999</year><year>2015</year>
+ <year>1999</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -35,12 +35,12 @@
<section>
<title>New Extended Time Functionality</title>
- <note><p>As of OTP 18 (<c>ERTS</c> version 7.0) the time functionality of
- Erlang has been extended. This includes a
+ <note><p>As from Erlang/OTP 18 (ERTS 7.0) the time functionality
+ has been extended. This includes a
<seealso marker="#The_New_Time_API">new API</seealso>
for time and
<seealso marker="#Time_Warp_Modes">time warp
- modes</seealso> that alter the system behavior when
+ modes</seealso> that change the system behavior when
system time changes.</p>
<p>The <seealso marker="#No_Time_Warp_Mode">default
@@ -106,14 +106,18 @@
<section>
<title>POSIX Time</title>
<p>Time since
- <url href="http://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap03.html#tag_21_03_00_17">Epoch</url>.
+ <url href="http://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap03.html#tag_21_03_00_17">
+ Epoch</url>.
Epoch is defined to be 00:00:00 <seealso marker="#UTC">UTC</seealso>,
1970-01-01.
- <url href="http://pubs.opengroup.org/onlinepubs/009604499/basedefs/xbd_chap04.html#tag_04_14">A day in POSIX time</url>
- is defined to be exactly 86400 seconds long. Strangely enough
+ <url href="http://pubs.opengroup.org/onlinepubs/009604499/basedefs/xbd_chap04.html#tag_04_14">
+ A day in POSIX time</url>
+ is defined to be exactly 86400 seconds long. Strangely enough,
Epoch is defined to be a time in UTC, and UTC has another
definition of how long a day is. Quoting the Open Group
- <url href="http://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_15">"POSIX time is therefore not necessarily UTC, despite its appearance"</url>.
+ <url href="http://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_15">
+ "POSIX time is therefore not necessarily UTC, despite its
+ appearance"</url>.
The effect of this is that when an UTC leap second is
inserted, POSIX time either stops for a second, or repeats the
last second. If an UTC leap second would be deleted (which has not
@@ -157,7 +161,8 @@
<p>The operating systems view of
<seealso marker="#POSIX_Time">POSIX time</seealso>. To
retrieve it, call
- <seealso marker="kernel:os#system_time/0"><c>os:system_time()</c></seealso>.
+ <seealso marker="kernel:os#system_time/0">
+ <c>os:system_time()</c></seealso>.
This may or may not be an accurate view of POSIX time. This time
may typically be adjusted both backwards and forwards without
limitation. That is, <seealso marker="#Time_Warp">time warps</seealso>
@@ -165,25 +170,26 @@
<p>To get information about the Erlang runtime
system's source of OS system time, call
- <seealso marker="erlang#system_info_os_system_time_source"><c>erlang:system_info(os_system_time_source)</c></seealso>.</p>
+ <seealso marker="erlang#system_info_os_system_time_source">
+ <c>erlang:system_info(os_system_time_source)</c></seealso>.</p>
</section>
<marker id="OS_Monotonic_Time"/>
<section>
<title>OS Monotonic Time</title>
- <p>A monotonically increasing time provided by the operating
- system. This time does not leap and has a relatively steady
+ <p>A monotonically increasing time provided by the OS.
+ This time does not leap and has a relatively steady
frequency although not completely correct. However, it is not
uncommon that OS monotonic time stops if the system is
suspended. This time typically increases since some
unspecified point in time that is not connected to
<seealso marker="#OS_System_Time">OS system time</seealso>.
- This type of time is not necessarily provided by all
- operating systems.</p>
+ This type of time is not necessarily provided by all OSs.</p>
<p>To get information about the Erlang
runtime system's source of OS monotonic time, call
- <seealso marker="erlang#system_info_os_monotonic_time_source"><c>erlang:system_info(os_monotonic_time_source)</c></seealso>.</p>
+ <seealso marker="erlang#system_info_os_monotonic_time_source">
+ <c>erlang:system_info(os_monotonic_time_source)</c></seealso>.</p>
</section>
<marker id="Erlang_System_Time"/>
@@ -192,7 +198,8 @@
<p>The Erlang runtime systems view of
<seealso marker="#POSIX_Time">POSIX time</seealso>. To
retrieve it, call
- <seealso marker="erlang#system_time/0"><c>erlang:system_time()</c></seealso>.</p>
+ <seealso marker="erlang#system_time/0">
+ <c>erlang:system_time()</c></seealso>.</p>
<p>This time may or may not be an accurate view of POSIX time,
and may
@@ -211,8 +218,8 @@
<p>A monotonically increasing time provided by the
Erlang runtime system. Erlang monotonic time increases since
some unspecified point in time. To retrieve it, call
- <seealso marker="erlang#monotonic_time/0"><c>erlang:monotonic_time()</c></seealso>.
- </p>
+ <seealso marker="erlang#monotonic_time/0">
+ <c>erlang:monotonic_time()</c></seealso>.</p>
<p>The <seealso marker="#Time_Accuracy">accuracy</seealso> and
<seealso marker="#Time_Precision">precision</seealso> of Erlang
@@ -225,7 +232,8 @@
<item>Accuracy and precision of
<seealso marker="#OS_System_Time">OS system time</seealso>
</item>
- <item><seealso marker="#Time_Warp_Modes">time warp mode</seealso> used
+ <item><seealso marker="#Time_Warp_Modes">
+ time warp mode</seealso> used
</item>
</list>
@@ -238,16 +246,17 @@
time is the "time engine" that is used for more or less
everything that has anything to do with time. All timers,
regardless of it is a <c>receive ... after</c> timer, BIF timer,
- or a timer in the <c>timer</c> module, are triggered
- relative Erlang monotonic time. Even
+ or a timer in the
+ <seealso marker="stdlib:timer"><c>timer(3)</c></seealso>
+ module, are triggered relative Erlang monotonic time. Even
<seealso marker="#Erlang_System_Time">Erlang system
time</seealso> is based on Erlang monotonic time.
By adding current Erlang monotonic time with current time
offset, you get current Erlang system time.</p>
- <p>To retrieve current time offset, call
- <seealso marker="erlang#time_offset/0"><c>erlang:time_offset/0</c></seealso>.
- </p>
+ <p>To retrieve the current time offset, call
+ <seealso marker="erlang#time_offset/0">
+ <c>erlang:time_offset/0</c></seealso>.</p>
</section>
</section>
@@ -278,7 +287,7 @@
less potent time-keeper than an atomic clock is used.</p>
<p>However, NTP is not fail-safe. The NTP server can be unavailable,
- <c>ntp.conf</c> can be wrongly configured, or your computer may
+ <c>ntp.conf</c> can be wrongly configured, or your computer can
sometimes be disconnected from Internet. Furthermore, you can have a
user (or even system administrator) who thinks the correct
way to handle Daylight Saving Time is to adjust the clock one
@@ -327,16 +336,18 @@
implementation in the Erlang runtime system using
OS monotonic time. To check if your system has support
for OS monotonic time, call
- <seealso marker="erlang#system_info_os_monotonic_time_source"><c>erlang:system_info(os_monotonic_time_source)</c></seealso>.
+ <seealso marker="erlang#system_info_os_monotonic_time_source">
+ <c>erlang:system_info(os_monotonic_time_source)</c></seealso>.
To check if time correction is enabled on your system, call
- <seealso marker="erlang#system_info_time_correction"><c>erlang:system_info(time_correction)</c></seealso>.</p>
+ <seealso marker="erlang#system_info_time_correction">
+ <c>erlang:system_info(time_correction)</c></seealso>.</p>
<p>To enable or disable time correction, pass command-line argument
- <seealso marker="erl#+c"><c>+c [true|false]</c></seealso>
- to <c>erl</c>.</p>
+ <seealso marker="erl#+c"><c>+c [true|false]</c></seealso> to
+ <seealso marker="erl"><c>erl(1)</c></seealso>.</p>
<p>If time correction is disabled, Erlang monotonic time
- may warp forwards or stop, or even freeze for extended
+ can warp forwards or stop, or even freeze for extended
periods of time. There are then no guarantees that the frequency
of the Erlang monotonic clock is accurate or stable.</p>
@@ -369,7 +380,7 @@
<c>erlang:now/0</c> are suboptimal</em> from a performance
and scalability perspective. So you really want to replace
the use of it with other functionality. For examples
- of how to replace the use of <c>erlang:now/0</c>, see Section
+ of how to replace the use of <c>erlang:now/0</c>, see section
<seealso marker="#Dos_and_Donts">How to Work with the New
API</seealso>.</p>
</section>
@@ -378,7 +389,7 @@
<title>Time Warp Modes</title>
<marker id="Time_Warp_Modes"/>
<p>Current <seealso marker="#Erlang_System_Time">Erlang system
- time</seealso> is determined by adding current
+ time</seealso> is determined by adding the current
<seealso marker="erlang#monotonic_time/0">Erlang monotonic time</seealso>
with current
<seealso marker="erlang#time_offset/0">time offset</seealso>. The
@@ -387,8 +398,8 @@
<p>To set the time warp mode, pass command-line argument
<seealso marker="erl#+C_"><c>+C
- [no_time_warp|single_time_warp|multi_time_warp]</c></seealso>
- to <c>erl</c>.</p>
+ [no_time_warp|single_time_warp|multi_time_warp]</c></seealso> to
+ <seealso marker="erl"><c>erl(1)</c></seealso>.</p>
<marker id="No_Time_Warp_Mode"/>
<section>
@@ -397,8 +408,8 @@
and does not change later. This is the default behavior, but
not because it is the best mode (which it is not). It is
default <em>only</em> because this is how the runtime system
- behaved until <c>ERTS</c> 7.0.
- Ensure that your Erlang code that may execute during a time
+ behaved until ERTS 7.0.
+ Ensure that your Erlang code that can execute during a time
warp is <seealso marker="#Time_Warp_Safe_Code">time warp
safe</seealso> before enabling other modes.</p>
@@ -422,8 +433,8 @@
<marker id="Single_Time_Warp_Mode"/>
<section>
<title>Single Time Warp Mode</title>
- <p>This mode is more or less a backwards compatibility mode
- as of its introduction.</p>
+ <p>This mode is more or less a backward compatibility mode
+ as from its introduction.</p>
<p>On an embedded system it is not uncommon that the system
has no power supply, not even a battery, when it is
@@ -475,11 +486,12 @@
<item>
<p>This phase begins when the user finalizes the time
offset by calling
- <seealso marker="erlang#system_flag_time_offset"><c>erlang:system_flag(time_offset, finalize)</c></seealso>.
+ <seealso marker="erlang#system_flag_time_offset">
+ <c>erlang:system_flag(time_offset, finalize)</c></seealso>.
The finalization can only be performed once.</p>
<p>During finalization, the time offset is adjusted and
- fixated so that current Erlang system time aligns with
+ fixed so that current Erlang system time aligns with the
current OS system time. As the time offset can
change during the finalization, Erlang system time
can do a time warp at this point. The time offset is
@@ -488,7 +500,7 @@
correction from now on also makes adjustments
to align Erlang system time with OS system
time. When the system is in the final phase, it behaves
- exactly as in the <seealso marker="#No_Time_Warp_Mode">no
+ exactly as in <seealso marker="#No_Time_Warp_Mode">no
time warp mode</seealso>.</p>
</item>
</taglist>
@@ -520,7 +532,7 @@
may behave very bad.</p>
<p>Assuming that these requirements are fulfilled,
- time correction is enabled, and that OS system time
+ time correction is enabled, and OS system time
is adjusted using a time adjustment protocol such as NTP,
only small adjustments of Erlang monotonic
time are needed to keep system times
@@ -529,9 +541,10 @@
inserted (or deleted) leap seconds.</p>
<warning><p>To use this mode, ensure that
- all Erlang code that will execute in both phases are
+ all Erlang code that will execute in both phases is
<seealso marker="#Time_Warp_Safe_Code">time warp
safe</seealso>.</p>
+
<p>Code executing only in the final phase does not have
to be able to cope with the time warp.</p></warning>
</section>
@@ -542,13 +555,13 @@
<p><em>Multi-time warp mode in combination with time
correction is the preferred configuration</em>. This as
the Erlang runtime system have better performance, scale
- better, and behave better on almost all platforms. In
- addition, the accuracy and precision of time measurements
+ better, and behave better on almost all platforms.
+ Also, the accuracy and precision of time measurements
are better. Only Erlang runtime systems executing on
ancient platforms benefit from another configuration.</p>
- <p>The time offset may change at any time without limitations.
- That is, Erlang system time may perform time warps both
+ <p>The time offset can change at any time without limitations.
+ That is, Erlang system time can perform time warps both
forwards and backwards at <em>any</em> time. As we align
Erlang system time with OS system time by changing
the time offset, we can enable a time correction that tries
@@ -582,8 +595,8 @@
such issues. To improve this, the new API spreads different
functionality over multiple functions.</p>
- <p>To be backwards compatible, <c>erlang:now/0</c>
- remains as is, but <em>you are strongly discouraged from using
+ <p>To be backward compatible, <c>erlang:now/0</c>
+ remains "as is", but <em>you are strongly discouraged from using
it</em>. Many use cases of <c>erlang:now/0</c>
prevents you from using the new
<seealso marker="#Multi_Time_Warp_Mode">multi-time warp
@@ -597,38 +610,60 @@
<p>The new API consists of the following new BIFs:</p>
<list>
- <item><p><seealso marker="erlang#convert_time_unit/3"><c>erlang:convert_time_unit/3</c></seealso></p></item>
- <item><p><seealso marker="erlang#monotonic_time/0"><c>erlang:monotonic_time/0</c></seealso></p></item>
- <item><p><seealso marker="erlang#monotonic_time/1"><c>erlang:monotonic_time/1</c></seealso></p></item>
- <item><p><seealso marker="erlang#system_time/0"><c>erlang:system_time/0</c></seealso></p></item>
- <item><p><seealso marker="erlang#system_time/1"><c>erlang:system_time/1</c></seealso></p></item>
- <item><p><seealso marker="erlang#time_offset/0"><c>erlang:time_offset/0</c></seealso></p></item>
- <item><p><seealso marker="erlang#time_offset/1"><c>erlang:time_offset/1</c></seealso></p></item>
- <item><p><seealso marker="erlang#timestamp/0"><c>erlang:timestamp/0</c></seealso></p></item>
- <item><p><seealso marker="erlang#unique_integer/0"><c>erlang:unique_integer/0</c></seealso></p></item>
- <item><p><seealso marker="erlang#unique_integer/1"><c>erlang:unique_integer/1</c></seealso></p></item>
- <item><p><seealso marker="kernel:os#system_time/0"><c>os:system_time/0</c></seealso></p></item>
- <item><p><seealso marker="kernel:os#system_time/1"><c>os:system_time/1</c></seealso></p></item>
+ <item><p><seealso marker="erlang#convert_time_unit/3">
+ <c>erlang:convert_time_unit/3</c></seealso></p></item>
+ <item><p><seealso marker="erlang#monotonic_time/0">
+ <c>erlang:monotonic_time/0</c></seealso></p></item>
+ <item><p><seealso marker="erlang#monotonic_time/1">
+ <c>erlang:monotonic_time/1</c></seealso></p></item>
+ <item><p><seealso marker="erlang#system_time/0">
+ <c>erlang:system_time/0</c></seealso></p></item>
+ <item><p><seealso marker="erlang#system_time/1">
+ <c>erlang:system_time/1</c></seealso></p></item>
+ <item><p><seealso marker="erlang#time_offset/0">
+ <c>erlang:time_offset/0</c></seealso></p></item>
+ <item><p><seealso marker="erlang#time_offset/1">
+ <c>erlang:time_offset/1</c></seealso></p></item>
+ <item><p><seealso marker="erlang#timestamp/0">
+ <c>erlang:timestamp/0</c></seealso></p></item>
+ <item><p><seealso marker="erlang#unique_integer/0">
+ <c>erlang:unique_integer/0</c></seealso></p></item>
+ <item><p><seealso marker="erlang#unique_integer/1">
+ <c>erlang:unique_integer/1</c></seealso></p></item>
+ <item><p><seealso marker="kernel:os#system_time/0">
+ <c>os:system_time/0</c></seealso></p></item>
+ <item><p><seealso marker="kernel:os#system_time/1">
+ <c>os:system_time/1</c></seealso></p></item>
</list>
- <p>The new API also consists of extensions of the following existing BIFs:</p>
+ <p>The new API also consists of extensions of the following existing BIFs:
+ </p>
<list>
- <item><p><seealso marker="erlang#monitor/2"><c>erlang:monitor(time_offset, clock_service)</c></seealso></p></item>
- <item><p><seealso marker="erlang#system_flag_time_offset"><c>erlang:system_flag(time_offset, finalize)</c></seealso></p></item>
- <item><p><seealso marker="erlang#system_info_os_monotonic_time_source"><c>erlang:system_info(os_monotonic_time_source)</c></seealso></p></item>
- <item><p><seealso marker="erlang#system_info_os_system_time_source"><c>erlang:system_info(os_system_time_source)</c></seealso></p></item>
- <item><p><seealso marker="erlang#system_info_time_offset"><c>erlang:system_info(time_offset)</c></seealso></p></item>
- <item><p><seealso marker="erlang#system_info_time_warp_mode"><c>erlang:system_info(time_warp_mode)</c></seealso></p></item>
- <item><p><seealso marker="erlang#system_info_time_correction"><c>erlang:system_info(time_correction)</c></seealso></p></item>
- <item><p><seealso marker="erlang#system_info_start_time"><c>erlang:system_info(start_time)</c></seealso></p></item>
- <item><p><seealso marker="erlang#system_info_end_time"><c>erlang:system_info(end_time)</c></seealso></p></item>
+ <item><p><seealso marker="erlang#monitor/2">
+ <c>erlang:monitor(time_offset, clock_service)</c></seealso></p></item>
+ <item><p><seealso marker="erlang#system_flag_time_offset">
+ <c>erlang:system_flag(time_offset, finalize)</c></seealso></p></item>
+ <item><p><seealso marker="erlang#system_info_os_monotonic_time_source">
+ <c>erlang:system_info(os_monotonic_time_source)</c></seealso></p></item>
+ <item><p><seealso marker="erlang#system_info_os_system_time_source">
+ <c>erlang:system_info(os_system_time_source)</c></seealso></p></item>
+ <item><p><seealso marker="erlang#system_info_time_offset">
+ <c>erlang:system_info(time_offset)</c></seealso></p></item>
+ <item><p><seealso marker="erlang#system_info_time_warp_mode">
+ <c>erlang:system_info(time_warp_mode)</c></seealso></p></item>
+ <item><p><seealso marker="erlang#system_info_time_correction">
+ <c>erlang:system_info(time_correction)</c></seealso></p></item>
+ <item><p><seealso marker="erlang#system_info_start_time">
+ <c>erlang:system_info(start_time)</c></seealso></p></item>
+ <item><p><seealso marker="erlang#system_info_end_time">
+ <c>erlang:system_info(end_time)</c></seealso></p></item>
</list>
<marker id="The_New_Erlang_Monotonic_Time"/>
<section>
<title>New Erlang Monotonic Time</title>
- <p>Erlang monotonic time as such is new as of <c>ERTS</c> 7.0.
+ <p>Erlang monotonic time as such is new as from ERTS 7.0.
It is introduced to detach time measurements, such as elapsed
time from calendar time. In many use cases there is a need to
measure elapsed time or specify a time relative to another point
@@ -649,7 +684,7 @@
modes, and only fully separated in the
<seealso marker="#Multi_Time_Warp_Mode">multi-time
warp mode</seealso>. All other modes than the
- multi-time warp mode are for backwards
+ multi-time warp mode are for backward
compatibility reasons. When using these modes, the
accuracy of Erlang monotonic time suffer, as
the adjustments of Erlang monotonic time in these
@@ -672,15 +707,17 @@
<p>To be able to react to a change in Erlang
system time, you must be able to detect that it
- happened. The change in Erlang system time occurs when
+ happened. The change in Erlang system time occurs when the
current time offset is changed. We have therefore
introduced the possibility to monitor the time offset using
- <seealso marker="erlang#monitor/2"><c>erlang:monitor(time_offset, clock_service)</c></seealso>.
+ <seealso marker="erlang#monitor/2">
+ <c>erlang:monitor(time_offset, clock_service)</c></seealso>.
A process monitoring the time
offset is sent a message on the following format
when the time offset is changed:</p>
- <code type="none">{'CHANGE', MonitorReference, time_offset, clock_service, NewTimeOffset}</code>
+ <code type="none">
+{'CHANGE', MonitorReference, time_offset, clock_service, NewTimeOffset}</code>
</section>
<marker id="Unique_Values"/>
@@ -690,8 +727,8 @@
produces unique and strictly monotonically increasing
values. To detach this functionality from
time measurements, we have introduced
- <seealso marker="erlang#unique_integer/1"><c>erlang:unique_integer()</c></seealso>.
- </p>
+ <seealso marker="erlang#unique_integer/1">
+ <c>erlang:unique_integer()</c></seealso>.</p>
</section>
<marker id="Dos_and_Donts"/>
@@ -699,27 +736,28 @@
<title>How to Work with the New API</title>
<p>Previously <c>erlang:now/0</c> was the only option for doing
many things. This section deals with some things that
- <c>erlang:now/0</c> can be used for, and how you are to
- these using the new API.</p>
+ <c>erlang:now/0</c> can be used for, and how you use the new API.</p>
<marker id="Dos_and_Donts_Retrieve_Erlang_System_Time"/>
<section>
<title>Retrieve Erlang System Time</title>
<dont>
<p>
- Use <c>erlang:now/0</c> to retrieve current Erlang system time.
+ Use <c>erlang:now/0</c> to retrieve the current Erlang system time.
</p>
</dont>
<do>
<p>
Use
- <seealso marker="erlang#system_time/1"><c>erlang:system_time/1</c></seealso>
- to retrieve current Erlang system time on the
+ <seealso marker="erlang#system_time/1">
+ <c>erlang:system_time/1</c></seealso>
+ to retrieve the current Erlang system time on the
<seealso marker="erlang#type_time_unit">time unit</seealso>
of your choice.</p>
- <p>If you want the same format as returned by <c>erlang:now/0</c>, use
- <seealso marker="erlang#timestamp/0"><c>erlang:timestamp/0</c></seealso>.
- </p>
+ <p>If you want the same format as returned by <c>erlang:now/0</c>,
+ use <seealso marker="erlang#timestamp/0">
+ <c>erlang:timestamp/0</c></seealso>.
+ </p>
</do>
</section>
@@ -728,28 +766,31 @@
<title>Measure Elapsed Time</title>
<dont>
<p>
- Take timestamps with <c>erlang:now/0</c> and calculate
+ Take time stamps with <c>erlang:now/0</c> and calculate
the difference in time with
- <seealso marker="stdlib:timer#now_diff/2"><c>timer:now_diff/2</c></seealso>.
+ <seealso marker="stdlib:timer#now_diff/2">
+ <c>timer:now_diff/2</c></seealso>.
</p>
</dont>
<do>
<p>
- Take timestamps with
- <seealso marker="erlang#monotonic_time/0"><c>erlang:monotonic_time/0</c></seealso>
+ Take time stamps with
+ <seealso marker="erlang#monotonic_time/0">
+ <c>erlang:monotonic_time/0</c></seealso>
and calculate the time difference using ordinary subtraction.
- The result will be in <c>native</c>
+ The result is in <c>native</c>
<seealso marker="erlang#type_time_unit">time unit</seealso>.
If you want to convert the
result to another time unit, you can use
- <seealso marker="erlang#convert_time_unit/3"><c>erlang:convert_time_unit/3</c></seealso>.
- </p>
-
- <p>An easier way to do this is to use
- <seealso marker="erlang#monotonic_time/1"><c>erlang:monotonic_time/1</c></seealso>
+ <seealso marker="erlang#convert_time_unit/3">
+ <c>erlang:convert_time_unit/3</c></seealso>.
+ </p>
+ <p>An easier way to do this is to use
+ <seealso marker="erlang#monotonic_time/1">
+ <c>erlang:monotonic_time/1</c></seealso>
with the desired time unit. However, you can then lose accuracy
and precision.
- </p>
+ </p>
</do>
</section>
@@ -758,7 +799,7 @@
<title>Determine Order of Events</title>
<dont>
<p>
- Determine the order of events by saving a timestamp
+ Determine the order of events by saving a time stamp
with <c>erlang:now/0</c> when the event occurs.
</p>
</dont>
@@ -766,8 +807,9 @@
<p>
Determine the order of events by saving the integer
returned by
- <seealso marker="erlang#unique_integer/1"><c>erlang:unique_integer([monotonic])</c></seealso>
- when the event occurs. These integers will be strictly
+ <seealso marker="erlang#unique_integer/1">
+ <c>erlang:unique_integer([monotonic])</c></seealso>
+ when the event occurs. These integers are strictly
monotonically ordered on current runtime system instance
corresponding to creation time.
</p>
@@ -779,7 +821,7 @@
<title>Determine Order of Events with Time of the Event</title>
<dont>
<p>
- Determine the order of events by saving a timestamp
+ Determine the order of events by saving a time stamp
with <c>erlang:now/0</c> when the event occurs.
</p>
</dont>
@@ -795,18 +837,19 @@ Time = erlang:monotonic_time(),
UMI = erlang:unique_integer([monotonic]),
EventTag = {Time, UMI}</code>
- <p>These tuples will be strictly monotonically ordered
- on current runtime system instance according to
+ <p>These tuples are strictly monotonically ordered
+ on the current runtime system instance according to
creation time. It is important that the
monotonic time is in the first element (the most
- significant element when comparing 2-tuples). Using
+ significant element when comparing two-tuples). Using
the monotonic time in the tuples, you can calculate time
between events.</p>
<p>If you are interested in Erlang system time at the
time when the event occurred, you can also save the time
offset before or after saving the events using
- <seealso marker="erlang#time_offset/0"><c>erlang:time_offset/0</c></seealso>.
+ <seealso marker="erlang#time_offset/0">
+ <c>erlang:time_offset/0</c></seealso>.
Erlang monotonic time added with the time
offset corresponds to Erlang system time.</p>
@@ -814,7 +857,7 @@ EventTag = {Time, UMI}</code>
can change, and you want to get the actual
Erlang system time when the event occurred, you can
save the time offset as a third element in the tuple
- (the least significant element when comparing 3-tuples).</p>
+ (the least significant element when comparing three-tuples).</p>
</do>
</section>
@@ -830,10 +873,12 @@ EventTag = {Time, UMI}</code>
<do>
<p>
Use the value returned from
- <seealso marker="erlang#unique_integer/0"><c>erlang:unique_integer/0</c></seealso>
+ <seealso marker="erlang#unique_integer/0">
+ <c>erlang:unique_integer/0</c></seealso>
to create a name unique on the current runtime system
instance. If you only want positive integers, you can use
- <seealso marker="erlang#unique_integer/1"><c>erlang:unique_integer([positive])</c></seealso>.
+ <seealso marker="erlang#unique_integer/1">
+ <c>erlang:unique_integer([positive])</c></seealso>.
</p>
</do>
</section>
@@ -849,12 +894,15 @@ EventTag = {Time, UMI}</code>
<do>
<p>
Seed random number generation using a combination of
- <seealso marker="erlang#monotonic_time/0"><c>erlang:monotonic_time()</c></seealso>,
- <seealso marker="erlang#time_offset/0"><c>erlang:time_offset()</c></seealso>,
- <seealso marker="erlang#unique_integer/0"><c>erlang:unique_integer()</c></seealso>,
+ <seealso marker="erlang#monotonic_time/0">
+ <c>erlang:monotonic_time()</c></seealso>,
+ <seealso marker="erlang#time_offset/0">
+ <c>erlang:time_offset()</c></seealso>,
+ <seealso marker="erlang#unique_integer/0">
+ <c>erlang:unique_integer()</c></seealso>,
and other functionality.
</p>
- </do>
+ </do>
</section>
<p>To sum up this section: <em>Do not use <c>erlang:now/0</c>.</em></p>
@@ -867,9 +915,9 @@ EventTag = {Time, UMI}</code>
<p>It can be required that your code must run on a variety
of OTP installations of different OTP releases. If so, you
cannot use the new API out of the box, as it will
- not be available on old pre OTP 18 releases. The solution
+ not be available on releases before OTP 18. The solution
is <em>not</em> to avoid using the new API, as your
- code then would not benefit from the scalability
+ code would then not benefit from the scalability
and accuracy improvements made. Instead, use the
new API when available, and fall back on <c>erlang:now/0</c>
when the new API is unavailable.</p>
@@ -879,16 +927,20 @@ EventTag = {Time, UMI}</code>
<list type="bulleted">
<item>
- <seealso marker="erlang#system_info_start_time"><c>erlang:system_info(start_time)</c></seealso>
+ <seealso marker="erlang#system_info_start_time">
+ <c>erlang:system_info(start_time)</c></seealso>
</item>
<item>
- <seealso marker="erlang#system_info_end_time"><c>erlang:system_info(end_time)</c></seealso>
+ <seealso marker="erlang#system_info_end_time">
+ <c>erlang:system_info(end_time)</c></seealso>
</item>
<item>
- <seealso marker="erlang#system_info_os_monotonic_time_source"><c>erlang:system_info(os_monotonic_time_source)</c></seealso>
+ <seealso marker="erlang#system_info_os_monotonic_time_source">
+ <c>erlang:system_info(os_monotonic_time_source)</c></seealso>
</item>
<item>
- <seealso marker="erlang#system_info_os_system_time_source"><c>erlang:system_info(os_system_time_source)</c></seealso>)
+ <seealso marker="erlang#system_info_os_system_time_source">
+ <c>erlang:system_info(os_system_time_source)</c></seealso>)
</item>
</list>
diff --git a/erts/doc/src/tty.xml b/erts/doc/src/tty.xml
index cd46d1203c..51db1ba8e2 100644
--- a/erts/doc/src/tty.xml
+++ b/erts/doc/src/tty.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -22,7 +22,7 @@
</legalnotice>
- <title>tty - A command line interface</title>
+ <title>tty - A Command-Line Interface</title>
<prepared>ETX/B/SFP C. Granbom</prepared>
<responsible></responsible>
<docno></docno>
@@ -32,24 +32,48 @@
<rev>A</rev>
<file>tty.xml</file>
</header>
- <p><c><![CDATA[tty]]></c> is a simple command line interface program where keystrokes are collected and interpreted. Completed lines are sent to the shell for interpretation. There is a simple history mechanism, which saves previous lines. These can be edited before sending them to the shell.
- <c><![CDATA[tty]]></c> is started when Erlang is started with the command:<br></br></p>
- <p><em>erl</em></p>
- <p><c><![CDATA[tty]]></c> operates in one of two modes:<br></br></p>
+ <p><c><![CDATA[tty]]></c> is a simple command-line interface program where
+ keystrokes are collected and interpreted. Completed lines are sent to the
+ shell for interpretation. A simple history mechanism saves previous lines,
+ which can be edited before sending them to the shell. <c><![CDATA[tty]]></c>
+ is started when Erlang is started with the following command:</p>
+
+ <pre>
+erl</pre>
+
+ <p><c><![CDATA[tty]]></c> operates in one of two modes:</p>
+
<list type="bulleted">
<item>
- <p><em>normal mode</em>, in which lines of text can be edited and sent to the shell.</p>
+ <p>Normal mode, in which text lines can be edited and sent to the
+ shell.</p>
</item>
<item>
- <p><em>shell break</em> mode, which allows the user to kill the current shell, start multiple shells etc. Shell break mode is started by typing <em>Control G</em>.</p>
+ <p>Shell break mode, which allows the user to kill the current shell,
+ start multiple shells, and so on.</p>
</item>
</list>
<section>
<title>Normal Mode</title>
- <p>In normal mode keystrokes from the user are collected and interpreted by <c><![CDATA[tty]]></c>. Most of the <em>emacs</em> line editing commands are supported. The following is a complete list of the supported line editing commands.<br></br></p>
- <p><em>Note:</em> The notation <c><![CDATA[C-a]]></c> means pressing the control key and the letter <c><![CDATA[a]]></c> simultaneously. <c><![CDATA[M-f]]></c> means pressing the <c><![CDATA[ESC]]></c> key followed by the letter <c><![CDATA[f]]></c>. <c><![CDATA[Home]]></c> and <c><![CDATA[End]]></c> represent the keys with the same name on the keyboard, whereas <c><![CDATA[Left]]></c> and <c><![CDATA[Right]]></c> represent the corresponding arrow keys.
- </p>
+ <p>In normal mode keystrokes from the user are collected and interpreted by
+ <c><![CDATA[tty]]></c>. Most of the <em>Emacs</em> line-editing commands
+ are supported. The following is a complete list of the supported
+ line-editing commands.</p>
+
+ <p>Typographic conventions:</p>
+
+ <list type="bulleted">
+ <item><c>C-a</c> means pressing the <em>Ctrl</em> key and the letter
+ <c>a</c> simultaneously.</item>
+ <item><c>M-f</c> means pressing the <em>Esc</em> key and the letter
+ <c>f</c> in sequence.</item>
+ <item><c>Home</c> and <c>End</c> represent the keys with the same
+ name on the keyboard.</item>
+ <item><c>Left</c> and <c>Right</c> represent the corresponding arrow
+ keys.</item>
+ </list>
+
<table>
<row>
<cell align="left" valign="middle"><em>Key Sequence</em></cell>
@@ -121,11 +145,13 @@
</row>
<row>
<cell align="left" valign="middle">C-n</cell>
- <cell align="left" valign="middle">Fetch next line from the history buffer</cell>
+ <cell align="left" valign="middle">Fetch next line from the history
+ buffer</cell>
</row>
<row>
<cell align="left" valign="middle">C-p</cell>
- <cell align="left" valign="middle">Fetch previous line from the history buffer</cell>
+ <cell align="left" valign="middle">Fetch previous line from the history
+ buffer</cell>
</row>
<row>
<cell align="left" valign="middle">C-t</cell>
@@ -139,23 +165,18 @@
<cell align="left" valign="middle">C-y</cell>
<cell align="left" valign="middle">Insert previously killed text</cell>
</row>
- <tcaption>tty text editing</tcaption>
+ <tcaption>tty Text Editing</tcaption>
</table>
</section>
<section>
<title>Shell Break Mode</title>
- <p><em>tty</em> enters <em>shell</em> break mode when you type <em>Control G</em>. In this mode you can:<br></br></p>
+ <p>In this mode the following can be done:</p>
+
<list type="bulleted">
- <item>
- <p>Kill or suspend the current shell</p>
- </item>
- <item>
- <p>Connect to a suspended shell</p>
- </item>
- <item>
- <p>Start a new shell</p>
- </item>
+ <item>Kill or suspend the current shell</item>
+ <item>Connect to a suspended shell</item>
+ <item>Start a new shell</item>
</list>
</section>
</chapter>
diff --git a/erts/doc/src/werl.xml b/erts/doc/src/werl.xml
index 9e7ad584eb..792fe204e8 100644
--- a/erts/doc/src/werl.xml
+++ b/erts/doc/src/werl.xml
@@ -4,7 +4,7 @@
<comref>
<header>
<copyright>
- <year>1998</year><year>2013</year>
+ <year>1998</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -28,62 +28,91 @@
<docno>1</docno>
<approved>Bjarne D&auml;cker</approved>
<checked></checked>
- <date>98-01-26</date>
+ <date>1998-01-26</date>
<rev>A</rev>
<file>werl.xml</file>
</header>
<com>werl</com>
<comsummary>The Erlang Emulator</comsummary>
<description>
- <p>On Windows, the preferred way to start the Erlang system for interactive use is:</p>
+ <p>On Windows, the preferred way to start the Erlang system for interactive
+ use is as follows:</p>
+
<p><c><![CDATA[werl <arguments>]]></c></p>
- <p>This will start Erlang in its own window, with fully
- functioning command-line editing and scrollbars. All flags
+ <p>This starts Erlang in its own window, with fully
+ functioning command-line editing and scrollbars. All flags
except <c><![CDATA[-oldshell]]></c> work as they do for
- the <seealso marker="erl">erl</seealso> command.</p>
+ <seealso marker="erl"><c>erl(1)</c></seealso>.</p>
+
+ <list type="bulleted">
+ <item>
+ <p>To copy text to the clipboard, use <c>Ctrl-C</c>.</p>
+ </item>
+ <item>
+ <p>To paste text, use <c>Ctrl-V</c>.</p>
+ </item>
+ <item>
+ <p>To interrupt the runtime system or the shell process (depending
+ on what has been specified with system flag <c>+B</c>), use
+ <c>Ctrl-Break</c>.</p>
+ </item>
+ </list>
- <p>Ctrl-C is reserved for copying text to the clipboard (Ctrl-V to paste).
- To interrupt the runtime system or the shell process (depending on what
- has been specified with the +B system flag), you should use Ctrl-Break.</p>
<p>In cases where you want to redirect standard input and/or
- standard output or use Erlang in a pipeline, the <c>werl</c> is
- not suitable, and the <c>erl</c> program should be used instead.</p>
+ standard output or use Erlang in a pipeline, <c>werl</c> is
+ not suitable, and the <c>erl</c> program is to be used instead.</p>
- <p>The <c>werl</c> window is in many ways modelled after the <c>xterm</c>
+ <p>The <c>werl</c> window is in many ways modeled after the <c>xterm</c>
window present on other platforms, as the <c>xterm</c> model
- fits well with line oriented command based interaction. This
- means that selecting text is line oriented rather than rectangle
- oriented.</p>
-
- <p>To select text in the <c>werl</c> window , simply press and hold
- the left mouse button and drag the mouse over the text you want
- to select. If the selection crosses line boundaries, the
- selected text will consist of complete lines where applicable
- (just like in a word processor). To select more text than fits
- in the window, start by selecting a small portion in the
- beginning of the text you want, then use the scrollbar
- to view the end of the desired selection, point to it and press
- the <em>right</em> mouse-button. The whole area between your
- first selection and the point where you right-clicked will be
- included in the selection.</p>
+ fits well with line-oriented command-based interaction. This
+ means that selecting text is line-oriented rather than
+ rectangle-oriented.</p>
- <p>The selected text is copied to the clipboard by either
- pressing <c>Ctrl-C</c>, using the menu or pressing the copy
- button in the toolbar.</p>
+ <list type="bulleted">
+ <item>
+ <p>To select text in the <c>werl</c> window, press and hold
+ the left mouse button and drag the mouse over the text you want
+ to select. If the selection crosses line boundaries, the
+ selected text consists of complete lines where applicable
+ (just like in a word processor).</p>
+ </item>
+ <item>
+ <p>To select more text than fits
+ in the window, start by selecting a small part in the
+ beginning of the text you want, then use the scrollbar
+ to view the end of the desired selection, point to it, and press
+ the <em>right</em> mouse button. The whole area between your
+ first selection and the point where you right-clicked is
+ included in the selection.</p>
+ </item>
+ <item>
+ <p>To copy the selected text to the clipboard, either
+ use <c>Ctrl-C</c>, use the menu, or press the copy
+ button in the toolbar.</p>
+ </item>
+ </list>
- <p>Pasted text is always inserted at the current prompt position
- and will be interpreted by Erlang as usual keyboard input.</p>
+ <p>Pasted text is inserted at the current prompt position
+ and is interpreted by Erlang as usual keyboard input.</p>
- <p>Previous command lines can be retrieved by pressing the <c>Up
- arrow</c> or by pressing <c>Ctrl-P</c>. There is also a drop
- down box in the toolbar containing the command
- history. Selecting a command in the drop down box will insert it
- at the prompt, just as if you used the keyboard to retrieve the
+ <list type="bulleted">
+ <item>
+ <p>To retrieve previous command lines, press the <c>Up arrow</c> or
+ use <c>Ctrl-P</c>.</p>
+ </item>
+ </list>
+
+ <p>A drop-down box in the toolbar contains the command
+ history. Selecting a command in the drop-down box inserts the command
+ at the prompt, as if you used the keyboard to retrieve the
command.</p>
-
- <p>Closing the <c>werl</c> window will stop the Erlang emulator.</p>
+ <list type="bulleted">
+ <item>
+ <p>To stop the Erlang emulator, close the <c>werl</c> window.</p>
+ </item>
+ </list>
</description>
</comref>
diff --git a/erts/doc/src/zlib.xml b/erts/doc/src/zlib.xml
index 0a641346d9..f8140544b7 100644
--- a/erts/doc/src/zlib.xml
+++ b/erts/doc/src/zlib.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2005</year><year>2013</year>
+ <year>2005</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,13 +30,18 @@
<file>zlib.xml</file>
</header>
<module>zlib</module>
- <modulesummary>Zlib Compression interface.</modulesummary>
+ <modulesummary>zlib compression interface.</modulesummary>
<description>
- <p>The zlib module provides an API for the zlib library
- (http://www.zlib.org).
- It is used to compress and decompress data. The
- data format is described by RFCs 1950 to 1952.</p>
- <p>A typical (compress) usage looks like:</p>
+ <p>This module provides an API for the zlib library
+ (<url href="http://www.zlib.net">www.zlib.net</url>).
+ It is used to compress and decompress data.
+ The data format is described by
+ <url href="https://www.ietf.org/rfc/rfc1950.txt">RFC 1950</url>,
+ <url href="https://www.ietf.org/rfc/rfc1951.txt">RFC 1951</url>, and
+ <url href="https://www.ietf.org/rfc/rfc1952.txt">RFC 1952</url>.</p>
+
+ <p>A typical (compress) usage is as follows:</p>
+
<pre>
Z = zlib:open(),
ok = zlib:deflateInit(Z,default),
@@ -50,29 +55,25 @@ Last = zlib:deflate(Z, [], finish),
ok = zlib:deflateEnd(Z),
zlib:close(Z),
list_to_binary([Compressed|Last])</pre>
+
<p>In all functions errors, <c>{'EXIT',{Reason,Backtrace}}</c>,
- might be thrown, where <c>Reason</c> describes the
- error. Typical reasons are:</p>
+ can be thrown, where <c>Reason</c> describes the error.</p>
+
+ <p>Typical <c>Reasons</c>s:</p>
+
<taglist>
<tag><c>badarg</c></tag>
- <item>
- <p>Bad argument</p>
+ <item>Bad argument.
</item>
<tag><c>data_error</c></tag>
- <item>
- <p>The data contains errors</p>
+ <item>The data contains errors.
</item>
<tag><c>stream_error</c></tag>
- <item>
- <p>Inconsistent stream state</p>
- </item>
+ <item>Inconsistent stream state.</item>
<tag><c>einval</c></tag>
- <item>
- <p>Bad value or wrong function called</p>
- </item>
+ <item>Bad value or wrong function called.</item>
<tag><c>{need_dictionary,Adler32}</c></tag>
- <item>
- <p>See <c>inflate/2</c></p>
+ <item>See <seealso marker="#inflate/2"><c>inflate/2</c></seealso>.
</item>
</taglist>
</description>
@@ -81,7 +82,7 @@ list_to_binary([Compressed|Last])</pre>
<datatype>
<name name="zstream"/>
<desc>
- <p>A zlib stream, see <seealso marker="#open/0">open/0</seealso>.
+ <p>A zlib stream, see <seealso marker="#open/0"><c>open/0</c></seealso>.
</p>
</desc>
</datatype>
@@ -104,116 +105,144 @@ list_to_binary([Compressed|Last])</pre>
</desc>
</datatype>
</datatypes>
+
<funcs>
<func>
- <name name="open" arity="0"/>
- <fsummary>Open a stream and return a stream reference</fsummary>
+ <name name="adler32" arity="2"/>
+ <fsummary>Calculate the Adler checksum.</fsummary>
+ <desc>
+ <p>Calculates the Adler-32 checksum for <c><anno>Data</anno></c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="adler32" arity="3"/>
+ <fsummary>Calculate the Adler checksum.</fsummary>
+ <desc>
+ <p>Updates a running Adler-32 checksum for <c><anno>Data</anno></c>.
+ If <c><anno>Data</anno></c> is the empty binary or the empty iolist,
+ this function returns the required initial value for the checksum.</p>
+ <p>Example:</p>
+ <pre>
+Crc = lists:foldl(fun(Data,Crc0) ->
+ zlib:adler32(Z, Crc0, Data),
+ end, zlib:adler32(Z,&lt;&lt; &gt;&gt;), Datas)</pre>
+ </desc>
+ </func>
+
+ <func>
+ <name name="adler32_combine" arity="4"/>
+ <fsummary>Combine two Adler-32 checksums.</fsummary>
<desc>
- <p>Open a zlib stream.</p>
+ <p>Combines two Adler-32 checksums into one. For two binaries or
+ iolists, <c>Data1</c> and <c>Data2</c> with sizes of <c>Size1</c>
+ and <c><anno>Size2</anno></c>, with Adler-32 checksums
+ <c><anno>Adler1</anno></c> and <c><anno>Adler2</anno></c>.</p>
+ <p>This function returns the <c><anno>Adler</anno></c> checksum of
+ <c>[Data1,Data2]</c>, requiring only <c><anno>Adler1</anno></c>,
+ <c><anno>Adler2</anno></c>, and <c><anno>Size2</anno></c>.</p>
</desc>
</func>
+
<func>
<name name="close" arity="1"/>
- <fsummary>Close a stream</fsummary>
+ <fsummary>Close a stream.</fsummary>
<desc>
<p>Closes the stream referenced by <c><anno>Z</anno></c>.</p>
</desc>
</func>
+
<func>
- <name name="deflateInit" arity="1"/>
- <fsummary>Initialize a session for compression</fsummary>
+ <name name="compress" arity="1"/>
+ <fsummary>Compress data with standard zlib functionality.</fsummary>
<desc>
- <p>Same as <c>zlib:deflateInit(<anno>Z</anno>, default)</c>.</p>
+ <p>Compresses data with zlib headers and checksum.</p>
</desc>
</func>
+
<func>
- <name name="deflateInit" arity="2"/>
- <fsummary>Initialize a session for compression</fsummary>
+ <name name="crc32" arity="1"/>
+ <fsummary>Get current CRC.</fsummary>
<desc>
- <p>Initialize a zlib stream for compression.</p>
- <p><c><anno>Level</anno></c> decides the compression level to be used, 0
- (<c>none</c>), gives no compression at all, 1
- (<c>best_speed</c>) gives best speed and 9
- (<c>best_compression</c>) gives best compression.</p>
+ <p>Gets the current calculated CRC checksum.</p>
</desc>
</func>
+
<func>
- <name name="deflateInit" arity="6"/>
- <fsummary>Initialize a session for compression</fsummary>
+ <name name="crc32" arity="2"/>
+ <fsummary>Calculate CRC.</fsummary>
<desc>
- <p>Initiates a zlib stream for compression.</p>
- <p>The <c><anno>Level</anno></c> parameter decides the compression level to be
- used, 0 (<c>none</c>), gives no compression at all, 1
- (<c>best_speed</c>) gives best speed and 9
- (<c>best_compression</c>) gives best compression.</p>
- <p>The <c><anno>Method</anno></c> parameter decides which compression method to use,
- currently the only supported method is <c>deflated</c>.</p>
- <p>The <c><anno>WindowBits</anno></c> parameter is the base two logarithm
- of the window size (the size of the history buffer). It
- should be in the range 8 through 15. Larger values
- of this parameter result in better compression at the
- expense of memory usage. The default value is 15 if
- <c>deflateInit/2</c>. A negative <c><anno>WindowBits</anno></c>
- value suppresses the zlib header (and checksum) from the
- stream. Note that the zlib source mentions this only as a
- undocumented feature.</p>
- <p>The <c><anno>MemLevel</anno></c> parameter specifies how much memory
- should be allocated for the internal compression
- state. <c><anno>MemLevel</anno></c>=1 uses minimum memory but is slow and
- reduces compression ratio; <c><anno>MemLevel</anno></c>=9 uses maximum
- memory for optimal speed. The default value is 8.</p>
- <p>The <c><anno>Strategy</anno></c> parameter is used to tune
- the compression algorithm. Use the value <c>default</c> for
- normal data, <c>filtered</c> for data produced by a filter (or
- predictor), <c>huffman_only</c> to force Huffman encoding
- only (no string match), or <c>rle</c> to limit match
- distances to one (run-length encoding). Filtered data
- consists mostly of small values with a somewhat random
- distribution. In this case, the compression algorithm is tuned
- to compress them better. The effect of <c>filtered</c>is to
- force more Huffman coding and less string matching; it is
- somewhat intermediate between <c>default</c> and
- <c>huffman_only</c>. <c>rle</c> is designed to be almost as
- fast as <c>huffman_only</c>, but give better compression for PNG
- image data. The <c><anno>Strategy</anno></c> parameter only
- affects the compression ratio but not the correctness of the
- compressed output even if it is not set appropriately.</p>
+ <p>Calculates the CRC checksum for <c><anno>Data</anno></c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="crc32" arity="3"/>
+ <fsummary>Calculate CRC.</fsummary>
+ <desc>
+ <p>Updates a running CRC checksum for <c><anno>Data</anno></c>.
+ If <c><anno>Data</anno></c> is the empty binary or the empty iolist,
+ this function returns the required initial value for the CRC.</p>
+ <p>Example:</p>
+ <pre>
+Crc = lists:foldl(fun(Data,Crc0) ->
+ zlib:crc32(Z, Crc0, Data),
+ end, zlib:crc32(Z,&lt;&lt; &gt;&gt;), Datas)</pre>
+ </desc>
+ </func>
+
+ <func>
+ <name name="crc32_combine" arity="4"/>
+ <fsummary>Combine two CRCs.</fsummary>
+ <desc>
+ <p>Combines two CRC checksums into one. For two binaries or iolists,
+ <c>Data1</c> and <c>Data2</c> with sizes of <c>Size1</c> and
+ <c><anno>Size2</anno></c>, with CRC checksums <c><anno>CRC1</anno></c>
+ and <c><anno>CRC2</anno></c>.</p>
+ <p>This function returns the <c><anno>CRC</anno></c> checksum of
+ <c>[Data1,Data2]</c>, requiring only <c><anno>CRC1</anno></c>,
+ <c><anno>CRC2</anno></c>, and <c><anno>Size2</anno></c>.</p>
</desc>
</func>
+
<func>
<name name="deflate" arity="2"/>
- <fsummary>Compress data</fsummary>
+ <fsummary>Compress data.</fsummary>
<desc>
<p>Same as <c>deflate(<anno>Z</anno>, <anno>Data</anno>, none)</c>.</p>
</desc>
</func>
+
<func>
<name name="deflate" arity="3"/>
- <fsummary>Compress data</fsummary>
+ <fsummary>Compress data.</fsummary>
<desc>
- <p><c>deflate/3</c> compresses as much data as possible, and
- stops when the input buffer becomes empty. It may introduce
+ <p>Compresses as much data as possible, and
+ stops when the input buffer becomes empty. It can introduce
some output latency (reading input without producing any
output) except when forced to flush.</p>
- <p>If the parameter <c><anno>Flush</anno></c> is set to <c>sync</c>, all
+ <p>If <c><anno>Flush</anno></c> is set to <c>sync</c>, all
pending output is flushed to the output buffer and the
output is aligned on a byte boundary, so that the
decompressor can get all input data available so far.
- Flushing may degrade compression for some compression algorithms and so
- it should be used only when necessary.</p>
- <p>If <c><anno>Flush</anno></c> is set to <c>full</c>, all output is flushed as with
- <c>sync</c>, and the compression state is reset so that decompression can
- restart from this point if previous compressed data has been damaged or if
- random access is desired. Using <c>full</c> too often can seriously degrade
- the compression.</p>
- <p>If the parameter <c><anno>Flush</anno></c> is set to <c>finish</c>,
- pending input is processed, pending output is flushed and
- <c>deflate/3</c> returns. Afterwards the only possible
- operations on the stream are <c>deflateReset/1</c> or <c>deflateEnd/1</c>.</p>
- <p><c><anno>Flush</anno></c> can be set to <c>finish</c> immediately after
- <c>deflateInit</c> if all compression is to be done in one step.</p>
+ Flushing can degrade compression for some compression algorithms;
+ thus, use it only when necessary.</p>
+ <p>If <c><anno>Flush</anno></c> is set to <c>full</c>, all output is
+ flushed as with <c>sync</c>, and the compression state is reset so
+ that decompression can restart from this point if previous compressed
+ data has been damaged or if random access is desired. Using
+ <c>full</c> too often can seriously degrade the compression.</p>
+ <p>If <c><anno>Flush</anno></c> is set to <c>finish</c>,
+ pending input is processed, pending output is flushed, and
+ <c>deflate/3</c> returns. Afterwards the only possible operations
+ on the stream are
+ <seealso marker="#deflateReset/1"><c>deflateReset/1</c></seealso> or
+ <seealso marker="#deflateEnd/1"><c>deflateEnd/1</c></seealso>.</p>
+ <p><c><anno>Flush</anno></c> can be set to <c>finish</c> immediately
+ after <seealso marker="#deflateInit/1"><c>deflateInit</c></seealso>
+ if all compression is to be done in one step.</p>
+ <p>Example:</p>
<pre>
-
zlib:deflateInit(Z),
B1 = zlib:deflate(Z,Data),
B2 = zlib:deflate(Z,&lt;&lt; &gt;&gt;,finish),
@@ -221,116 +250,243 @@ zlib:deflateEnd(Z),
list_to_binary([B1,B2])</pre>
</desc>
</func>
+
<func>
- <name name="deflateSetDictionary" arity="2"/>
- <fsummary>Initialize the compression dictionary</fsummary>
+ <name name="deflateEnd" arity="1"/>
+ <fsummary>End deflate session.</fsummary>
<desc>
- <p>Initializes the compression dictionary from the given byte
- sequence without producing any compressed output. This
- function must be called immediately after
- <c>deflateInit/[1|2|6]</c> or <c>deflateReset/1</c>, before
- any call of <c>deflate/3</c>. The compressor and
- decompressor must use exactly the same dictionary (see
- <c>inflateSetDictionary/2</c>). The adler checksum of the
- dictionary is returned.</p>
+ <p>Ends the deflate session and cleans all data used. Notice that this
+ function throws a <c>data_error</c> exception if the last call to
+ <seealso marker="#deflate/3"><c>deflate/3</c></seealso>
+ was not called with <c>Flush</c> set to <c>finish</c>.</p>
</desc>
</func>
+
<func>
- <name name="deflateReset" arity="1"/>
- <fsummary>Reset the deflate session</fsummary>
+ <name name="deflateInit" arity="1"/>
+ <fsummary>Initialize a session for compression.</fsummary>
<desc>
- <p>This function is equivalent to <c>deflateEnd/1</c>
- followed by <c>deflateInit/[1|2|6]</c>, but does not free
- and reallocate all the internal compression state. The
- stream will keep the same compression level and any other
- attributes.</p>
+ <p>Same as <c>zlib:deflateInit(<anno>Z</anno>, default)</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="deflateInit" arity="2"/>
+ <fsummary>Initialize a session for compression.</fsummary>
+ <desc>
+ <p>Initializes a zlib stream for compression.</p>
+ <p><c><anno>Level</anno></c> decides the compression level to be
+ used:</p>
+ <list type="bulleted">
+ <item>0 (<c>none</c>), gives no compression</item>
+ <item>1 (<c>best_speed</c>) gives best speed</item>
+ <item>9 (<c>best_compression</c>) gives best compression</item>
+ </list>
+ </desc>
+ </func>
+
+ <func>
+ <name name="deflateInit" arity="6"/>
+ <fsummary>Initialize a session for compression.</fsummary>
+ <desc>
+ <p>Initiates a zlib stream for compression.</p>
+ <taglist>
+ <tag><c><anno>Level</anno></c></tag>
+ <item>
+ <p>Compression level to use:</p>
+ <list type="bulleted">
+ <item>0 (<c>none</c>), gives no compression</item>
+ <item>1 (<c>best_speed</c>) gives best speed</item>
+ <item>9 (<c>best_compression</c>) gives best compression</item>
+ </list>
+ </item>
+ <tag><c><anno>Method</anno></c></tag>
+ <item>
+ <p>Compression method to use, currently the only supported method
+ is <c>deflated</c>.</p>
+ </item>
+ <tag><c><anno>WindowBits</anno></c></tag>
+ <item>
+ <p>The base two logarithm of the window size (the size of the
+ history buffer). It is to be in the range 8 through 15. Larger
+ values result in better compression at the expense of memory
+ usage. Defaults to 15 if <seealso marker="#deflateInit/2">
+ <c>deflateInit/2</c></seealso> is used. A negative
+ <c><anno>WindowBits</anno></c> value suppresses the zlib header
+ (and checksum) from the stream. Notice that the zlib source
+ mentions this only as a undocumented feature.</p>
+ <warning>
+ <p>Due to a known bug in the underlying zlib library, <c>WindowBits</c> values 8 and -8
+ do not work as expected. In zlib versions before 1.2.9 values
+ 8 and -8 are automatically changed to 9 and -9. <em>From zlib version 1.2.9
+ value -8 is rejected</em> causing <c>zlib:deflateInit/6</c> to fail
+ (8 is still changed to 9). It also seem possible that future versions
+ of zlib may fix this bug and start accepting 8 and -8 as is.</p>
+ <p>Conclusion: Avoid values 8 and -8 unless you know your zlib version supports them.</p>
+ </warning>
+ </item>
+ <tag><c><anno>MemLevel</anno></c></tag>
+ <item>
+ <p>Specifies how much memory is to be allocated for the internal
+ compression state: <c><anno>MemLevel</anno></c>=1 uses minimum
+ memory but is slow and reduces compression ratio;
+ <c><anno>MemLevel</anno></c>=9 uses maximum memory for optimal
+ speed. Defaults to 8.</p>
+ </item>
+ <tag><c><anno>Strategy</anno></c></tag>
+ <item>
+ <p>Tunes the compression algorithm. Use the following values:</p>
+ <list type="bulleted">
+ <item><c>default</c> for normal data</item>
+ <item><c>filtered</c> for data produced by a filter (or
+ predictor)</item>
+ <item><c>huffman_only</c> to force Huffman encoding only
+ (no string match)</item>
+ <item><c>rle</c> to limit match distances to one (run-length
+ encoding)</item>
+ </list>
+ <p>Filtered data consists mostly of small values with a somewhat
+ random distribution. In this case, the compression algorithm is
+ tuned to compress them better. The effect of <c>filtered</c> is to
+ force more Huffman coding and less string matching; it is somewhat
+ intermediate between <c>default</c> and <c>huffman_only</c>.
+ <c>rle</c> is designed to be almost as fast as
+ <c>huffman_only</c>, but gives better compression for PNG image
+ data.</p>
+ <p><c><anno>Strategy</anno></c> affects only the compression ratio,
+ but not the correctness of the compressed output even if it is not
+ set appropriately.</p>
+ </item>
+ </taglist>
</desc>
</func>
+
<func>
<name name="deflateParams" arity="3"/>
- <fsummary>Dynamicly update deflate parameters</fsummary>
+ <fsummary>Dynamicly update deflate parameters.</fsummary>
<desc>
- <p>Dynamically update the compression level and compression
- strategy. The interpretation of <c><anno>Level</anno></c> and
- <c><anno>Strategy</anno></c> is as in <c>deflateInit/6</c>. This can be
+ <p>Dynamically updates the compression level and compression
+ strategy. The interpretation of <c><anno>Level</anno></c> and
+ <c><anno>Strategy</anno></c> is as in
+ <seealso marker="#deflateInit/6"><c>deflateInit/6</c></seealso>.
+ This can be
used to switch between compression and straight copy of the
input data, or to switch to a different kind of input data
requiring a different strategy. If the compression level is
changed, the input available so far is compressed with the
- old level (and may be flushed); the new level will take
- effect only at the next call of <c>deflate/3</c>.</p>
- <p>Before the call of <c>deflateParams</c>, the stream state must be set as for
- a call of <c>deflate/3</c>, since the currently available input may have to
- be compressed and flushed.</p>
+ old level (and can be flushed); the new level takes
+ effect only at the next call of
+ <seealso marker="#deflate/3"><c>deflate/3</c></seealso>.</p>
+ <p>Before the call of <c>deflateParams</c>, the stream state must be
+ set as for a call of <c>deflate/3</c>, as the currently available
+ input may have to be compressed and flushed.</p>
</desc>
</func>
+
<func>
- <name name="deflateEnd" arity="1"/>
- <fsummary>End deflate session</fsummary>
+ <name name="deflateReset" arity="1"/>
+ <fsummary>Reset the deflate session.</fsummary>
+ <desc>
+ <p>Equivalent to
+ <seealso marker="#deflateEnd/1"><c>deflateEnd/1</c></seealso>
+ followed by
+ <seealso marker="#deflateInit/1"><c>deflateInit/1,2,6</c></seealso>,
+ but does not free and reallocate all the internal compression state.
+ The stream keeps the same compression level and any other
+ attributes.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="deflateSetDictionary" arity="2"/>
+ <fsummary>Initialize the compression dictionary.</fsummary>
<desc>
- <p>End the deflate session and cleans all data used.
- Note that this function will throw an <c>data_error</c>
- exception if the last call to
- <c>deflate/3</c> was not called with <c>Flush</c> set to
- <c>finish</c>.</p>
+ <p>Initializes the compression dictionary from the specified byte
+ sequence without producing any compressed output.</p>
+ <p>This function must be called immediately after
+ <seealso marker="#deflateInit/1"><c>deflateInit/1,2,6</c></seealso> or
+ <seealso marker="#deflateReset/1"><c>deflateReset/1</c></seealso>,
+ before any call of
+ <seealso marker="#deflate/3"><c>deflate/3</c></seealso>.
+ The compressor and decompressor must use the same dictionary (see
+ <seealso marker="#inflateSetDictionary/2">
+ <c>inflateSetDictionary/2</c></seealso>).</p>
+ <p>The Adler checksum of the dictionary is returned.</p>
</desc>
</func>
+
<func>
- <name name="inflateInit" arity="1"/>
- <fsummary>Initialize a session for decompression</fsummary>
+ <name name="getBufSize" arity="1"/>
+ <fsummary>Get buffer size.</fsummary>
<desc>
- <p>Initialize a zlib stream for decompression.</p>
+ <p>Gets the size of the intermediate buffer.</p>
</desc>
</func>
+
<func>
- <name name="inflateInit" arity="2"/>
- <fsummary>Initialize a session for decompression</fsummary>
+ <name name="gunzip" arity="1"/>
+ <fsummary>Uncompress data with gz header.</fsummary>
<desc>
- <p>Initialize decompression session on zlib stream.</p>
- <p>The <c><anno>WindowBits</anno></c> parameter is the base two logarithm
- of the maximum window size (the size of the history buffer).
- It should be in the range 8 through 15.
- The default value is 15 if <c>inflateInit/1</c> is used.
- If a compressed stream with a larger window size is
- given as input, inflate() will throw the <c>data_error</c>
- exception. A negative <c><anno>WindowBits</anno></c> value makes zlib ignore the
- zlib header (and checksum) from the stream. Note that the zlib
- source mentions this only as a undocumented feature.</p>
+ <p>Uncompresses data with gz headers and checksum.</p>
</desc>
</func>
+
+ <func>
+ <name name="gzip" arity="1"/>
+ <fsummary>Compress data with gz header.</fsummary>
+ <desc>
+ <p>Compresses data with gz headers and checksum.</p>
+ </desc>
+ </func>
+
<func>
<name name="inflate" arity="2"/>
- <fsummary>Decompress data</fsummary>
+ <fsummary>Decompress data.</fsummary>
<desc>
- <p><c>inflate/2</c> decompresses as much data as possible.
- It may introduce some output latency (reading
+ <p>Decompresses as much data as possible.
+ It can introduce some output latency (reading
input without producing any output).</p>
<p>If a preset dictionary is needed at this point (see
- <c>inflateSetDictionary</c> below), <c>inflate/2</c> throws a
- <c>{need_dictionary,Adler}</c> exception where <c>Adler</c> is
- the adler32 checksum of the dictionary chosen by the
- compressor.</p>
+ <seealso marker="#inflateSetDictionary/2">
+ <c>inflateSetDictionary/2</c></seealso>), <c>inflate/2</c> throws a
+ <c>{need_dictionary,Adler}</c> exception, where <c>Adler</c> is
+ the Adler-32 checksum of the dictionary chosen by the compressor.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="inflateChunk" arity="1"/>
+ <fsummary>Read next uncompressed chunk.</fsummary>
+ <desc>
+ <p>Reads the next chunk of uncompressed data, initialized by
+ <seealso marker="#inflateChunk/2"><c>inflateChunk/2</c></seealso>.</p>
+ <p>This function is to be repeatedly called, while it returns
+ <c>{more, Decompressed}</c>.</p>
</desc>
</func>
+
<func>
<name name="inflateChunk" arity="2"/>
- <fsummary>Decompress data with limited output size</fsummary>
+ <fsummary>Decompress data with limited output size.</fsummary>
<desc>
- <p>Like <c>inflate/2</c>, but decompress no more data than
- will fit in the buffer configured via <c>setBufSize/2</c>.
+ <p>Like <seealso marker="#inflate/2"><c>inflate/2</c></seealso>,
+ but decompresses no more data than will fit in the buffer configured
+ through <seealso marker="#setBufSize/2"><c>setBufSize/2</c></seealso>.
Is is useful when decompressing a stream with a high compression
- ratio such that a small amount of compressed input may expand up to
- 1000 times.
- It returns <c>{more, Decompressed}</c>, when there is more output
- available, and <c>inflateChunk/1</c> should be used to read it.
- It may introduce some output latency (reading
+ ratio, such that a small amount of compressed input can expand up to
+ 1000 times.</p>
+ <p>This function returns <c>{more, Decompressed}</c>, when there is
+ more output available, and
+ <seealso marker="#inflateChunk/1"><c>inflateChunk/1</c></seealso>
+ is to be used to read it.</p>
+ <p>This function can introduce some output latency (reading
input without producing any output).</p>
<p>If a preset dictionary is needed at this point (see
- <c>inflateSetDictionary</c> below), <c>inflateChunk/2</c> throws a
- <c>{need_dictionary,Adler}</c> exception where <c>Adler</c> is
- the adler32 checksum of the dictionary chosen by the
- compressor.</p>
-
+ <seealso marker="#inflateSetDictionary/2">
+ <c>inflateSetDictionary/2</c></seealso>), this function throws a
+ <c>{need_dictionary,Adler}</c> exception, where <c>Adler</c> is
+ the Adler-32 checksum of the dictionary chosen by the compressor.</p>
+ <p>Example:</p>
<pre>
walk(Compressed, Handler) ->
Z = zlib:open(),
@@ -345,191 +501,126 @@ loop(Z, Handler, {more, Uncompressed}) ->
Handler(Uncompressed),
loop(Z, Handler, zlib:inflateChunk(Z));
loop(Z, Handler, Uncompressed) ->
- Handler(Uncompressed).
- </pre>
- </desc>
- </func>
- <func>
- <name name="inflateChunk" arity="1"/>
- <fsummary>Read next uncompressed chunk</fsummary>
- <desc>
- <p>Read next chunk of uncompressed data, initialized by
- <c>inflateChunk/2</c>.</p>
- <p>This function should be repeatedly called, while it returns
- <c>{more, Decompressed}</c>.</p>
- </desc>
- </func>
- <func>
- <name name="inflateSetDictionary" arity="2"/>
- <fsummary>Initialize the decompression dictionary</fsummary>
- <desc>
- <p>Initializes the decompression dictionary from the given
- uncompressed byte sequence. This function must be called
- immediately after a call of <c>inflate/2</c> if this call
- threw a <c>{need_dictionary,Adler}</c> exception.
- The dictionary chosen by the
- compressor can be determined from the Adler value thrown
- by the call to <c>inflate/2</c>. The compressor and decompressor
- must use exactly the same dictionary (see <c>deflateSetDictionary/2</c>).</p>
- <p>Example:</p>
- <pre>
-unpack(Z, Compressed, Dict) ->
- case catch zlib:inflate(Z, Compressed) of
- {'EXIT',{{need_dictionary,DictID},_}} ->
- zlib:inflateSetDictionary(Z, Dict),
- Uncompressed = zlib:inflate(Z, []);
- Uncompressed ->
- Uncompressed
- end.</pre>
- </desc>
- </func>
- <func>
- <name name="inflateReset" arity="1"/>
- <fsummary>>Reset the inflate session</fsummary>
- <desc>
- <p>This function is equivalent to <c>inflateEnd/1</c> followed
- by <c>inflateInit/1</c>, but does not free and reallocate all
- the internal decompression state. The stream will keep
- attributes that may have been set by <c>inflateInit/[1|2]</c>.</p>
+ Handler(Uncompressed).</pre>
</desc>
</func>
+
<func>
<name name="inflateEnd" arity="1"/>
- <fsummary>End inflate session</fsummary>
+ <fsummary>End inflate session.</fsummary>
<desc>
- <p>End the inflate session and cleans all data used. Note
- that this function will throw a <c>data_error</c> exception
+ <p>Ends the inflate session and cleans all data used. Notice
+ that this function throws a <c>data_error</c> exception
if no end of stream was found (meaning that not all data
has been uncompressed).</p>
</desc>
</func>
+
<func>
- <name name="setBufSize" arity="2"/>
- <fsummary>Set buffer size</fsummary>
- <desc>
- <p>Sets the intermediate buffer size.</p>
- </desc>
- </func>
- <func>
- <name name="getBufSize" arity="1"/>
- <fsummary>Get buffer size</fsummary>
- <desc>
- <p>Get the size of intermediate buffer.</p>
- </desc>
- </func>
- <func>
- <name name="crc32" arity="1"/>
- <fsummary>Get current CRC</fsummary>
- <desc>
- <p>Get the current calculated CRC checksum.</p>
- </desc>
- </func>
- <func>
- <name name="crc32" arity="2"/>
- <fsummary>Calculate CRC</fsummary>
- <desc>
- <p>Calculate the CRC checksum for <c><anno>Data</anno></c>.</p>
- </desc>
- </func>
- <func>
- <name name="crc32" arity="3"/>
- <fsummary>Calculate CRC</fsummary>
+ <name name="inflateInit" arity="1"/>
+ <fsummary>Initialize a session for decompression.</fsummary>
<desc>
- <p>Update a running CRC checksum for <c><anno>Data</anno></c>.
- If <c><anno>Data</anno></c> is the empty binary or the empty iolist, this function returns
- the required initial value for the crc.</p>
- <pre>
-Crc = lists:foldl(fun(Data,Crc0) ->
- zlib:crc32(Z, Crc0, Data),
- end, zlib:crc32(Z,&lt;&lt; &gt;&gt;), Datas)</pre>
+ <p>Initializes a zlib stream for decompression.</p>
</desc>
</func>
+
<func>
- <name name="crc32_combine" arity="4"/>
- <fsummary>Combine two CRC's</fsummary>
+ <name name="inflateInit" arity="2"/>
+ <fsummary>Initialize a session for decompression.</fsummary>
<desc>
- <p>Combine two CRC checksums into one. For two binaries or iolists,
- <c>Data1</c> and <c>Data2</c> with sizes of <c>Size1</c> and
- <c><anno>Size2</anno></c>, with CRC checksums <c><anno>CRC1</anno></c> and
- <c><anno>CRC2</anno></c>. <c>crc32_combine/4</c> returns the <c><anno>CRC</anno></c>
- checksum of <c>[Data1,Data2]</c>, requiring
- only <c><anno>CRC1</anno></c>, <c><anno>CRC2</anno></c>, and <c><anno>Size2</anno></c>.
- </p>
+ <p>Initializes a decompression session on zlib stream.</p>
+ <p><c><anno>WindowBits</anno></c> is the base two logarithm
+ of the maximum window size (the size of the history buffer).
+ It is to be in the range 8 through 15. Default to 15 if
+ <seealso marker="#inflateInit/1"><c>inflateInit/1</c></seealso>
+ is used.</p>
+ <p>If a compressed stream with a larger window size is specified as
+ input, <seealso marker="#inflate/2"><c>inflate/2</c></seealso>
+ throws the <c>data_error</c> exception.</p>
+ <p>A negative <c><anno>WindowBits</anno></c> value makes zlib
+ ignore the zlib header (and checksum) from the stream. Notice that
+ the zlib source mentions this only as a undocumented feature.</p>
</desc>
</func>
+
<func>
- <name name="adler32" arity="2"/>
- <fsummary>Calculate the adler checksum</fsummary>
+ <name name="inflateReset" arity="1"/>
+ <fsummary>>Reset the inflate session.</fsummary>
<desc>
- <p>Calculate the Adler-32 checksum for <c><anno>Data</anno></c>.</p>
+ <p>Equivalent to
+ <seealso marker="#inflateEnd/1"><c>inflateEnd/1</c></seealso>
+ followed by
+ <seealso marker="#inflateInit/1"><c>inflateInit/1</c></seealso>,
+ but does not free and reallocate all the internal decompression state.
+ The stream will keep attributes that could have been set by
+ <c>inflateInit/1,2</c>.</p>
</desc>
</func>
+
<func>
- <name name="adler32" arity="3"/>
- <fsummary>Calculate the adler checksum</fsummary>
+ <name name="inflateSetDictionary" arity="2"/>
+ <fsummary>Initialize the decompression dictionary.</fsummary>
<desc>
- <p>Update a running Adler-32 checksum for <c><anno>Data</anno></c>.
- If <c><anno>Data</anno></c> is the empty binary or the empty iolist, this function returns
- the required initial value for the checksum.</p>
+ <p>Initializes the decompression dictionary from the specified
+ uncompressed byte sequence. This function must be called
+ immediately after a call of
+ <seealso marker="#inflate/2"><c>inflate/2</c></seealso>
+ if this call threw a <c>{need_dictionary,Adler}</c> exception.
+ The dictionary chosen by the compressor can be determined from the
+ Adler value thrown by the call to <c>inflate/2</c>.
+ The compressor and decompressor must use the same dictionary (see
+ <seealso marker="#deflateSetDictionary/2">
+ <c>deflateSetDictionary/2</c></seealso>).</p>
+ <p>Example:</p>
<pre>
-Crc = lists:foldl(fun(Data,Crc0) ->
- zlib:adler32(Z, Crc0, Data),
- end, zlib:adler32(Z,&lt;&lt; &gt;&gt;), Datas)</pre>
+unpack(Z, Compressed, Dict) ->
+ case catch zlib:inflate(Z, Compressed) of
+ {'EXIT',{{need_dictionary,DictID},_}} ->
+ zlib:inflateSetDictionary(Z, Dict),
+ Uncompressed = zlib:inflate(Z, []);
+ Uncompressed ->
+ Uncompressed
+ end.</pre>
</desc>
</func>
+
<func>
- <name name="adler32_combine" arity="4"/>
- <fsummary>Combine two Adler-32 checksums</fsummary>
+ <name name="open" arity="0"/>
+ <fsummary>Open a stream and return a stream reference.</fsummary>
<desc>
- <p>Combine two Adler-32 checksums into one. For two binaries or iolists,
- <c>Data1</c> and <c>Data2</c> with sizes of <c>Size1</c> and
- <c><anno>Size2</anno></c>, with Adler-32 checksums <c><anno>Adler1</anno></c> and
- <c><anno>Adler2</anno></c>. <c>adler32_combine/4</c> returns the <c><anno>Adler</anno></c>
- checksum of <c>[Data1,Data2]</c>, requiring
- only <c><anno>Adler1</anno></c>, <c><anno>Adler2</anno></c>, and <c><anno>Size2</anno></c>.
- </p>
+ <p>Opens a zlib stream.</p>
</desc>
</func>
+
<func>
- <name name="compress" arity="1"/>
- <fsummary>Compress data with standard zlib functionality</fsummary>
+ <name name="setBufSize" arity="2"/>
+ <fsummary>Set buffer size.</fsummary>
<desc>
- <p>Compress data (with zlib headers and checksum).</p>
+ <p>Sets the intermediate buffer size.</p>
</desc>
</func>
+
<func>
<name name="uncompress" arity="1"/>
- <fsummary>Uncompress data with standard zlib functionality</fsummary>
+ <fsummary>Uncompress data with standard zlib functionality.</fsummary>
<desc>
- <p>Uncompress data (with zlib headers and checksum).</p>
- </desc>
- </func>
- <func>
- <name name="zip" arity="1"/>
- <fsummary>Compress data without the zlib headers</fsummary>
- <desc>
- <p>Compress data (without zlib headers and checksum).</p>
+ <p>Uncompresses data with zlib headers and checksum.</p>
</desc>
</func>
+
<func>
<name name="unzip" arity="1"/>
- <fsummary>Uncompress data without the zlib headers</fsummary>
+ <fsummary>Uncompress data without the zlib headers.</fsummary>
<desc>
- <p>Uncompress data (without zlib headers and checksum).</p>
- </desc>
- </func>
- <func>
- <name name="gzip" arity="1"/>
- <fsummary>Compress data with gz header</fsummary>
- <desc>
- <p>Compress data (with gz headers and checksum).</p>
+ <p>Uncompresses data without zlib headers and checksum.</p>
</desc>
</func>
+
<func>
- <name name="gunzip" arity="1"/>
- <fsummary>Uncompress data with gz header</fsummary>
+ <name name="zip" arity="1"/>
+ <fsummary>Compress data without the zlib headers.</fsummary>
<desc>
- <p>Uncompress data (with gz headers and checksum).</p>
+ <p>Compresses data without zlib headers and checksum.</p>
</desc>
</func>
</funcs>
diff --git a/erts/emulator/Makefile b/erts/emulator/Makefile
index 550e6e6f5b..65fdbdb747 100644
--- a/erts/emulator/Makefile
+++ b/erts/emulator/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index a919f0e3ac..8772befe27 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2013. All Rights Reserved.
+# Copyright Ericsson AB 1996-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -23,10 +23,6 @@ include ../vsn.mk
include $(ERL_TOP)/make/$(TARGET)/otp.mk
-include $(TARGET)/gen_git_version.mk
-ifeq ($(findstring ose,$(TARGET)),ose)
-include $(ERL_TOP)/make/$(TARGET)/ose_lm.mk
-endif
-
ENABLE_ALLOC_TYPE_VARS = @ENABLE_ALLOC_TYPE_VARS@
HIPE_ENABLED=@HIPE_ENABLED@
DTRACE_ENABLED=@DTRACE_ENABLED@
@@ -53,6 +49,10 @@ CREATE_DIRS=
LDFLAGS=@LDFLAGS@
ARFLAGS=rc
OMIT_OMIT_FP=no
+TYPE_LIBS=
+
+DIRTY_SCHEDULER_SUPPORT=@DIRTY_SCHEDULER_SUPPORT@
+NEW_PURGE_STRATEGY=@NEW_PURGE_STRATEGY@
ifeq ($(TYPE),debug)
PURIFY =
@@ -91,7 +91,7 @@ PURIFY =
TYPEMARKER = .gcov
TYPE_FLAGS = $(DEBUG_CFLAGS) -DERTS_GCOV -DNO_JUMP_TABLE -fprofile-arcs -ftest-coverage -O0 -DERTS_CAN_INLINE=0 -DERTS_INLINE=
ifneq ($(findstring solaris,$(TARGET)),solaris)
-LIBS += -lgcov
+TYPE_LIBS = -lgcov
endif
ENABLE_ALLOC_TYPE_VARS += debug
else
@@ -147,6 +147,8 @@ endif
endif
endif
+LIBS += $(TYPE_LIBS)
+
comma:=,
space:=
space+=
@@ -178,6 +180,10 @@ FLAVOR_MARKER=.smp
FLAVOR_FLAGS=-DERTS_SMP
ENABLE_ALLOC_TYPE_VARS += smp nofrag
M4FLAGS += -DERTS_SMP=1
+ifeq ($(DIRTY_SCHEDULER_SUPPORT),yes)
+THR_DEFS += -DERTS_DIRTY_SCHEDULERS
+endif
+
else
# If flavor isn't one of the above, it *is* plain flavor...
@@ -186,7 +192,6 @@ FLAVOR_MARKER=
FLAVOR_FLAGS=
ENABLE_ALLOC_TYPE_VARS += nofrag
M4FLAGS +=
-
endif
TF_MARKER=$(TYPEMARKER)$(FLAVOR_MARKER)
@@ -245,9 +250,7 @@ HCC = @HCC@
LD = @LD@
DEXPORT = @DEXPORT@
RANLIB = @RANLIB@
-ifneq ($(findstring ose,$(TARGET)),ose)
STRIP = strip
-endif
PERL = @PERL@
RM = @RM@
MKDIR = @MKDIR@
@@ -323,7 +326,7 @@ else
CS_CFLAGS = $(CS_CFLAGS_)
endif
CS_LDFLAGS = $(LDFLAGS)
-CS_LIBS = -L../lib/internal/$(TARGET) -lerts_internal$(TYPEMARKER) @ERTS_INTERNAL_X_LIBS@
+CS_LIBS = -L../lib/internal/$(TARGET) -lerts_internal$(TYPEMARKER) @ERTS_INTERNAL_X_LIBS@ @SOCKET_LIBS@
LIBS += @TERMCAP_LIB@ -L../lib/internal/$(TARGET) @ERTS_INTERNAL_X_LIBS@
@@ -404,7 +407,7 @@ EMULATOR_EXECUTABLE = beam$(TF_MARKER).dll
else
EMULATOR_EXECUTABLE = beam$(TF_MARKER)
endif
-CS_EXECUTABLE = child_setup$(TYPEMARKER)
+CS_EXECUTABLE = erl_child_setup$(TYPEMARKER)
# ----------------------------------------------------------------------
@@ -583,7 +586,7 @@ GENERATE += $(TARGET)/erl_version.h
# driver table
$(TTF_DIR)/driver_tab.c: Makefile.in utils/make_driver_tab
- $(gen_verbose)LANG=C $(PERL) utils/make_driver_tab -o $@ -nifs $(STATIC_NIF_LIBS) -drivers $(DRV_OBJS) $(STATIC_DRIVER_LIBS)
+ $(gen_verbose)LANG=C $(PERL) utils/make_driver_tab -o $@ -nifs $(NIF_OBJS) $(STATIC_NIF_LIBS) -drivers $(DRV_OBJS) $(STATIC_DRIVER_LIBS)
GENERATE += $(TTF_DIR)/driver_tab.c
@@ -592,11 +595,9 @@ GENERATE += $(TTF_DIR)/driver_tab.c
#
# This list must be consistent with PRE_LOADED_MODULES in
# erts/preloaded/src/Makefile.
-ifeq ($(TARGET),win32)
-# On windows the preloaded objects are in a resource object.
-PRELOAD_OBJ = $(OBJDIR)/beams.$(RES_EXT)
-PRELOAD_SRC = $(TARGET)/beams.rc
-$(PRELOAD_SRC): $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \
+
+PRELOAD_BEAM = $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \
+ $(ERL_TOP)/erts/preloaded/ebin/erts_code_purger.beam \
$(ERL_TOP)/erts/preloaded/ebin/init.beam \
$(ERL_TOP)/erts/preloaded/ebin/prim_eval.beam \
$(ERL_TOP)/erts/preloaded/ebin/prim_inet.beam \
@@ -605,21 +606,21 @@ $(PRELOAD_SRC): $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \
$(ERL_TOP)/erts/preloaded/ebin/prim_zip.beam \
$(ERL_TOP)/erts/preloaded/ebin/erl_prim_loader.beam \
$(ERL_TOP)/erts/preloaded/ebin/erlang.beam \
- $(ERL_TOP)/erts/preloaded/ebin/erts_internal.beam
+ $(ERL_TOP)/erts/preloaded/ebin/erts_internal.beam \
+ $(ERL_TOP)/erts/preloaded/ebin/erl_tracer.beam \
+ $(ERL_TOP)/erts/preloaded/ebin/erts_literal_area_collector.beam \
+ $(ERL_TOP)/erts/preloaded/ebin/erts_dirty_process_code_checker.beam
+
+ifeq ($(TARGET),win32)
+# On windows the preloaded objects are in a resource object.
+PRELOAD_OBJ = $(OBJDIR)/beams.$(RES_EXT)
+PRELOAD_SRC = $(TARGET)/beams.rc
+$(PRELOAD_SRC): $(PRELOAD_BEAM)
$(gen_verbose)LANG=C $(PERL) utils/make_preload $(MAKE_PRELOAD_EXTRA) -rc $^ > $@
else
PRELOAD_OBJ = $(OBJDIR)/preload.o
PRELOAD_SRC = $(TARGET)/preload.c
-$(PRELOAD_SRC): $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \
- $(ERL_TOP)/erts/preloaded/ebin/init.beam \
- $(ERL_TOP)/erts/preloaded/ebin/prim_eval.beam \
- $(ERL_TOP)/erts/preloaded/ebin/prim_inet.beam \
- $(ERL_TOP)/erts/preloaded/ebin/prim_file.beam \
- $(ERL_TOP)/erts/preloaded/ebin/zlib.beam \
- $(ERL_TOP)/erts/preloaded/ebin/prim_zip.beam \
- $(ERL_TOP)/erts/preloaded/ebin/erl_prim_loader.beam \
- $(ERL_TOP)/erts/preloaded/ebin/erlang.beam \
- $(ERL_TOP)/erts/preloaded/ebin/erts_internal.beam
+$(PRELOAD_SRC): $(PRELOAD_BEAM)
$(gen_verbose)LANG=C $(PERL) utils/make_preload -old $^ > $@
endif
@@ -684,14 +685,6 @@ $(OBJDIR)/%.o: $(TTF_DIR)/%.c
$(OBJDIR)/%.o: sys/$(ERLANG_OSTYPE)/%.c
$(V_CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
-ifeq ($(findstring ose,$(TARGET)),ose)
-$(OBJDIR)/ose_confd.o: $(OSE_CONFD)
- $(V_CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
-
-$(OBJDIR)/crt0_lm.o: $(CRT0_LM)
- $(V_CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
-endif
-
$(OBJDIR)/%.o: sys/common/%.c
$(V_CC) $(subst -O2, $(GEN_OPT_FLGS), $(CFLAGS)) $(INCLUDES) -c $< -o $@
@@ -701,14 +694,17 @@ $(OBJDIR)/%.o: drivers/common/%.c
$(OBJDIR)/%.o: drivers/$(ERLANG_OSTYPE)/%.c
$(V_CC) $(CFLAGS) $(INCLUDES) -Idrivers/common -Idrivers/$(ERLANG_OSTYPE) -I../etc/$(ERLANG_OSTYPE) -c $< -o $@
+$(OBJDIR)/%.o: nifs/common/%.c
+ $(V_CC) $(CFLAGS) -DLIBSCTP=$(LIBSCTP) $(INCLUDES) -Inifs/common -Inifs/$(ERLANG_OSTYPE) -c $< -o $@
+
# ----------------------------------------------------------------------
# Specials
#
-CS_SRC = sys/$(ERLANG_OSTYPE)/erl_child_setup.c
+CS_OBJ = $(OBJDIR)/erl_child_setup.o $(OBJDIR)/sys_uds.o $(OBJDIR)/hash.o
-$(BINDIR)/$(CS_EXECUTABLE): $(TTF_DIR)/GENERATED $(PRELOAD_SRC) $(CS_SRC) $(ERTS_LIB)
- $(ld_verbose)$(CS_PURIFY) $(CC) $(CS_LDFLAGS) -o $(BINDIR)/$(CS_EXECUTABLE) \
- $(CS_CFLAGS) $(COMMON_INCLUDES) $(CS_SRC) $(CS_LIBS)
+$(BINDIR)/$(CS_EXECUTABLE): $(TTF_DIR)/GENERATED $(PRELOAD_SRC) $(CS_OBJ) $(ERTS_LIB)
+ $(ld_verbose)$(CS_PURIFY) $(LD) $(CS_LDFLAGS) -o $(BINDIR)/$(CS_EXECUTABLE) \
+ $(CS_CFLAGS) $(COMMON_INCLUDES) $(CS_OBJ) $(CS_LIBS)
$(OBJDIR)/%.kp.o: sys/common/%.c
$(V_CC) -DERTS_KERNEL_POLL_VERSION $(subst -O2, $(GEN_OPT_FLGS), $(CFLAGS)) $(INCLUDES) -c $< -o $@
@@ -746,7 +742,7 @@ EMU_OBJS = \
$(OBJDIR)/beam_ranges.o
RUN_OBJS = \
- $(OBJDIR)/erl_pbifs.o $(OBJDIR)/benchmark.o \
+ $(OBJDIR)/erl_pbifs.o \
$(OBJDIR)/erl_alloc.o $(OBJDIR)/erl_mtrace.o \
$(OBJDIR)/erl_alloc_util.o $(OBJDIR)/erl_goodfit_alloc.o \
$(OBJDIR)/erl_bestfit_alloc.o $(OBJDIR)/erl_afit_alloc.o \
@@ -787,7 +783,11 @@ RUN_OBJS = \
$(OBJDIR)/erl_zlib.o $(OBJDIR)/erl_nif.o \
$(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_ptab.o $(OBJDIR)/erl_map.o \
+ $(OBJDIR)/erl_msacc.o
+
+LTTNG_OBJS = $(OBJDIR)/erlang_lttng.o
+NIF_OBJS = $(OBJDIR)/erl_tracer_nif.o
ifeq ($(TARGET),win32)
DRV_OBJS = \
@@ -810,31 +810,10 @@ OS_OBJS = \
$(OBJDIR)/dosmap.o
else
-ifeq ($(findstring ose,$(TARGET)),ose)
-OS_OBJS = \
- $(OBJDIR)/sys.o \
- $(OBJDIR)/driver_tab.o \
- $(OBJDIR)/ose_efile.o \
- $(OBJDIR)/gzio.o \
- $(OBJDIR)/elib_memmove.o
-
-OS_OBJS += $(OBJDIR)/ose_confd.o \
- $(OBJDIR)/crt0_lm.o
-
-OS_OBJS += $(OBJDIR)/sys_float.o \
- $(OBJDIR)/sys_time.o
-
-DRV_OBJS = \
- $(OBJDIR)/efile_drv.o \
- $(OBJDIR)/ose_signal_drv.o \
- $(OBJDIR)/inet_drv.o \
- $(OBJDIR)/zlib_drv.o \
- $(OBJDIR)/ram_file_drv.o \
- $(OBJDIR)/ttsl_drv.o
-
-else
OS_OBJS = \
$(OBJDIR)/sys.o \
+ $(OBJDIR)/sys_drivers.o \
+ $(OBJDIR)/sys_uds.o \
$(OBJDIR)/driver_tab.o \
$(OBJDIR)/unix_efile.o \
$(OBJDIR)/gzio.o \
@@ -849,7 +828,6 @@ DRV_OBJS = \
$(OBJDIR)/ram_file_drv.o \
$(OBJDIR)/ttsl_drv.o
endif
-endif
ifneq ($(STATIC_NIFS),no)
STATIC_NIF_LIBS = $(STATIC_NIFS)
@@ -917,9 +895,9 @@ ifdef HIPE_ENABLED
EXTRA_BASE_OBJS += $(HIPE_OBJS)
endif
-BASE_OBJS = $(EMU_OBJS) $(RUN_OBJS) $(OS_OBJS) $(EXTRA_BASE_OBJS)
+BASE_OBJS = $(EMU_OBJS) $(RUN_OBJS) $(OS_OBJS) $(EXTRA_BASE_OBJS) $(LTTNG_OBJS)
-before_DTrace_OBJS = $(BASE_OBJS) $(DRV_OBJS)
+before_DTrace_OBJS = $(BASE_OBJS) $(DRV_OBJS) $(NIF_OBJS)
DTRACE_OBJS =
ifdef DTRACE_ENABLED_2STEP
@@ -956,7 +934,7 @@ $(OBJDIR)/%.o: hipe/%.c
$(V_CC) $(subst O2,O3, $(CFLAGS)) $(INCLUDES) -c $< -o $@
$(BINDIR)/hipe_mkliterals$(TF_MARKER): $(OBJDIR)/hipe_mkliterals.o
- $(ld_verbose)$(CC) $(CFLAGS) $(INCLUDES) -o $@ $<
+ $(ld_verbose)$(CC) $(LDFLAGS) -o $@ $< $(TYPE_LIBS)
$(OBJDIR)/hipe_mkliterals.o: $(HIPE_ASM) $(TTF_DIR)/erl_alloc_types.h $(DTRACE_HEADERS) \
$(TTF_DIR)/OPCODES-GENERATED $(TARGET)/TABLES-GENERATED
@@ -1022,19 +1000,12 @@ $(BINDIR)/$(EMULATOR_EXECUTABLE): $(INIT_OBJS) $(OBJS) $(DEPLIBS)
$(STATIC_DRIVER_LIBS) $(LIBS)
else
-ifeq ($(findstring ose,$(TARGET)),ose)
-$(BINDIR)/$(EMULATOR_EXECUTABLE): $(INIT_OBJS) $(OBJS) $(DEPLIBS) $(LCF)
- $(call build-ose-load-module, $@, $(INIT_OBJS) $(OBJS), $(STATIC_NIF_LIBS) \
- $(STATIC_DRIVER_LIBS) $(LIBS), $(BEAM_LMCONF))
-
-else
$(BINDIR)/$(EMULATOR_EXECUTABLE): $(INIT_OBJS) $(OBJS) $(DEPLIBS)
$(ld_verbose)$(PURIFY) $(LD) -o $(BINDIR)/$(EMULATOR_EXECUTABLE) \
$(HIPEBEAMLDFLAGS) $(LDFLAGS) $(DEXPORT) $(INIT_OBJS) $(OBJS) \
$(STATIC_NIF_LIBS) $(STATIC_DRIVER_LIBS) $(LIBS)
endif
-endif
# ----------------------------------------------------------------------
# Dependencies
@@ -1079,6 +1050,7 @@ endif
BEAM_SRC=$(wildcard beam/*.c)
DRV_COMMON_SRC=$(wildcard drivers/common/*.c)
DRV_OSTYPE_SRC=$(wildcard drivers/$(ERLANG_OSTYPE)/*.c)
+NIF_COMMON_SRC=$(wildcard nifs/common/*.c)
ALL_SYS_SRC=$(wildcard sys/$(ERLANG_OSTYPE)/*.c) $(wildcard sys/common/*.c)
# We use $(shell ls) here instead of wildcard as $(wildcard ) resolved at
# loadtime of the makefile and at that time these files are not generated yet.
@@ -1111,7 +1083,7 @@ MG_FLAG=-MG
endif
DEP_CC=$(CC)
-DEP_FLAGS=-MM $(MG_FLAG) $(CFLAGS) $(INCLUDES) -Idrivers/common -Idrivers/$(ERLANG_OSTYPE)
+DEP_FLAGS=-MM $(MG_FLAG) $(CFLAGS) $(INCLUDES) -Inifs/common -Idrivers/common -Idrivers/$(ERLANG_OSTYPE)
SYS_SRC=$(ALL_SYS_SRC)
endif
@@ -1136,6 +1108,8 @@ $(TTF_DIR)/depend.mk: $(TTF_DIR)/GENERATED $(PRELOAD_SRC)
| $(SED_DEPEND) >> $(TTF_DIR)/depend.mk
$(V_at)$(DEP_CC) $(DEP_FLAGS) -I../etc/$(ERLANG_OSTYPE) $(DRV_OSTYPE_SRC) \
| $(SED_DEPEND) >> $(TTF_DIR)/depend.mk
+ $(V_at)$(DEP_CC) $(DEP_FLAGS) $(NIF_COMMON_SRC) \
+ | $(SED_DEPEND) >> $(TTF_DIR)/depend.mk
$(V_at)$(DEP_CC) $(DEP_FLAGS) $(SYS_SRC) \
| $(SED_DEPEND) >> $(TTF_DIR)/depend.mk
$(V_at)$(DEP_CC) $(DEP_FLAGS) $(TARGET_SRC) \
diff --git a/erts/emulator/beam/atom.c b/erts/emulator/beam/atom.c
index fe91134ef4..2052afe52b 100644
--- a/erts/emulator/beam/atom.c
+++ b/erts/emulator/beam/atom.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -68,7 +68,7 @@ static Uint atom_space; /* Amount of atom text space used */
/*
* Print info about atom tables
*/
-void atom_info(int to, void *to_arg)
+void atom_info(fmtfn_t to, void *to_arg)
{
int lock = !ERTS_IS_CRASH_DUMPING;
if (lock)
@@ -176,7 +176,7 @@ atom_alloc(Atom* tmpl)
/*
* Precompute ordinal value of first 3 bytes + 7 bits.
- * This is used by utils.c:cmp_atoms().
+ * This is used by utils.c:erts_cmp_atoms().
* We cannot use the full 32 bits of the first 4 bytes,
* since we use the sign of the difference between two
* ordinal values to represent their relative order.
@@ -435,6 +435,9 @@ init_atom_table(void)
f.cmp = (HCMP_FUN) atom_cmp;
f.alloc = (HALLOC_FUN) atom_alloc;
f.free = (HFREE_FUN) atom_free;
+ f.meta_alloc = (HMALLOC_FUN) erts_alloc;
+ f.meta_free = (HMFREE_FUN) erts_free;
+ f.meta_print = (HMPRINT_FUN) erts_print;
atom_text_pos = NULL;
atom_text_end = NULL;
@@ -467,7 +470,7 @@ init_atom_table(void)
}
void
-dump_atoms(int to, void *to_arg)
+dump_atoms(fmtfn_t to, void *to_arg)
{
int i = erts_atom_table.entries;
diff --git a/erts/emulator/beam/atom.h b/erts/emulator/beam/atom.h
index ead56c83d8..a82efabb9f 100644
--- a/erts/emulator/beam/atom.h
+++ b/erts/emulator/beam/atom.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,10 +21,7 @@
#ifndef __ATOM_H__
#define __ATOM_H__
-#ifndef __INDEX_H__
#include "index.h"
-#endif
-
#include "erl_atom_table.h"
#define MAX_ATOM_CHARACTERS 255
@@ -129,6 +126,7 @@ typedef enum {
(erts_is_atom_utf8_bytes((byte *) LSTR, sizeof(LSTR) - 1, (TERM)))
#define ERTS_DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1)
#define ERTS_INIT_AM(S) AM_ ## S = am_atom_put(#S, sizeof(#S) - 1)
+#define ERTS_MAKE_AM(Str) am_atom_put(Str, sizeof(Str) - 1)
int atom_table_size(void); /* number of elements */
int atom_table_sz(void); /* table size in bytes, excluding stored objects */
@@ -138,8 +136,8 @@ Eterm erts_atom_put(const byte *name, int len, ErtsAtomEncoding enc, int trunc);
int atom_erase(byte*, int);
int atom_static_put(byte*, int);
void init_atom_table(void);
-void atom_info(int, void *);
-void dump_atoms(int, void *);
+void atom_info(fmtfn_t, void *);
+void dump_atoms(fmtfn_t, void *);
int erts_atom_get(const char* name, int len, Eterm* ap, ErtsAtomEncoding enc);
void erts_atom_get_text_space_sizes(Uint *reserved, Uint *used);
#endif
diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names
index cb6d294a41..2b66bf6f4e 100644
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2013. All Rights Reserved.
+# Copyright Ericsson AB 1996-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -43,7 +43,7 @@ atom false true
atom Underscore='_'
atom Noname='nonode@nohost'
atom EOT='$end_of_table'
-atom Cookie=''
+atom Empty=''
#
# Used in the Beam emulator loop. (Smaller literals usually means tighter code.)
@@ -65,6 +65,7 @@ atom undefined_lambda
atom DOWN='DOWN'
atom UP='UP'
atom EXIT='EXIT'
+atom abort
atom aborted
atom abs_path
atom absoluteURI
@@ -102,6 +103,7 @@ atom asynchronous
atom atom
atom atom_used
atom attributes
+atom await_microstate_accounting_modifications
atom await_port_send_result
atom await_proc_exit
atom await_result
@@ -118,14 +120,15 @@ atom bif_timer_server
atom binary
atom binary_bin_to_list_trap
atom binary_copy_trap
+atom binary_find_trap
atom binary_longest_prefix_trap
atom binary_longest_suffix_trap
-atom binary_match_trap
-atom binary_matches_trap
atom binary_to_list_continue
atom binary_to_term_trap
atom block
+atom block_normal
atom blocked
+atom blocked_normal
atom bm
atom bnot
atom bor
@@ -159,9 +162,11 @@ atom close
atom closed
atom code
atom command
+atom commandv
atom compact
atom compat_rel
atom compile
+atom complete
atom compressed
atom config_h
atom convert_time_unit
@@ -173,6 +178,8 @@ atom const
atom context_switches
atom control
atom copy
+atom copy_literals
+atom counters
atom cpu
atom cpu_timestamp
atom cr
@@ -189,9 +196,13 @@ atom dexit
atom depth
atom dgroup_leader
atom dictionary
+atom dirty_cpu
atom dirty_cpu_schedulers_online
+atom dirty_execution
+atom dirty_io
atom disable_trace
atom disabled
+atom discard
atom display_items
atom dist
atom dist_cmd
@@ -210,7 +221,9 @@ atom dsend
atom dsend_continue_trap
atom dunlink
atom duplicate_bag
+atom duplicated
atom dupnames
+atom einval
atom elib_malloc
atom emulator
atom enable_trace
@@ -219,26 +232,32 @@ atom endian
atom env
atom eof
atom eol
-atom exception_from
-atom exception_trace
-atom extended
atom Eq='=:='
atom Eqeq='=='
+atom erl_tracer
atom erlang
atom ERROR='ERROR'
atom error_handler
atom error_logger
+atom erts_code_purger
atom erts_internal
atom ets
atom ETS_TRANSFER='ETS-TRANSFER'
atom event
atom exact_reductions
+atom exception_from
+atom exception_trace
atom exclusive
atom exit_status
atom existing
+atom existing_processes
+atom existing_ports
+atom existing
atom exiting
atom exports
+atom extended
atom external
+atom extra
atom false
atom fcgi
atom fd
@@ -258,7 +277,13 @@ atom functions
atom function_clause
atom garbage_collecting
atom garbage_collection
+atom garbage_collection_info
atom gc_end
+atom gc_major_end
+atom gc_major_start
+atom gc_max_heap_size
+atom gc_minor_end
+atom gc_minor_start
atom gc_start
atom Ge='>='
atom generational
@@ -268,6 +293,7 @@ atom get_tcw
atom getenv
atom gather_gc_info_result
atom gather_io_bytes
+atom gather_microstate_accounting_result
atom gather_sched_wall_time_result
atom gather_system_check_result
atom getting_linked
@@ -300,6 +326,7 @@ atom index
atom infinity
atom info
atom info_msg
+atom init
atom initial_call
atom input
atom internal
@@ -343,8 +370,10 @@ atom match
atom match_limit
atom match_limit_recursion
atom match_spec
+atom match_spec_result
atom max
atom maximum
+atom max_heap_size
atom max_tables max_processes
atom mbuf_size
atom md5
@@ -353,13 +382,17 @@ atom memory_internal
atom memory_types
atom message
atom message_binary
+atom message_queue_data
atom message_queue_len
atom messages
atom merge_trap
atom meta
atom meta_match_spec
atom micro_seconds
+atom microsecond
+atom microstate_accounting
atom milli_seconds
+atom millisecond
atom min_heap_size
atom min_bin_vheap_size
atom minor_version
@@ -376,11 +409,13 @@ atom more
atom multi_scheduling
atom multiline
atom nano_seconds
+atom nanosecond
atom name
atom named_table
atom namelist
atom native
atom native_addresses
+atom need_gc
atom Neq='=/='
atom Neqeq='/='
atom net_kernel
@@ -388,6 +423,8 @@ atom net_kernel_terminated
atom never_utf
atom new
atom new_index
+atom new_processes
+atom new_ports
atom new_uniq
atom newline
atom next
@@ -427,10 +464,12 @@ atom notify
atom notsup
atom nouse_stdio
atom objects
+atom off_heap
atom offset
atom ok
atom old_heap_block_size
atom old_heap_size
+atom on_heap
atom on_load
atom open
atom open_error
@@ -441,13 +480,6 @@ atom orelse
atom os_pid
atom os_type
atom os_version
-atom ose_bg_proc
-atom ose_int_proc
-atom ose_phantom
-atom ose_pri_proc
-atom ose_process_prio
-atom ose_process_type
-atom ose_ti_proc
atom out
atom out_exited
atom out_exiting
@@ -462,6 +494,7 @@ atom pause
atom pending
atom pending_driver
atom pending_process
+atom pending_purge_lambda
atom pending_reload
atom permanent
atom pid
@@ -471,6 +504,8 @@ atom port_count
atom port_limit
atom port_op
atom positive
+atom prepare
+atom prepare_on_load
atom print
atom priority
atom private
@@ -531,7 +566,9 @@ atom schedulers_online
atom scheme
atom scientific
atom scope
+atom second
atom seconds
+atom send_to_non_existing_process
atom sensitive
atom sequential_tracer
atom sequential_trace_token
@@ -553,6 +590,7 @@ atom size
atom sl_alloc
atom spawn_executable
atom spawn_driver
+atom spawned
atom ssl_tls
atom stack_size
atom start
@@ -561,6 +599,7 @@ atom static
atom stderr_to_stdout
atom stop
atom stream
+atom strict_monotonic
atom strict_monotonic_timestamp
atom sunrm
atom suspend
@@ -588,10 +627,13 @@ atom total_active_tasks
atom total_heap_size
atom total_run_queue_lengths
atom tpkt
-atom trace trace_ts traced
+atom trace trace_ts traced
atom trace_control_word
+atom trace_status
atom tracer
atom trap_exit
+atom trim
+atom trim_all
atom try_clause
atom true
atom tuple
@@ -607,6 +649,7 @@ atom use_stdio
atom used
atom utf8
atom unblock
+atom unblock_normal
atom uniq
atom unless_suspending
atom unloaded
diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c
index 0e192b1ebd..b664532c1c 100644
--- a/erts/emulator/beam/beam_bif_load.c
+++ b/erts/emulator/beam/beam_bif_load.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2013. 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.
@@ -37,13 +37,93 @@
#include "erl_bits.h"
#include "erl_thr_progress.h"
+#ifdef HIPE
+# include "hipe_stack.h"
+#endif
+
+static struct {
+ Eterm module;
+ erts_smp_mtx_t mtx;
+ Export *pending_purge_lambda;
+ Eterm *sprocs;
+ Eterm def_sprocs[10];
+ Uint sp_size;
+ Uint sp_ix;
+ ErlFunEntry **funs;
+ ErlFunEntry *def_funs[10];
+ Uint fe_size;
+ Uint fe_ix;
+ struct erl_module_instance saved_old;
+} purge_state;
+
+Process *erts_code_purger = NULL;
+
+#ifdef ERTS_DIRTY_SCHEDULERS
+Process *erts_dirty_process_code_checker;
+#endif
+erts_smp_atomic_t erts_copy_literal_area__;
+#define ERTS_SET_COPY_LITERAL_AREA(LA) \
+ erts_smp_atomic_set_nob(&erts_copy_literal_area__, \
+ (erts_aint_t) (LA))
+#ifdef ERTS_NEW_PURGE_STRATEGY
+Process *erts_literal_area_collector = NULL;
+
+typedef struct ErtsLiteralAreaRef_ ErtsLiteralAreaRef;
+struct ErtsLiteralAreaRef_ {
+ ErtsLiteralAreaRef *next;
+ ErtsLiteralArea *literal_area;
+};
+
+struct {
+ erts_smp_mtx_t mtx;
+ ErtsLiteralAreaRef *first;
+ ErtsLiteralAreaRef *last;
+} release_literal_areas;
+#endif
+
static void set_default_trace_pattern(Eterm module);
-static Eterm check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp);
+static Eterm check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls);
static void delete_code(Module* modp);
-static void decrement_refc(BeamInstr* code);
static int any_heap_ref_ptrs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size);
static int any_heap_refs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size);
+static void
+init_purge_state(void)
+{
+ purge_state.module = THE_NON_VALUE;
+
+ erts_smp_mtx_init(&purge_state.mtx, "purge_state");
+
+ purge_state.pending_purge_lambda =
+ erts_export_put(am_erts_code_purger, am_pending_purge_lambda, 3);
+
+ purge_state.sprocs = &purge_state.def_sprocs[0];
+ purge_state.sp_size = sizeof(purge_state.def_sprocs);
+ purge_state.sp_size /= sizeof(purge_state.def_sprocs[0]);
+ purge_state.sp_ix = 0;
+
+ purge_state.funs = &purge_state.def_funs[0];
+ purge_state.fe_size = sizeof(purge_state.def_funs);
+ purge_state.fe_size /= sizeof(purge_state.def_funs[0]);
+ purge_state.fe_ix = 0;
+
+ purge_state.saved_old.code_hdr = 0;
+}
+
+void
+erts_beam_bif_load_init(void)
+{
+#ifdef ERTS_NEW_PURGE_STRATEGY
+ erts_smp_mtx_init(&release_literal_areas.mtx, "release_literal_areas");
+ release_literal_areas.first = NULL;
+ release_literal_areas.last = NULL;
+#endif
+ erts_smp_atomic_init_nob(&erts_copy_literal_area__,
+ (erts_aint_t) NULL);
+
+ init_purge_state();
+}
+
BIF_RETTYPE code_is_module_native_1(BIF_ALIST_1)
{
Module* modp;
@@ -58,8 +138,8 @@ BIF_RETTYPE code_is_module_native_1(BIF_ALIST_1)
return am_undefined;
}
erts_rlock_old_code(code_ix);
- res = (erts_is_module_native(modp->curr.code) ||
- erts_is_module_native(modp->old.code)) ?
+ res = (erts_is_module_native(modp->curr.code_hdr) ||
+ erts_is_module_native(modp->old.code_hdr)) ?
am_true : am_false;
erts_runlock_old_code(code_ix);
return res;
@@ -81,12 +161,12 @@ BIF_RETTYPE code_make_stub_module_3(BIF_ALIST_3)
modp = erts_get_module(BIF_ARG_1, erts_active_code_ix());
if (modp && modp->curr.num_breakpoints > 0) {
- ASSERT(modp->curr.code != NULL);
+ ASSERT(modp->curr.code_hdr != NULL);
erts_clear_module_break(modp);
ASSERT(modp->curr.num_breakpoints == 0);
}
- erts_start_staging_code_ix();
+ erts_start_staging_code_ix(1);
res = erts_make_stub_module(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
@@ -139,6 +219,25 @@ prepare_loading_2(BIF_ALIST_2)
BIF_RET(res);
}
+BIF_RETTYPE
+has_prepared_code_on_load_1(BIF_ALIST_1)
+{
+ Eterm res;
+ ProcBin* pb;
+
+ if (!ERTS_TERM_IS_MAGIC_BINARY(BIF_ARG_1)) {
+ error:
+ BIF_ERROR(BIF_P, BADARG);
+ }
+
+ pb = (ProcBin*) binary_val(BIF_ARG_1);
+ res = erts_has_code_on_load(pb->val);
+ if (res == NIL) {
+ goto error;
+ }
+ BIF_RET(res);
+}
+
struct m {
Binary* code;
Eterm module;
@@ -154,7 +253,7 @@ static struct /* Protected by code_write_permission */
{
Process* stager;
ErtsThrPrgrLaterOp lop;
-}commiter_state;
+} committer_state;
#endif
static Eterm
@@ -163,14 +262,13 @@ exception_list(Process* p, Eterm tag, struct m* mp, Sint exceptions)
Eterm* hp = HAlloc(p, 3 + 2*exceptions);
Eterm res = NIL;
- mp += exceptions - 1;
while (exceptions > 0) {
if (mp->exception) {
res = CONS(hp, mp->module, res);
hp += 2;
exceptions--;
}
- mp--;
+ mp++;
}
return TUPLE2(hp, tag, res);
}
@@ -179,8 +277,8 @@ exception_list(Process* p, Eterm tag, struct m* mp, Sint exceptions)
BIF_RETTYPE
finish_loading_1(BIF_ALIST_1)
{
- int i;
- int n;
+ Sint i;
+ Sint n;
struct m* p = NULL;
Uint exceptions;
Eterm res;
@@ -201,9 +299,13 @@ finish_loading_1(BIF_ALIST_1)
*/
n = erts_list_length(BIF_ARG_1);
- if (n == -1) {
- ERTS_BIF_PREP_ERROR(res, BIF_P, BADARG);
- goto done;
+ if (n < 0) {
+ badarg:
+ if (p) {
+ erts_free(ERTS_ALC_T_LOADER_TMP, p);
+ }
+ erts_release_code_write_permission();
+ BIF_ERROR(BIF_P, BADARG);
}
p = erts_alloc(ERTS_ALC_T_LOADER_TMP, n*sizeof(struct m));
@@ -218,29 +320,32 @@ finish_loading_1(BIF_ALIST_1)
ProcBin* pb;
if (!ERTS_TERM_IS_MAGIC_BINARY(term)) {
- ERTS_BIF_PREP_ERROR(res, BIF_P, BADARG);
- goto done;
+ goto badarg;
}
pb = (ProcBin*) binary_val(term);
p[i].code = pb->val;
p[i].module = erts_module_for_prepared_code(p[i].code);
if (p[i].module == NIL) {
- ERTS_BIF_PREP_ERROR(res, BIF_P, BADARG);
- goto done;
+ goto badarg;
}
BIF_ARG_1 = CDR(cons);
}
/*
* Since we cannot handle atomic loading of a group of modules
- * if one or more of them uses on_load, we will only allow one
- * element in the list. This limitation is intended to be
- * lifted in the future.
+ * if one or more of them uses on_load, we will only allow
+ * more than one element in the list if none of the modules
+ * have an on_load function.
*/
if (n > 1) {
- ERTS_BIF_PREP_ERROR(res, BIF_P, SYSTEM_LIMIT);
- goto done;
+ for (i = 0; i < n; i++) {
+ if (erts_has_code_on_load(p[i].code) == am_true) {
+ erts_free(ERTS_ALC_T_LOADER_TMP, p);
+ erts_release_code_write_permission();
+ BIF_ERROR(BIF_P, SYSTEM_LIMIT);
+ }
+ }
}
/*
@@ -252,11 +357,27 @@ finish_loading_1(BIF_ALIST_1)
*/
res = am_ok;
- erts_start_staging_code_ix();
+ erts_start_staging_code_ix(n);
for (i = 0; i < n; i++) {
p[i].modp = erts_put_module(p[i].module);
+ p[i].modp->seen = 0;
}
+
+ exceptions = 0;
+ for (i = 0; i < n; i++) {
+ p[i].exception = 0;
+ if (p[i].modp->seen) {
+ p[i].exception = 1;
+ exceptions++;
+ }
+ p[i].modp->seen = 1;
+ }
+ if (exceptions) {
+ res = exception_list(BIF_P, am_duplicated, p, exceptions);
+ goto done;
+ }
+
for (i = 0; i < n; i++) {
if (p[i].modp->curr.num_breakpoints > 0 ||
p[i].modp->curr.num_traced_exports > 0 ||
@@ -281,7 +402,7 @@ finish_loading_1(BIF_ALIST_1)
exceptions = 0;
for (i = 0; i < n; i++) {
p[i].exception = 0;
- if (p[i].modp->curr.code && p[i].modp->old.code) {
+ if (p[i].modp->curr.code_hdr && p[i].modp->old.code_hdr) {
p[i].exception = 1;
exceptions++;
}
@@ -367,9 +488,9 @@ staging_epilogue(Process* c_p, int commit, Eterm res, int is_blocking,
* schedulers to read active code_ix in a safe way while executing
* without any memory barriers at all.
*/
- ASSERT(commiter_state.stager == NULL);
- commiter_state.stager = c_p;
- erts_schedule_thr_prgr_later_op(smp_code_ix_commiter, NULL, &commiter_state.lop);
+ ASSERT(committer_state.stager == NULL);
+ committer_state.stager = c_p;
+ erts_schedule_thr_prgr_later_op(smp_code_ix_commiter, NULL, &committer_state.lop);
erts_proc_inc_refc(c_p);
erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL);
/*
@@ -385,11 +506,11 @@ staging_epilogue(Process* c_p, int commit, Eterm res, int is_blocking,
#ifdef ERTS_SMP
static void smp_code_ix_commiter(void* null)
{
- Process* p = commiter_state.stager;
+ Process* p = committer_state.stager;
erts_commit_staging_code_ix();
#ifdef DEBUG
- commiter_state.stager = NULL;
+ committer_state.stager = NULL;
#endif
erts_release_code_write_permission();
erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
@@ -417,7 +538,7 @@ check_old_code_1(BIF_ALIST_1)
modp = erts_get_module(BIF_ARG_1, code_ix);
if (modp != NULL) {
erts_rlock_old_code(code_ix);
- if (modp->old.code != NULL) {
+ if (modp->old.code_hdr) {
res = am_true;
}
erts_runlock_old_code(code_ix);
@@ -426,7 +547,7 @@ check_old_code_1(BIF_ALIST_1)
}
Eterm
-erts_check_process_code(Process *c_p, Eterm module, int allow_gc, int *redsp)
+erts_check_process_code(Process *c_p, Eterm module, Uint flags, int *redsp, int fcalls)
{
Module* modp;
Eterm res;
@@ -441,7 +562,8 @@ erts_check_process_code(Process *c_p, Eterm module, int allow_gc, int *redsp)
if (!modp)
return am_false;
erts_rlock_old_code(code_ix);
- res = modp->old.code ? check_process_code(c_p, modp, allow_gc, redsp) : am_false;
+ res = (!modp->old.code_hdr ? am_false :
+ check_process_code(c_p, modp, flags, redsp, fcalls));
erts_runlock_old_code(code_ix);
return res;
@@ -450,49 +572,21 @@ erts_check_process_code(Process *c_p, Eterm module, int allow_gc, int *redsp)
BIF_RETTYPE erts_internal_check_process_code_2(BIF_ALIST_2)
{
int reds = 0;
+ Uint flags;
Eterm res;
- Eterm olist = BIF_ARG_2;
- int allow_gc = 1;
if (is_not_atom(BIF_ARG_1))
goto badarg;
- while (is_list(olist)) {
- Eterm *lp = list_val(olist);
- Eterm opt = CAR(lp);
- if (is_tuple(opt)) {
- Eterm* tp = tuple_val(opt);
- switch (arityval(tp[0])) {
- case 2:
- switch (tp[1]) {
- case am_allow_gc:
- switch (tp[2]) {
- case am_false:
- allow_gc = 0;
- break;
- case am_true:
- allow_gc = 1;
- break;
- default:
- goto badarg;
- }
- break;
- default:
- goto badarg;
- }
- break;
- default:
- goto badarg;
- }
- }
- else
- goto badarg;
- olist = CDR(lp);
+ if (is_not_small(BIF_ARG_2))
+ goto badarg;
+
+ flags = unsigned_val(BIF_ARG_2);
+ if (flags & ~ERTS_CPC_ALL) {
+ goto badarg;
}
- if (is_not_nil(olist))
- goto badarg;
- res = erts_check_process_code(BIF_P, BIF_ARG_1, allow_gc, &reds);
+ res = erts_check_process_code(BIF_P, BIF_ARG_1, flags, &reds, BIF_P->fcalls);
ASSERT(is_value(res));
@@ -502,6 +596,43 @@ badarg:
BIF_ERROR(BIF_P, BADARG);
}
+BIF_RETTYPE erts_internal_check_dirty_process_code_2(BIF_ALIST_2)
+{
+#if !defined(ERTS_DIRTY_SCHEDULERS)
+ BIF_ERROR(BIF_P, EXC_NOTSUP);
+#else
+ Process *rp;
+ int reds = 0;
+ Eterm res;
+
+ if (BIF_P != erts_dirty_process_code_checker)
+ BIF_ERROR(BIF_P, EXC_NOTSUP);
+
+ if (is_not_internal_pid(BIF_ARG_1))
+ BIF_ERROR(BIF_P, BADARG);
+
+ if (is_not_atom(BIF_ARG_2))
+ BIF_ERROR(BIF_P, BADARG);
+
+ rp = erts_pid2proc_not_running(BIF_P, ERTS_PROC_LOCK_MAIN,
+ BIF_ARG_1, ERTS_PROC_LOCK_MAIN);
+ if (rp == ERTS_PROC_LOCK_BUSY)
+ ERTS_BIF_YIELD2(bif_export[BIF_erts_internal_check_dirty_process_code_2],
+ BIF_P, BIF_ARG_1, BIF_ARG_2);
+ if (!rp)
+ BIF_RET(am_false);
+
+ res = erts_check_process_code(rp, BIF_ARG_2, 0, &reds, BIF_P->fcalls);
+
+ if (BIF_P != rp)
+ erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_MAIN);
+
+ ASSERT(is_value(res));
+
+ BIF_RET2(res, reds);
+#endif
+}
+
BIF_RETTYPE delete_module_1(BIF_ALIST_1)
{
ErtsCodeIndex code_ix;
@@ -519,15 +650,15 @@ BIF_RETTYPE delete_module_1(BIF_ALIST_1)
}
{
- erts_start_staging_code_ix();
+ erts_start_staging_code_ix(0);
code_ix = erts_staging_code_ix();
modp = erts_get_module(BIF_ARG_1, code_ix);
if (!modp) {
res = am_undefined;
}
- else if (modp->old.code != 0) {
+ else if (modp->old.code_hdr) {
erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
- erts_dsprintf(dsbufp, "Module %T must be purged before loading\n",
+ erts_dsprintf(dsbufp, "Module %T must be purged before deleting\n",
BIF_ARG_1);
erts_send_error_to_logger(BIF_P->group_leader, dsbufp);
ERTS_BIF_PREP_ERROR(res, BIF_P, BADARG);
@@ -563,8 +694,8 @@ BIF_RETTYPE module_loaded_1(BIF_ALIST_1)
}
code_ix = erts_active_code_ix();
if ((modp = erts_get_module(BIF_ARG_1, code_ix)) != NULL) {
- if (modp->curr.code != NULL
- && modp->curr.code[MI_ON_LOAD_FUNCTION_PTR] == 0) {
+ if (modp->curr.code_hdr
+ && modp->curr.code_hdr->on_load_function_ptr == NULL) {
res = am_true;
}
}
@@ -611,10 +742,13 @@ BIF_RETTYPE call_on_load_function_1(BIF_ALIST_1)
{
Module* modp = erts_get_module(BIF_ARG_1, erts_active_code_ix());
- if (modp && modp->curr.code) {
- BIF_TRAP_CODE_PTR_0(BIF_P, modp->curr.code[MI_ON_LOAD_FUNCTION_PTR]);
+ if (!modp || !modp->on_load) {
+ BIF_ERROR(BIF_P, BADARG);
}
- else {
+ if (modp->on_load->code_hdr) {
+ BIF_TRAP_CODE_PTR_0(BIF_P,
+ modp->on_load->code_hdr->on_load_function_ptr);
+ } else {
BIF_ERROR(BIF_P, BADARG);
}
}
@@ -623,7 +757,6 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2)
{
ErtsCodeIndex code_ix;
Module* modp;
- Eterm on_load;
if (!erts_try_seize_code_write_permission(BIF_P)) {
ERTS_BIF_YIELD2(bif_export[BIF_finish_after_on_load_2],
@@ -638,14 +771,14 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2)
code_ix = erts_active_code_ix();
modp = erts_get_module(BIF_ARG_1, code_ix);
- if (!modp || modp->curr.code == 0) {
+ if (!modp || !modp->on_load || !modp->on_load->code_hdr) {
error:
erts_smp_thr_progress_unblock();
erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
erts_release_code_write_permission();
BIF_ERROR(BIF_P, BADARG);
}
- if ((on_load = modp->curr.code[MI_ON_LOAD_FUNCTION_PTR]) == 0) {
+ if (modp->on_load->code_hdr->on_load_function_ptr == NULL) {
goto error;
}
if (BIF_ARG_2 != am_false && BIF_ARG_2 != am_true) {
@@ -653,42 +786,61 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2)
}
if (BIF_ARG_2 == am_true) {
- int i;
+ int i, num_exps;
+
+ /*
+ * Make the code with the on_load function current.
+ */
+
+ if (modp->curr.code_hdr) {
+ modp->old = modp->curr;
+ }
+ modp->curr = *modp->on_load;
+ erts_free(ERTS_ALC_T_PREPARED_CODE, modp->on_load);
+ modp->on_load = 0;
/*
* The on_load function succeded. Fix up export entries.
*/
- for (i = 0; i < export_list_size(code_ix); i++) {
+ num_exps = export_list_size(code_ix);
+ for (i = 0; i < num_exps; i++) {
Export *ep = export_list(i,code_ix);
- if (ep != NULL &&
- ep->code[0] == BIF_ARG_1 &&
- ep->code[4] != 0) {
+ if (ep == NULL || ep->code[0] != BIF_ARG_1) {
+ continue;
+ }
+ if (ep->code[4] != 0) {
ep->addressv[code_ix] = (void *) ep->code[4];
ep->code[4] = 0;
+ } else {
+ if (ep->addressv[code_ix] == ep->code+3 &&
+ ep->code[3] == (BeamInstr) em_apply_bif) {
+ continue;
+ }
+ ep->addressv[code_ix] = ep->code+3;
+ ep->code[3] = (BeamInstr) em_call_error_handler;
}
}
- modp->curr.code[MI_ON_LOAD_FUNCTION_PTR] = 0;
+ modp->curr.code_hdr->on_load_function_ptr = NULL;
set_default_trace_pattern(BIF_ARG_1);
} else if (BIF_ARG_2 == am_false) {
- BeamInstr* code;
- BeamInstr* end;
+ int i, num_exps;
/*
- * The on_load function failed. Remove the loaded code.
- * This is an combination of delete and purge. We purge
- * the current code; the old code is not touched.
+ * The on_load function failed. Remove references to the
+ * code that is about to be purged from the export entries.
*/
- erts_total_code_size -= modp->curr.code_length;
- code = modp->curr.code;
- end = (BeamInstr *)((char *)code + modp->curr.code_length);
- erts_cleanup_funs_on_purge(code, end);
- beam_catches_delmod(modp->curr.catches, code, modp->curr.code_length,
- erts_active_code_ix());
- erts_free(ERTS_ALC_T_CODE, (void *) code);
- modp->curr.code = NULL;
- modp->curr.code_length = 0;
- modp->curr.catches = BEAM_CATCHES_NIL;
- erts_remove_from_ranges(code);
+
+ num_exps = export_list_size(code_ix);
+ for (i = 0; i < num_exps; i++) {
+ Export *ep = export_list(i,code_ix);
+ if (ep == NULL || ep->code[0] != BIF_ARG_1) {
+ continue;
+ }
+ if (ep->code[3] == (BeamInstr) em_apply_bif) {
+ continue;
+ }
+ ep->code[4] = 0;
+ }
}
erts_smp_thr_progress_unblock();
erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
@@ -703,13 +855,13 @@ set_default_trace_pattern(Eterm module)
Binary *match_spec;
Binary *meta_match_spec;
struct trace_pattern_flags trace_pattern_flags;
- Eterm meta_tracer_pid;
+ ErtsTracer meta_tracer;
erts_get_default_trace_pattern(&trace_pattern_is_on,
&match_spec,
&meta_match_spec,
&trace_pattern_flags,
- &meta_tracer_pid);
+ &meta_tracer);
if (trace_pattern_is_on) {
Eterm mfa[1];
mfa[0] = module;
@@ -717,43 +869,334 @@ set_default_trace_pattern(Eterm module)
match_spec,
meta_match_spec,
1, trace_pattern_flags,
- meta_tracer_pid, 1);
+ meta_tracer, 1);
+ }
+}
+
+#ifndef ERTS_NEW_PURGE_STRATEGY
+
+static ERTS_INLINE int
+check_mod_funs(Process *p, ErlOffHeap *off_heap, char *area, size_t area_size)
+{
+ struct erl_off_heap_header* oh;
+ for (oh = off_heap->first; oh; oh = oh->next) {
+ if (thing_subtag(oh->thing_word) == FUN_SUBTAG) {
+ ErlFunThing* funp = (ErlFunThing*) oh;
+ if (ErtsInArea(funp->fe->address, area, area_size))
+ return !0;
+ }
+ }
+ return 0;
+}
+
+#endif
+
+static Uint hfrag_literal_size(Eterm* start, Eterm* end,
+ char* lit_start, Uint lit_size);
+static void hfrag_literal_copy(Eterm **hpp, ErlOffHeap *ohp,
+ Eterm *start, Eterm *end,
+ char *lit_start, Uint lit_size);
+
+#ifdef ERTS_NEW_PURGE_STRATEGY
+
+Eterm
+erts_proc_copy_literal_area(Process *c_p, int *redsp, int fcalls, int gc_allowed)
+{
+ ErtsLiteralArea *la;
+ ErtsMessage *msgp;
+ struct erl_off_heap_header* oh;
+ char *literals;
+ Uint lit_bsize;
+ ErlHeapFragment *hfrag;
+
+ la = ERTS_COPY_LITERAL_AREA();
+ if (!la)
+ return am_ok;
+
+ oh = la->off_heap;
+ literals = (char *) &la->start[0];
+ lit_bsize = (char *) la->end - literals;
+
+ /*
+ * If a literal is in the message queue we make an explicit copy of
+ * it and attach it to the heap fragment. Each message needs to be
+ * self contained, we cannot save the literal in the old_heap or
+ * any other heap than the message it self.
+ */
+
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ);
+ ERTS_SMP_MSGQ_MV_INQ2PRIVQ(c_p);
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ);
+
+ for (msgp = c_p->msg.first; msgp; msgp = msgp->next) {
+ ErlHeapFragment *hf;
+ Uint lit_sz = 0;
+
+ *redsp += 1;
+
+ if (msgp->data.attached == ERTS_MSG_COMBINED_HFRAG)
+ hfrag = &msgp->hfrag;
+ else if (is_value(ERL_MESSAGE_TERM(msgp)) && msgp->data.heap_frag)
+ hfrag = msgp->data.heap_frag;
+ else
+ continue; /* Content on heap or in external term format... */
+
+ for (hf = hfrag; hf; hf = hf->next) {
+ lit_sz += hfrag_literal_size(&hf->mem[0], &hf->mem[hf->used_size],
+ literals, lit_bsize);
+ *redsp += 1;
+ }
+
+ *redsp += lit_sz / 16; /* Better value needed... */
+ if (lit_sz > 0) {
+ ErlHeapFragment *bp = new_message_buffer(lit_sz);
+ Eterm *hp = bp->mem;
+
+ for (hf = hfrag; hf; hf = hf->next) {
+ hfrag_literal_copy(&hp, &bp->off_heap,
+ &hf->mem[0], &hf->mem[hf->used_size],
+ literals, lit_bsize);
+ hfrag = hf;
+ }
+
+ /* link new hfrag last */
+ ASSERT(hfrag->next == NULL);
+ hfrag->next = bp;
+ bp->next = NULL;
+ }
+ }
+
+ if (gc_allowed) {
+ /*
+ * Current implementation first tests without
+ * allowing GC, and then restarts the operation
+ * allowing GC if it is needed. It is therfore
+ * very likely that we will need the GC (although
+ * this is not completely certain). We go for
+ * the GC directly instead of scanning everything
+ * one more time...
+ */
+ goto literal_gc;
+ }
+
+ *redsp += 2;
+ if (any_heap_ref_ptrs(&c_p->fvalue, &c_p->fvalue+1, literals, lit_bsize)) {
+ c_p->freason = EXC_NULL;
+ c_p->fvalue = NIL;
+ c_p->ftrace = NIL;
+ }
+
+ if (any_heap_ref_ptrs(c_p->stop, c_p->hend, literals, lit_bsize))
+ goto literal_gc;
+ *redsp += 1;
+#ifdef HIPE
+ if (nstack_any_heap_ref_ptrs(c_p, literals, lit_bsize))
+ goto literal_gc;
+ *redsp += 1;
+#endif
+ if (any_heap_refs(c_p->heap, c_p->htop, literals, lit_bsize))
+ goto literal_gc;
+ *redsp += 1;
+ if (c_p->abandoned_heap) {
+ if (any_heap_refs(c_p->abandoned_heap, c_p->abandoned_heap + c_p->heap_sz,
+ literals, lit_bsize))
+ goto literal_gc;
+ *redsp += 1;
+ }
+ if (any_heap_refs(c_p->old_heap, c_p->old_htop, literals, lit_bsize))
+ goto literal_gc;
+
+ /* Check dictionary */
+ *redsp += 1;
+ if (c_p->dictionary) {
+ Eterm* start = ERTS_PD_START(c_p->dictionary);
+ Eterm* end = start + ERTS_PD_SIZE(c_p->dictionary);
+
+ if (any_heap_ref_ptrs(start, end, literals, lit_bsize))
+ goto literal_gc;
+ }
+
+ /* Check heap fragments */
+ for (hfrag = c_p->mbuf; hfrag; hfrag = hfrag->next) {
+ Eterm *hp, *hp_end;
+
+ *redsp += 1;
+
+ hp = &hfrag->mem[0];
+ hp_end = &hfrag->mem[hfrag->used_size];
+ if (any_heap_refs(hp, hp_end, literals, lit_bsize))
+ goto literal_gc;
+ }
+
+ /*
+ * Message buffer fragments (matched messages)
+ * - off heap lists should already have been moved into
+ * process off heap structure.
+ * - Check for literals
+ */
+ for (msgp = c_p->msg_frag; msgp; msgp = msgp->next) {
+ hfrag = erts_message_to_heap_frag(msgp);
+ for (; hfrag; hfrag = hfrag->next) {
+ Eterm *hp, *hp_end;
+
+ *redsp += 1;
+
+ hp = &hfrag->mem[0];
+ hp_end = &hfrag->mem[hfrag->used_size];
+
+ if (any_heap_refs(hp, hp_end, literals, lit_bsize))
+ goto literal_gc;
+ }
+ }
+
+ return am_ok;
+
+literal_gc: {
+ int hibernated = !!(c_p->flags & F_HIBERNATED);
+ int gc_cost;
+
+ if (!gc_allowed)
+ return am_need_gc;
+
+ if (c_p->flags & F_DISABLE_GC)
+ return THE_NON_VALUE;
+
+ FLAGS(c_p) |= F_NEED_FULLSWEEP;
+
+ gc_cost = erts_garbage_collect_nobump(c_p, 0, c_p->arg_reg, c_p->arity, fcalls);
+ *redsp += gc_cost;
+
+ erts_garbage_collect_literals(c_p, (Eterm *) literals, lit_bsize, oh);
+
+ *redsp += lit_bsize / 64; /* Need, better value... */
+
+ if (hibernated) {
+ erts_garbage_collect_hibernate(c_p);
+ *redsp += gc_cost;
+ }
+
+ return am_ok;
}
}
static Eterm
-check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp)
+check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls)
{
BeamInstr* start;
char* mod_start;
Uint mod_size;
- BeamInstr* end;
Eterm* sp;
- struct erl_off_heap_header* oh;
+
+ *redsp += 1;
+
+ /*
+ * Pick up limits for the module.
+ */
+ start = (BeamInstr*) modp->old.code_hdr;
+ mod_start = (char *) start;
+ mod_size = modp->old.code_length;
+
+ /*
+ * Check if current instruction or continuation pointer points into module.
+ */
+ if (ErtsInArea(rp->i, mod_start, mod_size)
+ || ErtsInArea(rp->cp, mod_start, mod_size)) {
+ return am_true;
+ }
+
+ *redsp += 1;
+
+ if (erts_check_nif_export_in_area(rp, mod_start, mod_size))
+ return am_true;
+
+ *redsp += (STACK_START(rp) - rp->stop) / 32;
+
+ /*
+ * Check all continuation pointers stored on the stack.
+ */
+ for (sp = rp->stop; sp < STACK_START(rp); sp++) {
+ if (is_CP(*sp) && ErtsInArea(cp_val(*sp), mod_start, mod_size)) {
+ return am_true;
+ }
+ }
+
+ /*
+ * Check all continuation pointers stored in stackdump
+ * and clear exception stackdump if there is a pointer
+ * to the module.
+ */
+ if (rp->ftrace != NIL) {
+ struct StackTrace *s;
+ ASSERT(is_list(rp->ftrace));
+ s = (struct StackTrace *) big_val(CDR(list_val(rp->ftrace)));
+ if ((s->pc && ErtsInArea(s->pc, mod_start, mod_size)) ||
+ (s->current && ErtsInArea(s->current, mod_start, mod_size))) {
+ rp->freason = EXC_NULL;
+ rp->fvalue = NIL;
+ rp->ftrace = NIL;
+ } else {
+ int i;
+ for (i = 0; i < s->depth; i++) {
+ if (ErtsInArea(s->trace[i], mod_start, mod_size)) {
+ rp->freason = EXC_NULL;
+ rp->fvalue = NIL;
+ rp->ftrace = NIL;
+ break;
+ }
+ }
+ }
+ }
+
+ return am_false;
+}
+
+#else /* !ERTS_NEW_PURGE_STRATEGY, i.e, old style purge... */
+
+static Eterm
+check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls)
+{
+ BeamInstr* start;
+ char* literals;
+ Uint lit_bsize;
+ char* mod_start;
+ Uint mod_size;
+ Eterm* sp;
int done_gc = 0;
+ int need_gc = 0;
+ int hibernated = !!(rp->flags & F_HIBERNATED);
+ int gc_cost = 0;
+ ErtsMessage *msgp;
+ ErlHeapFragment *hfrag;
-#define INSIDE(a) (start <= (a) && (a) < end)
+#define ERTS_ORDINARY_GC__ (1 << 0)
+#define ERTS_LITERAL_GC__ (1 << 1)
/*
* Pick up limits for the module.
*/
- start = modp->old.code;
- end = (BeamInstr *)((char *)start + modp->old.code_length);
+ start = (BeamInstr*) modp->old.code_hdr;
mod_start = (char *) start;
mod_size = modp->old.code_length;
/*
* Check if current instruction or continuation pointer points into module.
*/
- if (INSIDE(rp->i) || INSIDE(rp->cp)) {
+ if (ErtsInArea(rp->i, mod_start, mod_size)
+ || ErtsInArea(rp->cp, mod_start, mod_size)) {
return am_true;
}
+
+ *redsp += 1;
+
+ if (erts_check_nif_export_in_area(rp, mod_start, mod_size))
+ return am_true;
+
/*
* Check all continuation pointers stored on the stack.
*/
for (sp = rp->stop; sp < STACK_START(rp); sp++) {
- if (is_CP(*sp) && INSIDE(cp_val(*sp))) {
+ if (is_CP(*sp) && ErtsInArea(cp_val(*sp), mod_start, mod_size)) {
return am_true;
}
}
@@ -767,15 +1210,15 @@ check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp)
struct StackTrace *s;
ASSERT(is_list(rp->ftrace));
s = (struct StackTrace *) big_val(CDR(list_val(rp->ftrace)));
- if ((s->pc && INSIDE(s->pc)) ||
- (s->current && INSIDE(s->current))) {
+ if ((s->pc && ErtsInArea(s->pc, mod_start, mod_size)) ||
+ (s->current && ErtsInArea(s->current, mod_start, mod_size))) {
rp->freason = EXC_NULL;
rp->fvalue = NIL;
rp->ftrace = NIL;
} else {
int i;
for (i = 0; i < s->depth; i++) {
- if (INSIDE(s->trace[i])) {
+ if (ErtsInArea(s->trace[i], mod_start, mod_size)) {
rp->freason = EXC_NULL;
rp->fvalue = NIL;
rp->ftrace = NIL;
@@ -796,111 +1239,187 @@ check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp)
}
/*
- * See if there are funs that refer to the old version of the module.
+ * Message queue can contains funs, and may contain
+ * literals. If we got references to this module from the message
+ * queue.
+ *
+ * If a literal is in the message queue we maka an explicit copy of
+ * and attach it to the heap fragment. Each message needs to be
+ * self contained, we cannot save the literal in the old_heap or
+ * any other heap than the message it self.
*/
- rescan:
- for (oh = MSO(rp).first; oh; oh = oh->next) {
- if (thing_subtag(oh->thing_word) == FUN_SUBTAG) {
- ErlFunThing* funp = (ErlFunThing*) oh;
+ erts_smp_proc_lock(rp, ERTS_PROC_LOCK_MSGQ);
+ ERTS_SMP_MSGQ_MV_INQ2PRIVQ(rp);
+ erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_MSGQ);
- if (INSIDE((BeamInstr *) funp->fe->address)) {
- if (done_gc) {
- return am_true;
- } else {
- if (!allow_gc)
- return am_aborted;
- /*
- * Try to get rid of this fun by garbage collecting.
- * Clear both fvalue and ftrace to make sure they
- * don't hold any funs.
- */
- rp->freason = EXC_NULL;
- rp->fvalue = NIL;
- rp->ftrace = NIL;
- done_gc = 1;
- FLAGS(rp) |= F_NEED_FULLSWEEP;
- *redsp += erts_garbage_collect(rp, 0, rp->arg_reg, rp->arity);
- goto rescan;
- }
- }
- }
+ if (modp->old.code_hdr->literal_area) {
+ literals = (char*) modp->old.code_hdr->literal_area->start;
+ lit_bsize = (char*) modp->old.code_hdr->literal_area->end - literals;
+ }
+ else {
+ literals = NULL;
+ lit_bsize = 0;
}
- /*
- * See if there are constants inside the module referenced by the process.
- */
- done_gc = 0;
- for (;;) {
- ErlMessage* mp;
+ for (msgp = rp->msg.first; msgp; msgp = msgp->next) {
+ if (msgp->data.attached == ERTS_MSG_COMBINED_HFRAG)
+ hfrag = &msgp->hfrag;
+ else if (is_value(ERL_MESSAGE_TERM(msgp)) && msgp->data.heap_frag)
+ hfrag = msgp->data.heap_frag;
+ else
+ continue;
+ {
+ ErlHeapFragment *hf;
+ Uint lit_sz;
+ for (hf=hfrag; hf; hf = hf->next) {
+ if (check_mod_funs(rp, &hfrag->off_heap, mod_start, mod_size))
+ return am_true;
+ lit_sz = hfrag_literal_size(&hf->mem[0], &hf->mem[hf->used_size],
+ literals, lit_bsize);
+ }
+ if (lit_sz > 0) {
+ ErlHeapFragment *bp = new_message_buffer(lit_sz);
+ Eterm *hp = bp->mem;
+
+ for (hf=hfrag; hf; hf = hf->next) {
+ hfrag_literal_copy(&hp, &bp->off_heap,
+ &hf->mem[0], &hf->mem[hf->used_size],
+ literals, lit_bsize);
+ hfrag=hf;
+ }
+ /* link new hfrag last */
+ ASSERT(hfrag->next == NULL);
+ hfrag->next = bp;
+ bp->next = NULL;
+ }
+ }
+ }
+
+ while (1) {
- if (any_heap_ref_ptrs(&rp->fvalue, &rp->fvalue+1, mod_start, mod_size)) {
+ /* Check heap, stack etc... */
+ if (check_mod_funs(rp, &rp->off_heap, mod_start, mod_size)) {
+ if (hibernated) {
+ /* GC wont help; everything on heap is live... */
+ return am_true;
+ }
+ goto try_gc;
+ }
+ if (any_heap_ref_ptrs(&rp->fvalue, &rp->fvalue+1, literals, lit_bsize)) {
rp->freason = EXC_NULL;
rp->fvalue = NIL;
rp->ftrace = NIL;
}
- if (any_heap_ref_ptrs(rp->stop, rp->hend, mod_start, mod_size)) {
- goto need_gc;
- }
- if (any_heap_refs(rp->heap, rp->htop, mod_start, mod_size)) {
- goto need_gc;
+ if (any_heap_ref_ptrs(rp->stop, rp->hend, literals, lit_bsize))
+ goto try_literal_gc;
+#ifdef HIPE
+ if (nstack_any_heap_ref_ptrs(rp, literals, lit_bsize))
+ goto try_literal_gc;
+#endif
+ if (any_heap_refs(rp->heap, rp->htop, literals, lit_bsize))
+ goto try_literal_gc;
+ if (rp->abandoned_heap) {
+ if (any_heap_refs(rp->abandoned_heap, rp->abandoned_heap + rp->heap_sz,
+ literals, lit_bsize))
+ goto try_literal_gc;
}
+ if (any_heap_refs(rp->old_heap, rp->old_htop, literals, lit_bsize))
+ goto try_literal_gc;
- if (any_heap_refs(rp->old_heap, rp->old_htop, mod_start, mod_size)) {
- goto need_gc;
+ /* Check dictionary */
+ if (rp->dictionary) {
+ Eterm* start = ERTS_PD_START(rp->dictionary);
+ Eterm* end = start + ERTS_PD_SIZE(rp->dictionary);
+
+ if (any_heap_ref_ptrs(start, end, literals, lit_bsize))
+ goto try_literal_gc;
}
- if (rp->dictionary != NULL) {
- Eterm* start = rp->dictionary->data;
- Eterm* end = start + rp->dictionary->used;
+ /* Check heap fragments */
+ for (hfrag = rp->mbuf; hfrag; hfrag = hfrag->next) {
+ Eterm *hp, *hp_end;
+ /* Off heap lists should already have been moved into process */
+ ASSERT(!check_mod_funs(rp, &hfrag->off_heap, mod_start, mod_size));
- if (any_heap_ref_ptrs(start, end, mod_start, mod_size)) {
- goto need_gc;
- }
+ hp = &hfrag->mem[0];
+ hp_end = &hfrag->mem[hfrag->used_size];
+ if (any_heap_refs(hp, hp_end, literals, lit_bsize))
+ goto try_literal_gc;
}
- for (mp = rp->msg.first; mp != NULL; mp = mp->next) {
- if (any_heap_ref_ptrs(mp->m, mp->m+2, mod_start, mod_size)) {
- goto need_gc;
+ /*
+ * Message buffer fragments (matched messages)
+ * - off heap lists should already have been moved into
+ * process off heap structure.
+ * - Check for literals
+ */
+ for (msgp = rp->msg_frag; msgp; msgp = msgp->next) {
+ hfrag = erts_message_to_heap_frag(msgp);
+ for (; hfrag; hfrag = hfrag->next) {
+ Eterm *hp, *hp_end;
+ ASSERT(!check_mod_funs(rp, &hfrag->off_heap, mod_start, mod_size));
+
+ hp = &hfrag->mem[0];
+ hp_end = &hfrag->mem[hfrag->used_size];
+
+ if (any_heap_refs(hp, hp_end, literals, lit_bsize))
+ goto try_literal_gc;
}
}
- break;
- need_gc:
- if (done_gc) {
+ return am_false;
+
+ try_literal_gc:
+ need_gc |= ERTS_LITERAL_GC__;
+
+ try_gc:
+ need_gc |= ERTS_ORDINARY_GC__;
+
+ if ((done_gc & need_gc) == need_gc)
return am_true;
- } else {
- Eterm* literals;
- Uint lit_size;
- struct erl_off_heap_header* oh;
- if (!allow_gc)
- return am_aborted;
+ if (!(flags & ERTS_CPC_ALLOW_GC))
+ return am_aborted;
- /*
- * Try to get rid of constants by by garbage collecting.
- * Clear both fvalue and ftrace.
- */
- rp->freason = EXC_NULL;
- rp->fvalue = NIL;
- rp->ftrace = NIL;
- done_gc = 1;
+ need_gc &= ~done_gc;
+
+ /*
+ * Try to get rid of literals by by garbage collecting.
+ * Clear both fvalue and ftrace.
+ */
+
+ rp->freason = EXC_NULL;
+ rp->fvalue = NIL;
+ rp->ftrace = NIL;
+
+ if (need_gc & ERTS_ORDINARY_GC__) {
FLAGS(rp) |= F_NEED_FULLSWEEP;
- *redsp += erts_garbage_collect(rp, 0, rp->arg_reg, rp->arity);
- literals = (Eterm *) modp->old.code[MI_LITERALS_START];
- lit_size = (Eterm *) modp->old.code[MI_LITERALS_END] - literals;
- oh = (struct erl_off_heap_header *)
- modp->old.code[MI_LITERALS_OFF_HEAP];
- *redsp += lit_size / 10; /* Need, better value... */
- erts_garbage_collect_literals(rp, literals, lit_size, oh);
+ gc_cost = erts_garbage_collect_nobump(rp, 0, rp->arg_reg, rp->arity, fcalls);
+ ASSERT(!hibernated || (need_gc & ERTS_LITERAL_GC__));
+ *redsp += gc_cost;
+ done_gc |= ERTS_ORDINARY_GC__;
+ }
+ if (need_gc & ERTS_LITERAL_GC__) {
+ struct erl_off_heap_header* oh;
+ oh = modp->old.code_hdr->literal_area->off_heap;
+ *redsp += lit_bsize / 64; /* Need, better value... */
+ erts_garbage_collect_literals(rp, (Eterm*)literals, lit_bsize, oh);
+ done_gc |= ERTS_LITERAL_GC__;
+ if (hibernated) {
+ erts_garbage_collect_hibernate(rp);
+ *redsp += gc_cost;
+ }
}
+ need_gc = 0;
}
- return am_false;
-#undef INSIDE
+
+#undef ERTS_ORDINARY_GC__
+#undef ERTS_LITERAL_GC__
+
}
-#define in_area(ptr,start,nbytes) \
- ((UWord)((char*)(ptr) - (char*)(start)) < (nbytes))
+#endif /* !ERTS_NEW_PURGE_STRATEGY */
static int
any_heap_ref_ptrs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size)
@@ -913,7 +1432,7 @@ any_heap_ref_ptrs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size)
switch (primary_tag(val)) {
case TAG_PRIMARY_BOXED:
case TAG_PRIMARY_LIST:
- if (in_area(EXPAND_POINTER(val), mod_start, mod_size)) {
+ if (ErtsInArea(val, mod_start, mod_size)) {
return 1;
}
break;
@@ -933,7 +1452,7 @@ any_heap_refs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size)
switch (primary_tag(val)) {
case TAG_PRIMARY_BOXED:
case TAG_PRIMARY_LIST:
- if (in_area(EXPAND_POINTER(val), mod_start, mod_size)) {
+ if (ErtsInArea(val, mod_start, mod_size)) {
return 1;
}
break;
@@ -943,7 +1462,7 @@ any_heap_refs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size)
if (header_is_bin_matchstate(val)) {
ErlBinMatchState *ms = (ErlBinMatchState*) p;
ErlBinMatchBuffer *mb = &(ms->mb);
- if (in_area(EXPAND_POINTER(mb->orig), mod_start, mod_size)) {
+ if (ErtsInArea(mb->orig, mod_start, mod_size)) {
return 1;
}
}
@@ -956,100 +1475,576 @@ any_heap_refs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size)
return 0;
}
-#undef in_area
+static Uint
+hfrag_literal_size(Eterm* start, Eterm* end, char* lit_start, Uint lit_size)
+{
+ Eterm* p;
+ Eterm val;
+ Uint sz = 0;
-BIF_RETTYPE purge_module_1(BIF_ALIST_1)
+ for (p = start; p < end; p++) {
+ val = *p;
+ switch (primary_tag(val)) {
+ case TAG_PRIMARY_BOXED:
+ case TAG_PRIMARY_LIST:
+ if (ErtsInArea(val, lit_start, lit_size)) {
+ sz += size_object(val);
+ }
+ break;
+ case TAG_PRIMARY_HEADER:
+ if (!header_is_transparent(val)) {
+ Eterm* new_p;
+ if (header_is_bin_matchstate(val)) {
+ ErlBinMatchState *ms = (ErlBinMatchState*) p;
+ ErlBinMatchBuffer *mb = &(ms->mb);
+ if (ErtsInArea(mb->orig, lit_start, lit_size)) {
+ sz += size_object(mb->orig);
+ }
+ }
+ new_p = p + thing_arityval(val);
+ ASSERT(start <= new_p && new_p < end);
+ p = new_p;
+ }
+ }
+ }
+ return sz;
+}
+
+static void
+hfrag_literal_copy(Eterm **hpp, ErlOffHeap *ohp,
+ Eterm *start, Eterm *end,
+ char *lit_start, Uint lit_size) {
+ Eterm* p;
+ Eterm val;
+ Uint sz;
+
+ for (p = start; p < end; p++) {
+ val = *p;
+ switch (primary_tag(val)) {
+ case TAG_PRIMARY_BOXED:
+ case TAG_PRIMARY_LIST:
+ if (ErtsInArea(val, lit_start, lit_size)) {
+ sz = size_object(val);
+ val = copy_struct(val, sz, hpp, ohp);
+ *p = val;
+ }
+ break;
+ case TAG_PRIMARY_HEADER:
+ if (!header_is_transparent(val)) {
+ Eterm* new_p;
+ /* matchstate in message, not possible. */
+ if (header_is_bin_matchstate(val)) {
+ ErlBinMatchState *ms = (ErlBinMatchState*) p;
+ ErlBinMatchBuffer *mb = &(ms->mb);
+ if (ErtsInArea(mb->orig, lit_start, lit_size)) {
+ sz = size_object(mb->orig);
+ mb->orig = copy_struct(mb->orig, sz, hpp, ohp);
+ }
+ }
+ new_p = p + thing_arityval(val);
+ ASSERT(start <= new_p && new_p < end);
+ p = new_p;
+ }
+ }
+ }
+}
+
+#ifdef ERTS_NEW_PURGE_STRATEGY
+
+#ifdef ERTS_SMP
+
+ErtsThrPrgrLaterOp later_literal_area_switch;
+
+typedef struct {
+ ErtsThrPrgrLaterOp lop;
+ ErtsLiteralArea *la;
+} ErtsLaterReleasLiteralArea;
+
+static void
+later_release_literal_area(void *vlrlap)
{
- ErtsCodeIndex code_ix;
- BeamInstr* code;
- BeamInstr* end;
- Module* modp;
- int is_blocking = 0;
- Eterm ret;
+ ErtsLaterReleasLiteralArea *lrlap;
+ lrlap = (ErtsLaterReleasLiteralArea *) vlrlap;
+ erts_release_literal_area(lrlap->la);
+ erts_free(ERTS_ALC_T_RELEASE_LAREA, vlrlap);
+}
- if (is_not_atom(BIF_ARG_1)) {
- BIF_ERROR(BIF_P, BADARG);
+static void
+complete_literal_area_switch(void *literal_area)
+{
+ Process *p = erts_literal_area_collector;
+ erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
+ erts_resume(p, ERTS_PROC_LOCK_STATUS);
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ if (literal_area)
+ erts_release_literal_area((ErtsLiteralArea *) literal_area);
+}
+#endif
+
+#endif /* ERTS_NEW_PURGE_STRATEGY */
+
+BIF_RETTYPE erts_internal_release_literal_area_switch_0(BIF_ALIST_0)
+{
+#ifndef ERTS_NEW_PURGE_STRATEGY
+ BIF_ERROR(BIF_P, EXC_NOTSUP);
+#else
+ ErtsLiteralArea *unused_la;
+ ErtsLiteralAreaRef *la_ref;
+
+ if (BIF_P != erts_literal_area_collector)
+ BIF_ERROR(BIF_P, EXC_NOTSUP);
+
+ erts_smp_mtx_lock(&release_literal_areas.mtx);
+
+ la_ref = release_literal_areas.first;
+ if (la_ref) {
+ release_literal_areas.first = la_ref->next;
+ if (!release_literal_areas.first)
+ release_literal_areas.last = NULL;
}
- if (!erts_try_seize_code_write_permission(BIF_P)) {
- ERTS_BIF_YIELD1(bif_export[BIF_purge_module_1], BIF_P, BIF_ARG_1);
+ erts_smp_mtx_unlock(&release_literal_areas.mtx);
+
+ unused_la = ERTS_COPY_LITERAL_AREA();
+
+ if (!la_ref) {
+ ERTS_SET_COPY_LITERAL_AREA(NULL);
+ if (unused_la) {
+#ifdef ERTS_SMP
+ ErtsLaterReleasLiteralArea *lrlap;
+ lrlap = erts_alloc(ERTS_ALC_T_RELEASE_LAREA,
+ sizeof(ErtsLaterReleasLiteralArea));
+ lrlap->la = unused_la;
+ erts_schedule_thr_prgr_later_cleanup_op(
+ later_release_literal_area,
+ (void *) lrlap,
+ &lrlap->lop,
+ (sizeof(ErtsLaterReleasLiteralArea)
+ + sizeof(ErtsLiteralArea)
+ + ((unused_la->end
+ - &unused_la->start[0])
+ - 1)*(sizeof(Eterm))));
+#else
+ erts_release_literal_area(unused_la);
+#endif
+ }
+ BIF_RET(am_false);
}
- code_ix = erts_active_code_ix();
+ ERTS_SET_COPY_LITERAL_AREA(la_ref->literal_area);
+
+ erts_free(ERTS_ALC_T_LITERAL_REF, la_ref);
+
+#ifdef ERTS_SMP
+ erts_schedule_thr_prgr_later_op(complete_literal_area_switch,
+ unused_la,
+ &later_literal_area_switch);
+ erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, NULL);
+ ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
+#else
+ erts_release_literal_area(unused_la);
+ BIF_RET(am_true);
+#endif
+
+#endif /* ERTS_NEW_PURGE_STRATEGY */
+}
+
+void
+erts_purge_state_add_fun(ErlFunEntry *fe)
+{
+ ASSERT(is_value(purge_state.module));
+ if (purge_state.fe_ix >= purge_state.fe_size) {
+ ErlFunEntry **funs;
+ purge_state.fe_size += 100;
+ funs = erts_alloc(ERTS_ALC_T_PURGE_DATA,
+ sizeof(ErlFunEntry *)*purge_state.fe_size);
+ sys_memcpy((void *) funs,
+ (void *) purge_state.funs,
+ purge_state.fe_ix*sizeof(ErlFunEntry *));
+ if (purge_state.funs != &purge_state.def_funs[0])
+ erts_free(ERTS_ALC_T_PURGE_DATA, purge_state.funs);
+ purge_state.funs = funs;
+ }
+ purge_state.funs[purge_state.fe_ix++] = fe;
+}
+
+Export *
+erts_suspend_process_on_pending_purge_lambda(Process *c_p, ErlFunEntry* fe)
+{
+ erts_smp_mtx_lock(&purge_state.mtx);
+ if (purge_state.module == fe->module) {
+ /*
+ * The process c_p is about to call a fun in the code
+ * that we are trying to purge. Suspend it and call
+ * erts_code_purger:pending_purge_lambda/3. The process
+ * will be resumed when the purge completes or aborts,
+ * and will then try to do the call again.
+ */
+ if (purge_state.sp_ix >= purge_state.sp_size) {
+ Eterm *sprocs;
+ purge_state.sp_size += 100;
+ sprocs = erts_alloc(ERTS_ALC_T_PURGE_DATA,
+ (sizeof(ErlFunEntry *)
+ * purge_state.sp_size));
+ sys_memcpy((void *) sprocs,
+ (void *) purge_state.sprocs,
+ purge_state.sp_ix*sizeof(ErlFunEntry *));
+ if (purge_state.sprocs != &purge_state.def_sprocs[0])
+ erts_free(ERTS_ALC_T_PURGE_DATA, purge_state.sprocs);
+ purge_state.sprocs = sprocs;
+ }
+ purge_state.sprocs[purge_state.sp_ix++] = c_p->common.id;
+ erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL);
+ ERTS_VBUMP_ALL_REDS(c_p);
+ }
+ erts_smp_mtx_unlock(&purge_state.mtx);
+ return purge_state.pending_purge_lambda;
+}
+
+static void
+finalize_purge_operation(Process *c_p, int succeded)
+{
+ Uint ix;
+
+ if (c_p)
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
+
+ erts_smp_mtx_lock(&purge_state.mtx);
+
+ ASSERT(purge_state.module != THE_NON_VALUE);
+
+ purge_state.module = THE_NON_VALUE;
/*
- * Correct module?
+ * Resume all processes that have tried to call
+ * funs in this code.
*/
+ for (ix = 0; ix < purge_state.sp_ix; ix++) {
+ Process *rp = erts_pid2proc(NULL, 0,
+ purge_state.sprocs[ix],
+ ERTS_PROC_LOCK_STATUS);
+ if (rp) {
+ erts_resume(rp, ERTS_PROC_LOCK_STATUS);
+ erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_STATUS);
+ }
+ }
- if ((modp = erts_get_module(BIF_ARG_1, code_ix)) == NULL) {
- ERTS_BIF_PREP_ERROR(ret, BIF_P, BADARG);
+ erts_smp_mtx_unlock(&purge_state.mtx);
+
+ if (c_p)
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
+
+ if (purge_state.sprocs != &purge_state.def_sprocs[0]) {
+ erts_free(ERTS_ALC_T_PURGE_DATA, purge_state.sprocs);
+ purge_state.sprocs = &purge_state.def_sprocs[0];
+ purge_state.sp_size = sizeof(purge_state.def_sprocs);
+ purge_state.sp_size /= sizeof(purge_state.def_sprocs[0]);
}
- else {
- erts_rwlock_old_code(code_ix);
+ purge_state.sp_ix = 0;
+ if (purge_state.funs != &purge_state.def_funs[0]) {
+ erts_free(ERTS_ALC_T_PURGE_DATA, purge_state.funs);
+ purge_state.funs = &purge_state.def_funs[0];
+ purge_state.fe_size = sizeof(purge_state.def_funs);
+ purge_state.fe_size /= sizeof(purge_state.def_funs[0]);
+ }
+ purge_state.fe_ix = 0;
+}
+
+#ifdef ERTS_SMP
+
+static ErtsThrPrgrLaterOp purger_lop_data;
+
+static void
+resume_purger(void *unused)
+{
+ Process *p = erts_code_purger;
+ erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
+ erts_resume(p, ERTS_PROC_LOCK_STATUS);
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+}
+
+static void
+finalize_purge_abort(void *unused)
+{
+ erts_fun_purge_abort_finalize(purge_state.funs, purge_state.fe_ix);
+
+ finalize_purge_operation(NULL, 0);
+
+ resume_purger(NULL);
+}
+
+#endif /* ERTS_SMP */
+
+BIF_RETTYPE erts_internal_purge_module_2(BIF_ALIST_2)
+{
+ if (BIF_P != erts_code_purger)
+ BIF_ERROR(BIF_P, EXC_NOTSUP);
+
+ if (is_not_atom(BIF_ARG_1))
+ BIF_ERROR(BIF_P, BADARG);
+
+ switch (BIF_ARG_2) {
+
+ case am_prepare:
+ case am_prepare_on_load: {
/*
- * Any code to purge?
+ * Prepare for purge by marking all fun
+ * entries referring to the code to purge
+ * with "pending purge" markers.
*/
- if (modp->old.code == 0) {
- ERTS_BIF_PREP_ERROR(ret, BIF_P, BADARG);
- }
+ ErtsCodeIndex code_ix;
+ Module* modp;
+ Eterm res;
+
+ if (is_value(purge_state.module))
+ BIF_ERROR(BIF_P, BADARG);
+
+ code_ix = erts_active_code_ix();
+
+ /*
+ * Correct module?
+ */
+ modp = erts_get_module(BIF_ARG_1, code_ix);
+ if (!modp)
+ res = am_false;
else {
/*
- * Unload any NIF library
+ * Any code to purge?
*/
- if (modp->old.nif != NULL) {
- /* ToDo: Do unload nif without blocking */
- erts_rwunlock_old_code(code_ix);
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- erts_smp_thr_progress_block();
- is_blocking = 1;
- erts_rwlock_old_code(code_ix);
- erts_unload_nif(modp->old.nif);
- modp->old.nif = NULL;
+
+ if (BIF_ARG_2 == am_prepare_on_load) {
+ erts_rwlock_old_code(code_ix);
+ } else {
+ erts_rlock_old_code(code_ix);
+ }
+
+ if (BIF_ARG_2 == am_prepare_on_load) {
+ ASSERT(modp->on_load);
+ ASSERT(modp->on_load->code_hdr);
+ purge_state.saved_old = modp->old;
+ modp->old = *modp->on_load;
+ erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) modp->on_load);
+ modp->on_load = 0;
+ }
+
+ if (!modp->old.code_hdr)
+ res = am_false;
+ else {
+ BeamInstr* code;
+ BeamInstr* end;
+ erts_smp_mtx_lock(&purge_state.mtx);
+ purge_state.module = BIF_ARG_1;
+ erts_smp_mtx_unlock(&purge_state.mtx);
+ res = am_true;
+ code = (BeamInstr*) modp->old.code_hdr;
+ end = (BeamInstr *)((char *)code + modp->old.code_length);
+ erts_fun_purge_prepare(code, end);
+#if !defined(ERTS_NEW_PURGE_STRATEGY)
+ ASSERT(!ERTS_COPY_LITERAL_AREA());
+ ERTS_SET_COPY_LITERAL_AREA(modp->old.code_hdr->literal_area);
+#endif
}
+ if (BIF_ARG_2 == am_prepare_on_load) {
+ erts_rwunlock_old_code(code_ix);
+ } else {
+ erts_runlock_old_code(code_ix);
+ }
+ }
+
+#ifndef ERTS_SMP
+ BIF_RET(res);
+#else
+ if (res != am_true)
+ BIF_RET(res);
+ else {
/*
- * Remove the old code.
+ * We'll be resumed when all schedulers are guaranteed
+ * to see the "pending purge" markers that we've made on
+ * all fun entries of the code that we are about to purge.
+ * Processes trying to call these funs will be suspended
+ * before calling the funs. That is we are guaranteed not
+ * to get any more direct references into the code while
+ * checking for such references...
*/
- ASSERT(erts_total_code_size >= modp->old.code_length);
- erts_total_code_size -= modp->old.code_length;
- code = modp->old.code;
- end = (BeamInstr *)((char *)code + modp->old.code_length);
- erts_cleanup_funs_on_purge(code, end);
- beam_catches_delmod(modp->old.catches, code, modp->old.code_length,
- code_ix);
- decrement_refc(code);
- erts_free(ERTS_ALC_T_CODE, (void *) code);
- modp->old.code = NULL;
- modp->old.code_length = 0;
- modp->old.catches = BEAM_CATCHES_NIL;
- erts_remove_from_ranges(code);
- ERTS_BIF_PREP_RET(ret, am_true);
+ erts_schedule_thr_prgr_later_op(resume_purger,
+ NULL,
+ &purger_lop_data);
+ erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, NULL);
+ ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
}
- erts_rwunlock_old_code(code_ix);
+#endif
}
- if (is_blocking) {
- erts_smp_thr_progress_unblock();
- erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+
+ case am_abort: {
+ /*
+ * Soft purge that detected direct references into the code
+ * we set out to purge. Abort the purge.
+ */
+
+ if (purge_state.module != BIF_ARG_1)
+ BIF_ERROR(BIF_P, BADARG);
+
+ erts_fun_purge_abort_prepare(purge_state.funs, purge_state.fe_ix);
+
+#if !defined(ERTS_NEW_PURGE_STRATEGY)
+ ASSERT(ERTS_COPY_LITERAL_AREA());
+ ERTS_SET_COPY_LITERAL_AREA(NULL);
+#endif
+#ifndef ERTS_SMP
+ erts_fun_purge_abort_finalize(purge_state.funs, purge_state.fe_ix);
+ finalize_purge_operation(BIF_P, 0);
+ BIF_RET(am_false);
+#else
+ /*
+ * We need to restore the code addresses of the funs in
+ * two stages in order to ensure that we do not get any
+ * stale suspended processes due to the purge abort.
+ * Restore address pointer (erts_fun_purge_abort_prepare);
+ * wait for thread progress; clear pending purge address
+ * pointer (erts_fun_purge_abort_finalize), and then
+ * resume processes that got suspended
+ * (finalize_purge_operation).
+ */
+ erts_schedule_thr_prgr_later_op(finalize_purge_abort,
+ NULL,
+ &purger_lop_data);
+ erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, NULL);
+ ERTS_BIF_YIELD_RETURN(BIF_P, am_false);
+#endif
}
- erts_release_code_write_permission();
- return ret;
-}
-static void
-decrement_refc(BeamInstr* code)
-{
- struct erl_off_heap_header* oh =
- (struct erl_off_heap_header *) code[MI_LITERALS_OFF_HEAP];
-
- while (oh) {
- Binary* bptr;
- ASSERT(thing_subtag(oh->thing_word) == REFC_BINARY_SUBTAG);
- bptr = ((ProcBin*)oh)->val;
- if (erts_refc_dectest(&bptr->refc, 0) == 0) {
- erts_bin_free(bptr);
+ case am_complete: {
+ ErtsCodeIndex code_ix;
+ BeamInstr* code;
+ Module* modp;
+ int is_blocking = 0;
+ Eterm ret;
+ ErtsLiteralArea *literals = NULL;
+
+
+ /*
+ * We have no direct references into the code.
+ * Complete to purge.
+ */
+
+ if (purge_state.module != BIF_ARG_1)
+ BIF_ERROR(BIF_P, BADARG);
+
+ if (!erts_try_seize_code_write_permission(BIF_P)) {
+ ERTS_BIF_YIELD2(bif_export[BIF_erts_internal_purge_module_2],
+ BIF_P, BIF_ARG_1, BIF_ARG_2);
+ }
+
+ code_ix = erts_active_code_ix();
+
+ /*
+ * Correct module?
+ */
+
+ if ((modp = erts_get_module(BIF_ARG_1, code_ix)) == NULL) {
+ ERTS_BIF_PREP_RET(ret, am_false);
+ }
+ else {
+
+ erts_rwlock_old_code(code_ix);
+
+ /*
+ * Any code to purge?
+ */
+ if (!modp->old.code_hdr) {
+ ERTS_BIF_PREP_RET(ret, am_false);
+ }
+ else {
+ /*
+ * Unload any NIF library
+ */
+ if (modp->old.nif != NULL) {
+ /* ToDo: Do unload nif without blocking */
+ erts_rwunlock_old_code(code_ix);
+ erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_smp_thr_progress_block();
+ is_blocking = 1;
+ erts_rwlock_old_code(code_ix);
+ erts_unload_nif(modp->old.nif);
+ modp->old.nif = NULL;
+ }
+
+ /*
+ * Remove the old code.
+ */
+ ASSERT(erts_total_code_size >= modp->old.code_length);
+ erts_total_code_size -= modp->old.code_length;
+ code = (BeamInstr*) modp->old.code_hdr;
+ erts_fun_purge_complete(purge_state.funs, purge_state.fe_ix);
+ beam_catches_delmod(modp->old.catches, code, modp->old.code_length,
+ code_ix);
+ literals = modp->old.code_hdr->literal_area;
+ modp->old.code_hdr->literal_area = NULL;
+ erts_free(ERTS_ALC_T_CODE, (void *) code);
+ modp->old.code_hdr = NULL;
+ modp->old.code_length = 0;
+ modp->old.catches = BEAM_CATCHES_NIL;
+ erts_remove_from_ranges(code);
+
+ ERTS_BIF_PREP_RET(ret, am_true);
+ }
+
+ if (purge_state.saved_old.code_hdr) {
+ modp->old = purge_state.saved_old;
+ purge_state.saved_old.code_hdr = 0;
+ }
+ erts_rwunlock_old_code(code_ix);
+ }
+ if (is_blocking) {
+ erts_smp_thr_progress_unblock();
+ erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ }
+
+ erts_release_code_write_permission();
+
+ finalize_purge_operation(BIF_P, ret == am_true);
+
+#if !defined(ERTS_NEW_PURGE_STRATEGY)
+
+ ASSERT(ERTS_COPY_LITERAL_AREA() == literals);
+ ERTS_SET_COPY_LITERAL_AREA(NULL);
+ erts_release_literal_area(literals);
+
+#else /* ERTS_NEW_PURGE_STRATEGY */
+
+ if (literals) {
+ ErtsLiteralAreaRef *ref;
+ ref = erts_alloc(ERTS_ALC_T_LITERAL_REF,
+ sizeof(ErtsLiteralAreaRef));
+ ref->literal_area = literals;
+ ref->next = NULL;
+ erts_smp_mtx_lock(&release_literal_areas.mtx);
+ if (release_literal_areas.last) {
+ release_literal_areas.last->next = ref;
+ release_literal_areas.last = ref;
+ }
+ else {
+ release_literal_areas.first = ref;
+ release_literal_areas.last = ref;
+ }
+ erts_smp_mtx_unlock(&release_literal_areas.mtx);
+ erts_queue_message(erts_literal_area_collector,
+ 0,
+ erts_alloc_message(0, NULL),
+ am_copy_literals,
+ BIF_P->common.id);
}
- oh = oh->next;
+
+#endif /* ERTS_NEW_PURGE_STRATEGY */
+
+ return ret;
+ }
+
+ default:
+ BIF_ERROR(BIF_P, BADARG);
+
}
}
@@ -1062,9 +2057,9 @@ delete_code(Module* modp)
{
ErtsCodeIndex code_ix = erts_staging_code_ix();
Eterm module = make_atom(modp->module);
- int i;
+ int i, num_exps = export_list_size(code_ix);
- for (i = 0; i < export_list_size(code_ix); i++) {
+ for (i = 0; i < num_exps; i++) {
Export *ep = export_list(i, code_ix);
if (ep != NULL && (ep->code[0] == module)) {
if (ep->addressv[code_ix] == ep->code+3) {
@@ -1089,10 +2084,11 @@ delete_code(Module* modp)
ASSERT(modp->curr.num_breakpoints == 0);
ASSERT(modp->curr.num_traced_exports == 0);
modp->old = modp->curr;
- modp->curr.code = NULL;
+ modp->curr.code_hdr = NULL;
modp->curr.code_length = 0;
modp->curr.catches = BEAM_CATCHES_NIL;
modp->curr.nif = NULL;
+
}
@@ -1106,9 +2102,9 @@ beam_make_current_old(Process *c_p, ErtsProcLocks c_p_locks, Eterm module)
* if not, delete old code; error if old code already exists.
*/
- if (modp->curr.code != NULL && modp->old.code != NULL) {
+ if (modp->curr.code_hdr && modp->old.code_hdr) {
return am_not_purged;
- } else if (modp->old.code == NULL) { /* Make the current version old. */
+ } else if (!modp->old.code_hdr) { /* Make the current version old. */
delete_code(modp);
}
return NIL;
diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c
index 9860968687..6d723a4b92 100644
--- a/erts/emulator/beam/beam_bp.c
+++ b/erts/emulator/beam/beam_bp.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2013. 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.
@@ -74,6 +74,9 @@ extern BeamInstr beam_return_time_trace[1]; /* OpCode(i_return_time_trace) */
erts_smp_atomic32_t erts_active_bp_index;
erts_smp_atomic32_t erts_staging_bp_index;
+#ifdef ERTS_DIRTY_SCHEDULERS
+erts_smp_mtx_t erts_dirty_bp_ix_mtx;
+#endif
/*
* Inlined helpers
@@ -82,9 +85,34 @@ erts_smp_atomic32_t erts_staging_bp_index;
static ERTS_INLINE ErtsMonotonicTime
get_mtime(Process *c_p)
{
- return erts_get_monotonic_time(ERTS_PROC_GET_SCHDATA(c_p));
+ return erts_get_monotonic_time(erts_proc_sched_data(c_p));
+}
+
+static ERTS_INLINE Uint32
+acquire_bp_sched_ix(Process *c_p)
+{
+ ErtsSchedulerData *esdp = erts_proc_sched_data(c_p);
+ ASSERT(esdp);
+#ifdef ERTS_DIRTY_SCHEDULERS
+ if (ERTS_SCHEDULER_IS_DIRTY(esdp)) {
+ erts_smp_mtx_lock(&erts_dirty_bp_ix_mtx);
+ return (Uint32) erts_no_schedulers;
+ }
+#endif
+ return (Uint32) esdp->no - 1;
+}
+
+static ERTS_INLINE void
+release_bp_sched_ix(Uint32 ix)
+{
+#ifdef ERTS_DIRTY_SCHEDULERS
+ if (ix == (Uint32) erts_no_schedulers)
+ erts_smp_mtx_unlock(&erts_dirty_bp_ix_mtx);
+#endif
}
+
+
/* *************************************************************************
** Local prototypes
*/
@@ -92,15 +120,15 @@ get_mtime(Process *c_p)
/*
** Helpers
*/
-static Eterm do_call_trace(Process* c_p, BeamInstr* I, Eterm* reg,
- int local, Binary* ms, Eterm tracer_pid);
+static ErtsTracer do_call_trace(Process* c_p, BeamInstr* I, Eterm* reg,
+ int local, Binary* ms, ErtsTracer tracer);
static void set_break(BpFunctions* f, Binary *match_spec, Uint break_flags,
- enum erts_break_op count_op, Eterm tracer_pid);
+ enum erts_break_op count_op, ErtsTracer tracer);
static void set_function_break(BeamInstr *pc,
Binary *match_spec,
Uint break_flags,
enum erts_break_op count_op,
- Eterm tracer_pid);
+ ErtsTracer tracer);
static void clear_break(BpFunctions* f, Uint break_flags);
static int clear_function_break(BeamInstr *pc, Uint break_flags);
@@ -108,7 +136,7 @@ static int clear_function_break(BeamInstr *pc, Uint break_flags);
static BpDataTime* get_time_break(BeamInstr *pc);
static GenericBpData* check_break(BeamInstr *pc, Uint break_flags);
-static void bp_meta_unref(BpMetaPid* bmp);
+static void bp_meta_unref(BpMetaTracer* bmt);
static void bp_count_unref(BpCount* bcp);
static void bp_time_unref(BpDataTime* bdt);
static void consolidate_bp_data(Module* modp, BeamInstr* pc, int local);
@@ -135,6 +163,9 @@ void
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");
+#endif
}
@@ -154,8 +185,8 @@ erts_bp_match_functions(BpFunctions* f, Eterm mfa[3], int specified)
num_modules = 0;
for (current = 0; current < max_modules; current++) {
modp = module_code(current, code_ix);
- if (modp->curr.code) {
- max_funcs += modp->curr.code[MI_NUM_FUNCTIONS];
+ if (modp->curr.code_hdr) {
+ max_funcs += modp->curr.code_hdr->num_functions;
module[num_modules++] = modp;
}
}
@@ -163,9 +194,9 @@ erts_bp_match_functions(BpFunctions* f, Eterm mfa[3], int specified)
f->matching = (BpFunction *) Alloc(max_funcs*sizeof(BpFunction));
i = 0;
for (current = 0; current < num_modules; current++) {
- BeamInstr** code_base = (BeamInstr **) module[current]->curr.code;
+ BeamCodeHeader* code_hdr = module[current]->curr.code_hdr;
BeamInstr* code;
- Uint num_functions = (Uint)(UWord) code_base[MI_NUM_FUNCTIONS];
+ Uint num_functions = (Uint)(UWord) code_hdr->num_functions;
Uint fi;
if (specified > 0) {
@@ -179,7 +210,7 @@ erts_bp_match_functions(BpFunctions* f, Eterm mfa[3], int specified)
BeamInstr* pc;
int wi;
- code = code_base[MI_FUNCTIONS+fi];
+ code = code_hdr->functions[fi];
ASSERT(code[0] == (BeamInstr) BeamOp(op_i_func_info_IaaI));
pc = code+5;
if (erts_is_native_break(pc)) {
@@ -248,7 +279,10 @@ erts_bp_match_export(BpFunctions* f, Eterm mfa[3], int specified)
void
erts_bp_free_matched_functions(BpFunctions* f)
{
- Free(f->matching);
+ if (f->matching) {
+ Free(f->matching);
+ }
+ else ASSERT(f->matched == 0);
}
void
@@ -302,7 +336,7 @@ consolidate_bp_data(Module* modp, BeamInstr* pc, int local)
MatchSetUnref(dst->local_ms);
}
if (flags & ERTS_BPF_META_TRACE) {
- bp_meta_unref(dst->meta_pid);
+ bp_meta_unref(dst->meta_tracer);
MatchSetUnref(dst->meta_ms);
}
if (flags & ERTS_BPF_COUNT) {
@@ -343,18 +377,18 @@ consolidate_bp_data(Module* modp, BeamInstr* pc, int local)
MatchSetRef(dst->local_ms);
}
if (flags & ERTS_BPF_META_TRACE) {
- dst->meta_pid = src->meta_pid;
- erts_refc_inc(&dst->meta_pid->refc, 1);
+ dst->meta_tracer = src->meta_tracer;
+ erts_smp_refc_inc(&dst->meta_tracer->refc, 1);
dst->meta_ms = src->meta_ms;
MatchSetRef(dst->meta_ms);
}
if (flags & ERTS_BPF_COUNT) {
dst->count = src->count;
- erts_refc_inc(&dst->count->refc, 1);
+ erts_smp_refc_inc(&dst->count->refc, 1);
}
if (flags & ERTS_BPF_TIME_TRACE) {
dst->time = src->time;
- erts_refc_inc(&dst->time->refc, 1);
+ erts_smp_refc_inc(&dst->time->refc, 1);
ASSERT(dst->time->hash);
}
}
@@ -436,13 +470,13 @@ uninstall_breakpoint(BeamInstr* pc)
void
erts_set_trace_break(BpFunctions* f, Binary *match_spec)
{
- set_break(f, match_spec, ERTS_BPF_LOCAL_TRACE, 0, am_true);
+ set_break(f, match_spec, ERTS_BPF_LOCAL_TRACE, 0, erts_tracer_true);
}
void
-erts_set_mtrace_break(BpFunctions* f, Binary *match_spec, Eterm tracer_pid)
+erts_set_mtrace_break(BpFunctions* f, Binary *match_spec, ErtsTracer tracer)
{
- set_break(f, match_spec, ERTS_BPF_META_TRACE, 0, tracer_pid);
+ set_break(f, match_spec, ERTS_BPF_META_TRACE, 0, tracer);
}
void
@@ -450,13 +484,13 @@ erts_set_call_trace_bif(BeamInstr *pc, Binary *match_spec, int local)
{
Uint flags = local ? ERTS_BPF_LOCAL_TRACE : ERTS_BPF_GLOBAL_TRACE;
- set_function_break(pc, match_spec, flags, 0, NIL);
+ set_function_break(pc, match_spec, flags, 0, erts_tracer_nil);
}
void
-erts_set_mtrace_bif(BeamInstr *pc, Binary *match_spec, Eterm tracer_pid)
+erts_set_mtrace_bif(BeamInstr *pc, Binary *match_spec, ErtsTracer tracer)
{
- set_function_break(pc, match_spec, ERTS_BPF_META_TRACE, 0, tracer_pid);
+ set_function_break(pc, match_spec, ERTS_BPF_META_TRACE, 0, tracer);
}
void
@@ -464,7 +498,7 @@ erts_set_time_trace_bif(BeamInstr *pc, enum erts_break_op count_op)
{
set_function_break(pc, NULL,
ERTS_BPF_TIME_TRACE|ERTS_BPF_TIME_TRACE_ACTIVE,
- count_op, NIL);
+ count_op, erts_tracer_nil);
}
void
@@ -474,21 +508,21 @@ erts_clear_time_trace_bif(BeamInstr *pc) {
void
erts_set_debug_break(BpFunctions* f) {
- set_break(f, NULL, ERTS_BPF_DEBUG, 0, NIL);
+ set_break(f, NULL, ERTS_BPF_DEBUG, 0, erts_tracer_nil);
}
void
erts_set_count_break(BpFunctions* f, enum erts_break_op count_op)
{
set_break(f, 0, ERTS_BPF_COUNT|ERTS_BPF_COUNT_ACTIVE,
- count_op, NIL);
+ count_op, erts_tracer_nil);
}
void
erts_set_time_break(BpFunctions* f, enum erts_break_op count_op)
{
set_break(f, 0, ERTS_BPF_TIME_TRACE|ERTS_BPF_TIME_TRACE_ACTIVE,
- count_op, NIL);
+ count_op, erts_tracer_nil);
}
void
@@ -549,21 +583,21 @@ erts_clear_all_breaks(BpFunctions* f)
int
erts_clear_module_break(Module *modp) {
- BeamInstr** code_base;
+ BeamCodeHeader* code_hdr;
Uint n;
Uint i;
ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
ASSERT(modp);
- code_base = (BeamInstr **) modp->curr.code;
- if (code_base == NULL) {
+ code_hdr = modp->curr.code_hdr;
+ if (!code_hdr) {
return 0;
}
- n = (Uint)(UWord) code_base[MI_NUM_FUNCTIONS];
+ n = (Uint)(UWord) code_hdr->num_functions;
for (i = 0; i < n; ++i) {
BeamInstr* pc;
- pc = code_base[MI_FUNCTIONS+i] + 5;
+ pc = code_hdr->functions[i] + 5;
if (erts_is_native_break(pc)) {
continue;
}
@@ -575,7 +609,7 @@ erts_clear_module_break(Module *modp) {
for (i = 0; i < n; ++i) {
BeamInstr* pc;
- pc = code_base[MI_FUNCTIONS+i] + 5;
+ pc = code_hdr->functions[i] + 5;
if (erts_is_native_break(pc)) {
continue;
}
@@ -625,19 +659,26 @@ erts_generic_breakpoint(Process* c_p, BeamInstr* I, Eterm* reg)
if (bp_flags & ERTS_BPF_LOCAL_TRACE) {
ASSERT((bp_flags & ERTS_BPF_GLOBAL_TRACE) == 0);
- (void) do_call_trace(c_p, I, reg, 1, bp->local_ms, am_true);
+ (void) do_call_trace(c_p, I, reg, 1, bp->local_ms, erts_tracer_true);
} else if (bp_flags & ERTS_BPF_GLOBAL_TRACE) {
- (void) do_call_trace(c_p, I, reg, 0, bp->local_ms, am_true);
+ (void) do_call_trace(c_p, I, reg, 0, bp->local_ms, erts_tracer_true);
}
if (bp_flags & ERTS_BPF_META_TRACE) {
- Eterm old_pid;
- Eterm new_pid;
-
- old_pid = (Eterm) erts_smp_atomic_read_nob(&bp->meta_pid->pid);
- new_pid = do_call_trace(c_p, I, reg, 1, bp->meta_ms, old_pid);
- if (new_pid != old_pid) {
- erts_smp_atomic_set_nob(&bp->meta_pid->pid, new_pid);
+ ErtsTracer old_tracer, new_tracer;
+
+ old_tracer = erts_smp_atomic_read_nob(&bp->meta_tracer->tracer);
+
+ new_tracer = do_call_trace(c_p, I, reg, 1, bp->meta_ms, old_tracer);
+ if (!ERTS_TRACER_COMPARE(new_tracer, old_tracer)) {
+ if (old_tracer == erts_smp_atomic_cmpxchg_acqb(
+ &bp->meta_tracer->tracer,
+ (erts_aint_t)new_tracer,
+ (erts_aint_t)old_tracer)) {
+ ERTS_TRACER_CLEAR(&old_tracer);
+ } else {
+ ERTS_TRACER_CLEAR(&new_tracer);
+ }
}
}
@@ -645,7 +686,7 @@ erts_generic_breakpoint(Process* c_p, BeamInstr* I, Eterm* reg)
erts_smp_atomic_inc_nob(&bp->count->acount);
}
- if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE && erts_is_tracer_proc_valid(c_p)) {
+ if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE) {
Eterm w;
erts_trace_time_call(c_p, I, bp->time);
w = (BeamInstr) *c_p->cp;
@@ -690,7 +731,7 @@ erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I)
Eterm (*func)(Process*, Eterm*, BeamInstr*);
Export* ep = bif_export[bif_index];
Uint32 flags = 0, flags_meta = 0;
- Eterm meta_tracer_pid = NIL;
+ ErtsTracer meta_tracer = erts_tracer_nil;
int applying = (I == &(ep->code[3])); /* Yup, the apply code for a bif
* is actually in the
* export entry */
@@ -718,23 +759,31 @@ erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I)
IS_TRACED_FL(p, F_TRACE_CALLS)) {
int local = !!(bp_flags & ERTS_BPF_LOCAL_TRACE);
flags = erts_call_trace(p, ep->code, bp->local_ms, args,
- local, &ERTS_TRACER_PROC(p));
+ local, &ERTS_TRACER(p));
}
if (bp_flags & ERTS_BPF_META_TRACE) {
- Eterm tpid1, tpid2;
+ ErtsTracer old_tracer;
- tpid1 = tpid2 =
- (Eterm) erts_smp_atomic_read_nob(&bp->meta_pid->pid);
+ meta_tracer = erts_smp_atomic_read_nob(&bp->meta_tracer->tracer);
+ old_tracer = meta_tracer;
flags_meta = erts_call_trace(p, ep->code, bp->meta_ms, args,
- 0, &tpid2);
- meta_tracer_pid = tpid2;
- if (tpid1 != tpid2) {
- erts_smp_atomic_set_nob(&bp->meta_pid->pid, tpid2);
+ 0, &meta_tracer);
+
+ if (!ERTS_TRACER_COMPARE(old_tracer, meta_tracer)) {
+ ErtsTracer new_tracer = erts_tracer_nil;
+ erts_tracer_update(&new_tracer, meta_tracer);
+ if (old_tracer == erts_smp_atomic_cmpxchg_acqb(
+ &bp->meta_tracer->tracer,
+ (erts_aint_t)new_tracer,
+ (erts_aint_t)old_tracer)) {
+ ERTS_TRACER_CLEAR(&old_tracer);
+ } else {
+ ERTS_TRACER_CLEAR(&new_tracer);
+ }
}
}
if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE &&
- IS_TRACED_FL(p, F_TRACE_CALLS) &&
- erts_is_tracer_proc_valid(p)) {
+ IS_TRACED_FL(p, F_TRACE_CALLS)) {
BeamInstr *pc = (BeamInstr *)ep->code+3;
erts_trace_time_call(p, pc, bp->time);
}
@@ -778,8 +827,6 @@ erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I)
if (reason != TRAP) {
Eterm class;
Eterm value = p->fvalue;
- DeclareTmpHeapNoproc(nocatch,3);
- UseTmpHeapNoproc(3);
/* Expand error value like in handle_error() */
if (reason & EXF_ARGLIST) {
Eterm *tp;
@@ -788,7 +835,8 @@ erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I)
value = tp[1];
}
if ((reason & EXF_THROWN) && (p->catches <= 0)) {
- value = TUPLE2(nocatch, am_nocatch, value);
+ Eterm *hp = HAlloc(p, 3);
+ value = TUPLE2(hp, am_nocatch, value);
reason = EXC_ERROR;
}
/* Note: expand_error_value() could theoretically
@@ -801,11 +849,11 @@ erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I)
if (flags_meta & MATCH_SET_EXCEPTION_TRACE) {
erts_trace_exception(p, ep->code, class, value,
- &meta_tracer_pid);
+ &meta_tracer);
}
if (flags & MATCH_SET_EXCEPTION_TRACE) {
erts_trace_exception(p, ep->code, class, value,
- &ERTS_TRACER_PROC(p));
+ &ERTS_TRACER(p));
}
if ((flags & MATCH_SET_RETURN_TO_TRACE) && p->catches > 0) {
/* can only happen if(local)*/
@@ -827,7 +875,6 @@ erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I)
}
}
}
- UnUseTmpHeapNoproc(3);
if ((flags_meta|flags) & MATCH_SET_EXCEPTION_TRACE) {
erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR);
ERTS_TRACE_FLAGS(p) |= F_EXCEPTION_TRACE;
@@ -836,13 +883,14 @@ erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I)
}
} else {
if (flags_meta & MATCH_SET_RX_TRACE) {
- erts_trace_return(p, ep->code, result, &meta_tracer_pid);
+ erts_trace_return(p, ep->code, result, &meta_tracer);
}
/* MATCH_SET_RETURN_TO_TRACE cannot occur if(meta) */
if (flags & MATCH_SET_RX_TRACE) {
- erts_trace_return(p, ep->code, result, &ERTS_TRACER_PROC(p));
+ erts_trace_return(p, ep->code, result, &ERTS_TRACER(p));
}
- if (flags & MATCH_SET_RETURN_TO_TRACE) {
+ if (flags & MATCH_SET_RETURN_TO_TRACE &&
+ IS_TRACED_FL(p, F_TRACE_RETURN_TO)) {
/* can only happen if(local)*/
if (applying) {
/* Apply of BIF, cp is in calling function */
@@ -857,14 +905,14 @@ erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I)
return result;
}
-static Eterm
+static ErtsTracer
do_call_trace(Process* c_p, BeamInstr* I, Eterm* reg,
- int local, Binary* ms, Eterm tracer_pid)
+ int local, Binary* ms, ErtsTracer tracer)
{
Eterm* cpp;
int return_to_trace = 0;
BeamInstr w;
- BeamInstr *cp_save;
+ BeamInstr *cp_save = c_p->cp;
Uint32 flags;
Uint need = 0;
Eterm* E = c_p->stop;
@@ -899,7 +947,7 @@ do_call_trace(Process* c_p, BeamInstr* I, Eterm* reg,
ASSERT(is_CP(*cpp));
}
ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
- flags = erts_call_trace(c_p, I-3, ms, reg, local, &tracer_pid);
+ flags = erts_call_trace(c_p, I-3, ms, reg, local, &tracer);
ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
if (cpp) {
c_p->cp = cp_save;
@@ -910,7 +958,7 @@ do_call_trace(Process* c_p, BeamInstr* I, Eterm* reg,
need += 1;
}
if (flags & MATCH_SET_RX_TRACE) {
- need += 3;
+ need += 3 + size_object(tracer);
}
if (need) {
ASSERT(c_p->htop <= E && E <= c_p->hend);
@@ -926,14 +974,15 @@ do_call_trace(Process* c_p, BeamInstr* I, Eterm* reg,
E[0] = make_cp(c_p->cp);
c_p->cp = beam_return_to_trace;
}
- if (flags & MATCH_SET_RX_TRACE) {
+ if (flags & MATCH_SET_RX_TRACE)
+ {
E -= 3;
+ c_p->stop = E;
ASSERT(c_p->htop <= E && E <= c_p->hend);
ASSERT(is_CP((Eterm) (UWord) (I - 3)));
- ASSERT(am_true == tracer_pid ||
- is_internal_pid(tracer_pid) || is_internal_port(tracer_pid));
+ ASSERT(IS_TRACER_VALID(tracer));
E[2] = make_cp(c_p->cp);
- E[1] = tracer_pid;
+ E[1] = copy_object(tracer, c_p);
E[0] = make_cp(I - 3); /* We ARE at the beginning of an
instruction,
the funcinfo is above i. */
@@ -942,9 +991,9 @@ do_call_trace(Process* c_p, BeamInstr* I, Eterm* reg,
erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
ERTS_TRACE_FLAGS(c_p) |= F_EXCEPTION_TRACE;
erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
- }
- c_p->stop = E;
- return tracer_pid;
+ } else
+ c_p->stop = E;
+ return tracer;
}
void
@@ -955,13 +1004,15 @@ erts_trace_time_call(Process* c_p, BeamInstr* I, BpDataTime* bdt)
bp_data_time_item_t sitem, *item = NULL;
bp_time_hash_t *h = NULL;
BpDataTime *pbdt = NULL;
+ Uint32 six = acquire_bp_sched_ix(c_p);
ASSERT(c_p);
- ASSERT(erts_smp_atomic32_read_acqb(&c_p->state) & ERTS_PSFLG_RUNNING);
+ ASSERT(erts_smp_atomic32_read_acqb(&c_p->state) & (ERTS_PSFLG_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING));
/* get previous timestamp and breakpoint
* from the process psd */
-
+
pbt = ERTS_PROC_GET_CALL_TIME(c_p);
time = get_mtime(c_p);
@@ -974,7 +1025,7 @@ erts_trace_time_call(Process* c_p, BeamInstr* I, BpDataTime* bdt)
if (pbt == 0) {
/* First call of process to instrumented function */
pbt = Alloc(sizeof(process_breakpoint_time_t));
- (void) ERTS_PROC_SET_CALL_TIME(c_p, ERTS_PROC_LOCK_MAIN, pbt);
+ (void) ERTS_PROC_SET_CALL_TIME(c_p, pbt);
} else {
ASSERT(pbt->pc);
/* add time to previous code */
@@ -987,7 +1038,7 @@ erts_trace_time_call(Process* c_p, BeamInstr* I, BpDataTime* bdt)
/* if null then the breakpoint was removed */
if (pbdt) {
- h = &(pbdt->hash[bp_sched2ix_proc(c_p)]);
+ h = &(pbdt->hash[six]);
ASSERT(h);
ASSERT(h->item);
@@ -1008,7 +1059,7 @@ erts_trace_time_call(Process* c_p, BeamInstr* I, BpDataTime* bdt)
/* this breakpoint */
ASSERT(bdt);
- h = &(bdt->hash[bp_sched2ix_proc(c_p)]);
+ h = &(bdt->hash[six]);
ASSERT(h);
ASSERT(h->item);
@@ -1022,6 +1073,8 @@ erts_trace_time_call(Process* c_p, BeamInstr* I, BpDataTime* bdt)
pbt->pc = I;
pbt->time = time;
+
+ release_bp_sched_ix(six);
}
void
@@ -1032,9 +1085,11 @@ erts_trace_time_return(Process *p, BeamInstr *pc)
bp_data_time_item_t sitem, *item = NULL;
bp_time_hash_t *h = NULL;
BpDataTime *pbdt = NULL;
+ Uint32 six = acquire_bp_sched_ix(p);
ASSERT(p);
- ASSERT(erts_smp_atomic32_read_acqb(&p->state) & ERTS_PSFLG_RUNNING);
+ ASSERT(erts_smp_atomic32_read_acqb(&p->state) & (ERTS_PSFLG_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING));
/* get previous timestamp and breakpoint
* from the process psd */
@@ -1051,6 +1106,7 @@ erts_trace_time_return(Process *p, BeamInstr *pc)
*/
if (pbt) {
+
/* might have been removed due to
* trace_pattern(false)
*/
@@ -1065,7 +1121,8 @@ erts_trace_time_return(Process *p, BeamInstr *pc)
/* beware, the trace_pattern might have been removed */
if (pbdt) {
- h = &(pbdt->hash[bp_sched2ix_proc(p)]);
+
+ h = &(pbdt->hash[six]);
ASSERT(h);
ASSERT(h->item);
@@ -1076,11 +1133,15 @@ erts_trace_time_return(Process *p, BeamInstr *pc)
} else {
BP_TIME_ADD(item, &sitem);
}
+
}
pbt->pc = pc;
pbt->time = time;
+
}
+
+ release_bp_sched_ix(six);
}
int
@@ -1098,9 +1159,9 @@ erts_is_trace_break(BeamInstr *pc, Binary **match_spec_ret, int local)
return 0;
}
-int
+int
erts_is_mtrace_break(BeamInstr *pc, Binary **match_spec_ret,
- Eterm *tracer_pid_ret)
+ ErtsTracer *tracer_ret)
{
GenericBpData* bp = check_break(pc, ERTS_BPF_META_TRACE);
@@ -1108,9 +1169,8 @@ erts_is_mtrace_break(BeamInstr *pc, Binary **match_spec_ret,
if (match_spec_ret) {
*match_spec_ret = bp->meta_ms;
}
- if (tracer_pid_ret) {
- *tracer_pid_ret =
- (Eterm) erts_smp_atomic_read_nob(&bp->meta_pid->pid);
+ if (tracer_ret) {
+ *tracer_ret = erts_smp_atomic_read_nob(&bp->meta_tracer->tracer);
}
return 1;
}
@@ -1205,17 +1265,17 @@ int erts_is_time_break(Process *p, BeamInstr *pc, Eterm *retval) {
BeamInstr *
erts_find_local_func(Eterm mfa[3]) {
Module *modp;
- BeamInstr** code_base;
+ BeamCodeHeader* code_hdr;
BeamInstr* code_ptr;
Uint i,n;
if ((modp = erts_get_module(mfa[0], erts_active_code_ix())) == NULL)
return NULL;
- if ((code_base = (BeamInstr **) modp->curr.code) == NULL)
+ if ((code_hdr = modp->curr.code_hdr) == NULL)
return NULL;
- n = (BeamInstr) code_base[MI_NUM_FUNCTIONS];
+ n = (BeamInstr) code_hdr->num_functions;
for (i = 0; i < n; ++i) {
- code_ptr = code_base[MI_FUNCTIONS+i];
+ code_ptr = code_hdr->functions[i];
ASSERT(((BeamInstr) BeamOp(op_i_func_info_IaaI)) == code_ptr[0]);
ASSERT(mfa[0] == ((Eterm) code_ptr[2]) ||
is_nil((Eterm) code_ptr[2]));
@@ -1334,6 +1394,7 @@ void erts_schedule_time_break(Process *p, Uint schedule) {
bp_data_time_item_t sitem, *item = NULL;
bp_time_hash_t *h = NULL;
BpDataTime *pbdt = NULL;
+ Uint32 six = acquire_bp_sched_ix(p);
ASSERT(p);
@@ -1356,7 +1417,7 @@ void erts_schedule_time_break(Process *p, Uint schedule) {
sitem.pid = p->common.id;
sitem.count = 0;
- h = &(pbdt->hash[bp_sched2ix_proc(p)]);
+ h = &(pbdt->hash[six]);
ASSERT(h);
ASSERT(h->item);
@@ -1382,6 +1443,8 @@ void erts_schedule_time_break(Process *p, Uint schedule) {
break;
}
} /* pbt */
+
+ release_bp_sched_ix(six);
}
/* *************************************************************************
@@ -1391,7 +1454,7 @@ void erts_schedule_time_break(Process *p, Uint schedule) {
static void
set_break(BpFunctions* f, Binary *match_spec, Uint break_flags,
- enum erts_break_op count_op, Eterm tracer_pid)
+ enum erts_break_op count_op, ErtsTracer tracer)
{
Uint i;
Uint n;
@@ -1400,13 +1463,13 @@ set_break(BpFunctions* f, Binary *match_spec, Uint break_flags,
for (i = 0; i < n; i++) {
BeamInstr* pc = f->matching[i].pc;
set_function_break(pc, match_spec, break_flags,
- count_op, tracer_pid);
+ count_op, tracer);
}
}
static void
set_function_break(BeamInstr *pc, Binary *match_spec, Uint break_flags,
- enum erts_break_op count_op, Eterm tracer_pid)
+ enum erts_break_op count_op, ErtsTracer tracer)
{
GenericBp* g;
GenericBpData* bp;
@@ -1417,7 +1480,7 @@ set_function_break(BeamInstr *pc, Binary *match_spec, Uint break_flags,
g = (GenericBp *) pc[-4];
if (g == 0) {
int i;
- if (count_op == erts_break_reset || count_op == erts_break_stop) {
+ if (count_op == ERTS_BREAK_RESTART || count_op == ERTS_BREAK_PAUSE) {
/* Do not insert a new breakpoint */
return;
}
@@ -1439,9 +1502,9 @@ set_function_break(BeamInstr *pc, Binary *match_spec, Uint break_flags,
MatchSetUnref(bp->local_ms);
} else if (common & ERTS_BPF_META_TRACE) {
MatchSetUnref(bp->meta_ms);
- bp_meta_unref(bp->meta_pid);
+ bp_meta_unref(bp->meta_tracer);
} else if (common & ERTS_BPF_COUNT) {
- if (count_op == erts_break_stop) {
+ if (count_op == ERTS_BREAK_PAUSE) {
bp->flags &= ~ERTS_BPF_COUNT_ACTIVE;
} else {
bp->flags |= ERTS_BPF_COUNT_ACTIVE;
@@ -1453,7 +1516,7 @@ set_function_break(BeamInstr *pc, Binary *match_spec, Uint break_flags,
BpDataTime* bdt = bp->time;
Uint i = 0;
- if (count_op == erts_break_stop) {
+ if (count_op == ERTS_BREAK_PAUSE) {
bp->flags &= ~ERTS_BPF_TIME_TRACE_ACTIVE;
} else {
bp->flags |= ERTS_BPF_TIME_TRACE_ACTIVE;
@@ -1474,19 +1537,21 @@ set_function_break(BeamInstr *pc, Binary *match_spec, Uint break_flags,
MatchSetRef(match_spec);
bp->local_ms = match_spec;
} else if (break_flags & ERTS_BPF_META_TRACE) {
- BpMetaPid* bmp;
+ BpMetaTracer* bmt;
+ ErtsTracer meta_tracer = erts_tracer_nil;
MatchSetRef(match_spec);
bp->meta_ms = match_spec;
- bmp = Alloc(sizeof(BpMetaPid));
- erts_refc_init(&bmp->refc, 1);
- erts_smp_atomic_init_nob(&bmp->pid, tracer_pid);
- bp->meta_pid = bmp;
+ bmt = Alloc(sizeof(BpMetaTracer));
+ erts_smp_refc_init(&bmt->refc, 1);
+ erts_tracer_update(&meta_tracer, tracer); /* copy tracer */
+ erts_smp_atomic_init_nob(&bmt->tracer, (erts_aint_t)meta_tracer);
+ bp->meta_tracer = bmt;
} else if (break_flags & ERTS_BPF_COUNT) {
BpCount* bcp;
ASSERT((bp->flags & ERTS_BPF_COUNT) == 0);
bcp = Alloc(sizeof(BpCount));
- erts_refc_init(&bcp->refc, 1);
+ erts_smp_refc_init(&bcp->refc, 1);
erts_smp_atomic_init_nob(&bcp->acount, 0);
bp->count = bcp;
} else if (break_flags & ERTS_BPF_TIME_TRACE) {
@@ -1495,8 +1560,12 @@ set_function_break(BeamInstr *pc, Binary *match_spec, Uint break_flags,
ASSERT((bp->flags & ERTS_BPF_TIME_TRACE) == 0);
bdt = Alloc(sizeof(BpDataTime));
- erts_refc_init(&bdt->refc, 1);
+ erts_smp_refc_init(&bdt->refc, 1);
+#ifdef ERTS_DIRTY_SCHEDULERS
+ bdt->n = erts_no_schedulers + 1;
+#else
bdt->n = erts_no_schedulers;
+#endif
bdt->hash = Alloc(sizeof(bp_time_hash_t)*(bdt->n));
for (i = 0; i < bdt->n; i++) {
bp_hash_init(&(bdt->hash[i]), 32);
@@ -1544,7 +1613,7 @@ clear_function_break(BeamInstr *pc, Uint break_flags)
}
if (common & ERTS_BPF_META_TRACE) {
MatchSetUnref(bp->meta_ms);
- bp_meta_unref(bp->meta_pid);
+ bp_meta_unref(bp->meta_tracer);
}
if (common & ERTS_BPF_COUNT) {
ASSERT((bp->flags & ERTS_BPF_COUNT_ACTIVE) == 0);
@@ -1560,17 +1629,19 @@ clear_function_break(BeamInstr *pc, Uint break_flags)
}
static void
-bp_meta_unref(BpMetaPid* bmp)
+bp_meta_unref(BpMetaTracer* bmt)
{
- if (erts_refc_dectest(&bmp->refc, 0) <= 0) {
- Free(bmp);
+ if (erts_smp_refc_dectest(&bmt->refc, 0) <= 0) {
+ ErtsTracer trc = erts_smp_atomic_read_nob(&bmt->tracer);
+ ERTS_TRACER_CLEAR(&trc);
+ Free(bmt);
}
}
static void
bp_count_unref(BpCount* bcp)
{
- if (erts_refc_dectest(&bcp->refc, 0) <= 0) {
+ if (erts_smp_refc_dectest(&bcp->refc, 0) <= 0) {
Free(bcp);
}
}
@@ -1578,7 +1649,7 @@ bp_count_unref(BpCount* bcp)
static void
bp_time_unref(BpDataTime* bdt)
{
- if (erts_refc_dectest(&bdt->refc, 0) <= 0) {
+ if (erts_smp_refc_dectest(&bdt->refc, 0) <= 0) {
Uint i = 0;
Uint j = 0;
Process *h_p = NULL;
@@ -1598,9 +1669,7 @@ bp_time_unref(BpDataTime* bdt)
h_p = erts_pid2proc(NULL, 0, item->pid,
ERTS_PROC_LOCK_MAIN);
if (h_p) {
- pbt = ERTS_PROC_SET_CALL_TIME(h_p,
- ERTS_PROC_LOCK_MAIN,
- NULL);
+ pbt = ERTS_PROC_SET_CALL_TIME(h_p, NULL);
if (pbt) {
Free(pbt);
}
diff --git a/erts/emulator/beam/beam_bp.h b/erts/emulator/beam/beam_bp.h
index 2b89d6fc71..7372b9b258 100644
--- a/erts/emulator/beam/beam_bp.h
+++ b/erts/emulator/beam/beam_bp.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2013. 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.
@@ -41,7 +41,7 @@ typedef struct {
typedef struct bp_data_time { /* Call time */
Uint n;
bp_time_hash_t *hash;
- erts_refc_t refc;
+ erts_smp_refc_t refc;
} BpDataTime;
typedef struct {
@@ -51,19 +51,19 @@ typedef struct {
typedef struct {
erts_smp_atomic_t acount;
- erts_refc_t refc;
+ erts_smp_refc_t refc;
} BpCount;
typedef struct {
- erts_smp_atomic_t pid;
- erts_refc_t refc;
-} BpMetaPid;
+ erts_smp_atomic_t tracer;
+ erts_smp_refc_t refc;
+} BpMetaTracer;
typedef struct generic_bp_data {
Uint flags;
Binary* local_ms; /* Match spec for local call trace */
Binary* meta_ms; /* Match spec for meta trace */
- BpMetaPid* meta_pid; /* Meta trace pid */
+ BpMetaTracer* meta_tracer; /* Meta tracer */
BpCount* count; /* For call count */
BpDataTime* time; /* For time trace */
} GenericBpData;
@@ -79,17 +79,15 @@ typedef struct generic_bp {
#define ERTS_BP_CALL_TIME_SCHEDULE_OUT (1)
#define ERTS_BP_CALL_TIME_SCHEDULE_EXITING (2)
-#ifdef ERTS_SMP
-#define bp_sched2ix_proc(p) ((p)->scheduler_data->no - 1)
-#else
-#define bp_sched2ix_proc(p) (0)
+#ifdef ERTS_DIRTY_SCHEDULERS
+extern erts_smp_mtx_t erts_dirty_bp_ix_mtx;
#endif
enum erts_break_op{
- erts_break_nop = 0, /* Must be false */
- erts_break_set = !0, /* Must be true */
- erts_break_reset,
- erts_break_stop
+ ERTS_BREAK_NOP = 0, /* Must be false */
+ ERTS_BREAK_SET = !0, /* Must be true */
+ ERTS_BREAK_RESTART,
+ ERTS_BREAK_PAUSE
};
typedef Uint32 ErtsBpIndex;
@@ -132,10 +130,10 @@ void erts_set_call_trace_bif(BeamInstr *pc, Binary *match_spec, int local);
void erts_clear_call_trace_bif(BeamInstr *pc, int local);
void erts_set_mtrace_break(BpFunctions *f, Binary *match_spec,
- Eterm tracer_pid);
+ ErtsTracer tracer);
void erts_clear_mtrace_break(BpFunctions *f);
void erts_set_mtrace_bif(BeamInstr *pc, Binary *match_spec,
- Eterm tracer_pid);
+ ErtsTracer tracer);
void erts_clear_mtrace_bif(BeamInstr *pc);
void erts_set_debug_break(BpFunctions *f);
@@ -150,13 +148,13 @@ void erts_clear_export_break(Module *modp, BeamInstr* pc);
BeamInstr erts_generic_breakpoint(Process* c_p, BeamInstr* I, Eterm* reg);
BeamInstr erts_trace_break(Process *p, BeamInstr *pc, Eterm *args,
- Uint32 *ret_flags, Eterm *tracer_pid);
+ Uint32 *ret_flags, ErtsTracer *tracer);
int erts_is_trace_break(BeamInstr *pc, Binary **match_spec_ret, int local);
int erts_is_mtrace_break(BeamInstr *pc, Binary **match_spec_ret,
- Eterm *tracer_pid_rte);
+ ErtsTracer *tracer_ret);
int erts_is_mtrace_bif(BeamInstr *pc, Binary **match_spec_ret,
- Eterm *tracer_pid_ret);
+ ErtsTracer *tracer_ret);
int erts_is_native_break(BeamInstr *pc);
int erts_is_count_break(BeamInstr *pc, Uint *count_ret);
int erts_is_time_break(Process *p, BeamInstr *pc, Eterm *call_time);
@@ -173,19 +171,7 @@ void erts_clear_time_trace_bif(BeamInstr *pc);
BeamInstr *erts_find_local_func(Eterm mfa[3]);
-ERTS_GLB_INLINE Uint erts_bp_sched2ix(void);
-
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-ERTS_GLB_INLINE Uint erts_bp_sched2ix(void)
-{
-#ifdef ERTS_SMP
- ErtsSchedulerData *esdp;
- esdp = erts_get_scheduler_data();
- return esdp->no - 1;
-#else
- return 0;
-#endif
-}
extern erts_smp_atomic32_t erts_active_bp_index;
extern erts_smp_atomic32_t erts_staging_bp_index;
diff --git a/erts/emulator/beam/beam_catches.c b/erts/emulator/beam/beam_catches.c
index 7a1f4901aa..cd592c7e5e 100644
--- a/erts/emulator/beam/beam_catches.c
+++ b/erts/emulator/beam/beam_catches.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2000-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/beam_catches.h b/erts/emulator/beam/beam_catches.h
index 59ee64d033..8eb2165ac9 100644
--- a/erts/emulator/beam/beam_catches.h
+++ b/erts/emulator/beam/beam_catches.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2000-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c
index 90985e4f53..21d336049f 100644
--- a/erts/emulator/beam/beam_debug.c
+++ b/erts/emulator/beam/beam_debug.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -50,7 +50,7 @@
void dbg_bt(Process* p, Eterm* sp);
void dbg_where(BeamInstr* addr, Eterm x0, Eterm* reg);
-static int print_op(int to, void *to_arg, int op, int size, BeamInstr* addr);
+static int print_op(fmtfn_t to, void *to_arg, int op, int size, BeamInstr* addr);
BIF_RETTYPE
erts_debug_same_2(BIF_ALIST_2)
@@ -73,6 +73,40 @@ erts_debug_flat_size_1(BIF_ALIST_1)
}
}
+BIF_RETTYPE
+erts_debug_size_shared_1(BIF_ALIST_1)
+{
+ Process* p = BIF_P;
+ Eterm term = BIF_ARG_1;
+ Uint size = size_shared(term);
+
+ if (IS_USMALL(0, size)) {
+ BIF_RET(make_small(size));
+ } else {
+ Eterm* hp = HAlloc(p, BIG_UINT_HEAP_SIZE);
+ BIF_RET(uint_to_big(size, hp));
+ }
+}
+
+BIF_RETTYPE
+erts_debug_copy_shared_1(BIF_ALIST_1)
+{
+ Process* p = BIF_P;
+ Eterm term = BIF_ARG_1;
+ Uint size;
+ Eterm* hp;
+ Eterm copy;
+ erts_shcopy_t info;
+ INITIALIZE_SHCOPY(info);
+
+ size = copy_shared_calculate(term, &info);
+ if (size > 0) {
+ hp = HAlloc(p, size);
+ }
+ copy = copy_shared_perform(term, size, &info, &hp, &p->off_heap);
+ DESTROY_SHCOPY(info);
+ BIF_RET(copy);
+}
BIF_RETTYPE
erts_debug_breakpoint_2(BIF_ALIST_2)
@@ -208,7 +242,7 @@ erts_debug_disassemble_1(BIF_ALIST_1)
Eterm bin;
Eterm mfa;
BeamInstr* funcinfo = NULL; /* Initialized to eliminate warning. */
- BeamInstr* code_base;
+ BeamCodeHeader* code_hdr;
BeamInstr* code_ptr = NULL; /* Initialized to eliminate warning. */
BeamInstr instr;
BeamInstr uaddr;
@@ -258,12 +292,12 @@ erts_debug_disassemble_1(BIF_ALIST_1)
*/
code_ptr = ((BeamInstr *) ep->addressv[code_ix]) - 5;
funcinfo = code_ptr+2;
- } else if (modp == NULL || (code_base = modp->curr.code) == NULL) {
+ } else if (modp == NULL || (code_hdr = modp->curr.code_hdr) == NULL) {
BIF_RET(am_undef);
} else {
- n = code_base[MI_NUM_FUNCTIONS];
+ n = code_hdr->num_functions;
for (i = 0; i < n; i++) {
- code_ptr = (BeamInstr *) code_base[MI_FUNCTIONS+i];
+ code_ptr = code_hdr->functions[i];
if (code_ptr[3] == name && code_ptr[4] == arity) {
funcinfo = code_ptr+2;
break;
@@ -343,7 +377,7 @@ dbg_where(BeamInstr* addr, Eterm x0, Eterm* reg)
}
static int
-print_op(int to, void *to_arg, int op, int size, BeamInstr* addr)
+print_op(fmtfn_t to, void *to_arg, int op, int size, BeamInstr* addr)
{
int i;
BeamInstr tag;
@@ -397,7 +431,7 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr)
packed >>= 10;
break;
case '0': /* Tight shift */
- *ap++ = packed & (BEAM_TIGHT_MASK / sizeof(Eterm));
+ *ap++ = packed & BEAM_TIGHT_MASK;
packed >>= BEAM_TIGHT_SHIFT;
break;
case '6': /* Shift 16 steps */
@@ -432,39 +466,33 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr)
while (*sign) {
switch (*sign) {
case 'r': /* x(0) */
- erts_print(to, to_arg, "x(0)");
+ erts_print(to, to_arg, "r(0)");
break;
case 'x': /* x(N) */
- if (reg_index(ap[0]) == 0) {
- erts_print(to, to_arg, "x[0]");
- } else {
- erts_print(to, to_arg, "x(%d)", reg_index(ap[0]));
+ {
+ Uint n = ap[0] / sizeof(Eterm);
+ erts_print(to, to_arg, "x(%d)", n);
+ ap++;
}
- ap++;
break;
case 'y': /* y(N) */
- erts_print(to, to_arg, "y(%d)", reg_index(ap[0]) - CP_SIZE);
- ap++;
+ {
+ Uint n = ap[0] / sizeof(Eterm) - CP_SIZE;
+ erts_print(to, to_arg, "y(%d)", n);
+ ap++;
+ }
break;
case 'n': /* Nil */
erts_print(to, to_arg, "[]");
break;
case 's': /* Any source (tagged constant or register) */
- tag = beam_reg_tag(*ap);
- if (tag == X_REG_DEF) {
- if (reg_index(*ap) == 0) {
- erts_print(to, to_arg, "x[0]");
- } else {
- erts_print(to, to_arg, "x(%d)", reg_index(*ap));
- }
- ap++;
- break;
- } else if (tag == Y_REG_DEF) {
- erts_print(to, to_arg, "y(%d)", reg_index(*ap) - CP_SIZE);
+ tag = loader_tag(*ap);
+ if (tag == LOADER_X_REG) {
+ erts_print(to, to_arg, "x(%d)", loader_x_reg_index(*ap));
ap++;
break;
- } else if (tag == R_REG_DEF) {
- erts_print(to, to_arg, "x(0)");
+ } else if (tag == LOADER_Y_REG) {
+ erts_print(to, to_arg, "y(%d)", loader_y_reg_index(*ap) - CP_SIZE);
ap++;
break;
}
@@ -481,20 +509,12 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr)
ap++;
break;
case 'd': /* Destination (x(0), x(N), y(N)) */
- switch (beam_reg_tag(*ap)) {
- case X_REG_DEF:
- if (reg_index(*ap) == 0) {
- erts_print(to, to_arg, "x[0]");
- } else {
- erts_print(to, to_arg, "x(%d)", reg_index(*ap));
- }
- break;
- case Y_REG_DEF:
- erts_print(to, to_arg, "y(%d)", reg_index(*ap) - CP_SIZE);
- break;
- case R_REG_DEF:
- erts_print(to, to_arg, "x(0)");
- break;
+ if (*ap & 1) {
+ erts_print(to, to_arg, "y(%d)",
+ *ap / sizeof(Eterm) - CP_SIZE);
+ } else {
+ erts_print(to, to_arg, "x(%d)",
+ *ap / sizeof(Eterm));
}
ap++;
break;
@@ -561,7 +581,7 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr)
ap++;
break;
case 'l': /* fr(N) */
- erts_print(to, to_arg, "fr(%d)", reg_index(ap[0]));
+ erts_print(to, to_arg, "fr(%d)", loader_reg_index(ap[0]));
ap++;
break;
default:
@@ -580,7 +600,6 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr)
unpacked = ap;
ap = addr + size;
switch (op) {
- case op_i_select_val_lins_rfI:
case op_i_select_val_lins_xfI:
case op_i_select_val_lins_yfI:
{
@@ -600,7 +619,6 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr)
}
}
break;
- case op_i_select_val_bins_rfI:
case op_i_select_val_bins_xfI:
case op_i_select_val_bins_yfI:
{
@@ -614,7 +632,6 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr)
}
}
break;
- case op_i_select_tuple_arity_rfI:
case op_i_select_tuple_arity_xfI:
case op_i_select_tuple_arity_yfI:
{
@@ -639,7 +656,6 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr)
}
}
break;
- case op_i_jump_on_val_rfII:
case op_i_jump_on_val_xfII:
case op_i_jump_on_val_yfII:
{
@@ -651,7 +667,6 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr)
}
}
break;
- case op_i_jump_on_val_zero_rfI:
case op_i_jump_on_val_zero_xfI:
case op_i_jump_on_val_zero_yfI:
{
@@ -663,7 +678,6 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr)
}
}
break;
- case op_i_put_tuple_rI:
case op_i_put_tuple_xI:
case op_i_put_tuple_yI:
case op_new_map_dII:
@@ -673,20 +687,16 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr)
int n = unpacked[-1];
while (n > 0) {
- if (!is_header(ap[0])) {
+ switch (loader_tag(ap[0])) {
+ case LOADER_X_REG:
+ erts_print(to, to_arg, " x(%d)", loader_x_reg_index(ap[0]));
+ break;
+ case LOADER_Y_REG:
+ erts_print(to, to_arg, " x(%d)", loader_y_reg_index(ap[0]));
+ break;
+ default:
erts_print(to, to_arg, " %T", (Eterm) ap[0]);
- } else {
- switch ((ap[0] >> 2) & 0x03) {
- case R_REG_DEF:
- erts_print(to, to_arg, " x(0)");
- break;
- case X_REG_DEF:
- erts_print(to, to_arg, " x(%d)", ap[0] >> 4);
- break;
- case Y_REG_DEF:
- erts_print(to, to_arg, " y(%d)", ap[0] >> 4);
- break;
- }
+ break;
}
ap++, size++, n--;
}
@@ -699,18 +709,16 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr)
while (n > 0) {
if (n % 3 == 1) {
erts_print(to, to_arg, " %X", ap[0]);
- } else if (!is_header(ap[0])) {
- erts_print(to, to_arg, " %T", (Eterm) ap[0]);
} else {
- switch ((ap[0] >> 2) & 0x03) {
- case R_REG_DEF:
- erts_print(to, to_arg, " x(0)");
+ switch (loader_tag(ap[0])) {
+ case LOADER_X_REG:
+ erts_print(to, to_arg, " x(%d)", loader_x_reg_index(ap[0]));
break;
- case X_REG_DEF:
- erts_print(to, to_arg, " x(%d)", ap[0] >> 4);
+ case LOADER_Y_REG:
+ erts_print(to, to_arg, " y(%d)", loader_y_reg_index(ap[0]));
break;
- case Y_REG_DEF:
- erts_print(to, to_arg, " y(%d)", ap[0] >> 4);
+ default:
+ erts_print(to, to_arg, " %T", (Eterm) ap[0]);
break;
}
}
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 1fe4cc9374..1ad13c32e3 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2014. 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.
@@ -64,18 +64,21 @@
# ifdef ERTS_SMP
# define PROCESS_MAIN_CHK_LOCKS(P) \
do { \
- if ((P)) { \
+ if ((P)) \
erts_proc_lc_chk_only_proc_main((P)); \
- } \
- else \
- erts_lc_check_exact(NULL, 0); \
- ERTS_SMP_LC_ASSERT(!erts_thr_progress_is_blocking()); \
+ ERTS_SMP_LC_ASSERT(!erts_thr_progress_is_blocking()); \
+} while (0)
+# define ERTS_SMP_REQ_PROC_MAIN_LOCK(P) \
+do { \
+ if ((P)) \
+ erts_proc_lc_require_lock((P), ERTS_PROC_LOCK_MAIN, \
+ __FILE__, __LINE__); \
+} while (0)
+# define ERTS_SMP_UNREQ_PROC_MAIN_LOCK(P) \
+do { \
+ if ((P)) \
+ erts_proc_lc_unrequire_lock((P), ERTS_PROC_LOCK_MAIN); \
} while (0)
-# define ERTS_SMP_REQ_PROC_MAIN_LOCK(P) \
- if ((P)) erts_proc_lc_require_lock((P), ERTS_PROC_LOCK_MAIN,\
- __FILE__, __LINE__)
-# define ERTS_SMP_UNREQ_PROC_MAIN_LOCK(P) \
- if ((P)) erts_proc_lc_unrequire_lock((P), ERTS_PROC_LOCK_MAIN)
# else
# define ERTS_SMP_REQ_PROC_MAIN_LOCK(P)
# define ERTS_SMP_UNREQ_PROC_MAIN_LOCK(P)
@@ -99,10 +102,7 @@ do { \
do { \
int i_; \
int Arity_ = PC[-1]; \
- if (Arity_ > 0) { \
- CHECK_TERM(r(0)); \
- } \
- for (i_ = 1; i_ < Arity_; i_++) { \
+ for (i_ = 0; i_ < Arity_; i_++) { \
CHECK_TERM(x(i_)); \
} \
} while (0)
@@ -116,6 +116,9 @@ do { \
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#endif
+#define GET_BIF_MODULE(p) ((Eterm) (((Export *) p)->code[0]))
+#define GET_BIF_FUNCTION(p) ((Eterm) (((Export *) p)->code[1]))
+#define GET_BIF_ARITY(p) ((Eterm) (((Export *) p)->code[2]))
#define GET_BIF_ADDRESS(p) ((BifFunction) (((Export *) p)->code[4]))
#define TermWords(t) (((t) / (sizeof(BeamInstr)/sizeof(Eterm))) + !!((t) % (sizeof(BeamInstr)/sizeof(Eterm))))
@@ -151,25 +154,21 @@ do { \
ASSERT(VALID_INSTR(* (Eterm *)(ip))); \
I = (ip)
-#define FetchArgs(S1, S2) tmp_arg1 = (S1); tmp_arg2 = (S2)
+/*
+ * Register target (X or Y register).
+ */
+#define REG_TARGET(Target) (*(((Target) & 1) ? &yb(Target-1) : &xb(Target)))
/*
* Store a result into a register given a destination descriptor.
*/
-#define StoreResult(Result, DestDesc) \
- do { \
- Eterm stb_reg; \
- stb_reg = (DestDesc); \
- CHECK_TERM(Result); \
- switch (beam_reg_tag(stb_reg)) { \
- case R_REG_DEF: \
- r(0) = (Result); break; \
- case X_REG_DEF: \
- xb(x_reg_offset(stb_reg)) = (Result); break; \
- default: \
- yb(y_reg_offset(stb_reg)) = (Result); break; \
- } \
+#define StoreResult(Result, DestDesc) \
+ do { \
+ Eterm stb_reg; \
+ stb_reg = (DestDesc); \
+ CHECK_TERM(Result); \
+ REG_TARGET(stb_reg) = (Result); \
} while (0)
#define StoreSimpleDest(Src, Dest) Dest = (Src)
@@ -180,22 +179,16 @@ do { \
* be just before the next instruction.
*/
-#define StoreBifResult(Dst, Result) \
- do { \
- BeamInstr* stb_next; \
- Eterm stb_reg; \
- stb_reg = Arg(Dst); \
- I += (Dst) + 2; \
- stb_next = (BeamInstr *) *I; \
- CHECK_TERM(Result); \
- switch (beam_reg_tag(stb_reg)) { \
- case R_REG_DEF: \
- r(0) = (Result); Goto(stb_next); \
- case X_REG_DEF: \
- xb(x_reg_offset(stb_reg)) = (Result); Goto(stb_next); \
- default: \
- yb(y_reg_offset(stb_reg)) = (Result); Goto(stb_next); \
- } \
+#define StoreBifResult(Dst, Result) \
+ do { \
+ BeamInstr* stb_next; \
+ Eterm stb_reg; \
+ stb_reg = Arg(Dst); \
+ I += (Dst) + 2; \
+ stb_next = (BeamInstr *) *I; \
+ CHECK_TERM(Result); \
+ REG_TARGET(stb_reg) = (Result); \
+ Goto(stb_next); \
} while (0)
#define ClauseFail() goto jump_f
@@ -250,6 +243,14 @@ void** beam_ops;
HEAP_TOP(c_p) = HTOP; \
c_p->stop = E
+#define HEAVY_SWAPIN \
+ SWAPIN; \
+ FCALLS = c_p->fcalls
+
+#define HEAVY_SWAPOUT \
+ SWAPOUT; \
+ c_p->fcalls = FCALLS
+
/*
* Use LIGHT_SWAPOUT when the called function
* will call HeapOnlyAlloc() (and never HAlloc()).
@@ -293,7 +294,7 @@ void** beam_ops;
#define Ib(N) (N)
#define x(N) reg[N]
#define y(N) E[N]
-#define r(N) x##N
+#define r(N) x(N)
/*
* Makes sure that there are StackNeed + HeapNeed + 1 words available
@@ -309,12 +310,11 @@ void** beam_ops;
needed = (StackNeed) + 1; \
if (E - HTOP < (needed + (HeapNeed))) { \
SWAPOUT; \
- reg[0] = r(0); \
PROCESS_MAIN_CHK_LOCKS(c_p); \
- FCALLS -= erts_garbage_collect(c_p, needed + (HeapNeed), reg, (M)); \
+ FCALLS -= erts_garbage_collect_nobump(c_p, needed + (HeapNeed), \
+ reg, (M), FCALLS); \
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); \
PROCESS_MAIN_CHK_LOCKS(c_p); \
- r(0) = reg[0]; \
SWAPIN; \
} \
E -= needed; \
@@ -363,12 +363,10 @@ void** beam_ops;
unsigned need = (Nh); \
if ((E - HTOP < need) || (MSO(c_p).overhead + (VNh) >= BIN_VHEAP_SZ(c_p))) {\
SWAPOUT; \
- reg[0] = r(0); \
PROCESS_MAIN_CHK_LOCKS(c_p); \
- FCALLS -= erts_garbage_collect(c_p, need, reg, (Live)); \
+ FCALLS -= erts_garbage_collect_nobump(c_p, need, reg, (Live), FCALLS); \
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); \
PROCESS_MAIN_CHK_LOCKS(c_p); \
- r(0) = reg[0]; \
SWAPIN; \
} \
HEAP_SPACE_VERIFIED(need); \
@@ -386,12 +384,10 @@ void** beam_ops;
unsigned need = (Nh); \
if (E - HTOP < need) { \
SWAPOUT; \
- reg[0] = r(0); \
PROCESS_MAIN_CHK_LOCKS(c_p); \
- FCALLS -= erts_garbage_collect(c_p, need, reg, (Live)); \
+ FCALLS -= erts_garbage_collect_nobump(c_p, need, reg, (Live), FCALLS); \
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); \
PROCESS_MAIN_CHK_LOCKS(c_p); \
- r(0) = reg[0]; \
SWAPIN; \
} \
HEAP_SPACE_VERIFIED(need); \
@@ -408,15 +404,11 @@ void** beam_ops;
unsigned need = (Nh); \
if (E - HTOP < need) { \
SWAPOUT; \
- reg[0] = r(0); \
reg[Live] = Extra; \
PROCESS_MAIN_CHK_LOCKS(c_p); \
- FCALLS -= erts_garbage_collect(c_p, need, reg, (Live)+1); \
+ FCALLS -= erts_garbage_collect_nobump(c_p, need, reg, (Live)+1, FCALLS); \
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); \
PROCESS_MAIN_CHK_LOCKS(c_p); \
- if (Live > 0) { \
- r(0) = reg[0]; \
- } \
Extra = reg[Live]; \
SWAPIN; \
} \
@@ -438,10 +430,9 @@ void** beam_ops;
#define MakeFun(FunP, NumFree) \
do { \
- SWAPOUT; \
- reg[0] = r(0); \
+ HEAVY_SWAPOUT; \
r(0) = new_fun(c_p, reg, (ErlFunEntry *) FunP, NumFree); \
- SWAPIN; \
+ HEAVY_SWAPIN; \
} while (0)
#define PutTuple(Dst, Arity) \
@@ -531,15 +522,19 @@ void** beam_ops;
ASSERT(VALID_INSTR(Dst)); \
Goto(Dst)
-#define GetR(pos, tr) \
- do { \
- tr = Arg(pos); \
- switch (beam_reg_tag(tr)) { \
- case R_REG_DEF: tr = r(0); break; \
- case X_REG_DEF: tr = xb(x_reg_offset(tr)); break; \
- case Y_REG_DEF: ASSERT(y_reg_offset(tr) >= 1); tr = yb(y_reg_offset(tr)); break; \
- } \
- CHECK_TERM(tr); \
+#define GetR(pos, tr) \
+ do { \
+ tr = Arg(pos); \
+ switch (loader_tag(tr)) { \
+ case LOADER_X_REG: \
+ tr = x(loader_x_reg_index(tr)); \
+ break; \
+ case LOADER_Y_REG: \
+ ASSERT(loader_y_reg_index(tr) >= 1); \
+ tr = y(loader_y_reg_index(tr)); \
+ break; \
+ } \
+ CHECK_TERM(tr); \
} while (0)
#define GetArg1(N, Dst) GetR((N), Dst)
@@ -557,24 +552,93 @@ void** beam_ops;
HTOP += 2; \
} while (0)
+#define Swap(R1, R2) \
+ do { \
+ Eterm V = R1; \
+ R1 = R2; \
+ R2 = V; \
+ } while (0)
+
+#define SwapTemp(R1, R2, Tmp) \
+ do { \
+ Eterm V = R1; \
+ R1 = R2; \
+ R2 = Tmp = V; \
+ } while (0)
+
#define Move(Src, Dst, Store) \
do { \
Eterm term = (Src); \
Store(term, Dst); \
} while (0)
-#define Move2(S1, D1, S2, D2) D1 = (S1); D2 = (S2)
+#define Move2Par(S1, D1, S2, D2) \
+ do { \
+ Eterm V1, V2; \
+ V1 = (S1); V2 = (S2); D1 = V1; D2 = V2; \
+ } while (0)
+
+#define MoveShift(Src, SD, D) \
+ do { \
+ Eterm V; \
+ V = Src; D = SD; SD = V; \
+ } while (0)
+
+#define MoveDup(Src, D1, D2) \
+ do { \
+ D1 = D2 = (Src); \
+ } while (0)
+
#define Move3(S1, D1, S2, D2, S3, D3) D1 = (S1); D2 = (S2); D3 = (S3)
-#define MoveGenDest(src, dstp) \
- if ((dstp) == NULL) { r(0) = (src); } else { *(dstp) = src; }
+#define MoveWindow3(S1, S2, S3, D) \
+ do { \
+ Eterm xt0, xt1, xt2; \
+ Eterm *y = &D; \
+ xt0 = S1; \
+ xt1 = S2; \
+ xt2 = S3; \
+ y[0] = xt0; \
+ y[1] = xt1; \
+ y[2] = xt2; \
+ } while (0)
+
+#define MoveWindow4(S1, S2, S3, S4, D) \
+ do { \
+ Eterm xt0, xt1, xt2, xt3; \
+ Eterm *y = &D; \
+ xt0 = S1; \
+ xt1 = S2; \
+ xt2 = S3; \
+ xt3 = S4; \
+ y[0] = xt0; \
+ y[1] = xt1; \
+ y[2] = xt2; \
+ y[3] = xt3; \
+ } while (0)
-#define MoveReturn(Src, Dest) \
- (Dest) = (Src); \
- I = c_p->cp; \
- ASSERT(VALID_INSTR(*c_p->cp)); \
- c_p->cp = 0; \
- CHECK_TERM(r(0)); \
+#define MoveWindow5(S1, S2, S3, S4, S5, D) \
+ do { \
+ Eterm xt0, xt1, xt2, xt3, xt4; \
+ Eterm *y = &D; \
+ xt0 = S1; \
+ xt1 = S2; \
+ xt2 = S3; \
+ xt3 = S4; \
+ xt4 = S5; \
+ y[0] = xt0; \
+ y[1] = xt1; \
+ y[2] = xt2; \
+ y[3] = xt3; \
+ y[4] = xt4; \
+ } while (0)
+
+#define MoveReturn(Src) \
+ x(0) = (Src); \
+ I = c_p->cp; \
+ ASSERT(VALID_INSTR(*c_p->cp)); \
+ c_p->cp = 0; \
+ CHECK_TERM(r(0)); \
Goto(*I)
#define DeallocateReturn(Deallocate) \
@@ -586,26 +650,26 @@ void** beam_ops;
Goto(*I); \
} while (0)
-#define MoveDeallocateReturn(Src, Dest, Deallocate) \
- (Dest) = (Src); \
+#define MoveDeallocateReturn(Src, Deallocate) \
+ x(0) = (Src); \
DeallocateReturn(Deallocate)
-#define MoveCall(Src, Dest, CallDest, Size) \
- (Dest) = (Src); \
+#define MoveCall(Src, CallDest, Size) \
+ x(0) = (Src); \
SET_CP(c_p, I+Size+1); \
- SET_I((BeamInstr *) CallDest); \
+ SET_I((BeamInstr *) CallDest); \
Dispatch();
-#define MoveCallLast(Src, Dest, CallDest, Deallocate) \
- (Dest) = (Src); \
- RESTORE_CP(E); \
- E = ADD_BYTE_OFFSET(E, (Deallocate)); \
- SET_I((BeamInstr *) CallDest); \
+#define MoveCallLast(Src, CallDest, Deallocate) \
+ x(0) = (Src); \
+ RESTORE_CP(E); \
+ E = ADD_BYTE_OFFSET(E, (Deallocate)); \
+ SET_I((BeamInstr *) CallDest); \
Dispatch();
-#define MoveCallOnly(Src, Dest, CallDest) \
- (Dest) = (Src); \
- SET_I((BeamInstr *) CallDest); \
+#define MoveCallOnly(Src, CallDest) \
+ x(0) = (Src); \
+ SET_I((BeamInstr *) CallDest); \
Dispatch();
#define MoveJump(Src) \
@@ -613,60 +677,69 @@ void** beam_ops;
SET_I((BeamInstr *) Arg(0)); \
Goto(*I);
-#define GetList(Src, H, T) do { \
- Eterm* tmp_ptr = list_val(Src); \
- H = CAR(tmp_ptr); \
- T = CDR(tmp_ptr); } while (0)
-
-#define GetTupleElement(Src, Element, Dest) \
- do { \
- tmp_arg1 = (Eterm) COMPRESS_POINTER(((unsigned char *) tuple_val(Src)) + \
- (Element)); \
- (Dest) = (*(Eterm *) EXPAND_POINTER(tmp_arg1)); \
+#define GetList(Src, H, T) \
+ do { \
+ Eterm* tmp_ptr = list_val(Src); \
+ Eterm hd, tl; \
+ hd = CAR(tmp_ptr); \
+ tl = CDR(tmp_ptr); \
+ H = hd; T = tl; \
} while (0)
-#define ExtractNextElement(Dest) \
- tmp_arg1 += sizeof(Eterm); \
- (Dest) = (* (Eterm *) (((unsigned char *) EXPAND_POINTER(tmp_arg1))))
-
-#define ExtractNextElement2(Dest) \
- do { \
- Eterm* ene_dstp = &(Dest); \
- ene_dstp[0] = ((Eterm *) EXPAND_POINTER(tmp_arg1))[1]; \
- ene_dstp[1] = ((Eterm *) EXPAND_POINTER(tmp_arg1))[2]; \
- tmp_arg1 += sizeof(Eterm) + sizeof(Eterm); \
+#define GetTupleElement(Src, Element, Dest) \
+ do { \
+ Eterm* src; \
+ src = ADD_BYTE_OFFSET(tuple_val(Src), (Element)); \
+ (Dest) = *src; \
} while (0)
-#define ExtractNextElement3(Dest) \
- do { \
- Eterm* ene_dstp = &(Dest); \
- ene_dstp[0] = ((Eterm *) EXPAND_POINTER(tmp_arg1))[1]; \
- ene_dstp[1] = ((Eterm *) EXPAND_POINTER(tmp_arg1))[2]; \
- ene_dstp[2] = ((Eterm *) EXPAND_POINTER(tmp_arg1))[3]; \
- tmp_arg1 += 3*sizeof(Eterm); \
+#define GetTupleElement2(Src, Element, Dest) \
+ do { \
+ Eterm* src; \
+ Eterm* dst; \
+ Eterm E1, E2; \
+ src = ADD_BYTE_OFFSET(tuple_val(Src), (Element)); \
+ dst = &(Dest); \
+ E1 = src[0]; \
+ E2 = src[1]; \
+ dst[0] = E1; \
+ dst[1] = E2; \
} while (0)
-#define ExtractNextElement4(Dest) \
- do { \
- Eterm* ene_dstp = &(Dest); \
- ene_dstp[0] = ((Eterm *) EXPAND_POINTER(tmp_arg1))[1]; \
- ene_dstp[1] = ((Eterm *) EXPAND_POINTER(tmp_arg1))[2]; \
- ene_dstp[2] = ((Eterm *) EXPAND_POINTER(tmp_arg1))[3]; \
- ene_dstp[3] = ((Eterm *) EXPAND_POINTER(tmp_arg1))[4]; \
- tmp_arg1 += 4*sizeof(Eterm); \
+#define GetTupleElement2Y(Src, Element, D1, D2) \
+ do { \
+ Eterm* src; \
+ Eterm E1, E2; \
+ src = ADD_BYTE_OFFSET(tuple_val(Src), (Element)); \
+ E1 = src[0]; \
+ E2 = src[1]; \
+ D1 = E1; \
+ D2 = E2; \
} while (0)
-#define ExtractElement(Element, Dest) \
- do { \
- tmp_arg1 += (Element); \
- (Dest) = (* (Eterm *) EXPAND_POINTER(tmp_arg1)); \
+#define GetTupleElement3(Src, Element, Dest) \
+ do { \
+ Eterm* src; \
+ Eterm* dst; \
+ Eterm E1, E2, E3; \
+ src = ADD_BYTE_OFFSET(tuple_val(Src), (Element)); \
+ dst = &(Dest); \
+ E1 = src[0]; \
+ E2 = src[1]; \
+ E3 = src[2]; \
+ dst[0] = E1; \
+ dst[1] = E2; \
+ dst[2] = E3; \
} while (0)
#define EqualImmed(X, Y, Action) if (X != Y) { Action; }
#define NotEqualImmed(X, Y, Action) if (X == Y) { Action; }
#define EqualExact(X, Y, Action) if (!EQ(X,Y)) { Action; }
-#define IsLessThan(X, Y, Action) if (CMP_GE(X, Y)) { Action; }
-#define IsGreaterEqual(X, Y, Action) if (CMP_LT(X, Y)) { Action; }
+#define NotEqualExact(X, Y, Action) if (EQ(X,Y)) { Action; }
+#define Equal(X, Y, Action) CMP_EQ_ACTION(X,Y,Action)
+#define NotEqual(X, Y, Action) CMP_NE_ACTION(X,Y,Action)
+#define IsLessThan(X, Y, Action) CMP_LT_ACTION(X,Y,Action)
+#define IsGreaterEqual(X, Y, Action) CMP_GE_ACTION(X,Y,Action)
#define IsFloat(Src, Fail) if (is_not_float(Src)) { Fail; }
@@ -690,18 +763,26 @@ void** beam_ops;
if (is_not_list(Src)) { Fail; } \
A(Need, Alive)
-#define IsNonemptyListTestHeap(Src, Need, Alive, Fail) \
- if (is_not_list(Src)) { Fail; } \
+#define IsNonemptyListTestHeap(Need, Alive, Fail) \
+ if (is_not_list(x(0))) { Fail; } \
TestHeap(Need, Alive)
+#define IsNonemptyListGetList(Src, H, T, Fail) \
+ if (is_not_list(Src)) { \
+ Fail; \
+ } else { \
+ Eterm* tmp_ptr = list_val(Src); \
+ Eterm hd, tl; \
+ hd = CAR(tmp_ptr); \
+ tl = CDR(tmp_ptr); \
+ H = hd; T = tl; \
+ }
+
#define IsTuple(X, Action) if (is_not_tuple(X)) Action
-#define IsArity(Pointer, Arity, Fail) \
- if (*(Eterm *) \
- EXPAND_POINTER(tmp_arg1 = (Eterm) \
- COMPRESS_POINTER(tuple_val(Pointer))) != (Arity)) \
- { \
- Fail; \
+#define IsArity(Pointer, Arity, Fail) \
+ if (*tuple_val(Pointer) != (Arity)) { \
+ Fail; \
}
#define IsMap(Src, Fail) if (!is_map(Src)) { Fail; }
@@ -738,15 +819,21 @@ void** beam_ops;
} \
} while (0)
-#define IsTupleOfArity(Src, Arity, Fail) \
- do { \
- if (is_not_tuple(Src) || \
- *(Eterm *) \
- EXPAND_POINTER(tmp_arg1 = \
- (Eterm) COMPRESS_POINTER(tuple_val(Src))) != Arity) { \
- Fail; \
- } \
+#ifdef DEBUG
+#define IsTupleOfArity(Src, Arityval, Fail) \
+ do { \
+ if (!(is_tuple(Src) && *tuple_val(Src) == Arityval)) { \
+ Fail; \
+ } \
} while (0)
+#else
+#define IsTupleOfArity(Src, Arityval, Fail) \
+ do { \
+ if (!(is_boxed(Src) && *tuple_val(Src) == Arityval)) { \
+ Fail; \
+ } \
+ } while (0)
+#endif
#define IsBoolean(X, Fail) if ((X) != am_true && (X) != am_false) { Fail; }
@@ -756,7 +843,7 @@ void** beam_ops;
#define IsBitstring(Src, Fail) \
if (is_not_binary(Src)) { Fail; }
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
#define BsSafeMul(A, B, Fail, Target) \
do { Uint64 _res = (A) * (B); \
if (_res / B != A) { Fail; } \
@@ -773,6 +860,7 @@ void** beam_ops;
#define BsGetFieldSize(Bits, Unit, Fail, Target) \
do { \
Sint _signed_size; Uint _uint_size; \
+ Uint temp_bits; \
if (is_small(Bits)) { \
_signed_size = signed_val(Bits); \
if (_signed_size < 0) { Fail; } \
@@ -787,6 +875,7 @@ void** beam_ops;
#define BsGetUncheckedFieldSize(Bits, Unit, Fail, Target) \
do { \
Sint _signed_size; Uint _uint_size; \
+ Uint temp_bits; \
if (is_small(Bits)) { \
_signed_size = signed_val(Bits); \
if (_signed_size < 0) { Fail; } \
@@ -958,9 +1047,11 @@ static BeamInstr* handle_error(Process* c_p, BeamInstr* pc,
Eterm* reg, BifFunction bf) NOINLINE;
static BeamInstr* call_error_handler(Process* p, BeamInstr* ip,
Eterm* reg, Eterm func) NOINLINE;
-static BeamInstr* fixed_apply(Process* p, Eterm* reg, Uint arity) NOINLINE;
+static BeamInstr* fixed_apply(Process* p, Eterm* reg, Uint arity,
+ BeamInstr *I, Uint offs) NOINLINE;
static BeamInstr* apply(Process* p, Eterm module, Eterm function,
- Eterm args, Eterm* reg) NOINLINE;
+ Eterm args, Eterm* reg,
+ BeamInstr *I, Uint offs) NOINLINE;
static BeamInstr* call_fun(Process* p, int arity,
Eterm* reg, Eterm args) NOINLINE;
static BeamInstr* apply_fun(Process* p, Eterm fun,
@@ -998,23 +1089,17 @@ init_emulator(void)
*/
#if defined(__GNUC__) && defined(sparc) && !defined(DEBUG)
-# define REG_x0 asm("%l0")
# define REG_xregs asm("%l1")
# define REG_htop asm("%l2")
# define REG_stop asm("%l3")
# define REG_I asm("%l4")
# define REG_fcalls asm("%l5")
-# define REG_tmp_arg1 asm("%l6")
-# define REG_tmp_arg2 asm("%l7")
#else
-# define REG_x0
# define REG_xregs
# define REG_htop
# define REG_stop
# define REG_I
# define REG_fcalls
-# define REG_tmp_arg1
-# define REG_tmp_arg2
#endif
#ifdef USE_VM_PROBES
@@ -1117,6 +1202,25 @@ init_emulator(void)
#define DTRACE_NIF_RETURN(p, m, f, a) do {} while (0)
#endif /* USE_VM_PROBES */
+#ifdef DEBUG
+#define ERTS_DBG_CHK_REDS(P, FC) \
+ do { \
+ if (ERTS_PROC_GET_SAVED_CALLS_BUF((P))) { \
+ ASSERT(FC <= 0); \
+ ASSERT(erts_proc_sched_data(c_p)->virtual_reds \
+ <= 0 - (FC)); \
+ } \
+ else { \
+ ASSERT(FC <= CONTEXT_REDS); \
+ ASSERT(erts_proc_sched_data(c_p)->virtual_reds \
+ <= CONTEXT_REDS - (FC)); \
+ } \
+} while (0)
+#else
+#define ERTS_DBG_CHK_REDS(P, FC)
+#endif
+
+
/*
* process_main() is called twice:
* The first call performs some initialisation, including exporting
@@ -1132,11 +1236,6 @@ void process_main(void)
ERTS_DECLARE_DUMMY(Eterm pid);
#endif
- /*
- * X register zero; also called r(0)
- */
- register Eterm x0 REG_x0 = NIL;
-
/* Pointer to X registers: x(1)..x(N); reg[0] is used when doing GC,
* in all other cases x0 is used.
*/
@@ -1164,17 +1263,6 @@ void process_main(void)
register Sint FCALLS REG_fcalls = 0;
/*
- * Temporaries used for picking up arguments for instructions.
- */
- register Eterm tmp_arg1 REG_tmp_arg1 = NIL;
- register Eterm tmp_arg2 REG_tmp_arg2 = NIL;
-#if HEAP_ON_C_STACK
- Eterm tmp_big[2]; /* Temporary buffer for small bignums if HEAP_ON_C_STACK. */
-#else
- Eterm *tmp_big; /* Temporary buffer for small bignums if !HEAP_ON_C_STACK. */
-#endif
-
- /*
* X registers and floating point registers are located in
* scheduler specific data.
*/
@@ -1185,8 +1273,6 @@ void process_main(void)
*/
int neg_o_reds = 0;
- Eterm (*arith_func)(Process* p, Eterm* reg, Uint live);
-
#ifdef ERTS_OPCODE_COUNTER_SUPPORT
static void* counting_opcodes[] = { DEFINE_COUNTING_OPCODES };
#else
@@ -1197,13 +1283,13 @@ void process_main(void)
#endif
#endif
- Uint temp_bits; /* Temporary used by BsSkipBits2 & BsGetInteger2 */
-
Eterm pt_arity; /* Used by do_put_tuple */
Uint64 start_time = 0; /* Monitor long schedule */
BeamInstr* start_time_i = NULL;
+ ERTS_MSACC_DECLARE_CACHE_X() /* a cached value of the tsd pointer for msacc */
+
ERL_BITS_DECLARE_STATEP; /* Has to be last declaration */
@@ -1229,16 +1315,17 @@ void process_main(void)
goto do_schedule1;
do_schedule:
- reds_used = REDS_IN(c_p) - FCALLS;
+ ASSERT(c_p->debug_reds_in == REDS_IN(c_p));
+ if (!ERTS_PROC_GET_SAVED_CALLS_BUF(c_p))
+ reds_used = REDS_IN(c_p) - FCALLS;
+ else
+ reds_used = REDS_IN(c_p) - (CONTEXT_REDS + FCALLS);
+ ASSERT(reds_used >= 0);
do_schedule1:
if (start_time != 0) {
Sint64 diff = erts_timestamp_millis() - start_time;
- if (diff > 0 && (Uint) diff > erts_system_monitor_long_schedule
-#ifdef ERTS_DIRTY_SCHEDULERS
- && !ERTS_SCHEDULER_IS_DIRTY(c_p->scheduler_data)
-#endif
- ) {
+ if (diff > 0 && (Uint) diff > erts_system_monitor_long_schedule) {
BeamInstr *inptr = find_function_from_pc(start_time_i);
BeamInstr *outptr = find_function_from_pc(c_p->i);
monitor_long_schedule_proc(c_p,inptr,outptr,(Uint) diff);
@@ -1247,11 +1334,9 @@ void process_main(void)
PROCESS_MAIN_CHK_LOCKS(c_p);
ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
-#if HALFWORD_HEAP
- ASSERT(erts_get_scheduler_data()->num_tmp_heap_used == 0);
-#endif
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
- c_p = schedule(c_p, reds_used);
+ c_p = erts_schedule(NULL, c_p, reds_used);
+ ASSERT(!(c_p->flags & F_HIPE_MODE));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
start_time = 0;
#ifdef DEBUG
@@ -1260,16 +1345,15 @@ void process_main(void)
ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
+ ERTS_MSACC_UPDATE_CACHE_X();
+
if (erts_system_monitor_long_schedule != 0) {
start_time = erts_timestamp_millis();
start_time_i = c_p->i;
}
- reg = ERTS_PROC_GET_SCHDATA(c_p)->x_reg_array;
- freg = ERTS_PROC_GET_SCHDATA(c_p)->f_reg_array;
-#if !HEAP_ON_C_STACK
- tmp_big = ERTS_PROC_GET_SCHDATA(c_p)->beam_emu_tmp_heap;
-#endif
+ reg = erts_proc_sched_data(c_p)->x_reg_array;
+ freg = erts_proc_sched_data(c_p)->f_reg_array;
ERL_BITS_RELOAD_STATEP(c_p);
{
int reds;
@@ -1278,7 +1362,7 @@ void process_main(void)
int i;
argp = c_p->arg_reg;
- for (i = c_p->arity - 1; i > 0; i--) {
+ for (i = c_p->arity - 1; i >= 0; i--) {
reg[i] = argp[i];
CHECK_TERM(reg[i]);
}
@@ -1291,23 +1375,22 @@ void process_main(void)
SET_I(c_p->i);
- reds = c_p->fcalls;
- if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p)
- && (ERTS_TRACE_FLAGS(c_p) & F_SENSITIVE) == 0) {
- neg_o_reds = -reds;
- FCALLS = REDS_IN(c_p) = 0;
+ REDS_IN(c_p) = reds = c_p->fcalls;
+#ifdef DEBUG
+ c_p->debug_reds_in = reds;
+#endif
+
+ if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p)) {
+ neg_o_reds = -CONTEXT_REDS;
+ FCALLS = neg_o_reds + reds;
} else {
neg_o_reds = 0;
- FCALLS = REDS_IN(c_p) = reds;
+ FCALLS = reds;
}
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
+
next = (BeamInstr *) *I;
- r(0) = c_p->arg_reg[0];
-#ifdef HARDDEBUG
- if (c_p->arity > 0) {
- CHECK_TERM(r(0));
- }
-#endif
SWAPIN;
ASSERT(VALID_INSTR(next));
@@ -1346,26 +1429,24 @@ void process_main(void)
#endif
#include "beam_hot.h"
-#define STORE_ARITH_RESULT(res) StoreBifResult(2, (res));
-#define ARITH_FUNC(name) erts_gc_##name
-
{
Eterm increment_reg_val;
Eterm increment_val;
Uint live;
Eterm result;
- OpCase(i_increment_yIId):
- increment_reg_val = yb(Arg(0));
+ OpCase(i_increment_rIId):
+ increment_reg_val = x(0);
+ I--;
goto do_increment;
OpCase(i_increment_xIId):
increment_reg_val = xb(Arg(0));
goto do_increment;
- OpCase(i_increment_rIId):
- increment_reg_val = r(0);
- I--;
+ OpCase(i_increment_yIId):
+ increment_reg_val = yb(Arg(0));
+ goto do_increment;
do_increment:
increment_val = Arg(1);
@@ -1374,229 +1455,133 @@ void process_main(void)
ASSERT(MY_IS_SSMALL(i) == IS_SSMALL(i));
if (MY_IS_SSMALL(i)) {
result = make_small(i);
- store_result:
StoreBifResult(3, result);
}
}
live = Arg(2);
- SWAPOUT;
- reg[0] = r(0);
+ HEAVY_SWAPOUT;
reg[live] = increment_reg_val;
reg[live+1] = make_small(increment_val);
result = erts_gc_mixed_plus(c_p, reg, live);
- r(0) = reg[0];
- SWAPIN;
+ HEAVY_SWAPIN;
ERTS_HOLE_CHECK(c_p);
if (is_value(result)) {
- goto store_result;
+ StoreBifResult(3, result);
}
ASSERT(c_p->freason != BADMATCH || is_value(c_p->fvalue));
goto find_func_info;
}
-#define DO_BIG_ARITH(Func,Arg1,Arg2) \
- do { \
- Uint live = Arg(1); \
- SWAPOUT; \
- reg[0] = r(0); \
- reg[live] = (Arg1); \
- reg[live+1] = (Arg2); \
- result = (Func)(c_p, reg, live); \
- r(0) = reg[0]; \
- SWAPIN; \
- ERTS_HOLE_CHECK(c_p); \
- if (is_value(result)) { \
- StoreBifResult(4,result); \
- } \
- goto lb_Cl_error; \
- } while(0)
+#define DO_OUTLINED_ARITH_2(name, Op1, Op2) \
+ do { \
+ Eterm result; \
+ Uint live = Arg(1); \
+ \
+ HEAVY_SWAPOUT; \
+ reg[live] = Op1; \
+ reg[live+1] = Op2; \
+ result = erts_gc_##name(c_p, reg, live); \
+ HEAVY_SWAPIN; \
+ ERTS_HOLE_CHECK(c_p); \
+ if (is_value(result)) { \
+ StoreBifResult(4, result); \
+ } \
+ goto lb_Cl_error; \
+ } while (0)
- OpCase(i_plus_jIxxd):
{
+ Eterm PlusOp1, PlusOp2;
Eterm result;
- if (is_both_small(xb(Arg(2)), xb(Arg(3)))) {
- Sint i = signed_val(xb(Arg(2))) + signed_val(xb(Arg(3)));
+ OpCase(i_plus_jIxxd):
+ PlusOp1 = xb(Arg(2));
+ PlusOp2 = xb(Arg(3));
+ goto do_plus;
+
+ OpCase(i_plus_jIxyd):
+ PlusOp1 = xb(Arg(2));
+ PlusOp2 = yb(Arg(3));
+ goto do_plus;
+
+ OpCase(i_plus_jIssd):
+ GetArg2(2, PlusOp1, PlusOp2);
+ goto do_plus;
+
+ do_plus:
+ if (is_both_small(PlusOp1, PlusOp2)) {
+ Sint i = signed_val(PlusOp1) + signed_val(PlusOp2);
ASSERT(MY_IS_SSMALL(i) == IS_SSMALL(i));
if (MY_IS_SSMALL(i)) {
result = make_small(i);
StoreBifResult(4, result);
}
}
- DO_BIG_ARITH(ARITH_FUNC(mixed_plus), xb(Arg(2)), xb(Arg(3)));
+ DO_OUTLINED_ARITH_2(mixed_plus, PlusOp1, PlusOp2);
}
- OpCase(i_plus_jId):
{
+ Eterm MinusOp1, MinusOp2;
Eterm result;
- if (is_both_small(tmp_arg1, tmp_arg2)) {
- Sint i = signed_val(tmp_arg1) + signed_val(tmp_arg2);
- ASSERT(MY_IS_SSMALL(i) == IS_SSMALL(i));
- if (MY_IS_SSMALL(i)) {
- result = make_small(i);
- STORE_ARITH_RESULT(result);
- }
- }
- arith_func = ARITH_FUNC(mixed_plus);
- goto do_big_arith2;
- }
-
OpCase(i_minus_jIxxd):
- {
- Eterm result;
-
- if (is_both_small(xb(Arg(2)), xb(Arg(3)))) {
- Sint i = signed_val(xb(Arg(2))) - signed_val(xb(Arg(3)));
- ASSERT(MY_IS_SSMALL(i) == IS_SSMALL(i));
- if (MY_IS_SSMALL(i)) {
- result = make_small(i);
- StoreBifResult(4, result);
- }
- }
- DO_BIG_ARITH(ARITH_FUNC(mixed_minus), xb(Arg(2)), xb(Arg(3)));
- }
+ MinusOp1 = xb(Arg(2));
+ MinusOp2 = xb(Arg(3));
+ goto do_minus;
- OpCase(i_minus_jId):
- {
- Eterm result;
+ OpCase(i_minus_jIssd):
+ GetArg2(2, MinusOp1, MinusOp2);
+ goto do_minus;
- if (is_both_small(tmp_arg1, tmp_arg2)) {
- Sint i = signed_val(tmp_arg1) - signed_val(tmp_arg2);
+ do_minus:
+ if (is_both_small(MinusOp1, MinusOp2)) {
+ Sint i = signed_val(MinusOp1) - signed_val(MinusOp2);
ASSERT(MY_IS_SSMALL(i) == IS_SSMALL(i));
if (MY_IS_SSMALL(i)) {
result = make_small(i);
- STORE_ARITH_RESULT(result);
+ StoreBifResult(4, result);
}
}
- arith_func = ARITH_FUNC(mixed_minus);
- goto do_big_arith2;
+ DO_OUTLINED_ARITH_2(mixed_minus, MinusOp1, MinusOp2);
}
- OpCase(i_is_lt_f):
- if (CMP_GE(tmp_arg1, tmp_arg2)) {
- ClauseFail();
- }
- Next(1);
-
- OpCase(i_is_ge_f):
- if (CMP_LT(tmp_arg1, tmp_arg2)) {
- ClauseFail();
- }
- Next(1);
-
- OpCase(i_is_eq_f):
- if (CMP_NE(tmp_arg1, tmp_arg2)) {
- ClauseFail();
- }
- Next(1);
-
- OpCase(i_is_ne_f):
- if (CMP_EQ(tmp_arg1, tmp_arg2)) {
- ClauseFail();
- }
- Next(1);
-
- OpCase(i_is_eq_exact_f):
- if (!EQ(tmp_arg1, tmp_arg2)) {
- ClauseFail();
- }
- Next(1);
-
{
Eterm is_eq_exact_lit_val;
- OpCase(i_is_eq_exact_literal_xfc):
- is_eq_exact_lit_val = xb(Arg(0));
- I++;
+ OpCase(i_is_eq_exact_literal_fxc):
+ is_eq_exact_lit_val = xb(Arg(1));
goto do_is_eq_exact_literal;
- OpCase(i_is_eq_exact_literal_yfc):
- is_eq_exact_lit_val = yb(Arg(0));
- I++;
+ OpCase(i_is_eq_exact_literal_fyc):
+ is_eq_exact_lit_val = yb(Arg(1));
goto do_is_eq_exact_literal;
- OpCase(i_is_eq_exact_literal_rfc):
- is_eq_exact_lit_val = r(0);
-
do_is_eq_exact_literal:
- if (!eq(Arg(1), is_eq_exact_lit_val)) {
+ if (!eq(Arg(2), is_eq_exact_lit_val)) {
ClauseFail();
}
- Next(2);
+ Next(3);
}
{
Eterm is_ne_exact_lit_val;
- OpCase(i_is_ne_exact_literal_xfc):
- is_ne_exact_lit_val = xb(Arg(0));
- I++;
+ OpCase(i_is_ne_exact_literal_fxc):
+ is_ne_exact_lit_val = xb(Arg(1));
goto do_is_ne_exact_literal;
- OpCase(i_is_ne_exact_literal_yfc):
- is_ne_exact_lit_val = yb(Arg(0));
- I++;
+ OpCase(i_is_ne_exact_literal_fyc):
+ is_ne_exact_lit_val = yb(Arg(1));
goto do_is_ne_exact_literal;
- OpCase(i_is_ne_exact_literal_rfc):
- is_ne_exact_lit_val = r(0);
-
do_is_ne_exact_literal:
- if (eq(Arg(1), is_ne_exact_lit_val)) {
+ if (eq(Arg(2), is_ne_exact_lit_val)) {
ClauseFail();
}
- Next(2);
+ Next(3);
}
- OpCase(move_window3_xxxy): {
- BeamInstr *next;
- Eterm xt0, xt1, xt2;
- Eterm *y = (Eterm *)(((unsigned char *)E) + (Arg(3)));
- PreFetch(4, next);
- xt0 = xb(Arg(0));
- xt1 = xb(Arg(1));
- xt2 = xb(Arg(2));
- y[0] = xt0;
- y[1] = xt1;
- y[2] = xt2;
- NextPF(4, next);
- }
- OpCase(move_window4_xxxxy): {
- BeamInstr *next;
- Eterm xt0, xt1, xt2, xt3;
- Eterm *y = (Eterm *)(((unsigned char *)E) + (Arg(4)));
- PreFetch(5, next);
- xt0 = xb(Arg(0));
- xt1 = xb(Arg(1));
- xt2 = xb(Arg(2));
- xt3 = xb(Arg(3));
- y[0] = xt0;
- y[1] = xt1;
- y[2] = xt2;
- y[3] = xt3;
- NextPF(5, next);
- }
- OpCase(move_window5_xxxxxy): {
- BeamInstr *next;
- Eterm xt0, xt1, xt2, xt3, xt4;
- Eterm *y = (Eterm *)(((unsigned char *)E) + (Arg(5)));
- PreFetch(6, next);
- xt0 = xb(Arg(0));
- xt1 = xb(Arg(1));
- xt2 = xb(Arg(2));
- xt3 = xb(Arg(3));
- xt4 = xb(Arg(4));
- y[0] = xt0;
- y[1] = xt1;
- y[2] = xt2;
- y[3] = xt3;
- y[4] = xt4;
- NextPF(6, next);
- }
-
- OpCase(i_move_call_only_fcr): {
+ OpCase(i_move_call_only_fc): {
r(0) = Arg(1);
}
/* FALL THROUGH */
@@ -1606,7 +1591,7 @@ void process_main(void)
Dispatch();
}
- OpCase(i_move_call_last_fPcr): {
+ OpCase(i_move_call_last_fPc): {
r(0) = Arg(2);
}
/* FALL THROUGH */
@@ -1618,7 +1603,7 @@ void process_main(void)
Dispatch();
}
- OpCase(i_move_call_crf): {
+ OpCase(i_move_call_cf): {
r(0) = Arg(0);
I++;
}
@@ -1630,7 +1615,7 @@ void process_main(void)
Dispatch();
}
- OpCase(i_move_call_ext_last_ePcr): {
+ OpCase(i_move_call_ext_last_ePc): {
r(0) = Arg(2);
}
/* FALL THROUGH */
@@ -1646,7 +1631,7 @@ void process_main(void)
DTRACE_GLOBAL_CALL_FROM_EXPORT(c_p, Arg(0));
Dispatchx();
- OpCase(i_move_call_ext_cre): {
+ OpCase(i_move_call_ext_ce): {
r(0) = Arg(0);
I++;
}
@@ -1656,7 +1641,7 @@ void process_main(void)
DTRACE_GLOBAL_CALL_FROM_EXPORT(c_p, Arg(0));
Dispatchx();
- OpCase(i_move_call_ext_only_ecr): {
+ OpCase(i_move_call_ext_only_ec): {
r(0) = Arg(1);
}
/* FALL THROUGH */
@@ -1720,19 +1705,21 @@ void process_main(void)
BeamInstr *next;
Eterm result;
+ if (!(FCALLS > 0 || FCALLS > neg_o_reds)) {
+ /* If we have run out of reductions, we do a context
+ switch before calling the bif */
+ c_p->arity = 2;
+ c_p->current = NULL;
+ goto context_switch3;
+ }
+
PRE_BIF_SWAPOUT(c_p);
c_p->fcalls = FCALLS - 1;
- reg[0] = r(0);
result = erl_send(c_p, r(0), x(1));
PreFetch(0, next);
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
- if (c_p->mbuf || MSO(c_p).overhead >= BIN_VHEAP_SZ(c_p)) {
- result = erts_gc_after_bif_call(c_p, result, reg, 2);
- r(0) = reg[0];
- E = c_p->stop;
- }
HTOP = HEAP_TOP(c_p);
FCALLS = c_p->fcalls;
if (is_value(result)) {
@@ -1743,7 +1730,6 @@ void process_main(void)
SET_CP(c_p, I+1);
SET_I(c_p->i);
SWAPIN;
- r(0) = reg[0];
Dispatch();
}
goto find_func_info;
@@ -1753,29 +1739,23 @@ void process_main(void)
Eterm element_index;
Eterm element_tuple;
- OpCase(i_element_xjsd):
- element_tuple = xb(Arg(0));
- I++;
+ OpCase(i_element_jxsd):
+ element_tuple = xb(Arg(1));
goto do_element;
- OpCase(i_element_yjsd):
- element_tuple = yb(Arg(0));
- I++;
+ OpCase(i_element_jysd):
+ element_tuple = yb(Arg(1));
goto do_element;
- OpCase(i_element_rjsd):
- element_tuple = r(0);
- /* Fall through */
-
do_element:
- GetArg1(1, element_index);
+ GetArg1(2, element_index);
if (is_small(element_index) && is_tuple(element_tuple)) {
Eterm* tp = tuple_val(element_tuple);
if ((signed_val(element_index) >= 1) &&
(signed_val(element_index) <= arityval(*tp))) {
Eterm result = tp[signed_val(element_index)];
- StoreBifResult(2, result);
+ StoreBifResult(3, result);
}
}
}
@@ -1789,29 +1769,24 @@ void process_main(void)
{
Eterm fast_element_tuple;
- OpCase(i_fast_element_rjId):
- fast_element_tuple = r(0);
+ OpCase(i_fast_element_jxId):
+ fast_element_tuple = xb(Arg(1));
+ goto do_fast_element;
+
+ OpCase(i_fast_element_jyId):
+ fast_element_tuple = yb(Arg(1));
+ goto do_fast_element;
do_fast_element:
if (is_tuple(fast_element_tuple)) {
Eterm* tp = tuple_val(fast_element_tuple);
- Eterm pos = Arg(1); /* Untagged integer >= 1 */
+ Eterm pos = Arg(2); /* Untagged integer >= 1 */
if (pos <= arityval(*tp)) {
Eterm result = tp[pos];
- StoreBifResult(2, result);
+ StoreBifResult(3, result);
}
}
goto badarg;
-
- OpCase(i_fast_element_xjId):
- fast_element_tuple = xb(Arg(0));
- I++;
- goto do_fast_element;
-
- OpCase(i_fast_element_yjId):
- fast_element_tuple = yb(Arg(0));
- I++;
- goto do_fast_element;
}
OpCase(catch_yf):
@@ -1832,11 +1807,10 @@ void process_main(void)
SWAPIN;
}
/* only x(2) is included in the rootset here */
- if (E - HTOP < 3 || c_p->mbuf) { /* Force GC in case add_stacktrace()
- * created heap fragments */
+ if (E - HTOP < 3) {
SWAPOUT;
PROCESS_MAIN_CHK_LOCKS(c_p);
- FCALLS -= erts_garbage_collect(c_p, 3, reg+2, 1);
+ FCALLS -= erts_garbage_collect_nobump(c_p, 3, reg+2, 1, FCALLS);
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
SWAPIN;
@@ -1917,10 +1891,18 @@ void process_main(void)
* Pick up the next message and place it in x(0).
* If no message, jump to a wait or wait_timeout instruction.
*/
- OpCase(i_loop_rec_fr):
+ OpCase(i_loop_rec_f):
{
BeamInstr *next;
- ErlMessage* msgp;
+ ErtsMessage* msgp;
+
+ /*
+ * We need to disable GC while matching messages
+ * in the queue. This since messages with data outside
+ * the heap will be corrupted by a GC.
+ */
+ ASSERT(!(c_p->flags & F_DELAY_GC));
+ c_p->flags |= F_DELAY_GC;
loop_rec__:
@@ -1935,6 +1917,7 @@ void process_main(void)
if (ERTS_PROC_PENDING_EXIT(c_p)) {
erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
SWAPOUT;
+ c_p->flags &= ~F_DELAY_GC;
goto do_schedule; /* Will be rescheduled for exit */
}
ERTS_SMP_MSGQ_MV_INQ2PRIVQ(c_p);
@@ -1944,32 +1927,27 @@ void process_main(void)
else
#endif
{
+ c_p->flags &= ~F_DELAY_GC;
SET_I((BeamInstr *) Arg(0));
Goto(*I); /* Jump to a wait or wait_timeout instruction */
}
}
- ErtsMoveMsgAttachmentIntoProc(msgp, c_p, E, HTOP, FCALLS,
- {
- SWAPOUT;
- reg[0] = r(0);
- PROCESS_MAIN_CHK_LOCKS(c_p);
- },
- {
- ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
- PROCESS_MAIN_CHK_LOCKS(c_p);
- r(0) = reg[0];
- SWAPIN;
- });
if (is_non_value(ERL_MESSAGE_TERM(msgp))) {
- /*
- * A corrupt distribution message that we weren't able to decode;
- * remove it...
- */
- ASSERT(!msgp->data.attached);
- /* TODO: Add DTrace probe for this bad message situation? */
- UNLINK_MESSAGE(c_p, msgp);
- free_message(msgp);
- goto loop_rec__;
+ SWAPOUT; /* erts_decode_dist_message() may write to heap... */
+ if (!erts_decode_dist_message(c_p, ERTS_PROC_LOCK_MAIN, msgp, 0)) {
+ /*
+ * A corrupt distribution message that we weren't able to decode;
+ * remove it...
+ */
+ /* No swapin should be needed */
+ ASSERT(HTOP == c_p->htop && E == c_p->stop);
+ /* TODO: Add DTrace probe for this bad message situation? */
+ UNLINK_MESSAGE(c_p, msgp);
+ msgp->next = NULL;
+ erts_cleanup_messages(msgp);
+ goto loop_rec__;
+ }
+ SWAPIN;
}
PreFetch(1, next);
r(0) = ERL_MESSAGE_TERM(msgp);
@@ -1981,10 +1959,11 @@ void process_main(void)
*/
OpCase(remove_message): {
BeamInstr *next;
- ErlMessage* msgp;
-
+ ErtsMessage* msgp;
PROCESS_MAIN_CHK_LOCKS(c_p);
+ ERTS_CHK_MBUF_SZ(c_p);
+
PreFetch(0, next);
msgp = PEEK_MESSAGE(c_p);
@@ -1996,20 +1975,7 @@ void process_main(void)
if (DT_UTAG(c_p) != NIL) {
if (DT_UTAG_FLAGS(c_p) & DT_UTAG_PERMANENT) {
SEQ_TRACE_TOKEN(c_p) = am_have_dt_utag;
-#ifdef DTRACE_TAG_HARDDEBUG
- if (DT_UTAG_FLAGS(c_p) & DT_UTAG_SPREADING)
- erts_fprintf(stderr,
- "Dtrace -> (%T) stop spreading "
- "tag %T with message %T\r\n",
- c_p->common.id,DT_UTAG(c_p),ERL_MESSAGE_TERM(msgp));
-#endif
} else {
-#ifdef DTRACE_TAG_HARDDEBUG
- erts_fprintf(stderr,
- "Dtrace -> (%T) kill tag %T with "
- "message %T\r\n",
- c_p->common.id,DT_UTAG(c_p),ERL_MESSAGE_TERM(msgp));
-#endif
DT_UTAG(c_p) = NIL;
SEQ_TRACE_TOKEN(c_p) = NIL;
}
@@ -2029,12 +1995,6 @@ void process_main(void)
DT_UTAG(c_p) = ERL_MESSAGE_DT_UTAG(msgp);
}
DT_UTAG_FLAGS(c_p) |= DT_UTAG_SPREADING;
-#ifdef DTRACE_TAG_HARDDEBUG
- erts_fprintf(stderr,
- "Dtrace -> (%T) receive tag (%T) "
- "with message %T\r\n",
- c_p->common.id, DT_UTAG(c_p), ERL_MESSAGE_TERM(msgp));
-#endif
} else {
#endif
ASSERT(is_tuple(SEQ_TRACE_TOKEN(c_p)));
@@ -2064,7 +2024,7 @@ void process_main(void)
dtrace_proc_str(c_p, receiver_name);
token2 = SEQ_TRACE_TOKEN(c_p);
- if (token2 != NIL && token2 != am_have_dt_utag) {
+ if (have_seqtrace(token2)) {
tok_label = signed_val(SEQ_TRACE_T_LABEL(token2));
tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token2));
tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token2));
@@ -2077,11 +2037,24 @@ void process_main(void)
UNLINK_MESSAGE(c_p, msgp);
JOIN_MESSAGE(c_p);
CANCEL_TIMER(c_p);
- free_message(msgp);
+
+ erts_save_message_in_proc(c_p, msgp);
+ c_p->flags &= ~F_DELAY_GC;
+
+ if (ERTS_IS_GC_DESIRED_INTERNAL(c_p, HTOP, E)) {
+ /*
+ * We want to GC soon but we leave a few
+ * reductions giving the message some time
+ * to turn into garbage.
+ */
+ ERTS_VBUMP_LEAVE_REDS_INTERNAL(c_p, 5, FCALLS);
+ }
+
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
+ ERTS_CHK_MBUF_SZ(c_p);
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
-
NextPF(0, next);
}
@@ -2090,9 +2063,22 @@ void process_main(void)
* message didn't match), then jump to the loop_rec instruction.
*/
OpCase(loop_rec_end_f): {
+
+ ASSERT(c_p->flags & F_DELAY_GC);
+
SET_I((BeamInstr *) Arg(0));
SAVE_MESSAGE(c_p);
- goto loop_rec__;
+ if (FCALLS > 0 || FCALLS > neg_o_reds) {
+ FCALLS--;
+ goto loop_rec__;
+ }
+
+ c_p->flags &= ~F_DELAY_GC;
+ c_p->i = I;
+ SWAPOUT;
+ c_p->arity = 0;
+ c_p->current = NULL;
+ goto do_schedule;
}
/*
* Prepare to wait for a message or a timeout, whichever occurs first.
@@ -2131,8 +2117,6 @@ void process_main(void)
* c_p->def_arg_reg[0]. Note that it is safe to use this
* location because there are no living x registers in
* a receive statement.
- * Note that for the halfword emulator, the two first elements
- * of the array are used.
*/
BeamInstr** pi = (BeamInstr**) c_p->def_arg_reg;
*pi = I+3;
@@ -2182,7 +2166,10 @@ void process_main(void)
c_p->i = (BeamInstr *) Arg(0); /* L1 */
SWAPOUT;
c_p->arity = 0;
- erts_smp_atomic32_read_band_relb(&c_p->state, ~ERTS_PSFLG_ACTIVE);
+
+ if (!ERTS_PTMR_IS_TIMED_OUT(c_p))
+ erts_smp_atomic32_read_band_relb(&c_p->state,
+ ~ERTS_PSFLG_ACTIVE);
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
c_p->current = NULL;
@@ -2228,7 +2215,7 @@ void process_main(void)
PreFetch(0, next);
if (IS_TRACED_FL(c_p, F_TRACE_RECEIVE)) {
- trace_receive(c_p, am_timeout);
+ trace_receive(c_p, am_clock_service, am_timeout, NULL);
}
if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p)) {
save_calls(c_p, &exp_timeout);
@@ -2249,10 +2236,6 @@ void process_main(void)
select_val2 = xb(Arg(0));
goto do_select_tuple_arity2;
- OpCase(i_select_tuple_arity2_rfAAff):
- select_val2 = r(0);
- I--;
-
do_select_tuple_arity2:
if (is_not_tuple(select_val2)) {
goto select_val2_fail;
@@ -2268,10 +2251,6 @@ void process_main(void)
select_val2 = xb(Arg(0));
goto do_select_val2;
- OpCase(i_select_val2_rfccff):
- select_val2 = r(0);
- I--;
-
do_select_val2:
if (select_val2 == Arg(2)) {
I += 3;
@@ -2295,10 +2274,6 @@ void process_main(void)
select_val = yb(Arg(0));
goto do_select_tuple_arity;
- OpCase(i_select_tuple_arity_rfI):
- select_val = r(0);
- I--;
-
do_select_tuple_arity:
if (is_tuple(select_val)) {
select_val = *tuple_val(select_val);
@@ -2315,10 +2290,6 @@ void process_main(void)
select_val = yb(Arg(0));
goto do_linear_search;
- OpCase(i_select_val_lins_rfI):
- select_val = r(0);
- I--;
-
do_linear_search: {
BeamInstr *vs = &Arg(3);
int ix = 0;
@@ -2345,10 +2316,6 @@ void process_main(void)
select_val = yb(Arg(0));
goto do_binary_search;
- OpCase(i_select_val_bins_rfI):
- select_val = r(0);
- I--;
-
do_binary_search:
{
struct Pairs {
@@ -2409,10 +2376,6 @@ void process_main(void)
jump_on_val_zero_index = xb(Arg(0));
goto do_jump_on_val_zero_index;
- OpCase(i_jump_on_val_zero_rfI):
- jump_on_val_zero_index = r(0);
- I--;
-
do_jump_on_val_zero_index:
if (is_small(jump_on_val_zero_index)) {
jump_on_val_zero_index = signed_val(jump_on_val_zero_index);
@@ -2437,10 +2400,6 @@ void process_main(void)
jump_on_val_index = xb(Arg(0));
goto do_jump_on_val_index;
- OpCase(i_jump_on_val_rfII):
- jump_on_val_index = r(0);
- I--;
-
do_jump_on_val_index:
if (is_small(jump_on_val_index)) {
jump_on_val_index = (Uint) (signed_val(jump_on_val_index) - Arg(3));
@@ -2460,15 +2419,12 @@ void process_main(void)
do {
Eterm term = *I++;
- switch (term & _TAG_IMMED1_MASK) {
- case (R_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER:
- *hp++ = r(0);
- break;
- case (X_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER:
- *hp++ = x(term >> _TAG_IMMED1_SIZE);
+ switch (loader_tag(term)) {
+ case LOADER_X_REG:
+ *hp++ = x(loader_x_reg_index(term));
break;
- case (Y_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER:
- *hp++ = y(term >> _TAG_IMMED1_SIZE);
+ case LOADER_Y_REG:
+ *hp++ = y(loader_y_reg_index(term));
break;
default:
*hp++ = term;
@@ -2482,31 +2438,26 @@ void process_main(void)
OpCase(new_map_dII): {
Eterm res;
- x(0) = r(0);
- SWAPOUT;
+ HEAVY_SWAPOUT;
res = new_map(c_p, reg, I-1);
- SWAPIN;
- r(0) = x(0);
+ HEAVY_SWAPIN;
StoreResult(res, Arg(0));
Next(3+Arg(2));
}
-#define PUT_TERM_REG(term, desc) \
-do { \
- switch ((desc) & _TAG_IMMED1_MASK) { \
- case (R_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \
- r(0) = (term); \
- break; \
- case (X_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \
- x((desc) >> _TAG_IMMED1_SIZE) = (term); \
- break; \
- case (Y_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \
- y((desc) >> _TAG_IMMED1_SIZE) = (term); \
- break; \
- default: \
- ASSERT(0); \
- break; \
- } \
+#define PUT_TERM_REG(term, desc) \
+do { \
+ switch (loader_tag(desc)) { \
+ case LOADER_X_REG: \
+ x(loader_x_reg_index(desc)) = (term); \
+ break; \
+ case LOADER_Y_REG: \
+ y(loader_y_reg_index(desc)) = (term); \
+ break; \
+ default: \
+ ASSERT(0); \
+ break; \
+ } \
} while(0)
OpCase(i_get_map_elements_fsI): {
@@ -2577,12 +2528,10 @@ do { \
Eterm map;
GetArg1(1, map);
- x(0) = r(0);
- SWAPOUT;
+ HEAVY_SWAPOUT;
res = update_map_assoc(c_p, reg, map, I);
- SWAPIN;
+ HEAVY_SWAPIN;
if (is_value(res)) {
- r(0) = x(0);
StoreResult(res, Arg(2));
Next(5+Arg(4));
} else {
@@ -2601,12 +2550,10 @@ do { \
Eterm map;
GetArg1(1, map);
- x(0) = r(0);
- SWAPOUT;
+ HEAVY_SWAPOUT;
res = update_map_exact(c_p, reg, map, I);
- SWAPIN;
+ HEAVY_SWAPIN;
if (is_value(res)) {
- r(0) = x(0);
StoreResult(res, Arg(2));
Next(5+Arg(4));
} else {
@@ -2637,15 +2584,19 @@ do { \
GetArg1(2, tmp_reg[0]);
bf = (BifFunction) Arg(1);
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
c_p->fcalls = FCALLS;
PROCESS_MAIN_CHK_LOCKS(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
+ ERTS_CHK_MBUF_SZ(c_p);
result = (*bf)(c_p, tmp_reg);
+ ERTS_CHK_MBUF_SZ(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
ERTS_HOLE_CHECK(c_p);
FCALLS = c_p->fcalls;
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
if (is_value(result)) {
StoreBifResult(3, result);
}
@@ -2666,15 +2617,19 @@ do { \
GetArg1(1, tmp_reg[0]);
bf = (BifFunction) Arg(0);
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
c_p->fcalls = FCALLS;
PROCESS_MAIN_CHK_LOCKS(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
+ ERTS_CHK_MBUF_SZ(c_p);
result = (*bf)(c_p, tmp_reg);
+ ERTS_CHK_MBUF_SZ(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
ERTS_HOLE_CHECK(c_p);
FCALLS = c_p->fcalls;
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
if (is_value(result)) {
StoreBifResult(2, result);
}
@@ -2688,26 +2643,26 @@ do { \
{
typedef Eterm (*GcBifFunction)(Process*, Eterm*, Uint);
GcBifFunction bf;
- Eterm arg;
Eterm result;
Uint live = (Uint) Arg(3);
- GetArg1(2, arg);
- reg[0] = r(0);
- reg[live] = arg;
+ GetArg1(2, x(live));
bf = (GcBifFunction) Arg(1);
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
c_p->fcalls = FCALLS;
SWAPOUT;
PROCESS_MAIN_CHK_LOCKS(c_p);
ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
+ ERTS_CHK_MBUF_SZ(c_p);
result = (*bf)(c_p, reg, live);
+ ERTS_CHK_MBUF_SZ(c_p);
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
SWAPIN;
- r(0) = reg[0];
ERTS_HOLE_CHECK(c_p);
FCALLS = c_p->fcalls;
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
if (is_value(result)) {
StoreBifResult(4, result);
}
@@ -2715,12 +2670,12 @@ do { \
SET_I((BeamInstr *) Arg(0));
Goto(*I);
}
- reg[0] = arg;
+ x(0) = x(live);
I = handle_error(c_p, I, reg, translate_gc_bif((void *) bf));
goto post_error_handling;
}
- OpCase(i_gc_bif2_jIId): /* Note, one less parameter than the i_gc_bif1
+ OpCase(i_gc_bif2_jIIssd): /* Note, one less parameter than the i_gc_bif1
and i_gc_bif3 */
{
typedef Eterm (*GcBifFunction)(Process*, Eterm*, Uint);
@@ -2728,71 +2683,85 @@ do { \
Eterm result;
Uint live = (Uint) Arg(2);
- reg[0] = r(0);
- reg[live++] = tmp_arg1;
- reg[live] = tmp_arg2;
+ GetArg2(3, x(live), x(live+1));
+ /*
+ * XXX This calling convention does not make sense. 'live'
+ * should point out the first argument, not the second
+ * (i.e. 'live' should not be incremented below).
+ */
+ live++;
bf = (GcBifFunction) Arg(1);
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
c_p->fcalls = FCALLS;
SWAPOUT;
PROCESS_MAIN_CHK_LOCKS(c_p);
ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
+ ERTS_CHK_MBUF_SZ(c_p);
result = (*bf)(c_p, reg, live);
+ ERTS_CHK_MBUF_SZ(c_p);
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
SWAPIN;
- r(0) = reg[0];
ERTS_HOLE_CHECK(c_p);
FCALLS = c_p->fcalls;
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
if (is_value(result)) {
- StoreBifResult(3, result);
+ StoreBifResult(5, result);
}
if (Arg(0) != 0) {
SET_I((BeamInstr *) Arg(0));
Goto(*I);
}
- reg[0] = tmp_arg1;
- reg[1] = tmp_arg2;
+ live--;
+ x(0) = x(live);
+ x(1) = x(live+1);
I = handle_error(c_p, I, reg, translate_gc_bif((void *) bf));
goto post_error_handling;
}
- OpCase(i_gc_bif3_jIsId):
+ OpCase(i_gc_bif3_jIIssd):
{
typedef Eterm (*GcBifFunction)(Process*, Eterm*, Uint);
GcBifFunction bf;
- Eterm arg;
Eterm result;
- Uint live = (Uint) Arg(3);
+ Uint live = (Uint) Arg(2);
- GetArg1(2, arg);
- reg[0] = r(0);
- reg[live++] = arg;
- reg[live++] = tmp_arg1;
- reg[live] = tmp_arg2;
+ x(live) = x(SCRATCH_X_REG);
+ GetArg2(3, x(live+1), x(live+2));
+ /*
+ * XXX This calling convention does not make sense. 'live'
+ * should point out the first argument, not the third
+ * (i.e. 'live' should not be incremented below).
+ */
+ live += 2;
bf = (GcBifFunction) Arg(1);
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
c_p->fcalls = FCALLS;
SWAPOUT;
PROCESS_MAIN_CHK_LOCKS(c_p);
ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
+ ERTS_CHK_MBUF_SZ(c_p);
result = (*bf)(c_p, reg, live);
+ ERTS_CHK_MBUF_SZ(c_p);
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
SWAPIN;
- r(0) = reg[0];
ERTS_HOLE_CHECK(c_p);
FCALLS = c_p->fcalls;
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
if (is_value(result)) {
- StoreBifResult(4, result);
+ StoreBifResult(5, result);
}
if (Arg(0) != 0) {
SET_I((BeamInstr *) Arg(0));
Goto(*I);
}
- reg[0] = arg;
- reg[1] = tmp_arg1;
- reg[2] = tmp_arg2;
+ live -= 2;
+ x(0) = x(live);
+ x(1) = x(live+1);
+ x(2) = x(live+2);
I = handle_error(c_p, I, reg, translate_gc_bif((void *) bf));
goto post_error_handling;
}
@@ -2800,24 +2769,29 @@ do { \
/*
* Guards bifs and, or, xor in guards.
*/
- OpCase(i_bif2_fbd):
+ OpCase(i_bif2_fbssd):
{
- Eterm tmp_reg[2] = {tmp_arg1, tmp_arg2};
+ Eterm tmp_reg[2];
Eterm (*bf)(Process*, Eterm*);
Eterm result;
+ GetArg2(2, tmp_reg[0], tmp_reg[1]);
bf = (BifFunction) Arg(1);
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
c_p->fcalls = FCALLS;
PROCESS_MAIN_CHK_LOCKS(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
+ ERTS_CHK_MBUF_SZ(c_p);
result = (*bf)(c_p, tmp_reg);
+ ERTS_CHK_MBUF_SZ(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
ERTS_HOLE_CHECK(c_p);
FCALLS = c_p->fcalls;
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
if (is_value(result)) {
- StoreBifResult(2, result);
+ StoreBifResult(4, result);
}
SET_I((BeamInstr *) Arg(0));
Goto(*I);
@@ -2826,26 +2800,29 @@ do { \
/*
* Guards bifs and, or, xor, relational operators in body.
*/
- OpCase(i_bif2_body_bd):
+ OpCase(i_bif2_body_bssd):
{
- Eterm tmp_reg[2] = {tmp_arg1, tmp_arg2};
+ Eterm tmp_reg[2];
Eterm (*bf)(Process*, Eterm*);
Eterm result;
+ GetArg2(1, tmp_reg[0], tmp_reg[1]);
bf = (BifFunction) Arg(0);
PROCESS_MAIN_CHK_LOCKS(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
+ ERTS_CHK_MBUF_SZ(c_p);
result = (*bf)(c_p, tmp_reg);
+ ERTS_CHK_MBUF_SZ(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
ERTS_HOLE_CHECK(c_p);
if (is_value(result)) {
ASSERT(!is_CP(result));
- StoreBifResult(1, result);
+ StoreBifResult(3, result);
}
- reg[0] = tmp_arg1;
- reg[1] = tmp_arg2;
+ reg[0] = tmp_reg[0];
+ reg[1] = tmp_reg[1];
SWAPOUT;
I = handle_error(c_p, I, reg, bf);
goto post_error_handling;
@@ -2857,11 +2834,26 @@ do { \
*/
OpCase(call_bif_e):
{
- Eterm (*bf)(Process*, Eterm*, BeamInstr*) = GET_BIF_ADDRESS(Arg(0));
+ Eterm (*bf)(Process*, Eterm*, BeamInstr*);
Eterm result;
BeamInstr *next;
+ ErlHeapFragment *live_hf_end;
+
+
+ if (!((FCALLS - 1) > 0 || (FCALLS-1) > neg_o_reds)) {
+ /* If we have run out of reductions, we do a context
+ switch before calling the bif */
+ c_p->arity = ((Export *)Arg(0))->code[2];
+ c_p->current = ((Export *)Arg(0))->code;
+ goto context_switch3;
+ }
+
+ ERTS_MSACC_SET_BIF_STATE_CACHED_X(GET_BIF_MODULE(Arg(0)), GET_BIF_ADDRESS(Arg(0)));
+
+ bf = GET_BIF_ADDRESS(Arg(0));
PRE_BIF_SWAPOUT(c_p);
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
c_p->fcalls = FCALLS - 1;
if (FCALLS <= 0) {
save_calls(c_p, (Export *) Arg(0));
@@ -2869,20 +2861,29 @@ do { \
PreFetch(1, next);
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
- reg[0] = r(0);
+ live_hf_end = c_p->mbuf;
+ ERTS_CHK_MBUF_SZ(c_p);
result = (*bf)(c_p, reg, I);
+ ERTS_CHK_MBUF_SZ(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
ERTS_HOLE_CHECK(c_p);
ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
- PROCESS_MAIN_CHK_LOCKS(c_p);
- if (c_p->mbuf || MSO(c_p).overhead >= BIN_VHEAP_SZ(c_p)) {
+ if (ERTS_IS_GC_DESIRED(c_p)) {
Uint arity = ((Export *)Arg(0))->code[2];
- result = erts_gc_after_bif_call(c_p, result, reg, arity);
+ result = erts_gc_after_bif_call_lhf(c_p, live_hf_end, result, reg, arity);
E = c_p->stop;
}
+ PROCESS_MAIN_CHK_LOCKS(c_p);
HTOP = HEAP_TOP(c_p);
FCALLS = c_p->fcalls;
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
+ /* We have to update the cache if we are enabled in order
+ to make sure no book keeping is done after we disabled
+ msacc. We don't always do this as it is quite expensive. */
+ if (ERTS_MSACC_IS_ENABLED_CACHED_X())
+ ERTS_MSACC_UPDATE_CACHE_X();
+ ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_EMULATOR);
if (is_value(result)) {
r(0) = result;
CHECK_TERM(r(0));
@@ -2891,7 +2892,6 @@ do { \
SET_CP(c_p, I+2);
SET_I(c_p->i);
SWAPIN;
- r(0) = reg[0];
Dispatch();
}
@@ -2907,111 +2907,81 @@ do { \
* Arithmetic operations.
*/
- OpCase(i_times_jId):
+ OpCase(i_times_jIssd):
{
- arith_func = ARITH_FUNC(mixed_times);
- goto do_big_arith2;
+ Eterm Op1, Op2;
+ GetArg2(2, Op1, Op2);
+ DO_OUTLINED_ARITH_2(mixed_times, Op1, Op2);
}
- OpCase(i_m_div_jId):
+ OpCase(i_m_div_jIssd):
{
- arith_func = ARITH_FUNC(mixed_div);
- goto do_big_arith2;
+ Eterm Op1, Op2;
+ GetArg2(2, Op1, Op2);
+ DO_OUTLINED_ARITH_2(mixed_div, Op1, Op2);
}
- OpCase(i_int_div_jId):
+ OpCase(i_int_div_jIssd):
{
- Eterm result;
+ Eterm Op1, Op2;
- if (tmp_arg2 == SMALL_ZERO) {
+ GetArg2(2, Op1, Op2);
+ if (Op2 == SMALL_ZERO) {
goto badarith;
- } else if (is_both_small(tmp_arg1, tmp_arg2)) {
- Sint ires = signed_val(tmp_arg1) / signed_val(tmp_arg2);
+ } else if (is_both_small(Op1, Op2)) {
+ Sint ires = signed_val(Op1) / signed_val(Op2);
if (MY_IS_SSMALL(ires)) {
- result = make_small(ires);
- STORE_ARITH_RESULT(result);
+ Eterm result = make_small(ires);
+ StoreBifResult(4, result);
}
}
- arith_func = ARITH_FUNC(int_div);
- goto do_big_arith2;
+ DO_OUTLINED_ARITH_2(int_div, Op1, Op2);
}
- OpCase(i_rem_jIxxd):
{
- Eterm result;
+ Eterm RemOp1, RemOp2;
- if (xb(Arg(3)) == SMALL_ZERO) {
- goto badarith;
- } else if (is_both_small(xb(Arg(2)), xb(Arg(3)))) {
- result = make_small(signed_val(xb(Arg(2))) % signed_val(xb(Arg(3))));
+ OpCase(i_rem_jIxxd):
+ RemOp1 = xb(Arg(2));
+ RemOp2 = xb(Arg(3));
+ goto do_rem;
+
+ OpCase(i_rem_jIssd):
+ GetArg2(2, RemOp1, RemOp2);
+ goto do_rem;
+
+ do_rem:
+ if (RemOp2 == SMALL_ZERO) {
+ goto badarith;
+ } else if (is_both_small(RemOp1, RemOp2)) {
+ Eterm result = make_small(signed_val(RemOp1) % signed_val(RemOp2));
StoreBifResult(4, result);
+ } else {
+ DO_OUTLINED_ARITH_2(int_rem, RemOp1, RemOp2);
}
- DO_BIG_ARITH(ARITH_FUNC(int_rem),xb(Arg(2)),xb(Arg(3)));
}
- OpCase(i_rem_jId):
{
- Eterm result;
-
- if (tmp_arg2 == SMALL_ZERO) {
- goto badarith;
- } else if (is_both_small(tmp_arg1, tmp_arg2)) {
- result = make_small(signed_val(tmp_arg1) % signed_val(tmp_arg2));
- STORE_ARITH_RESULT(result);
- } else {
- arith_func = ARITH_FUNC(int_rem);
- goto do_big_arith2;
- }
- }
+ Eterm BandOp1, BandOp2;
OpCase(i_band_jIxcd):
- {
- Eterm result;
+ BandOp1 = xb(Arg(2));
+ BandOp2 = Arg(3);
+ goto do_band;
- if (is_both_small(xb(Arg(2)), Arg(3))) {
+ OpCase(i_band_jIssd):
+ GetArg2(2, BandOp1, BandOp2);
+ goto do_band;
+
+ do_band:
+ if (is_both_small(BandOp1, BandOp2)) {
/*
* No need to untag -- TAG & TAG == TAG.
*/
- result = xb(Arg(2)) & Arg(3);
+ Eterm result = BandOp1 & BandOp2;
StoreBifResult(4, result);
}
- DO_BIG_ARITH(ARITH_FUNC(band),xb(Arg(2)),Arg(3));
- }
-
- OpCase(i_band_jId):
- {
- Eterm result;
-
- if (is_both_small(tmp_arg1, tmp_arg2)) {
- /*
- * No need to untag -- TAG & TAG == TAG.
- */
- result = tmp_arg1 & tmp_arg2;
- STORE_ARITH_RESULT(result);
- }
- arith_func = ARITH_FUNC(band);
- goto do_big_arith2;
- }
-
-#undef DO_BIG_ARITH
-
- do_big_arith2:
- {
- Eterm result;
- Uint live = Arg(1);
-
- SWAPOUT;
- reg[0] = r(0);
- reg[live] = tmp_arg1;
- reg[live+1] = tmp_arg2;
- result = arith_func(c_p, reg, live);
- r(0) = reg[0];
- SWAPIN;
- ERTS_HOLE_CHECK(c_p);
- if (is_value(result)) {
- STORE_ARITH_RESULT(result);
- }
- goto lb_Cl_error;
+ DO_OUTLINED_ARITH_2(band, BandOp1, BandOp2);
}
/*
@@ -3032,97 +3002,101 @@ do { \
goto find_func_info;
}
- OpCase(i_bor_jId):
+ OpCase(i_bor_jIssd):
{
- Eterm result;
+ Eterm Op1, Op2;
- if (is_both_small(tmp_arg1, tmp_arg2)) {
+ GetArg2(2, Op1, Op2);
+ if (is_both_small(Op1, Op2)) {
/*
* No need to untag -- TAG | TAG == TAG.
*/
- result = tmp_arg1 | tmp_arg2;
- STORE_ARITH_RESULT(result);
+ Eterm result = Op1 | Op2;
+ StoreBifResult(4, result);
}
- arith_func = ARITH_FUNC(bor);
- goto do_big_arith2;
+ DO_OUTLINED_ARITH_2(bor, Op1, Op2);
}
- OpCase(i_bxor_jId):
+ OpCase(i_bxor_jIssd):
{
- Eterm result;
+ Eterm Op1, Op2;
- if (is_both_small(tmp_arg1, tmp_arg2)) {
+ GetArg2(2, Op1, Op2);
+ if (is_both_small(Op1, Op2)) {
/*
* We could extract the tag from one argument, but a tag extraction
* could mean a shift. Therefore, play it safe here.
*/
- result = make_small(signed_val(tmp_arg1) ^ signed_val(tmp_arg2));
- STORE_ARITH_RESULT(result);
+ Eterm result = make_small(signed_val(Op1) ^ signed_val(Op2));
+ StoreBifResult(4, result);
}
- arith_func = ARITH_FUNC(bxor);
- goto do_big_arith2;
+ DO_OUTLINED_ARITH_2(bxor, Op1, Op2);
}
{
+ Eterm Op1, Op2;
Sint i;
Sint ires;
Eterm* bigp;
+ Eterm tmp_big[2];
- OpCase(i_bsr_jId):
- if (is_small(tmp_arg2)) {
- i = -signed_val(tmp_arg2);
- if (is_small(tmp_arg1)) {
+ OpCase(i_bsr_jIssd):
+ GetArg2(2, Op1, Op2);
+ if (is_small(Op2)) {
+ i = -signed_val(Op2);
+ if (is_small(Op1)) {
goto small_shift;
- } else if (is_big(tmp_arg1)) {
+ } else if (is_big(Op1)) {
if (i == 0) {
- StoreBifResult(2, tmp_arg1);
+ StoreBifResult(4, Op1);
}
+ ires = big_size(Op1);
goto big_shift;
}
- } else if (is_big(tmp_arg2)) {
+ } else if (is_big(Op2)) {
/*
* N bsr NegativeBigNum == N bsl MAX_SMALL
* N bsr PositiveBigNum == N bsl MIN_SMALL
*/
- tmp_arg2 = make_small(bignum_header_is_neg(*big_val(tmp_arg2)) ?
+ Op2 = make_small(bignum_header_is_neg(*big_val(Op2)) ?
MAX_SMALL : MIN_SMALL);
goto do_bsl;
}
goto badarith;
- OpCase(i_bsl_jId):
+ OpCase(i_bsl_jIssd):
+ GetArg2(2, Op1, Op2);
do_bsl:
- if (is_small(tmp_arg2)) {
- i = signed_val(tmp_arg2);
+ if (is_small(Op2)) {
+ i = signed_val(Op2);
- if (is_small(tmp_arg1)) {
+ if (is_small(Op1)) {
small_shift:
- ires = signed_val(tmp_arg1);
+ ires = signed_val(Op1);
if (i == 0 || ires == 0) {
- StoreBifResult(2, tmp_arg1);
+ StoreBifResult(4, Op1);
} else if (i < 0) { /* Right shift */
i = -i;
if (i >= SMALL_BITS-1) {
- tmp_arg1 = (ires < 0) ? SMALL_MINUS_ONE : SMALL_ZERO;
+ Op1 = (ires < 0) ? SMALL_MINUS_ONE : SMALL_ZERO;
} else {
- tmp_arg1 = make_small(ires >> i);
+ Op1 = make_small(ires >> i);
}
- StoreBifResult(2, tmp_arg1);
+ StoreBifResult(4, Op1);
} else if (i < SMALL_BITS-1) { /* Left shift */
if ((ires > 0 && ((~(Uint)0 << ((SMALL_BITS-1)-i)) & ires) == 0) ||
((~(Uint)0 << ((SMALL_BITS-1)-i)) & ~ires) == 0) {
- tmp_arg1 = make_small(ires << i);
- StoreBifResult(2, tmp_arg1);
+ Op1 = make_small(ires << i);
+ StoreBifResult(4, Op1);
}
}
- tmp_arg1 = small_to_big(ires, tmp_big);
+ ires = 1; /* big_size(small_to_big(Op1)) */
big_shift:
if (i > 0) { /* Left shift. */
- ires = big_size(tmp_arg1) + (i / D_EXP);
+ ires += (i / D_EXP);
} else { /* Right shift. */
- ires = big_size(tmp_arg1);
if (ires <= (-i / D_EXP))
ires = 3; /* ??? */
else
@@ -3140,14 +3114,17 @@ do { \
c_p->freason = SYSTEM_LIMIT;
goto lb_Cl_error;
}
- TestHeapPreserve(ires+1, Arg(1), tmp_arg1);
+ TestHeapPreserve(ires+1, Arg(1), Op1);
+ if (is_small(Op1)) {
+ Op1 = small_to_big(signed_val(Op1), tmp_big);
+ }
bigp = HTOP;
- tmp_arg1 = big_lshift(tmp_arg1, i, bigp);
- if (is_big(tmp_arg1)) {
+ Op1 = big_lshift(Op1, i, bigp);
+ if (is_big(Op1)) {
HTOP += bignum_header_arity(*HTOP) + 1;
}
HEAP_SPACE_VERIFIED(0);
- if (is_nil(tmp_arg1)) {
+ if (is_nil(Op1)) {
/*
* This result must have been only slight larger
* than allowed since it wasn't caught by the
@@ -3157,25 +3134,26 @@ do { \
goto lb_Cl_error;
}
ERTS_HOLE_CHECK(c_p);
- StoreBifResult(2, tmp_arg1);
+ StoreBifResult(4, Op1);
}
- } else if (is_big(tmp_arg1)) {
+ } else if (is_big(Op1)) {
if (i == 0) {
- StoreBifResult(2, tmp_arg1);
+ StoreBifResult(4, Op1);
}
+ ires = big_size(Op1);
goto big_shift;
}
- } else if (is_big(tmp_arg2)) {
- if (bignum_header_is_neg(*big_val(tmp_arg2))) {
+ } else if (is_big(Op2)) {
+ if (bignum_header_is_neg(*big_val(Op2))) {
/*
* N bsl NegativeBigNum is either 0 or -1, depending on
* the sign of N. Since we don't believe this case
* is common, do the calculation with the minimum
* amount of code.
*/
- tmp_arg2 = make_small(MIN_SMALL);
+ Op2 = make_small(MIN_SMALL);
goto do_bsl;
- } else if (is_small(tmp_arg1) || is_big(tmp_arg1)) {
+ } else if (is_small(Op1) || is_big(Op1)) {
/*
* N bsl PositiveBigNum is too large to represent.
*/
@@ -3199,12 +3177,10 @@ do { \
bnot_val = make_small(~signed_val(bnot_val));
} else {
Uint live = Arg(2);
- SWAPOUT;
- reg[0] = r(0);
+ HEAVY_SWAPOUT;
reg[live] = bnot_val;
bnot_val = erts_gc_bnot(c_p, reg, live);
- r(0) = reg[0];
- SWAPIN;
+ HEAVY_SWAPIN;
ERTS_HOLE_CHECK(c_p);
if (is_nil(bnot_val)) {
goto lb_Cl_error;
@@ -3219,11 +3195,10 @@ do { \
OpCase(i_apply): {
BeamInstr *next;
- SWAPOUT;
- next = apply(c_p, r(0), x(1), x(2), reg);
- SWAPIN;
+ HEAVY_SWAPOUT;
+ next = apply(c_p, r(0), x(1), x(2), reg, NULL, 0);
+ HEAVY_SWAPIN;
if (next != NULL) {
- r(0) = reg[0];
SET_CP(c_p, I+1);
SET_I(next);
Dispatch();
@@ -3234,12 +3209,11 @@ do { \
OpCase(i_apply_last_P): {
BeamInstr *next;
- SWAPOUT;
- next = apply(c_p, r(0), x(1), x(2), reg);
- SWAPIN;
+ HEAVY_SWAPOUT;
+ next = apply(c_p, r(0), x(1), x(2), reg, I, Arg(0));
+ HEAVY_SWAPIN;
if (next != NULL) {
- r(0) = reg[0];
- SET_CP(c_p, (BeamInstr *) EXPAND_POINTER(E[0]));
+ SET_CP(c_p, (BeamInstr *) E[0]);
E = ADD_BYTE_OFFSET(E, Arg(0));
SET_I(next);
Dispatch();
@@ -3250,11 +3224,10 @@ do { \
OpCase(i_apply_only): {
BeamInstr *next;
- SWAPOUT;
- next = apply(c_p, r(0), x(1), x(2), reg);
- SWAPIN;
+ HEAVY_SWAPOUT;
+ next = apply(c_p, r(0), x(1), x(2), reg, I, 0);
+ HEAVY_SWAPIN;
if (next != NULL) {
- r(0) = reg[0];
SET_I(next);
Dispatch();
}
@@ -3265,12 +3238,10 @@ do { \
OpCase(apply_I): {
BeamInstr *next;
- reg[0] = r(0);
- SWAPOUT;
- next = fixed_apply(c_p, reg, Arg(0));
- SWAPIN;
+ HEAVY_SWAPOUT;
+ next = fixed_apply(c_p, reg, Arg(0), NULL, 0);
+ HEAVY_SWAPIN;
if (next != NULL) {
- r(0) = reg[0];
SET_CP(c_p, I+2);
SET_I(next);
Dispatch();
@@ -3282,13 +3253,11 @@ do { \
OpCase(apply_last_IP): {
BeamInstr *next;
- reg[0] = r(0);
- SWAPOUT;
- next = fixed_apply(c_p, reg, Arg(0));
- SWAPIN;
+ HEAVY_SWAPOUT;
+ next = fixed_apply(c_p, reg, Arg(0), I, Arg(1));
+ HEAVY_SWAPIN;
if (next != NULL) {
- r(0) = reg[0];
- SET_CP(c_p, (BeamInstr *) EXPAND_POINTER(E[0]));
+ SET_CP(c_p, (BeamInstr *) E[0]);
E = ADD_BYTE_OFFSET(E, Arg(1));
SET_I(next);
Dispatch();
@@ -3300,11 +3269,10 @@ do { \
OpCase(i_apply_fun): {
BeamInstr *next;
- SWAPOUT;
+ HEAVY_SWAPOUT;
next = apply_fun(c_p, r(0), x(1), reg);
- SWAPIN;
+ HEAVY_SWAPIN;
if (next != NULL) {
- r(0) = reg[0];
SET_CP(c_p, I+1);
SET_I(next);
Dispatchfun();
@@ -3315,12 +3283,11 @@ do { \
OpCase(i_apply_fun_last_P): {
BeamInstr *next;
- SWAPOUT;
+ HEAVY_SWAPOUT;
next = apply_fun(c_p, r(0), x(1), reg);
- SWAPIN;
+ HEAVY_SWAPIN;
if (next != NULL) {
- r(0) = reg[0];
- SET_CP(c_p, (BeamInstr *) EXPAND_POINTER(E[0]));
+ SET_CP(c_p, (BeamInstr *) E[0]);
E = ADD_BYTE_OFFSET(E, Arg(0));
SET_I(next);
Dispatchfun();
@@ -3331,11 +3298,10 @@ do { \
OpCase(i_apply_fun_only): {
BeamInstr *next;
- SWAPOUT;
+ HEAVY_SWAPOUT;
next = apply_fun(c_p, r(0), x(1), reg);
- SWAPIN;
+ HEAVY_SWAPIN;
if (next != NULL) {
- r(0) = reg[0];
SET_I(next);
Dispatchfun();
}
@@ -3345,13 +3311,10 @@ do { \
OpCase(i_call_fun_I): {
BeamInstr *next;
- SWAPOUT;
- reg[0] = r(0);
-
+ HEAVY_SWAPOUT;
next = call_fun(c_p, Arg(0), reg, THE_NON_VALUE);
- SWAPIN;
+ HEAVY_SWAPIN;
if (next != NULL) {
- r(0) = reg[0];
SET_CP(c_p, I+2);
SET_I(next);
Dispatchfun();
@@ -3362,13 +3325,11 @@ do { \
OpCase(i_call_fun_last_IP): {
BeamInstr *next;
- SWAPOUT;
- reg[0] = r(0);
+ HEAVY_SWAPOUT;
next = call_fun(c_p, Arg(0), reg, THE_NON_VALUE);
- SWAPIN;
+ HEAVY_SWAPIN;
if (next != NULL) {
- r(0) = reg[0];
- SET_CP(c_p, (BeamInstr *) EXPAND_POINTER(E[0]));
+ SET_CP(c_p, (BeamInstr *) E[0]);
E = ADD_BYTE_OFFSET(E, Arg(1));
SET_I(next);
Dispatchfun();
@@ -3411,10 +3372,19 @@ do { \
context_switch2: /* Entry for fun calls. */
c_p->current = I-3; /* Pointer to Mod, Func, Arity */
+ context_switch3:
+
{
Eterm* argp;
int i;
+ if (erts_smp_atomic32_read_nob(&c_p->state) & ERTS_PSFLG_EXITING) {
+ c_p->i = beam_exit;
+ c_p->arity = 0;
+ c_p->current = NULL;
+ goto do_schedule;
+ }
+
/*
* Make sure that there is enough room for the argument registers to be saved.
*/
@@ -3445,17 +3415,21 @@ do { \
* (beacuse the code for the Dispatch() macro becomes shorter that way).
*/
- reds_used = REDS_IN(c_p) - FCALLS + 1;
-
+ ASSERT(c_p->debug_reds_in == REDS_IN(c_p));
+ if (!ERTS_PROC_GET_SAVED_CALLS_BUF(c_p))
+ reds_used = REDS_IN(c_p) - FCALLS;
+ else
+ reds_used = REDS_IN(c_p) - (CONTEXT_REDS + FCALLS);
+ ASSERT(reds_used >= 0);
+
/*
* Save the argument registers and everything else.
*/
argp = c_p->arg_reg;
- for (i = c_p->arity - 1; i > 0; i--) {
+ for (i = c_p->arity - 1; i >= 0; i--) {
argp[i] = reg[i];
}
- c_p->arg_reg[0] = r(0);
SWAPOUT;
c_p->i = I;
goto do_schedule1;
@@ -3468,23 +3442,18 @@ do { \
Eterm* p;
PreFetch(3, next);
- GetArg2(0, element, tuple);
+ GetArg1(0, element);
+ tuple = REG_TARGET(Arg(1));
ASSERT(is_tuple(tuple));
p = (Eterm *) ((unsigned char *) tuple_val(tuple) + Arg(2));
*p = element;
NextPF(3, next);
}
- OpCase(i_is_ne_exact_f):
- if (EQ(tmp_arg1, tmp_arg2)) {
- ClauseFail();
- }
- Next(1);
-
OpCase(normal_exit): {
SWAPOUT;
c_p->freason = EXC_NORMAL;
- c_p->arity = 0; /* In case this process will ever be garbed again. */
+ c_p->arity = 0; /* In case this process will never be garbed again. */
ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
erts_do_exit_process(c_p, am_normal);
ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
@@ -3498,48 +3467,18 @@ do { \
goto do_schedule;
}
- OpCase(raise_ss): {
- /* This was not done very well in R10-0; then, we passed the tag in
- the first argument and hoped that the existing c_p->ftrace was
- still correct. But the ftrace-object already includes the tag
- (or rather, the freason). Now, we pass the original ftrace in
- the first argument. We also handle atom tags in the first
- argument for backwards compatibility.
- */
- Eterm raise_val1;
- Eterm raise_val2;
- GetArg2(0, raise_val1, raise_val2);
- c_p->fvalue = raise_val2;
- if (c_p->freason == EXC_NULL) {
- /* a safety check for the R10-0 case; should not happen */
- c_p->ftrace = NIL;
- c_p->freason = EXC_ERROR;
- }
- /* for R10-0 code, keep existing c_p->ftrace and hope it's correct */
- switch (raise_val1) {
- case am_throw:
- c_p->freason = EXC_THROWN & ~EXF_SAVETRACE;
- break;
- case am_error:
- c_p->freason = EXC_ERROR & ~EXF_SAVETRACE;
- break;
- case am_exit:
- c_p->freason = EXC_EXIT & ~EXF_SAVETRACE;
- break;
- default:
- {/* R10-1 and later
- XXX note: should do sanity check on given trace if it can be
- passed from a user! Currently only expecting generated calls.
- */
- struct StackTrace *s;
- c_p->ftrace = raise_val1;
- s = get_trace_from_exc(raise_val1);
- if (s == NULL) {
- c_p->freason = EXC_ERROR;
- } else {
- c_p->freason = PRIMARY_EXCEPTION(s->freason);
- }
- }
+ OpCase(i_raise): {
+ Eterm raise_trace = x(2);
+ Eterm raise_value = x(1);
+ struct StackTrace *s;
+
+ c_p->fvalue = raise_value;
+ c_p->ftrace = raise_trace;
+ s = get_trace_from_exc(raise_trace);
+ if (s == NULL) {
+ c_p->freason = EXC_ERROR;
+ } else {
+ c_p->freason = PRIMARY_EXCEPTION(s->freason);
}
goto find_func_info;
}
@@ -3547,25 +3486,14 @@ do { \
{
Eterm badmatch_val;
- OpCase(badmatch_y):
- badmatch_val = yb(Arg(0));
- goto do_badmatch;
-
OpCase(badmatch_x):
badmatch_val = xb(Arg(0));
- goto do_badmatch;
-
- OpCase(badmatch_r):
- badmatch_val = r(0);
-
- do_badmatch:
c_p->fvalue = badmatch_val;
c_p->freason = BADMATCH;
}
/* Fall through here */
find_func_info: {
- reg[0] = r(0);
SWAPOUT;
I = handle_error(c_p, I, reg, NULL);
goto post_error_handling;
@@ -3582,11 +3510,9 @@ do { \
* code[3]: &&call_error_handler
* code[4]: Not used
*/
- SWAPOUT;
- reg[0] = r(0);
+ HEAVY_SWAPOUT;
I = call_error_handler(c_p, I-3, reg, am_undefined_function);
- r(0) = reg[0];
- SWAPIN;
+ HEAVY_SWAPIN;
if (I) {
Goto(*I);
}
@@ -3594,18 +3520,13 @@ do { \
/* Fall through */
OpCase(error_action_code): {
handle_error:
- reg[0] = r(0);
SWAPOUT;
I = handle_error(c_p, NULL, reg, NULL);
post_error_handling:
if (I == 0) {
goto do_schedule;
} else {
- r(0) = reg[0];
ASSERT(!is_value(r(0)));
- if (c_p->mbuf) {
- erts_garbage_collect(c_p, 0, reg+1, 3);
- }
SWAPIN;
Goto(*I);
}
@@ -3629,11 +3550,13 @@ do { \
* I[3]: Function pointer to dirty NIF
*/
BifFunction vbf;
+ ErlHeapFragment *live_hf_end;
+
+ ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_NIF);
DTRACE_NIF_ENTRY(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]);
c_p->current = I-3; /* current and vbf set to please handle_error */
- SWAPOUT;
- c_p->fcalls = FCALLS - 1;
+ HEAVY_SWAPOUT;
PROCESS_MAIN_CHK_LOCKS(c_p);
bif_nif_arity = I[-1];
ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
@@ -3643,16 +3566,24 @@ do { \
typedef Eterm NifF(struct enif_environment_t*, int argc, Eterm argv[]);
NifF* fp = vbf = (NifF*) I[1];
struct enif_environment_t env;
- erts_pre_nif(&env, c_p, (struct erl_module_nif*)I[2]);
- reg[0] = r(0);
+#ifdef ERTS_SMP
+ ASSERT(c_p->scheduler_data);
+#endif
+ live_hf_end = c_p->mbuf;
+ ERTS_CHK_MBUF_SZ(c_p);
+ erts_pre_nif(&env, c_p, (struct erl_module_nif*)I[2], NULL);
nif_bif_result = (*fp)(&env, bif_nif_arity, reg);
if (env.exception_thrown)
nif_bif_result = THE_NON_VALUE;
erts_post_nif(&env);
+ ERTS_CHK_MBUF_SZ(c_p);
+
+ PROCESS_MAIN_CHK_LOCKS(c_p);
+ ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
+ ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_EMULATOR);
+ ASSERT(!env.exiting);
+ ASSERT(!ERTS_PROC_IS_EXITING(c_p));
}
- ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(nif_bif_result));
- PROCESS_MAIN_CHK_LOCKS(c_p);
- ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
DTRACE_NIF_RETURN(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]);
goto apply_bif_or_nif_epilogue;
@@ -3669,6 +3600,14 @@ do { \
* code[4]: Function pointer to BIF function
*/
+ if (!((FCALLS - 1) > 0 || (FCALLS - 1) > neg_o_reds)) {
+ /* If we have run out of reductions, we do a context
+ switch before calling the bif */
+ goto context_switch;
+ }
+
+ ERTS_MSACC_SET_BIF_STATE_CACHED_X((Eterm)I[-3], (BifFunction)Arg(0));
+
c_p->current = I-3; /* In case we apply process_info/1,2 or load_nif/1 */
c_p->i = I; /* In case we apply check_process_code/2. */
c_p->arity = 0; /* To allow garbage collection on ourselves
@@ -3677,34 +3616,45 @@ do { \
DTRACE_BIF_ENTRY(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]);
SWAPOUT;
+ ERTS_DBG_CHK_REDS(c_p, FCALLS - 1);
c_p->fcalls = FCALLS - 1;
vbf = (BifFunction) Arg(0);
PROCESS_MAIN_CHK_LOCKS(c_p);
bif_nif_arity = I[-1];
ASSERT(bif_nif_arity <= 4);
ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
- reg[0] = r(0);
+ ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
{
Eterm (*bf)(Process*, Eterm*, BeamInstr*) = vbf;
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
+ live_hf_end = c_p->mbuf;
+ ERTS_CHK_MBUF_SZ(c_p);
nif_bif_result = (*bf)(c_p, reg, I);
+ ERTS_CHK_MBUF_SZ(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p) ||
is_non_value(nif_bif_result));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
}
-
+ /* We have to update the cache if we are enabled in order
+ to make sure no book keeping is done after we disabled
+ msacc. We don't always do this as it is quite expensive. */
+ if (ERTS_MSACC_IS_ENABLED_CACHED_X())
+ ERTS_MSACC_UPDATE_CACHE_X();
+ ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_EMULATOR);
DTRACE_BIF_RETURN(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]);
apply_bif_or_nif_epilogue:
ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
ERTS_HOLE_CHECK(c_p);
- if (c_p->mbuf) {
- nif_bif_result = erts_gc_after_bif_call(c_p, nif_bif_result,
- reg, bif_nif_arity);
+ if (ERTS_IS_GC_DESIRED(c_p)) {
+ nif_bif_result = erts_gc_after_bif_call_lhf(c_p, live_hf_end,
+ nif_bif_result,
+ reg, bif_nif_arity);
}
SWAPIN; /* There might have been a garbage collection. */
FCALLS = c_p->fcalls;
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
if (is_value(nif_bif_result)) {
r(0) = nif_bif_result;
CHECK_TERM(r(0));
@@ -3713,7 +3663,6 @@ do { \
Goto(*I);
} else if (c_p->freason == TRAP) {
SET_I(c_p->i);
- r(0) = reg[0];
if (c_p->flags & F_HIBERNATE_SCHED) {
c_p->flags &= ~F_HIBERNATE_SCHED;
goto do_schedule;
@@ -3735,21 +3684,21 @@ do { \
StoreBifResult(1, result);
}
+ OpCase(i_get_hash_cId):
+ {
+ Eterm arg;
+ Eterm result;
+
+ GetArg1(0, arg);
+ result = erts_pd_hash_get_with_hx(c_p, Arg(1), arg);
+ StoreBifResult(2, result);
+ }
+
{
Eterm case_end_val;
OpCase(case_end_x):
case_end_val = xb(Arg(0));
- goto do_case_end;
-
- OpCase(case_end_y):
- case_end_val = yb(Arg(0));
- goto do_case_end;
-
- OpCase(case_end_r):
- case_end_val = r(0);
-
- do_case_end:
c_p->fvalue = case_end_val;
c_p->freason = EXC_CASE_CLAUSE;
goto find_func_info;
@@ -3797,19 +3746,13 @@ do { \
goto do_bs_init_bits_known;
}
- OpCase(i_bs_init_bits_fail_heap_IjId): {
- /* tmp_arg1 was fetched by an i_fetch instruction */
- num_bits_term = tmp_arg1;
- alloc = Arg(0);
- I++;
+ OpCase(i_bs_init_bits_fail_heap_sIjId): {
+ GetArg1(0, num_bits_term);
+ alloc = Arg(1);
+ I += 2;
goto do_bs_init_bits;
}
- OpCase(i_bs_init_bits_fail_rjId): {
- num_bits_term = r(0);
- alloc = 0;
- goto do_bs_init_bits;
- }
OpCase(i_bs_init_bits_fail_yjId): {
num_bits_term = yb(Arg(0));
I++;
@@ -3929,52 +3872,48 @@ do { \
}
{
- OpCase(i_bs_init_fail_heap_IjId): {
- /* tmp_arg1 was fetched by an i_fetch instruction */
- tmp_arg2 = Arg(0);
- I++;
- goto do_bs_init;
- }
+ Eterm BsOp1, BsOp2;
- OpCase(i_bs_init_fail_rjId): {
- tmp_arg1 = r(0);
- tmp_arg2 = 0;
+ OpCase(i_bs_init_fail_heap_sIjId): {
+ GetArg1(0, BsOp1);
+ BsOp2 = Arg(1);
+ I += 2;
goto do_bs_init;
}
OpCase(i_bs_init_fail_yjId): {
- tmp_arg1 = yb(Arg(0));
- tmp_arg2 = 0;
+ BsOp1 = yb(Arg(0));
+ BsOp2 = 0;
I++;
goto do_bs_init;
}
OpCase(i_bs_init_fail_xjId): {
- tmp_arg1 = xb(Arg(0));
- tmp_arg2 = 0;
+ BsOp1 = xb(Arg(0));
+ BsOp2 = 0;
I++;
}
/* FALL THROUGH */
do_bs_init:
- if (is_small(tmp_arg1)) {
- Sint size = signed_val(tmp_arg1);
+ if (is_small(BsOp1)) {
+ Sint size = signed_val(BsOp1);
if (size < 0) {
goto badarg;
}
- tmp_arg1 = (Eterm) size;
+ BsOp1 = (Eterm) size;
} else {
Uint bytes;
- if (!term_to_Uint(tmp_arg1, &bytes)) {
+ if (!term_to_Uint(BsOp1, &bytes)) {
c_p->freason = bytes;
goto lb_Cl_error;
}
if ((bytes >> (8*sizeof(Uint)-3)) != 0) {
goto system_limit;
}
- tmp_arg1 = (Eterm) bytes;
+ BsOp1 = (Eterm) bytes;
}
- if (tmp_arg1 <= ERL_ONHEAP_BIN_LIMIT) {
+ if (BsOp1 <= ERL_ONHEAP_BIN_LIMIT) {
goto do_heap_bin_alloc;
} else {
goto do_proc_bin_alloc;
@@ -3982,15 +3921,15 @@ do { \
OpCase(i_bs_init_heap_IIId): {
- tmp_arg1 = Arg(0);
- tmp_arg2 = Arg(1);
+ BsOp1 = Arg(0);
+ BsOp2 = Arg(1);
I++;
goto do_proc_bin_alloc;
}
OpCase(i_bs_init_IId): {
- tmp_arg1 = Arg(0);
- tmp_arg2 = 0;
+ BsOp1 = Arg(0);
+ BsOp2 = 0;
}
/* FALL THROUGH */
do_proc_bin_alloc: {
@@ -3999,13 +3938,13 @@ do { \
erts_bin_offset = 0;
erts_writable_bin = 0;
- TestBinVHeap(tmp_arg1 / sizeof(Eterm),
- tmp_arg2 + PROC_BIN_SIZE + ERL_SUB_BIN_SIZE, Arg(1));
+ TestBinVHeap(BsOp1 / sizeof(Eterm),
+ BsOp2 + PROC_BIN_SIZE + ERL_SUB_BIN_SIZE, Arg(1));
/*
* Allocate the binary struct itself.
*/
- bptr = erts_bin_nrml_alloc(tmp_arg1);
+ bptr = erts_bin_nrml_alloc(BsOp1);
erts_refc_init(&bptr->refc, 1);
erts_current_bin = (byte *) bptr->orig_bytes;
@@ -4015,28 +3954,28 @@ do { \
pb = (ProcBin *) HTOP;
HTOP += PROC_BIN_SIZE;
pb->thing_word = HEADER_PROC_BIN;
- pb->size = tmp_arg1;
+ pb->size = BsOp1;
pb->next = MSO(c_p).first;
MSO(c_p).first = (struct erl_off_heap_header*) pb;
pb->val = bptr;
pb->bytes = (byte*) bptr->orig_bytes;
pb->flags = 0;
- OH_OVERHEAD(&(MSO(c_p)), tmp_arg1 / sizeof(Eterm));
+ OH_OVERHEAD(&(MSO(c_p)), BsOp1 / sizeof(Eterm));
StoreBifResult(2, make_binary(pb));
}
OpCase(i_bs_init_heap_bin_heap_IIId): {
- tmp_arg1 = Arg(0);
- tmp_arg2 = Arg(1);
+ BsOp1 = Arg(0);
+ BsOp2 = Arg(1);
I++;
goto do_heap_bin_alloc;
}
OpCase(i_bs_init_heap_bin_IId): {
- tmp_arg1 = Arg(0);
- tmp_arg2 = 0;
+ BsOp1 = Arg(0);
+ BsOp2 = 0;
}
/* Fall through */
do_heap_bin_alloc:
@@ -4044,33 +3983,36 @@ do { \
ErlHeapBin* hb;
Uint bin_need;
- bin_need = heap_bin_size(tmp_arg1);
+ bin_need = heap_bin_size(BsOp1);
erts_bin_offset = 0;
erts_writable_bin = 0;
- TestHeap(bin_need+tmp_arg2+ERL_SUB_BIN_SIZE, Arg(1));
+ TestHeap(bin_need+BsOp2+ERL_SUB_BIN_SIZE, Arg(1));
hb = (ErlHeapBin *) HTOP;
HTOP += bin_need;
- hb->thing_word = header_heap_bin(tmp_arg1);
- hb->size = tmp_arg1;
+ hb->thing_word = header_heap_bin(BsOp1);
+ hb->size = BsOp1;
erts_current_bin = (byte *) hb->data;
- tmp_arg1 = make_binary(hb);
- StoreBifResult(2, tmp_arg1);
+ BsOp1 = make_binary(hb);
+ StoreBifResult(2, BsOp1);
}
}
- OpCase(i_bs_add_jId): {
- Uint Unit = Arg(1);
- if (is_both_small(tmp_arg1, tmp_arg2)) {
- Sint Arg1 = signed_val(tmp_arg1);
- Sint Arg2 = signed_val(tmp_arg2);
+ OpCase(bs_add_jssId): {
+ Eterm Op1, Op2;
+ Uint Unit = Arg(3);
+
+ GetArg2(1, Op1, Op2);
+ if (is_both_small(Op1, Op2)) {
+ Sint Arg1 = signed_val(Op1);
+ Sint Arg2 = signed_val(Op2);
if (Arg1 >= 0 && Arg2 >= 0) {
- BsSafeMul(Arg2, Unit, goto system_limit, tmp_arg1);
- tmp_arg1 += Arg1;
+ BsSafeMul(Arg2, Unit, goto system_limit, Op1);
+ Op1 += Arg1;
store_bs_add_result:
- if (tmp_arg1 <= MAX_SMALL) {
- tmp_arg1 = make_small(tmp_arg1);
+ if (Op1 <= MAX_SMALL) {
+ Op1 = make_small(Op1);
} else {
/*
* May generate a heap fragment, but in this
@@ -4082,10 +4024,10 @@ do { \
* references (such as the heap).
*/
SWAPOUT;
- tmp_arg1 = erts_make_integer(tmp_arg1, c_p);
+ Op1 = erts_make_integer(Op1, c_p);
HTOP = HEAP_TOP(c_p);
}
- StoreBifResult(2, tmp_arg1);
+ StoreBifResult(4, Op1);
}
goto badarg;
} else {
@@ -4108,16 +4050,16 @@ do { \
* an Uint, the reason is SYSTEM_LIMIT.
*/
- if (!term_to_Uint(tmp_arg1, &a)) {
+ if (!term_to_Uint(Op1, &a)) {
if (a == BADARG) {
goto badarg;
}
- if (!term_to_Uint(tmp_arg2, &b)) {
+ if (!term_to_Uint(Op2, &b)) {
c_p->freason = b;
goto lb_Cl_error;
}
goto system_limit;
- } else if (!term_to_Uint(tmp_arg2, &b)) {
+ } else if (!term_to_Uint(Op2, &b)) {
c_p->freason = b;
goto lb_Cl_error;
}
@@ -4127,8 +4069,8 @@ do { \
*/
BsSafeMul(b, Unit, goto system_limit, c);
- tmp_arg1 = a + c;
- if (tmp_arg1 < a) {
+ Op1 = a + c;
+ if (Op1 < a) {
/*
* If the result is less than one of the
* arguments, there must have been an overflow.
@@ -4150,52 +4092,47 @@ do { \
}
/*
- * tmp_arg1 = Number of bytes to build
- * tmp_arg2 = Source binary
- * Operands: Fail ExtraHeap Live Unit Dst
+ * x(SCRATCH_X_REG);
+ * Operands: Fail ExtraHeap Live Unit Size Dst
*/
- OpCase(i_bs_append_jIIId): {
+ OpCase(i_bs_append_jIIIsd): {
Uint live = Arg(2);
Uint res;
+ Eterm Size;
- SWAPOUT;
- reg[0] = r(0);
- reg[live] = tmp_arg2;
- res = erts_bs_append(c_p, reg, live, tmp_arg1, Arg(1), Arg(3));
- r(0) = reg[0];
- SWAPIN;
+ GetArg1(4, Size);
+ HEAVY_SWAPOUT;
+ reg[live] = x(SCRATCH_X_REG);
+ res = erts_bs_append(c_p, reg, live, Size, Arg(1), Arg(3));
+ HEAVY_SWAPIN;
if (is_non_value(res)) {
/* c_p->freason is already set (may be either BADARG or SYSTEM_LIMIT). */
goto lb_Cl_error;
}
- StoreBifResult(4, res);
+ StoreBifResult(5, res);
}
/*
- * tmp_arg1 = Number of bytes to build
- * tmp_arg2 = Source binary
- * Operands: Fail Unit Dst
+ * Operands: Fail Size Src Unit Dst
*/
- OpCase(i_bs_private_append_jId): {
+ OpCase(i_bs_private_append_jIssd): {
Eterm res;
+ Eterm Size, Src;
- res = erts_bs_private_append(c_p, tmp_arg2, tmp_arg1, Arg(1));
+ GetArg2(2, Size, Src);
+ res = erts_bs_private_append(c_p, Src, Size, Arg(1));
if (is_non_value(res)) {
/* c_p->freason is already set (may be either BADARG or SYSTEM_LIMIT). */
goto lb_Cl_error;
}
- StoreBifResult(2, res);
+ StoreBifResult(4, res);
}
- /*
- * tmp_arg1 = Initial size of writable binary
- * Operands: Live Dst
- */
OpCase(bs_init_writable): {
- SWAPOUT;
+ HEAVY_SWAPOUT;
r(0) = erts_bs_init_writable(c_p, r(0));
- SWAPIN;
+ HEAVY_SWAPIN;
Next(0);
}
@@ -4252,7 +4189,7 @@ do { \
StoreBifResult(1, result);
}
- OpCase(i_bs_put_utf16_jIs): {
+ OpCase(bs_put_utf16_jIs): {
Eterm arg;
GetArg1(2, arg);
@@ -4286,26 +4223,29 @@ do { \
/*
* Only used for validating a value matched out.
- *
- * tmp_arg1 = Integer to validate
- * tmp_arg2 = Match context
*/
- OpCase(i_bs_validate_unicode_retract_j): {
- /*
- * There is no need to untag the integer, but it IS necessary
- * to make sure it is small (a bignum pointer could fall in
- * the valid range).
- */
- if (is_not_small(tmp_arg1) || tmp_arg1 > make_small(0x10FFFFUL) ||
- (make_small(0xD800UL) <= tmp_arg1 &&
- tmp_arg1 <= make_small(0xDFFFUL))) {
- ErlBinMatchBuffer *mb = ms_matchbuffer(tmp_arg2);
+ OpCase(i_bs_validate_unicode_retract_jss): {
+ Eterm i; /* Integer to validate */
- mb->offset -= 32;
- goto badarg;
- }
- Next(1);
- }
+ /*
+ * There is no need to untag the integer, but it IS necessary
+ * to make sure it is small (a bignum pointer could fall in
+ * the valid range).
+ */
+
+ GetArg1(1, i);
+ if (is_not_small(i) || i > make_small(0x10FFFFUL) ||
+ (make_small(0xD800UL) <= i && i <= make_small(0xDFFFUL))) {
+ Eterm ms; /* Match context */
+ ErlBinMatchBuffer* mb;
+
+ GetArg1(2, ms);
+ mb = ms_matchbuffer(ms);
+ mb->offset -= 32;
+ goto badarg;
+ }
+ Next(3);
+ }
/*
* Matching of binaries.
@@ -4317,9 +4257,6 @@ do { \
Uint slots;
Eterm context;
- OpCase(i_bs_start_match2_rfIId): {
- context = r(0);
-
do_start_match:
slots = Arg(2);
if (!is_boxed(context)) {
@@ -4366,7 +4303,7 @@ do { \
ClauseFail();
}
NextPF(4, next);
- }
+
OpCase(i_bs_start_match2_xfIId): {
context = xb(Arg(0));
I++;
@@ -4379,18 +4316,6 @@ do { \
}
}
- OpCase(bs_test_zero_tail2_fr): {
- BeamInstr *next;
- ErlBinMatchBuffer *_mb;
-
- PreFetch(1, next);
- _mb = (ErlBinMatchBuffer*) ms_matchbuffer(r(0));
- if (_mb->size != _mb->offset) {
- ClauseFail();
- }
- NextPF(1, next);
- }
-
OpCase(bs_test_zero_tail2_fx): {
BeamInstr *next;
ErlBinMatchBuffer *_mb;
@@ -4403,16 +4328,6 @@ do { \
NextPF(2, next);
}
- OpCase(bs_test_tail_imm2_frI): {
- BeamInstr *next;
- ErlBinMatchBuffer *_mb;
- PreFetch(2, next);
- _mb = ms_matchbuffer(r(0));
- if (_mb->size - _mb->offset != Arg(1)) {
- ClauseFail();
- }
- NextPF(2, next);
- }
OpCase(bs_test_tail_imm2_fxI): {
BeamInstr *next;
ErlBinMatchBuffer *_mb;
@@ -4424,16 +4339,6 @@ do { \
NextPF(3, next);
}
- OpCase(bs_test_unit_frI): {
- BeamInstr *next;
- ErlBinMatchBuffer *_mb;
- PreFetch(2, next);
- _mb = ms_matchbuffer(r(0));
- if ((_mb->size - _mb->offset) % Arg(1)) {
- ClauseFail();
- }
- NextPF(2, next);
- }
OpCase(bs_test_unit_fxI): {
BeamInstr *next;
ErlBinMatchBuffer *_mb;
@@ -4445,16 +4350,6 @@ do { \
NextPF(3, next);
}
- OpCase(bs_test_unit8_fr): {
- BeamInstr *next;
- ErlBinMatchBuffer *_mb;
- PreFetch(1, next);
- _mb = ms_matchbuffer(r(0));
- if ((_mb->size - _mb->offset) & 7) {
- ClauseFail();
- }
- NextPF(1, next);
- }
OpCase(bs_test_unit8_fx): {
BeamInstr *next;
ErlBinMatchBuffer *_mb;
@@ -4469,19 +4364,11 @@ do { \
{
Eterm bs_get_integer8_context;
- OpCase(i_bs_get_integer_8_rfd): {
- bs_get_integer8_context = r(0);
- goto do_bs_get_integer_8;
- }
-
OpCase(i_bs_get_integer_8_xfd): {
- bs_get_integer8_context = xb(Arg(0));
- I++;
- }
-
- do_bs_get_integer_8: {
ErlBinMatchBuffer *_mb;
Eterm _result;
+ bs_get_integer8_context = xb(Arg(0));
+ I++;
_mb = ms_matchbuffer(bs_get_integer8_context);
if (_mb->size - _mb->offset < 8) {
ClauseFail();
@@ -4499,15 +4386,10 @@ do { \
{
Eterm bs_get_integer_16_context;
- OpCase(i_bs_get_integer_16_rfd):
- bs_get_integer_16_context = r(0);
- goto do_bs_get_integer_16;
-
OpCase(i_bs_get_integer_16_xfd):
bs_get_integer_16_context = xb(Arg(0));
I++;
- do_bs_get_integer_16:
{
ErlBinMatchBuffer *_mb;
Eterm _result;
@@ -4528,17 +4410,10 @@ do { \
{
Eterm bs_get_integer_32_context;
- OpCase(i_bs_get_integer_32_rfId):
- bs_get_integer_32_context = r(0);
- goto do_bs_get_integer_32;
-
-
OpCase(i_bs_get_integer_32_xfId):
bs_get_integer_32_context = xb(Arg(0));
I++;
-
- do_bs_get_integer_32:
{
ErlBinMatchBuffer *_mb;
Uint32 _integer;
@@ -4551,11 +4426,11 @@ do { \
_integer = get_int32(_mb->base + _mb->offset/8);
}
_mb->offset += 32;
-#if !defined(ARCH_64) || HALFWORD_HEAP
+#if !defined(ARCH_64)
if (IS_USMALL(0, _integer)) {
#endif
_result = make_small(_integer);
-#if !defined(ARCH_64) || HALFWORD_HEAP
+#if !defined(ARCH_64)
} else {
TestHeap(BIG_UINT_HEAP_SIZE, Arg(1));
_result = uint_to_big((Uint) _integer, HTOP);
@@ -4567,103 +4442,82 @@ do { \
}
}
- /* Operands: Size Live Fail Flags Dst */
- OpCase(i_bs_get_integer_imm_rIIfId): {
- tmp_arg1 = r(0);
- /* Operands: Size Live Fail Flags Dst */
- goto do_bs_get_integer_imm_test_heap;
- }
+ {
+ Eterm Ms, Sz;
- /* Operands: x(Reg) Size Live Fail Flags Dst */
+ /* Operands: x(Reg) Size Live Fail Flags Dst */
OpCase(i_bs_get_integer_imm_xIIfId): {
- tmp_arg1 = xb(Arg(0));
- I++;
- /* Operands: Size Live Fail Flags Dst */
- goto do_bs_get_integer_imm_test_heap;
- }
-
- /*
- * tmp_arg1 = match context
- * Operands: Size Live Fail Flags Dst
- */
- do_bs_get_integer_imm_test_heap: {
- Uint wordsneeded;
- tmp_arg2 = Arg(0);
- wordsneeded = 1+WSIZE(NBYTES(tmp_arg2));
- TestHeapPreserve(wordsneeded, Arg(1), tmp_arg1);
- I += 2;
- /* Operands: Fail Flags Dst */
- goto do_bs_get_integer_imm;
- }
-
- /* Operands: Size Fail Flags Dst */
- OpCase(i_bs_get_integer_small_imm_rIfId): {
- tmp_arg1 = r(0);
- tmp_arg2 = Arg(0);
- I++;
- /* Operands: Fail Flags Dst */
- goto do_bs_get_integer_imm;
- }
+ Uint wordsneeded;
+ Ms = xb(Arg(0));
+ Sz = Arg(1);
+ wordsneeded = 1+WSIZE(NBYTES(Sz));
+ TestHeapPreserve(wordsneeded, Arg(2), Ms);
+ I += 3;
+ /* Operands: Fail Flags Dst */
+ goto do_bs_get_integer_imm;
+ }
- /* Operands: x(Reg) Size Fail Flags Dst */
+ /* Operands: x(Reg) Size Fail Flags Dst */
OpCase(i_bs_get_integer_small_imm_xIfId): {
- tmp_arg1 = xb(Arg(0));
- tmp_arg2 = Arg(1);
- I += 2;
- /* Operands: Fail Flags Dst */
- goto do_bs_get_integer_imm;
- }
+ Ms = xb(Arg(0));
+ Sz = Arg(1);
+ I += 2;
+ /* Operands: Fail Flags Dst */
+ goto do_bs_get_integer_imm;
+ }
- /*
- * tmp_arg1 = match context
- * tmp_arg2 = size of field
- * Operands: Fail Flags Dst
- */
+ /*
+ * Ms = match context
+ * Sz = size of field
+ * Operands: Fail Flags Dst
+ */
do_bs_get_integer_imm: {
- ErlBinMatchBuffer* mb;
- Eterm result;
+ ErlBinMatchBuffer* mb;
+ Eterm result;
- mb = ms_matchbuffer(tmp_arg1);
- LIGHT_SWAPOUT;
- result = erts_bs_get_integer_2(c_p, tmp_arg2, Arg(1), mb);
- LIGHT_SWAPIN;
- HEAP_SPACE_VERIFIED(0);
- if (is_non_value(result)) {
- ClauseFail();
+ mb = ms_matchbuffer(Ms);
+ LIGHT_SWAPOUT;
+ result = erts_bs_get_integer_2(c_p, Sz, Arg(1), mb);
+ LIGHT_SWAPIN;
+ HEAP_SPACE_VERIFIED(0);
+ if (is_non_value(result)) {
+ ClauseFail();
+ }
+ StoreBifResult(2, result);
}
- StoreBifResult(2, result);
}
/*
- * tmp_arg1 = Match context
- * tmp_arg2 = Size field
- * Operands: Fail Live FlagsAndUnit Dst
+ * Operands: Fail Live FlagsAndUnit Ms Sz Dst
*/
- OpCase(i_bs_get_integer_fIId): {
+ OpCase(i_bs_get_integer_fIIssd): {
Uint flags;
Uint size;
+ Eterm Ms;
+ Eterm Sz;
ErlBinMatchBuffer* mb;
Eterm result;
flags = Arg(2);
- BsGetFieldSize(tmp_arg2, (flags >> 3), ClauseFail(), size);
+ GetArg2(3, Ms, Sz);
+ BsGetFieldSize(Sz, (flags >> 3), ClauseFail(), size);
if (size >= SMALL_BITS) {
Uint wordsneeded;
- /* check bits size before potential gc.
+ /* Check bits size before potential gc.
* We do not want a gc and then realize we don't need
- * the allocated space (i.e. if the op fails)
+ * the allocated space (i.e. if the op fails).
*
- * remember to reacquire the matchbuffer after gc.
+ * Remember to re-acquire the matchbuffer after gc.
*/
- mb = ms_matchbuffer(tmp_arg1);
+ mb = ms_matchbuffer(Ms);
if (mb->size - mb->offset < size) {
ClauseFail();
}
wordsneeded = 1+WSIZE(NBYTES((Uint) size));
- TestHeapPreserve(wordsneeded, Arg(1), tmp_arg1);
+ TestHeapPreserve(wordsneeded, Arg(1), Ms);
}
- mb = ms_matchbuffer(tmp_arg1);
+ mb = ms_matchbuffer(Ms);
LIGHT_SWAPOUT;
result = erts_bs_get_integer_2(c_p, size, flags, mb);
LIGHT_SWAPIN;
@@ -4671,18 +4525,13 @@ do { \
if (is_non_value(result)) {
ClauseFail();
}
- StoreBifResult(3, result);
+ StoreBifResult(5, result);
}
{
Eterm get_utf8_context;
/* Operands: MatchContext Fail Dst */
- OpCase(i_bs_get_utf8_rfd): {
- get_utf8_context = r(0);
- goto do_bs_get_utf8;
- }
-
OpCase(i_bs_get_utf8_xfd): {
get_utf8_context = xb(Arg(0));
I++;
@@ -4693,7 +4542,7 @@ do { \
* Operands: Fail Dst
*/
- do_bs_get_utf8: {
+ {
Eterm result = erts_bs_get_utf8(ms_matchbuffer(get_utf8_context));
if (is_non_value(result)) {
ClauseFail();
@@ -4706,12 +4555,7 @@ do { \
Eterm get_utf16_context;
/* Operands: MatchContext Fail Flags Dst */
- OpCase(i_bs_get_utf16_rfId): {
- get_utf16_context = r(0);
- goto do_bs_get_utf16;
- }
-
- OpCase(i_bs_get_utf16_xfId): {
+ OpCase(i_bs_get_utf16_xfId): {
get_utf16_context = xb(Arg(0));
I++;
}
@@ -4720,7 +4564,7 @@ do { \
* get_utf16_context = match_context
* Operands: Fail Flags Dst
*/
- do_bs_get_utf16: {
+ {
Eterm result = erts_bs_get_utf16(ms_matchbuffer(get_utf16_context),
Arg(1));
if (is_non_value(result)) {
@@ -4739,26 +4583,10 @@ do { \
Uint orig;
Uint hole_size;
- OpCase(bs_context_to_binary_r): {
- context_to_binary_context = x0;
- I -= 2;
- goto do_context_to_binary;
- }
-
- /* Unfortunately, inlining can generate this instruction. */
- OpCase(bs_context_to_binary_y): {
- context_to_binary_context = yb(Arg(0));
- goto do_context_to_binary0;
- }
-
- OpCase(bs_context_to_binary_x): {
+ OpCase(bs_context_to_binary_x):
context_to_binary_context = xb(Arg(0));
-
- do_context_to_binary0:
I--;
- }
- do_context_to_binary:
if (is_boxed(context_to_binary_context) &&
header_is_bin_matchstate(*boxed_val(context_to_binary_context))) {
ErlBinMatchState* ms;
@@ -4770,17 +4598,11 @@ do { \
}
Next(2);
- OpCase(i_bs_get_binary_all_reuse_rfI): {
- context_to_binary_context = x0;
- goto do_bs_get_binary_all_reuse;
- }
-
OpCase(i_bs_get_binary_all_reuse_xfI): {
context_to_binary_context = xb(Arg(0));
I++;
}
- do_bs_get_binary_all_reuse:
mb = ms_matchbuffer(context_to_binary_context);
size = mb->size - mb->offset;
if (size % Arg(1) != 0) {
@@ -4808,16 +4630,11 @@ do { \
{
Eterm match_string_context;
- OpCase(i_bs_match_string_rfII): {
- match_string_context = r(0);
- goto do_bs_match_string;
- }
OpCase(i_bs_match_string_xfII): {
match_string_context = xb(Arg(0));
I++;
}
- do_bs_match_string:
{
BeamInstr *next;
byte* bytes;
@@ -4845,14 +4662,6 @@ do { \
}
}
- OpCase(i_bs_save2_rI): {
- BeamInstr *next;
- ErlBinMatchState *_ms;
- PreFetch(1, next);
- _ms = (ErlBinMatchState*) boxed_val((Eterm) r(0));
- _ms->save_offset[Arg(0)] = _ms->mb.offset;
- NextPF(1, next);
- }
OpCase(i_bs_save2_xI): {
BeamInstr *next;
ErlBinMatchState *_ms;
@@ -4862,14 +4671,6 @@ do { \
NextPF(2, next);
}
- OpCase(i_bs_restore2_rI): {
- BeamInstr *next;
- ErlBinMatchState *_ms;
- PreFetch(1, next);
- _ms = (ErlBinMatchState*) boxed_val((Eterm) r(0));
- _ms->mb.offset = _ms->save_offset[Arg(0)];
- NextPF(1, next);
- }
OpCase(i_bs_restore2_xI): {
BeamInstr *next;
ErlBinMatchState *_ms;
@@ -4904,7 +4705,7 @@ do { \
SWAPOUT; /* Needed for shared heap */
ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
- erts_trace_return(c_p, code, r(0), E+1/*Process tracer*/);
+ erts_trace_return(c_p, code, r(0), ERTS_TRACER_FROM_ETERM(E+1)/* tracer */);
ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
SWAPIN;
c_p->cp = NULL;
@@ -4916,11 +4717,9 @@ do { \
OpCase(i_generic_breakpoint): {
BeamInstr real_I;
ASSERT(I[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI));
- SWAPOUT;
- reg[0] = r(0);
+ HEAVY_SWAPOUT;
real_I = erts_generic_breakpoint(c_p, I, reg);
- r(0) = reg[0];
- SWAPIN;
+ HEAVY_SWAPIN;
ASSERT(VALID_INSTR(real_I));
Goto(real_I);
}
@@ -4979,7 +4778,7 @@ do { \
BeamInstr *next;
PreFetch(2, next);
- GetR(0, targ1);
+ targ1 = REG_TARGET(Arg(0));
/* Arg(0) == HEADER_FLONUM */
GET_DOUBLE(targ1, *(FloatDef*)ADD_BYTE_OFFSET(freg, fr));
NextPF(2, next);
@@ -4999,7 +4798,7 @@ do { \
Eterm fr = Arg(1);
BeamInstr *next;
- GetR(0, targ1);
+ targ1 = REG_TARGET(Arg(0));
PreFetch(2, next);
if (is_small(targ1)) {
fb(fr) = (double) signed_val(targ1);
@@ -5095,7 +4894,13 @@ do { \
#ifdef HIPE
{
- unsigned cmd;
+#define HIPE_MODE_SWITCH(Cmd) \
+ SWAPOUT; \
+ ERTS_DBG_CHK_REDS(c_p, FCALLS); \
+ c_p->fcalls = FCALLS; \
+ c_p->def_arg_reg[4] = -neg_o_reds; \
+ c_p = hipe_mode_switch(c_p, Cmd, reg); \
+ goto L_post_hipe_mode_switch
OpCase(hipe_trap_call): {
/*
@@ -5109,52 +4914,49 @@ do { \
*/
ASSERT(I[-5] == (Uint) OpCode(i_func_info_IaaI));
c_p->hipe.u.ncallee = (void(*)(void)) I[-4];
- cmd = HIPE_MODE_SWITCH_CMD_CALL | (I[-1] << 8);
++hipe_trap_count;
- goto L_hipe_mode_switch;
+ HIPE_MODE_SWITCH(HIPE_MODE_SWITCH_CMD_CALL | (I[-1] << 8));
}
OpCase(hipe_trap_call_closure): {
ASSERT(I[-5] == (Uint) OpCode(i_func_info_IaaI));
c_p->hipe.u.ncallee = (void(*)(void)) I[-4];
- cmd = HIPE_MODE_SWITCH_CMD_CALL_CLOSURE | (I[-1] << 8);
++hipe_trap_count;
- goto L_hipe_mode_switch;
+ HIPE_MODE_SWITCH(HIPE_MODE_SWITCH_CMD_CALL_CLOSURE | (I[-1] << 8));
}
OpCase(hipe_trap_return): {
- cmd = HIPE_MODE_SWITCH_CMD_RETURN;
- goto L_hipe_mode_switch;
+ HIPE_MODE_SWITCH(HIPE_MODE_SWITCH_CMD_RETURN);
}
OpCase(hipe_trap_throw): {
- cmd = HIPE_MODE_SWITCH_CMD_THROW;
- goto L_hipe_mode_switch;
+ HIPE_MODE_SWITCH(HIPE_MODE_SWITCH_CMD_THROW);
}
OpCase(hipe_trap_resume): {
- cmd = HIPE_MODE_SWITCH_CMD_RESUME;
- goto L_hipe_mode_switch;
+ HIPE_MODE_SWITCH(HIPE_MODE_SWITCH_CMD_RESUME);
}
- L_hipe_mode_switch:
- /* XXX: this abuse of def_arg_reg[] is horrid! */
- SWAPOUT;
- c_p->fcalls = FCALLS;
- c_p->def_arg_reg[4] = -neg_o_reds;
- reg[0] = r(0);
- c_p = hipe_mode_switch(c_p, cmd, reg);
- reg = ERTS_PROC_GET_SCHDATA(c_p)->x_reg_array;
- freg = ERTS_PROC_GET_SCHDATA(c_p)->f_reg_array;
+#undef HIPE_MODE_SWITCH
+
+ L_post_hipe_mode_switch:
+#ifdef DEBUG
+ pid = c_p->common.id; /* may have switched process... */
+#endif
+ reg = erts_proc_sched_data(c_p)->x_reg_array;
+ freg = erts_proc_sched_data(c_p)->f_reg_array;
ERL_BITS_RELOAD_STATEP(c_p);
+ /* XXX: this abuse of def_arg_reg[] is horrid! */
neg_o_reds = -c_p->def_arg_reg[4];
FCALLS = c_p->fcalls;
SWAPIN;
- switch( c_p->def_arg_reg[3] ) { /* Halfword wont work with hipe yet! */
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
+ switch( c_p->def_arg_reg[3] ) {
case HIPE_MODE_SWITCH_RES_RETURN:
ASSERT(is_value(reg[0]));
- MoveReturn(reg[0], r(0));
+ SET_I(c_p->cp);
+ c_p->cp = 0;
+ Goto(*I);
case HIPE_MODE_SWITCH_RES_CALL_EXPORTED:
c_p->i = c_p->hipe.u.callee_exp->addressv[erts_active_code_ix()];
/*fall through*/
case HIPE_MODE_SWITCH_RES_CALL_BEAM:
SET_I(c_p->i);
- r(0) = reg[0];
Dispatch();
case HIPE_MODE_SWITCH_RES_CALL_CLOSURE:
/* This can be used to call any function value, but currently it's
@@ -5163,9 +4965,8 @@ do { \
BeamInstr *next;
next = call_fun(c_p, c_p->arity - 1, reg, THE_NON_VALUE);
- SWAPIN;
+ HEAVY_SWAPIN;
if (next != NULL) {
- r(0) = reg[0];
SET_I(next);
Dispatchfun();
}
@@ -5213,22 +5014,54 @@ do { \
}
OpCase(i_hibernate): {
- SWAPOUT;
+ HEAVY_SWAPOUT;
if (erts_hibernate(c_p, r(0), x(1), x(2), reg)) {
+ FCALLS = c_p->fcalls;
c_p->flags &= ~F_HIBERNATE_SCHED;
goto do_schedule;
} else {
+ HEAVY_SWAPIN;
I = handle_error(c_p, I, reg, hibernate_3);
goto post_error_handling;
}
}
+ /* This is optimised as an instruction because
+ it has to be very very fast */
+ OpCase(i_perf_counter): {
+ BeamInstr* next;
+ ErtsSysPerfCounter ts;
+ PreFetch(0, next);
+
+ ts = erts_sys_perf_counter();
+
+ if (IS_SSMALL(ts)) {
+ r(0) = make_small((Sint)ts);
+ } else {
+ TestHeap(ERTS_SINT64_HEAP_SIZE(ts),0);
+ r(0) = make_big(HTOP);
+#if defined(ARCH_32) || HALFWORD_HEAP
+ if (ts >= (((Uint64) 1) << 32)) {
+ *HTOP = make_pos_bignum_header(2);
+ BIG_DIGIT(HTOP, 0) = (Uint) (ts & ((Uint) 0xffffffff));
+ BIG_DIGIT(HTOP, 1) = (Uint) ((ts >> 32) & ((Uint) 0xffffffff));
+ HTOP += 3;
+ }
+ else
+#endif
+ {
+ *HTOP = make_pos_bignum_header(1);
+ BIG_DIGIT(HTOP, 0) = (Uint) ts;
+ HTOP += 2;
+ }
+ }
+ NextPF(0, next);
+ }
+
OpCase(i_debug_breakpoint): {
- SWAPOUT;
- reg[0] = r(0);
+ HEAVY_SWAPOUT;
I = call_error_handler(c_p, I-3, reg, am_breakpoint);
- r(0) = reg[0];
- SWAPIN;
+ HEAVY_SWAPIN;
if (I) {
Goto(*I);
}
@@ -5329,6 +5162,273 @@ do { \
}
}
+/*
+ * erts_dirty_process_main() is what dirty schedulers execute. Since they handle
+ * only NIF calls they do not need to be able to execute all BEAM
+ * instructions.
+ */
+void erts_dirty_process_main(ErtsSchedulerData *esdp)
+{
+#ifdef ERTS_DIRTY_SCHEDULERS
+ Process* c_p = NULL;
+ ErtsMonotonicTime start_time;
+#ifdef DEBUG
+ ERTS_DECLARE_DUMMY(Eterm pid);
+#endif
+
+ /* Pointer to X registers: x(1)..x(N); reg[0] is used when doing GC,
+ * in all other cases x0 is used.
+ */
+ register Eterm* reg REG_xregs = NULL;
+
+ /*
+ * Top of heap (next free location); grows upwards.
+ */
+ register Eterm* HTOP REG_htop = NULL;
+
+ /* Stack pointer. Grows downwards; points
+ * to last item pushed (normally a saved
+ * continuation pointer).
+ */
+ register Eterm* E REG_stop = NULL;
+
+ /*
+ * Pointer to next threaded instruction.
+ */
+ register BeamInstr *I REG_I = NULL;
+
+ ERTS_MSACC_DECLARE_CACHE_X() /* a cached value of the tsd pointer for msacc */
+
+ /*
+ * start_time always positive for dirty CPU schedulers,
+ * and negative for dirty I/O schedulers.
+ */
+
+ if (ERTS_SCHEDULER_IS_DIRTY_CPU(esdp)) {
+ start_time = erts_get_monotonic_time(NULL);
+ ASSERT(start_time >= 0);
+ }
+ else {
+ start_time = ERTS_SINT64_MIN;
+ ASSERT(start_time < 0);
+ }
+
+ goto do_dirty_schedule;
+
+ context_switch:
+ c_p->arity = I[-1];
+ c_p->current = I-3; /* Pointer to Mod, Func, Arity */
+
+ {
+ int reds_used;
+ Eterm* argp;
+ int i;
+
+ /*
+ * Make sure that there is enough room for the argument registers to be saved.
+ */
+ if (c_p->arity > c_p->max_arg_reg) {
+ /*
+ * Yes, this is an expensive operation, but you only pay it the first
+ * time you call a function with more than 6 arguments which is
+ * scheduled out. This is better than paying for 26 words of wasted
+ * space for most processes which never call functions with more than
+ * 6 arguments.
+ */
+ Uint size = c_p->arity * sizeof(c_p->arg_reg[0]);
+ if (c_p->arg_reg != c_p->def_arg_reg) {
+ c_p->arg_reg = (Eterm *) erts_realloc(ERTS_ALC_T_ARG_REG,
+ (void *) c_p->arg_reg,
+ size);
+ } else {
+ c_p->arg_reg = (Eterm *) erts_alloc(ERTS_ALC_T_ARG_REG, size);
+ }
+ c_p->max_arg_reg = c_p->arity;
+ }
+
+ /*
+ * Save the argument registers and everything else.
+ */
+
+ argp = c_p->arg_reg;
+ for (i = c_p->arity - 1; i >= 0; i--) {
+ argp[i] = reg[i];
+ }
+ SWAPOUT;
+ c_p->i = I;
+
+ do_dirty_schedule:
+
+ if (start_time < 0) {
+ /*
+ * Dirty I/O scheduler:
+ * One reduction consumed regardless of
+ * time spent in the dirty NIF.
+ */
+ reds_used = esdp->virtual_reds + 1;
+ }
+ else {
+ /*
+ * Dirty CPU scheduler:
+ * Reductions based on time consumed by
+ * the dirty NIF.
+ */
+ Sint64 treds;
+ treds = erts_time2reds(start_time,
+ erts_get_monotonic_time(esdp));
+ treds += esdp->virtual_reds;
+ reds_used = treds > INT_MAX ? INT_MAX : (int) treds;
+ }
+
+ PROCESS_MAIN_CHK_LOCKS(c_p);
+ ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
+ ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
+ c_p = erts_schedule(esdp, c_p, reds_used);
+
+ if (start_time >= 0) {
+ start_time = erts_get_monotonic_time(esdp);
+ ASSERT(start_time >= 0);
+ }
+ }
+
+ ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
+#ifdef DEBUG
+ pid = c_p->common.id; /* Save for debugging purposes */
+#endif
+ ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
+ PROCESS_MAIN_CHK_LOCKS(c_p);
+
+ ASSERT(!(c_p->flags & F_HIPE_MODE));
+ ERTS_MSACC_UPDATE_CACHE_X();
+
+ reg = esdp->x_reg_array;
+ {
+ Eterm* argp;
+ int i;
+
+ argp = c_p->arg_reg;
+ for (i = c_p->arity - 1; i >= 0; i--) {
+ reg[i] = argp[i];
+ CHECK_TERM(reg[i]);
+ }
+
+ /*
+ * We put the original reduction count in the process structure, to reduce
+ * the code size (referencing a field in a struct through a pointer stored
+ * in a register gives smaller code than referencing a global variable).
+ */
+
+ I = c_p->i;
+
+ ASSERT(em_call_nif == (BeamInstr *) *I);
+
+ /*
+ * Set fcalls even though we ignore it, so we don't
+ * confuse code accessing it...
+ */
+ if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p))
+ c_p->fcalls = 0;
+ else
+ c_p->fcalls = CONTEXT_REDS;
+
+ SWAPIN;
+
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(process_scheduled)) {
+ DTRACE_CHARBUF(process_buf, DTRACE_TERM_BUF_SIZE);
+ DTRACE_CHARBUF(fun_buf, DTRACE_TERM_BUF_SIZE);
+ dtrace_proc_str(c_p, process_buf);
+
+ if (ERTS_PROC_IS_EXITING(c_p)) {
+ strcpy(fun_buf, "<exiting>");
+ } else {
+ BeamInstr *fptr = find_function_from_pc(c_p->i);
+ if (fptr) {
+ dtrace_fun_decode(c_p, (Eterm)fptr[0],
+ (Eterm)fptr[1], (Uint)fptr[2],
+ NULL, fun_buf);
+ } else {
+ erts_snprintf(fun_buf, sizeof(DTRACE_CHARBUF_NAME(fun_buf)),
+ "<unknown/%p>", *I);
+ }
+ }
+
+ DTRACE2(process_scheduled, process_buf, fun_buf);
+ }
+#endif
+ }
+
+ {
+#ifdef DEBUG
+ Eterm result;
+#endif
+ Eterm arity;
+
+ {
+ /*
+ * call_nif is always first instruction in function:
+ *
+ * I[-3]: Module
+ * I[-2]: Function
+ * I[-1]: Arity
+ * I[0]: &&call_nif
+ * I[1]: Function pointer to NIF function
+ * I[2]: Pointer to erl_module_nif
+ * I[3]: Function pointer to dirty NIF
+ */
+ BifFunction vbf;
+
+ ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_NIF);
+
+ DTRACE_NIF_ENTRY(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]);
+ c_p->current = I-3; /* current and vbf set to please handle_error */
+ SWAPOUT;
+ PROCESS_MAIN_CHK_LOCKS(c_p);
+ arity = I[-1];
+ ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
+
+ ASSERT(!ERTS_PROC_IS_EXITING(c_p));
+ {
+ typedef Eterm NifF(struct enif_environment_t*, int argc, Eterm argv[]);
+ NifF* fp = vbf = (NifF*) I[1];
+ struct enif_environment_t env;
+ ASSERT(!c_p->scheduler_data);
+
+ erts_pre_dirty_nif(esdp, &env, c_p,
+ (struct erl_module_nif*)I[2]);
+
+#ifdef DEBUG
+ result =
+#else
+ (void)
+#endif
+ (*fp)(&env, arity, reg);
+
+ erts_post_dirty_nif(&env);
+
+ ASSERT(!is_value(result));
+ ASSERT(c_p->freason == TRAP);
+ ASSERT(!(c_p->flags & F_HIBERNATE_SCHED));
+
+ PROCESS_MAIN_CHK_LOCKS(c_p);
+ ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
+ ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
+ ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_EMULATOR);
+ if (env.exiting)
+ goto do_dirty_schedule;
+ ASSERT(!ERTS_PROC_IS_EXITING(c_p));
+ }
+
+ DTRACE_NIF_RETURN(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]);
+ ERTS_HOLE_CHECK(c_p);
+ SWAPIN;
+ I = c_p->i;
+ goto context_switch;
+ }
+ }
+#endif /* ERTS_DIRTY_SCHEDULERS */
+}
+
static BifFunction
translate_gc_bif(void* gcf)
{
@@ -5517,7 +5617,8 @@ next_catch(Process* c_p, Eterm *reg) {
BeamInstr *cpp = c_p->cp;
if (cpp == beam_exception_trace) {
erts_trace_exception(c_p, cp_val(ptr[0]),
- reg[1], reg[2], ptr+1);
+ reg[1], reg[2],
+ ERTS_TRACER_FROM_ETERM(ptr+1));
/* Skip return_trace parameters */
ptr += 2;
} else if (cpp == beam_return_trace) {
@@ -5544,7 +5645,8 @@ next_catch(Process* c_p, Eterm *reg) {
}
if (cp_val(*prev) == beam_exception_trace) {
erts_trace_exception(c_p, cp_val(ptr[0]),
- reg[1], reg[2], ptr+1);
+ reg[1], reg[2],
+ ERTS_TRACER_FROM_ETERM(ptr+1));
}
/* Skip return_trace parameters */
ptr += 2;
@@ -5737,12 +5839,13 @@ save_stacktrace(Process* c_p, BeamInstr* pc, Eterm* reg, BifFunction bf,
s->depth = 0;
/*
- * If the failure was in a BIF other than 'error', 'exit' or
- * 'throw', find the bif-table index and save the argument
- * registers by consing up an arglist.
+ * If the failure was in a BIF other than 'error/1', 'error/2',
+ * 'exit/1' or 'throw/1', find the bif-table index and save the
+ * argument registers by consing up an arglist.
*/
- if (bf != NULL && bf != error_1 && bf != error_2 &&
- bf != exit_1 && bf != throw_1) {
+ if (bf != NULL && bf != error_1 && bf != error_2 && bf != exit_1
+ && bf != throw_1 && bf != wrap_error_1 && bf != wrap_error_2
+ && bf != wrap_exit_1 && bf != wrap_throw_1) {
int i;
int a = 0;
for (i = 0; i < BIF_SIZE; i++) {
@@ -5838,12 +5941,30 @@ erts_save_stacktrace(Process* p, struct StackTrace* s, int depth)
p->cp) {
/* Cannot follow cp here - code may be unloaded */
BeamInstr *cpp = p->cp;
+ int trace_cp;
if (cpp == beam_exception_trace || cpp == beam_return_trace) {
/* Skip return_trace parameters */
ptr += 2;
+ trace_cp = 1;
} else if (cpp == beam_return_to_trace) {
/* Skip return_to_trace parameters */
ptr += 1;
+ trace_cp = 1;
+ }
+ else {
+ trace_cp = 0;
+ }
+ if (trace_cp && s->pc == cpp) {
+ /*
+ * If process 'cp' points to a return/exception trace
+ * instruction and 'cp' has been saved as 'pc' in
+ * stacktrace, we need to update 'pc' in stacktrace
+ * with the actual 'cp' located on the top of the
+ * stack; otherwise, we will lose the top stackframe
+ * when building the stack trace.
+ */
+ ASSERT(is_CP(p->stop[0]));
+ s->pc = cp_val(p->stop[0]);
}
}
while (ptr < STACK_START(p) && depth > 0) {
@@ -5963,12 +6084,15 @@ build_stacktrace(Process* c_p, Eterm exc) {
erts_set_current_function(&fi, s->current);
}
+ depth = s->depth;
/*
- * If fi.current is still NULL, default to the initial function
+ * If fi.current is still NULL, and we have no
+ * stack at all, default to the initial function
* (e.g. spawn_link(erlang, abs, [1])).
*/
if (fi.current == NULL) {
- erts_set_current_function(&fi, c_p->u.initial);
+ if (depth <= 0)
+ erts_set_current_function(&fi, c_p->u.initial);
args = am_true; /* Just in case */
} else {
args = get_args_from_exc(exc);
@@ -5978,10 +6102,9 @@ build_stacktrace(Process* c_p, Eterm exc) {
* Look up all saved continuation pointers and calculate
* needed heap space.
*/
- depth = s->depth;
stk = stkp = (FunctionInfo *) erts_alloc(ERTS_ALC_T_TMP,
depth*sizeof(FunctionInfo));
- heap_size = fi.needed + 2;
+ heap_size = fi.current ? fi.needed + 2 : 0;
for (i = 0; i < depth; i++) {
erts_lookup_function_info(stkp, s->trace[i], 1);
if (stkp->current) {
@@ -6000,8 +6123,10 @@ build_stacktrace(Process* c_p, Eterm exc) {
res = CONS(hp, mfa, res);
hp += 2;
}
- hp = erts_build_mfa_item(&fi, hp, args, &mfa);
- res = CONS(hp, mfa, res);
+ if (fi.current) {
+ hp = erts_build_mfa_item(&fi, hp, args, &mfa);
+ res = CONS(hp, mfa, res);
+ }
erts_free(ERTS_ALC_T_TMP, (void *) stk);
return res;
@@ -6098,8 +6223,107 @@ apply_setup_error_handler(Process* p, Eterm module, Eterm function, Uint arity,
return ep;
}
+static ERTS_INLINE void
+apply_bif_error_adjustment(Process *p, Export *ep,
+ Eterm *reg, Uint arity,
+ BeamInstr *I, Uint stack_offset)
+{
+ /*
+ * I is only set when the apply is a tail call, i.e.,
+ * from the instructions i_apply_only, i_apply_last_P,
+ * and apply_last_IP.
+ */
+ if (I
+ && ep->code[3] == (BeamInstr) em_apply_bif
+ && (ep == bif_export[BIF_error_1]
+ || ep == bif_export[BIF_error_2]
+ || ep == bif_export[BIF_exit_1]
+ || ep == bif_export[BIF_throw_1])) {
+ /*
+ * We are about to tail apply one of the BIFs
+ * erlang:error/1, erlang:error/2, erlang:exit/1,
+ * or erlang:throw/1. Error handling of these BIFs is
+ * special!
+ *
+ * We need 'p->cp' to point into the calling
+ * function when handling the error after the BIF has
+ * been applied. This in order to get the topmost
+ * stackframe correct. Without the following adjustment,
+ * 'p->cp' will point into the function that called
+ * current function when handling the error. We add a
+ * dummy stackframe in order to achive this.
+ *
+ * Note that these BIFs unconditionally will cause
+ * an exception to be raised. That is, our modifications
+ * of 'p->cp' as well as the stack will be corrected by
+ * the error handling code.
+ *
+ * If we find an exception/return-to trace continuation
+ * pointer as the topmost continuation pointer, we do not
+ * need to do anything since the information already will
+ * be available for generation of the stacktrace.
+ */
+ int apply_only = stack_offset == 0;
+ BeamInstr *cpp;
+
+ if (apply_only) {
+ ASSERT(p->cp != NULL);
+ cpp = p->cp;
+ }
+ else {
+ ASSERT(is_CP(p->stop[0]));
+ cpp = cp_val(p->stop[0]);
+ }
+
+ if (cpp != beam_exception_trace
+ && cpp != beam_return_trace
+ && cpp != beam_return_to_trace) {
+ Uint need = stack_offset /* bytes */ / sizeof(Eterm);
+ if (need == 0)
+ need = 1; /* i_apply_only */
+ if (p->stop - p->htop < need)
+ erts_garbage_collect(p, (int) need, reg, arity+1);
+ p->stop -= need;
+
+ if (apply_only) {
+ /*
+ * Called from the i_apply_only instruction.
+ *
+ * 'p->cp' contains continuation pointer pointing
+ * into the function that called current function.
+ * We push that continuation pointer onto the stack,
+ * and set 'p->cp' to point into current function.
+ */
+
+ p->stop[0] = make_cp(p->cp);
+ p->cp = I;
+ }
+ else {
+ /*
+ * Called from an i_apply_last_p, or apply_last_IP,
+ * instruction.
+ *
+ * Calling instruction will after we return read
+ * a continuation pointer from the stack and write
+ * it to 'p->cp', and then remove the topmost
+ * stackframe of size 'stack_offset'.
+ *
+ * We have sized the dummy-stackframe so that it
+ * will be removed by the instruction we currently
+ * are executing, and leave the stackframe that
+ * normally would have been removed intact.
+ *
+ */
+ p->stop[0] = make_cp(I);
+ }
+ }
+ }
+}
+
static BeamInstr*
-apply(Process* p, Eterm module, Eterm function, Eterm args, Eterm* reg)
+apply(
+Process* p, Eterm module, Eterm function, Eterm args, Eterm* reg,
+BeamInstr *I, Uint stack_offset)
{
int arity;
Export* ep;
@@ -6124,21 +6348,54 @@ apply(Process* p, Eterm module, Eterm function, Eterm args, Eterm* reg)
return 0;
}
- /* 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;
+ 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_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 (module != am_erlang || function != am_apply)
+ break;
+
+ /* Adjust for multiple apply of apply/3... */
+
+ a = args;
+ if (is_list(a)) {
+ Eterm *consp = list_val(a);
+ m = CAR(consp);
+ a = CDR(consp);
+ if (is_list(a)) {
+ consp = list_val(a);
+ f = CAR(consp);
+ a = CDR(consp);
+ if (is_list(a)) {
+ consp = list_val(a);
+ a = CAR(consp);
+ if (is_nil(CDR(consp))) {
+ /* erlang:apply/3 */
+ module = m;
+ function = f;
+ args = a;
+ if (is_not_atom(f))
+ goto error;
+ continue;
+ }
+ }
+ }
+ }
+ break; /* != erlang:apply/3 */
}
-
/*
* Walk down the 3rd parameter of apply (the argument list) and copy
* the parameters to the x registers (reg[]). If the module argument
@@ -6176,12 +6433,14 @@ apply(Process* p, Eterm module, Eterm function, Eterm args, Eterm* reg)
} else if (ERTS_PROC_GET_SAVED_CALLS_BUF(p)) {
save_calls(p, ep);
}
+ apply_bif_error_adjustment(p, ep, reg, arity, I, stack_offset);
DTRACE_GLOBAL_CALL_FROM_EXPORT(p, ep);
return ep->addressv[erts_active_code_ix()];
}
static BeamInstr*
-fixed_apply(Process* p, Eterm* reg, Uint arity)
+fixed_apply(Process* p, Eterm* reg, Uint arity,
+ BeamInstr *I, Uint stack_offset)
{
Export* ep;
Eterm module;
@@ -6211,6 +6470,10 @@ fixed_apply(Process* p, Eterm* reg, Uint arity)
if (is_not_atom(module)) goto error;
++arity;
}
+
+ /* Handle apply of apply/3... */
+ if (module == am_erlang && function == am_apply && arity == 3)
+ return apply(p, reg[0], reg[1], reg[2], reg, I, stack_offset);
/*
* Get the index into the export table, or failing that the export
@@ -6225,6 +6488,7 @@ fixed_apply(Process* p, Eterm* reg, Uint arity)
} else if (ERTS_PROC_GET_SAVED_CALLS_BUF(p)) {
save_calls(p, ep);
}
+ apply_bif_error_adjustment(p, ep, reg, arity, I, stack_offset);
DTRACE_GLOBAL_CALL_FROM_EXPORT(p, ep);
return ep->addressv[erts_active_code_ix()];
}
@@ -6338,7 +6602,7 @@ erts_hibernate(Process* c_p, Eterm module, Eterm function, Eterm args, Eterm* re
return -1;
}
#else /* ERTS_SMP */
- ERTS_SMP_MSGQ_MV_INQ2PRIVQ(c_p);
+ ERTS_SMP_MSGQ_MV_INQ2PRIVQ(c_p);
if (!c_p->msg.len)
#endif
erts_smp_atomic32_read_band_relb(&c_p->state, ~ERTS_PSFLG_ACTIVE);
@@ -6437,34 +6701,49 @@ call_fun(Process* p, /* Current process. */
* representation (the module has never been loaded),
* or the module defining the fun has been unloaded.
*/
+
module = fe->module;
- if ((modp = erts_get_module(module, code_ix)) != NULL
- && modp->curr.code != NULL) {
+
+ ERTS_SMP_READ_MEMORY_BARRIER;
+ if (fe->pend_purge_address) {
/*
- * There is a module loaded, but obviously the fun is not
- * defined in it. We must not call the error_handler
- * (or we will get into an infinite loop).
+ * The system is currently trying to purge the
+ * module containing this fun. Suspend the process
+ * and let it try again when the purge operation is
+ * done (may succeed or not).
*/
- goto badfun;
+ ep = erts_suspend_process_on_pending_purge_lambda(p, fe);
+ ASSERT(ep);
}
+ else {
+ if ((modp = erts_get_module(module, code_ix)) != NULL
+ && modp->curr.code_hdr != NULL) {
+ /*
+ * There is a module loaded, but obviously the fun is not
+ * defined in it. We must not call the error_handler
+ * (or we will get into an infinite loop).
+ */
+ goto badfun;
+ }
- /*
- * No current code for this module. Call the error_handler module
- * to attempt loading the module.
- */
+ /*
+ * No current code for this module. Call the error_handler module
+ * to attempt loading the module.
+ */
- ep = erts_find_function(erts_proc_get_error_handler(p),
- am_undefined_lambda, 3, code_ix);
- if (ep == NULL) { /* No error handler */
- p->current = NULL;
- p->freason = EXC_UNDEF;
- return NULL;
+ ep = erts_find_function(erts_proc_get_error_handler(p),
+ am_undefined_lambda, 3, code_ix);
+ if (ep == NULL) { /* No error handler */
+ p->current = NULL;
+ p->freason = EXC_UNDEF;
+ return NULL;
+ }
}
reg[0] = module;
reg[1] = fun;
reg[2] = args;
reg[3] = NIL;
- return ep->addressv[erts_active_code_ix()];
+ return ep->addressv[code_ix];
}
}
} else if (is_export_header(hdr)) {
@@ -6556,7 +6835,7 @@ new_fun(Process* p, Eterm* reg, ErlFunEntry* fe, int num_free)
p->htop = hp + needed;
funp = (ErlFunThing *) hp;
hp = funp->env;
- erts_refc_inc(&fe->refc, 2);
+ erts_smp_refc_inc(&fe->refc, 2);
funp->thing_word = HEADER_FUN;
funp->next = MSO(p).first;
MSO(p).first = (struct erl_off_heap_header*) funp;
@@ -6644,23 +6923,20 @@ static Eterm get_map_element_hash(Eterm map, Eterm key, Uint32 hx)
return vs ? *vs : THE_NON_VALUE;
}
-#define GET_TERM(term, dest) \
-do { \
- Eterm src = (Eterm)(term); \
- switch (src & _TAG_IMMED1_MASK) { \
- case (R_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \
- dest = x(0); \
- break; \
- case (X_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \
- dest = x(src >> _TAG_IMMED1_SIZE); \
- break; \
- case (Y_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \
- dest = y(src >> _TAG_IMMED1_SIZE); \
- break; \
- default: \
- dest = src; \
- break; \
- } \
+#define GET_TERM(term, dest) \
+do { \
+ Eterm src = (Eterm)(term); \
+ switch (loader_tag(src)) { \
+ case LOADER_X_REG: \
+ dest = x(loader_x_reg_index(src)); \
+ break; \
+ case LOADER_Y_REG: \
+ dest = y(loader_y_reg_index(src)); \
+ break; \
+ default: \
+ dest = src; \
+ break; \
+ } \
} while(0)
@@ -6699,13 +6975,6 @@ new_map(Process* p, Eterm* reg, BeamInstr* I)
erts_factory_proc_init(&factory, p);
res = erts_hashmap_from_array(&factory, thp, n/2, 0);
erts_factory_close(&factory);
- if (p->mbuf) {
- Uint live = Arg(2);
- reg[live] = res;
- erts_garbage_collect(p, 0, reg, live+1);
- res = reg[live];
- E = p->stop;
- }
return res;
}
@@ -6771,13 +7040,6 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
hx = hashmap_make_hash(new_key);
res = erts_hashmap_insert(p, hx, new_key, val, res, 0);
- if (p->mbuf) {
- Uint live = Arg(3);
- reg[live] = res;
- erts_garbage_collect(p, 0, reg, live+1);
- res = reg[live];
- E = p->stop;
- }
new_p += 2;
}
@@ -6936,13 +7198,11 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
/* The expensive case, need to build a hashmap */
if (n > MAP_SMALL_MAP_LIMIT) {
- res = erts_hashmap_from_ks_and_vs(p,flatmap_get_keys(mp),flatmap_get_values(mp),n);
- if (p->mbuf) {
- Uint live = Arg(3);
- reg[live] = res;
- erts_garbage_collect(p, 0, reg, live+1);
- res = reg[live];
- }
+ ErtsHeapFactory factory;
+ erts_factory_proc_init(&factory, p);
+ res = erts_hashmap_from_ks_and_vs(&factory,flatmap_get_keys(mp),
+ flatmap_get_values(mp),n);
+ erts_factory_close(&factory);
}
return res;
}
@@ -6998,14 +7258,6 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
return res;
}
- if (p->mbuf) {
- Uint live = Arg(3);
- reg[live] = res;
- erts_garbage_collect(p, 0, reg, live+1);
- res = reg[live];
- E = p->stop;
- }
-
new_p += 2;
}
return res;
@@ -7114,6 +7366,15 @@ erts_is_builtin(Eterm Mod, Eterm Name, int arity)
Export e;
Export* ep;
+ if (Mod == am_erlang && Name == am_apply && arity == 3) {
+ /*
+ * Special case. apply/3 is built-in (implemented in C),
+ * but implemented in a different way than all other
+ * BIFs.
+ */
+ return 1;
+ }
+
e.code[0] = Mod;
e.code[1] = Name;
e.code[2] = arity;
@@ -7137,7 +7398,7 @@ erts_current_reductions(Process *current, Process *p)
if (current != p) {
return 0;
} else if (current->fcalls < 0 && ERTS_PROC_GET_SAVED_CALLS_BUF(current)) {
- return -current->fcalls;
+ return current->fcalls + CONTEXT_REDS;
} else {
return REDS_IN(current) - current->fcalls;
}
diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c
index a6dce2d1d2..2bad3ab4c5 100644
--- a/erts/emulator/beam/beam_load.c
+++ b/erts/emulator/beam/beam_load.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2014. 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,6 +32,7 @@
#include "bif.h"
#include "external.h"
#include "beam_load.h"
+#include "beam_bp.h"
#include "big.h"
#include "erl_bits.h"
#include "beam_catches.h"
@@ -80,7 +81,7 @@ ErlDrvBinary* erts_gzinflate_buffer(char*, int);
typedef struct {
Uint value; /* Value of label (NULL if not known yet). */
- Uint patches; /* Index (into code buffer) to first location
+ Sint patches; /* Index (into code buffer) to first location
* which must be patched with the value of this label.
*/
#ifdef ERTS_SMP
@@ -104,7 +105,7 @@ typedef struct {
*/
typedef struct genop {
- int op; /* Opcode. */
+ unsigned int op; /* Opcode. */
int arity; /* Number of arguments. */
GenOpArg def_args[MAX_OPARGS]; /* Default buffer for arguments. */
GenOpArg* a; /* The arguments. */
@@ -282,11 +283,12 @@ typedef struct LoaderState {
byte* code_start; /* Start of code file. */
unsigned code_size; /* Size of code file. */
int specific_op; /* Specific opcode (-1 if not found). */
- int num_functions; /* Number of functions in module. */
- int num_labels; /* Number of labels. */
- int code_buffer_size; /* Size of code buffer in words. */
- BeamInstr* code; /* Loaded code. */
- int ci; /* Current index into loaded code. */
+ unsigned int num_functions; /* Number of functions in module. */
+ unsigned int num_labels; /* Number of labels. */
+ BeamCodeHeader* hdr; /* Loaded code header */
+ BeamInstr* codev; /* Loaded code buffer */
+ int codev_size; /* Size of code buffer in words. */
+ int ci; /* Current index into loaded code buffer. */
Label* labels;
StringPatch* string_patches; /* Linked list of position into string table to patch. */
BeamInstr catches; /* Linked list of catch_yf instructions. */
@@ -301,13 +303,13 @@ typedef struct LoaderState {
* Atom table.
*/
- int num_atoms; /* Number of atoms in atom table. */
+ unsigned int num_atoms; /* Number of atoms in atom table. */
Eterm* atom; /* Atom table. */
- int num_exps; /* Number of exports. */
+ unsigned int num_exps; /* Number of exports. */
ExportEntry* export; /* Pointer to export table. */
- int num_imports; /* Number of imports. */
+ unsigned int num_imports; /* Number of imports. */
ImportEntry* import; /* Import entry (translated information). */
/*
@@ -321,8 +323,8 @@ typedef struct LoaderState {
* Lambda table.
*/
- int num_lambdas; /* Number of lambdas in table. */
- int lambdas_allocated; /* Size of allocated lambda table. */
+ unsigned int num_lambdas; /* Number of lambdas in table. */
+ unsigned int lambdas_allocated; /* Size of allocated lambda table. */
Lambda* lambdas; /* Pointer to lambdas. */
Lambda def_lambdas[16]; /* Default storage for lambda table. */
char* lambda_error; /* Delayed missing 'FunT' error. */
@@ -331,8 +333,8 @@ typedef struct LoaderState {
* Literals (constant pool).
*/
- int num_literals; /* Number of literals in table. */
- int allocated_literals; /* Number of literal entries allocated. */
+ unsigned int num_literals; /* Number of literals in table. */
+ unsigned int allocated_literals; /* Number of literal entries allocated. */
Literal* literals; /* Array of literals. */
LiteralPatch* literal_patches; /* Operands that need to be patched. */
Uint total_literal_size; /* Total heap size for all literals. */
@@ -341,13 +343,13 @@ typedef struct LoaderState {
* Line table.
*/
BeamInstr* line_item; /* Line items from the BEAM file. */
- int num_line_items; /* Number of line items. */
+ unsigned int num_line_items;/* Number of line items. */
LineInstr* line_instr; /* Line instructions */
- int num_line_instrs; /* Maximum number of line instructions */
- int current_li; /* Current line instruction */
- int* func_line; /* Mapping from function to first line instr */
+ unsigned int num_line_instrs; /* Maximum number of line instructions */
+ unsigned int current_li; /* Current line instruction */
+ unsigned int* func_line; /* Mapping from function to first line instr */
Eterm* fname; /* List of file names */
- int num_fnames; /* Number of filenames in fname table */
+ unsigned int num_fnames; /* Number of filenames in fname table */
int loc_size; /* Size of location info in bytes (2/4) */
} LoaderState;
@@ -478,9 +480,9 @@ static void free_loader_state(Binary* magic);
static ErlHeapFragment* new_literal_fragment(Uint size);
static void free_literal_fragment(ErlHeapFragment*);
static void loader_state_dtor(Binary* magic);
-static Eterm insert_new_code(Process *c_p, ErtsProcLocks c_p_locks,
- Eterm group_leader, Eterm module,
- BeamInstr* code, Uint size);
+static Eterm stub_insert_new_code(Process *c_p, ErtsProcLocks c_p_locks,
+ Eterm group_leader, Eterm module,
+ BeamCodeHeader* code, Uint size);
static int init_iff_file(LoaderState* stp, byte* code, Uint size);
static int scan_iff_file(LoaderState* stp, Uint* chunk_types,
Uint num_types, Uint num_mandatory);
@@ -512,7 +514,7 @@ static GenOp* gen_get_map_element(LoaderState* stp, GenOpArg Fail, GenOpArg Src,
static int freeze_code(LoaderState* stp);
-static void final_touch(LoaderState* stp);
+static void final_touch(LoaderState* stp, struct erl_module_instance* inst_p);
static void short_file(int line, LoaderState* stp, unsigned needed);
static void load_printf(int line, LoaderState* context, char *fmt, ...);
static int transform_engine(LoaderState* st);
@@ -526,15 +528,15 @@ static void new_string_patch(LoaderState* stp, int pos);
static Uint new_literal(LoaderState* stp, Eterm** hpp, Uint heap_size);
static int genopargcompare(GenOpArg* a, GenOpArg* b);
static Eterm get_module_info(Process* p, ErtsCodeIndex code_ix,
- BeamInstr* code, Eterm module, Eterm what);
+ BeamCodeHeader*, Eterm module, Eterm what);
static Eterm exported_from_module(Process* p, ErtsCodeIndex code_ix,
Eterm mod);
-static Eterm functions_in_module(Process* p, BeamInstr* code);
-static Eterm attributes_for_module(Process* p, BeamInstr* code);
-static Eterm compilation_info_for_module(Process* p, BeamInstr* code);
-static Eterm md5_of_module(Process* p, BeamInstr* code);
-static Eterm has_native(BeamInstr* code);
-static Eterm native_addresses(Process* p, BeamInstr* code);
+static Eterm functions_in_module(Process* p, BeamCodeHeader*);
+static Eterm attributes_for_module(Process* p, BeamCodeHeader*);
+static Eterm compilation_info_for_module(Process* p, BeamCodeHeader*);
+static Eterm md5_of_module(Process* p, BeamCodeHeader*);
+static Eterm has_native(BeamCodeHeader*);
+static Eterm native_addresses(Process* p, BeamCodeHeader*);
int patch_funentries(Eterm Patchlist);
int patch(Eterm Addresses, Uint fe);
static int safe_mul(UWord a, UWord b, UWord* resp);
@@ -601,6 +603,7 @@ extern void check_allocated_block(Uint type, void *blk);
#define CHKBLK(TYPE,BLK) /* nothing */
#endif
+
Eterm
erts_prepare_loading(Binary* magic, Process *c_p, Eterm group_leader,
Eterm* modp, byte* code, Uint unloaded_size)
@@ -641,20 +644,27 @@ erts_prepare_loading(Binary* magic, Process *c_p, Eterm group_leader,
/*
* Initialize code area.
*/
- stp->code_buffer_size = 2048 + stp->num_functions;
- stp->code = (BeamInstr *) erts_alloc(ERTS_ALC_T_CODE,
- sizeof(BeamInstr) * stp->code_buffer_size);
+ stp->codev_size = 2048 + stp->num_functions;
+ stp->hdr = (BeamCodeHeader*) erts_alloc(ERTS_ALC_T_CODE,
+ (offsetof(BeamCodeHeader,functions)
+ + sizeof(BeamInstr) * stp->codev_size));
- stp->code[MI_NUM_FUNCTIONS] = stp->num_functions;
- stp->ci = MI_FUNCTIONS + stp->num_functions + 1;
+ stp->hdr->num_functions = stp->num_functions;
- stp->code[MI_ATTR_PTR] = 0;
- stp->code[MI_ATTR_SIZE] = 0;
- stp->code[MI_ATTR_SIZE_ON_HEAP] = 0;
- stp->code[MI_COMPILE_PTR] = 0;
- stp->code[MI_COMPILE_SIZE] = 0;
- stp->code[MI_COMPILE_SIZE_ON_HEAP] = 0;
- stp->code[MI_MD5_PTR] = 0;
+ /* Let the codev array start at functions[0] in order to index
+ * both function pointers and the loaded code itself that follows.
+ */
+ stp->codev = (BeamInstr*) &stp->hdr->functions;
+ stp->ci = stp->num_functions + 1;
+
+ stp->hdr->attr_ptr = NULL;
+ stp->hdr->attr_size = 0;
+ stp->hdr->attr_size_on_heap = 0;
+ stp->hdr->compile_ptr = NULL;
+ stp->hdr->compile_size = 0;
+ stp->hdr->compile_size_on_heap = 0;
+ stp->hdr->literal_area = NULL;
+ stp->hdr->md5_ptr = NULL;
/*
* Read the atom table.
@@ -757,8 +767,11 @@ Eterm
erts_finish_loading(Binary* magic, Process* c_p,
ErtsProcLocks c_p_locks, Eterm* modp)
{
- Eterm retval;
+ Eterm retval = NIL;
LoaderState* stp = ERTS_MAGIC_BIN_DATA(magic);
+ Module* mod_tab_p;
+ struct erl_module_instance* inst_p;
+ Uint size;
/*
* No other process may run since we will update the export
@@ -767,27 +780,89 @@ erts_finish_loading(Binary* magic, Process* c_p,
ERTS_SMP_LC_ASSERT(erts_initialized == 0 || erts_has_code_write_permission() ||
erts_smp_thr_progress_is_blocking());
-
/*
* Make current code for the module old and insert the new code
* as current. This will fail if there already exists old code
* for the module.
*/
+ mod_tab_p = erts_put_module(stp->module);
CHKBLK(ERTS_ALC_T_CODE,stp->code);
- retval = insert_new_code(c_p, c_p_locks, stp->group_leader, stp->module,
- stp->code, stp->loaded_size);
- if (retval != NIL) {
- goto load_error;
+ if (!stp->on_load) {
+ /*
+ * Normal case -- no -on_load() function.
+ */
+ retval = beam_make_current_old(c_p, c_p_locks, stp->module);
+ ASSERT(retval == NIL);
+ } else {
+ ErtsCodeIndex code_ix = erts_staging_code_ix();
+ Eterm module = stp->module;
+ int i, num_exps;
+
+ /*
+ * There is an -on_load() function. We will keep the current
+ * code, but we must turn off any tracing.
+ */
+ num_exps = export_list_size(code_ix);
+ for (i = 0; i < num_exps; i++) {
+ Export *ep = export_list(i, code_ix);
+ if (ep == NULL || ep->code[0] != module) {
+ continue;
+ }
+ if (ep->addressv[code_ix] == ep->code+3) {
+ if (ep->code[3] == (BeamInstr) em_apply_bif) {
+ continue;
+ } else if (ep->code[3] ==
+ (BeamInstr) BeamOp(op_i_generic_breakpoint)) {
+ ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
+ ASSERT(mod_tab_p->curr.num_traced_exports > 0);
+ erts_clear_export_break(mod_tab_p, ep->code+3);
+ ep->addressv[code_ix] = (BeamInstr *) ep->code[4];
+ ep->code[4] = 0;
+ }
+ ASSERT(ep->code[4] == 0);
+ }
+ }
+ ASSERT(mod_tab_p->curr.num_breakpoints == 0);
+ ASSERT(mod_tab_p->curr.num_traced_exports == 0);
+ }
+
+ /*
+ * Update module table.
+ */
+
+ size = stp->loaded_size;
+ erts_total_code_size += size;
+
+ if (!stp->on_load) {
+ inst_p = &mod_tab_p->curr;
+ } else {
+ mod_tab_p->on_load =
+ (struct erl_module_instance *)
+ erts_alloc(ERTS_ALC_T_PREPARED_CODE,
+ sizeof(struct erl_module_instance));
+ inst_p = mod_tab_p->on_load;
+ inst_p->nif = 0;
+ inst_p->num_breakpoints = 0;
+ inst_p->num_traced_exports = 0;
}
+ inst_p->code_hdr = stp->hdr;
+ inst_p->code_length = size;
+
+ /*
+ * Update ranges (used for finding a function from a PC value).
+ */
+
+ erts_update_ranges((BeamInstr*)inst_p->code_hdr, size);
+
/*
* Ready for the final touch: fixing the export table entries for
* exported and imported functions. This can't fail.
*/
CHKBLK(ERTS_ALC_T_CODE,stp->code);
- final_touch(stp);
+ final_touch(stp, inst_p);
/*
* Loading succeded.
@@ -799,7 +874,8 @@ erts_finish_loading(Binary* magic, Process* c_p,
debug_dump_code(stp->code,stp->ci);
#endif
#endif
- stp->code = NULL; /* Prevent code from being freed. */
+ stp->hdr = NULL; /* Prevent code from being freed. */
+ stp->codev = NULL;
*modp = stp->module;
/*
@@ -810,7 +886,6 @@ erts_finish_loading(Binary* magic, Process* c_p,
retval = am_on_load;
}
- load_error:
free_loader_state(magic);
return retval;
}
@@ -831,7 +906,8 @@ erts_alloc_loader_state(void)
stp->specific_op = -1;
stp->genop = NULL;
stp->atom = NULL;
- stp->code = NULL;
+ stp->hdr = NULL;
+ stp->codev = NULL;
stp->labels = NULL;
stp->import = NULL;
stp->export = NULL;
@@ -870,13 +946,30 @@ erts_module_for_prepared_code(Binary* magic)
return NIL;
}
stp = ERTS_MAGIC_BIN_DATA(magic);
- if (stp->code != 0) {
+ if (stp->hdr != 0) {
return stp->module;
} else {
return NIL;
}
}
+/*
+ * Return a non-zero value if the module has an on_load function,
+ * or 0 if it does not.
+ */
+
+Eterm
+erts_has_code_on_load(Binary* magic)
+{
+ LoaderState* stp;
+
+ if (ERTS_MAGIC_BIN_DESTRUCTOR(magic) != loader_state_dtor) {
+ return NIL;
+ }
+ stp = ERTS_MAGIC_BIN_DATA(magic);
+ return stp->on_load ? am_true : am_false;
+}
+
static void
free_loader_state(Binary* magic)
{
@@ -891,7 +984,7 @@ static ErlHeapFragment* new_literal_fragment(Uint size)
ErlHeapFragment* bp;
bp = (ErlHeapFragment*) ERTS_HEAP_ALLOC(ERTS_ALC_T_PREPARED_CODE,
ERTS_HEAP_FRAG_SIZE(size));
- ERTS_INIT_HEAP_FRAG(bp, size);
+ ERTS_INIT_HEAP_FRAG(bp, size, size);
return bp;
}
@@ -920,9 +1013,14 @@ loader_state_dtor(Binary* magic)
driver_free_binary(stp->bin);
stp->bin = 0;
}
- if (stp->code != 0) {
- erts_free(ERTS_ALC_T_CODE, stp->code);
- stp->code = 0;
+ if (stp->hdr != 0) {
+ if (stp->hdr->literal_area) {
+ erts_release_literal_area(stp->hdr->literal_area);
+ stp->hdr->literal_area = NULL;
+ }
+ erts_free(ERTS_ALC_T_CODE, stp->hdr);
+ stp->hdr = 0;
+ stp->codev = 0;
}
if (stp->labels != 0) {
erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) stp->labels);
@@ -994,9 +1092,9 @@ loader_state_dtor(Binary* magic)
}
static Eterm
-insert_new_code(Process *c_p, ErtsProcLocks c_p_locks,
- Eterm group_leader, Eterm module, BeamInstr* code,
- Uint size)
+stub_insert_new_code(Process *c_p, ErtsProcLocks c_p_locks,
+ Eterm group_leader, Eterm module,
+ BeamCodeHeader* code_hdr, Uint size)
{
Module* modp;
Eterm retval;
@@ -1016,7 +1114,7 @@ insert_new_code(Process *c_p, ErtsProcLocks c_p_locks,
erts_total_code_size += size;
modp = erts_put_module(module);
- modp->curr.code = code;
+ modp->curr.code_hdr = code_hdr;
modp->curr.code_length = size;
modp->curr.catches = BEAM_CATCHES_NIL; /* Will be filled in later. */
@@ -1024,7 +1122,7 @@ insert_new_code(Process *c_p, ErtsProcLocks c_p_locks,
* Update ranges (used for finding a function from a PC value).
*/
- erts_update_ranges(code, size);
+ erts_update_ranges((BeamInstr*)modp->curr.code_hdr, size);
return NIL;
}
@@ -1242,7 +1340,7 @@ verify_chunks(LoaderState* stp)
static int
load_atom_table(LoaderState* stp)
{
- int i;
+ unsigned int i;
GetInt(stp, 4, stp->num_atoms);
stp->num_atoms++;
@@ -1287,13 +1385,13 @@ load_atom_table(LoaderState* stp)
static int
load_import_table(LoaderState* stp)
{
- int i;
+ unsigned int i;
GetInt(stp, 4, stp->num_imports);
stp->import = erts_alloc(ERTS_ALC_T_PREPARED_CODE,
stp->num_imports * sizeof(ImportEntry));
for (i = 0; i < stp->num_imports; i++) {
- int n;
+ unsigned int n;
Eterm mod;
Eterm func;
Uint arity;
@@ -1301,17 +1399,17 @@ load_import_table(LoaderState* stp)
GetInt(stp, 4, n);
if (n >= stp->num_atoms) {
- LoadError2(stp, "import entry %d: invalid atom number %d", i, n);
+ LoadError2(stp, "import entry %u: invalid atom number %u", i, n);
}
mod = stp->import[i].module = stp->atom[n];
GetInt(stp, 4, n);
if (n >= stp->num_atoms) {
- LoadError2(stp, "import entry %d: invalid atom number %d", i, n);
+ LoadError2(stp, "import entry %u: invalid atom number %u", i, n);
}
func = stp->import[i].function = stp->atom[n];
GetInt(stp, 4, arity);
if (arity > MAX_REG) {
- LoadError2(stp, "import entry %d: invalid arity %d", i, arity);
+ LoadError2(stp, "import entry %u: invalid arity %d", i, arity);
}
stp->import[i].arity = arity;
stp->import[i].patches = 0;
@@ -1339,12 +1437,12 @@ load_import_table(LoaderState* stp)
static int
read_export_table(LoaderState* stp)
{
- int i;
+ unsigned int i;
BeamInstr* address;
GetInt(stp, 4, stp->num_exps);
if (stp->num_exps > stp->num_functions) {
- LoadError2(stp, "%d functions exported; only %d functions defined",
+ LoadError2(stp, "%u functions exported; only %u functions defined",
stp->num_exps, stp->num_functions);
}
stp->export
@@ -1362,18 +1460,18 @@ read_export_table(LoaderState* stp)
stp->export[i].function = func;
GetInt(stp, 4, arity);
if (arity > MAX_REG) {
- LoadError2(stp, "export table entry %d: absurdly high arity %d", i, arity);
+ LoadError2(stp, "export table entry %u: absurdly high arity %u", i, arity);
}
stp->export[i].arity = arity;
GetInt(stp, 4, n);
if (n >= stp->num_labels) {
- LoadError3(stp, "export table entry %d: invalid label %d (highest defined label is %d)", i, n, stp->num_labels);
+ LoadError3(stp, "export table entry %u: invalid label %u (highest defined label is %u)", i, n, stp->num_labels);
}
value = stp->labels[n].value;
if (value == 0) {
- LoadError2(stp, "export table entry %d: label %d not resolved", i, n);
+ LoadError2(stp, "export table entry %u: label %u not resolved", i, n);
}
- stp->export[i].address = address = stp->code + value;
+ stp->export[i].address = address = stp->codev + value;
/*
* Find out if there is a BIF with the same name.
@@ -1392,7 +1490,7 @@ read_export_table(LoaderState* stp)
* any other functions that walk through all local functions.
*/
- if (stp->labels[n].patches) {
+ if (stp->labels[n].patches >= 0) {
LoadError3(stp, "there are local calls to the stub for "
"the BIF %T:%T/%d",
stp->module, func, arity);
@@ -1432,7 +1530,7 @@ is_bif(Eterm mod, Eterm func, unsigned arity)
static int
read_lambda_table(LoaderState* stp)
{
- int i;
+ unsigned int i;
GetInt(stp, 4, stp->num_lambdas);
if (stp->num_lambdas > stp->lambdas_allocated) {
@@ -1452,12 +1550,12 @@ read_lambda_table(LoaderState* stp)
GetAtom(stp, n, stp->lambdas[i].function);
GetInt(stp, 4, arity);
if (arity > MAX_REG) {
- LoadError2(stp, "lambda entry %d: absurdly high arity %d", i, arity);
+ LoadError2(stp, "lambda entry %u: absurdly high arity %u", i, arity);
}
stp->lambdas[i].arity = arity;
GetInt(stp, 4, n);
if (n >= stp->num_labels) {
- LoadError3(stp, "lambda entry %d: invalid label %d (highest defined label is %d)",
+ LoadError3(stp, "lambda entry %u: invalid label %u (highest defined label is %u)",
i, n, stp->num_labels);
}
stp->lambdas[i].label = n;
@@ -1478,7 +1576,7 @@ read_lambda_table(LoaderState* stp)
static int
read_literal_table(LoaderState* stp)
{
- int i;
+ unsigned int i;
uLongf uncompressed_sz;
byte* uncompressed = 0;
@@ -1500,7 +1598,7 @@ read_literal_table(LoaderState* stp)
}
for (i = 0; i < stp->num_literals; i++) {
- int sz;
+ Uint sz;
Sint heap_size;
byte* p;
Eterm val;
@@ -1509,17 +1607,17 @@ read_literal_table(LoaderState* stp)
GetInt(stp, 4, sz); /* Size of external term format. */
GetString(stp, p, sz);
if ((heap_size = erts_decode_ext_size(p, sz)) < 0) {
- LoadError1(stp, "literal %d: bad external format", i);
+ LoadError1(stp, "literal %u: bad external format", i);
}
if (heap_size > 0) {
- erts_factory_message_init(&factory, NULL, NULL,
- new_literal_fragment(heap_size));
+ erts_factory_heap_frag_init(&factory,
+ new_literal_fragment(heap_size));
factory.alloc_type = ERTS_ALC_T_PREPARED_CODE;
- val = erts_decode_ext(&factory, &p);
+ val = erts_decode_ext(&factory, &p, 0);
if (is_non_value(val)) {
- LoadError1(stp, "literal %d: bad external format", i);
+ LoadError1(stp, "literal %u: bad external format", i);
}
erts_factory_close(&factory);
stp->literals[i].heap_frags = factory.heap_frags;
@@ -1527,9 +1625,9 @@ read_literal_table(LoaderState* stp)
}
else {
erts_factory_dummy_init(&factory);
- val = erts_decode_ext(&factory, &p);
+ val = erts_decode_ext(&factory, &p, 0);
if (is_non_value(val)) {
- LoadError1(stp, "literal %d: bad external format", i);
+ LoadError1(stp, "literal %u: bad external format", i);
}
ASSERT(is_immed(val));
stp->literals[i].heap_frags = NULL;
@@ -1552,9 +1650,9 @@ read_line_table(LoaderState* stp)
{
unsigned version;
ERTS_DECLARE_DUMMY(unsigned flags);
- int num_line_items;
+ unsigned int num_line_items;
BeamInstr* lp;
- int i;
+ unsigned int i;
BeamInstr fname_index;
BeamInstr tag;
@@ -1633,7 +1731,7 @@ read_line_table(LoaderState* stp)
}
} else if (tag == TAG_a) {
if (val > stp->num_fnames) {
- LoadError2(stp, "file index overflow (%d/%d)",
+ LoadError2(stp, "file index overflow (%u/%u)",
val, stp->num_fnames);
}
fname_index = val;
@@ -1669,9 +1767,9 @@ read_line_table(LoaderState* stp)
stp->num_line_instrs *
sizeof(LineInstr));
stp->current_li = 0;
- stp->func_line = (int *) erts_alloc(ERTS_ALC_T_PREPARED_CODE,
- stp->num_functions *
- sizeof(int));
+ stp->func_line = (unsigned int *) erts_alloc(ERTS_ALC_T_PREPARED_CODE,
+ stp->num_functions *
+ sizeof(int));
return 1;
@@ -1695,6 +1793,10 @@ read_code_header(LoaderState* stp)
*/
GetInt(stp, 4, head_size);
+ if (head_size > stp->file_left) {
+ LoadError2(stp, "invalid code header size %u; bytes left %u",
+ head_size, stp->file_left);
+ }
stp->code_start = stp->file_p + head_size;
stp->code_size = stp->file_left - head_size;
stp->file_left = head_size;
@@ -1735,7 +1837,7 @@ read_code_header(LoaderState* stp)
stp->num_labels * sizeof(Label));
for (i = 0; i < stp->num_labels; i++) {
stp->labels[i].value = 0;
- stp->labels[i].patches = 0;
+ stp->labels[i].patches = -1;
#ifdef ERTS_SMP
stp->labels[i].looprec_targeted = 0;
#endif
@@ -1754,13 +1856,14 @@ read_code_header(LoaderState* stp)
} else {}
#define CodeNeed(w) do { \
- ASSERT(ci <= code_buffer_size); \
- if (code_buffer_size < ci+(w)) { \
- code_buffer_size = 2*ci+(w); \
- stp->code = code = \
- (BeamInstr *) erts_realloc(ERTS_ALC_T_CODE, \
- (void *) code, \
- code_buffer_size * sizeof(BeamInstr)); \
+ ASSERT(ci <= codev_size); \
+ if (codev_size < ci+(w)) { \
+ codev_size = 2*ci+(w); \
+ stp->hdr = (BeamCodeHeader*) erts_realloc(ERTS_ALC_T_CODE, \
+ (void *) stp->hdr, \
+ (offsetof(BeamCodeHeader,functions) \
+ + codev_size * sizeof(BeamInstr))); \
+ code = stp->codev = (BeamInstr*) &stp->hdr->functions; \
} \
} while (0)
@@ -1776,7 +1879,7 @@ load_code(LoaderState* stp)
int arg; /* Number of current argument. */
int num_specific; /* Number of specific ops for current. */
BeamInstr* code;
- int code_buffer_size;
+ int codev_size;
int specific;
Uint last_label = 0; /* Number of last label. */
Uint function_number = 0;
@@ -1793,23 +1896,23 @@ load_code(LoaderState* stp)
FUNC_INFO_SZ = 5
};
- code = stp->code;
- code_buffer_size = stp->code_buffer_size;
+ code = stp->codev;
+ codev_size = stp->codev_size;
ci = stp->ci;
for (;;) {
- int new_op;
+ unsigned int new_op;
GenOp* tmp_op;
- ASSERT(ci <= code_buffer_size);
+ ASSERT(ci <= codev_size);
get_next_instr:
GetByte(stp, new_op);
if (new_op >= NUM_GENERIC_OPS) {
- LoadError1(stp, "invalid opcode %d", new_op);
+ LoadError1(stp, "invalid opcode %u", new_op);
}
if (gen_opc[new_op].name[0] == '\0') {
- LoadError1(stp, "invalid opcode %d", new_op);
+ LoadError1(stp, "invalid opcode %u", new_op);
}
@@ -1847,9 +1950,7 @@ load_code(LoaderState* stp)
case TAG_o:
break;
case TAG_x:
- if (last_op->a[arg].val == 0) {
- last_op->a[arg].type = TAG_r;
- } else if (last_op->a[arg].val >= MAX_REG) {
+ if (last_op->a[arg].val >= MAX_REG) {
LoadError1(stp, "invalid x register number: %u",
last_op->a[arg].val);
}
@@ -1894,15 +1995,14 @@ load_code(LoaderState* stp)
*/
{
Eterm* hp;
-/* XXX:PaN - Halfword should use ARCH_64 variant instead */
-#if !defined(ARCH_64) || HALFWORD_HEAP
+#if !defined(ARCH_64)
Uint high, low;
# endif
last_op->a[arg].val = new_literal(stp, &hp,
FLOAT_SIZE_OBJECT);
hp[0] = HEADER_FLONUM;
last_op->a[arg].type = TAG_q;
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
GetInt(stp, 8, hp[1]);
# else
GetInt(stp, 4, high);
@@ -1998,42 +2098,47 @@ load_code(LoaderState* stp)
ASSERT(arity == last_op->arity);
do_transform:
- if (stp->genop == NULL) {
- last_op_next = NULL;
- goto get_next_instr;
- }
-
+ ASSERT(stp->genop != NULL);
if (gen_opc[stp->genop->op].transform != -1) {
- int need;
- tmp_op = stp->genop;
-
- for (need = gen_opc[stp->genop->op].min_window-1; need > 0; need--) {
- if (tmp_op == NULL) {
- goto get_next_instr;
- }
- tmp_op = tmp_op->next;
+ if (stp->genop->next == NULL) {
+ /*
+ * Simple heuristic: Most transformations requires
+ * at least two instructions, so make sure that
+ * there are. That will reduce the number of
+ * TE_SHORT_WINDOWs.
+ */
+ goto get_next_instr;
}
switch (transform_engine(stp)) {
case TE_FAIL:
- last_op_next = NULL;
- last_op = NULL;
+ /*
+ * No transformation found. stp->genop != NULL and
+ * last_op_next is still valid. Go ahead and load
+ * the instruction.
+ */
break;
case TE_OK:
+ /*
+ * Some transformation was applied. last_op_next is
+ * no longer valid and stp->genop may be NULL.
+ * Try to transform again.
+ */
+ if (stp->genop == NULL) {
+ last_op_next = &stp->genop;
+ goto get_next_instr;
+ }
last_op_next = NULL;
- last_op = NULL;
goto do_transform;
case TE_SHORT_WINDOW:
- last_op_next = NULL;
- last_op = NULL;
+ /*
+ * No transformation applied. stp->genop != NULL and
+ * last_op_next is still valid. Fetch a new instruction
+ * before trying the transformation again.
+ */
goto get_next_instr;
}
}
- if (stp->genop == NULL) {
- last_op_next = NULL;
- goto get_next_instr;
- }
-
/*
* From the collected generic instruction, find the specific
* instruction.
@@ -2056,7 +2161,42 @@ load_code(LoaderState* stp)
if (((opc[specific].mask[0] & mask[0]) == mask[0]) &&
((opc[specific].mask[1] & mask[1]) == mask[1]) &&
((opc[specific].mask[2] & mask[2]) == mask[2])) {
- break;
+
+ if (!opc[specific].involves_r) {
+ break; /* No complications - match */
+ }
+
+ /*
+ * The specific operation uses the 'r' operand,
+ * which is shorthand for x(0). Now things
+ * get complicated. First we must check whether
+ * all operands that should be of type 'r' use
+ * x(0) (as opposed to some other X register).
+ */
+ for (arg = 0; arg < arity; arg++) {
+ if (opc[specific].involves_r & (1 << arg) &&
+ tmp_op->a[arg].type == TAG_x) {
+ if (tmp_op->a[arg].val != 0) {
+ break; /* Other X register than 0 */
+ }
+ }
+ }
+
+ if (arg == arity) {
+ /*
+ * All 'r' operands use x(0) in the generic
+ * operation. That means a match. Now we
+ * will need to rewrite the generic instruction
+ * to actually use 'r' instead of 'x(0)'.
+ */
+ for (arg = 0; arg < arity; arg++) {
+ if (opc[specific].involves_r & (1 << arg) &&
+ tmp_op->a[arg].type == TAG_x) {
+ tmp_op->a[arg].type = TAG_r;
+ }
+ }
+ break; /* Match */
+ }
}
specific++;
}
@@ -2167,14 +2307,11 @@ load_code(LoaderState* stp)
break;
case 's': /* Any source (tagged constant or register) */
switch (tag) {
- case TAG_r:
- code[ci++] = make_rreg();
- break;
case TAG_x:
- code[ci++] = make_xreg(tmp_op->a[arg].val);
+ code[ci++] = make_loader_x_reg(tmp_op->a[arg].val);
break;
case TAG_y:
- code[ci++] = make_yreg(tmp_op->a[arg].val);
+ code[ci++] = make_loader_y_reg(tmp_op->a[arg].val);
break;
case TAG_i:
code[ci++] = (BeamInstr) make_small((Uint)tmp_op->a[arg].val);
@@ -2185,6 +2322,10 @@ load_code(LoaderState* stp)
case TAG_n:
code[ci++] = NIL;
break;
+ case TAG_q:
+ new_literal_patch(stp, ci);
+ code[ci++] = tmp_op->a[arg].val;
+ break;
default:
LoadError1(stp, "bad tag %d for general source",
tmp_op->a[arg].type);
@@ -2193,14 +2334,11 @@ load_code(LoaderState* stp)
break;
case 'd': /* Destination (x(0), x(N), y(N) */
switch (tag) {
- case TAG_r:
- code[ci++] = make_rreg();
- break;
case TAG_x:
- code[ci++] = make_xreg(tmp_op->a[arg].val);
+ code[ci++] = tmp_op->a[arg].val * sizeof(Eterm);
break;
case TAG_y:
- code[ci++] = make_yreg(tmp_op->a[arg].val);
+ code[ci++] = tmp_op->a[arg].val * sizeof(Eterm) + 1;
break;
default:
LoadError1(stp, "bad tag %d for destination",
@@ -2244,7 +2382,7 @@ load_code(LoaderState* stp)
VerifyTag(stp, tag, TAG_u);
last_label = tmp_op->a[arg].val;
if (!(0 < last_label && last_label < stp->num_labels)) {
- LoadError2(stp, "invalid label num %d (0 < label < %d)",
+ LoadError2(stp, "invalid label num %u (0 < label < %u)",
tmp_op->a[arg].val, stp->num_labels);
}
if (stp->labels[last_label].value != 0) {
@@ -2357,20 +2495,13 @@ load_code(LoaderState* stp)
stp->labels[tmp_op->a[arg].val].patches = ci;
ci++;
break;
- case TAG_r:
- CodeNeed(1);
- code[ci++] = (R_REG_DEF << _TAG_PRIMARY_SIZE) |
- TAG_PRIMARY_HEADER;
- break;
case TAG_x:
CodeNeed(1);
- code[ci++] = (tmp_op->a[arg].val << _TAG_IMMED1_SIZE) |
- (X_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER;
+ code[ci++] = make_loader_x_reg(tmp_op->a[arg].val);
break;
case TAG_y:
CodeNeed(1);
- code[ci++] = (tmp_op->a[arg].val << _TAG_IMMED1_SIZE) |
- (Y_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER;
+ code[ci++] = make_loader_y_reg(tmp_op->a[arg].val);
break;
case TAG_n:
CodeNeed(1);
@@ -2393,10 +2524,9 @@ load_code(LoaderState* stp)
switch (stp->specific_op) {
case op_i_func_info_IaaI:
{
- Uint offset;
-
+ Sint offset;
if (function_number >= stp->num_functions) {
- LoadError1(stp, "too many functions in module (header said %d)",
+ LoadError1(stp, "too many functions in module (header said %u)",
stp->num_functions);
}
@@ -2436,15 +2566,15 @@ load_code(LoaderState* stp)
stp->arity = code[ci-1];
ASSERT(stp->labels[last_label].value == ci - FUNC_INFO_SZ);
- offset = MI_FUNCTIONS + function_number;
- code[offset] = stp->labels[last_label].patches;
+ stp->hdr->functions[function_number] = (BeamInstr*) stp->labels[last_label].patches;
+ offset = function_number;
stp->labels[last_label].patches = offset;
function_number++;
if (stp->arity > MAX_ARG) {
LoadError1(stp, "too many arguments: %d", stp->arity);
}
#ifdef DEBUG
- ASSERT(stp->labels[0].patches == 0); /* Should not be referenced. */
+ ASSERT(stp->labels[0].patches < 0); /* Should not be referenced. */
for (i = 1; i < stp->num_labels; i++) {
ASSERT(stp->labels[i].patches < ci);
}
@@ -2458,7 +2588,6 @@ load_code(LoaderState* stp)
stp->on_load = ci;
break;
case op_bs_put_string_II:
- case op_i_bs_match_string_rfII:
case op_i_bs_match_string_xfII:
new_string_patch(stp, ci-1);
break;
@@ -2476,14 +2605,14 @@ load_code(LoaderState* stp)
if (stp->line_item) {
BeamInstr item = code[ci-1];
BeamInstr loc;
- int li;
+ unsigned int li;
if (item >= stp->num_line_items) {
- LoadError2(stp, "line instruction index overflow (%d/%d)",
+ LoadError2(stp, "line instruction index overflow (%u/%u)",
item, stp->num_line_items);
}
li = stp->current_li;
if (li >= stp->num_line_instrs) {
- LoadError2(stp, "line instruction table overflow (%d/%d)",
+ LoadError2(stp, "line instruction table overflow (%u/%u)",
li, stp->num_line_instrs);
}
loc = stp->line_item[item];
@@ -2515,7 +2644,11 @@ load_code(LoaderState* stp)
* End of code found.
*/
case op_int_code_end:
- stp->code_buffer_size = code_buffer_size;
+ if (function_number != stp->num_functions) {
+ LoadError2(stp, "too few functions (%u) in module (header said %u)",
+ function_number, stp->num_functions);
+ }
+ stp->codev_size = codev_size;
stp->ci = ci;
stp->function = THE_NON_VALUE;
stp->genop = NULL;
@@ -2530,7 +2663,10 @@ load_code(LoaderState* stp)
{
GenOp* next = stp->genop->next;
FREE_GENOP(stp, stp->genop);
- stp->genop = next;
+ if ((stp->genop = next) == NULL) {
+ last_op_next = &stp->genop;
+ goto get_next_instr;
+ }
goto do_transform;
}
}
@@ -2674,12 +2810,18 @@ mixed_types(LoaderState* stp, GenOpArg Size, GenOpArg* Rest)
}
static int
-same_label(LoaderState* stp, GenOpArg Target, GenOpArg Label)
+is_killed_apply(LoaderState* stp, GenOpArg Reg, GenOpArg Live)
{
- return Target.type = TAG_f && Label.type == TAG_u &&
- Target.val == Label.val;
+ return Reg.type == TAG_x && Live.type == TAG_u &&
+ Live.val+2 <= Reg.val;
}
+static int
+is_killed(LoaderState* stp, GenOpArg Reg, GenOpArg Live)
+{
+ return Reg.type == TAG_x && Live.type == TAG_u &&
+ Live.val <= Reg.val;
+}
/*
* Generate an instruction for element/2.
@@ -2696,17 +2838,17 @@ gen_element(LoaderState* stp, GenOpArg Fail, GenOpArg Index,
op->next = NULL;
if (Index.type == TAG_i && Index.val > 0 &&
- (Tuple.type == TAG_r || Tuple.type == TAG_x || Tuple.type == TAG_y)) {
+ (Tuple.type == TAG_x || Tuple.type == TAG_y)) {
op->op = genop_i_fast_element_4;
- op->a[0] = Tuple;
- op->a[1] = Fail;
+ op->a[0] = Fail;
+ op->a[1] = Tuple;
op->a[2].type = TAG_u;
op->a[2].val = Index.val;
op->a[3] = Dst;
} else {
op->op = genop_i_element_4;
- op->a[0] = Tuple;
- op->a[1] = Fail;
+ op->a[0] = Fail;
+ op->a[1] = Tuple;
op->a[2] = Index;
op->a[3] = Dst;
}
@@ -2832,23 +2974,16 @@ gen_get_integer2(LoaderState* stp, GenOpArg Fail, GenOpArg Ms, GenOpArg Live,
goto generic;
}
} else {
- GenOp* op2;
- NEW_GENOP(stp, op2);
-
- op->op = genop_i_fetch_2;
- op->arity = 2;
- op->a[0] = Ms;
- op->a[1] = Size;
- op->next = op2;
-
- op2->op = genop_i_bs_get_integer_4;
- op2->arity = 4;
- op2->a[0] = Fail;
- op2->a[1] = Live;
- op2->a[2].type = TAG_u;
- op2->a[2].val = (Unit.val << 3) | Flags.val;
- op2->a[3] = Dst;
- op2->next = NULL;
+ op->op = genop_i_bs_get_integer_6;
+ op->arity = 6;
+ op->a[0] = Fail;
+ op->a[1] = Live;
+ op->a[2].type = TAG_u;
+ op->a[2].val = (Unit.val << 3) | Flags.val;
+ op->a[3] = Ms;
+ op->a[4] = Size;
+ op->a[5] = Dst;
+ op->next = NULL;
return op;
}
op->next = NULL;
@@ -3272,14 +3407,14 @@ gen_literal_timeout(LoaderState* stp, GenOpArg Fail, GenOpArg Time)
op->a[1].type = TAG_u;
if (Time.type == TAG_i && (timeout = Time.val) >= 0 &&
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
(timeout >> 32) == 0
#else
1
#endif
) {
op->a[1].val = timeout;
-#if !defined(ARCH_64) || HALFWORD_HEAP
+#if !defined(ARCH_64)
} else if (Time.type == TAG_q) {
Eterm big;
@@ -3296,7 +3431,7 @@ gen_literal_timeout(LoaderState* stp, GenOpArg Fail, GenOpArg Time)
}
#endif
} else {
-#if !defined(ARCH_64) || HALFWORD_HEAP
+#if !defined(ARCH_64)
error:
#endif
op->op = genop_i_wait_error_0;
@@ -3319,14 +3454,14 @@ gen_literal_timeout_locked(LoaderState* stp, GenOpArg Fail, GenOpArg Time)
op->a[1].type = TAG_u;
if (Time.type == TAG_i && (timeout = Time.val) >= 0 &&
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
(timeout >> 32) == 0
#else
1
#endif
) {
op->a[1].val = timeout;
-#if !defined(ARCH_64) || HALFWORD_HEAP
+#if !defined(ARCH_64)
} else if (Time.type == TAG_q) {
Eterm big;
@@ -3343,7 +3478,7 @@ gen_literal_timeout_locked(LoaderState* stp, GenOpArg Fail, GenOpArg Time)
}
#endif
} else {
-#if !defined(ARCH_64) || HALFWORD_HEAP
+#if !defined(ARCH_64)
error:
#endif
op->op = genop_i_wait_error_locked_0;
@@ -3899,9 +4034,7 @@ gen_make_fun2(LoaderState* stp, GenOpArg idx)
/*
* Rewrite gc_bifs with one parameter (the common case). Utilized
* in ops.tab to rewrite instructions calling bif's in guards
- * to use a garbage collecting implementation. The instructions
- * are sometimes once again rewritten to handle literals (putting the
- * parameter in the mostly unused r[0] before the instruction is executed).
+ * to use a garbage collecting implementation.
*/
static GenOp*
gen_guard_bif1(LoaderState* stp, GenOpArg Fail, GenOpArg Live, GenOpArg Bif,
@@ -3956,10 +4089,6 @@ gen_guard_bif1(LoaderState* stp, GenOpArg Fail, GenOpArg Live, GenOpArg Bif,
/*
* This is used by the ops.tab rule that rewrites gc_bifs with two parameters.
- * The instruction returned is then again rewritten to an i_load instruction
- * followed by i_gc_bif2_jIId, to handle literals properly.
- * As opposed to the i_gc_bif1_jIsId, the instruction i_gc_bif2_jIId is
- * always rewritten, regardless of if there actually are any literals.
*/
static GenOp*
gen_guard_bif2(LoaderState* stp, GenOpArg Fail, GenOpArg Live, GenOpArg Bif,
@@ -3986,23 +4115,19 @@ gen_guard_bif2(LoaderState* stp, GenOpArg Fail, GenOpArg Live, GenOpArg Bif,
op->a[2].val = stp->import[Bif.val].arity;
return op;
}
- op->op = genop_ii_gc_bif2_6;
+ op->op = genop_i_gc_bif2_6;
op->arity = 6;
op->a[0] = Fail;
op->a[1].type = TAG_u;
- op->a[2] = S1;
- op->a[3] = S2;
- op->a[4] = Live;
+ op->a[2] = Live;
+ op->a[3] = S1;
+ op->a[4] = S2;
op->a[5] = Dst;
return op;
}
/*
* This is used by the ops.tab rule that rewrites gc_bifs with three parameters.
- * The instruction returned is then again rewritten to a move instruction that
- * uses r[0] for temp storage, followed by an i_load instruction,
- * followed by i_gc_bif3_jIsId, to handle literals properly. Rewriting
- * always occur, as with the gc_bif2 counterpart.
*/
static GenOp*
gen_guard_bif3(LoaderState* stp, GenOpArg Fail, GenOpArg Live, GenOpArg Bif,
@@ -4033,10 +4158,10 @@ gen_guard_bif3(LoaderState* stp, GenOpArg Fail, GenOpArg Live, GenOpArg Bif,
op->arity = 7;
op->a[0] = Fail;
op->a[1].type = TAG_u;
- op->a[2] = S1;
- op->a[3] = S2;
- op->a[4] = S3;
- op->a[5] = Live;
+ op->a[2] = Live;
+ op->a[3] = S1;
+ op->a[4] = S2;
+ op->a[5] = S3;
op->a[6] = Dst;
op->next = NULL;
return op;
@@ -4258,6 +4383,53 @@ gen_get_map_element(LoaderState* stp, GenOpArg Fail, GenOpArg Src,
return op;
}
+static int
+hash_internal_genop_arg(LoaderState* stp, GenOpArg Key, Uint32* hx)
+{
+ switch (Key.type) {
+ case TAG_a:
+ *hx = atom_tab(atom_val(Key.val))->slot.bucket.hvalue;
+ return 1;
+ case TAG_i:
+ *hx = Key.val;
+ return 1;
+ case TAG_n:
+ *hx = make_internal_hash(NIL);
+ return 1;
+ case TAG_q:
+ *hx = make_internal_hash(stp->literals[Key.val].term);
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+
+static GenOp*
+gen_get(LoaderState* stp, GenOpArg Src, GenOpArg Dst)
+{
+ GenOp* op;
+ Uint32 hx = 0;
+
+ NEW_GENOP(stp, op);
+ op->next = NULL;
+ if (hash_internal_genop_arg(stp, Src, &hx)) {
+ op->arity = 3;
+ op->op = genop_i_get_hash_3;
+ op->a[0] = Src;
+ op->a[1].type = TAG_u;
+ op->a[1].val = (BeamInstr) hx;
+ op->a[2] = Dst;
+ } else {
+ op->arity = 2;
+ op->op = genop_i_get_2;
+ op->a[0] = Src;
+ op->a[1] = Dst;
+ }
+ return op;
+}
+
+
static GenOp*
gen_get_map_elements(LoaderState* stp, GenOpArg Fail, GenOpArg Src,
GenOpArg Size, GenOpArg* Rest)
@@ -4270,6 +4442,7 @@ gen_get_map_elements(LoaderState* stp, GenOpArg Fail, GenOpArg Src,
int good_hash;
#endif
+ ERTS_UNDEF(hx, 0);
ASSERT(Size.type == TAG_u);
NEW_GENOP(stp, op);
@@ -4323,7 +4496,7 @@ gen_has_map_fields(LoaderState* stp, GenOpArg Fail, GenOpArg Src,
for (i = 0; i < n; i++) {
op->a[3+2*i] = Rest[i];
op->a[3+2*i+1].type = TAG_x;
- op->a[3+2*i+1].val = 0; /* x(0); normally not used */
+ op->a[3+2*i+1].val = SCRATCH_X_REG; /* Ignore result */
}
return op;
}
@@ -4336,8 +4509,8 @@ gen_has_map_fields(LoaderState* stp, GenOpArg Fail, GenOpArg Src,
static int
freeze_code(LoaderState* stp)
{
- BeamInstr* code = stp->code;
- Uint *literal_end = NULL;
+ BeamCodeHeader* code_hdr = stp->hdr;
+ BeamInstr* codev = (BeamInstr*) &stp->hdr->functions;
int i;
byte* str_table;
unsigned strtab_size = stp->chunks[STR_CHUNK].size;
@@ -4362,77 +4535,80 @@ freeze_code(LoaderState* stp)
if (stp->line_instr == 0) {
line_size = 0;
} else {
- line_size = (MI_LINE_FUNC_TAB + (stp->num_functions + 1) +
- (stp->current_li+1) + stp->num_fnames) *
- sizeof(Eterm) + (stp->current_li+1) * stp->loc_size;
+ line_size = (offsetof(BeamCodeLineTab,func_tab)
+ + (stp->num_functions + 1) * sizeof(BeamInstr**) /* func_tab */
+ + (stp->current_li + 1) * sizeof(BeamInstr*) /* line items */
+ + stp->num_fnames * sizeof(Eterm) /* fname table */
+ + (stp->current_li + 1) * stp->loc_size); /* loc_tab */
}
- size = (stp->ci * sizeof(BeamInstr)) +
- (stp->total_literal_size * sizeof(Eterm)) +
+ size = offsetof(BeamCodeHeader,functions) + (stp->ci * sizeof(BeamInstr)) +
strtab_size + attr_size + compile_size + MD5_SIZE + line_size;
/*
* Move the code to its final location.
*/
- code = (BeamInstr *) erts_realloc(ERTS_ALC_T_CODE, (void *) code, size);
- CHKBLK(ERTS_ALC_T_CODE,code);
+ code_hdr = (BeamCodeHeader*) erts_realloc(ERTS_ALC_T_CODE, (void *) code_hdr, size);
+ codev = (BeamInstr*) &code_hdr->functions;
+ CHKBLK(ERTS_ALC_T_CODE,code_hdr);
/*
* Place a pointer to the op_int_code_end instruction in the
* function table in the beginning of the file.
*/
- code[MI_FUNCTIONS+stp->num_functions] = (BeamInstr) (code + stp->ci - 1);
- CHKBLK(ERTS_ALC_T_CODE,code);
+ code_hdr->functions[stp->num_functions] = (codev + stp->ci - 1);
+ CHKBLK(ERTS_ALC_T_CODE,code_hdr);
/*
* Store the pointer to the on_load function.
*/
if (stp->on_load) {
- code[MI_ON_LOAD_FUNCTION_PTR] = (BeamInstr) (code + stp->on_load);
+ code_hdr->on_load_function_ptr = codev + stp->on_load;
} else {
- code[MI_ON_LOAD_FUNCTION_PTR] = 0;
+ code_hdr->on_load_function_ptr = NULL;
}
- CHKBLK(ERTS_ALC_T_CODE,code);
+ CHKBLK(ERTS_ALC_T_CODE,code_hdr);
- literal_end = (Uint *) (code+stp->ci);
/*
- * Place the literal heap directly after the code and fix up all
- * instructions that refer to it.
+ * Place the literals in their own allocated heap (for fast range check)
+ * and fix up all instructions that refer to it.
*/
{
- Uint* ptr;
- Uint* low;
- Uint* high;
+ Eterm* ptr;
LiteralPatch* lp;
ErlOffHeap code_off_heap;
+ ErtsLiteralArea *literal_area;
+ Uint lit_asize;
ERTS_INIT_OFF_HEAP(&code_off_heap);
- low = (Uint *) (code+stp->ci);
- high = low + stp->total_literal_size;
- code[MI_LITERALS_START] = (BeamInstr) low;
- code[MI_LITERALS_END] = (BeamInstr) high;
- ptr = low;
+ lit_asize = ERTS_LITERAL_AREA_ALLOC_SIZE(stp->total_literal_size);
+ literal_area = erts_alloc(ERTS_ALC_T_LITERAL, lit_asize);
+ ptr = &literal_area->start[0];
+ literal_area->end = ptr + stp->total_literal_size;
+
for (i = 0; i < stp->num_literals; i++) {
- if (stp->literals[i].heap_frags) {
- move_multi_frags(&ptr, &code_off_heap, stp->literals[i].heap_frags,
- &stp->literals[i].term, 1);
+ if (is_not_immed(stp->literals[i].term)) {
+ erts_move_multi_frags(&ptr, &code_off_heap,
+ stp->literals[i].heap_frags,
+ &stp->literals[i].term, 1, 1);
+ ASSERT(erts_is_literal(stp->literals[i].term,
+ ptr_val(stp->literals[i].term)));
}
- else ASSERT(is_immed(stp->literals[i].term));
}
- code[MI_LITERALS_OFF_HEAP] = (BeamInstr) code_off_heap.first;
+ literal_area->off_heap = code_off_heap.first;
lp = stp->literal_patches;
while (lp != 0) {
BeamInstr* op_ptr;
Literal* lit;
- op_ptr = code + lp->pos;
+ op_ptr = codev + lp->pos;
lit = &stp->literals[op_ptr[0]];
op_ptr[0] = lit->term;
lp = lp->next;
}
- literal_end += stp->total_literal_size;
+ code_hdr->literal_area = literal_area;
}
CHKBLK(ERTS_ALC_T_CODE,code);
@@ -4440,52 +4616,49 @@ freeze_code(LoaderState* stp)
* If there is line information, place it here.
*/
if (stp->line_instr == 0) {
- code[MI_LINE_TABLE] = (BeamInstr) 0;
- str_table = (byte *) literal_end;
+ code_hdr->line_table = NULL;
+ str_table = (byte *) (codev + stp->ci);
} else {
- Eterm* line_tab = (Eterm *) literal_end;
- Eterm* p;
- int ftab_size = stp->num_functions;
- int num_instrs = stp->current_li;
- Eterm* first_line_item;
+ BeamCodeLineTab* const line_tab = (BeamCodeLineTab *) (codev+stp->ci);
+ const unsigned int ftab_size = stp->num_functions;
+ const unsigned int num_instrs = stp->current_li;
+ const BeamInstr** const line_items =
+ (const BeamInstr**) &line_tab->func_tab[ftab_size + 1];
- code[MI_LINE_TABLE] = (BeamInstr) line_tab;
- p = line_tab + MI_LINE_FUNC_TAB;
+ code_hdr->line_table = line_tab;
- first_line_item = (p + ftab_size + 1);
for (i = 0; i < ftab_size; i++) {
- *p++ = (Eterm) (BeamInstr) (first_line_item + stp->func_line[i]);
+ line_tab->func_tab[i] = line_items + stp->func_line[i];
}
- *p++ = (Eterm) (BeamInstr) (first_line_item + num_instrs);
- ASSERT(p == first_line_item);
+ line_tab->func_tab[i] = line_items + num_instrs;
+
for (i = 0; i < num_instrs; i++) {
- *p++ = (Eterm) (BeamInstr) (code + stp->line_instr[i].pos);
+ line_items[i] = codev + stp->line_instr[i].pos;
}
- *p++ = (Eterm) (BeamInstr) (code + stp->ci - 1);
+ line_items[i] = codev + stp->ci - 1;
- line_tab[MI_LINE_FNAME_PTR] = (Eterm) (BeamInstr) p;
- memcpy(p, stp->fname, stp->num_fnames*sizeof(Eterm));
- p += stp->num_fnames;
+ line_tab->fname_ptr = (Eterm*) &line_items[i + 1];
+ memcpy(line_tab->fname_ptr, stp->fname, stp->num_fnames*sizeof(Eterm));
- line_tab[MI_LINE_LOC_TAB] = (Eterm) (BeamInstr) p;
- line_tab[MI_LINE_LOC_SIZE] = stp->loc_size;
+ line_tab->loc_size = stp->loc_size;
if (stp->loc_size == 2) {
- Uint16* locp = (Uint16 *) p;
- for (i = 0; i < num_instrs; i++) {
+ Uint16* locp = (Uint16 *) &line_tab->fname_ptr[stp->num_fnames];
+ line_tab->loc_tab.p2 = locp;
+ for (i = 0; i < num_instrs; i++) {
*locp++ = (Uint16) stp->line_instr[i].loc;
- }
- *locp++ = LINE_INVALID_LOCATION;
- str_table = (byte *) locp;
+ }
+ *locp++ = LINE_INVALID_LOCATION;
+ str_table = (byte *) locp;
} else {
- Uint32* locp = (Uint32 *) p;
- ASSERT(stp->loc_size == 4);
+ Uint32* locp = (Uint32 *) &line_tab->fname_ptr[stp->num_fnames];
+ ASSERT(stp->loc_size == 4);
+ line_tab->loc_tab.p4 = locp;
for (i = 0; i < num_instrs; i++) {
*locp++ = stp->line_instr[i].loc;
}
*locp++ = LINE_INVALID_LOCATION;
- str_table = (byte *) locp;
+ str_table = (byte *) locp;
}
-
CHKBLK(ERTS_ALC_T_CODE,code);
}
@@ -4497,13 +4670,13 @@ freeze_code(LoaderState* stp)
if (attr_size) {
byte* attr = str_table + strtab_size;
sys_memcpy(attr, stp->chunks[ATTR_CHUNK].start, stp->chunks[ATTR_CHUNK].size);
- code[MI_ATTR_PTR] = (BeamInstr) attr;
- code[MI_ATTR_SIZE] = (BeamInstr) stp->chunks[ATTR_CHUNK].size;
+ code_hdr->attr_ptr = attr;
+ code_hdr->attr_size = (BeamInstr) stp->chunks[ATTR_CHUNK].size;
decoded_size = erts_decode_ext_size(attr, attr_size);
if (decoded_size < 0) {
LoadError0(stp, "bad external term representation of module attributes");
}
- code[MI_ATTR_SIZE_ON_HEAP] = decoded_size;
+ code_hdr->attr_size_on_heap = decoded_size;
}
CHKBLK(ERTS_ALC_T_CODE,code);
if (compile_size) {
@@ -4513,9 +4686,9 @@ freeze_code(LoaderState* stp)
stp->chunks[COMPILE_CHUNK].size);
CHKBLK(ERTS_ALC_T_CODE,code);
- code[MI_COMPILE_PTR] = (BeamInstr) compile_info;
+ code_hdr->compile_ptr = compile_info;
CHKBLK(ERTS_ALC_T_CODE,code);
- code[MI_COMPILE_SIZE] = (BeamInstr) stp->chunks[COMPILE_CHUNK].size;
+ code_hdr->compile_size = (BeamInstr) stp->chunks[COMPILE_CHUNK].size;
CHKBLK(ERTS_ALC_T_CODE,code);
decoded_size = erts_decode_ext_size(compile_info, compile_size);
CHKBLK(ERTS_ALC_T_CODE,code);
@@ -4523,7 +4696,7 @@ freeze_code(LoaderState* stp)
LoadError0(stp, "bad external term representation of compilation information");
}
CHKBLK(ERTS_ALC_T_CODE,code);
- code[MI_COMPILE_SIZE_ON_HEAP] = decoded_size;
+ code_hdr->compile_size_on_heap = decoded_size;
}
CHKBLK(ERTS_ALC_T_CODE,code);
{
@@ -4531,7 +4704,7 @@ freeze_code(LoaderState* stp)
CHKBLK(ERTS_ALC_T_CODE,code);
sys_memcpy(md5_sum, stp->mod_md5, MD5_SIZE);
CHKBLK(ERTS_ALC_T_CODE,code);
- code[MI_MD5_PTR] = (BeamInstr) md5_sum;
+ code_hdr->md5_ptr = md5_sum;
CHKBLK(ERTS_ALC_T_CODE,code);
}
CHKBLK(ERTS_ALC_T_CODE,code);
@@ -4540,7 +4713,7 @@ freeze_code(LoaderState* stp)
* Make sure that we have not overflowed the allocated code space.
*/
ASSERT(str_table + strtab_size + attr_size + compile_size + MD5_SIZE ==
- ((byte *) code) + size);
+ ((byte *) code_hdr) + size);
/*
* Patch all instructions that refer to the string table.
@@ -4552,46 +4725,47 @@ freeze_code(LoaderState* stp)
BeamInstr* op_ptr;
byte* strp;
- op_ptr = code + sp->pos;
+ op_ptr = codev + sp->pos;
strp = str_table + op_ptr[0];
op_ptr[0] = (BeamInstr) strp;
sp = sp->next;
}
}
- CHKBLK(ERTS_ALC_T_CODE,code);
+ CHKBLK(ERTS_ALC_T_CODE,code_hdr);
/*
* Resolve all labels.
*/
for (i = 0; i < stp->num_labels; i++) {
- Uint this_patch;
- Uint next_patch;
+ Sint this_patch;
+ Sint next_patch;
Uint value = stp->labels[i].value;
- if (value == 0 && stp->labels[i].patches != 0) {
+ if (value == 0 && stp->labels[i].patches >= 0) {
LoadError1(stp, "label %d not resolved", i);
}
ASSERT(value < stp->ci);
this_patch = stp->labels[i].patches;
- while (this_patch != 0) {
+ while (this_patch >= 0) {
ASSERT(this_patch < stp->ci);
- next_patch = code[this_patch];
+ next_patch = codev[this_patch];
ASSERT(next_patch < stp->ci);
- code[this_patch] = (BeamInstr) (code + value);
+ codev[this_patch] = (BeamInstr) (codev + value);
this_patch = next_patch;
}
}
- CHKBLK(ERTS_ALC_T_CODE,code);
+ CHKBLK(ERTS_ALC_T_CODE,code_hdr);
/*
* Save the updated code pointer and code size.
*/
- stp->code = code;
+ stp->hdr = code_hdr;
+ stp->codev = codev;
stp->loaded_size = size;
- CHKBLK(ERTS_ALC_T_CODE,code);
+ CHKBLK(ERTS_ALC_T_CODE,code_hdr);
return 1;
load_error:
@@ -4599,19 +4773,19 @@ freeze_code(LoaderState* stp)
* Make sure that the caller frees the newly reallocated block, and
* not the old one (in case it has moved).
*/
- stp->code = code;
+ stp->hdr = code_hdr;
+ stp->codev = codev;
return 0;
}
static void
-final_touch(LoaderState* stp)
+final_touch(LoaderState* stp, struct erl_module_instance* inst_p)
{
- int i;
+ unsigned int i;
int on_load = stp->on_load;
unsigned catches;
Uint index;
- BeamInstr* code = stp->code;
- Module* modp;
+ BeamInstr* codev = stp->codev;
/*
* Allocate catch indices and fix up all catch_yf instructions.
@@ -4620,14 +4794,13 @@ final_touch(LoaderState* stp)
index = stp->catches;
catches = BEAM_CATCHES_NIL;
while (index != 0) {
- BeamInstr next = code[index];
- code[index] = BeamOpCode(op_catch_yf);
- catches = beam_catches_cons((BeamInstr *)code[index+2], catches);
- code[index+2] = make_catch(catches);
+ BeamInstr next = codev[index];
+ codev[index] = BeamOpCode(op_catch_yf);
+ catches = beam_catches_cons((BeamInstr *)codev[index+2], catches);
+ codev[index+2] = make_catch(catches);
index = next;
}
- modp = erts_put_module(stp->module);
- modp->curr.catches = catches;
+ inst_p->catches = catches;
/*
* Export functions.
@@ -4647,10 +4820,10 @@ final_touch(LoaderState* stp)
ep->addressv[erts_staging_code_ix()] = address;
} else {
/*
- * Don't make any of the exported functions
- * callable yet.
+ * on_load: Don't make any of the exported functions
+ * callable yet. Keep any function in the current
+ * code callable.
*/
- ep->addressv[erts_staging_code_ix()] = ep->code+3;
ep->code[4] = (BeamInstr) address;
}
}
@@ -4674,8 +4847,8 @@ final_touch(LoaderState* stp)
current = stp->import[i].patches;
while (current != 0) {
ASSERT(current < stp->ci);
- next = stp->code[current];
- stp->code[current] = import;
+ next = stp->codev[current];
+ stp->codev[current] = import;
current = next;
}
}
@@ -4688,13 +4861,13 @@ final_touch(LoaderState* stp)
for (i = 0; i < stp->num_lambdas; i++) {
unsigned entry_label = stp->lambdas[i].label;
ErlFunEntry* fe = stp->lambdas[i].fe;
- BeamInstr* code_ptr = (BeamInstr *) (stp->code + stp->labels[entry_label].value);
+ BeamInstr* code_ptr = stp->codev + stp->labels[entry_label].value;
if (fe->address[0] != 0) {
/*
* We are hiding a pointer into older code.
*/
- erts_refc_dec(&fe->refc, 1);
+ erts_smp_refc_dec(&fe->refc, 1);
}
fe->address = code_ptr;
#ifdef HIPE
@@ -4710,31 +4883,25 @@ transform_engine(LoaderState* st)
Uint op;
int ap; /* Current argument. */
Uint* restart; /* Where to restart if current match fails. */
- GenOpArg def_vars[TE_MAX_VARS]; /* Default buffer for variables. */
- GenOpArg* var = def_vars;
- int num_vars = 0;
+ GenOpArg var[TE_MAX_VARS]; /* Buffer for variables. */
+ GenOpArg* rest_args = NULL;
+ int num_rest_args = 0;
int i; /* General index. */
Uint mask;
GenOp* instr;
+ GenOp* first = st->genop;
+ GenOp* keep = NULL;
Uint* pc;
- int rval;
static Uint restart_fail[1] = {TOP_fail};
- ASSERT(gen_opc[st->genop->op].transform != -1);
- pc = op_transform + gen_opc[st->genop->op].transform;
- restart = pc;
+ ASSERT(gen_opc[first->op].transform != -1);
+ restart = op_transform + gen_opc[first->op].transform;
restart:
- if (var != def_vars) {
- erts_free(ERTS_ALC_T_LOADER_TMP, (void *) var);
- var = def_vars;
- }
ASSERT(restart != NULL);
pc = restart;
ASSERT(*pc < NUM_TOPS); /* Valid instruction? */
- instr = st->genop;
-
-#define RETURN(r) rval = (r); goto do_return;
+ instr = first;
#ifdef DEBUG
restart = NULL;
@@ -4752,7 +4919,7 @@ transform_engine(LoaderState* st)
* We'll need at least one more instruction to decide whether
* this combination matches or not.
*/
- RETURN(TE_SHORT_WINDOW);
+ return TE_SHORT_WINDOW;
}
if (*pc++ != instr->op)
goto restart;
@@ -4801,7 +4968,8 @@ transform_engine(LoaderState* st)
if (var[i].type != instr->a[ap].type)
goto restart;
switch (var[i].type) {
- case TAG_r: case TAG_n: break;
+ case TAG_n:
+ break;
default:
if (var[i].val != instr->a[ap].val)
goto restart;
@@ -4913,19 +5081,9 @@ transform_engine(LoaderState* st)
#if defined(TOP_rest_args)
case TOP_rest_args:
{
- int n = *pc++;
int formal_arity = gen_opc[instr->op].arity;
- int j = formal_arity;
-
- num_vars = n + (instr->arity - formal_arity);
- var = erts_alloc(ERTS_ALC_T_LOADER_TMP,
- num_vars * sizeof(GenOpArg));
- for (i = 0; i < n; i++) {
- var[i] = def_vars[i];
- }
- while (i < num_vars) {
- var[i++] = instr->a[j++];
- }
+ num_rest_args = instr->arity - formal_arity;
+ rest_args = instr->a + formal_arity;
}
break;
#endif
@@ -4934,21 +5092,22 @@ transform_engine(LoaderState* st)
break;
case TOP_commit:
instr = instr->next; /* The next_instr was optimized away. */
-
- /*
- * The left-hand side of this transformation matched.
- * Delete all matched instructions.
- */
- while (st->genop != instr) {
- GenOp* next = st->genop->next;
- FREE_GENOP(st, st->genop);
- st->genop = next;
- }
+ keep = instr;
+ st->genop = instr;
#ifdef DEBUG
instr = 0;
#endif
break;
-
+#if defined(TOP_keep)
+ case TOP_keep:
+ /* Keep the current instruction unchanged. */
+ keep = instr;
+ st->genop = instr;
+#ifdef DEBUG
+ instr = 0;
+#endif
+ break;
+#endif
#if defined(TOP_call_end)
case TOP_call_end:
{
@@ -4973,22 +5132,19 @@ transform_engine(LoaderState* st)
lastp = &((*lastp)->next);
}
- instr = instr->next; /* The next_instr was optimized away. */
-
- /*
- * The left-hand side of this transformation matched.
- * Delete all matched instructions.
- */
- while (st->genop != instr) {
- GenOp* next = st->genop->next;
- FREE_GENOP(st, st->genop);
- st->genop = next;
- }
- *lastp = st->genop;
+ keep = instr->next; /* The next_instr was optimized away. */
+ *lastp = keep;
st->genop = new_instr;
}
- RETURN(TE_OK);
+ /* FALLTHROUGH */
#endif
+ case TOP_end:
+ while (first != keep) {
+ GenOp* next = first->next;
+ FREE_GENOP(st, first);
+ first = next;
+ }
+ return TE_OK;
case TOP_new_instr:
/*
* Note that the instructions are generated in reverse order.
@@ -5000,6 +5156,12 @@ transform_engine(LoaderState* st)
instr->arity = gen_opc[op].arity;
ap = 0;
break;
+#ifdef TOP_rename
+ case TOP_rename:
+ instr->op = op = *pc++;
+ instr->arity = gen_opc[op].arity;
+ return TE_OK;
+#endif
case TOP_store_type:
i = *pc++;
instr->a[ap].type = i;
@@ -5019,14 +5181,10 @@ transform_engine(LoaderState* st)
#if defined(TOP_store_rest_args)
case TOP_store_rest_args:
{
- int n = *pc++;
- int num_extra = num_vars - n;
-
- ASSERT(n <= num_vars);
- GENOP_ARITY(instr, instr->arity+num_extra);
+ GENOP_ARITY(instr, instr->arity+num_rest_args);
memcpy(instr->a, instr->def_args, ap*sizeof(GenOpArg));
- memcpy(instr->a+ap, var+n, num_extra*sizeof(GenOpArg));
- ap += num_extra;
+ memcpy(instr->a+ap, rest_args, num_rest_args*sizeof(GenOpArg));
+ ap += num_rest_args;
}
break;
#endif
@@ -5038,21 +5196,12 @@ transform_engine(LoaderState* st)
case TOP_try_me_else_fail:
restart = restart_fail;
break;
- case TOP_end:
- RETURN(TE_OK);
case TOP_fail:
- RETURN(TE_FAIL);
+ return TE_FAIL;
default:
ASSERT(0);
}
}
-#undef RETURN
-
- do_return:
- if (var != def_vars) {
- erts_free(ERTS_ALC_T_LOADER_TMP, (void *) var);
- }
- return rval;
}
static void
@@ -5304,14 +5453,14 @@ new_genop(LoaderState* stp)
static int
new_label(LoaderState* stp)
{
- int num = stp->num_labels;
+ unsigned int num = stp->num_labels;
stp->num_labels++;
stp->labels = (Label *) erts_realloc(ERTS_ALC_T_PREPARED_CODE,
(void *) stp->labels,
stp->num_labels * sizeof(Label));
stp->labels[num].value = 0;
- stp->labels[num].patches = 0;
+ stp->labels[num].patches = -1;
return num;
}
@@ -5371,7 +5520,7 @@ erts_module_info_0(Process* p, Eterm module)
{
Module* modp;
ErtsCodeIndex code_ix = erts_active_code_ix();
- BeamInstr* code;
+ BeamCodeHeader* code_hdr;
Eterm *hp;
Eterm list = NIL;
Eterm tup;
@@ -5385,13 +5534,13 @@ erts_module_info_0(Process* p, Eterm module)
return THE_NON_VALUE;
}
- code = modp->curr.code;
- if (code == NULL) {
+ code_hdr = modp->curr.code_hdr;
+ if (code_hdr == NULL) {
return THE_NON_VALUE;
}
#define BUILD_INFO(What) \
- tup = get_module_info(p, code_ix, code, module, What); \
+ tup = get_module_info(p, code_ix, code_hdr, module, What); \
hp = HAlloc(p, 5); \
tup = TUPLE2(hp, What, tup); \
hp += 3; \
@@ -5414,7 +5563,7 @@ erts_module_info_1(Process* p, Eterm module, Eterm what)
{
Module* modp;
ErtsCodeIndex code_ix = erts_active_code_ix();
- BeamInstr* code;
+ BeamCodeHeader* code_hdr;
if (is_not_atom(module)) {
return THE_NON_VALUE;
@@ -5425,34 +5574,34 @@ erts_module_info_1(Process* p, Eterm module, Eterm what)
return THE_NON_VALUE;
}
- code = modp->curr.code;
- if (code == NULL) {
+ code_hdr = modp->curr.code_hdr;
+ if (code_hdr == NULL) {
return THE_NON_VALUE;
}
- return get_module_info(p, code_ix, code, module, what);
+ return get_module_info(p, code_ix, code_hdr, module, what);
}
static Eterm
-get_module_info(Process* p, ErtsCodeIndex code_ix, BeamInstr* code,
+get_module_info(Process* p, ErtsCodeIndex code_ix, BeamCodeHeader* code_hdr,
Eterm module, Eterm what)
{
if (what == am_module) {
return module;
} else if (what == am_md5) {
- return md5_of_module(p, code);
+ return md5_of_module(p, code_hdr);
} else if (what == am_exports) {
return exported_from_module(p, code_ix, module);
} else if (what == am_functions) {
- return functions_in_module(p, code);
+ return functions_in_module(p, code_hdr);
} else if (what == am_attributes) {
- return attributes_for_module(p, code);
+ return attributes_for_module(p, code_hdr);
} else if (what == am_compile) {
- return compilation_info_for_module(p, code);
+ return compilation_info_for_module(p, code_hdr);
} else if (what == am_native_addresses) {
- return native_addresses(p, code);
+ return native_addresses(p, code_hdr);
} else if (what == am_native) {
- return has_native(code);
+ return has_native(code_hdr);
}
return THE_NON_VALUE;
}
@@ -5464,7 +5613,7 @@ get_module_info(Process* p, ErtsCodeIndex code_ix, BeamInstr* code,
Eterm
functions_in_module(Process* p, /* Process whose heap to use. */
- BeamInstr* code)
+ BeamCodeHeader* code_hdr)
{
int i;
Uint num_functions;
@@ -5473,12 +5622,12 @@ functions_in_module(Process* p, /* Process whose heap to use. */
Eterm* hp_end;
Eterm result = NIL;
- num_functions = code[MI_NUM_FUNCTIONS];
+ num_functions = code_hdr->num_functions;
need = 5*num_functions;
hp = HAlloc(p, need);
hp_end = hp + need;
for (i = num_functions-1; i >= 0 ; i--) {
- BeamInstr* func_info = (BeamInstr *) code[MI_FUNCTIONS+i];
+ BeamInstr* func_info = code_hdr->functions[i];
Eterm name = (Eterm) func_info[3];
int arity = (int) func_info[4];
Eterm tuple;
@@ -5504,27 +5653,49 @@ functions_in_module(Process* p, /* Process whose heap to use. */
*/
static Eterm
-has_native(BeamInstr *code)
+has_native(BeamCodeHeader *code_hdr)
{
Eterm result = am_false;
#ifdef HIPE
- if (erts_is_module_native(code)) {
+ if (erts_is_module_native(code_hdr)) {
result = am_true;
}
#endif
return result;
}
+void
+erts_release_literal_area(ErtsLiteralArea* literal_area)
+{
+ struct erl_off_heap_header* oh;
+
+ if (!literal_area)
+ return;
+
+ oh = literal_area->off_heap;
+
+ while (oh) {
+ Binary* bptr;
+ ASSERT(thing_subtag(oh->thing_word) == REFC_BINARY_SUBTAG);
+ bptr = ((ProcBin*)oh)->val;
+ if (erts_refc_dectest(&bptr->refc, 0) == 0) {
+ erts_bin_free(bptr);
+ }
+ oh = oh->next;
+ }
+ erts_free(ERTS_ALC_T_LITERAL, literal_area);
+}
+
int
-erts_is_module_native(BeamInstr* code)
+erts_is_module_native(BeamCodeHeader* code_hdr)
{
Uint i, num_functions;
/* Check NativeAdress of first real function in module */
- if (code != NULL) {
- num_functions = code[MI_NUM_FUNCTIONS];
+ if (code_hdr != NULL) {
+ num_functions = code_hdr->num_functions;
for (i=0; i<num_functions; i++) {
- BeamInstr* func_info = (BeamInstr *) code[MI_FUNCTIONS+i];
+ BeamInstr* func_info = (BeamInstr *) code_hdr->functions[i];
Eterm name = (Eterm) func_info[3];
if (is_atom(name)) {
return func_info[1] != 0;
@@ -5541,7 +5712,7 @@ erts_is_module_native(BeamInstr* code)
*/
static Eterm
-native_addresses(Process* p, BeamInstr* code)
+native_addresses(Process* p, BeamCodeHeader* code_hdr)
{
int i;
Eterm* hp;
@@ -5550,12 +5721,12 @@ native_addresses(Process* p, BeamInstr* code)
Eterm* hp_end;
Eterm result = NIL;
- num_functions = code[MI_NUM_FUNCTIONS];
+ num_functions = code_hdr->num_functions;
need = (6+BIG_UINT_HEAP_SIZE)*num_functions;
hp = HAlloc(p, need);
hp_end = hp + need;
for (i = num_functions-1; i >= 0 ; i--) {
- BeamInstr* func_info = (BeamInstr *) code[MI_FUNCTIONS+i];
+ BeamInstr* func_info = code_hdr->functions[i];
Eterm name = (Eterm) func_info[3];
int arity = (int) func_info[4];
Eterm tuple;
@@ -5583,12 +5754,13 @@ exported_from_module(Process* p, /* Process whose heap to use. */
ErtsCodeIndex code_ix,
Eterm mod) /* Tagged atom for module. */
{
- int i;
+ int i, num_exps;
Eterm* hp = NULL;
Eterm* hend = NULL;
Eterm result = NIL;
- for (i = 0; i < export_list_size(code_ix); i++) {
+ num_exps = export_list_size(code_ix);
+ for (i = 0; i < num_exps; i++) {
Export* ep = export_list(i,code_ix);
if (ep->code[0] == mod) {
@@ -5621,16 +5793,16 @@ exported_from_module(Process* p, /* Process whose heap to use. */
Eterm
attributes_for_module(Process* p, /* Process whose heap to use. */
- BeamInstr* code)
+ BeamCodeHeader* code_hdr)
{
byte* ext;
Eterm result = NIL;
- ext = (byte *) code[MI_ATTR_PTR];
+ ext = code_hdr->attr_ptr;
if (ext != NULL) {
ErtsHeapFactory factory;
- erts_factory_proc_prealloc_init(&factory, p, code[MI_ATTR_SIZE_ON_HEAP]);
- result = erts_decode_ext(&factory, &ext);
+ erts_factory_proc_prealloc_init(&factory, p, code_hdr->attr_size_on_heap);
+ result = erts_decode_ext(&factory, &ext, 0);
if (is_value(result)) {
erts_factory_close(&factory);
}
@@ -5644,16 +5816,16 @@ attributes_for_module(Process* p, /* Process whose heap to use. */
Eterm
compilation_info_for_module(Process* p, /* Process whose heap to use. */
- BeamInstr* code)
+ BeamCodeHeader* code_hdr)
{
byte* ext;
Eterm result = NIL;
- ext = (byte *) code[MI_COMPILE_PTR];
+ ext = code_hdr->compile_ptr;
if (ext != NULL) {
ErtsHeapFactory factory;
- erts_factory_proc_prealloc_init(&factory, p, code[MI_COMPILE_SIZE_ON_HEAP]);
- result = erts_decode_ext(&factory, &ext);
+ erts_factory_proc_prealloc_init(&factory, p, code_hdr->compile_size_on_heap);
+ result = erts_decode_ext(&factory, &ext, 0);
if (is_value(result)) {
erts_factory_close(&factory);
}
@@ -5667,9 +5839,9 @@ compilation_info_for_module(Process* p, /* Process whose heap to use. */
Eterm
md5_of_module(Process* p, /* Process whose heap to use. */
- BeamInstr* code)
+ BeamCodeHeader* code_hdr)
{
- return new_binary(p, (byte *) code[MI_MD5_PTR], MD5_SIZE);
+ return new_binary(p, code_hdr->md5_ptr, MD5_SIZE);
}
/*
@@ -5868,7 +6040,7 @@ make_stub(BeamInstr* fp, Eterm mod, Eterm func, Uint arity, Uint native, BeamIns
fp[4] = arity;
#ifdef HIPE
if (native) {
- fp[5] = BeamOpCode(op_move_return_nr);
+ fp[5] = BeamOpCode(op_move_return_n);
hipe_mfa_save_orig_beam_op(mod, func, arity, fp+5);
}
#endif
@@ -5880,7 +6052,7 @@ static byte*
stub_copy_info(LoaderState* stp,
int chunk, /* Chunk: ATTR_CHUNK or COMPILE_CHUNK */
byte* info, /* Where to store info. */
- BeamInstr* ptr_word, /* Where to store pointer into info. */
+ byte** ptr_word, /* Where to store pointer into info. */
BeamInstr* size_word, /* Where to store size into info. */
BeamInstr* size_on_heap_word) /* Where to store size on heap. */
{
@@ -5888,7 +6060,7 @@ stub_copy_info(LoaderState* stp,
Uint size = stp->chunks[chunk].size;
if (size != 0) {
memcpy(info, stp->chunks[chunk].start, size);
- *ptr_word = (BeamInstr) info;
+ *ptr_word = info;
decoded_size = erts_decode_ext_size(info, size);
if (decoded_size < 0) {
return 0;
@@ -5902,11 +6074,11 @@ stub_copy_info(LoaderState* stp,
static int
stub_read_export_table(LoaderState* stp)
{
- int i;
+ unsigned int i;
GetInt(stp, 4, stp->num_exps);
if (stp->num_exps > stp->num_functions) {
- LoadError2(stp, "%d functions exported; only %d functions defined",
+ LoadError2(stp, "%u functions exported; only %u functions defined",
stp->num_exps, stp->num_functions);
}
stp->export
@@ -5920,7 +6092,7 @@ stub_read_export_table(LoaderState* stp)
GetAtom(stp, n, stp->export[i].function);
GetInt(stp, 4, n);
if (n > MAX_REG) {
- LoadError2(stp, "export table entry %d: absurdly high arity %d", i, n);
+ LoadError2(stp, "export table entry %u: absurdly high arity %u", i, n);
}
stp->export[i].arity = n;
GetInt(stp, 4, n); /* Ignore label */
@@ -5934,8 +6106,8 @@ stub_read_export_table(LoaderState* stp)
static void
stub_final_touch(LoaderState* stp, BeamInstr* fp)
{
- int i;
- int n = stp->num_exps;
+ unsigned int i;
+ unsigned int n = stp->num_exps;
Eterm mod = fp[2];
Eterm function = fp[3];
int arity = fp[4];
@@ -6115,7 +6287,7 @@ patch_funentries(Eterm Patchlist)
*
* Reproduced on a debug emulator with stdlib_test/qlc_SUITE:join_merge
*
- * erts_refc_dec(&fe->refc, 1);
+ * erts_smp_refc_dec(&fe->refc, 1);
*/
if (!patch(Addresses, (Uint) fe))
@@ -6143,15 +6315,14 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info)
BeamInstr Patchlist;
Eterm MD5Bin;
Eterm* tp;
- BeamInstr* code = NULL;
- BeamInstr* ptrs;
+ BeamCodeHeader* code_hdr;
+ BeamInstr* code_base;
BeamInstr* fp;
byte* info;
- Uint ci;
- int n;
+ Sint n;
int code_size;
int rval;
- int i;
+ Sint i;
byte* temp_alloc = NULL;
byte* bytes;
Uint size;
@@ -6225,40 +6396,38 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info)
* Allocate memory for the stub module.
*/
- code_size = ((WORDS_PER_FUNCTION+1)*n + MI_FUNCTIONS + 2) * sizeof(BeamInstr);
- code_size += stp->chunks[ATTR_CHUNK].size;
- code_size += stp->chunks[COMPILE_CHUNK].size;
- code_size += MD5_SIZE;
- code = erts_alloc_fnf(ERTS_ALC_T_CODE, code_size);
- if (!code) {
+ code_size = (offsetof(BeamCodeHeader,functions)
+ + ((n+1) * sizeof(BeamInstr*))
+ + (WORDS_PER_FUNCTION*n + 1) * sizeof(BeamInstr)
+ + stp->chunks[ATTR_CHUNK].size
+ + stp->chunks[COMPILE_CHUNK].size
+ + MD5_SIZE);
+ code_hdr = erts_alloc_fnf(ERTS_ALC_T_CODE, code_size);
+ if (!code_hdr) {
goto error;
}
/*
- * Initialize code area.
+ * Initialize code header.
*/
- code[MI_NUM_FUNCTIONS] = n;
- code[MI_ATTR_PTR] = 0;
- code[MI_ATTR_SIZE] = 0;
- code[MI_ATTR_SIZE_ON_HEAP] = 0;
- code[MI_COMPILE_PTR] = 0;
- code[MI_COMPILE_SIZE] = 0;
- code[MI_COMPILE_SIZE_ON_HEAP] = 0;
- code[MI_LITERALS_START] = 0;
- code[MI_LITERALS_END] = 0;
- code[MI_LITERALS_OFF_HEAP] = 0;
- code[MI_ON_LOAD_FUNCTION_PTR] = 0;
- code[MI_LINE_TABLE] = 0;
- code[MI_MD5_PTR] = 0;
- ci = MI_FUNCTIONS + n + 1;
+ code_hdr->num_functions = n;
+ code_hdr->attr_ptr = NULL;
+ code_hdr->attr_size = 0;
+ code_hdr->attr_size_on_heap = 0;
+ code_hdr->compile_ptr = NULL;
+ code_hdr->compile_size = 0;
+ code_hdr->compile_size_on_heap = 0;
+ code_hdr->literal_area = NULL;
+ code_hdr->on_load_function_ptr = NULL;
+ code_hdr->line_table = NULL;
+ code_hdr->md5_ptr = NULL;
/*
* Make stubs for all functions.
*/
- ptrs = code + MI_FUNCTIONS;
- fp = code + ci;
+ fp = code_base = (BeamInstr*) &code_hdr->functions[n+1];
for (i = 0; i < n; i++) {
Eterm* listp;
Eterm tuple;
@@ -6301,11 +6470,11 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info)
* Set the pointer and make the stub. Put a return instruction
* as the body until we know what kind of trap we should put there.
*/
- ptrs[i] = (BeamInstr) fp;
+ code_hdr->functions[i] = fp;
#ifdef HIPE
op = (Eterm) BeamOpCode(op_hipe_trap_call); /* Might be changed later. */
#else
- op = (Eterm) BeamOpCode(op_move_return_nr);
+ op = (Eterm) BeamOpCode(op_move_return_n);
#endif
fp = make_stub(fp, Mod, func, arity, (Uint)native_address, op);
}
@@ -6314,7 +6483,7 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info)
* Insert the last pointer and the int_code_end instruction.
*/
- ptrs[i] = (BeamInstr) fp;
+ code_hdr->functions[i] = fp;
*fp++ = (BeamInstr) BeamOp(op_int_code_end);
/*
@@ -6323,16 +6492,16 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info)
info = (byte *) fp;
info = stub_copy_info(stp, ATTR_CHUNK, info,
- code+MI_ATTR_PTR,
- code+MI_ATTR_SIZE,
- code+MI_ATTR_SIZE_ON_HEAP);
+ &code_hdr->attr_ptr,
+ &code_hdr->attr_size,
+ &code_hdr->attr_size_on_heap);
if (info == NULL) {
goto error;
}
info = stub_copy_info(stp, COMPILE_CHUNK, info,
- code+MI_COMPILE_PTR,
- code+MI_COMPILE_SIZE,
- code+MI_COMPILE_SIZE_ON_HEAP);
+ &code_hdr->compile_ptr,
+ &code_hdr->compile_size,
+ &code_hdr->compile_size_on_heap);
if (info == NULL) {
goto error;
}
@@ -6341,7 +6510,7 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info)
byte *md5 = NULL;
if ((md5 = erts_get_aligned_binary_bytes(MD5Bin, &tmp)) != NULL) {
sys_memcpy(info, md5, MD5_SIZE);
- code[MI_MD5_PTR] = (BeamInstr) info;
+ code_hdr->md5_ptr = info;
}
erts_free_aligned_binary_bytes(tmp);
}
@@ -6350,7 +6519,8 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info)
* Insert the module in the module table.
*/
- rval = insert_new_code(p, 0, p->group_leader, Mod, code, code_size);
+ rval = stub_insert_new_code(p, 0, p->group_leader, Mod,
+ code_hdr, code_size);
if (rval != NIL) {
goto error;
}
@@ -6359,7 +6529,7 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info)
* Export all stub functions and insert the correct type of HiPE trap.
*/
- fp = code + ci;
+ fp = code_base;
for (i = 0; i < n; i++) {
stub_final_touch(stp, fp);
fp += WORDS_PER_FUNCTION;
diff --git a/erts/emulator/beam/beam_load.h b/erts/emulator/beam/beam_load.h
index d5af634fad..9be5e14e40 100644
--- a/erts/emulator/beam/beam_load.h
+++ b/erts/emulator/beam/beam_load.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,7 +24,6 @@
#include "beam_opcodes.h"
#include "erl_process.h"
-int erts_is_module_native(BeamInstr* code);
Eterm beam_make_current_old(Process *c_p, ErtsProcLocks c_p_locks,
Eterm module);
@@ -34,7 +33,6 @@ typedef struct gen_op_entry {
int specific;
int num_specific;
int transform;
- int min_window;
} GenOpEntry;
extern GenOpEntry gen_opc[];
@@ -52,6 +50,8 @@ extern BeamInstr* em_call_error_handler;
extern BeamInstr* em_apply_bif;
extern BeamInstr* em_call_nif;
+struct ErtsLiteralArea_;
+
/*
* The following variables keep a sorted list of address ranges for
* each module. It allows us to quickly find a function given an
@@ -60,72 +60,85 @@ extern BeamInstr* em_call_nif;
/* Total code size in bytes */
extern Uint erts_total_code_size;
-/*
- * Index into start of code chunks which contains additional information
- * about the loaded module.
- *
- * First number of functions.
- */
-#define MI_NUM_FUNCTIONS 0
+typedef struct BeamCodeLineTab_ BeamCodeLineTab;
/*
- * The attributes retrieved by Mod:module_info(attributes).
- */
-
-#define MI_ATTR_PTR 1
-#define MI_ATTR_SIZE 2
-#define MI_ATTR_SIZE_ON_HEAP 3
-
-/*
- * The compilation information retrieved by Mod:module_info(compile).
- */
-
-#define MI_COMPILE_PTR 4
-#define MI_COMPILE_SIZE 5
-#define MI_COMPILE_SIZE_ON_HEAP 6
-
-/*
- * Literal area (constant pool).
- */
-#define MI_LITERALS_START 7
-#define MI_LITERALS_END 8
-#define MI_LITERALS_OFF_HEAP 9
-
-/*
- * Pointer to the on_load function (or NULL if none).
- */
-#define MI_ON_LOAD_FUNCTION_PTR 10
-
-/*
- * Pointer to the line table (or NULL if none).
- */
-#define MI_LINE_TABLE 11
-
-/*
- * Pointer to the module MD5 sum (16 bytes)
- */
-#define MI_MD5_PTR 12
-
-/*
- * Start of function pointer table. This table contains pointers to
- * all functions in the module plus an additional pointer just beyond
- * the end of the last function.
- *
- * The actual loaded code (for the first function) start just beyond
- * this table.
+ * Header of code chunks which contains additional information
+ * about the loaded module.
*/
-
-#define MI_FUNCTIONS 13
+typedef struct beam_code_header {
+ /*
+ * Number of functions.
+ */
+ UWord num_functions;
+
+ /*
+ * The attributes retrieved by Mod:module_info(attributes).
+ */
+ byte* attr_ptr;
+ UWord attr_size;
+ UWord attr_size_on_heap;
+
+ /*
+ * The compilation information retrieved by Mod:module_info(compile).
+ */
+ byte* compile_ptr;
+ UWord compile_size;
+ UWord compile_size_on_heap;
+
+ /*
+ * Literal area (constant pool).
+ */
+ struct ErtsLiteralArea_ *literal_area;
+
+ /*
+ * Pointer to the on_load function (or NULL if none).
+ */
+ BeamInstr* on_load_function_ptr;
+
+ /*
+ * Pointer to the line table (or NULL if none).
+ */
+ BeamCodeLineTab* line_table;
+
+ /*
+ * Pointer to the module MD5 sum (16 bytes)
+ */
+ byte* md5_ptr;
+
+ /*
+ * Start of function pointer table. This table contains pointers to
+ * all functions in the module plus an additional pointer just beyond
+ * the end of the last function.
+ *
+ * The actual loaded code (for the first function) start just beyond
+ * this table.
+ */
+ BeamInstr* functions[1];
+
+}BeamCodeHeader;
+
+void erts_release_literal_area(struct ErtsLiteralArea_* literal_area);
+int erts_is_module_native(BeamCodeHeader* code);
+void erts_beam_bif_load_init(void);
+struct erl_fun_entry;
+void erts_purge_state_add_fun(struct erl_fun_entry *fe);
+Export *erts_suspend_process_on_pending_purge_lambda(Process *c_p,
+ struct erl_fun_entry*);
/*
* Layout of the line table.
*/
-
-#define MI_LINE_FNAME_PTR 0
-#define MI_LINE_LOC_TAB 1
-#define MI_LINE_LOC_SIZE 2
-#define MI_LINE_FUNC_TAB 3
+struct BeamCodeLineTab_ {
+ Eterm* fname_ptr;
+ int loc_size;
+ union {
+ Uint16* p2;
+ Uint32* p4;
+ }loc_tab;
+ const BeamInstr** func_tab[1];
+};
#define LINE_INVALID_LOCATION (0)
diff --git a/erts/emulator/beam/beam_ranges.c b/erts/emulator/beam/beam_ranges.c
index 19079ba150..55342a38c6 100644
--- a/erts/emulator/beam/beam_ranges.c
+++ b/erts/emulator/beam/beam_ranges.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2012. All Rights Reserved.
+ * Copyright Ericsson AB 2012-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,8 +37,8 @@ typedef struct {
#define RANGE_END(R) ((BeamInstr*)erts_smp_atomic_read_nob(&(R)->end))
static Range* find_range(BeamInstr* pc);
-static void lookup_loc(FunctionInfo* fi, BeamInstr* pc,
- BeamInstr* modp, int idx);
+static void lookup_loc(FunctionInfo* fi, const BeamInstr* pc,
+ BeamCodeHeader*, int idx);
/*
* The following variables keep a sorted list of address ranges for
@@ -53,6 +53,7 @@ struct ranges {
};
static struct ranges r[ERTS_NUM_CODE_IX];
static erts_smp_atomic_t mem_used;
+static Range* write_ptr;
#ifdef HARD_DEBUG
static void check_consistency(struct ranges* p)
@@ -72,6 +73,17 @@ static void check_consistency(struct ranges* p)
# define CHECK(r)
#endif /* HARD_DEBUG */
+static int
+rangecompare(Range* a, Range* b)
+{
+ if (a->start < b->start) {
+ return -1;
+ } else if (a->start == b->start) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
void
erts_init_ranges(void)
@@ -88,45 +100,70 @@ erts_init_ranges(void)
}
void
-erts_start_staging_ranges(void)
+erts_start_staging_ranges(int num_new)
{
+ ErtsCodeIndex src = erts_active_code_ix();
ErtsCodeIndex dst = erts_staging_code_ix();
+ Sint need;
if (r[dst].modules) {
erts_smp_atomic_add_nob(&mem_used, -r[dst].allocated);
erts_free(ERTS_ALC_T_MODULE_REFS, r[dst].modules);
- r[dst].modules = NULL;
}
+
+ need = r[dst].allocated = r[src].n + num_new;
+ erts_smp_atomic_add_nob(&mem_used, need);
+ write_ptr = erts_alloc(ERTS_ALC_T_MODULE_REFS,
+ need * sizeof(Range));
+ r[dst].modules = write_ptr;
}
void
erts_end_staging_ranges(int commit)
{
- ErtsCodeIndex dst = erts_staging_code_ix();
-
- if (commit && r[dst].modules == NULL) {
+ if (commit) {
Sint i;
- Sint n;
-
- /* No modules added, just clone src and remove purged code. */
ErtsCodeIndex src = erts_active_code_ix();
+ ErtsCodeIndex dst = erts_staging_code_ix();
+ Range* mp;
+ Sint num_inserted;
- erts_smp_atomic_add_nob(&mem_used, r[src].n);
- r[dst].modules = erts_alloc(ERTS_ALC_T_MODULE_REFS,
- r[src].n * sizeof(Range));
- r[dst].allocated = r[src].n;
- n = 0;
+ mp = r[dst].modules;
+ num_inserted = write_ptr - mp;
for (i = 0; i < r[src].n; i++) {
Range* rp = r[src].modules+i;
if (rp->start < RANGE_END(rp)) {
/* Only insert a module that has not been purged. */
- r[dst].modules[n] = *rp;
- n++;
+ write_ptr->start = rp->start;
+ erts_smp_atomic_init_nob(&write_ptr->end,
+ (erts_aint_t)(RANGE_END(rp)));
+ write_ptr++;
+ }
+ }
+
+ /*
+ * There are num_inserted new range entries (unsorted) at the
+ * beginning of the modules array, followed by the old entries
+ * (sorted). We must now sort the entire array.
+ */
+
+ r[dst].n = write_ptr - mp;
+ if (num_inserted > 1) {
+ qsort(mp, r[dst].n, sizeof(Range),
+ (int (*)(const void *, const void *)) rangecompare);
+ } else if (num_inserted == 1) {
+ /* Sift the new range into place. This is faster than qsort(). */
+ Range t = mp[0];
+ for (i = 0; i < r[dst].n-1 && t.start > mp[i+1].start; i++) {
+ mp[i] = mp[i+1];
}
+ mp[i] = t;
}
- r[dst].n = n;
+ r[dst].modules = mp;
+ CHECK(&r[dst]);
erts_smp_atomic_set_nob(&r[dst].mid,
- (erts_aint_t) (r[dst].modules + n / 2));
+ (erts_aint_t) (r[dst].modules +
+ r[dst].n / 2));
}
}
@@ -135,82 +172,29 @@ erts_update_ranges(BeamInstr* code, Uint size)
{
ErtsCodeIndex dst = erts_staging_code_ix();
ErtsCodeIndex src = erts_active_code_ix();
- Sint i;
- Sint n;
- Sint need;
if (src == dst) {
ASSERT(!erts_initialized);
/*
- * During start-up of system, the indices are the same.
- * Handle this by faking a source area.
+ * During start-up of system, the indices are the same
+ * and erts_start_staging_ranges() has not been called.
*/
- src = (src+1) % ERTS_NUM_CODE_IX;
- if (r[src].modules) {
- erts_smp_atomic_add_nob(&mem_used, -r[src].allocated);
- erts_free(ERTS_ALC_T_MODULE_REFS, r[src].modules);
+ if (r[dst].modules == NULL) {
+ Sint need = 128;
+ erts_smp_atomic_add_nob(&mem_used, need);
+ r[dst].modules = erts_alloc(ERTS_ALC_T_MODULE_REFS,
+ need * sizeof(Range));
+ r[dst].allocated = need;
+ write_ptr = r[dst].modules;
}
- r[src] = r[dst];
- r[dst].modules = 0;
}
- CHECK(&r[src]);
-
- ASSERT(r[dst].modules == NULL);
- need = r[dst].allocated = r[src].n + 1;
- erts_smp_atomic_add_nob(&mem_used, need);
- r[dst].modules = (Range *) erts_alloc(ERTS_ALC_T_MODULE_REFS,
- need * sizeof(Range));
- n = 0;
- for (i = 0; i < r[src].n; i++) {
- Range* rp = r[src].modules+i;
- if (code < rp->start) {
- r[dst].modules[n].start = code;
- erts_smp_atomic_init_nob(&r[dst].modules[n].end,
- (erts_aint_t)(((byte *)code) + size));
- ASSERT(!n || RANGE_END(&r[dst].modules[n-1]) < code);
- n++;
- break;
- }
- if (rp->start < RANGE_END(rp)) {
- /* Only insert a module that has not been purged. */
- r[dst].modules[n].start = rp->start;
- erts_smp_atomic_init_nob(&r[dst].modules[n].end,
- (erts_aint_t)(RANGE_END(rp)));
- ASSERT(!n || RANGE_END(&r[dst].modules[n-1]) < rp->start);
- n++;
- }
- }
-
- while (i < r[src].n) {
- Range* rp = r[src].modules+i;
- if (rp->start < RANGE_END(rp)) {
- /* Only insert a module that has not been purged. */
- r[dst].modules[n].start = rp->start;
- erts_smp_atomic_init_nob(&r[dst].modules[n].end,
- (erts_aint_t)(RANGE_END(rp)));
- ASSERT(!n || RANGE_END(&r[dst].modules[n-1]) < rp->start);
- n++;
- }
- i++;
- }
-
- if (n == 0 || code > r[dst].modules[n-1].start) {
- r[dst].modules[n].start = code;
- erts_smp_atomic_init_nob(&r[dst].modules[n].end,
- (erts_aint_t)(((byte *)code) + size));
- ASSERT(!n || RANGE_END(&r[dst].modules[n-1]) < code);
- n++;
- }
-
- ASSERT(n <= r[src].n+1);
- r[dst].n = n;
- erts_smp_atomic_set_nob(&r[dst].mid,
- (erts_aint_t) (r[dst].modules + n / 2));
-
- CHECK(&r[dst]);
- CHECK(&r[src]);
+ ASSERT(r[dst].modules);
+ write_ptr->start = code;
+ erts_smp_atomic_init_nob(&(write_ptr->end),
+ (erts_aint_t)(((byte *)code) + size));
+ write_ptr++;
}
void
@@ -241,6 +225,7 @@ erts_lookup_function_info(FunctionInfo* fi, BeamInstr* pc, int full_info)
BeamInstr** high;
BeamInstr** mid;
Range* rp;
+ BeamCodeHeader* hdr;
fi->current = NULL;
fi->needed = 5;
@@ -249,9 +234,10 @@ erts_lookup_function_info(FunctionInfo* fi, BeamInstr* pc, int full_info)
if (rp == 0) {
return;
}
+ hdr = (BeamCodeHeader*) rp->start;
- low = (BeamInstr **) (rp->start + MI_FUNCTIONS);
- high = low + rp->start[MI_NUM_FUNCTIONS];
+ low = hdr->functions;
+ high = low + hdr->num_functions;
while (low < high) {
mid = low + (high-low) / 2;
if (pc < mid[0]) {
@@ -259,10 +245,9 @@ erts_lookup_function_info(FunctionInfo* fi, BeamInstr* pc, int full_info)
} else if (pc < mid[1]) {
fi->current = mid[0]+2;
if (full_info) {
- BeamInstr** fp = (BeamInstr **) (rp->start +
- MI_FUNCTIONS);
+ BeamInstr** fp = hdr->functions;
int idx = mid - fp;
- lookup_loc(fi, pc, rp->start, idx);
+ lookup_loc(fi, pc, hdr, idx);
}
return;
} else {
@@ -295,39 +280,34 @@ find_range(BeamInstr* pc)
}
static void
-lookup_loc(FunctionInfo* fi, BeamInstr* orig_pc, BeamInstr* modp, int idx)
+lookup_loc(FunctionInfo* fi, const BeamInstr* pc,
+ BeamCodeHeader* code_hdr, int idx)
{
- Eterm* line = (Eterm *) modp[MI_LINE_TABLE];
- Eterm* low;
- Eterm* high;
- Eterm* mid;
- Eterm pc;
+ BeamCodeLineTab* lt = code_hdr->line_table;
+ const BeamInstr** low;
+ const BeamInstr** high;
+ const BeamInstr** mid;
- if (line == 0) {
+ if (lt == NULL) {
return;
}
- pc = (Eterm) (BeamInstr) orig_pc;
- fi->fname_ptr = (Eterm *) (BeamInstr) line[MI_LINE_FNAME_PTR];
- low = (Eterm *) (BeamInstr) line[MI_LINE_FUNC_TAB+idx];
- high = (Eterm *) (BeamInstr) line[MI_LINE_FUNC_TAB+idx+1];
+ fi->fname_ptr = lt->fname_ptr;
+ low = lt->func_tab[idx];
+ high = lt->func_tab[idx+1];
while (high > low) {
mid = low + (high-low) / 2;
if (pc < mid[0]) {
high = mid;
} else if (pc < mid[1]) {
int file;
- int index = mid - (Eterm *) (BeamInstr) line[MI_LINE_FUNC_TAB];
+ int index = mid - lt->func_tab[0];
- if (line[MI_LINE_LOC_SIZE] == 2) {
- Uint16* loc_table =
- (Uint16 *) (BeamInstr) line[MI_LINE_LOC_TAB];
- fi->loc = loc_table[index];
+ if (lt->loc_size == 2) {
+ fi->loc = lt->loc_tab.p2[index];
} else {
- Uint32* loc_table =
- (Uint32 *) (BeamInstr) line[MI_LINE_LOC_TAB];
- ASSERT(line[MI_LINE_LOC_SIZE] == 4);
- fi->loc = loc_table[index];
+ ASSERT(lt->loc_size == 4);
+ fi->loc = lt->loc_tab.p4[index];
}
if (fi->loc == LINE_INVALID_LOCATION) {
return;
diff --git a/erts/emulator/beam/benchmark.c b/erts/emulator/beam/benchmark.c
deleted file mode 100644
index 44f5c760c2..0000000000
--- a/erts/emulator/beam/benchmark.c
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2002-2012. 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 "sys.h"
-#include "global.h"
-#include "benchmark.h"
-
-#ifdef BM_COUNTERS
-unsigned long long processes_busy;
-unsigned long long processes_spawned;
-unsigned long long messages_sent;
-unsigned long long messages_copied;
-unsigned long long messages_ego;
-unsigned long long minor_gc;
-unsigned long long major_gc;
-#endif /* BM_COUNTERS */
-
-#ifdef BM_TIMERS
-
-/* assuming Solaris */
-#include <time.h>
-BM_TIMER_T system_clock;
-
-unsigned long local_pause_times[MAX_PAUSE_TIME];
-unsigned long pause_times[MAX_PAUSE_TIME];
-unsigned long pause_times_old[MAX_PAUSE_TIME];
-
-BM_TIMER_T mmu;
-BM_TIMER_T mmu_counter;
-
-BM_NEW_TIMER(timer);
-BM_NEW_TIMER(system);
-BM_NEW_TIMER(gc);
-BM_NEW_TIMER(minor_gc);
-BM_NEW_TIMER(major_gc);
-BM_NEW_TIMER(minor_global_gc);
-BM_NEW_TIMER(major_global_gc);
-BM_NEW_TIMER(send);
-BM_NEW_TIMER(copy);
-BM_NEW_TIMER(size);
-BM_NEW_TIMER(max_minor);
-BM_NEW_TIMER(max_major);
-BM_NEW_TIMER(max_global_minor);
-BM_NEW_TIMER(max_global_major);
-BM_NEW_TIMER(misc0);
-BM_NEW_TIMER(misc1);
-BM_NEW_TIMER(misc2);
-#endif /* BM_TIMERS */
-
-#ifdef BM_HEAP_SIZES
-unsigned long long max_used_heap;
-unsigned long long max_allocated_heap;
-unsigned long long max_used_global_heap;
-unsigned long long max_allocated_global_heap;
-#endif /* BM_HEAP_SIZES */
-
-#ifdef BM_MESSAGE_SIZES
-unsigned long long words_sent;
-unsigned long long words_copied;
-unsigned long long words_prealloc;
-unsigned long long message_sizes[1000];
-#endif /* BM_MESSAGE_SIZES */
-
-/*****
- * The following functions have to be defined, but they only have contents
- * if certain keywords are defined.
- */
-
-void init_benchmarking()
-{
-#ifdef BM_TIMERS
- int i;
- for (i = 0; i < 1000; i++)
- {
- BM_START_TIMER(system);
- BM_STOP_TIMER(system);
- }
- timer_time = system_time / 1000;
-
- for (i = 0; i < MAX_PAUSE_TIME; i++) {
- local_pause_times[i] = 0;
- pause_times[i] = 0;
- pause_times_old[i] = 0;
- }
-
- mmu = 0;
- mmu_counter = 0;
-
- BM_MMU_INIT();
-#endif /* BM_TIMERS */
-
-#ifdef BM_COUNTERS
- processes_busy = 0;
- processes_spawned = 0;
- messages_sent = 0;
- messages_copied = 0;
- messages_ego = 0;
- minor_gc = 0;
- major_gc = 0;
-#endif /* BM_COUNTERS */
-
-#ifdef BM_HEAP_SIZES
- max_used_heap = 0;
- max_allocated_heap = 0;
- max_used_global_heap = 0;
- max_allocated_global_heap = 0;
-#endif /* BM_HEAP_SIZES */
-
-#ifdef BM_MESSAGE_SIZES
- words_sent = 0;
- words_copied = 0;
- words_prealloc = 0;
- {
- int i;
- for (i = 0; i < 1000; i++)
- message_sizes[i] = 0;
- }
-#endif /* BM_MESSAGE_SIZES */
-}
-
-void save_statistics()
-{
-#ifdef BM_STATISTICS
- FILE *file = fopen(BM_STATISTICS_FILE,"a");
- long i = 0;
-
- if (file)
- {
- erts_fprintf(file,"-------------------------------------------------------------------------\n");
- erts_fprintf(file,"The counters are reset at system start and are sums over the entire node.\n");
- erts_fprintf(file,"You may reset them manually using the BIFs in the module hipe_bifs.\n");
- erts_fprintf(file,"All times are given in milliseconds.\n");
- erts_fprintf(file,"-------------------------------------------------------------------------\n");
-
- erts_fprintf(file,"Node: %T\n",erts_this_node->sysname);
-
-#ifdef BM_COUNTERS
- erts_fprintf(file,"Number of processes spawned: %lld\n",processes_spawned);
- erts_fprintf(file,"Number of local minor GCs: %lld\n",minor_gc);
- erts_fprintf(file,"Number of local major GCs: %lld\n",major_gc);
- erts_fprintf(file,"Number of messages sent: %lld\n",messages_sent);
- erts_fprintf(file,"Number of messages copied: %lld\n",messages_copied);
- erts_fprintf(file,"Number of messages sent to self: %lld\n",messages_ego);
-#endif /* BM_COUNTERS */
-
-#ifdef BM_MESSAGE_SIZES
- erts_fprintf(file,"Number of words sent: %lld\n",words_sent);
- erts_fprintf(file,"Number of words copied: %lld\n",words_copied);
- erts_fprintf(file,"Number of words preallocated: %lld\n",words_prealloc);
-#endif /* BM_MESSAGE_SIZES */
-
-#ifdef BM_HEAP_SIZES
- erts_fprintf(file,"Biggest local heap used (in words): %lld\n",max_used_heap);
- erts_fprintf(file,"Biggest local heap allocated (in words): %lld\n",max_allocated_heap);
- erts_fprintf(file,"Biggest global heap used (in words): %lld\n",max_used_global_heap);
- erts_fprintf(file,"Biggest global heap allocated (in words): %lld\n",max_allocated_global_heap);
-#endif /* BM_HEAP_SIZES */
-
-#ifdef BM_TIMERS
- erts_fprintf(file,"--- The total active system time is the sum of all times below ---\n");
- BM_TIME_PRINTER("Mutator time",system_time);
- BM_TIME_PRINTER("Time spent in send (excluding size & copy)",send_time);
- BM_TIME_PRINTER("Time spent in size",size_time);
- BM_TIME_PRINTER("Time spent in copy",copy_time);
- BM_TIME_PRINTER("Time spent in local minor GC",minor_gc_time);
- BM_TIME_PRINTER("Time spent in local major GC",major_gc_time);
- BM_TIME_PRINTER("Time spent in global minor GC",minor_global_gc_time);
- BM_TIME_PRINTER("Time spent in global major GC",major_global_gc_time);
- erts_fprintf(file,"---\n");
- BM_TIME_PRINTER("Maximum time spent in one separate local minor GC",max_minor_time);
- BM_TIME_PRINTER("Maximum time spent in one separate local major GC",max_major_time);
- BM_TIME_PRINTER("Maximum time spent in one separate global minor GC",max_global_minor_time);
- BM_TIME_PRINTER("Maximum time spent in one separate global major GC",max_global_major_time);
-#endif /* BM_TIMERS */
-
-#if 0
- /* Save a log file for import into excel */
-
- long long total_time, n;
- long left, right, mid;
-
-#ifdef BM_COUNTERS
- erts_fprintf(file,"Spawns\tLocalGC\tMAGC\tMessages\tMutator_t\tLocalGC_t\tMAGC_t\tLocMaxP\tLocMeanP\tLocGeoMP\tMAMaxP\tMAMeanP\tMAGeoMP\t\tCMAGC\tCMAGC_t\n");
- erts_fprintf(file,"%lld\t%lld\t%lld\t%lld\t",
- processes_spawned,
- minor_garbage_cols + major_garbage_cols,
- minor_global_garbage_cols + major_global_garbage_cols,
- messages_sent);
-#endif /* BM_COUNTERS */
-
-#ifdef BM_TIMERS
- erts_fprintf(file,"%lld\t%lld\t%lld\t",
- (long long)(system_time + send_time + size_time + copy_time),
- (long long)(minor_gc_time + major_gc_time),
- (long long)(minor_global_gc_time + major_global_gc_time));
-
- total_time = 0; n = 0;
- left = 0; right = 0; mid = 0;
- for (i = 0; i < MAX_PAUSE_TIME; i++) {
- total_time += local_pause_times[i] * i;
- n += local_pause_times[i];
- if (i > mid)
- right += local_pause_times[i];
- while(right > left) {
- left += local_pause_times[mid++];
- right -= local_pause_times[mid];
- }
- }
- erts_fprintf(file,"%lld\t%lld\t%ld\t",
- (long long)((max_minor_time > max_major_time ?
- max_minor_time :
- max_major_time)*1000),
- total_time / n,
- mid);
-
- total_time = 0; n = 0;
- left = 0; right = 0; mid = 0;
- for (i = 0; i < MAX_PAUSE_TIME; i++) {
- if (pause_times[i] > 0) {
- total_time += pause_times[i] * i;
- n += pause_times[i];
- if (i > mid)
- right += pause_times[i];
- while(right > left) {
- left += pause_times[mid++];
- right -= pause_times[mid];
- }
- }
- }
- erts_fprintf(file,"%lld\t%lld\t%ld\t",
- (long long)((max_global_minor_time > max_global_major_time ?
- max_global_minor_time :
- max_global_major_time)*1000),
- (n > 0 ? total_time / n : 0),
- mid);
-
- erts_fprintf(file,"\t%lld\t%lld\n",n,total_time);
-
- erts_fprintf(file,"\nMinor:\n");
- for (i = 0; i < MAX_PAUSE_TIME; i++) {
- if (i < 1000 || pause_times[i] > 0) {
- erts_fprintf(file,"%d\t%ld\n",i,pause_times[i]);
- }
- }
-
- fprintf(file,"Major:\n");
- for (i = 0; i < MAX_PAUSE_TIME; i++) {
- if (pause_times_old[i] > 0) {
- fprintf(file,"%d\t%ld\n",i,pause_times_old[i]);
- }
- }
-#endif /* BM_TIMERS */
-
-#ifdef BM_TIMERS
- total_time = 0; n = 0;
- left = 0; right = 0; mid = 0;
- fprintf(file,"\nLocal:\n");
- for (i = 0; i < MAX_PAUSE_TIME; i++) {
- if (local_pause_times[i] > 0) {
- erts_fprintf(file,"%d\t%ld\n",i,local_pause_times[i]);
- total_time += local_pause_times[i] * i;
- n += local_pause_times[i];
- if (i > mid)
- right += local_pause_times[i];
- while(right > left) {
- left += local_pause_times[mid++];
- right -= local_pause_times[mid];
- }
- }
- }
- erts_fprintf(file,"Mid: %ld Mean: %ld\n",(long)mid,
- (long)(n > 0 ? total_time / n : 0));
-#endif
-#endif /* 0 */
- fclose(file);
- }
- else
- fprintf(stderr,"Sorry... Can not write to %s!\n\r",BM_STATISTICS_FILE);
-#endif /* BM_STATISTICS */
-}
diff --git a/erts/emulator/beam/benchmark.h b/erts/emulator/beam/benchmark.h
deleted file mode 100644
index 0fb0b93f12..0000000000
--- a/erts/emulator/beam/benchmark.h
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2002-2012. 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 __BENCHMARK_H__
-#define __BENCHMARK_H__
-
-/* The define __BENCHMARK__ is the master switch to turn on and off
- * benchmarking. This will enable the benchmark-BIFs in hipe_bif1.c.
- * Documentation for the BIFs is in hipe_bif1.c, and that is where you
- * will find the information about how to accually get some data out
- * from these timers and counters.
- */
-/* #define __BENCHMARK__ */
-
-#ifdef __BENCHMARK__
-/*
- * The defines below enables different parts of the benchmaring.
- * Counters and timers that are disabled, always report zero in
- * the BIFs.
- */
-
-/* BM_TIMERS keeps track of the time spent in diferent parts of the
- * system. It only measures accual active time, not time spent in idle
- * mode. Currently, the Solaris hrtime_t will be used.
- * To add new timers look below.
- */
-#define BM_TIMERS
-
-/* BM_COUNTERS count all kinds of events that occurs in the system.
- * Among other things it counts the number of messages, then number of
- * garbage collections, the number of processes spawned etc.
- * To add new counters look below.
- */
-#define BM_COUNTERS
-
-/* BM_MESSAGE_SIZES keeps a log of the size of all messages sent in
- * the system. This introduce an overhead in time for the shared heap
- * system since all message sizes have to be calculated at send.
- */
-/* #define BM_MESSAGE_SIZES */
-
-/* BM_HEAP_SIZES goes through all processes at garbage collection time
- * to sum their allocated and used heap sizes. In anything else than a
- * shared heap system, this will cost.
- */
-/* #define BM_HEAP_SIZES */
-
-/* BM_STATISTICS saves an entry in the file BM_STATISTICS_FILE. This
- * is done for each erlang node at exit time.
- */
-/* #define BM_STATISTICS */
-
-#endif /* __BENCHMARK__ */
-
-
-#ifdef BM_STATISTICS
-# define BM_STATISTICS_FILE "/tmp/erlang_statistics.joppe.log"
-#endif /* BM_STATISTICS */
-
-
-/************ There are no more settings below this line *************/
-
-/*
- * Maintenance and how to add new stuff is documented by the code
- * below ;-)
- */
-
-#ifdef BM_COUNTERS
-/*********************************************************************
- * To add new counters:
- *
- * Add the variable here AND in benchmark.c. Use the macro
- * BM_COUNT(var) in the code where you want to increase it.
- *
- */
-extern unsigned long long processes_busy;
-extern unsigned long long processes_spawned;
-extern unsigned long long messages_sent;
-extern unsigned long long messages_copied;
-extern unsigned long long messages_ego;
-extern unsigned long long minor_gc;
-extern unsigned long long major_gc;
-
-#define BM_COUNT(var) (var)++;
-
-#define BM_EGO_COUNT(send,rec) { \
- if ((send) == (rec)) \
- BM_COUNT(messages_ego); }
-
-#define BM_LAZY_COPY_START long long gcs = minor_global_gc + major_global_gc;
-#define BM_LAZY_COPY_STOP { gcs = (minor_global_gc + major_global_gc) - gcs; \
- if (gcs > gc_in_copy) gc_in_copy = gcs; }
-
-#else /* !BM_COUNTERS */
-# define BM_COUNT(var)
-# define BM_EGO_COUNT(send,rec)
-# define BM_LAZY_COPY_START
-# define BM_LAZY_COPY_STOP
-#endif /* BM_COUNTERS */
-
-
-#ifdef BM_TIMERS
-/*********************************************************************
- * To add new timers:
- *
- * Add the variable below using the form extern BM_TIMER_T blah_time.
- * Also add them in benchmark.c using the macro NEW_TIMER(blah). Use
- * the macro BM_SWAP_TIMER(from,blah) ... BM_SWAP_TIMER(blah,to) to
- * start and stop the new timer. Note, that you have to know what
- * timer is running at the place where you want to insert your new
- * timer to be able to stop and start (from,to) it.
- *
- * You can use the macros BM_STOP_TIMER(blah) and BM_START_TIMER(blah)
- * around code that should not be timed at all. As above, you have to
- * know what timer to start and stop. The system timer is running at
- * most places in the emulator. Only the garbage collector and the
- * message sending has its own timers at the moment.
- *
- * The timer_time used when stopping timers is the time it takes to
- * start and stop the timers, calculated in init_benchmarking(). If it
- * is not there, the time it takes to do this will accually be
- * substantial compared to some small times in the system we want to
- * meassure (send time in shared heap for instance).
- */
-
-/* (Assuming Solaris) */
-
-#define BM_TIMER_T ErtsMonotonicTime
-#define BM_START_TIMER(t) system_clock = ERTS_MONOTONIC_TO_NSEC(erts_os_monotonic_time())
-#define BM_STOP_TIMER(t) do { \
- BM_TIMER_T tmp = (ERTS_MONOTONIC_TO_NSEC(erts_os_monotonic_time()) - system_clock) - timer_time; \
- t##_time += (tmp > 0 ? tmp : 0); \
-} while(0)
-
-#define BM_TIME_PRINTER(str,time) do { \
- int min,sec,milli,micro; \
- BM_TIMER_T tmp; \
- tmp = (time) / 1000; \
- micro = tmp % 1000; \
- tmp /= 1000; \
- milli = tmp % 1000; \
- tmp /= 1000; \
- sec = tmp % 60; \
- min = tmp / 60; \
- erts_fprintf(file,str": %d:%02d.%03d %03d\n",min,sec,milli,micro); \
-} while(0)
-
-extern BM_TIMER_T system_clock;
-
-extern BM_TIMER_T timer_time;
-extern BM_TIMER_T system_time;
-extern BM_TIMER_T gc_time;
-extern BM_TIMER_T minor_gc_time;
-extern BM_TIMER_T major_gc_time;
-extern BM_TIMER_T minor_global_gc_time;
-extern BM_TIMER_T major_global_gc_time;
-extern BM_TIMER_T send_time;
-extern BM_TIMER_T copy_time;
-extern BM_TIMER_T size_time;
-extern BM_TIMER_T max_minor_time;
-extern BM_TIMER_T max_major_time;
-extern BM_TIMER_T max_global_minor_time;
-extern BM_TIMER_T max_global_major_time;
-extern BM_TIMER_T misc0_time;
-extern BM_TIMER_T misc1_time;
-extern BM_TIMER_T misc2_time;
-
-#define MAX_PAUSE_TIME 500000
-extern unsigned long local_pause_times[MAX_PAUSE_TIME];
-extern unsigned long pause_times[MAX_PAUSE_TIME];
-extern unsigned long pause_times_old[MAX_PAUSE_TIME];
-
-#define MMU_INTERVAL 5 /* milli seconds */
-extern BM_TIMER_T mmu_counter;
-extern BM_TIMER_T mmu;
-
-#define BM_NEW_TIMER(t) BM_TIMER_T t##_time = 0;
-#define BM_RESET_TIMER(t) t##_time = 0;
-#define BM_SWAP_TIMER(t1,t2) do { BM_STOP_TIMER(t1); BM_START_TIMER(t2); } while(0)
-#define BM_MMU_INIT() do { \
- BM_TIMER_T gc = gc_time; \
- while (gc > 0) { \
- if (gc > MMU_INTERVAL) { \
- gc -= MMU_INTERVAL - mmu_counter; \
- erts_printf("%d\n",(int)((mmu / MMU_INTERVAL) * 100)); \
- mmu_counter = 0; mmu = 0; \
- } else { \
- mmu_counter += gc; \
- if (mmu_counter >= MMU_INTERVAL) { \
- mmu_counter -= MMU_INTERVAL; \
- erts_printf("%d\n",(int)((mmu / MMU_INTERVAL) * 100)); \
- mmu = 0; \
- } \
- gc = 0; \
- } \
- } \
- BM_RESET_TIMER(system); \
- BM_RESET_TIMER(send); \
- BM_RESET_TIMER(copy); \
- BM_RESET_TIMER(size); \
-} while(0)
-
-#define BM_MMU_READ() do { \
- BM_TIMER_T mut = system_time + send_time + copy_time + size_time; \
- while (mut > 0) { \
- if (mut > MMU_INTERVAL) { \
- BM_TIMER_T tmp = MMU_INTERVAL - mmu_counter; \
- mmu += tmp; mut -= tmp; \
- erts_printf("%d\n",(int)((mmu / MMU_INTERVAL) * 100)); \
- mmu_counter = 0; mmu = 0; \
- } else { \
- mmu_counter += mut; mmu += mut; \
- if (mmu_counter >= MMU_INTERVAL) { \
- mmu_counter -= MMU_INTERVAL; \
- mmu -= mmu_counter; \
- erts_printf("%d\n",(int)((mmu / MMU_INTERVAL) * 100)); \
- mmu = mmu_counter; \
- } \
- mut = 0; \
- } \
- } \
-} while(0)
-
-#else /* !BM_TIMERS */
-# define BM_NEW_TIMER(t)
-# define BM_START_TIMER(t)
-# define BM_STOP_TIMER(t)
-# define BM_RESET_TIMER(t)
-# define BM_SWAP_TIMER(t1,t2)
-# define BM_TIME_PRINTER(str,time)
-# define BM_MMU_INIT()
-# define BM_MMU_READ()
-#endif /* BM_TIMERS */
-
-#ifdef BM_HEAP_SIZES
-extern unsigned long long max_used_heap;
-extern unsigned long long max_allocated_heap;
-extern unsigned long long max_used_global_heap;
-extern unsigned long long max_allocated_global_heap;
-#endif /* BM_HEAP_SIZES */
-
-#ifdef BM_MESSAGE_SIZES
-extern unsigned long long words_sent;
-extern unsigned long long words_copied;
-extern unsigned long long words_prealloc;
-extern unsigned long long message_sizes[1000];
-
-#define BM_MESSAGE_COPIED(size) { \
- words_copied += size; \
- BM_COUNT(messages_copied); }
-
-#define BM_PREALLOC_DATA(size) { \
- words_prealloc += size; }
-
-#define BM_MESSAGE(mess,send,rec) { \
- Uint msize = size_object(mess); \
- words_sent += msize; \
- if (msize < 1000) \
- message_sizes[msize]++; \
- else \
- message_sizes[999]++; \
- BM_EGO_COUNT(send,rec); \
- BM_COUNT(messages_sent); }
-
-#else /* !BM_MESSAGE_SIZES */
-
-#define BM_MESSAGE_COPIED(size) BM_COUNT(messages_copied);
-#define BM_PREALLOC_DATA(size)
-#define BM_MESSAGE(mess,send,rec) { \
- BM_EGO_COUNT(send,rec); \
- BM_COUNT(messages_sent); }
-
-#endif /* BM_MESSAGE_SIZES */
-
-void init_benchmarking(void);
-void save_statistics(void);
-
-#endif /* _BENCHMARK_H_ */
diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c
index 095df36a43..d9048065c8 100644
--- a/erts/emulator/beam/bif.c
+++ b/erts/emulator/beam/bif.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2014. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -44,6 +44,8 @@
#include "erl_ptab.h"
#include "erl_bits.h"
#include "erl_bif_unique.h"
+#include "erl_map.h"
+#include "erl_msacc.h"
Export *erts_await_result;
static Export* flush_monitor_messages_trap = NULL;
@@ -54,6 +56,9 @@ Export* erts_format_cpu_topology_trap = NULL;
static Export dsend_continue_trap_export;
Export *erts_convert_time_unit_trap = NULL;
+static Export *await_msacc_mod_trap = NULL;
+static erts_smp_atomic32_t msacc;
+
static Export *await_sched_wall_time_mod_trap;
static erts_smp_atomic32_t sched_wall_time;
@@ -72,7 +77,7 @@ BIF_RETTYPE spawn_3(BIF_ALIST_3)
ErlSpawnOpts so;
Eterm pid;
- so.flags = 0;
+ so.flags = erts_default_spo_flags;
pid = erl_create_process(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, &so);
if (is_non_value(pid)) {
BIF_ERROR(BIF_P, so.error_code);
@@ -119,15 +124,12 @@ static int insert_internal_link(Process* p, Eterm rpid)
erts_add_link(&ERTS_P_LINKS(p), LINK_PID, rp->common.id);
erts_add_link(&ERTS_P_LINKS(rp), LINK_PID, p->common.id);
- ASSERT(is_nil(ERTS_TRACER_PROC(p))
- || is_internal_pid(ERTS_TRACER_PROC(p))
- || is_internal_port(ERTS_TRACER_PROC(p)));
+ ASSERT(IS_TRACER_VALID(ERTS_TRACER(p)));
if (IS_TRACED(p)) {
if (ERTS_TRACE_FLAGS(p) & (F_TRACE_SOL|F_TRACE_SOL1)) {
ERTS_TRACE_FLAGS(rp) |= (ERTS_TRACE_FLAGS(p) & TRACEE_FLAGS);
- ERTS_TRACER_PROC(rp) = ERTS_TRACER_PROC(p); /* maybe steal */
-
+ erts_tracer_replace(&rp->common, ERTS_TRACER(p));
if (ERTS_TRACE_FLAGS(p) & F_TRACE_SOL1) { /* maybe override */
ERTS_TRACE_FLAGS(rp) &= ~(F_TRACE_SOL1 | F_TRACE_SOL);
ERTS_TRACE_FLAGS(p) &= ~(F_TRACE_SOL1 | F_TRACE_SOL);
@@ -136,7 +138,8 @@ static int insert_internal_link(Process* p, Eterm rpid)
}
}
if (IS_TRACED_FL(rp, F_TRACE_PROCS))
- trace_proc(p, rp, am_getting_linked, p->common.id);
+ trace_proc(p, p == rp ? rp_locks : ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK,
+ rp, am_getting_linked, p->common.id);
if (p == rp)
erts_smp_proc_unlock(p, rp_locks & ~ERTS_PROC_LOCK_MAIN);
@@ -155,7 +158,7 @@ BIF_RETTYPE link_1(BIF_ALIST_1)
DistEntry *dep;
if (IS_TRACED_FL(BIF_P, F_TRACE_PROCS)) {
- trace_proc(BIF_P, BIF_P, am_link, BIF_ARG_1);
+ trace_proc(BIF_P, ERTS_PROC_LOCK_MAIN, BIF_P, am_link, BIF_ARG_1);
}
/* check that the pid or port which is our argument is OK */
@@ -279,20 +282,17 @@ res_no_proc: {
}
}
-#define ERTS_DEMONITOR_FALSE 2
-#define ERTS_DEMONITOR_TRUE 1
-#define ERTS_DEMONITOR_BADARG 0
-#define ERTS_DEMONITOR_YIELD_TRUE -1
-#define ERTS_DEMONITOR_INTERNAL_ERROR -2
-
-static int
+/* This function is allowed to return range of values handled by demonitor/1-2
+ * Namely: atoms true, false, yield, internal_error, badarg or THE_NON_VALUE
+ */
+static Eterm
remote_demonitor(Process *c_p, DistEntry *dep, Eterm ref, Eterm to)
{
ErtsDSigData dsd;
ErtsMonitor *dmon;
ErtsMonitor *mon;
int code;
- int res;
+ Eterm res = am_false;
#ifndef ERTS_SMP
int stale_mon = 0;
#endif
@@ -325,7 +325,7 @@ remote_demonitor(Process *c_p, DistEntry *dep, Eterm ref, Eterm to)
mon = erts_remove_monitor(&ERTS_P_MONITORS(c_p), ref);
erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_LINK);
- res = ERTS_DEMONITOR_TRUE;
+ res = am_true;
break;
case ERTS_DSIG_PREP_CONNECTED:
@@ -349,7 +349,7 @@ remote_demonitor(Process *c_p, DistEntry *dep, Eterm ref, Eterm to)
* This is possible when smp support is enabled.
* 'DOWN' message just arrived.
*/
- res = ERTS_DEMONITOR_TRUE;
+ res = am_true;
}
else {
/*
@@ -364,16 +364,13 @@ remote_demonitor(Process *c_p, DistEntry *dep, Eterm ref, Eterm to)
: mon->pid),
ref,
0);
- res = (code == ERTS_DSIG_SEND_YIELD
- ? ERTS_DEMONITOR_YIELD_TRUE
- : ERTS_DEMONITOR_TRUE);
+ res = (code == ERTS_DSIG_SEND_YIELD ? am_yield : am_true);
erts_destroy_monitor(dmon);
-
}
break;
default:
ASSERT(! "Invalid dsig prepare result");
- return ERTS_DEMONITOR_INTERNAL_ERROR;
+ return am_internal_error;
}
#ifndef ERTS_SMP
@@ -401,27 +398,96 @@ remote_demonitor(Process *c_p, DistEntry *dep, Eterm ref, Eterm to)
return res;
}
-static int demonitor(Process *c_p, Eterm ref, Eterm *multip)
+static ERTS_INLINE void
+demonitor_local_process(Process *c_p, Eterm ref, Eterm to, Eterm *res)
{
- ErtsMonitor *mon = NULL; /* The monitor entry to delete */
- Process *rp; /* Local target process */
- Eterm to = NIL; /* Monitor link traget */
- DistEntry *dep = NULL; /* Target's distribution entry */
- int deref_de = 0;
- int res;
- int unlock_link = 1;
+ Process *rp = erts_pid2proc_opt(c_p,
+ ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK,
+ to,
+ ERTS_PROC_LOCK_LINK,
+ ERTS_P2P_FLG_ALLOW_OTHER_X);
+ ErtsMonitor *mon = erts_remove_monitor(&ERTS_P_MONITORS(c_p), ref);
+#ifndef ERTS_SMP
+ ASSERT(mon);
+#else
+ if (!mon)
+ *res = am_false;
+ else
+#endif
+ {
+ *res = am_true;
+ erts_destroy_monitor(mon);
+ }
+ if (rp) {
+ ErtsMonitor *rmon;
+ rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), ref);
+ if (rp != c_p)
+ erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
+ if (rmon != NULL)
+ erts_destroy_monitor(rmon);
+ }
+ else {
+ ERTS_SMP_ASSERT_IS_NOT_EXITING(c_p);
+ }
+}
+
+static ERTS_INLINE BIF_RETTYPE
+demonitor_local_port(Process *origin, Eterm ref, Eterm target)
+{
+ BIF_RETTYPE res = am_false;
+ Port *port = erts_port_lookup_raw(target);
+
+ if (!port) {
+ BIF_ERROR(origin, BADARG);
+ }
+ erts_smp_proc_unlock(origin, ERTS_PROC_LOCK_LINK);
+
+ if (port) {
+ Eterm trap_ref;
+ switch (erts_port_demonitor(origin, ERTS_PORT_DEMONITOR_NORMAL,
+ port, ref, &trap_ref)) {
+ case ERTS_PORT_OP_DROPPED:
+ case ERTS_PORT_OP_BADARG:
+ break;
+ case ERTS_PORT_OP_SCHEDULED:
+ BIF_TRAP3(await_port_send_result_trap, origin, trap_ref,
+ am_busy_port, am_true);
+ /* the busy_port atom will never be returned, because it cannot be
+ * returned from erts_port_(de)monitor, but just in case if in future
+ * internal API changes - you may see this atom */
+ default:
+ break;
+ }
+ }
+ else {
+ ERTS_SMP_ASSERT_IS_NOT_EXITING(origin);
+ }
+ BIF_RET(res);
+}
+
+/* Can return atom true, false, yield, internal_error, badarg or
+ * THE_NON_VALUE if error occured or trap has been set up
+ */
+static
+BIF_RETTYPE demonitor(Process *c_p, Eterm ref, Eterm *multip)
+{
+ ErtsMonitor *mon = NULL; /* The monitor entry to delete */
+ Eterm to = NIL; /* Monitor link traget */
+ DistEntry *dep = NULL; /* Target's distribution entry */
+ int deref_de = 0;
+ BIF_RETTYPE res = am_false;
+ int unlock_link = 1;
erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_LINK);
if (is_not_internal_ref(ref)) {
- res = ERTS_DEMONITOR_BADARG;
+ res = am_badarg;
goto done; /* Cannot be this monitor's ref */
}
mon = erts_lookup_monitor(ERTS_P_MONITORS(c_p), ref);
if (!mon) {
- res = ERTS_DEMONITOR_FALSE;
goto done;
}
@@ -429,70 +495,50 @@ static int demonitor(Process *c_p, Eterm ref, Eterm *multip)
case MON_TIME_OFFSET:
*multip = am_true;
erts_demonitor_time_offset(ref);
- res = ERTS_DEMONITOR_TRUE;
+ res = am_true;
break;
case MON_ORIGIN:
to = mon->pid;
*multip = am_false;
if (is_atom(to)) {
- /* Monitoring a name at node to */
- ASSERT(is_node_name_atom(to));
- dep = erts_sysname_to_connected_dist_entry(to);
- ASSERT(dep != erts_this_dist_entry);
- if (dep)
- deref_de = 1;
+ /* Monitoring a name at node to */
+ ASSERT(is_node_name_atom(to));
+ dep = erts_sysname_to_connected_dist_entry(to);
+ ASSERT(dep != erts_this_dist_entry);
+ if (dep)
+ deref_de = 1;
+ } else if (is_port(to)) {
+ if (port_dist_entry(to) != erts_this_dist_entry) {
+ goto badarg;
+ }
+ res = demonitor_local_port(c_p, ref, to);
+ unlock_link = 0;
+ goto done;
} else {
- ASSERT(is_pid(to));
- dep = pid_dist_entry(to);
+ ASSERT(is_pid(to));
+ dep = pid_dist_entry(to);
}
if (dep != erts_this_dist_entry) {
- res = remote_demonitor(c_p, dep, ref, to);
- /* remote_demonitor() unlocks link lock on c_p */
- unlock_link = 0;
+ res = remote_demonitor(c_p, dep, ref, to);
+ /* remote_demonitor() unlocks link lock on c_p */
+ unlock_link = 0;
}
else { /* Local monitor */
- if (deref_de) {
- deref_de = 0;
- erts_deref_dist_entry(dep);
- }
- dep = NULL;
- rp = erts_pid2proc_opt(c_p,
- ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK,
- to,
- ERTS_PROC_LOCK_LINK,
- ERTS_P2P_FLG_ALLOW_OTHER_X);
- mon = erts_remove_monitor(&ERTS_P_MONITORS(c_p), ref);
-#ifndef ERTS_SMP
- ASSERT(mon);
-#else
- if (!mon)
- res = ERTS_DEMONITOR_FALSE;
- else
-#endif
- {
- res = ERTS_DEMONITOR_TRUE;
- erts_destroy_monitor(mon);
- }
- if (rp) {
- ErtsMonitor *rmon;
- rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), ref);
- if (rp != c_p)
- erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- if (rmon != NULL)
- erts_destroy_monitor(rmon);
- }
- else {
- ERTS_SMP_ASSERT_IS_NOT_EXITING(c_p);
- }
-
+ if (deref_de) {
+ deref_de = 0;
+ erts_deref_dist_entry(dep);
+ }
+ dep = NULL;
+ demonitor_local_process(c_p, ref, to, &res);
}
break;
- default:
- res = ERTS_DEMONITOR_BADARG;
+ default /* case */ :
+badarg:
+ res = am_badarg; /* will be converted to error by caller */
*multip = am_false;
break;
}
- done:
+done:
if (unlock_link)
erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_LINK);
@@ -503,21 +549,20 @@ static int demonitor(Process *c_p, Eterm ref, Eterm *multip)
}
ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(c_p));
- return res;
+ BIF_RET(res);
}
BIF_RETTYPE demonitor_1(BIF_ALIST_1)
{
Eterm multi;
switch (demonitor(BIF_P, BIF_ARG_1, &multi)) {
- case ERTS_DEMONITOR_FALSE:
- case ERTS_DEMONITOR_TRUE:
- BIF_RET(am_true);
- case ERTS_DEMONITOR_YIELD_TRUE:
- ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
- case ERTS_DEMONITOR_BADARG:
- BIF_ERROR(BIF_P, BADARG);
- case ERTS_DEMONITOR_INTERNAL_ERROR:
+ case am_false:
+ case am_true: BIF_RET(am_true);
+ case THE_NON_VALUE: BIF_RET(THE_NON_VALUE);
+ case am_yield: ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
+ case am_badarg: BIF_ERROR(BIF_P, BADARG);
+
+ case am_internal_error:
default:
ASSERT(! "demonitor(): internal error");
BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
@@ -526,11 +571,11 @@ BIF_RETTYPE demonitor_1(BIF_ALIST_1)
BIF_RETTYPE demonitor_2(BIF_ALIST_2)
{
- Eterm res = am_true;
- Eterm multi = am_false;
- int info = 0;
- int flush = 0;
- Eterm list = BIF_ARG_2;
+ BIF_RETTYPE res = am_true;
+ Eterm multi = am_false;
+ int info = 0;
+ int flush = 0;
+ Eterm list = BIF_ARG_2;
while (is_list(list)) {
Eterm* consp = list_val(list);
@@ -551,24 +596,27 @@ BIF_RETTYPE demonitor_2(BIF_ALIST_2)
goto badarg;
switch (demonitor(BIF_P, BIF_ARG_1, &multi)) {
- case ERTS_DEMONITOR_FALSE:
+ case THE_NON_VALUE:
+ /* If other error occured or trap has been set up - pass through */
+ BIF_RET(THE_NON_VALUE);
+ case am_false:
if (info)
res = am_false;
if (flush) {
- flush_messages:
+flush_messages:
BIF_TRAP3(flush_monitor_messages_trap, BIF_P,
BIF_ARG_1, multi, res);
}
- case ERTS_DEMONITOR_TRUE:
+ case am_true:
if (multi == am_true && flush)
goto flush_messages;
BIF_RET(res);
- case ERTS_DEMONITOR_YIELD_TRUE:
+ case am_yield:
ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
- case ERTS_DEMONITOR_BADARG:
- badarg:
+ case am_badarg:
+badarg:
BIF_ERROR(BIF_P, BADARG);
- case ERTS_DEMONITOR_INTERNAL_ERROR:
+ case am_internal_error:
default:
ASSERT(! "demonitor(): internal error");
BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
@@ -589,7 +637,7 @@ erts_queue_monitor_message(Process *p,
Eterm reason_copy, ref_copy, item_copy;
Uint reason_size, ref_size, item_size, heap_size;
ErlOffHeap *ohp;
- ErlHeapFragment *bp;
+ ErtsMessage *msgp;
reason_size = IS_CONST(reason) ? 0 : size_object(reason);
item_size = IS_CONST(item) ? 0 : size_object(item);
@@ -597,11 +645,8 @@ erts_queue_monitor_message(Process *p,
heap_size = 6+reason_size+ref_size+item_size;
- hp = erts_alloc_message_heap(heap_size,
- &bp,
- &ohp,
- p,
- p_locksp);
+ msgp = erts_alloc_message_heap(p, p_locksp, heap_size,
+ &hp, &ohp);
reason_copy = (IS_CONST(reason)
? reason
@@ -612,17 +657,16 @@ erts_queue_monitor_message(Process *p,
ref_copy = copy_struct(ref, ref_size, &hp, ohp);
tup = TUPLE5(hp, am_DOWN, ref_copy, type, item_copy, reason_copy);
- erts_queue_message(p, p_locksp, bp, tup, NIL);
+ erts_queue_message(p, *p_locksp, msgp, tup, am_system);
}
-static BIF_RETTYPE
+static Eterm
local_pid_monitor(Process *p, Eterm target, Eterm mon_ref, int boolean)
{
- BIF_RETTYPE ret;
- Process *rp;
+ Eterm ret = mon_ref;
+ Process *rp;
ErtsProcLocks p_locks = ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK;
- ERTS_BIF_PREP_RET(ret, mon_ref);
if (target == p->common.id) {
return ret;
}
@@ -658,40 +702,112 @@ local_pid_monitor(Process *p, Eterm target, Eterm mon_ref, int boolean)
}
static BIF_RETTYPE
-local_name_monitor(Process *p, Eterm target_name)
+local_port_monitor(Process *origin, Eterm target)
{
- BIF_RETTYPE ret;
- Eterm mon_ref;
- ErtsProcLocks p_locks = ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK;
- Process *rp;
+ BIF_RETTYPE ref = erts_make_ref(origin);
+ Port *port = erts_sig_lookup_port(origin, target);
+ ErtsProcLocks p_locks = ERTS_PROC_LOCK_MAIN;
- mon_ref = erts_make_ref(p);
- ERTS_BIF_PREP_RET(ret, mon_ref);
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_LINK);
- rp = erts_whereis_process(p, p_locks, target_name, ERTS_PROC_LOCK_LINK,
- ERTS_P2P_FLG_ALLOW_OTHER_X);
- if (!rp) {
- DeclareTmpHeap(lhp,3,p);
+ if (!port) {
+res_no_proc:
+ /* Send the DOWN message immediately. Ref is made on the fly because
+ * caller has never seen it yet. */
+ erts_queue_monitor_message(origin, &p_locks, ref,
+ am_port, target, am_noproc);
+ }
+ else {
+ switch (erts_port_monitor(origin, port, target, &ref)) {
+ case ERTS_PORT_OP_DROPPED:
+ case ERTS_PORT_OP_BADARG:
+ goto res_no_proc;
+ case ERTS_PORT_OP_SCHEDULED:
+ BIF_TRAP3(await_port_send_result_trap, origin, ref,
+ am_busy_port, ref);
+ /* the busy_port atom will never be returned, because it cannot be
+ * returned from erts_port_monitor, but just in case if in future
+ * internal API changes - you may see this atom */
+ default:
+ break;
+ }
+ }
+ erts_smp_proc_unlock(origin, p_locks & ~ERTS_PROC_LOCK_MAIN);
+ BIF_RET(ref);
+}
+
+/* Type = process | port :: atom(), 1st argument passed to erlang:monitor/2
+ */
+static BIF_RETTYPE
+local_name_monitor(Process *self, Eterm type, Eterm target_name)
+{
+ BIF_RETTYPE ret = erts_make_ref(self);
+
+ ErtsProcLocks p_locks = ERTS_PROC_LOCK_MAIN | ERTS_PROC_LOCK_LINK;
+ Process *proc = NULL;
+ Port *port = NULL;
+
+ erts_smp_proc_lock(self, ERTS_PROC_LOCK_LINK);
+
+ erts_whereis_name(self, p_locks, target_name,
+ &proc, ERTS_PROC_LOCK_LINK,
+ ERTS_P2P_FLG_ALLOW_OTHER_X,
+ &port, 0);
+
+ /* If the name is not registered,
+ * or if we asked for proc and got a port,
+ * or if we asked for port and got a proc,
+ * we just send the 'DOWN' message.
+ */
+ if ((!proc && !port) ||
+ (type == am_process && port) ||
+ (type == am_port && proc)) {
+ DeclareTmpHeap(lhp,3,self);
Eterm item;
- UseTmpHeap(3,p);
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_LINK);
+ UseTmpHeap(3,self);
+
+ erts_smp_proc_unlock(self, ERTS_PROC_LOCK_LINK);
p_locks &= ~ERTS_PROC_LOCK_LINK;
+
item = TUPLE2(lhp, target_name, erts_this_dist_entry->sysname);
- erts_queue_monitor_message(p, &p_locks,
- mon_ref, am_process, item, am_noproc);
- UnUseTmpHeap(3,p);
- }
- else if (rp != p) {
- erts_add_monitor(&ERTS_P_MONITORS(p), MON_ORIGIN, mon_ref, rp->common.id,
- target_name);
- erts_add_monitor(&ERTS_P_MONITORS(rp), MON_TARGET, mon_ref, p->common.id,
- target_name);
- erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
+ erts_queue_monitor_message(self, &p_locks,
+ ret,
+ type, /* = process|port :: atom() */
+ item, am_noproc);
+ UnUseTmpHeap(3,self);
+ }
+ else if (port) {
+ erts_smp_proc_unlock(self, p_locks & ~ERTS_PROC_LOCK_MAIN);
+ p_locks &= ~ERTS_PROC_LOCK_MAIN;
+
+ switch (erts_port_monitor(self, port, target_name, &ret)) {
+ case ERTS_PORT_OP_DONE:
+ return ret;
+ case ERTS_PORT_OP_SCHEDULED: { /* Scheduled a signal */
+ ASSERT(is_internal_ref(ret));
+ BIF_TRAP3(await_port_send_result_trap, self,
+ ret, am_true, ret);
+ /* bif_trap returns */
+ } break;
+ default:
+ goto badarg;
+ }
+ }
+ else if (proc != self) {
+ erts_add_monitor(&ERTS_P_MONITORS(self), MON_ORIGIN, ret,
+ proc->common.id, target_name);
+ erts_add_monitor(&ERTS_P_MONITORS(proc), MON_TARGET, ret,
+ self->common.id, target_name);
+ erts_smp_proc_unlock(proc, ERTS_PROC_LOCK_LINK);
}
- erts_smp_proc_unlock(p, p_locks & ~ERTS_PROC_LOCK_MAIN);
-
- return ret;
+ if (p_locks) {
+ erts_smp_proc_unlock(self, p_locks & ~ERTS_PROC_LOCK_MAIN);
+ }
+ BIF_RET(ret);
+badarg:
+ if (p_locks) {
+ erts_smp_proc_unlock(self, p_locks & ~ERTS_PROC_LOCK_MAIN);
+ }
+ BIF_ERROR(self, BADARG);
}
static BIF_RETTYPE
@@ -758,7 +874,7 @@ remote_monitor(Process *p, Eterm bifarg1, Eterm bifarg2,
break;
}
- return ret;
+ BIF_RET(ret);
}
BIF_RETTYPE monitor_2(BIF_ALIST_2)
@@ -772,8 +888,9 @@ BIF_RETTYPE monitor_2(BIF_ALIST_2)
switch (BIF_ARG_1) {
case am_time_offset: {
Eterm ref;
- if (BIF_ARG_2 != am_clock_service)
- goto error;
+ if (BIF_ARG_2 != am_clock_service) {
+ goto badarg;
+ }
ref = erts_make_ref(BIF_P);
erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK);
erts_add_monitor(&ERTS_P_MONITORS(BIF_P), MON_TIME_OFFSET,
@@ -783,46 +900,57 @@ BIF_RETTYPE monitor_2(BIF_ALIST_2)
BIF_RET(ref);
}
case am_process:
+ case am_port:
break;
default:
- goto error;
+ goto badarg;
}
- if (is_internal_pid(target)) {
- local_pid:
- ret = local_pid_monitor(BIF_P, target, erts_make_ref(BIF_P), 0);
- } else if (is_external_pid(target)) {
+ if (is_internal_pid(target) && BIF_ARG_1 == am_process) {
+local_pid:
+ ret = local_pid_monitor(BIF_P, target, erts_make_ref(BIF_P), 0);
+ } else if (is_external_pid(target) && BIF_ARG_1 == am_process) {
dep = external_pid_dist_entry(target);
if (dep == erts_this_dist_entry)
goto local_pid;
ret = remote_monitor(BIF_P, BIF_ARG_1, BIF_ARG_2, dep, target, 0);
+ } else if (is_internal_port(target) && BIF_ARG_1 == am_port) {
+local_port:
+ ret = local_port_monitor(BIF_P, target);
+ } else if (is_external_port(target) && BIF_ARG_1 == am_port) {
+ dep = external_port_dist_entry(target);
+ if (dep == erts_this_dist_entry) {
+ goto local_port;
+ }
+ goto badarg; /* No want remote port */
} else if (is_atom(target)) {
- ret = local_name_monitor(BIF_P, target);
+ ret = local_name_monitor(BIF_P, BIF_ARG_1, target);
} else if (is_tuple(target)) {
Eterm *tp = tuple_val(target);
Eterm remote_node;
Eterm name;
- if (arityval(*tp) != 2)
- goto error;
+ if (arityval(*tp) != 2) {
+ goto badarg;
+ }
remote_node = tp[2];
name = tp[1];
if (!is_atom(remote_node) || !is_atom(name)) {
- goto error;
+ goto badarg;
}
if (!erts_is_alive && remote_node != am_Noname) {
- goto error; /* Remote monitor from (this) undistributed node */
+ goto badarg; /* Remote monitor from (this) undistributed node */
}
dep = erts_sysname_to_connected_dist_entry(remote_node);
if (dep == erts_this_dist_entry) {
deref_de = 1;
- ret = local_name_monitor(BIF_P, name);
+ ret = local_name_monitor(BIF_P, BIF_ARG_1, name);
} else {
if (dep)
deref_de = 1;
ret = remote_monitor(BIF_P, BIF_ARG_1, BIF_ARG_2, dep, name, 1);
}
} else {
- error:
+badarg:
ERTS_BIF_PREP_ERROR(ret, BIF_P, BADARG);
}
if (deref_de) {
@@ -841,7 +969,7 @@ BIF_RETTYPE spawn_link_3(BIF_ALIST_3)
ErlSpawnOpts so;
Eterm pid;
- so.flags = SPO_LINK;
+ so.flags = erts_default_spo_flags|SPO_LINK;
pid = erl_create_process(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, &so);
if (is_non_value(pid)) {
BIF_ERROR(BIF_P, so.error_code);
@@ -878,9 +1006,11 @@ BIF_RETTYPE spawn_opt_1(BIF_ALIST_1)
/*
* Store default values for options.
*/
- so.flags = SPO_USE_ARGS;
+ so.flags = erts_default_spo_flags|SPO_USE_ARGS;
so.min_heap_size = H_MIN_SIZE;
so.min_vheap_size = BIN_VH_MIN_SIZE;
+ so.max_heap_size = H_MAX_SIZE;
+ so.max_heap_flags = H_MAX_FLAGS;
so.priority = PRIORITY_NORMAL;
so.max_gen_gcs = (Uint16) erts_smp_atomic32_read_nob(&erts_max_gen_gcs);
so.scheduler = 0;
@@ -913,6 +1043,19 @@ BIF_RETTYPE spawn_opt_1(BIF_ALIST_1)
so.priority = PRIORITY_LOW;
else
goto error;
+ } else if (arg == am_message_queue_data) {
+ switch (val) {
+ case am_on_heap:
+ so.flags &= ~SPO_OFF_HEAP_MSGQ;
+ so.flags |= SPO_ON_HEAP_MSGQ;
+ break;
+ case am_off_heap:
+ so.flags &= ~SPO_ON_HEAP_MSGQ;
+ so.flags |= SPO_OFF_HEAP_MSGQ;
+ break;
+ default:
+ goto error;
+ }
} else if (arg == am_min_heap_size && is_small(val)) {
Sint min_heap_size = signed_val(val);
if (min_heap_size < 0) {
@@ -922,6 +1065,9 @@ BIF_RETTYPE spawn_opt_1(BIF_ALIST_1)
} else {
so.min_heap_size = erts_next_heap_size(min_heap_size, 0);
}
+ } else if (arg == am_max_heap_size) {
+ if (!erts_max_heap_size(val, &so.max_heap_size, &so.max_heap_flags))
+ goto error;
} else if (arg == am_min_bin_vheap_size && is_small(val)) {
Sint min_vheap_size = signed_val(val);
if (min_vheap_size < 0) {
@@ -955,6 +1101,10 @@ BIF_RETTYPE spawn_opt_1(BIF_ALIST_1)
goto error;
}
+ if (so.max_heap_size != 0 && so.max_heap_size < so.min_heap_size) {
+ goto error;
+ }
+
/*
* Spawn the process.
*/
@@ -984,6 +1134,7 @@ BIF_RETTYPE unlink_1(BIF_ALIST_1)
Process *rp;
DistEntry *dep;
ErtsLink *l = NULL, *rl = NULL;
+ ErtsProcLocks cp_locks = ERTS_PROC_LOCK_MAIN;
/*
* SMP specific note concerning incoming exit signals:
@@ -998,7 +1149,7 @@ BIF_RETTYPE unlink_1(BIF_ALIST_1)
*/
if (IS_TRACED_FL(BIF_P, F_TRACE_PROCS)) {
- trace_proc(BIF_P, BIF_P, am_unlink, BIF_ARG_1);
+ trace_proc(BIF_P, cp_locks, BIF_P, am_unlink, BIF_ARG_1);
}
if (is_internal_port(BIF_ARG_1)) {
@@ -1103,10 +1254,10 @@ BIF_RETTYPE unlink_1(BIF_ALIST_1)
erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS);
+ cp_locks |= ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS;
+
/* get process struct */
- rp = erts_pid2proc_opt(BIF_P, (ERTS_PROC_LOCK_MAIN
- | ERTS_PROC_LOCK_LINK
- | ERTS_PROC_LOCK_STATUS),
+ rp = erts_pid2proc_opt(BIF_P, cp_locks,
BIF_ARG_1, ERTS_PROC_LOCK_LINK,
ERTS_P2P_FLG_ALLOW_OTHER_X);
@@ -1132,14 +1283,17 @@ BIF_RETTYPE unlink_1(BIF_ALIST_1)
erts_destroy_link(rl);
if (IS_TRACED_FL(rp, F_TRACE_PROCS) && rl != NULL) {
- trace_proc(BIF_P, rp, am_getting_unlinked, BIF_P->common.id);
+ erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_STATUS);
+ cp_locks &= ~ERTS_PROC_LOCK_STATUS;
+ trace_proc(BIF_P, (ERTS_PROC_LOCK_MAIN | ERTS_PROC_LOCK_LINK),
+ rp, am_getting_unlinked, BIF_P->common.id);
}
if (rp != BIF_P)
erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
}
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS);
+ erts_smp_proc_unlock(BIF_P, cp_locks & ~ERTS_PROC_LOCK_MAIN);
BIF_RET(am_true);
@@ -1550,7 +1704,32 @@ static BIF_RETTYPE process_flag_aux(Process *BIF_P,
scb->n = 0;
}
- scb = ERTS_PROC_SET_SAVED_CALLS_BUF(rp, ERTS_PROC_LOCK_MAIN, scb);
+#ifdef HIPE
+ if (rp->flags & F_HIPE_MODE) {
+ ASSERT(!ERTS_PROC_GET_SAVED_CALLS_BUF(rp));
+ scb = ERTS_PROC_SET_SUSPENDED_SAVED_CALLS_BUF(rp, scb);
+ }
+ else
+#endif
+ {
+#ifdef HIPE
+ ASSERT(!ERTS_PROC_GET_SUSPENDED_SAVED_CALLS_BUF(rp));
+#endif
+ scb = ERTS_PROC_SET_SAVED_CALLS_BUF(rp, scb);
+ if (rp == BIF_P && ((scb && i == 0) || (!scb && i != 0))) {
+ /* Adjust fcalls to match save calls setting... */
+ if (i == 0)
+ BIF_P->fcalls += CONTEXT_REDS; /* disabled it */
+ else
+ BIF_P->fcalls -= CONTEXT_REDS; /* enabled it */
+
+ /*
+ * Make sure we reschedule immediately so the
+ * change take effect at once.
+ */
+ ERTS_VBUMP_ALL_REDS(BIF_P);
+ }
+ }
if (!scb)
old_value = make_small(0);
@@ -1559,12 +1738,7 @@ static BIF_RETTYPE process_flag_aux(Process *BIF_P,
erts_free(ERTS_ALC_T_CALLS_BUF, (void *) scb);
}
- /* Make sure the process in question is rescheduled
- immediately, if it's us, so the call saving takes effect. */
- if (rp == BIF_P)
- BIF_RET2(old_value, CONTEXT_REDS);
- else
- BIF_RET(old_value);
+ BIF_RET(old_value);
}
error:
@@ -1578,9 +1752,7 @@ BIF_RETTYPE process_flag_2(BIF_ALIST_2)
if (is_not_atom(BIF_ARG_2)) {
goto error;
}
- old_value = erts_proc_set_error_handler(BIF_P,
- ERTS_PROC_LOCK_MAIN,
- BIF_ARG_2);
+ old_value = erts_proc_set_error_handler(BIF_P, BIF_ARG_2);
BIF_RET(old_value);
}
else if (BIF_ARG_1 == am_priority) {
@@ -1649,7 +1821,7 @@ BIF_RETTYPE process_flag_2(BIF_ALIST_2)
ERTS_PSFLG_BOUND);
}
- curr = ERTS_GET_SCHEDULER_DATA_FROM_PROC(BIF_P)->run_queue;
+ curr = erts_proc_sched_data(BIF_P)->run_queue;
old = (ERTS_PSFLG_BOUND & state) ? curr : NULL;
ASSERT(!old || old == curr);
@@ -1694,6 +1866,29 @@ BIF_RETTYPE process_flag_2(BIF_ALIST_2)
}
BIF_RET(old_value);
}
+ else if (BIF_ARG_1 == am_max_heap_size) {
+ Eterm *hp;
+ Uint sz = 0, max_heap_size, max_heap_flags;
+
+ if (!erts_max_heap_size(BIF_ARG_2, &max_heap_size, &max_heap_flags))
+ goto error;
+
+ if ((max_heap_size < MIN_HEAP_SIZE(BIF_P) && max_heap_size != 0))
+ goto error;
+
+ erts_max_heap_size_map(MAX_HEAP_SIZE_GET(BIF_P), MAX_HEAP_SIZE_FLAGS_GET(BIF_P), NULL, &sz);
+ hp = HAlloc(BIF_P, sz);
+ old_value = erts_max_heap_size_map(MAX_HEAP_SIZE_GET(BIF_P), MAX_HEAP_SIZE_FLAGS_GET(BIF_P), &hp, NULL);
+ MAX_HEAP_SIZE_SET(BIF_P, max_heap_size);
+ MAX_HEAP_SIZE_FLAGS_SET(BIF_P, max_heap_flags);
+ BIF_RET(old_value);
+ }
+ else if (BIF_ARG_1 == am_message_queue_data) {
+ old_value = erts_change_message_queue_management(BIF_P, BIF_ARG_2);
+ if (is_non_value(old_value))
+ goto error;
+ BIF_RET(old_value);
+ }
else if (BIF_ARG_1 == am_sensitive) {
Uint is_sensitive;
if (BIF_ARG_2 == am_true) {
@@ -1713,7 +1908,9 @@ BIF_RETTYPE process_flag_2(BIF_ALIST_2)
ERTS_TRACE_FLAGS(BIF_P) &= ~F_SENSITIVE;
}
erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCKS_ALL_MINOR);
- BIF_RET(old_value);
+ /* make sure to bump all reds so that we get
+ rescheduled immediately so setting takes effect */
+ BIF_RET2(old_value, CONTEXT_REDS);
}
else if (BIF_ARG_1 == am_monitor_nodes) {
/*
@@ -1752,10 +1949,18 @@ BIF_RETTYPE process_flag_3(BIF_ALIST_3)
Process *rp;
Eterm res;
- if ((rp = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN,
- BIF_ARG_1, ERTS_PROC_LOCK_MAIN)) == NULL) {
+#ifdef ERTS_SMP
+ rp = erts_pid2proc_not_running(BIF_P, ERTS_PROC_LOCK_MAIN,
+ BIF_ARG_1, ERTS_PROC_LOCK_MAIN);
+ if (rp == ERTS_PROC_LOCK_BUSY)
+ ERTS_BIF_YIELD3(bif_export[BIF_process_flag_3], BIF_P,
+ BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
+#else
+ rp = erts_proc_lookup(BIF_ARG_1);
+#endif
+
+ if (!rp)
BIF_ERROR(BIF_P, BADARG);
- }
res = process_flag_aux(BIF_P, rp, BIF_ARG_2, BIF_ARG_3);
@@ -1888,7 +2093,7 @@ static Sint remote_send(Process *p, DistEntry *dep,
}
if (res >= 0) {
- if (IS_TRACED(p))
+ if (IS_TRACED_FL(p, F_TRACE_SEND))
trace_send(p, full_to, msg);
if (ERTS_PROC_GET_SAVED_CALLS_BUF(p))
save_calls(p, &exp_send);
@@ -1907,7 +2112,7 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext* ctx)
Eterm* tp;
if (is_internal_pid(to)) {
- if (IS_TRACED(p))
+ if (IS_TRACED_FL(p, F_TRACE_SEND))
trace_send(p, to, msg);
if (ERTS_PROC_GET_SAVED_CALLS_BUF(p))
save_calls(p, &exp_send);
@@ -1934,9 +2139,9 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext* ctx)
} else if (is_atom(to)) {
Eterm id = erts_whereis_name_to_id(p, to);
- rp = erts_proc_lookup(id);
+ rp = erts_proc_lookup_raw(id);
if (rp) {
- if (IS_TRACED(p))
+ if (IS_TRACED_FL(p, F_TRACE_SEND))
trace_send(p, to, msg);
if (ERTS_PROC_GET_SAVED_CALLS_BUF(p))
save_calls(p, &exp_send);
@@ -1952,7 +2157,7 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext* ctx)
goto port_common;
}
- if (IS_TRACED(p))
+ if (IS_TRACED_FL(p, F_TRACE_SEND))
trace_send(p, to, msg);
if (ERTS_PROC_GET_SAVED_CALLS_BUF(p))
save_calls(p, &exp_send);
@@ -1983,11 +2188,20 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext* ctx)
port_common:
ret_val = 0;
-
+
if (pt) {
int ps_flags = ctx->suspend ? 0 : ERTS_PORT_SIG_FLG_NOSUSPEND;
*refp = NIL;
+ if (IS_TRACED_FL(p, F_TRACE_SEND)) /* trace once only !! */
+ trace_send(p, portid, msg);
+
+ if (have_seqtrace(SEQ_TRACE_TOKEN(p))) {
+ seq_trace_update_send(p);
+ seq_trace_output(SEQ_TRACE_TOKEN(p), msg,
+ SEQ_TRACE_SEND, portid, p);
+ }
+
switch (erts_port_command(p, ps_flags, pt, msg, refp)) {
case ERTS_PORT_OP_CALLER_EXIT:
/* We are exiting... */
@@ -2020,22 +2234,10 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext* ctx)
break;
}
}
-
- if (IS_TRACED(p)) /* trace once only !! */
- trace_send(p, portid, msg);
+
if (ERTS_PROC_GET_SAVED_CALLS_BUF(p))
save_calls(p, &exp_send);
-
- if (SEQ_TRACE_TOKEN(p) != NIL
-#ifdef USE_VM_PROBES
- && SEQ_TRACE_TOKEN(p) != am_have_dt_utag
-#endif
- ) {
- seq_trace_update_send(p);
- seq_trace_output(SEQ_TRACE_TOKEN(p), msg,
- SEQ_TRACE_SEND, portid, p);
- }
-
+
if (ERTS_PROC_IS_EXITING(p)) {
KILL_CATCHES(p); /* Must exit */
return SEND_USER_ERROR;
@@ -2058,7 +2260,7 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext* ctx)
if (dep == erts_this_dist_entry) {
Eterm id;
erts_deref_dist_entry(dep);
- if (IS_TRACED(p))
+ if (IS_TRACED_FL(p, F_TRACE_SEND))
trace_send(p, to, msg);
if (ERTS_PROC_GET_SAVED_CALLS_BUF(p))
save_calls(p, &exp_send);
@@ -2089,7 +2291,7 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext* ctx)
}
return ret;
} else {
- if (IS_TRACED(p)) /* XXX Is this really neccessary ??? */
+ if (IS_TRACED_FL(p, F_TRACE_SEND))
trace_send(p, to, msg);
if (ERTS_PROC_GET_SAVED_CALLS_BUF(p))
save_calls(p, &exp_send);
@@ -2131,7 +2333,11 @@ BIF_RETTYPE send_3(BIF_ALIST_3)
int connect = !0;
Eterm l = opts;
Sint result;
+
DeclareTypedTmpHeap(ErtsSendContext, ctx, BIF_P);
+
+ ERTS_MSACC_PUSH_STATE_M_X();
+
UseTmpHeap(sizeof(ErtsSendContext)/sizeof(Eterm), BIF_P);
ctx->suspend = !0;
@@ -2160,7 +2366,10 @@ BIF_RETTYPE send_3(BIF_ALIST_3)
ref = NIL;
#endif
+ ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_SEND);
result = do_send(p, to, msg, &ref, ctx);
+ ERTS_MSACC_POP_STATE_M_X();
+
if (result > 0) {
ERTS_VBUMP_REDS(p, result);
if (ERTS_IS_PROC_OUT_OF_REDS(p))
@@ -2276,8 +2485,8 @@ Eterm erl_send(Process *p, Eterm to, Eterm msg)
Eterm ref;
Sint result;
DeclareTypedTmpHeap(ErtsSendContext, ctx, p);
+ ERTS_MSACC_PUSH_AND_SET_STATE_M_X(ERTS_MSACC_STATE_SEND);
UseTmpHeap(sizeof(ErtsSendContext)/sizeof(Eterm), p);
-
#ifdef DEBUG
ref = NIL;
#endif
@@ -2288,7 +2497,9 @@ Eterm erl_send(Process *p, Eterm to, Eterm msg)
ctx->dss.phase = ERTS_DSIG_SEND_PHASE_INIT;
result = do_send(p, to, msg, &ref, ctx);
-
+
+ ERTS_MSACC_POP_STATE_M_X();
+
if (result > 0) {
ERTS_VBUMP_REDS(p, result);
if (ERTS_IS_PROC_OUT_OF_REDS(p))
@@ -2812,7 +3023,7 @@ BIF_RETTYPE list_to_atom_1(BIF_ALIST_1)
{
Eterm res;
char *buf = (char *) erts_alloc(ERTS_ALC_T_TMP, MAX_ATOM_CHARACTERS);
- int i = intlist_to_buf(BIF_ARG_1, buf, MAX_ATOM_CHARACTERS);
+ Sint i = intlist_to_buf(BIF_ARG_1, buf, MAX_ATOM_CHARACTERS);
if (i < 0) {
erts_free(ERTS_ALC_T_TMP, (void *) buf);
@@ -2832,7 +3043,7 @@ BIF_RETTYPE list_to_atom_1(BIF_ALIST_1)
BIF_RETTYPE list_to_existing_atom_1(BIF_ALIST_1)
{
- int i;
+ Sint i;
char *buf = (char *) erts_alloc(ERTS_ALC_T_TMP, MAX_ATOM_CHARACTERS);
if ((i = intlist_to_buf(BIF_ARG_1, buf, MAX_ATOM_CHARACTERS)) < 0) {
@@ -2892,175 +3103,20 @@ BIF_RETTYPE integer_to_list_1(BIF_ALIST_1)
/**********************************************************************/
-/* convert a list of ascii ascii integer value to an integer */
-
-
-#define LTI_BAD_STRUCTURE 0
-#define LTI_NO_INTEGER 1
-#define LTI_SOME_INTEGER 2
-#define LTI_ALL_INTEGER 3
-
-static int do_list_to_integer(Process *p, Eterm orig_list,
- Eterm *integer, Eterm *rest)
-{
- Sint i = 0;
- Uint ui = 0;
- int skip = 0;
- int neg = 0;
- Sint n = 0;
- int m;
- int lg2;
- Eterm res;
- Eterm* hp;
- Eterm *hp_end;
- Eterm lst = orig_list;
- Eterm tail = lst;
- int error_res = LTI_BAD_STRUCTURE;
-
- if (is_nil(lst)) {
- error_res = LTI_NO_INTEGER;
- error:
- *rest = tail;
- *integer = make_small(0);
- return error_res;
- }
- if (is_not_list(lst))
- goto error;
-
- /* if first char is a '-' then it is a negative integer */
- if (CAR(list_val(lst)) == make_small('-')) {
- neg = 1;
- skip = 1;
- lst = CDR(list_val(lst));
- if (is_not_list(lst)) {
- tail = lst;
- error_res = LTI_NO_INTEGER;
- goto error;
- }
- } else if (CAR(list_val(lst)) == make_small('+')) {
- /* ignore plus */
- skip = 1;
- lst = CDR(list_val(lst));
- if (is_not_list(lst)) {
- tail = lst;
- error_res = LTI_NO_INTEGER;
- goto error;
- }
- }
-
- /* Calculate size and do type check */
-
- while(1) {
- if (is_not_small(CAR(list_val(lst)))) {
- break;
- }
- if (unsigned_val(CAR(list_val(lst))) < '0' ||
- unsigned_val(CAR(list_val(lst))) > '9') {
- break;
- }
- ui = ui * 10;
- ui = ui + unsigned_val(CAR(list_val(lst))) - '0';
- n++;
- lst = CDR(list_val(lst));
- if (is_nil(lst)) {
- break;
- }
- if (is_not_list(lst)) {
- break;
- }
- }
-
- tail = lst;
- if (!n) {
- error_res = LTI_NO_INTEGER;
- goto error;
- }
-
-
- /* If n <= 8 then we know it's a small int
- ** since 2^27 = 134217728. If n > 8 then we must
- ** construct a bignum and let that routine do the checking
- */
-
- if (n <= SMALL_DIGITS) { /* It must be small */
- if (neg) i = -(Sint)ui;
- else i = (Sint)ui;
- res = make_small(i);
- } else {
- /* Convert from log10 to log2 by multiplying with 1/log10(2)=3.3219
- which we round up to (3 + 1/3) */
- lg2 = (n+1)*3 + (n+1)/3 + 1;
- m = (lg2+D_EXP-1)/D_EXP; /* number of digits */
- m = BIG_NEED_SIZE(m); /* number of words + thing */
-
- hp = HAlloc(p, m);
- hp_end = hp + m;
-
- lst = orig_list;
- if (skip)
- lst = CDR(list_val(lst));
-
- /* load first digits (at least one digit) */
- if ((i = (n % D_DECIMAL_EXP)) == 0)
- i = D_DECIMAL_EXP;
- n -= i;
- m = 0;
- while(i--) {
- m = 10*m + (unsigned_val(CAR(list_val(lst))) - '0');
- lst = CDR(list_val(lst));
- }
- res = small_to_big(m, hp); /* load first digits */
-
- while(n) {
- i = D_DECIMAL_EXP;
- n -= D_DECIMAL_EXP;
- m = 0;
- while(i--) {
- m = 10*m + (unsigned_val(CAR(list_val(lst))) - '0');
- lst = CDR(list_val(lst));
- }
- if (is_small(res))
- res = small_to_big(signed_val(res), hp);
- res = big_times_small(res, D_DECIMAL_BASE, hp);
- if (is_small(res))
- res = small_to_big(signed_val(res), hp);
- res = big_plus_small(res, m, hp);
- }
-
- if (neg) {
- if (is_small(res))
- res = make_small(-signed_val(res));
- else {
- Uint *big = big_val(res); /* point to thing */
- *big = bignum_header_neg(*big);
- }
- }
-
- if (is_not_small(res)) {
- res = big_plus_small(res, 0, hp); /* includes conversion to small */
+/*
+ * Converts a list of ascii base10 digits to an integer fully or partially.
+ * Returns result and the remaining tail.
+ * On error returns: {error,not_a_list}, or {error, no_integer}
+ */
- if (is_not_small(res)) {
- hp += (big_arity(res)+1);
- }
- }
- HRelease(p,hp_end,hp);
- }
- *integer = res;
- *rest = tail;
- if (tail != NIL) {
- return LTI_SOME_INTEGER;
- }
- return LTI_ALL_INTEGER;
-}
BIF_RETTYPE string_to_integer_1(BIF_ALIST_1)
{
Eterm res;
Eterm tail;
Eterm *hp;
/* must be a list */
- switch (do_list_to_integer(BIF_P,BIF_ARG_1,&res,&tail)) {
- /* HAlloc after do_list_to_integer as it
- might HAlloc itself (bignum) */
+ switch (erts_list_to_integer(BIF_P, BIF_ARG_1, 10, &res, &tail)) {
+ /* HAlloc after erts_list_to_integer as it might HAlloc itself (bignum) */
case LTI_BAD_STRUCTURE:
hp = HAlloc(BIF_P,3);
BIF_RET(TUPLE2(hp, am_error, am_not_a_list));
@@ -3075,13 +3131,14 @@ BIF_RETTYPE string_to_integer_1(BIF_ALIST_1)
BIF_RETTYPE list_to_integer_1(BIF_ALIST_1)
{
- /* Using do_list_to_integer is about twice as fast as using
+ /* Using erts_list_to_integer is about twice as fast as using
erts_chars_to_integer because we do not have to copy the
entire list */
Eterm res;
Eterm dummy;
/* must be a list */
- if (do_list_to_integer(BIF_P,BIF_ARG_1,&res,&dummy) != LTI_ALL_INTEGER) {
+ if (erts_list_to_integer(BIF_P, BIF_ARG_1, 10,
+ &res, &dummy) != LTI_ALL_INTEGER) {
BIF_ERROR(BIF_P,BADARG);
}
BIF_RET(res);
@@ -3089,14 +3146,12 @@ BIF_RETTYPE list_to_integer_1(BIF_ALIST_1)
BIF_RETTYPE list_to_integer_2(BIF_ALIST_2)
{
-
/* Bif implementation is about 50% faster than pure erlang,
and since we have erts_chars_to_integer now it is simpler
as well. This could be optmized further if we did not have to
copy the list to buf. */
- int i;
- Eterm res;
- char *buf = NULL;
+ Sint i;
+ Eterm res, dummy;
int base;
i = erts_list_length(BIF_ARG_1);
@@ -3104,31 +3159,16 @@ BIF_RETTYPE list_to_integer_2(BIF_ALIST_2)
BIF_ERROR(BIF_P, BADARG);
base = signed_val(BIF_ARG_2);
-
+
if (base < 2 || base > 36)
BIF_ERROR(BIF_P, BADARG);
- /* Take fast path if base it 10 */
- if (base == 10)
- return list_to_integer_1(BIF_P,&BIF_ARG_1);
-
- buf = (char *) erts_alloc(ERTS_ALC_T_TMP, i + 1);
-
- if (intlist_to_buf(BIF_ARG_1, buf, i) < 0)
- goto list_to_integer_1_error;
- buf[i] = '\0'; /* null terminal */
-
- if ((res = erts_chars_to_integer(BIF_P,buf,i,base)) == THE_NON_VALUE)
- goto list_to_integer_1_error;
-
- erts_free(ERTS_ALC_T_TMP, (void *) buf);
+ if (erts_list_to_integer(BIF_P, BIF_ARG_1, base,
+ &res, &dummy) != LTI_ALL_INTEGER) {
+ BIF_ERROR(BIF_P,BADARG);
+ }
BIF_RET(res);
-
- list_to_integer_1_error:
- erts_free(ERTS_ALC_T_TMP, (void *) buf);
- BIF_ERROR(BIF_P, BADARG);
-
- }
+}
/**********************************************************************/
@@ -3434,7 +3474,7 @@ static BIF_RETTYPE do_charbuf_to_float(Process *BIF_P,char *buf) {
BIF_RETTYPE list_to_float_1(BIF_ALIST_1)
{
- int i;
+ Sint i;
Eterm res;
char *buf = NULL;
@@ -3551,7 +3591,7 @@ BIF_RETTYPE list_to_tuple_1(BIF_ALIST_1)
Eterm* cons;
Eterm res;
Eterm* hp;
- int len;
+ Sint len;
if ((len = erts_list_length(list)) < 0 || len > ERTS_MAX_TUPLE_SIZE) {
BIF_ERROR(BIF_P, BADARG);
@@ -3827,11 +3867,9 @@ BIF_RETTYPE now_0(BIF_ALIST_0)
BIF_RETTYPE garbage_collect_0(BIF_ALIST_0)
{
- int reds;
-
FLAGS(BIF_P) |= F_NEED_FULLSWEEP;
- reds = erts_garbage_collect(BIF_P, 0, NULL, 0);
- BIF_RET2(am_true, reds);
+ erts_garbage_collect(BIF_P, 0, NULL, 0);
+ BIF_RET(am_true);
}
/**********************************************************************/
@@ -3900,7 +3938,7 @@ BIF_RETTYPE display_string_1(BIF_ALIST_1)
{
Process* p = BIF_P;
Eterm string = BIF_ARG_1;
- int len = is_string(string);
+ Sint len = is_string(string);
char *str;
if (len <= 0) {
@@ -3923,59 +3961,11 @@ BIF_RETTYPE display_nl_0(BIF_ALIST_0)
/**********************************************************************/
-/* stop the system */
-/* ARGSUSED */
-BIF_RETTYPE halt_0(BIF_ALIST_0)
-{
- VERBOSE(DEBUG_SYSTEM,("System halted by BIF halt()\n"));
- erts_halt(0);
- ERTS_BIF_YIELD1(bif_export[BIF_halt_1], BIF_P, am_undefined);
-}
-
-/**********************************************************************/
#define HALT_MSG_SIZE 200
-static char halt_msg[HALT_MSG_SIZE];
-
-/* stop the system with exit code */
-/* ARGSUSED */
-BIF_RETTYPE halt_1(BIF_ALIST_1)
-{
- Uint code;
-
- if (term_to_Uint_mask(BIF_ARG_1, &code)) {
- int pos_int_code = (int) (code & INT_MAX);
- VERBOSE(DEBUG_SYSTEM,("System halted by BIF halt(%T)\n", BIF_ARG_1));
- erts_halt(pos_int_code);
- ERTS_BIF_YIELD1(bif_export[BIF_halt_1], BIF_P, am_undefined);
- }
- else if (ERTS_IS_ATOM_STR("abort", BIF_ARG_1)) {
- VERBOSE(DEBUG_SYSTEM,("System halted by BIF halt(%T)\n", BIF_ARG_1));
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- erts_exit(ERTS_ABORT_EXIT, "");
- }
- else if (is_string(BIF_ARG_1) || BIF_ARG_1 == NIL) {
- int i;
-
- if ((i = intlist_to_buf(BIF_ARG_1, halt_msg, HALT_MSG_SIZE-1)) < 0) {
- goto error;
- }
- halt_msg[i] = '\0';
- VERBOSE(DEBUG_SYSTEM,("System halted by BIF halt(%T)\n", BIF_ARG_1));
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- erts_exit(ERTS_DUMP_EXIT, "%s\n", halt_msg);
- }
- else
- goto error;
- return NIL; /* Pedantic (lint does not know about erts_exit) */
- error:
- BIF_ERROR(BIF_P, BADARG);
-}
-
-/**********************************************************************/
+static char halt_msg[HALT_MSG_SIZE+1];
/* stop the system with exit code and flags */
-/* ARGSUSED */
BIF_RETTYPE halt_2(BIF_ALIST_2)
{
Uint code;
@@ -4011,7 +4001,7 @@ BIF_RETTYPE halt_2(BIF_ALIST_2)
("System halted by BIF halt(%T, %T)\n", BIF_ARG_1, BIF_ARG_2));
if (flush) {
erts_halt(pos_int_code);
- ERTS_BIF_YIELD1(bif_export[BIF_halt_1], BIF_P, am_undefined);
+ ERTS_BIF_YIELD2(bif_export[BIF_halt_2], BIF_P, am_undefined, am_undefined);
}
else {
erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
@@ -4025,11 +4015,14 @@ BIF_RETTYPE halt_2(BIF_ALIST_2)
erts_exit(ERTS_ABORT_EXIT, "");
}
else if (is_string(BIF_ARG_1) || BIF_ARG_1 == NIL) {
- int i;
+ Sint i;
- if ((i = intlist_to_buf(BIF_ARG_1, halt_msg, HALT_MSG_SIZE-1)) < 0) {
- goto error;
- }
+ if ((i = intlist_to_buf(BIF_ARG_1, halt_msg, HALT_MSG_SIZE)) == -1) {
+ goto error;
+ }
+ if (i == -2) /* truncated string */
+ i = HALT_MSG_SIZE;
+ ASSERT(i >= 0 && i <= HALT_MSG_SIZE);
halt_msg[i] = '\0';
VERBOSE(DEBUG_SYSTEM,
("System halted by BIF halt(%T, %T)\n", BIF_ARG_1, BIF_ARG_2));
@@ -4122,16 +4115,9 @@ BIF_RETTYPE make_fun_3(BIF_ALIST_3)
if (arity < 0) {
goto error;
}
-#if HALFWORD_HEAP
- hp = HAlloc(BIF_P, 3);
- hp[0] = HEADER_EXPORT;
- /* Yes, May be misaligned, but X86_64 will fix it... */
- *((Export **) (hp+1)) = erts_export_get_or_make_stub(BIF_ARG_1, BIF_ARG_2, (Uint) arity);
-#else
hp = HAlloc(BIF_P, 2);
hp[0] = HEADER_EXPORT;
hp[1] = (Eterm) erts_export_get_or_make_stub(BIF_ARG_1, BIF_ARG_2, (Uint) arity);
-#endif
BIF_RET(make_export(hp));
}
@@ -4175,7 +4161,7 @@ BIF_RETTYPE list_to_pid_1(BIF_ALIST_1)
{
Uint a = 0, b = 0, c = 0;
char* cp;
- int i;
+ Sint i;
DistEntry *dep = NULL;
char *buf = (char *) erts_alloc(ERTS_ALC_T_TMP, 65);
/*
@@ -4319,8 +4305,30 @@ BIF_RETTYPE group_leader_2(BIF_ALIST_2)
else {
locks &= ~ERTS_PROC_LOCK_STATUS;
erts_smp_proc_unlock(new_member, ERTS_PROC_LOCK_STATUS);
- new_member->group_leader = STORE_NC_IN_PROC(new_member,
- BIF_ARG_1);
+ if (new_member == BIF_P
+ || !(erts_smp_atomic32_read_nob(&new_member->state)
+ & (ERTS_PSFLG_DIRTY_RUNNING|ERTS_PSFLG_DIRTY_RUNNING_SYS))) {
+ new_member->group_leader = STORE_NC_IN_PROC(new_member,
+ BIF_ARG_1);
+ }
+ else {
+ ErlHeapFragment *bp;
+ Eterm *hp;
+ /*
+ * Other process executing on a dirty scheduler,
+ * so we are not allowed to write to its heap.
+ * Store in heap fragment.
+ */
+
+ bp = new_message_buffer(NC_HEAP_SIZE(BIF_ARG_1));
+ hp = bp->mem;
+ new_member->group_leader = STORE_NC(&hp,
+ &new_member->off_heap,
+ BIF_ARG_1);
+ bp->next = new_member->mbuf;
+ new_member->mbuf = bp;
+ new_member->mbuf_sz += bp->used_size;
+ }
}
}
@@ -4352,22 +4360,33 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2)
Sint n;
if (BIF_ARG_1 == am_multi_scheduling) {
- if (BIF_ARG_2 == am_block || BIF_ARG_2 == am_unblock) {
+ if (BIF_ARG_2 == am_block || BIF_ARG_2 == am_unblock
+ || BIF_ARG_2 == am_block_normal || BIF_ARG_2 == am_unblock_normal) {
#ifndef ERTS_SMP
BIF_RET(am_disabled);
#else
+ int block = (BIF_ARG_2 == am_block
+ || BIF_ARG_2 == am_block_normal);
+ int normal = (BIF_ARG_2 == am_block_normal
+ || BIF_ARG_2 == am_unblock_normal);
if (erts_no_schedulers == 1)
BIF_RET(am_disabled);
else {
switch (erts_block_multi_scheduling(BIF_P,
ERTS_PROC_LOCK_MAIN,
- BIF_ARG_2 == am_block,
+ block,
+ normal,
0)) {
case ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED:
BIF_RET(am_blocked);
+ case ERTS_SCHDLR_SSPND_DONE_NMSCHED_BLOCKED:
+ BIF_RET(am_blocked_normal);
case ERTS_SCHDLR_SSPND_YIELD_DONE_MSCHED_BLOCKED:
ERTS_BIF_YIELD_RETURN_X(BIF_P, am_blocked,
am_multi_scheduling);
+ case ERTS_SCHDLR_SSPND_YIELD_DONE_NMSCHED_BLOCKED:
+ ERTS_BIF_YIELD_RETURN_X(BIF_P, am_blocked_normal,
+ am_multi_scheduling);
case ERTS_SCHDLR_SSPND_DONE:
BIF_RET(am_enabled);
case ERTS_SCHDLR_SSPND_YIELD_RESTART:
@@ -4400,11 +4419,7 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2)
switch (erts_set_schedulers_online(BIF_P,
ERTS_PROC_LOCK_MAIN,
signed_val(BIF_ARG_2),
- &old_no
-#ifdef ERTS_DIRTY_SCHEDULERS
- , 0
-#endif
- )) {
+ &old_no, 0)) {
case ERTS_SCHDLR_SSPND_DONE:
BIF_RET(make_small(old_no));
case ERTS_SCHDLR_SSPND_YIELD_RESTART:
@@ -4464,6 +4479,31 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2)
erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
BIF_RET(make_small(oval));
+ } else if (BIF_ARG_1 == am_max_heap_size) {
+
+ Eterm *hp, old_value;
+ Uint sz = 0, max_heap_size, max_heap_flags;
+
+ if (!erts_max_heap_size(BIF_ARG_2, &max_heap_size, &max_heap_flags))
+ goto error;
+
+ if (max_heap_size < H_MIN_SIZE && max_heap_size != 0)
+ goto error;
+
+ erts_max_heap_size_map(H_MAX_SIZE, H_MAX_FLAGS, NULL, &sz);
+ hp = HAlloc(BIF_P, sz);
+ old_value = erts_max_heap_size_map(H_MAX_SIZE, H_MAX_FLAGS, &hp, NULL);
+
+ erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_smp_thr_progress_block();
+
+ H_MAX_SIZE = max_heap_size;
+ H_MAX_FLAGS = max_heap_flags;
+
+ erts_smp_thr_progress_unblock();
+ erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+
+ BIF_RET(old_value);
} else if (BIF_ARG_1 == am_display_items) {
int oval = display_items;
if (!is_small(BIF_ARG_2) || (n = signed_val(BIF_ARG_2)) < 0) {
@@ -4484,15 +4524,37 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2)
} else if (BIF_ARG_1 == am_trace_control_word) {
BIF_RET(db_set_trace_control_word(BIF_P, BIF_ARG_2));
} else if (BIF_ARG_1 == am_sequential_tracer) {
- Eterm old_value = erts_set_system_seq_tracer(BIF_P,
- ERTS_PROC_LOCK_MAIN,
- BIF_ARG_2);
- if (old_value != THE_NON_VALUE) {
- BIF_RET(old_value);
- }
+ ErtsTracer new_seq_tracer, old_seq_tracer;
+ Eterm ret;
+
+ if (BIF_ARG_2 == am_false)
+ new_seq_tracer = erts_tracer_nil;
+ else
+ new_seq_tracer = erts_term_to_tracer(THE_NON_VALUE, BIF_ARG_2);
+
+ if (new_seq_tracer == THE_NON_VALUE)
+ goto error;
+
+ old_seq_tracer = erts_set_system_seq_tracer(BIF_P,
+ ERTS_PROC_LOCK_MAIN,
+ new_seq_tracer);
+
+ ERTS_TRACER_CLEAR(&new_seq_tracer);
+
+ if (old_seq_tracer == THE_NON_VALUE)
+ goto error;
+
+ if (ERTS_TRACER_IS_NIL(old_seq_tracer))
+ BIF_RET(am_false);
+
+ ret = erts_tracer_to_term(BIF_P, old_seq_tracer);
+
+ ERTS_TRACER_CLEAR(&old_seq_tracer);
+
+ BIF_RET(ret);
} else if (BIF_ARG_1 == make_small(1)) {
int i, max;
- ErlMessage* mp;
+ ErtsMessage* mp;
erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
erts_smp_thr_progress_block();
@@ -4585,6 +4647,31 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2)
default:
ERTS_INTERNAL_ERROR("Unknown state");
}
+#ifdef ERTS_ENABLE_MSACC
+ } else if (BIF_ARG_1 == am_microstate_accounting) {
+ Eterm threads;
+ if (BIF_ARG_2 == am_true || BIF_ARG_2 == am_false) {
+ erts_aint32_t new = BIF_ARG_2 == am_true ? ERTS_MSACC_ENABLE : ERTS_MSACC_DISABLE;
+ erts_aint32_t old = erts_smp_atomic32_xchg_nob(&msacc, new);
+ Eterm ref = erts_msacc_request(BIF_P, new, &threads);
+ if (is_non_value(ref))
+ BIF_RET(old ? am_true : am_false);
+ BIF_TRAP3(await_msacc_mod_trap,
+ BIF_P,
+ ref,
+ old ? am_true : am_false,
+ threads);
+ } else if (BIF_ARG_2 == am_reset) {
+ Eterm ref = erts_msacc_request(BIF_P, ERTS_MSACC_RESET, &threads);
+ erts_aint32_t old = erts_smp_atomic32_read_nob(&msacc);
+ ASSERT(is_value(ref));
+ BIF_TRAP3(await_msacc_mod_trap,
+ BIF_P,
+ ref,
+ old ? am_true : am_false,
+ threads);
+ }
+#endif
} else if (ERTS_IS_ATOM_STR("scheduling_statistics", BIF_ARG_1)) {
int what;
if (ERTS_IS_ATOM_STR("disable", BIF_ARG_2))
@@ -4637,7 +4724,7 @@ BIF_RETTYPE hash_2(BIF_ALIST_2)
if ((range = signed_val(BIF_ARG_2)) <= 0) { /* [1..MAX_SMALL] */
BIF_ERROR(BIF_P, BADARG);
}
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
if (range > ((1L << 27) - 1))
BIF_ERROR(BIF_P, BADARG);
#endif
@@ -4709,7 +4796,7 @@ BIF_RETTYPE phash2_2(BIF_ALIST_2)
/*
* Return either a small or a big. Use the heap for bigs if there is room.
*/
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
BIF_RET(make_small(final_hash));
#else
if (IS_USMALL(0, final_hash)) {
@@ -4749,29 +4836,27 @@ BIF_RETTYPE erts_internal_cmp_term_2(BIF_ALIST_2) {
/*
* Processes doing yield on return in a bif ends up in bif_return_trap().
*/
-static BIF_RETTYPE bif_return_trap(
-#ifdef DEBUG
- BIF_ALIST_2
-#else
- BIF_ALIST_1
-#endif
- )
+static BIF_RETTYPE bif_return_trap(BIF_ALIST_2)
{
-#ifdef DEBUG
+ Eterm res = BIF_ARG_1;
+
switch (BIF_ARG_2) {
- case am_multi_scheduling:
#ifdef ERTS_SMP
- erts_dbg_multi_scheduling_return_trap(BIF_P, BIF_ARG_1);
-#endif
- break;
- case am_schedulers_online:
+ case am_multi_scheduling: {
+ int msb = erts_is_multi_scheduling_blocked();
+ if (msb > 0)
+ res = am_blocked;
+ else if (msb < 0)
+ res = am_blocked_normal;
+ else
+ ERTS_INTERNAL_ERROR("Unexpected multi scheduling block state");
break;
+ }
+#endif
default:
break;
}
-#endif
-
- BIF_RET(BIF_ARG_1);
+ BIF_RET(res);
}
/*
@@ -4803,7 +4888,7 @@ skip_current_msgq(Process *c_p)
res = 0;
}
else {
- ERTS_SMP_MSGQ_MV_INQ2PRIVQ(c_p);
+ ERTS_SMP_MSGQ_MV_INQ2PRIVQ(c_p);
c_p->msg.save = c_p->msg.last;
res = 1;
}
@@ -4876,17 +4961,12 @@ void erts_init_bif(void)
erts_smp_atomic_init_nob(&erts_dead_ports_ptr, (erts_aint_t) NULL);
/*
- * bif_return_trap/1 is a hidden BIF that bifs that need to
- * yield the calling process traps to. The only thing it does:
- * return the value passed as argument.
+ * bif_return_trap/2 is a hidden BIF that bifs that need to
+ * yield the calling process traps to.
*/
- erts_init_trap_export(&bif_return_trap_export, am_erlang, am_bif_return_trap,
-#ifdef DEBUG
- 2
-#else
- 1
-#endif
- , &bif_return_trap);
+ erts_init_trap_export(&bif_return_trap_export,
+ am_erlang, am_bif_return_trap, 2,
+ &bif_return_trap);
erts_await_result = erts_export_put(am_erts_internal,
am_await_result,
@@ -4914,8 +4994,12 @@ void erts_init_bif(void)
await_port_send_result_trap
= erts_export_put(am_erts_internal, am_await_port_send_result, 3);
await_sched_wall_time_mod_trap
- = erts_export_put(am_erlang, am_await_sched_wall_time_modifications, 2);
+ = erts_export_put(am_erlang, am_await_sched_wall_time_modifications, 2);
+ await_msacc_mod_trap
+ = erts_export_put(am_erts_internal, am_await_microstate_accounting_modifications, 3);
+
erts_smp_atomic32_init_nob(&sched_wall_time, 0);
+ erts_smp_atomic32_init_nob(&msacc, ERTS_MSACC_IS_ENABLED());
}
#ifdef HARDDEBUG
diff --git a/erts/emulator/beam/bif.h b/erts/emulator/beam/bif.h
index c6ed60376a..2203182a0d 100644
--- a/erts/emulator/beam/bif.h
+++ b/erts/emulator/beam/bif.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -52,24 +52,27 @@ extern Export *erts_convert_time_unit_trap;
(p)->fcalls = 0; \
else \
(p)->fcalls = -CONTEXT_REDS; \
+ ASSERT(ERTS_BIF_REDS_LEFT((p)) == 0); \
} while(0)
-
-#define ERTS_VBUMP_ALL_REDS(p) \
+#define ERTS_VBUMP_ALL_REDS_INTERNAL(p, fcalls) \
do { \
if (!ERTS_PROC_GET_SAVED_CALLS_BUF((p))) { \
- if ((p)->fcalls > 0) \
- ERTS_PROC_GET_SCHDATA((p))->virtual_reds += (p)->fcalls; \
- (p)->fcalls = 0; \
+ if ((fcalls) > 0) \
+ erts_proc_sched_data((p))->virtual_reds += (fcalls); \
+ (fcalls) = 0; \
} \
else { \
- if ((p)->fcalls > -CONTEXT_REDS) \
- ERTS_PROC_GET_SCHDATA((p))->virtual_reds \
- += ((p)->fcalls - (-CONTEXT_REDS)); \
- (p)->fcalls = -CONTEXT_REDS; \
+ if ((fcalls) > -CONTEXT_REDS) \
+ erts_proc_sched_data((p))->virtual_reds \
+ += ((fcalls) - (-CONTEXT_REDS)); \
+ (fcalls) = -CONTEXT_REDS; \
} \
} while(0)
+#define ERTS_VBUMP_ALL_REDS(p) \
+ ERTS_VBUMP_ALL_REDS_INTERNAL((p), (p)->fcalls)
+
#define BUMP_REDS(p, gc) do { \
ASSERT(p); \
ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(p));\
@@ -88,32 +91,56 @@ do { \
if (!ERTS_PROC_GET_SAVED_CALLS_BUF((p))) { \
if ((p)->fcalls >= reds) { \
(p)->fcalls -= reds; \
- ERTS_PROC_GET_SCHDATA((p))->virtual_reds += reds; \
+ erts_proc_sched_data((p))->virtual_reds += reds; \
} \
else { \
if ((p)->fcalls > 0) \
- ERTS_PROC_GET_SCHDATA((p))->virtual_reds += (p)->fcalls;\
+ erts_proc_sched_data((p))->virtual_reds += (p)->fcalls; \
(p)->fcalls = 0; \
} \
} \
else { \
if ((p)->fcalls >= reds - CONTEXT_REDS) { \
(p)->fcalls -= reds; \
- ERTS_PROC_GET_SCHDATA((p))->virtual_reds += reds; \
+ erts_proc_sched_data((p))->virtual_reds += reds; \
} \
else { \
if ((p)->fcalls > -CONTEXT_REDS) \
- ERTS_PROC_GET_SCHDATA((p))->virtual_reds \
+ erts_proc_sched_data((p))->virtual_reds \
+= (p)->fcalls - (-CONTEXT_REDS); \
(p)->fcalls = -CONTEXT_REDS; \
} \
} \
} while(0)
-#define ERTS_BIF_REDS_LEFT(p) \
+#define ERTS_VBUMP_LEAVE_REDS_INTERNAL(P, Reds, FCalls) \
+ do { \
+ if (ERTS_PROC_GET_SAVED_CALLS_BUF((P))) { \
+ int nreds__ = ((int)(Reds)) - CONTEXT_REDS; \
+ if ((FCalls) > nreds__) { \
+ erts_proc_sched_data((P))->virtual_reds \
+ += (FCalls) - nreds__; \
+ (FCalls) = nreds__; \
+ } \
+ } \
+ else { \
+ if ((FCalls) > (Reds)) { \
+ erts_proc_sched_data((P))->virtual_reds \
+ += (FCalls) - (Reds); \
+ (FCalls) = (Reds); \
+ } \
+ } \
+ } while (0)
+
+#define ERTS_VBUMP_LEAVE_REDS(P, Reds) \
+ ERTS_VBUMP_LEAVE_REDS_INTERNAL(P, Reds, (P)->fcalls)
+
+#define ERTS_REDS_LEFT(p, FCalls) \
(ERTS_PROC_GET_SAVED_CALLS_BUF((p)) \
- ? ((p)->fcalls > -CONTEXT_REDS ? ((p)->fcalls - (-CONTEXT_REDS)) : 0)\
- : ((p)->fcalls > 0 ? (p)->fcalls : 0))
+ ? ((FCalls) > -CONTEXT_REDS ? ((FCalls) - (-CONTEXT_REDS)) : 0) \
+ : ((FCalls) > 0 ? (FCalls) : 0))
+
+#define ERTS_BIF_REDS_LEFT(p) ERTS_REDS_LEFT(p, p->fcalls)
#define BIF_RET2(x, gc) do { \
BUMP_REDS(BIF_P, (gc)); \
@@ -138,7 +165,7 @@ do { \
#define ERTS_BIF_ERROR_TRAPPED1(Proc, Reason, Bif, A0) \
do { \
- Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \
+ Eterm* reg = erts_proc_sched_data((Proc))->x_reg_array; \
(Proc)->freason = (Reason); \
(Proc)->current = (Bif)->code; \
reg[0] = (Eterm) (A0); \
@@ -147,7 +174,7 @@ do { \
#define ERTS_BIF_ERROR_TRAPPED2(Proc, Reason, Bif, A0, A1) \
do { \
- Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \
+ Eterm* reg = erts_proc_sched_data((Proc))->x_reg_array; \
(Proc)->freason = (Reason); \
(Proc)->current = (Bif)->code; \
reg[0] = (Eterm) (A0); \
@@ -157,7 +184,7 @@ do { \
#define ERTS_BIF_ERROR_TRAPPED3(Proc, Reason, Bif, A0, A1, A2) \
do { \
- Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \
+ Eterm* reg = erts_proc_sched_data((Proc))->x_reg_array; \
(Proc)->freason = (Reason); \
(Proc)->current = (Bif)->code; \
reg[0] = (Eterm) (A0); \
@@ -181,7 +208,7 @@ do { \
#define ERTS_BIF_PREP_ERROR_TRAPPED1(Ret, Proc, Reason, Bif, A0) \
do { \
- Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \
+ Eterm* reg = erts_proc_sched_data((Proc))->x_reg_array; \
(Proc)->freason = (Reason); \
(Proc)->current = (Bif)->code; \
reg[0] = (Eterm) (A0); \
@@ -190,7 +217,7 @@ do { \
#define ERTS_BIF_PREP_ERROR_TRAPPED2(Ret, Proc, Reason, Bif, A0, A1) \
do { \
- Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \
+ Eterm* reg = erts_proc_sched_data((Proc))->x_reg_array; \
(Proc)->freason = (Reason); \
(Proc)->current = (Bif)->code; \
reg[0] = (Eterm) (A0); \
@@ -200,7 +227,7 @@ do { \
#define ERTS_BIF_PREP_ERROR_TRAPPED3(Ret, Proc, Reason, Bif, A0, A1, A2) \
do { \
- Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \
+ Eterm* reg = erts_proc_sched_data((Proc))->x_reg_array; \
(Proc)->freason = (Reason); \
(Proc)->current = (Bif)->code; \
reg[0] = (Eterm) (A0); \
@@ -219,7 +246,7 @@ do { \
#define ERTS_BIF_PREP_TRAP1(Ret, Trap, Proc, A0) \
do { \
- Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \
+ Eterm* reg = erts_proc_sched_data((Proc))->x_reg_array; \
(Proc)->arity = 1; \
reg[0] = (Eterm) (A0); \
(Proc)->i = (BeamInstr*) ((Trap)->addressv[erts_active_code_ix()]); \
@@ -229,7 +256,7 @@ do { \
#define ERTS_BIF_PREP_TRAP2(Ret, Trap, Proc, A0, A1) \
do { \
- Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \
+ Eterm* reg = erts_proc_sched_data((Proc))->x_reg_array; \
(Proc)->arity = 2; \
reg[0] = (Eterm) (A0); \
reg[1] = (Eterm) (A1); \
@@ -240,7 +267,7 @@ do { \
#define ERTS_BIF_PREP_TRAP3(Ret, Trap, Proc, A0, A1, A2) \
do { \
- Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \
+ Eterm* reg = erts_proc_sched_data((Proc))->x_reg_array; \
(Proc)->arity = 3; \
reg[0] = (Eterm) (A0); \
reg[1] = (Eterm) (A1); \
@@ -252,7 +279,7 @@ do { \
#define ERTS_BIF_PREP_TRAP3_NO_RET(Trap, Proc, A0, A1, A2)\
do { \
- Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \
+ Eterm* reg = erts_proc_sched_data((Proc))->x_reg_array; \
(Proc)->arity = 3; \
reg[0] = (Eterm) (A0); \
reg[1] = (Eterm) (A1); \
@@ -269,7 +296,7 @@ do { \
} while(0)
#define BIF_TRAP1(Trap_, p, A0) do { \
- Eterm* reg = ERTS_PROC_GET_SCHDATA((p))->x_reg_array; \
+ Eterm* reg = erts_proc_sched_data((p))->x_reg_array; \
(p)->arity = 1; \
reg[0] = (A0); \
(p)->i = (BeamInstr*) ((Trap_)->addressv[erts_active_code_ix()]); \
@@ -278,7 +305,7 @@ do { \
} while(0)
#define BIF_TRAP2(Trap_, p, A0, A1) do { \
- Eterm* reg = ERTS_PROC_GET_SCHDATA((p))->x_reg_array; \
+ Eterm* reg = erts_proc_sched_data((p))->x_reg_array; \
(p)->arity = 2; \
reg[0] = (A0); \
reg[1] = (A1); \
@@ -288,7 +315,7 @@ do { \
} while(0)
#define BIF_TRAP3(Trap_, p, A0, A1, A2) do { \
- Eterm* reg = ERTS_PROC_GET_SCHDATA((p))->x_reg_array; \
+ Eterm* reg = erts_proc_sched_data((p))->x_reg_array; \
(p)->arity = 3; \
reg[0] = (A0); \
reg[1] = (A1); \
@@ -312,37 +339,20 @@ do { \
} while(0)
extern Export bif_return_trap_export;
-#ifdef DEBUG
-#define ERTS_BIF_PREP_YIELD_RETURN_X(RET, P, VAL, DEBUG_VAL) \
-do { \
- ERTS_VBUMP_ALL_REDS(P); \
- ERTS_BIF_PREP_TRAP2(RET, &bif_return_trap_export, (P), (VAL), \
- (DEBUG_VAL)); \
-} while (0)
-#else
-#define ERTS_BIF_PREP_YIELD_RETURN_X(RET, P, VAL, DEBUG_VAL) \
+#define ERTS_BIF_PREP_YIELD_RETURN_X(RET, P, VAL, OP) \
do { \
ERTS_VBUMP_ALL_REDS(P); \
- ERTS_BIF_PREP_TRAP1(RET, &bif_return_trap_export, (P), (VAL)); \
+ ERTS_BIF_PREP_TRAP2(RET, &bif_return_trap_export, (P), (VAL), (OP));\
} while (0)
-#endif
#define ERTS_BIF_PREP_YIELD_RETURN(RET, P, VAL) \
ERTS_BIF_PREP_YIELD_RETURN_X(RET, (P), (VAL), am_undefined)
-#ifdef DEBUG
-#define ERTS_BIF_YIELD_RETURN_X(P, VAL, DEBUG_VAL) \
+#define ERTS_BIF_YIELD_RETURN_X(P, VAL, OP) \
do { \
ERTS_VBUMP_ALL_REDS(P); \
- BIF_TRAP2(&bif_return_trap_export, (P), (VAL), (DEBUG_VAL)); \
+ BIF_TRAP2(&bif_return_trap_export, (P), (VAL), (OP)); \
} while (0)
-#else
-#define ERTS_BIF_YIELD_RETURN_X(P, VAL, DEBUG_VAL) \
-do { \
- ERTS_VBUMP_ALL_REDS(P); \
- BIF_TRAP1(&bif_return_trap_export, (P), (VAL)); \
-} while (0)
-#endif
#define ERTS_BIF_RETURN_YIELD(P) ERTS_VBUMP_ALL_REDS((P))
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 3177d5dae7..80db4eb6ff 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2013. All Rights Reserved.
+# Copyright Ericsson AB 1996-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -72,8 +72,6 @@ bif erlang:get/1
bif erlang:get_keys/1
bif erlang:group_leader/0
bif erlang:group_leader/2
-bif erlang:halt/0
-bif erlang:halt/1
bif erlang:halt/2
bif erlang:phash/2
bif erlang:phash2/1
@@ -115,7 +113,7 @@ bif erlang:time_offset/0
bif erlang:time_offset/1
bif erlang:timestamp/0
-bif erlang:open_port/2
+bif erts_internal:open_port/2
bif erlang:pid_to_list/1
bif erlang:ports/0
@@ -125,7 +123,6 @@ bif erlang:process_flag/3
bif erlang:process_info/1
bif erlang:process_info/2
bif erlang:processes/0
-bif erlang:purge_module/1
bif erlang:put/2
bif erlang:register/2
bif erlang:registered/0
@@ -164,26 +161,29 @@ bif erts_internal:port_close/1
bif erts_internal:port_connect/2
bif erts_internal:request_system_task/3
+bif erts_internal:request_system_task/4
bif erts_internal:check_process_code/2
bif erts_internal:map_to_tuple_keys/1
-bif erts_internal:map_type/1
+bif erts_internal:term_type/1
bif erts_internal:map_hashmap_children/1
bif erts_internal:time_unit/0
+bif erts_internal:perf_counter_unit/0
bif erts_internal:is_system_process/1
bif erts_internal:system_check/1
+bif erts_internal:release_literal_area_switch/0
+
# inet_db support
bif erlang:port_set_data/2
bif erlang:port_get_data/1
# Tracing & debugging.
-bif erlang:trace_pattern/2
-bif erlang:trace_pattern/3
-bif erlang:trace/3
+bif erts_internal:trace_pattern/3
+bif erts_internal:trace/3
bif erlang:trace_info/2
bif erlang:trace_delivered/1
bif erlang:seq_trace/2
@@ -372,6 +372,7 @@ bif os:getpid/0
bif os:timestamp/0
bif os:system_time/0
bif os:system_time/1
+bif os:perf_counter/0
#
# Bifs in the erl_ddll module (the module actually does not exist)
@@ -641,6 +642,21 @@ bif ets:update_counter/4
bif erts_debug:map_info/1
#
+# New in 19.0
+#
+
+bif erts_internal:is_process_executing_dirty/1
+bif erts_internal:check_dirty_process_code/2
+bif erts_internal:purge_module/2
+bif binary:split/2
+bif binary:split/3
+bif erts_debug:size_shared/1
+bif erts_debug:copy_shared/1
+bif erlang:has_prepared_code_on_load/1
+
+bif maps:take/2
+
+#
# Obsolete
#
diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c
index 87d3be2b0f..4baee7900b 100644
--- a/erts/emulator/beam/big.c
+++ b/erts/emulator/beam/big.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2014. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -48,7 +48,7 @@
_t_dst = (dst)+((sz)-1); \
_t_src = (src)+((sz)-1); \
while(_t_sz--) *_t_dst-- = *_t_src--; \
- } \
+ } \
} while(0)
/* add a and b with carry in + out */
@@ -423,6 +423,25 @@
#endif
+/* Forward declaration of lookup tables (See below in this file) used in list to
+ * integer conversions for different bases. Also used in bignum printing.
+ */
+static const byte digits_per_sint_lookup[36-1];
+static const byte digits_per_small_lookup[36-1];
+static const Sint largest_power_of_base_lookup[36-1];
+
+static ERTS_INLINE byte get_digits_per_signed_int(Uint base) {
+ return digits_per_sint_lookup[base-2];
+}
+
+static ERTS_INLINE byte get_digits_per_small(Uint base) {
+ return digits_per_small_lookup[base-2];
+}
+
+static ERTS_INLINE Sint get_largest_power_of_base(Uint base) {
+ return largest_power_of_base_lookup[base-2];
+}
+
/*
** compare two number vectors
*/
@@ -1487,20 +1506,8 @@ Eterm uint_to_big(Uint x, Eterm *y)
Eterm uword_to_big(UWord x, Eterm *y)
{
-#if HALFWORD_HEAP
- Uint upper = x >> 32;
- Uint lower = x & 0xFFFFFFFFUL;
- if (upper == 0) {
- *y = make_pos_bignum_header(1);
- } else {
- *y = make_pos_bignum_header(2);
- BIG_DIGIT(y, 1) = upper;
- }
- BIG_DIGIT(y, 0) = lower;
-#else
*y = make_pos_bignum_header(1);
BIG_DIGIT(y, 0) = x;
-#endif
return make_big(y);
}
@@ -1525,7 +1532,7 @@ Eterm small_to_big(Sint x, Eterm *y)
Eterm erts_uint64_to_big(Uint64 x, Eterm **hpp)
{
Eterm *hp = *hpp;
-#if defined(ARCH_32) || HALFWORD_HEAP
+#if defined(ARCH_32)
if (x >= (((Uint64) 1) << 32)) {
*hp = make_pos_bignum_header(2);
BIG_DIGIT(hp, 0) = (Uint) (x & ((Uint) 0xffffffff));
@@ -1555,7 +1562,7 @@ Eterm erts_sint64_to_big(Sint64 x, Eterm **hpp)
neg = 1;
ux = -(Uint64)x;
}
-#if defined(ARCH_32) || HALFWORD_HEAP
+#if defined(ARCH_32)
if (ux >= (((Uint64) 1) << 32)) {
if (neg)
*hp = make_neg_bignum_header(2);
@@ -1588,7 +1595,7 @@ erts_uint64_array_to_big(Uint **hpp, int neg, int len, Uint64 *array)
pot_digits = digits = 0;
for (i = 0; i < len; i++) {
-#if defined(ARCH_32) || HALFWORD_HEAP
+#if defined(ARCH_32)
Uint low_val = array[i] & ((Uint) 0xffffffff);
Uint high_val = (array[i] >> 32) & ((Uint) 0xffffffff);
BIG_DIGIT(headerp, pot_digits) = low_val;
@@ -1651,8 +1658,6 @@ big_to_double(Wterm x, double* resp)
/*
* Logic has been copied from erl_bif_guard.c and slightly
* modified to use a static instead of dynamic heap
- *
- * HALFWORD: Return relative term with 'heap' as base.
*/
Eterm
double_to_big(double x, Eterm *heap, Uint hsz)
@@ -1683,7 +1688,7 @@ double_to_big(double x, Eterm *heap, Uint hsz)
sz = BIG_NEED_SIZE(ds); /* number of words including arity */
hp = heap;
- res = make_big_rel(hp, heap);
+ res = make_big(hp);
xp = (ErtsDigit*) (hp + 1);
ASSERT(ds < hsz);
@@ -1733,8 +1738,10 @@ static Uint write_big(Wterm x, void (*write_func)(void *, char), void *arg)
short sign = BIG_SIGN(xp);
ErtsDigit rem;
Uint n = 0;
+ const Uint digits_per_Sint = get_digits_per_signed_int(10);
+ const Sint largest_pow_of_base = get_largest_power_of_base(10);
- if (xl == 1 && *dx < D_DECIMAL_BASE) {
+ if (xl == 1 && *dx < largest_pow_of_base) {
rem = *dx;
if (rem == 0) {
(*write_func)(arg, '0'); n++;
@@ -1752,7 +1759,7 @@ static Uint write_big(Wterm x, void (*write_func)(void *, char), void *arg)
MOVE_DIGITS(tmp, dx, xl);
while(1) {
- tmpl = D_div(tmp, tmpl, D_DECIMAL_BASE, tmp, &rem);
+ tmpl = D_div(tmp, tmpl, largest_pow_of_base, tmp, &rem);
if (tmpl == 1 && *tmp == 0) {
while(rem) {
(*write_func)(arg, (rem % 10)+'0'); n++;
@@ -1760,7 +1767,7 @@ static Uint write_big(Wterm x, void (*write_func)(void *, char), void *arg)
}
break;
} else {
- int i = D_DECIMAL_EXP;
+ Uint i = digits_per_Sint;
while(i--) {
(*write_func)(arg, (rem % 10)+'0'); n++;
rem /= 10;
@@ -2562,63 +2569,100 @@ int term_equals_2pow32(Eterm x)
}
}
+static ERTS_INLINE int c2int_is_invalid_char(byte ch, int base) {
+ return (ch < '0'
+ || (ch > ('0' + base - 1)
+ && !(base > 10
+ && ((ch >= 'a' && ch < ('a' + base - 10))
+ || (ch >= 'A' && ch < ('A' + base - 10))))));
+}
-#define IS_VALID_CHARACTER(CHAR,BASE) \
- (CHAR < '0' \
- || (CHAR > ('0' + BASE - 1) \
- && !(BASE > 10 \
- && ((CHAR >= 'a' && CHAR < ('a' + BASE - 10)) \
- || (CHAR >= 'A' && CHAR < ('A' + BASE - 10))))))
-#define CHARACTER_FROM_BASE(CHAR) \
- ((CHAR <= '9') ? CHAR - '0' : 10 + ((CHAR <= 'Z') ? CHAR - 'A' : CHAR - 'a'))
-#define D_BASE_EXP(BASE) (d_base_exp_lookup[BASE-2])
-#define D_BASE_BASE(BASE) (d_base_base_lookup[BASE-2])
-#define LG2_LOOKUP(BASE) (lg2_lookup[base-2])
+static ERTS_INLINE byte c2int_digit_from_base(byte ch) {
+ return ch <= '9' ? ch - '0'
+ : (10 + (ch <= 'Z' ? ch - 'A' : ch - 'a'));
+}
/*
- * for i in 2..64 do
- * lg2_lookup[i-2] = log2(i)
- * end
- * How many bits are needed to store string of size n
+ * How many bits are needed to store 1 digit of given base in binary
+ * Wo.Alpha formula: Table [log2[n], {n,2,36}]
*/
-const double lg2_lookup[] = { 1.0, 1.58496, 2, 2.32193, 2.58496, 2.80735, 3.0,
- 3.16993, 3.32193, 3.45943, 3.58496, 3.70044, 3.80735, 3.90689, 4.0,
- 4.08746, 4.16993, 4.24793, 4.32193, 4.39232, 4.45943, 4.52356, 4.58496,
- 4.64386, 4.70044, 4.75489, 4.80735, 4.85798, 4.90689, 4.9542, 5.0,
- 5.04439, 5.08746, 5.12928, 5.16993, 5.20945, 5.24793, 5.2854, 5.32193,
- 5.35755, 5.39232, 5.42626, 5.45943, 5.49185, 5.52356, 5.55459, 5.58496,
- 5.61471, 5.64386, 5.67243, 5.70044, 5.72792, 5.75489, 5.78136, 5.80735,
- 5.83289, 5.85798, 5.88264, 5.90689, 5.93074, 5.9542, 5.97728, 6.0 };
+static const double lg2_lookup[36-1] = {
+ 1.0, 1.58496, 2.0, 2.32193, 2.58496, 2.80735, 3.0, 3.16993, 3.32193,
+ 3.45943, 3.58496, 3.70044, 3.80735, 3.90689, 4.0, 4.08746, 4.16993, 4.24793,
+ 4.32193, 4.39232, 4.45943, 4.52356, 4.58496, 4.64386, 4.70044, 4.75489,
+ 4.80735, 4.85798, 4.90689, 4.9542, 5.0, 5.04439, 5.08746, 5.12928, 5.16993
+};
+static ERTS_INLINE double lookup_log2(Uint base) {
+ return lg2_lookup[base - 2];
+}
/*
- * for i in 2..64 do
- * d_base_exp_lookup[i-2] = 31 / lg2_lookup[i-2];
- * end
- * How many characters can fit in 31 bits
+ * How many digits can fit into a signed int (Sint) for given base, we take
+ * one digit away just to be on the safer side (some corner cases).
*/
-const byte d_base_exp_lookup[] = { 31, 19, 15, 13, 11, 11, 10, 9, 9, 8, 8, 8, 8,
- 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
- 5, 5 };
+static const byte digits_per_sint_lookup[36-1] = {
+#if (SIZEOF_VOID_P == 4)
+ /* Wo.Alpha formula: Table [Trunc[31 / log[2,n]]-1, {n, 2, 36}] */
+ 30, 18, 14, 12, 10, 10, 9, 8, 8, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4
+#elif (SIZEOF_VOID_P == 8)
+ /* Wo.Alpha formula: Table [Trunc[63 / log[2,n]]-1, {n, 2, 36}] */
+ 62, 38, 30, 26, 23, 21, 20, 18, 17, 17, 16, 16, 15, 15, 14, 14, 14, 13, 13,
+ 13, 13, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 11
+#else
+ #error "Please produce a lookup table for the new architecture"
+#endif
+};
+
+/*
+ * How many digits can fit into Erlang Small (SMALL_BITS-1) counting sign bit
+ */
+static const byte digits_per_small_lookup[36-1] = {
+#if (SIZEOF_VOID_P == 4)
+ /* Wo.Alpha formula: Table [Trunc[27 / log[2,n]]-1, {n, 2, 36}] */
+ 27, 17, 13, 11, 10, 9, 9, 8, 8, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
+#elif (SIZEOF_VOID_P == 8)
+ /* Wo.Alpha formula: Table [Trunc[59 / log[2,n]]-1, {n, 2, 36}] */
+ 59, 37, 29, 25, 22, 21, 19, 18, 17, 17, 16, 15, 15, 15, 14, 14, 14, 13, 13,
+ 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11
+#else
+ #error "Please produce a lookup table for the new architecture"
+#endif
+};
/*
- * for i in 2..64 do
- * d_base_base_lookup[i-2] = pow(i,d_base_exp_lookup[i-2]);
- * end
- * How much can the characters which fit in 31 bit represent
+ * Largest power of base which can be represented in a signed int (Sint).
+ * Calculated by base 2..36 to the power of corresponding element from
+ * digits_per_sint_lookup.
*/
-const Uint d_base_base_lookup[] = { 2147483648u, 1162261467u, 1073741824u,
- 1220703125u, 362797056u, 1977326743u, 1073741824u, 387420489u,
- 1000000000u, 214358881u, 429981696u, 815730721u, 1475789056u,
- 170859375u, 268435456u, 410338673u, 612220032u, 893871739u, 1280000000u,
- 1801088541u, 113379904u, 148035889u, 191102976u, 244140625u, 308915776u,
- 387420489u, 481890304u, 594823321u, 729000000u, 887503681u, 1073741824u,
- 1291467969u, 1544804416u, 1838265625u, 60466176u, 69343957u, 79235168u,
- 90224199u, 102400000u, 115856201u, 130691232u, 147008443u, 164916224u,
- 184528125u, 205962976u, 229345007u, 254803968u, 282475249u, 312500000u,
- 345025251u, 380204032u, 418195493u, 459165024u, 503284375u, 550731776u,
- 601692057u, 656356768u, 714924299u, 777600000u, 844596301u, 916132832u,
- 992436543u, 1073741824u };
+static const Sint largest_power_of_base_lookup[36-1] = {
+#if (SIZEOF_VOID_P == 4)
+ /* Wo.Alpha formula: Table [Pow[n, Trunc[31 / log[2,n]]-1], {n, 2, 36}] */
+ 1073741824, 387420489, 268435456, 244140625, 60466176, 282475249, 134217728,
+ 43046721, 100000000, 19487171, 35831808, 62748517, 105413504, 11390625,
+ 16777216, 24137569, 34012224, 47045881, 64000000, 85766121, 5153632,
+ 6436343,7962624, 9765625, 11881376, 14348907, 17210368, 20511149, 24300000,
+ 28629151, 33554432, 39135393, 45435424, 52521875, 1679616
+#elif (SIZEOF_VOID_P == 8)
+ /* Wo.Alpha formula: Table [Pow[n, Trunc[63 / log[2,n]]-1], {n, 2, 36}]
+ * with LL added after each element manually */
+ 4611686018427387904LL, 1350851717672992089LL, 1152921504606846976LL,
+ 1490116119384765625LL, 789730223053602816LL, 558545864083284007LL,
+ 1152921504606846976LL, 150094635296999121LL, 100000000000000000LL,
+ 505447028499293771LL, 184884258895036416LL, 665416609183179841LL,
+ 155568095557812224LL, 437893890380859375LL, 72057594037927936LL,
+ 168377826559400929LL, 374813367582081024LL, 42052983462257059LL,
+ 81920000000000000LL, 154472377739119461LL, 282810057883082752LL,
+ 21914624432020321LL, 36520347436056576LL, 59604644775390625LL,
+ 95428956661682176LL, 150094635296999121LL, 232218265089212416LL,
+ 12200509765705829LL, 17714700000000000LL, 25408476896404831LL,
+ 36028797018963968LL, 50542106513726817LL, 70188843638032384LL,
+ 96549157373046875LL, 131621703842267136LL
+#else
+ #error "Please produce a lookup table for the new architecture"
+#endif
+};
Eterm erts_chars_to_integer(Process *BIF_P, char *bytes,
Uint size, const int base) {
@@ -2628,8 +2672,11 @@ Eterm erts_chars_to_integer(Process *BIF_P, char *bytes,
int neg = 0;
byte b;
Eterm *hp, *hp_end;
- int m;
+ Sint m;
int lg2;
+ const Uint digits_per_small = get_digits_per_small(base);
+ const Uint digits_per_Sint = get_digits_per_signed_int(base);
+ const Sint largest_pow_of_base = get_largest_power_of_base(base);
if (size == 0)
goto bytebuf_to_integer_1_error;
@@ -2644,57 +2691,68 @@ Eterm erts_chars_to_integer(Process *BIF_P, char *bytes,
size--;
}
+ /* Trim leading zeroes */
+ if (size) {
+ while (*bytes == '0') {
+ bytes++;
+ size--;
+ if (!size) {
+ /* All zero! */
+ res = make_small(0);
+ goto bytebuf_to_integer_1_done;
+ }
+ }
+ }
+
if (size == 0)
goto bytebuf_to_integer_1_error;
- if (size < SMALL_DIGITS && base <= 10) {
- /* *
- * Take shortcut if we know that all chars are '0' < b < '9' and
- * fit in a small. This improves speed by about 10% over the generic
- * small case.
- * */
- while (size--) {
- b = *bytes++;
+ if (size < digits_per_small) {
+ if (base <= 10) {
+ /* *
+ * Take shortcut if we know that all chars are '0' < b < '9' and
+ * fit in a small. This improves speed by about 10% over the generic
+ * small case.
+ * */
+ while (size--) {
+ b = *bytes++;
- if (b < '0' || b > ('0'+base-1))
- goto bytebuf_to_integer_1_error;
+ if (b < '0' || b > ('0'+base-1))
+ goto bytebuf_to_integer_1_error;
- i = i * base + b - '0';
- }
+ i = i * base + b - '0';
+ }
- if (neg)
- i = -i;
- res = make_small(i);
- goto bytebuf_to_integer_1_done;
+ if (neg)
+ i = -i;
+ res = make_small(i);
+ goto bytebuf_to_integer_1_done;
+ }
+
+ /* Take shortcut if we know it will fit in a small.
+ * This improves speed by about 30%.
+ */
+ while (size) {
+ b = *bytes++;
+ size--;
+
+ if (c2int_is_invalid_char(b, base))
+ goto bytebuf_to_integer_1_error;
+
+ i = i * base + c2int_digit_from_base(b);
+ }
+
+ if (neg)
+ i = -i;
+ res = make_small(i);
+ goto bytebuf_to_integer_1_done;
}
/*
* Calculate the maximum number of bits which will
* be needed to represent the binary
*/
- lg2 = ((size+2)*LG2_LOOKUP(base)+1);
-
- if (lg2 < SMALL_BITS) {
- /* Take shortcut if we know it will fit in a small.
- * This improves speed by about 30%.
- */
- while (size) {
- b = *bytes++;
- size--;
-
- if (IS_VALID_CHARACTER(b,base))
- goto bytebuf_to_integer_1_error;
-
- i = i * base + CHARACTER_FROM_BASE(b);
-
- }
-
- if (neg)
- i = -i;
- res = make_small(i);
- goto bytebuf_to_integer_1_done;
-
- }
+ lg2 = ((size+2)*lookup_log2(base)+1);
/* Start calculating bignum */
m = (lg2 + D_EXP-1)/D_EXP;
@@ -2703,8 +2761,8 @@ Eterm erts_chars_to_integer(Process *BIF_P, char *bytes,
hp = HAlloc(BIF_P, m);
hp_end = hp + m;
- if ((i = (size % D_BASE_EXP(base))) == 0)
- i = D_BASE_EXP(base);
+ if ((i = (size % digits_per_Sint)) == 0)
+ i = digits_per_Sint;
n = size - i;
m = 0;
@@ -2712,34 +2770,34 @@ Eterm erts_chars_to_integer(Process *BIF_P, char *bytes,
while (i--) {
b = *bytes++;
- if (IS_VALID_CHARACTER(b,base)) {
+ if (c2int_is_invalid_char(b,base)) {
HRelease(BIF_P, hp_end, hp);
goto bytebuf_to_integer_1_error;
}
- m = base * m + CHARACTER_FROM_BASE(b);
+ m = base * m + c2int_digit_from_base(b);
}
res = small_to_big(m, hp);
while (n) {
- i = D_BASE_EXP(base);
- n -= D_BASE_EXP(base);
+ i = digits_per_Sint;
+ n -= digits_per_Sint;
m = 0;
while (i--) {
b = *bytes++;
- if (IS_VALID_CHARACTER(b,base)) {
+ if (c2int_is_invalid_char(b,base)) {
HRelease(BIF_P, hp_end, hp);
goto bytebuf_to_integer_1_error;
}
- m = base * m + CHARACTER_FROM_BASE(b);
+ m = base * m + c2int_digit_from_base(b);
}
if (is_small(res)) {
res = small_to_big(signed_val(res), hp);
}
- res = big_times_small(res, D_BASE_BASE(base), hp);
+ res = big_times_small(res, largest_pow_of_base, hp);
if (is_small(res)) {
res = small_to_big(signed_val(res), hp);
}
@@ -2770,5 +2828,166 @@ bytebuf_to_integer_1_error:
bytebuf_to_integer_1_done:
return res;
+}
+/* Converts list of digits with given 'base' to integer sequentially. Returns
+ * result in 'integer_out', remaining tail goes to 'tail_out' and returns result
+ * code if the list was consumed fully or partially or there was an error
+ */
+LTI_result_t erts_list_to_integer(Process *BIF_P, Eterm orig_list,
+ const Uint base,
+ Eterm *integer_out, Eterm *tail_out)
+{
+ Sint i = 0;
+ Uint ui = 0;
+ int skip = 0;
+ int neg = 0;
+ Sint n = 0;
+ Sint m;
+ int lg2;
+ Eterm res;
+ Eterm lst = orig_list;
+ Eterm tail = lst;
+ int error_res = LTI_BAD_STRUCTURE;
+ const Uint digits_per_small = get_digits_per_small(base);
+ const Uint digits_per_Sint = get_digits_per_signed_int(base);
+
+ if (is_nil(lst)) {
+ error_res = LTI_NO_INTEGER;
+ error:
+ *tail_out = tail;
+ *integer_out = make_small(0);
+ return error_res;
+ }
+ if (is_not_list(lst))
+ goto error;
+
+ /* if first char is a '-' then it is a negative integer */
+ if (CAR(list_val(lst)) == make_small('-')) {
+ neg = 1;
+ skip = 1;
+ lst = CDR(list_val(lst));
+ if (is_not_list(lst)) {
+ tail = lst;
+ error_res = LTI_NO_INTEGER;
+ goto error;
+ }
+ } else if (CAR(list_val(lst)) == make_small('+')) {
+ /* ignore plus */
+ skip = 1;
+ lst = CDR(list_val(lst));
+ if (is_not_list(lst)) {
+ tail = lst;
+ error_res = LTI_NO_INTEGER;
+ goto error;
+ }
+ }
+
+ /* Calculate size and do type check */
+
+ while(1) {
+ byte ch;
+ if (is_not_small(CAR(list_val(lst)))) {
+ break;
+ }
+ ch = unsigned_val(CAR(list_val(lst)));
+ if (c2int_is_invalid_char(ch, base)) {
+ break;
+ }
+ ui = ui * base;
+ ui = ui + c2int_digit_from_base(ch);
+ n++;
+ lst = CDR(list_val(lst));
+ if (is_nil(lst)) {
+ break;
+ }
+ if (is_not_list(lst)) {
+ break;
+ }
+ }
+
+ tail = lst;
+ if (!n) {
+ error_res = LTI_NO_INTEGER;
+ goto error;
+ }
+
+
+ /* If length fits inside Sint then we know it's a small int. Else we
+ * must construct a bignum and let that routine do the checking
+ */
+
+ if (n <= digits_per_small) { /* It must be small */
+ i = neg ? -(Sint)ui : (Sint)ui;
+ res = make_small(i);
+ } else {
+ const Sint largest_pow_of_base = get_largest_power_of_base(base);
+ Eterm *hp;
+ Eterm *hp_end;
+
+ /* Convert from log_base to log2 using lookup table */
+ lg2 = ((n+2)*lookup_log2(base)+1);
+ m = (lg2+D_EXP-1)/D_EXP; /* number of digits */
+ m = BIG_NEED_SIZE(m); /* number of words + thing */
+
+ hp = HAlloc(BIF_P, m);
+ hp_end = hp + m;
+
+ lst = orig_list;
+ if (skip)
+ lst = CDR(list_val(lst));
+
+ /* load first digits (at least one digit) */
+ if ((i = (n % digits_per_Sint)) == 0)
+ i = digits_per_Sint;
+ n -= i;
+ m = 0;
+ while(i--) {
+ m *= base;
+ m += c2int_digit_from_base(unsigned_val(CAR(list_val(lst))));
+ lst = CDR(list_val(lst));
+ }
+ res = small_to_big(m, hp); /* load first digits */
+
+ while(n) {
+ i = digits_per_Sint;
+ n -= digits_per_Sint;
+ m = 0;
+ while(i--) {
+ m *= base;
+ m += c2int_digit_from_base(unsigned_val(CAR(list_val(lst))));
+ lst = CDR(list_val(lst));
+ }
+ if (is_small(res))
+ res = small_to_big(signed_val(res), hp);
+ res = big_times_small(res, largest_pow_of_base, hp);
+ if (is_small(res))
+ res = small_to_big(signed_val(res), hp);
+ res = big_plus_small(res, m, hp);
+ }
+
+ if (neg) {
+ if (is_small(res))
+ res = make_small(-signed_val(res));
+ else {
+ Uint *big = big_val(res); /* point to thing */
+ *big = bignum_header_neg(*big);
+ }
+ }
+
+ if (is_not_small(res)) {
+ res = big_plus_small(res, 0, hp); /* includes conversion to small */
+
+ if (is_not_small(res)) {
+ hp += (big_arity(res)+1);
+ }
+ }
+ HRelease(BIF_P, hp_end, hp);
+ }
+ *integer_out = res;
+ *tail_out = tail;
+ if (tail != NIL) {
+ return LTI_SOME_INTEGER;
+ }
+ return LTI_ALL_INTEGER;
}
diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h
index 3c1f1e6b23..4a96d971c3 100644
--- a/erts/emulator/beam/big.h
+++ b/erts/emulator/beam/big.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2014. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,21 +21,12 @@
#ifndef __BIG_H__
#define __BIG_H__
-#ifndef __SYS_H__
#include "sys.h"
-#endif
-
-#ifndef __CONFIG_H__
-#include "erl_vm.h"
-#endif
-
-#ifndef __GLOBAL_H__
#include "global.h"
-#endif
typedef Uint ErtsDigit;
-#if ((SIZEOF_VOID_P == 4) || HALFWORD_HEAP) && defined(SIZEOF_LONG_LONG) && (SIZEOF_LONG_LONG == 8)
+#if (SIZEOF_VOID_P == 4) && defined(SIZEOF_LONG_LONG) && (SIZEOF_LONG_LONG == 8)
/* Assume 32-bit machine with long long support */
typedef Uint64 ErtsDoubleDigit;
typedef Uint16 ErtsHalfDigit;
@@ -54,9 +45,6 @@ typedef Uint32 ErtsHalfDigit;
#error "can not determine machine size"
#endif
-#define D_DECIMAL_EXP 9
-#define D_DECIMAL_BASE 1000000000
-
typedef Uint dsize_t; /* Vector size type */
#define D_EXP (ERTS_SIZEOF_ETERM*8)
@@ -90,13 +78,9 @@ typedef Uint dsize_t; /* Vector size type */
#define BIG_UINT_HEAP_SIZE (1 + 1) /* always, since sizeof(Uint) <= sizeof(Eterm) */
-#if HALFWORD_HEAP
-#define BIG_UWORD_HEAP_SIZE(UW) (((UW) >> (sizeof(Uint) * 8)) ? 3 : 2)
-#else
#define BIG_UWORD_HEAP_SIZE(UW) BIG_UINT_HEAP_SIZE
-#endif
-#if defined(ARCH_32) || HALFWORD_HEAP
+#if defined(ARCH_32)
#define ERTS_UINT64_BIG_HEAP_SIZE__(X) \
((X) >= (((Uint64) 1) << 32) ? (1 + 2) : (1 + 1))
@@ -178,5 +162,15 @@ Eterm erts_sint64_to_big(Sint64, Eterm **);
Eterm erts_chars_to_integer(Process *, char*, Uint, const int);
+/* How list_to_integer classifies the input, was it even a string? */
+typedef enum {
+ LTI_BAD_STRUCTURE = 0,
+ LTI_NO_INTEGER = 1,
+ LTI_SOME_INTEGER = 2,
+ LTI_ALL_INTEGER = 3
+} LTI_result_t;
+
+LTI_result_t erts_list_to_integer(Process *BIF_P, Eterm orig_list,
+ const Uint base,
+ Eterm *integer_out, Eterm *tail_out);
#endif
-
diff --git a/erts/emulator/beam/binary.c b/erts/emulator/beam/binary.c
index 247c8f1122..071a356260 100644
--- a/erts/emulator/beam/binary.c
+++ b/erts/emulator/beam/binary.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,6 +34,9 @@
#include "erl_binary.h"
#include "erl_bits.h"
+#define L2B_B2L_MIN_EXEC_REDS (CONTEXT_REDS/4)
+#define L2B_B2L_RESCHED_REDS (CONTEXT_REDS/40)
+
static Export binary_to_list_continue_export;
static Export list_to_binary_continue_export;
@@ -114,7 +117,7 @@ new_binary(Process *p, byte *buf, Uint len)
* When heap binary is not desired...
*/
-Eterm erts_new_mso_binary(Process *p, byte *buf, int len)
+Eterm erts_new_mso_binary(Process *p, byte *buf, Uint len)
{
ProcBin* pb;
Binary* bptr;
@@ -415,10 +418,10 @@ binary_to_list_chunk(Process *c_p,
}
static ERTS_INLINE BIF_RETTYPE
-binary_to_list(Process *c_p, Eterm *hp, Eterm tail, byte *bytes, Uint size, Uint bitoffs)
+binary_to_list(Process *c_p, Eterm *hp, Eterm tail, byte *bytes,
+ Uint size, Uint bitoffs, int reds_left, int one_chunk)
{
- int reds_left = ERTS_BIF_REDS_LEFT(c_p);
- if (size < reds_left*ERTS_B2L_BYTES_PER_REDUCTION) {
+ if (one_chunk) {
Eterm res;
BIF_RETTYPE ret;
int bump_reds = (size - 1)/ERTS_B2L_BYTES_PER_REDUCTION + 1;
@@ -472,11 +475,29 @@ BIF_RETTYPE binary_to_list_1(BIF_ALIST_1)
Uint size;
Uint bitsize;
Uint bitoffs;
+ int reds_left;
+ int one_chunk;
if (is_not_binary(BIF_ARG_1)) {
goto error;
}
+
size = binary_size(BIF_ARG_1);
+ reds_left = ERTS_BIF_REDS_LEFT(BIF_P);
+ one_chunk = size < reds_left*ERTS_B2L_BYTES_PER_REDUCTION;
+ if (!one_chunk) {
+ if (size < L2B_B2L_MIN_EXEC_REDS*ERTS_B2L_BYTES_PER_REDUCTION) {
+ if (reds_left <= L2B_B2L_RESCHED_REDS) {
+ /* Yield and do it with full context reds... */
+ ERTS_BIF_YIELD1(bif_export[BIF_binary_to_list_1],
+ BIF_P, BIF_ARG_1);
+ }
+ /* Allow a bit more reductions... */
+ one_chunk = 1;
+ reds_left = L2B_B2L_MIN_EXEC_REDS;
+ }
+ }
+
ERTS_GET_REAL_BIN(BIF_ARG_1, real_bin, offset, bitoffs, bitsize);
if (bitsize != 0) {
goto error;
@@ -486,7 +507,8 @@ BIF_RETTYPE binary_to_list_1(BIF_ALIST_1)
} else {
Eterm* hp = HAlloc(BIF_P, 2 * size);
byte* bytes = binary_bytes(real_bin)+offset;
- return binary_to_list(BIF_P, hp, NIL, bytes, size, bitoffs);
+ return binary_to_list(BIF_P, hp, NIL, bytes, size,
+ bitoffs, reds_left, one_chunk);
}
error:
@@ -505,6 +527,8 @@ BIF_RETTYPE binary_to_list_3(BIF_ALIST_3)
Uint start;
Uint stop;
Eterm* hp;
+ int reds_left;
+ int one_chunk;
if (is_not_binary(BIF_ARG_1)) {
goto error;
@@ -513,6 +537,21 @@ BIF_RETTYPE binary_to_list_3(BIF_ALIST_3)
goto error;
}
size = binary_size(BIF_ARG_1);
+ reds_left = ERTS_BIF_REDS_LEFT(BIF_P);
+ one_chunk = size < reds_left*ERTS_B2L_BYTES_PER_REDUCTION;
+ if (!one_chunk) {
+ if (size < L2B_B2L_MIN_EXEC_REDS*ERTS_B2L_BYTES_PER_REDUCTION) {
+ if (reds_left <= L2B_B2L_RESCHED_REDS) {
+ /* Yield and do it with full context reds... */
+ ERTS_BIF_YIELD3(bif_export[BIF_binary_to_list_3],
+ BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
+ }
+ /* Allow a bit more reductions... */
+ one_chunk = 1;
+ reds_left = L2B_B2L_MIN_EXEC_REDS;
+ }
+ }
+
ERTS_GET_BINARY_BYTES(BIF_ARG_1, bytes, bitoffs, bitsize);
if (start < 1 || start > size || stop < 1 ||
stop > size || stop < start ) {
@@ -520,7 +559,8 @@ BIF_RETTYPE binary_to_list_3(BIF_ALIST_3)
}
i = stop-start+1;
hp = HAlloc(BIF_P, 2*i);
- return binary_to_list(BIF_P, hp, NIL, bytes+start-1, i, bitoffs);
+ return binary_to_list(BIF_P, hp, NIL, bytes+start-1, i,
+ bitoffs, reds_left, one_chunk);
error:
BIF_ERROR(BIF_P, BADARG);
}
@@ -537,11 +577,27 @@ BIF_RETTYPE bitstring_to_list_1(BIF_ALIST_1)
byte* bytes;
Eterm previous = NIL;
Eterm* hp;
+ int reds_left;
+ int one_chunk;
if (is_not_binary(BIF_ARG_1)) {
BIF_ERROR(BIF_P, BADARG);
}
size = binary_size(BIF_ARG_1);
+ reds_left = ERTS_BIF_REDS_LEFT(BIF_P);
+ one_chunk = size < reds_left*ERTS_B2L_BYTES_PER_REDUCTION;
+ if (!one_chunk) {
+ if (size < L2B_B2L_MIN_EXEC_REDS*ERTS_B2L_BYTES_PER_REDUCTION) {
+ if (reds_left <= L2B_B2L_RESCHED_REDS) {
+ /* Yield and do it with full context reds... */
+ ERTS_BIF_YIELD1(bif_export[BIF_bitstring_to_list_1],
+ BIF_P, BIF_ARG_1);
+ }
+ /* Allow a bit more reductions... */
+ one_chunk = 1;
+ reds_left = L2B_B2L_MIN_EXEC_REDS;
+ }
+ }
ERTS_GET_REAL_BIN(BIF_ARG_1, real_bin, offset, bitoffs, bitsize);
bytes = binary_bytes(real_bin)+offset;
if (bitsize == 0) {
@@ -566,7 +622,8 @@ BIF_RETTYPE bitstring_to_list_1(BIF_ALIST_1)
hp += 2;
}
- return binary_to_list(BIF_P, hp, previous, bytes, size, bitoffs);
+ return binary_to_list(BIF_P, hp, previous, bytes, size,
+ bitoffs, reds_left, one_chunk);
}
@@ -795,8 +852,19 @@ static BIF_RETTYPE list_to_binary_continue(BIF_ALIST_1)
BIF_RETTYPE erts_list_to_binary_bif(Process *c_p, Eterm arg, Export *bif)
{
+ int orig_reds_left = ERTS_BIF_REDS_LEFT(c_p);
BIF_RETTYPE ret;
+ if (orig_reds_left < L2B_B2L_MIN_EXEC_REDS) {
+ if (orig_reds_left <= L2B_B2L_RESCHED_REDS) {
+ /* Yield and do it with full context reds... */
+ ERTS_BIF_PREP_YIELD1(ret, bif, c_p, arg);
+ return ret;
+ }
+ /* Allow a bit more reductions... */
+ orig_reds_left = L2B_B2L_MIN_EXEC_REDS;
+ }
+
if (is_nil(arg))
ERTS_BIF_PREP_RET(ret, new_binary(c_p, (byte *) "", 0));
else if (is_not_list(arg))
@@ -818,7 +886,6 @@ BIF_RETTYPE erts_list_to_binary_bif(Process *c_p, Eterm arg, Export *bif)
bif,
erts_iolist_size_yielding,
erts_iolist_to_buf_yielding);
- int orig_reds_left = ERTS_BIF_REDS_LEFT(c_p);
/*
* First try to do it all at once without having to use
diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c
index 0ddf7f4e6d..74a8a3b852 100644
--- a/erts/emulator/beam/break.c
+++ b/erts/emulator/beam/break.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2017. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -44,23 +44,22 @@
static void process_killer(void);
void do_break(void);
void erl_crash_dump_v(char *file, int line, char* fmt, va_list args);
-void erl_crash_dump(char* file, int line, char* fmt, ...);
#ifdef DEBUG
static void bin_check(void);
#endif
-static void print_garb_info(int to, void *to_arg, Process* p);
+static void print_garb_info(fmtfn_t to, void *to_arg, Process* p);
#ifdef OPPROF
static void dump_frequencies(void);
#endif
-static void dump_attributes(int to, void *to_arg, byte* ptr, int size);
+static void dump_attributes(fmtfn_t to, void *to_arg, byte* ptr, int size);
extern char* erts_system_version[];
static void
-port_info(int to, void *to_arg)
+port_info(fmtfn_t to, void *to_arg)
{
int i, max = erts_ptab_max(&erts_port);
for (i = 0; i < max; i++) {
@@ -71,7 +70,7 @@ port_info(int to, void *to_arg)
}
void
-process_info(int to, void *to_arg)
+process_info(fmtfn_t to, void *to_arg)
{
int i, max = erts_ptab_max(&erts_proc);
for (i = 0; i < max; i++) {
@@ -118,7 +117,9 @@ process_killer(void)
| ERTS_PSFLG_ACTIVE_SYS
| ERTS_PSFLG_IN_RUNQ
| ERTS_PSFLG_RUNNING
- | ERTS_PSFLG_RUNNING_SYS)) {
+ | ERTS_PSFLG_RUNNING_SYS
+ | ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
erts_printf("Can only kill WAITING processes this way\n");
}
else {
@@ -146,14 +147,14 @@ process_killer(void)
typedef struct {
int is_first;
- int to;
+ fmtfn_t to;
void *to_arg;
} PrintMonitorContext;
static void doit_print_link(ErtsLink *lnk, void *vpcontext)
{
PrintMonitorContext *pcontext = vpcontext;
- int to = pcontext->to;
+ fmtfn_t to = pcontext->to;
void *to_arg = pcontext->to_arg;
if (pcontext->is_first) {
@@ -168,7 +169,7 @@ static void doit_print_link(ErtsLink *lnk, void *vpcontext)
static void doit_print_monitor(ErtsMonitor *mon, void *vpcontext)
{
PrintMonitorContext *pcontext = vpcontext;
- int to = pcontext->to;
+ fmtfn_t to = pcontext->to;
void *to_arg = pcontext->to_arg;
char *prefix = ", ";
@@ -195,7 +196,7 @@ static void doit_print_monitor(ErtsMonitor *mon, void *vpcontext)
/* Display info about an individual Erlang process */
void
-print_process_info(int to, void *to_arg, Process *p)
+print_process_info(fmtfn_t to, void *to_arg, Process *p)
{
time_t approx_started;
int garbing = 0;
@@ -214,7 +215,8 @@ print_process_info(int to, void *to_arg, Process *p)
if (state & ERTS_PSFLG_GC) {
garbing = 1;
running = 1;
- } else if (state & ERTS_PSFLG_RUNNING)
+ } else if (state & (ERTS_PSFLG_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING))
running = 1;
/*
@@ -252,7 +254,7 @@ print_process_info(int to, void *to_arg, Process *p)
/* display the message queue only if there is anything in it */
if (!ERTS_IS_CRASH_DUMPING && p->msg.first != NULL && !garbing) {
- ErlMessage* mp;
+ ErtsMessage* mp;
erts_print(to, to_arg, "Message queue: [");
for (mp = p->msg.first; mp; mp = mp->next)
erts_print(to, to_arg, mp->next ? "%T," : "%T", ERL_MESSAGE_TERM(mp));
@@ -297,7 +299,7 @@ print_process_info(int to, void *to_arg, Process *p)
/* display the links only if there are any*/
if (ERTS_P_LINKS(p) || ERTS_P_MONITORS(p)) {
- PrintMonitorContext context = {1,to};
+ PrintMonitorContext context = {1, to, to_arg};
erts_print(to, to_arg,"Link list: [");
erts_doforall_links(ERTS_P_LINKS(p), &doit_print_link, &context);
erts_doforall_monitors(ERTS_P_MONITORS(p), &doit_print_monitor, &context);
@@ -323,7 +325,7 @@ print_process_info(int to, void *to_arg, Process *p)
erts_print(to, to_arg, "Heap unused: %bpu\n", (p->hend - p->htop));
erts_print(to, to_arg, "OldHeap unused: %bpu\n",
(OLD_HEAP(p) == NULL) ? 0 : (OLD_HEND(p) - OLD_HTOP(p)) );
- erts_print(to, to_arg, "Memory: %beu\n", erts_process_memory(p));
+ erts_print(to, to_arg, "Memory: %beu\n", erts_process_memory(p, !0));
if (garbing) {
print_garb_info(to, to_arg, p);
@@ -345,10 +347,13 @@ print_process_info(int to, void *to_arg, Process *p)
}
static void
-print_garb_info(int to, void *to_arg, Process* p)
+print_garb_info(fmtfn_t to, void *to_arg, Process* p)
{
+#ifdef ERTS_SMP
/* ERTS_SMP: A scheduler is probably concurrently doing gc... */
-#ifndef ERTS_SMP
+ if (!ERTS_IS_CRASH_DUMPING)
+ return;
+#endif
erts_print(to, to_arg, "New heap start: %bpX\n", p->heap);
erts_print(to, to_arg, "New heap top: %bpX\n", p->htop);
erts_print(to, to_arg, "Stack top: %bpX\n", p->stop);
@@ -356,11 +361,10 @@ print_garb_info(int to, void *to_arg, Process* p)
erts_print(to, to_arg, "Old heap start: %bpX\n", OLD_HEAP(p));
erts_print(to, to_arg, "Old heap top: %bpX\n", OLD_HTOP(p));
erts_print(to, to_arg, "Old heap end: %bpX\n", OLD_HEND(p));
-#endif
}
void
-info(int to, void *to_arg)
+info(fmtfn_t to, void *to_arg)
{
erts_memory(&to, to_arg, NULL, THE_NON_VALUE);
atom_info(to, to_arg);
@@ -375,13 +379,25 @@ info(int to, void *to_arg)
}
+static int code_size(struct erl_module_instance* modi)
+{
+ int size = modi->code_length;
+
+ if (modi->code_hdr) {
+ ErtsLiteralArea* lit = modi->code_hdr->literal_area;
+ if (lit)
+ size += (lit->end - lit->start) * sizeof(Eterm);
+ }
+ return size;
+}
+
void
-loaded(int to, void *to_arg)
+loaded(fmtfn_t to, void *to_arg)
{
int i;
int old = 0;
int cur = 0;
- BeamInstr* code;
+ BeamCodeHeader* code;
Module* modp;
ErtsCodeIndex code_ix;
@@ -392,13 +408,9 @@ loaded(int to, void *to_arg)
* Calculate and print totals.
*/
for (i = 0; i < module_code_size(code_ix); i++) {
- if ((modp = module_code(i, code_ix)) != NULL &&
- ((modp->curr.code_length != 0) ||
- (modp->old.code_length != 0))) {
- cur += modp->curr.code_length;
- if (modp->old.code_length != 0) {
- old += modp->old.code_length;
- }
+ if ((modp = module_code(i, code_ix)) != NULL) {
+ cur += code_size(&modp->curr);
+ old += code_size(&modp->old);
}
}
erts_print(to, to_arg, "Current code: %d\n", cur);
@@ -414,55 +426,49 @@ loaded(int to, void *to_arg)
/*
* Interactive dump; keep it brief.
*/
- if (modp != NULL &&
- ((modp->curr.code_length != 0) ||
- (modp->old.code_length != 0))) {
- erts_print(to, to_arg, "%T", make_atom(modp->module));
- cur += modp->curr.code_length;
- erts_print(to, to_arg, " %d", modp->curr.code_length );
- if (modp->old.code_length != 0) {
- erts_print(to, to_arg, " (%d old)",
- modp->old.code_length );
- old += modp->old.code_length;
- }
+ if (modp != NULL && ((modp->curr.code_length != 0) ||
+ (modp->old.code_length != 0))) {
+ erts_print(to, to_arg, "%T %d", make_atom(modp->module),
+ code_size(&modp->curr));
+ if (modp->old.code_length != 0)
+ erts_print(to, to_arg, " (%d old)", code_size(&modp->old));
erts_print(to, to_arg, "\n");
}
} else {
/*
* To crash dump; make it parseable.
*/
- if (modp != NULL &&
- ((modp->curr.code_length != 0) ||
- (modp->old.code_length != 0))) {
+ if (modp != NULL && ((modp->curr.code_length != 0) ||
+ (modp->old.code_length != 0))) {
erts_print(to, to_arg, "=mod:");
erts_print(to, to_arg, "%T", make_atom(modp->module));
erts_print(to, to_arg, "\n");
erts_print(to, to_arg, "Current size: %d\n",
- modp->curr.code_length);
- code = modp->curr.code;
- if (code != NULL && code[MI_ATTR_PTR]) {
+ code_size(&modp->curr));
+ code = modp->curr.code_hdr;
+ if (code != NULL && code->attr_ptr) {
erts_print(to, to_arg, "Current attributes: ");
- dump_attributes(to, to_arg, (byte *) code[MI_ATTR_PTR],
- code[MI_ATTR_SIZE]);
+ dump_attributes(to, to_arg, code->attr_ptr,
+ code->attr_size);
}
- if (code != NULL && code[MI_COMPILE_PTR]) {
+ if (code != NULL && code->compile_ptr) {
erts_print(to, to_arg, "Current compilation info: ");
- dump_attributes(to, to_arg, (byte *) code[MI_COMPILE_PTR],
- code[MI_COMPILE_SIZE]);
+ dump_attributes(to, to_arg, code->compile_ptr,
+ code->compile_size);
}
if (modp->old.code_length != 0) {
- erts_print(to, to_arg, "Old size: %d\n", modp->old.code_length);
- code = modp->old.code;
- if (code[MI_ATTR_PTR]) {
+ erts_print(to, to_arg, "Old size: %d\n", code_size(&modp->old));
+ code = modp->old.code_hdr;
+ if (code->attr_ptr) {
erts_print(to, to_arg, "Old attributes: ");
- dump_attributes(to, to_arg, (byte *) code[MI_ATTR_PTR],
- code[MI_ATTR_SIZE]);
+ dump_attributes(to, to_arg, code->attr_ptr,
+ code->attr_size);
}
- if (code[MI_COMPILE_PTR]) {
+ if (code->compile_ptr) {
erts_print(to, to_arg, "Old compilation info: ");
- dump_attributes(to, to_arg, (byte *) code[MI_COMPILE_PTR],
- code[MI_COMPILE_SIZE]);
+ dump_attributes(to, to_arg, code->compile_ptr,
+ code->compile_size);
}
}
}
@@ -473,7 +479,7 @@ loaded(int to, void *to_arg)
static void
-dump_attributes(int to, void *to_arg, byte* ptr, int size)
+dump_attributes(fmtfn_t to, void *to_arg, byte* ptr, int size)
{
while (size-- > 0) {
erts_print(to, to_arg, "%02X", *ptr++);
@@ -643,7 +649,7 @@ bin_check(void)
erts_printf("%p orig_size: %bpd, norefs = %bpd\n",
bp->val,
bp->val->orig_size,
- erts_smp_atomic_read_nob(&bp->val->refc));
+ erts_refc_read(&bp->val->refc, 1));
}
}
if (printed) {
@@ -657,6 +663,26 @@ bin_check(void)
#endif
+static Sint64 crash_dump_limit = ERTS_SINT64_MAX;
+static Sint64 crash_dump_written = 0;
+
+static int crash_dump_limited_writer(void* vfdp, char* buf, size_t len)
+{
+ const char stop_msg[] = "\n=abort:CRASH DUMP SIZE LIMIT REACHED\n";
+
+ crash_dump_written += len;
+ if (crash_dump_written <= crash_dump_limit) {
+ return erts_write_fd(vfdp, buf, len);
+ }
+
+ len -= (crash_dump_written - crash_dump_limit);
+ erts_write_fd(vfdp, buf, len);
+ erts_write_fd(vfdp, (char*)stop_msg, sizeof(stop_msg)-1);
+
+ /* We assume that crash dump was called from erts_exit_vv() */
+ erts_exit_epilogue();
+}
+
/* XXX THIS SHOULD BE IN SYSTEM !!!! */
void
erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
@@ -674,6 +700,8 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
int secs;
int env_erl_crash_dump_seconds_set = 1;
int i;
+ fmtfn_t to = &erts_write_fd;
+ void* to_arg;
if (ERTS_SOMEONE_IS_CRASH_DUMPING)
return;
@@ -684,13 +712,15 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
crash dump. */
erts_thr_progress_fatal_error_block(&tpd_buf);
-#ifdef ERTS_THR_HAVE_SIG_FUNCS
+#ifdef ERTS_SYS_SUSPEND_SIGNAL
/*
* We suspend all scheduler threads so that we can dump some
* data about the currently running processes and scheduler data.
* We have to be very very careful when doing this as the schedulers
* could be anywhere.
*/
+ sys_init_suspend_handler();
+
for (i = 0; i < erts_no_schedulers; i++) {
erts_tid_t tid = ERTS_SCHEDULER_IX(i)->tid;
if (!erts_equal_tids(tid,erts_thr_self()))
@@ -754,6 +784,21 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
return;
}
+ crash_dump_limit = ERTS_SINT64_MAX;
+ envsz = sizeof(env);
+ if (erts_sys_getenv__("ERL_CRASH_DUMP_BYTES", env, &envsz) == 0) {
+ Sint64 limit;
+ char* endptr;
+ errno = 0;
+ limit = ErtsStrToSint64(env, &endptr, 10);
+ if (errno == 0 && limit >= 0 && endptr != env && *endptr == 0) {
+ if (limit == 0)
+ return;
+ crash_dump_limit = limit;
+ to = &crash_dump_limited_writer;
+ }
+ }
+
if (erts_sys_getenv__("ERL_CRASH_DUMP",&dumpnamebuf[0],&dumpnamebufsize) != 0)
dumpname = "erl_crash.dump";
else
@@ -764,39 +809,40 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
fd = open(dumpname,O_WRONLY | O_CREAT | O_TRUNC,0640);
if (fd < 0)
return; /* Can't create the crash dump, skip it */
+ to_arg = (void*)&fd;
time(&now);
- erts_fdprintf(fd, "=erl_crash_dump:0.3\n%s", ctime(&now));
+ erts_cbprintf(to, to_arg, "=erl_crash_dump:0.3\n%s", ctime(&now));
if (file != NULL)
- erts_fdprintf(fd, "The error occurred in file %s, line %d\n", file, line);
+ erts_cbprintf(to, to_arg, "The error occurred in file %s, line %d\n", file, line);
if (fmt != NULL && *fmt != '\0') {
- erts_fdprintf(fd, "Slogan: ");
- erts_vfdprintf(fd, fmt, args);
+ erts_cbprintf(to, to_arg, "Slogan: ");
+ erts_vcbprintf(to, to_arg, fmt, args);
}
- erts_fdprintf(fd, "System version: ");
- erts_print_system_version(fd, NULL, NULL);
+ erts_cbprintf(to, to_arg, "System version: ");
+ erts_print_system_version(to, to_arg, NULL);
#if ERTS_SAVED_COMPILE_TIME
- erts_fdprintf(fd, "%s\n", "Compiled: " ERLANG_COMPILE_DATE);
+ erts_cbprintf(to, to_arg, "%s\n", "Compiled: " ERLANG_COMPILE_DATE);
#endif
- erts_fdprintf(fd, "Taints: ");
- erts_print_nif_taints(fd, NULL);
- erts_fdprintf(fd, "Atoms: %d\n", atom_table_size());
+ erts_cbprintf(to, to_arg, "Taints: ");
+ erts_print_nif_taints(to, to_arg);
+ erts_cbprintf(to, to_arg, "Atoms: %d\n", atom_table_size());
#ifdef USE_THREADS
/* We want to note which thread it was that called erts_exit */
if (erts_get_scheduler_data()) {
- erts_fdprintf(fd, "Calling Thread: scheduler:%d\n",
+ erts_cbprintf(to, to_arg, "Calling Thread: scheduler:%d\n",
erts_get_scheduler_data()->no);
} else {
if (!erts_thr_getname(erts_thr_self(), dumpnamebuf, MAXPATHLEN))
- erts_fdprintf(fd, "Calling Thread: %s\n", dumpnamebuf);
+ erts_cbprintf(to, to_arg, "Calling Thread: %s\n", dumpnamebuf);
else
- erts_fdprintf(fd, "Calling Thread: %p\n", erts_thr_self());
+ erts_cbprintf(to, to_arg, "Calling Thread: %p\n", erts_thr_self());
}
#else
- erts_fdprintf(fd, "Calling Thread: scheduler:1\n");
+ erts_cbprintf(to, to_arg, "Calling Thread: scheduler:1\n");
#endif
#if defined(ERTS_HAVE_TRY_CATCH)
@@ -811,14 +857,14 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
*/
for (i = 0; i < erts_no_schedulers; i++) {
ERTS_SYS_TRY_CATCH(
- erts_print_scheduler_info(fd, NULL, ERTS_SCHEDULER_IX(i)),
- erts_fdprintf(fd, "** crashed **\n"));
+ erts_print_scheduler_info(to, to_arg, ERTS_SCHEDULER_IX(i)),
+ erts_cbprintf(to, to_arg, "** crashed **\n"));
}
#endif
#ifdef ERTS_SMP
-#if defined(ERTS_THR_HAVE_SIG_FUNCS)
+#ifdef ERTS_SYS_SUSPEND_SIGNAL
/* We resume all schedulers so that we are in a known safe state
when we write the rest of the crash dump */
@@ -843,48 +889,39 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
#ifndef ERTS_HAVE_TRY_CATCH
/* This is safe to call here, as all schedulers are blocked */
for (i = 0; i < erts_no_schedulers; i++) {
- erts_print_scheduler_info(fd, NULL, ERTS_SCHEDULER_IX(i));
+ erts_print_scheduler_info(to, to_arg, ERTS_SCHEDULER_IX(i));
}
#endif
- info(fd, NULL); /* General system info */
+ info(to, to_arg); /* General system info */
if (erts_ptab_initialized(&erts_proc))
- process_info(fd, NULL); /* Info about each process and port */
- db_info(fd, NULL, 0);
- erts_print_bif_timer_info(fd, NULL);
- distribution_info(fd, NULL);
- erts_fdprintf(fd, "=loaded_modules\n");
- loaded(fd, NULL);
- erts_dump_fun_entries(fd, NULL);
- erts_deep_process_dump(fd, NULL);
- erts_fdprintf(fd, "=atoms\n");
- dump_atoms(fd, NULL);
+ process_info(to, to_arg); /* Info about each process and port */
+ db_info(to, to_arg, 0);
+ erts_print_bif_timer_info(to, to_arg);
+ distribution_info(to, to_arg);
+ erts_cbprintf(to, to_arg, "=loaded_modules\n");
+ loaded(to, to_arg);
+ erts_dump_fun_entries(to, to_arg);
+ erts_deep_process_dump(to, to_arg);
+ erts_cbprintf(to, to_arg, "=atoms\n");
+ dump_atoms(to, to_arg);
/* Keep the instrumentation data at the end of the dump */
if (erts_instr_memory_map || erts_instr_stat) {
- erts_fdprintf(fd, "=instr_data\n");
+ erts_cbprintf(to, to_arg, "=instr_data\n");
if (erts_instr_stat) {
- erts_fdprintf(fd, "=memory_status\n");
- erts_instr_dump_stat_to_fd(fd, 0);
+ erts_cbprintf(to, to_arg, "=memory_status\n");
+ erts_instr_dump_stat_to(to, to_arg, 0);
}
if (erts_instr_memory_map) {
- erts_fdprintf(fd, "=memory_map\n");
- erts_instr_dump_memory_map_to_fd(fd);
+ erts_cbprintf(to, to_arg, "=memory_map\n");
+ erts_instr_dump_memory_map_to(to, to_arg);
}
}
- erts_fdprintf(fd, "=end\n");
+ erts_cbprintf(to, to_arg, "=end\n");
close(fd);
erts_fprintf(stderr,"done\n");
}
-void
-erl_crash_dump(char* file, int line, char* fmt, ...)
-{
- va_list args;
-
- va_start(args, fmt);
- erl_crash_dump_v(file, line, fmt, args);
- va_end(args);
-}
diff --git a/erts/emulator/beam/code_ix.c b/erts/emulator/beam/code_ix.c
index 209d008d18..ec6267711b 100644
--- a/erts/emulator/beam/code_ix.c
+++ b/erts/emulator/beam/code_ix.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2012. All Rights Reserved.
+ * Copyright Ericsson AB 2012-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -65,12 +65,12 @@ void erts_code_ix_init(void)
CIX_TRACE("init");
}
-void erts_start_staging_code_ix(void)
+void erts_start_staging_code_ix(int num_new)
{
beam_catches_start_staging();
export_start_staging();
module_start_staging();
- erts_start_staging_ranges();
+ erts_start_staging_ranges(num_new);
CIX_TRACE("start");
}
@@ -94,6 +94,7 @@ void erts_commit_staging_code_ix(void)
ix = (ix + 1) % ERTS_NUM_CODE_IX;
erts_smp_atomic32_set_nob(&the_staging_code_index, ix);
export_staging_unlock();
+ erts_tracer_nif_clear();
CIX_TRACE("activate");
}
diff --git a/erts/emulator/beam/code_ix.h b/erts/emulator/beam/code_ix.h
index 5f00b409ef..584a605771 100644
--- a/erts/emulator/beam/code_ix.h
+++ b/erts/emulator/beam/code_ix.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2012-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2012-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -100,7 +100,7 @@ void erts_release_code_write_permission(void);
* Must be followed by calls to either "end" and "commit" or "abort" before
* code write permission can be released.
*/
-void erts_start_staging_code_ix(void);
+void erts_start_staging_code_ix(int num_new);
/* End the staging.
* Preceded by "start" and must be followed by "commit".
diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c
index 64be43edb4..de68adbeb6 100644
--- a/erts/emulator/beam/copy.c
+++ b/erts/emulator/beam/copy.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2012. 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.
@@ -35,56 +35,59 @@
#include "erl_bits.h"
#include "dtrace-wrapper.h"
-static void move_one_frag(Eterm** hpp, ErlHeapFragment*, ErlOffHeap*);
+static void move_one_frag(Eterm** hpp, ErlHeapFragment*, ErlOffHeap*, int);
/*
* Copy object "obj" to process p.
*/
-Eterm
-copy_object(Eterm obj, Process* to)
-{
- Uint size = size_object(obj);
- Eterm* hp = HAlloc(to, size);
- Eterm res;
+Eterm copy_object_x(Eterm obj, Process* to, Uint extra) {
+ if (!is_immed(obj)) {
+ Uint size = size_object(obj);
+ Eterm* hp = HAllocX(to, size, extra);
+ Eterm res;
#ifdef USE_VM_PROBES
- if (DTRACE_ENABLED(copy_object)) {
- DTRACE_CHARBUF(proc_name, 64);
+ if (DTRACE_ENABLED(copy_object)) {
+ DTRACE_CHARBUF(proc_name, 64);
- erts_snprintf(proc_name, sizeof(DTRACE_CHARBUF_NAME(proc_name)),
- "%T", to->common.id);
- DTRACE2(copy_object, proc_name, size);
- }
+ erts_snprintf(proc_name, sizeof(DTRACE_CHARBUF_NAME(proc_name)),
+ "%T", to->common.id);
+ DTRACE2(copy_object, proc_name, size);
+ }
#endif
- res = copy_struct(obj, size, &hp, &to->off_heap);
+ res = copy_struct(obj, size, &hp, &to->off_heap);
#ifdef DEBUG
- if (eq(obj, res) == 0) {
- erts_exit(ERTS_ABORT_EXIT, "copy not equal to source\n");
- }
+ if (eq(obj, res) == 0) {
+ erts_exit(ERTS_ABORT_EXIT, "copy not equal to source\n");
+ }
#endif
- return res;
+ return res;
+ }
+ return obj;
}
/*
* Return the "flat" size of the object.
*/
-#if HALFWORD_HEAP
-Uint size_object_rel(Eterm obj, Eterm* base)
-#else
Uint size_object(Eterm obj)
-#endif
{
Uint sum = 0;
Eterm* ptr;
int arity;
+#ifdef DEBUG
+ Eterm mypid = erts_get_current_pid();
+#endif
DECLARE_ESTACK(s);
+
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] size_object %p\n", mypid, obj));
+
for (;;) {
switch (primary_tag(obj)) {
case TAG_PRIMARY_LIST:
sum += 2;
- ptr = list_val_rel(obj,base);
+ ptr = list_val(obj);
obj = *ptr++;
if (!IS_CONST(obj)) {
ESTACK_PUSH(s, obj);
@@ -93,11 +96,11 @@ Uint size_object(Eterm obj)
break;
case TAG_PRIMARY_BOXED:
{
- Eterm hdr = *boxed_val_rel(obj,base);
+ Eterm hdr = *boxed_val(obj);
ASSERT(is_header(hdr));
switch (hdr & _TAG_HEADER_MASK) {
case ARITYVAL_SUBTAG:
- ptr = tuple_val_rel(obj,base);
+ ptr = tuple_val(obj);
arity = header_arity(hdr);
sum += arity + 1;
if (arity == 0) { /* Empty tuple -- unusual. */
@@ -113,7 +116,7 @@ Uint size_object(Eterm obj)
break;
case FUN_SUBTAG:
{
- Eterm* bptr = fun_val_rel(obj,base);
+ Eterm* bptr = fun_val(obj);
ErlFunThing* funp = (ErlFunThing *) bptr;
unsigned eterms = 1 /* creator */ + funp->num_free;
unsigned sz = thing_arityval(hdr);
@@ -134,7 +137,7 @@ Uint size_object(Eterm obj)
{
Uint n;
flatmap_t *mp;
- mp = (flatmap_t*)flatmap_val_rel(obj,base);
+ mp = (flatmap_t*)flatmap_val(obj);
ptr = (Eterm *)mp;
n = flatmap_get_size(mp) + 1;
sum += n + 2;
@@ -153,7 +156,7 @@ Uint size_object(Eterm obj)
{
Eterm *head;
Uint sz;
- head = hashmap_val_rel(obj, base);
+ head = hashmap_val(obj);
sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
sum += 1 + sz + header_arity(hdr);
head += 1 + header_arity(hdr);
@@ -182,7 +185,7 @@ Uint size_object(Eterm obj)
Uint bitoffs;
Uint extra_bytes;
Eterm hdr;
- ERTS_GET_REAL_BIN_REL(obj, real_bin, offset, bitoffs, bitsize, base);
+ ERTS_GET_REAL_BIN(obj, real_bin, offset, bitoffs, bitsize);
if ((bitsize + bitoffs) > 8) {
sum += ERL_SUB_BIN_SIZE;
extra_bytes = 2;
@@ -192,11 +195,11 @@ Uint size_object(Eterm obj)
} else {
extra_bytes = 0;
}
- hdr = *binary_val_rel(real_bin,base);
+ hdr = *binary_val(real_bin);
if (thing_subtag(hdr) == REFC_BINARY_SUBTAG) {
sum += PROC_BIN_SIZE;
} else {
- sum += heap_bin_size(binary_size_rel(obj,base)+extra_bytes);
+ sum += heap_bin_size(binary_size(obj)+extra_bytes);
}
goto pop_next;
}
@@ -214,6 +217,7 @@ Uint size_object(Eterm obj)
pop_next:
if (ESTACK_ISEMPTY(s)) {
DESTROY_ESTACK(s);
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] size was: %u\n", mypid, sum));
return sum;
}
obj = ESTACK_POP(s);
@@ -225,14 +229,377 @@ Uint size_object(Eterm obj)
}
/*
+ * Machinery for sharing preserving information
+ * Using a WSTACK but not very transparently; consider refactoring
+ */
+
+#define DECLARE_BITSTORE(s) \
+ DECLARE_WSTACK(s); \
+ int WSTK_CONCAT(s,_bitoffs) = 0; \
+ int WSTK_CONCAT(s,_offset) = 0; \
+ UWord WSTK_CONCAT(s,_buffer) = 0
+
+#define DESTROY_BITSTORE(s) DESTROY_WSTACK(s)
+#define BITSTORE_PUT(s,i) \
+do { \
+ WSTK_CONCAT(s,_buffer) |= i << WSTK_CONCAT(s,_bitoffs); \
+ WSTK_CONCAT(s,_bitoffs) += 2; \
+ if (WSTK_CONCAT(s,_bitoffs) >= 8*sizeof(UWord)) { \
+ WSTACK_PUSH(s, WSTK_CONCAT(s,_buffer)); \
+ WSTK_CONCAT(s,_bitoffs) = 0; \
+ WSTK_CONCAT(s,_buffer) = 0; \
+ } \
+} while(0)
+#define BITSTORE_CLOSE(s) \
+do { \
+ if (WSTK_CONCAT(s,_bitoffs) > 0) { \
+ WSTACK_PUSH(s, WSTK_CONCAT(s,_buffer)); \
+ WSTK_CONCAT(s,_bitoffs) = 0; \
+ } \
+} while(0)
+
+#define BITSTORE_FETCH(s,dst) \
+do { \
+ UWord result; \
+ if (WSTK_CONCAT(s,_bitoffs) <= 0) { \
+ WSTK_CONCAT(s,_buffer) = s.wstart[WSTK_CONCAT(s,_offset)]; \
+ WSTK_CONCAT(s,_offset)++; \
+ WSTK_CONCAT(s,_bitoffs) = 8*sizeof(UWord); \
+ } \
+ WSTK_CONCAT(s,_bitoffs) -= 2; \
+ result = WSTK_CONCAT(s,_buffer) & 3; \
+ WSTK_CONCAT(s,_buffer) >>= 2; \
+ (dst) = result; \
+} while(0)
+
+#define BOXED_VISITED_MASK ((Eterm) 3)
+#define BOXED_VISITED ((Eterm) 1)
+#define BOXED_SHARED_UNPROCESSED ((Eterm) 2)
+#define BOXED_SHARED_PROCESSED ((Eterm) 3)
+
+#define COUNT_OFF_HEAP (0)
+
+#define IN_LITERAL_PURGE_AREA(info, ptr) \
+ ((info)->range_ptr && ( \
+ (info)->range_ptr <= (ptr) && \
+ (ptr) < ((info)->range_ptr + (info)->range_sz)))
+/*
+ * Return the real size of an object and find sharing information
+ * This currently returns the same as erts_debug:size/1.
+ * It is argued whether the size of subterms in constant pools
+ * should be counted or not.
+ */
+
+Uint size_shared(Eterm obj)
+{
+ Eterm saved_obj = obj;
+ Uint sum = 0;
+ Eterm* ptr;
+
+ DECLARE_EQUEUE(s);
+ DECLARE_BITSTORE(b);
+
+ for (;;) {
+ switch (primary_tag(obj)) {
+ case TAG_PRIMARY_LIST: {
+ Eterm head, tail;
+ ptr = list_val(obj);
+ /* we're not counting anything that's outside our heap */
+ if (!COUNT_OFF_HEAP && erts_is_literal(obj,ptr)) {
+ goto pop_next;
+ }
+ head = CAR(ptr);
+ tail = CDR(ptr);
+ /* if it's visited, don't count it */
+ if (primary_tag(tail) == TAG_PRIMARY_HEADER ||
+ primary_tag(head) == TAG_PRIMARY_HEADER) {
+ goto pop_next;
+ }
+ /* else make it visited now */
+ switch (primary_tag(tail)) {
+ case TAG_PRIMARY_LIST:
+ ptr[1] = (tail - TAG_PRIMARY_LIST) | TAG_PRIMARY_HEADER;
+ break;
+ case TAG_PRIMARY_IMMED1:
+ CAR(ptr) = (head - primary_tag(head)) | TAG_PRIMARY_HEADER;
+ CDR(ptr) = (tail - TAG_PRIMARY_IMMED1) | primary_tag(head);
+ break;
+ case TAG_PRIMARY_BOXED:
+ BITSTORE_PUT(b, primary_tag(head));
+ CAR(ptr) = (head - primary_tag(head)) | TAG_PRIMARY_HEADER;
+ CDR(ptr) = (tail - TAG_PRIMARY_BOXED) | TAG_PRIMARY_HEADER;
+ break;
+ }
+ /* and count it */
+ sum += 2;
+ if (!IS_CONST(head)) {
+ EQUEUE_PUT(s, head);
+ }
+ obj = tail;
+ break;
+ }
+ case TAG_PRIMARY_BOXED: {
+ Eterm hdr;
+ ptr = boxed_val(obj);
+ /* we're not counting anything that's outside our heap */
+ if (!COUNT_OFF_HEAP && erts_is_literal(obj,ptr)) {
+ goto pop_next;
+ }
+ hdr = *ptr;
+ /* if it's visited, don't count it */
+ if (primary_tag(hdr) != TAG_PRIMARY_HEADER) {
+ goto pop_next;
+ }
+ /* else make it visited now */
+ *ptr = (hdr - primary_tag(hdr)) + BOXED_VISITED;
+ /* and count it */
+ ASSERT(is_header(hdr));
+ switch (hdr & _TAG_HEADER_MASK) {
+ case ARITYVAL_SUBTAG: {
+ int arity = header_arity(hdr);
+ sum += arity + 1;
+ if (arity == 0) { /* Empty tuple -- unusual. */
+ goto pop_next;
+ }
+ while (arity-- > 0) {
+ obj = *++ptr;
+ if (!IS_CONST(obj)) {
+ EQUEUE_PUT(s, obj);
+ }
+ }
+ goto pop_next;
+ }
+ case FUN_SUBTAG: {
+ ErlFunThing* funp = (ErlFunThing *) ptr;
+ unsigned eterms = 1 /* creator */ + funp->num_free;
+ unsigned sz = thing_arityval(hdr);
+ sum += 1 /* header */ + sz + eterms;
+ ptr += 1 /* header */ + sz;
+ while (eterms-- > 0) {
+ obj = *ptr++;
+ if (!IS_CONST(obj)) {
+ EQUEUE_PUT(s, obj);
+ }
+ }
+ goto pop_next;
+ }
+ case SUB_BINARY_SUBTAG: {
+ ErlSubBin* sb = (ErlSubBin *) ptr;
+ Uint extra_bytes;
+ Eterm hdr;
+ ASSERT((sb->thing_word & ~BOXED_VISITED_MASK) == HEADER_SUB_BIN);
+ if (sb->bitsize + sb->bitoffs > 8) {
+ sum += ERL_SUB_BIN_SIZE;
+ extra_bytes = 2;
+ } else if (sb->bitsize + sb->bitoffs > 0) {
+ sum += ERL_SUB_BIN_SIZE;
+ extra_bytes = 1;
+ } else {
+ extra_bytes = 0;
+ }
+ ptr = binary_val(sb->orig);
+ hdr = (*ptr) & ~BOXED_VISITED_MASK;
+ if (thing_subtag(hdr) == REFC_BINARY_SUBTAG) {
+ sum += PROC_BIN_SIZE;
+ } else {
+ ASSERT(thing_subtag(hdr) == HEAP_BINARY_SUBTAG);
+ sum += heap_bin_size(binary_size(obj) + extra_bytes);
+ }
+ goto pop_next;
+ }
+ case MAP_SUBTAG:
+ switch (MAP_HEADER_TYPE(hdr)) {
+ case MAP_HEADER_TAG_FLATMAP_HEAD : {
+ flatmap_t *mp = (flatmap_t*)flatmap_val(obj);
+ Uint n = flatmap_get_size(mp) + 1;
+ ptr = (Eterm *)mp;
+ sum += n + 2;
+ ptr += 2; /* hdr + size words */
+ while (n--) {
+ obj = *ptr++;
+ if (!IS_CONST(obj)) {
+ EQUEUE_PUT(s, obj);
+ }
+ }
+ goto pop_next;
+ }
+ case MAP_HEADER_TAG_HAMT_HEAD_BITMAP :
+ case MAP_HEADER_TAG_HAMT_HEAD_ARRAY :
+ case MAP_HEADER_TAG_HAMT_NODE_BITMAP : {
+ Uint n = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+ sum += 1 + n + header_arity(hdr);
+ ptr += 1 + header_arity(hdr);
+ while (n--) {
+ obj = *ptr++;
+ if (!IS_CONST(obj)) {
+ EQUEUE_PUT(s, obj);
+ }
+ }
+ goto pop_next;
+ }
+ default:
+ erts_exit(ERTS_ABORT_EXIT, "size_shared: bad hashmap type %d\n", MAP_HEADER_TYPE(hdr));
+ }
+ case BIN_MATCHSTATE_SUBTAG:
+ erts_exit(ERTS_ABORT_EXIT,
+ "size_shared: matchstate term not allowed");
+ default:
+ sum += thing_arityval(hdr) + 1;
+ goto pop_next;
+ }
+ break;
+ }
+ case TAG_PRIMARY_IMMED1:
+ pop_next:
+ if (EQUEUE_ISEMPTY(s)) {
+ goto cleanup;
+ }
+ obj = EQUEUE_GET(s);
+ break;
+ default:
+ erts_exit(ERTS_ABORT_EXIT, "size_shared: bad tag for %#x\n", obj);
+ }
+ }
+
+cleanup:
+ obj = saved_obj;
+ BITSTORE_CLOSE(b);
+ for (;;) {
+ switch (primary_tag(obj)) {
+ case TAG_PRIMARY_LIST: {
+ Eterm head, tail;
+ ptr = list_val(obj);
+ if (!COUNT_OFF_HEAP && erts_is_literal(obj,ptr)) {
+ goto cleanup_next;
+ }
+ head = CAR(ptr);
+ tail = CDR(ptr);
+ /* if not already clean, clean it up */
+ if (primary_tag(tail) == TAG_PRIMARY_HEADER) {
+ if (primary_tag(head) == TAG_PRIMARY_HEADER) {
+ Eterm saved;
+ BITSTORE_FETCH(b, saved);
+ CAR(ptr) = head = (head - TAG_PRIMARY_HEADER) | saved;
+ CDR(ptr) = tail = (tail - TAG_PRIMARY_HEADER) | TAG_PRIMARY_BOXED;
+ } else {
+ CDR(ptr) = tail = (tail - TAG_PRIMARY_HEADER) | TAG_PRIMARY_LIST;
+ }
+ } else if (primary_tag(head) == TAG_PRIMARY_HEADER) {
+ CAR(ptr) = head = (head - TAG_PRIMARY_HEADER) | primary_tag(tail);
+ CDR(ptr) = tail = (tail - primary_tag(tail)) | TAG_PRIMARY_IMMED1;
+ } else {
+ goto cleanup_next;
+ }
+ /* and its children too */
+ if (!IS_CONST(head)) {
+ EQUEUE_PUT_UNCHECKED(s, head);
+ }
+ obj = tail;
+ break;
+ }
+ case TAG_PRIMARY_BOXED: {
+ Eterm hdr;
+ ptr = boxed_val(obj);
+ if (!COUNT_OFF_HEAP && erts_is_literal(obj,ptr)) {
+ goto cleanup_next;
+ }
+ hdr = *ptr;
+ /* if not already clean, clean it up */
+ if (primary_tag(hdr) == TAG_PRIMARY_HEADER) {
+ goto cleanup_next;
+ }
+ else {
+ ASSERT(primary_tag(hdr) == BOXED_VISITED);
+ *ptr = hdr = (hdr - BOXED_VISITED) + TAG_PRIMARY_HEADER;
+ }
+ /* and its children too */
+ switch (hdr & _TAG_HEADER_MASK) {
+ case ARITYVAL_SUBTAG: {
+ int arity = header_arity(hdr);
+ if (arity == 0) { /* Empty tuple -- unusual. */
+ goto cleanup_next;
+ }
+ while (arity-- > 0) {
+ obj = *++ptr;
+ if (!IS_CONST(obj)) {
+ EQUEUE_PUT_UNCHECKED(s, obj);
+ }
+ }
+ goto cleanup_next;
+ }
+ case FUN_SUBTAG: {
+ ErlFunThing* funp = (ErlFunThing *) ptr;
+ unsigned eterms = 1 /* creator */ + funp->num_free;
+ unsigned sz = thing_arityval(hdr);
+ ptr += 1 /* header */ + sz;
+ while (eterms-- > 0) {
+ obj = *ptr++;
+ if (!IS_CONST(obj)) {
+ EQUEUE_PUT_UNCHECKED(s, obj);
+ }
+ }
+ goto cleanup_next;
+ }
+ case MAP_SUBTAG:
+ switch (MAP_HEADER_TYPE(hdr)) {
+ case MAP_HEADER_TAG_FLATMAP_HEAD : {
+ flatmap_t *mp = (flatmap_t *) ptr;
+ Uint n = flatmap_get_size(mp) + 1;
+ ptr += 2; /* hdr + size words */
+ while (n--) {
+ obj = *ptr++;
+ if (!IS_CONST(obj)) {
+ EQUEUE_PUT_UNCHECKED(s, obj);
+ }
+ }
+ goto cleanup_next;
+ }
+ case MAP_HEADER_TAG_HAMT_HEAD_BITMAP :
+ case MAP_HEADER_TAG_HAMT_HEAD_ARRAY :
+ case MAP_HEADER_TAG_HAMT_NODE_BITMAP : {
+ Uint n = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+ sum += 1 + n + header_arity(hdr);
+ ptr += 1 + header_arity(hdr);
+ while (n--) {
+ obj = *ptr++;
+ if (!IS_CONST(obj)) {
+ EQUEUE_PUT_UNCHECKED(s, obj);
+ }
+ }
+ goto cleanup_next;
+ }
+ default:
+ erts_exit(ERTS_ABORT_EXIT, "size_shared: bad hashmap type %d\n", MAP_HEADER_TYPE(hdr));
+ }
+ default:
+ goto cleanup_next;
+ }
+ break;
+ }
+ case TAG_PRIMARY_IMMED1:
+ cleanup_next:
+ if (EQUEUE_ISEMPTY(s)) {
+ goto all_clean;
+ }
+ obj = EQUEUE_GET(s);
+ break;
+ default:
+ erts_exit(ERTS_ABORT_EXIT, "size_shared: bad tag for %#x\n", obj);
+ }
+ }
+
+ all_clean:
+ /* Return the result */
+ DESTROY_EQUEUE(s);
+ DESTROY_BITSTORE(b);
+ return sum;
+}
+
+
+/*
* Copy a structure to a heap.
*/
-#if HALFWORD_HEAP
-Eterm copy_struct_rel(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap,
- Eterm* src_base, Eterm* dst_base)
-#else
-Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
-#endif
+Eterm copy_struct_x(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap, Uint *bsz)
{
char* hstart;
Uint hsize;
@@ -247,19 +614,23 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
Eterm* argp;
Eterm* const_tuple;
Eterm hdr;
+ Eterm *hend;
int i;
#ifdef DEBUG
Eterm org_obj = obj;
Uint org_sz = sz;
+ Eterm mypid = erts_get_current_pid();
#endif
if (IS_CONST(obj))
return obj;
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] copy_struct %p\n", mypid, obj));
+
DTRACE1(copy_struct, (int32_t)sz);
hp = htop = *hpp;
- hbot = htop + sz;
+ hbot = hend = htop + sz;
hstart = (char *)htop;
hsize = (char*) hbot - hstart;
const_tuple = 0;
@@ -268,7 +639,7 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
switch (primary_tag(obj)) {
case TAG_PRIMARY_LIST:
argp = &res;
- objp = list_val_rel(obj,src_base);
+ objp = list_val(obj);
goto L_copy_list;
case TAG_PRIMARY_BOXED: argp = &res; goto L_copy_boxed;
default:
@@ -286,14 +657,11 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
hp++;
break;
case TAG_PRIMARY_LIST:
- objp = list_val_rel(obj,src_base);
- #if !HALFWORD_HEAP || defined(DEBUG)
- if (in_area(objp,hstart,hsize)) {
- ASSERT(!HALFWORD_HEAP);
+ objp = list_val(obj);
+ if (ErtsInArea(objp,hstart,hsize)) {
hp++;
break;
}
- #endif
argp = hp++;
/* Fall through */
@@ -309,23 +677,15 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
}
else {
CAR(htop) = elem;
- #if HALFWORD_HEAP
- CDR(htop) = CDR(objp);
- *tailp = make_list_rel(htop,dst_base);
- htop += 2;
- goto L_copy;
- #else
tailp = &CDR(htop);
htop += 2;
- #endif
}
- ASSERT(!HALFWORD_HEAP || tp < hp || tp >= hbot);
- *tp = make_list_rel(tailp - 1, dst_base);
+ *tp = make_list(tailp - 1);
obj = CDR(objp);
if (!is_list(obj)) {
break;
}
- objp = list_val_rel(obj,src_base);
+ objp = list_val(obj);
}
switch (primary_tag(obj)) {
case TAG_PRIMARY_IMMED1: *tailp = obj; goto L_copy;
@@ -337,24 +697,21 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
}
case TAG_PRIMARY_BOXED:
- #if !HALFWORD_HEAP || defined(DEBUG)
- if (in_area(boxed_val_rel(obj,src_base),hstart,hsize)) {
- ASSERT(!HALFWORD_HEAP);
+ if (ErtsInArea(boxed_val(obj),hstart,hsize)) {
hp++;
break;
}
- #endif
argp = hp++;
L_copy_boxed:
- objp = boxed_val_rel(obj, src_base);
+ objp = boxed_val(obj);
hdr = *objp;
switch (hdr & _TAG_HEADER_MASK) {
case ARITYVAL_SUBTAG:
{
int const_flag = 1; /* assume constant tuple */
i = arityval(hdr);
- *argp = make_tuple_rel(htop, dst_base);
+ *argp = make_tuple(htop);
tp = htop; /* tp is pointer to new arity value */
*htop++ = *objp++; /* copy arity value */
while (i--) {
@@ -383,7 +740,7 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
while (i--) {
*tp++ = *objp++;
}
- *argp = make_binary_rel(hbot, dst_base);
+ *argp = make_binary(hbot);
pb = (ProcBin*) hbot;
erts_refc_inc(&pb->val->refc, 2);
pb->next = off_heap->first;
@@ -410,7 +767,7 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
extra_bytes = 0;
}
real_size = size+extra_bytes;
- objp = binary_val_rel(real_bin,src_base);
+ objp = binary_val(real_bin);
if (thing_subtag(*objp) == HEAP_BINARY_SUBTAG) {
ErlHeapBin* from = (ErlHeapBin *) objp;
ErlHeapBin* to;
@@ -440,7 +797,7 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
off_heap->first = (struct erl_off_heap_header*) to;
OH_OVERHEAD(off_heap, to->size / sizeof(Eterm));
}
- *argp = make_binary_rel(hbot, dst_base);
+ *argp = make_binary(hbot);
if (extra_bytes != 0) {
ErlSubBin* res;
hbot -= ERL_SUB_BIN_SIZE;
@@ -452,7 +809,7 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
res->offs = 0;
res->is_writable = 0;
res->orig = *argp;
- *argp = make_binary_rel(hbot, dst_base);
+ *argp = make_binary(hbot);
}
break;
}
@@ -469,8 +826,8 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
funp = (ErlFunThing *) tp;
funp->next = off_heap->first;
off_heap->first = (struct erl_off_heap_header*) funp;
- erts_refc_inc(&funp->fe->refc, 2);
- *argp = make_fun_rel(tp, dst_base);
+ erts_smp_refc_inc(&funp->fe->refc, 2);
+ *argp = make_fun(tp);
}
break;
case EXTERNAL_PID_SUBTAG:
@@ -488,9 +845,9 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
etp->next = off_heap->first;
off_heap->first = (struct erl_off_heap_header*)etp;
- erts_refc_inc(&etp->node->refc, 2);
+ erts_smp_refc_inc(&etp->node->refc, 2);
- *argp = make_external_rel(tp, dst_base);
+ *argp = make_external(tp);
}
break;
case MAP_SUBTAG:
@@ -498,7 +855,7 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
switch (MAP_HEADER_TYPE(hdr)) {
case MAP_HEADER_TAG_FLATMAP_HEAD :
i = flatmap_get_size(objp) + 3;
- *argp = make_flatmap_rel(htop, dst_base);
+ *argp = make_flatmap(htop);
while (i--) {
*htop++ = *objp++;
}
@@ -509,7 +866,7 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
case MAP_HEADER_TAG_HAMT_NODE_BITMAP :
i = 1 + hashmap_bitcount(MAP_HEADER_VAL(hdr));
while (i--) { *htop++ = *objp++; }
- *argp = make_hashmap_rel(tp, dst_base);
+ *argp = make_hashmap(tp);
break;
default:
erts_exit(ERTS_ABORT_EXIT, "copy_struct: bad hashmap type %d\n", MAP_HEADER_TYPE(hdr));
@@ -522,7 +879,7 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
i = thing_arityval(hdr)+1;
hbot -= i;
tp = hbot;
- *argp = make_boxed_rel(hbot, dst_base);
+ *argp = make_boxed(hbot);
while (i--) {
*tp++ = *objp++;
}
@@ -538,22 +895,870 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
}
}
+ if (bsz) {
+ *hpp = htop;
+ *bsz = hend - hbot;
+ } else {
#ifdef DEBUG
- if (htop != hbot)
- erts_exit(ERTS_ABORT_EXIT,
- "Internal error in copy_struct() when copying %T:"
- " htop=%p != hbot=%p (sz=%beu)\n",
- org_obj, htop, hbot, org_sz);
+ if (htop != hbot)
+ erts_exit(ERTS_ABORT_EXIT,
+ "Internal error in copy_struct() when copying %T:"
+ " htop=%p != hbot=%p (sz=%beu)\n",
+ org_obj, htop, hbot, org_sz);
#else
- if (htop > hbot) {
- erts_exit(ERTS_ABORT_EXIT,
- "Internal error in copy_struct(): htop, hbot overrun\n");
- }
+ if (htop > hbot) {
+ erts_exit(ERTS_ABORT_EXIT,
+ "Internal error in copy_struct(): htop, hbot overrun\n");
+ }
#endif
- *hpp = (Eterm *) (hstart+hsize);
+ *hpp = (Eterm *) (hstart+hsize);
+ }
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] result is at %p\n", mypid, res));
return res;
}
+
+/*
+ * Machinery for the table used by the sharing preserving copier
+ * Using an ESTACK but not very transparently; consider refactoring
+ */
+
+#define DECLARE_SHTABLE(s) \
+ DECLARE_ESTACK(s); \
+ Uint ESTK_CONCAT(s,_offset) = 0
+#define DESTROY_SHTABLE(s) DESTROY_ESTACK(s)
+#define SHTABLE_INCR 4
+#define SHTABLE_NEXT(s) ESTK_CONCAT(s,_offset)
+#define SHTABLE_PUSH(s,x,y,b) \
+do { \
+ if (s.sp > s.end - SHTABLE_INCR) { \
+ erl_grow_estack(&(s), SHTABLE_INCR); \
+ } \
+ *s.sp++ = (x); \
+ *s.sp++ = (y); \
+ *s.sp++ = (Eterm) NULL; \
+ *s.sp++ = (Eterm) (b); \
+ ESTK_CONCAT(s,_offset) += SHTABLE_INCR; \
+} while(0)
+#define SHTABLE_X(s,e) (s.start[e])
+#define SHTABLE_Y(s,e) (s.start[(e)+1])
+#define SHTABLE_FWD(s,e) ((Eterm *) (s.start[(e)+2]))
+#define SHTABLE_FWD_UPD(s,e,p) (s.start[(e)+2] = (Eterm) (p))
+#define SHTABLE_REV(s,e) ((Eterm *) (s.start[(e)+3]))
+
+#define LIST_SHARED_UNPROCESSED ((Eterm) 0)
+#define LIST_SHARED_PROCESSED ((Eterm) 1)
+
+#define HEAP_ELEM_TO_BE_FILLED _unchecked_make_list(NULL)
+
+
+/*
+ * Specialized macros for using/reusing the persistent state
+ */
+
+#define DECLARE_EQUEUE_INIT_INFO(q, info) \
+ UWord* EQUE_DEF_QUEUE(q) = info->queue_default; \
+ ErtsEQueue q = { \
+ EQUE_DEF_QUEUE(q), /* start */ \
+ EQUE_DEF_QUEUE(q), /* front */ \
+ EQUE_DEF_QUEUE(q), /* back */ \
+ 1, /* possibly_empty */ \
+ EQUE_DEF_QUEUE(q) + DEF_EQUEUE_SIZE, /* end */ \
+ ERTS_ALC_T_ESTACK /* alloc_type */ \
+ }
+
+#define DECLARE_EQUEUE_FROM_INFO(q, info) \
+ /* no EQUE_DEF_QUEUE(q), read-only */ \
+ ErtsEQueue q = { \
+ info->queue_start, /* start */ \
+ info->queue_start, /* front */ \
+ info->queue_start, /* back */ \
+ 1, /* possibly_empty */ \
+ info->queue_end, /* end */ \
+ info->queue_alloc_type /* alloc_type */ \
+ }
+
+#define DECLARE_BITSTORE_INIT_INFO(s, info) \
+ UWord* WSTK_DEF_STACK(s) = info->bitstore_default; \
+ ErtsWStack s = { \
+ WSTK_DEF_STACK(s), /* wstart */ \
+ WSTK_DEF_STACK(s), /* wsp */ \
+ WSTK_DEF_STACK(s) + DEF_WSTACK_SIZE, /* wend */ \
+ WSTK_DEF_STACK(s), /* wdflt */ \
+ ERTS_ALC_T_ESTACK /* alloc_type */ \
+ }; \
+ int WSTK_CONCAT(s,_bitoffs) = 0; \
+ /* no WSTK_CONCAT(s,_offset), write-only */ \
+ UWord WSTK_CONCAT(s,_buffer) = 0
+
+#define DECLARE_BITSTORE_FROM_INFO(s, info) \
+ /* no WSTK_DEF_STACK(s), read-only */ \
+ ErtsWStack s = { \
+ info->bitstore_start, /* wstart */ \
+ NULL, /* wsp, read-only */ \
+ NULL, /* wend, read-only */ \
+ NULL, /* wdef, read-only */ \
+ info->bitstore_alloc_type /* alloc_type */ \
+ }; \
+ int WSTK_CONCAT(s,_bitoffs) = 0; \
+ int WSTK_CONCAT(s,_offset) = 0; \
+ UWord WSTK_CONCAT(s,_buffer) = 0
+
+#define DECLARE_SHTABLE_INIT_INFO(s, info) \
+ Eterm* ESTK_DEF_STACK(s) = info->shtable_default; \
+ ErtsEStack s = { \
+ ESTK_DEF_STACK(s), /* start */ \
+ ESTK_DEF_STACK(s), /* sp */ \
+ ESTK_DEF_STACK(s) + DEF_ESTACK_SIZE, /* end */ \
+ ESTK_DEF_STACK(s), /* default */ \
+ ERTS_ALC_T_ESTACK /* alloc_type */ \
+ }; \
+ Uint ESTK_CONCAT(s,_offset) = 0
+
+#define DECLARE_SHTABLE_FROM_INFO(s, info) \
+ /* no ESTK_DEF_STACK(s), read-only */ \
+ ErtsEStack s = { \
+ info->shtable_start, /* start */ \
+ NULL, /* sp, read-only */ \
+ NULL, /* end, read-only */ \
+ NULL, /* def, read-only */ \
+ info->shtable_alloc_type /* alloc_type */ \
+ }; \
+ /* no ESTK_CONCAT(s,_offset), read-only */
+
+/*
+ * Copy object "obj" preserving sharing.
+ * First half: count size and calculate sharing.
+ */
+Uint copy_shared_calculate(Eterm obj, erts_shcopy_t *info)
+{
+ Uint sum;
+ Uint e;
+ unsigned sz;
+ Eterm* ptr;
+#ifdef DEBUG
+ Eterm mypid = erts_get_current_pid();
+#endif
+
+ DECLARE_EQUEUE_INIT_INFO(s, info);
+ DECLARE_BITSTORE_INIT_INFO(b, info);
+ DECLARE_SHTABLE_INIT_INFO(t, info);
+
+ /* step #0:
+ -------------------------------------------------------
+ get rid of the easy cases first:
+ - copying constants
+ - if not a proper process, do flat copy
+ */
+
+ if (IS_CONST(obj))
+ return 0;
+
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] copy_shared_calculate %p\n", mypid, obj));
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] message is %T\n", mypid, obj));
+
+ /* step #1:
+ -------------------------------------------------------
+ traverse the term and calculate the size;
+ when traversing, transform as you do in size_shared
+ but when you find shared objects:
+
+ a. add entry in the table, indexed by i
+ b. mark them:
+ b1. boxed terms, set header to (i | 11)
+ store (old header, NONV, NULL, backptr) in the entry
+ b2. cons cells, set CDR to NONV, set CAR to i
+ store (old CAR, old CDR, NULL, backptr) in the entry
+ */
+
+ sum = 0;
+
+ for (;;) {
+ switch (primary_tag(obj)) {
+ case TAG_PRIMARY_LIST: {
+ Eterm head, tail;
+ ptr = list_val(obj);
+ /* off heap list pointers are copied verbatim */
+ if (erts_is_literal(obj,ptr)) {
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] bypassed copying %p is %T\n", mypid, ptr, obj));
+ if (IN_LITERAL_PURGE_AREA(info,ptr))
+ info->literal_size += size_object(obj);
+ goto pop_next;
+ }
+ head = CAR(ptr);
+ tail = CDR(ptr);
+ /* if it's visited, don't count it;
+ if not already shared, make it shared and store it in the table */
+ if (primary_tag(tail) == TAG_PRIMARY_HEADER ||
+ primary_tag(head) == TAG_PRIMARY_HEADER) {
+ if (tail != THE_NON_VALUE) {
+ e = SHTABLE_NEXT(t);
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] tabling L %p\n", mypid, ptr));
+ SHTABLE_PUSH(t, head, tail, ptr);
+ CAR(ptr) = (e << _TAG_PRIMARY_SIZE) | LIST_SHARED_UNPROCESSED;
+ CDR(ptr) = THE_NON_VALUE;
+ }
+ goto pop_next;
+ }
+ /* else make it visited now */
+ switch (primary_tag(tail)) {
+ case TAG_PRIMARY_LIST:
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] mangling L/L %p\n", mypid, ptr));
+ CDR(ptr) = (tail - TAG_PRIMARY_LIST) | TAG_PRIMARY_HEADER;
+ break;
+ case TAG_PRIMARY_IMMED1:
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] mangling L/I %p\n", mypid, ptr));
+ CAR(ptr) = (head - primary_tag(head)) | TAG_PRIMARY_HEADER;
+ CDR(ptr) = (tail - TAG_PRIMARY_IMMED1) | primary_tag(head);
+ break;
+ case TAG_PRIMARY_BOXED:
+ BITSTORE_PUT(b, primary_tag(head));
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] mangling L/B %p\n", mypid, ptr));
+ CAR(ptr) = (head - primary_tag(head)) | TAG_PRIMARY_HEADER;
+ CDR(ptr) = (tail - TAG_PRIMARY_BOXED) | TAG_PRIMARY_HEADER;
+ break;
+ }
+ /* and count it */
+ sum += 2;
+ if (!IS_CONST(head)) {
+ EQUEUE_PUT(s, head);
+ }
+ obj = tail;
+ break;
+ }
+ case TAG_PRIMARY_BOXED: {
+ Eterm hdr;
+ ptr = boxed_val(obj);
+ /* off heap pointers to boxes are copied verbatim */
+ if (erts_is_literal(obj,ptr)) {
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] bypassed copying %p is %T\n", mypid, ptr, obj));
+ if (IN_LITERAL_PURGE_AREA(info,ptr))
+ info->literal_size += size_object(obj);
+ goto pop_next;
+ }
+ hdr = *ptr;
+ /* if it's visited, don't count it;
+ if not already shared, make it shared and store it in the table */
+ if (primary_tag(hdr) != TAG_PRIMARY_HEADER) {
+ if (primary_tag(hdr) == BOXED_VISITED) {
+ e = SHTABLE_NEXT(t);
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] tabling B %p\n", mypid, ptr));
+ SHTABLE_PUSH(t, hdr, THE_NON_VALUE, ptr);
+ *ptr = (e << _TAG_PRIMARY_SIZE) | BOXED_SHARED_UNPROCESSED;
+ }
+ goto pop_next;
+ }
+ /* else make it visited now */
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] mangling B %p\n", mypid, ptr));
+ *ptr = (hdr - primary_tag(hdr)) + BOXED_VISITED;
+ /* and count it */
+ ASSERT(is_header(hdr));
+ switch (hdr & _TAG_HEADER_MASK) {
+ case ARITYVAL_SUBTAG: {
+ int arity = header_arity(hdr);
+ sum += arity + 1;
+ if (arity == 0) { /* Empty tuple -- unusual. */
+ goto pop_next;
+ }
+ while (arity-- > 0) {
+ obj = *++ptr;
+ if (!IS_CONST(obj)) {
+ EQUEUE_PUT(s, obj);
+ }
+ }
+ goto pop_next;
+ }
+ case FUN_SUBTAG: {
+ ErlFunThing* funp = (ErlFunThing *) ptr;
+ unsigned eterms = 1 /* creator */ + funp->num_free;
+ sz = thing_arityval(hdr);
+ sum += 1 /* header */ + sz + eterms;
+ ptr += 1 /* header */ + sz;
+ while (eterms-- > 0) {
+ obj = *ptr++;
+ if (!IS_CONST(obj)) {
+ EQUEUE_PUT(s, obj);
+ }
+ }
+ goto pop_next;
+ }
+ case SUB_BINARY_SUBTAG: {
+ ErlSubBin* sb = (ErlSubBin *) ptr;
+ Eterm real_bin = sb->orig;
+ Uint bit_offset = sb->bitoffs;
+ Uint bit_size = sb->bitsize;
+ size_t size = sb->size;
+ Uint extra_bytes;
+ Eterm hdr;
+ if (bit_size + bit_offset > 8) {
+ sum += ERL_SUB_BIN_SIZE;
+ extra_bytes = 2;
+ } else if (bit_size + bit_offset > 0) {
+ sum += ERL_SUB_BIN_SIZE;
+ extra_bytes = 1;
+ } else {
+ extra_bytes = 0;
+ }
+ ASSERT(is_boxed(real_bin) &&
+ (((*boxed_val(real_bin)) &
+ (_TAG_HEADER_MASK - _BINARY_XXX_MASK - BOXED_VISITED_MASK))
+ == _TAG_HEADER_REFC_BIN));
+ hdr = *_unchecked_binary_val(real_bin) & ~BOXED_VISITED_MASK;
+ if (thing_subtag(hdr) == HEAP_BINARY_SUBTAG) {
+ sum += heap_bin_size(size+extra_bytes);
+ } else {
+ ASSERT(thing_subtag(hdr) == REFC_BINARY_SUBTAG);
+ sum += PROC_BIN_SIZE;
+ }
+ goto pop_next;
+ }
+ case MAP_SUBTAG:
+ switch (MAP_HEADER_TYPE(hdr)) {
+ case MAP_HEADER_TAG_FLATMAP_HEAD : {
+ flatmap_t *mp = (flatmap_t *) ptr;
+ Uint n = flatmap_get_size(mp) + 1;
+ sum += n + 2;
+ ptr += 2; /* hdr + size words */
+ while (n--) {
+ obj = *ptr++;
+ if (!IS_CONST(obj)) {
+ EQUEUE_PUT(s, obj);
+ }
+ }
+ goto pop_next;
+ }
+ case MAP_HEADER_TAG_HAMT_HEAD_BITMAP :
+ case MAP_HEADER_TAG_HAMT_HEAD_ARRAY :
+ case MAP_HEADER_TAG_HAMT_NODE_BITMAP : {
+ Uint n = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+ sum += 1 + n + header_arity(hdr);
+ ptr += 1 + header_arity(hdr);
+
+ if (n == 0) {
+ goto pop_next;
+ }
+ while(n--) {
+ obj = *ptr++;
+ if (!IS_CONST(obj)) {
+ EQUEUE_PUT(s, obj);
+ }
+ }
+ goto pop_next;
+ }
+ default:
+ erts_exit(ERTS_ABORT_EXIT, "copy_shared_calculate: bad hashmap type %d\n", MAP_HEADER_TYPE(hdr));
+ }
+ case BIN_MATCHSTATE_SUBTAG:
+ erts_exit(ERTS_ABORT_EXIT,
+ "size_shared: matchstate term not allowed");
+ default:
+ sum += thing_arityval(hdr) + 1;
+ goto pop_next;
+ }
+ break;
+ }
+ case TAG_PRIMARY_IMMED1:
+ pop_next:
+ if (EQUEUE_ISEMPTY(s)) {
+ /* add sentinel to the table */
+ SHTABLE_PUSH(t, THE_NON_VALUE, THE_NON_VALUE, NULL);
+ /* store persistent info */
+ BITSTORE_CLOSE(b);
+ info->queue_start = s.start;
+ info->queue_end = s.end;
+ info->queue_alloc_type = s.alloc_type;
+ info->bitstore_start = b.wstart;
+ info->bitstore_alloc_type = b.alloc_type;
+ info->shtable_start = t.start;
+ info->shtable_alloc_type = t.alloc_type;
+ /* single point of return: the size of the object */
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] size was: %u\n", mypid, sum));
+ return sum + info->literal_size;
+ }
+ obj = EQUEUE_GET(s);
+ break;
+ default:
+ erts_exit(ERTS_ABORT_EXIT, "[pid=%T] size_shared: bad tag for %#x\n", obj);
+ }
+ }
+}
+
+/*
+ * Copy object "obj" preserving sharing.
+ * Second half: copy and restore the object.
+ */
+Uint copy_shared_perform(Eterm obj, Uint size, erts_shcopy_t *info,
+ Eterm** hpp, ErlOffHeap* off_heap) {
+ Uint e;
+ unsigned sz;
+ Eterm* ptr;
+ Eterm* hp;
+ Eterm* hscan;
+ Eterm result;
+ Eterm* resp;
+ Eterm *hbot, *hend;
+ unsigned remaining;
+#ifdef DEBUG
+ Eterm mypid = erts_get_current_pid();
+ Eterm saved_obj = obj;
+#endif
+
+ DECLARE_EQUEUE_FROM_INFO(s, info);
+ DECLARE_BITSTORE_FROM_INFO(b, info);
+ DECLARE_SHTABLE_FROM_INFO(t, info);
+
+ /* step #0:
+ -------------------------------------------------------
+ get rid of the easy cases first:
+ - copying constants
+ - if not a proper process, do flat copy
+ */
+
+ if (IS_CONST(obj))
+ return obj;
+
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] copy_shared_perform %p\n", mypid, obj));
+
+ /* step #2: was performed before this function was called
+ -------------------------------------------------------
+ allocate new space
+ */
+
+ hscan = hp = *hpp;
+ hbot = hend = hp + size;
+
+ /* step #3:
+ -------------------------------------------------------
+ traverse the term a second time and when traversing:
+ a. if the object is marked as shared
+ a1. if the entry contains a forwarding ptr, use that
+ a2. otherwise, copy it to the new space and store the
+ forwarding ptr to the entry
+ b. otherwise, reverse-transform as you do in size_shared
+ and copy to the new space
+ */
+
+ resp = &result;
+ remaining = 0;
+ for (;;) {
+ switch (primary_tag(obj)) {
+ case TAG_PRIMARY_LIST: {
+ Eterm head, tail;
+ ptr = list_val(obj);
+ /* off heap list pointers are copied verbatim */
+ if (erts_is_literal(obj,ptr)) {
+ if (!IN_LITERAL_PURGE_AREA(info,ptr)) {
+ *resp = obj;
+ } else {
+ Uint bsz = 0;
+ *resp = copy_struct_x(obj, hbot - hp, &hp, off_heap, &bsz);
+ hbot -= bsz;
+ }
+ goto cleanup_next;
+ }
+ head = CAR(ptr);
+ tail = CDR(ptr);
+ /* if it is shared */
+ if (tail == THE_NON_VALUE) {
+ e = head >> _TAG_PRIMARY_SIZE;
+ /* if it has been processed, just use the forwarding pointer */
+ if (primary_tag(head) == LIST_SHARED_PROCESSED) {
+ *resp = make_list(SHTABLE_FWD(t, e));
+ goto cleanup_next;
+ }
+ /* else, let's process it now,
+ copy it and keep the forwarding pointer */
+ else {
+ CAR(ptr) = (head - primary_tag(head)) + LIST_SHARED_PROCESSED;
+ head = SHTABLE_X(t, e);
+ tail = SHTABLE_Y(t, e);
+ ptr = &(SHTABLE_X(t, e));
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] tabled L %p is %p\n", mypid, ptr, SHTABLE_REV(t, e)));
+ SHTABLE_FWD_UPD(t, e, hp);
+ }
+ }
+ /* if not already clean, clean it up and copy it */
+ if (primary_tag(tail) == TAG_PRIMARY_HEADER) {
+ if (primary_tag(head) == TAG_PRIMARY_HEADER) {
+ Eterm saved;
+ BITSTORE_FETCH(b, saved);
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] unmangling L/B %p\n", mypid, ptr));
+ CAR(ptr) = head = (head - TAG_PRIMARY_HEADER) + saved;
+ CDR(ptr) = tail = (tail - TAG_PRIMARY_HEADER) + TAG_PRIMARY_BOXED;
+ } else {
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] unmangling L/L %p\n", mypid, ptr));
+ CDR(ptr) = tail = (tail - TAG_PRIMARY_HEADER) + TAG_PRIMARY_LIST;
+ }
+ } else if (primary_tag(head) == TAG_PRIMARY_HEADER) {
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] unmangling L/I %p\n", mypid, ptr));
+ CAR(ptr) = head = (head - TAG_PRIMARY_HEADER) | primary_tag(tail);
+ CDR(ptr) = tail = (tail - primary_tag(tail)) | TAG_PRIMARY_IMMED1;
+ } else {
+ ASSERT(0 && "cannot come here");
+ goto cleanup_next;
+ }
+ /* and its children too */
+ if (IS_CONST(head)) {
+ CAR(hp) = head;
+ } else {
+ EQUEUE_PUT_UNCHECKED(s, head);
+ CAR(hp) = HEAP_ELEM_TO_BE_FILLED;
+ }
+ *resp = make_list(hp);
+ resp = &(CDR(hp));
+ hp += 2;
+ obj = tail;
+ break;
+ }
+ case TAG_PRIMARY_BOXED: {
+ Eterm hdr;
+ ptr = boxed_val(obj);
+ /* off heap pointers to boxes are copied verbatim */
+ if (erts_is_literal(obj,ptr)) {
+ if (!IN_LITERAL_PURGE_AREA(info,ptr)) {
+ *resp = obj;
+ } else {
+ Uint bsz = 0;
+ *resp = copy_struct_x(obj, hbot - hp, &hp, off_heap, &bsz);
+ hbot -= bsz;
+ }
+ goto cleanup_next;
+ }
+ hdr = *ptr;
+ /* clean it up, unless it's already clean or shared and processed */
+ switch (primary_tag(hdr)) {
+ case TAG_PRIMARY_HEADER:
+ ASSERT(0 && "cannot come here");
+ /* if it is shared and has been processed,
+ just use the forwarding pointer */
+ case BOXED_SHARED_PROCESSED:
+ e = hdr >> _TAG_PRIMARY_SIZE;
+ *resp = make_boxed(SHTABLE_FWD(t, e));
+ goto cleanup_next;
+ /* if it is shared but has not been processed yet, let's process
+ it now: copy it and keep the forwarding pointer */
+ case BOXED_SHARED_UNPROCESSED:
+ e = hdr >> _TAG_PRIMARY_SIZE;
+ *ptr = (hdr - primary_tag(hdr)) + BOXED_SHARED_PROCESSED;
+ hdr = SHTABLE_X(t, e);
+ ASSERT(primary_tag(hdr) == BOXED_VISITED);
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] tabled B %p is %p\n", mypid, ptr, SHTABLE_REV(t, e)));
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] unmangling B %p\n", mypid, ptr));
+ SHTABLE_X(t, e) = hdr = (hdr - BOXED_VISITED) + TAG_PRIMARY_HEADER;
+ SHTABLE_FWD_UPD(t, e, hp);
+ break;
+ case BOXED_VISITED:
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] unmangling B %p\n", mypid, ptr));
+ *ptr = hdr = (hdr - BOXED_VISITED) + TAG_PRIMARY_HEADER;
+ break;
+ }
+ /* and its children too */
+ switch (hdr & _TAG_HEADER_MASK) {
+ case ARITYVAL_SUBTAG: {
+ int arity = header_arity(hdr);
+ *resp = make_boxed(hp);
+ *hp++ = hdr;
+ while (arity-- > 0) {
+ obj = *++ptr;
+ if (IS_CONST(obj)) {
+ *hp++ = obj;
+ } else {
+ EQUEUE_PUT_UNCHECKED(s, obj);
+ *hp++ = HEAP_ELEM_TO_BE_FILLED;
+ }
+ }
+ goto cleanup_next;
+ }
+ case FUN_SUBTAG: {
+ ErlFunThing* funp = (ErlFunThing *) ptr;
+ unsigned eterms = 1 /* creator */ + funp->num_free;
+ sz = thing_arityval(hdr);
+ funp = (ErlFunThing *) hp;
+ *resp = make_fun(hp);
+ *hp++ = hdr;
+ ptr++;
+ while (sz-- > 0) {
+ *hp++ = *ptr++;
+ }
+ while (eterms-- > 0) {
+ obj = *ptr++;
+ if (IS_CONST(obj)) {
+ *hp++ = obj;
+ } else {
+ EQUEUE_PUT_UNCHECKED(s, obj);
+ *hp++ = HEAP_ELEM_TO_BE_FILLED;
+ }
+ }
+ funp->next = off_heap->first;
+ off_heap->first = (struct erl_off_heap_header*) funp;
+ erts_smp_refc_inc(&funp->fe->refc, 2);
+ goto cleanup_next;
+ }
+ case MAP_SUBTAG:
+ *resp = make_flatmap(hp);
+ *hp++ = hdr;
+ switch (MAP_HEADER_TYPE(hdr)) {
+ case MAP_HEADER_TAG_FLATMAP_HEAD : {
+ flatmap_t *mp = (flatmap_t *) ptr;
+ Uint n = flatmap_get_size(mp) + 1;
+ *hp++ = *++ptr; /* keys */
+ while (n--) {
+ obj = *++ptr;
+ if (IS_CONST(obj)) {
+ *hp++ = obj;
+ } else {
+ EQUEUE_PUT_UNCHECKED(s, obj);
+ *hp++ = HEAP_ELEM_TO_BE_FILLED;
+ }
+ }
+ goto cleanup_next;
+ }
+ case MAP_HEADER_TAG_HAMT_HEAD_BITMAP :
+ case MAP_HEADER_TAG_HAMT_HEAD_ARRAY :
+ *hp++ = *++ptr; /* total map size */
+ case MAP_HEADER_TAG_HAMT_NODE_BITMAP : {
+ Uint n = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+ while (n--) {
+ obj = *++ptr;
+ if (IS_CONST(obj)) {
+ *hp++ = obj;
+ } else {
+ EQUEUE_PUT_UNCHECKED(s, obj);
+ *hp++ = HEAP_ELEM_TO_BE_FILLED;
+ }
+ }
+ goto cleanup_next;
+ }
+ default:
+ erts_exit(ERTS_ABORT_EXIT, "copy_shared_perform: bad hashmap type %d\n", MAP_HEADER_TYPE(hdr));
+ }
+ case REFC_BINARY_SUBTAG: {
+ ProcBin* pb = (ProcBin *) ptr;
+ sz = thing_arityval(hdr);
+ if (pb->flags) {
+ erts_emasculate_writable_binary(pb);
+ }
+ pb = (ProcBin *) hp;
+ *resp = make_binary(hp);
+ *hp++ = hdr;
+ ptr++;
+ while (sz-- > 0) {
+ *hp++ = *ptr++;
+ }
+ erts_refc_inc(&pb->val->refc, 2);
+ pb->next = off_heap->first;
+ pb->flags = 0;
+ off_heap->first = (struct erl_off_heap_header*) pb;
+ OH_OVERHEAD(off_heap, pb->size / sizeof(Eterm));
+ goto cleanup_next;
+ }
+ case SUB_BINARY_SUBTAG: {
+ ErlSubBin* sb = (ErlSubBin *) ptr;
+ Eterm real_bin = sb->orig;
+ Uint bit_offset = sb->bitoffs;
+ Uint bit_size = sb->bitsize;
+ Uint offset = sb->offs;
+ size_t size = sb->size;
+ Uint extra_bytes;
+ Uint real_size;
+ if ((bit_size + bit_offset) > 8) {
+ extra_bytes = 2;
+ } else if ((bit_size + bit_offset) > 0) {
+ extra_bytes = 1;
+ } else {
+ extra_bytes = 0;
+ }
+ real_size = size+extra_bytes;
+ ASSERT(is_boxed(real_bin) &&
+ (((*boxed_val(real_bin)) &
+ (_TAG_HEADER_MASK - _BINARY_XXX_MASK - BOXED_VISITED_MASK))
+ == _TAG_HEADER_REFC_BIN));
+ ptr = _unchecked_binary_val(real_bin);
+ *resp = make_binary(hp);
+ if (extra_bytes != 0) {
+ ErlSubBin* res = (ErlSubBin *) hp;
+ hp += ERL_SUB_BIN_SIZE;
+ res->thing_word = HEADER_SUB_BIN;
+ res->size = size;
+ res->bitsize = bit_size;
+ res->bitoffs = bit_offset;
+ res->offs = 0;
+ res->is_writable = 0;
+ res->orig = make_binary(hp);
+ }
+ if (thing_subtag(*ptr & ~BOXED_VISITED_MASK) == HEAP_BINARY_SUBTAG) {
+ ErlHeapBin* from = (ErlHeapBin *) ptr;
+ ErlHeapBin* to = (ErlHeapBin *) hp;
+ hp += heap_bin_size(real_size);
+ to->thing_word = header_heap_bin(real_size);
+ to->size = real_size;
+ sys_memcpy(to->data, ((byte *)from->data)+offset, real_size);
+ } else {
+ ProcBin* from = (ProcBin *) ptr;
+ ProcBin* to = (ProcBin *) hp;
+ ASSERT(thing_subtag(*ptr & ~BOXED_VISITED_MASK) == REFC_BINARY_SUBTAG);
+ if (from->flags) {
+ erts_emasculate_writable_binary(from);
+ }
+ hp += PROC_BIN_SIZE;
+ to->thing_word = HEADER_PROC_BIN;
+ to->size = real_size;
+ to->val = from->val;
+ erts_refc_inc(&to->val->refc, 2);
+ to->bytes = from->bytes + offset;
+ to->next = off_heap->first;
+ to->flags = 0;
+ off_heap->first = (struct erl_off_heap_header*) to;
+ OH_OVERHEAD(off_heap, to->size / sizeof(Eterm));
+ }
+ goto cleanup_next;
+ }
+ case EXTERNAL_PID_SUBTAG:
+ case EXTERNAL_PORT_SUBTAG:
+ case EXTERNAL_REF_SUBTAG: {
+ ExternalThing *etp = (ExternalThing *) hp;
+ sz = thing_arityval(hdr);
+ *resp = make_external(hp);
+ *hp++ = hdr;
+ ptr++;
+ while (sz-- > 0) {
+ *hp++ = *ptr++;
+ }
+ etp->next = off_heap->first;
+ off_heap->first = (struct erl_off_heap_header*) etp;
+ erts_smp_refc_inc(&etp->node->refc, 2);
+ goto cleanup_next;
+ }
+ default:
+ sz = thing_arityval(hdr);
+ *resp = make_boxed(hp);
+ *hp++ = hdr;
+ ptr++;
+ while (sz-- > 0) {
+ *hp++ = *ptr++;
+ }
+ goto cleanup_next;
+ }
+ break;
+ }
+ case TAG_PRIMARY_IMMED1:
+ *resp = obj;
+ cleanup_next:
+ if (EQUEUE_ISEMPTY(s)) {
+ goto all_clean;
+ }
+ obj = EQUEUE_GET(s);
+ for (;;) {
+ ASSERT(hscan < hp);
+ if (remaining == 0) {
+ if (*hscan == HEAP_ELEM_TO_BE_FILLED) {
+ resp = hscan;
+ hscan += 2;
+ break; /* scanning loop */
+ } else if (primary_tag(*hscan) == TAG_PRIMARY_HEADER) {
+ switch (*hscan & _TAG_HEADER_MASK) {
+ case ARITYVAL_SUBTAG:
+ remaining = header_arity(*hscan);
+ hscan++;
+ break;
+ case FUN_SUBTAG: {
+ ErlFunThing* funp = (ErlFunThing *) hscan;
+ hscan += 1 + thing_arityval(*hscan);
+ remaining = 1 + funp->num_free;
+ break;
+ }
+ case MAP_SUBTAG:
+ switch (MAP_HEADER_TYPE(*hscan)) {
+ case MAP_HEADER_TAG_FLATMAP_HEAD : {
+ flatmap_t *mp = (flatmap_t *) hscan;
+ remaining = flatmap_get_size(mp) + 1;
+ hscan += 2;
+ break;
+ }
+ case MAP_HEADER_TAG_HAMT_HEAD_BITMAP :
+ case MAP_HEADER_TAG_HAMT_HEAD_ARRAY :
+ case MAP_HEADER_TAG_HAMT_NODE_BITMAP :
+ remaining = hashmap_bitcount(MAP_HEADER_VAL(*hscan));
+ hscan += MAP_HEADER_ARITY(*hscan) + 1;
+ break;
+ default:
+ erts_exit(ERTS_ABORT_EXIT,
+ "copy_shared_perform: bad hashmap type %d\n",
+ MAP_HEADER_TYPE(*hscan));
+ }
+ break;
+ case SUB_BINARY_SUBTAG:
+ ASSERT(((ErlSubBin *) hscan)->bitoffs +
+ ((ErlSubBin *) hscan)->bitsize > 0);
+ hscan += ERL_SUB_BIN_SIZE;
+ break;
+ default:
+ hscan += 1 + thing_arityval(*hscan);
+ break;
+ }
+ } else {
+ hscan++;
+ }
+ } else if (*hscan == HEAP_ELEM_TO_BE_FILLED) {
+ resp = hscan++;
+ remaining--;
+ break; /* scanning loop */
+ } else {
+ hscan++;
+ remaining--;
+ }
+ }
+ ASSERT(resp < hp);
+ break;
+ default:
+ erts_exit(ERTS_ABORT_EXIT, "size_shared: bad tag for %#x\n", obj);
+ }
+ }
+
+ /* step #4:
+ -------------------------------------------------------
+ traverse the table and reverse-transform all stored entries
+ */
+
+all_clean:
+ for (e = 0; ; e += SHTABLE_INCR) {
+ ptr = SHTABLE_REV(t, e);
+ if (ptr == NULL)
+ break;
+ VERBOSE(DEBUG_SHCOPY, ("[copy] restoring shared: %x\n", ptr));
+ /* entry was a list */
+ if (SHTABLE_Y(t, e) != THE_NON_VALUE) {
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] untabling L %p\n", mypid, ptr));
+ CAR(ptr) = SHTABLE_X(t, e);
+ CDR(ptr) = SHTABLE_Y(t, e);
+ }
+ /* entry was boxed */
+ else {
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] untabling B %p\n", mypid, ptr));
+ *ptr = SHTABLE_X(t, e);
+ ASSERT(primary_tag(*ptr) == TAG_PRIMARY_HEADER);
+ }
+ }
+
+#ifdef DEBUG
+ if (eq(saved_obj, result) == 0) {
+ erts_fprintf(stderr, "original = %T\n", saved_obj);
+ erts_fprintf(stderr, "copy = %T\n", result);
+ erts_exit(ERTS_ABORT_EXIT, "copy (shared) not equal to source\n");
+ }
+#endif
+
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] original was %T\n", mypid, saved_obj));
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] copy is %T\n", mypid, result));
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] result is at %p\n", mypid, result));
+
+ ASSERT(hbot == hp);
+ ASSERT(size == ((hp - *hpp) + (hend - hbot)));
+ *hpp = hend;
+ return result;
+}
+
+
/*
* Copy a term that is guaranteed to be contained in a single
* heap block. The heap block is copied word by word, and any
@@ -563,21 +1768,12 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
*
* NOTE: Assumes that term is a tuple (ptr is an untagged tuple ptr).
*/
-#if HALFWORD_HEAP
-Eterm copy_shallow_rel(Eterm* ptr, Uint sz, Eterm** hpp, ErlOffHeap* off_heap,
- Eterm* src_base)
-#else
Eterm copy_shallow(Eterm* ptr, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
-#endif
{
Eterm* tp = ptr;
Eterm* hp = *hpp;
const Eterm res = make_tuple(hp);
-#if HALFWORD_HEAP
- const Sint offs = COMPRESS_POINTER(hp - (tp - src_base));
-#else
const Sint offs = (hp - tp) * sizeof(Eterm);
-#endif
while (sz--) {
Eterm val = *tp++;
@@ -606,7 +1802,7 @@ Eterm copy_shallow(Eterm* ptr, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
case FUN_SUBTAG:
{
ErlFunThing* funp = (ErlFunThing *) (tp-1);
- erts_refc_inc(&funp->fe->refc, 2);
+ erts_smp_refc_inc(&funp->fe->refc, 2);
}
goto off_heap_common;
case EXTERNAL_PID_SUBTAG:
@@ -614,7 +1810,7 @@ Eterm copy_shallow(Eterm* ptr, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
case EXTERNAL_REF_SUBTAG:
{
ExternalThing* etp = (ExternalThing *) (tp-1);
- erts_refc_inc(&etp->node->refc, 2);
+ erts_smp_refc_inc(&etp->node->refc, 2);
}
off_heap_common:
{
@@ -652,17 +1848,24 @@ Eterm copy_shallow(Eterm* ptr, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
* move markers.
* Typically used to copy a multi-fragmented message (from NIF).
*/
-void move_multi_frags(Eterm** hpp, ErlOffHeap* off_heap, ErlHeapFragment* first,
- Eterm* refs, unsigned nrefs)
+void erts_move_multi_frags(Eterm** hpp, ErlOffHeap* off_heap, ErlHeapFragment* first,
+ Eterm* refs, unsigned nrefs, int literals)
{
ErlHeapFragment* bp;
Eterm* hp_start = *hpp;
Eterm* hp_end;
Eterm* hp;
unsigned i;
+ Eterm literal_tag;
+
+#ifdef TAG_LITERAL_PTR
+ literal_tag = (Eterm) literals ? TAG_LITERAL_PTR : 0;
+#else
+ literal_tag = (Eterm) 0;
+#endif
for (bp=first; bp!=NULL; bp=bp->next) {
- move_one_frag(hpp, bp, off_heap);
+ move_one_frag(hpp, bp, off_heap, literals);
}
hp_end = *hpp;
for (hp=hp_start; hp<hp_end; ++hp) {
@@ -675,6 +1878,9 @@ void move_multi_frags(Eterm** hpp, ErlOffHeap* off_heap, ErlHeapFragment* first,
val = *ptr;
if (IS_MOVED_BOXED(val)) {
ASSERT(is_boxed(val));
+#ifdef TAG_LITERAL_PTR
+ val |= literal_tag;
+#endif
*hp = val;
}
break;
@@ -682,7 +1888,11 @@ void move_multi_frags(Eterm** hpp, ErlOffHeap* off_heap, ErlHeapFragment* first,
ptr = list_val(gval);
val = *ptr;
if (IS_MOVED_CONS(val)) {
- *hp = ptr[1];
+ val = ptr[1];
+#ifdef TAG_LITERAL_PTR
+ val |= literal_tag;
+#endif
+ *hp = val;
}
break;
case TAG_PRIMARY_HEADER:
@@ -693,12 +1903,12 @@ void move_multi_frags(Eterm** hpp, ErlOffHeap* off_heap, ErlHeapFragment* first,
}
}
for (i=0; i<nrefs; ++i) {
- refs[i] = follow_moved(refs[i]);
+ refs[i] = follow_moved(refs[i], literal_tag);
}
}
static void
-move_one_frag(Eterm** hpp, ErlHeapFragment* frag, ErlOffHeap* off_heap)
+move_one_frag(Eterm** hpp, ErlHeapFragment* frag, ErlOffHeap* off_heap, int literals)
{
Eterm* ptr = frag->mem;
Eterm* end = ptr + frag->used_size;
@@ -735,4 +1945,3 @@ move_one_frag(Eterm** hpp, ErlHeapFragment* frag, ErlOffHeap* off_heap)
OH_OVERHEAD(off_heap, frag->off_heap.overhead);
frag->off_heap.first = NULL;
}
-
diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c
index 787241b960..3611eeee02 100644
--- a/erts/emulator/beam/dist.c
+++ b/erts/emulator/beam/dist.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2014. 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.
@@ -337,7 +337,7 @@ static void doit_link_net_exits_sub(ErtsLink *sublnk, void *vlnecp)
erts_destroy_link(rlnk);
if (xres >= 0 && IS_TRACED_FL(rp, F_TRACE_PROCS)) {
/* We didn't exit the process and it is traced */
- trace_proc(NULL, rp, am_getting_unlinked, sublnk->pid);
+ trace_proc(NULL, 0, rp, am_getting_unlinked, sublnk->pid);
}
}
erts_smp_proc_unlock(rp, rp_locks);
@@ -378,10 +378,11 @@ static void doit_node_link_net_exits(ErtsLink *lnk, void *vnecp)
ASSERT(lnk->type == LINK_NODE);
if (is_internal_pid(lnk->pid)) {
ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK;
- rp = erts_pid2proc(NULL, 0, lnk->pid, rp_locks);
- if (!rp) {
+ ErlOffHeap *ohp;
+ rp = erts_proc_lookup(lnk->pid);
+ if (!rp)
goto done;
- }
+ erts_smp_proc_lock(rp, rp_locks);
rlnk = erts_remove_link(&ERTS_P_LINKS(rp), name);
if (rlnk != NULL) {
ASSERT(is_atom(rlnk->pid) && (rlnk->type == LINK_NODE));
@@ -389,12 +390,14 @@ static void doit_node_link_net_exits(ErtsLink *lnk, void *vnecp)
}
n = ERTS_LINK_REFC(lnk);
for (i = 0; i < n; ++i) {
- ErlHeapFragment* bp;
- ErlOffHeap *ohp;
Eterm tup;
- Eterm *hp = erts_alloc_message_heap(3,&bp,&ohp,rp,&rp_locks);
+ Eterm *hp;
+ ErtsMessage *msgp;
+
+ msgp = erts_alloc_message_heap(rp, &rp_locks,
+ 3, &hp, &ohp);
tup = TUPLE2(hp, am_nodedown, name);
- erts_queue_message(rp, &rp_locks, bp, tup, NIL);
+ erts_queue_message(rp, rp_locks, msgp, tup, am_system);
}
erts_smp_proc_unlock(rp, rp_locks);
}
@@ -737,19 +740,11 @@ Eterm erts_dsend_export_trap_context(Process* p, ErtsSendContext* ctx)
Binary* ctx_bin = erts_create_magic_binary(sizeof(struct exported_ctx),
erts_dsend_context_dtor);
struct exported_ctx* dst = ERTS_MAGIC_BIN_DATA(ctx_bin);
- Uint ctl_size = !HALFWORD_HEAP ? 0 : (arityval(ctx->ctl_heap[0]) + 1);
- Eterm* hp = HAlloc(p, ctl_size + PROC_BIN_SIZE);
+ Eterm* hp = HAlloc(p, PROC_BIN_SIZE);
sys_memcpy(&dst->ctx, ctx, sizeof(ErtsSendContext));
ASSERT(ctx->dss.ctl == make_tuple(ctx->ctl_heap));
-#if !HALFWORD_HEAP
dst->ctx.dss.ctl = make_tuple(dst->ctx.ctl_heap);
-#else
- /* Must put control tuple in low mem */
- sys_memcpy(hp, ctx->ctl_heap, ctl_size*sizeof(Eterm));
- dst->ctx.dss.ctl = make_tuple(hp);
- hp += ctl_size;
-#endif
if (ctx->dss.acmp) {
sys_memcpy(&dst->acm, ctx->dss.acmp, sizeof(ErtsAtomCacheMap));
dst->ctx.dss.acmp = &dst->acm;
@@ -886,11 +881,7 @@ erts_dsig_send_msg(Eterm remote, Eterm message, ErtsSendContext* ctx)
DTRACE_CHARBUF(receiver_name, 64);
#endif
- if (SEQ_TRACE_TOKEN(sender) != NIL
-#ifdef USE_VM_PROBES
- && SEQ_TRACE_TOKEN(sender) != am_have_dt_utag
-#endif
- ) {
+ if (have_seqtrace(SEQ_TRACE_TOKEN(sender))) {
seq_trace_update_send(sender);
token = SEQ_TRACE_TOKEN(sender);
seq_trace_output(token, message, SEQ_TRACE_SEND, remote, sender);
@@ -905,7 +896,7 @@ erts_dsig_send_msg(Eterm remote, Eterm message, ErtsSendContext* ctx)
erts_snprintf(receiver_name, sizeof(DTRACE_CHARBUF_NAME(receiver_name)),
"%T", remote);
msize = size_object(message);
- if (token != NIL && token != am_have_dt_utag) {
+ if (have_seqtrace(token)) {
tok_label = signed_val(SEQ_TRACE_T_LABEL(token));
tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token));
tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token));
@@ -915,9 +906,9 @@ erts_dsig_send_msg(Eterm remote, Eterm message, ErtsSendContext* ctx)
if (token != NIL)
ctl = TUPLE4(&ctx->ctl_heap[0],
- make_small(DOP_SEND_TT), am_Cookie, remote, token);
+ make_small(DOP_SEND_TT), am_Empty, remote, token);
else
- ctl = TUPLE3(&ctx->ctl_heap[0], make_small(DOP_SEND), am_Cookie, remote);
+ ctl = TUPLE3(&ctx->ctl_heap[0], make_small(DOP_SEND), am_Empty, remote);
DTRACE6(message_send, sender_name, receiver_name,
msize, tok_label, tok_lastcnt, tok_serial);
DTRACE7(message_send_remote, sender_name, node_name, receiver_name,
@@ -947,11 +938,7 @@ erts_dsig_send_reg_msg(Eterm remote_name, Eterm message,
DTRACE_CHARBUF(receiver_name, 128);
#endif
- if (SEQ_TRACE_TOKEN(sender) != NIL
-#ifdef USE_VM_PROBES
- && SEQ_TRACE_TOKEN(sender) != am_have_dt_utag
-#endif
- ) {
+ if (have_seqtrace(SEQ_TRACE_TOKEN(sender))) {
seq_trace_update_send(sender);
token = SEQ_TRACE_TOKEN(sender);
seq_trace_output(token, message, SEQ_TRACE_SEND, remote_name, sender);
@@ -966,7 +953,7 @@ erts_dsig_send_reg_msg(Eterm remote_name, Eterm message,
erts_snprintf(receiver_name, sizeof(DTRACE_CHARBUF_NAME(receiver_name)),
"{%T,%s}", remote_name, node_name);
msize = size_object(message);
- if (token != NIL && token != am_have_dt_utag) {
+ if (have_seqtrace(token)) {
tok_label = signed_val(SEQ_TRACE_T_LABEL(token));
tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token));
tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token));
@@ -976,10 +963,10 @@ erts_dsig_send_reg_msg(Eterm remote_name, Eterm message,
if (token != NIL)
ctl = TUPLE5(&ctx->ctl_heap[0], make_small(DOP_REG_SEND_TT),
- sender->common.id, am_Cookie, remote_name, token);
+ sender->common.id, am_Empty, remote_name, token);
else
ctl = TUPLE4(&ctx->ctl_heap[0], make_small(DOP_REG_SEND),
- sender->common.id, am_Cookie, remote_name);
+ sender->common.id, am_Empty, remote_name);
DTRACE6(message_send, sender_name, receiver_name,
msize, tok_label, tok_lastcnt, tok_serial);
DTRACE7(message_send_remote, sender_name, node_name, receiver_name,
@@ -1011,11 +998,7 @@ erts_dsig_send_exit_tt(ErtsDSigData *dsdp, Eterm local, Eterm remote,
#endif
UseTmpHeapNoproc(6);
- if (token != NIL
-#ifdef USE_VM_PROBES
- && token != am_have_dt_utag
-#endif
- ) {
+ if (have_seqtrace(token)) {
seq_trace_update_send(dsdp->proc);
seq_trace_output_exit(token, reason, SEQ_TRACE_SEND, remote, local);
ctl = TUPLE5(&ctl_heap[0],
@@ -1034,7 +1017,7 @@ erts_dsig_send_exit_tt(ErtsDSigData *dsdp, Eterm local, Eterm remote,
"{%T,%s}", remote, node_name);
erts_snprintf(reason_str, sizeof(DTRACE_CHARBUF_NAME(reason_str)),
"%T", reason);
- if (token != NIL && token != am_have_dt_utag) {
+ if (have_seqtrace(token)) {
tok_label = signed_val(SEQ_TRACE_T_LABEL(token));
tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token));
tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token));
@@ -1292,7 +1275,7 @@ int erts_net_message(Port *prt,
erts_smp_de_links_unlock(dep);
if (IS_TRACED_FL(rp, F_TRACE_PROCS))
- trace_proc(NULL, rp, am_getting_linked, from);
+ trace_proc(NULL, 0, rp, am_getting_linked, from);
erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
break;
@@ -1317,7 +1300,7 @@ int erts_net_message(Port *prt,
lnk = erts_remove_link(&ERTS_P_LINKS(rp), from);
if (IS_TRACED_FL(rp, F_TRACE_PROCS) && lnk != NULL) {
- trace_proc(NULL, rp, am_getting_unlinked, from);
+ trace_proc(NULL, 0, rp, am_getting_unlinked, from);
}
erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
@@ -1466,14 +1449,14 @@ int erts_net_message(Port *prt,
ErlOffHeap *ohp;
ASSERT(xsize);
heap_frag = erts_dist_ext_trailer(ede_copy);
- ERTS_INIT_HEAP_FRAG(heap_frag, token_size);
+ ERTS_INIT_HEAP_FRAG(heap_frag, token_size, token_size);
hp = heap_frag->mem;
ohp = &heap_frag->off_heap;
token = tuple[5];
token = copy_struct(token, token_size, &hp, ohp);
}
- erts_queue_dist_message(rp, &locks, ede_copy, token);
+ erts_queue_dist_message(rp, locks, ede_copy, token, from);
if (locks)
erts_smp_proc_unlock(rp, locks);
}
@@ -1515,14 +1498,14 @@ int erts_net_message(Port *prt,
ErlOffHeap *ohp;
ASSERT(xsize);
heap_frag = erts_dist_ext_trailer(ede_copy);
- ERTS_INIT_HEAP_FRAG(heap_frag, token_size);
+ ERTS_INIT_HEAP_FRAG(heap_frag, token_size, token_size);
hp = heap_frag->mem;
ohp = &heap_frag->off_heap;
token = tuple[4];
token = copy_struct(token, token_size, &hp, ohp);
}
- erts_queue_dist_message(rp, &locks, ede_copy, token);
+ erts_queue_dist_message(rp, locks, ede_copy, token, tuple[2]);
if (locks)
erts_smp_proc_unlock(rp, locks);
}
@@ -1645,7 +1628,11 @@ int erts_net_message(Port *prt,
ERTS_XSIG_FLG_IGN_KILL);
if (xres >= 0 && IS_TRACED_FL(rp, F_TRACE_PROCS)) {
/* We didn't exit the process and it is traced */
- trace_proc(NULL, rp, am_getting_unlinked, from);
+ if (rp_locks & ERTS_PROC_LOCKS_XSIG_SEND) {
+ erts_smp_proc_unlock(rp, ERTS_PROC_LOCKS_XSIG_SEND);
+ rp_locks &= ~ERTS_PROC_LOCKS_XSIG_SEND;
+ }
+ trace_proc(NULL, 0, rp, am_getting_unlinked, from);
}
}
erts_smp_proc_unlock(rp, rp_locks);
@@ -1736,7 +1723,7 @@ decode_error:
}
data_error:
UnUseTmpHeapNoproc(DIST_CTL_DEFAULT_SIZE);
- erts_deliver_port_exit(prt, dep->cid, am_killed, 0);
+ erts_deliver_port_exit(prt, dep->cid, am_killed, 0, 1);
ERTS_SMP_CHK_NO_PROC_LOCKS;
return -1;
}
@@ -1980,7 +1967,7 @@ dist_port_command(Port *prt, ErtsDistOutputBuf *obuf)
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
if (size > (Uint) INT_MAX)
- erts_exit(ERTS_ABORT_EXIT,
+ erts_exit(ERTS_DUMP_EXIT,
"Absurdly large distribution output data buffer "
"(%beu bytes) passed.\n",
size);
@@ -2020,7 +2007,7 @@ dist_port_commandv(Port *prt, ErtsDistOutputBuf *obuf)
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
if (size > (Uint) INT_MAX)
- erts_exit(ERTS_ABORT_EXIT,
+ erts_exit(ERTS_DUMP_EXIT,
"Absurdly large distribution output data buffer "
"(%beu bytes) passed.\n",
size);
@@ -2062,9 +2049,9 @@ dist_port_commandv(Port *prt, ErtsDistOutputBuf *obuf)
}
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
#define ERTS_PORT_REDS_MASK__ 0x003fffffffffffffL
-#elif defined(ARCH_32) || HALFWORD_HEAP
+#elif defined(ARCH_32)
#define ERTS_PORT_REDS_MASK__ 0x003fffff
#else
# error "Ohh come on ... !?!"
@@ -2094,7 +2081,7 @@ erts_dist_command(Port *prt, int reds_limit)
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
- erts_refc_inc(&dep->refc, 1); /* Otherwise dist_entry might be
+ erts_smp_refc_inc(&dep->refc, 1); /* Otherwise dist_entry might be
removed if port command fails */
erts_smp_atomic_set_mb(&dep->dist_cmd_scheduled, 0);
@@ -2106,7 +2093,7 @@ erts_dist_command(Port *prt, int reds_limit)
erts_smp_de_runlock(dep);
if (status & ERTS_DE_SFLG_EXITING) {
- erts_deliver_port_exit(prt, prt->common.id, am_killed, 0);
+ erts_deliver_port_exit(prt, prt->common.id, am_killed, 0, 1);
erts_deref_dist_entry(dep);
return reds + ERTS_PORT_REDS_DIST_CMD_EXIT;
}
@@ -2415,13 +2402,13 @@ erts_kill_dist_connection(DistEntry *dep, Uint32 connection_id)
}
struct print_to_data {
- int to;
+ fmtfn_t to;
void *arg;
};
static void doit_print_monitor_info(ErtsMonitor *mon, void *vptdp)
{
- int to = ((struct print_to_data *) vptdp)->to;
+ fmtfn_t to = ((struct print_to_data *) vptdp)->to;
void *arg = ((struct print_to_data *) vptdp)->arg;
Process *rp;
ErtsMonitor *rmon;
@@ -2444,7 +2431,7 @@ static void doit_print_monitor_info(ErtsMonitor *mon, void *vptdp)
}
}
-static void print_monitor_info(int to, void *arg, ErtsMonitor *mon)
+static void print_monitor_info(fmtfn_t to, void *arg, ErtsMonitor *mon)
{
struct print_to_data ptd = {to, arg};
erts_doforall_monitors(mon,&doit_print_monitor_info,&ptd);
@@ -2470,7 +2457,7 @@ static void doit_print_link_info(ErtsLink *lnk, void *vptdp)
}
}
-static void print_link_info(int to, void *arg, ErtsLink *lnk)
+static void print_link_info(fmtfn_t to, void *arg, ErtsLink *lnk)
{
struct print_to_data ptd = {to, arg};
erts_doforall_links(lnk, &doit_print_link_info, (void *) &ptd);
@@ -2491,7 +2478,7 @@ static void doit_print_nodelink_info(ErtsLink *lnk, void *vpcontext)
"Remote monitoring: %T %T\n", lnk->pid, pcontext->sysname);
}
-static void print_nodelink_info(int to, void *arg, ErtsLink *lnk, Eterm sysname)
+static void print_nodelink_info(fmtfn_t to, void *arg, ErtsLink *lnk, Eterm sysname)
{
PrintNodeLinkContext context = {{to, arg}, sysname};
erts_doforall_links(lnk, &doit_print_nodelink_info, &context);
@@ -2499,7 +2486,7 @@ static void print_nodelink_info(int to, void *arg, ErtsLink *lnk, Eterm sysname)
static int
-info_dist_entry(int to, void *arg, DistEntry *dep, int visible, int connected)
+info_dist_entry(fmtfn_t to, void *arg, DistEntry *dep, int visible, int connected)
{
if (visible && connected) {
@@ -2527,7 +2514,7 @@ info_dist_entry(int to, void *arg, DistEntry *dep, int visible, int connected)
erts_print(to, arg, "Name: %T", dep->sysname);
#ifdef DEBUG
- erts_print(to, arg, " (refc=%d)", erts_refc_read(&dep->refc, 0));
+ erts_print(to, arg, " (refc=%d)", erts_smp_refc_read(&dep->refc, 0));
#endif
erts_print(to, arg, "\n");
if (!connected && is_nil(dep->cid)) {
@@ -2548,7 +2535,7 @@ info_dist_entry(int to, void *arg, DistEntry *dep, int visible, int connected)
return 0;
}
-int distribution_info(int to, void *arg) /* Called by break handler */
+int distribution_info(fmtfn_t to, void *arg) /* Called by break handler */
{
DistEntry *dep;
@@ -3275,11 +3262,16 @@ send_nodes_mon_msg(Process *rp,
Uint sz)
{
Eterm msg;
- ErlHeapFragment* bp;
+ Eterm *hp;
+ ErtsMessage *mp;
ErlOffHeap *ohp;
- Eterm *hp = erts_alloc_message_heap(sz, &bp, &ohp, rp, rp_locksp);
#ifdef DEBUG
- Eterm *hend = hp + sz;
+ Eterm *hend;
+#endif
+
+ mp = erts_alloc_message_heap(rp, rp_locksp, sz, &hp, &ohp);
+#ifdef DEBUG
+ hend = hp + sz;
#endif
if (!nmp->opts) {
@@ -3325,7 +3317,7 @@ send_nodes_mon_msg(Process *rp,
}
ASSERT(hend == hp);
- erts_queue_message(rp, rp_locksp, bp, msg, NIL);
+ erts_queue_message(rp, *rp_locksp, mp, msg, am_system);
}
static void
diff --git a/erts/emulator/beam/dist.h b/erts/emulator/beam/dist.h
index fb777d9ac1..e82b416286 100644
--- a/erts/emulator/beam/dist.h
+++ b/erts/emulator/beam/dist.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2014. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -43,6 +43,7 @@
#define DFLAG_INTERNAL_TAGS 0x8000
#define DFLAG_UTF8_ATOMS 0x10000
#define DFLAG_MAP_TAG 0x20000
+#define DFLAG_BIG_CREATION 0x40000
/* All flags that should be enabled when term_to_binary/1 is used. */
#define TERM_TO_BINARY_DFLAGS (DFLAG_EXTENDED_REFERENCES \
@@ -51,7 +52,8 @@
| DFLAG_EXTENDED_PIDS_PORTS \
| DFLAG_EXPORT_PTR_TAG \
| DFLAG_BIT_BINARIES \
- | DFLAG_MAP_TAG)
+ | DFLAG_MAP_TAG \
+ | DFLAG_BIG_CREATION)
/* opcodes used in distribution messages */
#define DOP_LINK 1
diff --git a/erts/emulator/beam/dtrace-wrapper.h b/erts/emulator/beam/dtrace-wrapper.h
index d343dc5ab0..6f70d5961e 100644
--- a/erts/emulator/beam/dtrace-wrapper.h
+++ b/erts/emulator/beam/dtrace-wrapper.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Dustin Sallings, Michal Ptaszek, Scott Lystig Fritchie 2011-2012.
+ * Copyright Dustin Sallings, Michal Ptaszek, Scott Lystig Fritchie 2011-2016.
* All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/erts/emulator/beam/elib_memmove.c b/erts/emulator/beam/elib_memmove.c
index d4ca30158e..2f45f69026 100644
--- a/erts/emulator/beam/elib_memmove.c
+++ b/erts/emulator/beam/elib_memmove.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erl_afit_alloc.c b/erts/emulator/beam/erl_afit_alloc.c
index 4b0541c10e..4ebe37ee1d 100644
--- a/erts/emulator/beam/erl_afit_alloc.c
+++ b/erts/emulator/beam/erl_afit_alloc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2003-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -54,7 +54,7 @@ static void link_free_block (Allctr_t *, Block_t *);
static void unlink_free_block (Allctr_t *, Block_t *);
-static Eterm info_options (Allctr_t *, char *, int *,
+static Eterm info_options (Allctr_t *, char *, fmtfn_t *,
void *arg, Uint **, Uint *);
static void init_atoms (void);
@@ -227,7 +227,7 @@ add_2tup(Uint **hpp, Uint *szp, Eterm *lp, Eterm el1, Eterm el2)
static Eterm
info_options(Allctr_t *allctr,
char *prefix,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
diff --git a/erts/emulator/beam/erl_afit_alloc.h b/erts/emulator/beam/erl_afit_alloc.h
index ef050ff50e..74258e284a 100644
--- a/erts/emulator/beam/erl_afit_alloc.h
+++ b/erts/emulator/beam/erl_afit_alloc.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2003-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c
index 5f8cd2bbf7..214fb1f2af 100644
--- a/erts/emulator/beam/erl_alloc.c
+++ b/erts/emulator/beam/erl_alloc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2002-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@
#endif
#define ERTS_ALLOC_C__
#define ERTS_ALC_INTERNAL__
+#define ERTS_WANT_MEM_MAPPERS
#include "sys.h"
#define ERL_THREADS_EMU_INTERNAL__
#include "erl_threads.h"
@@ -103,9 +104,9 @@ static Uint install_debug_functions(void);
static int lock_all_physical_memory = 0;
-ErtsAllocatorFunctions_t erts_allctrs[ERTS_ALC_A_MAX+1];
+ErtsAllocatorFunctions_t ERTS_WRITE_UNLIKELY(erts_allctrs[ERTS_ALC_A_MAX+1]);
ErtsAllocatorInfo_t erts_allctrs_info[ERTS_ALC_A_MAX+1];
-ErtsAllocatorThrSpec_t erts_allctr_thr_spec[ERTS_ALC_A_MAX+1];
+ErtsAllocatorThrSpec_t ERTS_WRITE_UNLIKELY(erts_allctr_thr_spec[ERTS_ALC_A_MAX+1]);
#define ERTS_MIN(A, B) ((A) < (B) ? (A) : (B))
#define ERTS_MAX(A, B) ((A) > (B) ? (A) : (B))
@@ -123,10 +124,6 @@ typedef union {
static ErtsAllocatorState_t std_alloc_state;
static ErtsAllocatorState_t ll_alloc_state;
-#if HALFWORD_HEAP
-static ErtsAllocatorState_t std_low_alloc_state;
-static ErtsAllocatorState_t ll_low_alloc_state;
-#endif
static ErtsAllocatorState_t sl_alloc_state;
static ErtsAllocatorState_t temp_alloc_state;
static ErtsAllocatorState_t eheap_alloc_state;
@@ -134,8 +131,20 @@ static ErtsAllocatorState_t binary_alloc_state;
static ErtsAllocatorState_t ets_alloc_state;
static ErtsAllocatorState_t driver_alloc_state;
static ErtsAllocatorState_t fix_alloc_state;
+static ErtsAllocatorState_t literal_alloc_state;
+#ifdef ERTS_ALC_A_EXEC
+static ErtsAllocatorState_t exec_alloc_state;
+#endif
static ErtsAllocatorState_t test_alloc_state;
+enum {
+ ERTS_ALC_INFO_A_ALLOC_UTIL = ERTS_ALC_A_MAX + 1,
+ ERTS_ALC_INFO_A_MSEG_ALLOC,
+ ERTS_ALC_INFO_A_ERTS_MMAP,
+ ERTS_ALC_INFO_A_DISABLED_EXEC, /* fake a disabled "exec_alloc" */
+ ERTS_ALC_INFO_A_END
+};
+
typedef struct {
erts_smp_atomic32_t refc;
int only_sz;
@@ -144,31 +153,13 @@ typedef struct {
Process *proc;
Eterm ref;
Eterm ref_heap[REF_THING_SIZE];
- int allocs[ERTS_ALC_A_MAX-ERTS_ALC_A_MIN+1+2];
+ int allocs[ERTS_ALC_INFO_A_END - ERTS_ALC_A_MIN + 1];
} ErtsAllocInfoReq;
-#define ERTS_ALC_INFO_A_ALLOC_UTIL (ERTS_ALC_A_MAX + 1)
-#define ERTS_ALC_INFO_A_MSEG_ALLOC (ERTS_ALC_A_MAX + 2)
-#define ERTS_ALC_INFO_A_MAX ERTS_ALC_INFO_A_MSEG_ALLOC
-
-#if !HALFWORD_HEAP
ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(aireq,
- ErtsAllocInfoReq,
- 5,
- ERTS_ALC_T_AINFO_REQ)
-#else
-static ERTS_INLINE ErtsAllocInfoReq *
-aireq_alloc(void)
-{
- return erts_alloc(ERTS_ALC_T_AINFO_REQ, sizeof(ErtsAllocInfoReq));
-}
-
-static ERTS_INLINE void
-aireq_free(ErtsAllocInfoReq *ptr)
-{
- erts_free(ERTS_ALC_T_AINFO_REQ, ptr);
-}
-#endif
+ ErtsAllocInfoReq,
+ 5,
+ ERTS_ALC_T_AINFO_REQ)
ErtsAlcType_t erts_fix_core_allocator_ix;
@@ -182,6 +173,8 @@ enum allctr_type {
struct au_init {
int enable;
int thr_spec;
+ int disable_allowed;
+ int thr_spec_allowed;
int carrier_migration_allowed;
enum allctr_type atype;
struct {
@@ -230,14 +223,12 @@ typedef struct {
struct au_init ets_alloc;
struct au_init driver_alloc;
struct au_init fix_alloc;
-#if HALFWORD_HEAP
- struct au_init std_low_alloc;
- struct au_init ll_low_alloc;
-#endif
+ struct au_init literal_alloc;
+ struct au_init exec_alloc;
struct au_init test_alloc;
} erts_alc_hndl_args_init_t;
-#define ERTS_AU_INIT__ {0, 0, 1, GOODFIT, DEFAULT_ALLCTR_INIT, {1,1,1,1}}
+#define ERTS_AU_INIT__ {0, 0, 1, 1, 1, GOODFIT, DEFAULT_ALLCTR_INIT, {1,1,1,1}}
#define SET_DEFAULT_ALLOC_OPTS(IP) \
do { \
@@ -261,10 +252,6 @@ set_default_sl_alloc_opts(struct au_init *ip)
#endif
ip->init.util.ts = ERTS_ALC_MTA_SHORT_LIVED;
ip->init.util.rsbcst = 80;
-#if HALFWORD_HEAP
- ip->init.util.force = 1;
- ip->init.util.low_mem = 1;
-#endif
ip->init.util.acul = ERTS_ALC_DEFAULT_ACUL;
}
@@ -300,9 +287,9 @@ set_default_ll_alloc_opts(struct au_init *ip)
ip->init.util.name_prefix = "ll_";
ip->init.util.alloc_no = ERTS_ALC_A_LONG_LIVED;
#ifndef SMALL_MEMORY
- ip->init.util.mmbcs = 2*1024*1024 - 40; /* Main carrier size */
+ ip->init.util.mmbcs = 2*1024*1024; /* Main carrier size */
#else
- ip->init.util.mmbcs = 1*1024*1024 - 40; /* Main carrier size */
+ ip->init.util.mmbcs = 1*1024*1024; /* Main carrier size */
#endif
ip->init.util.ts = ERTS_ALC_MTA_LONG_LIVED;
ip->init.util.asbcst = 0;
@@ -313,6 +300,87 @@ set_default_ll_alloc_opts(struct au_init *ip)
}
static void
+set_default_literal_alloc_opts(struct au_init *ip)
+{
+ SET_DEFAULT_ALLOC_OPTS(ip);
+ ip->enable = 1;
+ ip->thr_spec = 0;
+ ip->disable_allowed = 0;
+ ip->thr_spec_allowed = 0;
+ ip->carrier_migration_allowed = 0;
+ ip->atype = BESTFIT;
+ ip->init.bf.ao = 1;
+ ip->init.util.ramv = 0;
+ ip->init.util.mmsbc = 0;
+ ip->init.util.sbct = ~((UWord) 0);
+ ip->init.util.name_prefix = "literal_";
+ ip->init.util.alloc_no = ERTS_ALC_A_LITERAL;
+#ifndef SMALL_MEMORY
+ ip->init.util.mmbcs = 1024*1024; /* Main carrier size */
+#else
+ ip->init.util.mmbcs = 256*1024; /* Main carrier size */
+#endif
+ ip->init.util.ts = ERTS_ALC_MTA_LITERAL;
+ ip->init.util.asbcst = 0;
+ ip->init.util.rsbcst = 0;
+ ip->init.util.rsbcmt = 0;
+ ip->init.util.rmbcmt = 0;
+ ip->init.util.acul = 0;
+
+#if defined(ARCH_32)
+# if HAVE_ERTS_MSEG
+ ip->init.util.mseg_alloc = &erts_alcu_literal_32_mseg_alloc;
+ ip->init.util.mseg_realloc = &erts_alcu_literal_32_mseg_realloc;
+ ip->init.util.mseg_dealloc = &erts_alcu_literal_32_mseg_dealloc;
+# endif
+ ip->init.util.sys_alloc = &erts_alcu_literal_32_sys_alloc;
+ ip->init.util.sys_realloc = &erts_alcu_literal_32_sys_realloc;
+ ip->init.util.sys_dealloc = &erts_alcu_literal_32_sys_dealloc;
+#elif defined(ARCH_64)
+# ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION
+ ip->init.util.mseg_alloc = &erts_alcu_mmapper_mseg_alloc;
+ ip->init.util.mseg_realloc = &erts_alcu_mmapper_mseg_realloc;
+ ip->init.util.mseg_dealloc = &erts_alcu_mmapper_mseg_dealloc;
+ ip->init.util.mseg_mmapper = &erts_literal_mmapper;
+# endif
+#else
+# error Unknown architecture
+#endif
+}
+
+#ifdef ERTS_ALC_A_EXEC
+static void
+set_default_exec_alloc_opts(struct au_init *ip)
+{
+ SET_DEFAULT_ALLOC_OPTS(ip);
+ ip->enable = 1;
+ ip->thr_spec = 0;
+ ip->disable_allowed = 0;
+ ip->thr_spec_allowed = 0;
+ ip->carrier_migration_allowed = 0;
+ ip->atype = BESTFIT;
+ ip->init.bf.ao = 1;
+ ip->init.util.ramv = 0;
+ ip->init.util.mmsbc = 0;
+ ip->init.util.sbct = ~((UWord) 0);
+ ip->init.util.name_prefix = "exec_";
+ ip->init.util.alloc_no = ERTS_ALC_A_EXEC;
+ ip->init.util.mmbcs = 0; /* No main carrier */
+ ip->init.util.ts = ERTS_ALC_MTA_EXEC;
+ ip->init.util.asbcst = 0;
+ ip->init.util.rsbcst = 0;
+ ip->init.util.rsbcmt = 0;
+ ip->init.util.rmbcmt = 0;
+ ip->init.util.acul = 0;
+
+ ip->init.util.mseg_alloc = &erts_alcu_mmapper_mseg_alloc;
+ ip->init.util.mseg_realloc = &erts_alcu_mmapper_mseg_realloc;
+ ip->init.util.mseg_dealloc = &erts_alcu_mmapper_mseg_dealloc;
+ ip->init.util.mseg_mmapper = &erts_exec_mmapper;
+}
+#endif /* ERTS_ALC_A_EXEC */
+
+static void
set_default_temp_alloc_opts(struct au_init *ip)
{
SET_DEFAULT_ALLOC_OPTS(ip);
@@ -330,10 +398,6 @@ set_default_temp_alloc_opts(struct au_init *ip)
ip->init.util.ts = ERTS_ALC_MTA_TEMPORARY;
ip->init.util.rsbcst = 90;
ip->init.util.rmbcmt = 100;
-#if HALFWORD_HEAP
- ip->init.util.force = 1;
- ip->init.util.low_mem = 1;
-#endif
}
static void
@@ -352,10 +416,6 @@ set_default_eheap_alloc_opts(struct au_init *ip)
#endif
ip->init.util.ts = ERTS_ALC_MTA_EHEAP;
ip->init.util.rsbcst = 50;
-#if HALFWORD_HEAP
- ip->init.util.force = 1;
- ip->init.util.low_mem = 1;
-#endif
ip->init.util.acul = ERTS_ALC_DEFAULT_ACUL_EHEAP_ALLOC;
}
@@ -589,18 +649,16 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop)
fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_PROC)]
= sizeof(Process);
-#if !HALFWORD_HEAP
fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_MONITOR_SH)]
= ERTS_MONITOR_SH_SIZE * sizeof(Uint);
fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_NLINK_SH)]
= ERTS_LINK_SH_SIZE * sizeof(Uint);
-#endif
fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_DRV_EV_D_STATE)]
= sizeof(ErtsDrvEventDataState);
fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_DRV_SEL_D_STATE)]
= sizeof(ErtsDrvSelectDataState);
fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_MSG_REF)]
- = sizeof(ErlMessage);
+ = sizeof(ErtsMessageRef);
#ifdef ERTS_SMP
fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_THR_Q_EL_SL)]
= sizeof(ErtsThrQElement_t);
@@ -642,6 +700,10 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop)
set_default_driver_alloc_opts(&init.driver_alloc);
set_default_fix_alloc_opts(&init.fix_alloc,
fix_type_sizes);
+ set_default_literal_alloc_opts(&init.literal_alloc);
+#ifdef ERTS_ALC_A_EXEC
+ set_default_exec_alloc_opts(&init.exec_alloc);
+#endif
set_default_test_alloc_opts(&init.test_alloc);
if (argc && argv)
@@ -670,6 +732,10 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop)
init.ets_alloc.thr_spec = 0;
init.driver_alloc.thr_spec = 0;
init.fix_alloc.thr_spec = 0;
+ init.literal_alloc.thr_spec = 0;
+#ifdef ERTS_ALC_A_EXEC
+ init.exec_alloc.thr_spec = 0;
+#endif
#endif
/* Make adjustments for carrier migration support */
@@ -682,6 +748,10 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop)
adjust_carrier_migration_support(&init.ets_alloc);
adjust_carrier_migration_support(&init.driver_alloc);
adjust_carrier_migration_support(&init.fix_alloc);
+ adjust_carrier_migration_support(&init.literal_alloc);
+#ifdef ERTS_ALC_A_EXEC
+ adjust_carrier_migration_support(&init.exec_alloc);
+#endif
if (init.erts_alloc_config) {
/* Adjust flags that erts_alloc_config won't like */
@@ -696,6 +766,10 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop)
init.ets_alloc.thr_spec = 0;
init.driver_alloc.thr_spec = 0;
init.fix_alloc.thr_spec = 0;
+ init.literal_alloc.thr_spec = 0;
+#ifdef ERTS_ALC_A_EXEC
+ init.exec_alloc.thr_spec = 0;
+#endif
/* No carrier migration */
init.temp_alloc.init.util.acul = 0;
@@ -707,6 +781,10 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop)
init.ets_alloc.init.util.acul = 0;
init.driver_alloc.init.util.acul = 0;
init.fix_alloc.init.util.acul = 0;
+ init.literal_alloc.init.util.acul = 0;
+#ifdef ERTS_ALC_A_EXEC
+ init.exec_alloc.init.util.acul = 0;
+#endif
}
#ifdef ERTS_SMP
@@ -723,6 +801,10 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop)
adjust_tpref(&init.ets_alloc, erts_no_schedulers);
adjust_tpref(&init.driver_alloc, erts_no_schedulers);
adjust_tpref(&init.fix_alloc, erts_no_schedulers);
+ adjust_tpref(&init.literal_alloc, erts_no_schedulers);
+#ifdef ERTS_ALC_A_EXEC
+ adjust_tpref(&init.exec_alloc, erts_no_schedulers);
+#endif
#else
/* No thread specific if not smp */
@@ -741,6 +823,10 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop)
refuse_af_strategy(&init.ets_alloc);
refuse_af_strategy(&init.driver_alloc);
refuse_af_strategy(&init.fix_alloc);
+ refuse_af_strategy(&init.literal_alloc);
+#ifdef ERTS_ALC_A_EXEC
+ refuse_af_strategy(&init.exec_alloc);
+#endif
#ifdef ERTS_SMP
if (!init.temp_alloc.thr_spec)
@@ -775,24 +861,6 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop)
erts_allctrs[ERTS_ALC_A_SYSTEM].free = erts_sys_free;
erts_allctrs_info[ERTS_ALC_A_SYSTEM].enabled = 1;
-#if HALFWORD_HEAP
- /* Init low memory variants by cloning */
- init.std_low_alloc = init.std_alloc;
- init.std_low_alloc.init.util.name_prefix = "std_low_";
- init.std_low_alloc.init.util.alloc_no = ERTS_ALC_A_STANDARD_LOW;
- init.std_low_alloc.init.util.force = 1;
- init.std_low_alloc.init.util.low_mem = 1;
-
- init.ll_low_alloc = init.ll_alloc;
- init.ll_low_alloc.init.util.name_prefix = "ll_low_";
- init.ll_low_alloc.init.util.alloc_no = ERTS_ALC_A_LONG_LIVED_LOW;
- init.ll_low_alloc.init.util.force = 1;
- init.ll_low_alloc.init.util.low_mem = 1;
-
- set_au_allocator(ERTS_ALC_A_STANDARD_LOW, &init.std_low_alloc, ncpu);
- set_au_allocator(ERTS_ALC_A_LONG_LIVED_LOW, &init.ll_low_alloc, ncpu);
-#endif /* HALFWORD */
-
set_au_allocator(ERTS_ALC_A_TEMPORARY, &init.temp_alloc, ncpu);
set_au_allocator(ERTS_ALC_A_SHORT_LIVED, &init.sl_alloc, ncpu);
set_au_allocator(ERTS_ALC_A_STANDARD, &init.std_alloc, ncpu);
@@ -802,6 +870,10 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop)
set_au_allocator(ERTS_ALC_A_ETS, &init.ets_alloc, ncpu);
set_au_allocator(ERTS_ALC_A_DRIVER, &init.driver_alloc, ncpu);
set_au_allocator(ERTS_ALC_A_FIXED_SIZE, &init.fix_alloc, ncpu);
+ set_au_allocator(ERTS_ALC_A_LITERAL, &init.literal_alloc, ncpu);
+#ifdef ERTS_ALC_A_EXEC
+ set_au_allocator(ERTS_ALC_A_EXEC, &init.exec_alloc, ncpu);
+#endif
set_au_allocator(ERTS_ALC_A_TEST, &init.test_alloc, ncpu);
for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) {
@@ -836,14 +908,6 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop)
start_au_allocator(ERTS_ALC_A_LONG_LIVED,
&init.ll_alloc,
&ll_alloc_state);
-#if HALFWORD_HEAP
- start_au_allocator(ERTS_ALC_A_LONG_LIVED_LOW,
- &init.ll_low_alloc,
- &ll_low_alloc_state);
- start_au_allocator(ERTS_ALC_A_STANDARD_LOW,
- &init.std_low_alloc,
- &std_low_alloc_state);
-#endif
start_au_allocator(ERTS_ALC_A_EHEAP,
&init.eheap_alloc,
&eheap_alloc_state);
@@ -863,7 +927,14 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop)
start_au_allocator(ERTS_ALC_A_FIXED_SIZE,
&init.fix_alloc,
&fix_alloc_state);
-
+ start_au_allocator(ERTS_ALC_A_LITERAL,
+ &init.literal_alloc,
+ &literal_alloc_state);
+#ifdef ERTS_ALC_A_EXEC
+ start_au_allocator(ERTS_ALC_A_EXEC,
+ &init.exec_alloc,
+ &exec_alloc_state);
+#endif
start_au_allocator(ERTS_ALC_A_TEST,
&init.test_alloc,
&test_alloc_state);
@@ -871,9 +942,7 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop)
erts_mtrace_install_wrapper_functions();
extra_block_size += erts_instr_init(init.instr.stat, init.instr.map);
-#if !HALFWORD_HEAP
init_aireq_alloc();
-#endif
#ifdef DEBUG
extra_block_size += install_debug_functions();
@@ -971,6 +1040,10 @@ set_au_allocator(ErtsAlcType_t alctr_n, struct au_init *init, int ncpu)
else
#endif
{
+#ifdef ERTS_SMP
+ erts_exit(ERTS_ABORT_EXIT, "%salloc is not thread safe\n",
+ init->init.util.name_prefix);
+#else
af->alloc = erts_alcu_alloc;
if (init->init.util.fix_type_size)
af->realloc = erts_realloc_fixed_size;
@@ -979,6 +1052,7 @@ set_au_allocator(ErtsAlcType_t alctr_n, struct au_init *init, int ncpu)
else
af->realloc = erts_alcu_realloc;
af->free = erts_alcu_free;
+#endif
}
af->extra = NULL;
ai->alloc_util = 1;
@@ -1051,7 +1125,7 @@ start_au_allocator(ErtsAlcType_t alctr_n,
}
for (i = 0; i < size; i++) {
- void *as;
+ Allctr_t *as;
atype = init->atype;
if (!init->thr_spec)
@@ -1088,22 +1162,22 @@ start_au_allocator(ErtsAlcType_t alctr_n,
switch (atype) {
case GOODFIT:
- as = (void *) erts_gfalc_start((GFAllctr_t *) as0,
+ as = erts_gfalc_start((GFAllctr_t *) as0,
&init->init.gf,
&init->init.util);
break;
case BESTFIT:
- as = (void *) erts_bfalc_start((BFAllctr_t *) as0,
+ as = erts_bfalc_start((BFAllctr_t *) as0,
&init->init.bf,
&init->init.util);
break;
case AFIT:
- as = (void *) erts_afalc_start((AFAllctr_t *) as0,
+ as = erts_afalc_start((AFAllctr_t *) as0,
&init->init.af,
&init->init.util);
break;
case AOFIRSTFIT:
- as = (void *) erts_aoffalc_start((AOFFAllctr_t *) as0,
+ as = erts_aoffalc_start((AOFFAllctr_t *) as0,
&init->init.aoff,
&init->init.util);
break;
@@ -1273,9 +1347,6 @@ get_acul_value(struct au_init *auip, char *param_end, char** argv, int* ip)
if (sys_strcmp(value, "de") == 0) {
switch (auip->init.util.alloc_no) {
case ERTS_ALC_A_LONG_LIVED:
-#if HALFWORD_HEAP
- case ERTS_ALC_A_LONG_LIVED_LOW:
-#endif
return ERTS_ALC_DEFAULT_ENABLED_ACUL_LL_ALLOC;
case ERTS_ALC_A_EHEAP:
return ERTS_ALC_DEFAULT_ENABLED_ACUL_EHEAP_ALLOC;
@@ -1353,9 +1424,17 @@ handle_au_arg(struct au_init *auip,
else
goto bad_switch;
break;
- case 'e':
- auip->enable = get_bool_value(sub_param+1, argv, ip);
+ case 'e': {
+ int e = get_bool_value(sub_param + 1, argv, ip);
+ if (!auip->disable_allowed && !e) {
+ if (!u_switch)
+ bad_value(param, sub_param + 1, "false");
+ else
+ ASSERT(auip->enable); /* ignore */
+ }
+ else auip->enable = e;
break;
+ }
case 'l':
if (has_prefix("lmbcs", sub_param)) {
auip->default_.lmbcs = 0;
@@ -1424,7 +1503,14 @@ handle_au_arg(struct au_init *auip,
case 't': {
int res = get_bool_value(sub_param+1, argv, ip);
if (res > 0) {
- auip->thr_spec = 1;
+ if (!auip->thr_spec_allowed) {
+ if (!u_switch)
+ bad_value(param, sub_param + 1, "true");
+ else
+ ASSERT(!auip->thr_spec); /* ignore */
+ }
+ else
+ auip->thr_spec = 1;
break;
}
else if (res == 0) {
@@ -1473,6 +1559,26 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init)
case 'B':
handle_au_arg(&init->binary_alloc, &argv[i][3], argv, &i, 0);
break;
+ case 'I':
+ if (has_prefix("scs", argv[i]+3)) {
+#if HAVE_ERTS_MSEG
+ init->mseg.literal_mmap.scs =
+#endif
+ get_mb_value(argv[i]+6, argv, &i);
+ }
+ else
+ handle_au_arg(&init->literal_alloc, &argv[i][3], argv, &i, 0);
+ break;
+ case 'X':
+ if (has_prefix("scs", argv[i]+3)) {
+#ifdef ERTS_ALC_A_EXEC
+ init->mseg.exec_mmap.scs =
+#endif
+ get_mb_value(argv[i]+6, argv, &i);
+ }
+ else
+ handle_au_arg(&init->exec_alloc, &argv[i][3], argv, &i, 0);
+ break;
case 'D':
handle_au_arg(&init->std_alloc, &argv[i][3], argv, &i, 0);
break;
@@ -1509,25 +1615,25 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init)
}
else if (has_prefix("scs", argv[i]+3)) {
#if HAVE_ERTS_MSEG
- init->mseg.mmap.scs =
+ init->mseg.dflt_mmap.scs =
#endif
get_mb_value(argv[i]+6, argv, &i);
}
else if (has_prefix("sco", argv[i]+3)) {
#if HAVE_ERTS_MSEG
- init->mseg.mmap.sco =
+ init->mseg.dflt_mmap.sco =
#endif
get_bool_value(argv[i]+6, argv, &i);
}
else if (has_prefix("scrpm", argv[i]+3)) {
#if HAVE_ERTS_MSEG
- init->mseg.mmap.scrpm =
+ init->mseg.dflt_mmap.scrpm =
#endif
get_bool_value(argv[i]+8, argv, &i);
}
else if (has_prefix("scrfsd", argv[i]+3)) {
#if HAVE_ERTS_MSEG
- init->mseg.mmap.scrfsd =
+ init->mseg.dflt_mmap.scrfsd =
#endif
get_amount_value(argv[i]+9, argv, &i);
}
@@ -1996,48 +2102,6 @@ alcu_size(ErtsAlcType_t ai, ErtsAlcUFixInfo_t *fi, int fisz)
return res;
}
-#if HALFWORD_HEAP
-static ERTS_INLINE int
-alcu_is_low(ErtsAlcType_t ai)
-{
- int is_low = 0;
- ASSERT(erts_allctrs_info[ai].enabled);
- ASSERT(erts_allctrs_info[ai].alloc_util);
-
- if (!erts_allctrs_info[ai].thr_spec) {
- Allctr_t *allctr = erts_allctrs_info[ai].extra;
- is_low = allctr->mseg_opt.low_mem;
- }
- else {
- ErtsAllocatorThrSpec_t *tspec = &erts_allctr_thr_spec[ai];
- int i;
-# ifdef DEBUG
- int found_one = 0;
-# endif
-
- ASSERT(tspec->enabled);
-
- for (i = tspec->size - 1; i >= 0; i--) {
- Allctr_t *allctr = tspec->allctr[i];
- if (allctr) {
-# ifdef DEBUG
- if (!found_one) {
- is_low = allctr->mseg_opt.low_mem;
- found_one = 1;
- }
- else ASSERT(is_low == allctr->mseg_opt.low_mem);
-# else
- is_low = allctr->mseg_opt.low_mem;
- break;
-# endif
- }
- }
- ASSERT(found_one);
- }
- return is_low;
-}
-#endif /* HALFWORD */
-
static ERTS_INLINE void
add_fix_values(UWord *ap, UWord *up, ErtsAlcUFixInfo_t *fi, ErtsAlcType_t type)
{
@@ -2049,7 +2113,7 @@ add_fix_values(UWord *ap, UWord *up, ErtsAlcUFixInfo_t *fi, ErtsAlcType_t type)
}
Eterm
-erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg)
+erts_memory(fmtfn_t *print_to_p, void *print_to_arg, void *proc, Eterm earg)
{
/*
* NOTE! When updating this function, make sure to also update
@@ -2067,9 +2131,6 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg)
int code;
int ets;
int maximum;
-#if HALFWORD_HEAP
- int low;
-#endif
} want = {0};
struct {
UWord total;
@@ -2082,9 +2143,6 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg)
UWord code;
UWord ets;
UWord maximum;
-#if HALFWORD_HEAP
- UWord low;
-#endif
} size = {0};
Eterm atoms[sizeof(size)/sizeof(UWord)];
UWord *uintps[sizeof(size)/sizeof(UWord)];
@@ -2143,11 +2201,6 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg)
atoms[length] = am_maximum;
uintps[length++] = &size.maximum;
}
-#if HALFWORD_HEAP
- want.low = 1;
- atoms[length] = am_low;
- uintps[length++] = &size.low;
-#endif
}
else {
DeclareTmpHeapNoproc(tmp_heap,2);
@@ -2241,15 +2294,6 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg)
return am_badarg;
}
break;
-#if HALFWORD_HEAP
- case am_low:
- if (!want.low) {
- want.low = 1;
- atoms[length] = am_low;
- uintps[length++] = &size.low;
- }
- break;
-#endif
default:
UnUseTmpHeapNoproc(2);
return am_badarg;
@@ -2330,11 +2374,6 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg)
if (save)
*save = asz;
size.total += asz;
-#if HALFWORD_HEAP
- if (alcu_is_low(ai)) {
- size.low += asz;
- }
-#endif
}
}
}
@@ -2361,7 +2400,6 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg)
&size.processes_used,
fi,
ERTS_ALC_T_PROC);
-#if !HALFWORD_HEAP
add_fix_values(&size.processes,
&size.processes_used,
fi,
@@ -2371,7 +2409,6 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg)
&size.processes_used,
fi,
ERTS_ALC_T_NLINK_SH);
-#endif
add_fix_values(&size.processes,
&size.processes_used,
fi,
@@ -2439,7 +2476,7 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg)
if (print_to_p) {
int i;
- int to = *print_to_p;
+ fmtfn_t to = *print_to_p;
void *arg = print_to_arg;
/* Print result... */
@@ -2493,7 +2530,7 @@ struct aa_values {
};
Eterm
-erts_allocated_areas(int *print_to_p, void *print_to_arg, void *proc)
+erts_allocated_areas(fmtfn_t *print_to_p, void *print_to_arg, void *proc)
{
#define MAX_AA_VALUES (24)
struct aa_values values[MAX_AA_VALUES];
@@ -2628,7 +2665,7 @@ erts_allocated_areas(int *print_to_p, void *print_to_arg, void *proc)
if (print_to_p) {
/* Print result... */
- int to = *print_to_p;
+ fmtfn_t to = *print_to_p;
void *arg = print_to_arg;
erts_print(to, arg, "=allocated_areas\n");
@@ -2742,7 +2779,7 @@ erts_alloc_util_allocators(void *proc)
}
void
-erts_allocator_info(int to, void *arg)
+erts_allocator_info(fmtfn_t to, void *arg)
{
ErtsAlcType_t a;
@@ -2807,10 +2844,18 @@ erts_allocator_info(int to, void *arg)
int i;
for (i = 0; i <= max; i++) {
erts_print(to, arg, "=allocator:mseg_alloc[%d]\n", i);
- erts_mseg_info(i, &to, arg, 0, NULL, NULL);
+ erts_mseg_info(i, &to, arg, 0, 0, NULL, NULL);
}
- erts_print(to, arg, "=allocator:mseg_alloc.erts_mmap\n");
- erts_mmap_info(&to, arg, NULL, NULL, &emis);
+ erts_print(to, arg, "=allocator:erts_mmap.default_mmap\n");
+ erts_mmap_info(&erts_dflt_mmapper, &to, arg, NULL, NULL, &emis);
+#if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
+ erts_print(to, arg, "=allocator:erts_mmap.literal_mmap\n");
+ erts_mmap_info(&erts_literal_mmapper, &to, arg, NULL, NULL, &emis);
+#endif
+#ifdef ERTS_ALC_A_EXEC
+ erts_print(to, arg, "=allocator:erts_mmap.exec_mmap\n");
+ erts_mmap_info(&erts_exec_mmapper, &to, arg, NULL, NULL, &emis);
+#endif
}
#endif
@@ -2921,6 +2966,11 @@ erts_allocator_options(void *proc)
atoms[length] = am_atom_put("alloc_util", 10);
terms[length++] = erts_alcu_au_info_options(NULL, NULL, hpp, szp);
+#if HAVE_ERTS_MMAP
+ atoms[length] = ERTS_MAKE_AM("erts_mmap");
+ terms[length++] = erts_mmap_info_options(&erts_dflt_mmapper, NULL, NULL,
+ NULL, hpp, szp);
+#endif
{
Eterm o[3], v[3];
o[0] = am_atom_put("m", 1);
@@ -2957,7 +3007,12 @@ erts_allocator_options(void *proc)
#if ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC
terms[length++] = am_atom_put("sys_aligned_alloc", 17);
#endif
-
+#if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
+ terms[length++] = ERTS_MAKE_AM("literal_mmap");
+#endif
+#ifdef ERTS_ALC_A_EXEC
+ terms[length++] = ERTS_MAKE_AM("exec_mmap");
+#endif
features = length ? erts_bld_list(hpp, szp, length, terms) : NIL;
#if defined(__GLIBC__)
@@ -3036,18 +3091,26 @@ reply_alloc_info(void *vair)
int global_instances = air->req_sched == sched_id;
ErtsProcLocks rp_locks;
Process *rp = air->proc;
- Eterm ref_copy = NIL, ai_list, msg;
- Eterm *hp = NULL, *hp_end = NULL, *hp_start = NULL;
+ Eterm ref_copy = NIL, ai_list, msg = NIL;
+ Eterm *hp = NULL, *hp_start = NULL, *hp_end = NULL;
Eterm **hpp;
Uint sz, *szp;
ErlOffHeap *ohp = NULL;
- ErlHeapFragment *bp = NULL;
- struct erts_mmap_info_struct emis;
+ ErtsMessage *mp = NULL;
+#if HAVE_ERTS_MMAP
+ struct erts_mmap_info_struct mmap_info_dflt;
+# if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
+ struct erts_mmap_info_struct mmap_info_literal;
+# endif
+# ifdef ERTS_ALC_A_EXEC
+ struct erts_mmap_info_struct mmap_info_exec;
+# endif
+#endif
int i;
Eterm (*info_func)(Allctr_t *,
int,
int,
- int *,
+ fmtfn_t *,
void *,
Uint **,
Uint *) = (air->only_sz
@@ -3148,30 +3211,64 @@ reply_alloc_info(void *vair)
make_small(0),
ainfo);
break;
+ case ERTS_ALC_INFO_A_ERTS_MMAP:
+ alloc_atom = erts_bld_atom(hpp, szp, "erts_mmap");
+#if HAVE_ERTS_MMAP
+ ainfo = (air->only_sz ? NIL :
+ erts_mmap_info(&erts_dflt_mmapper, NULL, NULL,
+ hpp, szp, &mmap_info_dflt));
+ ainfo = erts_bld_tuple3(hpp, szp,
+ alloc_atom,
+ erts_bld_atom(hpp,szp,"default_mmap"),
+ ainfo);
+# if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
+ ai_list = erts_bld_cons(hpp, szp,
+ ainfo, ai_list);
+ ainfo = (air->only_sz ? NIL :
+ erts_mmap_info(&erts_literal_mmapper, NULL, NULL,
+ hpp, szp, &mmap_info_literal));
+ ainfo = erts_bld_tuple3(hpp, szp,
+ alloc_atom,
+ erts_bld_atom(hpp,szp,"literal_mmap"),
+ ainfo);
+# endif
+# ifdef ERTS_ALC_A_EXEC
+ ai_list = erts_bld_cons(hpp, szp,
+ ainfo, ai_list);
+ ainfo = (air->only_sz ? NIL :
+ erts_mmap_info(&erts_exec_mmapper, NULL, NULL,
+ hpp, szp, &mmap_info_exec));
+ ainfo = erts_bld_tuple3(hpp, szp,
+ alloc_atom,
+ erts_bld_atom(hpp,szp,"exec_mmap"),
+ ainfo);
+# endif
+#else /* !HAVE_ERTS_MMAP */
+ ainfo = erts_bld_tuple2(hpp, szp, alloc_atom,
+ am_false);
+#endif
+ break;
case ERTS_ALC_INFO_A_MSEG_ALLOC:
alloc_atom = erts_bld_atom(hpp, szp, "mseg_alloc");
#if HAVE_ERTS_MSEG
- ainfo = (air->only_sz
- ? NIL
- : erts_mseg_info(0, NULL, NULL, hpp != NULL,
- hpp, szp));
+ ainfo = erts_mseg_info(0, NULL, NULL, hpp != NULL,
+ air->only_sz, hpp, szp);
ainfo = erts_bld_tuple3(hpp, szp,
alloc_atom,
make_small(0),
ainfo);
- ai_list = erts_bld_cons(hpp, szp,
- ainfo, ai_list);
- ainfo = (air->only_sz ? NIL : erts_mmap_info(NULL, NULL, hpp, szp, &emis));
- ainfo = erts_bld_tuple3(hpp, szp,
- alloc_atom,
- erts_bld_atom(hpp,szp,"erts_mmap"),
- ainfo);
#else
ainfo = erts_bld_tuple2(hpp, szp, alloc_atom,
am_false);
#endif
break;
+#ifndef ERTS_ALC_A_EXEC
+ case ERTS_ALC_INFO_A_DISABLED_EXEC:
+ alloc_atom = erts_bld_atom(hpp, szp, "exec_alloc");
+ ainfo = erts_bld_tuple2(hpp, szp, alloc_atom, am_false);
+ break;
+#endif
default:
alloc_atom = erts_bld_atom(hpp, szp,
(char *) ERTS_ALC_A2AD(ai));
@@ -3198,15 +3295,15 @@ reply_alloc_info(void *vair)
}
switch (ai) {
case ERTS_ALC_A_SYSTEM:
- case ERTS_ALC_INFO_A_ALLOC_UTIL:
+ case ERTS_ALC_INFO_A_ALLOC_UTIL:
+ case ERTS_ALC_INFO_A_ERTS_MMAP:
+ case ERTS_ALC_INFO_A_DISABLED_EXEC:
break;
case ERTS_ALC_INFO_A_MSEG_ALLOC:
#if HAVE_ERTS_MSEG && defined(ERTS_SMP)
alloc_atom = erts_bld_atom(hpp, szp, "mseg_alloc");
- ainfo = (air->only_sz
- ? NIL
- : erts_mseg_info(sched_id, NULL, NULL,
- hpp != NULL, hpp, szp));
+ ainfo = erts_mseg_info(sched_id, NULL, NULL,
+ hpp != NULL, air->only_sz, hpp, szp);
ainfo = erts_bld_tuple(hpp, szp, 3,
alloc_atom,
make_small(sched_id),
@@ -3242,20 +3339,17 @@ reply_alloc_info(void *vair)
if (hpp)
break;
- hp = erts_alloc_message_heap(sz, &bp, &ohp, rp, &rp_locks);
+ mp = erts_alloc_message_heap(rp, &rp_locks, sz, &hp, &ohp);
hp_start = hp;
hp_end = hp + sz;
szp = NULL;
hpp = &hp;
}
- if (bp)
- bp = erts_resize_message_buffer(bp, hp - hp_start, &msg, 1);
- else {
- ASSERT(hp);
- HRelease(rp, hp_end, hp);
- }
- erts_queue_message(rp, &rp_locks, bp, msg, NIL);
+ if (hp != hp_end)
+ erts_shrink_message_heap(&mp, rp, hp_start, hp, hp_end, &msg, 1);
+
+ erts_queue_message(rp, rp_locks, mp, msg, am_system);
if (air->req_sched == sched_id)
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
@@ -3275,7 +3369,7 @@ erts_request_alloc_info(struct process *c_p,
int internal)
{
ErtsAllocInfoReq *air = aireq_alloc();
- Eterm req_ai[ERTS_ALC_A_MAX+1+2] = {0};
+ Eterm req_ai[ERTS_ALC_INFO_A_END] = {0};
Eterm alist;
Eterm *hp;
int airix = 0, ai;
@@ -3311,6 +3405,16 @@ erts_request_alloc_info(struct process *c_p,
ai = ERTS_ALC_INFO_A_MSEG_ALLOC;
goto save_alloc;
}
+ if (erts_is_atom_str("erts_mmap", alloc, 0)) {
+ ai = ERTS_ALC_INFO_A_ERTS_MMAP;
+ goto save_alloc;
+ }
+#ifndef ERTS_ALC_A_EXEC
+ if (erts_is_atom_str("exec_alloc", alloc, 0)) {
+ ai = ERTS_ALC_INFO_A_DISABLED_EXEC;
+ goto save_alloc;
+ }
+#endif
if (erts_is_atom_str("alloc_util", alloc, 0)) {
ai = ERTS_ALC_INFO_A_ALLOC_UTIL;
save_alloc:
@@ -3479,8 +3583,11 @@ UWord erts_alc_test(UWord op, UWord a1, UWord a2, UWord a3)
init.enable = 1;
init.atype = GOODFIT;
init.init.util.name_prefix = (char *) a1;
- init.init.util.ts = a2 ? 1 : 0;
-
+#ifdef ERTS_SMP
+ init.init.util.ts = 1;
+#else
+ init.init.util.ts = a2 ? 1 : 0;
+#endif
if ((char **) a3) {
char **argv = (char **) a3;
int i = 0;
diff --git a/erts/emulator/beam/erl_alloc.h b/erts/emulator/beam/erl_alloc.h
index f540bae20d..56a3b73bf9 100644
--- a/erts/emulator/beam/erl_alloc.h
+++ b/erts/emulator/beam/erl_alloc.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2002-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@
#ifdef USE_THREADS
#include "erl_threads.h"
#endif
+#include "erl_mmap.h"
#ifdef DEBUG
# undef ERTS_ALC_WANT_INLINE
@@ -43,9 +44,11 @@
#if ERTS_CAN_INLINE && ERTS_ALC_WANT_INLINE
# define ERTS_ALC_DO_INLINE 1
# define ERTS_ALC_INLINE static ERTS_INLINE
+# define ERTS_ALC_FORCE_INLINE static ERTS_FORCE_INLINE
#else
# define ERTS_ALC_DO_INLINE 0
# define ERTS_ALC_INLINE
+# define ERTS_ALC_FORCE_INLINE
#endif
#define ERTS_ALC_NO_FIXED_SIZES \
@@ -66,11 +69,11 @@ void *erts_sys_aligned_realloc(UWord alignment, void *ptr, UWord size, UWord old
void erts_sys_aligned_free(UWord alignment, void *ptr);
#endif
-Eterm erts_memory(int *, void *, void *, Eterm);
-Eterm erts_allocated_areas(int *, void *, void *);
+Eterm erts_memory(fmtfn_t *, void *, void *, Eterm);
+Eterm erts_allocated_areas(fmtfn_t *, void *, void *);
Eterm erts_alloc_util_allocators(void *proc);
-void erts_allocator_info(int, void *);
+void erts_allocator_info(fmtfn_t, void *);
Eterm erts_allocator_options(void *proc);
struct process;
@@ -177,6 +180,12 @@ void sys_free(void *) __deprecated; /* erts_free() */
void *sys_alloc(Uint ) __deprecated; /* erts_alloc_fnf() */
void *sys_realloc(void *, Uint) __deprecated; /* erts_realloc_fnf() */
+#undef ERTS_HAVE_IS_IN_LITERAL_RANGE
+#if defined(ARCH_32) || defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
+# define ERTS_HAVE_IS_IN_LITERAL_RANGE
+#endif
+
+
/*
* erts_alloc[_fnf](), erts_realloc[_fnf](), erts_free() works as
* malloc(), realloc(), and free() with the following exceptions:
@@ -204,6 +213,9 @@ void erts_free(ErtsAlcType_t type, void *ptr);
void *erts_alloc_fnf(ErtsAlcType_t type, Uint size);
void *erts_realloc_fnf(ErtsAlcType_t type, void *ptr, Uint size);
int erts_is_allctr_wrapper_prelocked(void);
+#ifdef ERTS_HAVE_IS_IN_LITERAL_RANGE
+int erts_is_in_literal_range(void* ptr);
+#endif
#endif /* #if !ERTS_ALC_DO_INLINE */
@@ -221,12 +233,14 @@ ERTS_ALC_INLINE
void *erts_alloc(ErtsAlcType_t type, Uint size)
{
void *res;
+ ERTS_MSACC_PUSH_AND_SET_STATE_X(ERTS_MSACC_STATE_ALLOC);
res = (*erts_allctrs[ERTS_ALC_T2A(type)].alloc)(
- ERTS_ALC_T2N(type),
- erts_allctrs[ERTS_ALC_T2A(type)].extra,
- size);
+ ERTS_ALC_T2N(type),
+ erts_allctrs[ERTS_ALC_T2A(type)].extra,
+ size);
if (!res)
erts_alloc_n_enomem(ERTS_ALC_T2N(type), size);
+ ERTS_MSACC_POP_STATE_X();
return res;
}
@@ -234,6 +248,7 @@ ERTS_ALC_INLINE
void *erts_realloc(ErtsAlcType_t type, void *ptr, Uint size)
{
void *res;
+ ERTS_MSACC_PUSH_AND_SET_STATE_X(ERTS_MSACC_STATE_ALLOC);
res = (*erts_allctrs[ERTS_ALC_T2A(type)].realloc)(
ERTS_ALC_T2N(type),
erts_allctrs[ERTS_ALC_T2A(type)].extra,
@@ -241,37 +256,48 @@ void *erts_realloc(ErtsAlcType_t type, void *ptr, Uint size)
size);
if (!res)
erts_realloc_n_enomem(ERTS_ALC_T2N(type), ptr, size);
+ ERTS_MSACC_POP_STATE_X();
return res;
}
ERTS_ALC_INLINE
void erts_free(ErtsAlcType_t type, void *ptr)
{
+ ERTS_MSACC_PUSH_AND_SET_STATE_X(ERTS_MSACC_STATE_ALLOC);
(*erts_allctrs[ERTS_ALC_T2A(type)].free)(
ERTS_ALC_T2N(type),
erts_allctrs[ERTS_ALC_T2A(type)].extra,
ptr);
+ ERTS_MSACC_POP_STATE_X();
}
ERTS_ALC_INLINE
void *erts_alloc_fnf(ErtsAlcType_t type, Uint size)
{
- return (*erts_allctrs[ERTS_ALC_T2A(type)].alloc)(
+ void *res;
+ ERTS_MSACC_PUSH_AND_SET_STATE_X(ERTS_MSACC_STATE_ALLOC);
+ res = (*erts_allctrs[ERTS_ALC_T2A(type)].alloc)(
ERTS_ALC_T2N(type),
erts_allctrs[ERTS_ALC_T2A(type)].extra,
size);
+ ERTS_MSACC_POP_STATE_X();
+ return res;
}
ERTS_ALC_INLINE
void *erts_realloc_fnf(ErtsAlcType_t type, void *ptr, Uint size)
{
- return (*erts_allctrs[ERTS_ALC_T2A(type)].realloc)(
+ void *res;
+ ERTS_MSACC_PUSH_AND_SET_STATE_X(ERTS_MSACC_STATE_ALLOC);
+ res = (*erts_allctrs[ERTS_ALC_T2A(type)].realloc)(
ERTS_ALC_T2N(type),
erts_allctrs[ERTS_ALC_T2A(type)].extra,
ptr,
size);
+ ERTS_MSACC_POP_STATE_X();
+ return res;
}
ERTS_ALC_INLINE
@@ -281,6 +307,28 @@ int erts_is_allctr_wrapper_prelocked(void)
&& !!erts_tsd_get(erts_allctr_prelock_tsd_key); /* by me */
}
+#ifdef ERTS_HAVE_IS_IN_LITERAL_RANGE
+
+ERTS_ALC_FORCE_INLINE
+int erts_is_in_literal_range(void* ptr)
+{
+#if defined(ARCH_32)
+ Uint ix = (UWord)ptr >> ERTS_MMAP_SUPERALIGNED_BITS;
+
+ return erts_literal_vspace_map[ix / ERTS_VSPACE_WORD_BITS]
+ & ((UWord)1 << (ix % ERTS_VSPACE_WORD_BITS));
+
+#elif defined(ARCH_64)
+ extern char* erts_literals_start;
+ extern UWord erts_literals_size;
+ return ErtsInArea(ptr, erts_literals_start, erts_literals_size);
+#else
+# error No ARCH_xx
+#endif
+}
+
+#endif /* ERTS_HAVE_IS_IN_LITERAL_RANGE */
+
#endif /* #if ERTS_ALC_DO_INLINE || defined(ERTS_ALC_INTERNAL__) */
#define ERTS_ALC_GET_THR_IX() ((int) erts_get_scheduler_id())
@@ -516,5 +564,3 @@ NAME##_free(TYPE *p) \
#undef ERTS_ALC_ATTRIBUTES
#endif /* #ifndef ERL_ALLOC_H__ */
-
-
diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types
index 7738531142..6e8710eb8a 100644
--- a/erts/emulator/beam/erl_alloc.types
+++ b/erts/emulator/beam/erl_alloc.types
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2014. All Rights Reserved.
+# Copyright Ericsson AB 2003-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -86,10 +86,9 @@ allocator LONG_LIVED true ll_alloc
allocator EHEAP true eheap_alloc
allocator ETS true ets_alloc
allocator FIXED_SIZE true fix_alloc
-
-+if halfword
-allocator LONG_LIVED_LOW true ll_low_alloc
-allocator STANDARD_LOW true std_low_alloc
+allocator LITERAL true literal_alloc
++if exec_alloc
+allocator EXEC true exec_alloc
+endif
+else # Non smp build
@@ -101,10 +100,9 @@ allocator LONG_LIVED false ll_alloc
allocator EHEAP false eheap_alloc
allocator ETS false ets_alloc
allocator FIXED_SIZE false fix_alloc
-
-+if halfword
-allocator LONG_LIVED_LOW false ll_low_alloc
-allocator STANDARD_LOW false std_low_alloc
+allocator LITERAL false literal_alloc
++if exec_alloc
+allocator EXEC false exec_alloc
+endif
+endif
@@ -160,6 +158,8 @@ type OLD_HEAP EHEAP PROCESSES old_heap
type HEAP_FRAG EHEAP PROCESSES heap_frag
type TMP_HEAP TEMPORARY PROCESSES tmp_heap
type MSG_REF FIXED_SIZE PROCESSES msg_ref
+type MSG EHEAP PROCESSES message
+type MSGQ_CHNG SHORT_LIVED PROCESSES messages_queue_change
type MSG_ROOTS TEMPORARY PROCESSES msg_roots
type ROOTSET TEMPORARY PROCESSES root_set
type LOADER_TMP TEMPORARY CODE loader_tmp
@@ -264,7 +264,6 @@ type PRTSD STANDARD SYSTEM port_specific_data
type CPUDATA LONG_LIVED SYSTEM cpu_data
type TMP_CPU_IDS SHORT_LIVED SYSTEM tmp_cpu_ids
type EXT_TERM_DATA SHORT_LIVED PROCESSES external_term_data
-type ZLIB STANDARD SYSTEM zlib
type CPU_GRPS_MAP LONG_LIVED SYSTEM cpu_groups_map
type AUX_WORK_TMO LONG_LIVED SYSTEM aux_work_timeouts
type MISC_AUX_WORK_Q LONG_LIVED SYSTEM misc_aux_work_q
@@ -276,6 +275,11 @@ type PROC_SYS_TSK SHORT_LIVED PROCESSES proc_sys_task
type PROC_SYS_TSK_QS SHORT_LIVED PROCESSES proc_sys_task_queues
type NEW_TIME_OFFSET SHORT_LIVED SYSTEM new_time_offset
type IOB_REQ SHORT_LIVED SYSTEM io_bytes_request
+type TRACER_NIF LONG_LIVED SYSTEM tracer_nif
+type TRACE_MSG_QUEUE SHORT_LIVED SYSTEM trace_message_queue
+type SCHED_ASYNC_JOB SHORT_LIVED SYSTEM async_calls
+type DIRTY_START STANDARD PROCESSES dirty_start
+type DIRTY_SL SHORT_LIVED SYSTEM dirty_short_lived
+if threads_no_smp
# Need thread safe allocs, but std_alloc and fix_alloc are not;
@@ -294,8 +298,10 @@ type THR_Q_LL LONG_LIVED SYSTEM long_lived_thr_queue
+if smp
type ASYNC SHORT_LIVED SYSTEM async
+type ZLIB STANDARD SYSTEM zlib
+else
-# sl_alloc is not thread safe in non smp build; therefore, we use driver_alloc
+# sl/std_alloc is not thread safe in non smp build; therefore, we use driver_alloc
+type ZLIB DRIVER SYSTEM zlib
type ASYNC DRIVER SYSTEM async
+endif
@@ -308,6 +314,7 @@ type RUNQ_BLNS LONG_LIVED SYSTEM run_queue_balancing
type THR_PRGR_IDATA LONG_LIVED SYSTEM thr_prgr_internal_data
type THR_PRGR_DATA LONG_LIVED SYSTEM thr_prgr_data
type T_THR_PRGR_DATA SHORT_LIVED SYSTEM temp_thr_prgr_data
+type RELEASE_LAREA SHORT_LIVED SYSTEM release_literal_area
+endif
#
@@ -341,42 +348,29 @@ type SL_MPATHS SHORT_LIVED SYSTEM sl_migration_paths
# Currently most hipe code use this type.
type HIPE SYSTEM SYSTEM hipe_data
++if exec_alloc
+type HIPE_EXEC EXEC CODE hipe_code
+endif
-+if heap_frag_elim_test
-
-type SSB SHORT_LIVED PROCESSES ssb
-
+endif
-+if halfword
-type DDLL_PROCESS STANDARD_LOW SYSTEM ddll_processes
-type MONITOR_LH STANDARD_LOW PROCESSES monitor_lh
-type NLINK_LH STANDARD_LOW PROCESSES nlink_lh
-type CODE LONG_LIVED_LOW CODE code
-type DB_HEIR_DATA STANDARD_LOW ETS db_heir_data
-type DB_MS_PSDO_PROC LONG_LIVED_LOW ETS db_match_pseudo_proc
-type SCHDLR_DATA LONG_LIVED_LOW SYSTEM scheduler_data
-type LL_TEMP_TERM LONG_LIVED_LOW SYSTEM ll_temp_term
++if heap_frag_elim_test
-type NIF_TRAP_EXPORT STANDARD_LOW CODE nif_trap_export_entry
-type EXPORT LONG_LIVED_LOW CODE export_entry
-type MONITOR_SH STANDARD_LOW PROCESSES monitor_sh
-type NLINK_SH STANDARD_LOW PROCESSES nlink_sh
-type AINFO_REQ STANDARD_LOW SYSTEM alloc_info_request
-type SCHED_WTIME_REQ STANDARD_LOW SYSTEM sched_wall_time_request
-type GC_INFO_REQ STANDARD_LOW SYSTEM gc_info_request
-type PORT_DATA_HEAP STANDARD_LOW SYSTEM port_data_heap
-type SYS_CHECK_REQ STANDARD_LOW SYSTEM system_check_request
+type SSB SHORT_LIVED PROCESSES ssb
-+else # "fullword"
++endif
+
+type DEBUG SHORT_LIVED SYSTEM debugging
type DDLL_PROCESS STANDARD SYSTEM ddll_processes
type MONITOR_LH STANDARD PROCESSES monitor_lh
type NLINK_LH STANDARD PROCESSES nlink_lh
type CODE LONG_LIVED CODE code
+type LITERAL LITERAL CODE literal
+type LITERAL_REF SHORT_LIVED CODE literal_area_ref
+type PURGE_DATA SHORT_LIVED CODE purge_data
type DB_HEIR_DATA STANDARD ETS db_heir_data
type DB_MS_PSDO_PROC LONG_LIVED ETS db_match_pseudo_proc
type SCHDLR_DATA LONG_LIVED SYSTEM scheduler_data
@@ -390,11 +384,9 @@ type AINFO_REQ SHORT_LIVED SYSTEM alloc_info_request
type SCHED_WTIME_REQ SHORT_LIVED SYSTEM sched_wall_time_request
type GC_INFO_REQ SHORT_LIVED SYSTEM gc_info_request
type PORT_DATA_HEAP STANDARD SYSTEM port_data_heap
+type MSACC DRIVER SYSTEM microstate_accounting
type SYS_CHECK_REQ SHORT_LIVED SYSTEM system_check_request
-+endif
-
-
#
# Types used by system specific code
#
@@ -432,21 +424,6 @@ type SYS_WRITE_BUF BINARY SYSTEM sys_write_buf
+endif
-+if ose
-
-type SYS_READ_BUF TEMPORARY SYSTEM sys_read_buf
-type FD_TAB LONG_LIVED SYSTEM fd_tab
-type FD_ENTRY_BUF STANDARD SYSTEM fd_entry_buf
-type FD_SIG_LIST SHORT_LIVED SYSTEM fd_sig_list
-type DRV_EV STANDARD SYSTEM driver_event
-type CS_PROG_PATH LONG_LIVED SYSTEM cs_prog_path
-type ENVIRONMENT TEMPORARY SYSTEM environment
-type PUTENV_STR SYSTEM SYSTEM putenv_string
-type PRT_REP_EXIT STANDARD SYSTEM port_report_exit
-
-+endif
-
-
+if win32
type DRV_DATA_BUF SYSTEM SYSTEM drv_data_buf
diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c
index d9eaa7b32c..230ca6ccbb 100644
--- a/erts/emulator/beam/erl_alloc_util.c
+++ b/erts/emulator/beam/erl_alloc_util.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2002-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -52,6 +52,7 @@
#ifdef ERTS_ENABLE_LOCK_COUNT
#include "erl_lock_count.h"
#endif
+#include "lttng-wrapper.h"
#if defined(ERTS_ALLOC_UTIL_HARD_DEBUG) && defined(__GNUC__)
#warning "* * * * * * * * * *"
@@ -751,12 +752,77 @@ internal_free(void *ptr)
#endif
+#ifdef ARCH_32
+
+/*
+ * Bit vector for the entire 32-bit virtual address space
+ * with one bit for each super aligned memory segment.
+ */
+
+#define VSPACE_MAP_BITS (1 << (32 - ERTS_MMAP_SUPERALIGNED_BITS))
+#define VSPACE_MAP_SZ (VSPACE_MAP_BITS / ERTS_VSPACE_WORD_BITS)
+
+static ERTS_INLINE void set_bit(UWord* map, Uint ix)
+{
+ ASSERT(ix / ERTS_VSPACE_WORD_BITS < VSPACE_MAP_SZ);
+ map[ix / ERTS_VSPACE_WORD_BITS]
+ |= ((UWord)1 << (ix % ERTS_VSPACE_WORD_BITS));
+}
+
+static ERTS_INLINE void clr_bit(UWord* map, Uint ix)
+{
+ ASSERT(ix / ERTS_VSPACE_WORD_BITS < VSPACE_MAP_SZ);
+ map[ix / ERTS_VSPACE_WORD_BITS]
+ &= ~((UWord)1 << (ix % ERTS_VSPACE_WORD_BITS));
+}
+
+static ERTS_INLINE int is_bit_set(UWord* map, Uint ix)
+{
+ ASSERT(ix / ERTS_VSPACE_WORD_BITS < VSPACE_MAP_SZ);
+ return map[ix / ERTS_VSPACE_WORD_BITS]
+ & ((UWord)1 << (ix % ERTS_VSPACE_WORD_BITS));
+}
+
+UWord erts_literal_vspace_map[VSPACE_MAP_SZ];
+
+static void set_literal_range(void* start, Uint size)
+{
+ Uint ix = (UWord)start >> ERTS_MMAP_SUPERALIGNED_BITS;
+ Uint n = size >> ERTS_MMAP_SUPERALIGNED_BITS;
+
+ ASSERT(!((UWord)start & ERTS_INV_SUPERALIGNED_MASK));
+ ASSERT(!((UWord)size & ERTS_INV_SUPERALIGNED_MASK));
+ ASSERT(n);
+ while (n--) {
+ ASSERT(!is_bit_set(erts_literal_vspace_map, ix));
+ set_bit(erts_literal_vspace_map, ix);
+ ix++;
+ }
+}
+
+static void clear_literal_range(void* start, Uint size)
+{
+ Uint ix = (UWord)start >> ERTS_MMAP_SUPERALIGNED_BITS;
+ Uint n = size >> ERTS_MMAP_SUPERALIGNED_BITS;
+
+ ASSERT(!((UWord)start & ERTS_INV_SUPERALIGNED_MASK));
+ ASSERT(!((UWord)size & ERTS_INV_SUPERALIGNED_MASK));
+ ASSERT(n);
+ while (n--) {
+ ASSERT(is_bit_set(erts_literal_vspace_map, ix));
+ clr_bit(erts_literal_vspace_map, ix);
+ ix++;
+ }
+}
+
+#endif /* ARCH_32 */
+
/* mseg ... */
#if HAVE_ERTS_MSEG
-static ERTS_INLINE void *
-alcu_mseg_alloc(Allctr_t *allctr, Uint *size_p, Uint flags)
+void*
+erts_alcu_mseg_alloc(Allctr_t *allctr, Uint *size_p, Uint flags)
{
void *res;
UWord size = (UWord) *size_p;
@@ -766,8 +832,9 @@ alcu_mseg_alloc(Allctr_t *allctr, Uint *size_p, Uint flags)
return res;
}
-static ERTS_INLINE void *
-alcu_mseg_realloc(Allctr_t *allctr, void *seg, Uint old_size, Uint *new_size_p)
+void*
+erts_alcu_mseg_realloc(Allctr_t *allctr, void *seg,
+ Uint old_size, Uint *new_size_p)
{
void *res;
UWord new_size = (UWord) *new_size_p;
@@ -778,19 +845,116 @@ alcu_mseg_realloc(Allctr_t *allctr, void *seg, Uint old_size, Uint *new_size_p)
return res;
}
-static ERTS_INLINE void
-alcu_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size, Uint flags)
+void
+erts_alcu_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size, Uint flags)
{
erts_mseg_dealloc_opt(allctr->alloc_no, seg, (UWord) size, flags, &allctr->mseg_opt);
INC_CC(allctr->calls.mseg_dealloc);
}
-#endif
-static ERTS_INLINE void *
-alcu_sys_alloc(Allctr_t *allctr, Uint size, int superalign)
+#if defined(ARCH_32)
+
+void*
+erts_alcu_literal_32_mseg_alloc(Allctr_t *allctr, Uint *size_p, Uint flags)
+{
+ void* res;
+ Uint sz = ERTS_SUPERALIGNED_CEILING(*size_p);
+ ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL &&
+ allctr->t == 0);
+ ERTS_SMP_LC_ASSERT(allctr->thread_safe);
+
+ res = erts_alcu_mseg_alloc(allctr, &sz, flags);
+ if (res) {
+ set_literal_range(res, sz);
+ *size_p = sz;
+ }
+ return res;
+}
+
+void*
+erts_alcu_literal_32_mseg_realloc(Allctr_t *allctr, void *seg,
+ Uint old_size, Uint *new_size_p)
+{
+ void* res;
+ Uint new_sz = ERTS_SUPERALIGNED_CEILING(*new_size_p);
+ ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL &&
+ allctr->t == 0);
+ ERTS_SMP_LC_ASSERT(allctr->thread_safe);
+
+ if (seg && old_size)
+ clear_literal_range(seg, old_size);
+ res = erts_alcu_mseg_realloc(allctr, seg, old_size, &new_sz);
+ if (res) {
+ set_literal_range(res, new_sz);
+ *new_size_p = new_sz;
+ }
+ return res;
+}
+
+void
+erts_alcu_literal_32_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size,
+ Uint flags)
+{
+ ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL &&
+ allctr->t == 0);
+ ERTS_SMP_LC_ASSERT(allctr->thread_safe);
+
+ erts_alcu_mseg_dealloc(allctr, seg, size, flags);
+
+ clear_literal_range(seg, size);
+}
+
+#elif defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
+
+/* Used by literal allocator that has its own mmapper (super carrier) */
+void*
+erts_alcu_mmapper_mseg_alloc(Allctr_t *allctr, Uint *size_p, Uint flags)
+{
+ void* res;
+ UWord size = (UWord) *size_p;
+ Uint32 mmap_flags = ERTS_MMAPFLG_SUPERCARRIER_ONLY;
+ if (flags & ERTS_MSEG_FLG_2POW)
+ mmap_flags |= ERTS_MMAPFLG_SUPERALIGNED;
+
+ res = erts_mmap(allctr->mseg_mmapper, mmap_flags, &size);
+ *size_p = (Uint)size;
+ INC_CC(allctr->calls.mseg_alloc);
+ return res;
+}
+
+void*
+erts_alcu_mmapper_mseg_realloc(Allctr_t *allctr, void *seg,
+ Uint old_size, Uint *new_size_p)
{
void *res;
+ UWord new_size = (UWord) *new_size_p;
+ res = erts_mremap(allctr->mseg_mmapper, ERTS_MSEG_FLG_NONE, seg, old_size, &new_size);
+ *new_size_p = (Uint) new_size;
+ INC_CC(allctr->calls.mseg_realloc);
+ return res;
+}
+
+void
+erts_alcu_mmapper_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size,
+ Uint flags)
+{
+ Uint32 mmap_flags = ERTS_MMAPFLG_SUPERCARRIER_ONLY;
+ if (flags & ERTS_MSEG_FLG_2POW)
+ mmap_flags |= ERTS_MMAPFLG_SUPERALIGNED;
+
+ erts_munmap(allctr->mseg_mmapper, mmap_flags, seg, (UWord)size);
+ INC_CC(allctr->calls.mseg_dealloc);
+}
+#endif /* ARCH_64 && ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION */
+
+#endif /* HAVE_ERTS_MSEG */
+
+void*
+erts_alcu_sys_alloc(Allctr_t *allctr, Uint* size_p, int superalign)
+{
+ void *res;
+ const Uint size = *size_p;
#if ERTS_SA_MB_CARRIERS && ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC
if (superalign)
res = erts_sys_aligned_alloc(ERTS_SACRR_UNIT_SZ, size);
@@ -803,10 +967,11 @@ alcu_sys_alloc(Allctr_t *allctr, Uint size, int superalign)
return res;
}
-static ERTS_INLINE void *
-alcu_sys_realloc(Allctr_t *allctr, void *ptr, Uint size, Uint old_size, int superalign)
+void*
+erts_alcu_sys_realloc(Allctr_t *allctr, void *ptr, Uint *size_p, Uint old_size, int superalign)
{
void *res;
+ const Uint size = *size_p;
#if ERTS_SA_MB_CARRIERS && ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC
if (superalign)
@@ -824,8 +989,8 @@ alcu_sys_realloc(Allctr_t *allctr, void *ptr, Uint size, Uint old_size, int supe
return res;
}
-static ERTS_INLINE void
-alcu_sys_free(Allctr_t *allctr, void *ptr, int superalign)
+void
+erts_alcu_sys_dealloc(Allctr_t *allctr, void *ptr, Uint size, int superalign)
{
#if ERTS_SA_MB_CARRIERS && ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC
if (superalign)
@@ -838,6 +1003,59 @@ alcu_sys_free(Allctr_t *allctr, void *ptr, int superalign)
erts_mtrace_crr_free(allctr->alloc_no, ERTS_ALC_A_SYSTEM, ptr);
}
+#ifdef ARCH_32
+
+void*
+erts_alcu_literal_32_sys_alloc(Allctr_t *allctr, Uint* size_p, int superalign)
+{
+ void* res;
+ Uint size = ERTS_SUPERALIGNED_CEILING(*size_p);
+ ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL &&
+ allctr->t == 0);
+ ERTS_SMP_LC_ASSERT(allctr->thread_safe);
+
+ res = erts_alcu_sys_alloc(allctr, &size, 1);
+ if (res) {
+ set_literal_range(res, size);
+ *size_p = size;
+ }
+ return res;
+}
+
+void*
+erts_alcu_literal_32_sys_realloc(Allctr_t *allctr, void *ptr, Uint* size_p, Uint old_size, int superalign)
+{
+ void* res;
+ Uint size = ERTS_SUPERALIGNED_CEILING(*size_p);
+
+ ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL &&
+ allctr->t == 0);
+ ERTS_SMP_LC_ASSERT(allctr->thread_safe);
+
+ if (ptr && old_size)
+ clear_literal_range(ptr, old_size);
+ res = erts_alcu_sys_realloc(allctr, ptr, &size, old_size, 1);
+ if (res) {
+ set_literal_range(res, size);
+ *size_p = size;
+ }
+ return res;
+}
+
+void
+erts_alcu_literal_32_sys_dealloc(Allctr_t *allctr, void *ptr, Uint size, int superalign)
+{
+ ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL &&
+ allctr->t == 0);
+ ERTS_SMP_LC_ASSERT(allctr->thread_safe);
+
+ erts_alcu_sys_dealloc(allctr, ptr, size, 1);
+
+ clear_literal_range(ptr, size);
+}
+
+#endif /* ARCH_32 */
+
static Uint
get_next_mbc_size(Allctr_t *allctr)
{
@@ -2084,7 +2302,7 @@ mbc_alloc_block(Allctr_t *allctr, Uint size, Uint *blk_szp)
if (!blk) {
blk = create_carrier(allctr, get_blk_sz, CFLG_MBC);
-#if !HALFWORD_HEAP && !ERTS_SUPER_ALIGNED_MSEG_ONLY
+#if !ERTS_SUPER_ALIGNED_MSEG_ONLY
if (!blk) {
/* Emergency! We couldn't create the carrier as we wanted.
Try to place it in a sys_alloced sbc. */
@@ -2930,6 +3148,7 @@ cpool_insert(Allctr_t *allctr, Carrier_t *crr)
erts_smp_atomic_set_wb(&crr->allctr,
((erts_aint_t) allctr)|ERTS_CRR_ALCTR_FLG_IN_POOL);
+ LTTNG3(carrier_pool_put, ERTS_ALC_A2AD(allctr->alloc_no), allctr->ix, CARRIER_SZ(crr));
}
static void
@@ -3045,6 +3264,7 @@ cpool_fetch(Allctr_t *allctr, UWord size)
first_old_traitor = allctr->cpool.traitor_list.next;
cpool_entrance = NULL;
+ LTTNG3(carrier_pool_get, ERTS_ALC_A2AD(allctr->alloc_no), allctr->ix, (unsigned long)size);
/*
* Search my own pooled_list,
* i.e my abandoned carriers that were in the pool last time I checked.
@@ -3531,8 +3751,7 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags)
int is_mseg = 0;
#endif
- if (HALFWORD_HEAP
- || (ERTS_SUPER_ALIGNED_MSEG_ONLY && (flags & CFLG_MBC))
+ if ((ERTS_SUPER_ALIGNED_MSEG_ONLY && (flags & CFLG_MBC))
|| !allow_sys_alloc_carriers) {
flags |= CFLG_FORCE_MSEG;
flags &= ~CFLG_FORCE_SYS_ALLOC;
@@ -3540,6 +3759,8 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags)
return NULL;
#endif
}
+ flags |= allctr->crr_set_flgs;
+ flags &= ~allctr->crr_clr_flgs;
ASSERT((flags & CFLG_SBC && !(flags & CFLG_MBC))
|| (flags & CFLG_MBC && !(flags & CFLG_SBC)));
@@ -3555,7 +3776,21 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags)
return NULL;
}
- blk_sz = UMEMSZ2BLKSZ(allctr, umem_sz);
+ if (flags & CFLG_MAIN_CARRIER) {
+ ASSERT(flags & CFLG_MBC);
+ ASSERT(flags & CFLG_NO_CPOOL);
+ ASSERT(umem_sz == allctr->main_carrier_size);
+ ERTS_UNDEF(blk_sz, 0);
+
+ if (allctr->main_carrier_size < allctr->min_mbc_size)
+ allctr->main_carrier_size = allctr->min_mbc_size;
+ crr_sz = bcrr_sz = allctr->main_carrier_size;
+ }
+ else {
+ ERTS_UNDEF(bcrr_sz, 0);
+ ERTS_UNDEF(crr_sz, 0);
+ blk_sz = UMEMSZ2BLKSZ(allctr, umem_sz);
+ }
#ifdef ERTS_SMP
allctr->cpool.disable_abandon = ERTS_ALC_CPOOL_MAX_DISABLE_ABANDON;
@@ -3601,13 +3836,15 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags)
mseg_flags = ERTS_MSEG_FLG_NONE;
}
else {
- crr_sz = (*allctr->get_next_mbc_size)(allctr);
- if (crr_sz < MBC_HEADER_SIZE(allctr) + blk_sz)
- crr_sz = MBC_HEADER_SIZE(allctr) + blk_sz;
- mseg_flags = ERTS_MSEG_FLG_2POW;
+ if (!(flags & CFLG_MAIN_CARRIER)) {
+ crr_sz = (*allctr->get_next_mbc_size)(allctr);
+ if (crr_sz < MBC_HEADER_SIZE(allctr) + blk_sz)
+ crr_sz = MBC_HEADER_SIZE(allctr) + blk_sz;
+ }
+ mseg_flags = ERTS_MSEG_FLG_2POW;
}
- crr = (Carrier_t *) alcu_mseg_alloc(allctr, &crr_sz, mseg_flags);
+ crr = (Carrier_t *) allctr->mseg_alloc(allctr, &crr_sz, mseg_flags);
if (!crr) {
have_tried_mseg = 1;
if (!(have_tried_sys_alloc || flags & CFLG_FORCE_MSEG))
@@ -3639,23 +3876,22 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags)
if (flags & CFLG_SBC) {
bcrr_sz = blk_sz + SBC_HEADER_SIZE;
}
- else {
+ else if (!(flags & CFLG_MAIN_CARRIER)) {
bcrr_sz = MBC_HEADER_SIZE(allctr) + blk_sz;
- if (!(flags & CFLG_MAIN_CARRIER)
- && bcrr_sz < allctr->smallest_mbc_size)
- bcrr_sz = allctr->smallest_mbc_size;
+ if (bcrr_sz < allctr->smallest_mbc_size)
+ bcrr_sz = allctr->smallest_mbc_size;
}
crr_sz = (flags & CFLG_FORCE_SIZE
? UNIT_CEILING(bcrr_sz)
: SYS_ALLOC_CARRIER_CEILING(bcrr_sz));
- crr = (Carrier_t *) alcu_sys_alloc(allctr, crr_sz, flags & CFLG_MBC);
+ crr = (Carrier_t *) allctr->sys_alloc(allctr, &crr_sz, flags & CFLG_MBC);
if (!crr) {
if (crr_sz > UNIT_CEILING(bcrr_sz)) {
crr_sz = UNIT_CEILING(bcrr_sz);
- crr = (Carrier_t *) alcu_sys_alloc(allctr, crr_sz, flags & CFLG_MBC);
+ crr = (Carrier_t *) allctr->sys_alloc(allctr, &crr_sz, flags & CFLG_MBC);
}
if (!crr) {
#if HAVE_ERTS_MSEG
@@ -3714,6 +3950,21 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags)
}
+#ifdef USE_LTTNG_VM_TRACEPOINTS
+ if (LTTNG_ENABLED(carrier_create)) {
+ lttng_decl_carrier_stats(mbc_stats);
+ lttng_decl_carrier_stats(sbc_stats);
+ LTTNG_CARRIER_STATS_TO_LTTNG_STATS(&(allctr->mbcs), mbc_stats);
+ LTTNG_CARRIER_STATS_TO_LTTNG_STATS(&(allctr->sbcs), sbc_stats);
+ LTTNG5(carrier_create,
+ ERTS_ALC_A2AD(allctr->alloc_no),
+ allctr->ix,
+ crr_sz,
+ mbc_stats,
+ sbc_stats);
+ }
+#endif
+
DEBUG_SAVE_ALIGNMENT(crr);
return blk;
}
@@ -3754,7 +4005,7 @@ resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags)
new_crr_sz = new_blk_sz + SBC_HEADER_SIZE;
new_crr_sz = ERTS_SACRR_UNIT_CEILING(new_crr_sz);
- new_crr = (Carrier_t *) alcu_mseg_realloc(allctr,
+ new_crr = (Carrier_t *) allctr->mseg_realloc(allctr,
old_crr,
old_crr_sz,
&new_crr_sz);
@@ -3769,11 +4020,6 @@ resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags)
DEBUG_SAVE_ALIGNMENT(new_crr);
return new_blk;
}
-#if HALFWORD_HEAP
- /* Old carrier unchanged; restore stat */
- STAT_MSEG_SBC_ALLOC(allctr, old_crr_sz, old_blk_sz);
- return NULL;
-#endif
create_flags |= CFLG_FORCE_SYS_ALLOC; /* since mseg_realloc()
failed */
}
@@ -3784,7 +4030,7 @@ resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags)
(void *) BLK2UMEM(old_blk),
MIN(new_blk_sz, old_blk_sz) - ABLK_HDR_SZ);
unlink_carrier(&allctr->sbc_list, old_crr);
- alcu_mseg_dealloc(allctr, old_crr, old_crr_sz, ERTS_MSEG_FLG_NONE);
+ allctr->mseg_dealloc(allctr, old_crr, old_crr_sz, ERTS_MSEG_FLG_NONE);
}
else {
/* Old carrier unchanged; restore stat */
@@ -3801,9 +4047,9 @@ resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags)
? UNIT_CEILING(new_bcrr_sz)
: SYS_ALLOC_CARRIER_CEILING(new_bcrr_sz));
- new_crr = (Carrier_t *) alcu_sys_realloc(allctr,
+ new_crr = (Carrier_t *) allctr->sys_realloc(allctr,
(void *) old_crr,
- new_crr_sz,
+ &new_crr_sz,
old_crr_sz,
0);
if (new_crr) {
@@ -3822,9 +4068,9 @@ resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags)
else if (new_crr_sz > UNIT_CEILING(new_bcrr_sz)) {
new_crr_sz = new_blk_sz + SBC_HEADER_SIZE;
new_crr_sz = UNIT_CEILING(new_crr_sz);
- new_crr = (Carrier_t *) alcu_sys_realloc(allctr,
+ new_crr = (Carrier_t *) allctr->sys_realloc(allctr,
(void *) old_crr,
- new_crr_sz,
+ &new_crr_sz,
old_crr_sz,
0);
if (new_crr)
@@ -3845,7 +4091,7 @@ resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags)
(void *) BLK2UMEM(old_blk),
MIN(new_blk_sz, old_blk_sz) - ABLK_HDR_SZ);
unlink_carrier(&allctr->sbc_list, old_crr);
- alcu_sys_free(allctr, old_crr, 0);
+ allctr->sys_dealloc(allctr, old_crr, CARRIER_SZ(old_crr), 0);
}
else {
/* Old carrier unchanged; restore... */
@@ -3861,13 +4107,13 @@ dealloc_carrier(Allctr_t *allctr, Carrier_t *crr, int superaligned)
{
#if HAVE_ERTS_MSEG
if (IS_MSEG_CARRIER(crr))
- alcu_mseg_dealloc(allctr, crr, CARRIER_SZ(crr),
+ allctr->mseg_dealloc(allctr, crr, CARRIER_SZ(crr),
(superaligned
? ERTS_MSEG_FLG_2POW
: ERTS_MSEG_FLG_NONE));
else
#endif
- alcu_sys_free(allctr, crr, superaligned);
+ allctr->sys_dealloc(allctr, crr, CARRIER_SZ(crr), superaligned);
}
static void
@@ -3907,9 +4153,9 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk, Carrier_t **busy_pcrr_pp)
ASSERT(IS_LAST_BLK(blk));
#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
- (*allctr->link_free_block)(allctr, blk, 0);
+ (*allctr->link_free_block)(allctr, blk);
HARD_CHECK_BLK_CARRIER(allctr, blk);
- (*allctr->unlink_free_block)(allctr, blk, 0);
+ (*allctr->unlink_free_block)(allctr, blk);
#endif
}
#endif
@@ -3942,6 +4188,21 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk, Carrier_t **busy_pcrr_pp)
allctr->remove_mbc(allctr, crr);
}
+#ifdef USE_LTTNG_VM_TRACEPOINTS
+ if (LTTNG_ENABLED(carrier_destroy)) {
+ lttng_decl_carrier_stats(mbc_stats);
+ lttng_decl_carrier_stats(sbc_stats);
+ LTTNG_CARRIER_STATS_TO_LTTNG_STATS(&(allctr->mbcs), mbc_stats);
+ LTTNG_CARRIER_STATS_TO_LTTNG_STATS(&(allctr->sbcs), sbc_stats);
+ LTTNG5(carrier_destroy,
+ ERTS_ALC_A2AD(allctr->alloc_no),
+ allctr->ix,
+ crr_sz,
+ mbc_stats,
+ sbc_stats);
+ }
+#endif
+
#ifdef ERTS_SMP
schedule_dealloc_carrier(allctr, crr);
#else
@@ -3962,9 +4223,6 @@ static struct {
Eterm e;
Eterm t;
Eterm ramv;
-#if HALFWORD_HEAP
- Eterm low;
-#endif
Eterm sbct;
#if HAVE_ERTS_MSEG
Eterm asbcst;
@@ -4055,9 +4313,6 @@ init_atoms(Allctr_t *allctr)
AM_INIT(e);
AM_INIT(t);
AM_INIT(ramv);
-#if HALFWORD_HEAP
- AM_INIT(low);
-#endif
AM_INIT(sbct);
#if HAVE_ERTS_MSEG
AM_INIT(asbcst);
@@ -4218,7 +4473,7 @@ add_fix_types(Allctr_t *allctr, int internal, Uint **hpp, Uint *szp,
static Eterm
sz_info_fix(Allctr_t *allctr,
int internal,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
@@ -4239,7 +4494,7 @@ sz_info_fix(Allctr_t *allctr,
UWord used = fix->type_size * fix->u.cpool.used;
if (print_to_p) {
- int to = *print_to_p;
+ fmtfn_t to = *print_to_p;
void *arg = print_to_arg;
erts_print(to,
arg,
@@ -4267,7 +4522,7 @@ sz_info_fix(Allctr_t *allctr,
UWord used = fix->type_size*fix->u.nocpool.used;
if (print_to_p) {
- int to = *print_to_p;
+ fmtfn_t to = *print_to_p;
void *arg = print_to_arg;
erts_print(to,
arg,
@@ -4293,7 +4548,7 @@ static Eterm
sz_info_carriers(Allctr_t *allctr,
CarriersStats_t *cs,
char *prefix,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
@@ -4302,7 +4557,7 @@ sz_info_carriers(Allctr_t *allctr,
UWord curr_size = cs->curr.norm.mseg.size + cs->curr.norm.sys_alloc.size;
if (print_to_p) {
- int to = *print_to_p;
+ fmtfn_t to = *print_to_p;
void *arg = print_to_arg;
erts_print(to,
arg,
@@ -4343,7 +4598,7 @@ static Eterm
info_cpool(Allctr_t *allctr,
int sz_only,
char *prefix,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
@@ -4360,7 +4615,7 @@ info_cpool(Allctr_t *allctr,
}
if (print_to_p) {
- int to = *print_to_p;
+ fmtfn_t to = *print_to_p;
void *arg = print_to_arg;
if (!sz_only)
erts_print(to, arg, "%sblocks: %bpu\n", prefix, nob);
@@ -4397,7 +4652,7 @@ static Eterm
info_carriers(Allctr_t *allctr,
CarriersStats_t *cs,
char *prefix,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
@@ -4409,7 +4664,7 @@ info_carriers(Allctr_t *allctr,
curr_size = cs->curr.norm.mseg.size + cs->curr.norm.sys_alloc.size;
if (print_to_p) {
- int to = *print_to_p;
+ fmtfn_t to = *print_to_p;
void *arg = print_to_arg;
erts_print(to,
arg,
@@ -4535,7 +4790,7 @@ make_name_atoms(Allctr_t *allctr)
static Eterm
info_calls(Allctr_t *allctr,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
@@ -4552,7 +4807,7 @@ info_calls(Allctr_t *allctr,
erts_print(TO, TOA, "%s%s calls: %b64u\n",PRFX,NAME,CC)
char *prefix = allctr->name_prefix;
- int to = *print_to_p;
+ fmtfn_t to = *print_to_p;
void *arg = print_to_arg;
PRINT_CC_5(to, arg, prefix, "alloc", allctr->calls.this_alloc);
@@ -4628,7 +4883,7 @@ info_calls(Allctr_t *allctr,
static Eterm
info_options(Allctr_t *allctr,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
@@ -4663,9 +4918,6 @@ info_options(Allctr_t *allctr,
"option e: true\n"
"option t: %s\n"
"option ramv: %s\n"
-#if HALFWORD_HEAP
- "option low: %s\n"
-#endif
"option sbct: %beu\n"
#if HAVE_ERTS_MSEG
"option asbcst: %bpu\n"
@@ -4684,9 +4936,6 @@ info_options(Allctr_t *allctr,
"option acul: %d\n",
topt,
allctr->ramv ? "true" : "false",
-#if HALFWORD_HEAP
- allctr->mseg_opt.low_mem ? "true" : "false",
-#endif
allctr->sbc_threshold,
#if HAVE_ERTS_MSEG
allctr->mseg_opt.abs_shrink_th,
@@ -4749,9 +4998,6 @@ info_options(Allctr_t *allctr,
add_2tup(hpp, szp, &res,
am.sbct,
bld_uint(hpp, szp, allctr->sbc_threshold));
-#if HALFWORD_HEAP
- add_2tup(hpp, szp, &res, am.low, allctr->mseg_opt.low_mem ? am_true : am_false);
-#endif
add_2tup(hpp, szp, &res, am.ramv, allctr->ramv ? am_true : am_false);
add_2tup(hpp, szp, &res, am.t, (allctr->t ? am_true : am_false));
add_2tup(hpp, szp, &res, am.e, am_true);
@@ -4789,7 +5035,7 @@ reset_max_values(CarriersStats_t *cs)
\* */
Eterm
-erts_alcu_au_info_options(int *print_to_p, void *print_to_arg,
+erts_alcu_au_info_options(fmtfn_t *print_to_p, void *print_to_arg,
Uint **hpp, Uint *szp)
{
Eterm res = THE_NON_VALUE;
@@ -4832,7 +5078,7 @@ erts_alcu_au_info_options(int *print_to_p, void *print_to_arg,
Eterm
erts_alcu_info_options(Allctr_t *allctr,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
@@ -4864,7 +5110,7 @@ Eterm
erts_alcu_sz_info(Allctr_t *allctr,
int internal,
int begin_max_period,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
@@ -4950,7 +5196,7 @@ Eterm
erts_alcu_info(Allctr_t *allctr,
int internal,
int begin_max_period,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
@@ -5146,6 +5392,11 @@ do_erts_alcu_alloc(ErtsAlcType_t type, void *extra, Uint size)
void *erts_alcu_alloc(ErtsAlcType_t type, void *extra, Uint size)
{
void *res;
+#ifdef ERTS_SMP
+ ASSERT(!"This is not thread safe");
+#elif defined(USE_THREADS)
+ ASSERT(erts_equal_tids(erts_main_thread, erts_thr_self()));
+#endif
res = do_erts_alcu_alloc(type, extra, size);
DEBUG_CHECK_ALIGNMENT(res);
return res;
@@ -5436,11 +5687,7 @@ do_erts_alcu_realloc(ErtsAlcType_t type,
Block_t *new_blk;
if(IS_SBC_BLK(blk)) {
do_carrier_resize:
-#if HALFWORD_HEAP
- new_blk = resize_carrier(allctr, blk, size, CFLG_SBC | CFLG_FORCE_MSEG);
-#else
new_blk = resize_carrier(allctr, blk, size, CFLG_SBC);
-#endif
res = new_blk ? BLK2UMEM(new_blk) : NULL;
}
else if (alcu_flgs & ERTS_ALCU_FLG_FAIL_REALLOC_MOVE)
@@ -5734,11 +5981,8 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init)
#ifdef ERTS_SMP
if (init->tspec || init->tpref)
allctr->mseg_opt.sched_spec = 1;
-#endif
-# if HALFWORD_HEAP
- allctr->mseg_opt.low_mem = init->low_mem;
-# endif
-#endif
+#endif /* ERTS_SMP */
+#endif /* HAVE_ERTS_MSEG */
allctr->name_prefix = init->name_prefix;
if (!allctr->name_prefix)
@@ -5887,17 +6131,53 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init)
+ ABLK_HDR_SZ)
- ABLK_HDR_SZ);
+ if (init->sys_alloc) {
+ ASSERT(init->sys_realloc && init->sys_dealloc);
+ allctr->sys_alloc = init->sys_alloc;
+ allctr->sys_realloc = init->sys_realloc;
+ allctr->sys_dealloc = init->sys_dealloc;
+ }
+ else {
+ ASSERT(!init->sys_realloc && !init->sys_dealloc);
+ allctr->sys_alloc = &erts_alcu_sys_alloc;
+ allctr->sys_realloc = &erts_alcu_sys_realloc;
+ allctr->sys_dealloc = &erts_alcu_sys_dealloc;
+ }
+#if HAVE_ERTS_MSEG
+ if (init->mseg_alloc) {
+ ASSERT(init->mseg_realloc && init->mseg_dealloc);
+ allctr->mseg_alloc = init->mseg_alloc;
+ allctr->mseg_realloc = init->mseg_realloc;
+ allctr->mseg_dealloc = init->mseg_dealloc;
+ allctr->mseg_mmapper = init->mseg_mmapper;
+ }
+ else {
+ ASSERT(!init->mseg_realloc && !init->mseg_dealloc);
+ allctr->mseg_alloc = &erts_alcu_mseg_alloc;
+ allctr->mseg_realloc = &erts_alcu_mseg_realloc;
+ allctr->mseg_dealloc = &erts_alcu_mseg_dealloc;
+ }
+ /* If a custom carrier alloc function is specified, make sure it's used */
+ if (init->mseg_alloc && !init->sys_alloc) {
+ allctr->crr_set_flgs = CFLG_FORCE_MSEG;
+ allctr->crr_clr_flgs = CFLG_FORCE_SYS_ALLOC;
+ }
+ else if (!init->mseg_alloc && init->sys_alloc) {
+ allctr->crr_set_flgs = CFLG_FORCE_SYS_ALLOC;
+ allctr->crr_clr_flgs = CFLG_FORCE_MSEG;
+ }
+#endif
+
if (allctr->main_carrier_size) {
Block_t *blk;
blk = create_carrier(allctr,
allctr->main_carrier_size,
- CFLG_MBC
+ (ERTS_SUPER_ALIGNED_MSEG_ONLY
+ ? CFLG_FORCE_MSEG : CFLG_FORCE_SYS_ALLOC)
+ | CFLG_MBC
| CFLG_FORCE_SIZE
| CFLG_NO_CPOOL
-#if !HALFWORD_HEAP && !ERTS_SUPER_ALIGNED_MSEG_ONLY
- | CFLG_FORCE_SYS_ALLOC
-#endif
| CFLG_MAIN_CARRIER);
if (!blk) {
#ifdef USE_THREADS
@@ -6160,11 +6440,6 @@ check_blk_carrier(Allctr_t *allctr, Block_t *iblk)
ASSERT(SBC2BLK(allctr, sbc) == iblk);
ASSERT(CARRIER_SZ(sbc) - SBC_HEADER_SIZE >= SBC_BLK_SZ(iblk));
-#if HAVE_ERTS_MSEG
- if (IS_MSEG_CARRIER(sbc)) {
- ASSERT(CARRIER_SZ(sbc) % ERTS_SACRR_UNIT_SZ == 0);
- }
-#endif
crr = sbc;
cl = &allctr->sbc_list;
}
diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h
index f4a2ae7ff3..81180382af 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-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2002-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,13 @@
#define ERTS_ALCU_VSN_STR "3.0"
#include "erl_alloc_types.h"
+#ifdef USE_THREADS
+#define ERL_THREADS_EMU_INTERNAL__
+#include "erl_threads.h"
+#endif
+
+#include "erl_mseg.h"
+#include "lttng-wrapper.h"
#define ERTS_AU_PREF_ALLOC_BITS 11
#define ERTS_AU_MAX_PREF_ALLOC_INSTANCES (1 << ERTS_AU_PREF_ALLOC_BITS)
@@ -45,7 +52,6 @@ typedef struct {
int tspec;
int tpref;
int ramv;
- int low_mem; /* HALFWORD only */
UWord sbct;
UWord asbcst;
UWord rsbcst;
@@ -61,6 +67,16 @@ typedef struct {
void *fix;
size_t *fix_type_size;
+
+#if HAVE_ERTS_MSEG
+ void* (*mseg_alloc)(Allctr_t*, Uint *size_p, Uint flags);
+ void* (*mseg_realloc)(Allctr_t*, void *seg, Uint old_size, Uint *new_size_p);
+ void (*mseg_dealloc)(Allctr_t*, void *seg, Uint size, Uint flags);
+ ErtsMemMapper *mseg_mmapper;
+#endif
+ void* (*sys_alloc)(Allctr_t *allctr, Uint *size_p, int superalign);
+ void* (*sys_realloc)(Allctr_t *allctr, void *ptr, Uint *size_p, Uint old_size, int superalign);
+ void (*sys_dealloc)(Allctr_t *allctr, void *ptr, Uint size, int superalign);
} AllctrInit_t;
typedef struct {
@@ -90,7 +106,6 @@ typedef struct {
0, /* (bool) tspec: thread specific */\
0, /* (bool) tpref: thread preferred */\
0, /* (bool) ramv: realloc always moves */\
- 0, /* (bool) low_mem: HALFWORD only */\
512*1024, /* (bytes) sbct: sbc threshold */\
2*1024*2024, /* (amount) asbcst: abs sbc shrink threshold */\
20, /* (%) rsbcst: rel sbc shrink threshold */\
@@ -125,7 +140,6 @@ typedef struct {
0, /* (bool) tspec: thread specific */\
0, /* (bool) tpref: thread preferred */\
0, /* (bool) ramv: realloc always moves */\
- 0, /* (bool) low_mem: HALFWORD only */\
64*1024, /* (bytes) sbct: sbc threshold */\
2*1024*2024, /* (amount) asbcst: abs sbc shrink threshold */\
20, /* (%) rsbcst: rel sbc shrink threshold */\
@@ -164,10 +178,10 @@ void * erts_alcu_realloc_mv_thr_pref(ErtsAlcType_t, void *, void *, Uint);
void erts_alcu_free_thr_pref(ErtsAlcType_t, void *, void *);
#endif
#endif
-Eterm erts_alcu_au_info_options(int *, void *, Uint **, Uint *);
-Eterm erts_alcu_info_options(Allctr_t *, int *, void *, Uint **, Uint *);
-Eterm erts_alcu_sz_info(Allctr_t *, int, int, int *, void *, Uint **, Uint *);
-Eterm erts_alcu_info(Allctr_t *, int, int, int *, void *, Uint **, Uint *);
+Eterm erts_alcu_au_info_options(fmtfn_t *, void *, Uint **, Uint *);
+Eterm erts_alcu_info_options(Allctr_t *, fmtfn_t *, void *, Uint **, Uint *);
+Eterm erts_alcu_sz_info(Allctr_t *, int, int, fmtfn_t *, void *, Uint **, Uint *);
+Eterm erts_alcu_info(Allctr_t *, int, int, fmtfn_t *, void *, Uint **, Uint *);
void erts_alcu_init(AlcUInit_t *);
void erts_alcu_current_size(Allctr_t *, AllctrSize_t *,
ErtsAlcUFixInfo_t *, int);
@@ -176,20 +190,44 @@ void erts_alcu_check_delayed_dealloc(Allctr_t *, int, int *, ErtsThrPrgrVal *
#endif
erts_aint32_t erts_alcu_fix_alloc_shrink(Allctr_t *, erts_aint32_t);
+#ifdef ARCH_32
+extern UWord erts_literal_vspace_map[];
+# define ERTS_VSPACE_WORD_BITS (sizeof(UWord)*8)
+#endif
+
+void* erts_alcu_mseg_alloc(Allctr_t*, Uint *size_p, Uint flags);
+void* erts_alcu_mseg_realloc(Allctr_t*, void *seg, Uint old_size, Uint *new_size_p);
+void erts_alcu_mseg_dealloc(Allctr_t*, void *seg, Uint size, Uint flags);
+
+#if HAVE_ERTS_MSEG
+# if defined(ARCH_32)
+void* erts_alcu_literal_32_mseg_alloc(Allctr_t*, Uint *size_p, Uint flags);
+void* erts_alcu_literal_32_mseg_realloc(Allctr_t*, void *seg, Uint old_size, Uint *new_size_p);
+void erts_alcu_literal_32_mseg_dealloc(Allctr_t*, void *seg, Uint size, Uint flags);
+
+# elif defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
+void* erts_alcu_mmapper_mseg_alloc(Allctr_t*, Uint *size_p, Uint flags);
+void* erts_alcu_mmapper_mseg_realloc(Allctr_t*, void *seg, Uint old_size, Uint *new_size_p);
+void erts_alcu_mmapper_mseg_dealloc(Allctr_t*, void *seg, Uint size, Uint flags);
+# endif
+#endif /* HAVE_ERTS_MSEG */
+
+void* erts_alcu_sys_alloc(Allctr_t*, Uint *size_p, int superalign);
+void* erts_alcu_sys_realloc(Allctr_t*, void *ptr, Uint *size_p, Uint old_size, int superalign);
+void erts_alcu_sys_dealloc(Allctr_t*, void *ptr, Uint size, int superalign);
+#ifdef ARCH_32
+void* erts_alcu_literal_32_sys_alloc(Allctr_t*, Uint *size_p, int superalign);
+void* erts_alcu_literal_32_sys_realloc(Allctr_t*, void *ptr, Uint *size_p, Uint old_size, int superalign);
+void erts_alcu_literal_32_sys_dealloc(Allctr_t*, void *ptr, Uint size, int superalign);
#endif
+#endif /* !ERL_ALLOC_UTIL__ */
+
#if defined(GET_ERL_ALLOC_UTIL_IMPL) && !defined(ERL_ALLOC_UTIL_IMPL__)
#define ERL_ALLOC_UTIL_IMPL__
#define ERTS_ALCU_FLG_FAIL_REALLOC_MOVE (((Uint32) 1) << 0)
-#ifdef USE_THREADS
-#define ERL_THREADS_EMU_INTERNAL__
-#include "erl_threads.h"
-#endif
-
-#include "erl_mseg.h"
-
#undef ERTS_ALLOC_UTIL_HARD_DEBUG
#ifdef DEBUG
# if 0
@@ -223,7 +261,7 @@ erts_aint32_t erts_alcu_fix_alloc_shrink(Allctr_t *, erts_aint32_t);
#if ERTS_HAVE_MSEG_SUPER_ALIGNED \
|| (!HAVE_ERTS_MSEG && ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC)
-# ifndef MSEG_ALIGN_BITS
+# ifdef MSEG_ALIGN_BITS
# define ERTS_SUPER_ALIGN_BITS MSEG_ALIGN_BITS
# else
# define ERTS_SUPER_ALIGN_BITS 18
@@ -381,6 +419,18 @@ typedef struct {
} blocks;
} CarriersStats_t;
+#ifdef USE_LTTNG_VM_TRACEPOINTS
+#define LTTNG_CARRIER_STATS_TO_LTTNG_STATS(CSP, LSP) \
+ do { \
+ (LSP)->carriers.size = (CSP)->curr.norm.mseg.size \
+ + (CSP)->curr.norm.sys_alloc.size; \
+ (LSP)->carriers.no = (CSP)->curr.norm.mseg.no \
+ + (CSP)->curr.norm.sys_alloc.no; \
+ (LSP)->blocks.size = (CSP)->blocks.curr.size; \
+ (LSP)->blocks.no = (CSP)->blocks.curr.no; \
+ } while (0)
+#endif
+
#ifdef ERTS_SMP
typedef union ErtsAllctrDDBlock_t_ ErtsAllctrDDBlock_t;
@@ -501,6 +551,8 @@ struct Allctr_t_ {
Uint min_mbc_size;
Uint min_mbc_first_free_size;
Uint min_block_size;
+ UWord crr_set_flgs;
+ UWord crr_clr_flgs;
/* Carriers */
CarrierList_t mbc_list;
@@ -534,7 +586,7 @@ struct Allctr_t_ {
Block_t *, Uint);
void (*link_free_block) (Allctr_t *, Block_t *);
void (*unlink_free_block) (Allctr_t *, Block_t *);
- Eterm (*info_options) (Allctr_t *, char *, int *,
+ Eterm (*info_options) (Allctr_t *, char *, fmtfn_t *,
void *, Uint **, Uint *);
Uint (*get_next_mbc_size) (Allctr_t *);
@@ -546,6 +598,16 @@ struct Allctr_t_ {
void (*remove_mbc) (Allctr_t *, Carrier_t *);
UWord (*largest_fblk_in_mbc) (Allctr_t *, Carrier_t *);
+#if HAVE_ERTS_MSEG
+ void* (*mseg_alloc)(Allctr_t*, Uint *size_p, Uint flags);
+ void* (*mseg_realloc)(Allctr_t*, void *seg, Uint old_size, Uint *new_size_p);
+ void (*mseg_dealloc)(Allctr_t*, void *seg, Uint size, Uint flags);
+ ErtsMemMapper *mseg_mmapper;
+#endif
+ void* (*sys_alloc)(Allctr_t *allctr, Uint *size_p, int superalign);
+ void* (*sys_realloc)(Allctr_t *allctr, void *ptr, Uint *size_p, Uint old_size, int superalign);
+ void (*sys_dealloc)(Allctr_t *allctr, void *ptr, Uint size, int superalign);
+
void (*init_atoms) (void);
#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
diff --git a/erts/emulator/beam/erl_ao_firstfit_alloc.c b/erts/emulator/beam/erl_ao_firstfit_alloc.c
index 5cd067ff54..05ba1f9891 100644
--- a/erts/emulator/beam/erl_ao_firstfit_alloc.c
+++ b/erts/emulator/beam/erl_ao_firstfit_alloc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2003-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -123,7 +123,7 @@ struct AOFF_Carrier_t_ {
AOFF_RBTree_t rbt_node; /* My node in the carrier tree */
AOFF_RBTree_t* root; /* Root of my block tree */
};
-#define RBT_NODE_TO_MBC(PTR) ((AOFF_Carrier_t*)((char*)(PTR) - offsetof(AOFF_Carrier_t, rbt_node)))
+#define RBT_NODE_TO_MBC(PTR) ErtsContainerStruct((PTR), AOFF_Carrier_t, rbt_node)
/*
To support carrier migration we keep two kinds of rb-trees:
@@ -224,7 +224,7 @@ static AOFF_RBTree_t* rbt_search(AOFF_RBTree_t* root, Uint size);
static int rbt_assert_is_member(AOFF_RBTree_t* root, AOFF_RBTree_t* node);
#endif
-static Eterm info_options(Allctr_t *, char *, int *, void *, Uint **, Uint *);
+static Eterm info_options(Allctr_t *, char *, fmtfn_t *, void *, Uint **, Uint *);
static void init_atoms(void);
@@ -1014,7 +1014,7 @@ add_2tup(Uint **hpp, Uint *szp, Eterm *lp, Eterm el1, Eterm el2)
static Eterm
info_options(Allctr_t *allctr,
char *prefix,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
diff --git a/erts/emulator/beam/erl_ao_firstfit_alloc.h b/erts/emulator/beam/erl_ao_firstfit_alloc.h
index 4200f20622..7349c6ab19 100644
--- a/erts/emulator/beam/erl_ao_firstfit_alloc.h
+++ b/erts/emulator/beam/erl_ao_firstfit_alloc.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2003-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erl_arith.c b/erts/emulator/beam/erl_arith.c
index b8c5ef9b09..861532f241 100644
--- a/erts/emulator/beam/erl_arith.c
+++ b/erts/emulator/beam/erl_arith.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2010. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -42,15 +42,8 @@
# define MAX(x, y) (((x) > (y)) ? (x) : (y))
#endif
-#if !HEAP_ON_C_STACK
-# define DECLARE_TMP(VariableName,N,P) \
- Eterm *VariableName = ((ERTS_PROC_GET_SCHDATA(P)->erl_arith_tmp_heap) + (2 * N))
-#else
-# define DECLARE_TMP(VariableName,N,P) \
- Eterm VariableName[2]
-#endif
-# define ARG_IS_NOT_TMP(Arg,Tmp) ((Arg) != make_big((Tmp)))
-
+#define DECLARE_TMP(VariableName,N,P) Eterm VariableName[2]
+#define ARG_IS_NOT_TMP(Arg,Tmp) ((Arg) != make_big((Tmp)))
static Eterm shift(Process* p, Eterm arg1, Eterm arg2, int right);
diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c
index f071898046..84254af0c2 100644
--- a/erts/emulator/beam/erl_async.c
+++ b/erts/emulator/beam/erl_async.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2000-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@
#include "erl_thr_queue.h"
#include "erl_async.h"
#include "dtrace-wrapper.h"
+#include "lttng-wrapper.h"
#define ERTS_MAX_ASYNC_READY_CALLS_IN_SEQ 20
@@ -167,7 +168,6 @@ async_ready_q(Uint sched_id)
#endif
-
void
erts_init_async(void)
{
@@ -282,6 +282,13 @@ static ERTS_INLINE void async_add(ErtsAsync *a, ErtsAsyncQ* q)
#endif
erts_thr_q_enqueue(&q->thr_q, a);
+#ifdef USE_LTTNG_VM_TRACEPOINTS
+ if (LTTNG_ENABLED(aio_pool_put)) {
+ lttng_decl_portbuf(port_str);
+ lttng_portid_to_str(a->port, port_str);
+ LTTNG2(aio_pool_put, port_str, -1);
+ }
+#endif
#ifdef USE_VM_PROBES
if (DTRACE_ENABLED(aio_pool_add)) {
DTRACE_CHARBUF(port_str, 16);
@@ -318,6 +325,14 @@ static ERTS_INLINE ErtsAsync *async_get(ErtsThrQ_t *q,
if (saved_fin_deq)
erts_thr_q_append_finalize_dequeue_data(&a->q.fin_deq, &fin_deq);
#endif
+#ifdef USE_LTTNG_VM_TRACEPOINTS
+ if (LTTNG_ENABLED(aio_pool_get)) {
+ lttng_decl_portbuf(port_str);
+ int length = erts_thr_q_length_dirty(q);
+ lttng_portid_to_str(a->port, port_str);
+ LTTNG2(aio_pool_get, port_str, length);
+ }
+#endif
#ifdef USE_VM_PROBES
if (DTRACE_ENABLED(aio_pool_get)) {
DTRACE_CHARBUF(port_str, 16);
@@ -401,13 +416,19 @@ static ERTS_INLINE void call_async_ready(ErtsAsync *a)
ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP);
#endif
if (!p) {
- if (a->async_free)
+ if (a->async_free) {
+ ERTS_MSACC_PUSH_AND_SET_STATE(ERTS_MSACC_STATE_PORT);
a->async_free(a->async_data);
+ ERTS_MSACC_POP_STATE();
+ }
}
else {
if (async_ready(p, a->async_data)) {
- if (a->async_free)
+ if (a->async_free) {
+ ERTS_MSACC_PUSH_AND_SET_STATE(ERTS_MSACC_STATE_PORT);
a->async_free(a->async_data);
+ ERTS_MSACC_POP_STATE();
+ }
}
#if ERTS_USE_ASYNC_READY_Q
erts_port_release(p);
@@ -461,6 +482,8 @@ static erts_tse_t *async_thread_init(ErtsAsyncQ *aq)
{
ErtsThrQInit_t qinit = ERTS_THR_Q_INIT_DEFAULT;
erts_tse_t *tse = erts_tse_fetch();
+ ERTS_DECLARE_DUMMY(Uint no);
+
#ifdef ERTS_SMP
ErtsThrPrgrCallbacks callbacks;
@@ -484,10 +507,12 @@ static erts_tse_t *async_thread_init(ErtsAsyncQ *aq)
/* Inform main thread that we are done initializing... */
erts_mtx_lock(&async->init.data.mtx);
- async->init.data.no_initialized++;
+ no = async->init.data.no_initialized++;
erts_cnd_signal(&async->init.data.cnd);
erts_mtx_unlock(&async->init.data.mtx);
+ erts_msacc_init_thread("async", no, 0);
+
return tse;
}
@@ -495,6 +520,7 @@ static void *async_main(void* arg)
{
ErtsAsyncQ *aq = (ErtsAsyncQ *) arg;
erts_tse_t *tse = async_thread_init(aq);
+ ERTS_MSACC_DECLARE_CACHE();
while (1) {
ErtsThrQPrepEnQ_t *prep_enq;
@@ -502,11 +528,14 @@ static void *async_main(void* arg)
if (is_nil(a->port))
break; /* Time to die */
+ ERTS_MSACC_UPDATE_CACHE();
+
#if ERTS_ASYNC_PRINT_JOB
erts_fprintf(stderr, "<- %ld\n", a->async_id);
#endif
-
+ ERTS_MSACC_SET_STATE_CACHED(ERTS_MSACC_STATE_PORT);
a->async_invoke(a->async_data);
+ ERTS_MSACC_SET_STATE_CACHED(ERTS_MSACC_STATE_OTHER);
async_reply(a, prep_enq);
}
@@ -629,10 +658,13 @@ long driver_async(ErlDrvPort ix, unsigned int* key,
unsigned int qix;
#if ERTS_USE_ASYNC_READY_Q
Uint sched_id;
+ ERTS_MSACC_PUSH_STATE();
sched_id = erts_get_scheduler_id();
if (!sched_id)
sched_id = 1;
+#else
+ ERTS_MSACC_PUSH_STATE();
#endif
prt = erts_drvport2port(ix);
@@ -685,12 +717,17 @@ long driver_async(ErlDrvPort ix, unsigned int* key,
return id;
}
#endif
-
+
+ ERTS_MSACC_SET_STATE_CACHED(ERTS_MSACC_STATE_PORT);
(*a->async_invoke)(a->async_data);
+ ERTS_MSACC_POP_STATE();
if (async_ready(prt, a->async_data)) {
- if (a->async_free != NULL)
+ if (a->async_free != NULL) {
+ ERTS_MSACC_SET_STATE_CACHED(ERTS_MSACC_STATE_PORT);
(*a->async_free)(a->async_data);
+ ERTS_MSACC_POP_STATE();
+ }
}
erts_free(ERTS_ALC_T_ASYNC, (void *) a);
diff --git a/erts/emulator/beam/erl_async.h b/erts/emulator/beam/erl_async.h
index 65538bcef0..473c7686e5 100644
--- a/erts/emulator/beam/erl_async.h
+++ b/erts/emulator/beam/erl_async.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2011. All Rights Reserved.
+ * Copyright Ericsson AB 2011-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erl_bestfit_alloc.c b/erts/emulator/beam/erl_bestfit_alloc.c
index f39a18ac88..6173c408e1 100644
--- a/erts/emulator/beam/erl_bestfit_alloc.c
+++ b/erts/emulator/beam/erl_bestfit_alloc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2003-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -104,7 +104,7 @@ static void bf_link_free_block (Allctr_t *, Block_t *);
static ERTS_INLINE void bf_unlink_free_block (Allctr_t *, Block_t *);
-static Eterm info_options (Allctr_t *, char *, int *,
+static Eterm info_options (Allctr_t *, char *, fmtfn_t *,
void *, Uint **, Uint *);
static void init_atoms (void);
@@ -921,7 +921,7 @@ add_2tup(Uint **hpp, Uint *szp, Eterm *lp, Eterm el1, Eterm el2)
static Eterm
info_options(Allctr_t *allctr,
char *prefix,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
diff --git a/erts/emulator/beam/erl_bestfit_alloc.h b/erts/emulator/beam/erl_bestfit_alloc.h
index b315518b88..3a5f51f5dc 100644
--- a/erts/emulator/beam/erl_bestfit_alloc.h
+++ b/erts/emulator/beam/erl_bestfit_alloc.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2003-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c
index 134aa2d396..6e10980b6b 100644
--- a/erts/emulator/beam/erl_bif_binary.c
+++ b/erts/emulator/beam/erl_bif_binary.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2010-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -55,10 +55,8 @@
/* Init and local variables */
-static Export binary_match_trap_export;
-static BIF_RETTYPE binary_match_trap(BIF_ALIST_3);
-static Export binary_matches_trap_export;
-static BIF_RETTYPE binary_matches_trap(BIF_ALIST_3);
+static Export binary_find_trap_export;
+static BIF_RETTYPE binary_find_trap(BIF_ALIST_3);
static Export binary_longest_prefix_trap_export;
static BIF_RETTYPE binary_longest_prefix_trap(BIF_ALIST_3);
static Export binary_longest_suffix_trap_export;
@@ -70,19 +68,15 @@ static BIF_RETTYPE binary_copy_trap(BIF_ALIST_2);
static Uint max_loop_limit;
static BIF_RETTYPE
-binary_match(Process *p, Eterm arg1, Eterm arg2, Eterm arg3);
+binary_match(Process *p, Eterm arg1, Eterm arg2, Eterm arg3, Uint flags);
static BIF_RETTYPE
-binary_matches(Process *p, Eterm arg1, Eterm arg2, Eterm arg3);
+binary_split(Process *p, Eterm arg1, Eterm arg2, Eterm arg3);
void erts_init_bif_binary(void)
{
- erts_init_trap_export(&binary_match_trap_export,
- am_erlang, am_binary_match_trap, 3,
- &binary_match_trap);
-
- erts_init_trap_export(&binary_matches_trap_export,
- am_erlang, am_binary_matches_trap, 3,
- &binary_matches_trap);
+ erts_init_trap_export(&binary_find_trap_export,
+ am_erlang, am_binary_find_trap, 3,
+ &binary_find_trap);
erts_init_trap_export(&binary_longest_prefix_trap_export,
am_erlang, am_binary_longest_prefix_trap, 3,
@@ -314,8 +308,8 @@ static BMData *create_bmdata(MyAllocator *my, byte *x, Uint len,
/*
* Aho Corasick - Build a Trie and fill in the failure functions
* when all strings are added.
- * The algorithm is nicely described by Dieter B�hler of University of
- * T�bingen:
+ * The algorithm is nicely described by Dieter Bühler of University of
+ * Tübingen:
* http://www-sr.informatik.uni-tuebingen.de/~buehler/AC/AC.html
*/
@@ -537,21 +531,23 @@ static void ac_init_find_all(ACFindAllState *state, ACTrie *act, Sint startpos,
state->out = NULL;
}
-static void ac_restore_find_all(ACFindAllState *state, char *buff)
+static void ac_restore_find_all(ACFindAllState *state,
+ const ACFindAllState *src)
{
- memcpy(state,buff,sizeof(ACFindAllState));
+ memcpy(state, src, sizeof(ACFindAllState));
if (state->allocated > 0) {
state->out = erts_alloc(ERTS_ALC_T_TMP, sizeof(FindallData) * (state->allocated));
- memcpy(state->out,buff+sizeof(ACFindAllState),sizeof(FindallData)*state->m);
+ memcpy(state->out, src+1, sizeof(FindallData)*state->m);
} else {
state->out = NULL;
}
}
-static void ac_serialize_find_all(ACFindAllState *state, char *buff)
+static void ac_serialize_find_all(const ACFindAllState *state,
+ ACFindAllState *dst)
{
- memcpy(buff,state,sizeof(ACFindAllState));
- memcpy(buff+sizeof(ACFindAllState),state->out,sizeof(FindallData)*state->m);
+ memcpy(dst, state, sizeof(ACFindAllState));
+ memcpy(dst+1, state->out, sizeof(FindallData)*state->m);
}
static void ac_clean_find_all(ACFindAllState *state)
@@ -565,9 +561,6 @@ static void ac_clean_find_all(ACFindAllState *state)
#endif
}
-#define SIZEOF_AC_SERIALIZED_FIND_ALL_STATE(S) \
- (sizeof(ACFindAllState)+(sizeof(FindallData)*(S).m))
-
/*
* Differs to the find_first function in that it stores all matches and the values
* arte returned only in the state.
@@ -814,24 +807,24 @@ static void bm_init_find_all(BMFindAllState *state, Sint startpos, Uint len)
state->out = NULL;
}
-static void bm_restore_find_all(BMFindAllState *state, char *buff)
+static void bm_restore_find_all(BMFindAllState *state,
+ const BMFindAllState *src)
{
- memcpy(state,buff,sizeof(BMFindAllState));
+ memcpy(state, src, sizeof(BMFindAllState));
if (state->allocated > 0) {
state->out = erts_alloc(ERTS_ALC_T_TMP, sizeof(FindallData) *
(state->allocated));
- memcpy(state->out,buff+sizeof(BMFindAllState),
- sizeof(FindallData)*state->m);
+ memcpy(state->out, src+1, sizeof(FindallData)*state->m);
} else {
state->out = NULL;
}
}
-static void bm_serialize_find_all(BMFindAllState *state, char *buff)
+static void bm_serialize_find_all(const BMFindAllState *state,
+ BMFindAllState *dst)
{
- memcpy(buff,state,sizeof(BMFindAllState));
- memcpy(buff+sizeof(BMFindAllState),state->out,
- sizeof(FindallData)*state->m);
+ memcpy(dst, state, sizeof(BMFindAllState));
+ memcpy(dst+1, state->out, sizeof(FindallData)*state->m);
}
static void bm_clean_find_all(BMFindAllState *state)
@@ -845,9 +838,6 @@ static void bm_clean_find_all(BMFindAllState *state)
#endif
}
-#define SIZEOF_BM_SERIALIZED_FIND_ALL_STATE(S) \
- (sizeof(BMFindAllState)+(sizeof(FindallData)*(S).m))
-
/*
* Differs to the find_first function in that it stores all matches and the
* values are returned only in the state.
@@ -1030,283 +1020,142 @@ BIF_RETTYPE binary_compile_pattern_1(BIF_ALIST_1)
#define DO_BIN_MATCH_BADARG -1
#define DO_BIN_MATCH_RESTART -2
-static int do_binary_match(Process *p, Eterm subject, Uint hsstart, Uint hsend,
- Eterm type, Binary *bin, Eterm state_term,
- Eterm *res_term)
-{
- byte *bytes;
- Uint bitoffs, bitsize;
- byte *temp_alloc = NULL;
-
- ERTS_GET_BINARY_BYTES(subject, bytes, bitoffs, bitsize);
- if (bitsize != 0) {
- goto badarg;
- }
- if (bitoffs != 0) {
- bytes = erts_get_aligned_binary_bytes(subject, &temp_alloc);
- }
- if (state_term != NIL) {
- Eterm *ptr = big_val(state_term);
- type = ptr[1];
- }
-
- if (type == am_bm) {
- BMData *bm;
- Sint pos;
- Eterm ret;
- Eterm *hp;
- BMFindFirstState state;
- Uint reds = get_reds(p, BM_LOOP_FACTOR);
- Uint save_reds = reds;
-
- bm = (BMData *) ERTS_MAGIC_BIN_DATA(bin);
-#ifdef HARDDEBUG
- dump_bm_data(bm);
-#endif
- if (state_term == NIL) {
- bm_init_find_first_match(&state, hsstart, hsend);
- } else {
- Eterm *ptr = big_val(state_term);
- memcpy(&state,ptr+2,sizeof(state));
- }
-#ifdef HARDDEBUG
- erts_printf("(bm) state->pos = %ld, state->len = %lu\n",state.pos,
- state.len);
-#endif
- pos = bm_find_first_match(&state, bm, bytes, &reds);
- if (pos == BM_NOT_FOUND) {
- ret = am_nomatch;
- } else if (pos == BM_RESTART) {
- int x = (sizeof(BMFindFirstState) / sizeof(Eterm)) +
- !!(sizeof(BMFindFirstState) % sizeof(Eterm));
-#ifdef HARDDEBUG
- erts_printf("Trap bm!\n");
-#endif
- hp = HAlloc(p,x+2);
- hp[0] = make_pos_bignum_header(x+1);
- hp[1] = type;
- memcpy(hp+2,&state,sizeof(state));
- *res_term = make_big(hp);
- erts_free_aligned_binary_bytes(temp_alloc);
- return DO_BIN_MATCH_RESTART;
- } else {
- Eterm erlen = erts_make_integer((Uint) bm->len, p);
- ret = erts_make_integer(pos,p);
- hp = HAlloc(p,3);
- ret = TUPLE2(hp, ret, erlen);
- }
- erts_free_aligned_binary_bytes(temp_alloc);
- BUMP_REDS(p, (save_reds - reds) / BM_LOOP_FACTOR);
- *res_term = ret;
- return DO_BIN_MATCH_OK;
- } else if (type == am_ac) {
- ACTrie *act;
- Uint pos, rlen;
- int acr;
- ACFindFirstState state;
- Eterm ret;
- Eterm *hp;
- Uint reds = get_reds(p, AC_LOOP_FACTOR);
- Uint save_reds = reds;
+#define BINARY_FIND_ALL 0x01
+#define BINARY_SPLIT_TRIM 0x02
+#define BINARY_SPLIT_TRIM_ALL 0x04
- act = (ACTrie *) ERTS_MAGIC_BIN_DATA(bin);
-#ifdef HARDDEBUG
- dump_ac_trie(act);
-#endif
- if (state_term == NIL) {
- ac_init_find_first_match(&state, act, hsstart, hsend);
- } else {
- Eterm *ptr = big_val(state_term);
- memcpy(&state,ptr+2,sizeof(state));
- }
- acr = ac_find_first_match(&state, bytes, &pos, &rlen, &reds);
- if (acr == AC_NOT_FOUND) {
- ret = am_nomatch;
- } else if (acr == AC_RESTART) {
- int x = (sizeof(state) / sizeof(Eterm)) +
- !!(sizeof(ACFindFirstState) % sizeof(Eterm));
-#ifdef HARDDEBUG
- erts_printf("Trap ac!\n");
-#endif
- hp = HAlloc(p,x+2);
- hp[0] = make_pos_bignum_header(x+1);
- hp[1] = type;
- memcpy(hp+2,&state,sizeof(state));
- *res_term = make_big(hp);
- erts_free_aligned_binary_bytes(temp_alloc);
- return DO_BIN_MATCH_RESTART;
- } else {
- Eterm epos = erts_make_integer(pos,p);
- Eterm erlen = erts_make_integer(rlen,p);
- hp = HAlloc(p,3);
- ret = TUPLE2(hp, epos, erlen);
- }
- erts_free_aligned_binary_bytes(temp_alloc);
- BUMP_REDS(p, (save_reds - reds) / AC_LOOP_FACTOR);
- *res_term = ret;
- return DO_BIN_MATCH_OK;
- }
- badarg:
- return DO_BIN_MATCH_BADARG;
-}
+typedef struct BinaryFindState {
+ Eterm type;
+ Uint flags;
+ Uint hsstart;
+ Uint hsend;
+ Eterm (*not_found_result) (Process *, Eterm, struct BinaryFindState *);
+ Eterm (*single_result) (Process *, Eterm, struct BinaryFindState *, Sint, Sint);
+ Eterm (*global_result) (Process *, Eterm, struct BinaryFindState *, FindallData *, Uint);
+} BinaryFindState;
+
+typedef struct BinaryFindState_bignum {
+ Eterm bignum_hdr;
+ BinaryFindState bfs;
+ union {
+ BMFindFirstState bmffs;
+ BMFindAllState bmfas;
+ ACFindFirstState acffs;
+ ACFindAllState acfas;
+ } data;
+} BinaryFindState_bignum;
+
+#define SIZEOF_BINARY_FIND_STATE(S) \
+ (sizeof(BinaryFindState)+sizeof(S))
+
+#define SIZEOF_BINARY_FIND_ALL_STATE(S) \
+ (sizeof(BinaryFindState)+sizeof(S)+(sizeof(FindallData)*(S).m))
+
+static Eterm do_match_not_found_result(Process *p, Eterm subject, BinaryFindState *bfs);
+static Eterm do_match_single_result(Process *p, Eterm subject, BinaryFindState *bfs,
+ Sint pos, Sint len);
+static Eterm do_match_global_result(Process *p, Eterm subject, BinaryFindState *bfs,
+ FindallData *fad, Uint fad_sz);
+static Eterm do_split_not_found_result(Process *p, Eterm subject, BinaryFindState *bfs);
+static Eterm do_split_single_result(Process *p, Eterm subject, BinaryFindState *bfs,
+ Sint pos, Sint len);
+static Eterm do_split_global_result(Process *p, Eterm subject, BinaryFindState *bfs,
+ FindallData *fad, Uint fad_sz);
-static int do_binary_matches(Process *p, Eterm subject, Uint hsstart,
- Uint hsend, Eterm type, Binary *bin,
- Eterm state_term, Eterm *res_term)
+static int parse_match_opts_list(Eterm l, Eterm bin, Uint *posp, Uint *endp)
{
- byte *bytes;
- Uint bitoffs, bitsize;
- byte *temp_alloc = NULL;
-
- ERTS_GET_BINARY_BYTES(subject, bytes, bitoffs, bitsize);
- if (bitsize != 0) {
- goto badarg;
- }
- if (bitoffs != 0) {
- bytes = erts_get_aligned_binary_bytes(subject, &temp_alloc);
- }
- if (state_term != NIL) {
- Eterm *ptr = big_val(state_term);
- type = ptr[1];
- }
-
- if (type == am_bm) {
- BMData *bm;
- Sint pos;
- Eterm ret,tpl;
- Eterm *hp;
- BMFindAllState state;
- Uint reds = get_reds(p, BM_LOOP_FACTOR);
- Uint save_reds = reds;
-
- bm = (BMData *) ERTS_MAGIC_BIN_DATA(bin);
-#ifdef HARDDEBUG
- dump_bm_data(bm);
-#endif
- if (state_term == NIL) {
- bm_init_find_all(&state, hsstart, hsend);
- } else {
- Eterm *ptr = big_val(state_term);
- bm_restore_find_all(&state,(char *) (ptr+2));
- }
-
- pos = bm_find_all_non_overlapping(&state, bm, bytes, &reds);
- if (pos == BM_NOT_FOUND) {
- ret = NIL;
- } else if (pos == BM_RESTART) {
- int x =
- (SIZEOF_BM_SERIALIZED_FIND_ALL_STATE(state) / sizeof(Eterm)) +
- !!(SIZEOF_BM_SERIALIZED_FIND_ALL_STATE(state) % sizeof(Eterm));
-#ifdef HARDDEBUG
- erts_printf("Trap bm!\n");
-#endif
- hp = HAlloc(p,x+2);
- hp[0] = make_pos_bignum_header(x+1);
- hp[1] = type;
- bm_serialize_find_all(&state, (char *) (hp+2));
- *res_term = make_big(hp);
- erts_free_aligned_binary_bytes(temp_alloc);
- bm_clean_find_all(&state);
- return DO_BIN_MATCH_RESTART;
- } else {
- FindallData *fad = state.out;
- int i;
- for (i = 0; i < state.m; ++i) {
- fad[i].epos = erts_make_integer(fad[i].pos,p);
- fad[i].elen = erts_make_integer(fad[i].len,p);
+ Eterm *tp;
+ Uint pos;
+ Sint len;
+ if (l == THE_NON_VALUE || l == NIL) {
+ /* Invalid term or NIL, we're called from binary_match(es)_2 or
+ have no options*/
+ *posp = 0;
+ *endp = binary_size(bin);
+ return 0;
+ } else if (is_list(l)) {
+ while(is_list(l)) {
+ Eterm t = CAR(list_val(l));
+ Uint orig_size;
+ if (!is_tuple(t)) {
+ goto badarg;
}
- hp = HAlloc(p,state.m * (3 + 2));
- ret = NIL;
- for (i = state.m - 1; i >= 0; --i) {
- tpl = TUPLE2(hp, fad[i].epos, fad[i].elen);
- hp +=3;
- ret = CONS(hp,tpl,ret);
- hp += 2;
+ tp = tuple_val(t);
+ if (arityval(*tp) != 2) {
+ goto badarg;
}
- }
- erts_free_aligned_binary_bytes(temp_alloc);
- bm_clean_find_all(&state);
- BUMP_REDS(p, (save_reds - reds) / BM_LOOP_FACTOR);
- *res_term = ret;
- return DO_BIN_MATCH_OK;
- } else if (type == am_ac) {
- ACTrie *act;
- int acr;
- ACFindAllState state;
- Eterm ret,tpl;
- Eterm *hp;
- Uint reds = get_reds(p, AC_LOOP_FACTOR);
- Uint save_reds = reds;
-
- act = (ACTrie *) ERTS_MAGIC_BIN_DATA(bin);
-#ifdef HARDDEBUG
- dump_ac_trie(act);
-#endif
- if (state_term == NIL) {
- ac_init_find_all(&state, act, hsstart, hsend);
- } else {
- Eterm *ptr = big_val(state_term);
- ac_restore_find_all(&state,(char *) (ptr+2));
- }
- acr = ac_find_all_non_overlapping(&state, bytes, &reds);
- if (acr == AC_NOT_FOUND) {
- ret = NIL;
- } else if (acr == AC_RESTART) {
- int x =
- (SIZEOF_AC_SERIALIZED_FIND_ALL_STATE(state) / sizeof(Eterm)) +
- !!(SIZEOF_AC_SERIALIZED_FIND_ALL_STATE(state) % sizeof(Eterm));
-#ifdef HARDDEBUG
- erts_printf("Trap ac!\n");
-#endif
- hp = HAlloc(p,x+2);
- hp[0] = make_pos_bignum_header(x+1);
- hp[1] = type;
- ac_serialize_find_all(&state, (char *) (hp+2));
- *res_term = make_big(hp);
- erts_free_aligned_binary_bytes(temp_alloc);
- ac_clean_find_all(&state);
- return DO_BIN_MATCH_RESTART;
- } else {
- FindallData *fad = state.out;
- int i;
- for (i = 0; i < state.m; ++i) {
- fad[i].epos = erts_make_integer(fad[i].pos,p);
- fad[i].elen = erts_make_integer(fad[i].len,p);
+ if (tp[1] != am_scope || is_not_tuple(tp[2])) {
+ goto badarg;
}
- hp = HAlloc(p,state.m * (3 + 2));
- ret = NIL;
- for (i = state.m - 1; i >= 0; --i) {
- tpl = TUPLE2(hp, fad[i].epos, fad[i].elen);
- hp +=3;
- ret = CONS(hp,tpl,ret);
- hp += 2;
+ tp = tuple_val(tp[2]);
+ if (arityval(*tp) != 2) {
+ goto badarg;
+ }
+ if (!term_to_Uint(tp[1], &pos)) {
+ goto badarg;
}
+ if (!term_to_Sint(tp[2], &len)) {
+ goto badarg;
+ }
+ if (len < 0) {
+ Uint lentmp = -(Uint)len;
+ /* overflow */
+ if ((Sint)lentmp < 0) {
+ goto badarg;
+ }
+ len = lentmp;
+ pos -= len;
+ }
+ /* overflow */
+ if ((pos + len) < pos || (len > 0 && (pos + len) == pos)) {
+ goto badarg;
+ }
+ *endp = len + pos;
+ *posp = pos;
+ if ((orig_size = binary_size(bin)) < pos ||
+ orig_size < (*endp)) {
+ goto badarg;
+ }
+ l = CDR(list_val(l));
}
- erts_free_aligned_binary_bytes(temp_alloc);
- ac_clean_find_all(&state);
- BUMP_REDS(p, (save_reds - reds) / AC_LOOP_FACTOR);
- *res_term = ret;
- return DO_BIN_MATCH_OK;
+ return 0;
+ } else {
+ badarg:
+ return 1;
}
- badarg:
- return DO_BIN_MATCH_BADARG;
}
-static int parse_match_opts_list(Eterm l, Eterm bin, Uint *posp, Uint *endp)
+static int parse_split_opts_list(Eterm l, Eterm bin, Uint *posp, Uint *endp, Uint *optp)
{
Eterm *tp;
Uint pos;
Sint len;
- if (l == ((Eterm) 0) || l == NIL) {
- /* Invalid term or NIL, we're called from binary_match(es)_2 or
- have no options*/
- *posp = 0;
- *endp = binary_size(bin);
+ *optp = 0;
+ *posp = 0;
+ *endp = binary_size(bin);
+ if (l == THE_NON_VALUE || l == NIL) {
return 0;
} else if (is_list(l)) {
while(is_list(l)) {
Eterm t = CAR(list_val(l));
Uint orig_size;
+ if (is_atom(t)) {
+ if (t == am_global) {
+ *optp |= BINARY_FIND_ALL;
+ l = CDR(list_val(l));
+ continue;
+ }
+ if (t == am_trim) {
+ *optp |= BINARY_SPLIT_TRIM;
+ l = CDR(list_val(l));
+ continue;
+ }
+ if (t == am_trim_all) {
+ *optp |= BINARY_SPLIT_TRIM_ALL;
+ l = CDR(list_val(l));
+ continue;
+ }
+ }
if (!is_tuple(t)) {
goto badarg;
}
@@ -1355,48 +1204,207 @@ static int parse_match_opts_list(Eterm l, Eterm bin, Uint *posp, Uint *endp)
}
}
-static BIF_RETTYPE binary_match_trap(BIF_ALIST_3)
+static int do_binary_find(Process *p, Eterm subject, BinaryFindState *bfs, Binary *bin,
+ Eterm state_term, Eterm *res_term)
{
- int runres;
- Eterm result;
- Binary *bin = ((ProcBin *) binary_val(BIF_ARG_3))->val;
- runres = do_binary_match(BIF_P,BIF_ARG_1,0,0,NIL,bin,BIF_ARG_2,&result);
- if (runres == DO_BIN_MATCH_OK) {
- BIF_RET(result);
- } else {
- BUMP_ALL_REDS(BIF_P);
- BIF_TRAP3(&binary_match_trap_export, BIF_P, BIF_ARG_1, result,
- BIF_ARG_3);
+ byte *bytes;
+ Uint bitoffs, bitsize;
+ byte *temp_alloc = NULL;
+ BinaryFindState_bignum *state_ptr = NULL;
+
+ ERTS_GET_BINARY_BYTES(subject, bytes, bitoffs, bitsize);
+ if (bitsize != 0) {
+ goto badarg;
+ }
+ if (bitoffs != 0) {
+ bytes = erts_get_aligned_binary_bytes(subject, &temp_alloc);
+ }
+ if (state_term != NIL) {
+ state_ptr = (BinaryFindState_bignum *)(big_val(state_term));
+ bfs = &(state_ptr->bfs);
}
-}
-static BIF_RETTYPE binary_matches_trap(BIF_ALIST_3)
-{
- int runres;
- Eterm result;
- Binary *bin = ((ProcBin *) binary_val(BIF_ARG_3))->val;
- runres = do_binary_matches(BIF_P,BIF_ARG_1,0,0,NIL,bin,BIF_ARG_2,&result);
- if (runres == DO_BIN_MATCH_OK) {
- BIF_RET(result);
+ if (bfs->flags & BINARY_FIND_ALL) {
+ if (bfs->type == am_bm) {
+ BMData *bm;
+ Sint pos;
+ BMFindAllState state;
+ Uint reds = get_reds(p, BM_LOOP_FACTOR);
+ Uint save_reds = reds;
+
+ bm = (BMData *) ERTS_MAGIC_BIN_DATA(bin);
+#ifdef HARDDEBUG
+ dump_bm_data(bm);
+#endif
+ if (state_term == NIL) {
+ bm_init_find_all(&state, bfs->hsstart, bfs->hsend);
+ } else {
+ bm_restore_find_all(&state, &(state_ptr->data.bmfas));
+ }
+
+ pos = bm_find_all_non_overlapping(&state, bm, bytes, &reds);
+ if (pos == BM_NOT_FOUND) {
+ *res_term = bfs->not_found_result(p, subject, bfs);
+ } else if (pos == BM_RESTART) {
+ int x =
+ (SIZEOF_BINARY_FIND_ALL_STATE(state) / sizeof(Eterm)) +
+ !!(SIZEOF_BINARY_FIND_ALL_STATE(state) % sizeof(Eterm));
+#ifdef HARDDEBUG
+ erts_printf("Trap bm!\n");
+#endif
+ state_ptr = (BinaryFindState_bignum*) HAlloc(p, x+1);
+ state_ptr->bignum_hdr = make_pos_bignum_header(x);
+ memcpy(&state_ptr->bfs, bfs, sizeof(BinaryFindState));
+ bm_serialize_find_all(&state, &state_ptr->data.bmfas);
+ *res_term = make_big(&state_ptr->bignum_hdr);
+ erts_free_aligned_binary_bytes(temp_alloc);
+ bm_clean_find_all(&state);
+ return DO_BIN_MATCH_RESTART;
+ } else {
+ *res_term = bfs->global_result(p, subject, bfs, state.out, state.m);
+ }
+ erts_free_aligned_binary_bytes(temp_alloc);
+ bm_clean_find_all(&state);
+ BUMP_REDS(p, (save_reds - reds) / BM_LOOP_FACTOR);
+ return DO_BIN_MATCH_OK;
+ } else if (bfs->type == am_ac) {
+ ACTrie *act;
+ int acr;
+ ACFindAllState state;
+ Uint reds = get_reds(p, AC_LOOP_FACTOR);
+ Uint save_reds = reds;
+
+ act = (ACTrie *) ERTS_MAGIC_BIN_DATA(bin);
+#ifdef HARDDEBUG
+ dump_ac_trie(act);
+#endif
+ if (state_term == NIL) {
+ ac_init_find_all(&state, act, bfs->hsstart, bfs->hsend);
+ } else {
+ ac_restore_find_all(&state, &(state_ptr->data.acfas));
+ }
+ acr = ac_find_all_non_overlapping(&state, bytes, &reds);
+ if (acr == AC_NOT_FOUND) {
+ *res_term = bfs->not_found_result(p, subject, bfs);
+ } else if (acr == AC_RESTART) {
+ int x =
+ (SIZEOF_BINARY_FIND_ALL_STATE(state) / sizeof(Eterm)) +
+ !!(SIZEOF_BINARY_FIND_ALL_STATE(state) % sizeof(Eterm));
+#ifdef HARDDEBUG
+ erts_printf("Trap ac!\n");
+#endif
+ state_ptr = (BinaryFindState_bignum*) HAlloc(p, x+1);
+ state_ptr->bignum_hdr = make_pos_bignum_header(x);
+ memcpy(&state_ptr->bfs, bfs, sizeof(BinaryFindState));
+ ac_serialize_find_all(&state, &state_ptr->data.acfas);
+ *res_term = make_big(&state_ptr->bignum_hdr);
+ erts_free_aligned_binary_bytes(temp_alloc);
+ ac_clean_find_all(&state);
+ return DO_BIN_MATCH_RESTART;
+ } else {
+ *res_term = bfs->global_result(p, subject, bfs, state.out, state.m);
+ }
+ erts_free_aligned_binary_bytes(temp_alloc);
+ ac_clean_find_all(&state);
+ BUMP_REDS(p, (save_reds - reds) / AC_LOOP_FACTOR);
+ return DO_BIN_MATCH_OK;
+ }
} else {
- BUMP_ALL_REDS(BIF_P);
- BIF_TRAP3(&binary_matches_trap_export, BIF_P, BIF_ARG_1, result,
- BIF_ARG_3);
- }
-}
+ if (bfs->type == am_bm) {
+ BMData *bm;
+ Sint pos;
+ BMFindFirstState state;
+ Uint reds = get_reds(p, BM_LOOP_FACTOR);
+ Uint save_reds = reds;
+
+ bm = (BMData *) ERTS_MAGIC_BIN_DATA(bin);
+#ifdef HARDDEBUG
+ dump_bm_data(bm);
+#endif
+ if (state_term == NIL) {
+ bm_init_find_first_match(&state, bfs->hsstart, bfs->hsend);
+ } else {
+ memcpy(&state, &state_ptr->data.bmffs, sizeof(BMFindFirstState));
+ }
-BIF_RETTYPE binary_match_3(BIF_ALIST_3)
-{
- return binary_match(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
+#ifdef HARDDEBUG
+ erts_printf("(bm) state->pos = %ld, state->len = %lu\n",state.pos,
+ state.len);
+#endif
+ pos = bm_find_first_match(&state, bm, bytes, &reds);
+ if (pos == BM_NOT_FOUND) {
+ *res_term = bfs->not_found_result(p, subject, bfs);
+ } else if (pos == BM_RESTART) {
+ int x =
+ (SIZEOF_BINARY_FIND_STATE(state) / sizeof(Eterm)) +
+ !!(SIZEOF_BINARY_FIND_STATE(state) % sizeof(Eterm));
+#ifdef HARDDEBUG
+ erts_printf("Trap bm!\n");
+#endif
+ state_ptr = (BinaryFindState_bignum*) HAlloc(p, x+1);
+ state_ptr->bignum_hdr = make_pos_bignum_header(x);
+ memcpy(&state_ptr->bfs, bfs, sizeof(BinaryFindState));
+ memcpy(&state_ptr->data.acffs, &state, sizeof(BMFindFirstState));
+ *res_term = make_big(&state_ptr->bignum_hdr);
+ erts_free_aligned_binary_bytes(temp_alloc);
+ return DO_BIN_MATCH_RESTART;
+ } else {
+ *res_term = bfs->single_result(p, subject, bfs, pos, bm->len);
+ }
+ erts_free_aligned_binary_bytes(temp_alloc);
+ BUMP_REDS(p, (save_reds - reds) / BM_LOOP_FACTOR);
+ return DO_BIN_MATCH_OK;
+ } else if (bfs->type == am_ac) {
+ ACTrie *act;
+ Uint pos, rlen;
+ int acr;
+ ACFindFirstState state;
+ Uint reds = get_reds(p, AC_LOOP_FACTOR);
+ Uint save_reds = reds;
+
+ act = (ACTrie *) ERTS_MAGIC_BIN_DATA(bin);
+#ifdef HARDDEBUG
+ dump_ac_trie(act);
+#endif
+ if (state_term == NIL) {
+ ac_init_find_first_match(&state, act, bfs->hsstart, bfs->hsend);
+ } else {
+ memcpy(&state, &state_ptr->data.acffs, sizeof(ACFindFirstState));
+ }
+ acr = ac_find_first_match(&state, bytes, &pos, &rlen, &reds);
+ if (acr == AC_NOT_FOUND) {
+ *res_term = bfs->not_found_result(p, subject, bfs);
+ } else if (acr == AC_RESTART) {
+ int x =
+ (SIZEOF_BINARY_FIND_STATE(state) / sizeof(Eterm)) +
+ !!(SIZEOF_BINARY_FIND_STATE(state) % sizeof(Eterm));
+#ifdef HARDDEBUG
+ erts_printf("Trap ac!\n");
+#endif
+ state_ptr = (BinaryFindState_bignum*) HAlloc(p, x+1);
+ state_ptr->bignum_hdr = make_pos_bignum_header(x);
+ memcpy(&state_ptr->bfs, bfs, sizeof(BinaryFindState));
+ memcpy(&state_ptr->data.acffs, &state, sizeof(ACFindFirstState));
+ *res_term = make_big(&state_ptr->bignum_hdr);
+ erts_free_aligned_binary_bytes(temp_alloc);
+ return DO_BIN_MATCH_RESTART;
+ } else {
+ *res_term = bfs->single_result(p, subject, bfs, pos, rlen);
+ }
+ erts_free_aligned_binary_bytes(temp_alloc);
+ BUMP_REDS(p, (save_reds - reds) / AC_LOOP_FACTOR);
+ return DO_BIN_MATCH_OK;
+ }
+ }
+ badarg:
+ return DO_BIN_MATCH_BADARG;
}
static BIF_RETTYPE
-binary_match(Process *p, Eterm arg1, Eterm arg2, Eterm arg3)
+binary_match(Process *p, Eterm arg1, Eterm arg2, Eterm arg3, Uint flags)
{
- Uint hsstart;
- Uint hsend;
+ BinaryFindState bfs;
Eterm *tp;
- Eterm type;
Binary *bin;
Eterm bin_term = NIL;
int runres;
@@ -1405,11 +1413,12 @@ binary_match(Process *p, Eterm arg1, Eterm arg2, Eterm arg3)
if (is_not_binary(arg1)) {
goto badarg;
}
- if (parse_match_opts_list(arg3,arg1,&hsstart,&hsend)) {
+ bfs.flags = flags;
+ if (parse_match_opts_list(arg3, arg1, &(bfs.hsstart), &(bfs.hsend))) {
goto badarg;
}
- if (hsend == 0) {
- BIF_RET(am_nomatch);
+ if (bfs.hsend == 0) {
+ BIF_RET(do_match_not_found_result(p, arg1, &bfs));
}
if (is_tuple(arg2)) {
tp = tuple_val(arg2);
@@ -1420,21 +1429,24 @@ binary_match(Process *p, Eterm arg1, Eterm arg2, Eterm arg3)
!ERTS_TERM_IS_MAGIC_BINARY(tp[2])) {
goto badarg;
}
- type = tp[1];
+ bfs.type = tp[1];
bin = ((ProcBin *) binary_val(tp[2]))->val;
- if (type == am_bm &&
+ if (bfs.type == am_bm &&
ERTS_MAGIC_BIN_DESTRUCTOR(bin) != cleanup_my_data_bm) {
goto badarg;
}
- if (type == am_ac &&
+ if (bfs.type == am_ac &&
ERTS_MAGIC_BIN_DESTRUCTOR(bin) != cleanup_my_data_ac) {
goto badarg;
}
bin_term = tp[2];
- } else if (do_binary_match_compile(arg2,&type,&bin)) {
+ } else if (do_binary_match_compile(arg2, &(bfs.type), &bin)) {
goto badarg;
}
- runres = do_binary_match(p,arg1,hsstart,hsend,type,bin,NIL,&result);
+ bfs.not_found_result = &do_match_not_found_result;
+ bfs.single_result = &do_match_single_result;
+ bfs.global_result = &do_match_global_result;
+ runres = do_binary_find(p, arg1, &bfs, bin, NIL, &result);
if (runres == DO_BIN_MATCH_RESTART && bin_term == NIL) {
Eterm *hp = HAlloc(p, PROC_BIN_SIZE);
bin_term = erts_mk_magic_binary_term(&hp, &MSO(p), bin);
@@ -1446,7 +1458,7 @@ binary_match(Process *p, Eterm arg1, Eterm arg2, Eterm arg3)
BIF_RET(result);
case DO_BIN_MATCH_RESTART:
BUMP_ALL_REDS(p);
- BIF_TRAP3(&binary_match_trap_export, p, arg1, result, bin_term);
+ BIF_TRAP3(&binary_find_trap_export, p, arg1, result, bin_term);
default:
goto badarg;
}
@@ -1454,17 +1466,31 @@ binary_match(Process *p, Eterm arg1, Eterm arg2, Eterm arg3)
BIF_ERROR(p,BADARG);
}
+BIF_RETTYPE binary_match_2(BIF_ALIST_2)
+{
+ return binary_match(BIF_P, BIF_ARG_1, BIF_ARG_2, THE_NON_VALUE, 0);
+}
+
+BIF_RETTYPE binary_match_3(BIF_ALIST_3)
+{
+ return binary_match(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, 0);
+}
+
+BIF_RETTYPE binary_matches_2(BIF_ALIST_2)
+{
+ return binary_match(BIF_P, BIF_ARG_1, BIF_ARG_2, THE_NON_VALUE, BINARY_FIND_ALL);
+}
+
BIF_RETTYPE binary_matches_3(BIF_ALIST_3)
{
- return binary_matches(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
+ return binary_match(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, BINARY_FIND_ALL);
}
static BIF_RETTYPE
-binary_matches(Process *p, Eterm arg1, Eterm arg2, Eterm arg3)
+binary_split(Process *p, Eterm arg1, Eterm arg2, Eterm arg3)
{
- Uint hsstart, hsend;
+ BinaryFindState bfs;
Eterm *tp;
- Eterm type;
Binary *bin;
Eterm bin_term = NIL;
int runres;
@@ -1473,11 +1499,12 @@ binary_matches(Process *p, Eterm arg1, Eterm arg2, Eterm arg3)
if (is_not_binary(arg1)) {
goto badarg;
}
- if (parse_match_opts_list(arg3,arg1,&hsstart,&hsend)) {
+ if (parse_split_opts_list(arg3, arg1, &(bfs.hsstart), &(bfs.hsend), &(bfs.flags))) {
goto badarg;
}
- if (hsend == 0) {
- BIF_RET(NIL);
+ if (bfs.hsend == 0) {
+ result = do_split_not_found_result(p, arg1, &bfs);
+ BIF_RET(result);
}
if (is_tuple(arg2)) {
tp = tuple_val(arg2);
@@ -1488,54 +1515,267 @@ binary_matches(Process *p, Eterm arg1, Eterm arg2, Eterm arg3)
!ERTS_TERM_IS_MAGIC_BINARY(tp[2])) {
goto badarg;
}
- type = tp[1];
+ bfs.type = tp[1];
bin = ((ProcBin *) binary_val(tp[2]))->val;
- if (type == am_bm &&
+ if (bfs.type == am_bm &&
ERTS_MAGIC_BIN_DESTRUCTOR(bin) != cleanup_my_data_bm) {
goto badarg;
}
- if (type == am_ac &&
+ if (bfs.type == am_ac &&
ERTS_MAGIC_BIN_DESTRUCTOR(bin) != cleanup_my_data_ac) {
goto badarg;
}
bin_term = tp[2];
- } else if (do_binary_match_compile(arg2,&type,&bin)) {
+ } else if (do_binary_match_compile(arg2, &(bfs.type), &bin)) {
goto badarg;
}
- runres = do_binary_matches(p,arg1,hsstart,hsend,type,bin,
- NIL,&result);
+ bfs.not_found_result = &do_split_not_found_result;
+ bfs.single_result = &do_split_single_result;
+ bfs.global_result = &do_split_global_result;
+ runres = do_binary_find(p, arg1, &bfs, bin, NIL, &result);
if (runres == DO_BIN_MATCH_RESTART && bin_term == NIL) {
Eterm *hp = HAlloc(p, PROC_BIN_SIZE);
bin_term = erts_mk_magic_binary_term(&hp, &MSO(p), bin);
} else if (bin_term == NIL) {
erts_bin_free(bin);
}
- switch (runres) {
+ switch(runres) {
case DO_BIN_MATCH_OK:
BIF_RET(result);
case DO_BIN_MATCH_RESTART:
- BUMP_ALL_REDS(p);
- BIF_TRAP3(&binary_matches_trap_export, p, arg1, result,
- bin_term);
+ BIF_TRAP3(&binary_find_trap_export, p, arg1, result, bin_term);
default:
goto badarg;
}
badarg:
- BIF_ERROR(p,BADARG);
+ BIF_ERROR(p, BADARG);
}
+BIF_RETTYPE binary_split_2(BIF_ALIST_2)
+{
+ return binary_split(BIF_P, BIF_ARG_1, BIF_ARG_2, THE_NON_VALUE);
+}
-BIF_RETTYPE binary_match_2(BIF_ALIST_2)
+BIF_RETTYPE binary_split_3(BIF_ALIST_3)
{
- return binary_match(BIF_P,BIF_ARG_1,BIF_ARG_2,((Eterm) 0));
+ return binary_split(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
+}
+
+static Eterm do_match_not_found_result(Process *p, Eterm subject, BinaryFindState *bfs)
+{
+ if (bfs->flags & BINARY_FIND_ALL) {
+ return NIL;
+ } else {
+ return am_nomatch;
+ }
}
+static Eterm do_match_single_result(Process *p, Eterm subject, BinaryFindState *bfs,
+ Sint pos, Sint len)
+{
+ Eterm erlen;
+ Eterm *hp;
+ Eterm ret;
-BIF_RETTYPE binary_matches_2(BIF_ALIST_2)
+ erlen = erts_make_integer((Uint)(len), p);
+ ret = erts_make_integer(pos, p);
+ hp = HAlloc(p, 3);
+ ret = TUPLE2(hp, ret, erlen);
+
+ return ret;
+}
+
+static Eterm do_match_global_result(Process *p, Eterm subject, BinaryFindState *bfs,
+ FindallData *fad, Uint fad_sz)
+{
+ Sint i;
+ Eterm tpl;
+ Eterm *hp;
+ Eterm ret;
+
+ for (i = 0; i < fad_sz; ++i) {
+ fad[i].epos = erts_make_integer(fad[i].pos, p);
+ fad[i].elen = erts_make_integer(fad[i].len, p);
+ }
+ hp = HAlloc(p, fad_sz * (3 + 2));
+ ret = NIL;
+ for (i = fad_sz - 1; i >= 0; --i) {
+ tpl = TUPLE2(hp, fad[i].epos, fad[i].elen);
+ hp += 3;
+ ret = CONS(hp, tpl, ret);
+ hp += 2;
+ }
+
+ return ret;
+}
+
+static Eterm do_split_not_found_result(Process *p, Eterm subject, BinaryFindState *bfs)
{
- return binary_matches(BIF_P,BIF_ARG_1,BIF_ARG_2,((Eterm) 0));
+ Eterm *hp;
+ Eterm ret;
+
+ if (bfs->flags & (BINARY_SPLIT_TRIM | BINARY_SPLIT_TRIM_ALL)
+ && binary_size(subject) == 0) {
+ return NIL;
+ }
+ hp = HAlloc(p, 2);
+ ret = CONS(hp, subject, NIL);
+
+ return ret;
}
+static Eterm do_split_single_result(Process *p, Eterm subject, BinaryFindState *bfs,
+ Sint pos, Sint len)
+{
+ size_t orig_size;
+ Eterm orig;
+ Uint offset;
+ Uint bit_offset;
+ Uint bit_size;
+ ErlSubBin *sb1;
+ ErlSubBin *sb2;
+ Eterm *hp;
+ Eterm ret;
+
+ orig_size = binary_size(subject);
+
+ if ((bfs->flags & (BINARY_SPLIT_TRIM | BINARY_SPLIT_TRIM_ALL)) &&
+ (orig_size - pos - len) == 0) {
+ if (pos == 0) {
+ ret = NIL;
+ } else {
+ hp = HAlloc(p, (ERL_SUB_BIN_SIZE + 2));
+ ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size);
+ sb1 = (ErlSubBin *) hp;
+ sb1->thing_word = HEADER_SUB_BIN;
+ sb1->size = pos;
+ sb1->offs = offset;
+ sb1->orig = orig;
+ sb1->bitoffs = bit_offset;
+ sb1->bitsize = bit_size;
+ sb1->is_writable = 0;
+ hp += ERL_SUB_BIN_SIZE;
+
+ ret = CONS(hp, make_binary(sb1), NIL);
+ hp += 2;
+ }
+ } else {
+ if ((bfs->flags & BINARY_SPLIT_TRIM_ALL) && (pos == 0)) {
+ hp = HAlloc(p, 1 * (ERL_SUB_BIN_SIZE + 2));
+ ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size);
+ sb1 = NULL;
+ } else {
+ hp = HAlloc(p, 2 * (ERL_SUB_BIN_SIZE + 2));
+ ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size);
+ sb1 = (ErlSubBin *) hp;
+ sb1->thing_word = HEADER_SUB_BIN;
+ sb1->size = pos;
+ sb1->offs = offset;
+ sb1->orig = orig;
+ sb1->bitoffs = bit_offset;
+ sb1->bitsize = 0;
+ sb1->is_writable = 0;
+ hp += ERL_SUB_BIN_SIZE;
+ }
+
+ sb2 = (ErlSubBin *) hp;
+ sb2->thing_word = HEADER_SUB_BIN;
+ sb2->size = orig_size - pos - len;
+ sb2->offs = offset + pos + len;
+ sb2->orig = orig;
+ sb2->bitoffs = bit_offset;
+ sb2->bitsize = bit_size;
+ sb2->is_writable = 0;
+ hp += ERL_SUB_BIN_SIZE;
+
+ ret = CONS(hp, make_binary(sb2), NIL);
+ hp += 2;
+ if (sb1 != NULL) {
+ ret = CONS(hp, make_binary(sb1), ret);
+ hp += 2;
+ }
+ }
+ return ret;
+}
+
+static Eterm do_split_global_result(Process *p, Eterm subject, BinaryFindState *bfs,
+ FindallData *fad, Uint fad_sz)
+{
+ size_t orig_size;
+ Eterm orig;
+ Uint offset;
+ Uint bit_offset;
+ Uint bit_size;
+ ErlSubBin *sb;
+ Sint i;
+ Sint tail;
+ Uint list_size;
+ Uint end_pos;
+ Uint do_trim = bfs->flags & (BINARY_SPLIT_TRIM | BINARY_SPLIT_TRIM_ALL);
+ Eterm *hp;
+ Eterm *hendp;
+ Eterm ret;
+
+ tail = fad_sz - 1;
+ list_size = fad_sz + 1;
+ orig_size = binary_size(subject);
+ end_pos = (Uint)(orig_size);
+
+ hp = HAlloc(p, list_size * (ERL_SUB_BIN_SIZE + 2));
+ hendp = hp + list_size * (ERL_SUB_BIN_SIZE + 2);
+ ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size);
+ ASSERT(bit_size == 0);
+
+ ret = NIL;
+
+ for (i = tail; i >= 0; --i) {
+ sb = (ErlSubBin *)(hp);
+ sb->size = end_pos - (fad[i].pos + fad[i].len);
+ if (!(sb->size == 0 && do_trim)) {
+ sb->thing_word = HEADER_SUB_BIN;
+ sb->offs = offset + fad[i].pos + fad[i].len;
+ sb->orig = orig;
+ sb->bitoffs = bit_offset;
+ sb->bitsize = 0;
+ sb->is_writable = 0;
+ hp += ERL_SUB_BIN_SIZE;
+ ret = CONS(hp, make_binary(sb), ret);
+ hp += 2;
+ do_trim &= ~BINARY_SPLIT_TRIM;
+ }
+ end_pos = fad[i].pos;
+ }
+
+ sb = (ErlSubBin *)(hp);
+ sb->size = fad[0].pos;
+ if (!(sb->size == 0 && do_trim)) {
+ sb->thing_word = HEADER_SUB_BIN;
+ sb->offs = offset;
+ sb->orig = orig;
+ sb->bitoffs = bit_offset;
+ sb->bitsize = 0;
+ sb->is_writable = 0;
+ hp += ERL_SUB_BIN_SIZE;
+ ret = CONS(hp, make_binary(sb), ret);
+ hp += 2;
+ }
+ HRelease(p, hendp, hp);
+ return ret;
+}
+
+static BIF_RETTYPE binary_find_trap(BIF_ALIST_3)
+{
+ int runres;
+ Eterm result;
+ Binary *bin = ((ProcBin *) binary_val(BIF_ARG_3))->val;
+ runres = do_binary_find(BIF_P, BIF_ARG_1, NULL, bin, BIF_ARG_2, &result);
+ if (runres == DO_BIN_MATCH_OK) {
+ BIF_RET(result);
+ } else {
+ BUMP_ALL_REDS(BIF_P);
+ BIF_TRAP3(&binary_find_trap_export, BIF_P, BIF_ARG_1, result, BIF_ARG_3);
+ }
+}
BIF_RETTYPE erts_binary_part(Process *p, Eterm binary, Eterm epos, Eterm elen)
{
@@ -2550,7 +2790,6 @@ BIF_RETTYPE binary_referenced_byte_size_1(BIF_ALIST_1)
}
pb = (ProcBin *) binary_val(bin);
if (pb->thing_word == HEADER_PROC_BIN) {
- /* XXX:PaN - Halfword - orig_size is a long, we should handle that */
res = erts_make_integer((Uint) pb->val->orig_size, BIF_P);
} else { /* heap binary */
res = erts_make_integer((Uint) ((ErlHeapBin *) pb)->size, BIF_P);
@@ -2568,7 +2807,7 @@ BIF_RETTYPE binary_referenced_byte_size_1(BIF_ALIST_1)
#endif
static int get_need(Uint u) {
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
if (u > 0xFFFFFFFFUL) {
if (u > 0xFFFFFFFFFFFFUL) {
if (u > 0xFFFFFFFFFFFFFFUL) {
diff --git a/erts/emulator/beam/erl_bif_chksum.c b/erts/emulator/beam/erl_bif_chksum.c
index e3074d6309..9417803e14 100644
--- a/erts/emulator/beam/erl_bif_chksum.c
+++ b/erts/emulator/beam/erl_bif_chksum.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erl_bif_ddll.c b/erts/emulator/beam/erl_bif_ddll.c
index f4ef59993a..4948975851 100644
--- a/erts/emulator/beam/erl_bif_ddll.c
+++ b/erts/emulator/beam/erl_bif_ddll.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2013. 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.
@@ -48,6 +48,7 @@
#include "erl_version.h"
#include "erl_bif_unique.h"
#include "dtrace-wrapper.h"
+#include "lttng-wrapper.h"
#ifdef ERTS_SMP
#define DDLL_SMP 1
@@ -1108,25 +1109,25 @@ void erts_ddll_decrement_port_count(DE_Handle *dh)
static void first_ddll_reference(DE_Handle *dh)
{
assert_drv_list_rwlocked();
- erts_refc_init(&(dh->refc),1);
+ erts_smp_refc_init(&(dh->refc),1);
}
void erts_ddll_reference_driver(DE_Handle *dh)
{
assert_drv_list_locked();
- if (erts_refc_inctest(&(dh->refc),1) == 1) {
- erts_refc_inc(&(dh->refc),2); /* add a reference for the scheduled operation */
+ if (erts_smp_refc_inctest(&(dh->refc),1) == 1) {
+ erts_smp_refc_inc(&(dh->refc),2); /* add a reference for the scheduled operation */
}
}
void erts_ddll_reference_referenced_driver(DE_Handle *dh)
{
- erts_refc_inc(&(dh->refc),2);
+ erts_smp_refc_inc(&(dh->refc),2);
}
void erts_ddll_dereference_driver(DE_Handle *dh)
{
- if (erts_refc_dectest(&(dh->refc),0) == 0) {
+ if (erts_smp_refc_dectest(&(dh->refc),0) == 0) {
/* No lock here, but if the driver is referenced again,
the scheduled deletion is added as a reference too, see above */
erts_schedule_misc_op(ddll_no_more_references, (void *) dh);
@@ -1149,11 +1150,11 @@ static void restore_process_references(DE_Handle *dh)
{
DE_ProcEntry *p;
assert_drv_list_rwlocked();
- ASSERT(erts_refc_read(&(dh->refc),0) == 0);
+ ASSERT(erts_smp_refc_read(&(dh->refc),0) == 0);
for(p = dh->procs;p != NULL; p = p->next) {
if (p->awaiting_status == ERL_DE_PROC_LOADED) {
ASSERT(p->flags & ERL_DE_FL_DEREFERENCED);
- erts_refc_inc(&(dh->refc),1);
+ erts_smp_refc_inc(&(dh->refc),1);
p->flags &= ~ERL_DE_FL_DEREFERENCED;
}
}
@@ -1175,9 +1176,9 @@ static void ddll_no_more_references(void *vdh)
lock_drv_list();
- x = erts_refc_read(&(dh->refc),0);
+ x = erts_smp_refc_read(&(dh->refc),0);
if (x > 0) {
- x = erts_refc_dectest(&(dh->refc),0); /* delete the reference added for me */
+ x = erts_smp_refc_dectest(&(dh->refc),0); /* delete the reference added for me */
}
@@ -1619,6 +1620,7 @@ static int do_unload_driver_entry(DE_Handle *dh, Eterm *save_name)
if (q->finish) {
int fpe_was_unmasked = erts_block_fpe();
DTRACE1(driver_finish, q->name);
+ LTTNG1(driver_finish, q->name);
(*(q->finish))();
erts_unblock_fpe(fpe_was_unmasked);
}
@@ -1641,7 +1643,7 @@ static int load_driver_entry(DE_Handle **dhp, char *path, char *name)
dh->handle = NULL;
dh->procs = NULL;
erts_smp_atomic32_init_nob(&dh->port_count, 0);
- erts_refc_init(&(dh->refc), (erts_aint_t) 0);
+ erts_smp_refc_init(&(dh->refc), (erts_aint_t) 0);
dh->status = -1;
dh->reload_full_path = NULL;
dh->reload_driver_name = NULL;
@@ -1679,7 +1681,7 @@ static int reload_driver_entry(DE_Handle *dh)
dh->reload_full_path = NULL;
dh->reload_driver_name = NULL;
- ASSERT(erts_refc_read(&(dh->refc),0) == 0);
+ ASSERT(erts_smp_refc_read(&(dh->refc),0) == 0);
ASSERT(dh->full_path != NULL);
erts_free(ERTS_ALC_T_DDLL_HANDLE, (void *) dh->full_path);
dh->full_path = NULL;
@@ -1707,18 +1709,19 @@ static void notify_proc(Process *proc, Eterm ref, Eterm driver_name, Eterm type,
Eterm mess;
Eterm r;
Eterm *hp;
- ErlHeapFragment *bp;
- ErlOffHeap *ohp;
+ ErtsMessage *mp;
ErtsProcLocks rp_locks = 0;
+ ErlOffHeap *ohp;
ERTS_SMP_CHK_NO_PROC_LOCKS;
assert_drv_list_rwlocked();
if (errcode != 0) {
int need = load_error_need(errcode);
Eterm e;
- hp = erts_alloc_message_heap(6 /* tuple */ + 3 /* Error tuple */ +
- REF_THING_SIZE + need, &bp, &ohp,
- proc, &rp_locks);
+ mp = erts_alloc_message_heap(proc, &rp_locks,
+ (6 /* tuple */ + 3 /* Error tuple */ +
+ REF_THING_SIZE + need),
+ &hp, &ohp);
r = copy_ref(ref,hp);
hp += REF_THING_SIZE;
e = build_load_error_hp(hp, errcode);
@@ -1727,12 +1730,14 @@ static void notify_proc(Process *proc, Eterm ref, Eterm driver_name, Eterm type,
hp += 3;
mess = TUPLE5(hp,type,r,am_driver,driver_name,mess);
} else {
- hp = erts_alloc_message_heap(6 /* tuple */ + REF_THING_SIZE, &bp, &ohp, proc, &rp_locks);
+ mp = erts_alloc_message_heap(proc, &rp_locks,
+ 6 /* tuple */ + REF_THING_SIZE,
+ &hp, &ohp);
r = copy_ref(ref,hp);
hp += REF_THING_SIZE;
mess = TUPLE5(hp,type,r,am_driver,driver_name,tag);
}
- erts_queue_message(proc, &rp_locks, bp, mess, am_undefined);
+ erts_queue_message(proc, rp_locks, mp, mess, am_system);
erts_smp_proc_unlock(proc, rp_locks);
ERTS_SMP_CHK_NO_PROC_LOCKS;
}
diff --git a/erts/emulator/beam/erl_bif_guard.c b/erts/emulator/beam/erl_bif_guard.c
index 4a9a6a5fcd..ea508bd1c4 100644
--- a/erts/emulator/beam/erl_bif_guard.c
+++ b/erts/emulator/beam/erl_bif_guard.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2011. 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.
@@ -157,7 +157,7 @@ BIF_RETTYPE round_1(BIF_ALIST_1)
GET_DOUBLE(BIF_ARG_1, f);
/* round it and return the resultant integer */
- res = double_to_integer(BIF_P, (f.fd > 0.0) ? f.fd + 0.5 : f.fd - 0.5);
+ res = double_to_integer(BIF_P, round(f.fd));
BIF_RET(res);
}
@@ -597,8 +597,7 @@ Eterm erts_gc_round_1(Process* p, Eterm* reg, Uint live)
}
GET_DOUBLE(arg, f);
- return gc_double_to_integer(p, (f.fd > 0.0) ? f.fd + 0.5 : f.fd - 0.5,
- reg, live);
+ return gc_double_to_integer(p, round(f.fd), reg, live);
}
Eterm erts_gc_trunc_1(Process* p, Eterm* reg, Uint live)
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index ac7a70c642..7bd45916f5 100644
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2013. 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.
@@ -22,6 +22,7 @@
# include "config.h"
#endif
+#define ERTS_WANT_MEM_MAPPERS
#include "sys.h"
#include "erl_vm.h"
#include "global.h"
@@ -44,6 +45,7 @@
#include "erl_async.h"
#include "erl_thr_progress.h"
#include "erl_bif_unique.h"
+#include "erl_map.h"
#define ERTS_PTAB_WANT_DEBUG_FUNCS__
#include "erl_ptab.h"
#ifdef HIPE
@@ -64,6 +66,7 @@ static Export* alloc_sizes_trap = NULL;
static Export* gather_io_bytes_trap = NULL;
static Export *gather_sched_wall_time_res_trap;
+static Export *gather_msacc_res_trap;
static Export *gather_gc_info_res_trap;
static Export *gather_system_check_res_trap;
@@ -74,9 +77,6 @@ static char otp_version[] = ERLANG_OTP_VERSION;
static char erts_system_version[] = ("Erlang/OTP " ERLANG_OTP_RELEASE
"%s"
" [erts-" ERLANG_VERSION "]"
-#if !HEAP_ON_C_STACK && !HALFWORD_HEAP
- " [no-c-stack-objects]"
-#endif
#ifndef OTP_RELEASE
#ifdef ERLANG_GIT_VERSION
" [source-" ERLANG_GIT_VERSION "]"
@@ -85,12 +85,8 @@ static char erts_system_version[] = ("Erlang/OTP " ERLANG_OTP_RELEASE
#endif
#endif
#ifdef ARCH_64
-#if HALFWORD_HEAP
- " [64-bit halfword]"
-#else
" [64-bit]"
#endif
-#endif
#ifdef ERTS_SMP
" [smp:%beu:%beu]"
#endif
@@ -132,12 +128,18 @@ static char erts_system_version[] = ("Erlang/OTP " ERLANG_OTP_RELEASE
#ifdef ERTS_FRMPTR
" [frame-pointer]"
#endif
+#ifdef USE_LTTNG
+ " [lttng]"
+#endif
#ifdef USE_DTRACE
" [dtrace]"
#endif
#ifdef USE_SYSTEMTAP
" [systemtap]"
#endif
+#ifdef SHCOPY
+ " [sharing-preserving]"
+#endif
"\n");
#define ASIZE(a) (sizeof(a)/sizeof(a[0]))
@@ -172,7 +174,7 @@ bld_bin_list(Uint **hpp, Uint *szp, ErlOffHeap* oh)
if (szp)
*szp += 4+2;
if (hpp) {
- Uint refc = (Uint) erts_smp_atomic_read_nob(&pb->val->refc);
+ Uint refc = (Uint) erts_refc_read(&pb->val->refc, 1);
tuple = TUPLE3(*hpp, val, orig_size, make_small(refc));
res = CONS(*hpp + 4, tuple, res);
*hpp += 4+2;
@@ -315,7 +317,7 @@ make_link_list(Process *p, ErtsLink *root, Eterm tail)
}
int
-erts_print_system_version(int to, void *arg, Process *c_p)
+erts_print_system_version(fmtfn_t to, void *arg, Process *c_p)
{
int i, rc = -1;
char *rc_str = "";
@@ -323,13 +325,11 @@ erts_print_system_version(int to, void *arg, Process *c_p)
char *ov = otp_version;
#ifdef ERTS_SMP
Uint total, online, active;
-#ifdef ERTS_DIRTY_SCHEDULERS
Uint dirty_cpu, dirty_cpu_onln, dirty_io;
- (void) erts_schedulers_state(&total, &online, &active, &dirty_cpu, &dirty_cpu_onln, &dirty_io, 0);
-#else
- (void) erts_schedulers_state(&total, &online, &active, NULL, NULL, NULL, 0);
-#endif
+ erts_schedulers_state(&total, &online, &active,
+ &dirty_cpu, &dirty_cpu_onln, NULL,
+ &dirty_io, NULL);
#endif
for (i = 0; i < sizeof(otp_version)-4; i++) {
if (ov[i] == '-' && ov[i+1] == 'r' && ov[i+2] == 'c')
@@ -361,8 +361,13 @@ erts_print_system_version(int to, void *arg, Process *c_p)
}
typedef struct {
+ /* {Entity,Node} = {monitor.Name,monitor.Pid} for external by name
+ * {Entity,Node} = {monitor.Pid,NIL} for external/external by pid
+ * {Entity,Node} = {monitor.Name,erlang:node()} for internal by name */
Eterm entity;
Eterm node;
+ /* pid is actual target being monitored, no matter pid/port or name */
+ Eterm pid;
} MonitorInfo;
typedef struct {
@@ -420,21 +425,27 @@ static void collect_one_origin_monitor(ErtsMonitor *mon, void *vmicp)
EXTEND_MONITOR_INFOS(micp);
if (is_atom(mon->pid)) { /* external by name */
micp->mi[micp->mi_i].entity = mon->name;
- micp->mi[micp->mi_i].node = mon->pid;
- micp->sz += 3; /* need one 2-tuple */
+ micp->mi[micp->mi_i].node = mon->pid;
+ micp->sz += 3; /* need one 2-tuple */
} else if (is_external_pid(mon->pid)) { /* external by pid */
micp->mi[micp->mi_i].entity = mon->pid;
- micp->mi[micp->mi_i].node = NIL;
- micp->sz += NC_HEAP_SIZE(mon->pid);
+ micp->mi[micp->mi_i].node = NIL;
+ micp->sz += NC_HEAP_SIZE(mon->pid);
} else if (!is_nil(mon->name)) { /* internal by name */
micp->mi[micp->mi_i].entity = mon->name;
- micp->mi[micp->mi_i].node = erts_this_dist_entry->sysname;
- micp->sz += 3; /* need one 2-tuple */
+ micp->mi[micp->mi_i].node = erts_this_dist_entry->sysname;
+ micp->sz += 3; /* need one 2-tuple */
} else { /* internal by pid */
micp->mi[micp->mi_i].entity = mon->pid;
- micp->mi[micp->mi_i].node = NIL;
+ micp->mi[micp->mi_i].node = NIL;
/* no additional heap space needed */
}
+
+ /* have always pid at hand, to assist with figuring out if its a port or
+ * a process, when we monitored by name and process_info is requested.
+ * See: erl_bif_info.c:process_info_aux section for am_monitors */
+ micp->mi[micp->mi_i].pid = mon->pid;
+
micp->mi_i++;
micp->sz += 2 + 3; /* For a cons cell and a 2-tuple */
}
@@ -595,8 +606,11 @@ static Eterm pi_args[] = {
am_suspending,
am_min_heap_size,
am_min_bin_vheap_size,
+ am_max_heap_size,
am_current_location,
am_current_stacktrace,
+ am_message_queue_data,
+ am_garbage_collection_info
};
#define ERTS_PI_ARGS ((int) (sizeof(pi_args)/sizeof(Eterm)))
@@ -642,8 +656,11 @@ pi_arg2ix(Eterm arg)
case am_suspending: return 26;
case am_min_heap_size: return 27;
case am_min_bin_vheap_size: return 28;
- case am_current_location: return 29;
- case am_current_stacktrace: return 30;
+ case am_max_heap_size: return 29;
+ case am_current_location: return 30;
+ case am_current_stacktrace: return 31;
+ case am_message_queue_data: return 32;
+ case am_garbage_collection_info: return 33;
default: return -1;
}
}
@@ -672,18 +689,12 @@ static Eterm pi_1_keys[] = {
#define ERTS_PI_1_NO_OF_KEYS (sizeof(pi_1_keys)/sizeof(Eterm))
static Eterm pi_1_keys_list;
-#if HEAP_ON_C_STACK
static Eterm pi_1_keys_list_heap[2*ERTS_PI_1_NO_OF_KEYS];
-#endif
static void
process_info_init(void)
{
-#if HEAP_ON_C_STACK
Eterm *hp = &pi_1_keys_list_heap[0];
-#else
- Eterm *hp = erts_alloc(ERTS_ALC_T_LL_TEMP_TERM,sizeof(Eterm)*2*ERTS_PI_1_NO_OF_KEYS);
-#endif
int i;
pi_1_keys_list = NIL;
@@ -732,9 +743,10 @@ pi_pid2proc(Process *c_p, Eterm pid, ErtsProcLocks info_locks)
-BIF_RETTYPE
+static BIF_RETTYPE
process_info_aux(Process *BIF_P,
Process *rp,
+ ErtsProcLocks rp_locks,
Eterm rpid,
Eterm item,
int always_wrap);
@@ -825,10 +837,32 @@ process_info_list(Process *c_p, Eterm pid, Eterm list, int always_wrap,
*fail_type = ERTS_PI_FAIL_TYPE_AWAIT_EXIT;
goto done;
}
- else if (!(locks & ERTS_PROC_LOCK_STATUS)) {
- erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_STATUS);
+ else {
+ ErtsProcLocks unlock_locks = 0;
+
+ if (c_p == rp)
+ locks |= ERTS_PROC_LOCK_MAIN;
+
+ if (!(locks & ERTS_PROC_LOCK_STATUS))
+ unlock_locks |= ERTS_PROC_LOCK_STATUS;
+
+ if (locks & ERTS_PROC_LOCK_MSGQ) {
+ /*
+ * Move in queue into private queue and
+ * release msgq lock, enabling others to
+ * send messages to the process while it
+ * is being inspected...
+ */
+ ASSERT(locks & ERTS_PROC_LOCK_MAIN);
+ ERTS_SMP_MSGQ_MV_INQ2PRIVQ(rp);
+ locks &= ~ERTS_PROC_LOCK_MSGQ;
+ unlock_locks |= ERTS_PROC_LOCK_MSGQ;
+ }
+
+ if (unlock_locks)
+ erts_smp_proc_unlock(rp, unlock_locks);
+
}
-
/*
* We always handle 'messages' first if it should be part
@@ -840,7 +874,7 @@ process_info_list(Process *c_p, Eterm pid, Eterm list, int always_wrap,
if (want_messages) {
ix = pi_arg2ix(am_messages);
ASSERT(part_res[ix] == THE_NON_VALUE);
- part_res[ix] = process_info_aux(c_p, rp, pid, am_messages, always_wrap);
+ part_res[ix] = process_info_aux(c_p, rp, locks, pid, am_messages, always_wrap);
ASSERT(part_res[ix] != THE_NON_VALUE);
}
@@ -848,7 +882,7 @@ process_info_list(Process *c_p, Eterm pid, Eterm list, int always_wrap,
ix = res_elem_ix[res_elem_ix_ix];
if (part_res[ix] == THE_NON_VALUE) {
arg = pi_ix2arg(ix);
- part_res[ix] = process_info_aux(c_p, rp, pid, arg, always_wrap);
+ part_res[ix] = process_info_aux(c_p, rp, locks, pid, arg, always_wrap);
ASSERT(part_res[ix] != THE_NON_VALUE);
}
}
@@ -979,9 +1013,31 @@ BIF_RETTYPE process_info_2(BIF_ALIST_2)
ERTS_BIF_AWAIT_X_DATA_TRAP(BIF_P, BIF_ARG_1, am_undefined);
}
else {
+ ErtsProcLocks unlock_locks = 0;
+
+ if (BIF_P == rp)
+ info_locks |= ERTS_PROC_LOCK_MAIN;
+
if (!(info_locks & ERTS_PROC_LOCK_STATUS))
- erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_STATUS);
- res = process_info_aux(BIF_P, rp, pid, BIF_ARG_2, 0);
+ unlock_locks |= ERTS_PROC_LOCK_STATUS;
+
+ if (info_locks & ERTS_PROC_LOCK_MSGQ) {
+ /*
+ * Move in queue into private queue and
+ * release msgq lock, enabling others to
+ * send messages to the process while it
+ * is being inspected...
+ */
+ ASSERT(info_locks & ERTS_PROC_LOCK_MAIN);
+ ERTS_SMP_MSGQ_MV_INQ2PRIVQ(rp);
+ info_locks &= ~ERTS_PROC_LOCK_MSGQ;
+ unlock_locks |= ERTS_PROC_LOCK_MSGQ;
+ }
+
+ if (unlock_locks)
+ erts_smp_proc_unlock(rp, unlock_locks);
+
+ res = process_info_aux(BIF_P, rp, info_locks, pid, BIF_ARG_2, 0);
}
ASSERT(is_value(res));
@@ -999,6 +1055,7 @@ BIF_RETTYPE process_info_2(BIF_ALIST_2)
Eterm
process_info_aux(Process *BIF_P,
Process *rp,
+ ErtsProcLocks rp_locks,
Eterm rpid,
Eterm item,
int always_wrap)
@@ -1064,177 +1121,61 @@ process_info_aux(Process *BIF_P,
break;
case am_status:
- res = erts_process_status(BIF_P, ERTS_PROC_LOCK_MAIN, rp, rpid);
+ res = erts_process_status(rp, rpid);
ASSERT(res != am_undefined);
hp = HAlloc(BIF_P, 3);
break;
case am_messages: {
- ErlMessage* mp;
- int n;
-
- ERTS_SMP_MSGQ_MV_INQ2PRIVQ(rp);
- n = rp->msg.len;
- if (n == 0 || ERTS_TRACE_FLAGS(rp) & F_SENSITIVE) {
+ if (rp->msg.len == 0 || ERTS_TRACE_FLAGS(rp) & F_SENSITIVE) {
hp = HAlloc(BIF_P, 3);
} else {
- int remove_bad_messages = 0;
- struct {
- Uint copy_struct_size;
- ErlMessage* msgp;
- } *mq = erts_alloc(ERTS_ALC_T_TMP, n*sizeof(*mq));
- Sint i = 0;
- Uint heap_need = 3;
+ ErtsMessageInfo *mip;
+ Sint i;
+ Uint heap_need;
+#ifdef DEBUG
Eterm *hp_end;
+#endif
- for (mp = rp->msg.first; mp; mp = mp->next) {
- heap_need += 2;
- mq[i].msgp = mp;
- if (rp != BIF_P) {
- Eterm msg = ERL_MESSAGE_TERM(mq[i].msgp);
- if (is_value(msg)) {
- mq[i].copy_struct_size = (is_immed(msg)? 0 :
- size_object(msg));
- }
- else if (mq[i].msgp->data.attached) {
- mq[i].copy_struct_size
- = erts_msg_attached_data_size(mq[i].msgp);
- }
- else {
- /* Bad distribution message; ignore */
- remove_bad_messages = 1;
- mq[i].copy_struct_size = 0;
- }
- heap_need += mq[i].copy_struct_size;
- }
- else {
- mq[i].copy_struct_size = mp->data.attached ?
- erts_msg_attached_data_size(mp) : 0;
- }
- i++;
- }
+ mip = erts_alloc(ERTS_ALC_T_TMP,
+ rp->msg.len*sizeof(ErtsMessageInfo));
- if (rp != BIF_P) {
- hp = HAlloc(BIF_P, heap_need);
- hp_end = hp + heap_need;
- ASSERT(i == n);
- for (i--; i >= 0; i--) {
- Eterm msg = ERL_MESSAGE_TERM(mq[i].msgp);
- if (is_value(msg)) {
- if (mq[i].copy_struct_size)
- msg = copy_struct(msg,
- mq[i].copy_struct_size,
- &hp,
- &MSO(BIF_P));
- }
- else if (mq[i].msgp->data.attached) {
- ErlHeapFragment *hfp;
- /*
- * Decode it into a message buffer and attach it
- * to the message instead of the attached external
- * term.
- *
- * Note that we may not pass a process pointer
- * to erts_msg_distext2heap(), since it would then
- * try to alter locks on that process.
- */
- msg = erts_msg_distext2heap(
- NULL, NULL, &hfp, &ERL_MESSAGE_TOKEN(mq[i].msgp),
- mq[i].msgp->data.dist_ext);
-
- ERL_MESSAGE_TERM(mq[i].msgp) = msg;
- mq[i].msgp->data.heap_frag = hfp;
-
- if (is_non_value(msg)) {
- ASSERT(!mq[i].msgp->data.heap_frag);
- /* Bad distribution message; ignore */
- remove_bad_messages = 1;
- continue;
- }
- else {
- /* Make our copy of the message */
- ASSERT(size_object(msg) == erts_used_frag_sz(hfp));
- msg = copy_struct(msg,
- erts_used_frag_sz(hfp),
- &hp,
- &MSO(BIF_P));
- }
- }
- else {
- /* Bad distribution message; ignore */
- remove_bad_messages = 1;
- continue;
- }
- res = CONS(hp, msg, res);
- hp += 2;
- }
- HRelease(BIF_P, hp_end, hp+3);
- }
- else {
- for (i--; i >= 0; i--) {
- ErtsHeapFactory factory;
- Eterm msg = ERL_MESSAGE_TERM(mq[i].msgp);
-
- erts_factory_proc_prealloc_init(&factory, BIF_P,
- mq[i].copy_struct_size+2);
- if (mq[i].msgp->data.attached) {
- /* Decode it on the heap */
- erts_move_msg_attached_data_to_heap(&factory,
- mq[i].msgp);
- msg = ERL_MESSAGE_TERM(mq[i].msgp);
- ASSERT(!mq[i].msgp->data.attached);
- }
- if (is_value(msg)) {
- hp = erts_produce_heap(&factory, 2, 0);
- res = CONS(hp, msg, res);
- }
- else {
- /* Bad distribution message; ignore */
- remove_bad_messages = 1;
- continue;
- }
- erts_factory_close(&factory);
- }
- hp = HAlloc(BIF_P, 3);
- }
- erts_free(ERTS_ALC_T_TMP, mq);
- if (remove_bad_messages) {
- ErlMessage **mpp;
- /*
- * We need to remove bad distribution messages from
- * the queue, so that the value returned for
- * 'message_queue_len' is consistent with the value
- * returned for 'messages'.
- */
- mpp = &rp->msg.first;
- mp = rp->msg.first;
- while (mp) {
- if (is_value(ERL_MESSAGE_TERM(mp))) {
- mpp = &mp->next;
- mp = mp->next;
- }
- else {
- ErlMessage* bad_mp = mp;
- ASSERT(!mp->data.attached);
- if (rp->msg.save == &mp->next)
- rp->msg.save = mpp;
- if (rp->msg.last == &mp->next)
- rp->msg.last = mpp;
- *mpp = mp->next;
- mp = mp->next;
- rp->msg.len--;
- free_message(bad_mp);
- }
- }
+ /*
+ * Note that message queue may shrink when calling
+ * erts_prep_msgq_for_inspection() since it removes
+ * corrupt distribution messages.
+ */
+ heap_need = erts_prep_msgq_for_inspection(BIF_P, rp, rp_locks, mip);
+ heap_need += 3; /* top 2-tuple */
+ heap_need += rp->msg.len*2; /* Cons cells */
+
+ hp = HAlloc(BIF_P, heap_need); /* heap_need is exact */
+#ifdef DEBUG
+ hp_end = hp + heap_need;
+#endif
+
+ /* Build list of messages... */
+ for (i = rp->msg.len - 1, res = NIL; i >= 0; i--) {
+ Eterm msg = ERL_MESSAGE_TERM(mip[i].msgp);
+ Uint sz = mip[i].size;
+
+ if (sz != 0)
+ msg = copy_struct(msg, sz, &hp, &BIF_P->off_heap);
+
+ res = CONS(hp, msg, res);
+ hp += 2;
}
+
+ ASSERT(hp_end == hp + 3);
+
+ erts_free(ERTS_ALC_T_TMP, mip);
}
break;
}
case am_message_queue_len:
hp = HAlloc(BIF_P, 3);
- ERTS_SMP_MSGQ_MV_INQ2PRIVQ(rp);
res = make_small(rp->msg.len);
break;
@@ -1260,37 +1201,49 @@ process_info_aux(Process *BIF_P,
case am_monitors: {
MonitorInfoCollection mic;
- int i;
+ int i;
INIT_MONITOR_INFOS(mic);
- erts_doforall_monitors(ERTS_P_MONITORS(rp),&collect_one_origin_monitor,&mic);
- hp = HAlloc(BIF_P, 3 + mic.sz);
+ erts_doforall_monitors(ERTS_P_MONITORS(rp),
+ &collect_one_origin_monitor, &mic);
+ hp = HAlloc(BIF_P, 3 + mic.sz);
res = NIL;
for (i = 0; i < mic.mi_i; i++) {
if (is_atom(mic.mi[i].entity)) {
/* Monitor by name.
- * Build {process, {Name, Node}} and cons it.
+ * Build {process|port, {Name, Node}} and cons it.
*/
Eterm t1, t2;
+ /* If pid is an atom, then it is a remote named monitor, which
+ has to be a process */
+ Eterm m_type = is_port(mic.mi[i].pid) ? am_port : am_process;
+ ASSERT(is_pid(mic.mi[i].pid)
+ || is_port(mic.mi[i].pid)
+ || is_atom(mic.mi[i].pid));
t1 = TUPLE2(hp, mic.mi[i].entity, mic.mi[i].node);
hp += 3;
- t2 = TUPLE2(hp, am_process, t1);
+ t2 = TUPLE2(hp, m_type, t1);
hp += 3;
res = CONS(hp, t2, res);
- hp += 2;
+ hp += 2;
}
else {
- /* Monitor by pid. Build {process, Pid} and cons it. */
+ /* Monitor by pid. Build {process|port, Pid} and cons it. */
Eterm t;
Eterm pid = STORE_NC(&hp, &MSO(BIF_P), mic.mi[i].entity);
- t = TUPLE2(hp, am_process, pid);
+
+ Eterm m_type = is_port(mic.mi[i].pid) ? am_port : am_process;
+ ASSERT(is_pid(mic.mi[i].pid)
+ || is_port(mic.mi[i].pid));
+
+ t = TUPLE2(hp, m_type, pid);
hp += 3;
res = CONS(hp, t, res);
- hp += 2;
+ hp += 2;
}
}
- DESTROY_MONITOR_INFOS(mic);
+ DESTROY_MONITOR_INFOS(mic);
break;
}
@@ -1421,8 +1374,20 @@ process_info_aux(Process *BIF_P,
break;
}
+ case am_max_heap_size: {
+ Uint hsz = 3;
+ (void) erts_max_heap_size_map(MAX_HEAP_SIZE_GET(rp),
+ MAX_HEAP_SIZE_FLAGS_GET(rp),
+ NULL, &hsz);
+ hp = HAlloc(BIF_P, hsz);
+ res = erts_max_heap_size_map(MAX_HEAP_SIZE_GET(rp),
+ MAX_HEAP_SIZE_FLAGS_GET(rp),
+ &hp, NULL);
+ break;
+ }
+
case am_total_heap_size: {
- ErlMessage *mp;
+ ErtsMessage *mp;
Uint total_heap_size;
Uint hsz = 3;
@@ -1432,11 +1397,10 @@ process_info_aux(Process *BIF_P,
total_heap_size += rp->mbuf_sz;
- ERTS_SMP_MSGQ_MV_INQ2PRIVQ(rp);
-
- for (mp = rp->msg.first; mp; mp = mp->next)
- if (mp->data.attached)
- total_heap_size += erts_msg_attached_data_size(mp);
+ if (rp->flags & F_ON_HEAP_MSGQ)
+ for (mp = rp->msg.first; mp; mp = mp->next)
+ if (mp->data.attached)
+ total_heap_size += erts_msg_attached_data_size(mp);
(void) erts_bld_uint(NULL, &hsz, total_heap_size);
hp = HAlloc(BIF_P, hsz);
@@ -1455,7 +1419,7 @@ process_info_aux(Process *BIF_P,
case am_memory: { /* Memory consumed in bytes */
Uint hsz = 3;
- Uint size = erts_process_memory(rp);
+ Uint size = erts_process_memory(rp, 0);
(void) erts_bld_uint(NULL, &hsz, size);
hp = HAlloc(BIF_P, hsz);
res = erts_bld_uint(&hp, NULL, size);
@@ -1465,8 +1429,12 @@ process_info_aux(Process *BIF_P,
case am_garbage_collection: {
DECL_AM(minor_gcs);
Eterm t;
+ Uint map_sz = 0;
+
+ erts_max_heap_size_map(MAX_HEAP_SIZE_GET(rp), MAX_HEAP_SIZE_FLAGS_GET(rp), NULL, &map_sz);
- hp = HAlloc(BIF_P, 3+2 + 3+2 + 3+2 + 3+2 + 3); /* last "3" is for outside tuple */
+ hp = HAlloc(BIF_P, 3+2 + 3+2 + 3+2 + 3+2 + 3+2 + map_sz + 3);
+ /* last "3" is for outside tuple */
t = TUPLE2(hp, AM_minor_gcs, make_small(GEN_GCS(rp))); hp += 3;
res = CONS(hp, t, NIL); hp += 2;
@@ -1477,9 +1445,40 @@ process_info_aux(Process *BIF_P,
res = CONS(hp, t, res); hp += 2;
t = TUPLE2(hp, am_min_bin_vheap_size, make_small(MIN_VHEAP_SIZE(rp))); hp += 3;
res = CONS(hp, t, res); hp += 2;
+
+ t = erts_max_heap_size_map(MAX_HEAP_SIZE_GET(rp), MAX_HEAP_SIZE_FLAGS_GET(rp), &hp, NULL);
+
+ t = TUPLE2(hp, am_max_heap_size, t); hp += 3;
+ res = CONS(hp, t, res); hp += 2;
break;
}
+ case am_garbage_collection_info: {
+ Uint sz = 0, actual_sz = 0;
+
+ if (rp == BIF_P) {
+ sz += ERTS_PROCESS_GC_INFO_MAX_SIZE;
+ } else {
+ erts_process_gc_info(rp, &sz, NULL, 0, 0);
+ sz += 3;
+ }
+
+ hp = HAlloc(BIF_P, sz);
+ res = erts_process_gc_info(rp, &actual_sz, &hp, 0, 0);
+
+ /* We may have some extra space, fill with 0 tuples */
+ if (actual_sz <= sz - 3) {
+ for (; actual_sz < sz - 3; hp++, actual_sz++)
+ hp[0] = make_arityval(0);
+ } else {
+ for (; actual_sz < sz; hp++, actual_sz++)
+ hp[0] = make_arityval(0);
+ hp = HAlloc(BIF_P, 3);
+ }
+
+ break;
+ }
+
case am_group_leader: {
int sz = NC_HEAP_SIZE(rp->group_leader);
hp = HAlloc(BIF_P, 3 + sz);
@@ -1581,6 +1580,22 @@ process_info_aux(Process *BIF_P,
break;
}
+ case am_message_queue_data:
+ switch (rp->flags & (F_OFF_HEAP_MSGQ|F_ON_HEAP_MSGQ)) {
+ case F_OFF_HEAP_MSGQ:
+ res = am_off_heap;
+ break;
+ case F_ON_HEAP_MSGQ:
+ res = am_on_heap;
+ break;
+ default:
+ res = am_error;
+ ERTS_INTERNAL_ERROR("Inconsistent message queue management state");
+ break;
+ }
+ hp = HAlloc(BIF_P, 3);
+ break;
+
default:
return THE_NON_VALUE; /* will produce badarg */
@@ -1657,11 +1672,11 @@ current_stacktrace(Process* p, Process* rp, Eterm** hpp)
Eterm mfa;
Eterm res = NIL;
- depth = 8;
+ depth = erts_backtrace_depth;
sz = offsetof(struct StackTrace, trace) + sizeof(BeamInstr *)*depth;
s = (struct StackTrace *) erts_alloc(ERTS_ALC_T_TMP, sz);
s->depth = 0;
- if (rp->i) {
+ if (depth > 0 && rp->i) {
s->trace[s->depth++] = rp->i;
depth--;
}
@@ -1755,7 +1770,7 @@ info_1_tuple(Process* BIF_P, /* Pointer to current process. */
if (arity == 2) {
Eterm res = THE_NON_VALUE;
char *buf;
- int len = is_string(*tp);
+ Sint len = is_string(*tp);
if (len <= 0)
return res;
buf = (char *) erts_alloc(ERTS_ALC_T_TMP, len+1);
@@ -1774,7 +1789,7 @@ info_1_tuple(Process* BIF_P, /* Pointer to current process. */
else {
Eterm res = THE_NON_VALUE;
char *buf;
- int len = is_string(tp[1]);
+ Sint len = is_string(tp[1]);
if (len <= 0)
return res;
buf = (char *) erts_alloc(ERTS_ALC_T_TMP, len+1);
@@ -2064,12 +2079,8 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
Uint arity = *tp++;
return info_1_tuple(BIF_P, tp, arityval(arity));
} else if (BIF_ARG_1 == am_scheduler_id) {
-#ifdef ERTS_SMP
- ASSERT(BIF_P->scheduler_data);
- BIF_RET(make_small(BIF_P->scheduler_data->no));
-#else
- BIF_RET(make_small(1));
-#endif
+ ErtsSchedulerData *esdp = erts_proc_sched_data(BIF_P);
+ BIF_RET(make_small(esdp->no));
} else if (BIF_ARG_1 == am_compat_rel) {
ASSERT(erts_compat_rel > 0);
BIF_RET(make_small(erts_compat_rel));
@@ -2077,12 +2088,18 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
#ifndef ERTS_SMP
BIF_RET(am_disabled);
#else
+#ifndef ERTS_DIRTY_SCHEDULERS
if (erts_no_schedulers == 1)
BIF_RET(am_disabled);
- else {
- BIF_RET(erts_is_multi_scheduling_blocked()
- ? am_blocked
- : am_enabled);
+ else
+#endif
+ {
+ int msb = erts_is_multi_scheduling_blocked();
+ BIF_RET(!msb
+ ? am_enabled
+ : (msb > 0
+ ? am_blocked
+ : am_blocked_normal));
}
#endif
} else if (BIF_ARG_1 == am_build_type) {
@@ -2188,15 +2205,15 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
res = build_snifs_term(&hp, NULL, NIL);
BIF_RET(res);
} else if (BIF_ARG_1 == am_sequential_tracer) {
- val = erts_get_system_seq_tracer();
- ASSERT(is_internal_pid(val) || is_internal_port(val) || val==am_false);
+ ErtsTracer seq_tracer = erts_get_system_seq_tracer();
+ val = erts_tracer_to_term(BIF_P, seq_tracer);
hp = HAlloc(BIF_P, 3);
res = TUPLE2(hp, am_sequential_tracer, val);
BIF_RET(res);
} else if (BIF_ARG_1 == am_garbage_collection){
Uint val = (Uint) erts_smp_atomic32_read_nob(&erts_max_gen_gcs);
Eterm tup;
- hp = HAlloc(BIF_P, 3+2 + 3+2 + 3+2);
+ hp = HAlloc(BIF_P, 3+2 + 3+2 + 3+2 + 3+2);
tup = TUPLE2(hp, am_fullsweep_after, make_small(val)); hp += 3;
res = CONS(hp, tup, NIL); hp += 2;
@@ -2207,6 +2224,9 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
tup = TUPLE2(hp, am_min_bin_vheap_size, make_small(BIN_VH_MIN_SIZE)); hp += 3;
res = CONS(hp, tup, res); hp += 2;
+ tup = TUPLE2(hp, am_max_heap_size, make_small(H_MAX_SIZE)); hp += 3;
+ res = CONS(hp, tup, res); hp += 2;
+
BIF_RET(res);
} else if (BIF_ARG_1 == am_fullsweep_after){
Uint val = (Uint) erts_smp_atomic32_read_nob(&erts_max_gen_gcs);
@@ -2217,6 +2237,12 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
hp = HAlloc(BIF_P, 3);
res = TUPLE2(hp, am_min_heap_size,make_small(H_MIN_SIZE));
BIF_RET(res);
+ } else if (BIF_ARG_1 == am_max_heap_size) {
+ Uint sz = 0;
+ erts_max_heap_size_map(H_MAX_SIZE, H_MAX_FLAGS, NULL, &sz);
+ hp = HAlloc(BIF_P, sz);
+ res = erts_max_heap_size_map(H_MAX_SIZE, H_MAX_FLAGS, &hp, NULL);
+ BIF_RET(res);
} else if (BIF_ARG_1 == am_min_bin_vheap_size) {
hp = HAlloc(BIF_P, 3);
res = TUPLE2(hp, am_min_bin_vheap_size,make_small(BIN_VH_MIN_SIZE));
@@ -2258,9 +2284,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
} else if (ERTS_IS_ATOM_STR("dist_ctrl", BIF_ARG_1)) {
DistEntry *dep;
i = 0;
- /* Need to be the only thread running... */
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- erts_smp_thr_progress_block();
+ erts_smp_rwmtx_rlock(&erts_dist_table_rwmtx);
for (dep = erts_visible_dist_entries; dep; dep = dep->next)
++i;
for (dep = erts_hidden_dist_entries; dep; dep = dep->next)
@@ -2283,8 +2307,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
res = CONS(hp, tpl, res);
hp += 2;
}
- erts_smp_thr_progress_unblock();
- erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_smp_rwmtx_runlock(&erts_dist_table_rwmtx);
BIF_RET(res);
} else if (BIF_ARG_1 == am_system_version) {
erts_dsprintf_buf_t *dsbufp = erts_create_tmp_dsbuf(0);
@@ -2551,77 +2574,120 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
res = TUPLE3(hp, make_small(1), make_small(1), make_small(1));
BIF_RET(res);
#else
+ Eterm *hp;
Uint total, online, active;
- switch (erts_schedulers_state(&total,
- &online,
- &active,
- NULL,
- NULL,
- NULL,
- 1)) {
- case ERTS_SCHDLR_SSPND_DONE: {
- Eterm *hp = HAlloc(BIF_P, 4);
- res = TUPLE3(hp,
- make_small(total),
- make_small(online),
- make_small(active));
- BIF_RET(res);
+ erts_schedulers_state(&total, &online, &active,
+ NULL, NULL, NULL, NULL, NULL);
+ hp = HAlloc(BIF_P, 4);
+ res = TUPLE3(hp,
+ make_small(total),
+ make_small(online),
+ make_small(active));
+ BIF_RET(res);
+#endif
+ } else if (ERTS_IS_ATOM_STR("schedulers_state", BIF_ARG_1)) {
+#ifndef ERTS_SMP
+ Eterm *hp = HAlloc(BIF_P, 4);
+ res = TUPLE3(hp, make_small(1), make_small(1), make_small(1));
+ BIF_RET(res);
+#else
+ Eterm *hp;
+ Uint total, online, active;
+ erts_schedulers_state(&total, &online, &active,
+ NULL, NULL, NULL, NULL, NULL);
+ hp = HAlloc(BIF_P, 4);
+ res = TUPLE3(hp,
+ make_small(total),
+ make_small(online),
+ make_small(active));
+ BIF_RET(res);
+#endif
+ } else if (ERTS_IS_ATOM_STR("all_schedulers_state", BIF_ARG_1)) {
+#ifndef ERTS_SMP
+ Eterm *hp = HAlloc(BIF_P, 2+5);
+ res = CONS(hp+5,
+ TUPLE4(hp,
+ am_normal,
+ make_small(1),
+ make_small(1),
+ make_small(1)),
+ NIL);
+ BIF_RET(res);
+#else
+ Eterm *hp, tpl;
+ Uint sz, total, online, active,
+ dirty_cpu_total, dirty_cpu_online, dirty_cpu_active,
+ dirty_io_total, dirty_io_active;
+ erts_schedulers_state(&total, &online, &active,
+ &dirty_cpu_total, &dirty_cpu_online, &dirty_cpu_active,
+ &dirty_io_total, &dirty_io_active);
+
+ sz = 2+5;
+ if (dirty_cpu_total)
+ sz += 2+5;
+ if (dirty_io_total)
+ sz += 2+5;
+
+ hp = HAlloc(BIF_P, sz);
+
+ res = NIL;
+ if (dirty_io_total) {
+ tpl = TUPLE4(hp,
+ am_dirty_io,
+ make_small(dirty_io_total),
+ make_small(dirty_io_total),
+ make_small(dirty_io_active));
+ hp += 5;
+ res = CONS(hp, tpl, res);
+ hp += 2;
}
- case ERTS_SCHDLR_SSPND_YIELD_RESTART:
- ERTS_VBUMP_ALL_REDS(BIF_P);
- BIF_TRAP1(bif_export[BIF_system_info_1],
- BIF_P, BIF_ARG_1);
- default:
- ASSERT(0);
- BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
+ if (dirty_cpu_total) {
+ tpl = TUPLE4(hp,
+ am_dirty_cpu,
+ make_small(dirty_cpu_total),
+ make_small(dirty_cpu_online),
+ make_small(dirty_cpu_active));
+ hp += 5;
+ res = CONS(hp, tpl, res);
+ hp += 2;
}
+ tpl = TUPLE4(hp,
+ am_normal,
+ make_small(total),
+ make_small(online),
+ make_small(active));
+ hp += 5;
+ res = CONS(hp, tpl, res);
+ BIF_RET(res);
#endif
} else if (ERTS_IS_ATOM_STR("schedulers_online", BIF_ARG_1)) {
#ifndef ERTS_SMP
BIF_RET(make_small(1));
#else
- Uint total, online, active;
- switch (erts_schedulers_state(&total, &online, &active, NULL, NULL, NULL, 1)) {
- case ERTS_SCHDLR_SSPND_DONE:
- BIF_RET(make_small(online));
- case ERTS_SCHDLR_SSPND_YIELD_RESTART:
- ERTS_VBUMP_ALL_REDS(BIF_P);
- BIF_TRAP1(bif_export[BIF_system_info_1],
- BIF_P, BIF_ARG_1);
- default:
- ASSERT(0);
- BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
- }
+ Uint online;
+ erts_schedulers_state(NULL, &online, NULL, NULL, NULL, NULL, NULL, NULL);
+ BIF_RET(make_small(online));
#endif
} else if (ERTS_IS_ATOM_STR("schedulers_active", BIF_ARG_1)) {
#ifndef ERTS_SMP
BIF_RET(make_small(1));
#else
- Uint total, online, active;
- switch (erts_schedulers_state(&total, &online, &active, NULL, NULL, NULL, 1)) {
- case ERTS_SCHDLR_SSPND_DONE:
- BIF_RET(make_small(active));
- case ERTS_SCHDLR_SSPND_YIELD_RESTART:
- ERTS_VBUMP_ALL_REDS(BIF_P);
- BIF_TRAP1(bif_export[BIF_system_info_1],
- BIF_P, BIF_ARG_1);
- default:
- ASSERT(0);
- BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
- }
+ Uint active;
+ erts_schedulers_state(NULL, NULL, &active, NULL, NULL, NULL, NULL, NULL);
+ BIF_RET(make_small(active));
#endif
#if defined(ERTS_SMP) && defined(ERTS_DIRTY_SCHEDULERS)
} else if (ERTS_IS_ATOM_STR("dirty_cpu_schedulers", BIF_ARG_1)) {
Uint dirty_cpu;
- erts_schedulers_state(NULL, NULL, NULL, &dirty_cpu, NULL, NULL, 1);
+ erts_schedulers_state(NULL, NULL, NULL, &dirty_cpu, NULL, NULL, NULL, NULL);
BIF_RET(make_small(dirty_cpu));
} else if (ERTS_IS_ATOM_STR("dirty_cpu_schedulers_online", BIF_ARG_1)) {
Uint dirty_cpu_onln;
- erts_schedulers_state(NULL, NULL, NULL, NULL, &dirty_cpu_onln, NULL, 1);
+ erts_schedulers_state(NULL, NULL, NULL, NULL, &dirty_cpu_onln, NULL, NULL, NULL);
BIF_RET(make_small(dirty_cpu_onln));
} else if (ERTS_IS_ATOM_STR("dirty_io_schedulers", BIF_ARG_1)) {
Uint dirty_io;
- erts_schedulers_state(NULL, NULL, NULL, NULL, NULL, &dirty_io, 1);
+ erts_schedulers_state(NULL, NULL, NULL, NULL, NULL, NULL, &dirty_io, NULL);
BIF_RET(make_small(dirty_io));
#endif
} else if (ERTS_IS_ATOM_STR("run_queues", BIF_ARG_1)) {
@@ -2675,7 +2741,16 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
if (erts_no_schedulers == 1)
BIF_RET(NIL);
else
- BIF_RET(erts_multi_scheduling_blockers(BIF_P));
+ BIF_RET(erts_multi_scheduling_blockers(BIF_P, 0));
+#endif
+ } else if (ERTS_IS_ATOM_STR("normal_multi_scheduling_blockers", BIF_ARG_1)) {
+#ifndef ERTS_SMP
+ BIF_RET(NIL);
+#else
+ if (erts_no_schedulers == 1)
+ BIF_RET(NIL);
+ else
+ BIF_RET(erts_multi_scheduling_blockers(BIF_P, 1));
#endif
} else if (ERTS_IS_ATOM_STR("modified_timing_level", BIF_ARG_1)) {
BIF_RET(ERTS_USE_MODIFIED_TIMING()
@@ -2725,6 +2800,9 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
#elif defined(USE_SYSTEMTAP)
DECL_AM(systemtap);
BIF_RET(AM_systemtap);
+#elif defined(USE_LTTNG)
+ DECL_AM(lttng);
+ BIF_RET(AM_lttng);
#else
BIF_RET(am_none);
#endif
@@ -2742,6 +2820,17 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
BIF_RET(am_true);
}
#endif
+ else if (BIF_ARG_1 == am_message_queue_data) {
+ switch (erts_default_spo_flags & (SPO_ON_HEAP_MSGQ|SPO_OFF_HEAP_MSGQ)) {
+ case SPO_OFF_HEAP_MSGQ:
+ BIF_RET(am_off_heap);
+ case SPO_ON_HEAP_MSGQ:
+ BIF_RET(am_on_heap);
+ default:
+ ERTS_INTERNAL_ERROR("Inconsistent message queue management state");
+ BIF_RET(am_error);
+ }
+ }
else if (ERTS_IS_ATOM_STR("compile_info",BIF_ARG_1)) {
Uint sz;
Eterm res = NIL, tup, text;
@@ -2780,6 +2869,41 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
else if (ERTS_IS_ATOM_STR("eager_check_io",BIF_ARG_1)) {
BIF_RET(erts_eager_check_io ? am_true : am_false);
}
+ else if (ERTS_IS_ATOM_STR("literal_test",BIF_ARG_1)) {
+#ifdef ERTS_HAVE_IS_IN_LITERAL_RANGE
+#ifdef ARCH_64
+ DECL_AM(range);
+ BIF_RET(AM_range);
+#else /* ARCH_32 */
+ DECL_AM(range_bitmask);
+ BIF_RET(AM_range_bitmask);
+#endif /* ARCH_32 */
+#else /* ! ERTS_HAVE_IS_IN_LITERAL_RANGE */
+ DECL_AM(tag);
+ BIF_RET(AM_tag);
+#endif
+ }
+ else if (ERTS_IS_ATOM_STR("check_process_code",BIF_ARG_1)) {
+ Eterm terms[3];
+ Sint length = 1;
+ Uint sz = 0;
+ Eterm *hp, res;
+ DECL_AM(direct_references);
+
+ terms[0] = AM_direct_references;
+#if !defined(ERTS_NEW_PURGE_STRATEGY)
+ {
+ DECL_AM(indirect_references);
+ terms[1] = AM_indirect_references;
+ terms[2] = am_copy_literals;
+ length = 3;
+ }
+#endif
+ erts_bld_list(NULL, &sz, length, terms);
+ hp = HAlloc(BIF_P, sz);
+ res = erts_bld_list(&hp, NULL, length, terms);
+ BIF_RET(res);
+ }
BIF_ERROR(BIF_P, BADARG);
}
@@ -2797,7 +2921,8 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
*/
Eterm
-erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt, Eterm item)
+erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt,
+ Eterm item)
{
Eterm res = THE_NON_VALUE;
@@ -2845,8 +2970,8 @@ erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt, Eterm ite
Eterm item;
INIT_MONITOR_INFOS(mic);
-
- erts_doforall_monitors(ERTS_P_MONITORS(prt), &collect_one_origin_monitor, &mic);
+ erts_doforall_monitors(ERTS_P_MONITORS(prt),
+ &collect_one_origin_monitor, &mic);
if (szp)
*szp += mic.sz;
@@ -2855,14 +2980,16 @@ erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt, Eterm ite
res = NIL;
for (i = 0; i < mic.mi_i; i++) {
Eterm t;
- item = STORE_NC(hpp, ohp, mic.mi[i].entity);
- t = TUPLE2(*hpp, am_process, item);
+ Eterm m_type;
+
+ item = STORE_NC(hpp, ohp, mic.mi[i].entity);
+ m_type = is_port(item) ? am_port : am_process;
+ t = TUPLE2(*hpp, m_type, item);
*hpp += 3;
res = CONS(*hpp, t, res);
*hpp += 2;
}
- }
-
+ } // hpp
DESTROY_MONITOR_INFOS(mic);
if (szp) {
@@ -2870,6 +2997,32 @@ erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt, Eterm ite
goto done;
}
}
+ else if (item == am_monitored_by) {
+ MonitorInfoCollection mic;
+ int i;
+ Eterm item;
+
+ INIT_MONITOR_INFOS(mic);
+ erts_doforall_monitors(ERTS_P_MONITORS(prt),
+ &collect_one_target_monitor, &mic);
+ if (szp)
+ *szp += mic.sz;
+
+ if (hpp) {
+ res = NIL;
+ for (i = 0; i < mic.mi_i; ++i) {
+ item = STORE_NC(hpp, ohp, mic.mi[i].entity);
+ res = CONS(*hpp, item, res);
+ *hpp += 2;
+ }
+ } // hpp
+ DESTROY_MONITOR_INFOS(mic);
+
+ if (szp) {
+ res = am_true;
+ goto done;
+ }
+ }
else if (item == am_name) {
int count = sys_strlen(prt->name);
@@ -3268,6 +3421,14 @@ BIF_RETTYPE statistics_1(BIF_ALIST_1)
szp = NULL;
hpp = &hp;
}
+#ifdef ERTS_ENABLE_MSACC
+ } else if (BIF_ARG_1 == am_microstate_accounting) {
+ Eterm threads;
+ res = erts_msacc_request(BIF_P, ERTS_MSACC_GATHER, &threads);
+ if (is_non_value(res))
+ BIF_RET(am_undefined);
+ BIF_TRAP2(gather_msacc_res_trap, BIF_P, res, threads);
+#endif
} else if (BIF_ARG_1 == am_context_switches) {
Eterm cs = erts_make_integer(erts_get_total_context_switches(), BIF_P);
hp = HAlloc(BIF_P, 3);
@@ -3522,10 +3683,7 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
/* Used by timer process_SUITE, timer_bif_SUITE, and
node_container_SUITE (emulator) */
if (is_internal_pid(tp[2])) {
- BIF_RET(erts_process_status(BIF_P,
- ERTS_PROC_LOCK_MAIN,
- NULL,
- tp[2]));
+ BIF_RET(erts_process_status(NULL, tp[2]));
}
}
else if (ERTS_IS_ATOM_STR("link_list", tp[1])) {
@@ -3910,9 +4068,7 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2)
BIF_RET(am_false);
}
else {
- FLAGS(rp) |= F_FORCE_GC;
- if (BIF_P != rp)
- erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_MAIN);
+ ERTS_FORCE_GC(BIF_P);
BIF_RET(am_true);
}
}
@@ -4131,6 +4287,17 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2)
BIF_RET(am_ok);
}
}
+ else if (ERTS_IS_ATOM_STR("fill_heap", BIF_ARG_1)) {
+ UWord left = HeapWordsLeft(BIF_P);
+ if (left > 1) {
+ Eterm* hp = HAlloc(BIF_P, left);
+ *hp = make_pos_bignum_header(left - 1);
+ }
+ if (BIF_ARG_2 == am_true) {
+ FLAGS(BIF_P) |= F_NEED_FULLSWEEP;
+ }
+ BIF_RET(am_ok);
+ }
}
BIF_ERROR(BIF_P, BADARG);
@@ -4380,14 +4547,17 @@ static void os_info_init(void)
os_flavor(buf, 1024);
flav = erts_atom_put((byte *) buf, strlen(buf), ERTS_ATOM_ENC_LATIN1, 1);
erts_free(ERTS_ALC_T_TMP, (void *) buf);
- hp = erts_alloc(ERTS_ALC_T_LL_TEMP_TERM, (3+4)*sizeof(Eterm));
+ hp = erts_alloc(ERTS_ALC_T_LITERAL, (3+4)*sizeof(Eterm));
os_type_tuple = TUPLE2(hp, type, flav);
+ erts_set_literal_tag(&os_type_tuple, hp, 3);
+
hp += 3;
os_version(&major, &minor, &build);
os_version_tuple = TUPLE3(hp,
make_small(major),
make_small(minor),
make_small(build));
+ erts_set_literal_tag(&os_version_tuple, hp, 4);
}
void
@@ -4404,6 +4574,8 @@ erts_bif_info_init(void)
= erts_export_put(am_erlang, am_gather_gc_info_result, 1);
gather_io_bytes_trap
= erts_export_put(am_erts_internal, am_gather_io_bytes, 2);
+ gather_msacc_res_trap
+ = erts_export_put(am_erts_internal, am_gather_microstate_accounting_result, 2);
gather_system_check_res_trap
= erts_export_put(am_erts_internal, am_gather_system_check_result, 1);
process_info_init();
diff --git a/erts/emulator/beam/erl_bif_lists.c b/erts/emulator/beam/erl_bif_lists.c
index 5583dcb371..73d327da3e 100644
--- a/erts/emulator/beam/erl_bif_lists.c
+++ b/erts/emulator/beam/erl_bif_lists.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -40,32 +40,93 @@ static BIF_RETTYPE append(Process* p, Eterm A, Eterm B)
Eterm list;
Eterm copy;
Eterm last;
- size_t need;
- Eterm* hp;
- int i;
+ Eterm* hp = NULL;
+ Sint i;
- if ((i = erts_list_length(A)) < 0) {
- BIF_ERROR(p, BADARG);
+ list = A;
+
+ if (is_nil(list)) {
+ BIF_RET(B);
}
- if (i == 0) {
- BIF_RET(B);
- } else if (is_nil(B)) {
- BIF_RET(A);
+
+ if (is_not_list(list)) {
+ BIF_ERROR(p, BADARG);
}
- need = 2*i;
- hp = HAlloc(p, need);
- list = A;
+ /* optimistic append on heap first */
+
+ if ((i = HeapWordsLeft(p) / 2) < 4) {
+ goto list_tail;
+ }
+
+ hp = HEAP_TOP(p);
copy = last = CONS(hp, CAR(list_val(list)), make_list(hp+2));
list = CDR(list_val(list));
- hp += 2;
+ hp += 2;
+ i -= 2; /* don't use the last 2 words (extra i--;) */
+
+ while(i-- && is_list(list)) {
+ Eterm* listp = list_val(list);
+ last = CONS(hp, CAR(listp), make_list(hp+2));
+ list = CDR(listp);
+ hp += 2;
+ }
+
+ /* A is proper and B is NIL return A as-is, don't update HTOP */
+
+ if (is_nil(list) && is_nil(B)) {
+ BIF_RET(A);
+ }
+
+ if (is_nil(list)) {
+ HEAP_TOP(p) = hp;
+ CDR(list_val(last)) = B;
+ BIF_RET(copy);
+ }
+
+list_tail:
+
+ if ((i = erts_list_length(list)) < 0) {
+ BIF_ERROR(p, BADARG);
+ }
+
+ /* remaining list was proper and B is NIL */
+ if (is_nil(B)) {
+ BIF_RET(A);
+ }
+
+ if (hp) {
+ /* Note: fall through case, already written
+ * on the heap.
+ * The last 2 words of the heap is not written yet
+ */
+ Eterm *hp_save = hp;
+ ASSERT(i != 0);
+ HEAP_TOP(p) = hp + 2;
+ if (i == 1) {
+ hp[0] = CAR(list_val(list));
+ hp[1] = B;
+ BIF_RET(copy);
+ }
+ hp = HAlloc(p, 2*(i - 1));
+ last = CONS(hp_save, CAR(list_val(list)), make_list(hp));
+ } else {
+ hp = HAlloc(p, 2*i);
+ copy = last = CONS(hp, CAR(list_val(list)), make_list(hp+2));
+ hp += 2;
+ }
+
+ list = CDR(list_val(list));
i--;
+
+ ASSERT(i > -1);
while(i--) {
- Eterm* listp = list_val(list);
- last = CONS(hp, CAR(listp), make_list(hp+2));
- list = CDR(listp);
- hp += 2;
+ Eterm* listp = list_val(list);
+ last = CONS(hp, CAR(listp), make_list(hp+2));
+ list = CDR(listp);
+ hp += 2;
}
+
CDR(list_val(last)) = B;
BIF_RET(copy);
}
@@ -99,9 +160,9 @@ static Eterm subtract(Process* p, Eterm A, Eterm B)
Eterm small_vec[SMALL_VEC_SIZE]; /* Preallocated memory for small lists */
Eterm* vec_p;
Eterm* vp;
- int i;
- int n;
- int m;
+ Sint i;
+ Sint n;
+ Sint m;
if ((n = erts_list_length(A)) < 0) {
BIF_ERROR(p, BADARG);
diff --git a/erts/emulator/beam/erl_bif_op.c b/erts/emulator/beam/erl_bif_op.c
index c9192fc420..aecb8bf0c1 100644
--- a/erts/emulator/beam/erl_bif_op.c
+++ b/erts/emulator/beam/erl_bif_op.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -89,22 +89,22 @@ BIF_RETTYPE not_1(BIF_ALIST_1)
BIF_RETTYPE sgt_2(BIF_ALIST_2)
{
- BIF_RET(cmp_gt(BIF_ARG_1, BIF_ARG_2) ? am_true : am_false);
+ BIF_RET(CMP_GT(BIF_ARG_1, BIF_ARG_2) ? am_true : am_false);
}
BIF_RETTYPE sge_2(BIF_ALIST_2)
{
- BIF_RET(cmp_ge(BIF_ARG_1, BIF_ARG_2) ? am_true : am_false);
+ BIF_RET(CMP_GE(BIF_ARG_1, BIF_ARG_2) ? am_true : am_false);
}
BIF_RETTYPE slt_2(BIF_ALIST_2)
{
- BIF_RET(cmp_lt(BIF_ARG_1, BIF_ARG_2) ? am_true : am_false);
+ BIF_RET(CMP_LT(BIF_ARG_1, BIF_ARG_2) ? am_true : am_false);
}
BIF_RETTYPE sle_2(BIF_ALIST_2)
{
- BIF_RET(cmp_le(BIF_ARG_1, BIF_ARG_2) ? am_true : am_false);
+ BIF_RET(CMP_LE(BIF_ARG_1, BIF_ARG_2) ? am_true : am_false);
}
BIF_RETTYPE seq_2(BIF_ALIST_2)
@@ -114,7 +114,7 @@ BIF_RETTYPE seq_2(BIF_ALIST_2)
BIF_RETTYPE seqeq_2(BIF_ALIST_2)
{
- BIF_RET(cmp_eq(BIF_ARG_1, BIF_ARG_2) ? am_true : am_false);
+ BIF_RET(CMP_EQ(BIF_ARG_1, BIF_ARG_2) ? am_true : am_false);
}
BIF_RETTYPE sneq_2(BIF_ALIST_2)
@@ -124,7 +124,7 @@ BIF_RETTYPE sneq_2(BIF_ALIST_2)
BIF_RETTYPE sneqeq_2(BIF_ALIST_2)
{
- BIF_RET(cmp_ne(BIF_ARG_1, BIF_ARG_2) ? am_true : am_false);
+ BIF_RET(CMP_NE(BIF_ARG_1, BIF_ARG_2) ? am_true : am_false);
}
BIF_RETTYPE is_atom_1(BIF_ALIST_1)
@@ -258,7 +258,7 @@ Eterm erl_is_function(Process* p, Eterm arg1, Eterm arg2)
BIF_RET(am_true);
}
} else if (is_export(arg1)) {
- Export* exp = (Export *) EXPAND_POINTER((export_val(arg1))[1]);
+ Export* exp = (Export *) (export_val(arg1)[1]);
if (exp->code[2] == (Uint) arity) {
BIF_RET(am_true);
diff --git a/erts/emulator/beam/erl_bif_os.c b/erts/emulator/beam/erl_bif_os.c
index 2333ca0851..46777d3aa5 100644
--- a/erts/emulator/beam/erl_bif_os.c
+++ b/erts/emulator/beam/erl_bif_os.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2012. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c
index 27c24197ea..90e78a9b0b 100644
--- a/erts/emulator/beam/erl_bif_port.c
+++ b/erts/emulator/beam/erl_bif_port.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -41,6 +41,7 @@
#include "external.h"
#include "packet_parser.h"
#include "erl_bits.h"
+#include "erl_bif_unique.h"
#include "dtrace-wrapper.h"
static Port *open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump);
@@ -50,10 +51,10 @@ static void free_args(char **);
char *erts_default_arg0 = "default";
-BIF_RETTYPE open_port_2(BIF_ALIST_2)
+BIF_RETTYPE erts_internal_open_port_2(BIF_ALIST_2)
{
Port *port;
- Eterm port_id;
+ Eterm res;
char *str;
int err_type, err_num;
@@ -61,27 +62,63 @@ BIF_RETTYPE open_port_2(BIF_ALIST_2)
if (!port) {
if (err_type == -3) {
ASSERT(err_num == BADARG || err_num == SYSTEM_LIMIT);
- BIF_ERROR(BIF_P, err_num);
+ if (err_num == BADARG)
+ res = am_badarg;
+ else if (err_num == SYSTEM_LIMIT)
+ res = am_system_limit;
+ else
+ /* this is only here to silence gcc, it should not happen */
+ BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
} else if (err_type == -2) {
str = erl_errno_id(err_num);
+ res = erts_atom_put((byte *) str, strlen(str), ERTS_ATOM_ENC_LATIN1, 1);
} else {
- str = "einval";
+ res = am_einval;
}
- BIF_P->fvalue = erts_atom_put((byte *) str, strlen(str), ERTS_ATOM_ENC_LATIN1, 1);
- BIF_ERROR(BIF_P, EXC_ERROR);
+ BIF_RET(res);
}
- erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK);
+ if (port->drv_ptr->flags & ERL_DRV_FLAG_USE_INIT_ACK) {
+
+ /* Copied from erl_port_task.c */
+ port->async_open_port = erts_alloc(ERTS_ALC_T_PRTSD,
+ sizeof(*port->async_open_port));
+ erts_make_ref_in_array(port->async_open_port->ref);
+ port->async_open_port->to = BIF_P->common.id;
+
+ erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCKS_MSG_RECEIVE | ERTS_PROC_LOCK_LINK);
+ if (ERTS_PROC_PENDING_EXIT(BIF_P)) {
+ /* need to exit caller instead */
+ erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCKS_MSG_RECEIVE | ERTS_PROC_LOCK_LINK);
+ KILL_CATCHES(BIF_P);
+ BIF_P->freason = EXC_EXIT;
+ erts_port_release(port);
+ BIF_RET(am_badarg);
+ }
+
+ ERTS_SMP_MSGQ_MV_INQ2PRIVQ(BIF_P);
+ BIF_P->msg.save = BIF_P->msg.last;
+
+ erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCKS_MSG_RECEIVE);
+
+ res = erts_proc_store_ref(BIF_P, port->async_open_port->ref);
+ } else {
+ res = port->common.id;
+ erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK);
+ }
- port_id = port->common.id;
erts_add_link(&ERTS_P_LINKS(port), LINK_PID, BIF_P->common.id);
- erts_add_link(&ERTS_P_LINKS(BIF_P), LINK_PID, port_id);
+ erts_add_link(&ERTS_P_LINKS(BIF_P), LINK_PID, port->common.id);
+
+ if (IS_TRACED_FL(BIF_P, F_TRACE_PROCS))
+ trace_proc(BIF_P, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK, BIF_P,
+ am_link, port->common.id);
erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK);
erts_port_release(port);
- BIF_RET(port_id);
+ BIF_RET(res);
}
static ERTS_INLINE Port *
@@ -102,6 +139,12 @@ sig_lookup_port(Process *c_p, Eterm id_or_name)
return lookup_port(c_p, id_or_name, ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP);
}
+/* Non-inline copy of sig_lookup_port to be exported */
+Port *erts_sig_lookup_port(Process *c_p, Eterm id_or_name)
+{
+ return lookup_port(c_p, id_or_name, ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP);
+}
+
static ERTS_INLINE Port *
data_lookup_port(Process *c_p, Eterm id_or_name)
{
@@ -307,8 +350,7 @@ BIF_RETTYPE erts_internal_port_close_1(BIF_ALIST_1)
if (!prt)
BIF_RET(am_badarg);
-
- switch (erts_port_exit(BIF_P, 0, prt, prt->common.id, am_normal, &ref)) {
+ switch (erts_port_exit(BIF_P, 0, prt, BIF_P->common.id, am_normal, &ref)) {
case ERTS_PORT_OP_CALLER_EXIT:
case ERTS_PORT_OP_BADARG:
case ERTS_PORT_OP_DROPPED:
@@ -341,7 +383,7 @@ BIF_RETTYPE erts_internal_port_connect_2(BIF_ALIST_2)
ref = NIL;
#endif
- switch (erts_port_connect(BIF_P, 0, prt, prt->common.id, BIF_ARG_2, &ref)) {
+ switch (erts_port_connect(BIF_P, 0, prt, BIF_P->common.id, BIF_ARG_2, &ref)) {
case ERTS_PORT_OP_CALLER_EXIT:
case ERTS_PORT_OP_BADARG:
case ERTS_PORT_OP_DROPPED:
@@ -617,7 +659,7 @@ BIF_RETTYPE port_get_data_1(BIF_ALIST_1)
static Port *
open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump)
{
- int i;
+ Sint i;
Eterm option;
Uint arity;
Eterm* tp;
@@ -879,7 +921,7 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump)
}
if (IS_TRACED_FL(p, F_TRACE_SCHED_PROCS)) {
- trace_virtual_sched(p, am_out);
+ trace_sched(p, ERTS_PROC_LOCK_MAIN, am_out);
}
@@ -896,21 +938,22 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump)
DTRACE3(port_open, process_str, name_buf, port_str);
}
#endif
+
+ if (port && IS_TRACED_FL(port, F_TRACE_PORTS))
+ trace_port(port, am_getting_linked, p->common.id);
+
erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN);
+ if (IS_TRACED_FL(p, F_TRACE_SCHED_PROCS)) {
+ trace_sched(p, ERTS_PROC_LOCK_MAIN, am_in);
+ }
+
if (!port) {
DEBUGF(("open_driver returned (%d:%d)\n",
err_typep ? *err_typep : 4711,
err_nump ? *err_nump : 4711));
- if (IS_TRACED_FL(p, F_TRACE_SCHED_PROCS)) {
- trace_virtual_sched(p, am_in);
- }
goto do_return;
}
-
- if (IS_TRACED_FL(p, F_TRACE_SCHED_PROCS)) {
- trace_virtual_sched(p, am_in);
- }
if (linebuf && port->linebuf == NULL){
port->linebuf = allocate_linebuf(linebuf);
@@ -945,14 +988,16 @@ static char **convert_args(Eterm l)
{
char **pp;
char *b;
- int n;
- int i = 0;
+ Sint n;
+ Sint i = 0;
Eterm str;
if (is_not_list(l) && is_not_nil(l)) {
return NULL;
}
n = erts_list_length(l);
+ if (n < 0)
+ return NULL;
/* We require at least one element in argv[0] + NULL at end */
pp = erts_alloc(ERTS_ALC_T_TMP, (n + 2) * sizeof(char **));
pp[i++] = erts_default_arg0;
@@ -992,7 +1037,7 @@ static byte* convert_environment(Process* p, Eterm env)
Eterm* temp_heap;
Eterm* hp;
Uint heap_size;
- int n;
+ Sint n;
Sint size;
byte* bytes;
int encoding = erts_get_native_filename_encoding();
@@ -1372,7 +1417,7 @@ BIF_RETTYPE decode_packet_3(BIF_ALIST_3)
trunc_len = val;
goto next_option;
case am_line_delimiter:
- if (type == TCP_PB_LINE_LF && val >= 0 && val <= 255) {
+ if (type == TCP_PB_LINE_LF && val <= 255) {
delimiter = (char)val;
goto next_option;
}
diff --git a/erts/emulator/beam/erl_bif_re.c b/erts/emulator/beam/erl_bif_re.c
index 86951f32b0..ff7746ce1d 100644
--- a/erts/emulator/beam/erl_bif_re.c
+++ b/erts/emulator/beam/erl_bif_re.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -100,7 +100,7 @@ Sint erts_re_set_loop_limit(Sint limit)
static int term_to_int(Eterm term, int *sp)
{
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
if (is_small(term)) {
Uint x = signed_val(term);
@@ -151,7 +151,7 @@ static int term_to_int(Eterm term, int *sp)
static Eterm make_signed_integer(int x, Process *p)
{
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
return make_small(x);
#else
Eterm* hp;
@@ -630,9 +630,15 @@ static Eterm build_exec_return(Process *p, int rc, RestartContext *restartp, Ete
}
} else {
ReturnInfo *ri;
- ReturnInfo defri = {RetIndex,0,{0}};
+ ReturnInfo defri;
if (restartp->ret_info == NULL) {
+ /* OpenBSD 5.8 gcc compiler for some reason creates
+ bad code if the above initialization is done
+ inline with the struct. So don't do that. */
+ defri.type = RetIndex;
+ defri.num_spec = 0;
+ defri.v[0] = 0;
ri = &defri;
} else {
ri = restartp->ret_info;
diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c
index 08807d72c9..4fed09f085 100644
--- a/erts/emulator/beam/erl_bif_trace.c
+++ b/erts/emulator/beam/erl_bif_trace.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2013. 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.
@@ -52,7 +52,7 @@ static int erts_default_trace_pattern_is_on;
static Binary *erts_default_match_spec;
static Binary *erts_default_meta_match_spec;
static struct trace_pattern_flags erts_default_trace_pattern_flags;
-static Eterm erts_default_meta_tracer_pid;
+static ErtsTracer erts_default_meta_tracer;
static struct { /* Protected by code write permission */
int current;
@@ -68,6 +68,9 @@ static struct { /* Protected by code write permission */
static Eterm
trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist);
+static int
+erts_set_tracing_event_pattern(Eterm event, Binary*, int on);
+
#ifdef ERTS_SMP
static void smp_bp_finisher(void* arg);
#endif
@@ -75,11 +78,11 @@ static BIF_RETTYPE
system_monitor(Process *p, Eterm monitor_pid, Eterm list);
static void new_seq_trace_token(Process* p); /* help func for seq_trace_2*/
-static int already_traced(Process *p, Process *tracee_p, Eterm tracer);
-static int port_already_traced(Process *p, Port *tracee_port, Eterm tracer);
static Eterm trace_info_pid(Process* p, Eterm pid_spec, Eterm key);
static Eterm trace_info_func(Process* p, Eterm pid_spec, Eterm key);
static Eterm trace_info_on_load(Process* p, Eterm key);
+static Eterm trace_info_event(Process* p, Eterm event, Eterm key);
+
static void reset_bif_trace(void);
static void setup_bif_trace(void);
@@ -87,28 +90,34 @@ static void install_exp_breakpoints(BpFunctions* f);
static void uninstall_exp_breakpoints(BpFunctions* f);
static void clean_export_entries(BpFunctions* f);
+ErtsTracingEvent erts_send_tracing[ERTS_NUM_BP_IX];
+ErtsTracingEvent erts_receive_tracing[ERTS_NUM_BP_IX];
+
void
erts_bif_trace_init(void)
{
+ int i;
+
erts_default_trace_pattern_is_on = 0;
erts_default_match_spec = NULL;
erts_default_meta_match_spec = NULL;
erts_default_trace_pattern_flags = erts_trace_pattern_flags_off;
- erts_default_meta_tracer_pid = NIL;
+ erts_default_meta_tracer = erts_tracer_nil;
+
+ for (i=0; i<ERTS_NUM_BP_IX; i++) {
+ erts_send_tracing[i].on = 1;
+ erts_send_tracing[i].match_spec = NULL;
+ erts_receive_tracing[i].on = 1;
+ erts_receive_tracing[i].match_spec = NULL;
+ }
}
/*
* Turn on/off call tracing for the given function(s).
*/
-
-Eterm
-trace_pattern_2(BIF_ALIST_2)
-{
- return trace_pattern(BIF_P, BIF_ARG_1, BIF_ARG_2, NIL);
-}
Eterm
-trace_pattern_3(BIF_ALIST_3)
+erts_internal_trace_pattern_3(BIF_ALIST_3)
{
return trace_pattern(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
}
@@ -125,11 +134,10 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist)
Eterm l;
struct trace_pattern_flags flags = erts_trace_pattern_flags_off;
int is_global;
- Process *meta_tracer_proc = p;
- Eterm meta_tracer_pid = p->common.id;
+ ErtsTracer meta_tracer = erts_tracer_nil;
if (!erts_try_seize_code_write_permission(p)) {
- ERTS_BIF_YIELD3(bif_export[BIF_trace_pattern_3], p, MFA, Pattern, flaglist);
+ ERTS_BIF_YIELD3(bif_export[BIF_erts_internal_trace_pattern_3], p, MFA, Pattern, flaglist);
}
finish_bp.current = -1;
@@ -146,45 +154,28 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist)
on = 1;
} else if (Pattern == am_restart) {
match_prog_set = NULL;
- on = erts_break_reset;
+ on = ERTS_BREAK_RESTART;
} else if (Pattern == am_pause) {
match_prog_set = NULL;
- on = erts_break_stop;
- } else if ((match_prog_set = erts_match_set_compile(p, Pattern)) != NULL) {
- MatchSetRef(match_prog_set);
- on = 1;
- } else{
- goto error;
+ on = ERTS_BREAK_PAUSE;
+ } else {
+ match_prog_set = erts_match_set_compile(p, Pattern, MFA);
+ if (match_prog_set) {
+ MatchSetRef(match_prog_set);
+ on = 1;
+ } else{
+ goto error;
+ }
}
is_global = 0;
for(l = flaglist; is_list(l); l = CDR(list_val(l))) {
if (is_tuple(CAR(list_val(l)))) {
- Eterm *tp = tuple_val(CAR(list_val(l)));
-
- if (arityval(tp[0]) != 2 || tp[1] != am_meta) {
- goto error;
- }
- meta_tracer_pid = tp[2];
- if (is_internal_pid(meta_tracer_pid)) {
- meta_tracer_proc = erts_pid2proc(NULL, 0, meta_tracer_pid, 0);
- if (!meta_tracer_proc) {
- goto error;
- }
- } else if (is_internal_port(meta_tracer_pid)) {
- Port *meta_tracer_port;
- meta_tracer_proc = NULL;
- meta_tracer_port = (erts_port_lookup(
- meta_tracer_pid,
- ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP));
- if (!meta_tracer_port)
- goto error;
- } else {
- goto error;
- }
- if (is_global) {
- goto error;
- }
+ meta_tracer = erts_term_to_tracer(am_meta, CAR(list_val(l)));
+ if (meta_tracer == THE_NON_VALUE) {
+ meta_tracer = erts_tracer_nil;
+ goto error;
+ }
flags.breakpoint = 1;
flags.meta = 1;
} else {
@@ -202,6 +193,8 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist)
}
flags.breakpoint = 1;
flags.meta = 1;
+ if (ERTS_TRACER_IS_NIL(meta_tracer))
+ meta_tracer = erts_term_to_tracer(THE_NON_VALUE, p->common.id);
break;
case am_global:
if (flags.breakpoint) {
@@ -252,14 +245,11 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist)
MatchSetUnref(erts_default_meta_match_spec);
erts_default_meta_match_spec = match_prog_set;
MatchSetRef(erts_default_meta_match_spec);
- erts_default_meta_tracer_pid = meta_tracer_pid;
- if (meta_tracer_proc) {
- ERTS_TRACE_FLAGS(meta_tracer_proc) |= F_TRACER;
- }
+ erts_tracer_update(&erts_default_meta_tracer, meta_tracer);
} else if (! flags.breakpoint) {
MatchSetUnref(erts_default_meta_match_spec);
erts_default_meta_match_spec = NULL;
- erts_default_meta_tracer_pid = NIL;
+ ERTS_TRACER_CLEAR(&erts_default_meta_tracer);
}
if (erts_default_trace_pattern_flags.breakpoint &&
flags.breakpoint) {
@@ -340,20 +330,23 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist)
if (is_small(mfa[2])) {
mfa[2] = signed_val(mfa[2]);
}
-
- if (meta_tracer_proc) {
- ERTS_TRACE_FLAGS(meta_tracer_proc) |= F_TRACER;
- }
matches = erts_set_trace_pattern(p, mfa, specified,
match_prog_set, match_prog_set,
- on, flags, meta_tracer_pid, 0);
+ on, flags, meta_tracer, 0);
+ } else if (is_atom(MFA)) {
+ if (is_global || flags.breakpoint || on > ERTS_BREAK_SET) {
+ goto error;
+ }
+ matches = erts_set_tracing_event_pattern(MFA, match_prog_set, on);
}
error:
MatchSetUnref(match_prog_set);
UnUseTmpHeap(3,p);
+ ERTS_TRACER_CLEAR(&meta_tracer);
+
#ifdef ERTS_SMP
if (finish_bp.current >= 0) {
ASSERT(matches >= 0);
@@ -404,7 +397,7 @@ erts_get_default_trace_pattern(int *trace_pattern_is_on,
Binary **match_spec,
Binary **meta_match_spec,
struct trace_pattern_flags *trace_pattern_flags,
- Eterm *meta_tracer_pid)
+ ErtsTracer *meta_tracer)
{
ERTS_SMP_LC_ASSERT(erts_has_code_write_permission() ||
erts_smp_thr_progress_is_blocking());
@@ -416,8 +409,8 @@ erts_get_default_trace_pattern(int *trace_pattern_is_on,
*meta_match_spec = erts_default_meta_match_spec;
if (trace_pattern_flags)
*trace_pattern_flags = erts_default_trace_pattern_flags;
- if (meta_tracer_pid)
- *meta_tracer_pid = erts_default_meta_tracer_pid;
+ if (meta_tracer)
+ *meta_tracer = erts_default_meta_tracer;
}
int erts_is_default_trace_enabled(void)
@@ -465,12 +458,12 @@ erts_trace_flag2bit(Eterm flag)
** occurred in the argument list.
*/
int
-erts_trace_flags(Eterm List,
- Uint *pMask, Eterm *pTracer, int *pCpuTimestamp)
+erts_trace_flags(Eterm List,
+ Uint *pMask, ErtsTracer *pTracer, int *pCpuTimestamp)
{
Eterm list = List;
Uint mask = 0;
- Eterm tracer = NIL;
+ ErtsTracer tracer = erts_tracer_nil;
int cpu_timestamp = 0;
while (is_list(list)) {
@@ -483,33 +476,72 @@ erts_trace_flags(Eterm List,
cpu_timestamp = !0;
#endif
} else if (is_tuple(item)) {
- Eterm* tp = tuple_val(item);
-
- if (arityval(tp[0]) != 2 || tp[1] != am_tracer) goto error;
- if (is_internal_pid(tp[2]) || is_internal_port(tp[2])) {
- tracer = tp[2];
- } else goto error;
+ tracer = erts_term_to_tracer(am_tracer, item);
+ if (tracer == THE_NON_VALUE)
+ goto error;
} else goto error;
list = CDR(list_val(list));
}
if (is_not_nil(list)) goto error;
- if (pMask && mask) *pMask = mask;
- if (pTracer && tracer != NIL) *pTracer = tracer;
- if (pCpuTimestamp && cpu_timestamp) *pCpuTimestamp = cpu_timestamp;
+ if (pMask && mask) *pMask = mask;
+ if (pTracer && !ERTS_TRACER_IS_NIL(tracer)) *pTracer = tracer;
+ if (pCpuTimestamp && cpu_timestamp) *pCpuTimestamp = cpu_timestamp;
return !0;
error:
return 0;
}
-Eterm trace_3(BIF_ALIST_3)
+static ERTS_INLINE int
+start_trace(Process *c_p, ErtsTracer tracer,
+ ErtsPTabElementCommon *common,
+ int on, int mask)
+{
+ /* We can use the common part of both port+proc without checking what it is
+ In the code below port is used for both proc and port */
+ Port *port = (Port*)common;
+
+ /*
+ * SMP build assumes that either system is blocked or:
+ * * main lock is held on c_p
+ * * all locks are held on port common
+ */
+
+ if (!ERTS_TRACER_IS_NIL(tracer)) {
+ if ((ERTS_TRACE_FLAGS(port) & TRACEE_FLAGS)
+ && !ERTS_TRACER_COMPARE(ERTS_TRACER(port), tracer)) {
+ /* This tracee is already being traced, and not by the
+ * tracer to be */
+ if (erts_is_tracer_enabled(ERTS_TRACER(port), common)) {
+ /* The tracer is still in use */
+ return 1;
+ }
+ /* Current tracer now invalid */
+ }
+ }
+
+ if (on)
+ ERTS_TRACE_FLAGS(port) |= mask;
+ else
+ ERTS_TRACE_FLAGS(port) &= ~mask;
+
+ if ((ERTS_TRACE_FLAGS(port) & TRACEE_FLAGS) == 0) {
+ tracer = erts_tracer_nil;
+ erts_tracer_replace(common, erts_tracer_nil);
+ } else if (!ERTS_TRACER_IS_NIL(tracer))
+ erts_tracer_replace(common, tracer);
+
+ return 0;
+}
+
+Eterm erts_internal_trace_3(BIF_ALIST_3)
{
Process* p = BIF_P;
Eterm pid_spec = BIF_ARG_1;
Eterm how = BIF_ARG_2;
Eterm list = BIF_ARG_3;
int on;
- Eterm tracer = NIL;
+ ErtsTracer tracer = erts_tracer_nil;
int matches = 0;
Uint mask = 0;
int cpu_ts = 0;
@@ -522,41 +554,24 @@ Eterm trace_3(BIF_ALIST_3)
}
if (!erts_try_seize_code_write_permission(BIF_P)) {
- ERTS_BIF_YIELD3(bif_export[BIF_trace_3], BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
+ ERTS_BIF_YIELD3(bif_export[BIF_erts_internal_trace_3],
+ BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
}
- if (is_nil(tracer) || is_internal_pid(tracer)) {
- Process *tracer_proc = erts_pid2proc(p,
- ERTS_PROC_LOCK_MAIN,
- is_nil(tracer) ? p->common.id : tracer,
- ERTS_PROC_LOCKS_ALL);
- if (!tracer_proc)
- goto error;
- ERTS_TRACE_FLAGS(tracer_proc) |= F_TRACER;
- erts_smp_proc_unlock(tracer_proc,
- (tracer_proc == p
- ? ERTS_PROC_LOCKS_ALL_MINOR
- : ERTS_PROC_LOCKS_ALL));
- } else if (is_internal_port(tracer)) {
- Port *tracer_port = erts_id2port_sflgs(tracer,
- p,
- ERTS_PROC_LOCK_MAIN,
- ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP);
- if (!tracer_port)
- goto error;
- ERTS_TRACE_FLAGS(tracer_port) |= F_TRACER;
- erts_port_release(tracer_port);
- } else
- goto error;
-
switch (how) {
case am_false:
on = 0;
break;
case am_true:
on = 1;
- if (is_nil(tracer))
- tracer = p->common.id;
+ if (ERTS_TRACER_IS_NIL(tracer))
+ tracer = erts_term_to_tracer(am_tracer, p->common.id);
+
+ if (tracer == THE_NON_VALUE) {
+ tracer = erts_tracer_nil;
+ goto error;
+ }
+
break;
default:
goto error;
@@ -575,34 +590,20 @@ Eterm trace_3(BIF_ALIST_3)
}
#endif
- if (pid_spec == tracer)
- goto error;
-
tracee_port = erts_id2port_sflgs(pid_spec,
p,
ERTS_PROC_LOCK_MAIN,
ERTS_PORT_SFLGS_INVALID_LOOKUP);
+
if (!tracee_port)
goto error;
-
- if (tracer != NIL && port_already_traced(p, tracee_port, tracer)) {
+
+ if (start_trace(p, tracer, &tracee_port->common, on, mask)) {
erts_port_release(tracee_port);
goto already_traced;
- }
-
- if (on)
- ERTS_TRACE_FLAGS(tracee_port) |= mask;
- else
- ERTS_TRACE_FLAGS(tracee_port) &= ~mask;
-
- if (!ERTS_TRACE_FLAGS(tracee_port))
- ERTS_TRACER_PROC(tracee_port) = NIL;
- else if (tracer != NIL)
- ERTS_TRACER_PROC(tracee_port) = tracer;
-
- erts_port_release(tracee_port);
-
- matches = 1;
+ }
+ erts_port_release(tracee_port);
+ matches = 1;
} else if (is_pid(pid_spec)) {
Process *tracee_p;
@@ -615,33 +616,19 @@ Eterm trace_3(BIF_ALIST_3)
* and not about to be tracing.
*/
- if (pid_spec == tracer)
- goto error;
-
tracee_p = erts_pid2proc(p, ERTS_PROC_LOCK_MAIN,
pid_spec, ERTS_PROC_LOCKS_ALL);
if (!tracee_p)
goto error;
- if (tracer != NIL && already_traced(p, tracee_p, tracer)) {
+ if (start_trace(tracee_p, tracer, &tracee_p->common, on, mask)) {
erts_smp_proc_unlock(tracee_p,
(tracee_p == p
? ERTS_PROC_LOCKS_ALL_MINOR
: ERTS_PROC_LOCKS_ALL));
goto already_traced;
- }
-
- if (on)
- ERTS_TRACE_FLAGS(tracee_p) |= mask;
- else
- ERTS_TRACE_FLAGS(tracee_p) &= ~mask;
-
- if ((ERTS_TRACE_FLAGS(tracee_p) & TRACEE_FLAGS) == 0)
- ERTS_TRACER_PROC(tracee_p) = NIL;
- else if (tracer != NIL)
- ERTS_TRACER_PROC(tracee_p) = tracer;
-
- erts_smp_proc_unlock(tracee_p,
+ }
+ erts_smp_proc_unlock(tracee_p,
(tracee_p == p
? ERTS_PROC_LOCKS_ALL_MINOR
: ERTS_PROC_LOCKS_ALL));
@@ -692,18 +679,27 @@ Eterm trace_3(BIF_ALIST_3)
}
#endif
- if (pid_spec == am_all || pid_spec == am_existing) {
+ if (pid_spec == am_all || pid_spec == am_existing ||
+ pid_spec == am_ports || pid_spec == am_processes ||
+ pid_spec == am_existing_ports || pid_spec == am_existing_processes
+ ) {
int i;
int procs = 0;
int ports = 0;
int mods = 0;
if (mask & (ERTS_PROC_TRACEE_FLAGS & ~ERTS_TRACEE_MODIFIER_FLAGS))
- procs = 1;
+ procs = pid_spec != am_ports && pid_spec != am_existing_ports;
if (mask & (ERTS_PORT_TRACEE_FLAGS & ~ERTS_TRACEE_MODIFIER_FLAGS))
- ports = 1;
- if (mask & ERTS_TRACEE_MODIFIER_FLAGS)
- mods = 1;
+ ports = pid_spec != am_processes && pid_spec != am_existing_processes;
+ if (mask & ERTS_TRACEE_MODIFIER_FLAGS) {
+ if (pid_spec == am_ports || pid_spec == am_existing_ports)
+ ports = 1;
+ else if (pid_spec == am_processes || pid_spec == am_existing_processes)
+ procs = 1;
+ else
+ mods = 1;
+ }
#ifdef ERTS_SMP
erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN);
@@ -719,23 +715,8 @@ Eterm trace_3(BIF_ALIST_3)
Process* tracee_p = erts_pix2proc(i);
if (! tracee_p)
continue;
- if (tracer != NIL) {
- if (tracee_p->common.id == tracer)
- continue;
- if (already_traced(NULL, tracee_p, tracer))
- continue;
- }
- if (on) {
- ERTS_TRACE_FLAGS(tracee_p) |= mask;
- } else {
- ERTS_TRACE_FLAGS(tracee_p) &= ~mask;
- }
- if(!(ERTS_TRACE_FLAGS(tracee_p) & TRACEE_FLAGS)) {
- ERTS_TRACER_PROC(tracee_p) = NIL;
- } else if (tracer != NIL) {
- ERTS_TRACER_PROC(tracee_p) = tracer;
- }
- matches++;
+ if (!start_trace(p, tracer, &tracee_p->common, on, mask))
+ matches++;
}
}
if (ports || mods) {
@@ -749,33 +730,26 @@ Eterm trace_3(BIF_ALIST_3)
state = erts_atomic32_read_nob(&tracee_port->state);
if (state & ERTS_PORT_SFLGS_DEAD)
continue;
- if (tracer != NIL) {
- if (tracee_port->common.id == tracer)
- continue;
- if (port_already_traced(NULL, tracee_port, tracer))
- continue;
- }
-
- if (on) ERTS_TRACE_FLAGS(tracee_port) |= mask;
- else ERTS_TRACE_FLAGS(tracee_port) &= ~mask;
-
- if (!(ERTS_TRACE_FLAGS(tracee_port) & TRACEE_FLAGS)) {
- ERTS_TRACER_PROC(tracee_port) = NIL;
- } else if (tracer != NIL) {
- ERTS_TRACER_PROC(tracee_port) = tracer;
- }
- /* matches are not counted for ports since it would violate compatibility */
- /* This could be a reason to modify this function or make a new one. */
+ if (!start_trace(p, tracer, &tracee_port->common, on, mask))
+ matches++;
}
}
}
- if (pid_spec == am_all || pid_spec == am_new) {
- Uint def_flags = mask;
- Eterm def_tracer = tracer;
+ if (pid_spec == am_all || pid_spec == am_new
+ || pid_spec == am_ports || pid_spec == am_processes
+ || pid_spec == am_new_ports || pid_spec == am_new_processes
+ ) {
ok = 1;
- erts_change_default_tracing(on, &def_flags, &def_tracer);
+ if (mask & ERTS_PROC_TRACEE_FLAGS &&
+ pid_spec != am_ports && pid_spec != am_new_ports)
+ erts_change_default_proc_tracing(
+ on, mask & ERTS_PROC_TRACEE_FLAGS, tracer);
+ if (mask & ERTS_PORT_TRACEE_FLAGS &&
+ pid_spec != am_processes && pid_spec != am_new_processes)
+ erts_change_default_port_tracing(
+ on, mask & ERTS_PORT_TRACEE_FLAGS, tracer);
#ifdef HAVE_ERTS_NOW_CPU
if (cpu_ts && !on) {
@@ -801,6 +775,7 @@ Eterm trace_3(BIF_ALIST_3)
}
#endif
erts_release_code_write_permission();
+ ERTS_TRACER_CLEAR(&tracer);
BIF_RET(make_small(matches));
@@ -810,6 +785,8 @@ Eterm trace_3(BIF_ALIST_3)
error:
+ ERTS_TRACER_CLEAR(&tracer);
+
#ifdef ERTS_SMP
if (system_blocked) {
erts_smp_thr_progress_unblock();
@@ -821,88 +798,6 @@ Eterm trace_3(BIF_ALIST_3)
BIF_ERROR(p, BADARG);
}
-/* Check that the process to be traced is not already traced
- * by a valid other tracer than the tracer to be.
- */
-static int port_already_traced(Process *c_p, Port *tracee_port, Eterm tracer)
-{
- /*
- * SMP build assumes that either system is blocked or:
- * * main lock is held on c_p
- * * all locks are held on port tracee_p
- */
- if ((ERTS_TRACE_FLAGS(tracee_port) & TRACEE_FLAGS)
- && ERTS_TRACER_PROC(tracee_port) != tracer) {
- /* This tracee is already being traced, and not by the
- * tracer to be */
- if (is_internal_port(ERTS_TRACER_PROC(tracee_port))) {
- if (!erts_is_valid_tracer_port(ERTS_TRACER_PROC(tracee_port))) {
- /* Current trace port now invalid
- * - discard it and approve the new. */
- goto remove_tracer;
- } else
- return 1;
- }
- else if(is_internal_pid(ERTS_TRACER_PROC(tracee_port))) {
- Process *tracer_p = erts_proc_lookup(ERTS_TRACER_PROC(tracee_port));
- if (!tracer_p) {
- /* Current trace process now invalid
- * - discard it and approve the new. */
- goto remove_tracer;
- } else
- return 1;
- }
- else {
- remove_tracer:
- ERTS_TRACE_FLAGS(tracee_port) &= ~TRACEE_FLAGS;
- ERTS_TRACER_PROC(tracee_port) = NIL;
- }
- }
- return 0;
-}
-
-/* Check that the process to be traced is not already traced
- * by a valid other tracer than the tracer to be.
- */
-static int already_traced(Process *c_p, Process *tracee_p, Eterm tracer)
-{
- /*
- * SMP build assumes that either system is blocked or:
- * * main lock is held on c_p
- * * all locks multiple are held on tracee_p
- */
- if ((ERTS_TRACE_FLAGS(tracee_p) & TRACEE_FLAGS)
- && ERTS_TRACER_PROC(tracee_p) != tracer) {
- /* This tracee is already being traced, and not by the
- * tracer to be */
- if (is_internal_port(ERTS_TRACER_PROC(tracee_p))) {
- if (!erts_is_valid_tracer_port(ERTS_TRACER_PROC(tracee_p))) {
- /* Current trace port now invalid
- * - discard it and approve the new. */
- goto remove_tracer;
- } else
- return 1;
- }
- else if(is_internal_pid(ERTS_TRACER_PROC(tracee_p))) {
- Process *tracer_p;
-
- tracer_p = erts_proc_lookup(ERTS_TRACER_PROC(tracee_p));
- if (!tracer_p) {
- /* Current trace process now invalid
- * - discard it and approve the new. */
- goto remove_tracer;
- } else
- return 1;
- }
- else {
- remove_tracer:
- ERTS_TRACE_FLAGS(tracee_p) &= ~TRACEE_FLAGS;
- ERTS_TRACER_PROC(tracee_p) = NIL;
- }
- }
- return 0;
-}
-
/*
* Return information about a process or an external function being traced.
*/
@@ -920,7 +815,9 @@ Eterm trace_info_2(BIF_ALIST_2)
if (What == am_on_load) {
res = trace_info_on_load(p, Key);
- } else if (is_atom(What) || is_pid(What)) {
+ } else if (What == am_send || What == am_receive) {
+ res = trace_info_event(p, What, Key);
+ } else if (is_atom(What) || is_pid(What) || is_port(What)) {
res = trace_info_pid(p, What, Key);
} else if (is_tuple(What)) {
res = trace_info_func(p, What, Key);
@@ -936,41 +833,54 @@ static Eterm
trace_info_pid(Process* p, Eterm pid_spec, Eterm key)
{
Eterm tracer;
- Uint trace_flags;
+ Uint trace_flags = am_false;
Eterm* hp;
- if (pid_spec == am_new) {
- erts_get_default_tracing(&trace_flags, &tracer);
+ if (pid_spec == am_new || pid_spec == am_new_processes) {
+ ErtsTracer def_tracer;
+ erts_get_default_proc_tracing(&trace_flags, &def_tracer);
+ tracer = erts_tracer_to_term(p, def_tracer);
+ ERTS_TRACER_CLEAR(&def_tracer);
+ } else if (pid_spec == am_new_ports) {
+ ErtsTracer def_tracer;
+ erts_get_default_port_tracing(&trace_flags, &def_tracer);
+ tracer = erts_tracer_to_term(p, def_tracer);
+ ERTS_TRACER_CLEAR(&def_tracer);
+ } else if (is_internal_port(pid_spec)) {
+ Port *tracee;
+ tracee = erts_id2port_sflgs(pid_spec, p, ERTS_PROC_LOCK_MAIN,
+ ERTS_PORT_SFLGS_INVALID_LOOKUP);
+
+ if (!tracee)
+ return am_undefined;
+
+ if (!ERTS_TRACER_IS_NIL(ERTS_TRACER(tracee)))
+ erts_is_tracer_proc_enabled(NULL, 0, &tracee->common);
+
+ tracer = erts_tracer_to_term(p, ERTS_TRACER(tracee));
+ trace_flags = ERTS_TRACE_FLAGS(tracee);
+
+ erts_port_release(tracee);
+
} else if (is_internal_pid(pid_spec)) {
- Process *tracee;
- tracee = erts_pid2proc(p, ERTS_PROC_LOCK_MAIN,
- pid_spec, ERTS_PROC_LOCKS_ALL);
+ Process *tracee = erts_pid2proc_not_running(p, ERTS_PROC_LOCK_MAIN,
+ pid_spec, ERTS_PROC_LOCK_MAIN);
+
+ if (tracee == ERTS_PROC_LOCK_BUSY)
+ ERTS_BIF_YIELD2(bif_export[BIF_trace_info_2], p, pid_spec, key);
- if (!tracee) {
+ if (!tracee)
return am_undefined;
- } else {
- tracer = ERTS_TRACER_PROC(tracee);
- trace_flags = ERTS_TRACE_FLAGS(tracee);
- }
- if (is_internal_pid(tracer)) {
- if (!erts_proc_lookup(tracer)) {
- reset_tracer:
- ERTS_TRACE_FLAGS(tracee) &= ~TRACEE_FLAGS;
- trace_flags = ERTS_TRACE_FLAGS(tracee);
- tracer = ERTS_TRACER_PROC(tracee) = NIL;
- }
- }
- else if (is_internal_port(tracer)) {
- if (!erts_is_valid_tracer_port(tracer))
- goto reset_tracer;
- }
-#ifdef ERTS_SMP
- erts_smp_proc_unlock(tracee,
- (tracee == p
- ? ERTS_PROC_LOCKS_ALL_MINOR
- : ERTS_PROC_LOCKS_ALL));
-#endif
+ if (!ERTS_TRACER_IS_NIL(ERTS_TRACER(tracee)))
+ erts_is_tracer_proc_enabled(tracee, ERTS_PROC_LOCK_MAIN,
+ &tracee->common);
+
+ tracer = erts_tracer_to_term(p, ERTS_TRACER(tracee));
+ trace_flags = ERTS_TRACE_FLAGS(tracee);
+
+ if (tracee != p)
+ erts_smp_proc_unlock(tracee, ERTS_PROC_LOCK_MAIN);
} else if (is_external_pid(pid_spec)
&& external_pid_dist_entry(pid_spec) == erts_this_dist_entry) {
return am_undefined;
@@ -1024,8 +934,10 @@ trace_info_pid(Process* p, Eterm pid_spec, Eterm key)
HRelease(p,limit,hp+3);
return TUPLE2(hp, key, flag_list);
} else if (key == am_tracer) {
- hp = HAlloc(p, 3);
- return TUPLE2(hp, key, tracer); /* Local pid or port */
+ if (tracer == am_false)
+ tracer = NIL;
+ hp = HAlloc(p, 3);
+ return TUPLE2(hp, key, tracer);
} else {
goto error;
}
@@ -1054,11 +966,11 @@ trace_info_pid(Process* p, Eterm pid_spec, Eterm key)
*/
static int function_is_traced(Process *p,
Eterm mfa[3],
- Binary **ms, /* out */
- Binary **ms_meta, /* out */
- Eterm *tracer_pid_meta, /* out */
- Uint *count, /* out */
- Eterm *call_time) /* out */
+ Binary **ms, /* out */
+ Binary **ms_meta, /* out */
+ ErtsTracer *tracer_pid_meta, /* out */
+ Uint *count, /* out */
+ Eterm *call_time) /* out */
{
Export e;
Export* ep;
@@ -1123,7 +1035,7 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key)
Eterm traced = am_false;
Eterm match_spec = am_false;
Eterm retval = am_false;
- Eterm meta = am_false;
+ ErtsTracer meta = erts_tracer_nil;
Eterm call_time = NIL;
int r;
@@ -1150,9 +1062,16 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key)
erts_smp_thr_progress_block();
}
#endif
+#ifdef ERTS_DIRTY_SCHEDULERS
+ erts_smp_mtx_lock(&erts_dirty_bp_ix_mtx);
+#endif
+
r = function_is_traced(p, mfa, &ms, &ms_meta, &meta, &count, &call_time);
+#ifdef ERTS_DIRTY_SCHEDULERS
+ erts_smp_mtx_unlock(&erts_dirty_bp_ix_mtx);
+#endif
#ifdef ERTS_SMP
if ( (key == am_call_time) || (key == am_all)) {
erts_smp_thr_progress_unblock();
@@ -1193,7 +1112,10 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key)
retval = match_spec;
break;
case am_meta:
- retval = meta;
+ retval = erts_tracer_to_term(p, meta);
+ if (retval == am_false)
+ /* backwards compatibility */
+ retval = NIL;
break;
case am_meta_match_spec:
if (r & FUNC_TRACE_META_TRACE) {
@@ -1216,7 +1138,8 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key)
}
break;
case am_all: {
- Eterm match_spec_meta = am_false, c = am_false, t, ct = am_false;
+ Eterm match_spec_meta = am_false, c = am_false, t, ct = am_false,
+ m = am_false;
if (ms) {
match_spec = MatchSetGetSource(ms);
@@ -1235,6 +1158,9 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key)
if (r & FUNC_TRACE_TIME_TRACE) {
ct = call_time;
}
+
+ m = erts_tracer_to_term(p, meta);
+
hp = HAlloc(p, (3+2)*6);
retval = NIL;
t = TUPLE2(hp, am_call_count, c); hp += 3;
@@ -1243,7 +1169,7 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key)
retval = CONS(hp, t, retval); hp += 2;
t = TUPLE2(hp, am_meta_match_spec, match_spec_meta); hp += 3;
retval = CONS(hp, t, retval); hp += 2;
- t = TUPLE2(hp, am_meta, meta); hp += 3;
+ t = TUPLE2(hp, am_meta, m); hp += 3;
retval = CONS(hp, t, retval); hp += 2;
t = TUPLE2(hp, am_match_spec, match_spec); hp += 3;
retval = CONS(hp, t, retval); hp += 2;
@@ -1306,7 +1232,8 @@ trace_info_on_load(Process* p, Eterm key)
case am_meta:
hp = HAlloc(p, 3);
if (erts_default_trace_pattern_flags.meta) {
- return TUPLE2(hp, key, erts_default_meta_tracer_pid);
+ ASSERT(!ERTS_TRACER_IS_NIL(erts_default_meta_tracer));
+ return TUPLE2(hp, key, erts_tracer_to_term(p, erts_default_meta_tracer));
} else {
return TUPLE2(hp, key, am_false);
}
@@ -1345,7 +1272,7 @@ trace_info_on_load(Process* p, Eterm key)
}
case am_all:
{
- Eterm match_spec = am_false, meta_match_spec = am_false, r = NIL, t;
+ Eterm match_spec = am_false, meta_match_spec = am_false, r = NIL, t, m;
if (erts_default_trace_pattern_flags.local ||
(! erts_default_trace_pattern_flags.breakpoint)) {
@@ -1363,6 +1290,8 @@ trace_info_on_load(Process* p, Eterm key)
MatchSetGetSource(erts_default_meta_match_spec);
meta_match_spec = copy_object(meta_match_spec, p);
}
+ m = (erts_default_trace_pattern_flags.meta
+ ? erts_tracer_to_term(p, erts_default_meta_tracer) : am_false);
hp = HAlloc(p, (3+2)*5 + 3);
t = TUPLE2(hp, am_call_count,
(erts_default_trace_pattern_flags.call_count
@@ -1370,9 +1299,7 @@ trace_info_on_load(Process* p, Eterm key)
r = CONS(hp, t, r); hp += 2;
t = TUPLE2(hp, am_meta_match_spec, meta_match_spec); hp += 3;
r = CONS(hp, t, r); hp += 2;
- t = TUPLE2(hp, am_meta,
- (erts_default_trace_pattern_flags.meta
- ? erts_default_meta_tracer_pid : am_false)); hp += 3;
+ t = TUPLE2(hp, am_meta, m); hp += 3;
r = CONS(hp, t, r); hp += 2;
t = TUPLE2(hp, am_match_spec, match_spec); hp += 3;
r = CONS(hp, t, r); hp += 2;
@@ -1388,6 +1315,42 @@ trace_info_on_load(Process* p, Eterm key)
}
}
+static Eterm
+trace_info_event(Process* p, Eterm event, Eterm key)
+{
+ ErtsTracingEvent* te;
+ Eterm retval;
+ Eterm* hp;
+
+ switch (event) {
+ case am_send: te = erts_send_tracing; break;
+ case am_receive: te = erts_receive_tracing; break;
+ default:
+ goto error;
+ }
+
+ if (key != am_match_spec)
+ goto error;
+
+ te = &te[erts_active_bp_ix()];
+
+ if (te->on) {
+ if (!te->match_spec)
+ retval = am_true;
+ else
+ retval = copy_object(MatchSetGetSource(te->match_spec), p);
+ }
+ else
+ retval = am_false;
+
+ hp = HAlloc(p, 3);
+ return TUPLE2(hp, key, retval);
+
+ error:
+ BIF_ERROR(p, BADARG);
+}
+
+
#undef FUNC_TRACE_NOEXIST
#undef FUNC_TRACE_UNTRACED
#undef FUNC_TRACE_GLOBAL_TRACE
@@ -1397,7 +1360,7 @@ int
erts_set_trace_pattern(Process*p, Eterm* mfa, int specified,
Binary* match_prog_set, Binary *meta_match_prog_set,
int on, struct trace_pattern_flags flags,
- Eterm meta_tracer_pid, int is_blocking)
+ ErtsTracer meta_tracer, int is_blocking)
{
const ErtsCodeIndex code_ix = erts_active_code_ix();
int matches = 0;
@@ -1415,7 +1378,7 @@ erts_set_trace_pattern(Process*p, Eterm* mfa, int specified,
for (i = 0; i < n; i++) {
BeamInstr* pc = fp[i].pc;
- Export* ep = (Export *)(((char *)(pc-3)) - offsetof(Export, code));
+ Export* ep = ErtsContainerStruct(pc, Export, code[3]);
if (on && !flags.breakpoint) {
/* Turn on global call tracing */
@@ -1487,7 +1450,7 @@ erts_set_trace_pattern(Process*p, Eterm* mfa, int specified,
}
if (flags.meta) {
erts_set_mtrace_bif(pc, meta_match_prog_set,
- meta_tracer_pid);
+ meta_tracer);
m = 1;
}
if (flags.call_time) {
@@ -1527,7 +1490,7 @@ erts_set_trace_pattern(Process*p, Eterm* mfa, int specified,
}
if (flags.meta) {
erts_set_mtrace_break(&finish_bp.f, meta_match_prog_set,
- meta_tracer_pid);
+ meta_tracer);
}
if (flags.call_count) {
erts_set_count_break(&finish_bp.f, on);
@@ -1576,12 +1539,57 @@ erts_set_trace_pattern(Process*p, Eterm* mfa, int specified,
}
int
+erts_set_tracing_event_pattern(Eterm event, Binary* match_spec, int on)
+{
+ ErtsBpIndex ix = erts_staging_bp_ix();
+ ErtsTracingEvent* st;
+
+ switch (event) {
+ case am_send: st = &erts_send_tracing[ix]; break;
+ case am_receive: st = &erts_receive_tracing[ix]; break;
+ default: return -1;
+ }
+
+ MatchSetUnref(st->match_spec);
+
+ st->on = on;
+ st->match_spec = match_spec;
+ MatchSetRef(match_spec);
+
+ finish_bp.current = 1; /* prepare phase not needed for event trace */
+ finish_bp.install = on;
+ finish_bp.e.matched = 0;
+ finish_bp.e.matching = NULL;
+ finish_bp.f.matched = 0;
+ finish_bp.f.matching = NULL;
+
+#ifndef ERTS_SMP
+ while (erts_finish_breakpointing()) {
+ /* Empty loop body */
+ }
+#endif
+ return 1;
+}
+
+static void
+consolidate_event_tracing(ErtsTracingEvent te[])
+{
+ ErtsTracingEvent* src = &te[erts_active_bp_ix()];
+ ErtsTracingEvent* dst = &te[erts_staging_bp_ix()];
+
+ MatchSetUnref(dst->match_spec);
+ dst->on = src->on;
+ dst->match_spec = src->match_spec;
+ MatchSetRef(dst->match_spec);
+}
+
+int
erts_finish_breakpointing(void)
{
ERTS_SMP_LC_ASSERT(erts_has_code_write_permission());
/*
- * Memory barriers will be issued for all processes *before*
+ * Memory barriers will be issued for all schedulers *before*
* each of the stages below. (Unless the other schedulers
* are blocked, in which case memory barriers will be issued
* when they are awaken.)
@@ -1650,6 +1658,8 @@ erts_finish_breakpointing(void)
erts_consolidate_bp_data(&finish_bp.f, 1);
erts_bp_free_matched_functions(&finish_bp.e);
erts_bp_free_matched_functions(&finish_bp.f);
+ consolidate_event_tracing(erts_send_tracing);
+ consolidate_event_tracing(erts_receive_tracing);
return 0;
default:
ASSERT(0);
@@ -1664,11 +1674,10 @@ install_exp_breakpoints(BpFunctions* f)
BpFunction* fp = f->matching;
Uint ne = f->matched;
Uint i;
- Uint offset = offsetof(Export, code) + 3*sizeof(BeamInstr);
for (i = 0; i < ne; i++) {
BeamInstr* pc = fp[i].pc;
- Export* ep = (Export *) (((char *)pc)-offset);
+ Export* ep = ErtsContainerStruct(pc, Export, code[3]);
ep->addressv[code_ix] = pc;
}
@@ -1681,11 +1690,10 @@ uninstall_exp_breakpoints(BpFunctions* f)
BpFunction* fp = f->matching;
Uint ne = f->matched;
Uint i;
- Uint offset = offsetof(Export, code) + 3*sizeof(BeamInstr);
for (i = 0; i < ne; i++) {
BeamInstr* pc = fp[i].pc;
- Export* ep = (Export *) (((char *)pc)-offset);
+ Export* ep = ErtsContainerStruct(pc, Export, code[3]);
if (ep->addressv[code_ix] != pc) {
continue;
@@ -1702,11 +1710,10 @@ clean_export_entries(BpFunctions* f)
BpFunction* fp = f->matching;
Uint ne = f->matched;
Uint i;
- Uint offset = offsetof(Export, code) + 3*sizeof(BeamInstr);
for (i = 0; i < ne; i++) {
BeamInstr* pc = fp[i].pc;
- Export* ep = (Export *) (((char *)pc)-offset);
+ Export* ep = ErtsContainerStruct(pc, Export, code[3]);
if (ep->addressv[code_ix] == pc) {
continue;
@@ -1887,11 +1894,7 @@ new_seq_trace_token(Process* p)
{
Eterm* hp;
- if (SEQ_TRACE_TOKEN(p) == NIL
-#ifdef USE_VM_PROBES
- || SEQ_TRACE_TOKEN(p) == am_have_dt_utag
-#endif
- ) {
+ if (have_no_seqtrace(SEQ_TRACE_TOKEN(p))) {
hp = HAlloc(p, 6);
SEQ_TRACE_TOKEN(p) = TUPLE5(hp, make_small(0), /* Flags */
make_small(0), /* Label */
@@ -1911,12 +1914,8 @@ BIF_RETTYPE erl_seq_trace_info(Process *p, Eterm item)
BIF_ERROR(p, BADARG);
}
- if (SEQ_TRACE_TOKEN(p) == NIL
-#ifdef USE_VM_PROBES
- || SEQ_TRACE_TOKEN(p) == am_have_dt_utag
-#endif
- ) {
- if ((item == am_send) || (item == am_receive) ||
+ if (have_no_seqtrace(SEQ_TRACE_TOKEN(p))) {
+ if ((item == am_send) || (item == am_receive) ||
(item == am_print) || (item == am_timestamp)
|| (item == am_monotonic_timestamp)
|| (item == am_strict_monotonic_timestamp)) {
@@ -1978,11 +1977,7 @@ BIF_RETTYPE seq_trace_info_1(BIF_ALIST_1)
*/
BIF_RETTYPE seq_trace_print_1(BIF_ALIST_1)
{
- if (SEQ_TRACE_TOKEN(BIF_P) == NIL
-#ifdef USE_VM_PROBES
- || SEQ_TRACE_TOKEN(BIF_P) == am_have_dt_utag
-#endif
- ) {
+ if (have_no_seqtrace(SEQ_TRACE_TOKEN(BIF_P))) {
BIF_RET(am_false);
}
seq_trace_update_send(BIF_P);
@@ -2001,11 +1996,7 @@ BIF_RETTYPE seq_trace_print_1(BIF_ALIST_1)
*/
BIF_RETTYPE seq_trace_print_2(BIF_ALIST_2)
{
- if (SEQ_TRACE_TOKEN(BIF_P) == NIL
-#ifdef USE_VM_PROBES
- || SEQ_TRACE_TOKEN(BIF_P) == am_have_dt_utag
-#endif
- ) {
+ if (have_no_seqtrace(SEQ_TRACE_TOKEN(BIF_P))) {
BIF_RET(am_false);
}
if (!(is_atom(BIF_ARG_1) || is_small(BIF_ARG_1))) {
@@ -2352,50 +2343,86 @@ BIF_RETTYPE system_profile_2(BIF_ALIST_2)
}
/* End: Trace for System Profiling */
-BIF_RETTYPE
-trace_delivered_1(BIF_ALIST_1)
+/* Trace delivered send an aux work message to all schedulers
+ and when all schedulers have acknowledged that they have seen
+ the message the message is sent to the requesting process.
+
+ IMPORTANT: We have to make sure that the all messages sent
+ using enif_send have been delivered before we send the message
+ to the caller.
+
+ There used to be a separate implementation for when only a pid
+ is passed in, but since this is not performance critical code
+ we now use the same approach for both.
+*/
+
+typedef struct {
+ Process *proc;
+ Eterm ref;
+ Eterm ref_heap[REF_THING_SIZE];
+ Eterm target;
+ erts_smp_atomic32_t refc;
+} ErtsTraceDeliveredAll;
+
+static void
+reply_trace_delivered_all(void *vtdarp)
{
- DECL_AM(trace_delivered);
-#ifdef ERTS_SMP
- ErlHeapFragment *bp;
-#else
- ErtsProcLocks locks = 0;
-#endif
- Eterm *hp;
- Eterm msg, ref, msg_ref;
- Process *p;
- if (BIF_ARG_1 == am_all) {
- p = NULL;
- } else if (! (p = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN,
- BIF_ARG_1, ERTS_PROC_LOCKS_ALL))) {
- if (is_not_internal_pid(BIF_ARG_1)) {
- BIF_ERROR(BIF_P, BADARG);
- }
- }
-
- ref = erts_make_ref(BIF_P);
+ ErtsTraceDeliveredAll *tdarp = (ErtsTraceDeliveredAll *) vtdarp;
+ if (erts_smp_atomic32_dec_read_nob(&tdarp->refc) == 0) {
+ Eterm ref_copy, msg;
+ Process *rp = tdarp->proc;
+ Eterm *hp = NULL;
+ ErlOffHeap *ohp;
#ifdef ERTS_SMP
- bp = new_message_buffer(REF_THING_SIZE + 4);
- hp = &bp->mem[0];
- msg_ref = STORE_NC(&hp, &bp->off_heap, ref);
+ ErlHeapFragment *bp;
+ bp = new_message_buffer(4 + NC_HEAP_SIZE(tdarp->ref));
+ hp = &bp->mem[0];
+ ohp = &bp->off_heap;
#else
- hp = HAlloc(BIF_P, 4);
- msg_ref = ref;
+ ErtsProcLocks rp_locks = 0;
+ ErtsMessage *mp;
+ mp = erts_alloc_message_heap(
+ rp, &rp_locks, 4 + NC_HEAP_SIZE(tdarp->ref), &hp, &ohp);
#endif
- msg = TUPLE3(hp, AM_trace_delivered, BIF_ARG_1, msg_ref);
+ ref_copy = STORE_NC(&hp, ohp, tdarp->ref);
+ msg = TUPLE3(hp, am_trace_delivered, tdarp->target, ref_copy);
#ifdef ERTS_SMP
- erts_send_sys_msg_proc(BIF_P->common.id, BIF_P->common.id, msg, bp);
- if (p)
- erts_smp_proc_unlock(p,
- (BIF_P == p
- ? ERTS_PROC_LOCKS_ALL_MINOR
- : ERTS_PROC_LOCKS_ALL));
+ erts_send_sys_msg_proc(rp->common.id, rp->common.id, msg, bp);
#else
- erts_send_message(BIF_P, BIF_P, &locks, msg, ERTS_SND_FLG_NO_SEQ_TRACE);
+ erts_queue_message(rp, rp_locks, mp, msg, am_system);
#endif
- BIF_RET(ref);
+ erts_free(ERTS_ALC_T_MISC_AUX_WORK, vtdarp);
+ erts_proc_dec_refc(rp);
+ }
+}
+
+BIF_RETTYPE
+trace_delivered_1(BIF_ALIST_1)
+{
+
+ if (BIF_ARG_1 == am_all || is_internal_pid(BIF_ARG_1)) {
+ Eterm *hp, ref;
+ ErtsTraceDeliveredAll *tdarp =
+ erts_alloc(ERTS_ALC_T_MISC_AUX_WORK, sizeof(ErtsTraceDeliveredAll));
+
+ tdarp->proc = BIF_P;
+ ref = erts_make_ref(BIF_P);
+ hp = &tdarp->ref_heap[0];
+ tdarp->ref = STORE_NC(&hp, NULL, ref);
+ tdarp->target = BIF_ARG_1;
+ erts_smp_atomic32_init_nob(&tdarp->refc,
+ (erts_aint32_t) erts_no_schedulers);
+ erts_proc_add_refc(BIF_P, 1);
+ erts_schedule_multi_misc_aux_work(0,
+ erts_no_schedulers,
+ reply_trace_delivered_all,
+ (void *) tdarp);
+ BIF_RET(ref);
+ } else {
+ BIF_ERROR(BIF_P, BADARG);
+ }
}
diff --git a/erts/emulator/beam/erl_bif_unique.c b/erts/emulator/beam/erl_bif_unique.c
index 5eca09c5a6..7c70217d8d 100644
--- a/erts/emulator/beam/erl_bif_unique.c
+++ b/erts/emulator/beam/erl_bif_unique.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2014. All Rights Reserved.
+ * Copyright Ericsson AB 2014-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -257,7 +257,7 @@ static ERTS_INLINE Eterm unique_integer_bif(Process *c_p, int positive)
Uint hsz;
Eterm *hp;
- esdp = ERTS_PROC_GET_SCHDATA(c_p);
+ esdp = erts_proc_sched_data(c_p);
thr_id = (Uint64) esdp->thr_id;
unique = esdp->unique++;
bld_unique_integer_term(NULL, &hsz, thr_id, unique, positive);
@@ -266,17 +266,19 @@ static ERTS_INLINE Eterm unique_integer_bif(Process *c_p, int positive)
}
Uint
-erts_raw_unique_integer_heap_size(Uint64 val[ERTS_UNIQUE_INT_RAW_VALUES])
+erts_raw_unique_integer_heap_size(Uint64 val[ERTS_UNIQUE_INT_RAW_VALUES],
+ int positive)
{
Uint sz;
- bld_unique_integer_term(NULL, &sz, val[0], val[1], 0);
+ bld_unique_integer_term(NULL, &sz, val[0], val[1], positive);
return sz;
}
Eterm
-erts_raw_make_unique_integer(Eterm **hpp, Uint64 val[ERTS_UNIQUE_INT_RAW_VALUES])
+erts_raw_make_unique_integer(Eterm **hpp, Uint64 val[ERTS_UNIQUE_INT_RAW_VALUES],
+ int positive)
{
- return bld_unique_integer_term(hpp, NULL, val[0], val[1], 0);
+ return bld_unique_integer_term(hpp, NULL, val[0], val[1], positive);
}
void
@@ -338,7 +340,7 @@ static struct {
} w;
} raw_unique_monotonic_integer erts_align_attribute(ERTS_CACHE_LINE_SIZE);
-#if defined(ARCH_32) || HALFWORD_HEAP
+#if defined(ARCH_32)
# define ERTS_UNIQUE_MONOTONIC_OFFSET ERTS_SINT64_MIN
#else
# define ERTS_UNIQUE_MONOTONIC_OFFSET MIN_SMALL
@@ -368,7 +370,7 @@ get_unique_monotonic_integer_heap_size(Uint64 raw, int positive)
Sint64 value = ((Sint64) raw) + ERTS_UNIQUE_MONOTONIC_OFFSET;
if (IS_SSMALL(value))
return 0;
-#if defined(ARCH_32) || HALFWORD_HEAP
+#if defined(ARCH_32)
return ERTS_SINT64_HEAP_SIZE(value);
#else
return ERTS_UINT64_HEAP_SIZE((Uint64) value);
@@ -393,7 +395,7 @@ make_unique_monotonic_integer_value(Eterm *hp, Uint hsz, Uint64 raw, int positiv
if (hsz == 0)
res = make_small(value);
else {
-#if defined(ARCH_32) || HALFWORD_HEAP
+#if defined(ARCH_32)
res = erts_sint64_to_big(value, &hp);
#else
res = erts_uint64_to_big((Uint64) value, &hp);
@@ -426,16 +428,16 @@ erts_raw_get_unique_monotonic_integer(void)
}
Uint
-erts_raw_unique_monotonic_integer_heap_size(Sint64 raw)
+erts_raw_unique_monotonic_integer_heap_size(Sint64 raw, int positive)
{
- return get_unique_monotonic_integer_heap_size(raw, 0);
+ return get_unique_monotonic_integer_heap_size(raw, positive);
}
Eterm
-erts_raw_make_unique_monotonic_integer_value(Eterm **hpp, Sint64 raw)
+erts_raw_make_unique_monotonic_integer_value(Eterm **hpp, Sint64 raw, int positive)
{
- Uint hsz = get_unique_monotonic_integer_heap_size(raw, 0);
- Eterm res = make_unique_monotonic_integer_value(*hpp, hsz, raw, 0);
+ Uint hsz = get_unique_monotonic_integer_heap_size(raw, positive);
+ Eterm res = make_unique_monotonic_integer_value(*hpp, hsz, raw, positive);
*hpp += hsz;
return res;
}
@@ -513,7 +515,7 @@ BIF_RETTYPE make_ref_0(BIF_ALIST_0)
hp = HAlloc(BIF_P, REF_THING_SIZE);
- res = erts_sched_make_ref_in_buffer(ERTS_PROC_GET_SCHDATA(BIF_P), hp);
+ res = erts_sched_make_ref_in_buffer(erts_proc_sched_data(BIF_P), hp);
BIF_RET(res);
}
diff --git a/erts/emulator/beam/erl_bif_unique.h b/erts/emulator/beam/erl_bif_unique.h
index 37d5d91c39..c6481864d0 100644
--- a/erts/emulator/beam/erl_bif_unique.h
+++ b/erts/emulator/beam/erl_bif_unique.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2014. All Rights Reserved.
+ * Copyright Ericsson AB 2014-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -41,8 +41,9 @@ void erts_make_ref_in_array(Uint32 ref[ERTS_MAX_REF_NUMBERS]);
* not necessarily correspond to the end result.
*/
Sint64 erts_raw_get_unique_monotonic_integer(void);
-Uint erts_raw_unique_monotonic_integer_heap_size(Sint64 raw);
-Eterm erts_raw_make_unique_monotonic_integer_value(Eterm **hpp, Sint64 raw);
+Uint erts_raw_unique_monotonic_integer_heap_size(Sint64 raw, int positive);
+Eterm erts_raw_make_unique_monotonic_integer_value(Eterm **hpp, Sint64 raw,
+ int positive);
Sint64 erts_get_min_unique_monotonic_integer(void);
@@ -53,8 +54,11 @@ Eterm erts_debug_get_unique_monotonic_integer_state(Process *c_p);
#define ERTS_UNIQUE_INT_RAW_VALUES 2
#define ERTS_MAX_UNIQUE_INT_HEAP_SIZE ERTS_UINT64_ARRAY_TO_BIG_MAX_HEAP_SZ(2)
-Uint erts_raw_unique_integer_heap_size(Uint64 val[ERTS_UNIQUE_INT_RAW_VALUES]);
-Eterm erts_raw_make_unique_integer(Eterm **hpp, Uint64 val[ERTS_UNIQUE_INT_RAW_VALUES]);
+Uint erts_raw_unique_integer_heap_size(Uint64 val[ERTS_UNIQUE_INT_RAW_VALUES],
+ int positive);
+Eterm erts_raw_make_unique_integer(Eterm **hpp,
+ Uint64 val[ERTS_UNIQUE_INT_RAW_VALUES],
+ int postive);
void erts_raw_get_unique_integer(Uint64 val[ERTS_UNIQUE_INT_RAW_VALUES]);
Sint64 erts_get_min_unique_integer(void);
diff --git a/erts/emulator/beam/erl_binary.h b/erts/emulator/beam/erl_binary.h
index ea01bf08f0..fdc5efea98 100644
--- a/erts/emulator/beam/erl_binary.h
+++ b/erts/emulator/beam/erl_binary.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2000-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -72,7 +72,6 @@ typedef struct erl_heap_bin {
*/
#define binary_size(Bin) (binary_val(Bin)[1])
-#define binary_size_rel(Bin,BasePtr) (binary_val_rel(Bin,BasePtr)[1])
#define binary_bitsize(Bin) \
((*binary_val(Bin) == HEADER_SUB_BIN) ? \
@@ -95,12 +94,9 @@ typedef struct erl_heap_bin {
* Bitsize: output variable (Uint)
*/
-#define ERTS_GET_BINARY_BYTES(Bin,Bytep,Bitoffs,Bitsize) \
- ERTS_GET_BINARY_BYTES_REL(Bin,Bytep,Bitoffs,Bitsize,NULL)
-
-#define ERTS_GET_BINARY_BYTES_REL(Bin,Bytep,Bitoffs,Bitsize,BasePtr) \
+#define ERTS_GET_BINARY_BYTES(Bin,Bytep,Bitoffs,Bitsize) \
do { \
- Eterm* _real_bin = binary_val_rel(Bin,BasePtr); \
+ Eterm* _real_bin = binary_val(Bin); \
Uint _offs = 0; \
Bitoffs = Bitsize = 0; \
if (*_real_bin == HEADER_SUB_BIN) { \
@@ -108,7 +104,7 @@ do { \
_offs = _sb->offs; \
Bitoffs = _sb->bitoffs; \
Bitsize = _sb->bitsize; \
- _real_bin = binary_val_rel(_sb->orig,BasePtr); \
+ _real_bin = binary_val(_sb->orig); \
} \
if (*_real_bin == HEADER_PROC_BIN) { \
Bytep = ((ProcBin *) _real_bin)->bytes + _offs; \
@@ -131,11 +127,8 @@ do { \
*/
#define ERTS_GET_REAL_BIN(Bin, RealBin, ByteOffset, BitOffset, BitSize) \
- ERTS_GET_REAL_BIN_REL(Bin, RealBin, ByteOffset, BitOffset, BitSize, NULL)
-
-#define ERTS_GET_REAL_BIN_REL(Bin, RealBin, ByteOffset, BitOffset, BitSize, BasePtr) \
do { \
- ErlSubBin* _sb = (ErlSubBin *) binary_val_rel(Bin,BasePtr); \
+ ErlSubBin* _sb = (ErlSubBin *) binary_val(Bin); \
if (_sb->thing_word == HEADER_SUB_BIN) { \
RealBin = _sb->orig; \
ByteOffset = _sb->offs; \
diff --git a/erts/emulator/beam/erl_bits.c b/erts/emulator/beam/erl_bits.c
index 01734c55d7..6bf52fb303 100644
--- a/erts/emulator/beam/erl_bits.c
+++ b/erts/emulator/beam/erl_bits.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -282,7 +282,7 @@ erts_bs_get_integer_2(Process *p, Uint num_bits, unsigned flags, ErlBinMatchBuff
* Simply shift whole bytes into the result.
*/
switch (BYTE_OFFSET(n)) {
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
case 7: w = (w << 8) | *bp++;
case 6: w = (w << 8) | *bp++;
case 5: w = (w << 8) | *bp++;
@@ -387,7 +387,7 @@ erts_bs_get_integer_2(Process *p, Uint num_bits, unsigned flags, ErlBinMatchBuff
case 3:
v32 = LSB[0] + (LSB[1]<<8) + (LSB[2]<<16);
goto big_small;
-#if !defined(ARCH_64) || HALFWORD_HEAP
+#if !defined(ARCH_64)
case 4:
v32 = (LSB[0] + (LSB[1]<<8) + (LSB[2]<<16) + (LSB[3]<<24));
if (!IS_USMALL(sgn, v32)) {
diff --git a/erts/emulator/beam/erl_bits.h b/erts/emulator/beam/erl_bits.h
index 8b7807fbd9..4bd5b24157 100644
--- a/erts/emulator/beam/erl_bits.h
+++ b/erts/emulator/beam/erl_bits.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -83,8 +83,8 @@ typedef struct erl_bin_match_struct{
#ifdef ERTS_SMP
/* the state resides in the current process' scheduler data */
#define ERL_BITS_DECLARE_STATEP struct erl_bits_state *EBS
-#define ERL_BITS_RELOAD_STATEP(P) do{EBS = &(P)->scheduler_data->erl_bits_state;}while(0)
-#define ERL_BITS_DEFINE_STATEP(P) struct erl_bits_state *EBS = &(P)->scheduler_data->erl_bits_state
+#define ERL_BITS_RELOAD_STATEP(P) do{EBS = &erts_proc_sched_data((P))->erl_bits_state;}while(0)
+#define ERL_BITS_DEFINE_STATEP(P) struct erl_bits_state *EBS = &erts_proc_sched_data((P))->erl_bits_state
#else
/* reentrant API but with a hidden single global state, for testing only */
extern struct erl_bits_state ErlBitsState_;
diff --git a/erts/emulator/beam/erl_cpu_topology.c b/erts/emulator/beam/erl_cpu_topology.c
index c263eebfcc..28aaeeb479 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-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2010-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erl_cpu_topology.h b/erts/emulator/beam/erl_cpu_topology.h
index f3fbfc6da0..45324ac4a0 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-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2010-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c
index 870b556851..23c8a5bae9 100644
--- a/erts/emulator/beam/erl_db.c
+++ b/erts/emulator/beam/erl_db.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2014. 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.
@@ -201,7 +201,7 @@ static int free_table_cont(Process *p,
DbTable *tb,
int first,
int clean_meta_tab);
-static void print_table(int to, void *to_arg, int show, DbTable* tb);
+static void print_table(fmtfn_t to, void *to_arg, int show, DbTable* tb);
static BIF_RETTYPE ets_select_delete_1(BIF_ALIST_1);
static BIF_RETTYPE ets_select_count_1(BIF_ALIST_1);
static BIF_RETTYPE ets_select_trap_1(BIF_ALIST_1);
@@ -266,7 +266,7 @@ static void schedule_free_dbtable(DbTable* tb)
* Caller is *not* allowed to access the specialized part
* (hash or tree) of *tb after this function has returned.
*/
- ASSERT(erts_refc_read(&tb->common.ref, 0) == 0);
+ ASSERT(erts_smp_refc_read(&tb->common.ref, 0) == 0);
erts_schedule_thr_prgr_later_cleanup_op(free_dbtable,
(void *) tb,
&tb->release.data,
@@ -600,11 +600,11 @@ done:
*/
static ERTS_INLINE void local_fix_table(DbTable* tb)
{
- erts_refc_inc(&tb->common.ref, 1);
+ erts_smp_refc_inc(&tb->common.ref, 1);
}
static ERTS_INLINE void local_unfix_table(DbTable* tb)
{
- if (erts_refc_dectest(&tb->common.ref, 0) == 0) {
+ if (erts_smp_refc_dectest(&tb->common.ref, 0) == 0) {
ASSERT(IS_HASH_TABLE(tb->common.status));
db_unfix_table_hash(&(tb->hash));
}
@@ -1094,7 +1094,7 @@ BIF_RETTYPE ets_insert_2(BIF_ALIST_2)
CHECK_TABLES();
- /* Write lock table if more than one object to keep atomicy */
+ /* Write lock table if more than one object to keep atomicity */
kind = ((is_list(BIF_ARG_2) && CDR(list_val(BIF_ARG_2)) != NIL)
? LCK_WRITE : LCK_WRITE_REC);
@@ -1164,7 +1164,7 @@ BIF_RETTYPE ets_insert_new_2(BIF_ALIST_2)
Eterm lookup_ret;
DbTableMethod* meth;
- /* More than one object, use LCK_WRITE to keep atomicy */
+ /* More than one object, use LCK_WRITE to keep atomicity */
kind = LCK_WRITE;
tb = db_get_table(BIF_P, BIF_ARG_1, DB_WRITE, kind);
if (tb == NULL) {
@@ -1487,7 +1487,7 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
tb->common.type = status & ERTS_ETS_TABLE_TYPES;
/* Note, 'type' is *read only* from now on... */
#endif
- erts_refc_init(&tb->common.ref, 0);
+ erts_smp_refc_init(&tb->common.ref, 0);
db_init_lock(tb, status & (DB_FINE_LOCKED|DB_FREQ_READ),
"db_tab", "db_tab_fix");
tb->common.keypos = keypos;
@@ -1773,15 +1773,9 @@ BIF_RETTYPE ets_delete_1(BIF_ALIST_1)
* (it looks like an continuation pointer), but that is will crash the
* emulator if this BIF is call traced.
*/
-#if HALFWORD_HEAP
- Eterm *hp = HAlloc(BIF_P, 3);
- hp[0] = make_pos_bignum_header(2);
- *((UWord *) (UWord) (hp+1)) = (UWord) tb;
-#else
Eterm *hp = HAlloc(BIF_P, 2);
hp[0] = make_pos_bignum_header(1);
hp[1] = (Eterm) tb;
-#endif
BIF_TRAP1(&ets_delete_continue_exp, BIF_P, make_big(hp));
}
else {
@@ -1837,7 +1831,7 @@ BIF_RETTYPE ets_give_away_3(BIF_ALIST_3)
tb->common.id,
from_pid,
BIF_ARG_3),
- 0);
+ 0);
erts_smp_proc_unlock(to_proc, to_locks);
UnUseTmpHeap(5,BIF_P);
BIF_RET(am_true);
@@ -2839,7 +2833,8 @@ BIF_RETTYPE ets_match_spec_run_r_3(BIF_ALIST_3)
BIF_TRAP3(bif_export[BIF_ets_match_spec_run_r_3],
BIF_P,lst,BIF_ARG_2,ret);
}
- res = db_prog_match(BIF_P, mp, CAR(list_val(lst)), NULL, NULL, 0,
+ res = db_prog_match(BIF_P, BIF_P,
+ mp, CAR(list_val(lst)), NULL, 0,
ERTS_PAM_COPY_RESULT, &dummy);
if (is_value(res)) {
hp = HAlloc(BIF_P, 2);
@@ -2995,7 +2990,7 @@ void init_db(ErtsDbSpinCount db_spin_count)
meta_pid_to_tab->common.meth = &db_hash;
meta_pid_to_tab->common.compress = 0;
- erts_refc_init(&meta_pid_to_tab->common.ref, 0);
+ erts_smp_refc_init(&meta_pid_to_tab->common.ref, 0);
/* Neither rwlock or fixlock used
db_init_lock(meta_pid_to_tab, "meta_pid_to_tab", "meta_pid_to_tab_FIX");*/
@@ -3026,7 +3021,7 @@ void init_db(ErtsDbSpinCount db_spin_count)
meta_pid_to_fixed_tab->common.meth = &db_hash;
meta_pid_to_fixed_tab->common.compress = 0;
- erts_refc_init(&meta_pid_to_fixed_tab->common.ref, 0);
+ erts_smp_refc_init(&meta_pid_to_fixed_tab->common.ref, 0);
/* Neither rwlock or fixlock used
db_init_lock(meta_pid_to_fixed_tab, "meta_pid_to_fixed_tab", "meta_pid_to_fixed_tab_FIX");*/
@@ -3216,7 +3211,7 @@ retry:
tb->common.id,
p->common.id,
heir_data),
- 0);
+ 0);
erts_smp_proc_unlock(to_proc, to_locks);
return !0;
}
@@ -3387,7 +3382,7 @@ erts_db_process_exiting(Process *c_p, ErtsProcLocks c_p_locks)
if ((*pp)->pid == pid) {
DbFixation* fix = *pp;
erts_aint_t diff = -((erts_aint_t) fix->counter);
- erts_refc_add(&tb->common.ref,diff,0);
+ erts_smp_refc_add(&tb->common.ref,diff,0);
*pp = fix->next;
erts_db_free(ERTS_ALC_T_DB_FIXATION,
tb, fix, sizeof(DbFixation));
@@ -3463,11 +3458,11 @@ static void fix_table_locked(Process* p, DbTable* tb)
#ifdef ERTS_SMP
erts_smp_mtx_lock(&tb->common.fixlock);
#endif
- erts_refc_inc(&tb->common.ref,1);
+ erts_smp_refc_inc(&tb->common.ref,1);
fix = tb->common.fixations;
if (fix == NULL) {
tb->common.time.monotonic
- = erts_get_monotonic_time(ERTS_PROC_GET_SCHDATA(p));
+ = erts_get_monotonic_time(erts_proc_sched_data(p));
tb->common.time.offset = erts_get_time_offset();
}
else {
@@ -3519,7 +3514,7 @@ static void unfix_table_locked(Process* p, DbTable* tb,
for (pp = &tb->common.fixations; *pp != NULL; pp = &(*pp)->next) {
if ((*pp)->pid == p->common.id) {
DbFixation* fix = *pp;
- erts_refc_dec(&tb->common.ref,0);
+ erts_smp_refc_dec(&tb->common.ref,0);
--(fix->counter);
ASSERT(fix->counter >= 0);
if (fix->counter > 0) {
@@ -3568,7 +3563,7 @@ static void free_fixations_locked(DbTable *tb)
fix = tb->common.fixations;
while (fix != NULL) {
erts_aint_t diff = -((erts_aint_t) fix->counter);
- erts_refc_add(&tb->common.ref,diff,0);
+ erts_smp_refc_add(&tb->common.ref,diff,0);
next_fix = fix->next;
db_meta_lock(meta_pid_to_fixed_tab, LCK_WRITE_REC);
db_erase_bag_exact2(meta_pid_to_fixed_tab,
@@ -3651,11 +3646,8 @@ static BIF_RETTYPE ets_delete_trap(BIF_ALIST_1)
Eterm* ptr = big_val(cont);
DbTable *tb = *((DbTable **) (UWord) (ptr + 1));
-#if HALFWORD_HEAP
- ASSERT(*ptr == make_pos_bignum_header(2));
-#else
ASSERT(*ptr == make_pos_bignum_header(1));
-#endif
+
db_lock(tb, LCK_WRITE);
trap = free_table_cont(p, tb, 0, 1);
db_unlock(tb, LCK_WRITE);
@@ -3879,7 +3871,7 @@ static Eterm table_info(Process* p, DbTable* tb, Eterm What)
return ret;
}
-static void print_table(int to, void *to_arg, int show, DbTable* tb)
+static void print_table(fmtfn_t to, void *to_arg, int show, DbTable* tb)
{
erts_print(to, to_arg, "Table: %T\n", tb->common.id);
erts_print(to, to_arg, "Name: %T\n", tb->common.the_name);
@@ -3899,7 +3891,7 @@ static void print_table(int to, void *to_arg, int show, DbTable* tb)
erts_print(to, to_arg, "Read Concurrency: %T\n", table_info(NULL, tb, am_read_concurrency));
}
-void db_info(int to, void *to_arg, int show) /* Called by break handler */
+void db_info(fmtfn_t to, void *to_arg, int show) /* Called by break handler */
{
int i;
for (i=0; i < db_max_tabs; i++)
diff --git a/erts/emulator/beam/erl_db.h b/erts/emulator/beam/erl_db.h
index a589af784c..b0508f2e74 100644
--- a/erts/emulator/beam/erl_db.h
+++ b/erts/emulator/beam/erl_db.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -74,7 +74,7 @@ typedef enum {
void init_db(ErtsDbSpinCount);
int erts_db_process_exiting(Process *, ErtsProcLocks);
-void db_info(int, void *, int);
+void db_info(fmtfn_t, void *, int);
void erts_db_foreach_table(void (*)(DbTable *, void *), void *);
void erts_db_foreach_offheap(DbTable *,
void (*func)(ErlOffHeap *, void *),
diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c
index fa925c94a5..390369fdb9 100644
--- a/erts/emulator/beam/erl_db_hash.c
+++ b/erts/emulator/beam/erl_db_hash.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2012. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -40,7 +40,7 @@
** DB_FINE_LOCKED set. The table variable is_thread_safe will then indicate
** if operations need to obtain fine grained locks or not. Some operations
** will for example always use exclusive table lock to guarantee
-** a higher level of atomicy.
+** a higher level of atomicity.
*/
/* FIXATION:
@@ -437,7 +437,7 @@ static int db_select_count_continue_hash(Process *p, DbTable *tbl,
static int db_select_delete_continue_hash(Process *p, DbTable *tbl,
Eterm continuation, Eterm *ret);
static int db_take_hash(Process *, DbTable *, Eterm, Eterm *);
-static void db_print_hash(int to,
+static void db_print_hash(fmtfn_t to,
void *to_arg,
int show,
DbTable *tbl);
@@ -469,9 +469,6 @@ static ERTS_INLINE void try_shrink(DbTableHash* tb)
}
}
-#define EQ_REL(x,y,y_base) \
- (is_same(x,NULL,y,y_base) || (is_not_both_immed((x),(y)) && eq_rel((x),NULL,(y),y_base)))
-
/* Is this a live object (not pseodo-deleted) with the specified key?
*/
static ERTS_INLINE int has_live_key(DbTableHash* tb, HashDbTerm* b,
@@ -481,7 +478,7 @@ static ERTS_INLINE int has_live_key(DbTableHash* tb, HashDbTerm* b,
else {
Eterm itemKey = GETKEY(tb, b->dbterm.tpl);
ASSERT(!is_header(itemKey));
- return EQ_REL(key, itemKey, b->dbterm.tpl);
+ return EQ(key, itemKey);
}
}
@@ -494,7 +491,7 @@ static ERTS_INLINE int has_key(DbTableHash* tb, HashDbTerm* b,
else {
Eterm itemKey = GETKEY(tb, b->dbterm.tpl);
ASSERT(!is_header(itemKey));
- return EQ_REL(key, itemKey, b->dbterm.tpl);
+ return EQ(key, itemKey);
}
}
@@ -2158,7 +2155,7 @@ int db_mark_all_deleted_hash(DbTable *tbl)
/* Display hash table contents (for dump) */
-static void db_print_hash(int to, void *to_arg, int show, DbTable *tbl)
+static void db_print_hash(fmtfn_t to, void *to_arg, int show, DbTable *tbl)
{
DbTableHash *tb = &tbl->hash;
DbHashStats stats;
@@ -2204,11 +2201,11 @@ static void db_print_hash(int to, void *to_arg, int show, DbTable *tbl)
erts_print(to, to_arg, "*");
if (tb->common.compress) {
Eterm key = GETKEY(tb, list->dbterm.tpl);
- erts_print(to, to_arg, "key=%R", key, list->dbterm.tpl);
+ erts_print(to, to_arg, "key=%T", key);
}
else {
- Eterm obj = make_tuple_rel(list->dbterm.tpl,list->dbterm.tpl);
- erts_print(to, to_arg, "%R", obj, list->dbterm.tpl);
+ Eterm obj = make_tuple(list->dbterm.tpl);
+ erts_print(to, to_arg, "%T", obj);
}
if (list->next != 0)
erts_print(to, to_arg, ",");
@@ -2869,15 +2866,7 @@ db_lookup_dbterm_hash(Process *p, DbTable *tbl, Eterm key, Eterm obj,
q->hvalue = hval;
q->next = NULL;
*bp = b = q;
-
- {
- int nitems = erts_smp_atomic_inc_read_nob(&tb->common.nitems);
- int nactive = NACTIVE(tb);
-
- if (nitems > nactive * (CHAIN_LEN + 1) && !IS_FIXED(tb)) {
- grow(tb, nactive);
- }
- }
+ flags |= DB_INC_TRY_GROW;
} else {
HashDbTerm *q, *next = b->next;
@@ -2899,9 +2888,6 @@ Ldone:
handle->dbterm = &b->dbterm;
handle->flags = flags;
handle->new_size = b->dbterm.size;
-#if HALFWORD_HEAP
- handle->abs_vec = NULL;
-#endif
handle->lck = lck;
return 1;
}
@@ -2916,6 +2902,7 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle)
HashDbTerm **bp = (HashDbTerm **) handle->bp;
HashDbTerm *b = *bp;
erts_smp_rwmtx_t* lck = (erts_smp_rwmtx_t*) handle->lck;
+ HashDbTerm* free_me = NULL;
ERTS_SMP_LC_ASSERT(IS_HASH_WLOCKED(tb, lck)); /* locked by db_lookup_dbterm_hash */
@@ -2927,21 +2914,34 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle)
b->hvalue = INVALID_HASH;
} else {
*bp = b->next;
- free_term(tb, b);
+ free_me = b;
}
WUNLOCK_HASH(lck);
erts_smp_atomic_dec_nob(&tb->common.nitems);
try_shrink(tb);
- } else if (handle->flags & DB_MUST_RESIZE) {
- db_finalize_resize(handle, offsetof(HashDbTerm,dbterm));
- WUNLOCK_HASH(lck);
-
- free_term(tb, b);
- }
- else {
- WUNLOCK_HASH(lck);
+ } else {
+ if (handle->flags & DB_MUST_RESIZE) {
+ db_finalize_resize(handle, offsetof(HashDbTerm,dbterm));
+ free_me = b;
+ }
+ if (handle->flags & DB_INC_TRY_GROW) {
+ int nactive;
+ int nitems = erts_smp_atomic_inc_read_nob(&tb->common.nitems);
+ WUNLOCK_HASH(lck);
+ nactive = NACTIVE(tb);
+
+ if (nitems > nactive * (CHAIN_LEN + 1) && !IS_FIXED(tb)) {
+ grow(tb, nactive);
+ }
+ } else {
+ WUNLOCK_HASH(lck);
+ }
}
+
+ if (free_me)
+ free_term(tb, free_me);
+
#ifdef DEBUG
handle->dbterm = 0;
#endif
diff --git a/erts/emulator/beam/erl_db_hash.h b/erts/emulator/beam/erl_db_hash.h
index 66d8ec71d9..e654363cd5 100644
--- a/erts/emulator/beam/erl_db_hash.h
+++ b/erts/emulator/beam/erl_db_hash.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2014. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c
index 61293fbf9a..dd9403e132 100644
--- a/erts/emulator/beam/erl_db_tree.c
+++ b/erts/emulator/beam/erl_db_tree.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -280,7 +280,7 @@ struct select_delete_context {
/*
** Forward declarations
*/
-static TreeDbTerm *linkout_tree(DbTableTree *tb, Eterm key, Eterm* key_base);
+static TreeDbTerm *linkout_tree(DbTableTree *tb, Eterm key);
static TreeDbTerm *linkout_object_tree(DbTableTree *tb,
Eterm object);
static int do_free_tree_cont(DbTableTree *tb, int num_left);
@@ -291,15 +291,15 @@ static int delsub(TreeDbTerm **this);
static TreeDbTerm *slot_search(Process *p, DbTableTree *tb, Sint slot);
static TreeDbTerm *find_node(DbTableTree *tb, Eterm key);
static TreeDbTerm **find_node2(DbTableTree *tb, Eterm key);
-static TreeDbTerm *find_next(DbTableTree *tb, DbTreeStack*, Eterm key, Eterm* kbase);
-static TreeDbTerm *find_prev(DbTableTree *tb, DbTreeStack*, Eterm key, Eterm* kbase);
+static TreeDbTerm *find_next(DbTableTree *tb, DbTreeStack*, Eterm key);
+static TreeDbTerm *find_prev(DbTableTree *tb, DbTreeStack*, Eterm key);
static TreeDbTerm *find_next_from_pb_key(DbTableTree *tb, DbTreeStack*,
Eterm key);
static TreeDbTerm *find_prev_from_pb_key(DbTableTree *tb, DbTreeStack*,
Eterm key);
static void traverse_backwards(DbTableTree *tb,
DbTreeStack*,
- Eterm lastkey, Eterm* lk_base,
+ Eterm lastkey,
int (*doit)(DbTableTree *tb,
TreeDbTerm *,
void *,
@@ -307,7 +307,7 @@ static void traverse_backwards(DbTableTree *tb,
void *context);
static void traverse_forward(DbTableTree *tb,
DbTreeStack*,
- Eterm lastkey, Eterm* lk_base,
+ Eterm lastkey,
int (*doit)(DbTableTree *tb,
TreeDbTerm *,
void *,
@@ -315,8 +315,8 @@ static void traverse_forward(DbTableTree *tb,
void *context);
static int key_given(DbTableTree *tb, Eterm pattern, TreeDbTerm **ret,
Eterm *partly_bound_key);
-static Sint cmp_partly_bound(Eterm partly_bound_key, Eterm bound_key, Eterm* bk_base);
-static Sint do_cmp_partly_bound(Eterm a, Eterm b, Eterm* b_base, int *done);
+static Sint cmp_partly_bound(Eterm partly_bound_key, Eterm bound_key);
+static Sint do_cmp_partly_bound(Eterm a, Eterm b, int *done);
static int analyze_pattern(DbTableTree *tb, Eterm pattern,
struct mp_info *mpi);
@@ -385,7 +385,7 @@ static int db_select_delete_tree(Process *p, DbTable *tbl,
static int db_select_delete_continue_tree(Process *p, DbTable *tbl,
Eterm continuation, Eterm *ret);
static int db_take_tree(Process *, DbTable *, Eterm, Eterm *);
-static void db_print_tree(int to, void *to_arg,
+static void db_print_tree(fmtfn_t to, void *to_arg,
int show, DbTable *tbl);
static int db_free_table_tree(DbTable *tbl);
@@ -517,7 +517,7 @@ static int db_next_tree(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
if (is_atom(key) && key == am_EOT)
return DB_ERROR_BADKEY;
stack = get_any_stack(tb);
- this = find_next(tb, stack, key, NULL);
+ this = find_next(tb, stack, key);
release_stack(tb,stack);
if (this == NULL) {
*ret = am_EOT;
@@ -563,7 +563,7 @@ static int db_prev_tree(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
if (is_atom(key) && key == am_EOT)
return DB_ERROR_BADKEY;
stack = get_any_stack(tb);
- this = find_prev(tb, stack, key, NULL);
+ this = find_prev(tb, stack, key);
release_stack(tb,stack);
if (this == NULL) {
*ret = am_EOT;
@@ -573,19 +573,13 @@ static int db_prev_tree(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
return DB_ERROR_NONE;
}
-static ERTS_INLINE Sint cmp_key(DbTableTree* tb, Eterm key, Eterm* key_base,
- TreeDbTerm* obj)
-{
- return cmp_rel(key, key_base,
- GETKEY(tb,obj->dbterm.tpl), obj->dbterm.tpl);
+static ERTS_INLINE Sint cmp_key(DbTableTree* tb, Eterm key, TreeDbTerm* obj) {
+ return CMP(key, GETKEY(tb,obj->dbterm.tpl));
}
-static ERTS_INLINE int cmp_key_eq(DbTableTree* tb, Eterm key, Eterm* key_base,
- TreeDbTerm* obj)
-{
+static ERTS_INLINE int cmp_key_eq(DbTableTree* tb, Eterm key, TreeDbTerm* obj) {
Eterm obj_key = GETKEY(tb,obj->dbterm.tpl);
- return is_same(key, key_base, obj_key, obj->dbterm.tpl)
- || cmp_rel(key, key_base, obj_key, obj->dbterm.tpl) == 0;
+ return is_same(key, obj_key) || CMP(key, obj_key) == 0;
}
static int db_put_tree(DbTable *tbl, Eterm obj, int key_clash_fail)
@@ -619,7 +613,7 @@ static int db_put_tree(DbTable *tbl, Eterm obj, int key_clash_fail)
(*this)->balance = 0;
(*this)->left = (*this)->right = NULL;
break;
- } else if ((c = cmp_key(tb, key, NULL, *this)) < 0) {
+ } else if ((c = cmp_key(tb, key, *this)) < 0) {
/* go lefts */
dstack[dpos++] = DIR_LEFT;
tstack[tpos++] = this;
@@ -774,7 +768,7 @@ static int db_erase_tree(DbTable *tbl, Eterm key, Eterm *ret)
*ret = am_true;
- if ((res = linkout_tree(tb, key, NULL)) != NULL) {
+ if ((res = linkout_tree(tb, key)) != NULL) {
free_term(tb, res);
}
return DB_ERROR_NONE;
@@ -970,15 +964,15 @@ static int db_select_continue_tree(Process *p,
stack = get_any_stack(tb);
if (chunk_size) {
if (reverse) {
- traverse_backwards(tb, stack, lastkey, NULL, &doit_select_chunk, &sc);
+ traverse_backwards(tb, stack, lastkey, &doit_select_chunk, &sc);
} else {
- traverse_forward(tb, stack, lastkey, NULL, &doit_select_chunk, &sc);
+ traverse_forward(tb, stack, lastkey, &doit_select_chunk, &sc);
}
} else {
if (reverse) {
- traverse_forward(tb, stack, lastkey, NULL, &doit_select, &sc);
+ traverse_forward(tb, stack, lastkey, &doit_select, &sc);
} else {
- traverse_backwards(tb, stack, lastkey, NULL, &doit_select, &sc);
+ traverse_backwards(tb, stack, lastkey, &doit_select, &sc);
}
}
release_stack(tb,stack);
@@ -1003,9 +997,9 @@ static int db_select_continue_tree(Process *p,
}
key = GETKEY(tb, sc.lastobj);
- sz = size_object_rel(key,sc.lastobj);
+ sz = size_object(key);
hp = HAlloc(p, 9 + sz);
- key = copy_struct_rel(key, sz, &hp, &MSO(p), sc.lastobj, NULL);
+ key = copy_struct(key, sz, &hp, &MSO(p));
continuation = TUPLE8
(hp,
tptr[1],
@@ -1026,8 +1020,8 @@ static int db_select_continue_tree(Process *p,
key = GETKEY(tb, sc.lastobj);
if (chunk_size) {
if (end_condition != NIL &&
- ((!reverse && cmp_partly_bound(end_condition,key,sc.lastobj) < 0) ||
- (reverse && cmp_partly_bound(end_condition,key,sc.lastobj) > 0))) {
+ ((!reverse && cmp_partly_bound(end_condition,key) < 0) ||
+ (reverse && cmp_partly_bound(end_condition,key) > 0))) {
/* done anyway */
if (!sc.got) {
RET_TO_BIF(am_EOT, DB_ERROR_NONE);
@@ -1039,16 +1033,16 @@ static int db_select_continue_tree(Process *p,
}
} else {
if (end_condition != NIL &&
- ((!reverse && cmp_partly_bound(end_condition,key,sc.lastobj) > 0) ||
- (reverse && cmp_partly_bound(end_condition,key,sc.lastobj) < 0))) {
+ ((!reverse && cmp_partly_bound(end_condition,key) > 0) ||
+ (reverse && cmp_partly_bound(end_condition,key) < 0))) {
/* done anyway */
RET_TO_BIF(sc.accum,DB_ERROR_NONE);
}
}
/* Not done yet, let's trap. */
- sz = size_object_rel(key,sc.lastobj);
+ sz = size_object(key);
hp = HAlloc(p, 9 + sz);
- key = copy_struct_rel(key, sz, &hp, &MSO(p), sc.lastobj, NULL);
+ key = copy_struct(key, sz, &hp, &MSO(p));
continuation = TUPLE8
(hp,
tptr[1],
@@ -1075,7 +1069,6 @@ static int db_select_tree(Process *p, DbTable *tbl,
struct select_context sc;
struct mp_info mpi;
Eterm lastkey = THE_NON_VALUE;
- Eterm* lk_base = NULL;
Eterm key;
Eterm continuation;
unsigned sz;
@@ -1127,20 +1120,18 @@ static int db_select_tree(Process *p, DbTable *tbl,
if (mpi.some_limitation) {
if ((this = find_prev_from_pb_key(tb, stack, mpi.least)) != NULL) {
lastkey = GETKEY(tb, this->dbterm.tpl);
- lk_base = this->dbterm.tpl;
}
sc.end_condition = mpi.most;
}
- traverse_forward(tb, stack, lastkey, lk_base, &doit_select, &sc);
+ traverse_forward(tb, stack, lastkey, &doit_select, &sc);
} else {
if (mpi.some_limitation) {
if ((this = find_next_from_pb_key(tb, stack, mpi.most)) != NULL) {
lastkey = GETKEY(tb, this->dbterm.tpl);
- lk_base = this->dbterm.tpl;
}
sc.end_condition = mpi.least;
}
- traverse_backwards(tb, stack, lastkey, lk_base, &doit_select, &sc);
+ traverse_backwards(tb, stack, lastkey, &doit_select, &sc);
}
release_stack(tb,stack);
#ifdef HARDDEBUG
@@ -1153,9 +1144,9 @@ static int db_select_tree(Process *p, DbTable *tbl,
}
key = GETKEY(tb, sc.lastobj);
- sz = size_object_rel(key, sc.lastobj);
+ sz = size_object(key);
hp = HAlloc(p, 9 + sz + PROC_BIN_SIZE);
- key = copy_struct_rel(key, sz, &hp, &MSO(p), sc.lastobj, NULL);
+ key = copy_struct(key, sz, &hp, &MSO(p));
if (mpi.all_objects)
(mpi.mp)->flags |= BIN_FLAG_ALL_OBJECTS;
mpb=db_make_mp_binary(p,mpi.mp,&hp);
@@ -1236,7 +1227,7 @@ static int db_select_count_continue_tree(Process *p,
}
stack = get_any_stack(tb);
- traverse_backwards(tb, stack, lastkey, NULL, &doit_select_count, &sc);
+ traverse_backwards(tb, stack, lastkey, &doit_select_count, &sc);
release_stack(tb,stack);
BUMP_REDS(p, 1000 - sc.max);
@@ -1246,12 +1237,12 @@ static int db_select_count_continue_tree(Process *p,
}
key = GETKEY(tb, sc.lastobj);
if (end_condition != NIL &&
- (cmp_partly_bound(end_condition,key,sc.lastobj) > 0)) {
+ (cmp_partly_bound(end_condition,key) > 0)) {
/* done anyway */
RET_TO_BIF(make_small(sc.got),DB_ERROR_NONE);
}
/* Not done yet, let's trap. */
- sz = size_object_rel(key, sc.lastobj);
+ sz = size_object(key);
if (IS_USMALL(0, sc.got)) {
hp = HAlloc(p, sz + 6);
egot = make_small(sc.got);
@@ -1261,7 +1252,7 @@ static int db_select_count_continue_tree(Process *p,
egot = uint_to_big(sc.got, hp);
hp += BIG_UINT_HEAP_SIZE;
}
- key = copy_struct_rel(key, sz, &hp, &MSO(p), sc.lastobj, NULL);
+ key = copy_struct(key, sz, &hp, &MSO(p));
continuation = TUPLE5
(hp,
tptr[1],
@@ -1284,7 +1275,6 @@ static int db_select_count_tree(Process *p, DbTable *tbl,
struct select_count_context sc;
struct mp_info mpi;
Eterm lastkey = THE_NON_VALUE;
- Eterm* lk_base = NULL;
Eterm key;
Eterm continuation;
unsigned sz;
@@ -1334,12 +1324,11 @@ static int db_select_count_tree(Process *p, DbTable *tbl,
if (mpi.some_limitation) {
if ((this = find_next_from_pb_key(tb, stack, mpi.most)) != NULL) {
lastkey = GETKEY(tb, this->dbterm.tpl);
- lk_base = this->dbterm.tpl;
}
sc.end_condition = mpi.least;
}
- traverse_backwards(tb, stack, lastkey, lk_base, &doit_select_count, &sc);
+ traverse_backwards(tb, stack, lastkey, &doit_select_count, &sc);
release_stack(tb,stack);
BUMP_REDS(p, 1000 - sc.max);
if (sc.max > 0) {
@@ -1347,7 +1336,7 @@ static int db_select_count_tree(Process *p, DbTable *tbl,
}
key = GETKEY(tb, sc.lastobj);
- sz = size_object_rel(key, sc.lastobj);
+ sz = size_object(key);
if (IS_USMALL(0, sc.got)) {
hp = HAlloc(p, sz + PROC_BIN_SIZE + 6);
egot = make_small(sc.got);
@@ -1357,7 +1346,7 @@ static int db_select_count_tree(Process *p, DbTable *tbl,
egot = uint_to_big(sc.got, hp);
hp += BIG_UINT_HEAP_SIZE;
}
- key = copy_struct_rel(key, sz, &hp, &MSO(p), sc.lastobj, NULL);
+ key = copy_struct(key, sz, &hp, &MSO(p));
if (mpi.all_objects)
(mpi.mp)->flags |= BIN_FLAG_ALL_OBJECTS;
mpb = db_make_mp_binary(p,mpi.mp,&hp);
@@ -1388,7 +1377,6 @@ static int db_select_chunk_tree(Process *p, DbTable *tbl,
struct select_context sc;
struct mp_info mpi;
Eterm lastkey = THE_NON_VALUE;
- Eterm* lk_base = NULL;
Eterm key;
Eterm continuation;
unsigned sz;
@@ -1445,20 +1433,18 @@ static int db_select_chunk_tree(Process *p, DbTable *tbl,
if (mpi.some_limitation) {
if ((this = find_next_from_pb_key(tb, stack, mpi.most)) != NULL) {
lastkey = GETKEY(tb, this->dbterm.tpl);
- lk_base = this->dbterm.tpl;
}
sc.end_condition = mpi.least;
}
- traverse_backwards(tb, stack, lastkey, lk_base, &doit_select_chunk, &sc);
+ traverse_backwards(tb, stack, lastkey, &doit_select_chunk, &sc);
} else {
if (mpi.some_limitation) {
if ((this = find_prev_from_pb_key(tb, stack, mpi.least)) != NULL) {
lastkey = GETKEY(tb, this->dbterm.tpl);
- lk_base = this->dbterm.tpl;
}
sc.end_condition = mpi.most;
}
- traverse_forward(tb, stack, lastkey, lk_base, &doit_select_chunk, &sc);
+ traverse_forward(tb, stack, lastkey, &doit_select_chunk, &sc);
}
release_stack(tb,stack);
@@ -1483,9 +1469,9 @@ static int db_select_chunk_tree(Process *p, DbTable *tbl,
}
key = GETKEY(tb, sc.lastobj);
- sz = size_object_rel(key, sc.lastobj);
+ sz = size_object(key);
hp = HAlloc(p, 9 + sz + PROC_BIN_SIZE);
- key = copy_struct_rel(key, sz, &hp, &MSO(p), sc.lastobj, NULL);
+ key = copy_struct(key, sz, &hp, &MSO(p));
if (mpi.all_objects)
(mpi.mp)->flags |= BIN_FLAG_ALL_OBJECTS;
mpb = db_make_mp_binary(p,mpi.mp,&hp);
@@ -1508,9 +1494,9 @@ static int db_select_chunk_tree(Process *p, DbTable *tbl,
}
key = GETKEY(tb, sc.lastobj);
- sz = size_object_rel(key, sc.lastobj);
+ sz = size_object(key);
hp = HAlloc(p, 9 + sz + PROC_BIN_SIZE);
- key = copy_struct_rel(key, sz, &hp, &MSO(p), sc.lastobj, NULL);
+ key = copy_struct(key, sz, &hp, &MSO(p));
if (mpi.all_objects)
(mpi.mp)->flags |= BIN_FLAG_ALL_OBJECTS;
@@ -1586,7 +1572,7 @@ static int db_select_delete_continue_tree(Process *p,
sc.keypos = tb->common.keypos;
ASSERT(!erts_smp_atomic_read_nob(&tb->is_stack_busy));
- traverse_backwards(tb, &tb->static_stack, lastkey, NULL, &doit_select_delete, &sc);
+ traverse_backwards(tb, &tb->static_stack, lastkey, &doit_select_delete, &sc);
BUMP_REDS(p, 1000 - sc.max);
@@ -1595,11 +1581,11 @@ static int db_select_delete_continue_tree(Process *p,
}
key = GETKEY(tb, (sc.lastterm)->dbterm.tpl);
if (end_condition != NIL &&
- cmp_partly_bound(end_condition,key,sc.lastterm->dbterm.tpl) > 0) { /* done anyway */
+ cmp_partly_bound(end_condition,key) > 0) { /* done anyway */
RET_TO_BIF(erts_make_integer(sc.accum,p),DB_ERROR_NONE);
}
/* Not done yet, let's trap. */
- sz = size_object_rel(key, sc.lastterm->dbterm.tpl);
+ sz = size_object(key);
if (IS_USMALL(0, sc.accum)) {
hp = HAlloc(p, sz + 6);
eaccsum = make_small(sc.accum);
@@ -1609,7 +1595,7 @@ static int db_select_delete_continue_tree(Process *p,
eaccsum = uint_to_big(sc.accum, hp);
hp += BIG_UINT_HEAP_SIZE;
}
- key = copy_struct_rel(key, sz, &hp, &MSO(p), sc.lastterm->dbterm.tpl, NULL);
+ key = copy_struct(key, sz, &hp, &MSO(p));
continuation = TUPLE5
(hp,
tptr[1],
@@ -1630,7 +1616,6 @@ static int db_select_delete_tree(Process *p, DbTable *tbl,
struct select_delete_context sc;
struct mp_info mpi;
Eterm lastkey = THE_NON_VALUE;
- Eterm* lk_base = NULL;
Eterm key;
Eterm continuation;
unsigned sz;
@@ -1683,12 +1668,11 @@ static int db_select_delete_tree(Process *p, DbTable *tbl,
if (mpi.some_limitation) {
if ((this = find_next_from_pb_key(tb, &tb->static_stack, mpi.most)) != NULL) {
lastkey = GETKEY(tb, this->dbterm.tpl);
- lk_base = this->dbterm.tpl;
}
sc.end_condition = mpi.least;
}
- traverse_backwards(tb, &tb->static_stack, lastkey, lk_base, &doit_select_delete, &sc);
+ traverse_backwards(tb, &tb->static_stack, lastkey, &doit_select_delete, &sc);
BUMP_REDS(p, 1000 - sc.max);
if (sc.max > 0) {
@@ -1696,7 +1680,7 @@ static int db_select_delete_tree(Process *p, DbTable *tbl,
}
key = GETKEY(tb, (sc.lastterm)->dbterm.tpl);
- sz = size_object_rel(key, sc.lastterm->dbterm.tpl);
+ sz = size_object(key);
if (IS_USMALL(0, sc.accum)) {
hp = HAlloc(p, sz + PROC_BIN_SIZE + 6);
eaccsum = make_small(sc.accum);
@@ -1706,7 +1690,7 @@ static int db_select_delete_tree(Process *p, DbTable *tbl,
eaccsum = uint_to_big(sc.accum, hp);
hp += BIG_UINT_HEAP_SIZE;
}
- key = copy_struct_rel(key, sz, &hp, &MSO(p), sc.lastterm->dbterm.tpl, NULL);
+ key = copy_struct(key, sz, &hp, &MSO(p));
mpb = db_make_mp_binary(p,mpi.mp,&hp);
continuation = TUPLE5
@@ -1734,7 +1718,7 @@ static int db_take_tree(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
TreeDbTerm *this;
*ret = NIL;
- this = linkout_tree(tb, key, NULL);
+ this = linkout_tree(tb, key);
if (this) {
Eterm copy, *hp, *hend;
@@ -1756,7 +1740,7 @@ static int db_take_tree(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
/* Display tree contents (for dump) */
-static void db_print_tree(int to, void *to_arg,
+static void db_print_tree(fmtfn_t to, void *to_arg,
int show,
DbTable *tbl)
{
@@ -1845,9 +1829,7 @@ do_db_tree_foreach_offheap(TreeDbTerm *tdbt,
do_db_tree_foreach_offheap(tdbt->right, func, arg);
}
-static TreeDbTerm *linkout_tree(DbTableTree *tb,
- Eterm key, Eterm* key_base)
-{
+static TreeDbTerm *linkout_tree(DbTableTree *tb, Eterm key) {
TreeDbTerm **tstack[STACK_NEED];
int tpos = 0;
int dstack[STACK_NEED+1];
@@ -1869,7 +1851,7 @@ static TreeDbTerm *linkout_tree(DbTableTree *tb,
for (;;) {
if (!*this) { /* Failure */
return NULL;
- } else if ((c = cmp_key(tb, key, key_base, *this)) < 0) {
+ } else if ((c = cmp_key(tb, key, *this)) < 0) {
dstack[dpos++] = DIR_LEFT;
tstack[tpos++] = this;
this = &((*this)->left);
@@ -1933,7 +1915,7 @@ static TreeDbTerm *linkout_object_tree(DbTableTree *tb,
for (;;) {
if (!*this) { /* Failure */
return NULL;
- } else if ((c = cmp_key(tb,key,NULL,*this)) < 0) {
+ } else if ((c = cmp_key(tb,key,*this)) < 0) {
dstack[dpos++] = DIR_LEFT;
tstack[tpos++] = this;
this = &((*this)->left);
@@ -2319,15 +2301,13 @@ done:
* Find next and previous in sort order
*/
-static TreeDbTerm *find_next(DbTableTree *tb, DbTreeStack* stack,
- Eterm key, Eterm* key_base)
-{
+static TreeDbTerm *find_next(DbTableTree *tb, DbTreeStack* stack, Eterm key) {
TreeDbTerm *this;
TreeDbTerm *tmp;
Sint c;
if(( this = TOP_NODE(stack)) != NULL) {
- if (!cmp_key_eq(tb,key,key_base,this)) {
+ if (!cmp_key_eq(tb,key,this)) {
/* Start from the beginning */
stack->pos = stack->slot = 0;
}
@@ -2337,7 +2317,7 @@ static TreeDbTerm *find_next(DbTableTree *tb, DbTreeStack* stack,
return NULL;
for (;;) {
PUSH_NODE(stack, this);
- if (( c = cmp_key(tb,key,key_base,this) ) > 0) {
+ if (( c = cmp_key(tb,key,this) ) > 0) {
if (this->right == NULL) /* We are at the previos
and the element does
not exist */
@@ -2377,15 +2357,13 @@ static TreeDbTerm *find_next(DbTableTree *tb, DbTreeStack* stack,
return this;
}
-static TreeDbTerm *find_prev(DbTableTree *tb, DbTreeStack* stack,
- Eterm key, Eterm* key_base)
-{
+static TreeDbTerm *find_prev(DbTableTree *tb, DbTreeStack* stack, Eterm key) {
TreeDbTerm *this;
TreeDbTerm *tmp;
Sint c;
if(( this = TOP_NODE(stack)) != NULL) {
- if (!cmp_key_eq(tb,key,key_base,this)) {
+ if (!cmp_key_eq(tb,key,this)) {
/* Start from the beginning */
stack->pos = stack->slot = 0;
}
@@ -2395,7 +2373,7 @@ static TreeDbTerm *find_prev(DbTableTree *tb, DbTreeStack* stack,
return NULL;
for (;;) {
PUSH_NODE(stack, this);
- if (( c = cmp_key(tb,key,key_base,this) ) < 0) {
+ if (( c = cmp_key(tb,key,this) ) < 0) {
if (this->left == NULL) /* We are at the next
and the element does
not exist */
@@ -2448,8 +2426,7 @@ static TreeDbTerm *find_next_from_pb_key(DbTableTree *tb, DbTreeStack* stack,
return NULL;
for (;;) {
PUSH_NODE(stack, this);
- if (( c = cmp_partly_bound(key,GETKEY(tb, this->dbterm.tpl),
- this->dbterm.tpl) ) >= 0) {
+ if (( c = cmp_partly_bound(key,GETKEY(tb, this->dbterm.tpl))) >= 0) {
if (this->right == NULL) {
do {
tmp = POP_NODE(stack);
@@ -2482,8 +2459,7 @@ static TreeDbTerm *find_prev_from_pb_key(DbTableTree *tb, DbTreeStack* stack,
return NULL;
for (;;) {
PUSH_NODE(stack, this);
- if (( c = cmp_partly_bound(key,GETKEY(tb, this->dbterm.tpl),
- this->dbterm.tpl) ) <= 0) {
+ if (( c = cmp_partly_bound(key,GETKEY(tb, this->dbterm.tpl))) <= 0) {
if (this->left == NULL) {
do {
tmp = POP_NODE(stack);
@@ -2514,10 +2490,10 @@ static TreeDbTerm *find_node(DbTableTree *tb, Eterm key)
DbTreeStack* stack = get_static_stack(tb);
if(!stack || EMPTY_NODE(stack)
- || !cmp_key_eq(tb, key, NULL, (this=TOP_NODE(stack)))) {
+ || !cmp_key_eq(tb, key, (this=TOP_NODE(stack)))) {
this = tb->root;
- while (this != NULL && (res = cmp_key(tb,key,NULL,this)) != 0) {
+ while (this != NULL && (res = cmp_key(tb,key,this)) != 0) {
if (res < 0)
this = this->left;
else
@@ -2539,7 +2515,7 @@ static TreeDbTerm **find_node2(DbTableTree *tb, Eterm key)
Sint res;
this = &tb->root;
- while ((*this) != NULL && (res = cmp_key(tb, key, NULL, *this)) != 0) {
+ while ((*this) != NULL && (res = cmp_key(tb, key, *this)) != 0) {
if (res < 0)
this = &((*this)->left);
else
@@ -2589,9 +2565,6 @@ db_lookup_dbterm_tree(Process *p, DbTable *tbl, Eterm key, Eterm obj,
handle->flags = flags;
handle->bp = (void**) pp;
handle->new_size = (*pp)->dbterm.size;
-#if HALFWORD_HEAP
- handle->abs_vec = NULL;
-#endif
return 1;
}
@@ -2622,7 +2595,7 @@ db_finalize_dbterm_tree(int cret, DbUpdateHandle *handle)
*/
static void traverse_backwards(DbTableTree *tb,
DbTreeStack* stack,
- Eterm lastkey, Eterm* lk_base,
+ Eterm lastkey,
int (*doit)(DbTableTree *,
TreeDbTerm *,
void *,
@@ -2641,16 +2614,15 @@ static void traverse_backwards(DbTableTree *tb,
this = this->right;
}
this = TOP_NODE(stack);
- next = find_prev(tb, stack, GETKEY(tb, this->dbterm.tpl),
- this->dbterm.tpl);
+ next = find_prev(tb, stack, GETKEY(tb, this->dbterm.tpl));
if (!((*doit)(tb, this, context, 0)))
return;
} else {
- next = find_prev(tb, stack, lastkey, lk_base);
+ next = find_prev(tb, stack, lastkey);
}
while ((this = next) != NULL) {
- next = find_prev(tb, stack, GETKEY(tb, this->dbterm.tpl), this->dbterm.tpl);
+ next = find_prev(tb, stack, GETKEY(tb, this->dbterm.tpl));
if (!((*doit)(tb, this, context, 0)))
return;
}
@@ -2661,7 +2633,7 @@ static void traverse_backwards(DbTableTree *tb,
*/
static void traverse_forward(DbTableTree *tb,
DbTreeStack* stack,
- Eterm lastkey, Eterm* lk_base,
+ Eterm lastkey,
int (*doit)(DbTableTree *,
TreeDbTerm *,
void *,
@@ -2680,15 +2652,15 @@ static void traverse_forward(DbTableTree *tb,
this = this->left;
}
this = TOP_NODE(stack);
- next = find_next(tb, stack, GETKEY(tb, this->dbterm.tpl), this->dbterm.tpl);
+ next = find_next(tb, stack, GETKEY(tb, this->dbterm.tpl));
if (!((*doit)(tb, this, context, 1)))
return;
} else {
- next = find_next(tb, stack, lastkey, lk_base);
+ next = find_next(tb, stack, lastkey);
}
while ((this = next) != NULL) {
- next = find_next(tb, stack, GETKEY(tb, this->dbterm.tpl), this->dbterm.tpl);
+ next = find_next(tb, stack, GETKEY(tb, this->dbterm.tpl));
if (!((*doit)(tb, this, context, 1)))
return;
}
@@ -2725,7 +2697,7 @@ static int key_given(DbTableTree *tb, Eterm pattern, TreeDbTerm **ret,
-static Sint do_cmp_partly_bound(Eterm a, Eterm b, Eterm* b_base, int *done)
+static Sint do_cmp_partly_bound(Eterm a, Eterm b, int *done)
{
Eterm* aa;
Eterm* bb;
@@ -2739,44 +2711,44 @@ static Sint do_cmp_partly_bound(Eterm a, Eterm b, Eterm* b_base, int *done)
*done = 1;
return 0;
}
- if (is_same(a,NULL,b,b_base))
+ if (is_same(a,b))
return 0;
switch (a & _TAG_PRIMARY_MASK) {
case TAG_PRIMARY_LIST:
if (!is_list(b)) {
- return cmp_rel(a,NULL,b,b_base);
+ return CMP(a,b);
}
aa = list_val(a);
- bb = list_val_rel(b,b_base);
+ bb = list_val(b);
while (1) {
- if ((j = do_cmp_partly_bound(*aa++, *bb++, b_base, done)) != 0 || *done)
+ if ((j = do_cmp_partly_bound(*aa++, *bb++, done)) != 0 || *done)
return j;
- if (is_same(*aa, NULL, *bb, b_base))
+ if (is_same(*aa, *bb))
return 0;
if (is_not_list(*aa) || is_not_list(*bb))
- return do_cmp_partly_bound(*aa, *bb, b_base, done);
+ return do_cmp_partly_bound(*aa, *bb, done);
aa = list_val(*aa);
- bb = list_val_rel(*bb,b_base);
+ bb = list_val(*bb);
}
case TAG_PRIMARY_BOXED:
if ((b & _TAG_PRIMARY_MASK) != TAG_PRIMARY_BOXED) {
- return cmp_rel(a,NULL,b,b_base);
+ return CMP(a,b);
}
a_hdr = ((*boxed_val(a)) & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE;
- b_hdr = ((*boxed_val_rel(b,b_base)) & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE;
+ b_hdr = ((*boxed_val(b)) & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE;
if (a_hdr != b_hdr) {
- return cmp_rel(a, NULL, b, b_base);
+ return CMP(a,b);
}
if (a_hdr == (_TAG_HEADER_ARITYVAL >> _TAG_PRIMARY_SIZE)) {
aa = tuple_val(a);
- bb = tuple_val_rel(b, b_base);
+ bb = tuple_val(b);
/* compare the arities */
i = arityval(*aa); /* get the arity*/
if (i < arityval(*bb)) return(-1);
if (i > arityval(*bb)) return(1);
while (i--) {
- if ((j = do_cmp_partly_bound(*++aa, *++bb, b_base, done)) != 0
+ if ((j = do_cmp_partly_bound(*++aa, *++bb, done)) != 0
|| *done)
return j;
}
@@ -2784,14 +2756,13 @@ static Sint do_cmp_partly_bound(Eterm a, Eterm b, Eterm* b_base, int *done)
}
/* Drop through */
default:
- return cmp_rel(a, NULL, b, b_base);
+ return CMP(a,b);
}
}
-static Sint cmp_partly_bound(Eterm partly_bound_key, Eterm bound_key, Eterm* bk_base)
-{
+static Sint cmp_partly_bound(Eterm partly_bound_key, Eterm bound_key) {
int done = 0;
- Sint ret = do_cmp_partly_bound(partly_bound_key, bound_key, bk_base, &done);
+ Sint ret = do_cmp_partly_bound(partly_bound_key, bound_key, &done);
#ifdef HARDDEBUG
erts_fprintf(stderr,"\ncmp_partly_bound: %T", partly_bound_key);
if (ret < 0)
@@ -2800,7 +2771,7 @@ static Sint cmp_partly_bound(Eterm partly_bound_key, Eterm bound_key, Eterm* bk_
erts_fprintf(stderr," > ");
else
erts_fprintf(stderr," == ");
- erts_fprintf(stderr,"%R\n", bound_key, bk_base);
+ erts_fprintf(stderr,"%T\n", bound_key);
#endif
return ret;
}
@@ -3017,12 +2988,10 @@ static int doit_select(DbTableTree *tb, TreeDbTerm *this, void *ptr,
if (sc->end_condition != NIL &&
((forward &&
cmp_partly_bound(sc->end_condition,
- GETKEY_WITH_POS(sc->keypos, this->dbterm.tpl),
- this->dbterm.tpl) < 0) ||
+ GETKEY_WITH_POS(sc->keypos, this->dbterm.tpl)) < 0) ||
(!forward &&
cmp_partly_bound(sc->end_condition,
- GETKEY_WITH_POS(sc->keypos, this->dbterm.tpl),
- this->dbterm.tpl) > 0))) {
+ GETKEY_WITH_POS(sc->keypos, this->dbterm.tpl)) > 0))) {
return 0;
}
ret = db_match_dbterm(&tb->common,sc->p,sc->mp,sc->all_objects,
@@ -3054,8 +3023,7 @@ static int doit_select_count(DbTableTree *tb, TreeDbTerm *this, void *ptr,
/* Always backwards traversing */
if (sc->end_condition != NIL &&
(cmp_partly_bound(sc->end_condition,
- GETKEY_WITH_POS(sc->keypos, this->dbterm.tpl),
- this->dbterm.tpl) > 0)) {
+ GETKEY_WITH_POS(sc->keypos, this->dbterm.tpl)) > 0)) {
return 0;
}
ret = db_match_dbterm(&tb->common, sc->p, sc->mp, 0,
@@ -3081,12 +3049,10 @@ static int doit_select_chunk(DbTableTree *tb, TreeDbTerm *this, void *ptr,
if (sc->end_condition != NIL &&
((forward &&
cmp_partly_bound(sc->end_condition,
- GETKEY_WITH_POS(sc->keypos, this->dbterm.tpl),
- this->dbterm.tpl) < 0) ||
+ GETKEY_WITH_POS(sc->keypos, this->dbterm.tpl)) < 0) ||
(!forward &&
cmp_partly_bound(sc->end_condition,
- GETKEY_WITH_POS(sc->keypos, this->dbterm.tpl),
- this->dbterm.tpl) > 0))) {
+ GETKEY_WITH_POS(sc->keypos, this->dbterm.tpl)) > 0))) {
return 0;
}
@@ -3124,14 +3090,13 @@ static int doit_select_delete(DbTableTree *tb, TreeDbTerm *this, void *ptr,
if (sc->end_condition != NIL &&
cmp_partly_bound(sc->end_condition,
- GETKEY_WITH_POS(sc->keypos, this->dbterm.tpl),
- this->dbterm.tpl) > 0)
+ GETKEY_WITH_POS(sc->keypos, this->dbterm.tpl)) > 0)
return 0;
ret = db_match_dbterm(&tb->common, sc->p, sc->mp, 0,
&this->dbterm, NULL, 0);
if (ret == am_true) {
key = GETKEY(sc->tb, this->dbterm.tpl);
- linkout_tree(sc->tb, key, this->dbterm.tpl);
+ linkout_tree(sc->tb, key);
sc->erase_lastterm = 1;
++sc->accum;
}
@@ -3157,11 +3122,10 @@ static void do_dump_tree2(DbTableTree* tb, int to, void *to_arg, int show,
}
else {
prefix = "";
- term = make_tuple_rel(t->dbterm.tpl,t->dbterm.tpl);
+ term = make_tuple(t->dbterm.tpl);
}
- erts_print(to, to_arg, "%*s%s%R (addr = %p, bal = %d)\n",
- offset, "", prefix, term, t->dbterm.tpl,
- t, t->balance);
+ erts_print(to, to_arg, "%*s%s%T (addr = %p, bal = %d)\n",
+ offset, "", prefix, term, t, t->balance);
}
do_dump_tree2(tb, to, to_arg, show, t->left, offset + 4);
}
diff --git a/erts/emulator/beam/erl_db_tree.h b/erts/emulator/beam/erl_db_tree.h
index 6098387f5d..72749ead1e 100644
--- a/erts/emulator/beam/erl_db_tree.h
+++ b/erts/emulator/beam/erl_db_tree.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index af5b611afd..3c5bfb2552 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2014. 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.
@@ -123,6 +123,9 @@ do { \
#define TermWords(t) (((t) / (sizeof(UWord)/sizeof(Eterm))) + !!((t) % (sizeof(UWord)/sizeof(Eterm))))
+#define add_dmc_err(EINFO, STR, VAR, TERM, SEV) \
+ vadd_dmc_err(EINFO, SEV, VAR, STR, TERM)
+
static ERTS_INLINE Process *
get_proc(Process *cp, Uint32 cp_locks, Eterm id, Uint32 id_locks)
@@ -135,21 +138,22 @@ get_proc(Process *cp, Uint32 cp_locks, Eterm id, Uint32 id_locks)
static Eterm
-set_tracee_flags(Process *tracee_p, Eterm tracer, Uint d_flags, Uint e_flags) {
+set_tracee_flags(Process *tracee_p, ErtsTracer tracer,
+ Uint d_flags, Uint e_flags) {
Eterm ret;
Uint flags;
- if (tracer == NIL) {
+ if (ERTS_TRACER_IS_NIL(tracer)) {
flags = ERTS_TRACE_FLAGS(tracee_p) & ~TRACEE_FLAGS;
} else {
flags = ((ERTS_TRACE_FLAGS(tracee_p) & ~d_flags) | e_flags);
- if (! flags) tracer = NIL;
+ if (! flags) tracer = erts_tracer_nil;
}
- ret = ((ERTS_TRACER_PROC(tracee_p) != tracer
+ ret = ((!ERTS_TRACER_COMPARE(ERTS_TRACER(tracee_p),tracer)
|| ERTS_TRACE_FLAGS(tracee_p) != flags)
? am_true
: am_false);
- ERTS_TRACER_PROC(tracee_p) = tracer;
+ erts_tracer_replace(&tracee_p->common, tracer);
ERTS_TRACE_FLAGS(tracee_p) = flags;
return ret;
}
@@ -163,40 +167,17 @@ set_tracee_flags(Process *tracee_p, Eterm tracer, Uint d_flags, Uint e_flags) {
** returns fail_term on failure. Fails if tracer pid or port is invalid.
*/
static Eterm
-set_match_trace(Process *tracee_p, Eterm fail_term, Eterm tracer,
+set_match_trace(Process *tracee_p, Eterm fail_term, ErtsTracer tracer,
Uint d_flags, Uint e_flags) {
- Eterm ret = fail_term;
- Process *tracer_p;
-
- ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCKS_ALL ==
- erts_proc_lc_my_proc_locks(tracee_p));
-
- if (is_internal_pid(tracer)
- && (tracer_p =
- erts_pid2proc(tracee_p, ERTS_PROC_LOCKS_ALL,
- tracer, ERTS_PROC_LOCKS_ALL))) {
- if (tracee_p != tracer_p) {
- ret = set_tracee_flags(tracee_p, tracer, d_flags, e_flags);
- ERTS_TRACE_FLAGS(tracer_p) |= (ERTS_TRACE_FLAGS(tracee_p)
- ? F_TRACER
- : 0);
- erts_smp_proc_unlock(tracer_p, ERTS_PROC_LOCKS_ALL);
- }
- } else if (is_internal_port(tracer)) {
- Port *tracer_port =
- erts_id2port_sflgs(tracer,
- tracee_p,
- ERTS_PROC_LOCKS_ALL,
- ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP);
- if (tracer_port) {
- ret = set_tracee_flags(tracee_p, tracer, d_flags, e_flags);
- erts_port_release(tracer_port);
- }
- } else {
- ASSERT(is_nil(tracer));
- ret = set_tracee_flags(tracee_p, tracer, d_flags, e_flags);
- }
- return ret;
+
+ ERTS_SMP_LC_ASSERT(
+ ERTS_PROC_LOCKS_ALL == erts_proc_lc_my_proc_locks(tracee_p)
+ || erts_thr_progress_is_blocking());
+
+ if (ERTS_TRACER_IS_NIL(tracer)
+ || erts_is_tracer_enabled(tracer, &tracee_p->common))
+ return set_tracee_flags(tracee_p, tracer, d_flags, e_flags);
+ return fail_term;
}
/*
@@ -239,11 +220,7 @@ typedef enum {
matchCall2,
matchCall3,
matchPushV,
-#if HALFWORD_HEAP
- matchPushVGuard, /* First guard-only variable reference */
-#endif
- matchPushVResult, /* First variable reference in result, or (if HALFWORD)
- in guard if also referenced in result */
+ matchPushVResult, /* First variable reference in result */
matchPushExpr, /* Push the whole expression we're matching ('$_') */
matchPushArrayAsList, /* Only when parameter is an Array and
not an erlang term (DCOMP_TRACE) */
@@ -310,9 +287,6 @@ DMC_DECLARE_STACK_TYPE(unsigned);
typedef struct DMCVariable {
int is_bound;
int is_in_body;
-#if HALFWORD_HEAP
- int first_guard_label; /* to maybe change from PushVGuard to PushVResult */
-#endif
} DMCVariable;
typedef struct DMCHeap {
@@ -374,7 +348,6 @@ typedef struct MatchVariable {
Eterm term;
#ifdef DEBUG
Process* proc;
- Eterm* base;
#endif
} MatchVariable;
@@ -415,7 +388,7 @@ cleanup_match_pseudo_process(ErtsMatchPseudoProcess *mpsp, int keep_heap)
else {
int i;
for (i = 0; i < ERTS_DEFAULT_MS_HEAP_SIZE; i++) {
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
mpsp->default_heap[i] = (Eterm) 0xdeadbeefdeadbeef;
#else
mpsp->default_heap[i] = (Eterm) 0xdeadbeef;
@@ -442,17 +415,27 @@ get_match_pseudo_process(Process *c_p, Uint heap_size)
{
ErtsMatchPseudoProcess *mpsp;
#ifdef ERTS_SMP
- mpsp = (ErtsMatchPseudoProcess *) c_p->scheduler_data->match_pseudo_process;
- if (mpsp)
+ ErtsSchedulerData *esdp;
+
+ esdp = c_p ? c_p->scheduler_data : erts_get_scheduler_data();
+
+ mpsp = esdp ? esdp->match_pseudo_process :
+ (ErtsMatchPseudoProcess*) erts_smp_tsd_get(match_pseudo_process_key);
+
+ if (mpsp) {
+ ASSERT(mpsp == erts_smp_tsd_get(match_pseudo_process_key));
+ ASSERT(mpsp->process.scheduler_data == esdp);
cleanup_match_pseudo_process(mpsp, 0);
+ }
else {
ASSERT(erts_smp_tsd_get(match_pseudo_process_key) == NULL);
mpsp = create_match_pseudo_process();
- c_p->scheduler_data->match_pseudo_process = (void *) mpsp;
+ if (esdp) {
+ esdp->match_pseudo_process = (void *) mpsp;
+ }
+ mpsp->process.scheduler_data = esdp;
erts_smp_tsd_set(match_pseudo_process_key, (void *) mpsp);
}
- ASSERT(mpsp == erts_smp_tsd_get(match_pseudo_process_key));
- mpsp->process.scheduler_data = c_p->scheduler_data;
#else
mpsp = match_pseudo_process;
cleanup_match_pseudo_process(mpsp, 0);
@@ -920,11 +903,7 @@ void db_match_dis(Binary *prog);
#define TRACE /* Nothing */
#define FENCE_PATTERN_SIZE 0
#endif
-static void add_dmc_err(DMCErrInfo *err_info,
- char *str,
- int variable,
- Eterm term,
- DMCErrorSeverity severity);
+static void vadd_dmc_err(DMCErrInfo*, DMCErrorSeverity, int var, const char *str, ...);
static Eterm dpm_array_to_list(Process *psp, Eterm *arr, int arity);
@@ -1020,12 +999,20 @@ Eterm erts_match_set_get_source(Binary *mpsp)
}
/* This one is for the tracing */
-Binary *erts_match_set_compile(Process *p, Eterm matchexpr) {
+Binary *erts_match_set_compile(Process *p, Eterm matchexpr, Eterm MFA) {
Binary *bin;
Uint sz;
Eterm *hp;
+ Uint flags;
+
+ switch (MFA) {
+ case am_receive: flags = DCOMP_TRACE; break;
+ case am_send: flags = DCOMP_TRACE | DCOMP_ALLOW_TRACE_OPS; break;
+ default:
+ flags = DCOMP_TRACE | DCOMP_CALL_TRACE | DCOMP_ALLOW_TRACE_OPS;
+ }
- bin = db_match_set_compile(p, matchexpr, DCOMP_TRACE);
+ bin = db_match_set_compile(p, matchexpr, flags);
if (bin != NULL) {
MatchProg *prog = Binary2MatchProg(bin);
sz = size_object(matchexpr);
@@ -1155,8 +1142,8 @@ Eterm db_match_set_lint(Process *p, Eterm matchexpr, Uint flags)
int i;
if (!is_list(matchexpr)) {
- add_dmc_err(err_info, "Match programs are not in a list.",
- -1, 0UL, dmcError);
+ add_dmc_err(err_info, "Match programs are not in a list.",
+ -1, 0UL, dmcError);
goto done;
}
num_heads = 0;
@@ -1164,9 +1151,8 @@ Eterm db_match_set_lint(Process *p, Eterm matchexpr, Uint flags)
++num_heads;
if (l != NIL) { /* proper list... */
- add_dmc_err(err_info, "Match programs are not in a proper "
- "list.",
- -1, 0UL, dmcError);
+ add_dmc_err(err_info, "Match programs are not in a proper list.",
+ -1, 0UL, dmcError);
goto done;
}
@@ -1233,30 +1219,37 @@ done:
return ret;
}
-Eterm erts_match_set_run(Process *p, Binary *mpsp,
- Eterm *args, int num_args,
- enum erts_pam_run_flags in_flags,
- Uint32 *return_flags)
+/* Returns
+ * am_false if no match or
+ * if {message,false} has been called,
+ * am_true if {message,_} has NOT been called or
+ * if {message,true} has been called,
+ * Msg if {message,Msg} has been called.
+ *
+ * If return value is_not_immed
+ * then erts_match_set_release_result_trace() must be called to release it.
+ */
+Eterm erts_match_set_run_trace(Process *c_p,
+ Process *self,
+ Binary *mpsp,
+ Eterm *args, int num_args,
+ enum erts_pam_run_flags in_flags,
+ Uint32 *return_flags)
{
Eterm ret;
- ret = db_prog_match(p, mpsp, NIL, NULL, args, num_args,
+ ret = db_prog_match(c_p, self, mpsp, NIL, args, num_args,
in_flags, return_flags);
-#if defined(HARDDEBUG)
- if (is_non_value(ret)) {
- erts_fprintf(stderr, "Failed\n");
- } else {
- erts_fprintf(stderr, "Returning : %T\n", ret);
+
+ ASSERT(!(is_non_value(ret) && *return_flags));
+
+ if (is_non_value(ret) || ret == am_false) {
+ erts_match_set_release_result(c_p);
+ return am_false;
}
-#endif
+ if (is_immed(ret))
+ erts_match_set_release_result(c_p);
return ret;
- /* Returns
- * THE_NON_VALUE if no match
- * am_false if {message,false} has been called,
- * am_true if {message,_} has not been called or
- * if {message,true} has been called,
- * Msg if {message,Msg} has been called.
- */
}
static Eterm erts_match_set_run_ets(Process *p, Binary *mpsp,
@@ -1265,7 +1258,8 @@ static Eterm erts_match_set_run_ets(Process *p, Binary *mpsp,
{
Eterm ret;
- ret = db_prog_match(p, mpsp, args, NULL, NULL, num_args,
+ ret = db_prog_match(p, p,
+ mpsp, args, NULL, num_args,
ERTS_PAM_COPY_RESULT,
return_flags);
#if defined(HARDDEBUG)
@@ -1755,79 +1749,16 @@ static Eterm dpm_array_to_list(Process *psp, Eterm *arr, int arity)
return ret;
}
-
-#if HALFWORD_HEAP
-struct heap_checkpoint_t
-{
- Process *p;
- Eterm* htop;
- ErlHeapFragment* mbuf;
- unsigned used_size;
- ErlOffHeap off_heap;
-};
-
-static void heap_checkpoint_init(Process* p, struct heap_checkpoint_t* hcp)
-{
- hcp->p = p;
- hcp->htop = HEAP_TOP(p);
- hcp->mbuf = MBUF(p);
- hcp->used_size = hcp->mbuf ? hcp->mbuf->used_size : 0;
- hcp->off_heap = MSO(p);
-}
-
-static void heap_checkpoint_revert(struct heap_checkpoint_t* hcp)
-{
- struct erl_off_heap_header* oh = MSO(hcp->p).first;
-
- if (oh != hcp->off_heap.first) {
- ASSERT(oh != NULL);
- if (hcp->off_heap.first) {
- while (oh->next != hcp->off_heap.first) {
- oh = oh->next;
- }
- oh->next = NULL;
- }
- erts_cleanup_offheap(&MSO(hcp->p));
- MSO(hcp->p) = hcp->off_heap;
- }
- if (MBUF(hcp->p) != hcp->mbuf) {
- ErlHeapFragment* hf = MBUF(hcp->p);
- ASSERT(hf != NULL);
- if (hcp->mbuf) {
- while (hf->next != hcp->mbuf) {
- hf = hf->next;
- }
- hf->next = NULL;
- }
- free_message_buffer(MBUF(hcp->p));
- MBUF(hcp->p) = hcp->mbuf;
- }
- if (hcp->mbuf != NULL && hcp->mbuf->used_size != hcp->used_size) {
- hcp->mbuf->used_size = hcp->used_size;
- }
- HEAP_TOP(hcp->p) = hcp->htop;
-}
-#endif /* HALFWORD_HEAP */
-
-static ERTS_INLINE Eterm copy_object_rel(Process* p, Eterm term, Eterm* base)
-{
- if (!is_immed(term)) {
- Uint sz = size_object_rel(term, base);
- Eterm* top = HAllocX(p, sz, HEAP_XTRA);
- return copy_struct_rel(term, sz, &top, &MSO(p), base, NULL);
- }
- return term;
-}
-
-
/*
** Execution of the match program, this is Pam.
** May return THE_NON_VALUE, which is a bailout.
** the parameter 'arity' is only used if 'term' is actually an array,
** i.e. 'DCOMP_TRACE' was specified
*/
-Eterm db_prog_match(Process *c_p, Binary *bprog,
- Eterm term, Eterm* base,
+Eterm db_prog_match(Process *c_p,
+ Process *self,
+ Binary *bprog,
+ Eterm term,
Eterm *termp,
int arity,
enum erts_pam_run_flags in_flags,
@@ -1839,10 +1770,10 @@ Eterm db_prog_match(Process *c_p, Binary *bprog,
Eterm *esp;
MatchVariable* variables;
BeamInstr *cp;
- UWord *pc = prog->text;
+ const UWord *pc = prog->text;
Eterm *ehp;
Eterm ret;
- Uint n = 0; /* To avoid warning. */
+ Uint n;
int i;
unsigned do_catch;
ErtsMatchPseudoProcess *mpsp;
@@ -1854,17 +1785,16 @@ Eterm db_prog_match(Process *c_p, Binary *bprog,
Eterm (*bif)(Process*, ...);
Eterm bif_args[3];
int fail_label;
- int atomic_trace;
-#if HALFWORD_HEAP
- struct heap_checkpoint_t c_p_checkpoint = {};
-#endif
#ifdef DMC_DEBUG
Uint *heap_fence;
Uint *stack_fence;
Uint save_op;
#endif /* DMC_DEBUG */
- ASSERT(base==NULL || HALFWORD_HEAP);
+ ERTS_UNDEF(n,0);
+ ERTS_UNDEF(current_scheduled,NULL);
+
+ ASSERT(c_p || !(in_flags & ERTS_PAM_COPY_RESULT));
mpsp = get_match_pseudo_process(c_p, prog->heap_size);
psp = &mpsp->process;
@@ -1872,33 +1802,11 @@ Eterm db_prog_match(Process *c_p, Binary *bprog,
/* We need to lure the scheduler into believing in the pseudo process,
because of floating point exceptions. Do *after* mpsp is set!!! */
- esdp = ERTS_GET_SCHEDULER_DATA_FROM_PROC(c_p);
- ASSERT(esdp != NULL);
- current_scheduled = esdp->current_process;
+ esdp = erts_get_scheduler_data();
+ if (esdp)
+ current_scheduled = esdp->current_process;
/* SMP: psp->scheduler_data is set by get_match_pseudo_process */
- atomic_trace = 0;
-#define BEGIN_ATOMIC_TRACE(p) \
- do { \
- if (! atomic_trace) { \
- erts_refc_inc(&bprog->refc, 2); \
- erts_smp_proc_unlock((p), ERTS_PROC_LOCK_MAIN); \
- erts_smp_thr_progress_block(); \
- atomic_trace = !0; \
- } \
- } while (0)
-#define END_ATOMIC_TRACE(p) \
- do { \
- if (atomic_trace) { \
- erts_smp_thr_progress_unblock(); \
- erts_smp_proc_lock((p), ERTS_PROC_LOCK_MAIN); \
- if (erts_refc_dectest(&bprog->refc, 0) == 0) {\
- erts_bin_free(bprog); \
- } \
- atomic_trace = 0; \
- } \
- } while (0)
-
#ifdef DMC_DEBUG
save_op = 0;
heap_fence = (Eterm*)((char*) mpsp->u.heap + prog->stack_offset) - 1;
@@ -1916,11 +1824,7 @@ Eterm db_prog_match(Process *c_p, Binary *bprog,
do_catch != 0 */
*return_flags = 0U;
-
variables = mpsp->u.variables;
-#if HALFWORD_HEAP
- c_p_checkpoint.p = NULL;
-#endif
restart:
ep = &term;
@@ -1931,14 +1835,12 @@ restart:
fail_label = -1;
build_proc = psp;
esdp->current_process = psp;
- ASSERT_HALFWORD(!c_p_checkpoint.p);
#ifdef DEBUG
ASSERT(variables == mpsp->u.variables);
for (i=0; i<prog->num_bindings; i++) {
variables[i].term = THE_NON_VALUE;
variables[i].proc = NULL;
- variables[i].base = base;
}
#endif
@@ -1974,9 +1876,9 @@ restart:
variables[n].term = dpm_array_to_list(psp, termp, arity);
break;
case matchTuple: /* *ep is a tuple of arity n */
- if (!is_tuple_rel(*ep,base))
+ if (!is_tuple(*ep))
FAIL();
- ep = tuple_val_rel(*ep,base);
+ ep = tuple_val(*ep);
n = *pc++;
if (arityval(*ep) != n)
FAIL();
@@ -1984,9 +1886,9 @@ restart:
break;
case matchPushT: /* *ep is a tuple of arity n,
push ptr to first element */
- if (!is_tuple_rel(*ep,base))
+ if (!is_tuple(*ep))
FAIL();
- tp = tuple_val_rel(*ep,base);
+ tp = tuple_val(*ep);
n = *pc++;
if (arityval(*tp) != n)
FAIL();
@@ -1996,51 +1898,51 @@ restart:
case matchList:
if (!is_list(*ep))
FAIL();
- ep = list_val_rel(*ep,base);
+ ep = list_val(*ep);
break;
case matchPushL:
if (!is_list(*ep))
FAIL();
- *sp++ = list_val_rel(*ep,base);
+ *sp++ = list_val(*ep);
++ep;
break;
case matchMap:
- if (!is_map_rel(*ep, base)) {
+ if (!is_map(*ep)) {
FAIL();
}
n = *pc++;
- if (is_flatmap_rel(*ep,base)) {
- if (flatmap_get_size(flatmap_val_rel(*ep, base)) < n) {
+ if (is_flatmap(*ep)) {
+ if (flatmap_get_size(flatmap_val(*ep)) < n) {
FAIL();
}
} else {
- ASSERT(is_hashmap_rel(*ep,base));
- if (hashmap_size_rel(*ep, base) < n) {
+ ASSERT(is_hashmap(*ep));
+ if (hashmap_size(*ep) < n) {
FAIL();
}
}
- ep = flatmap_val_rel(*ep, base);
+ ep = flatmap_val(*ep);
break;
case matchPushM:
- if (!is_map_rel(*ep, base)) {
+ if (!is_map(*ep)) {
FAIL();
}
n = *pc++;
- if (is_flatmap_rel(*ep,base)) {
- if (flatmap_get_size(flatmap_val_rel(*ep, base)) < n) {
+ if (is_flatmap(*ep)) {
+ if (flatmap_get_size(flatmap_val(*ep)) < n) {
FAIL();
}
} else {
- ASSERT(is_hashmap_rel(*ep,base));
- if (hashmap_size_rel(*ep, base) < n) {
+ ASSERT(is_hashmap(*ep));
+ if (hashmap_size(*ep) < n) {
FAIL();
}
}
- *sp++ = flatmap_val_rel(*ep++, base);
+ *sp++ = flatmap_val(*ep++);
break;
case matchKey:
t = (Eterm) *pc++;
- tp = erts_maps_get_rel(t, make_boxed_rel(ep, base), base);
+ tp = erts_maps_get(t, make_boxed(ep));
if (!tp) {
FAIL();
}
@@ -2061,29 +1963,29 @@ restart:
break;
case matchCmp:
n = *pc++;
- if (!eq_rel(variables[n].term, base, *ep, base))
+ if (!EQ(variables[n].term, *ep))
FAIL();
++ep;
break;
case matchEqBin:
t = (Eterm) *pc++;
- if (!eq_rel(t,NULL,*ep,base))
+ if (!EQ(t,*ep))
FAIL();
++ep;
break;
case matchEqFloat:
- if (!is_float_rel(*ep,base))
+ if (!is_float(*ep))
FAIL();
- if (memcmp(float_val_rel(*ep,base) + 1, pc, sizeof(double)))
+ if (memcmp(float_val(*ep) + 1, pc, sizeof(double)))
FAIL();
pc += TermWords(2);
++ep;
break;
case matchEqRef: {
Eterm* epc = (Eterm*)pc;
- if (!is_ref_rel(*ep,base))
+ if (!is_ref(*ep))
FAIL();
- if (!eq_rel(make_internal_ref_rel(epc, epc), epc, *ep, base)) {
+ if (!EQ(make_internal_ref(epc), *ep)) {
FAIL();
}
i = thing_arityval(*epc);
@@ -2092,9 +1994,9 @@ restart:
break;
}
case matchEqBig:
- if (!is_big_rel(*ep,base))
+ if (!is_big(*ep))
FAIL();
- tp = big_val_rel(*ep,base);
+ tp = big_val(*ep);
{
Eterm *epc = (Eterm *) pc;
if (*tp != *epc)
@@ -2236,62 +2138,36 @@ restart:
esp -= 2;
esp[-1] = t;
break;
-
- #if HALFWORD_HEAP
- case matchPushVGuard:
- if (!base) goto case_matchPushV;
- /* Build NULL-based copy on pseudo heap for easy disposal */
- n = *pc++;
- ASSERT(is_value(variables[n].term));
- ASSERT(!variables[n].proc);
- variables[n].term = copy_object_rel(psp, variables[n].term, base);
- *esp++ = variables[n].term;
- #ifdef DEBUG
- variables[n].proc = psp;
- variables[n].base = NULL;
- #endif
- break;
- #endif
case matchPushVResult:
if (!(in_flags & ERTS_PAM_COPY_RESULT)) goto case_matchPushV;
-
- /* Build (NULL-based) copy on callers heap */
- #if HALFWORD_HEAP
- if (!do_catch && !c_p_checkpoint.p) {
- heap_checkpoint_init(c_p, &c_p_checkpoint);
- }
- #endif
+ /* Build copy on callers heap */
n = *pc++;
ASSERT(is_value(variables[n].term));
ASSERT(!variables[n].proc);
- variables[n].term = copy_object_rel(c_p, variables[n].term, base);
+ variables[n].term = copy_object_x(variables[n].term, c_p, HEAP_XTRA);
*esp++ = variables[n].term;
#ifdef DEBUG
variables[n].proc = c_p;
- variables[n].base = NULL;
#endif
break;
case matchPushV:
case_matchPushV:
n = *pc++;
ASSERT(is_value(variables[n].term));
- ASSERT(!variables[n].base);
*esp++ = variables[n].term;
break;
case matchPushExpr:
if (in_flags & ERTS_PAM_COPY_RESULT) {
Uint sz;
Eterm* top;
- sz = size_object_rel(term, base);
+ sz = size_object(term);
top = HAllocX(build_proc, sz, HEAP_XTRA);
if (in_flags & ERTS_PAM_CONTIGUOUS_TUPLE) {
- ASSERT(is_tuple_rel(term,base));
- *esp++ = copy_shallow_rel(tuple_val_rel(term,base), sz,
- &top, &MSO(build_proc), base);
+ ASSERT(is_tuple(term));
+ *esp++ = copy_shallow(tuple_val(term), sz, &top, &MSO(build_proc));
}
else {
- *esp++ = copy_struct_rel(term, sz, &top, &MSO(build_proc),
- base, NULL);
+ *esp++ = copy_struct(term, sz, &top, &MSO(build_proc));
}
}
else {
@@ -2299,7 +2175,6 @@ restart:
}
break;
case matchPushArrayAsList:
- ASSERT_HALFWORD(base == NULL);
n = arity; /* Only happens when 'term' is an array */
tp = termp;
ehp = HAllocX(build_proc, n*2, HEAP_XTRA);
@@ -2315,7 +2190,6 @@ restart:
break;
case matchPushArrayAsListU:
/* This instruction is NOT efficient. */
- ASSERT_HALFWORD(base == NULL);
*esp++ = dpm_array_to_list(build_proc, termp, arity);
break;
case matchTrue:
@@ -2391,7 +2265,7 @@ restart:
pc += n;
break;
case matchSelf:
- *esp++ = c_p->common.id;
+ *esp++ = self->common.id;
break;
case matchWaste:
--esp;
@@ -2401,6 +2275,7 @@ restart:
break;
case matchProcessDump: {
erts_dsprintf_buf_t *dsbufp = erts_create_tmp_dsbuf(0);
+ ASSERT(c_p == self);
print_process_info(ERTS_PRINT_DSBUF, (void *) dsbufp, c_p);
*esp++ = new_binary(build_proc, (byte *)dsbufp->str,
dsbufp->str_len);
@@ -2419,18 +2294,16 @@ restart:
*return_flags |= MATCH_SET_EXCEPTION_TRACE;
*esp++ = am_true;
break;
- case matchIsSeqTrace:
- if (SEQ_TRACE_TOKEN(c_p) != NIL
-#ifdef USE_VM_PROBES
- && SEQ_TRACE_TOKEN(c_p) != am_have_dt_utag
-#endif
- )
+ case matchIsSeqTrace:
+ ASSERT(c_p == self);
+ if (have_seqtrace(SEQ_TRACE_TOKEN(c_p)))
*esp++ = am_true;
else
*esp++ = am_false;
break;
case matchSetSeqToken:
- t = erts_seq_trace(c_p, esp[-1], esp[-2], 0);
+ ASSERT(c_p == self);
+ t = erts_seq_trace(c_p, esp[-1], esp[-2], 0);
if (is_non_value(t)) {
esp[-2] = FAIL_TERM;
} else {
@@ -2438,7 +2311,8 @@ restart:
}
--esp;
break;
- case matchSetSeqTokenFake:
+ case matchSetSeqTokenFake:
+ ASSERT(c_p == self);
t = seq_trace_fake(c_p, esp[-1]);
if (is_non_value(t)) {
esp[-2] = FAIL_TERM;
@@ -2447,12 +2321,9 @@ restart:
}
--esp;
break;
- case matchGetSeqToken:
- if (SEQ_TRACE_TOKEN(c_p) == NIL
-#ifdef USE_VM_PROBES
- || SEQ_TRACE_TOKEN(c_p) == am_have_dt_utag
-#endif
- )
+ case matchGetSeqToken:
+ ASSERT(c_p == self);
+ if (have_no_seqtrace(SEQ_TRACE_TOKEN(c_p)))
*esp++ = NIL;
else {
Eterm sender = SEQ_TRACE_TOKEN_SENDER(c_p);
@@ -2475,49 +2346,62 @@ restart:
ASSERT(is_immed(ehp[5]));
}
break;
- case matchEnableTrace:
+ case matchEnableTrace:
+ ASSERT(c_p == self);
if ( (n = erts_trace_flag2bit(esp[-1]))) {
- BEGIN_ATOMIC_TRACE(c_p);
- set_tracee_flags(c_p, ERTS_TRACER_PROC(c_p), 0, n);
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
+ set_tracee_flags(c_p, ERTS_TRACER(c_p), 0, n);
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
esp[-1] = am_true;
} else {
esp[-1] = FAIL_TERM;
}
break;
- case matchEnableTrace2:
+ case matchEnableTrace2:
+ ASSERT(c_p == self);
n = erts_trace_flag2bit((--esp)[-1]);
esp[-1] = FAIL_TERM;
if (n) {
- BEGIN_ATOMIC_TRACE(c_p);
- if ( (tmpp = get_proc(c_p, 0, esp[0], 0))) {
+ if ( (tmpp = get_proc(c_p, ERTS_PROC_LOCK_MAIN, esp[0], ERTS_PROC_LOCKS_ALL))) {
/* Always take over the tracer of the current process */
- set_tracee_flags(tmpp, ERTS_TRACER_PROC(c_p), 0, n);
- esp[-1] = am_true;
+ set_tracee_flags(tmpp, ERTS_TRACER(c_p), 0, n);
+ if (tmpp == c_p)
+ erts_smp_proc_unlock(tmpp, ERTS_PROC_LOCKS_ALL_MINOR);
+ else
+ erts_smp_proc_unlock(tmpp, ERTS_PROC_LOCKS_ALL);
+ esp[-1] = am_true;
}
}
break;
- case matchDisableTrace:
+ case matchDisableTrace:
+ ASSERT(c_p == self);
if ( (n = erts_trace_flag2bit(esp[-1]))) {
- BEGIN_ATOMIC_TRACE(c_p);
- set_tracee_flags(c_p, ERTS_TRACER_PROC(c_p), n, 0);
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
+ set_tracee_flags(c_p, ERTS_TRACER(c_p), n, 0);
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
esp[-1] = am_true;
} else {
esp[-1] = FAIL_TERM;
}
break;
- case matchDisableTrace2:
+ case matchDisableTrace2:
+ ASSERT(c_p == self);
n = erts_trace_flag2bit((--esp)[-1]);
esp[-1] = FAIL_TERM;
if (n) {
- BEGIN_ATOMIC_TRACE(c_p);
- if ( (tmpp = get_proc(c_p, 0, esp[0], 0))) {
+ if ( (tmpp = get_proc(c_p, ERTS_PROC_LOCK_MAIN, esp[0], ERTS_PROC_LOCKS_ALL))) {
/* Always take over the tracer of the current process */
- set_tracee_flags(tmpp, ERTS_TRACER_PROC(c_p), n, 0);
- esp[-1] = am_true;
+ set_tracee_flags(tmpp, ERTS_TRACER(c_p), n, 0);
+ if (tmpp == c_p)
+ erts_smp_proc_unlock(tmpp, ERTS_PROC_LOCKS_ALL_MINOR);
+ else
+ erts_smp_proc_unlock(tmpp, ERTS_PROC_LOCKS_ALL);
+ esp[-1] = am_true;
}
}
break;
- case matchCaller:
+ case matchCaller:
+ ASSERT(c_p == self);
if (!(c_p->cp) || !(cp = find_function_from_pc(c_p->cp))) {
*esp++ = am_undefined;
} else {
@@ -2529,7 +2413,8 @@ restart:
ehp[3] = make_small((Uint) cp[2]);
}
break;
- case matchSilent:
+ case matchSilent:
+ ASSERT(c_p == self);
--esp;
if (in_flags & ERTS_PAM_IGNORE_TRACE_SILENT)
break;
@@ -2544,11 +2429,12 @@ restart:
erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
}
break;
- case matchTrace2:
+ case matchTrace2:
+ ASSERT(c_p == self);
{
/* disable enable */
Uint d_flags = 0, e_flags = 0; /* process trace flags */
- Eterm tracer = ERTS_TRACER_PROC(c_p);
+ ErtsTracer tracer = erts_tracer_nil;
/* XXX Atomicity note: Not fully atomic. Default tracer
* is sampled from current process but applied to
* tracee and tracer later after releasing main
@@ -2560,29 +2446,35 @@ restart:
* {trace,[],[{{tracer,Tracer}}]} is much, much older.
*/
int cputs = 0;
+ erts_tracer_update(&tracer, ERTS_TRACER(c_p));
if (! erts_trace_flags(esp[-1], &d_flags, &tracer, &cputs) ||
! erts_trace_flags(esp[-2], &e_flags, &tracer, &cputs) ||
cputs ) {
(--esp)[-1] = FAIL_TERM;
+ ERTS_TRACER_CLEAR(&tracer);
break;
}
erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
(--esp)[-1] = set_match_trace(c_p, FAIL_TERM, tracer,
d_flags, e_flags);
erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
+ ERTS_TRACER_CLEAR(&tracer);
}
break;
- case matchTrace3:
+ case matchTrace3:
+ ASSERT(c_p == self);
{
/* disable enable */
Uint d_flags = 0, e_flags = 0; /* process trace flags */
- Eterm tracer = ERTS_TRACER_PROC(c_p);
+ ErtsTracer tracer = erts_tracer_nil;
/* XXX Atomicity note. Not fully atomic. See above.
* Above it could possibly be solved, but not here.
*/
int cputs = 0;
Eterm tracee = (--esp)[0];
+
+ erts_tracer_update(&tracer, ERTS_TRACER(c_p));
if (! erts_trace_flags(esp[-1], &d_flags, &tracer, &cputs) ||
! erts_trace_flags(esp[-2], &e_flags, &tracer, &cputs) ||
@@ -2590,6 +2482,7 @@ restart:
! (tmpp = get_proc(c_p, ERTS_PROC_LOCK_MAIN,
tracee, ERTS_PROC_LOCKS_ALL))) {
(--esp)[-1] = FAIL_TERM;
+ ERTS_TRACER_CLEAR(&tracer);
break;
}
if (tmpp == c_p) {
@@ -2603,6 +2496,7 @@ restart:
erts_smp_proc_unlock(tmpp, ERTS_PROC_LOCKS_ALL);
erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
}
+ ERTS_TRACER_CLEAR(&tracer);
}
break;
case matchCatch: /* Match success, now build result */
@@ -2619,13 +2513,6 @@ restart:
}
}
fail:
-#if HALFWORD_HEAP
- if (c_p_checkpoint.p) {
- /* Dispose garbage built by guards on caller heap */
- heap_checkpoint_revert(&c_p_checkpoint);
- c_p_checkpoint.p = NULL;
- }
-#endif
*return_flags = 0U;
if (fail_label >= 0) { /* We failed during a "TryMeElse",
lets restart, with the next match
@@ -2649,15 +2536,12 @@ success:
}
#endif
- esdp->current_process = current_scheduled;
-
- END_ATOMIC_TRACE(c_p);
+ if (esdp)
+ esdp->current_process = current_scheduled;
return ret;
#undef FAIL
#undef FAIL_TERM
-#undef BEGIN_ATOMIC_TRACE
-#undef END_ATOMIC_TRACE
}
@@ -2788,13 +2672,6 @@ Wterm db_do_read_element(DbUpdateHandle* handle, Sint position)
{
Eterm elem = handle->dbterm->tpl[position];
if (!is_header(elem)) {
-#if HALFWORD_HEAP
- if (!is_immed(elem)
- && !handle->tb->common.compress
- && !(handle->abs_vec && handle->abs_vec[position])) {
- return rterm2wterm(elem, handle->dbterm->tpl);
- }
-#endif
return elem;
}
@@ -2822,9 +2699,6 @@ void db_do_update_element(DbUpdateHandle* handle,
Eterm* oldp;
Uint newval_sz;
Uint oldval_sz;
-#if HALFWORD_HEAP
- Eterm* old_base;
-#endif
if (is_both_immed(newval,oldval)) {
handle->dbterm->tpl[position] = newval;
@@ -2841,15 +2715,8 @@ void db_do_update_element(DbUpdateHandle* handle,
handle->dbterm);
handle->flags |= DB_MUST_RESIZE;
oldval = handle->dbterm->tpl[position];
- #if HALFWORD_HEAP
- old_base = NULL;
- #endif
}
else {
- #if HALFWORD_HEAP
- ASSERT(!handle->abs_vec);
- old_base = handle->dbterm->tpl;
- #endif
if (is_boxed(newval)) {
newp = boxed_val(newval);
switch (*newp & _TAG_HEADER_MASK) {
@@ -2859,7 +2726,7 @@ void db_do_update_element(DbUpdateHandle* handle,
case _TAG_HEADER_HEAP_BIN:
newval_sz = header_arity(*newp) + 1;
if (is_boxed(oldval)) {
- oldp = boxed_val_rel(oldval,old_base);
+ oldp = boxed_val(oldval);
switch (*oldp & _TAG_HEADER_MASK) {
case _TAG_HEADER_POS_BIG:
case _TAG_HEADER_NEG_BIG:
@@ -2879,20 +2746,13 @@ void db_do_update_element(DbUpdateHandle* handle,
}
}
}
-#if HALFWORD_HEAP
- else {
- old_base = (handle->tb->common.compress
- || (handle->abs_vec && handle->abs_vec[position])) ?
- NULL : handle->dbterm->tpl;
- }
-#endif
/* Not possible for simple memcpy or dbterm is already non-contiguous, */
/* need to realloc... */
newval_sz = is_immed(newval) ? 0 : size_object(newval);
new_size_set:
- oldval_sz = is_immed(oldval) ? 0 : size_object_rel(oldval,old_base);
+ oldval_sz = is_immed(oldval) ? 0 : size_object(oldval);
both_size_set:
handle->new_size = handle->new_size - oldval_sz + newval_sz;
@@ -2900,19 +2760,6 @@ both_size_set:
/* write new value in old dbterm, finalize will make a flat copy */
handle->dbterm->tpl[position] = newval;
handle->flags |= DB_MUST_RESIZE;
-
-#if HALFWORD_HEAP
- if (old_base && newval_sz > 0) {
- ASSERT(!handle->tb->common.compress);
- if (!handle->abs_vec) {
- int i = header_arity(handle->dbterm->tpl[0]);
- handle->abs_vec = erts_alloc(ERTS_ALC_T_TMP, (i+1)*sizeof(char));
- sys_memset(handle->abs_vec, 0, i+1);
- /* abs_vec[0] not used */
- }
- handle->abs_vec[position] = 1;
- }
-#endif
}
static ERTS_INLINE byte* db_realloc_term(DbTableCommon* tb, void* old,
@@ -3011,7 +2858,7 @@ static void* copy_to_comp(DbTableCommon* tb, Eterm obj, DbTerm* dest,
tpl[arity + 1] = alloc_size;
tmp_offheap.first = NULL;
- tpl[tb->keypos] = copy_struct_rel(key, size_object(key), &top.ep, &tmp_offheap, NULL, tpl);
+ tpl[tb->keypos] = copy_struct(key, size_object(key), &top.ep, &tmp_offheap);
dest->first_oh = tmp_offheap.first;
for (i=1; i<=arity; i++) {
if (i != tb->keypos) {
@@ -3030,7 +2877,7 @@ static void* copy_to_comp(DbTableCommon* tb, Eterm obj, DbTerm* dest,
Eterm* dbg_top = erts_alloc(ERTS_ALC_T_DB_TERM, dest->size * sizeof(Eterm));
dest->debug_clone = dbg_top;
tmp_offheap.first = dest->first_oh;
- copy_struct_rel(obj, dest->size, &dbg_top, &tmp_offheap, NULL, dbg_top);
+ copy_struct(obj, dest->size, &dbg_top, &tmp_offheap);
dest->first_oh = tmp_offheap.first;
ASSERT(dbg_top == dest->debug_clone + dest->size);
}
@@ -3077,7 +2924,7 @@ void* db_store_term(DbTableCommon *tb, DbTerm* old, Uint offset, Eterm obj)
newp->size = size;
top = newp->tpl;
tmp_offheap.first = NULL;
- copy_struct_rel(obj, size, &top, &tmp_offheap, NULL, top);
+ copy_struct(obj, size, &top, &tmp_offheap);
newp->first_oh = tmp_offheap.first;
#ifdef DEBUG_CLONE
newp->debug_clone = NULL;
@@ -3160,29 +3007,8 @@ void db_finalize_resize(DbUpdateHandle* handle, Uint offset)
tmp_offheap.first = NULL;
- #if HALFWORD_HEAP
- if (handle->abs_vec) {
- int i, arity = header_arity(handle->dbterm->tpl[0]);
-
- top[0] = tpl[0];
- top += arity + 1;
- for (i=1; i<=arity; i++) {
- Eterm* src_base = handle->abs_vec[i] ? NULL : tpl;
-
- newDbTerm->tpl[i] = copy_struct_rel(tpl[i],
- size_object_rel(tpl[i],src_base),
- &top, &tmp_offheap, src_base,
- newDbTerm->tpl);
- }
- newDbTerm->first_oh = tmp_offheap.first;
- ASSERT((byte*)top <= (newp + alloc_sz));
- erts_free(ERTS_ALC_T_TMP, handle->abs_vec);
- }
- else
- #endif /* HALFWORD_HEAP */
{
- copy_struct_rel(make_tuple_rel(tpl,tpl), handle->new_size, &top,
- &tmp_offheap, tpl, top);
+ copy_struct(make_tuple(tpl), handle->new_size, &top, &tmp_offheap);
newDbTerm->first_oh = tmp_offheap.first;
ASSERT((byte*)top == (newp + alloc_sz));
}
@@ -3199,9 +3025,9 @@ Eterm db_copy_from_comp(DbTableCommon* tb, DbTerm* bp, Eterm** hpp,
hp[0] = bp->tpl[0];
*hpp += arity + 1;
- hp[tb->keypos] = copy_struct_rel(bp->tpl[tb->keypos],
- size_object_rel(bp->tpl[tb->keypos], bp->tpl),
- hpp, off_heap, bp->tpl, NULL);
+ hp[tb->keypos] = copy_struct(bp->tpl[tb->keypos],
+ size_object(bp->tpl[tb->keypos]),
+ hpp, off_heap);
erts_factory_static_init(&factory, *hpp, bp->size - (arity+1), off_heap);
@@ -3221,7 +3047,7 @@ Eterm db_copy_from_comp(DbTableCommon* tb, DbTerm* bp, Eterm** hpp,
ASSERT((*hpp - hp) <= bp->size);
#ifdef DEBUG_CLONE
- ASSERT(eq_rel(make_tuple(hp),NULL,make_tuple(bp->debug_clone),bp->debug_clone));
+ ASSERT(EQ(make_tuple(hp),make_tuple(bp->debug_clone)));
#endif
return make_tuple(hp);
}
@@ -3245,14 +3071,14 @@ Eterm db_copy_element_from_ets(DbTableCommon* tb, Process* p,
*hpp = erts_produce_heap(&factory, extra, 0);
erts_factory_close(&factory);
#ifdef DEBUG_CLONE
- ASSERT(eq_rel(copy, NULL, obj->debug_clone[pos], obj->debug_clone));
+ ASSERT(EQ(copy, obj->debug_clone[pos]));
#endif
return copy;
}
else {
- Uint sz = size_object_rel(obj->tpl[pos], obj->tpl);
+ Uint sz = size_object(obj->tpl[pos]);
*hpp = HAlloc(p, sz + extra);
- return copy_struct_rel(obj->tpl[pos], sz, hpp, &MSO(p), obj->tpl, NULL);
+ return copy_struct(obj->tpl[pos], sz, hpp, &MSO(p));
}
}
@@ -3281,7 +3107,7 @@ void db_cleanup_offheap_comp(DbTerm* obj)
break;
case FUN_SUBTAG:
ASSERT(u.pb != &tmp);
- if (erts_refc_dectest(&u.fun->fe->refc, 0) == 0) {
+ if (erts_smp_refc_dectest(&u.fun->fe->refc, 0) == 0) {
erts_erase_fun_entry(u.fun->fe);
}
break;
@@ -3460,20 +3286,20 @@ int erts_db_is_compiled_ms(Eterm term)
** Utility to add an error
*/
-static void add_dmc_err(DMCErrInfo *err_info,
- char *str,
- int variable,
- Eterm term,
- DMCErrorSeverity severity)
+static void vadd_dmc_err(DMCErrInfo *err_info,
+ DMCErrorSeverity severity,
+ int variable,
+ const char *str,
+ ...)
{
+ DMCError *e;
+ va_list args;
+ va_start(args, str);
+
+
/* Linked in in reverse order, to ease the formatting */
- DMCError *e = erts_alloc(ERTS_ALC_T_DB_DMC_ERROR, sizeof(DMCError));
- if (term != 0UL) {
- erts_snprintf(e->error_string, DMC_ERR_STR_LEN, str, term);
- } else {
- strncpy(e->error_string, str, DMC_ERR_STR_LEN);
- e->error_string[DMC_ERR_STR_LEN] ='\0';
- }
+ e = erts_alloc(ERTS_ALC_T_DB_DMC_ERROR, sizeof(DMCError));
+ erts_vsnprintf(e->error_string, DMC_ERR_STR_LEN, str, args);
e->variable = variable;
e->severity = severity;
e->next = err_info->first;
@@ -3483,8 +3309,11 @@ static void add_dmc_err(DMCErrInfo *err_info,
err_info->first = e;
if (severity >= dmcError)
err_info->error_added = 1;
+
+ va_end(args);
}
+
/*
** Handle one term in the match expression (not the guard)
*/
@@ -3599,26 +3428,10 @@ static DMCRet dmc_one_term(DMCContext *context,
{
Eterm* ref_val = internal_ref_val(c);
DMC_PUSH(*text, matchEqRef);
-#if HALFWORD_HEAP
- {
- union {
- UWord u;
- Uint t[2];
- } fiddle;
- ASSERT(thing_arityval(ref_val[0]) == 3);
- fiddle.t[0] = ref_val[0];
- fiddle.t[1] = ref_val[1];
- DMC_PUSH(*text, fiddle.u);
- fiddle.t[0] = ref_val[2];
- fiddle.t[1] = ref_val[3];
- DMC_PUSH(*text, fiddle.u);
- }
-#else
n = thing_arityval(ref_val[0]);
for (i = 0; i <= n; ++i) {
DMC_PUSH(*text, ref_val[i]);
}
-#endif
break;
}
case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE):
@@ -3627,53 +3440,19 @@ static DMCRet dmc_one_term(DMCContext *context,
Eterm* bval = big_val(c);
n = thing_arityval(bval[0]);
DMC_PUSH(*text, matchEqBig);
-#if HALFWORD_HEAP
- {
- union {
- UWord u;
- Uint t[2];
- } fiddle;
- ASSERT(n >= 1);
- fiddle.t[0] = bval[0];
- fiddle.t[1] = bval[1];
- DMC_PUSH(*text, fiddle.u);
- for (i = 2; i <= n; ++i) {
- fiddle.t[0] = bval[i];
- if (++i <= n) {
- fiddle.t[1] = bval[i];
- } else {
- fiddle.t[1] = (Uint) 0;
- }
- DMC_PUSH(*text, fiddle.u);
- }
- }
-#else
for (i = 0; i <= n; ++i) {
DMC_PUSH(*text, (Uint) bval[i]);
}
-#endif
break;
}
case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
DMC_PUSH(*text,matchEqFloat);
-#if HALFWORD_HEAP
- {
- union {
- UWord u;
- Uint t[2];
- } fiddle;
- fiddle.t[0] = float_val(c)[1];
- fiddle.t[1] = float_val(c)[2];
- DMC_PUSH(*text, fiddle.u);
- }
-#else
DMC_PUSH(*text, (Uint) float_val(c)[1]);
#ifdef ARCH_64
DMC_PUSH(*text, (Uint) 0);
#else
DMC_PUSH(*text, (Uint) float_val(c)[2]);
#endif
-#endif
break;
default: /* BINARY, FUN, VECTOR, or EXTERNAL */
DMC_PUSH(*text, matchEqBin);
@@ -3733,24 +3512,21 @@ static void do_emit_constant(DMCContext *context, DMC_STACK_TYPE(UWord) *text,
context->stack_need = context->stack_used;
}
-#define RETURN_ERROR_X(String, X, Y, ContextP, ConstantF) \
-do { \
-if ((ContextP)->err_info != NULL) { \
- (ConstantF) = 0; \
- add_dmc_err((ContextP)->err_info, String, X, Y, dmcError); \
- return retOk; \
-} else \
- return retFail; \
-} while(0)
+#define RETURN_ERROR_X(VAR, ContextP, ConstantF, String, ARG) \
+ (((ContextP)->err_info != NULL) \
+ ? ((ConstantF) = 0, \
+ vadd_dmc_err((ContextP)->err_info, dmcError, VAR, String, ARG), \
+ retOk) \
+ : retFail)
#define RETURN_ERROR(String, ContextP, ConstantF) \
- RETURN_ERROR_X(String, -1, 0UL, ContextP, ConstantF)
+ return RETURN_ERROR_X(-1, ContextP, ConstantF, String, 0)
#define RETURN_VAR_ERROR(String, N, ContextP, ConstantF) \
- RETURN_ERROR_X(String, N, 0UL, ContextP, ConstantF)
+ return RETURN_ERROR_X(N, ContextP, ConstantF, String, 0)
#define RETURN_TERM_ERROR(String, T, ContextP, ConstantF) \
- RETURN_ERROR_X(String, -1, T, ContextP, ConstantF)
+ return RETURN_ERROR_X(-1, ContextP, ConstantF, String, T)
#define WARNING(String, ContextP) \
add_dmc_err((ContextP)->err_info, String, -1, 0UL, dmcWarning)
@@ -3997,24 +3773,8 @@ static void dmc_add_pushv_variant(DMCContext *context, DMCHeap *heap,
MatchOps instr = matchPushV;
ASSERT(n < heap->vars_used && v->is_bound);
- if (context->is_guard) {
- #if HALFWORD_HEAP
- if (!v->first_guard_label) {
- v->first_guard_label = DMC_STACK_NUM(*text);
- ASSERT(v->first_guard_label);
- instr = matchPushVGuard; /* may be changed to PushVResult below */
- }
- #endif
- }
- else { /* body */
- #if HALFWORD_HEAP
- if (v->first_guard_label) {
- /* Avoid double-copy, copy to result heap at first encounter in guard */
- DMC_POKE(*text, v->first_guard_label, matchPushVResult);
- v->is_in_body = 1;
- }
- #endif
- if (!v->is_in_body) {
+ if (!context->is_guard) {
+ if(!v->is_in_body) {
instr = matchPushVResult;
v->is_in_body = 1;
}
@@ -4032,7 +3792,7 @@ static DMCRet dmc_variable(DMCContext *context,
Uint n = db_is_variable(t);
if (n >= heap->vars_used || !heap->vars[n].is_bound) {
- RETURN_VAR_ERROR("Variable $%d is unbound.", n, context, *constant);
+ RETURN_VAR_ERROR("Variable $%%d is unbound.", n, context, *constant);
}
dmc_add_pushv_variant(context, heap, text, n);
@@ -4364,7 +4124,30 @@ static DMCRet dmc_exception_trace(DMCContext *context,
return retOk;
}
-
+static int check_trace(const char* op,
+ DMCContext *context,
+ int *constant,
+ int need_cflags,
+ int allow_in_guard,
+ DMCRet* retp)
+{
+ if (!(context->cflags & DCOMP_TRACE)) {
+ *retp = RETURN_ERROR_X(-1, context, *constant, "Special form '%s' "
+ "used in wrong dialect.", op);
+ return 0;
+ }
+ if ((context->cflags & need_cflags) != need_cflags) {
+ *retp = RETURN_ERROR_X(-1, context, *constant, "Special form '%s' "
+ "not allow for this trace event.", op);
+ return 0;
+ }
+ if (context->is_guard && !allow_in_guard) {
+ *retp = RETURN_ERROR_X(-1, context, *constant, "Special form '%s' "
+ "called in guard context.", op);
+ return 0;
+ }
+ return 1;
+}
static DMCRet dmc_is_seq_trace(DMCContext *context,
DMCHeap *heap,
@@ -4374,12 +4157,11 @@ static DMCRet dmc_is_seq_trace(DMCContext *context,
{
Eterm *p = tuple_val(t);
Uint a = arityval(*p);
+ DMCRet ret;
- if (!(context->cflags & DCOMP_TRACE)) {
- RETURN_ERROR("Special form 'is_seq_trace' used in wrong dialect.",
- context,
- *constant);
- }
+ if (!check_trace("is_seq_trace", context, constant, DCOMP_ALLOW_TRACE_OPS, 1, &ret))
+ return ret;
+
if (a != 1) {
RETURN_TERM_ERROR("Special form 'is_seq_trace' called with "
"arguments in %T.", t, context, *constant);
@@ -4403,16 +4185,8 @@ static DMCRet dmc_set_seq_token(DMCContext *context,
DMCRet ret;
int c;
-
- if (!(context->cflags & DCOMP_TRACE)) {
- RETURN_ERROR("Special form 'set_seq_token' used in wrong dialect.",
- context,
- *constant);
- }
- if (context->is_guard) {
- RETURN_ERROR("Special form 'set_seq_token' called in "
- "guard context.", context, *constant);
- }
+ if (!check_trace("set_seq_trace", context, constant, DCOMP_ALLOW_TRACE_OPS, 0, &ret))
+ return ret;
if (a != 3) {
RETURN_TERM_ERROR("Special form 'set_seq_token' called with wrong "
@@ -4449,16 +4223,11 @@ static DMCRet dmc_get_seq_token(DMCContext *context,
{
Eterm *p = tuple_val(t);
Uint a = arityval(*p);
+ DMCRet ret;
+
+ if (!check_trace("get_seq_token", context, constant, DCOMP_ALLOW_TRACE_OPS, 0, &ret))
+ return ret;
- if (!(context->cflags & DCOMP_TRACE)) {
- RETURN_ERROR("Special form 'get_seq_token' used in wrong dialect.",
- context,
- *constant);
- }
- if (context->is_guard) {
- RETURN_ERROR("Special form 'get_seq_token' called in "
- "guard context.", context, *constant);
- }
if (a != 1) {
RETURN_TERM_ERROR("Special form 'get_seq_token' called with "
"arguments in %T.", t, context,
@@ -4522,16 +4291,10 @@ static DMCRet dmc_process_dump(DMCContext *context,
{
Eterm *p = tuple_val(t);
Uint a = arityval(*p);
-
- if (!(context->cflags & DCOMP_TRACE)) {
- RETURN_ERROR("Special form 'process_dump' used in wrong dialect.",
- context,
- *constant);
- }
- if (context->is_guard) {
- RETURN_ERROR("Special form 'process_dump' called in "
- "guard context.", context, *constant);
- }
+ DMCRet ret;
+
+ if (!check_trace("process_dump", context, constant, DCOMP_ALLOW_TRACE_OPS, 0, &ret))
+ return ret;
if (a != 1) {
RETURN_TERM_ERROR("Special form 'process_dump' called with "
@@ -4555,17 +4318,8 @@ static DMCRet dmc_enable_trace(DMCContext *context,
DMCRet ret;
int c;
-
- if (!(context->cflags & DCOMP_TRACE)) {
- RETURN_ERROR("Special form 'enable_trace' used in wrong dialect.",
- context,
- *constant);
- }
- if (context->is_guard) {
- RETURN_ERROR("Special form 'enable_trace' called in guard context.",
- context,
- *constant);
- }
+ if (!check_trace("enable_trace", context, constant, DCOMP_ALLOW_TRACE_OPS, 0, &ret))
+ return ret;
switch (a) {
case 2:
@@ -4614,18 +4368,9 @@ static DMCRet dmc_disable_trace(DMCContext *context,
Uint a = arityval(*p);
DMCRet ret;
int c;
-
- if (!(context->cflags & DCOMP_TRACE)) {
- RETURN_ERROR("Special form 'disable_trace' used in wrong dialect.",
- context,
- *constant);
- }
- if (context->is_guard) {
- RETURN_ERROR("Special form 'disable_trace' called in guard context.",
- context,
- *constant);
- }
+ if (!check_trace("disable_trace", context, constant, DCOMP_ALLOW_TRACE_OPS, 0, &ret))
+ return ret;
switch (a) {
case 2:
@@ -4675,17 +4420,8 @@ static DMCRet dmc_trace(DMCContext *context,
DMCRet ret;
int c;
-
- if (!(context->cflags & DCOMP_TRACE)) {
- RETURN_ERROR("Special form 'trace' used in wrong dialect.",
- context,
- *constant);
- }
- if (context->is_guard) {
- RETURN_ERROR("Special form 'trace' called in guard context.",
- context,
- *constant);
- }
+ if (!check_trace("trace", context, constant, DCOMP_ALLOW_TRACE_OPS, 0, &ret))
+ return ret;
switch (a) {
case 3:
@@ -4746,16 +4482,11 @@ static DMCRet dmc_caller(DMCContext *context,
{
Eterm *p = tuple_val(t);
Uint a = arityval(*p);
+ DMCRet ret;
- if (!(context->cflags & DCOMP_TRACE)) {
- RETURN_ERROR("Special form 'caller' used in wrong dialect.",
- context,
- *constant);
- }
- if (context->is_guard) {
- RETURN_ERROR("Special form 'caller' called in "
- "guard context.", context, *constant);
- }
+ if (!check_trace("caller", context, constant,
+ (DCOMP_CALL_TRACE|DCOMP_ALLOW_TRACE_OPS), 0, &ret))
+ return ret;
if (a != 1) {
RETURN_TERM_ERROR("Special form 'caller' called with "
@@ -4781,15 +4512,8 @@ static DMCRet dmc_silent(DMCContext *context,
DMCRet ret;
int c;
- if (!(context->cflags & DCOMP_TRACE)) {
- RETURN_ERROR("Special form 'silent' used in wrong dialect.",
- context,
- *constant);
- }
- if (context->is_guard) {
- RETURN_ERROR("Special form 'silent' called in "
- "guard context.", context, *constant);
- }
+ if (!check_trace("silent", context, constant, DCOMP_ALLOW_TRACE_OPS, 0, &ret))
+ return ret;
if (a != 2) {
RETURN_TERM_ERROR("Special form 'silent' called with wrong "
@@ -5329,13 +5053,16 @@ static Eterm match_spec_test(Process *p, Eterm against, Eterm spec, int trace)
return THE_NON_VALUE;
}
if (trace) {
- lint_res = db_match_set_lint(p, spec, DCOMP_TRACE | DCOMP_FAKE_DESTRUCTIVE);
- mps = db_match_set_compile(p, spec, DCOMP_TRACE | DCOMP_FAKE_DESTRUCTIVE);
+ const Uint cflags = (DCOMP_TRACE | DCOMP_FAKE_DESTRUCTIVE |
+ DCOMP_CALL_TRACE | DCOMP_ALLOW_TRACE_OPS);
+ lint_res = db_match_set_lint(p, spec, cflags);
+ mps = db_match_set_compile(p, spec, cflags);
} else {
- lint_res = db_match_set_lint(p, spec, DCOMP_TABLE | DCOMP_FAKE_DESTRUCTIVE);
- mps = db_match_set_compile(p, spec, DCOMP_TABLE | DCOMP_FAKE_DESTRUCTIVE);
+ const Uint cflags = (DCOMP_TABLE | DCOMP_FAKE_DESTRUCTIVE);
+ lint_res = db_match_set_lint(p, spec, cflags);
+ mps = db_match_set_compile(p, spec, cflags);
}
-
+
if (mps == NULL) {
hp = HAlloc(p,3);
ret = TUPLE2(hp, am_error, lint_res);
@@ -5366,7 +5093,8 @@ static Eterm match_spec_test(Process *p, Eterm against, Eterm spec, int trace)
}
save_cp = p->cp;
p->cp = NULL;
- res = erts_match_set_run(p, mps, arr, n,
+ res = erts_match_set_run_trace(p, p,
+ mps, arr, n,
ERTS_PAM_COPY_RESULT|ERTS_PAM_IGNORE_TRACE_SILENT,
&ret_flags);
p->cp = save_cp;
@@ -5443,16 +5171,14 @@ Eterm db_match_dbterm(DbTableCommon* tb, Process* c_p, Binary* bprog,
int all, DbTerm* obj, Eterm** hpp, Uint extra)
{
Uint32 dummy;
- Eterm* base;
Eterm res;
if (tb->compress) {
obj = db_alloc_tmp_uncompressed(tb, obj);
- base = NULL;
}
- else base = HALFWORD_HEAP ? obj->tpl : NULL;
- res = db_prog_match(c_p, bprog, make_tuple_rel(obj->tpl,base), base, NULL, 0,
+ res = db_prog_match(c_p, c_p,
+ bprog, make_tuple(obj->tpl), NULL, 0,
ERTS_PAM_COPY_RESULT|ERTS_PAM_CONTIGUOUS_TUPLE, &dummy);
if (is_value(res) && hpp!=NULL) {
@@ -5573,7 +5299,7 @@ void db_match_dis(Binary *bp)
first = 0;
else
erts_printf(", ");
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
erts_printf("0x%016bex", rt->data.ui[ri]);
#else
erts_printf("0x%08bex", rt->data.ui[ri]);
@@ -5597,7 +5323,7 @@ void db_match_dis(Binary *bp)
first = 0;
else
erts_printf(", ");
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
erts_printf("0x%016bex", *et);
#else
erts_printf("0x%08bex", *et);
@@ -5720,13 +5446,6 @@ void db_match_dis(Binary *bp)
++t;
erts_printf("PushV\t%beu\n", n);
break;
- #if HALFWORD_HEAP
- case matchPushVGuard:
- n = (Uint) *++t;
- ++t;
- erts_printf("PushVGuard\t%beu\n", n);
- break;
- #endif
case matchPushVResult:
n = (Uint) *++t;
++t;
diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h
index 0903a40460..b30f0580dd 100644
--- a/erts/emulator/beam/erl_db_util.h
+++ b/erts/emulator/beam/erl_db_util.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2013. 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.
@@ -79,6 +79,7 @@ typedef union db_table DbTable;
#define DB_MUST_RESIZE 1
#define DB_NEW_OBJECT 2
+#define DB_INC_TRY_GROW 4
/* Info about a database entry while it's being updated
* (by update_counter or update_element)
@@ -90,9 +91,6 @@ typedef struct {
Uint new_size;
int flags;
void* lck;
-#if HALFWORD_HEAP
- unsigned char* abs_vec; /* [i] true if dbterm->tpl[i] is absolute Eterm */
-#endif
} DbUpdateHandle;
@@ -177,7 +175,7 @@ typedef struct db_table_method
int (*db_free_table)(DbTable* db /* [in out] */ );
int (*db_free_table_continue)(DbTable* db); /* [in out] */
- void (*db_print)(int to,
+ void (*db_print)(fmtfn_t to,
void* to_arg,
int show,
DbTable* tb /* [in out] */ );
@@ -213,7 +211,7 @@ typedef struct db_fixation {
*/
typedef struct db_table_common {
- erts_refc_t ref; /* fixation counter */
+ erts_smp_refc_t ref; /* fixation counter */
#ifdef ERTS_SMP
erts_smp_rwmtx_t rwlock; /* rw lock on table */
erts_smp_mtx_t fixlock; /* Protects fixations,megasec,sec,microsec */
@@ -262,7 +260,7 @@ typedef struct db_table_common {
(DB_BAG | DB_SET | DB_DUPLICATE_BAG)))
#define IS_TREE_TABLE(Status) (!!((Status) & \
DB_ORDERED_SET))
-#define NFIXED(T) (erts_refc_read(&(T)->common.ref,0))
+#define NFIXED(T) (erts_smp_refc_read(&(T)->common.ref,0))
#define IS_FIXED(T) (NFIXED(T) != 0)
/*
@@ -290,10 +288,10 @@ ERTS_GLB_INLINE Eterm db_copy_key(Process* p, DbTable* tb, DbTerm* obj)
Eterm key = GETKEY(tb, obj->tpl);
if IS_CONST(key) return key;
else {
- Uint size = size_object_rel(key, obj->tpl);
+ Uint size = size_object(key);
Eterm* hp = HAlloc(p, size);
- Eterm res = copy_struct_rel(key, size, &hp, &MSO(p), obj->tpl, NULL);
- ASSERT(eq_rel(res,NULL,key,obj->tpl));
+ Eterm res = copy_struct(key, size, &hp, &MSO(p));
+ ASSERT(EQ(res,key));
return res;
}
}
@@ -305,14 +303,14 @@ ERTS_GLB_INLINE Eterm db_copy_object_from_ets(DbTableCommon* tb, DbTerm* bp,
return db_copy_from_comp(tb, bp, hpp, off_heap);
}
else {
- return copy_shallow_rel(bp->tpl, bp->size, hpp, off_heap, bp->tpl);
+ return copy_shallow(bp->tpl, bp->size, hpp, off_heap);
}
}
ERTS_GLB_INLINE int db_eq(DbTableCommon* tb, Eterm a, DbTerm* b)
{
if (!tb->compress) {
- return eq_rel(a, NULL, make_tuple_rel(b->tpl,b->tpl), b->tpl);
+ return EQ(a, make_tuple(b->tpl));
}
else {
return db_eq_comp(tb, a, b);
@@ -428,6 +426,11 @@ typedef struct dmc_err_info {
#define DCOMP_FAKE_DESTRUCTIVE ((Uint) 8) /* When this is active, no setting of
trace control words or seq_trace tokens will be done. */
+/* Allow lock seizing operations on the tracee and 3rd party processes */
+#define DCOMP_ALLOW_TRACE_OPS ((Uint) 0x10)
+
+/* This is call trace */
+#define DCOMP_CALL_TRACE ((Uint) 0x20)
Binary *db_match_compile(Eterm *matchexpr, Eterm *guards,
Eterm *body, int num_matches,
@@ -438,7 +441,8 @@ Binary *db_match_compile(Eterm *matchexpr, Eterm *guards,
Eterm db_match_dbterm(DbTableCommon* tb, Process* c_p, Binary* bprog,
int all, DbTerm* obj, Eterm** hpp, Uint extra);
-Eterm db_prog_match(Process *p, Binary *prog, Eterm term, Eterm* base,
+Eterm db_prog_match(Process *p, Process *self,
+ Binary *prog, Eterm term,
Eterm *termp, int arity,
enum erts_pam_run_flags in_flags,
Uint32 *return_flags /* Zeroed on enter */);
diff --git a/erts/emulator/beam/erl_debug.c b/erts/emulator/beam/erl_debug.c
index 4928aae9c2..3526bb684d 100644
--- a/erts/emulator/beam/erl_debug.c
+++ b/erts/emulator/beam/erl_debug.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -60,10 +60,10 @@ static const char dashes[PTR_SIZE+3] = {
void pps(Process*, Eterm*);
void ptd(Process*, Eterm);
-void paranoid_display(int, void*, Process*, Eterm);
+void paranoid_display(fmtfn_t, void*, Process*, Eterm);
static int dcount;
-static int pdisplay1(int to, void *to_arg, Process* p, Eterm obj);
+static int pdisplay1(fmtfn_t to, void *to_arg, Process* p, Eterm obj);
void ptd(Process* p, Eterm x)
{
@@ -77,14 +77,14 @@ void ptd(Process* p, Eterm x)
*/
void
-paranoid_display(int to, void *to_arg, Process* p, Eterm obj)
+paranoid_display(fmtfn_t to, void *to_arg, Process* p, Eterm obj)
{
dcount = 100000;
pdisplay1(to, to_arg, p, obj);
}
static int
-pdisplay1(int to, void *to_arg, Process* p, Eterm obj)
+pdisplay1(fmtfn_t to, void *to_arg, Process* p, Eterm obj)
{
int i, k;
Eterm* nobj;
@@ -201,7 +201,7 @@ pdisplay1(int to, void *to_arg, Process* p, Eterm obj)
void
pps(Process* p, Eterm* stop)
{
- int to = ERTS_PRINT_STDOUT;
+ fmtfn_t to = ERTS_PRINT_STDOUT;
void *to_arg = NULL;
Eterm* sp = STACK_START(p) - 1;
@@ -312,6 +312,8 @@ void erts_check_for_holes(Process* p)
p->last_htop = HEAP_TOP(p);
for (hf = MBUF(p); hf != 0; hf = hf->next) {
+ if (hf == p->heap_hfrag)
+ continue;
if (hf == p->last_mbuf) {
break;
}
@@ -402,7 +404,7 @@ void verify_process(Process *p)
erts_exit(ERTS_ERROR_EXIT,"Wild pointer found in " name " of %T!\n",p->common.id); }
- ErlMessage* mp = p->msg.first;
+ ErtsMessage* mp = p->msg.first;
VERBOSE(DEBUG_MEMORY,("Verify process: %T...\n",p->common.id));
@@ -416,7 +418,7 @@ void verify_process(Process *p)
erts_check_heap(p);
if (p->dictionary)
- VERIFY_AREA("dictionary",p->dictionary->data, p->dictionary->used);
+ VERIFY_AREA("dictionary", ERTS_PD_START(p->dictionary), ERTS_PD_SIZE(p->dictionary));
VERIFY_ETERM("seq trace token",p->seq_trace_token);
VERIFY_ETERM("group leader",p->group_leader);
VERIFY_ETERM("fvalue",p->fvalue);
@@ -531,7 +533,7 @@ static void print_process_memory(Process *p)
PTR_SIZE, "PCB", dashes, dashes, dashes, dashes);
if (p->msg.first != NULL) {
- ErlMessage* mp;
+ ErtsMessage* mp;
erts_printf(" Message Queue:\n");
mp = p->msg.first;
while (mp != NULL) {
@@ -542,8 +544,8 @@ static void print_process_memory(Process *p)
}
if (p->dictionary != NULL) {
- int n = p->dictionary->used;
- Eterm *ptr = p->dictionary->data;
+ int n = ERTS_PD_SIZE(p->dictionary);
+ Eterm *ptr = ERTS_PD_START(p->dictionary);
erts_printf(" Dictionary: ");
while (n--) erts_printf("0x%0*lx ",PTR_SIZE,(unsigned long)ptr++);
erts_printf("\n");
@@ -631,29 +633,4 @@ void print_memory_info(Process *p)
}
erts_printf("+-----------------%s-%s-%s-%s-+\n",dashes,dashes,dashes,dashes);
}
-#if !HEAP_ON_C_STACK && defined(DEBUG)
-Eterm *erts_debug_allocate_tmp_heap(int size, Process *p)
-{
- ErtsSchedulerData *sd = ((p == NULL) ? erts_get_scheduler_data() : ERTS_PROC_GET_SCHDATA(p));
- int offset = sd->num_tmp_heap_used;
-
- ASSERT(offset+size <= TMP_HEAP_SIZE);
- return (sd->tmp_heap)+offset;
-}
-void erts_debug_use_tmp_heap(int size, Process *p)
-{
- ErtsSchedulerData *sd = ((p == NULL) ? erts_get_scheduler_data() : ERTS_PROC_GET_SCHDATA(p));
-
- sd->num_tmp_heap_used += size;
- ASSERT(sd->num_tmp_heap_used <= TMP_HEAP_SIZE);
-}
-void erts_debug_unuse_tmp_heap(int size, Process *p)
-{
- ErtsSchedulerData *sd = ((p == NULL) ? erts_get_scheduler_data() : ERTS_PROC_GET_SCHDATA(p));
-
- sd->num_tmp_heap_used -= size;
- ASSERT(sd->num_tmp_heap_used >= 0);
-}
-#endif
#endif
-
diff --git a/erts/emulator/beam/erl_debug.h b/erts/emulator/beam/erl_debug.h
index 4905e64f07..029320691d 100644
--- a/erts/emulator/beam/erl_debug.h
+++ b/erts/emulator/beam/erl_debug.h
@@ -48,6 +48,7 @@
#define DEBUG_THREADS 0x0010 /* Thread-related stuff */
#define DEBUG_PROCESSES 0x0020 /* Process creation and removal */
#define DEBUG_MEMORY 0x0040 /* Display results of memory checks */
+#define DEBUG_SHCOPY 0x0080 /* Sharing-preserving copying of terms */
extern Uint32 verbose;
@@ -92,10 +93,4 @@ extern void print_tagged_memory(Eterm *start, Eterm *end);
extern void print_untagged_memory(Eterm *start, Eterm *end);
extern void print_memory(Process *p);
extern void print_memory_info(Process *p);
-#if defined(DEBUG) && !HEAP_ON_C_STACK
-extern Eterm *erts_debug_allocate_tmp_heap(int, Process *);
-extern void erts_debug_use_tmp_heap(int, Process *);
-extern void erts_debug_unuse_tmp_heap(int, Process *);
-#endif
-
#endif /* _ERL_DEBUG_H_ */
diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h
index 7ab58e336b..97a69140c3 100644
--- a/erts/emulator/beam/erl_driver.h
+++ b/erts/emulator/beam/erl_driver.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2014. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,11 +37,6 @@
# endif
#endif
-/* This is OK to override by the NIF/driver implementor */
-#if defined(HALFWORD_HEAP_EMULATOR_SAVED__) && !defined(HALFWORD_HEAP_EMULATOR)
-#define HALFWORD_HEAP_EMULATOR HALFWORD_HEAP_EMULATOR_SAVED__
-#endif
-
#include "erl_drv_nif.h"
#include <stdlib.h>
@@ -131,6 +126,7 @@ typedef struct {
#define ERL_DRV_FLAG_USE_PORT_LOCKING (1 << 0)
#define ERL_DRV_FLAG_SOFT_BUSY (1 << 1)
#define ERL_DRV_FLAG_NO_BUSY_MSGQ (1 << 2)
+#define ERL_DRV_FLAG_USE_INIT_ACK (1 << 3)
/*
* Integer types
@@ -340,22 +336,23 @@ typedef struct erl_drv_entry {
#ifdef STATIC_ERLANG_DRIVER
# define ERLANG_DRIVER_NAME(NAME) NAME ## _driver_init
+# define ERL_DRIVER_EXPORT
#else
# define ERLANG_DRIVER_NAME(NAME) driver_init
+# if defined(__GNUC__) && __GNUC__ >= 4
+# define ERL_DRIVER_EXPORT __attribute__ ((visibility("default")))
+# elif defined (__SUNPRO_C) && (__SUNPRO_C >= 0x550)
+# define ERL_DRIVER_EXPORT __global
+# else
+# define ERL_DRIVER_EXPORT
+# endif
#endif
-/* For windows dynamic drivers */
#ifndef ERL_DRIVER_TYPES_ONLY
-#if defined(__WIN32__)
-# define DRIVER_INIT(DRIVER_NAME) \
- __declspec(dllexport) ErlDrvEntry* ERLANG_DRIVER_NAME(DRIVER_NAME)(void); \
- __declspec(dllexport) ErlDrvEntry* ERLANG_DRIVER_NAME(DRIVER_NAME)(void)
-#else
-# define DRIVER_INIT(DRIVER_NAME) \
- ErlDrvEntry* ERLANG_DRIVER_NAME(DRIVER_NAME)(void); \
- ErlDrvEntry* ERLANG_DRIVER_NAME(DRIVER_NAME)(void)
-#endif
+#define DRIVER_INIT(DRIVER_NAME) \
+ ERL_DRIVER_EXPORT ErlDrvEntry* ERLANG_DRIVER_NAME(DRIVER_NAME)(void); \
+ ERL_DRIVER_EXPORT ErlDrvEntry* ERLANG_DRIVER_NAME(DRIVER_NAME)(void)
#define ERL_DRV_BUSY_MSGQ_DISABLED (~((ErlDrvSizeT) 0))
#define ERL_DRV_BUSY_MSGQ_READ_ONLY ((ErlDrvSizeT) 0)
@@ -661,15 +658,11 @@ EXTERN char *driver_dl_error(void);
EXTERN int erl_drv_putenv(const char *key, char *value);
EXTERN int erl_drv_getenv(const char *key, char *value, size_t *value_size);
-#ifdef __OSE__
-typedef ErlDrvUInt ErlDrvOseEventId;
-EXTERN union SIGNAL *erl_drv_ose_get_signal(ErlDrvEvent ev);
-EXTERN ErlDrvEvent erl_drv_ose_event_alloc(SIGSELECT sig, ErlDrvOseEventId handle,
- ErlDrvOseEventId (*resolve_signal)(union SIGNAL *sig), void *extra);
-EXTERN void erl_drv_ose_event_free(ErlDrvEvent ev);
-EXTERN void erl_drv_ose_event_fetch(ErlDrvEvent ev, SIGSELECT *sig,
- ErlDrvOseEventId *handle, void **extra);
-#endif
+/* spawn start init ack */
+EXTERN void erl_drv_init_ack(ErlDrvPort ix, ErlDrvData res);
+
+/* set the pid seen in port_info */
+EXTERN void erl_drv_set_os_pid(ErlDrvPort ix, ErlDrvSInt pid);
#endif /* !ERL_DRIVER_TYPES_ONLY */
diff --git a/erts/emulator/beam/erl_drv_nif.h b/erts/emulator/beam/erl_drv_nif.h
index f6b946ae82..6ec5fbb895 100644
--- a/erts/emulator/beam/erl_drv_nif.h
+++ b/erts/emulator/beam/erl_drv_nif.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010. All Rights Reserved.
+ * Copyright Ericsson AB 2010-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -43,12 +43,11 @@ typedef struct {
int suggested_stack_size;
} ErlDrvThreadOpts;
-#if defined(ERL_DRV_DIRTY_SCHEDULER_SUPPORT) || defined(ERL_NIF_DIRTY_SCHEDULER_SUPPORT)
+
typedef enum {
- ERL_DRV_DIRTY_JOB_CPU_BOUND = 1,
- ERL_DRV_DIRTY_JOB_IO_BOUND = 2
-} ErlDrvDirtyJobFlags;
-#endif
+ ERL_DIRTY_JOB_CPU_BOUND = 1,
+ ERL_DIRTY_JOB_IO_BOUND = 2
+} ErlDirtyJobFlags;
#ifdef SIZEOF_CHAR
# define SIZEOF_CHAR_SAVED__ SIZEOF_CHAR
diff --git a/erts/emulator/beam/erl_drv_thread.c b/erts/emulator/beam/erl_drv_thread.c
index 184f8e8931..92edce5176 100644
--- a/erts/emulator/beam/erl_drv_thread.c
+++ b/erts/emulator/beam/erl_drv_thread.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2007-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2007-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erl_fun.c b/erts/emulator/beam/erl_fun.c
index 2f13aa364f..53a1801b72 100644
--- a/erts/emulator/beam/erl_fun.c
+++ b/erts/emulator/beam/erl_fun.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2010. 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.
@@ -66,12 +66,15 @@ erts_init_fun_table(void)
f.cmp = (HCMP_FUN) fun_cmp;
f.alloc = (HALLOC_FUN) fun_alloc;
f.free = (HFREE_FUN) fun_free;
+ f.meta_alloc = (HMALLOC_FUN) erts_alloc;
+ f.meta_free = (HMFREE_FUN) erts_free;
+ f.meta_print = (HMPRINT_FUN) erts_print;
hash_init(ERTS_ALC_T_FUN_TABLE, &erts_fun_table, "fun_table", 16, f);
}
void
-erts_fun_info(int to, void *to_arg)
+erts_fun_info(fmtfn_t to, void *to_arg)
{
int lock = !ERTS_IS_CRASH_DUMPING;
if (lock)
@@ -107,9 +110,9 @@ erts_put_fun_entry(Eterm mod, int uniq, int index)
fe = (ErlFunEntry *) hash_put(&erts_fun_table, (void*) &template);
sys_memset(fe->uniq, 0, sizeof(fe->uniq));
fe->index = 0;
- refc = erts_refc_inctest(&fe->refc, 0);
+ refc = erts_smp_refc_inctest(&fe->refc, 0);
if (refc < 2) /* New or pending delete */
- erts_refc_inc(&fe->refc, 1);
+ erts_smp_refc_inc(&fe->refc, 1);
erts_fun_write_unlock();
return fe;
}
@@ -131,9 +134,9 @@ erts_put_fun_entry2(Eterm mod, int old_uniq, int old_index,
sys_memcpy(fe->uniq, uniq, sizeof(fe->uniq));
fe->index = index;
fe->arity = arity;
- refc = erts_refc_inctest(&fe->refc, 0);
+ refc = erts_smp_refc_inctest(&fe->refc, 0);
if (refc < 2) /* New or pending delete */
- erts_refc_inc(&fe->refc, 1);
+ erts_smp_refc_inc(&fe->refc, 1);
erts_fun_write_unlock();
return fe;
}
@@ -158,9 +161,9 @@ erts_get_fun_entry(Eterm mod, int uniq, int index)
erts_fun_read_lock();
ret = (ErlFunEntry *) hash_get(&erts_fun_table, (void*) &template);
if (ret) {
- erts_aint_t refc = erts_refc_inctest(&ret->refc, 1);
+ erts_aint_t refc = erts_smp_refc_inctest(&ret->refc, 1);
if (refc < 2) /* Pending delete */
- erts_refc_inc(&ret->refc, 1);
+ erts_smp_refc_inc(&ret->refc, 1);
}
erts_fun_read_unlock();
return ret;
@@ -181,7 +184,7 @@ erts_erase_fun_entry(ErlFunEntry* fe)
* We have to check refc again since someone might have looked up
* the fun entry and incremented refc after last check.
*/
- if (erts_refc_dectest(&fe->refc, -1) <= 0)
+ if (erts_smp_refc_dectest(&fe->refc, -1) <= 0)
#endif
{
if (fe->address != unloaded_fun)
@@ -196,14 +199,13 @@ erts_erase_fun_entry(ErlFunEntry* fe)
}
void
-erts_cleanup_funs_on_purge(BeamInstr* start, BeamInstr* end)
+erts_fun_purge_prepare(BeamInstr* start, BeamInstr* end)
{
int limit;
HashBucket** bucket;
- ErlFunEntry* to_delete = NULL;
int i;
- erts_fun_write_lock();
+ erts_fun_read_lock();
limit = erts_fun_table.size;
bucket = erts_fun_table.bucket;
for (i = 0; i < limit; i++) {
@@ -214,26 +216,54 @@ erts_cleanup_funs_on_purge(BeamInstr* start, BeamInstr* end)
BeamInstr* addr = fe->address;
if (start <= addr && addr < end) {
+ fe->pend_purge_address = addr;
+ ERTS_SMP_WRITE_MEMORY_BARRIER;
fe->address = unloaded_fun;
- if (erts_refc_dectest(&fe->refc, 0) == 0) {
- fe->address = (void *) to_delete;
- to_delete = fe;
- }
+ erts_purge_state_add_fun(fe);
}
b = b->next;
}
}
+ erts_fun_read_unlock();
+}
+
+void
+erts_fun_purge_abort_prepare(ErlFunEntry **funs, Uint no)
+{
+ Uint ix;
- while (to_delete != NULL) {
- ErlFunEntry* next = (ErlFunEntry *) to_delete->address;
- erts_erase_fun_entry_unlocked(to_delete);
- to_delete = next;
+ for (ix = 0; ix < no; ix++) {
+ ErlFunEntry *fe = funs[ix];
+ if (fe->address == unloaded_fun)
+ fe->address = fe->pend_purge_address;
}
- erts_fun_write_unlock();
}
void
-erts_dump_fun_entries(int to, void *to_arg)
+erts_fun_purge_abort_finalize(ErlFunEntry **funs, Uint no)
+{
+ Uint ix;
+
+ for (ix = 0; ix < no; ix++)
+ funs[ix]->pend_purge_address = NULL;
+}
+
+void
+erts_fun_purge_complete(ErlFunEntry **funs, Uint no)
+{
+ Uint ix;
+
+ for (ix = 0; ix < no; ix++) {
+ ErlFunEntry *fe = funs[ix];
+ fe->pend_purge_address = NULL;
+ if (erts_smp_refc_dectest(&fe->refc, 0) == 0)
+ erts_erase_fun_entry(fe);
+ }
+ ERTS_SMP_WRITE_MEMORY_BARRIER;
+}
+
+void
+erts_dump_fun_entries(fmtfn_t to, void *to_arg)
{
int limit;
HashBucket** bucket;
@@ -258,7 +288,7 @@ erts_dump_fun_entries(int to, void *to_arg)
#ifdef HIPE
erts_print(to, to_arg, "Native_address: %p\n", fe->native_address);
#endif
- erts_print(to, to_arg, "Refc: %ld\n", erts_refc_read(&fe->refc, 1));
+ erts_print(to, to_arg, "Refc: %ld\n", erts_smp_refc_read(&fe->refc, 1));
b = b->next;
}
}
@@ -289,8 +319,9 @@ fun_alloc(ErlFunEntry* template)
obj->old_uniq = template->old_uniq;
obj->old_index = template->old_index;
obj->module = template->module;
- erts_refc_init(&obj->refc, -1);
+ erts_smp_refc_init(&obj->refc, -1);
obj->address = unloaded_fun;
+ obj->pend_purge_address = NULL;
#ifdef HIPE
obj->native_address = NULL;
#endif
diff --git a/erts/emulator/beam/erl_fun.h b/erts/emulator/beam/erl_fun.h
index 0024b1ff71..b1d5cddb34 100644
--- a/erts/emulator/beam/erl_fun.h
+++ b/erts/emulator/beam/erl_fun.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2012. 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.
@@ -42,8 +42,9 @@ typedef struct erl_fun_entry {
Uint arity; /* The arity of the fun. */
Eterm module; /* Tagged atom for module. */
- erts_refc_t refc; /* Reference count: One for code + one for each
+ erts_smp_refc_t refc; /* Reference count: One for code + one for each
fun object in each process. */
+ BeamInstr *pend_purge_address; /* address stored during a pending purge */
} ErlFunEntry;
/*
@@ -70,7 +71,7 @@ typedef struct erl_fun_thing {
#define ERL_FUN_SIZE ((sizeof(ErlFunThing)/sizeof(Eterm))-1)
void erts_init_fun_table(void);
-void erts_fun_info(int, void *);
+void erts_fun_info(fmtfn_t, void *);
int erts_fun_table_sz(void);
ErlFunEntry* erts_put_fun_entry(Eterm mod, int uniq, int index);
@@ -81,7 +82,10 @@ ErlFunEntry* erts_put_fun_entry2(Eterm mod, int old_uniq, int old_index,
void erts_erase_fun_entry(ErlFunEntry* fe);
void erts_cleanup_funs(ErlFunThing* funp);
-void erts_cleanup_funs_on_purge(BeamInstr* start, BeamInstr* end);
-void erts_dump_fun_entries(int, void *);
+void erts_fun_purge_prepare(BeamInstr* start, BeamInstr* end);
+void erts_fun_purge_abort_prepare(ErlFunEntry **funs, Uint no);
+void erts_fun_purge_abort_finalize(ErlFunEntry **funs, Uint no);
+void erts_fun_purge_complete(ErlFunEntry **funs, Uint no);
+void erts_dump_fun_entries(fmtfn_t, void *);
#endif
diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c
index f48c46ca33..d2560cfb49 100644
--- a/erts/emulator/beam/erl_gc.c
+++ b/erts/emulator/beam/erl_gc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2014. 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.
@@ -41,12 +41,28 @@
#endif
#include "dtrace-wrapper.h"
#include "erl_bif_unique.h"
+#include "dist.h"
#define ERTS_INACT_WR_PB_LEAVE_MUCH_LIMIT 1
#define ERTS_INACT_WR_PB_LEAVE_MUCH_PERCENTAGE 20
#define ERTS_INACT_WR_PB_LEAVE_LIMIT 10
#define ERTS_INACT_WR_PB_LEAVE_PERCENTAGE 10
+#if defined(DEBUG) || 0
+#define ERTS_GC_DEBUG
+#else
+#undef ERTS_GC_DEBUG
+#endif
+#ifdef ERTS_GC_DEBUG
+# define ERTS_GC_ASSERT ASSERT
+#else
+# define ERTS_GC_ASSERT(B) ((void) 1)
+#endif
+
+#if defined(DEBUG) && 0
+# define HARDDEBUG 1
+#endif
+
/*
* Returns number of elements in an array.
*/
@@ -67,10 +83,10 @@
#define ErtsGcQuickSanityCheck(P) \
do { \
ASSERT((P)->heap < (P)->hend); \
- ASSERT((P)->heap_sz == (P)->hend - (P)->heap); \
+ ASSERT((p)->abandoned_heap || (P)->heap_sz == (P)->hend - (P)->heap); \
ASSERT((P)->heap <= (P)->htop && (P)->htop <= (P)->hend); \
ASSERT((P)->heap <= (P)->stop && (P)->stop <= (P)->hend); \
- ASSERT((P)->heap <= (P)->high_water && (P)->high_water <= (P)->hend);\
+ ASSERT((p)->abandoned_heap || ((P)->heap <= (P)->high_water && (P)->high_water <= (P)->hend)); \
OverRunCheck((P)); \
} while (0)
#else
@@ -98,18 +114,32 @@ typedef struct {
static Uint setup_rootset(Process*, Eterm*, int, Rootset*);
static void cleanup_rootset(Rootset *rootset);
-static Uint combined_message_size(Process* p);
-static void remove_message_buffers(Process* p);
-static int major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl);
-static int minor_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl);
-static void do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj);
-static Eterm* sweep_rootset(Rootset *rootset, Eterm* htop, char* src, Uint src_size);
-static Eterm* sweep_one_area(Eterm* n_hp, Eterm* n_htop, char* src, Uint src_size);
-static Eterm* sweep_one_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm* htop,
- char* src, Uint src_size);
-static Eterm* collect_heap_frags(Process* p, Eterm* heap,
- Eterm* htop, Eterm* objv, int nobj);
-static void adjust_after_fullsweep(Process *p, int need, Eterm *objv, int nobj);
+static Eterm *full_sweep_heaps(Process *p,
+ int hibernate,
+ Eterm *n_heap, Eterm* n_htop,
+ char *oh, Uint oh_size,
+ Eterm *objv, int nobj);
+static int garbage_collect(Process* p, ErlHeapFragment *live_hf_end,
+ int need, Eterm* objv, int nobj, int fcalls);
+static int major_collection(Process* p, ErlHeapFragment *live_hf_end,
+ int need, Eterm* objv, int nobj, Uint *recl);
+static int minor_collection(Process* p, ErlHeapFragment *live_hf_end,
+ int need, Eterm* objv, int nobj, Uint *recl);
+static void do_minor(Process *p, ErlHeapFragment *live_hf_end,
+ char *mature, Uint mature_size,
+ Uint new_sz, Eterm* objv, int nobj);
+static Eterm *sweep_new_heap(Eterm *n_hp, Eterm *n_htop,
+ char* old_heap, Uint old_heap_size);
+static Eterm *sweep_heaps(Eterm *n_hp, Eterm *n_htop,
+ char* old_heap, Uint old_heap_size);
+static Eterm* sweep_literal_area(Eterm* n_hp, Eterm* n_htop,
+ char* old_heap, Uint old_heap_size,
+ char* src, Uint src_size);
+static Eterm* sweep_literals_to_old_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm* htop,
+ char* src, Uint src_size);
+static Eterm* collect_live_heap_frags(Process* p, ErlHeapFragment *live_hf_end,
+ Eterm* heap, Eterm* htop, Eterm* objv, int nobj);
+static int adjust_after_fullsweep(Process *p, int need, Eterm *objv, int nobj);
static void shrink_new_heap(Process *p, Uint new_sz, Eterm *objv, int nobj);
static void grow_new_heap(Process *p, Uint new_sz, Eterm* objv, int nobj);
static void sweep_off_heap(Process *p, int fullsweep);
@@ -119,16 +149,18 @@ static void offset_rootset(Process *p, Sint offs, char* area, Uint area_size,
Eterm* objv, int nobj);
static void offset_off_heap(Process* p, Sint offs, char* area, Uint area_size);
static void offset_mqueue(Process *p, Sint offs, char* area, Uint area_size);
-
+static void move_msgq_to_heap(Process *p);
+static int reached_max_heap_size(Process *p, Uint total_heap_size,
+ Uint extra_heap_size, Uint extra_old_heap_size);
static void init_gc_info(ErtsGCInfo *gcip);
+static Uint64 next_vheap_size(Process* p, Uint64 vheap, Uint64 vheap_sz);
#ifdef HARDDEBUG
-static void disallow_heap_frag_ref_in_heap(Process* p);
+static void disallow_heap_frag_ref_in_heap(Process *p, Eterm *heap, Eterm *htop);
static void disallow_heap_frag_ref_in_old_heap(Process* p);
-static void disallow_heap_frag_ref(Process* p, Eterm* n_htop, Eterm* objv, int nobj);
#endif
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
# define MAX_HEAP_SIZES 154
#else
# define MAX_HEAP_SIZES 59
@@ -147,26 +179,31 @@ typedef struct {
erts_smp_atomic32_t refc;
} ErtsGCInfoReq;
-#if !HALFWORD_HEAP
-ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(gcireq,
- ErtsGCInfoReq,
- 5,
- ERTS_ALC_T_GC_INFO_REQ)
-#else
-static ERTS_INLINE ErtsGCInfoReq *
-gcireq_alloc(void)
-{
- return erts_alloc(ERTS_ALC_T_GC_INFO_REQ,
- sizeof(ErtsGCInfoReq));
-}
+#ifdef ERTS_DIRTY_SCHEDULERS
+static struct {
+ erts_mtx_t mtx;
+ ErtsGCInfo info;
+} dirty_gc;
+#endif
-static ERTS_INLINE void
-gcireq_free(ErtsGCInfoReq *ptr)
+static ERTS_INLINE int
+gc_cost(Uint gc_moved_live_words, Uint resize_moved_words)
{
- erts_free(ERTS_ALC_T_GC_INFO_REQ, ptr);
+ Sint reds;
+
+ reds = gc_moved_live_words/10;
+ reds += resize_moved_words/100;
+ if (reds < 1)
+ return 1;
+ if (reds > INT_MAX)
+ return INT_MAX;
+ return (int) reds;
}
-#endif
+ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(gcireq,
+ ErtsGCInfoReq,
+ 5,
+ ERTS_ALC_T_GC_INFO_REQ)
/*
* Initialize GC global data.
*/
@@ -208,7 +245,7 @@ erts_init_gc(void)
}
- /* for 32 bit we want max_heap_size to be MAX(32bit) / 4 [words] (and halfword)
+ /* for 32 bit we want max_heap_size to be MAX(32bit) / 4 [words]
* for 64 bit we want max_heap_size to be MAX(52bit) / 8 [words]
*/
@@ -232,10 +269,12 @@ erts_init_gc(void)
init_gc_info(&esdp->gc_info);
}
-#if !HALFWORD_HEAP
- init_gcireq_alloc();
+#ifdef ERTS_DIRTY_SCHEDULERS
+ erts_smp_mtx_init(&dirty_gc.mtx, "dirty_gc_info");
+ init_gc_info(&dirty_gc.info);
#endif
+ init_gcireq_alloc();
}
/*
@@ -351,32 +390,54 @@ erts_offset_off_heap(ErlOffHeap *ohp, Sint offs, Eterm* low, Eterm* high)
#undef ptr_within
Eterm
-erts_gc_after_bif_call(Process* p, Eterm result, Eterm* regs, Uint arity)
+erts_gc_after_bif_call_lhf(Process* p, ErlHeapFragment *live_hf_end,
+ Eterm result, Eterm* regs, Uint arity)
{
int cost;
+ if (p->flags & F_HIBERNATE_SCHED) {
+ /*
+ * We just hibernated. We do *not* want to mess
+ * up the hibernation by an ordinary GC...
+ */
+ return result;
+ }
+
+ if (!p->mbuf) {
+ /* Must have GC:d in BIF call... invalidate live_hf_end */
+ live_hf_end = ERTS_INVALID_HFRAG_PTR;
+ }
+
if (is_non_value(result)) {
if (p->freason == TRAP) {
#if HIPE
if (regs == NULL) {
- regs = ERTS_PROC_GET_SCHDATA(p)->x_reg_array;
+ regs = erts_proc_sched_data(p)->x_reg_array;
}
#endif
- cost = erts_garbage_collect(p, 0, regs, p->arity);
+ cost = garbage_collect(p, live_hf_end, 0, regs, p->arity, p->fcalls);
} else {
- cost = erts_garbage_collect(p, 0, regs, arity);
+ cost = garbage_collect(p, live_hf_end, 0, regs, arity, p->fcalls);
}
} else {
Eterm val[1];
val[0] = result;
- cost = erts_garbage_collect(p, 0, val, 1);
+ cost = garbage_collect(p, live_hf_end, 0, val, 1, p->fcalls);
result = val[0];
}
BUMP_REDS(p, cost);
+
return result;
}
+Eterm
+erts_gc_after_bif_call(Process* p, Eterm result, Eterm* regs, Uint arity)
+{
+ return erts_gc_after_bif_call_lhf(p, ERTS_INVALID_HFRAG_PTR,
+ result, regs, arity);
+}
+
static ERTS_INLINE void reset_active_writer(Process *p)
{
struct erl_off_heap_header* ptr;
@@ -390,6 +451,156 @@ static ERTS_INLINE void reset_active_writer(Process *p)
}
}
+#define ERTS_DELAY_GC_EXTRA_FREE 40
+#define ERTS_ABANDON_HEAP_COST 10
+
+static int
+delay_garbage_collection(Process *p, ErlHeapFragment *live_hf_end, int need, int fcalls)
+{
+ ErlHeapFragment *hfrag;
+ Eterm *orig_heap, *orig_hend, *orig_htop, *orig_stop;
+ Eterm *stop, *hend;
+ Uint hsz, ssz;
+ int reds_left;
+
+ ERTS_HOLE_CHECK(p);
+
+ if ((p->flags & F_DISABLE_GC)
+ && p->live_hf_end == ERTS_INVALID_HFRAG_PTR) {
+ /*
+ * A BIF yielded with disabled GC. Remember
+ * heap fragments created by the BIF until we
+ * do next GC.
+ */
+ p->live_hf_end = live_hf_end;
+ }
+
+ if (need == 0)
+ return 1;
+
+ /*
+ * Satisfy need in a heap fragment...
+ */
+ ASSERT(need > 0);
+
+ orig_heap = p->heap;
+ orig_hend = p->hend;
+ orig_htop = p->htop;
+ orig_stop = p->stop;
+
+ ssz = orig_hend - orig_stop;
+ hsz = ssz + need + ERTS_DELAY_GC_EXTRA_FREE;
+
+ hfrag = new_message_buffer(hsz);
+ p->heap = p->htop = &hfrag->mem[0];
+ p->hend = hend = &hfrag->mem[hsz];
+ p->stop = stop = hend - ssz;
+ sys_memcpy((void *) stop, (void *) orig_stop, ssz * sizeof(Eterm));
+
+ if (p->abandoned_heap) {
+ /*
+ * Active heap already in a fragment; adjust it and
+ * save it into mbuf list...
+ */
+ ErlHeapFragment *hfrag = ((ErlHeapFragment *)
+ (((char *) orig_heap)
+ - offsetof(ErlHeapFragment, mem)));
+ Uint used = orig_htop - orig_heap;
+ hfrag->used_size = used;
+ p->mbuf_sz += used;
+ ASSERT(hfrag->used_size <= hfrag->alloc_size);
+ ASSERT(!hfrag->off_heap.first && !hfrag->off_heap.overhead);
+ hfrag->next = p->mbuf;
+ p->mbuf = hfrag;
+ }
+ else {
+ /* Do not leave a hole in the abandoned heap... */
+ if (orig_htop < orig_hend) {
+ *orig_htop = make_pos_bignum_header(orig_hend-orig_htop-1);
+ if (orig_htop + 1 < orig_hend) {
+ orig_hend[-1] = (Uint) (orig_htop - orig_heap);
+ p->flags |= F_ABANDONED_HEAP_USE;
+ }
+ }
+ p->abandoned_heap = orig_heap;
+ }
+
+#ifdef CHECK_FOR_HOLES
+ p->last_htop = p->htop;
+ p->heap_hfrag = hfrag;
+#endif
+
+ /* Make sure that we do a proper GC as soon as possible... */
+ p->flags |= F_FORCE_GC;
+ reds_left = ERTS_REDS_LEFT(p, fcalls);
+ ASSERT(CONTEXT_REDS - reds_left >= erts_proc_sched_data(p)->virtual_reds);
+
+ if (reds_left > ERTS_ABANDON_HEAP_COST) {
+ int vreds = reds_left - ERTS_ABANDON_HEAP_COST;
+ erts_proc_sched_data((p))->virtual_reds += vreds;
+ }
+
+ ERTS_CHK_MBUF_SZ(p);
+
+ ASSERT(CONTEXT_REDS >= erts_proc_sched_data(p)->virtual_reds);
+ return reds_left;
+}
+
+static ERTS_FORCE_INLINE Uint
+young_gen_usage(Process *p)
+{
+ Uint hsz;
+ Eterm *aheap;
+
+ ERTS_CHK_MBUF_SZ(p);
+
+ hsz = p->mbuf_sz;
+
+ if (p->flags & F_ON_HEAP_MSGQ) {
+ ErtsMessage *mp;
+ for (mp = p->msg.first; mp; mp = mp->next) {
+ /*
+ * We leave not yet decoded distribution messages
+ * as they are in the queue since it is not
+ * possible to determine a maximum size until
+ * actual decoding. However, we use their estimated
+ * size when calculating need, and by this making
+ * it more likely that they will fit on the heap
+ * when actually decoded.
+ */
+ if (mp->data.attached)
+ hsz += erts_msg_attached_data_size(mp);
+ }
+ }
+
+ hsz += p->htop - p->heap;
+ aheap = p->abandoned_heap;
+ if (aheap) {
+ /* used in orig heap */
+ if (p->flags & F_ABANDONED_HEAP_USE)
+ hsz += aheap[p->heap_sz-1];
+ else
+ hsz += p->heap_sz;
+ }
+ return hsz;
+}
+
+#define ERTS_GET_ORIG_HEAP(Proc, Heap, HTop) \
+ do { \
+ Eterm *aheap__ = (Proc)->abandoned_heap; \
+ if (!aheap__) { \
+ (Heap) = (Proc)->heap; \
+ (HTop) = (Proc)->htop; \
+ } \
+ else { \
+ (Heap) = aheap__; \
+ if ((Proc)->flags & F_ABANDONED_HEAP_USE) \
+ (HTop) = aheap__ + aheap__[(Proc)->heap_sz-1]; \
+ else \
+ (HTop) = aheap__ + (Proc)->heap_sz; \
+ } \
+ } while (0)
+
/*
* Garbage collect a process.
*
@@ -398,38 +609,49 @@ static ERTS_INLINE void reset_active_writer(Process *p)
* objv: Array of terms to add to rootset; that is to preserve.
* nobj: Number of objects in objv.
*/
-int
-erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj)
+static int
+garbage_collect(Process* p, ErlHeapFragment *live_hf_end,
+ int need, Eterm* objv, int nobj, int fcalls)
{
Uint reclaimed_now = 0;
- int done = 0;
- ErtsMonotonicTime start_time = 0; /* Shut up faulty warning... */
+ Eterm gc_trace_end_tag;
+ int reds;
+ ErtsMonotonicTime start_time;
ErtsSchedulerData *esdp;
+ erts_aint32_t state;
+ ERTS_MSACC_PUSH_STATE_M();
#ifdef USE_VM_PROBES
DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE);
#endif
- if (p->flags & F_DISABLE_GC) {
- ASSERT(need == 0);
- return 1;
- }
+ ERTS_UNDEF(start_time, 0);
+ ERTS_CHK_MBUF_SZ(p);
- esdp = erts_get_scheduler_data();
+ ASSERT(CONTEXT_REDS - ERTS_REDS_LEFT(p, fcalls)
+ >= erts_proc_sched_data(p)->virtual_reds);
- if (IS_TRACED_FL(p, F_TRACE_GC)) {
- trace_gc(p, am_gc_start);
- }
+ state = erts_smp_atomic32_read_nob(&p->state);
+
+ if (p->flags & (F_DISABLE_GC|F_DELAY_GC) || state & ERTS_PSFLG_EXITING)
+ return delay_garbage_collection(p, live_hf_end, need, fcalls);
+
+ if (p->abandoned_heap)
+ live_hf_end = ERTS_INVALID_HFRAG_PTR;
+ else if (p->live_hf_end != ERTS_INVALID_HFRAG_PTR)
+ live_hf_end = p->live_hf_end;
+
+ ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_GC);
+
+ esdp = erts_get_scheduler_data();
- (void) erts_smp_atomic32_read_bor_nob(&p->state, ERTS_PSFLG_GC);
+ erts_smp_atomic32_read_bor_nob(&p->state, ERTS_PSFLG_GC);
if (erts_system_monitor_long_gc != 0)
start_time = erts_get_monotonic_time(esdp);
ERTS_CHK_OFFHEAP(p);
ErtsGcQuickSanityCheck(p);
- if (GEN_GCS(p) >= MAX_GEN_GCS(p)) {
- FLAGS(p) |= F_NEED_FULLSWEEP;
- }
+
#ifdef USE_VM_PROBES
*pidbuf = '\0';
if (DTRACE_ENABLED(gc_major_start)
@@ -442,17 +664,34 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj)
/*
* Test which type of GC to do.
*/
- while (!done) {
- if ((FLAGS(p) & F_NEED_FULLSWEEP) != 0) {
- DTRACE2(gc_major_start, pidbuf, need);
- done = major_collection(p, need, objv, nobj, &reclaimed_now);
- DTRACE2(gc_major_end, pidbuf, reclaimed_now);
- } else {
- DTRACE2(gc_minor_start, pidbuf, need);
- done = minor_collection(p, need, objv, nobj, &reclaimed_now);
- DTRACE2(gc_minor_end, pidbuf, reclaimed_now);
- }
+
+ if (GEN_GCS(p) < MAX_GEN_GCS(p) && !(FLAGS(p) & F_NEED_FULLSWEEP)) {
+ if (IS_TRACED_FL(p, F_TRACE_GC)) {
+ trace_gc(p, am_gc_minor_start, need, THE_NON_VALUE);
+ }
+ DTRACE2(gc_minor_start, pidbuf, need);
+ reds = minor_collection(p, live_hf_end, need, objv, nobj, &reclaimed_now);
+ DTRACE2(gc_minor_end, pidbuf, reclaimed_now);
+ if (reds == -1) {
+ if (IS_TRACED_FL(p, F_TRACE_GC)) {
+ trace_gc(p, am_gc_minor_end, reclaimed_now, THE_NON_VALUE);
+ }
+ goto do_major_collection;
+ }
+ gc_trace_end_tag = am_gc_minor_end;
+ } else {
+do_major_collection:
+ ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_GC_FULL);
+ if (IS_TRACED_FL(p, F_TRACE_GC)) {
+ trace_gc(p, am_gc_major_start, need, THE_NON_VALUE);
+ }
+ DTRACE2(gc_major_start, pidbuf, need);
+ reds = major_collection(p, live_hf_end, need, objv, nobj, &reclaimed_now);
+ DTRACE2(gc_major_end, pidbuf, reclaimed_now);
+ gc_trace_end_tag = am_gc_major_end;
+ ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_GC);
}
+
reset_active_writer(p);
/*
@@ -463,10 +702,34 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj)
ErtsGcQuickSanityCheck(p);
+ /* Max heap size has been reached and the process was configured
+ to be killed, so we kill it and set it in a delayed garbage
+ collecting state. There should be no gc_end trace or
+ long_gc/large_gc triggers when this happens as process was
+ killed before a GC could be done. */
+ if (reds == -2) {
+ ErtsProcLocks locks = ERTS_PROC_LOCKS_ALL;
+ int res;
+
+ erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR);
+ erts_send_exit_signal(p, p->common.id, p, &locks,
+ am_kill, NIL, NULL, 0);
+ erts_smp_proc_unlock(p, locks & ERTS_PROC_LOCKS_ALL_MINOR);
+
+ /* erts_send_exit_signal looks for ERTS_PSFLG_GC, so
+ we have to remove it after the signal is sent */
+ erts_smp_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_GC);
+
+ /* We have to make sure that we have space for need on the heap */
+ res = delay_garbage_collection(p, live_hf_end, need, fcalls);
+ ERTS_MSACC_POP_STATE_M();
+ return res;
+ }
+
erts_smp_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_GC);
if (IS_TRACED_FL(p, F_TRACE_GC)) {
- trace_gc(p, am_gc_end);
+ trace_gc(p, gc_trace_end_tag, reclaimed_now, THE_NON_VALUE);
}
if (erts_system_monitor_long_gc != 0) {
@@ -487,10 +750,24 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj)
monitor_large_heap(p);
}
- esdp->gc_info.garbage_cols++;
- esdp->gc_info.reclaimed += reclaimed_now;
+#ifdef ERTS_DIRTY_SCHEDULERS
+ if (ERTS_SCHEDULER_IS_DIRTY(esdp)) {
+ erts_mtx_lock(&dirty_gc.mtx);
+ dirty_gc.info.garbage_cols++;
+ dirty_gc.info.reclaimed += reclaimed_now;
+ erts_mtx_unlock(&dirty_gc.mtx);
+ }
+ else
+#endif
+ {
+ esdp->gc_info.garbage_cols++;
+ esdp->gc_info.reclaimed += reclaimed_now;
+ }
- FLAGS(p) &= ~F_FORCE_GC;
+ FLAGS(p) &= ~(F_FORCE_GC|F_HIBERNATED);
+ p->live_hf_end = ERTS_INVALID_HFRAG_PTR;
+
+ ERTS_MSACC_POP_STATE_M();
#ifdef CHECK_FOR_HOLES
/*
@@ -512,17 +789,34 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj)
p->last_old_htop = p->old_htop;
#endif
- /* FIXME: This function should really return an Sint, i.e., a possibly
- 64 bit wide signed integer, but that requires updating all the code
- that calls it. For now, we just return INT_MAX if the result is too
- large for an int. */
- {
- Sint result = (HEAP_TOP(p) - HEAP_START(p)) / 10;
- if (result >= INT_MAX) return INT_MAX;
- else return (int) result;
- }
+ ASSERT(!p->mbuf);
+ ASSERT(!ERTS_IS_GC_DESIRED(p));
+ ASSERT(need <= HEAP_LIMIT(p) - HEAP_TOP(p));
+
+ return reds;
+}
+
+int
+erts_garbage_collect_nobump(Process* p, int need, Eterm* objv, int nobj, int fcalls)
+{
+ int reds = garbage_collect(p, ERTS_INVALID_HFRAG_PTR, need, objv, nobj, fcalls);
+ int reds_left = ERTS_REDS_LEFT(p, fcalls);
+ if (reds > reds_left)
+ reds = reds_left;
+ ASSERT(CONTEXT_REDS - (reds_left - reds) >= erts_proc_sched_data(p)->virtual_reds);
+ return reds;
}
+void
+erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj)
+{
+ int reds = garbage_collect(p, ERTS_INVALID_HFRAG_PTR, need, objv, nobj, p->fcalls);
+ BUMP_REDS(p, reds);
+ ASSERT(CONTEXT_REDS - ERTS_BIF_REDS_LEFT(p)
+ >= erts_proc_sched_data(p)->virtual_reds);
+}
+
+
/*
* Place all living data on a the new heap; deallocate any old heap.
* Meant to be used by hibernate/3.
@@ -533,13 +827,11 @@ erts_garbage_collect_hibernate(Process* p)
Uint heap_size;
Eterm* heap;
Eterm* htop;
- Rootset rootset;
- char* src;
- Uint src_size;
Uint actual_size;
char* area;
Uint area_size;
Sint offs;
+ int reds;
if (p->flags & F_DISABLE_GC)
ERTS_INTERNAL_ERROR("GC disabled");
@@ -549,8 +841,6 @@ erts_garbage_collect_hibernate(Process* p)
*/
erts_smp_atomic32_read_bor_nob(&p->state, ERTS_PSFLG_GC);
ErtsGcQuickSanityCheck(p);
- ASSERT(p->mbuf_sz == 0);
- ASSERT(p->mbuf == 0);
ASSERT(p->stop == p->hend); /* Stack must be empty. */
/*
@@ -558,46 +848,26 @@ erts_garbage_collect_hibernate(Process* p)
*/
- heap_size = p->heap_sz + (p->old_htop - p->old_heap);
+ heap_size = p->heap_sz + (p->old_htop - p->old_heap) + p->mbuf_sz;
+
heap = (Eterm*) ERTS_HEAP_ALLOC(ERTS_ALC_T_TMP_HEAP,
sizeof(Eterm)*heap_size);
htop = heap;
- (void) setup_rootset(p, p->arg_reg, p->arity, &rootset);
-#if HIPE
- hipe_empty_nstack(p);
-#endif
-
- src = (char *) p->heap;
- src_size = (char *) p->htop - src;
- htop = sweep_rootset(&rootset, htop, src, src_size);
- htop = sweep_one_area(heap, htop, src, src_size);
+ htop = full_sweep_heaps(p,
+ 1,
+ heap,
+ htop,
+ (char *) p->old_heap,
+ (char *) p->old_htop - (char *) p->old_heap,
+ p->arg_reg,
+ p->arity);
- if (p->old_heap) {
- src = (char *) p->old_heap;
- src_size = (char *) p->old_htop - src;
- htop = sweep_rootset(&rootset, htop, src, src_size);
- htop = sweep_one_area(heap, htop, src, src_size);
- }
-
- cleanup_rootset(&rootset);
-
- if (MSO(p).first) {
- sweep_off_heap(p, 1);
- }
+#ifdef HARDDEBUG
+ disallow_heap_frag_ref_in_heap(p, heap, htop);
+#endif
- /*
- * Update all pointers.
- */
- ERTS_HEAP_FREE(ERTS_ALC_T_HEAP,
- (void*)HEAP_START(p),
- HEAP_SIZE(p) * sizeof(Eterm));
- if (p->old_heap) {
- ERTS_HEAP_FREE(ERTS_ALC_T_OLD_HEAP,
- (void*)p->old_heap,
- (p->old_hend - p->old_heap) * sizeof(Eterm));
- p->old_heap = p->old_htop = p->old_hend = 0;
- }
+ erts_deallocate_young_generation(p);
p->heap = heap;
p->high_water = htop;
@@ -612,6 +882,7 @@ erts_garbage_collect_hibernate(Process* p)
}
FLAGS(p) &= ~F_FORCE_GC;
+ p->live_hf_end = ERTS_INVALID_HFRAG_PTR;
/*
* Move the heap to its final destination.
@@ -658,16 +929,73 @@ erts_garbage_collect_hibernate(Process* p)
ErtsGcQuickSanityCheck(p);
+ p->flags |= F_HIBERNATED;
+
erts_smp_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_GC);
+
+ reds = gc_cost(actual_size, actual_size);
+ BUMP_REDS(p, reds);
}
+/*
+ * HiPE native code stack scanning procedures:
+ * - fullsweep_nstack()
+ * - gensweep_nstack()
+ * - offset_nstack()
+ * - sweep_literals_nstack()
+ */
+#if defined(HIPE)
+
+#define GENSWEEP_NSTACK(p,old_htop,n_htop) \
+ do { \
+ Eterm *tmp_old_htop = old_htop; \
+ Eterm *tmp_n_htop = n_htop; \
+ gensweep_nstack((p), &tmp_old_htop, &tmp_n_htop); \
+ old_htop = tmp_old_htop; \
+ n_htop = tmp_n_htop; \
+ } while(0)
+
+/*
+ * offset_nstack() can ignore the descriptor-based traversal the other
+ * nstack procedures use and simply call offset_heap_ptr() instead.
+ * This relies on two facts:
+ * 1. The only live non-Erlang terms on an nstack are return addresses,
+ * and they will be skipped thanks to the low/high range check.
+ * 2. Dead values, even if mistaken for pointers into the low/high area,
+ * can be offset safely since they won't be dereferenced.
+ *
+ * XXX: WARNING: If HiPE starts storing other non-Erlang values on the
+ * nstack, such as floats, then this will have to be changed.
+ */
+static ERTS_INLINE void offset_nstack(Process* p, Sint offs,
+ char* area, Uint area_size)
+{
+ if (p->hipe.nstack) {
+ ASSERT(p->hipe.nsp && p->hipe.nstend);
+ offset_heap_ptr(hipe_nstack_start(p), hipe_nstack_used(p),
+ offs, area, area_size);
+ }
+ else {
+ ASSERT(!p->hipe.nsp && !p->hipe.nstend);
+ }
+}
+
+#else /* !HIPE */
+
+#define fullsweep_nstack(p,n_htop) (n_htop)
+#define GENSWEEP_NSTACK(p,old_htop,n_htop) do{}while(0)
+#define offset_nstack(p,offs,area,area_size) do{}while(0)
+#define sweep_literals_nstack(p,old_htop,area,area_size) (old_htop)
+
+#endif /* HIPE */
+
void
erts_garbage_collect_literals(Process* p, Eterm* literals,
- Uint lit_size,
+ Uint byte_lit_size,
struct erl_off_heap_header* oh)
{
- Uint byte_lit_size = sizeof(Eterm)*lit_size;
+ Uint lit_size = byte_lit_size / sizeof(Eterm);
Uint old_heap_size;
Eterm* temp_lit;
Sint offs;
@@ -724,7 +1052,7 @@ erts_garbage_collect_literals(Process* p, Eterm* literals,
area_size = byte_lit_size;
n = setup_rootset(p, p->arg_reg, p->arity, &rootset);
roots = rootset.roots;
- old_htop = p->old_htop;
+ old_htop = sweep_literals_nstack(p, p->old_htop, area, area_size);
while (n--) {
Eterm* g_ptr = roots->v;
Uint g_sz = roots->sz;
@@ -743,7 +1071,7 @@ erts_garbage_collect_literals(Process* p, Eterm* literals,
if (IS_MOVED_BOXED(val)) {
ASSERT(is_boxed(val));
*g_ptr++ = val;
- } else if (in_area(ptr, area, area_size)) {
+ } else if (ErtsInArea(ptr, area, area_size)) {
MOVE_BOXED(ptr,val,old_htop,g_ptr++);
} else {
g_ptr++;
@@ -754,7 +1082,7 @@ erts_garbage_collect_literals(Process* p, Eterm* literals,
val = *ptr;
if (IS_MOVED_CONS(val)) { /* Moved */
*g_ptr++ = ptr[1];
- } else if (in_area(ptr, area, area_size)) {
+ } else if (ErtsInArea(ptr, area, area_size)) {
MOVE_CONS(ptr,val,old_htop,g_ptr++);
} else {
g_ptr++;
@@ -774,8 +1102,10 @@ erts_garbage_collect_literals(Process* p, Eterm* literals,
* Now we'll have to go through all heaps updating all other references.
*/
- old_htop = sweep_one_heap(p->heap, p->htop, old_htop, area, area_size);
- old_htop = sweep_one_area(p->old_heap, old_htop, area, area_size);
+ old_htop = sweep_literals_to_old_heap(p->heap, p->htop, old_htop, area, area_size);
+ old_htop = sweep_literal_area(p->old_heap, old_htop,
+ (char *) p->old_heap, sizeof(Eterm)*old_heap_size,
+ area, area_size);
ASSERT(p->old_htop <= old_htop && old_htop <= p->old_hend);
p->old_htop = old_htop;
@@ -830,19 +1160,51 @@ erts_garbage_collect_literals(Process* p, Eterm* literals,
/*
* Restore status.
*/
+ p->flags &= ~F_HIBERNATED;
erts_smp_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_GC);
}
static int
-minor_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl)
+minor_collection(Process* p, ErlHeapFragment *live_hf_end,
+ int need, Eterm* objv, int nobj, Uint *recl)
{
- Uint mature = HIGH_WATER(p) - HEAP_START(p);
+ Eterm *mature = p->abandoned_heap ? p->abandoned_heap : p->heap;
+ Uint mature_size = p->high_water - mature;
+ Uint size_before = young_gen_usage(p);
+
+ /*
+ * Check if we have gone past the max heap size limit
+ */
+
+ if (MAX_HEAP_SIZE_GET(p)) {
+ Uint heap_size = size_before,
+ /* Note that we also count the un-allocated area
+ in between the stack and heap */
+ stack_size = HEAP_END(p) - HEAP_TOP(p),
+ extra_heap_size,
+ extra_old_heap_size = 0;
+
+ /* Add potential old heap size */
+ if (OLD_HEAP(p) == NULL && mature_size != 0) {
+ extra_old_heap_size = erts_next_heap_size(size_before, 1);
+ heap_size += extra_old_heap_size;
+ } else if (OLD_HEAP(p))
+ heap_size += OLD_HEND(p) - OLD_HEAP(p);
+
+ /* Add potential new young heap size */
+ extra_heap_size = next_heap_size(p, stack_size + size_before, 0);
+ heap_size += extra_heap_size;
+
+ if (heap_size > MAX_HEAP_SIZE_GET(p))
+ if (reached_max_heap_size(p, heap_size, extra_heap_size, extra_old_heap_size))
+ return -2;
+ }
/*
* Allocate an old heap if we don't have one and if we'll need one.
*/
- if (OLD_HEAP(p) == NULL && mature != 0) {
+ if (OLD_HEAP(p) == NULL && mature_size != 0) {
Eterm* n_old;
/* Note: We choose a larger heap size than strictly needed,
@@ -850,7 +1212,7 @@ minor_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl)
* This improved Estone by more than 1200 estones on my computer
* (Ultra Sparc 10).
*/
- Uint new_sz = erts_next_heap_size(HEAP_TOP(p) - HEAP_START(p), 1);
+ Uint new_sz = erts_next_heap_size(size_before, 1);
/* Create new, empty old_heap */
n_old = (Eterm *) ERTS_HEAP_ALLOC(ERTS_ALC_T_OLD_HEAP,
@@ -866,41 +1228,32 @@ minor_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl)
*/
if (OLD_HEAP(p) &&
- ((mature <= OLD_HEND(p) - OLD_HTOP(p)) &&
- ((BIN_VHEAP_MATURE(p) < ( BIN_OLD_VHEAP_SZ(p) - BIN_OLD_VHEAP(p)))) &&
- ((BIN_OLD_VHEAP_SZ(p) > BIN_OLD_VHEAP(p))) ) ) {
- ErlMessage *msgp;
- Uint size_after;
- Uint need_after;
- const Uint stack_size = STACK_SZ_ON_HEAP(p);
- const Uint size_before = MBUF_SIZE(p) + (HEAP_TOP(p) - HEAP_START(p));
- Uint new_sz = HEAP_SIZE(p) + MBUF_SIZE(p) + combined_message_size(p);
+ ((mature_size <= OLD_HEND(p) - OLD_HTOP(p)) &&
+ ((BIN_OLD_VHEAP_SZ(p) > BIN_OLD_VHEAP(p))) ) ) {
+ Eterm *prev_old_htop;
+ Uint stack_size, size_after, adjust_size, need_after, new_sz, new_mature;
+
+ stack_size = p->hend - p->stop;
+ new_sz = stack_size + size_before;
new_sz = next_heap_size(p, new_sz, 0);
- do_minor(p, new_sz, objv, nobj);
+ prev_old_htop = p->old_htop;
+ do_minor(p, live_hf_end, (char *) mature, mature_size*sizeof(Eterm),
+ new_sz, objv, nobj);
+
+ if (p->flags & F_ON_HEAP_MSGQ)
+ move_msgq_to_heap(p);
- size_after = HEAP_TOP(p) - HEAP_START(p);
+ new_mature = p->old_htop - prev_old_htop;
+
+ size_after = new_mature;
+ size_after += HEAP_TOP(p) - HEAP_START(p) + p->mbuf_sz;
*recl += (size_before - size_after);
- /*
- * Copy newly received message onto the end of the new heap.
- */
- ErtsGcQuickSanityCheck(p);
- for (msgp = p->msg.first; msgp; msgp = msgp->next) {
- if (msgp->data.attached) {
- ErtsHeapFactory factory;
- erts_factory_proc_prealloc_init(&factory, p,
- erts_msg_attached_data_size(msgp));
- erts_move_msg_attached_data_to_heap(&factory, msgp);
- erts_factory_close(&factory);
- ErtsGcQuickSanityCheck(p);
- }
- }
ErtsGcQuickSanityCheck(p);
GEN_GCS(p)++;
need_after = ((HEAP_TOP(p) - HEAP_START(p))
- + erts_used_frag_sz(MBUF(p))
+ need
+ stack_size);
@@ -913,6 +1266,8 @@ minor_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl)
* the heap size is substantial, we don't want to shrink.
*/
+ adjust_size = 0;
+
if ((HEAP_SIZE(p) > 3000) && (4 * need_after < HEAP_SIZE(p)) &&
((HEAP_SIZE(p) > 8000) ||
(HEAP_SIZE(p) > (OLD_HEND(p) - OLD_HEAP(p))))) {
@@ -934,82 +1289,41 @@ minor_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl)
: next_heap_size(p, wanted, 0);
if (wanted < HEAP_SIZE(p)) {
shrink_new_heap(p, wanted, objv, nobj);
+ adjust_size = p->htop - p->heap;
}
- ASSERT(HEAP_SIZE(p) == next_heap_size(p, HEAP_SIZE(p), 0));
- ASSERT(MBUF(p) == NULL);
- return 1; /* We are done. */
}
+ else if (need_after > HEAP_SIZE(p)) {
+ grow_new_heap(p, next_heap_size(p, need_after, 0), objv, nobj);
+ adjust_size = p->htop - p->heap;
+ }
+ /*else: The heap size turned out to be just right. We are done. */
- if (HEAP_SIZE(p) >= need_after) {
- /*
- * The heap size turned out to be just right. We are done.
- */
- ASSERT(HEAP_SIZE(p) == next_heap_size(p, HEAP_SIZE(p), 0));
- ASSERT(MBUF(p) == NULL);
- return 1;
- }
+ ASSERT(HEAP_SIZE(p) == next_heap_size(p, HEAP_SIZE(p), 0));
+
+ /* The heap usage during GC should be larger than what we end up
+ after a GC, even if we grow it. If this assertion is not true
+ we have to check size in grow_new_heap and potentially kill the
+ process from there */
+ ASSERT(!MAX_HEAP_SIZE_GET(p) ||
+ !(MAX_HEAP_SIZE_FLAGS_GET(p) & MAX_HEAP_SIZE_KILL) ||
+ MAX_HEAP_SIZE_GET(p) > (young_gen_usage(p) +
+ (OLD_HEND(p) - OLD_HEAP(p)) +
+ (HEAP_END(p) - HEAP_TOP(p))));
+
+ return gc_cost(size_after, adjust_size);
}
/*
- * Still not enough room after minor collection. Must force a major collection.
+ * Not enough room for a minor collection. Must force a major collection.
*/
- FLAGS(p) |= F_NEED_FULLSWEEP;
- return 0;
+ return -1;
}
-/*
- * HiPE native code stack scanning procedures:
- * - fullsweep_nstack()
- * - gensweep_nstack()
- * - offset_nstack()
- */
-#if defined(HIPE)
-
-#define GENSWEEP_NSTACK(p,old_htop,n_htop) \
- do { \
- Eterm *tmp_old_htop = old_htop; \
- Eterm *tmp_n_htop = n_htop; \
- gensweep_nstack((p), &tmp_old_htop, &tmp_n_htop); \
- old_htop = tmp_old_htop; \
- n_htop = tmp_n_htop; \
- } while(0)
-
-/*
- * offset_nstack() can ignore the descriptor-based traversal the other
- * nstack procedures use and simply call offset_heap_ptr() instead.
- * This relies on two facts:
- * 1. The only live non-Erlang terms on an nstack are return addresses,
- * and they will be skipped thanks to the low/high range check.
- * 2. Dead values, even if mistaken for pointers into the low/high area,
- * can be offset safely since they won't be dereferenced.
- *
- * XXX: WARNING: If HiPE starts storing other non-Erlang values on the
- * nstack, such as floats, then this will have to be changed.
- */
-static ERTS_INLINE void offset_nstack(Process* p, Sint offs,
- char* area, Uint area_size)
-{
- if (p->hipe.nstack) {
- ASSERT(p->hipe.nsp && p->hipe.nstend);
- offset_heap_ptr(hipe_nstack_start(p), hipe_nstack_used(p),
- offs, area, area_size);
- }
- else {
- ASSERT(!p->hipe.nsp && !p->hipe.nstend);
- }
-}
-
-#else /* !HIPE */
-
-#define fullsweep_nstack(p,n_htop) (n_htop)
-#define GENSWEEP_NSTACK(p,old_htop,n_htop) do{}while(0)
-#define offset_nstack(p,offs,area,area_size) do{}while(0)
-
-#endif /* HIPE */
-
static void
-do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj)
+do_minor(Process *p, ErlHeapFragment *live_hf_end,
+ char *mature, Uint mature_size,
+ Uint new_sz, Eterm* objv, int nobj)
{
Rootset rootset; /* Rootset for GC (stack, dictionary, etc). */
Roots* roots;
@@ -1018,17 +1332,24 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj)
Eterm* ptr;
Eterm val;
Eterm gval;
- char* heap = (char *) HEAP_START(p);
- Uint heap_size = (char *) HEAP_TOP(p) - heap;
- Uint mature_size = (char *) HIGH_WATER(p) - heap;
Eterm* old_htop = OLD_HTOP(p);
Eterm* n_heap;
+ char* oh = (char *) OLD_HEAP(p);
+ Uint oh_size = (char *) OLD_HTOP(p) - oh;
+
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] MINOR GC: %p %p %p %p\n", p->common.id,
+ HEAP_START(p), HEAP_END(p), OLD_HEAP(p), OLD_HEND(p)));
n_htop = n_heap = (Eterm*) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP,
sizeof(Eterm)*new_sz);
- if (MBUF(p) != NULL) {
- n_htop = collect_heap_frags(p, n_heap, n_htop, objv, nobj);
+ if (live_hf_end != ERTS_INVALID_HFRAG_PTR) {
+ /*
+ * Move heap frags that we know are completely live
+ * directly into the new young heap generation.
+ */
+ n_htop = collect_live_heap_frags(p, live_hf_end, n_heap, n_htop,
+ objv, nobj);
}
n = setup_rootset(p, objv, nobj, &rootset);
@@ -1051,9 +1372,9 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj)
if (IS_MOVED_BOXED(val)) {
ASSERT(is_boxed(val));
*g_ptr++ = val;
- } else if (in_area(ptr, heap, mature_size)) {
+ } else if (ErtsInArea(ptr, mature, mature_size)) {
MOVE_BOXED(ptr,val,old_htop,g_ptr++);
- } else if (in_area(ptr, heap, heap_size)) {
+ } else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) {
MOVE_BOXED(ptr,val,n_htop,g_ptr++);
} else {
g_ptr++;
@@ -1066,9 +1387,9 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj)
val = *ptr;
if (IS_MOVED_CONS(val)) { /* Moved */
*g_ptr++ = ptr[1];
- } else if (in_area(ptr, heap, mature_size)) {
+ } else if (ErtsInArea(ptr, mature, mature_size)) {
MOVE_CONS(ptr,val,old_htop,g_ptr++);
- } else if (in_area(ptr, heap, heap_size)) {
+ } else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) {
MOVE_CONS(ptr,val,n_htop,g_ptr++);
} else {
g_ptr++;
@@ -1093,7 +1414,7 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj)
*/
if (mature_size == 0) {
- n_htop = sweep_one_area(n_heap, n_htop, heap, heap_size);
+ n_htop = sweep_new_heap(n_heap, n_htop, oh, oh_size);
} else {
Eterm* n_hp = n_heap;
Eterm* ptr;
@@ -1110,9 +1431,9 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj)
if (IS_MOVED_BOXED(val)) {
ASSERT(is_boxed(val));
*n_hp++ = val;
- } else if (in_area(ptr, heap, mature_size)) {
+ } else if (ErtsInArea(ptr, mature, mature_size)) {
MOVE_BOXED(ptr,val,old_htop,n_hp++);
- } else if (in_area(ptr, heap, heap_size)) {
+ } else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) {
MOVE_BOXED(ptr,val,n_htop,n_hp++);
} else {
n_hp++;
@@ -1124,9 +1445,9 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj)
val = *ptr;
if (IS_MOVED_CONS(val)) {
*n_hp++ = ptr[1];
- } else if (in_area(ptr, heap, mature_size)) {
+ } else if (ErtsInArea(ptr, mature, mature_size)) {
MOVE_CONS(ptr,val,old_htop,n_hp++);
- } else if (in_area(ptr, heap, heap_size)) {
+ } else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) {
MOVE_CONS(ptr,val,n_htop,n_hp++);
} else {
n_hp++;
@@ -1146,10 +1467,10 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj)
if (IS_MOVED_BOXED(val)) {
*origptr = val;
mb->base = binary_bytes(val);
- } else if (in_area(ptr, heap, mature_size)) {
+ } else if (ErtsInArea(ptr, mature, mature_size)) {
MOVE_BOXED(ptr,val,old_htop,origptr);
mb->base = binary_bytes(mb->orig);
- } else if (in_area(ptr, heap, heap_size)) {
+ } else if (ErtsInYoungGen(*origptr, ptr, oh, oh_size)) {
MOVE_BOXED(ptr,val,n_htop,origptr);
mb->base = binary_bytes(mb->orig);
}
@@ -1170,9 +1491,8 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj)
* may point to the old (soon to be deleted) new_heap.
*/
- if (OLD_HTOP(p) < old_htop) {
- old_htop = sweep_one_area(OLD_HTOP(p), old_htop, heap, heap_size);
- }
+ if (OLD_HTOP(p) < old_htop)
+ old_htop = sweep_new_heap(OLD_HTOP(p), old_htop, oh, oh_size);
OLD_HTOP(p) = old_htop;
HIGH_WATER(p) = n_htop;
@@ -1203,18 +1523,16 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj)
}
#endif
- ERTS_HEAP_FREE(ERTS_ALC_T_HEAP,
- (void*)HEAP_START(p),
- HEAP_SIZE(p) * sizeof(Eterm));
+#ifdef HARDDEBUG
+ disallow_heap_frag_ref_in_heap(p, n_heap, n_htop);
+#endif
+
+ erts_deallocate_young_generation(p);
+
HEAP_START(p) = n_heap;
HEAP_TOP(p) = n_htop;
HEAP_SIZE(p) = new_sz;
HEAP_END(p) = n_heap + new_sz;
-
-#ifdef HARDDEBUG
- disallow_heap_frag_ref_in_heap(p);
-#endif
- remove_message_buffers(p);
}
/*
@@ -1222,31 +1540,30 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj)
*/
static int
-major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl)
+major_collection(Process* p, ErlHeapFragment *live_hf_end,
+ int need, Eterm* objv, int nobj, Uint *recl)
{
- Rootset rootset;
- Roots* roots;
- const Uint size_before = ((HEAP_TOP(p) - HEAP_START(p))
- + (OLD_HTOP(p) - OLD_HEAP(p))
- + MBUF_SIZE(p));
+ Uint size_before, size_after, stack_size;
Eterm* n_heap;
Eterm* n_htop;
- char* src = (char *) HEAP_START(p);
- Uint src_size = (char *) HEAP_TOP(p) - src;
char* oh = (char *) OLD_HEAP(p);
Uint oh_size = (char *) OLD_HTOP(p) - oh;
- Uint n;
- Uint new_sz;
- int done;
+ Uint new_sz, stk_sz;
+ int adjusted;
+
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] MAJOR GC: %p %p %p %p\n", p->common.id,
+ HEAP_START(p), HEAP_END(p), OLD_HEAP(p), OLD_HEND(p)));
/*
* Do a fullsweep GC. First figure out the size of the heap
* to receive all live data.
*/
- new_sz = (HEAP_SIZE(p) + MBUF_SIZE(p)
- + combined_message_size(p)
- + (OLD_HTOP(p) - OLD_HEAP(p)));
+ size_before = young_gen_usage(p);
+ size_before += p->old_htop - p->old_heap;
+ stack_size = p->hend - p->stop;
+
+ new_sz = stack_size + size_before;
new_sz = next_heap_size(p, new_sz, 0);
/*
@@ -1256,17 +1573,93 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl)
if (new_sz == HEAP_SIZE(p) && FLAGS(p) & F_HEAP_GROW) {
new_sz = next_heap_size(p, HEAP_SIZE(p), 1);
}
+
+
+ if (MAX_HEAP_SIZE_GET(p)) {
+ Uint heap_size = size_before;
+
+ /* Add unused space in old heap */
+ heap_size += OLD_HEND(p) - OLD_HTOP(p);
+
+ /* Add stack + unused space in young heap */
+ heap_size += HEAP_END(p) - HEAP_TOP(p);
+
+ /* Add size of new young heap */
+ heap_size += new_sz;
+
+ if (MAX_HEAP_SIZE_GET(p) < heap_size)
+ if (reached_max_heap_size(p, heap_size, new_sz, 0))
+ return -2;
+ }
+
FLAGS(p) &= ~(F_HEAP_GROW|F_NEED_FULLSWEEP);
n_htop = n_heap = (Eterm *) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP,
sizeof(Eterm)*new_sz);
- /*
- * Get rid of heap fragments.
- */
+ if (live_hf_end != ERTS_INVALID_HFRAG_PTR) {
+ /*
+ * Move heap frags that we know are completely live
+ * directly into the heap.
+ */
+ n_htop = collect_live_heap_frags(p, live_hf_end, n_heap, n_htop,
+ objv, nobj);
+ }
+
+ n_htop = full_sweep_heaps(p, 0, n_heap, n_htop, oh, oh_size, objv, nobj);
- if (MBUF(p) != NULL) {
- n_htop = collect_heap_frags(p, n_heap, n_htop, objv, nobj);
+ /* Move the stack to the end of the heap */
+ stk_sz = HEAP_END(p) - p->stop;
+ sys_memcpy(n_heap + new_sz - stk_sz, p->stop, stk_sz * sizeof(Eterm));
+ p->stop = n_heap + new_sz - stk_sz;
+
+#ifdef USE_VM_PROBES
+ if (HEAP_SIZE(p) != new_sz && DTRACE_ENABLED(process_heap_grow)) {
+ DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE);
+
+ dtrace_proc_str(p, pidbuf);
+ DTRACE3(process_heap_grow, pidbuf, HEAP_SIZE(p), new_sz);
}
+#endif
+
+#ifdef HARDDEBUG
+ disallow_heap_frag_ref_in_heap(p, n_heap, n_htop);
+#endif
+
+ erts_deallocate_young_generation(p);
+
+ HEAP_START(p) = n_heap;
+ HEAP_TOP(p) = n_htop;
+ HEAP_SIZE(p) = new_sz;
+ HEAP_END(p) = n_heap + new_sz;
+ GEN_GCS(p) = 0;
+
+ HIGH_WATER(p) = HEAP_TOP(p);
+
+ if (p->flags & F_ON_HEAP_MSGQ)
+ move_msgq_to_heap(p);
+
+ ErtsGcQuickSanityCheck(p);
+
+ size_after = HEAP_TOP(p) - HEAP_START(p) + p->mbuf_sz;
+ *recl += size_before - size_after;
+
+ adjusted = adjust_after_fullsweep(p, need, objv, nobj);
+
+ ErtsGcQuickSanityCheck(p);
+
+ return gc_cost(size_after, adjusted ? size_after : 0);
+}
+
+static Eterm *
+full_sweep_heaps(Process *p,
+ int hibernate,
+ Eterm *n_heap, Eterm* n_htop,
+ char *oh, Uint oh_size,
+ Eterm *objv, int nobj)
+{
+ Rootset rootset;
+ Roots *roots;
+ Uint n;
/*
* Copy all top-level terms directly referenced by the rootset to
@@ -1274,7 +1667,14 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl)
*/
n = setup_rootset(p, objv, nobj, &rootset);
- n_htop = fullsweep_nstack(p, n_htop);
+
+#ifdef HIPE
+ if (hibernate)
+ hipe_empty_nstack(p);
+ else
+ n_htop = fullsweep_nstack(p, n_htop);
+#endif
+
roots = rootset.roots;
while (n--) {
Eterm* g_ptr = roots->v;
@@ -1294,7 +1694,7 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl)
if (IS_MOVED_BOXED(val)) {
ASSERT(is_boxed(val));
*g_ptr++ = val;
- } else if (in_area(ptr, src, src_size) || in_area(ptr, oh, oh_size)) {
+ } else if (!erts_is_literal(gval, ptr)) {
MOVE_BOXED(ptr,val,n_htop,g_ptr++);
} else {
g_ptr++;
@@ -1307,7 +1707,7 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl)
val = *ptr;
if (IS_MOVED_CONS(val)) {
*g_ptr++ = ptr[1];
- } else if (in_area(ptr, src, src_size) || in_area(ptr, oh, oh_size)) {
+ } else if (!erts_is_literal(gval, ptr)) {
MOVE_CONS(ptr,val,n_htop,g_ptr++);
} else {
g_ptr++;
@@ -1332,74 +1732,7 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl)
* until all is copied.
*/
- if (oh_size == 0) {
- n_htop = sweep_one_area(n_heap, n_htop, src, src_size);
- } else {
- Eterm* n_hp = n_heap;
-
- while (n_hp != n_htop) {
- Eterm* ptr;
- Eterm val;
- Eterm gval = *n_hp;
-
- switch (primary_tag(gval)) {
- case TAG_PRIMARY_BOXED: {
- ptr = boxed_val(gval);
- val = *ptr;
- if (IS_MOVED_BOXED(val)) {
- ASSERT(is_boxed(val));
- *n_hp++ = val;
- } else if (in_area(ptr, src, src_size) || in_area(ptr, oh, oh_size)) {
- MOVE_BOXED(ptr,val,n_htop,n_hp++);
- } else {
- n_hp++;
- }
- break;
- }
- case TAG_PRIMARY_LIST: {
- ptr = list_val(gval);
- val = *ptr;
- if (IS_MOVED_CONS(val)) {
- *n_hp++ = ptr[1];
- } else if (in_area(ptr, src, src_size) || in_area(ptr, oh, oh_size)) {
- MOVE_CONS(ptr,val,n_htop,n_hp++);
- } else {
- n_hp++;
- }
- break;
- }
- case TAG_PRIMARY_HEADER: {
- if (!header_is_thing(gval))
- n_hp++;
- else {
- if (header_is_bin_matchstate(gval)) {
- ErlBinMatchState *ms = (ErlBinMatchState*) n_hp;
- ErlBinMatchBuffer *mb = &(ms->mb);
- Eterm* origptr;
- origptr = &(mb->orig);
- ptr = boxed_val(*origptr);
- val = *ptr;
- if (IS_MOVED_BOXED(val)) {
- *origptr = val;
- mb->base = binary_bytes(*origptr);
- } else if (in_area(ptr, src, src_size) ||
- in_area(ptr, oh, oh_size)) {
- MOVE_BOXED(ptr,val,n_htop,origptr);
- mb->base = binary_bytes(*origptr);
- ptr = boxed_val(*origptr);
- val = *ptr;
- }
- }
- n_hp += (thing_arityval(gval)+1);
- }
- break;
- }
- default:
- n_hp++;
- break;
- }
- }
- }
+ n_htop = sweep_heaps(n_heap, n_htop, oh, oh_size);
if (MSO(p).first) {
sweep_off_heap(p, 1);
@@ -1412,75 +1745,13 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl)
OLD_HEAP(p) = OLD_HTOP(p) = OLD_HEND(p) = NULL;
}
- /* Move the stack to the end of the heap */
- n = HEAP_END(p) - p->stop;
- sys_memcpy(n_heap + new_sz - n, p->stop, n * sizeof(Eterm));
- p->stop = n_heap + new_sz - n;
-
-#ifdef USE_VM_PROBES
- if (HEAP_SIZE(p) != new_sz && DTRACE_ENABLED(process_heap_grow)) {
- DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE);
-
- dtrace_proc_str(p, pidbuf);
- DTRACE3(process_heap_grow, pidbuf, HEAP_SIZE(p), new_sz);
- }
-#endif
-
- ERTS_HEAP_FREE(ERTS_ALC_T_HEAP,
- (void *) HEAP_START(p),
- (HEAP_END(p) - HEAP_START(p)) * sizeof(Eterm));
- HEAP_START(p) = n_heap;
- HEAP_TOP(p) = n_htop;
- HEAP_SIZE(p) = new_sz;
- HEAP_END(p) = n_heap + new_sz;
- GEN_GCS(p) = 0;
-
- HIGH_WATER(p) = HEAP_TOP(p);
-
- ErtsGcQuickSanityCheck(p);
-
- *recl += size_before - (HEAP_TOP(p) - HEAP_START(p));
-
- remove_message_buffers(p);
-
- {
- ErlMessage *msgp;
-
- /*
- * Copy newly received message onto the end of the new heap.
- */
- for (msgp = p->msg.first; msgp; msgp = msgp->next) {
- if (msgp->data.attached) {
- ErtsHeapFactory factory;
- erts_factory_proc_prealloc_init(&factory, p,
- erts_msg_attached_data_size(msgp));
- erts_move_msg_attached_data_to_heap(&factory, msgp);
- erts_factory_close(&factory);
- ErtsGcQuickSanityCheck(p);
- }
- }
- }
-
- if (MBUF(p)) {
- /* This is a very rare case when distributed messages copied above
- * contained maps so big they did not fit on the heap causing the
- * factory to create heap frags.
- * Solution: Trigger a minor gc (without tenuring)
- */
- HIGH_WATER(p) = HEAP_START(p);
- done = 0;
- } else {
- adjust_after_fullsweep(p, need, objv, nobj);
- done = 1;
- }
-
- ErtsGcQuickSanityCheck(p);
- return done;
+ return n_htop;
}
-static void
+static int
adjust_after_fullsweep(Process *p, int need, Eterm *objv, int nobj)
{
+ int adjusted = 0;
Uint wanted, sz, need_after;
Uint stack_size = STACK_SZ_ON_HEAP(p);
@@ -1493,6 +1764,7 @@ adjust_after_fullsweep(Process *p, int need, Eterm *objv, int nobj)
/* Too small - grow to match requested need */
sz = next_heap_size(p, need_after, 0);
grow_new_heap(p, sz, objv, nobj);
+ adjusted = 1;
} else if (3 * HEAP_SIZE(p) < 4 * need_after){
/* Need more than 75% of current, postpone to next GC.*/
FLAGS(p) |= F_HEAP_GROW;
@@ -1509,39 +1781,62 @@ adjust_after_fullsweep(Process *p, int need, Eterm *objv, int nobj)
if (sz < HEAP_SIZE(p)) {
shrink_new_heap(p, sz, objv, nobj);
+ adjusted = 1;
}
}
+ return adjusted;
}
-/*
- * Return the size of all message buffers that are NOT linked in the
- * mbuf list.
- */
-static Uint
-combined_message_size(Process* p)
+void
+erts_deallocate_young_generation(Process *c_p)
{
- Uint sz;
- ErlMessage *msgp;
+ Eterm *orig_heap;
- for (sz = 0, msgp = p->msg.first; msgp; msgp = msgp->next) {
- if (msgp->data.attached)
- sz += erts_msg_attached_data_size(msgp);
+ if (!c_p->abandoned_heap) {
+ orig_heap = c_p->heap;
+ ASSERT(!(c_p->flags & F_ABANDONED_HEAP_USE));
}
- return sz;
-}
+ else {
+ ErlHeapFragment *hfrag;
-/*
- * Remove all message buffers.
- */
-static void
-remove_message_buffers(Process* p)
-{
- if (MBUF(p) != NULL) {
- free_message_buffer(MBUF(p));
- MBUF(p) = NULL;
+ orig_heap = c_p->abandoned_heap;
+ c_p->abandoned_heap = NULL;
+ c_p->flags &= ~F_ABANDONED_HEAP_USE;
+
+ /*
+ * Temporary heap located in heap fragment
+ * only referred to by 'c_p->heap'. Add it to
+ * 'c_p->mbuf' list and deallocate it as any
+ * other heap fragment...
+ */
+ hfrag = ((ErlHeapFragment *)
+ (((char *) c_p->heap)
+ - offsetof(ErlHeapFragment, mem)));
+
+ ASSERT(!hfrag->off_heap.first);
+ ASSERT(!hfrag->off_heap.overhead);
+ ASSERT(!hfrag->next);
+ ASSERT(c_p->htop - c_p->heap <= hfrag->alloc_size);
+
+ hfrag->next = c_p->mbuf;
+ c_p->mbuf = hfrag;
+ }
+
+ if (c_p->mbuf) {
+ free_message_buffer(c_p->mbuf);
+ c_p->mbuf = NULL;
}
- MBUF_SIZE(p) = 0;
+ if (c_p->msg_frag) {
+ erts_cleanup_messages(c_p->msg_frag);
+ c_p->msg_frag = NULL;
+ }
+ c_p->mbuf_sz = 0;
+
+ ERTS_HEAP_FREE(ERTS_ALC_T_HEAP,
+ orig_heap,
+ c_p->heap_sz * sizeof(Eterm));
}
+
#ifdef HARDDEBUG
/*
@@ -1551,79 +1846,17 @@ remove_message_buffers(Process* p)
* For performance reasons, we use _unchecked_list_val(), _unchecked_boxed_val(),
* and so on to avoid a function call.
*/
-
-static void
-disallow_heap_frag_ref(Process* p, Eterm* n_htop, Eterm* objv, int nobj)
-{
- ErlHeapFragment* mbuf;
- ErlHeapFragment* qb;
- Eterm gval;
- Eterm* ptr;
- Eterm val;
-
- ASSERT(p->htop != NULL);
- mbuf = MBUF(p);
-
- while (nobj--) {
- gval = *objv;
-
- switch (primary_tag(gval)) {
-
- case TAG_PRIMARY_BOXED: {
- ptr = _unchecked_boxed_val(gval);
- val = *ptr;
- if (IS_MOVED_BOXED(val)) {
- ASSERT(is_boxed(val));
- objv++;
- } else {
- for (qb = mbuf; qb != NULL; qb = qb->next) {
- if (in_area(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) {
- abort();
- }
- }
- objv++;
- }
- break;
- }
-
- case TAG_PRIMARY_LIST: {
- ptr = _unchecked_list_val(gval);
- val = *ptr;
- if (IS_MOVED_CONS(val)) {
- objv++;
- } else {
- for (qb = mbuf; qb != NULL; qb = qb->next) {
- if (in_area(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) {
- abort();
- }
- }
- objv++;
- }
- break;
- }
-
- default: {
- objv++;
- break;
- }
- }
- }
-}
static void
-disallow_heap_frag_ref_in_heap(Process* p)
+disallow_heap_frag_ref_in_heap(Process *p, Eterm *heap, Eterm *htop)
{
Eterm* hp;
- Eterm* htop;
- Eterm* heap;
Uint heap_size;
if (p->mbuf == 0) {
return;
}
- htop = p->htop;
- heap = p->heap;
heap_size = (htop - heap)*sizeof(Eterm);
hp = heap;
@@ -1636,9 +1869,9 @@ disallow_heap_frag_ref_in_heap(Process* p)
switch (primary_tag(val)) {
case TAG_PRIMARY_BOXED:
ptr = _unchecked_boxed_val(val);
- if (!in_area(ptr, heap, heap_size)) {
+ if (!ErtsInArea(ptr, heap, heap_size)) {
for (qb = MBUF(p); qb != NULL; qb = qb->next) {
- if (in_area(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) {
+ if (ErtsInArea(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) {
abort();
}
}
@@ -1646,9 +1879,9 @@ disallow_heap_frag_ref_in_heap(Process* p)
break;
case TAG_PRIMARY_LIST:
ptr = _unchecked_list_val(val);
- if (!in_area(ptr, heap, heap_size)) {
+ if (!ErtsInArea(ptr, heap, heap_size)) {
for (qb = MBUF(p); qb != NULL; qb = qb->next) {
- if (in_area(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) {
+ if (ErtsInArea(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) {
abort();
}
}
@@ -1690,26 +1923,26 @@ disallow_heap_frag_ref_in_old_heap(Process* p)
val = *hp++;
switch (primary_tag(val)) {
case TAG_PRIMARY_BOXED:
- ptr = (Eterm *) EXPAND_POINTER(val);
- if (!in_area(ptr, old_heap, old_heap_size)) {
- if (in_area(ptr, new_heap, new_heap_size)) {
+ ptr = (Eterm *) val;
+ if (!ErtsInArea(ptr, old_heap, old_heap_size)) {
+ if (ErtsInArea(ptr, new_heap, new_heap_size)) {
abort();
}
for (qb = MBUF(p); qb != NULL; qb = qb->next) {
- if (in_area(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) {
+ if (ErtsInArea(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) {
abort();
}
}
}
break;
case TAG_PRIMARY_LIST:
- ptr = (Eterm *) EXPAND_POINTER(val);
- if (!in_area(ptr, old_heap, old_heap_size)) {
- if (in_area(ptr, new_heap, new_heap_size)) {
+ ptr = (Eterm *) val;
+ if (!ErtsInArea(ptr, old_heap, old_heap_size)) {
+ if (ErtsInArea(ptr, new_heap, new_heap_size)) {
abort();
}
for (qb = MBUF(p); qb != NULL; qb = qb->next) {
- if (in_area(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) {
+ if (ErtsInArea(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) {
abort();
}
}
@@ -1718,7 +1951,7 @@ disallow_heap_frag_ref_in_old_heap(Process* p)
case TAG_PRIMARY_HEADER:
if (header_is_thing(val)) {
hp += _unchecked_thing_arityval(val);
- if (!in_area(hp, old_heap, old_heap_size+1)) {
+ if (!ErtsInArea(hp, old_heap, old_heap_size+1)) {
abort();
}
}
@@ -1728,66 +1961,30 @@ disallow_heap_frag_ref_in_old_heap(Process* p)
}
#endif
-static Eterm*
-sweep_rootset(Rootset* rootset, Eterm* htop, char* src, Uint src_size)
+typedef enum {
+ ErtsSweepNewHeap,
+ ErtsSweepHeaps,
+ ErtsSweepLiteralArea
+} ErtsSweepType;
+
+static ERTS_FORCE_INLINE Eterm *
+sweep(Eterm *n_hp, Eterm *n_htop,
+ ErtsSweepType type,
+ char *oh, Uint ohsz,
+ char *src, Uint src_size)
{
- Roots* roots = rootset->roots;
- Uint n = rootset->num_roots;
Eterm* ptr;
- Eterm gval;
Eterm val;
+ Eterm gval;
- while (n--) {
- Eterm* g_ptr = roots->v;
- Uint g_sz = roots->sz;
-
- roots++;
- while (g_sz--) {
- gval = *g_ptr;
-
- switch (primary_tag(gval)) {
- case TAG_PRIMARY_BOXED: {
- ptr = boxed_val(gval);
- val = *ptr;
- if (IS_MOVED_BOXED(val)) {
- ASSERT(is_boxed(val));
- *g_ptr++ = val;
- } else if (in_area(ptr, src, src_size)) {
- MOVE_BOXED(ptr,val,htop,g_ptr++);
- } else {
- g_ptr++;
- }
- break;
- }
- case TAG_PRIMARY_LIST: {
- ptr = list_val(gval);
- val = *ptr;
- if (IS_MOVED_CONS(val)) {
- *g_ptr++ = ptr[1];
- } else if (in_area(ptr, src, src_size)) {
- MOVE_CONS(ptr,val,htop,g_ptr++);
- } else {
- g_ptr++;
- }
- break;
- }
-
- default:
- g_ptr++;
- break;
- }
- }
- }
- return htop;
-}
-
+#undef ERTS_IS_IN_SWEEP_AREA
-static Eterm*
-sweep_one_area(Eterm* n_hp, Eterm* n_htop, char* src, Uint src_size)
-{
- Eterm* ptr;
- Eterm val;
- Eterm gval;
+#define ERTS_IS_IN_SWEEP_AREA(TPtr, Ptr) \
+ (type == ErtsSweepHeaps \
+ ? !erts_is_literal((TPtr), (Ptr)) \
+ : (type == ErtsSweepNewHeap \
+ ? ErtsInYoungGen((TPtr), (Ptr), oh, ohsz) \
+ : ErtsInArea((Ptr), src, src_size)))
while (n_hp != n_htop) {
ASSERT(n_hp < n_htop);
@@ -1799,7 +1996,7 @@ sweep_one_area(Eterm* n_hp, Eterm* n_htop, char* src, Uint src_size)
if (IS_MOVED_BOXED(val)) {
ASSERT(is_boxed(val));
*n_hp++ = val;
- } else if (in_area(ptr, src, src_size)) {
+ } else if (ERTS_IS_IN_SWEEP_AREA(gval, ptr)) {
MOVE_BOXED(ptr,val,n_htop,n_hp++);
} else {
n_hp++;
@@ -1811,7 +2008,7 @@ sweep_one_area(Eterm* n_hp, Eterm* n_htop, char* src, Uint src_size)
val = *ptr;
if (IS_MOVED_CONS(val)) {
*n_hp++ = ptr[1];
- } else if (in_area(ptr, src, src_size)) {
+ } else if (ERTS_IS_IN_SWEEP_AREA(gval, ptr)) {
MOVE_CONS(ptr,val,n_htop,n_hp++);
} else {
n_hp++;
@@ -1832,7 +2029,7 @@ sweep_one_area(Eterm* n_hp, Eterm* n_htop, char* src, Uint src_size)
if (IS_MOVED_BOXED(val)) {
*origptr = val;
mb->base = binary_bytes(*origptr);
- } else if (in_area(ptr, src, src_size)) {
+ } else if (ERTS_IS_IN_SWEEP_AREA(*origptr, ptr)) {
MOVE_BOXED(ptr,val,n_htop,origptr);
mb->base = binary_bytes(*origptr);
}
@@ -1847,10 +2044,41 @@ sweep_one_area(Eterm* n_hp, Eterm* n_htop, char* src, Uint src_size)
}
}
return n_htop;
+#undef ERTS_IS_IN_SWEEP_AREA
+}
+
+static Eterm *
+sweep_new_heap(Eterm *n_hp, Eterm *n_htop, char* old_heap, Uint old_heap_size)
+{
+ return sweep(n_hp, n_htop,
+ ErtsSweepNewHeap,
+ old_heap, old_heap_size,
+ NULL, 0);
+}
+
+static Eterm *
+sweep_heaps(Eterm *n_hp, Eterm *n_htop, char* old_heap, Uint old_heap_size)
+{
+ return sweep(n_hp, n_htop,
+ ErtsSweepHeaps,
+ old_heap, old_heap_size,
+ NULL, 0);
+}
+
+static Eterm *
+sweep_literal_area(Eterm *n_hp, Eterm *n_htop,
+ char* old_heap, Uint old_heap_size,
+ char* src, Uint src_size)
+{
+ return sweep(n_hp, n_htop,
+ ErtsSweepLiteralArea,
+ old_heap, old_heap_size,
+ src, src_size);
}
static Eterm*
-sweep_one_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm* htop, char* src, Uint src_size)
+sweep_literals_to_old_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm* htop,
+ char* src, Uint src_size)
{
while (heap_ptr < heap_end) {
Eterm* ptr;
@@ -1864,7 +2092,7 @@ sweep_one_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm* htop, char* src, Uint sr
if (IS_MOVED_BOXED(val)) {
ASSERT(is_boxed(val));
*heap_ptr++ = val;
- } else if (in_area(ptr, src, src_size)) {
+ } else if (ErtsInArea(ptr, src, src_size)) {
MOVE_BOXED(ptr,val,htop,heap_ptr++);
} else {
heap_ptr++;
@@ -1876,7 +2104,7 @@ sweep_one_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm* htop, char* src, Uint sr
val = *ptr;
if (IS_MOVED_CONS(val)) {
*heap_ptr++ = ptr[1];
- } else if (in_area(ptr, src, src_size)) {
+ } else if (ErtsInArea(ptr, src, src_size)) {
MOVE_CONS(ptr,val,htop,heap_ptr++);
} else {
heap_ptr++;
@@ -1897,7 +2125,7 @@ sweep_one_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm* htop, char* src, Uint sr
if (IS_MOVED_BOXED(val)) {
*origptr = val;
mb->base = binary_bytes(*origptr);
- } else if (in_area(ptr, src, src_size)) {
+ } else if (ErtsInArea(ptr, src, src_size)) {
MOVE_BOXED(ptr,val,htop,origptr);
mb->base = binary_bytes(*origptr);
}
@@ -1948,43 +2176,21 @@ move_one_area(Eterm* n_htop, char* src, Uint src_size)
*/
static Eterm*
-collect_heap_frags(Process* p, Eterm* n_hstart, Eterm* n_htop,
- Eterm* objv, int nobj)
+collect_live_heap_frags(Process* p, ErlHeapFragment *live_hf_end,
+ Eterm* n_hstart, Eterm* n_htop,
+ Eterm* objv, int nobj)
{
ErlHeapFragment* qb;
char* frag_begin;
Uint frag_size;
/*
- * We don't allow references to a heap fragments from the stack, heap,
- * or process dictionary.
- */
-#ifdef HARDDEBUG
- disallow_heap_frag_ref(p, n_htop, p->stop, STACK_START(p) - p->stop);
- if (p->dictionary != NULL) {
- disallow_heap_frag_ref(p, n_htop, p->dictionary->data, p->dictionary->used);
- }
- /* OTP-18: Actually we do allow references from heap to heap fragments now.
- This can happen when doing "binary_to_term" with a "fat" map contained
- in another term. A "fat" map is a hashmap with higher heap demand than
- first estimated by "binary_to_term" causing the factory to allocate
- additional heap (fragments) for the hashmap tree nodes.
- Run map_SUITE:t_gc_rare_map_overflow to provoke this.
-
- Inverted references like this does not matter however. The copy done
- below by move_one_area() with move markers in the fragments and the
- sweeping done later by the GC should make everything ok in the end.
- */
- /***disallow_heap_frag_ref_in_heap(p);***/
-#endif
-
- /*
* Move the heap fragments to the new heap. Note that no GC is done on
* the heap fragments. Any garbage will thus be moved as well and survive
* until next GC.
*/
qb = MBUF(p);
- while (qb != NULL) {
+ while (qb != live_hf_end) {
ASSERT(!qb->off_heap.first); /* process fragments use the MSO(p) list */
frag_size = qb->used_size * sizeof(Eterm);
if (frag_size != 0) {
@@ -1996,12 +2202,182 @@ collect_heap_frags(Process* p, Eterm* n_hstart, Eterm* n_htop,
return n_htop;
}
+static ERTS_INLINE void
+copy_one_frag(Eterm** hpp, ErlOffHeap* off_heap,
+ ErlHeapFragment *bp, Eterm *refs, int nrefs)
+{
+ Uint sz;
+ int i;
+ Sint offs;
+ struct erl_off_heap_header* oh;
+ Eterm *fhp, *hp;
+
+ OH_OVERHEAD(off_heap, bp->off_heap.overhead);
+ sz = bp->used_size;
+
+ fhp = bp->mem;
+ hp = *hpp;
+ offs = hp - fhp;
+
+ oh = NULL;
+ while (sz--) {
+ Uint cpy_sz;
+ Eterm val = *fhp++;
+
+ switch (primary_tag(val)) {
+ case TAG_PRIMARY_IMMED1:
+ *hp++ = val;
+ break;
+ case TAG_PRIMARY_LIST:
+#ifdef SHCOPY_SEND
+ if (erts_is_literal(val,list_val(val))) {
+ *hp++ = val;
+ } else {
+ *hp++ = offset_ptr(val, offs);
+ }
+#else
+ *hp++ = offset_ptr(val, offs);
+#endif
+ break;
+ case TAG_PRIMARY_BOXED:
+#ifdef SHCOPY_SEND
+ if (erts_is_literal(val,boxed_val(val))) {
+ *hp++ = val;
+ } else {
+ *hp++ = offset_ptr(val, offs);
+ }
+#else
+ *hp++ = offset_ptr(val, offs);
+#endif
+ break;
+ case TAG_PRIMARY_HEADER:
+ *hp++ = val;
+ switch (val & _HEADER_SUBTAG_MASK) {
+ case ARITYVAL_SUBTAG:
+ break;
+ case REFC_BINARY_SUBTAG:
+ case FUN_SUBTAG:
+ case EXTERNAL_PID_SUBTAG:
+ case EXTERNAL_PORT_SUBTAG:
+ case EXTERNAL_REF_SUBTAG:
+ oh = (struct erl_off_heap_header*) (hp-1);
+ cpy_sz = thing_arityval(val);
+ goto cpy_words;
+ default:
+ cpy_sz = header_arity(val);
+
+ cpy_words:
+ ASSERT(sz >= cpy_sz);
+ sz -= cpy_sz;
+ while (cpy_sz >= 8) {
+ cpy_sz -= 8;
+ *hp++ = *fhp++;
+ *hp++ = *fhp++;
+ *hp++ = *fhp++;
+ *hp++ = *fhp++;
+ *hp++ = *fhp++;
+ *hp++ = *fhp++;
+ *hp++ = *fhp++;
+ *hp++ = *fhp++;
+ }
+ switch (cpy_sz) {
+ case 7: *hp++ = *fhp++;
+ case 6: *hp++ = *fhp++;
+ case 5: *hp++ = *fhp++;
+ case 4: *hp++ = *fhp++;
+ case 3: *hp++ = *fhp++;
+ case 2: *hp++ = *fhp++;
+ case 1: *hp++ = *fhp++;
+ default: break;
+ }
+ if (oh) {
+ /* Add to offheap list */
+ oh->next = off_heap->first;
+ off_heap->first = oh;
+ ASSERT(*hpp <= (Eterm*)oh);
+ ASSERT(hp > (Eterm*)oh);
+ oh = NULL;
+ }
+ break;
+ }
+ break;
+ }
+ }
+
+ ASSERT(bp->used_size == hp - *hpp);
+ *hpp = hp;
+
+ for (i = 0; i < nrefs; i++) {
+ if (is_not_immed(refs[i]))
+ refs[i] = offset_ptr(refs[i], offs);
+ }
+ bp->off_heap.first = NULL;
+}
+
+static void
+move_msgq_to_heap(Process *p)
+{
+
+ ErtsMessage **mpp = &p->msg.first;
+ Uint64 pre_oh = MSO(p).overhead;
+
+ while (*mpp) {
+ ErtsMessage *mp = *mpp;
+
+ if (mp->data.attached) {
+ ErlHeapFragment *bp;
+
+ /*
+ * We leave not yet decoded distribution messages
+ * as they are in the queue since it is not
+ * possible to determine a maximum size until
+ * actual decoding...
+ */
+ if (is_value(ERL_MESSAGE_TERM(mp))) {
+
+ bp = erts_message_to_heap_frag(mp);
+
+ if (bp->next)
+ erts_move_multi_frags(&p->htop, &p->off_heap, bp,
+ mp->m, ERL_MESSAGE_REF_ARRAY_SZ, 0);
+ else
+ copy_one_frag(&p->htop, &p->off_heap, bp,
+ mp->m, ERL_MESSAGE_REF_ARRAY_SZ);
+
+ if (mp->data.attached != ERTS_MSG_COMBINED_HFRAG) {
+ mp->data.heap_frag = NULL;
+ free_message_buffer(bp);
+ }
+ else {
+ ErtsMessage *new_mp = erts_alloc_message(0, NULL);
+ sys_memcpy((void *) new_mp->m, (void *) mp->m,
+ sizeof(Eterm)*ERL_MESSAGE_REF_ARRAY_SZ);
+ erts_msgq_replace_msg_ref(&p->msg, new_mp, mpp);
+ mp->next = NULL;
+ erts_cleanup_messages(mp);
+ mp = new_mp;
+ }
+ }
+ }
+
+ mpp = &(*mpp)->next;
+ }
+
+ if (pre_oh != MSO(p).overhead) {
+ /* Got new binaries; update vheap size... */
+ BIN_VHEAP_SZ(p) = next_vheap_size(p, MSO(p).overhead, BIN_VHEAP_SZ(p));
+ }
+}
+
static Uint
setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset)
{
- Uint avail;
+ /*
+ * NOTE!
+ * Remember to update offset_rootset() when changing
+ * this function.
+ */
Roots* roots;
- ErlMessage* mp;
Uint n;
n = 0;
@@ -2013,8 +2389,8 @@ setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset)
++n;
if (p->dictionary != NULL) {
- roots[n].v = p->dictionary->data;
- roots[n].sz = p->dictionary->used;
+ roots[n].v = ERTS_PD_START(p->dictionary);
+ roots[n].sz = ERTS_PD_SIZE(p->dictionary);
++n;
}
if (nobj > 0) {
@@ -2024,7 +2400,7 @@ setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset)
}
ASSERT((is_nil(p->seq_trace_token) ||
- is_tuple(follow_moved(p->seq_trace_token)) ||
+ is_tuple(follow_moved(p->seq_trace_token, (Eterm) 0)) ||
is_atom(p->seq_trace_token)));
if (is_not_immed(p->seq_trace_token)) {
roots[n].v = &p->seq_trace_token;
@@ -2038,11 +2414,9 @@ setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset)
n++;
}
#endif
- ASSERT(is_nil(ERTS_TRACER_PROC(p)) ||
- is_internal_pid(ERTS_TRACER_PROC(p)) ||
- is_internal_port(ERTS_TRACER_PROC(p)));
+ ASSERT(IS_TRACER_VALID(ERTS_TRACER(p)));
- ASSERT(is_pid(follow_moved(p->group_leader)));
+ ASSERT(is_pid(follow_moved(p->group_leader, (Eterm) 0)));
if (is_not_immed(p->group_leader)) {
roots[n].v = &p->group_leader;
roots[n].sz = 1;
@@ -2079,31 +2453,47 @@ setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset)
ASSERT(n <= rootset->size);
- mp = p->msg.first;
- avail = rootset->size - n;
- while (mp != NULL) {
- if (avail == 0) {
- Uint new_size = 2*rootset->size;
- if (roots == rootset->def) {
- roots = erts_alloc(ERTS_ALC_T_ROOTSET,
- new_size*sizeof(Roots));
- sys_memcpy(roots, rootset->def, sizeof(rootset->def));
- } else {
- roots = erts_realloc(ERTS_ALC_T_ROOTSET,
- (void *) roots,
- new_size*sizeof(Roots));
- }
+ switch (p->flags & (F_OFF_HEAP_MSGQ|F_OFF_HEAP_MSGQ_CHNG)) {
+ case F_OFF_HEAP_MSGQ|F_OFF_HEAP_MSGQ_CHNG:
+ (void) erts_move_messages_off_heap(p);
+ case F_OFF_HEAP_MSGQ:
+ break;
+ case F_OFF_HEAP_MSGQ_CHNG:
+ case 0: {
+ /*
+ * We do not have off heap message queue enabled, i.e. we
+ * need to add message queue to rootset...
+ */
+ ErtsMessage *mp;
+
+ /* Ensure large enough rootset... */
+ if (n + p->msg.len > rootset->size) {
+ Uint new_size = n + p->msg.len;
+ ERTS_GC_ASSERT(roots == rootset->def);
+ roots = erts_alloc(ERTS_ALC_T_ROOTSET,
+ new_size*sizeof(Roots));
+ sys_memcpy(roots, rootset->def, n*sizeof(Roots));
rootset->size = new_size;
- avail = new_size - n;
}
- if (mp->data.attached == NULL) {
- roots[n].v = mp->m;
- roots[n].sz = 2;
- n++;
- avail--;
+
+ for (mp = p->msg.first; mp; mp = mp->next) {
+
+ if (!mp->data.attached) {
+ /*
+ * Message may refer data on heap;
+ * add it to rootset...
+ */
+ roots[n].v = mp->m;
+ roots[n].sz = ERL_MESSAGE_REF_ARRAY_SZ;
+ n++;
+ }
}
- mp = mp->next;
+ break;
+ }
}
+
+ ASSERT(rootset->size >= n);
+
rootset->roots = roots;
rootset->num_roots = n;
return n;
@@ -2343,11 +2733,11 @@ sweep_off_heap(Process *p, int fullsweep)
*/
while (ptr) {
if (IS_MOVED_BOXED(ptr->thing_word)) {
- ASSERT(!in_area(ptr, oheap, oheap_sz));
+ ASSERT(!ErtsInArea(ptr, oheap, oheap_sz));
*prev = ptr = (struct erl_off_heap_header*) boxed_val(ptr->thing_word);
ASSERT(!IS_MOVED_BOXED(ptr->thing_word));
if (ptr->thing_word == HEADER_PROC_BIN) {
- int to_new_heap = !in_area(ptr, oheap, oheap_sz);
+ int to_new_heap = !ErtsInArea(ptr, oheap, oheap_sz);
ASSERT(to_new_heap == !seen_mature || (!to_new_heap && (seen_mature=1)));
if (to_new_heap) {
bin_vheap += ptr->size / sizeof(Eterm);
@@ -2361,7 +2751,7 @@ sweep_off_heap(Process *p, int fullsweep)
ptr = ptr->next;
}
}
- else if (!in_area(ptr, oheap, oheap_sz)) {
+ else if (!ErtsInArea(ptr, oheap, oheap_sz)) {
/* garbage */
switch (thing_subtag(ptr->thing_word)) {
case REFC_BINARY_SUBTAG:
@@ -2375,7 +2765,7 @@ sweep_off_heap(Process *p, int fullsweep)
case FUN_SUBTAG:
{
ErlFunEntry* fe = ((ErlFunThing*)ptr)->fe;
- if (erts_refc_dectest(&fe->refc, 0) == 0) {
+ if (erts_smp_refc_dectest(&fe->refc, 0) == 0) {
erts_erase_fun_entry(fe);
}
break;
@@ -2393,7 +2783,7 @@ sweep_off_heap(Process *p, int fullsweep)
* generational collection - keep objects in list.
*/
while (ptr) {
- ASSERT(in_area(ptr, oheap, oheap_sz));
+ ASSERT(ErtsInArea(ptr, oheap, oheap_sz));
ASSERT(!IS_MOVED_BOXED(ptr->thing_word));
if (ptr->thing_word == HEADER_PROC_BIN) {
BIN_OLD_VHEAP(p) += ptr->size / sizeof(Eterm); /* for binary gc (words)*/
@@ -2412,7 +2802,6 @@ sweep_off_heap(Process *p, int fullsweep)
}
BIN_VHEAP_SZ(p) = next_vheap_size(p, bin_vheap, BIN_VHEAP_SZ(p));
MSO(p).overhead = bin_vheap;
- BIN_VHEAP_MATURE(p) = bin_vheap;
/*
* If we got any shrink candidates, check them out.
@@ -2483,7 +2872,7 @@ offset_heap(Eterm* hp, Uint sz, Sint offs, char* area, Uint area_size)
switch (primary_tag(val)) {
case TAG_PRIMARY_LIST:
case TAG_PRIMARY_BOXED:
- if (in_area(ptr_val(val), area, area_size)) {
+ if (ErtsInArea(ptr_val(val), area, area_size)) {
*hp = offset_ptr(val, offs);
}
hp++;
@@ -2505,7 +2894,7 @@ offset_heap(Eterm* hp, Uint sz, Sint offs, char* area, Uint area_size)
{
struct erl_off_heap_header* oh = (struct erl_off_heap_header*) hp;
- if (in_area(oh->next, area, area_size)) {
+ if (ErtsInArea(oh->next, area, area_size)) {
Eterm** uptr = (Eterm **) (void *) &oh->next;
*uptr += offs; /* Patch the mso chain */
}
@@ -2515,7 +2904,7 @@ offset_heap(Eterm* hp, Uint sz, Sint offs, char* area, Uint area_size)
{
ErlBinMatchState *ms = (ErlBinMatchState*) hp;
ErlBinMatchBuffer *mb = &(ms->mb);
- if (in_area(ptr_val(mb->orig), area, area_size)) {
+ if (ErtsInArea(ptr_val(mb->orig), area, area_size)) {
mb->orig = offset_ptr(mb->orig, offs);
mb->base = binary_bytes(mb->orig);
}
@@ -2545,7 +2934,7 @@ offset_heap_ptr(Eterm* hp, Uint sz, Sint offs, char* area, Uint area_size)
switch (primary_tag(val)) {
case TAG_PRIMARY_LIST:
case TAG_PRIMARY_BOXED:
- if (in_area(ptr_val(val), area, area_size)) {
+ if (ErtsInArea(ptr_val(val), area, area_size)) {
*hp = offset_ptr(val, offs);
}
hp++;
@@ -2560,7 +2949,7 @@ offset_heap_ptr(Eterm* hp, Uint sz, Sint offs, char* area, Uint area_size)
static void
offset_off_heap(Process* p, Sint offs, char* area, Uint area_size)
{
- if (MSO(p).first && in_area((Eterm *)MSO(p).first, area, area_size)) {
+ if (MSO(p).first && ErtsInArea((Eterm *)MSO(p).first, area, area_size)) {
Eterm** uptr = (Eterm**) (void *) &MSO(p).first;
*uptr += offs;
}
@@ -2572,35 +2961,39 @@ offset_off_heap(Process* p, Sint offs, char* area, Uint area_size)
static void
offset_mqueue(Process *p, Sint offs, char* area, Uint area_size)
{
- ErlMessage* mp = p->msg.first;
-
- while (mp != NULL) {
- Eterm mesg = ERL_MESSAGE_TERM(mp);
- if (is_value(mesg)) {
- switch (primary_tag(mesg)) {
- case TAG_PRIMARY_LIST:
- case TAG_PRIMARY_BOXED:
- if (in_area(ptr_val(mesg), area, area_size)) {
- ERL_MESSAGE_TERM(mp) = offset_ptr(mesg, offs);
+ ErtsMessage* mp = p->msg.first;
+
+ if ((p->flags & (F_OFF_HEAP_MSGQ|F_OFF_HEAP_MSGQ_CHNG)) != F_OFF_HEAP_MSGQ) {
+
+ while (mp != NULL) {
+ Eterm mesg = ERL_MESSAGE_TERM(mp);
+ if (is_value(mesg)) {
+ switch (primary_tag(mesg)) {
+ case TAG_PRIMARY_LIST:
+ case TAG_PRIMARY_BOXED:
+ if (ErtsInArea(ptr_val(mesg), area, area_size)) {
+ ERL_MESSAGE_TERM(mp) = offset_ptr(mesg, offs);
+ }
+ break;
}
- break;
}
- }
- mesg = ERL_MESSAGE_TOKEN(mp);
- if (is_boxed(mesg) && in_area(ptr_val(mesg), area, area_size)) {
- ERL_MESSAGE_TOKEN(mp) = offset_ptr(mesg, offs);
- }
+ mesg = ERL_MESSAGE_TOKEN(mp);
+ if (is_boxed(mesg) && ErtsInArea(ptr_val(mesg), area, area_size)) {
+ ERL_MESSAGE_TOKEN(mp) = offset_ptr(mesg, offs);
+ }
#ifdef USE_VM_PROBES
- mesg = ERL_MESSAGE_DT_UTAG(mp);
- if (is_boxed(mesg) && in_area(ptr_val(mesg), area, area_size)) {
- ERL_MESSAGE_DT_UTAG(mp) = offset_ptr(mesg, offs);
- }
+ mesg = ERL_MESSAGE_DT_UTAG(mp);
+ if (is_boxed(mesg) && ErtsInArea(ptr_val(mesg), area, area_size)) {
+ ERL_MESSAGE_DT_UTAG(mp) = offset_ptr(mesg, offs);
+ }
#endif
- ASSERT((is_nil(ERL_MESSAGE_TOKEN(mp)) ||
- is_tuple(ERL_MESSAGE_TOKEN(mp)) ||
- is_atom(ERL_MESSAGE_TOKEN(mp))));
- mp = mp->next;
+ ASSERT((is_nil(ERL_MESSAGE_TOKEN(mp)) ||
+ is_tuple(ERL_MESSAGE_TOKEN(mp)) ||
+ is_atom(ERL_MESSAGE_TOKEN(mp))));
+ mp = mp->next;
+ }
+
}
}
@@ -2609,8 +3002,8 @@ offset_one_rootset(Process *p, Sint offs, char* area, Uint area_size,
Eterm* objv, int nobj)
{
if (p->dictionary) {
- offset_heap(p->dictionary->data,
- p->dictionary->used,
+ offset_heap(ERTS_PD_START(p->dictionary),
+ ERTS_PD_SIZE(p->dictionary),
offs, area, area_size);
}
@@ -2628,6 +3021,12 @@ offset_one_rootset(Process *p, Sint offs, char* area, Uint area_size,
offset_heap_ptr(objv, nobj, offs, area, area_size);
}
offset_off_heap(p, offs, area, area_size);
+ if (ERTS_PROC_GET_NIF_TRAP_EXPORT(p)) {
+ Eterm* argv;
+ int argc;
+ if (erts_setup_nif_gc(p, &argv, &argc))
+ offset_heap_ptr(argv, argc, offs, area, area_size);
+ }
}
static void
@@ -2659,12 +3058,24 @@ reply_gc_info(void *vgcirp)
Eterm **hpp;
Uint sz, *szp;
ErlOffHeap *ohp = NULL;
- ErlHeapFragment *bp = NULL;
+ ErtsMessage *mp = NULL;
ASSERT(esdp);
reclaimed = esdp->gc_info.reclaimed;
garbage_cols = esdp->gc_info.garbage_cols;
+#ifdef ERTS_DIRTY_SCHEDULERS
+ /*
+ * Add dirty schedulers info on requesting
+ * schedulers info
+ */
+ if (gcirp->req_sched == esdp->no) {
+ erts_mtx_lock(&dirty_gc.mtx);
+ reclaimed += dirty_gc.info.reclaimed;
+ garbage_cols += dirty_gc.info.garbage_cols;
+ erts_mtx_unlock(&dirty_gc.mtx);
+ }
+#endif
sz = 0;
hpp = NULL;
@@ -2685,12 +3096,13 @@ reply_gc_info(void *vgcirp)
if (hpp)
break;
- hp = erts_alloc_message_heap(sz, &bp, &ohp, rp, &rp_locks);
+ mp = erts_alloc_message_heap(rp, &rp_locks, sz, &hp, &ohp);
+
szp = NULL;
hpp = &hp;
}
- erts_queue_message(rp, &rp_locks, bp, msg, NIL);
+ erts_queue_message(rp, rp_locks, mp, msg, am_system);
if (gcirp->req_sched == esdp->no)
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
@@ -2707,7 +3119,7 @@ reply_gc_info(void *vgcirp)
Eterm
erts_gc_info_request(Process *c_p)
{
- ErtsSchedulerData *esdp = ERTS_PROC_GET_SCHDATA(c_p);
+ ErtsSchedulerData *esdp = erts_proc_sched_data(c_p);
Eterm ref;
ErtsGCInfoReq *gcirp;
Eterm *hp;
@@ -2737,41 +3149,250 @@ erts_gc_info_request(Process *c_p)
return ref;
}
+Eterm
+erts_process_gc_info(Process *p, Uint *sizep, Eterm **hpp,
+ Uint extra_heap_block,
+ Uint extra_old_heap_block_size)
+{
+ ERTS_DECL_AM(bin_vheap_size);
+ ERTS_DECL_AM(bin_vheap_block_size);
+ ERTS_DECL_AM(bin_old_vheap_size);
+ ERTS_DECL_AM(bin_old_vheap_block_size);
+ Eterm tags[] = {
+ /* If you increase the number of elements here, make sure to update
+ any call sites as they may have stack allocations that depend
+ on the number of elements here. */
+ am_old_heap_block_size,
+ am_heap_block_size,
+ am_mbuf_size,
+ am_recent_size,
+ am_stack_size,
+ am_old_heap_size,
+ am_heap_size,
+ AM_bin_vheap_size,
+ AM_bin_vheap_block_size,
+ AM_bin_old_vheap_size,
+ AM_bin_old_vheap_block_size
+ };
+ UWord values[] = {
+ OLD_HEAP(p) ? OLD_HEND(p) - OLD_HEAP(p) + extra_old_heap_block_size
+ : extra_old_heap_block_size,
+ HEAP_SIZE(p) + extra_heap_block,
+ MBUF_SIZE(p),
+ HIGH_WATER(p) - HEAP_START(p),
+ STACK_START(p) - p->stop,
+ OLD_HEAP(p) ? OLD_HTOP(p) - OLD_HEAP(p) : 0,
+ HEAP_TOP(p) - HEAP_START(p),
+ MSO(p).overhead,
+ BIN_VHEAP_SZ(p),
+ BIN_OLD_VHEAP(p),
+ BIN_OLD_VHEAP_SZ(p)
+ };
+
+ Eterm res = THE_NON_VALUE;
+ ErtsMessage *mp;
+
+ ERTS_CT_ASSERT(sizeof(values)/sizeof(*values) == sizeof(tags)/sizeof(*tags));
+ ERTS_CT_ASSERT(sizeof(values)/sizeof(*values) == ERTS_PROCESS_GC_INFO_MAX_TERMS);
+
+ if (p->abandoned_heap) {
+ Eterm *htop, *heap;
+ ERTS_GET_ORIG_HEAP(p, heap, htop);
+ values[3] = HIGH_WATER(p) - heap;
+ values[6] = htop - heap;
+ }
+
+ if (p->flags & F_ON_HEAP_MSGQ) {
+ /* If on heap messages in the internal queue are counted
+ as being part of the heap, so we have to add them to the
+ am_mbuf_size value. process_info(total_heap_size) should
+ be the same as adding old_heap_block_size + heap_block_size
+ + mbuf_size.
+ */
+ for (mp = p->msg.first; mp; mp = mp->next)
+ if (mp->data.attached)
+ values[2] += erts_msg_attached_data_size(mp);
+ }
+
+ res = erts_bld_atom_uword_2tup_list(hpp,
+ sizep,
+ sizeof(values)/sizeof(*values),
+ tags,
+ values);
+
+ return res;
+}
+
+static int
+reached_max_heap_size(Process *p, Uint total_heap_size,
+ Uint extra_heap_size, Uint extra_old_heap_size)
+{
+ Uint max_heap_flags = MAX_HEAP_SIZE_FLAGS_GET(p);
+ if (IS_TRACED_FL(p, F_TRACE_GC) ||
+ max_heap_flags & MAX_HEAP_SIZE_LOG) {
+ Eterm msg;
+ Uint size = 0;
+ Eterm *o_hp , *hp;
+ erts_process_gc_info(p, &size, NULL, extra_heap_size,
+ extra_old_heap_size);
+ o_hp = hp = erts_alloc(ERTS_ALC_T_TMP, size * sizeof(Eterm));
+ msg = erts_process_gc_info(p, NULL, &hp, extra_heap_size,
+ extra_old_heap_size);
+
+ if (max_heap_flags & MAX_HEAP_SIZE_LOG) {
+ int alive = erts_is_alive;
+ erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
+ Eterm *o_hp, *hp, args = NIL;
+
+ /* Build the format message */
+ erts_dsprintf(dsbufp, " Process: ~p ");
+ if (alive)
+ erts_dsprintf(dsbufp, "on node ~p");
+ erts_dsprintf(dsbufp, "~n Context: maximum heap size reached~n");
+ erts_dsprintf(dsbufp, " Max Heap Size: ~p~n");
+ erts_dsprintf(dsbufp, " Total Heap Size: ~p~n");
+ erts_dsprintf(dsbufp, " Kill: ~p~n");
+ erts_dsprintf(dsbufp, " Error Logger: ~p~n");
+ erts_dsprintf(dsbufp, " GC Info: ~p~n");
+
+ /* Build the args in reverse order */
+ o_hp = hp = erts_alloc(ERTS_ALC_T_TMP, 2*(alive ? 7 : 6) * sizeof(Eterm));
+ args = CONS(hp, msg, args); hp += 2;
+ args = CONS(hp, am_true, args); hp += 2;
+ args = CONS(hp, (max_heap_flags & MAX_HEAP_SIZE_KILL ? am_true : am_false), args); hp += 2;
+ args = CONS(hp, make_small(total_heap_size), args); hp += 2;
+ args = CONS(hp, make_small(MAX_HEAP_SIZE_GET(p)), args); hp += 2;
+ if (alive) {
+ args = CONS(hp, erts_this_node->sysname, args); hp += 2;
+ }
+ args = CONS(hp, p->common.id, args); hp += 2;
+
+ erts_send_error_term_to_logger(p->group_leader, dsbufp, args);
+ erts_free(ERTS_ALC_T_TMP, o_hp);
+ }
+
+ if (IS_TRACED_FL(p, F_TRACE_GC))
+ trace_gc(p, am_gc_max_heap_size, 0, msg);
+
+ erts_free(ERTS_ALC_T_TMP, o_hp);
+ }
+ /* returns true if we should kill the process */
+ return max_heap_flags & MAX_HEAP_SIZE_KILL;
+}
+
+Eterm
+erts_max_heap_size_map(Sint max_heap_size, Uint max_heap_flags,
+ Eterm **hpp, Uint *sz)
+{
+ if (!hpp) {
+ *sz += (2*3 + 1 + MAP_HEADER_FLATMAP_SZ);
+ return THE_NON_VALUE;
+ } else {
+ Eterm *hp = *hpp;
+ Eterm keys = TUPLE3(hp, am_error_logger, am_kill, am_size);
+ flatmap_t *mp;
+ hp += 4;
+ mp = (flatmap_t*) hp;
+ mp->thing_word = MAP_HEADER_FLATMAP;
+ mp->size = 3;
+ mp->keys = keys;
+ hp += MAP_HEADER_FLATMAP_SZ;
+ *hp++ = max_heap_flags & MAX_HEAP_SIZE_LOG ? am_true : am_false;
+ *hp++ = max_heap_flags & MAX_HEAP_SIZE_KILL ? am_true : am_false;
+ *hp++ = make_small(max_heap_size);
+ *hpp = hp;
+ return make_flatmap(mp);
+ }
+}
+
+int
+erts_max_heap_size(Eterm arg, Uint *max_heap_size, Uint *max_heap_flags)
+{
+ Sint sz;
+ *max_heap_flags = H_MAX_FLAGS;
+ if (is_small(arg)) {
+ sz = signed_val(arg);
+ *max_heap_flags = H_MAX_FLAGS;
+ } else if (is_map(arg)) {
+ const Eterm *size = erts_maps_get(am_size, arg);
+ const Eterm *kill = erts_maps_get(am_kill, arg);
+ const Eterm *log = erts_maps_get(am_error_logger, arg);
+ if (size && is_small(*size)) {
+ sz = signed_val(*size);
+ } else {
+ /* size is mandatory */
+ return 0;
+ }
+ if (kill) {
+ if (*kill == am_true)
+ *max_heap_flags |= MAX_HEAP_SIZE_KILL;
+ else if (*kill == am_false)
+ *max_heap_flags &= ~MAX_HEAP_SIZE_KILL;
+ else
+ return 0;
+ }
+ if (log) {
+ if (*log == am_true)
+ *max_heap_flags |= MAX_HEAP_SIZE_LOG;
+ else if (*log == am_false)
+ *max_heap_flags &= ~MAX_HEAP_SIZE_LOG;
+ else
+ return 0;
+ }
+ } else
+ return 0;
+ if (sz < 0)
+ return 0;
+ *max_heap_size = sz;
+ return 1;
+}
+
#if defined(DEBUG) || defined(ERTS_OFFHEAP_DEBUG)
static int
within2(Eterm *ptr, Process *p, Eterm *real_htop)
{
- ErlHeapFragment* bp = MBUF(p);
- ErlMessage* mp = p->msg.first;
- Eterm *htop = real_htop ? real_htop : HEAP_TOP(p);
+ ErlHeapFragment* bp;
+ ErtsMessage* mp;
+ Eterm *htop, *heap;
+
+ if (p->abandoned_heap) {
+ ERTS_GET_ORIG_HEAP(p, heap, htop);
+ if (heap <= ptr && ptr < htop)
+ return 1;
+ }
+
+ heap = p->heap;
+ htop = real_htop ? real_htop : HEAP_TOP(p);
if (OLD_HEAP(p) && (OLD_HEAP(p) <= ptr && ptr < OLD_HEND(p))) {
return 1;
}
- if (HEAP_START(p) <= ptr && ptr < htop) {
+ if (heap <= ptr && ptr < htop) {
return 1;
}
- while (bp != NULL) {
- if (bp->mem <= ptr && ptr < bp->mem + bp->used_size) {
- return 1;
- }
- bp = bp->next;
- }
+
+ mp = p->msg_frag;
+ bp = p->mbuf;
+
+ if (bp)
+ goto search_heap_frags;
+
while (mp) {
- if (mp->data.attached) {
- ErlHeapFragment *hfp;
- if (is_value(ERL_MESSAGE_TERM(mp)))
- hfp = mp->data.heap_frag;
- else if (is_not_nil(ERL_MESSAGE_TOKEN(mp)))
- hfp = erts_dist_ext_trailer(mp->data.dist_ext);
- else
- hfp = NULL;
- if (hfp && hfp->mem <= ptr && ptr < hfp->mem + hfp->used_size)
+
+ bp = erts_message_to_heap_frag(mp);
+ mp = mp->next;
+
+ search_heap_frags:
+
+ while (bp) {
+ if (bp->mem <= ptr && ptr < bp->mem + bp->used_size) {
return 1;
+ }
+ bp = bp->next;
}
- mp = mp->next;
}
+
return 0;
}
@@ -2793,11 +3414,11 @@ do { \
__FILE__, __LINE__, #EXP); \
} while (0)
+
#ifdef ERTS_OFFHEAP_DEBUG_CHK_CIRCULAR_LIST
# define ERTS_OFFHEAP_VISITED_BIT ((Eterm) 1 << 31)
#endif
-
void
erts_check_off_heap2(Process *p, Eterm *htop)
{
@@ -2814,19 +3435,19 @@ erts_check_off_heap2(Process *p, Eterm *htop)
refc = erts_refc_read(&u.pb->val->refc, 1);
break;
case FUN_SUBTAG:
- refc = erts_refc_read(&u.fun->fe->refc, 1);
+ refc = erts_smp_refc_read(&u.fun->fe->refc, 1);
break;
case EXTERNAL_PID_SUBTAG:
case EXTERNAL_PORT_SUBTAG:
case EXTERNAL_REF_SUBTAG:
- refc = erts_refc_read(&u.ext->node->refc, 1);
+ refc = erts_smp_refc_read(&u.ext->node->refc, 1);
break;
default:
ASSERT(!"erts_check_off_heap2: Invalid thing_word");
}
ERTS_CHK_OFFHEAP_ASSERT(refc >= 1);
#ifdef ERTS_OFFHEAP_DEBUG_CHK_CIRCULAR_LIST
- ERTS_CHK_OFFHEAP_ASSERT(!(u.hdr->thing_word & ERTS_EXTERNAL_VISITED_BIT));
+ ERTS_CHK_OFFHEAP_ASSERT(!(u.hdr->thing_word & ERTS_OFFHEAP_VISITED_BIT));
u.hdr->thing_word |= ERTS_OFFHEAP_VISITED_BIT;
#endif
if (old) {
@@ -2839,7 +3460,7 @@ erts_check_off_heap2(Process *p, Eterm *htop)
}
}
-#ifdef ERTS_OFFHEAP_DEBUG_CHK_CIRCULAR_EXTERNAL_LIST
+#ifdef ERTS_OFFHEAP_DEBUG_CHK_CIRCULAR_LIST
for (u.hdr = MSO(p).first; u.hdr; u.hdr = u.hdr->next)
u.hdr->thing_word &= ~ERTS_OFFHEAP_VISITED_BIT;
#endif
diff --git a/erts/emulator/beam/erl_gc.h b/erts/emulator/beam/erl_gc.h
index ecd1bf4d22..76dd74c866 100644
--- a/erts/emulator/beam/erl_gc.h
+++ b/erts/emulator/beam/erl_gc.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2007-2011. 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.
@@ -27,10 +27,6 @@
#include "erl_map.h"
-#if defined(DEBUG) && !ERTS_GLB_INLINE_INCL_FUNC_DEF
-# define HARDDEBUG 1
-#endif
-
#define IS_MOVED_BOXED(x) (!is_header((x)))
#define IS_MOVED_CONS(x) (is_non_value((x)))
@@ -69,17 +65,18 @@ do { \
while (nelts--) *HTOP++ = *PTR++; \
} while(0)
-#define in_area(ptr,start,nbytes) \
- ((UWord)((char*)(ptr) - (char*)(start)) < (nbytes))
-
#if defined(DEBUG) || defined(ERTS_OFFHEAP_DEBUG)
int within(Eterm *ptr, Process *p);
#endif
-ERTS_GLB_INLINE Eterm follow_moved(Eterm term);
+#define ErtsInYoungGen(TPtr, Ptr, OldHeap, OldHeapSz) \
+ (!erts_is_literal((TPtr), (Ptr)) \
+ & !ErtsInArea((Ptr), (OldHeap), (OldHeapSz)))
+
+ERTS_GLB_INLINE Eterm follow_moved(Eterm term, Eterm xptr_tag);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-ERTS_GLB_INLINE Eterm follow_moved(Eterm term)
+ERTS_GLB_INLINE Eterm follow_moved(Eterm term, Eterm xptr_tag)
{
Eterm* ptr;
switch (primary_tag(term)) {
@@ -87,17 +84,18 @@ ERTS_GLB_INLINE Eterm follow_moved(Eterm term)
break;
case TAG_PRIMARY_BOXED:
ptr = boxed_val(term);
- if (IS_MOVED_BOXED(*ptr)) term = *ptr;
+ if (IS_MOVED_BOXED(*ptr)) term = (*ptr) | xptr_tag;
break;
case TAG_PRIMARY_LIST:
ptr = list_val(term);
- if (IS_MOVED_CONS(ptr[0])) term = ptr[1];
+ if (IS_MOVED_CONS(ptr[0])) term = (ptr[1]) | xptr_tag;
break;
default:
ASSERT(!"strange tag in follow_moved");
}
return term;
}
+
#endif
#endif /* ERL_GC_C__ || HIPE_GC_C__ */
@@ -106,6 +104,23 @@ ERTS_GLB_INLINE Eterm follow_moved(Eterm term)
* Global exported
*/
+#define ERTS_IS_GC_DESIRED_INTERNAL(Proc, HTop, STop) \
+ ((((STop) - (HTop) < (Proc)->mbuf_sz)) \
+ | ((Proc)->off_heap.overhead > (Proc)->bin_vheap_sz) \
+ | !!((Proc)->flags & F_FORCE_GC))
+
+#define ERTS_IS_GC_DESIRED(Proc) \
+ ERTS_IS_GC_DESIRED_INTERNAL((Proc), (Proc)->htop, (Proc)->stop)
+
+#define ERTS_FORCE_GC_INTERNAL(Proc, FCalls) \
+ do { \
+ (Proc)->flags |= F_FORCE_GC; \
+ ERTS_VBUMP_ALL_REDS_INTERNAL((Proc), (FCalls)); \
+ } while (0)
+
+#define ERTS_FORCE_GC(Proc) \
+ ERTS_FORCE_GC_INTERNAL((Proc), (Proc)->fcalls)
+
extern Uint erts_test_long_gc_sleep;
typedef struct {
@@ -113,10 +128,18 @@ typedef struct {
Uint64 garbage_cols;
} ErtsGCInfo;
+#define ERTS_PROCESS_GC_INFO_MAX_TERMS (11) /* number of elements in process_gc_info*/
+#define ERTS_PROCESS_GC_INFO_MAX_SIZE \
+ (ERTS_PROCESS_GC_INFO_MAX_TERMS * (2/*cons*/ + 3/*2-tuple*/ + BIG_UINT_HEAP_SIZE))
+Eterm erts_process_gc_info(struct process*, Uint *, Eterm **, Uint, Uint);
+
void erts_gc_info(ErtsGCInfo *gcip);
void erts_init_gc(void);
-int erts_garbage_collect(struct process*, int, Eterm*, int);
+int erts_garbage_collect_nobump(struct process*, int, Eterm*, int, int);
+void erts_garbage_collect(struct process*, int, Eterm*, int);
void erts_garbage_collect_hibernate(struct process* p);
+Eterm erts_gc_after_bif_call_lhf(struct process* p, ErlHeapFragment *live_hf_end,
+ Eterm result, Eterm* regs, Uint arity);
Eterm erts_gc_after_bif_call(struct process* p, Eterm result, Eterm* regs, Uint arity);
void erts_garbage_collect_literals(struct process* p, Eterm* literals,
Uint lit_size,
@@ -128,5 +151,8 @@ void erts_offset_off_heap(struct erl_off_heap*, Sint, Eterm*, Eterm*);
void erts_offset_heap_ptr(Eterm*, Uint, Sint, Eterm*, Eterm*);
void erts_offset_heap(Eterm*, Uint, Sint, Eterm*, Eterm*);
void erts_free_heap_frags(struct process* p);
+Eterm erts_max_heap_size_map(Sint, Uint, Eterm **, Uint *);
+int erts_max_heap_size(Eterm, Uint *, Uint *);
+void erts_deallocate_young_generation(Process *c_p);
#endif /* __ERL_GC_H__ */
diff --git a/erts/emulator/beam/erl_goodfit_alloc.c b/erts/emulator/beam/erl_goodfit_alloc.c
index 9b4aad9d91..50aa41b4d2 100644
--- a/erts/emulator/beam/erl_goodfit_alloc.c
+++ b/erts/emulator/beam/erl_goodfit_alloc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2003-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -168,7 +168,7 @@ static Block_t * get_free_block (Allctr_t *, Uint,
static void link_free_block (Allctr_t *, Block_t *);
static void unlink_free_block (Allctr_t *, Block_t *);
static void update_last_aux_mbc (Allctr_t *, Carrier_t *);
-static Eterm info_options (Allctr_t *, char *, int *,
+static Eterm info_options (Allctr_t *, char *, fmtfn_t *,
void *, Uint **, Uint *);
static void init_atoms (void);
@@ -551,7 +551,7 @@ add_2tup(Uint **hpp, Uint *szp, Eterm *lp, Eterm el1, Eterm el2)
static Eterm
info_options(Allctr_t *allctr,
char *prefix,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
diff --git a/erts/emulator/beam/erl_goodfit_alloc.h b/erts/emulator/beam/erl_goodfit_alloc.h
index ababdbd0a1..76dd558234 100644
--- a/erts/emulator/beam/erl_goodfit_alloc.h
+++ b/erts/emulator/beam/erl_goodfit_alloc.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2003-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erl_hl_timer.c b/erts/emulator/beam/erl_hl_timer.c
index fb6d249145..a73f4ecfc7 100644
--- a/erts/emulator/beam/erl_hl_timer.c
+++ b/erts/emulator/beam/erl_hl_timer.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2015. 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.
@@ -735,7 +735,10 @@ proc_timeout_common(Process *proc, void *tmr)
if (tmr == (void *) erts_smp_atomic_cmpxchg_mb(&proc->common.timer,
ERTS_PTMR_TIMEDOUT,
(erts_aint_t) tmr)) {
- erts_aint32_t state = erts_smp_atomic32_read_acqb(&proc->state);
+ erts_aint32_t state;
+ erts_smp_proc_lock(proc, ERTS_PROC_LOCKS_MSG_RECEIVE);
+ state = erts_smp_atomic32_read_acqb(&proc->state);
+ erts_smp_proc_unlock(proc, ERTS_PROC_LOCKS_MSG_RECEIVE);
if (!(state & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_EXITING)))
erts_schedule_process(proc, state, 0);
return 1;
@@ -1245,8 +1248,10 @@ hlt_bif_timer_timeout(ErtsHLTimer *tmr, Uint32 roflgs)
* the middle of tree destruction).
*/
if (!ERTS_PROC_IS_EXITING(proc)) {
- erts_queue_message(proc, &proc_locks, tmr->btm.bp,
- tmr->btm.message, NIL);
+ ErtsMessage *mp = erts_alloc_message(0, NULL);
+ mp->data.heap_frag = tmr->btm.bp;
+ erts_queue_message(proc, proc_locks, mp,
+ tmr->btm.message, am_clock_service);
erts_smp_proc_unlock(proc, ERTS_PROC_LOCKS_MSG_SEND);
queued_message = 1;
proc_locks &= ~ERTS_PROC_LOCKS_MSG_SEND;
@@ -1764,7 +1769,7 @@ setup_bif_timer(Process *c_p, ErtsMonotonicTime timeout_pos,
if (is_not_internal_pid(rcvr) && is_not_atom(rcvr))
goto badarg;
- esdp = ERTS_PROC_GET_SCHDATA(c_p);
+ esdp = erts_proc_sched_data(c_p);
hp = HAlloc(c_p, REF_THING_SIZE);
ref = erts_sched_make_ref_in_buffer(esdp, hp);
@@ -1869,7 +1874,7 @@ access_sched_local_btm(Process *c_p, Eterm pid,
if (!c_p)
esdp = erts_get_scheduler_data();
else {
- esdp = ERTS_PROC_GET_SCHDATA(c_p);
+ esdp = erts_proc_sched_data(c_p);
ERTS_HLT_ASSERT(esdp == erts_get_scheduler_data());
}
@@ -1926,36 +1931,31 @@ access_sched_local_btm(Process *c_p, Eterm pid,
if (proc) {
Uint hsz;
- ErlOffHeap *ohp;
- ErlHeapFragment* bp;
+ ErtsMessage *mp;
Eterm *hp, msg, ref, result;
+ ErlOffHeap *ohp;
+ Uint32 *refn;
#ifdef ERTS_HLT_DEBUG
Eterm *hp_end;
#endif
- hsz = 3; /* 2-tuple */
- if (!async)
- hsz += REF_THING_SIZE;
+ hsz = REF_THING_SIZE;
+ if (async) {
+ refn = trefn; /* timer ref */
+ hsz += 4; /* 3-tuple */
+ }
else {
- if (is_non_value(tref) || proc != c_p)
- hsz += REF_THING_SIZE;
- hsz += 1; /* upgrade to 3-tuple */
+ refn = rrefn; /* request ref */
+ hsz += 3; /* 2-tuple */
}
+
+ ERTS_HLT_ASSERT(refn);
+
if (time_left > (Sint64) MAX_SMALL)
hsz += ERTS_SINT64_HEAP_SIZE(time_left);
- if (proc == c_p) {
- bp = NULL;
- ohp = NULL;
- hp = HAlloc(c_p, hsz);
- }
- else {
- hp = erts_alloc_message_heap(hsz,
- &bp,
- &ohp,
- proc,
- &proc_locks);
- }
+ mp = erts_alloc_message_heap(proc, &proc_locks,
+ hsz, &hp, &ohp);
#ifdef ERTS_HLT_DEBUG
hp_end = hp + hsz;
@@ -1968,35 +1968,22 @@ access_sched_local_btm(Process *c_p, Eterm pid,
else
result = erts_sint64_to_big(time_left, &hp);
- if (!async) {
- write_ref_thing(hp,
- rrefn[0],
- rrefn[1],
- rrefn[2]);
- ref = make_internal_ref(hp);
- hp += REF_THING_SIZE;
- msg = TUPLE2(hp, ref, result);
+ write_ref_thing(hp,
+ refn[0],
+ refn[1],
+ refn[2]);
+ ref = make_internal_ref(hp);
+ hp += REF_THING_SIZE;
- ERTS_HLT_ASSERT(hp + 3 == hp_end);
- }
- else {
- Eterm tag = cancel ? am_cancel_timer : am_read_timer;
- if (is_value(tref) && proc == c_p)
- ref = tref;
- else {
- write_ref_thing(hp,
- trefn[0],
- trefn[1],
- trefn[2]);
- ref = make_internal_ref(hp);
- hp += REF_THING_SIZE;
- }
- msg = TUPLE3(hp, tag, ref, result);
+ msg = (async
+ ? TUPLE3(hp, (cancel
+ ? am_cancel_timer
+ : am_read_timer), ref, result)
+ : TUPLE2(hp, ref, result));
- ERTS_HLT_ASSERT(hp + 4 == hp_end);
+ ERTS_HLT_ASSERT(hp + (async ? 4 : 3) == hp_end);
- }
- erts_queue_message(proc, &proc_locks, bp, msg, NIL);
+ erts_queue_message(proc, proc_locks, mp, msg, am_clock_service);
if (c_p)
proc_locks &= ~ERTS_PROC_LOCK_MAIN;
@@ -2034,7 +2021,7 @@ bif_timer_access_request(void *vreq)
static int
try_access_sched_remote_btm(ErtsSchedulerData *esdp,
Process *c_p, Uint32 sid,
- Eterm tref, Uint32 *trefn,
+ Uint32 *trefn,
int async, int cancel,
int info, Eterm *resp)
{
@@ -2093,21 +2080,31 @@ try_access_sched_remote_btm(ErtsSchedulerData *esdp,
}
}
else {
- Eterm tag, res, msg;
+ ErtsMessage *mp;
+ Eterm tag, res, msg, tref;
Uint hsz;
Eterm *hp;
ErtsProcLocks proc_locks = ERTS_PROC_LOCK_MAIN;
+ ErlOffHeap *ohp;
- hsz = 4;
+ hsz = 4 + REF_THING_SIZE;
if (time_left > (Sint64) MAX_SMALL)
hsz += ERTS_SINT64_HEAP_SIZE(time_left);
- hp = HAlloc(c_p, hsz);
+ mp = erts_alloc_message_heap(c_p, &proc_locks,
+ hsz, &hp, &ohp);
if (cancel)
tag = am_cancel_timer;
else
tag = am_read_timer;
+ write_ref_thing(hp,
+ trefn[0],
+ trefn[1],
+ trefn[2]);
+ tref = make_internal_ref(hp);
+ hp += REF_THING_SIZE;
+
if (time_left < 0)
res = am_false;
else if (time_left <= (Sint64) MAX_SMALL)
@@ -2117,7 +2114,7 @@ try_access_sched_remote_btm(ErtsSchedulerData *esdp,
msg = TUPLE3(hp, tag, tref, res);
- erts_queue_message(c_p, &proc_locks, NULL, msg, NIL);
+ erts_queue_message(c_p, proc_locks, mp, msg, am_clock_service);
proc_locks &= ~ERTS_PROC_LOCK_MAIN;
if (proc_locks)
@@ -2144,7 +2141,7 @@ access_bif_timer(Process *c_p, Eterm tref, int cancel, int async, int info)
goto no_timer;
}
- esdp = ERTS_PROC_GET_SCHDATA(c_p);
+ esdp = erts_proc_sched_data(c_p);
trefn = internal_ref_numbers(tref);
sid = erts_get_ref_numbers_thr_id(trefn);
@@ -2158,8 +2155,8 @@ access_bif_timer(Process *c_p, Eterm tref, int cancel, int async, int info)
info);
ERTS_BIF_PREP_RET(ret, res);
}
- else if (try_access_sched_remote_btm(esdp, c_p, sid,
- tref, trefn,
+ else if (try_access_sched_remote_btm(esdp, c_p,
+ sid, trefn,
async, cancel,
info, &res)) {
ERTS_BIF_PREP_RET(ret, res);
@@ -2369,7 +2366,7 @@ typedef struct {
int erts_cancel_bif_timers(Process *p, ErtsBifTimers *btm, void **vyspp)
{
- ErtsSchedulerData *esdp = ERTS_PROC_GET_SCHDATA(p);
+ ErtsSchedulerData *esdp = erts_proc_sched_data(p);
ErtsBifTimerYieldState ys = {btm, {ERTS_RBT_YIELD_STAT_INITER}};
ErtsBifTimerYieldState *ysp;
int res;
@@ -2415,7 +2412,7 @@ detach_bif_timer(ErtsHLTimer *tmr, void *vesdp)
int erts_detach_accessor_bif_timers(Process *p, ErtsBifTimers *btm, void **vyspp)
{
- ErtsSchedulerData *esdp = ERTS_PROC_GET_SCHDATA(p);
+ ErtsSchedulerData *esdp = erts_proc_sched_data(p);
ErtsBifTimerYieldState ys = {btm, {ERTS_RBT_YIELD_STAT_INITER}};
ErtsBifTimerYieldState *ysp;
int res;
@@ -2522,7 +2519,7 @@ BIF_RETTYPE send_after_3(BIF_ALIST_3)
ErtsMonotonicTime timeout_pos;
int short_time, tres;
- tres = parse_timeout_pos(ERTS_PROC_GET_SCHDATA(BIF_P), BIF_ARG_1, NULL,
+ tres = parse_timeout_pos(erts_proc_sched_data(BIF_P), BIF_ARG_1, NULL,
0, &timeout_pos, &short_time);
if (tres != 0)
BIF_ERROR(BIF_P, BADARG);
@@ -2540,7 +2537,7 @@ BIF_RETTYPE send_after_4(BIF_ALIST_4)
if (!parse_bif_timer_options(BIF_ARG_4, NULL, NULL, &abs, &accessor))
BIF_ERROR(BIF_P, BADARG);
- tres = parse_timeout_pos(ERTS_PROC_GET_SCHDATA(BIF_P), BIF_ARG_1, NULL,
+ tres = parse_timeout_pos(erts_proc_sched_data(BIF_P), BIF_ARG_1, NULL,
abs, &timeout_pos, &short_time);
if (tres != 0)
BIF_ERROR(BIF_P, BADARG);
@@ -2554,7 +2551,7 @@ BIF_RETTYPE start_timer_3(BIF_ALIST_3)
ErtsMonotonicTime timeout_pos;
int short_time, tres;
- tres = parse_timeout_pos(ERTS_PROC_GET_SCHDATA(BIF_P), BIF_ARG_1, NULL,
+ tres = parse_timeout_pos(erts_proc_sched_data(BIF_P), BIF_ARG_1, NULL,
0, &timeout_pos, &short_time);
if (tres != 0)
BIF_ERROR(BIF_P, BADARG);
@@ -2572,7 +2569,7 @@ BIF_RETTYPE start_timer_4(BIF_ALIST_4)
if (!parse_bif_timer_options(BIF_ARG_4, NULL, NULL, &abs, &accessor))
BIF_ERROR(BIF_P, BADARG);
- tres = parse_timeout_pos(ERTS_PROC_GET_SCHDATA(BIF_P), BIF_ARG_1, NULL,
+ tres = parse_timeout_pos(erts_proc_sched_data(BIF_P), BIF_ARG_1, NULL,
abs, &timeout_pos, &short_time);
if (tres != 0)
BIF_ERROR(BIF_P, BADARG);
@@ -2669,7 +2666,7 @@ erts_start_timer_callback(ErtsMonotonicTime tmo,
tmo);
twt = tmo < ERTS_TIMER_WHEEL_MSEC;
- if (esdp)
+ if (esdp && !ERTS_SCHEDULER_IS_DIRTY(esdp))
start_callback_timer(esdp,
twt,
timeout_pos,
@@ -2726,7 +2723,7 @@ set_proc_timer_common(Process *c_p, ErtsSchedulerData *esdp, Sint64 tmo,
int
erts_set_proc_timer_term(Process *c_p, Eterm etmo)
{
- ErtsSchedulerData *esdp = ERTS_PROC_GET_SCHDATA(c_p);
+ ErtsSchedulerData *esdp = erts_proc_sched_data(c_p);
ErtsMonotonicTime tmo, timeout_pos;
int short_time, tres;
@@ -2748,7 +2745,7 @@ erts_set_proc_timer_term(Process *c_p, Eterm etmo)
void
erts_set_proc_timer_uword(Process *c_p, UWord tmo)
{
- ErtsSchedulerData *esdp = ERTS_PROC_GET_SCHDATA(c_p);
+ ErtsSchedulerData *esdp = erts_proc_sched_data(c_p);
ERTS_HLT_ASSERT(erts_smp_atomic_read_nob(&c_p->common.timer)
== ERTS_PTMR_NONE);
@@ -2782,7 +2779,7 @@ erts_cancel_proc_timer(Process *c_p)
erts_smp_atomic_set_nob(&c_p->common.timer, ERTS_PTMR_NONE);
return;
}
- continue_cancel_ptimer(ERTS_PROC_GET_SCHDATA(c_p),
+ continue_cancel_ptimer(erts_proc_sched_data(c_p),
(ErtsTimer *) tval);
}
@@ -2854,7 +2851,7 @@ erts_read_port_timer(Port *c_prt)
*/
typedef struct {
- int to;
+ fmtfn_t to;
void *to_arg;
ErtsMonotonicTime now;
} ErtsBTMPrint;
@@ -2868,7 +2865,8 @@ btm_print(ErtsHLTimer *tmr, void *vbtmp)
if (tmr->timeout <= btmp->now)
left = 0;
- left = ERTS_CLKTCKS_TO_MSEC(tmr->timeout - btmp->now);
+ else
+ left = ERTS_CLKTCKS_TO_MSEC(tmr->timeout - btmp->now);
receiver = ((tmr->head.roflgs & ERTS_TMR_ROFLG_REG_NAME)
? tmr->receiver.name
@@ -2884,7 +2882,7 @@ btm_print(ErtsHLTimer *tmr, void *vbtmp)
}
void
-erts_print_bif_timer_info(int to, void *to_arg)
+erts_print_bif_timer_info(fmtfn_t to, void *to_arg)
{
ErtsBTMPrint btmp;
int six;
diff --git a/erts/emulator/beam/erl_hl_timer.h b/erts/emulator/beam/erl_hl_timer.h
index 0931bb8965..9cdcd581a0 100644
--- a/erts/emulator/beam/erl_hl_timer.h
+++ b/erts/emulator/beam/erl_hl_timer.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2015. All Rights Reserved.
+ * Copyright Ericsson AB 2015-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -72,7 +72,7 @@ erts_handle_canceled_timers(void *vesdp,
#endif
Uint erts_bif_timer_memory_size(void);
-void erts_print_bif_timer_info(int to, void *to_arg);
+void erts_print_bif_timer_info(fmtfn_t to, void *to_arg);
void erts_debug_bif_timer_foreach(void (*func)(Eterm,
Eterm,
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index e729574ec7..e8b8739852 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2013. 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.
@@ -91,11 +91,6 @@ const int etp_arch_bits = 32;
#else
# error "Not 64-bit, nor 32-bit arch"
#endif
-#if HALFWORD_HEAP
-const int etp_halfword = 1;
-#else
-const int etp_halfword = 0;
-#endif
#ifdef HIPE
const int etp_hipe = 1;
#else
@@ -122,6 +117,11 @@ const int etp_big_endian = 1;
const int etp_big_endian = 0;
#endif
const Eterm etp_the_non_value = THE_NON_VALUE;
+#ifdef ERTS_HOLE_MARKER
+const Eterm etp_hole_marker = ERTS_HOLE_MARKER;
+#else
+const Eterm etp_hole_marker = 0;
+#endif
/*
* Note about VxWorks: All variables must be initialized by executable code,
@@ -157,7 +157,7 @@ volatile int erts_writing_erl_crash_dump = 0;
int erts_initialized = 0;
#if defined(USE_THREADS) && !defined(ERTS_SMP)
-static erts_tid_t main_thread;
+erts_tid_t erts_main_thread;
#endif
int erts_use_sender_punish;
@@ -169,6 +169,8 @@ int erts_use_sender_punish;
Uint display_items; /* no of items to display in traces etc */
int H_MIN_SIZE; /* The minimum heap grain */
int BIN_VH_MIN_SIZE; /* The minimum binary virtual*/
+int H_MAX_SIZE; /* The maximum heap size */
+int H_MAX_FLAGS; /* The maximum heap flags */
Uint32 erts_debug_flags; /* Debug flags. */
int erts_backtrace_depth; /* How many functions to show in a backtrace
@@ -197,7 +199,7 @@ Uint32 verbose; /* See erl_debug.h for information about verbose */
int erts_atom_table_size = ATOM_LIMIT; /* Maximum number of atoms */
-int erts_pd_initial_size = 10;
+int erts_pd_initial_size = 8; /* must be power of 2 */
int erts_modified_timing_level;
@@ -211,15 +213,15 @@ int erts_no_line_info = 0; /* -L: Don't load line information */
ErtsModifiedTimings erts_modified_timings[] = {
/* 0 */ {make_small(0), CONTEXT_REDS, INPUT_REDUCTIONS},
- /* 1 */ {make_small(0), 2*CONTEXT_REDS, 2*INPUT_REDUCTIONS},
+ /* 1 */ {make_small(0), (3*CONTEXT_REDS)/4, 2*INPUT_REDUCTIONS},
/* 2 */ {make_small(0), CONTEXT_REDS/2, INPUT_REDUCTIONS/2},
- /* 3 */ {make_small(0), 3*CONTEXT_REDS, 3*INPUT_REDUCTIONS},
+ /* 3 */ {make_small(0), (7*CONTEXT_REDS)/8, 3*INPUT_REDUCTIONS},
/* 4 */ {make_small(0), CONTEXT_REDS/3, 3*INPUT_REDUCTIONS},
- /* 5 */ {make_small(0), 4*CONTEXT_REDS, INPUT_REDUCTIONS/2},
+ /* 5 */ {make_small(0), (10*CONTEXT_REDS)/11, INPUT_REDUCTIONS/2},
/* 6 */ {make_small(1), CONTEXT_REDS/4, 2*INPUT_REDUCTIONS},
- /* 7 */ {make_small(1), 5*CONTEXT_REDS, INPUT_REDUCTIONS/3},
+ /* 7 */ {make_small(1), (5*CONTEXT_REDS)/7, INPUT_REDUCTIONS/3},
/* 8 */ {make_small(10), CONTEXT_REDS/5, 3*INPUT_REDUCTIONS},
- /* 9 */ {make_small(10), 6*CONTEXT_REDS, INPUT_REDUCTIONS/4}
+ /* 9 */ {make_small(10), (6*CONTEXT_REDS)/7, INPUT_REDUCTIONS/4}
};
#define ERTS_MODIFIED_TIMING_LEVELS \
@@ -335,8 +337,6 @@ erl_init(int ncpu,
int node_tab_delete_delay,
ErtsDbSpinCount db_spin_count)
{
- init_benchmarking();
-
erts_bif_unique_init();
erts_init_monitors();
erts_init_time(time_correction, time_warp_mode);
@@ -387,17 +387,20 @@ erl_init(int ncpu,
erts_init_unicode(); /* after RE to get access to PCRE unicode */
erts_init_external();
erts_init_map();
+ erts_beam_bif_load_init();
erts_delay_trap = erts_export_put(am_erlang, am_delay_trap, 2);
erts_late_init_process();
#if HAVE_ERTS_MSEG
erts_mseg_late_init(); /* Must be after timer (erts_init_time()) and thread
initializations */
#endif
+ erl_sys_late_init();
#ifdef HIPE
hipe_mode_switch_init(); /* Must be after init_load/beam_catches/init */
#endif
packet_parser_init();
erl_nif_init();
+ erts_msacc_init();
}
static Eterm
@@ -436,13 +439,36 @@ erl_first_process_otp(char* modname, void* code, unsigned size, int argc, char**
hp += 2;
args = CONS(hp, env, args);
- so.flags = SPO_SYSTEM_PROC;
+ so.flags = erts_default_spo_flags|SPO_SYSTEM_PROC;
res = erl_create_process(&parent, start_mod, am_start, args, &so);
erts_smp_proc_unlock(&parent, ERTS_PROC_LOCK_MAIN);
erts_cleanup_empty_process(&parent);
return res;
}
+static Eterm
+erl_system_process_otp(Eterm parent_pid, char* modname)
+{
+ Eterm start_mod;
+ Process* parent;
+ ErlSpawnOpts so;
+ Eterm res;
+
+ start_mod = erts_atom_put((byte *) modname, sys_strlen(modname), ERTS_ATOM_ENC_LATIN1, 1);
+ if (erts_find_function(start_mod, am_start, 0,
+ erts_active_code_ix()) == NULL) {
+ erts_exit(ERTS_ERROR_EXIT, "No function %s:start/0\n", modname);
+ }
+
+ parent = erts_pid2proc(NULL, 0, parent_pid, ERTS_PROC_LOCK_MAIN);
+
+ so.flags = erts_default_spo_flags|SPO_SYSTEM_PROC;
+ res = erl_create_process(parent, start_mod, am_start, NIL, &so);
+ erts_smp_proc_unlock(parent, ERTS_PROC_LOCK_MAIN);
+ return res;
+}
+
+
Eterm
erts_preloaded(Process* p)
{
@@ -556,8 +582,14 @@ void erts_usage(void)
H_DEFAULT_SIZE);
erts_fprintf(stderr, "-hmbs size set minimum binary virtual heap size in words (default %d)\n",
VH_DEFAULT_SIZE);
+ erts_fprintf(stderr, "-hmax size set maximum heap size in words (default %d)\n",
+ H_DEFAULT_MAX_SIZE);
+ erts_fprintf(stderr, "-hmaxk bool enable or disable kill at max heap size (default true)\n");
+ erts_fprintf(stderr, "-hmaxel bool enable or disable error_logger report at max heap size (default true)\n");
erts_fprintf(stderr, "-hpds size initial process dictionary size (default %d)\n",
erts_pd_initial_size);
+ erts_fprintf(stderr, "-hmqd val set default message queue data flag for processes,\n");
+ erts_fprintf(stderr, " valid values are: off_heap | on_heap\n");
/* erts_fprintf(stderr, "-i module set the boot module (default init)\n"); */
@@ -723,6 +755,10 @@ early_init(int *argc, char **argv) /*
char envbuf[21]; /* enough for any 64-bit integer */
size_t envbufsz;
+#if defined(USE_THREADS) && !defined(ERTS_SMP)
+ erts_main_thread = erts_thr_self();
+#endif
+
erts_save_emu_args(*argc, argv);
erts_sched_compact_load = 1;
@@ -733,6 +769,8 @@ early_init(int *argc, char **argv) /*
erts_async_thread_suggested_stack_size = ERTS_ASYNC_THREAD_MIN_STACK_SIZE;
H_MIN_SIZE = H_DEFAULT_SIZE;
BIN_VH_MIN_SIZE = VH_DEFAULT_SIZE;
+ H_MAX_SIZE = H_DEFAULT_MAX_SIZE;
+ H_MAX_FLAGS = MAX_HEAP_SIZE_KILL|MAX_HEAP_SIZE_LOG;
erts_initialized = 0;
@@ -762,9 +800,6 @@ early_init(int *argc, char **argv) /*
erts_thr_progress_pre_init();
#endif
-#ifdef ERTS_ENABLE_LOCK_CHECK
- erts_lc_init();
-#endif
#ifdef ERTS_SMP
erts_smp_atomic32_init_nob(&erts_writing_erl_crash_dump, 0L);
erts_tsd_key_create(&erts_is_crash_dumping_key,"erts_is_crash_dumping_key");
@@ -776,9 +811,6 @@ early_init(int *argc, char **argv) /*
(erts_aint32_t) ((Uint16) -1));
erts_pre_init_process();
-#if defined(USE_THREADS) && !defined(ERTS_SMP)
- main_thread = erts_thr_self();
-#endif
/*
* We need to know the number of schedulers to use before we
@@ -1172,6 +1204,7 @@ early_init(int *argc, char **argv) /*
erts_thr_late_init(&elid);
}
#endif
+ erts_msacc_early_init();
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_lc_late_init();
@@ -1236,6 +1269,7 @@ erl_start(int argc, char **argv)
ErtsTimeWarpMode time_warp_mode;
int node_tab_delete_delay = ERTS_NODE_TAB_DELAY_GC_DEFAULT;
ErtsDbSpinCount db_spin_count = ERTS_DB_SPNCNT_NORMAL;
+ Eterm otp_ring0_pid;
set_default_time_adj(&time_correction,
&time_warp_mode);
@@ -1405,6 +1439,7 @@ erl_start(int argc, char **argv)
case 't': verbose |= DEBUG_THREADS; break;
case 'p': verbose |= DEBUG_PROCESSES; break;
case 'm': verbose |= DEBUG_MESSAGES; break;
+ case 'c': verbose |= DEBUG_SHCOPY; break;
default : erts_fprintf(stderr,"Unknown verbose option: %c\n",*ch);
}
}
@@ -1417,6 +1452,7 @@ erl_start(int argc, char **argv)
if (verbose & DEBUG_THREADS) erts_printf("THREADS ");
if (verbose & DEBUG_PROCESSES) erts_printf("PROCESSES ");
if (verbose & DEBUG_MESSAGES) erts_printf("MESSAGES ");
+ if (verbose & DEBUG_SHCOPY) erts_printf("SHCOPY ");
erts_printf("\n");
#else
erts_fprintf(stderr, "warning: -v (only in debug compiled code)\n");
@@ -1457,9 +1493,13 @@ erl_start(int argc, char **argv)
char *sub_param = argv[i]+2;
/* set default heap size
*
- * h|ms - min_heap_size
- * h|mbs - min_bin_vheap_size
- * h|pds - erts_pd_initial_size
+ * h|ms - min_heap_size
+ * h|mbs - min_bin_vheap_size
+ * h|pds - erts_pd_initial_size
+ * h|mqd - message_queue_data
+ * h|max - max_heap_size
+ * h|maxk - max_heap_kill
+ * h|maxel - max_heap_error_logger
*
*/
if (has_prefix("mbs", sub_param)) {
@@ -1479,12 +1519,62 @@ erl_start(int argc, char **argv)
VERBOSE(DEBUG_SYSTEM, ("using minimum heap size %d\n", H_MIN_SIZE));
} else if (has_prefix("pds", sub_param)) {
arg = get_arg(sub_param+3, argv[i+1], &i);
- if ((erts_pd_initial_size = atoi(arg)) <= 0) {
+ if (!erts_pd_set_initial_size(atoi(arg))) {
erts_fprintf(stderr, "bad initial process dictionary size %s\n", arg);
erts_usage();
}
VERBOSE(DEBUG_SYSTEM, ("using initial process dictionary size %d\n",
erts_pd_initial_size));
+ } else if (has_prefix("mqd", sub_param)) {
+ arg = get_arg(sub_param+3, argv[i+1], &i);
+ if (sys_strcmp(arg, "on_heap") == 0) {
+ erts_default_spo_flags &= ~SPO_OFF_HEAP_MSGQ;
+ erts_default_spo_flags |= SPO_ON_HEAP_MSGQ;
+ }
+ else if (sys_strcmp(arg, "off_heap") == 0) {
+ erts_default_spo_flags &= ~SPO_ON_HEAP_MSGQ;
+ erts_default_spo_flags |= SPO_OFF_HEAP_MSGQ;
+ }
+ else {
+ erts_fprintf(stderr,
+ "Invalid message_queue_data flag: %s\n", arg);
+ erts_usage();
+ }
+ } else if (has_prefix("maxk", sub_param)) {
+ arg = get_arg(sub_param+4, argv[i+1], &i);
+ if (strcmp(arg,"true") == 0) {
+ H_MAX_FLAGS |= MAX_HEAP_SIZE_KILL;
+ } else if (strcmp(arg,"false") == 0) {
+ H_MAX_FLAGS &= ~MAX_HEAP_SIZE_KILL;
+ } else {
+ erts_fprintf(stderr, "bad max heap kill %s\n", arg);
+ erts_usage();
+ }
+ VERBOSE(DEBUG_SYSTEM, ("using max heap kill %d\n", H_MAX_FLAGS));
+ } else if (has_prefix("maxel", sub_param)) {
+ arg = get_arg(sub_param+5, argv[i+1], &i);
+ if (strcmp(arg,"true") == 0) {
+ H_MAX_FLAGS |= MAX_HEAP_SIZE_LOG;
+ } else if (strcmp(arg,"false") == 0) {
+ H_MAX_FLAGS &= ~MAX_HEAP_SIZE_LOG;
+ } else {
+ erts_fprintf(stderr, "bad max heap error logger %s\n", arg);
+ erts_usage();
+ }
+ VERBOSE(DEBUG_SYSTEM, ("using max heap log %d\n", H_MAX_FLAGS));
+ } else if (has_prefix("max", sub_param)) {
+ arg = get_arg(sub_param+3, argv[i+1], &i);
+ if ((H_MAX_SIZE = atoi(arg)) < 0) {
+ erts_fprintf(stderr, "bad max heap size %s\n", arg);
+ erts_usage();
+ }
+ if (H_MAX_SIZE < H_MIN_SIZE && H_MAX_SIZE) {
+ erts_fprintf(stderr, "max heap size (%s) is not allowed to be "
+ "smaller than min heap size (%d)\n",
+ arg, H_MIN_SIZE);
+ erts_usage();
+ }
+ VERBOSE(DEBUG_SYSTEM, ("using max heap size %d\n", H_MAX_SIZE));
} else {
/* backward compatibility */
arg = get_arg(argv[i]+2, argv[i+1], &i);
@@ -2073,7 +2163,8 @@ erl_start(int argc, char **argv)
"Invalid ets busy wait threshold: %s\n", arg);
erts_usage();
}
- } else {
+ }
+ else {
erts_fprintf(stderr, "bad -z option %s\n", argv[i]);
erts_usage();
}
@@ -2156,8 +2247,45 @@ erl_start(int argc, char **argv)
erts_initialized = 1;
- (void) erl_first_process_otp("otp_ring0", NULL, 0,
- boot_argc, boot_argv);
+ otp_ring0_pid = erl_first_process_otp("otp_ring0", NULL, 0,
+ boot_argc, boot_argv);
+
+ {
+ /*
+ * The erts_code_purger and the erts_literal_area_collector
+ * system processes are *always* alive. If they terminate
+ * they bring the whole VM down.
+ */
+ Eterm pid;
+
+ pid = erl_system_process_otp(otp_ring0_pid, "erts_code_purger");
+ erts_code_purger
+ = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc,
+ internal_pid_index(pid));
+ ASSERT(erts_code_purger && erts_code_purger->common.id == pid);
+ erts_proc_inc_refc(erts_code_purger);
+
+#ifdef ERTS_NEW_PURGE_STRATEGY
+ pid = erl_system_process_otp(otp_ring0_pid, "erts_literal_area_collector");
+ erts_literal_area_collector
+ = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc,
+ internal_pid_index(pid));
+ ASSERT(erts_literal_area_collector
+ && erts_literal_area_collector->common.id == pid);
+ erts_proc_inc_refc(erts_literal_area_collector);
+#endif
+
+#ifdef ERTS_DIRTY_SCHEDULERS
+ pid = erl_system_process_otp(otp_ring0_pid, "erts_dirty_process_code_checker");
+ erts_dirty_process_code_checker
+ = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc,
+ internal_pid_index(pid));
+ ASSERT(erts_dirty_process_code_checker
+ && erts_dirty_process_code_checker->common.id == pid);
+ erts_proc_inc_refc(erts_dirty_process_code_checker);
+#endif
+
+ }
#ifdef ERTS_SMP
erts_start_schedulers();
@@ -2167,6 +2295,7 @@ erl_start(int argc, char **argv)
#else
{
ErtsSchedulerData *esdp = erts_get_scheduler_data();
+ erts_msacc_init_thread("scheduler", 1, 1);
erts_thr_set_main_status(1, 1);
#if ERTS_USE_ASYNC_READY_Q
esdp->aux_work_data.async_ready.queue
@@ -2232,7 +2361,7 @@ system_cleanup(int flush_async)
if (!flush_async
|| !erts_initialized
#if defined(USE_THREADS) && !defined(ERTS_SMP)
- || !erts_equal_tids(main_thread, erts_thr_self())
+ || !erts_equal_tids(erts_main_thread, erts_thr_self())
#endif
)
return;
@@ -2246,24 +2375,34 @@ system_cleanup(int flush_async)
erts_exit_flush_async();
}
+static int erts_exit_code;
+
static __decl_noreturn void __noreturn
erts_exit_vv(int n, int flush_async, char *fmt, va_list args1, va_list args2)
{
system_cleanup(flush_async);
- save_statistics();
-
if (erts_mtrace_enabled)
erts_mtrace_exit((Uint32) n);
- /* Produce an Erlang core dump if error */
+ if (fmt != NULL && *fmt != '\0')
+ erl_error(fmt, args2); /* Print error message. */
+
+ erts_exit_code = n;
+
+ /* Produce an Erlang crash dump if error */
if (((n == ERTS_ERROR_EXIT && erts_no_crash_dump == 0) || n == ERTS_DUMP_EXIT)
&& erts_initialized) {
erl_crash_dump_v((char*) NULL, 0, fmt, args1);
}
- if (fmt != NULL && *fmt != '\0')
- erl_error(fmt, args2); /* Print error message. */
+ erts_exit_epilogue();
+}
+
+__decl_noreturn void __noreturn erts_exit_epilogue(void)
+{
+ int n = erts_exit_code;
+
sys_tty_reset(n);
if (n == ERTS_INTR_EXIT)
diff --git a/erts/emulator/beam/erl_instrument.c b/erts/emulator/beam/erl_instrument.c
index 12a72ad839..4d4defd8b5 100644
--- a/erts/emulator/beam/erl_instrument.c
+++ b/erts/emulator/beam/erl_instrument.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2003-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -539,7 +539,7 @@ map_stat_free(ErtsAlcType_t n, void *extra, void *ptr)
}
-static void dump_memory_map_to_stream(FILE *fp)
+static void dump_memory_map_to_stream(fmtfn_t to, void* to_arg)
{
ErtsAlcType_t n;
MapStatBlock_t *bp;
@@ -551,7 +551,7 @@ static void dump_memory_map_to_stream(FILE *fp)
/* Write header */
- fprintf(fp,
+ erts_cbprintf(to, to_arg,
"{instr_hdr,\n"
" %lu,\n"
" %lu,\n"
@@ -574,7 +574,7 @@ static void dump_memory_map_to_stream(FILE *fp)
else
astr = ERTS_ALC_A2AD(ERTS_ALC_A_SYSTEM);
- fprintf(fp,
+ erts_cbprintf(to, to_arg,
"%s{%s,%s,%s}%s",
(n == ERTS_ALC_N_MIN) ? "" : " ",
ERTS_ALC_N2TD(n),
@@ -583,12 +583,12 @@ static void dump_memory_map_to_stream(FILE *fp)
(n == ERTS_ALC_N_MAX) ? "" : ",\n");
}
- fprintf(fp, "}}.\n");
+ erts_cbprintf(to, to_arg, "}}.\n");
/* Write memory data */
for (bp = mem_anchor; bp; bp = bp->next) {
if (is_internal_pid(bp->pid))
- fprintf(fp,
+ erts_cbprintf(to, to_arg,
"{%lu, %lu, %lu, {%lu,%lu,%lu}}.\n",
(UWord) bp->type_no,
(UWord) bp->mem,
@@ -597,7 +597,7 @@ static void dump_memory_map_to_stream(FILE *fp)
(UWord) pid_number(bp->pid),
(UWord) pid_serial(bp->pid));
else
- fprintf(fp,
+ erts_cbprintf(to, to_arg,
"{%lu, %lu, %lu, undefined}.\n",
(UWord) bp->type_no,
(UWord) bp->mem,
@@ -608,40 +608,29 @@ static void dump_memory_map_to_stream(FILE *fp)
erts_mtx_unlock(&instr_mutex);
}
-int erts_instr_dump_memory_map_to_fd(int fd)
+int erts_instr_dump_memory_map_to(fmtfn_t to, void* to_arg)
{
- char buf[BUFSIZ];
- FILE *f;
-
if (!erts_instr_memory_map)
return 0;
- f = fdopen(fd, "w");
- if (f == NULL)
- return 0;
-
- /* Avoid allocating memory; we may have run out of it at this point. */
- setbuf(f, buf);
-
- dump_memory_map_to_stream(f);
- fflush(f);
+ dump_memory_map_to_stream(to, to_arg);
return 1;
}
int erts_instr_dump_memory_map(const char *name)
{
- FILE *f;
+ int fd;
if (!erts_instr_memory_map)
return 0;
- f = fopen(name, "w");
- if (f == NULL)
+ fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0640);
+ if (fd < 0)
return 0;
- dump_memory_map_to_stream(f);
+ dump_memory_map_to_stream(erts_write_fd, (void*)&fd);
- fclose(f);
+ close(fd);
return 1;
}
@@ -998,19 +987,19 @@ erts_instr_get_stat(Process *proc, Eterm what, int begin_max_period)
}
static void
-dump_stat_to_stream(FILE *fp, int begin_max_period)
+dump_stat_to_stream(fmtfn_t to, void* to_arg, int begin_max_period)
{
ErtsAlcType_t i, a_max, a_min;
erts_mtx_lock(&instr_mutex);
- fprintf(fp,
+ erts_cbprintf(to, to_arg,
"{instr_vsn,%lu}.\n",
(unsigned long) ERTS_INSTR_VSN);
update_max_ever_values(&stats->tot, 0, 0);
- fprintf(fp,
+ erts_cbprintf(to, to_arg,
"{total,[{total,[{sizes,%lu,%lu,%lu},{blocks,%lu,%lu,%lu}]}]}.\n",
(UWord) stats->tot.size,
(UWord) stats->tot.max_size,
@@ -1038,7 +1027,7 @@ dump_stat_to_stream(FILE *fp, int begin_max_period)
for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) {
if (erts_allctrs_info[i].enabled) {
- fprintf(fp,
+ erts_cbprintf(to, to_arg,
"%s{%s,[{sizes,%lu,%lu,%lu},{blocks,%lu,%lu,%lu}]}%s",
i == a_min ? "{allocators,\n [" : " ",
ERTS_ALC_A2AD(i),
@@ -1055,7 +1044,7 @@ dump_stat_to_stream(FILE *fp, int begin_max_period)
update_max_ever_values(stats->c, ERTS_ALC_C_MIN, ERTS_ALC_C_MAX);
for (i = ERTS_ALC_C_MIN; i <= ERTS_ALC_C_MAX; i++) {
- fprintf(fp,
+ erts_cbprintf(to, to_arg,
"%s{%s,[{sizes,%lu,%lu,%lu},{blocks,%lu,%lu,%lu}]}%s",
i == ERTS_ALC_C_MIN ? "{classes,\n [" : " ",
ERTS_ALC_C2CD(i),
@@ -1071,7 +1060,7 @@ dump_stat_to_stream(FILE *fp, int begin_max_period)
update_max_ever_values(stats->n, ERTS_ALC_N_MIN, ERTS_ALC_N_MAX);
for (i = ERTS_ALC_N_MIN; i <= ERTS_ALC_N_MAX; i++) {
- fprintf(fp,
+ erts_cbprintf(to, to_arg,
"%s{%s,[{sizes,%lu,%lu,%lu},{blocks,%lu,%lu,%lu}]}%s",
i == ERTS_ALC_N_MIN ? "{types,\n [" : " ",
ERTS_ALC_N2TD(i),
@@ -1095,40 +1084,29 @@ dump_stat_to_stream(FILE *fp, int begin_max_period)
}
-int erts_instr_dump_stat_to_fd(int fd, int begin_max_period)
+int erts_instr_dump_stat_to(fmtfn_t to, void* to_arg, int begin_max_period)
{
- char buf[BUFSIZ];
- FILE *fp;
-
if (!erts_instr_stat)
return 0;
- fp = fdopen(fd, "w");
- if (fp == NULL)
- return 0;
-
- /* Avoid allocating memory; we may have run out of it at this point. */
- setbuf(fp, buf);
-
- dump_stat_to_stream(fp, begin_max_period);
- fflush(fp);
+ dump_stat_to_stream(to, to_arg, begin_max_period);
return 1;
}
int erts_instr_dump_stat(const char *name, int begin_max_period)
{
- FILE *file;
+ int fd;
if (!erts_instr_stat)
return 0;
- file = fopen(name, "w");
- if (file == NULL)
+ fd = open(name, O_WRONLY | O_CREAT | O_TRUNC,0640);
+ if (fd < 0)
return 0;
- dump_stat_to_stream(file, begin_max_period);
+ dump_stat_to_stream(erts_write_fd, (void*)&fd, begin_max_period);
- fclose(file);
+ close(fd);
return 1;
}
diff --git a/erts/emulator/beam/erl_instrument.h b/erts/emulator/beam/erl_instrument.h
index cb3b1920d3..351172b2fa 100644
--- a/erts/emulator/beam/erl_instrument.h
+++ b/erts/emulator/beam/erl_instrument.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2003-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,10 +29,10 @@ extern int erts_instr_memory_map;
extern int erts_instr_stat;
Uint erts_instr_init(int stat, int map_stat);
-int erts_instr_dump_memory_map_to_fd(int fd);
+int erts_instr_dump_memory_map_to(fmtfn_t to, void* to_arg);
int erts_instr_dump_memory_map(const char *name);
Eterm erts_instr_get_memory_map(Process *process);
-int erts_instr_dump_stat_to_fd(int fd, int begin_max_period);
+int erts_instr_dump_stat_to(fmtfn_t to, void* to_arg, int begin_max_period);
int erts_instr_dump_stat(const char *name, int begin_max_period);
Eterm erts_instr_get_stat(Process *proc, Eterm what, int begin_max_period);
Eterm erts_instr_get_type_info(Process *proc);
diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c
index 3b3b247020..c53bdc685b 100644
--- a/erts/emulator/beam/erl_lock_check.c
+++ b/erts/emulator/beam/erl_lock_check.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2013. 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.
@@ -96,7 +96,9 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "dist_entry", "address" },
{ "dist_entry_links", "address" },
{ "code_write_permission", NULL },
+ { "purge_state", NULL },
{ "proc_status", "pid" },
+ { "proc_trace", "pid" },
{ "ports_snapshot", NULL },
{ "meta_name_tab", "address" },
{ "meta_main_tab_slot", "address" },
@@ -111,11 +113,11 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "export_tab", NULL },
{ "fun_tab", NULL },
{ "environ", NULL },
+#ifdef ERTS_NEW_PURGE_STRATEGY
+ { "release_literal_areas", NULL },
#endif
- { "efile_drv", "address" },
-#if defined(ENABLE_CHILD_WAITER_THREAD) || defined(ERTS_SMP)
- { "child_status", NULL },
#endif
+ { "efile_drv", "address" },
{ "drv_ev_state_grow", NULL, },
{ "drv_ev_state", "address" },
{ "safe_hash", "address" },
@@ -127,6 +129,8 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "run_queue", "address" },
#ifdef ERTS_DIRTY_SCHEDULERS
{ "dirty_run_queue_sleep_list", "address" },
+ { "dirty_gc_info", NULL },
+ { "dirty_break_point_index", NULL },
#endif
{ "process_table", NULL },
{ "cpu_info", NULL },
@@ -140,6 +144,8 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "mmap_init_atoms", NULL },
{ "drv_tsd", NULL },
{ "async_enq_mtx", NULL },
+ { "msacc_list_mutex", NULL },
+ { "msacc_unmanaged_mutex", NULL },
#ifdef ERTS_SMP
{ "atom_tab", NULL },
{ "misc_op_list_pre_alloc_lock", "address" },
@@ -149,6 +155,7 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "dist_entry_out_queue", "address" },
{ "port_sched_lock", "port_id" },
{ "sys_msg_q", NULL },
+ { "tracer_mtx", NULL },
{ "port_table", NULL },
#endif
{ "mtrace_op", NULL },
@@ -156,17 +163,11 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "instr", NULL },
{ "alcu_allocator", "index" },
{ "mseg", NULL },
-#if HALFWORD_HEAP
- { "pmmap", NULL },
-#endif
#ifdef ERTS_SMP
{ "port_task_pre_alloc_lock", "address" },
{ "proclist_pre_alloc_lock", "address" },
{ "xports_list_pre_alloc_lock", "address" },
{ "inet_buffer_stack_lock", NULL },
- { "gc_info", NULL },
- { "io_wake", NULL },
- { "timer_wheel", NULL },
{ "system_block", NULL },
{ "timeofday", NULL },
{ "get_time", NULL },
diff --git a/erts/emulator/beam/erl_lock_check.h b/erts/emulator/beam/erl_lock_check.h
index 66251ef4e8..18296d1fec 100644
--- a/erts/emulator/beam/erl_lock_check.h
+++ b/erts/emulator/beam/erl_lock_check.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erl_lock_count.c b/erts/emulator/beam/erl_lock_count.c
index bd00480ba2..481e92b2cd 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-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -274,11 +274,11 @@ void erts_lcnt_init() {
lcnt_unlock();
- /* set start timer and zero statistics */
- erts_lcnt_clear_counters();
}
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);
}
diff --git a/erts/emulator/beam/erl_lock_count.h b/erts/emulator/beam/erl_lock_count.h
index 4cc6a5c695..3e8dcefe69 100644
--- a/erts/emulator/beam/erl_lock_count.h
+++ b/erts/emulator/beam/erl_lock_count.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 3c066cea7b..979a0040b0 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2014. All Rights Reserved.
+ * Copyright Ericsson AB 2014-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -54,6 +54,7 @@
* - maps:new/0
* - maps:put/3
* - maps:remove/2
+ * - maps:take/2
* - maps:to_list/1
* - maps:update/3
* - maps:values/1
@@ -93,7 +94,7 @@ static Uint hashmap_subtree_size(Eterm node);
static Eterm hashmap_to_list(Process *p, Eterm map);
static Eterm hashmap_keys(Process *p, Eterm map);
static Eterm hashmap_values(Process *p, Eterm map);
-static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node);
+static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node, Eterm *value);
static Eterm flatmap_from_validated_list(Process *p, Eterm list, Uint size);
static Eterm hashmap_from_validated_list(Process *p, Eterm list, Uint size);
static Eterm hashmap_from_unsorted_array(ErtsHeapFactory*, hxnode_t *hxns, Uint n, int reject_dupkeys);
@@ -172,26 +173,22 @@ BIF_RETTYPE maps_to_list_1(BIF_ALIST_1) {
*/
const Eterm *
-#if HALFWORD_HEAP
-erts_maps_get_rel(Eterm key, Eterm map, Eterm *map_base)
-#else
erts_maps_get(Eterm key, Eterm map)
-#endif
{
Uint32 hx;
- if (is_flatmap_rel(map, map_base)) {
+ if (is_flatmap(map)) {
Eterm *ks, *vs;
flatmap_t *mp;
Uint n, i;
- mp = (flatmap_t *)flatmap_val_rel(map, map_base);
+ mp = (flatmap_t *)flatmap_val(map);
n = flatmap_get_size(mp);
if (n == 0) {
return NULL;
}
- ks = (Eterm *)tuple_val_rel(mp->keys, map_base) + 1;
+ ks = (Eterm *)tuple_val(mp->keys) + 1;
vs = flatmap_get_values(mp);
if (is_immed(key)) {
@@ -200,19 +197,19 @@ erts_maps_get(Eterm key, Eterm map)
return &vs[i];
}
}
- }
-
- for (i = 0; i < n; i++) {
- if (eq_rel(ks[i], map_base, key, NULL)) {
- return &vs[i];
- }
- }
+ } else {
+ for (i = 0; i < n; i++) {
+ if (EQ(ks[i], key)) {
+ return &vs[i];
+ }
+ }
+ }
return NULL;
}
- ASSERT(is_hashmap_rel(map, map_base));
+ ASSERT(is_hashmap(map));
hx = hashmap_make_hash(key);
- return erts_hashmap_get_rel(hx, key, map, map_base);
+ return erts_hashmap_get(hx, key, map);
}
BIF_RETTYPE maps_find_2(BIF_ALIST_2) {
@@ -500,18 +497,50 @@ Eterm erts_hashmap_from_array(ErtsHeapFactory* factory, Eterm *leafs, Uint n,
return res;
}
+Eterm erts_map_from_ks_and_vs(ErtsHeapFactory *factory, Eterm *ks0, Eterm *vs0, Uint n)
+{
+ if (n < MAP_SMALL_MAP_LIMIT) {
+ Eterm *ks, *vs, *hp;
+ flatmap_t *mp;
+ Eterm keys;
-Eterm erts_hashmap_from_ks_and_vs_extra(Process *p, Eterm *ks, Eterm *vs, Uint n,
+ hp = erts_produce_heap(factory, 3 + 1 + (2 * n), 0);
+ keys = make_tuple(hp);
+ *hp++ = make_arityval(n);
+ ks = hp;
+ hp += n;
+ mp = (flatmap_t*)hp;
+ hp += MAP_HEADER_FLATMAP_SZ;
+ vs = hp;
+
+ mp->thing_word = MAP_HEADER_FLATMAP;
+ mp->size = n;
+ mp->keys = keys;
+
+ sys_memcpy(ks, ks0, n * sizeof(Eterm));
+ sys_memcpy(vs, vs0, n * sizeof(Eterm));
+
+ erts_validate_and_sort_flatmap(mp);
+
+ return make_flatmap(mp);
+ } else {
+ return erts_hashmap_from_ks_and_vs(factory, ks0, vs0, n);
+ }
+ return THE_NON_VALUE;
+}
+
+
+Eterm erts_hashmap_from_ks_and_vs_extra(ErtsHeapFactory *factory,
+ Eterm *ks, Eterm *vs, Uint n,
Eterm key, Eterm value) {
Uint32 sw, hx;
Uint i,sz;
hxnode_t *hxns;
- ErtsHeapFactory factory;
Eterm *hp, res;
sz = (key == THE_NON_VALUE) ? n : (n + 1);
ASSERT(sz > MAP_SMALL_MAP_LIMIT);
- hp = HAlloc(p, 2 * sz);
+ hp = erts_produce_heap(factory, 2 * sz, 0);
/* create tmp hx values and leaf ptrs */
hxns = (hxnode_t *)erts_alloc(ERTS_ALC_T_TMP, sz * sizeof(hxnode_t));
@@ -534,12 +563,9 @@ Eterm erts_hashmap_from_ks_and_vs_extra(Process *p, Eterm *ks, Eterm *vs, Uint n
hxns[i].i = i;
}
- erts_factory_proc_init(&factory, p);
- res = hashmap_from_unsorted_array(&factory, hxns, sz, 0);
- erts_factory_close(&factory);
+ res = hashmap_from_unsorted_array(factory, hxns, sz, 0);
erts_free(ERTS_ALC_T_TMP, (void *) hxns);
- ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
return res;
}
@@ -1525,10 +1551,45 @@ BIF_RETTYPE maps_put_3(BIF_ALIST_3) {
BIF_ERROR(BIF_P, BADMAP);
}
-/* maps:remove/3 */
+/* maps:take/2 */
-int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) {
+BIF_RETTYPE maps_take_2(BIF_ALIST_2) {
+ if (is_map(BIF_ARG_2)) {
+ Eterm res, map, val;
+ if (erts_maps_take(BIF_P, BIF_ARG_1, BIF_ARG_2, &map, &val)) {
+ Eterm *hp = HAlloc(BIF_P, 3);
+ res = make_tuple(hp);
+ *hp++ = make_arityval(2);
+ *hp++ = val;
+ *hp++ = map;
+ BIF_RET(res);
+ }
+ BIF_RET(am_error);
+ }
+ BIF_P->fvalue = BIF_ARG_2;
+ BIF_ERROR(BIF_P, BADMAP);
+}
+
+/* maps:remove/2 */
+
+BIF_RETTYPE maps_remove_2(BIF_ALIST_2) {
+ if (is_map(BIF_ARG_2)) {
+ Eterm res;
+ (void) erts_maps_take(BIF_P, BIF_ARG_1, BIF_ARG_2, &res, NULL);
+ BIF_RET(res);
+ }
+ BIF_P->fvalue = BIF_ARG_2;
+ BIF_ERROR(BIF_P, BADMAP);
+}
+
+/* erts_maps_take
+ * return 1 if key is found, otherwise 0
+ * If the key is not found res (output map) will be map (input map)
+ */
+int erts_maps_take(Process *p, Eterm key, Eterm map,
+ Eterm *res, Eterm *value) {
Uint32 hx;
+ Eterm ret;
if (is_flatmap(map)) {
Sint n;
Uint need;
@@ -1541,7 +1602,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) {
if (n == 0) {
*res = map;
- return 1;
+ return 0;
}
ks = flatmap_get_keys(mp);
@@ -1568,6 +1629,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) {
if (is_immed(key)) {
while (1) {
if (*ks == key) {
+ if (value) *value = *vs;
goto found_key;
} else if (--n) {
*mhp++ = *vs++;
@@ -1578,6 +1640,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) {
} else {
while(1) {
if (EQ(*ks, key)) {
+ if (value) *value = *vs;
goto found_key;
} else if (--n) {
*mhp++ = *vs++;
@@ -1593,7 +1656,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) {
HRelease(p, hp_start + need, hp_start);
*res = map;
- return 1;
+ return 0;
found_key:
/* Copy rest of keys and values */
@@ -1605,19 +1668,13 @@ found_key:
}
ASSERT(is_hashmap(map));
hx = hashmap_make_hash(key);
- *res = hashmap_delete(p, hx, key, map);
- return 1;
-}
-
-BIF_RETTYPE maps_remove_2(BIF_ALIST_2) {
- if (is_map(BIF_ARG_2)) {
- Eterm res;
- if (erts_maps_remove(BIF_P, BIF_ARG_1, BIF_ARG_2, &res)) {
- BIF_RET(res);
- }
+ ret = hashmap_delete(p, hx, key, map, value);
+ if (is_value(ret)) {
+ *res = ret;
+ return 1;
}
- BIF_P->fvalue = BIF_ARG_2;
- BIF_ERROR(BIF_P, BADMAP);
+ *res = map;
+ return 0;
}
int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) {
@@ -1752,11 +1809,14 @@ Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) {
/* the map will grow */
if (n >= MAP_SMALL_MAP_LIMIT) {
+ ErtsHeapFactory factory;
HRelease(p, shp + MAP_HEADER_FLATMAP_SZ + n, shp);
ks = flatmap_get_keys(mp);
vs = flatmap_get_values(mp);
- res = erts_hashmap_from_ks_and_vs_extra(p,ks,vs,n,key,value);
+ erts_factory_proc_init(&factory, p);
+ res = erts_hashmap_from_ks_and_vs_extra(&factory,ks,vs,n,key,value);
+ erts_factory_close(&factory);
return res;
}
@@ -1998,11 +2058,7 @@ Eterm* hashmap_iterator_prev(ErtsWStack* s) {
}
const Eterm *
-#if HALFWORD_HEAP
-erts_hashmap_get_rel(Uint32 hx, Eterm key, Eterm node, Eterm *map_base)
-#else
erts_hashmap_get(Uint32 hx, Eterm key, Eterm node)
-#endif
{
Eterm *ptr, hdr, *res;
Uint ix, lvl = 0;
@@ -2011,7 +2067,7 @@ erts_hashmap_get(Uint32 hx, Eterm key, Eterm node)
UseTmpHeapNoproc(2);
ASSERT(is_boxed(node));
- ptr = boxed_val_rel(node, map_base);
+ ptr = boxed_val(node);
hdr = *ptr;
ASSERT(is_header(hdr));
ASSERT(is_hashmap_header_head(hdr));
@@ -2032,15 +2088,15 @@ erts_hashmap_get(Uint32 hx, Eterm key, Eterm node)
node = ptr[ix+1];
if (is_list(node)) { /* LEAF NODE [K|V] */
- ptr = list_val_rel(node,map_base);
- res = eq_rel(CAR(ptr), map_base, key, NULL) ? &(CDR(ptr)) : NULL;
+ ptr = list_val(node);
+ res = EQ(CAR(ptr), key) ? &(CDR(ptr)) : NULL;
break;
}
hx = hashmap_shift_hash(th,hx,lvl,key);
ASSERT(is_boxed(node));
- ptr = boxed_val_rel(node, map_base);
+ ptr = boxed_val(node);
hdr = *ptr;
ASSERT(is_header(hdr));
ASSERT(!is_hashmap_header_head(hdr));
@@ -2330,7 +2386,8 @@ static Eterm hashmap_values(Process* p, Eterm node) {
return res;
}
-static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm map) {
+static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key,
+ Eterm map, Eterm *value) {
Eterm *hp = NULL, *nhp = NULL, *hp_end = NULL;
Eterm *ptr;
Eterm hdr, res = map, node = map;
@@ -2345,8 +2402,12 @@ static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm map) {
switch(primary_tag(node)) {
case TAG_PRIMARY_LIST:
if (EQ(CAR(list_val(node)), key)) {
+ if (value) {
+ *value = CDR(list_val(node));
+ }
goto unroll;
}
+ res = THE_NON_VALUE;
goto not_found;
case TAG_PRIMARY_BOXED:
ptr = boxed_val(node);
@@ -2373,6 +2434,7 @@ static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm map) {
n = hashmap_bitcount(hval);
} else {
/* not occupied */
+ res = THE_NON_VALUE;
goto not_found;
}
@@ -2402,6 +2464,7 @@ static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm map) {
break;
}
/* not occupied */
+ res = THE_NON_VALUE;
goto not_found;
default:
erts_exit(ERTS_ERROR_EXIT, "bad header tag %ld\r\n", hdr & _HEADER_MAP_SUBTAG_MASK);
@@ -2706,32 +2769,88 @@ BIF_RETTYPE erts_internal_map_to_tuple_keys_1(BIF_ALIST_1) {
}
/*
- * erts_internal:map_type/1
+ * erts_internal:term_type/1
*
* Used in erts_debug:size/1
*/
-BIF_RETTYPE erts_internal_map_type_1(BIF_ALIST_1) {
- DECL_AM(hashmap);
- DECL_AM(hashmap_node);
- DECL_AM(flatmap);
- if (is_map(BIF_ARG_1)) {
- Eterm hdr = *(boxed_val(BIF_ARG_1));
- ASSERT(is_header(hdr));
- switch (hdr & _HEADER_MAP_SUBTAG_MASK) {
- case HAMT_SUBTAG_HEAD_FLATMAP:
- BIF_RET(AM_flatmap);
- case HAMT_SUBTAG_HEAD_ARRAY:
- case HAMT_SUBTAG_HEAD_BITMAP:
- BIF_RET(AM_hashmap);
- case HAMT_SUBTAG_NODE_BITMAP:
- BIF_RET(AM_hashmap_node);
- default:
- erts_exit(ERTS_ERROR_EXIT, "bad header");
+BIF_RETTYPE erts_internal_term_type_1(BIF_ALIST_1) {
+ Eterm obj = BIF_ARG_1;
+ switch (primary_tag(obj)) {
+ case TAG_PRIMARY_LIST:
+ BIF_RET(ERTS_MAKE_AM("list"));
+ case TAG_PRIMARY_BOXED: {
+ Eterm hdr = *boxed_val(obj);
+ ASSERT(is_header(hdr));
+ switch (hdr & _TAG_HEADER_MASK) {
+ case ARITYVAL_SUBTAG:
+ BIF_RET(ERTS_MAKE_AM("tuple"));
+ case EXPORT_SUBTAG:
+ BIF_RET(ERTS_MAKE_AM("export"));
+ case FUN_SUBTAG:
+ BIF_RET(ERTS_MAKE_AM("fun"));
+ case MAP_SUBTAG:
+ switch (MAP_HEADER_TYPE(hdr)) {
+ case MAP_HEADER_TAG_FLATMAP_HEAD :
+ BIF_RET(ERTS_MAKE_AM("flatmap"));
+ case MAP_HEADER_TAG_HAMT_HEAD_BITMAP :
+ case MAP_HEADER_TAG_HAMT_HEAD_ARRAY :
+ BIF_RET(ERTS_MAKE_AM("hashmap"));
+ case MAP_HEADER_TAG_HAMT_NODE_BITMAP :
+ BIF_RET(ERTS_MAKE_AM("hashmap_node"));
+ default:
+ erts_exit(ERTS_ABORT_EXIT, "term_type: bad map header type %d\n", MAP_HEADER_TYPE(hdr));
+ }
+ case REFC_BINARY_SUBTAG:
+ BIF_RET(ERTS_MAKE_AM("refc_binary"));
+ case HEAP_BINARY_SUBTAG:
+ BIF_RET(ERTS_MAKE_AM("heap_binary"));
+ case SUB_BINARY_SUBTAG:
+ BIF_RET(ERTS_MAKE_AM("sub_binary"));
+ case BIN_MATCHSTATE_SUBTAG:
+ BIF_RET(ERTS_MAKE_AM("matchstate"));
+ case POS_BIG_SUBTAG:
+ case NEG_BIG_SUBTAG:
+ BIF_RET(ERTS_MAKE_AM("bignum"));
+ case REF_SUBTAG:
+ BIF_RET(ERTS_MAKE_AM("reference"));
+ case EXTERNAL_REF_SUBTAG:
+ BIF_RET(ERTS_MAKE_AM("external_reference"));
+ case EXTERNAL_PID_SUBTAG:
+ BIF_RET(ERTS_MAKE_AM("external_pid"));
+ case EXTERNAL_PORT_SUBTAG:
+ BIF_RET(ERTS_MAKE_AM("external_port"));
+ case FLOAT_SUBTAG:
+ BIF_RET(ERTS_MAKE_AM("hfloat"));
+ default:
+ erts_exit(ERTS_ABORT_EXIT, "term_type: Invalid tag (0x%X)\n", hdr);
+ }
}
+ case TAG_PRIMARY_IMMED1:
+ switch (obj & _TAG_IMMED1_MASK) {
+ case _TAG_IMMED1_SMALL:
+ BIF_RET(ERTS_MAKE_AM("fixnum"));
+ case _TAG_IMMED1_PID:
+ BIF_RET(ERTS_MAKE_AM("pid"));
+ case _TAG_IMMED1_PORT:
+ BIF_RET(ERTS_MAKE_AM("port"));
+ case _TAG_IMMED1_IMMED2:
+ switch (obj & _TAG_IMMED2_MASK) {
+ case _TAG_IMMED2_ATOM:
+ BIF_RET(ERTS_MAKE_AM("atom"));
+ case _TAG_IMMED2_CATCH:
+ BIF_RET(ERTS_MAKE_AM("catch"));
+ case _TAG_IMMED2_NIL:
+ BIF_RET(ERTS_MAKE_AM("nil"));
+ default:
+ erts_exit(ERTS_ABORT_EXIT, "term_type: Invalid tag (0x%X)\n", obj);
+ }
+ default:
+ erts_exit(ERTS_ABORT_EXIT, "term_type: Invalid tag (0x%X)\n", obj);
+ }
+ default:
+ erts_exit(ERTS_ABORT_EXIT, "term_type: Invalid tag (0x%X)\n", obj);
}
- BIF_P->fvalue = BIF_ARG_1;
- BIF_ERROR(BIF_P, BADMAP);
}
/*
diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h
index 4d9d74bc37..61a841f7f0 100644
--- a/erts/emulator/beam/erl_map.h
+++ b/erts/emulator/beam/erl_map.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2014. All Rights Reserved.
+ * Copyright Ericsson AB 2014-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -57,7 +57,6 @@ typedef struct flatmap_s {
#define hashmap_size(x) (((hashmap_head_t*) hashmap_val(x))->size)
-#define hashmap_size_rel(RTERM, BASE) hashmap_size(rterm2wterm(RTERM, BASE))
#define hashmap_make_hash(Key) make_internal_hash(Key)
#define hashmap_restore_hash(Heap,Lvl,Key) \
@@ -67,7 +66,7 @@ typedef struct flatmap_s {
/* erl_term.h stuff */
-#define flatmap_get_values(x) (((Eterm *)(x)) + 3)
+#define flatmap_get_values(x) (((Eterm *)(x)) + sizeof(flatmap_t)/sizeof(Eterm))
#define flatmap_get_keys(x) (((Eterm *)tuple_val(((flatmap_t *)(x))->keys)) + 1)
#define flatmap_get_size(x) (((flatmap_t*)(x))->size)
@@ -83,6 +82,7 @@ struct ErtsEStack_;
Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map);
int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res);
int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res);
+int erts_maps_take(Process *p, Eterm key, Eterm map, Eterm *res, Eterm *value);
Eterm erts_hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value,
Eterm node, int is_update);
@@ -98,29 +98,17 @@ Eterm* hashmap_iterator_prev(struct ErtsWStack_* s);
int hashmap_key_hash_cmp(Eterm* ap, Eterm* bp);
Eterm erts_hashmap_from_array(ErtsHeapFactory*, Eterm *leafs, Uint n, int reject_dupkeys);
-#define erts_hashmap_from_ks_and_vs(P, KS, VS, N) \
- erts_hashmap_from_ks_and_vs_extra((P), (KS), (VS), (N), THE_NON_VALUE, THE_NON_VALUE);
+#define erts_hashmap_from_ks_and_vs(F, KS, VS, N) \
+ erts_hashmap_from_ks_and_vs_extra((F), (KS), (VS), (N), THE_NON_VALUE, THE_NON_VALUE);
-Eterm erts_hashmap_from_ks_and_vs_extra(Process *p, Eterm *ks, Eterm *vs, Uint n,
+Eterm erts_map_from_ks_and_vs(ErtsHeapFactory *factory, Eterm *ks, Eterm *vs, Uint n);
+Eterm erts_hashmap_from_ks_and_vs_extra(ErtsHeapFactory *factory,
+ Eterm *ks, Eterm *vs, Uint n,
Eterm k, Eterm v);
-const Eterm *
-#if HALFWORD_HEAP
-erts_maps_get_rel(Eterm key, Eterm map, Eterm *map_base);
-# define erts_maps_get(A, B) erts_maps_get_rel(A, B, NULL)
-#else
-erts_maps_get(Eterm key, Eterm map);
-# define erts_maps_get_rel(A, B, B_BASE) erts_maps_get(A, B)
-#endif
+const Eterm *erts_maps_get(Eterm key, Eterm map);
-const Eterm *
-#if HALFWORD_HEAP
-erts_hashmap_get_rel(Uint32 hx, Eterm key, Eterm node, Eterm *map_base);
-# define erts_hashmap_get(Hx, K, M) erts_hashmap_get_rel(Hx, K, M, NULL)
-#else
-erts_hashmap_get(Uint32 hx, Eterm key, Eterm map);
-# define erts_hashmap_get_rel(Hx, K, M, M_BASE) erts_hashmap_get(Hx, K, M)
-#endif
+const Eterm *erts_hashmap_get(Uint32 hx, Eterm key, Eterm map);
/* hamt nodes v2.0
*
diff --git a/erts/emulator/beam/erl_math.c b/erts/emulator/beam/erl_math.c
index b46cc37495..fc0aaed18a 100644
--- a/erts/emulator/beam/erl_math.c
+++ b/erts/emulator/beam/erl_math.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c
index fa6b2fc613..cdd771ef7d 100644
--- a/erts/emulator/beam/erl_message.c
+++ b/erts/emulator/beam/erl_message.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2012. 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.
@@ -32,9 +32,10 @@
#include "erl_process.h"
#include "erl_binary.h"
#include "dtrace-wrapper.h"
+#include "beam_bp.h"
-ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(message,
- ErlMessage,
+ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(message_ref,
+ ErtsMessageRef,
ERL_MESSAGE_BUF_SZ,
ERTS_ALC_T_MSG_REF)
@@ -44,27 +45,20 @@ ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(message,
#undef HARD_DEBUG
#endif
-
-
-
-#ifdef DEBUG
-static ERTS_INLINE int in_heapfrag(const Eterm* ptr, const ErlHeapFragment *bp)
+void
+init_message(void)
{
- return ((unsigned)(ptr - bp->mem) < bp->used_size);
+ init_message_ref_alloc();
}
-#endif
-
-void
-init_message(void)
+void *erts_alloc_message_ref(void)
{
- init_message_alloc();
+ return (void *) message_ref_alloc();
}
-void
-free_message(ErlMessage* mp)
+void erts_free_message_ref(void *mp)
{
- message_free(mp);
+ message_ref_free((ErtsMessageRef *) mp);
}
/* Allocate message buffer (size in words) */
@@ -74,7 +68,8 @@ new_message_buffer(Uint size)
ErlHeapFragment* bp;
bp = (ErlHeapFragment*) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP_FRAG,
ERTS_HEAP_FRAG_SIZE(size));
- ERTS_INIT_HEAP_FRAG(bp, size);
+ ERTS_INIT_HEAP_FRAG(bp, size, size);
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] new message buffer %p\n", erts_get_current_pid(), bp->mem));
return bp;
}
@@ -177,7 +172,7 @@ erts_cleanup_offheap(ErlOffHeap *offheap)
}
break;
case FUN_SUBTAG:
- if (erts_refc_dectest(&u.fun->fe->refc, 0) == 0) {
+ if (erts_smp_refc_dectest(&u.fun->fe->refc, 0) == 0) {
erts_erase_fun_entry(u.fun->fe);
}
break;
@@ -198,96 +193,71 @@ free_message_buffer(ErlHeapFragment* bp)
erts_cleanup_offheap(&bp->off_heap);
ERTS_HEAP_FREE(ERTS_ALC_T_HEAP_FRAG, (void *) bp,
- ERTS_HEAP_FRAG_SIZE(bp->size));
+ ERTS_HEAP_FRAG_SIZE(bp->alloc_size));
bp = next_bp;
}while (bp != NULL);
}
-static ERTS_INLINE void
-link_mbuf_to_proc(Process *proc, ErlHeapFragment *bp)
+void
+erts_cleanup_messages(ErtsMessage *msgp)
{
- if (bp) {
- /* Link the message buffer */
- bp->next = MBUF(proc);
- MBUF(proc) = bp;
- MBUF_SIZE(proc) += bp->used_size;
- FLAGS(proc) |= F_FORCE_GC;
-
- /* Move any off_heap's into the process */
- if (bp->off_heap.first != NULL) {
- struct erl_off_heap_header** next_p = &bp->off_heap.first;
- while (*next_p != NULL) {
- next_p = &((*next_p)->next);
+ ErtsMessage *mp = msgp;
+ while (mp) {
+ ErtsMessage *fmp;
+ ErlHeapFragment *bp;
+ if (is_non_value(ERL_MESSAGE_TERM(mp))) {
+ if (is_not_immed(ERL_MESSAGE_TOKEN(mp))) {
+ bp = (ErlHeapFragment *) mp->data.dist_ext->ext_endp;
+ erts_cleanup_offheap(&bp->off_heap);
+ }
+ if (mp->data.dist_ext)
+ erts_free_dist_ext_copy(mp->data.dist_ext);
+ }
+ else {
+ if (mp->data.attached != ERTS_MSG_COMBINED_HFRAG)
+ bp = mp->data.heap_frag;
+ else {
+ bp = mp->hfrag.next;
+ erts_cleanup_offheap(&mp->hfrag.off_heap);
}
- *next_p = MSO(proc).first;
- MSO(proc).first = bp->off_heap.first;
- bp->off_heap.first = NULL;
- OH_OVERHEAD(&(MSO(proc)), bp->off_heap.overhead);
+ if (bp)
+ free_message_buffer(bp);
}
+ fmp = mp;
+ mp = mp->next;
+ erts_free_message(fmp);
}
}
-Eterm
-erts_msg_distext2heap(Process *pp,
- ErtsProcLocks *plcksp,
- ErlHeapFragment **bpp,
- Eterm *tokenp,
- ErtsDistExternal *dist_extp)
+ErtsMessage *
+erts_realloc_shrink_message(ErtsMessage *mp, Uint sz, Eterm *brefs, Uint brefs_size)
{
- Eterm msg;
- Uint tok_sz = 0;
- Eterm *hp = NULL;
- ErtsHeapFactory factory;
- Sint sz;
-
- *bpp = NULL;
- sz = erts_decode_dist_ext_size(dist_extp);
- if (sz < 0)
- goto decode_error;
- if (is_not_nil(*tokenp)) {
- ErlHeapFragment *heap_frag = erts_dist_ext_trailer(dist_extp);
- tok_sz = heap_frag->used_size;
- sz += tok_sz;
- }
- if (pp) {
- ErlOffHeap *ohp;
- hp = erts_alloc_message_heap(sz, bpp, &ohp, pp, plcksp);
- }
- else {
- *bpp = new_message_buffer(sz);
- hp = (*bpp)->mem;
- }
- erts_factory_message_init(&factory, pp, hp, *bpp);
- msg = erts_decode_dist_ext(&factory, dist_extp);
- if (is_non_value(msg))
- goto decode_error;
- if (is_not_nil(*tokenp)) {
- ErlHeapFragment *heap_frag = erts_dist_ext_trailer(dist_extp);
- hp = erts_produce_heap(&factory, tok_sz, 0);
- *tokenp = copy_struct(*tokenp, tok_sz, &hp, factory.off_heap);
- erts_cleanup_offheap(&heap_frag->off_heap);
+ ErtsMessage *nmp = erts_realloc(ERTS_ALC_T_MSG, mp,
+ sizeof(ErtsMessage) + (sz - 1)*sizeof(Eterm));
+ if (nmp != mp) {
+ Eterm *sp = &mp->hfrag.mem[0];
+ Eterm *ep = sp + sz;
+ Sint offs = &nmp->hfrag.mem[0] - sp;
+ erts_offset_off_heap(&nmp->hfrag.off_heap, offs, sp, ep);
+ erts_offset_heap(&nmp->hfrag.mem[0], sz, offs, sp, ep);
+ if (brefs && brefs_size)
+ erts_offset_heap_ptr(brefs, brefs_size, offs, sp, ep);
}
- erts_free_dist_ext_copy(dist_extp);
- erts_factory_close(&factory);
- return msg;
- decode_error:
- if (is_not_nil(*tokenp)) {
- ErlHeapFragment *heap_frag = erts_dist_ext_trailer(dist_extp);
- erts_cleanup_offheap(&heap_frag->off_heap);
- }
- erts_free_dist_ext_copy(dist_extp);
- *bpp = NULL;
- return THE_NON_VALUE;
- }
+ nmp->hfrag.used_size = sz;
+ nmp->hfrag.alloc_size = sz;
+
+ return nmp;
+}
void
erts_queue_dist_message(Process *rcvr,
- ErtsProcLocks *rcvr_locks,
+ ErtsProcLocks rcvr_locks,
ErtsDistExternal *dist_ext,
- Eterm token)
+ Eterm token,
+ Eterm from)
{
- ErlMessage* mp;
+ ErtsMessage* mp;
#ifdef USE_VM_PROBES
Sint tok_label = 0;
Sint tok_lastcnt = 0;
@@ -297,15 +267,25 @@ erts_queue_dist_message(Process *rcvr,
erts_aint_t state;
#endif
- ERTS_SMP_LC_ASSERT(*rcvr_locks == erts_proc_lc_my_proc_locks(rcvr));
+ ERTS_SMP_LC_ASSERT(rcvr_locks == erts_proc_lc_my_proc_locks(rcvr));
+
+ mp = erts_alloc_message(0, NULL);
+ mp->data.dist_ext = dist_ext;
- mp = message_alloc();
+ ERL_MESSAGE_TERM(mp) = THE_NON_VALUE;
+#ifdef USE_VM_PROBES
+ ERL_MESSAGE_DT_UTAG(mp) = NIL;
+ if (token == am_have_dt_utag)
+ ERL_MESSAGE_TOKEN(mp) = NIL;
+ else
+#endif
+ ERL_MESSAGE_TOKEN(mp) = token;
#ifdef ERTS_SMP
- if (!(*rcvr_locks & ERTS_PROC_LOCK_MSGQ)) {
+ if (!(rcvr_locks & ERTS_PROC_LOCK_MSGQ)) {
if (erts_smp_proc_trylock(rcvr, ERTS_PROC_LOCK_MSGQ) == EBUSY) {
ErtsProcLocks need_locks = ERTS_PROC_LOCK_MSGQ;
- if (*rcvr_locks & ERTS_PROC_LOCK_STATUS) {
+ if (rcvr_locks & ERTS_PROC_LOCK_STATUS) {
erts_smp_proc_unlock(rcvr, ERTS_PROC_LOCK_STATUS);
need_locks |= ERTS_PROC_LOCK_STATUS;
}
@@ -315,67 +295,52 @@ erts_queue_dist_message(Process *rcvr,
state = erts_smp_atomic32_read_acqb(&rcvr->state);
if (state & (ERTS_PSFLG_PENDING_EXIT|ERTS_PSFLG_EXITING)) {
- if (!(*rcvr_locks & ERTS_PROC_LOCK_MSGQ))
+ if (!(rcvr_locks & ERTS_PROC_LOCK_MSGQ))
erts_smp_proc_unlock(rcvr, ERTS_PROC_LOCK_MSGQ);
/* Drop message if receiver is exiting or has a pending exit ... */
- if (is_not_nil(token)) {
- ErlHeapFragment *heap_frag;
- heap_frag = erts_dist_ext_trailer(mp->data.dist_ext);
- erts_cleanup_offheap(&heap_frag->off_heap);
- }
- erts_free_dist_ext_copy(dist_ext);
- message_free(mp);
+ erts_cleanup_messages(mp);
}
else
#endif
if (IS_TRACED_FL(rcvr, F_TRACE_RECEIVE)) {
+ if (from == am_Empty)
+ from = dist_ext->dep->sysname;
+
/* Ahh... need to decode it in order to trace it... */
- ErlHeapFragment *mbuf;
- Eterm msg;
- if (!(*rcvr_locks & ERTS_PROC_LOCK_MSGQ))
+ if (!(rcvr_locks & ERTS_PROC_LOCK_MSGQ))
erts_smp_proc_unlock(rcvr, ERTS_PROC_LOCK_MSGQ);
- message_free(mp);
- msg = erts_msg_distext2heap(rcvr, rcvr_locks, &mbuf, &token, dist_ext);
- if (is_value(msg))
+ if (!erts_decode_dist_message(rcvr, rcvr_locks, mp, 0))
+ erts_free_message(mp);
+ else {
+ Eterm msg = ERL_MESSAGE_TERM(mp);
+ token = ERL_MESSAGE_TOKEN(mp);
#ifdef USE_VM_PROBES
- if (DTRACE_ENABLED(message_queued)) {
- DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE);
-
- dtrace_proc_str(rcvr, receiver_name);
- if (token != NIL && token != am_have_dt_utag) {
- tok_label = signed_val(SEQ_TRACE_T_LABEL(token));
- tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token));
- tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token));
- }
- DTRACE6(message_queued,
- receiver_name, size_object(msg), rcvr->msg.len,
- tok_label, tok_lastcnt, tok_serial);
- }
+ if (DTRACE_ENABLED(message_queued)) {
+ DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE);
+
+ dtrace_proc_str(rcvr, receiver_name);
+ if (have_seqtrace(token)) {
+ tok_label = signed_val(SEQ_TRACE_T_LABEL(token));
+ tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token));
+ tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token));
+ }
+ DTRACE6(message_queued,
+ receiver_name, size_object(msg), rcvr->msg.len,
+ tok_label, tok_lastcnt, tok_serial);
+ }
#endif
- erts_queue_message(rcvr, rcvr_locks, mbuf, msg, token);
+ erts_queue_message(rcvr, rcvr_locks, mp, msg, from);
+ }
}
else {
/* Enqueue message on external format */
- ERL_MESSAGE_TERM(mp) = THE_NON_VALUE;
-#ifdef USE_VM_PROBES
- ERL_MESSAGE_DT_UTAG(mp) = NIL;
- if (token == am_have_dt_utag) {
- ERL_MESSAGE_TOKEN(mp) = NIL;
- } else {
-#endif
- ERL_MESSAGE_TOKEN(mp) = token;
-#ifdef USE_VM_PROBES
- }
-#endif
- mp->next = NULL;
-
#ifdef USE_VM_PROBES
if (DTRACE_ENABLED(message_queued)) {
DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE);
dtrace_proc_str(rcvr, receiver_name);
- if (token != NIL && token != am_have_dt_utag) {
+ if (have_seqtrace(token)) {
tok_label = signed_val(SEQ_TRACE_T_LABEL(token));
tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token));
tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token));
@@ -388,15 +353,15 @@ erts_queue_dist_message(Process *rcvr,
tok_label, tok_lastcnt, tok_serial);
}
#endif
- mp->data.dist_ext = dist_ext;
- LINK_MESSAGE(rcvr, mp);
- if (!(*rcvr_locks & ERTS_PROC_LOCK_MSGQ))
+ LINK_MESSAGE(rcvr, mp, &mp->next, 1);
+
+ if (!(rcvr_locks & ERTS_PROC_LOCK_MSGQ))
erts_smp_proc_unlock(rcvr, ERTS_PROC_LOCK_MSGQ);
erts_proc_notify_new_message(rcvr,
#ifdef ERTS_SMP
- *rcvr_locks
+ rcvr_locks
#else
0
#endif
@@ -404,60 +369,57 @@ erts_queue_dist_message(Process *rcvr,
}
}
-/* Add a message last in message queue */
+/* Add messages last in message queue */
static Sint
-queue_message(Process *c_p,
- Process* receiver,
- ErtsProcLocks *receiver_locks,
- erts_aint32_t *receiver_state,
- ErlHeapFragment* bp,
- Eterm message,
- Eterm seq_trace_token
-#ifdef USE_VM_PROBES
- , Eterm dt_utag
-#endif
- )
+queue_messages(Process* receiver,
+ erts_aint32_t *receiver_state,
+ ErtsProcLocks receiver_locks,
+ ErtsMessage* first,
+ ErtsMessage** last,
+ Uint len,
+ Eterm from)
{
+ ErtsTracingEvent* te;
Sint res;
- ErlMessage* mp;
int locked_msgq = 0;
- erts_aint_t state;
-
-#ifndef ERTS_SMP
- ASSERT(bp != NULL || receiver->mbuf == NULL);
-#endif
-
- ERTS_SMP_LC_ASSERT(*receiver_locks == erts_proc_lc_my_proc_locks(receiver));
+ erts_aint32_t state;
- mp = message_alloc();
-
- if (receiver_state)
- state = *receiver_state;
- else
- state = erts_smp_atomic32_read_acqb(&receiver->state);
+ ASSERT(is_value(ERL_MESSAGE_TERM(first)));
+ ASSERT(ERL_MESSAGE_TOKEN(first) == am_undefined ||
+ ERL_MESSAGE_TOKEN(first) == NIL ||
+ is_tuple(ERL_MESSAGE_TOKEN(first)));
#ifdef ERTS_SMP
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(receiver) < ERTS_PROC_LOCK_MSGQ ||
+ receiver_locks == erts_proc_lc_my_proc_locks(receiver));
+#endif
- if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT))
- goto exiting;
-
- if (!(*receiver_locks & ERTS_PROC_LOCK_MSGQ)) {
+ if (!(receiver_locks & ERTS_PROC_LOCK_MSGQ)) {
if (erts_smp_proc_trylock(receiver, ERTS_PROC_LOCK_MSGQ) == EBUSY) {
- ErtsProcLocks need_locks = ERTS_PROC_LOCK_MSGQ;
- if (*receiver_locks & ERTS_PROC_LOCK_STATUS) {
- erts_smp_proc_unlock(receiver, ERTS_PROC_LOCK_STATUS);
- need_locks |= ERTS_PROC_LOCK_STATUS;
+ ErtsProcLocks need_locks;
+
+ if (receiver_state)
+ state = *receiver_state;
+ else
+ state = erts_smp_atomic32_read_nob(&receiver->state);
+ if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT))
+ goto exiting;
+
+ need_locks = receiver_locks & (ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ if (need_locks) {
+ erts_smp_proc_unlock(receiver, need_locks);
}
+ need_locks |= ERTS_PROC_LOCK_MSGQ;
erts_smp_proc_lock(receiver, need_locks);
}
locked_msgq = 1;
- state = erts_smp_atomic32_read_nob(&receiver->state);
- if (receiver_state)
- *receiver_state = state;
}
#endif
+ state = erts_smp_atomic32_read_nob(&receiver->state);
+
if (state & (ERTS_PSFLG_PENDING_EXIT|ERTS_PSFLG_EXITING)) {
#ifdef ERTS_SMP
exiting:
@@ -465,25 +427,13 @@ queue_message(Process *c_p,
/* Drop message if receiver is exiting or has a pending exit... */
if (locked_msgq)
erts_smp_proc_unlock(receiver, ERTS_PROC_LOCK_MSGQ);
- if (bp)
- free_message_buffer(bp);
- message_free(mp);
+ erts_cleanup_messages(first);
return 0;
}
- ERL_MESSAGE_TERM(mp) = message;
- ERL_MESSAGE_TOKEN(mp) = seq_trace_token;
-#ifdef USE_VM_PROBES
- ERL_MESSAGE_DT_UTAG(mp) = dt_utag;
-#endif
- mp->next = NULL;
- mp->data.heap_frag = bp;
-
-#ifndef ERTS_SMP
res = receiver->msg.len;
-#else
- res = receiver->msg_inq.len;
- if (*receiver_locks & ERTS_PROC_LOCK_MAIN) {
+#ifdef ERTS_SMP
+ if (receiver_locks & ERTS_PROC_LOCK_MAIN) {
/*
* We move 'in queue' to 'private queue' and place
* message at the end of 'private queue' in order
@@ -492,319 +442,120 @@ queue_message(Process *c_p,
* we don't need to include the 'in queue' in
* the root set when garbage collecting.
*/
- res += receiver->msg.len;
+ res += receiver->msg_inq.len;
ERTS_SMP_MSGQ_MV_INQ2PRIVQ(receiver);
- LINK_MESSAGE_PRIVQ(receiver, mp);
+ LINK_MESSAGE_PRIVQ(receiver, first, last, len);
}
else
#endif
{
- LINK_MESSAGE(receiver, mp);
+ LINK_MESSAGE(receiver, first, last, len);
}
+ if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE)
+ && (te = &erts_receive_tracing[erts_active_bp_ix()],
+ te->on)) {
+
+ ErtsMessage *msg = first;
+
#ifdef USE_VM_PROBES
- if (DTRACE_ENABLED(message_queued)) {
- DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE);
- Sint tok_label = 0;
- Sint tok_lastcnt = 0;
- Sint tok_serial = 0;
-
- dtrace_proc_str(receiver, receiver_name);
- if (seq_trace_token != NIL && is_tuple(seq_trace_token)) {
- tok_label = signed_val(SEQ_TRACE_T_LABEL(seq_trace_token));
- tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(seq_trace_token));
- tok_serial = signed_val(SEQ_TRACE_T_SERIAL(seq_trace_token));
+ if (DTRACE_ENABLED(message_queued)) {
+ DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE);
+ Sint tok_label = 0;
+ Sint tok_lastcnt = 0;
+ Sint tok_serial = 0;
+ Eterm seq_trace_token = ERL_MESSAGE_TOKEN(msg);
+
+ dtrace_proc_str(receiver, receiver_name);
+ if (seq_trace_token != NIL && is_tuple(seq_trace_token)) {
+ tok_label = signed_val(SEQ_TRACE_T_LABEL(seq_trace_token));
+ tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(seq_trace_token));
+ tok_serial = signed_val(SEQ_TRACE_T_SERIAL(seq_trace_token));
+ }
+ DTRACE6(message_queued,
+ receiver_name, size_object(ERL_MESSAGE_TERM(msg)),
+ receiver->msg.len,
+ tok_label, tok_lastcnt, tok_serial);
}
- DTRACE6(message_queued,
- receiver_name, size_object(message), receiver->msg.len,
- tok_label, tok_lastcnt, tok_serial);
- }
#endif
+ while (msg) {
+ trace_receive(receiver, from, ERL_MESSAGE_TERM(msg), te);
+ msg = msg->next;
+ }
- if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE))
- trace_receive(receiver, message);
-
- if (locked_msgq)
+ }
+ if (locked_msgq) {
erts_smp_proc_unlock(receiver, ERTS_PROC_LOCK_MSGQ);
+ }
- erts_proc_notify_new_message(receiver,
#ifdef ERTS_SMP
- *receiver_locks
+ erts_proc_notify_new_message(receiver, receiver_locks);
#else
- 0
-#endif
- );
-
-#ifndef ERTS_SMP
- ERTS_HOLE_CHECK(receiver);
+ erts_proc_notify_new_message(receiver, 0);
#endif
return res;
}
-void
-#ifdef USE_VM_PROBES
-erts_queue_message_probe(Process* receiver, ErtsProcLocks *receiver_locks,
- ErlHeapFragment* bp,
- Eterm message, Eterm seq_trace_token, Eterm dt_utag)
-#else
-erts_queue_message(Process* receiver, ErtsProcLocks *receiver_locks,
- ErlHeapFragment* bp,
- Eterm message, Eterm seq_trace_token)
-#endif
+static Sint
+queue_message(Process* receiver,
+ erts_aint32_t *receiver_state,
+ ErtsProcLocks receiver_locks,
+ ErtsMessage* mp, Eterm msg, Eterm from)
{
- queue_message(NULL,
- receiver,
- receiver_locks,
- NULL,
- bp,
- message,
- seq_trace_token
-#ifdef USE_VM_PROBES
- , dt_utag
-#endif
- );
+ ERL_MESSAGE_TERM(mp) = msg;
+ return queue_messages(receiver, receiver_state, receiver_locks,
+ mp, &mp->next, 1, from);
}
-void
-erts_link_mbuf_to_proc(struct process *proc, ErlHeapFragment *bp)
+Sint
+erts_queue_message(Process* receiver, ErtsProcLocks receiver_locks,
+ ErtsMessage* mp, Eterm msg, Eterm from)
{
- Eterm* htop = HEAP_TOP(proc);
+ return queue_message(receiver, NULL, receiver_locks, mp, msg, from);
+}
- link_mbuf_to_proc(proc, bp);
- if (htop < HEAP_LIMIT(proc)) {
- *htop = make_pos_bignum_header(HEAP_LIMIT(proc)-htop-1);
- HEAP_TOP(proc) = HEAP_LIMIT(proc);
- }
+
+Sint
+erts_queue_messages(Process* receiver, ErtsProcLocks receiver_locks,
+ ErtsMessage* first, ErtsMessage** last, Uint len,
+ Eterm from)
+{
+ return queue_messages(receiver, NULL, receiver_locks,
+ first, last, len, from);
}
-/*
- * Moves content of message buffer attached to a message into a heap.
- * The message buffer is deallocated.
- */
void
-erts_move_msg_mbuf_to_heap(Eterm** hpp, ErlOffHeap* off_heap, ErlMessage *msg)
+erts_link_mbuf_to_proc(Process *proc, ErlHeapFragment *first_bp)
{
- struct erl_off_heap_header* oh;
- Eterm term, token, *fhp, *hp;
- Sint offs;
- Uint sz;
- ErlHeapFragment *bp;
-#ifdef USE_VM_PROBES
- Eterm utag;
-#endif
-
-#ifdef HARD_DEBUG
- struct erl_off_heap_header* dbg_oh_start = off_heap->first;
- Eterm dbg_term, dbg_token;
- ErlHeapFragment *dbg_bp;
- Uint *dbg_hp, *dbg_thp_start;
- Uint dbg_term_sz, dbg_token_sz;
-#ifdef USE_VM_PROBES
- Eterm dbg_utag;
- Uint dbg_utag_sz;
-#endif
-#endif
-
- bp = msg->data.heap_frag;
- term = ERL_MESSAGE_TERM(msg);
- token = ERL_MESSAGE_TOKEN(msg);
-#ifdef USE_VM_PROBES
- utag = ERL_MESSAGE_DT_UTAG(msg);
-#endif
- if (!bp) {
-#ifdef USE_VM_PROBES
- ASSERT(is_immed(term) && is_immed(token) && is_immed(utag));
-#else
- ASSERT(is_immed(term) && is_immed(token));
-#endif
- return;
- }
-
-#ifdef HARD_DEBUG
- dbg_term_sz = size_object(term);
- dbg_token_sz = size_object(token);
- dbg_bp = new_message_buffer(dbg_term_sz + dbg_token_sz);
-#ifdef USE_VM_PROBES
- dbg_utag_sz = size_object(utag);
- dbg_bp = new_message_buffer(dbg_term_sz + dbg_token_sz + dbg_utag_sz );
-#endif
- /*ASSERT(dbg_term_sz + dbg_token_sz == erts_msg_used_frag_sz(msg));
- Copied size may be smaller due to removed SubBins's or garbage.
- Copied size may be larger due to duplicated shared terms.
- */
- dbg_hp = dbg_bp->mem;
- dbg_term = copy_struct(term, dbg_term_sz, &dbg_hp, &dbg_bp->off_heap);
- dbg_token = copy_struct(token, dbg_token_sz, &dbg_hp, &dbg_bp->off_heap);
-#ifdef USE_VM_PROBES
- dbg_utag = copy_struct(utag, dbg_utag_sz, &dbg_hp, &dbg_bp->off_heap);
-#endif
- dbg_thp_start = *hpp;
-#endif
-
- if (bp->next != NULL) {
- move_multi_frags(hpp, off_heap, bp, msg->m,
-#ifdef USE_VM_PROBES
- 3
-#else
- 2
-#endif
- );
- goto copy_done;
- }
-
- OH_OVERHEAD(off_heap, bp->off_heap.overhead);
- sz = bp->used_size;
-
- ASSERT(is_immed(term) || in_heapfrag(ptr_val(term),bp));
- ASSERT(is_immed(token) || in_heapfrag(ptr_val(token),bp));
-
- fhp = bp->mem;
- hp = *hpp;
- offs = hp - fhp;
-
- oh = NULL;
- while (sz--) {
- Uint cpy_sz;
- Eterm val = *fhp++;
-
- switch (primary_tag(val)) {
- case TAG_PRIMARY_IMMED1:
- *hp++ = val;
- break;
- case TAG_PRIMARY_LIST:
- case TAG_PRIMARY_BOXED:
- ASSERT(in_heapfrag(ptr_val(val), bp));
- *hp++ = offset_ptr(val, offs);
- break;
- case TAG_PRIMARY_HEADER:
- *hp++ = val;
- switch (val & _HEADER_SUBTAG_MASK) {
- case ARITYVAL_SUBTAG:
- break;
- case REFC_BINARY_SUBTAG:
- case FUN_SUBTAG:
- case EXTERNAL_PID_SUBTAG:
- case EXTERNAL_PORT_SUBTAG:
- case EXTERNAL_REF_SUBTAG:
- oh = (struct erl_off_heap_header*) (hp-1);
- cpy_sz = thing_arityval(val);
- goto cpy_words;
- default:
- cpy_sz = header_arity(val);
-
- cpy_words:
- ASSERT(sz >= cpy_sz);
- sz -= cpy_sz;
- while (cpy_sz >= 8) {
- cpy_sz -= 8;
- *hp++ = *fhp++;
- *hp++ = *fhp++;
- *hp++ = *fhp++;
- *hp++ = *fhp++;
- *hp++ = *fhp++;
- *hp++ = *fhp++;
- *hp++ = *fhp++;
- *hp++ = *fhp++;
- }
- switch (cpy_sz) {
- case 7: *hp++ = *fhp++;
- case 6: *hp++ = *fhp++;
- case 5: *hp++ = *fhp++;
- case 4: *hp++ = *fhp++;
- case 3: *hp++ = *fhp++;
- case 2: *hp++ = *fhp++;
- case 1: *hp++ = *fhp++;
- default: break;
+ if (first_bp) {
+ ErlHeapFragment *bp = first_bp;
+
+ while (1) {
+ /* Move any off_heap's into the process */
+ if (bp->off_heap.first != NULL) {
+ struct erl_off_heap_header** next_p = &bp->off_heap.first;
+ while (*next_p != NULL) {
+ next_p = &((*next_p)->next);
}
- if (oh) {
- /* Add to offheap list */
- oh->next = off_heap->first;
- off_heap->first = oh;
- ASSERT(*hpp <= (Eterm*)oh);
- ASSERT(hp > (Eterm*)oh);
- oh = NULL;
- }
- break;
+ *next_p = MSO(proc).first;
+ MSO(proc).first = bp->off_heap.first;
+ bp->off_heap.first = NULL;
+ OH_OVERHEAD(&(MSO(proc)), bp->off_heap.overhead);
}
- break;
+ MBUF_SIZE(proc) += bp->used_size;
+ if (!bp->next)
+ break;
+ bp = bp->next;
}
- }
-
- ASSERT(bp->used_size == hp - *hpp);
- *hpp = hp;
- if (is_not_immed(token)) {
- ASSERT(in_heapfrag(ptr_val(token), bp));
- ERL_MESSAGE_TOKEN(msg) = offset_ptr(token, offs);
-#ifdef HARD_DEBUG
- ASSERT(dbg_thp_start <= ptr_val(ERL_MESSAGE_TOKEN(msg)));
- ASSERT(hp > ptr_val(ERL_MESSAGE_TOKEN(msg)));
-#endif
- }
-
- if (is_not_immed(term)) {
- ASSERT(in_heapfrag(ptr_val(term),bp));
- ERL_MESSAGE_TERM(msg) = offset_ptr(term, offs);
-#ifdef HARD_DEBUG
- ASSERT(dbg_thp_start <= ptr_val(ERL_MESSAGE_TERM(msg)));
- ASSERT(hp > ptr_val(ERL_MESSAGE_TERM(msg)));
-#endif
- }
-#ifdef USE_VM_PROBES
- if (is_not_immed(utag)) {
- ASSERT(in_heapfrag(ptr_val(utag), bp));
- ERL_MESSAGE_DT_UTAG(msg) = offset_ptr(utag, offs);
-#ifdef HARD_DEBUG
- ASSERT(dbg_thp_start <= ptr_val(ERL_MESSAGE_DT_UTAG(msg)));
- ASSERT(hp > ptr_val(ERL_MESSAGE_DT_UTAG(msg)));
-#endif
- }
-#endif
-
-copy_done:
-
-#ifdef HARD_DEBUG
- {
- int i, j;
- ErlHeapFragment* frag;
- {
- struct erl_off_heap_header* dbg_oh = off_heap->first;
- i = j = 0;
- while (dbg_oh != dbg_oh_start) {
- dbg_oh = dbg_oh->next;
- i++;
- }
- for (frag=bp; frag; frag=frag->next) {
- dbg_oh = frag->off_heap.first;
- while (dbg_oh) {
- dbg_oh = dbg_oh->next;
- j++;
- }
- }
- ASSERT(i == j);
- }
+ /* Link the message buffer */
+ bp->next = MBUF(proc);
+ MBUF(proc) = first_bp;
}
-#endif
-
-
- bp->off_heap.first = NULL;
- free_message_buffer(bp);
- msg->data.heap_frag = NULL;
-
-#ifdef HARD_DEBUG
- ASSERT(eq(ERL_MESSAGE_TERM(msg), dbg_term));
- ASSERT(eq(ERL_MESSAGE_TOKEN(msg), dbg_token));
-#ifdef USE_VM_PROBES
- ASSERT(eq(ERL_MESSAGE_DT_UTAG(msg), dbg_utag));
-#endif
- free_message_buffer(dbg_bp);
-#endif
-
}
-
Uint
-erts_msg_attached_data_size_aux(ErlMessage *msg)
+erts_msg_attached_data_size_aux(ErtsMessage *msg)
{
Sint sz;
ASSERT(is_non_value(ERL_MESSAGE_TERM(msg)));
@@ -833,29 +584,89 @@ erts_msg_attached_data_size_aux(ErlMessage *msg)
return sz;
}
-void
-erts_move_msg_attached_data_to_heap(ErtsHeapFactory* factory,
- ErlMessage *msg)
+ErtsMessage *
+erts_try_alloc_message_on_heap(Process *pp,
+ erts_aint32_t *psp,
+ ErtsProcLocks *plp,
+ Uint sz,
+ Eterm **hpp,
+ ErlOffHeap **ohpp,
+ int *on_heap_p)
{
- if (is_value(ERL_MESSAGE_TERM(msg)))
- erts_move_msg_mbuf_to_heap(&factory->hp, factory->off_heap, msg);
- else if (msg->data.dist_ext) {
- ASSERT(msg->data.dist_ext->heap_size >= 0);
- if (is_not_nil(ERL_MESSAGE_TOKEN(msg))) {
- ErlHeapFragment *heap_frag;
- heap_frag = erts_dist_ext_trailer(msg->data.dist_ext);
- ERL_MESSAGE_TOKEN(msg) = copy_struct(ERL_MESSAGE_TOKEN(msg),
- heap_frag->used_size,
- &factory->hp,
- factory->off_heap);
- erts_cleanup_offheap(&heap_frag->off_heap);
+#ifdef ERTS_SMP
+ int locked_main = 0;
+#endif
+ ErtsMessage *mp;
+
+ ASSERT(!(*psp & ERTS_PSFLG_OFF_HEAP_MSGQ));
+
+ if ((*psp) & ERTS_PSFLGS_VOLATILE_HEAP)
+ goto in_message_fragment;
+ else if (
+#if defined(ERTS_SMP)
+ *plp & ERTS_PROC_LOCK_MAIN
+#else
+ pp
+#endif
+ ) {
+#ifdef ERTS_SMP
+ try_on_heap:
+#endif
+ if (((*psp) & ERTS_PSFLGS_VOLATILE_HEAP)
+ || (pp->flags & F_DISABLE_GC)
+ || HEAP_LIMIT(pp) - HEAP_TOP(pp) <= sz) {
+ /*
+ * The heap is either potentially in an inconsistent
+ * state, or not large enough.
+ */
+#ifdef ERTS_SMP
+ if (locked_main) {
+ *plp &= ~ERTS_PROC_LOCK_MAIN;
+ erts_smp_proc_unlock(pp, ERTS_PROC_LOCK_MAIN);
+ }
+#endif
+ goto in_message_fragment;
}
- ERL_MESSAGE_TERM(msg) = erts_decode_dist_ext(factory,
- msg->data.dist_ext);
- erts_free_dist_ext_copy(msg->data.dist_ext);
- msg->data.dist_ext = NULL;
+
+ *hpp = HEAP_TOP(pp);
+ HEAP_TOP(pp) = *hpp + sz;
+ *ohpp = &MSO(pp);
+ mp = erts_alloc_message(0, NULL);
+ mp->data.attached = NULL;
+ *on_heap_p = !0;
}
- /* else: bad external detected when calculating size */
+#ifdef ERTS_SMP
+ else if (pp && erts_smp_proc_trylock(pp, ERTS_PROC_LOCK_MAIN) == 0) {
+ locked_main = 1;
+ *psp = erts_smp_atomic32_read_nob(&pp->state);
+ *plp |= ERTS_PROC_LOCK_MAIN;
+ goto try_on_heap;
+ }
+#endif
+ else {
+ in_message_fragment:
+ if (!((*psp) & ERTS_PSFLG_ON_HEAP_MSGQ)) {
+ mp = erts_alloc_message(sz, hpp);
+ *ohpp = sz == 0 ? NULL : &mp->hfrag.off_heap;
+ }
+ else {
+ mp = erts_alloc_message(0, NULL);
+ if (!sz) {
+ *hpp = NULL;
+ *ohpp = NULL;
+ }
+ else {
+ ErlHeapFragment *bp;
+ bp = new_message_buffer(sz);
+ *hpp = &bp->mem[0];
+ mp->data.heap_frag = bp;
+ *ohpp = &bp->off_heap;
+ }
+ }
+ *on_heap_p = 0;
+ }
+
+ return mp;
}
/*
@@ -870,7 +681,8 @@ erts_send_message(Process* sender,
unsigned flags)
{
Uint msize;
- ErlHeapFragment* bp = NULL;
+ ErtsMessage* mp;
+ ErlOffHeap *ohp;
Eterm token = NIL;
Sint res = 0;
#ifdef USE_VM_PROBES
@@ -879,80 +691,91 @@ erts_send_message(Process* sender,
Sint tok_label = 0;
Sint tok_lastcnt = 0;
Sint tok_serial = 0;
+ Eterm utag = NIL;
+#endif
+ erts_aint32_t receiver_state;
+#ifdef SHCOPY_SEND
+ erts_shcopy_t info;
#endif
- BM_STOP_TIMER(system);
- BM_MESSAGE(message,sender,receiver);
- BM_START_TIMER(send);
- #ifdef USE_VM_PROBES
+#ifdef USE_VM_PROBES
*sender_name = *receiver_name = '\0';
- if (DTRACE_ENABLED(message_send)) {
+ if (DTRACE_ENABLED(message_send)) {
erts_snprintf(sender_name, sizeof(DTRACE_CHARBUF_NAME(sender_name)),
"%T", sender->common.id);
erts_snprintf(receiver_name, sizeof(DTRACE_CHARBUF_NAME(receiver_name)),
"%T", receiver->common.id);
}
#endif
+
+ receiver_state = erts_smp_atomic32_read_nob(&receiver->state);
+
if (SEQ_TRACE_TOKEN(sender) != NIL && !(flags & ERTS_SND_FLG_NO_SEQ_TRACE)) {
Eterm* hp;
Eterm stoken = SEQ_TRACE_TOKEN(sender);
Uint seq_trace_size = 0;
#ifdef USE_VM_PROBES
Uint dt_utag_size = 0;
- Eterm utag = NIL;
-#endif
-
- BM_SWAP_TIMER(send,size);
- msize = size_object(message);
- BM_SWAP_TIMER(size,send);
-
-#ifdef USE_VM_PROBES
- if (stoken != am_have_dt_utag) {
#endif
+ /* SHCOPY corrupts the heap between
+ * copy_shared_calculate, and
+ * copy_shared_perform. (it inserts move_markers like the gc).
+ * Make sure we don't use the heap between those instances.
+ */
+ if (have_seqtrace(stoken)) {
seq_trace_update_send(sender);
seq_trace_output(stoken, message, SEQ_TRACE_SEND,
receiver->common.id, sender);
seq_trace_size = 6; /* TUPLE5 */
-#ifdef USE_VM_PROBES
}
- if (DT_UTAG_FLAGS(sender) & DT_UTAG_SPREADING) {
- dt_utag_size = size_object(DT_UTAG(sender));
- } else if (stoken == am_have_dt_utag ) {
- stoken = NIL;
- }
-#endif
-
- bp = new_message_buffer(msize + seq_trace_size
#ifdef USE_VM_PROBES
- + dt_utag_size
+ if (DT_UTAG_FLAGS(sender) & DT_UTAG_SPREADING) {
+ dt_utag_size = size_object(DT_UTAG(sender));
+ } else if (stoken == am_have_dt_utag ) {
+ stoken = NIL;
+ }
#endif
- );
- hp = bp->mem;
-
- BM_SWAP_TIMER(send,copy);
- token = copy_struct(stoken,
- seq_trace_size,
- &hp,
- &bp->off_heap);
- message = copy_struct(message, msize, &hp, &bp->off_heap);
+#ifdef SHCOPY_SEND
+ INITIALIZE_SHCOPY(info);
+ msize = copy_shared_calculate(message, &info);
+#else
+ msize = size_object(message);
+#endif
+ mp = erts_alloc_message_heap_state(receiver,
+ &receiver_state,
+ receiver_locks,
+ (msize
#ifdef USE_VM_PROBES
- if (DT_UTAG_FLAGS(sender) & DT_UTAG_SPREADING) {
- utag = copy_struct(DT_UTAG(sender), dt_utag_size, &hp, &bp->off_heap);
-#ifdef DTRACE_TAG_HARDDEBUG
- erts_fprintf(stderr,
- "Dtrace -> (%T) Spreading tag (%T) with "
- "message %T!\r\n",sender->common.id, utag, message);
+ + dt_utag_size
#endif
- }
+ + seq_trace_size),
+ &hp,
+ &ohp);
+
+#ifdef SHCOPY_SEND
+ if (is_not_immed(message))
+ message = copy_shared_perform(message, msize, &info, &hp, ohp);
+ DESTROY_SHCOPY(info);
+#else
+ if (is_not_immed(message))
+ message = copy_struct(message, msize, &hp, ohp);
#endif
- BM_MESSAGE_COPIED(msize);
- BM_SWAP_TIMER(copy,send);
+ if (is_immed(stoken))
+ token = stoken;
+ else
+ token = copy_struct(stoken, seq_trace_size, &hp, ohp);
#ifdef USE_VM_PROBES
+ if (DT_UTAG_FLAGS(sender) & DT_UTAG_SPREADING) {
+ if (is_immed(DT_UTAG(sender)))
+ utag = DT_UTAG(sender);
+ else
+ utag = copy_struct(DT_UTAG(sender), dt_utag_size, &hp, ohp);
+ }
if (DTRACE_ENABLED(message_send)) {
- if (stoken != NIL && stoken != am_have_dt_utag) {
+ if (have_seqtrace(stoken)) {
tok_label = signed_val(SEQ_TRACE_T_LABEL(stoken));
tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(stoken));
tok_serial = signed_val(SEQ_TRACE_T_SERIAL(stoken));
@@ -961,103 +784,55 @@ erts_send_message(Process* sender,
msize, tok_label, tok_lastcnt, tok_serial);
}
#endif
- res = queue_message(NULL,
- receiver,
- receiver_locks,
- NULL,
- bp,
- message,
- token
-#ifdef USE_VM_PROBES
- , utag
-#endif
- );
- BM_SWAP_TIMER(send,system);
- } else if (sender == receiver) {
- /* Drop message if receiver has a pending exit ... */
-#ifdef ERTS_SMP
- ErtsProcLocks need_locks = (~(*receiver_locks)
- & (ERTS_PROC_LOCK_MSGQ
- | ERTS_PROC_LOCK_STATUS));
- if (need_locks) {
- *receiver_locks |= need_locks;
- if (erts_smp_proc_trylock(receiver, need_locks) == EBUSY) {
- if (need_locks == ERTS_PROC_LOCK_MSGQ) {
- erts_smp_proc_unlock(receiver, ERTS_PROC_LOCK_STATUS);
- need_locks = ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS;
- }
- erts_smp_proc_lock(receiver, need_locks);
- }
+ } else {
+ Eterm *hp;
+
+ if (receiver == sender && !(receiver_state & ERTS_PSFLG_OFF_HEAP_MSGQ)) {
+ mp = erts_alloc_message(0, NULL);
+ msize = 0;
}
- if (!ERTS_PROC_PENDING_EXIT(receiver))
+ else {
+#ifdef SHCOPY_SEND
+ INITIALIZE_SHCOPY(info);
+ msize = copy_shared_calculate(message, &info);
+#else
+ msize = size_object(message);
#endif
- {
- ErlMessage* mp = message_alloc();
-
- DTRACE6(message_send, sender_name, receiver_name,
- size_object(message), tok_label, tok_lastcnt, tok_serial);
- mp->data.attached = NULL;
- ERL_MESSAGE_TERM(mp) = message;
- ERL_MESSAGE_TOKEN(mp) = NIL;
-#ifdef USE_VM_PROBES
- ERL_MESSAGE_DT_UTAG(mp) = NIL;
+ mp = erts_alloc_message_heap_state(receiver,
+ &receiver_state,
+ receiver_locks,
+ msize,
+ &hp,
+ &ohp);
+#ifdef SHCOPY_SEND
+ if (is_not_immed(message))
+ message = copy_shared_perform(message, msize, &info, &hp, ohp);
+ DESTROY_SHCOPY(info);
+#else
+ if (is_not_immed(message))
+ message = copy_struct(message, msize, &hp, ohp);
#endif
- mp->next = NULL;
- /*
- * We move 'in queue' to 'private queue' and place
- * message at the end of 'private queue' in order
- * to ensure that the 'in queue' doesn't contain
- * references into the heap. By ensuring this,
- * we don't need to include the 'in queue' in
- * the root set when garbage collecting.
- */
-
- ERTS_SMP_MSGQ_MV_INQ2PRIVQ(receiver);
- LINK_MESSAGE_PRIVQ(receiver, mp);
-
- res = receiver->msg.len;
-
- if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE)) {
- trace_receive(receiver, message);
- }
}
- BM_SWAP_TIMER(send,system);
- } else {
- ErlOffHeap *ohp;
- Eterm *hp;
- erts_aint32_t state;
-
- BM_SWAP_TIMER(send,size);
- msize = size_object(message);
- BM_SWAP_TIMER(size,send);
- hp = erts_alloc_message_heap_state(msize,
- &bp,
- &ohp,
- receiver,
- receiver_locks,
- &state);
- BM_SWAP_TIMER(send,copy);
- message = copy_struct(message, msize, &hp, ohp);
- BM_MESSAGE_COPIED(msz);
- BM_SWAP_TIMER(copy,send);
+#ifdef USE_VM_PROBES
DTRACE6(message_send, sender_name, receiver_name,
msize, tok_label, tok_lastcnt, tok_serial);
- res = queue_message(sender,
- receiver,
- receiver_locks,
- &state,
- bp,
- message,
- token
-#ifdef USE_VM_PROBES
- , NIL
#endif
- );
- BM_SWAP_TIMER(send,system);
}
- return res;
+
+ ERL_MESSAGE_TOKEN(mp) = token;
+#ifdef USE_VM_PROBES
+ ERL_MESSAGE_DT_UTAG(mp) = utag;
+#endif
+ res = queue_message(receiver,
+ &receiver_state,
+ *receiver_locks,
+ mp, message,
+ sender->common.id);
+
+ return res;
}
+
/*
* This function delivers an EXIT message to a process
* which is trapping EXITs.
@@ -1075,46 +850,542 @@ erts_deliver_exit_message(Eterm from, Process *to, ErtsProcLocks *to_locksp,
Uint sz_from;
Eterm* hp;
Eterm temptoken;
- ErlHeapFragment* bp = NULL;
-
- if (token != NIL
-#ifdef USE_VM_PROBES
- && token != am_have_dt_utag
+ ErtsMessage* mp;
+ ErlOffHeap *ohp;
+#ifdef SHCOPY_SEND
+ erts_shcopy_t info;
#endif
- ) {
+ if (have_seqtrace(token)) {
ASSERT(is_tuple(token));
- sz_reason = size_object(reason);
sz_token = size_object(token);
sz_from = size_object(from);
- bp = new_message_buffer(sz_reason + sz_from + sz_token + 4);
- hp = bp->mem;
- mess = copy_struct(reason, sz_reason, &hp, &bp->off_heap);
- from_copy = copy_struct(from, sz_from, &hp, &bp->off_heap);
+#ifdef SHCOPY_SEND
+ INITIALIZE_SHCOPY(info);
+ sz_reason = copy_shared_calculate(reason, &info);
+#else
+ sz_reason = size_object(reason);
+#endif
+ mp = erts_alloc_message_heap(to, to_locksp,
+ sz_reason + sz_from + sz_token + 4,
+ &hp, &ohp);
+#ifdef SHCOPY_SEND
+ mess = copy_shared_perform(reason, sz_reason, &info, &hp, ohp);
+ DESTROY_SHCOPY(info);
+#else
+ mess = copy_struct(reason, sz_reason, &hp, ohp);
+#endif
+ from_copy = copy_struct(from, sz_from, &hp, ohp);
save = TUPLE3(hp, am_EXIT, from_copy, mess);
hp += 4;
/* the trace token must in this case be updated by the caller */
seq_trace_output(token, save, SEQ_TRACE_SEND, to->common.id, NULL);
- temptoken = copy_struct(token, sz_token, &hp, &bp->off_heap);
- erts_queue_message(to, to_locksp, bp, save, temptoken);
+ temptoken = copy_struct(token, sz_token, &hp, ohp);
+ ERL_MESSAGE_TOKEN(mp) = temptoken;
+ erts_queue_message(to, *to_locksp, mp, save, am_system);
} else {
- ErlOffHeap *ohp;
- sz_reason = size_object(reason);
sz_from = IS_CONST(from) ? 0 : size_object(from);
+#ifdef SHCOPY_SEND
+ INITIALIZE_SHCOPY(info);
+ sz_reason = copy_shared_calculate(reason, &info);
+#else
+ sz_reason = size_object(reason);
+#endif
+ mp = erts_alloc_message_heap(to, to_locksp,
+ sz_reason+sz_from+4, &hp, &ohp);
- hp = erts_alloc_message_heap(sz_reason+sz_from+4,
- &bp,
- &ohp,
- to,
- to_locksp);
-
+#ifdef SHCOPY_SEND
+ mess = copy_shared_perform(reason, sz_reason, &info, &hp, ohp);
+ DESTROY_SHCOPY(info);
+#else
mess = copy_struct(reason, sz_reason, &hp, ohp);
+#endif
from_copy = (IS_CONST(from)
? from
: copy_struct(from, sz_from, &hp, ohp));
save = TUPLE3(hp, am_EXIT, from_copy, mess);
- erts_queue_message(to, to_locksp, bp, save, NIL);
+ erts_queue_message(to, *to_locksp, mp, save, am_system);
+ }
+}
+
+void erts_save_message_in_proc(Process *p, ErtsMessage *msgp)
+{
+ ErlHeapFragment *hfp;
+
+ if (msgp->data.attached == ERTS_MSG_COMBINED_HFRAG)
+ hfp = &msgp->hfrag;
+ else if (msgp->data.attached) {
+ hfp = msgp->data.heap_frag;
+ }
+ else {
+ erts_free_message(msgp);
+ return; /* Nothing to save */
+ }
+
+ while (1) {
+ struct erl_off_heap_header *ohhp = hfp->off_heap.first;
+ if (ohhp) {
+ for ( ; ohhp->next; ohhp = ohhp->next)
+ ;
+ ohhp->next = p->off_heap.first;
+ p->off_heap.first = hfp->off_heap.first;
+ hfp->off_heap.first = NULL;
+ }
+ p->off_heap.overhead += hfp->off_heap.overhead;
+ hfp->off_heap.overhead = 0;
+ p->mbuf_sz += hfp->used_size;
+
+ if (!hfp->next)
+ break;
+ hfp = hfp->next;
+ }
+
+ msgp->next = p->msg_frag;
+ p->msg_frag = msgp;
+}
+
+Sint
+erts_move_messages_off_heap(Process *c_p)
+{
+ int reds = 1;
+ /*
+ * Move all messages off heap. This *only* occurs when the
+ * process had off heap message disabled and just enabled
+ * it...
+ */
+ ErtsMessage *mp;
+
+ reds += c_p->msg.len / 10;
+
+ ASSERT(erts_smp_atomic32_read_nob(&c_p->state)
+ & ERTS_PSFLG_OFF_HEAP_MSGQ);
+ ASSERT(c_p->flags & F_OFF_HEAP_MSGQ_CHNG);
+
+ for (mp = c_p->msg.first; mp; mp = mp->next) {
+ Uint msg_sz, token_sz;
+#ifdef USE_VM_PROBES
+ Uint utag_sz;
+#endif
+ Eterm *hp;
+ ErlHeapFragment *hfrag;
+
+ if (mp->data.attached)
+ continue;
+
+ if (is_immed(ERL_MESSAGE_TERM(mp))
+#ifdef USE_VM_PROBES
+ && is_immed(ERL_MESSAGE_DT_UTAG(mp))
+#endif
+ && is_not_immed(ERL_MESSAGE_TOKEN(mp)))
+ continue;
+
+ /*
+ * The message refers into the heap. Copy the message
+ * from the heap into a heap fragment and attach
+ * it to the message...
+ */
+ msg_sz = size_object(ERL_MESSAGE_TERM(mp));
+#ifdef USE_VM_PROBES
+ utag_sz = size_object(ERL_MESSAGE_DT_UTAG(mp));
+#endif
+ token_sz = size_object(ERL_MESSAGE_TOKEN(mp));
+
+ hfrag = new_message_buffer(msg_sz
+#ifdef USE_VM_PROBES
+ + utag_sz
+#endif
+ + token_sz);
+ hp = hfrag->mem;
+ if (is_not_immed(ERL_MESSAGE_TERM(mp)))
+ ERL_MESSAGE_TERM(mp) = copy_struct(ERL_MESSAGE_TERM(mp),
+ msg_sz, &hp,
+ &hfrag->off_heap);
+ if (is_not_immed(ERL_MESSAGE_TOKEN(mp)))
+ ERL_MESSAGE_TOKEN(mp) = copy_struct(ERL_MESSAGE_TOKEN(mp),
+ token_sz, &hp,
+ &hfrag->off_heap);
+#ifdef USE_VM_PROBES
+ if (is_not_immed(ERL_MESSAGE_DT_UTAG(mp)))
+ ERL_MESSAGE_DT_UTAG(mp) = copy_struct(ERL_MESSAGE_DT_UTAG(mp),
+ utag_sz, &hp,
+ &hfrag->off_heap);
+#endif
+ mp->data.heap_frag = hfrag;
+ reds += 1;
+ }
+
+ return reds;
+}
+
+Sint
+erts_complete_off_heap_message_queue_change(Process *c_p)
+{
+ int reds = 1;
+
+ ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(c_p));
+ ASSERT(c_p->flags & F_OFF_HEAP_MSGQ_CHNG);
+ ASSERT(erts_smp_atomic32_read_nob(&c_p->state) & ERTS_PSFLG_OFF_HEAP_MSGQ);
+
+ /*
+ * This job was first initiated when the process changed to off heap
+ * message queue management. Since then ERTS_PSFLG_OFF_HEAP_MSGQ
+ * has been set. However, the management state might have been changed
+ * again (multiple times) since then. Check users last requested state
+ * (the flags F_OFF_HEAP_MSGQ, and F_ON_HEAP_MSGQ), and make the state
+ * consistent with that.
+ */
+
+ if (!(c_p->flags & F_OFF_HEAP_MSGQ))
+ erts_smp_atomic32_read_band_nob(&c_p->state,
+ ~ERTS_PSFLG_OFF_HEAP_MSGQ);
+ else {
+ reds += 2;
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ);
+ ERTS_SMP_MSGQ_MV_INQ2PRIVQ(c_p);
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ);
+ reds += erts_move_messages_off_heap(c_p);
+ }
+ c_p->flags &= ~F_OFF_HEAP_MSGQ_CHNG;
+ return reds;
+}
+
+typedef struct {
+ Eterm pid;
+ ErtsThrPrgrLaterOp lop;
+} ErtsChangeOffHeapMessageQueue;
+
+static void
+change_off_heap_msgq(void *vcohmq)
+{
+ ErtsChangeOffHeapMessageQueue *cohmq;
+ /*
+ * Now we've waited thread progress which ensures that all
+ * messages to the process are enqueued off heap. Schedule
+ * completion of this change as a system task on the process
+ * itself. This in order to avoid lock contention on its
+ * main lock. We will be called in
+ * erts_complete_off_heap_message_queue_change() (above) when
+ * the system task has been selected for execution.
+ */
+ cohmq = (ErtsChangeOffHeapMessageQueue *) vcohmq;
+ erts_schedule_complete_off_heap_message_queue_change(cohmq->pid);
+ erts_free(ERTS_ALC_T_MSGQ_CHNG, vcohmq);
+}
+
+Eterm
+erts_change_message_queue_management(Process *c_p, Eterm new_state)
+{
+ Eterm res;
+
+#ifdef DEBUG
+ if (c_p->flags & F_OFF_HEAP_MSGQ) {
+ ASSERT(erts_smp_atomic32_read_nob(&c_p->state)
+ & ERTS_PSFLG_OFF_HEAP_MSGQ);
+ }
+ else {
+ if (c_p->flags & F_OFF_HEAP_MSGQ_CHNG) {
+ ASSERT(erts_smp_atomic32_read_nob(&c_p->state)
+ & ERTS_PSFLG_OFF_HEAP_MSGQ);
+ }
+ else {
+ ASSERT(!(erts_smp_atomic32_read_nob(&c_p->state)
+ & ERTS_PSFLG_OFF_HEAP_MSGQ));
+ }
+ }
+#endif
+
+ switch (c_p->flags & (F_OFF_HEAP_MSGQ|F_ON_HEAP_MSGQ)) {
+
+ case F_OFF_HEAP_MSGQ:
+ res = am_off_heap;
+
+ switch (new_state) {
+ case am_off_heap:
+ break;
+ case am_on_heap:
+ c_p->flags |= F_ON_HEAP_MSGQ;
+ c_p->flags &= ~F_OFF_HEAP_MSGQ;
+ erts_smp_atomic32_read_bor_nob(&c_p->state,
+ ERTS_PSFLG_ON_HEAP_MSGQ);
+ /*
+ * We are not allowed to clear ERTS_PSFLG_OFF_HEAP_MSGQ
+ * if a off heap change is ongoing. It will be adjusted
+ * when the change completes...
+ */
+ if (!(c_p->flags & F_OFF_HEAP_MSGQ_CHNG)) {
+ /* Safe to clear ERTS_PSFLG_OFF_HEAP_MSGQ... */
+ erts_smp_atomic32_read_band_nob(&c_p->state,
+ ~ERTS_PSFLG_OFF_HEAP_MSGQ);
+ }
+ break;
+ default:
+ res = THE_NON_VALUE; /* badarg */
+ break;
+ }
+ break;
+
+ case F_ON_HEAP_MSGQ:
+ res = am_on_heap;
+
+ switch (new_state) {
+ case am_on_heap:
+ break;
+ case am_off_heap:
+ c_p->flags &= ~F_ON_HEAP_MSGQ;
+ erts_smp_atomic32_read_band_nob(&c_p->state,
+ ~ERTS_PSFLG_ON_HEAP_MSGQ);
+ goto change_to_off_heap;
+ default:
+ res = THE_NON_VALUE; /* badarg */
+ break;
+ }
+ break;
+
+ default:
+ res = am_error;
+ ERTS_INTERNAL_ERROR("Inconsistent message queue management state");
+ break;
+ }
+
+ return res;
+
+change_to_off_heap:
+
+ c_p->flags |= F_OFF_HEAP_MSGQ;
+
+ /*
+ * We do not have to schedule a change if
+ * we have an ongoing off heap change...
+ */
+ if (!(c_p->flags & F_OFF_HEAP_MSGQ_CHNG)) {
+ ErtsChangeOffHeapMessageQueue *cohmq;
+ /*
+ * Need to set ERTS_PSFLG_OFF_HEAP_MSGQ and wait
+ * thread progress before completing the change in
+ * order to ensure that all senders observe that
+ * messages should be passed off heap. When the
+ * change has completed, GC does not need to inspect
+ * the message queue at all.
+ */
+ erts_smp_atomic32_read_bor_nob(&c_p->state,
+ ERTS_PSFLG_OFF_HEAP_MSGQ);
+ c_p->flags |= F_OFF_HEAP_MSGQ_CHNG;
+ cohmq = erts_alloc(ERTS_ALC_T_MSGQ_CHNG,
+ sizeof(ErtsChangeOffHeapMessageQueue));
+ cohmq->pid = c_p->common.id;
+ erts_schedule_thr_prgr_later_op(change_off_heap_msgq,
+ (void *) cohmq,
+ &cohmq->lop);
+ }
+
+ return res;
+}
+
+int
+erts_decode_dist_message(Process *proc, ErtsProcLocks proc_locks,
+ ErtsMessage *msgp, int force_off_heap)
+{
+ ErtsHeapFactory factory;
+ Eterm msg;
+ ErlHeapFragment *bp;
+ Sint need;
+ int decode_in_heap_frag;
+
+ decode_in_heap_frag = (force_off_heap
+ || !(proc_locks & ERTS_PROC_LOCK_MAIN)
+ || (proc->flags & F_OFF_HEAP_MSGQ));
+
+ if (msgp->data.dist_ext->heap_size >= 0)
+ need = msgp->data.dist_ext->heap_size;
+ else {
+ need = erts_decode_dist_ext_size(msgp->data.dist_ext);
+ if (need < 0) {
+ /* bad msg; remove it... */
+ if (is_not_immed(ERL_MESSAGE_TOKEN(msgp))) {
+ bp = erts_dist_ext_trailer(msgp->data.dist_ext);
+ erts_cleanup_offheap(&bp->off_heap);
+ }
+ erts_free_dist_ext_copy(msgp->data.dist_ext);
+ msgp->data.dist_ext = NULL;
+ return 0;
+ }
+
+ msgp->data.dist_ext->heap_size = need;
+ }
+
+ if (is_not_immed(ERL_MESSAGE_TOKEN(msgp))) {
+ bp = erts_dist_ext_trailer(msgp->data.dist_ext);
+ need += bp->used_size;
}
+
+ if (decode_in_heap_frag)
+ erts_factory_heap_frag_init(&factory, new_message_buffer(need));
+ else
+ erts_factory_proc_prealloc_init(&factory, proc, need);
+
+ ASSERT(msgp->data.dist_ext->heap_size >= 0);
+ if (is_not_immed(ERL_MESSAGE_TOKEN(msgp))) {
+ ErlHeapFragment *heap_frag;
+ heap_frag = erts_dist_ext_trailer(msgp->data.dist_ext);
+ ERL_MESSAGE_TOKEN(msgp) = copy_struct(ERL_MESSAGE_TOKEN(msgp),
+ heap_frag->used_size,
+ &factory.hp,
+ factory.off_heap);
+ erts_cleanup_offheap(&heap_frag->off_heap);
+ }
+
+ msg = erts_decode_dist_ext(&factory, msgp->data.dist_ext);
+ ERL_MESSAGE_TERM(msgp) = msg;
+ erts_free_dist_ext_copy(msgp->data.dist_ext);
+ msgp->data.attached = NULL;
+
+ if (is_non_value(msg)) {
+ erts_factory_undo(&factory);
+ return 0;
+ }
+
+ erts_factory_trim_and_close(&factory, msgp->m,
+ ERL_MESSAGE_REF_ARRAY_SZ);
+
+ ASSERT(!msgp->data.heap_frag);
+
+ if (decode_in_heap_frag)
+ msgp->data.heap_frag = factory.heap_frags;
+
+ return 1;
+}
+
+/*
+ * ERTS_INSPECT_MSGQ_KEEP_OH_MSGS == 0 will move off heap messages
+ * into the heap of the inspected process if off_heap_message_queue
+ * is false when process_info(_, messages) is called. That is, the
+ * following GC will have more data in the rootset compared to the
+ * scenario when process_info(_, messages) had not been called.
+ *
+ * ERTS_INSPECT_MSGQ_KEEP_OH_MSGS != 0 will keep off heap messages
+ * off heap when process_info(_, messages) is called regardless of
+ * the off_heap_message_queue setting of the process. That is, it
+ * will change the following execution of the process as little as
+ * possible.
+ */
+#define ERTS_INSPECT_MSGQ_KEEP_OH_MSGS 1
+
+Uint
+erts_prep_msgq_for_inspection(Process *c_p, Process *rp,
+ ErtsProcLocks rp_locks, ErtsMessageInfo *mip)
+{
+ Uint tot_heap_size;
+ ErtsMessage* mp;
+ Sint i;
+ int self_on_heap;
+
+ /*
+ * Prepare the message queue for inspection
+ * by process_info().
+ *
+ *
+ * - Decode all messages on external format
+ * - Remove all corrupt dist messages from queue
+ * - Save pointer to, and heap size need of each
+ * message in the mip array.
+ * - Return total heap size need for all messages
+ * that needs to be copied.
+ *
+ * If ERTS_INSPECT_MSGQ_KEEP_OH_MSGS == 0:
+ * - In case off heap messages is disabled and
+ * we are inspecting our own queue, move all
+ * off heap data into the heap.
+ */
+
+ self_on_heap = c_p == rp && !(c_p->flags & F_OFF_HEAP_MSGQ);
+
+ tot_heap_size = 0;
+ i = 0;
+ mp = rp->msg.first;
+ while (mp) {
+ Eterm msg = ERL_MESSAGE_TERM(mp);
+
+ mip[i].size = 0;
+
+ if (is_non_value(msg)) {
+ /* Dist message on external format; decode it... */
+ if (mp->data.attached)
+ erts_decode_dist_message(rp, rp_locks, mp,
+ ERTS_INSPECT_MSGQ_KEEP_OH_MSGS);
+
+ msg = ERL_MESSAGE_TERM(mp);
+
+ if (is_non_value(msg)) {
+ ErtsMessage **mpp;
+ ErtsMessage *bad_mp = mp;
+ /*
+ * Bad distribution message; remove
+ * it from the queue...
+ */
+ ASSERT(!mp->data.attached);
+
+ mpp = i == 0 ? &rp->msg.first : &mip[i-1].msgp->next;
+
+ ASSERT(*mpp == bad_mp);
+
+ erts_msgq_update_internal_pointers(&rp->msg, mpp, &bad_mp->next);
+
+ mp = mp->next;
+ *mpp = mp;
+ rp->msg.len--;
+ bad_mp->next = NULL;
+ erts_cleanup_messages(bad_mp);
+ continue;
+ }
+ }
+
+ ASSERT(is_value(msg));
+
+#if ERTS_INSPECT_MSGQ_KEEP_OH_MSGS
+ if (is_not_immed(msg) && (!self_on_heap || mp->data.attached)) {
+ Uint sz = size_object(msg);
+ mip[i].size = sz;
+ tot_heap_size += sz;
+ }
+#else
+ if (self_on_heap) {
+ if (mp->data.attached) {
+ ErtsMessage *tmp = NULL;
+ if (mp->data.attached != ERTS_MSG_COMBINED_HFRAG) {
+ erts_link_mbuf_to_proc(rp, mp->data.heap_frag);
+ mp->data.attached = NULL;
+ }
+ else {
+ /*
+ * Need to replace the message reference since
+ * we will get references to the message data
+ * from the heap...
+ */
+ ErtsMessage **mpp;
+ tmp = erts_alloc_message(0, NULL);
+ sys_memcpy((void *) tmp->m, (void *) mp->m,
+ sizeof(Eterm)*ERL_MESSAGE_REF_ARRAY_SZ);
+ mpp = i == 0 ? &rp->msg.first : &mip[i-1].msgp->next;
+ erts_msgq_replace_msg_ref(&rp->msg, tmp, mpp);
+ erts_save_message_in_proc(rp, mp);
+ mp = tmp;
+ }
+ }
+ }
+ else if (is_not_immed(msg)) {
+ Uint sz = size_object(msg);
+ mip[i].size = sz;
+ tot_heap_size += sz;
+ }
+
+#endif
+
+ mip[i].msgp = mp;
+ i++;
+ mp = mp->next;
+ }
+
+ return tot_heap_size;
}
void erts_factory_proc_init(ErtsHeapFactory* factory,
@@ -1127,47 +1398,138 @@ void erts_factory_proc_prealloc_init(ErtsHeapFactory* factory,
Process* p,
Sint size)
{
+ ErlHeapFragment *bp = p->mbuf;
factory->mode = FACTORY_HALLOC;
factory->p = p;
factory->hp_start = HAlloc(p, size);
factory->hp = factory->hp_start;
factory->hp_end = factory->hp_start + size;
factory->off_heap = &p->off_heap;
+ factory->message = NULL;
factory->off_heap_saved.first = p->off_heap.first;
factory->off_heap_saved.overhead = p->off_heap.overhead;
- factory->heap_frags_saved = p->mbuf;
+ factory->heap_frags_saved = bp;
+ factory->heap_frags_saved_used = bp ? bp->used_size : 0;
factory->heap_frags = NULL; /* not used */
factory->alloc_type = 0; /* not used */
}
-void erts_factory_message_init(ErtsHeapFactory* factory,
- Process* rp,
- Eterm* hp,
- ErlHeapFragment* bp)
+void erts_factory_heap_frag_init(ErtsHeapFactory* factory,
+ ErlHeapFragment* bp)
{
- if (bp) {
- factory->mode = FACTORY_HEAP_FRAGS;
- factory->p = NULL;
- factory->hp_start = bp->mem;
- factory->hp = hp ? hp : bp->mem;
- factory->hp_end = bp->mem + bp->alloc_size;
- factory->off_heap = &bp->off_heap;
- factory->heap_frags = bp;
- factory->heap_frags_saved = bp;
- factory->alloc_type = ERTS_ALC_T_HEAP_FRAG;
- ASSERT(!bp->next);
+ factory->mode = FACTORY_HEAP_FRAGS;
+ factory->p = NULL;
+ factory->hp_start = bp->mem;
+ factory->hp = bp->mem;
+ factory->hp_end = bp->mem + bp->alloc_size;
+ factory->off_heap = &bp->off_heap;
+ factory->message = NULL;
+ factory->heap_frags = bp;
+ factory->heap_frags_saved = NULL;
+ factory->heap_frags_saved_used = 0;
+ factory->alloc_type = ERTS_ALC_T_HEAP_FRAG;
+ ASSERT(!bp->next);
+ factory->off_heap_saved.first = factory->off_heap->first;
+ factory->off_heap_saved.overhead = factory->off_heap->overhead;
+
+ ASSERT(factory->hp >= factory->hp_start && factory->hp <= factory->hp_end);
+}
+
+
+ErtsMessage *
+erts_factory_message_create(ErtsHeapFactory* factory,
+ Process *proc,
+ ErtsProcLocks *proc_locksp,
+ Uint sz)
+{
+ Eterm *hp;
+ ErlOffHeap *ohp;
+ ErtsMessage *msgp;
+ int on_heap;
+ erts_aint32_t state;
+
+ state = proc ? erts_smp_atomic32_read_nob(&proc->state) : 0;
+
+ if (state & ERTS_PSFLG_OFF_HEAP_MSGQ) {
+ msgp = erts_alloc_message(sz, &hp);
+ ohp = sz == 0 ? NULL : &msgp->hfrag.off_heap;
+ on_heap = 0;
+ }
+ else {
+ msgp = erts_try_alloc_message_on_heap(proc, &state,
+ proc_locksp,
+ sz, &hp, &ohp,
+ &on_heap);
+ }
+
+ if (on_heap) {
+ ERTS_SMP_ASSERT(*proc_locksp & ERTS_PROC_LOCK_MAIN);
+ ASSERT(ohp == &proc->off_heap);
+ factory->mode = FACTORY_HALLOC;
+ factory->p = proc;
+ factory->heap_frags_saved = proc->mbuf;
+ factory->heap_frags_saved_used = proc->mbuf ? proc->mbuf->used_size : 0;
+ }
+ else {
+ factory->mode = FACTORY_MESSAGE;
+ factory->p = NULL;
+ factory->heap_frags_saved = NULL;
+ factory->heap_frags_saved_used = 0;
+
+ if (msgp->data.attached == ERTS_MSG_COMBINED_HFRAG) {
+ ASSERT(!msgp->hfrag.next);
+ factory->heap_frags = NULL;
+ }
+ else {
+ ASSERT(!msgp->data.heap_frag
+ || !msgp->data.heap_frag->next);
+ factory->heap_frags = msgp->data.heap_frag;
+ }
+ }
+ factory->hp_start = hp;
+ factory->hp = hp;
+ factory->hp_end = hp + sz;
+ factory->message = msgp;
+ factory->off_heap = ohp;
+ factory->alloc_type = ERTS_ALC_T_HEAP_FRAG;
+ if (ohp) {
+ factory->off_heap_saved.first = ohp->first;
+ factory->off_heap_saved.overhead = ohp->overhead;
+ }
+ else {
+ factory->off_heap_saved.first = NULL;
+ factory->off_heap_saved.overhead = 0;
+ }
+
+ ASSERT(factory->hp >= factory->hp_start && factory->hp <= factory->hp_end);
+
+ return msgp;
+}
+
+void erts_factory_selfcontained_message_init(ErtsHeapFactory* factory,
+ ErtsMessage *msgp,
+ Eterm *hp)
+{
+ ErlHeapFragment* bp;
+ if (msgp->data.attached == ERTS_MSG_COMBINED_HFRAG) {
+ bp = &msgp->hfrag;
+ factory->heap_frags = NULL;
}
else {
- factory->mode = FACTORY_HALLOC;
- factory->p = rp;
- factory->hp_start = hp;
- factory->hp = hp;
- factory->hp_end = HEAP_TOP(rp);
- factory->off_heap = &rp->off_heap;
- factory->heap_frags_saved = rp->mbuf;
- factory->heap_frags = NULL; /* not used */
- factory->alloc_type = 0; /* not used */
+ bp = msgp->data.heap_frag;
+ factory->heap_frags = bp;
}
+ factory->mode = FACTORY_MESSAGE;
+ factory->p = NULL;
+ factory->hp_start = bp->mem;
+ factory->hp = hp;
+ factory->hp_end = bp->mem + bp->alloc_size;
+ factory->message = msgp;
+ factory->off_heap = &bp->off_heap;
+ factory->heap_frags_saved = NULL;
+ factory->heap_frags_saved_used = 0;
+ factory->alloc_type = ERTS_ALC_T_HEAP_FRAG;
+ ASSERT(!bp->next);
factory->off_heap_saved.first = factory->off_heap->first;
factory->off_heap_saved.overhead = factory->off_heap->overhead;
@@ -1250,9 +1612,17 @@ static void reserve_heap(ErtsHeapFactory* factory, Uint need, Uint xtra)
factory->hp_end = factory->hp + need;
return;
- case FACTORY_HEAP_FRAGS:
- case FACTORY_TMP:
- bp = factory->heap_frags;
+ case FACTORY_MESSAGE:
+ if (!factory->heap_frags) {
+ ASSERT(factory->message->data.attached == ERTS_MSG_COMBINED_HFRAG);
+ bp = &factory->message->hfrag;
+ }
+ else {
+ /* Fall through */
+ case FACTORY_HEAP_FRAGS:
+ case FACTORY_TMP:
+ bp = factory->heap_frags;
+ }
if (bp) {
ASSERT(factory->hp > bp->mem);
@@ -1290,8 +1660,23 @@ void erts_factory_close(ErtsHeapFactory* factory)
HRelease(factory->p, factory->hp_end, factory->hp);
break;
- case FACTORY_HEAP_FRAGS:
- bp = factory->heap_frags;
+ case FACTORY_MESSAGE:
+ if (!factory->heap_frags) {
+ if (factory->message->data.attached == ERTS_MSG_COMBINED_HFRAG)
+ bp = &factory->message->hfrag;
+ else
+ bp = NULL;
+ }
+ else {
+ if (factory->message->data.attached == ERTS_MSG_COMBINED_HFRAG)
+ factory->message->hfrag.next = factory->heap_frags;
+ else
+ factory->message->data.heap_frag = factory->heap_frags;
+
+ /* Fall through */
+ case FACTORY_HEAP_FRAGS:
+ bp = factory->heap_frags;
+ }
if (bp) {
ASSERT(factory->hp >= bp->mem);
@@ -1315,17 +1700,47 @@ void erts_factory_close(ErtsHeapFactory* factory)
void erts_factory_trim_and_close(ErtsHeapFactory* factory,
Eterm *brefs, Uint brefs_size)
{
- if (factory->mode == FACTORY_HEAP_FRAGS) {
- ErlHeapFragment* bp = factory->heap_frags;
+ ErlHeapFragment *bp;
+
+ switch (factory->mode) {
+ case FACTORY_MESSAGE: {
+ ErtsMessage *mp = factory->message;
+ if (mp->data.attached == ERTS_MSG_COMBINED_HFRAG) {
+ if (!factory->heap_frags) {
+ Uint sz = factory->hp - factory->hp_start;
+ mp = erts_shrink_message(mp, sz, brefs, brefs_size);
+ factory->message = mp;
+ factory->mode = FACTORY_CLOSED;
+ return;
+ }
+ /*else we don't trim multi fragmented messages for now (off_heap...) */
+ break;
+ }
+ /* Fall through... */
+ }
+ case FACTORY_HEAP_FRAGS:
+ bp = factory->heap_frags;
+ if (!bp)
+ break;
if (bp->next == NULL) {
Uint used_sz = factory->hp - bp->mem;
ASSERT(used_sz <= bp->alloc_size);
- factory->heap_frags = erts_resize_message_buffer(bp, used_sz,
- brefs, brefs_size);
+ if (used_sz > 0)
+ bp = erts_resize_message_buffer(bp, used_sz,
+ brefs, brefs_size);
+ else {
+ free_message_buffer(bp);
+ bp = NULL;
+ }
+ factory->heap_frags = bp;
+ if (factory->mode == FACTORY_MESSAGE)
+ factory->message->data.heap_frag = bp;
factory->mode = FACTORY_CLOSED;
return;
}
- /*else we don't trim multi fragmented messages for now */
+ /*else we don't trim multi fragmented messages for now (off_heap...) */
+ default:
+ break;
}
erts_factory_close(factory);
}
@@ -1373,28 +1788,33 @@ void erts_factory_undo(ErtsHeapFactory* factory)
/* Rollback heap top
*/
- if (factory->heap_frags_saved == NULL) { /* No heap frags when we started */
- ASSERT(factory->hp_start >= HEAP_START(factory->p));
- ASSERT(factory->hp_start <= HEAP_LIMIT(factory->p));
- HEAP_TOP(factory->p) = factory->hp_start;
- }
- else {
+ if (HEAP_START(factory->p) <= factory->hp_start
+ && factory->hp_start <= HEAP_LIMIT(factory->p)) {
+ HEAP_TOP(factory->p) = factory->hp_start;
+ }
+
+ /* Fix last heap frag */
+ if (factory->heap_frags_saved) {
ASSERT(factory->heap_frags_saved == factory->p->mbuf);
- if (factory->hp_start == factory->heap_frags_saved->mem) {
+ if (factory->hp_start != factory->heap_frags_saved->mem)
+ factory->heap_frags_saved->used_size = factory->heap_frags_saved_used;
+ else {
factory->p->mbuf = factory->p->mbuf->next;
ERTS_HEAP_FREE(ERTS_ALC_T_HEAP_FRAG, factory->heap_frags_saved,
ERTS_HEAP_FRAG_SIZE(factory->heap_frags_saved->alloc_size));
}
- else if (factory->hp_start != factory->hp_end) {
- unsigned remains = factory->hp_start - factory->heap_frags_saved->mem;
- ASSERT(remains > 0 && remains < factory->heap_frags_saved->used_size);
- factory->heap_frags_saved->used_size = remains;
- }
}
}
break;
+ case FACTORY_MESSAGE:
+ if (factory->message->data.attached == ERTS_MSG_COMBINED_HFRAG)
+ factory->message->hfrag.next = factory->heap_frags;
+ else
+ factory->message->data.heap_frag = factory->heap_frags;
+ erts_cleanup_messages(factory->message);
+ break;
case FACTORY_TMP:
case FACTORY_HEAP_FRAGS:
erts_cleanup_offheap(factory->off_heap);
@@ -1423,3 +1843,19 @@ void erts_factory_undo(ErtsHeapFactory* factory)
#endif
}
+Uint
+erts_mbuf_size(Process *p)
+{
+ Uint sz = 0;
+ ErlHeapFragment* bp;
+ ErtsMessage* mp;
+
+ for (bp = p->mbuf; bp; bp = bp->next)
+ sz += bp->used_size;
+
+ for (mp = p->msg_frag; mp; mp = mp->next)
+ for (bp = erts_message_to_heap_frag(mp); bp; bp = bp->next)
+ sz += bp->used_size;
+
+ return sz;
+}
diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h
index 92ba3e571c..42ed14e69c 100644
--- a/erts/emulator/beam/erl_message.h
+++ b/erts/emulator/beam/erl_message.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2012. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,8 @@
struct proc_bin;
struct external_thing_;
+typedef struct erl_mesg ErtsMessage;
+
/*
* This struct represents data that must be updated by structure copy,
* but is stored outside of any heap.
@@ -32,9 +34,6 @@ struct external_thing_;
struct erl_off_heap_header {
Eterm thing_word;
Uint size;
-#if HALFWORD_HEAP
- void* dummy_ptr_padding__;
-#endif
struct erl_off_heap_header* next;
};
@@ -57,6 +56,7 @@ typedef struct {
enum {
FACTORY_CLOSED = 0,
FACTORY_HALLOC,
+ FACTORY_MESSAGE,
FACTORY_HEAP_FRAGS,
FACTORY_STATIC,
FACTORY_TMP
@@ -65,8 +65,10 @@ typedef struct {
Eterm* hp_start;
Eterm* hp;
Eterm* hp_end;
+ ErtsMessage *message;
struct erl_heap_fragment* heap_frags;
struct erl_heap_fragment* heap_frags_saved;
+ Uint heap_frags_saved_used;
ErlOffHeap* off_heap;
ErlOffHeap off_heap_saved;
Uint32 alloc_type;
@@ -74,7 +76,10 @@ typedef struct {
void erts_factory_proc_init(ErtsHeapFactory*, Process*);
void erts_factory_proc_prealloc_init(ErtsHeapFactory*, Process*, Sint size);
-void erts_factory_message_init(ErtsHeapFactory*, Process*, Eterm* hp, struct erl_heap_fragment*);
+void erts_factory_heap_frag_init(ErtsHeapFactory*, struct erl_heap_fragment*);
+ErtsMessage *erts_factory_message_create(ErtsHeapFactory *, Process *,
+ ErtsProcLocks *, Uint sz);
+void erts_factory_selfcontained_message_init(ErtsHeapFactory*, ErtsMessage *, Eterm *);
void erts_factory_static_init(ErtsHeapFactory*, Eterm* hp, Uint size, ErlOffHeap*);
void erts_factory_tmp_init(ErtsHeapFactory*, Eterm* hp, Uint size, Uint32 atype);
void erts_factory_dummy_init(ErtsHeapFactory*);
@@ -96,6 +101,8 @@ void erts_factory_undo(ErtsHeapFactory*);
#include "external.h"
#include "erl_process.h"
+#define ERTS_INVALID_HFRAG_PTR ((ErlHeapFragment *) ~((UWord) 7))
+
/*
* This struct represents a heap fragment, which is used when there
* isn't sufficient room in the process heap and we can't do a GC.
@@ -105,38 +112,58 @@ typedef struct erl_heap_fragment ErlHeapFragment;
struct erl_heap_fragment {
ErlHeapFragment* next; /* Next heap fragment */
ErlOffHeap off_heap; /* Offset heap data. */
- unsigned alloc_size; /* Size in (half)words of mem */
- unsigned used_size; /* With terms to be moved to heap by GC */
+ Uint alloc_size; /* Size in (half)words of mem */
+ Uint used_size; /* With terms to be moved to heap by GC */
Eterm mem[1]; /* Data */
};
-typedef struct erl_mesg {
- struct erl_mesg* next; /* Next message */
- union {
- ErtsDistExternal *dist_ext;
- ErlHeapFragment *heap_frag;
- void *attached;
- } data;
+/* m[0] = message, m[1] = seq trace token */
+#define ERL_MESSAGE_REF_ARRAY_SZ 2
+#define ERL_MESSAGE_TERM(mp) ((mp)->m[0])
+#define ERL_MESSAGE_TOKEN(mp) ((mp)->m[1])
+
#ifdef USE_VM_PROBES
- Eterm m[3]; /* m[0] = message, m[1] = seq trace token, m[3] = dynamic trace user tag */
+/* m[2] = dynamic trace user tag */
+#undef ERL_MESSAGE_REF_ARRAY_SZ
+#define ERL_MESSAGE_REF_ARRAY_SZ 3
+#define ERL_MESSAGE_DT_UTAG(mp) ((mp)->m[2])
#else
- Eterm m[2]; /* m[0] = message, m[1] = seq trace token */
#endif
-} ErlMessage;
-#define ERL_MESSAGE_TERM(mp) ((mp)->m[0])
-#define ERL_MESSAGE_TOKEN(mp) ((mp)->m[1])
#ifdef USE_VM_PROBES
-#define ERL_MESSAGE_DT_UTAG(mp) ((mp)->m[2])
+#define have_no_seqtrace(T) ((T) == NIL || (T) == am_have_dt_utag)
+#else
+#define have_no_seqtrace(T) ((T) == NIL)
#endif
+#define have_seqtrace(T) (!have_no_seqtrace(T))
+
+#define ERL_MESSAGE_REF_FIELDS__ \
+ ErtsMessage *next; /* Next message */ \
+ union { \
+ ErtsDistExternal *dist_ext; \
+ ErlHeapFragment *heap_frag; \
+ void *attached; \
+ } data; \
+ Eterm m[ERL_MESSAGE_REF_ARRAY_SZ]
+
+
+typedef struct erl_msg_ref__ {
+ ERL_MESSAGE_REF_FIELDS__;
+} ErtsMessageRef;
+
+struct erl_mesg {
+ ERL_MESSAGE_REF_FIELDS__;
+
+ ErlHeapFragment hfrag;
+};
/* Size of default message buffer (erl_message.c) */
#define ERL_MESSAGE_BUF_SZ 500
typedef struct {
- ErlMessage* first;
- ErlMessage** last; /* point to the last next pointer */
- ErlMessage** save;
+ ErtsMessage* first;
+ ErtsMessage** last; /* point to the last next pointer */
+ ErtsMessage** save;
Sint len; /* queue length */
/*
@@ -144,65 +171,81 @@ typedef struct {
* recv_set/1 instructions.
*/
BeamInstr* mark; /* address to rec_loop/2 instruction */
- ErlMessage** saved_last; /* saved last pointer */
+ ErtsMessage** saved_last; /* saved last pointer */
} ErlMessageQueue;
#ifdef ERTS_SMP
typedef struct {
- ErlMessage* first;
- ErlMessage** last; /* point to the last next pointer */
+ ErtsMessage* first;
+ ErtsMessage** last; /* point to the last next pointer */
Sint len; /* queue length */
} ErlMessageInQueue;
+typedef struct erl_trace_message_queue__ {
+ struct erl_trace_message_queue__ *next; /* point to the next receiver */
+ Eterm receiver;
+ ErtsMessage* first;
+ ErtsMessage** last; /* point to the last next pointer */
+ Sint len; /* queue length */
+} ErlTraceMessageQueue;
+
#endif
/* Get "current" message */
#define PEEK_MESSAGE(p) (*(p)->msg.save)
+#ifdef USE_VM_PROBES
+#define LINK_MESSAGE_DTAG(mp, dt) ERL_MESSAGE_DT_UTAG(mp) = dt
+#else
+#define LINK_MESSAGE_DTAG(mp, dt)
+#endif
-/* Add message last in private message queue */
-#define LINK_MESSAGE_PRIVQ(p, mp) do { \
- *(p)->msg.last = (mp); \
- (p)->msg.last = &(mp)->next; \
- (p)->msg.len++; \
-} while (0)
-
+#define LINK_MESSAGE_IMPL(p, first_msg, last_msg, num_msgs, where) do { \
+ *(p)->where.last = (first_msg); \
+ (p)->where.last = (last_msg); \
+ (p)->where.len += (num_msgs); \
+ } while(0)
#ifdef ERTS_SMP
-/* Move in message queue to end of private message queue */
-#define ERTS_SMP_MSGQ_MV_INQ2PRIVQ(P) \
-do { \
- if ((P)->msg_inq.first) { \
- *(P)->msg.last = (P)->msg_inq.first; \
- (P)->msg.last = (P)->msg_inq.last; \
- (P)->msg.len += (P)->msg_inq.len; \
- (P)->msg_inq.first = NULL; \
- (P)->msg_inq.last = &(P)->msg_inq.first; \
- (P)->msg_inq.len = 0; \
- } \
-} while (0)
-
-/* Add message last in message queue */
-#define LINK_MESSAGE(p, mp) do { \
- *(p)->msg_inq.last = (mp); \
- (p)->msg_inq.last = &(mp)->next; \
- (p)->msg_inq.len++; \
-} while(0)
+/* Add message last in private message queue */
+#define LINK_MESSAGE_PRIVQ(p, first_msg, last_msg, len) \
+ do { \
+ LINK_MESSAGE_IMPL(p, first_msg, last_msg, len, msg); \
+ } while (0)
+
+/* Add message last_msg in message queue */
+#define LINK_MESSAGE(p, first_msg, last_msg, len) \
+ LINK_MESSAGE_IMPL(p, first_msg, last_msg, len, msg_inq)
+
+#define ERTS_SMP_MSGQ_MV_INQ2PRIVQ(p) \
+ do { \
+ if (p->msg_inq.first) { \
+ *p->msg.last = p->msg_inq.first; \
+ p->msg.last = p->msg_inq.last; \
+ p->msg.len += p->msg_inq.len; \
+ p->msg_inq.first = NULL; \
+ p->msg_inq.last = &p->msg_inq.first; \
+ p->msg_inq.len = 0; \
+ } \
+ } while (0)
#else
-#define ERTS_SMP_MSGQ_MV_INQ2PRIVQ(P)
+#define ERTS_SMP_MSGQ_MV_INQ2PRIVQ(p)
-/* Add message last in message queue */
-#define LINK_MESSAGE(p, mp) LINK_MESSAGE_PRIVQ((p), (mp))
+/* Add message last_msg in message queue */
+#define LINK_MESSAGE(p, first_msg, last_msg, len) \
+ do { \
+ LINK_MESSAGE_IMPL(p, first_msg, last_msg, len, msg); \
+ } while(0)
#endif
/* Unlink current message */
#define UNLINK_MESSAGE(p,msgp) do { \
- ErlMessage* __mp = (msgp)->next; \
+ ErtsMessage* __mp = (msgp)->next; \
*(p)->msg.save = __mp; \
(p)->msg.len--; \
if (__mp == NULL) \
@@ -218,98 +261,190 @@ do { \
#define SAVE_MESSAGE(p) \
(p)->msg.save = &(*(p)->msg.save)->next
-/*
- * ErtsMoveMsgAttachmentIntoProc() moves data attached to a message
- * onto the heap of a process. The attached data is the content of
- * the the message either on the internal format or on the external
- * format, and also possibly a seq trace token on the internal format.
- * If the message content is on the external format, the decode might
- * fail. If the decoding fails, ERL_MESSAGE_TERM(M) will contain
- * THE_NON_VALUE. That is, ERL_MESSAGE_TERM(M) *has* to be checked
- * afterwards and taken care of appropriately.
- *
- * ErtsMoveMsgAttachmentIntoProc() will shallow copy to heap if
- * possible; otherwise, move to heap via garbage collection.
- *
- * ErtsMoveMsgAttachmentIntoProc() is used when receiveing messages
- * in process_main() and in hipe_check_get_msg().
- */
-
-#define ErtsMoveMsgAttachmentIntoProc(M, P, ST, HT, FC, SWPO, SWPI) \
-do { \
- if ((M)->data.attached) { \
- Uint need__ = erts_msg_attached_data_size((M)); \
- { SWPO ; } \
- if ((ST) - (HT) >= need__) { \
- ErtsHeapFactory factory__; \
- erts_factory_proc_prealloc_init(&factory__, (P), need__); \
- erts_move_msg_attached_data_to_heap(&factory__, (M)); \
- erts_factory_close(&factory__); \
- if ((P)->mbuf != NULL) { \
- /* Heap was exhausted by messages. This is a rare case */ \
- /* that can currently (OTP 18) only happen if hamts are */ \
- /* far exceeding the estimated heap size. Do GC. */ \
- (FC) -= erts_garbage_collect((P), 0, NULL, 0); \
- } \
- } \
- else { \
- (FC) -= erts_garbage_collect((P), 0, NULL, 0); \
- } \
- { SWPI ; } \
- ASSERT(!(M)->data.attached); \
- } \
-} while (0)
-
#define ERTS_SND_FLG_NO_SEQ_TRACE (((unsigned) 1) << 0)
#define ERTS_HEAP_FRAG_SIZE(DATA_WORDS) \
(sizeof(ErlHeapFragment) - sizeof(Eterm) + (DATA_WORDS)*sizeof(Eterm))
-#define ERTS_INIT_HEAP_FRAG(HEAP_FRAG_P, DATA_WORDS) \
-do { \
- (HEAP_FRAG_P)->next = NULL; \
- (HEAP_FRAG_P)->alloc_size = (DATA_WORDS); \
- (HEAP_FRAG_P)->used_size = (DATA_WORDS); \
- (HEAP_FRAG_P)->off_heap.first = NULL; \
- (HEAP_FRAG_P)->off_heap.overhead = 0; \
-} while (0)
+#define ERTS_INIT_HEAP_FRAG(HEAP_FRAG_P, USED_WORDS, DATA_WORDS) \
+ do { \
+ (HEAP_FRAG_P)->next = NULL; \
+ (HEAP_FRAG_P)->alloc_size = (DATA_WORDS); \
+ (HEAP_FRAG_P)->used_size = (USED_WORDS); \
+ (HEAP_FRAG_P)->off_heap.first = NULL; \
+ (HEAP_FRAG_P)->off_heap.overhead = 0; \
+ } while (0)
+
+#ifdef USE_VM_PROBES
+#define ERL_MESSAGE_DT_UTAG_INIT(MP) ERL_MESSAGE_DT_UTAG(MP) = NIL
+#else
+#define ERL_MESSAGE_DT_UTAG_INIT(MP) do{ } while (0)
+#endif
+
+#define ERTS_INIT_MESSAGE(MP) \
+ do { \
+ (MP)->next = NULL; \
+ ERL_MESSAGE_TERM(MP) = THE_NON_VALUE; \
+ ERL_MESSAGE_TOKEN(MP) = NIL; \
+ ERL_MESSAGE_DT_UTAG_INIT(MP); \
+ MP->data.attached = NULL; \
+ } while (0)
void init_message(void);
-void free_message(ErlMessage *);
ErlHeapFragment* new_message_buffer(Uint);
ErlHeapFragment* erts_resize_message_buffer(ErlHeapFragment *, Uint,
Eterm *, Uint);
void free_message_buffer(ErlHeapFragment *);
-void erts_queue_dist_message(Process*, ErtsProcLocks*, ErtsDistExternal *, Eterm);
-#ifdef USE_VM_PROBES
-void erts_queue_message_probe(Process*, ErtsProcLocks*, ErlHeapFragment*,
- Eterm message, Eterm seq_trace_token, Eterm dt_utag);
-#define erts_queue_message(RP,RL,BP,Msg,SEQ) \
- erts_queue_message_probe((RP),(RL),(BP),(Msg),(SEQ),NIL)
-#else
-void erts_queue_message(Process*, ErtsProcLocks*, ErlHeapFragment*,
- Eterm message, Eterm seq_trace_token);
-#define erts_queue_message_probe(RP,RL,BP,Msg,SEQ,TAG) \
- erts_queue_message((RP),(RL),(BP),(Msg),(SEQ))
-#endif
+void erts_queue_dist_message(Process*, ErtsProcLocks, ErtsDistExternal *, Eterm, Eterm);
+Sint erts_queue_message(Process*, ErtsProcLocks,ErtsMessage*, Eterm, Eterm);
+Sint erts_queue_messages(Process*, ErtsProcLocks,
+ ErtsMessage*, ErtsMessage**, Uint, Eterm);
void erts_deliver_exit_message(Eterm, Process*, ErtsProcLocks *, Eterm, Eterm);
Sint erts_send_message(Process*, Process*, ErtsProcLocks*, Eterm, unsigned);
void erts_link_mbuf_to_proc(Process *proc, ErlHeapFragment *bp);
-void erts_move_msg_mbuf_to_heap(Eterm**, ErlOffHeap*, ErlMessage *);
-
-Uint erts_msg_attached_data_size_aux(ErlMessage *msg);
-void erts_move_msg_attached_data_to_heap(ErtsHeapFactory*, ErlMessage *);
-Eterm erts_msg_distext2heap(Process *, ErtsProcLocks *, ErlHeapFragment **,
- Eterm *, ErtsDistExternal *);
+Uint erts_msg_attached_data_size_aux(ErtsMessage *msg);
void erts_cleanup_offheap(ErlOffHeap *offheap);
+void erts_save_message_in_proc(Process *p, ErtsMessage *msg);
+Sint erts_move_messages_off_heap(Process *c_p);
+Sint erts_complete_off_heap_message_queue_change(Process *c_p);
+Eterm erts_change_message_queue_management(Process *c_p, Eterm new_state);
+
+int erts_decode_dist_message(Process *, ErtsProcLocks, ErtsMessage *, int);
+void erts_cleanup_messages(ErtsMessage *mp);
+typedef struct {
+ Uint size;
+ ErtsMessage *msgp;
+} ErtsMessageInfo;
+
+Uint erts_prep_msgq_for_inspection(Process *c_p,
+ Process *rp,
+ ErtsProcLocks rp_locks,
+ ErtsMessageInfo *mip);
+
+void *erts_alloc_message_ref(void);
+void erts_free_message_ref(void *);
+
+#define ERTS_SMALL_FIX_MSG_SZ 10
+#define ERTS_MEDIUM_FIX_MSG_SZ 20
+#define ERTS_LARGE_FIX_MSG_SZ 30
+
+void *erts_alloc_small_message(void);
+void erts_free_small_message(void *mp);
+
+typedef struct {
+ ErtsMessage m;
+ Eterm data[ERTS_SMALL_FIX_MSG_SZ-1];
+} ErtsSmallFixSzMessage;
+
+typedef struct {
+ ErtsMessage m;
+ Eterm data[ERTS_MEDIUM_FIX_MSG_SZ-1];
+} ErtsMediumFixSzMessage;
+
+typedef struct {
+ ErtsMessage m;
+ Eterm data[ERTS_LARGE_FIX_MSG_SZ-1];
+} ErtsLargeFixSzMessage;
+
+ErtsMessage *erts_try_alloc_message_on_heap(Process *pp,
+ erts_aint32_t *psp,
+ ErtsProcLocks *plp,
+ Uint sz,
+ Eterm **hpp,
+ ErlOffHeap **ohpp,
+ int *on_heap_p);
+ErtsMessage *erts_realloc_shrink_message(ErtsMessage *mp, Uint sz,
+ Eterm *brefs, Uint brefs_size);
+
+ERTS_GLB_FORCE_INLINE ErtsMessage *erts_alloc_message(Uint sz, Eterm **hpp);
+ERTS_GLB_FORCE_INLINE ErtsMessage *erts_shrink_message(ErtsMessage *mp, Uint sz,
+ Eterm *brefs, Uint brefs_size);
+ERTS_GLB_FORCE_INLINE void erts_free_message(ErtsMessage *mp);
ERTS_GLB_INLINE Uint erts_used_frag_sz(const ErlHeapFragment*);
-ERTS_GLB_INLINE Uint erts_msg_attached_data_size(ErlMessage *msg);
+ERTS_GLB_INLINE Uint erts_msg_attached_data_size(ErtsMessage *msg);
+ERTS_GLB_INLINE void erts_msgq_update_internal_pointers(ErlMessageQueue *msgq,
+ ErtsMessage **newpp,
+ ErtsMessage **oldpp);
+ERTS_GLB_INLINE void erts_msgq_replace_msg_ref(ErlMessageQueue *msgq,
+ ErtsMessage *newp,
+ ErtsMessage **oldpp);
+
+#define ERTS_MSG_COMBINED_HFRAG ((void *) 0x1)
+
+#define erts_message_to_heap_frag(MP) \
+ (((MP)->data.attached == ERTS_MSG_COMBINED_HFRAG) ? \
+ &(MP)->hfrag : (MP)->data.heap_frag)
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_FORCE_INLINE ErtsMessage *erts_alloc_message(Uint sz, Eterm **hpp)
+{
+ ErtsMessage *mp;
+
+ if (sz == 0) {
+ mp = erts_alloc_message_ref();
+ ERTS_INIT_MESSAGE(mp);
+ if (hpp)
+ *hpp = NULL;
+ return mp;
+ }
+
+ mp = erts_alloc(ERTS_ALC_T_MSG,
+ sizeof(ErtsMessage) + (sz - 1)*sizeof(Eterm));
+
+ ERTS_INIT_MESSAGE(mp);
+ mp->data.attached = ERTS_MSG_COMBINED_HFRAG;
+ ERTS_INIT_HEAP_FRAG(&mp->hfrag, sz, sz);
+
+ if (hpp)
+ *hpp = &mp->hfrag.mem[0];
+
+ return mp;
+}
+
+ERTS_GLB_FORCE_INLINE ErtsMessage *
+erts_shrink_message(ErtsMessage *mp, Uint sz, Eterm *brefs, Uint brefs_size)
+{
+ if (sz == 0) {
+ ErtsMessage *nmp;
+ if (!mp->data.attached)
+ return mp;
+ ASSERT(mp->data.attached == ERTS_MSG_COMBINED_HFRAG);
+ nmp = erts_alloc_message_ref();
+#ifdef DEBUG
+ if (brefs && brefs_size) {
+ int i;
+ for (i = 0; i < brefs_size; i++)
+ ASSERT(is_non_value(brefs[i]) || is_immed(brefs[i]));
+ }
+#endif
+ erts_free(ERTS_ALC_T_MSG, mp);
+ return nmp;
+ }
+
+ ASSERT(mp->data.attached == ERTS_MSG_COMBINED_HFRAG);
+ ASSERT(mp->hfrag.used_size >= sz);
+
+ if (sz >= (mp->hfrag.alloc_size - mp->hfrag.alloc_size / 16)) {
+ mp->hfrag.used_size = sz;
+ return mp;
+ }
+
+ return erts_realloc_shrink_message(mp, sz, brefs, brefs_size);
+}
+
+ERTS_GLB_FORCE_INLINE void erts_free_message(ErtsMessage *mp)
+{
+ if (mp->data.attached != ERTS_MSG_COMBINED_HFRAG)
+ erts_free_message_ref(mp);
+ else
+ erts_free(ERTS_ALC_T_MSG, mp);
+}
+
ERTS_GLB_INLINE Uint erts_used_frag_sz(const ErlHeapFragment* bp)
{
Uint sz = 0;
@@ -319,11 +454,14 @@ ERTS_GLB_INLINE Uint erts_used_frag_sz(const ErlHeapFragment* bp)
return sz;
}
-ERTS_GLB_INLINE Uint erts_msg_attached_data_size(ErlMessage *msg)
+ERTS_GLB_INLINE Uint erts_msg_attached_data_size(ErtsMessage *msg)
{
ASSERT(msg->data.attached);
- if (is_value(ERL_MESSAGE_TERM(msg)))
- return erts_used_frag_sz(msg->data.heap_frag);
+ if (is_value(ERL_MESSAGE_TERM(msg))) {
+ ErlHeapFragment *bp;
+ bp = erts_message_to_heap_frag(msg);
+ return erts_used_frag_sz(bp);
+ }
else if (msg->data.dist_ext->heap_size < 0)
return erts_msg_attached_data_size_aux(msg);
else {
@@ -336,6 +474,40 @@ ERTS_GLB_INLINE Uint erts_msg_attached_data_size(ErlMessage *msg)
return sz;
}
}
+
+ERTS_GLB_INLINE void
+erts_msgq_update_internal_pointers(ErlMessageQueue *msgq,
+ ErtsMessage **newpp,
+ ErtsMessage **oldpp)
+{
+ if (msgq->save == oldpp)
+ msgq->save = newpp;
+ if (msgq->last == oldpp)
+ msgq->last = newpp;
+ if (msgq->saved_last == oldpp)
+ msgq->saved_last = newpp;
+}
+
+ERTS_GLB_INLINE void
+erts_msgq_replace_msg_ref(ErlMessageQueue *msgq, ErtsMessage *newp, ErtsMessage **oldpp)
+{
+ ErtsMessage *oldp = *oldpp;
+ newp->next = oldp->next;
+ erts_msgq_update_internal_pointers(msgq, &newp->next, &oldp->next);
+ *oldpp = newp;
+}
+
+#endif
+
+Uint erts_mbuf_size(Process *p);
+#if defined(DEBUG) || 0
+# define ERTS_CHK_MBUF_SZ(P) \
+ do { \
+ Uint actual_mbuf_sz__ = erts_mbuf_size((P)); \
+ ERTS_ASSERT((P)->mbuf_sz >= actual_mbuf_sz__); \
+ } while (0)
+#else
+# define ERTS_CHK_MBUF_SZ(P) ((void) 1)
#endif
#endif
diff --git a/erts/emulator/beam/erl_monitors.c b/erts/emulator/beam/erl_monitors.c
index bd899fa2f9..7244ae144f 100644
--- a/erts/emulator/beam/erl_monitors.c
+++ b/erts/emulator/beam/erl_monitors.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2013. 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.
@@ -104,7 +104,7 @@ do { \
(*((Hp)++)) = boxed_val((From))[i__]; \
if (is_external((To))) { \
external_thing_ptr((To))->next = NULL; \
- erts_refc_inc(&(external_thing_ptr((To))->node->refc), 2);\
+ erts_smp_refc_inc(&(external_thing_ptr((To))->node->refc), 2);\
} \
} \
} while (0)
diff --git a/erts/emulator/beam/erl_monitors.h b/erts/emulator/beam/erl_monitors.h
index 901ed8940b..9e2beedea3 100644
--- a/erts/emulator/beam/erl_monitors.h
+++ b/erts/emulator/beam/erl_monitors.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erl_msacc.c b/erts/emulator/beam/erl_msacc.c
new file mode 100644
index 0000000000..7ddf49937f
--- /dev/null
+++ b/erts/emulator/beam/erl_msacc.c
@@ -0,0 +1,488 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2014-2016. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Description: Microstate accounting.
+ *
+ * We keep track of the different states that the
+ * Erlang VM threads are in, in order to provide
+ * performance/debugging statistics. There is a
+ * small overhead in enabling this, but in the big
+ * scheme of things it should be negligible.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#define ERTS_MSACC_STATE_STRINGS 1
+
+#include "sys.h"
+#include "global.h"
+#include "erl_threads.h"
+#include "erl_bif_unique.h"
+#include "erl_map.h"
+#include "erl_msacc.h"
+#include "erl_bif_table.h"
+
+#if ERTS_ENABLE_MSACC
+
+static Eterm erts_msacc_gather_stats(ErtsMsAcc *msacc, ErtsHeapFactory *factory);
+static void erts_msacc_reset(ErtsMsAcc *msacc);
+static ErtsMsAcc* get_msacc(void);
+
+#ifdef USE_THREADS
+erts_tsd_key_t ERTS_WRITE_UNLIKELY(erts_msacc_key);
+#else
+ErtsMsAcc *ERTS_WRITE_UNLIKELY(erts_msacc) = NULL;
+#endif
+#ifndef ERTS_MSACC_ALWAYS_ON
+int ERTS_WRITE_UNLIKELY(erts_msacc_enabled);
+#endif
+
+static Eterm *erts_msacc_state_atoms = NULL;
+static erts_rwmtx_t msacc_mutex;
+static ErtsMsAcc *msacc_managed = NULL;
+#ifdef USE_THREADS
+static ErtsMsAcc *msacc_unmanaged = NULL;
+static Uint msacc_unmanaged_count = 0;
+#endif
+
+#if ERTS_MSACC_STATE_COUNT < MAP_SMALL_MAP_LIMIT
+#define DEFAULT_MSACC_MSG_SIZE (3 + 1 + ERTS_MSACC_STATE_COUNT * 2 + 3 + REF_THING_SIZE)
+#else
+#define DEFAULT_MSACC_MSG_SIZE (3 + ERTS_MSACC_STATE_COUNT * 3 + 3 + REF_THING_SIZE)
+#endif
+
+/* we have to split initiation as atoms are not inited in early init */
+void erts_msacc_early_init(void) {
+#ifndef ERTS_MSACC_ALWAYS_ON
+ erts_msacc_enabled = 0;
+#endif
+ erts_rwmtx_init(&msacc_mutex,"msacc_list_mutex");
+#ifdef USE_THREADS
+ erts_tsd_key_create(&erts_msacc_key,"erts_msacc_key");
+#else
+ erts_msacc = NULL;
+#endif
+}
+
+void erts_msacc_init(void) {
+ int i;
+ erts_msacc_state_atoms = erts_alloc(ERTS_ALC_T_MSACC,
+ sizeof(Eterm)*ERTS_MSACC_STATE_COUNT);
+ for (i = 0; i < ERTS_MSACC_STATE_COUNT; i++) {
+ erts_msacc_state_atoms[i] = am_atom_put(erts_msacc_states[i],
+ strlen(erts_msacc_states[i]));
+ }
+}
+
+void erts_msacc_init_thread(char *type, int id, int managed) {
+ ErtsMsAcc *msacc;
+
+ msacc = erts_alloc(ERTS_ALC_T_MSACC, sizeof(ErtsMsAcc) +
+ sizeof(ErtsMsAccPerfCntr) * ERTS_MSACC_STATE_COUNT);
+
+ msacc->type = strdup(type);
+ msacc->id = make_small(id);
+ msacc->unmanaged = !managed;
+ msacc->tid = erts_thr_self();
+ msacc->perf_counter = 0;
+
+#ifdef USE_THREADS
+ erts_rwmtx_rwlock(&msacc_mutex);
+ if (!managed) {
+ erts_mtx_init(&msacc->mtx,"msacc_unmanaged_mutex");
+ msacc->next = msacc_unmanaged;
+ msacc_unmanaged = msacc;
+ msacc_unmanaged_count++;
+ ERTS_MSACC_TSD_SET(msacc);
+ } else {
+ msacc->next = msacc_managed;
+ msacc_managed = msacc;
+ }
+ erts_rwmtx_rwunlock(&msacc_mutex);
+#else
+ msacc_managed = msacc;
+#endif
+
+ erts_msacc_reset(msacc);
+
+#ifdef ERTS_MSACC_ALWAYS_ON
+ ERTS_MSACC_TSD_SET(msacc);
+ msacc->perf_counter = erts_sys_perf_counter();
+ msacc->state = ERTS_MSACC_STATE_OTHER;
+#endif
+}
+
+#ifdef ERTS_MSACC_EXTENDED_STATES
+
+void erts_msacc_set_bif_state(ErtsMsAcc *__erts_msacc_cache, Eterm mod, void *fn) {
+
+#ifdef ERTS_MSACC_EXTENDED_BIFS
+#define BIF_LIST(Mod,Func,Arity,FuncAddr,Num) \
+ if (fn == &FuncAddr) { \
+ ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATIC_STATE_COUNT + Num); \
+ } else
+#include "erl_bif_list.h"
+#undef BIF_LIST
+ { /* The last else in the macro expansion,
+ this happens for internal bifs, i.e. traps etc */
+ ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_BIF);
+ }
+#else
+ if (mod == am_ets) {
+ ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_ETS);
+ } else {
+ ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_BIF);
+ }
+#endif
+}
+
+#endif
+
+/*
+ * Creates a structure looking like this
+ * #{ type => scheduler, id => 1, counters => #{ State1 => Counter1 ... StateN => CounterN}}
+ */
+static
+Eterm erts_msacc_gather_stats(ErtsMsAcc *msacc, ErtsHeapFactory *factory) {
+ Uint sz = 0;
+ Eterm *hp, cvs[ERTS_MSACC_STATE_COUNT];
+ Eterm key, state_map;
+ int i;
+ flatmap_t *map;
+
+ hp = erts_produce_heap(factory, 4, 0);
+ key = TUPLE3(hp,am_counters,am_id,am_type);
+
+ for (i = 0; i < ERTS_MSACC_STATE_COUNT; i++) {
+ cvs[i] = erts_bld_sint64(NULL, &sz,(Sint64)msacc->counters[i].pc);
+#ifdef ERTS_MSACC_STATE_COUNTERS
+ erts_bld_uint64(NULL,&sz,msacc->counters[i].sc);
+ sz += 3;
+#endif
+ }
+
+ hp = erts_produce_heap(factory, sz, 0);
+
+ for (i = 0; i < ERTS_MSACC_STATE_COUNT; i++) {
+ cvs[i] = erts_bld_sint64(&hp,NULL,(Sint64)msacc->counters[i].pc);
+#ifdef ERTS_MSACC_STATE_COUNTERS
+ Eterm counter__ = erts_bld_uint64(&hp,NULL,msacc->counters[i].sc);
+ cvs[i] = TUPLE2(hp,cvs[i],counter__);
+ hp += 3;
+#endif
+ }
+
+ state_map = erts_map_from_ks_and_vs(factory, erts_msacc_state_atoms, cvs,
+ ERTS_MSACC_STATE_COUNT);
+
+ hp = erts_produce_heap(factory, MAP_HEADER_FLATMAP_SZ + 3, 0);
+ map = (flatmap_t*)hp;
+ hp += MAP_HEADER_FLATMAP_SZ;
+ map->thing_word = MAP_HEADER_FLATMAP;
+ map->size = 3;
+ map->keys = key;
+ hp[0] = state_map;
+ hp[1] = msacc->id;
+ hp[2] = am_atom_put(msacc->type,strlen(msacc->type));
+
+ return make_flatmap(map);
+}
+
+typedef struct {
+ int action;
+ Process *proc;
+ Eterm ref;
+ Eterm ref_heap[REF_THING_SIZE];
+ Uint req_sched;
+ erts_smp_atomic32_t refc;
+} ErtsMSAccReq;
+
+static ErtsMsAcc* get_msacc(void) {
+ ErtsMsAcc *msacc;
+ erts_rwmtx_rlock(&msacc_mutex);
+ msacc = msacc_managed;
+ while (!erts_equal_tids(msacc->tid,erts_thr_self())) {
+ msacc = msacc->next;
+ ASSERT(msacc != NULL);
+ }
+ erts_rwmtx_runlock(&msacc_mutex);
+ return msacc;
+}
+
+static void send_reply(ErtsMsAcc *msacc, ErtsMSAccReq *msaccrp) {
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
+ Process *rp = msaccrp->proc;
+ ErtsMessage *msgp = NULL;
+ Eterm *hp;
+ Eterm ref_copy = NIL, msg;
+ ErtsProcLocks rp_locks = (esdp && msaccrp->req_sched == esdp->no
+ ? ERTS_PROC_LOCK_MAIN : 0);
+ ErtsHeapFactory factory;
+
+ if (msaccrp->action == ERTS_MSACC_GATHER) {
+
+ msgp = erts_factory_message_create(&factory, rp, &rp_locks, DEFAULT_MSACC_MSG_SIZE);
+
+ if (msacc->unmanaged) erts_mtx_lock(&msacc->mtx);
+
+ hp = erts_produce_heap(&factory, REF_THING_SIZE + 3 /* tuple */, 0);
+ ref_copy = STORE_NC(&hp, &msgp->hfrag.off_heap, msaccrp->ref);
+ msg = erts_msacc_gather_stats(msacc, &factory);
+ msg = TUPLE2(hp, ref_copy, msg);
+
+ if (msacc->unmanaged) erts_mtx_unlock(&msacc->mtx);
+
+ erts_factory_close(&factory);
+ } else {
+ ErlOffHeap *ohp = NULL;
+ msgp = erts_alloc_message_heap(rp, &rp_locks, REF_THING_SIZE, &hp, &ohp);
+ msg = STORE_NC(&hp, &msgp->hfrag.off_heap, msaccrp->ref);
+ }
+
+ erts_queue_message(rp, rp_locks, msgp, msg, am_system);
+
+ if (esdp && msaccrp->req_sched == esdp->no)
+ rp_locks &= ~ERTS_PROC_LOCK_MAIN;
+
+ if (rp_locks)
+ erts_smp_proc_unlock(rp, rp_locks);
+
+}
+
+static void
+reply_msacc(void *vmsaccrp)
+{
+ ErtsMsAcc *msacc = ERTS_MSACC_TSD_GET();
+ ErtsMSAccReq *msaccrp = (ErtsMSAccReq *) vmsaccrp;
+
+ ASSERT(!msacc || !msacc->unmanaged);
+
+ if (msaccrp->action == ERTS_MSACC_ENABLE && !msacc) {
+ msacc = get_msacc();
+
+ msacc->perf_counter = erts_sys_perf_counter();
+
+ msacc->state = ERTS_MSACC_STATE_OTHER;
+
+ ERTS_MSACC_TSD_SET(msacc);
+
+ } else if (msaccrp->action == ERTS_MSACC_DISABLE && msacc) {
+ ERTS_MSACC_TSD_SET(NULL);
+ } else if (msaccrp->action == ERTS_MSACC_RESET) {
+ msacc = msacc ? msacc : get_msacc();
+ erts_msacc_reset(msacc);
+ } else if (msaccrp->action == ERTS_MSACC_GATHER && !msacc) {
+ msacc = get_msacc();
+ }
+
+ ASSERT(!msacc || !msacc->unmanaged);
+
+ send_reply(msacc, msaccrp);
+
+ erts_proc_dec_refc(msaccrp->proc);
+
+ if (erts_smp_atomic32_dec_read_nob(&msaccrp->refc) == 0)
+ erts_free(ERTS_ALC_T_MSACC, vmsaccrp);
+}
+
+static void erts_msacc_reset(ErtsMsAcc *msacc) {
+ int i;
+ if (msacc->unmanaged) erts_mtx_lock(&msacc->mtx);
+
+ for (i = 0; i < ERTS_MSACC_STATE_COUNT; i++) {
+ msacc->counters[i].pc = 0;
+#ifdef ERTS_MSACC_STATE_COUNTERS
+ msacc->counters[i].sc = 0;
+#endif
+ }
+
+ if (msacc->unmanaged) erts_mtx_unlock(&msacc->mtx);
+}
+
+#endif /* ERTS_ENABLE_MSACC */
+
+
+/*
+ * This function is responsible for enabling, disabling, resetting and
+ * gathering data related to microstate accounting.
+ *
+ * Managed threads and unmanaged threads are handled differently.
+ * - managed threads get a misc_aux job telling them to switch on msacc
+ * - unmanaged have some fields protected by a mutex that has to be taken
+ * before any values can be updated
+ *
+ * For performance reasons there is also a global value erts_msacc_enabled
+ * that controls the state of all threads. Statistics gathering is only on
+ * if erts_msacc_enabled && msacc is true.
+ */
+Eterm
+erts_msacc_request(Process *c_p, int action, Eterm *threads)
+{
+#ifdef ERTS_ENABLE_MSACC
+ ErtsMsAcc *msacc = ERTS_MSACC_TSD_GET();
+ ErtsSchedulerData *esdp = erts_proc_sched_data(c_p);
+ Eterm ref;
+ ErtsMSAccReq *msaccrp;
+ Eterm *hp;
+
+
+#ifdef ERTS_MSACC_ALWAYS_ON
+ if (action == ERTS_MSACC_ENABLE || action == ERTS_MSACC_DISABLE)
+ return THE_NON_VALUE;
+#else
+ /* take care of double enable, and double disable here */
+ if (msacc && action == ERTS_MSACC_ENABLE) {
+ return THE_NON_VALUE;
+ } else if (!msacc && action == ERTS_MSACC_DISABLE) {
+ return THE_NON_VALUE;
+ }
+#endif
+
+ ref = erts_make_ref(c_p);
+
+ msaccrp = erts_alloc(ERTS_ALC_T_MSACC, sizeof(ErtsMSAccReq));
+ hp = &msaccrp->ref_heap[0];
+
+ msaccrp->action = action;
+ msaccrp->proc = c_p;
+ msaccrp->ref = STORE_NC(&hp, NULL, ref);
+ msaccrp->req_sched = esdp->no;
+
+#ifdef ERTS_SMP
+ *threads = erts_no_schedulers;
+ *threads += 1; /* aux thread */
+#else
+ *threads = 1;
+#endif
+
+ erts_smp_atomic32_init_nob(&msaccrp->refc,(erts_aint32_t)*threads);
+
+ erts_proc_add_refc(c_p, *threads);
+
+ if (erts_no_schedulers > 1)
+ erts_schedule_multi_misc_aux_work(1,
+ erts_no_schedulers,
+ reply_msacc,
+ (void *) msaccrp);
+#ifdef ERTS_SMP
+ /* aux thread */
+ erts_schedule_misc_aux_work(0, reply_msacc, (void *) msaccrp);
+#endif
+
+#ifdef USE_THREADS
+ /* Manage unmanaged threads */
+ switch (action) {
+ case ERTS_MSACC_GATHER: {
+ Uint unmanaged_count;
+ ErtsMsAcc *msacc, **unmanaged;
+ int i = 0;
+
+ /* we copy a list of pointers here so that we do not have to have
+ the msacc_mutex when sending messages */
+ erts_rwmtx_rlock(&msacc_mutex);
+ unmanaged_count = msacc_unmanaged_count;
+ unmanaged = erts_alloc(ERTS_ALC_T_MSACC,
+ sizeof(ErtsMsAcc*)*unmanaged_count);
+
+ for (i = 0, msacc = msacc_unmanaged;
+ i < unmanaged_count;
+ i++, msacc = msacc->next) {
+ unmanaged[i] = msacc;
+ }
+ erts_rwmtx_runlock(&msacc_mutex);
+
+ for (i = 0; i < unmanaged_count; i++) {
+ erts_mtx_lock(&unmanaged[i]->mtx);
+ if (unmanaged[i]->perf_counter) {
+ ErtsSysPerfCounter perf_counter;
+ /* if enabled update stats */
+ perf_counter = erts_sys_perf_counter();
+ unmanaged[i]->counters[unmanaged[i]->state].pc +=
+ perf_counter - unmanaged[i]->perf_counter;
+ unmanaged[i]->perf_counter = perf_counter;
+ }
+ erts_mtx_unlock(&unmanaged[i]->mtx);
+ send_reply(unmanaged[i],msaccrp);
+ }
+ erts_free(ERTS_ALC_T_MSACC,unmanaged);
+ /* We have just sent unmanaged_count messages, so bump no of threads */
+ *threads += unmanaged_count;
+ break;
+ }
+ case ERTS_MSACC_RESET: {
+ ErtsMsAcc *msacc;
+ erts_rwmtx_rlock(&msacc_mutex);
+ for (msacc = msacc_unmanaged; msacc != NULL; msacc = msacc->next)
+ erts_msacc_reset(msacc);
+ erts_rwmtx_runlock(&msacc_mutex);
+ break;
+ }
+ case ERTS_MSACC_ENABLE: {
+ erts_rwmtx_rlock(&msacc_mutex);
+ for (msacc = msacc_unmanaged; msacc != NULL; msacc = msacc->next) {
+ erts_mtx_lock(&msacc->mtx);
+ msacc->perf_counter = erts_sys_perf_counter();
+ /* we assume the unmanaged thread is sleeping */
+ msacc->state = ERTS_MSACC_STATE_SLEEP;
+ erts_mtx_unlock(&msacc->mtx);
+ }
+ erts_rwmtx_runlock(&msacc_mutex);
+ break;
+ }
+ case ERTS_MSACC_DISABLE: {
+ ErtsSysPerfCounter perf_counter;
+ erts_rwmtx_rlock(&msacc_mutex);
+ /* make sure to update stats with latest results */
+ for (msacc = msacc_unmanaged; msacc != NULL; msacc = msacc->next) {
+ erts_mtx_lock(&msacc->mtx);
+ perf_counter = erts_sys_perf_counter();
+ msacc->counters[msacc->state].pc += perf_counter - msacc->perf_counter;
+ msacc->perf_counter = 0;
+ erts_mtx_unlock(&msacc->mtx);
+ }
+ erts_rwmtx_runlock(&msacc_mutex);
+ break;
+ }
+ default: { ASSERT(0); }
+ }
+
+#endif
+
+ *threads = make_small(*threads);
+
+ reply_msacc((void *) msaccrp);
+
+#ifndef ERTS_MSACC_ALWAYS_ON
+ /* enable/disable the global value */
+ if (action == ERTS_MSACC_ENABLE) {
+ erts_msacc_enabled = 1;
+ } else if (action == ERTS_MSACC_DISABLE) {
+ erts_msacc_enabled = 0;
+ }
+#endif
+
+ return ref;
+#else
+ return THE_NON_VALUE;
+#endif
+}
diff --git a/erts/emulator/beam/erl_msacc.h b/erts/emulator/beam/erl_msacc.h
new file mode 100644
index 0000000000..4c8e1c8e22
--- /dev/null
+++ b/erts/emulator/beam/erl_msacc.h
@@ -0,0 +1,434 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2014-2016. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#ifndef ERL_MSACC_H__
+#define ERL_MSACC_H__
+
+/* Can be enabled/disabled via configure */
+#if ERTS_ENABLE_MSACC == 2
+#define ERTS_MSACC_EXTENDED_STATES 1
+#endif
+
+/* Uncomment this to also count the number of
+ transitions to a state. This will add a count
+ to the counter map. */
+/* #define ERTS_MSACC_STATE_COUNTERS 1 */
+
+/* Uncomment this to make msacc to always be on,
+ this reduces overhead a little bit when profiling */
+/* #define ERTS_MSACC_ALWAYS_ON 1 */
+
+/* Uncomment this to keep individual stats for all
+ of the bifs when extended states is enabled */
+/* #define ERTS_MSACC_EXTENDED_BIFS 1 */
+
+#define ERTS_MSACC_DISABLE 0
+#define ERTS_MSACC_ENABLE 1
+#define ERTS_MSACC_RESET 2
+#define ERTS_MSACC_GATHER 3
+
+/*
+ * When adding a new state, you have to:
+ * * Add it here
+ * * Increment ERTS_MSACC_STATE_COUNT
+ * * Add string value to erts_msacc_states
+ * * Have to be in alphabetical order!
+ * * Only add states to the non-extended section after
+ * careful benchmarking to make sure the overhead
+ * when disabled is minimal.
+ */
+
+#ifndef ERTS_MSACC_EXTENDED_STATES
+#define ERTS_MSACC_STATE_AUX 0
+#define ERTS_MSACC_STATE_CHECK_IO 1
+#define ERTS_MSACC_STATE_EMULATOR 2
+#define ERTS_MSACC_STATE_GC 3
+#define ERTS_MSACC_STATE_OTHER 4
+#define ERTS_MSACC_STATE_PORT 5
+#define ERTS_MSACC_STATE_SLEEP 6
+
+#define ERTS_MSACC_STATE_COUNT 7
+
+#if ERTS_MSACC_STATE_STRINGS && ERTS_ENABLE_MSACC
+static char *erts_msacc_states[] = {
+ "aux",
+ "check_io",
+ "emulator",
+ "gc",
+ "other",
+ "port",
+ "sleep"
+};
+#endif
+
+#else
+
+#define ERTS_MSACC_STATE_ALLOC 0
+#define ERTS_MSACC_STATE_AUX 1
+#define ERTS_MSACC_STATE_BIF 2
+#define ERTS_MSACC_STATE_BUSY_WAIT 3
+#define ERTS_MSACC_STATE_CHECK_IO 4
+#define ERTS_MSACC_STATE_EMULATOR 5
+#define ERTS_MSACC_STATE_ETS 6
+#define ERTS_MSACC_STATE_GC 7
+#define ERTS_MSACC_STATE_GC_FULL 8
+#define ERTS_MSACC_STATE_NIF 9
+#define ERTS_MSACC_STATE_OTHER 10
+#define ERTS_MSACC_STATE_PORT 11
+#define ERTS_MSACC_STATE_SEND 12
+#define ERTS_MSACC_STATE_SLEEP 13
+#define ERTS_MSACC_STATE_TIMERS 14
+
+#define ERTS_MSACC_STATIC_STATE_COUNT 15
+
+#ifdef ERTS_MSACC_EXTENDED_BIFS
+#define ERTS_MSACC_STATE_COUNT (ERTS_MSACC_STATIC_STATE_COUNT + BIF_SIZE)
+#else
+#define ERTS_MSACC_STATE_COUNT ERTS_MSACC_STATIC_STATE_COUNT
+#endif
+
+#if ERTS_MSACC_STATE_STRINGS
+static char *erts_msacc_states[] = {
+ "alloc",
+ "aux",
+ "bif",
+ "busy_wait",
+ "check_io",
+ "emulator",
+ "ets",
+ "gc",
+ "gc_full",
+ "nif",
+ "other",
+ "port",
+ "send",
+ "sleep",
+ "timers"
+#ifdef ERTS_MSACC_EXTENDED_BIFS
+#define BIF_LIST(Mod,Func,Arity,FuncAddr,Num) \
+ ,"bif_" #Mod "_" #Func "_" #Arity
+#include "erl_bif_list.h"
+#undef BIF_LIST
+#endif
+};
+#endif
+
+#endif
+
+typedef struct erl_msacc_t_ ErtsMsAcc;
+typedef struct erl_msacc_p_cnt_t_ {
+ ErtsSysPerfCounter pc;
+#ifdef ERTS_MSACC_STATE_COUNTERS
+ Uint64 sc;
+#endif
+} ErtsMsAccPerfCntr;
+
+struct erl_msacc_t_ {
+
+ /* protected by msacc_mutex in erl_msacc.c, and should be constant */
+ int unmanaged;
+ erts_mtx_t mtx;
+ ErtsMsAcc *next;
+ erts_tid_t tid;
+ Eterm id;
+ char *type;
+
+ /* the the values below are protected by mtx iff unmanaged = 1 */
+ ErtsSysPerfCounter perf_counter;
+ Uint state;
+ ErtsMsAccPerfCntr counters[];
+
+};
+
+#if ERTS_ENABLE_MSACC
+
+#ifdef USE_THREADS
+extern erts_tsd_key_t erts_msacc_key;
+#else
+extern ErtsMsAcc *erts_msacc;
+#endif
+
+#ifdef ERTS_MSACC_ALWAYS_ON
+#define erts_msacc_enabled 1
+#else
+extern int erts_msacc_enabled;
+#endif
+
+#ifdef USE_THREADS
+#define ERTS_MSACC_TSD_GET() erts_tsd_get(erts_msacc_key)
+#define ERTS_MSACC_TSD_SET(tsd) erts_tsd_set(erts_msacc_key,tsd)
+#else
+#define ERTS_MSACC_TSD_GET() erts_msacc
+#define ERTS_MSACC_TSD_SET(tsd) erts_msacc = tsd
+#endif
+
+void erts_msacc_early_init(void);
+void erts_msacc_init(void);
+void erts_msacc_init_thread(char *type, int id, int liberty);
+
+/* The defines below are used to instrument the vm code
+ * with different state changes. There are two variants
+ * of each define. One that has a cached ErtsMsAcc *
+ * that it can use, and one that does not.
+ * The cached values are necessary to have in order to get
+ * low enough overhead when running without msacc enabled.
+ *
+ * The two most common patterns to use the function with are:
+ *
+ * ERTS_MSACC_PUSH_AND_SET_STATE(ERTS_MSACC_STATE_NEWSTATE);
+ * ... call other function in new state ...
+ * ERTS_MSACC_POP_STATE();
+ *
+ * Note that the erts_msacc_push* function declare new variables, so
+ * to conform with C89 we have to call it in the beginning of a function.
+ * We might not want to change state it the beginning though, so we use this:
+ *
+ * ERTS_MSACC_PUSH_STATE();
+ * ... some other code ...
+ * ERTS_MSACC_SET_STATE_CACHED(ERTS_MSACC_STATE_NEWSTATE);
+ * ... call other function in new state ...
+ * ERTS_MSACC_POP_STATE();
+ *
+ * Notice that we used the cached version of set_state as push_state already
+ * read the erts_msacc_enabled to the cache.
+ *
+ * Most macros also have other variants with the suffix _m which means that
+ * they are known to only be called in managed threads, or with the _x suffix
+ * which means that it should only be used in an emulator compiled with
+ * extended states.
+ *
+ * Here is a listing of the entire api:
+ *
+ * void ERTS_MSACC_DECLARE_CACHE()
+ * void ERTS_MSACC_UPDATE_CACHE()
+ * void ERTS_MSACC_IS_ENABLED()
+ * void ERTS_MSACC_IS_ENABLED_CACHED()
+ *
+ * void ERTS_MSACC_PUSH_STATE()
+ * void ERTS_MSACC_SET_STATE(int state)
+ * void ERTS_MSACC_PUSH_AND_SET_STATE(int state)
+ *
+ * void ERTS_MSACC_PUSH_STATE_CACHED()
+ * void ERTS_MSACC_SET_STATE_CACHED(int state)
+ * void ERTS_MSACC_PUSH_AND_SET_STATE_CACHED(int state)
+ * void ERTS_MSACC_POP_STATE()
+ *
+ * void ERTS_MSACC_PUSH_STATE_M()
+ * void ERTS_MSACC_PUSH_STATE_CACHED_M()
+ * void ERTS_MSACC_SET_STATE_CACHED_M(int state)
+ * void ERTS_MSACC_SET_STATE_M(int state)
+ * void ERTS_MSACC_POP_STATE_M()
+ * void ERTS_MSACC_PUSH_AND_SET_STATE_M(int state)
+ *
+ * Most functions are also available with an _x suffix that are only enabled
+ * when using the extra states. If they are not, just add them to the end
+ * of this file.
+ */
+
+/* cache handling functions */
+#define ERTS_MSACC_IS_ENABLED() ERTS_UNLIKELY(erts_msacc_enabled)
+#define ERTS_MSACC_DECLARE_CACHE() \
+ ErtsMsAcc *ERTS_MSACC_UPDATE_CACHE(); \
+ ERTS_DECLARE_DUMMY(Uint __erts_msacc_state) = ERTS_MSACC_STATE_OTHER;
+#define ERTS_MSACC_IS_ENABLED_CACHED() ERTS_UNLIKELY(__erts_msacc_cache != NULL)
+#define ERTS_MSACC_UPDATE_CACHE() \
+ __erts_msacc_cache = erts_msacc_enabled ? ERTS_MSACC_TSD_GET() : NULL
+
+
+/* The defines below implicitly declare and load a new cache */
+#define ERTS_MSACC_PUSH_STATE() \
+ ERTS_MSACC_DECLARE_CACHE(); \
+ ERTS_MSACC_PUSH_STATE_CACHED()
+#define ERTS_MSACC_SET_STATE(state) \
+ ERTS_MSACC_DECLARE_CACHE(); \
+ ERTS_MSACC_SET_STATE_CACHED(state)
+#define ERTS_MSACC_PUSH_AND_SET_STATE(state) \
+ ERTS_MSACC_PUSH_STATE(); ERTS_MSACC_SET_STATE_CACHED(state)
+
+/* The defines below need an already declared cache to work */
+#define ERTS_MSACC_PUSH_STATE_CACHED() \
+ __erts_msacc_state = ERTS_MSACC_IS_ENABLED_CACHED() ? \
+ erts_msacc_get_state_um__(__erts_msacc_cache) : ERTS_MSACC_STATE_OTHER
+#define ERTS_MSACC_SET_STATE_CACHED(state) \
+ if (ERTS_MSACC_IS_ENABLED_CACHED()) \
+ erts_msacc_set_state_um__(__erts_msacc_cache, state, 1)
+#define ERTS_MSACC_PUSH_AND_SET_STATE_CACHED(state) \
+ ERTS_MSACC_PUSH_STATE_CACHED(); ERTS_MSACC_SET_STATE_CACHED(state)
+#define ERTS_MSACC_POP_STATE() \
+ if (ERTS_MSACC_IS_ENABLED_CACHED()) \
+ erts_msacc_set_state_um__(__erts_msacc_cache, __erts_msacc_state, 0)
+
+/* Only use these defines when we know that we have in a managed thread */
+#define ERTS_MSACC_PUSH_STATE_M() \
+ ERTS_MSACC_DECLARE_CACHE(); \
+ ERTS_MSACC_PUSH_STATE_CACHED_M()
+#define ERTS_MSACC_PUSH_STATE_CACHED_M() \
+ __erts_msacc_state = ERTS_MSACC_IS_ENABLED_CACHED() ? \
+ erts_msacc_get_state_m__(__erts_msacc_cache) : ERTS_MSACC_STATE_OTHER
+#define ERTS_MSACC_SET_STATE_M(state) \
+ ERTS_MSACC_DECLARE_CACHE(); \
+ ERTS_MSACC_SET_STATE_CACHED_M(state)
+#define ERTS_MSACC_SET_STATE_CACHED_M(state) \
+ if (ERTS_MSACC_IS_ENABLED_CACHED()) \
+ erts_msacc_set_state_m__(__erts_msacc_cache, state, 1)
+#define ERTS_MSACC_POP_STATE_M() \
+ if (ERTS_MSACC_IS_ENABLED_CACHED()) \
+ erts_msacc_set_state_m__(__erts_msacc_cache, __erts_msacc_state, 0)
+#define ERTS_MSACC_PUSH_AND_SET_STATE_M(state) \
+ ERTS_MSACC_PUSH_STATE_M(); ERTS_MSACC_SET_STATE_CACHED_M(state)
+
+ERTS_GLB_INLINE
+void erts_msacc_set_state_um__(ErtsMsAcc *msacc,Uint state,int increment);
+ERTS_GLB_INLINE
+void erts_msacc_set_state_m__(ErtsMsAcc *msacc,Uint state,int increment);
+
+ERTS_GLB_INLINE
+Uint erts_msacc_get_state_um__(ErtsMsAcc *msacc);
+ERTS_GLB_INLINE
+Uint erts_msacc_get_state_m__(ErtsMsAcc *msacc);
+
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE
+Uint erts_msacc_get_state_um__(ErtsMsAcc *msacc) {
+ Uint state;
+ if (msacc->unmanaged)
+ erts_mtx_lock(&msacc->mtx);
+ state = msacc->state;
+ if (msacc->unmanaged)
+ erts_mtx_unlock(&msacc->mtx);
+ return state;
+}
+
+ERTS_GLB_INLINE
+Uint erts_msacc_get_state_m__(ErtsMsAcc *msacc) {
+ return msacc->state;
+}
+
+ERTS_GLB_INLINE
+void erts_msacc_set_state_um__(ErtsMsAcc *msacc, Uint new_state, int increment) {
+ if (ERTS_UNLIKELY(msacc->unmanaged)) {
+ erts_mtx_lock(&msacc->mtx);
+ msacc->state = new_state;
+ if (ERTS_LIKELY(!msacc->perf_counter)) {
+ erts_mtx_unlock(&msacc->mtx);
+ return;
+ }
+ }
+
+ erts_msacc_set_state_m__(msacc,new_state,increment);
+
+ if (ERTS_UNLIKELY(msacc->unmanaged))
+ erts_mtx_unlock(&msacc->mtx);
+}
+
+ERTS_GLB_INLINE
+void erts_msacc_set_state_m__(ErtsMsAcc *msacc, Uint new_state, int increment) {
+ ErtsSysPerfCounter prev_perf_counter;
+ Sint64 diff;
+
+ if (new_state == msacc->state)
+ return;
+
+ prev_perf_counter = msacc->perf_counter;
+ msacc->perf_counter = erts_sys_perf_counter();
+ diff = msacc->perf_counter - prev_perf_counter;
+ ASSERT(diff >= 0);
+ msacc->counters[msacc->state].pc += diff;
+#ifdef ERTS_MSACC_STATE_COUNTERS
+ msacc->counters[new_state].sc += increment;
+#endif
+ msacc->state = new_state;
+}
+
+#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
+#else
+
+#define ERTS_MSACC_IS_ENABLED() 0
+#define erts_msacc_early_init()
+#define erts_msacc_init()
+#define erts_msacc_init_thread(type, id, liberty)
+#define ERTS_MSACC_PUSH_STATE()
+#define ERTS_MSACC_PUSH_STATE_CACHED()
+#define ERTS_MSACC_POP_STATE()
+#define ERTS_MSACC_SET_STATE(state)
+#define ERTS_MSACC_SET_STATE_CACHED(state)
+#define ERTS_MSACC_PUSH_AND_SET_STATE(state)
+#define ERTS_MSACC_PUSH_AND_SET_STATE_CACHED(state)
+#define ERTS_MSACC_UPDATE_CACHE()
+#define ERTS_MSACC_IS_ENABLED_CACHED()
+#define ERTS_MSACC_DECLARE_CACHE()
+#define ERTS_MSACC_PUSH_STATE_M()
+#define ERTS_MSACC_PUSH_STATE_CACHED_M()
+#define ERTS_MSACC_SET_STATE_CACHED_M(state)
+#define ERTS_MSACC_POP_STATE_M()
+#define ERTS_MSACC_PUSH_AND_SET_STATE_M(state)
+#define ERTS_MSACC_SET_BIF_STATE_CACHED_X(Mod,Addr)
+
+#endif /* ERTS_ENABLE_MSACC */
+
+#ifndef ERTS_MSACC_EXTENDED_STATES
+
+#define ERTS_MSACC_PUSH_STATE_X()
+#define ERTS_MSACC_POP_STATE_X()
+#define ERTS_MSACC_SET_STATE_X(state)
+#define ERTS_MSACC_SET_STATE_M_X(state)
+#define ERTS_MSACC_SET_STATE_CACHED_X(state)
+#define ERTS_MSACC_PUSH_AND_SET_STATE_X(state)
+#define ERTS_MSACC_PUSH_AND_SET_STATE_CACHED_X(state)
+#define ERTS_MSACC_UPDATE_CACHE_X()
+#define ERTS_MSACC_IS_ENABLED_CACHED_X() 0
+#define ERTS_MSACC_DECLARE_CACHE_X()
+#define ERTS_MSACC_PUSH_STATE_M_X()
+#define ERTS_MSACC_PUSH_STATE_CACHED_M_X()
+#define ERTS_MSACC_SET_STATE_CACHED_M_X(state)
+#define ERTS_MSACC_POP_STATE_M_X()
+#define ERTS_MSACC_PUSH_AND_SET_STATE_M_X(state)
+#define ERTS_MSACC_PUSH_AND_SET_STATE_CACHED_M_X(state)
+#define ERTS_MSACC_SET_BIF_STATE_CACHED_X(Mod,Addr)
+
+#else
+
+void erts_msacc_set_bif_state(ErtsMsAcc *msacc, Eterm mod, void *addr);
+
+#define ERTS_MSACC_PUSH_STATE_X() ERTS_MSACC_PUSH_STATE()
+#define ERTS_MSACC_POP_STATE_X() ERTS_MSACC_POP_STATE()
+#define ERTS_MSACC_SET_STATE_X(state) ERTS_MSACC_SET_STATE(state)
+#define ERTS_MSACC_SET_STATE_M_X(state) ERTS_MSACC_SET_STATE_M(state)
+#define ERTS_MSACC_SET_STATE_CACHED_X(state) ERTS_MSACC_SET_STATE_CACHED(state)
+#define ERTS_MSACC_PUSH_AND_SET_STATE_X(state) ERTS_MSACC_PUSH_AND_SET_STATE(state)
+#define ERTS_MSACC_PUSH_AND_SET_STATE_CACHED_X(state) ERTS_MSACC_PUSH_AND_SET_STATE_CACHED(state)
+#define ERTS_MSACC_UPDATE_CACHE_X() ERTS_MSACC_UPDATE_CACHE()
+#define ERTS_MSACC_IS_ENABLED_CACHED_X() ERTS_MSACC_IS_ENABLED_CACHED()
+#define ERTS_MSACC_DECLARE_CACHE_X() ERTS_MSACC_DECLARE_CACHE()
+#define ERTS_MSACC_PUSH_STATE_M_X() ERTS_MSACC_PUSH_STATE_M()
+#define ERTS_MSACC_PUSH_STATE_CACHED_M_X() ERTS_MSACC_PUSH_STATE_CACHED_M()
+#define ERTS_MSACC_SET_STATE_CACHED_M_X(state) ERTS_MSACC_SET_STATE_CACHED_M(state)
+#define ERTS_MSACC_POP_STATE_M_X() ERTS_MSACC_POP_STATE_M()
+#define ERTS_MSACC_PUSH_AND_SET_STATE_M_X(state) ERTS_MSACC_PUSH_AND_SET_STATE_M(state)
+#define ERTS_MSACC_SET_BIF_STATE_CACHED_X(Mod,Addr) \
+ if (ERTS_MSACC_IS_ENABLED_CACHED_X()) \
+ erts_msacc_set_bif_state(__erts_msacc_cache, Mod, Addr)
+
+#endif /* !ERTS_MSACC_EXTENDED_STATES */
+
+#endif /* ERL_MSACC_H__ */
diff --git a/erts/emulator/beam/erl_mtrace.c b/erts/emulator/beam/erl_mtrace.c
index fdaf02f37a..e275867928 100644
--- a/erts/emulator/beam/erl_mtrace.c
+++ b/erts/emulator/beam/erl_mtrace.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2003-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erl_mtrace.h b/erts/emulator/beam/erl_mtrace.h
index b34eab3c4a..776c70a819 100644
--- a/erts/emulator/beam/erl_mtrace.h
+++ b/erts/emulator/beam/erl_mtrace.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2003-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 1097916c97..479a2f4809 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2014. 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.
@@ -20,6 +20,23 @@
/* Erlang Native InterFace
*/
+/*
+ * Environment contains a pointer to currently executing process.
+ * In the dirty case this pointer do however not point to the
+ * actual process structure of the executing process, but instead
+ * a "shadow process structure". This in order to be able to handle
+ * heap allocation without the need to acquire the main lock on
+ * the process.
+ *
+ * The dirty process is allowed to allocate on the heap without
+ * the main lock, i.e., incrementing htop, but is not allowed to
+ * modify mbuf, offheap, etc without the main lock. The dirty
+ * process moves mbuf list and offheap list of the shadow process
+ * structure into the real structure when the dirty nif call
+ * completes.
+ */
+
+
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
@@ -79,9 +96,48 @@ void dtrace_nifenv_str(ErlNifEnv *, char *);
#endif
#define MIN_HEAP_FRAG_SZ 200
-static Eterm* alloc_heap_heavy(ErlNifEnv* env, unsigned need, Eterm* hp);
+static Eterm* alloc_heap_heavy(ErlNifEnv* env, size_t need, Eterm* hp);
+
+static ERTS_INLINE int
+is_scheduler(void)
+{
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
+ if (!esdp)
+ return 0;
+ if (ERTS_SCHEDULER_IS_DIRTY(esdp))
+ return -1;
+ return 1;
+}
+
+static ERTS_INLINE void
+execution_state(ErlNifEnv *env, Process **c_pp, int *schedp)
+{
+ if (schedp)
+ *schedp = is_scheduler();
+ if (c_pp) {
+ if (!env || env->proc->common.id == ERTS_INVALID_PID)
+ *c_pp = NULL;
+ else {
+ Process *c_p = env->proc;
+
+ if (!(c_p->static_flags & ERTS_STC_FLG_SHADOW_PROC)) {
+ ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p)
+ & ERTS_PROC_LOCK_MAIN);
+ }
+ else {
+ c_p = env->proc->next;
+ ASSERT(is_scheduler() < 0);
+ ASSERT(c_p && env->proc->common.id == c_p->common.id);
+ }
+
+ *c_pp = c_p;
-static ERTS_INLINE Eterm* alloc_heap(ErlNifEnv* env, unsigned need)
+ ASSERT(!(c_p->static_flags & ERTS_STC_FLG_SHADOW_PROC));
+ }
+ }
+}
+
+static ERTS_INLINE Eterm* alloc_heap(ErlNifEnv* env, size_t need)
{
Eterm* hp = env->hp;
env->hp += need;
@@ -91,15 +147,18 @@ static ERTS_INLINE Eterm* alloc_heap(ErlNifEnv* env, unsigned need)
return alloc_heap_heavy(env, need, hp);
}
-static Eterm* alloc_heap_heavy(ErlNifEnv* env, unsigned need, Eterm* hp)
-{
+static Eterm* alloc_heap_heavy(ErlNifEnv* env, size_t need, Eterm* hp)
+{
env->hp = hp;
- if (env->heap_frag == NULL) {
+ if (env->heap_frag == NULL) {
ASSERT(HEAP_LIMIT(env->proc) == env->hp_end);
- HEAP_TOP(env->proc) = env->hp;
+ ASSERT(env->hp + need > env->hp_end);
+ HEAP_TOP(env->proc) = env->hp;
}
else {
- env->heap_frag->used_size = hp - env->heap_frag->mem;
+ Uint usz = env->hp - env->heap_frag->mem;
+ env->proc->mbuf_sz += usz - env->heap_frag->used_size;
+ env->heap_frag->used_size = usz;
ASSERT(env->heap_frag->used_size <= env->heap_frag->alloc_size);
}
hp = erts_heap_alloc(env->proc, need, MIN_HEAP_FRAG_SZ);
@@ -111,7 +170,7 @@ static Eterm* alloc_heap_heavy(ErlNifEnv* env, unsigned need, Eterm* hp)
}
#if SIZEOF_LONG != ERTS_SIZEOF_ETERM
-static ERTS_INLINE void ensure_heap(ErlNifEnv* env, unsigned may_need)
+static ERTS_INLINE void ensure_heap(ErlNifEnv* env, size_t may_need)
{
if (env->hp + may_need > env->hp_end) {
alloc_heap_heavy(env, may_need, env->hp);
@@ -120,7 +179,8 @@ static ERTS_INLINE void ensure_heap(ErlNifEnv* env, unsigned may_need)
}
#endif
-void erts_pre_nif(ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif)
+void erts_pre_nif(ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif,
+ Process* tracee)
{
env->mod_nif = mod_nif;
env->proc = p;
@@ -130,18 +190,64 @@ void erts_pre_nif(ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif)
env->fpe_was_unmasked = erts_block_fpe();
env->tmp_obj_list = NULL;
env->exception_thrown = 0;
+ env->tracee = tracee;
+
+ ASSERT(p->common.id != ERTS_INVALID_PID);
+
+#if defined(DEBUG) && defined(ERTS_DIRTY_SCHEDULERS)
+ {
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
+ ASSERT(esdp);
+
+ if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) {
+ erts_aint32_t state = erts_smp_atomic32_read_nob(&p->state);
+
+ ASSERT(p->scheduler_data == esdp);
+ ASSERT((state & (ERTS_PSFLG_RUNNING
+ | ERTS_PSFLG_RUNNING_SYS))
+ && !(state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)));
+ }
+ }
+#endif
}
-static void pre_nif_noproc(ErlNifEnv* env, struct erl_module_nif* mod_nif)
+static void full_cache_env(ErlNifEnv *env);
+static void cache_env(ErlNifEnv* env);
+static void full_flush_env(ErlNifEnv *env);
+static void flush_env(ErlNifEnv* env);
+
+#ifdef ERTS_DIRTY_SCHEDULERS
+void erts_pre_dirty_nif(ErtsSchedulerData *esdp,
+ ErlNifEnv* env, Process* p,
+ struct erl_module_nif* mod_nif)
{
- env->mod_nif = mod_nif;
- env->proc = NULL;
- env->hp = NULL;
- env->hp_end = NULL;
- env->heap_frag = NULL;
- env->fpe_was_unmasked = erts_block_fpe();
- env->tmp_obj_list = NULL;
+ Process *sproc;
+#ifdef DEBUG
+ erts_aint32_t state = erts_smp_atomic32_read_nob(&p->state);
+
+ ASSERT(!p->scheduler_data);
+ ASSERT((state & ERTS_PSFLG_DIRTY_RUNNING)
+ && !(state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS)));
+ ASSERT(esdp);
+#endif
+
+ erts_pre_nif(env, p, mod_nif, NULL);
+
+ sproc = esdp->dirty_shadow_process;
+ ASSERT(sproc);
+ ASSERT(sproc->static_flags & ERTS_STC_FLG_SHADOW_PROC);
+ ASSERT(erts_smp_atomic32_read_nob(&sproc->state)
+ == (ERTS_PSFLG_ACTIVE
+ | ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_PROXY));
+
+ sproc->next = p;
+ sproc->common.id = p->common.id;
+ env->proc = sproc;
+ full_cache_env(env);
}
+#endif
/* Temporary object header, auto-deallocated when NIF returns
* or when independent environment is cleared.
@@ -165,27 +271,122 @@ static ERTS_INLINE void free_tmp_objs(ErlNifEnv* env)
void erts_post_nif(ErlNifEnv* env)
{
erts_unblock_fpe(env->fpe_was_unmasked);
- if (env->heap_frag == NULL) {
- ASSERT(env->hp_end == HEAP_LIMIT(env->proc));
- ASSERT(env->hp >= HEAP_TOP(env->proc));
- ASSERT(env->hp <= HEAP_LIMIT(env->proc));
- HEAP_TOP(env->proc) = env->hp;
- }
- else {
- ASSERT(env->hp_end != HEAP_LIMIT(env->proc));
- ASSERT(env->hp_end - env->hp <= env->heap_frag->alloc_size);
- env->heap_frag->used_size = env->hp - env->heap_frag->mem;
- ASSERT(env->heap_frag->used_size <= env->heap_frag->alloc_size);
- }
+ full_flush_env(env);
free_tmp_objs(env);
+ env->exiting = ERTS_PROC_IS_EXITING(env->proc);
}
-static void post_nif_noproc(ErlNifEnv* env)
+#ifdef ERTS_DIRTY_SCHEDULERS
+void erts_post_dirty_nif(ErlNifEnv* env)
{
+ Process *c_p;
+ ASSERT(env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC);
+ ASSERT(env->proc->next);
erts_unblock_fpe(env->fpe_was_unmasked);
+ full_flush_env(env);
free_tmp_objs(env);
+ c_p = env->proc->next;
+ env->exiting = ERTS_PROC_IS_EXITING(c_p);
+ ERTS_VBUMP_ALL_REDS(c_p);
+}
+#endif
+
+static void full_flush_env(ErlNifEnv* env)
+{
+#ifdef ERTS_DIRTY_SCHEDULERS
+ if (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC) {
+ /* Dirty nif call using shadow process struct */
+ Process *c_p = env->proc->next;
+
+ ASSERT(is_scheduler() < 0);
+ ASSERT(env->proc->common.id == c_p->common.id);
+ ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p)
+ & ERTS_PROC_LOCK_MAIN);
+
+ if (!env->heap_frag) {
+ ASSERT(env->hp_end == HEAP_LIMIT(c_p));
+ ASSERT(env->hp >= HEAP_TOP(c_p));
+ ASSERT(env->hp <= HEAP_LIMIT(c_p));
+ HEAP_TOP(c_p) = env->hp;
+ }
+ else {
+ Uint usz;
+ ASSERT(env->hp_end != HEAP_LIMIT(c_p));
+ ASSERT(env->hp_end - env->hp <= env->heap_frag->alloc_size);
+
+ HEAP_TOP(c_p) = HEAP_TOP(env->proc);
+ usz = env->hp - env->heap_frag->mem;
+ env->proc->mbuf_sz += usz - env->heap_frag->used_size;
+ env->heap_frag->used_size = usz;
+
+ ASSERT(env->heap_frag->used_size <= env->heap_frag->alloc_size);
+
+ if (c_p->mbuf) {
+ ErlHeapFragment *bp;
+ for (bp = env->proc->mbuf; bp->next; bp = bp->next)
+ ;
+ bp->next = c_p->mbuf;
+ }
+
+ c_p->mbuf = env->proc->mbuf;
+ c_p->mbuf_sz += env->proc->mbuf_sz;
+
+ }
+
+ if (!c_p->off_heap.first)
+ c_p->off_heap.first = env->proc->off_heap.first;
+ else if (env->proc->off_heap.first) {
+ struct erl_off_heap_header *ohhp;
+ for (ohhp = env->proc->off_heap.first; ohhp->next; ohhp = ohhp->next)
+ ;
+ ohhp->next = c_p->off_heap.first;
+ c_p->off_heap.first = env->proc->off_heap.first;
+ }
+ c_p->off_heap.overhead += env->proc->off_heap.overhead;
+
+ return;
+ }
+#endif
+
+ flush_env(env);
}
+static void full_cache_env(ErlNifEnv* env)
+{
+#ifdef ERTS_DIRTY_SCHEDULERS
+ if (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC) {
+ /* Dirty nif call using shadow process struct */
+ Process *sproc = env->proc;
+ Process *c_p = sproc->next;
+ ASSERT(c_p);
+ ASSERT(is_scheduler() < 0);
+ ASSERT(env->proc->common.id == c_p->common.id);
+ ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p)
+ & ERTS_PROC_LOCK_MAIN);
+
+ sproc->htop = c_p->htop;
+ sproc->stop = c_p->stop;
+ sproc->hend = c_p->hend;
+ sproc->heap = c_p->heap;
+ sproc->abandoned_heap = c_p->abandoned_heap;
+ sproc->heap_sz = c_p->heap_sz;
+ sproc->high_water = c_p->high_water;
+ sproc->old_hend = c_p->old_hend;
+ sproc->old_htop = c_p->old_htop;
+ sproc->old_heap = c_p->old_heap;
+ sproc->mbuf = NULL;
+ sproc->mbuf_sz = 0;
+ ERTS_INIT_OFF_HEAP(&sproc->off_heap);
+
+ env->hp_end = HEAP_LIMIT(c_p);
+ env->hp = HEAP_TOP(c_p);
+ env->heap_frag = NULL;
+ return;
+ }
+#endif
+
+ cache_env(env);
+}
/* Flush out our cached heap pointers to allow an ordinary HAlloc
*/
@@ -198,9 +399,12 @@ static void flush_env(ErlNifEnv* env)
HEAP_TOP(env->proc) = env->hp;
}
else {
+ Uint usz;
ASSERT(env->hp_end != HEAP_LIMIT(env->proc));
ASSERT(env->hp_end - env->hp <= env->heap_frag->alloc_size);
- env->heap_frag->used_size = env->hp - env->heap_frag->mem;
+ usz = env->hp - env->heap_frag->mem;
+ env->proc->mbuf_sz += usz - env->heap_frag->used_size;
+ env->heap_frag->used_size = usz;
ASSERT(env->heap_frag->used_size <= env->heap_frag->alloc_size);
}
}
@@ -209,6 +413,7 @@ static void flush_env(ErlNifEnv* env)
*/
static void cache_env(ErlNifEnv* env)
{
+ env->heap_frag = MBUF(env->proc);
if (env->heap_frag == NULL) {
ASSERT(env->hp_end == HEAP_LIMIT(env->proc));
ASSERT(env->hp <= HEAP_TOP(env->proc));
@@ -216,10 +421,6 @@ static void cache_env(ErlNifEnv* env)
env->hp = HEAP_TOP(env->proc);
}
else {
- ASSERT(env->hp_end != HEAP_LIMIT(env->proc));
- ASSERT(env->hp_end - env->hp <= env->heap_frag->alloc_size);
- env->heap_frag = MBUF(env->proc);
- ASSERT(env->heap_frag != NULL);
env->hp = env->heap_frag->mem + env->heap_frag->used_size;
env->hp_end = env->heap_frag->mem + env->heap_frag->alloc_size;
}
@@ -250,18 +451,20 @@ struct enif_msg_environment_t
Process phony_proc;
};
-ErlNifEnv* enif_alloc_env(void)
+static ERTS_INLINE void
+setup_nif_env(struct enif_msg_environment_t* msg_env,
+ struct erl_module_nif* mod,
+ Process* tracee)
{
- struct enif_msg_environment_t* msg_env =
- erts_alloc_fnf(ERTS_ALC_T_NIF, sizeof(struct enif_msg_environment_t));
Eterm* phony_heap = (Eterm*) msg_env; /* dummy non-NULL ptr */
-
- msg_env->env.hp = phony_heap;
+
+ msg_env->env.hp = phony_heap;
msg_env->env.hp_end = phony_heap;
msg_env->env.heap_frag = NULL;
- msg_env->env.mod_nif = NULL;
+ msg_env->env.mod_nif = mod;
msg_env->env.tmp_obj_list = NULL;
msg_env->env.proc = &msg_env->phony_proc;
+ msg_env->env.exception_thrown = 0;
memset(&msg_env->phony_proc, 0, sizeof(Process));
HEAP_START(&msg_env->phony_proc) = phony_heap;
HEAP_TOP(&msg_env->phony_proc) = phony_heap;
@@ -273,6 +476,14 @@ ErlNifEnv* enif_alloc_env(void)
msg_env->phony_proc.space_verified = 0;
msg_env->phony_proc.space_verified_from = NULL;
#endif
+ msg_env->env.tracee = tracee;
+}
+
+ErlNifEnv* enif_alloc_env(void)
+{
+ struct enif_msg_environment_t* msg_env =
+ erts_alloc_fnf(ERTS_ALC_T_NIF, sizeof(struct enif_msg_environment_t));
+ setup_nif_env(msg_env, NULL, NULL);
return &msg_env->env;
}
void enif_free_env(ErlNifEnv* env)
@@ -281,6 +492,20 @@ void enif_free_env(ErlNifEnv* env)
erts_free(ERTS_ALC_T_NIF, env);
}
+static ERTS_INLINE void pre_nif_noproc(struct enif_msg_environment_t* msg_env,
+ struct erl_module_nif* mod,
+ Process* tracee)
+{
+ setup_nif_env(msg_env, mod, tracee);
+ msg_env->env.fpe_was_unmasked = erts_block_fpe();
+}
+
+static ERTS_INLINE void post_nif_noproc(struct enif_msg_environment_t* msg_env)
+{
+ erts_unblock_fpe(msg_env->env.fpe_was_unmasked);
+ enif_clear_env(&msg_env->env);
+}
+
static ERTS_INLINE void clear_offheap(ErlOffHeap* oh)
{
oh->first = NULL;
@@ -307,75 +532,327 @@ void enif_clear_env(ErlNifEnv* env)
ASSERT(!is_offheap(&MSO(p)));
free_tmp_objs(env);
}
+
+#ifdef ERTS_SMP
+#ifdef DEBUG
+static int enif_send_delay = 0;
+#define ERTS_FORCE_ENIF_SEND_DELAY() (enif_send_delay++ % 2 == 0)
+#else
+#ifdef ERTS_PROC_LOCK_OWN_IMPL
+#define ERTS_FORCE_ENIF_SEND_DELAY() 0
+#else
+/*
+ * We always schedule messages if we do not use our own
+ * process lock implementation, as if we try to do a trylock on
+ * a lock that might already be locked by the same thread.
+ * And what happens then with different mutex implementations
+ * is not always guaranteed.
+ */
+#define ERTS_FORCE_ENIF_SEND_DELAY() 1
+#endif
+#endif
+
+int erts_flush_trace_messages(Process *c_p, ErtsProcLocks c_p_locks)
+{
+ ErlTraceMessageQueue *msgq, **last_msgq;
+ int reds = 0;
+
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_TRACE);
+
+ msgq = c_p->trace_msg_q;
+
+ if (!msgq)
+ goto error;
+
+ do {
+ Process* rp;
+ ErtsProcLocks rp_locks;
+ ErtsMessage *first, **last;
+ Uint len;
+
+ first = msgq->first;
+ last = msgq->last;
+ len = msgq->len;
+ msgq->first = NULL;
+ msgq->last = &msgq->first;
+ msgq->len = 0;
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_TRACE);
+
+ ASSERT(len != 0);
+
+ rp = erts_proc_lookup(msgq->receiver);
+ if (rp) {
+ rp_locks = 0;
+ if (rp->common.id == c_p->common.id)
+ rp_locks = c_p_locks;
+ erts_queue_messages(rp, rp_locks, first, last, len, c_p->common.id);
+ if (rp->common.id == c_p->common.id)
+ rp_locks &= ~c_p_locks;
+ if (rp_locks)
+ erts_smp_proc_unlock(rp, rp_locks);
+ reds += len;
+ } else {
+ erts_cleanup_messages(first);
+ }
+ reds += 1;
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_TRACE);
+ msgq = msgq->next;
+ } while (msgq);
+
+ last_msgq = &c_p->trace_msg_q;
+
+ while (*last_msgq) {
+ msgq = *last_msgq;
+ if (msgq->len == 0) {
+ *last_msgq = msgq->next;
+ erts_free(ERTS_ALC_T_TRACE_MSG_QUEUE, msgq);
+ } else {
+ last_msgq = &msgq->next;
+ }
+ }
+
+error:
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_TRACE);
+
+ return reds;
+}
+
+#endif
+
int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
ErlNifEnv* msg_env, ERL_NIF_TERM msg)
{
struct enif_msg_environment_t* menv = (struct enif_msg_environment_t*)msg_env;
ErtsProcLocks rp_locks = 0;
+#ifdef ERTS_SMP
+ ErtsProcLocks lc_locks = 0;
+#endif
Process* rp;
Process* c_p;
- ErlHeapFragment* frags;
+ ErtsMessage *mp;
Eterm receiver = to_pid->pid;
- int flush_me = 0;
- int scheduler = erts_get_scheduler_id() != 0;
+ int scheduler;
- if (env != NULL) {
- c_p = env->proc;
- if (receiver == c_p->common.id) {
- rp_locks = ERTS_PROC_LOCK_MAIN;
- flush_me = 1;
- }
+ execution_state(env, &c_p, &scheduler);
+
+#ifndef ERTS_SMP
+ if (!scheduler) {
+ erts_exit(ERTS_ABORT_EXIT,
+ "enif_send: called from non-scheduler thread on non-SMP VM");
+ return 0;
+ }
+#endif
+
+ if (scheduler > 0) { /* Normal scheduler */
+ rp = erts_proc_lookup(receiver);
+ if (!rp)
+ return 0;
}
else {
-#ifdef ERTS_SMP
- c_p = NULL;
-#else
- erts_exit(ERTS_ABORT_EXIT,"enif_send: env==NULL on non-SMP VM");
-#endif
- }
-
- rp = (scheduler
- ? erts_proc_lookup(receiver)
- : erts_pid2proc_opt(c_p, ERTS_PROC_LOCK_MAIN,
- receiver, rp_locks, ERTS_P2P_FLG_INC_REFC));
- if (rp == NULL) {
- ASSERT(env == NULL || receiver != c_p->common.id);
- return 0;
+ if (c_p) {
+ ASSERT(scheduler < 0); /* Dirty scheduler */
+ if (ERTS_PROC_IS_EXITING(c_p))
+ return 0;
+
+ if (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC) {
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
+ }
+ }
+
+ rp = erts_pid2proc_opt(c_p, ERTS_PROC_LOCK_MAIN,
+ receiver, rp_locks,
+ ERTS_P2P_FLG_INC_REFC);
+ if (!rp) {
+ if (c_p && (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC))
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
+ return 0;
+ }
}
- flush_env(msg_env);
- frags = menv->env.heap_frag;
- ASSERT(frags == MBUF(&menv->phony_proc));
- if (frags != NULL) {
- /* Move all offheap's from phony proc to the first fragment.
- Quick and dirty, but erts_move_msg_mbuf_to_heap doesn't care. */
- ASSERT(!is_offheap(&frags->off_heap));
- frags->off_heap = MSO(&menv->phony_proc);
- clear_offheap(&MSO(&menv->phony_proc));
- menv->env.heap_frag = NULL;
- MBUF(&menv->phony_proc) = NULL;
+
+ if (c_p == rp)
+ rp_locks = ERTS_PROC_LOCK_MAIN;
+
+ if (menv) {
+ flush_env(msg_env);
+ mp = erts_alloc_message(0, NULL);
+ mp->data.heap_frag = menv->env.heap_frag;
+ ASSERT(mp->data.heap_frag == MBUF(&menv->phony_proc));
+ if (mp->data.heap_frag != NULL) {
+ /* Move all offheap's from phony proc to the first fragment.
+ Quick and dirty... */
+ ASSERT(!is_offheap(&mp->data.heap_frag->off_heap));
+ mp->data.heap_frag->off_heap = MSO(&menv->phony_proc);
+ clear_offheap(&MSO(&menv->phony_proc));
+ menv->env.heap_frag = NULL;
+ MBUF(&menv->phony_proc) = NULL;
+ }
+ } else {
+ Uint sz = size_object(msg);
+ ErlOffHeap *ohp;
+ Eterm *hp;
+ if (c_p && !env->tracee) {
+ full_flush_env(env);
+ mp = erts_alloc_message_heap(rp, &rp_locks, sz, &hp, &ohp);
+ full_cache_env(env);
+ }
+ else {
+ erts_aint_t state = erts_smp_atomic32_read_nob(&rp->state);
+ if (state & ERTS_PSFLG_OFF_HEAP_MSGQ) {
+ mp = erts_alloc_message(sz, &hp);
+ ohp = sz == 0 ? NULL : &mp->hfrag.off_heap;
+ }
+ else {
+ ErlHeapFragment *bp = new_message_buffer(sz);
+ mp = erts_alloc_message(0, NULL);
+ mp->data.heap_frag = bp;
+ hp = bp->mem;
+ ohp = &bp->off_heap;
+ }
+ }
+ msg = copy_struct(msg, sz, &hp, ohp);
+ }
+
+ ERL_MESSAGE_TERM(mp) = msg;
+
+ if (!env || !env->tracee) {
+
+ if (c_p && IS_TRACED_FL(c_p, F_TRACE_SEND)) {
+ full_flush_env(env);
+ trace_send(c_p, receiver, msg);
+ full_cache_env(env);
+ }
}
- ASSERT(!is_offheap(&MSO(&menv->phony_proc)));
+#ifdef ERTS_SMP
+ else {
+ /* This clause is taken when the nif is called in the context
+ of a traced process. We do not know which locks we have
+ so we have to do a try lock and if that fails we enqueue
+ the message in a special trace message output queue of the
+ tracee */
+ ErlTraceMessageQueue *msgq;
+ Process *t_p = env->tracee;
+
+
+ erts_smp_proc_lock(t_p, ERTS_PROC_LOCK_TRACE);
+
+ msgq = t_p->trace_msg_q;
+
+ while (msgq != NULL) {
+ if (msgq->receiver == receiver) {
+ break;
+ }
+ msgq = msgq->next;
+ }
- if (flush_me) {
- flush_env(env); /* Needed for ERTS_HOLE_CHECK */
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ lc_locks = erts_proc_lc_my_proc_locks(rp);
+ rp_locks |= lc_locks;
+#endif
+ if (ERTS_FORCE_ENIF_SEND_DELAY() || msgq ||
+ rp_locks & ERTS_PROC_LOCK_MSGQ ||
+ erts_smp_proc_trylock(rp, ERTS_PROC_LOCK_MSGQ) == EBUSY) {
+
+ if (!msgq) {
+ msgq = erts_alloc(ERTS_ALC_T_TRACE_MSG_QUEUE,
+ sizeof(ErlTraceMessageQueue));
+ msgq->receiver = receiver;
+ msgq->first = mp;
+ msgq->last = &mp->next;
+ msgq->len = 1;
+
+ /* Insert in linked list */
+ msgq->next = t_p->trace_msg_q;
+ t_p->trace_msg_q = msgq;
+
+ erts_smp_proc_unlock(t_p, ERTS_PROC_LOCK_TRACE);
+
+ erts_schedule_flush_trace_messages(t_p, 0);
+ } else {
+ msgq->len++;
+ *msgq->last = mp;
+ msgq->last = &mp->next;
+ erts_smp_proc_unlock(t_p, ERTS_PROC_LOCK_TRACE);
+ }
+ goto done;
+ } else {
+ erts_smp_proc_unlock(t_p, ERTS_PROC_LOCK_TRACE);
+ rp_locks &= ~ERTS_PROC_LOCK_TRACE;
+ rp_locks |= ERTS_PROC_LOCK_MSGQ;
+ }
}
- erts_queue_message(rp, &rp_locks, frags, msg, am_undefined);
+#endif /* ERTS_SMP */
+
+ erts_queue_message(rp, rp_locks, mp, msg,
+ c_p ? c_p->common.id : am_undefined);
+
+#ifdef ERTS_SMP
+done:
if (c_p == rp)
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
- if (rp_locks)
- erts_smp_proc_unlock(rp, rp_locks);
- if (!scheduler)
+ if (rp_locks & ~lc_locks)
+ erts_smp_proc_unlock(rp, rp_locks & ~lc_locks);
+ if (c_p && (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC))
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
+#endif
+ if (scheduler <= 0)
erts_proc_dec_refc(rp);
- if (flush_me) {
- cache_env(env);
- }
+
return 1;
}
+int
+enif_port_command(ErlNifEnv *env, const ErlNifPort* to_port,
+ ErlNifEnv *msg_env, ERL_NIF_TERM msg)
+{
+ int iflags = (erts_port_synchronous_ops
+ ? ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP
+ : ERTS_PORT_SFLGS_INVALID_LOOKUP);
+ int scheduler;
+ Process *c_p;
+ Port *prt;
+ int res;
+
+ if (!env)
+ erts_exit(ERTS_ABORT_EXIT, "enif_port_command: env == NULL");
+
+ execution_state(env, &c_p, &scheduler);
+
+ if (!c_p)
+ c_p = env->proc;
+
+ if (scheduler > 0)
+ prt = erts_port_lookup(to_port->port_id, iflags);
+ else {
+#ifdef ERTS_SMP
+ if (ERTS_PROC_IS_EXITING(c_p))
+ return 0;
+ prt = erts_thr_port_lookup(to_port->port_id, iflags);
+#else
+ erts_exit(ERTS_ABORT_EXIT,
+ "enif_port_command: called from non-scheduler "
+ "thread on non-SMP VM");
+#endif
+ }
+
+ if (!prt)
+ res = 0;
+ else
+ res = erts_port_output_async(prt, c_p->common.id, msg);
+
+ if (scheduler <= 0)
+ erts_port_dec_refc(prt);
+
+ return res;
+}
+
ERL_NIF_TERM enif_make_copy(ErlNifEnv* dst_env, ERL_NIF_TERM src_term)
{
Uint sz;
Eterm* hp;
+ /*
+ * No preserved sharing allowed as long as literals are also preserved.
+ * Process independent environment can not be reached by purge.
+ */
sz = size_object(src_term);
hp = alloc_heap(dst_env, sz);
return copy_struct(src_term, sz, &hp, &MSO(dst_env->proc));
@@ -391,12 +868,28 @@ static int is_offheap(const ErlOffHeap* oh)
ErlNifPid* enif_self(ErlNifEnv* caller_env, ErlNifPid* pid)
{
+ if (caller_env->proc->common.id == ERTS_INVALID_PID)
+ return NULL;
pid->pid = caller_env->proc->common.id;
return pid;
}
+
int enif_get_local_pid(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifPid* pid)
{
- return is_internal_pid(term) ? (pid->pid=term, 1) : 0;
+ if (is_internal_pid(term)) {
+ pid->pid=term;
+ return 1;
+ }
+ return 0;
+}
+
+int enif_get_local_port(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifPort* port)
+{
+ if (is_internal_port(term)) {
+ port->port_id=term;
+ return 1;
+ }
+ return 0;
}
int enif_is_atom(ErlNifEnv* env, ERL_NIF_TERM term)
@@ -609,6 +1102,68 @@ unsigned char* enif_make_new_binary(ErlNifEnv* env, size_t size,
return binary_bytes(*termp);
}
+int enif_term_to_binary(ErlNifEnv *dst_env, ERL_NIF_TERM term,
+ ErlNifBinary *bin)
+{
+ Sint size;
+ byte *bp;
+ Binary* refbin;
+
+ size = erts_encode_ext_size(term);
+ if (!enif_alloc_binary(size, bin))
+ return 0;
+
+ refbin = bin->ref_bin;
+
+ bp = bin->data;
+
+ erts_encode_ext(term, &bp);
+
+ bin->size = bp - bin->data;
+ refbin->orig_size = bin->size;
+
+ ASSERT(bin->data + bin->size == bp);
+
+ return 1;
+}
+
+size_t enif_binary_to_term(ErlNifEnv *dst_env,
+ const unsigned char* data,
+ size_t data_sz,
+ ERL_NIF_TERM *term,
+ ErlNifBinaryToTerm opts)
+{
+ Sint size;
+ ErtsHeapFactory factory;
+ byte *bp = (byte*) data;
+
+ ERTS_CT_ASSERT(ERL_NIF_BIN2TERM_SAFE == ERTS_DIST_EXT_BTT_SAFE);
+
+ if (opts & ~ERL_NIF_BIN2TERM_SAFE) {
+ return 0;
+ }
+ if ((size = erts_decode_ext_size(bp, data_sz)) < 0)
+ return 0;
+
+ if (size > 0) {
+ flush_env(dst_env);
+ erts_factory_proc_prealloc_init(&factory, dst_env->proc, size);
+ } else {
+ erts_factory_dummy_init(&factory);
+ }
+
+ *term = erts_decode_ext(&factory, &bp, (Uint32)opts);
+
+ if (is_non_value(*term)) {
+ return 0;
+ }
+ erts_factory_close(&factory);
+ cache_env(dst_env);
+
+ ASSERT(bp > data);
+ return bp - data;
+}
+
int enif_is_identical(Eterm lhs, Eterm rhs)
{
return EQ(lhs,rhs);
@@ -715,7 +1270,7 @@ Eterm enif_make_sub_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term,
Eterm orig;
Uint offset, bit_offset, bit_size;
#ifdef DEBUG
- unsigned src_size;
+ size_t src_size;
ASSERT(is_binary(bin_term));
src_size = binary_size(bin_term);
@@ -743,15 +1298,21 @@ Eterm enif_make_badarg(ErlNifEnv* env)
Eterm enif_raise_exception(ErlNifEnv* env, ERL_NIF_TERM reason)
{
+ Process *c_p;
+
+ execution_state(env, &c_p, NULL);
+
env->exception_thrown = 1;
- env->proc->fvalue = reason;
- BIF_ERROR(env->proc, EXC_ERROR);
+ c_p->fvalue = reason;
+ BIF_ERROR(c_p, EXC_ERROR);
}
int enif_has_pending_exception(ErlNifEnv* env, ERL_NIF_TERM* reason)
{
if (env->exception_thrown && reason != NULL) {
- *reason = env->proc->fvalue;
+ Process *c_p;
+ execution_state(env, &c_p, NULL);
+ *reason = c_p->fvalue;
}
return env->exception_thrown;
}
@@ -898,8 +1459,13 @@ int enif_get_list_cell(ErlNifEnv* env, Eterm term, Eterm* head, Eterm* tail)
int enif_get_list_length(ErlNifEnv* env, Eterm term, unsigned* len)
{
- if (is_not_list(term) && is_not_nil(term)) return 0;
- *len = erts_list_length(term);
+ Sint i;
+ Uint u;
+
+ if ((i = erts_list_length(term)) < 0) return 0;
+ u = (Uint)i;
+ if ((unsigned)u != u) return 0;
+ *len = u;
return 1;
}
@@ -1140,6 +1706,120 @@ int enif_make_reverse_list(ErlNifEnv* env, ERL_NIF_TERM term, ERL_NIF_TERM *list
return 1;
}
+int enif_is_current_process_alive(ErlNifEnv* env)
+{
+ Process *c_p;
+ int scheduler;
+
+ execution_state(env, &c_p, &scheduler);
+
+ if (!c_p)
+ erts_exit(ERTS_ABORT_EXIT,
+ "enif_is_current_process_alive: "
+ "Invalid environment");
+
+ if (!scheduler)
+ erts_exit(ERTS_ABORT_EXIT, "enif_is_current_process_alive: "
+ "called from non-scheduler thread");
+
+ return !ERTS_PROC_IS_EXITING(c_p);
+}
+
+int enif_is_process_alive(ErlNifEnv* env, ErlNifPid *proc)
+{
+ int scheduler;
+
+ execution_state(env, NULL, &scheduler);
+
+ if (scheduler > 0)
+ return !!erts_proc_lookup(proc->pid);
+ else {
+#ifdef ERTS_SMP
+ Process* rp = erts_pid2proc_opt(NULL, 0, proc->pid, 0,
+ ERTS_P2P_FLG_INC_REFC);
+ if (rp)
+ erts_proc_dec_refc(rp);
+ return !!rp;
+#else
+ erts_exit(ERTS_ABORT_EXIT, "enif_is_process_alive: "
+ "called from non-scheduler thread "
+ "in non-smp emulator");
+ return 0;
+#endif
+ }
+}
+
+int enif_is_port_alive(ErlNifEnv *env, ErlNifPort *port)
+{
+ int scheduler;
+ Uint32 iflags = (erts_port_synchronous_ops
+ ? ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP
+ : ERTS_PORT_SFLGS_INVALID_LOOKUP);
+
+ execution_state(env, NULL, &scheduler);
+
+ if (scheduler > 0)
+ return !!erts_port_lookup(port->port_id, iflags);
+ else {
+#ifdef ERTS_SMP
+ Port *prt = erts_thr_port_lookup(port->port_id, iflags);
+ if (prt)
+ erts_port_dec_refc(prt);
+ return !!prt;
+#else
+ erts_exit(ERTS_ABORT_EXIT, "enif_is_port_alive: "
+ "called from non-scheduler thread "
+ "in non-smp emulator");
+ return 0;
+#endif
+ }
+}
+
+ERL_NIF_TERM
+enif_now_time(ErlNifEnv *env)
+{
+ Uint mega, sec, micro;
+ Eterm *hp;
+ get_now(&mega, &sec, &micro);
+ hp = alloc_heap(env, 4);
+ return TUPLE3(hp, make_small(mega), make_small(sec), make_small(micro));
+}
+
+ERL_NIF_TERM
+enif_cpu_time(ErlNifEnv *env)
+{
+#ifdef HAVE_ERTS_NOW_CPU
+ Uint mega, sec, micro;
+ Eterm *hp;
+ erts_get_now_cpu(&mega, &sec, &micro);
+ hp = alloc_heap(env, 4);
+ return TUPLE3(hp, make_small(mega), make_small(sec), make_small(micro));
+#else
+ return enif_make_badarg(env);
+#endif
+}
+
+ERL_NIF_TERM
+enif_make_unique_integer(ErlNifEnv *env, ErlNifUniqueInteger properties)
+{
+ int monotonic = properties & ERL_NIF_UNIQUE_MONOTONIC;
+ int positive = properties & ERL_NIF_UNIQUE_POSITIVE;
+ Eterm *hp;
+ Uint hsz;
+
+ if (monotonic) {
+ Sint64 raw_unique = erts_raw_get_unique_monotonic_integer();
+ hsz = erts_raw_unique_monotonic_integer_heap_size(raw_unique, positive);
+ hp = alloc_heap(env, hsz);
+ return erts_raw_make_unique_monotonic_integer_value(&hp, raw_unique, positive);
+ } else {
+ Uint64 raw_unique[ERTS_UNIQUE_INT_RAW_VALUES];
+ erts_raw_get_unique_integer(raw_unique);
+ hsz = erts_raw_unique_integer_heap_size(raw_unique, positive);
+ hp = alloc_heap(env, hsz);
+ return erts_raw_make_unique_integer(&hp, raw_unique, positive);
+ }
+}
ErlNifMutex* enif_mutex_create(char *name) { return erl_drv_mutex_create(name); }
void enif_mutex_destroy(ErlNifMutex *mtx) { erl_drv_mutex_destroy(mtx); }
@@ -1205,6 +1885,16 @@ int enif_fprintf(void* filep, const char* format, ...)
return ret;
}
+int enif_snprintf(char *buffer, size_t size, const char* format, ...)
+{
+ int ret;
+ va_list arglist;
+ va_start(arglist, format);
+ ret = erts_vsnprintf(buffer, size, format, arglist);
+ va_end(arglist);
+ return ret;
+}
+
/***********************************************************
** Memory managed (GC'ed) "resource" objects **
***********************************************************/
@@ -1266,10 +1956,10 @@ static void close_lib(struct erl_module_nif* lib)
ASSERT(erts_refc_read(&lib->rt_dtor_cnt,0) == 0);
if (lib->entry != NULL && lib->entry->unload != NULL) {
- ErlNifEnv env;
- pre_nif_noproc(&env, lib);
- lib->entry->unload(&env, lib->priv_data);
- post_nif_noproc(&env);
+ struct enif_msg_environment_t msg_env;
+ pre_nif_noproc(&msg_env, lib, NULL);
+ lib->entry->unload(&msg_env.env, lib->priv_data);
+ post_nif_noproc(&msg_env);
}
if (!erts_is_static_nif(lib->handle))
erts_sys_ddll_close(lib->handle);
@@ -1415,10 +2105,10 @@ static void nif_resource_dtor(Binary* bin)
ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(bin) == &nif_resource_dtor);
if (type->dtor != NULL) {
- ErlNifEnv env;
- pre_nif_noproc(&env, type->owner);
- type->dtor(&env,resource->data);
- post_nif_noproc(&env);
+ struct enif_msg_environment_t msg_env;
+ pre_nif_noproc(&msg_env, type->owner, NULL);
+ type->dtor(&msg_env.env, resource->data);
+ post_nif_noproc(&msg_env);
}
if (erts_refc_dectest(&type->refc, 0) == 0) {
ASSERT(type->next == NULL);
@@ -1559,23 +2249,26 @@ void* enif_dlsym(void* handle, const char* symbol,
int enif_consume_timeslice(ErlNifEnv* env, int percent)
{
+ Process *proc;
Sint reds;
+ execution_state(env, &proc, NULL);
+
ASSERT(is_proc_bound(env) && percent >= 1 && percent <= 100);
if (percent < 1) percent = 1;
else if (percent > 100) percent = 100;
reds = ((CONTEXT_REDS+99) / 100) * percent;
ASSERT(reds > 0 && reds <= CONTEXT_REDS);
- BUMP_REDS(env->proc, reds);
- return ERTS_BIF_REDS_LEFT(env->proc) == 0;
+ BUMP_REDS(proc, reds);
+ return ERTS_BIF_REDS_LEFT(proc) == 0;
}
/*
* NIF exports need a few more items than the Export struct provides,
* including the erl_module_nif* and a NIF function pointer, so the
* NifExport below adds those. The Export member must be first in the
- * struct. The saved_mfa, exception_thrown, saved_argc, rootset_extra, and
+ * struct. The saved_current, exception_thrown, saved_argc, rootset_extra, and
* rootset members are used to track the MFA, any pending exception, and
* arguments of the top NIF in case a chain of one or more
* enif_schedule_nif() calls results in an exception, since in that case
@@ -1589,7 +2282,7 @@ typedef struct {
Export exp;
struct erl_module_nif* m;
NativeFunPtr fp;
- Eterm saved_mfa[3];
+ BeamInstr *saved_current;
int exception_thrown;
int saved_argc;
int rootset_extra;
@@ -1616,6 +2309,17 @@ erts_setup_nif_gc(Process* proc, Eterm** objv, int* nobj)
return gc;
}
+int
+erts_check_nif_export_in_area(Process *p, char *start, Uint size)
+{
+ NifExport *nep = ERTS_PROC_GET_NIF_TRAP_EXPORT(p);
+ if (!nep || !nep->saved_current)
+ return 0;
+ if (ErtsInArea(nep->saved_current, start, size))
+ return 1;
+ return 0;
+}
+
/*
* Allocate a NifExport and set it in proc specific data
*/
@@ -1635,7 +2339,7 @@ allocate_nif_sched_data(Process* proc, int argc)
ep->exp.addressv[i] = &ep->exp.code[3];
}
ep->exp.code[3] = (BeamInstr) em_call_nif;
- (void) ERTS_PROC_SET_NIF_TRAP_EXPORT(proc, ERTS_PROC_LOCK_MAIN, ep);
+ (void) ERTS_PROC_SET_NIF_TRAP_EXPORT(proc, ep);
return ep;
}
@@ -1663,38 +2367,49 @@ static ERL_NIF_TERM
init_nif_sched_data(ErlNifEnv* env, NativeFunPtr direct_fp, NativeFunPtr indirect_fp,
int need_save, int argc, const ERL_NIF_TERM argv[])
{
- Process* proc = env->proc;
- Eterm* reg = ERTS_PROC_GET_SCHDATA(proc)->x_reg_array;
+ Process* proc;
+ Eterm* reg;
NifExport* ep;
- int i;
+ int i, scheduler;
+ int orig_argc;
+
+ execution_state(env, &proc, &scheduler);
+
+ ASSERT(scheduler);
+
+ ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(proc)
+ & ERTS_PROC_LOCK_MAIN);
+
+ reg = erts_proc_sched_data(proc)->x_reg_array;
+
+ ASSERT(!need_save || proc->current);
+ orig_argc = need_save ? (int) proc->current[2] : 0;
ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc);
if (!ep)
- ep = allocate_nif_sched_data(proc, argc);
- else if (need_save && ep->rootset_extra < argc) {
- NifExport* new_ep = allocate_nif_sched_data(proc, argc);
+ ep = allocate_nif_sched_data(proc, orig_argc);
+ else if (need_save && ep->rootset_extra < orig_argc) {
+ NifExport* new_ep = allocate_nif_sched_data(proc, orig_argc);
destroy_nif_export(ep);
ep = new_ep;
}
if (env->exception_thrown) {
ep->exception_thrown = 1;
- ep->rootset[0] = env->proc->fvalue;
+ ep->rootset[0] = proc->fvalue;
} else {
ep->exception_thrown = 0;
ep->rootset[0] = NIL;
}
- ERTS_VBUMP_ALL_REDS(proc);
- for (i = 0; i < argc; i++) {
- if (need_save)
- ep->rootset[i+1] = reg[i];
- reg[i] = (Eterm) argv[i];
- }
+ if (scheduler > 0)
+ ERTS_VBUMP_ALL_REDS(proc);
if (need_save) {
- ep->saved_mfa[0] = proc->current[0];
- ep->saved_mfa[1] = proc->current[1];
- ep->saved_mfa[2] = proc->current[2];
- ep->saved_argc = argc;
+ ep->saved_current = proc->current;
+ ep->saved_argc = orig_argc;
+ for (i = 0; i < orig_argc; i++)
+ ep->rootset[i+1] = reg[i];
}
+ for (i = 0; i < argc; i++)
+ reg[i] = (Eterm) argv[i];
proc->i = (BeamInstr*) ep->exp.addressv[0];
ep->exp.code[0] = (BeamInstr) proc->current[0];
ep->exp.code[1] = (BeamInstr) proc->current[1];
@@ -1715,16 +2430,21 @@ static void
restore_nif_mfa(Process* proc, NifExport* ep, int exception)
{
int i;
- Eterm* reg = ERTS_PROC_GET_SCHDATA(proc)->x_reg_array;
- proc->current[0] = ep->saved_mfa[0];
- proc->current[1] = ep->saved_mfa[1];
- proc->current[2] = ep->saved_mfa[2];
- if (exception)
+ ERTS_SMP_LC_ASSERT(!(proc->static_flags
+ & ERTS_STC_FLG_SHADOW_PROC));
+ ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(proc)
+ & ERTS_PROC_LOCK_MAIN);
+
+ ASSERT(ep->saved_current != &ep->exp.code[0]);
+ proc->current = ep->saved_current;
+ ep->saved_current = NULL;
+ if (exception) {
+ Eterm* reg = erts_proc_sched_data(proc)->x_reg_array;
for (i = 0; i < ep->saved_argc; i++)
reg[i] = ep->rootset[i+1];
+ }
ep->saved_argc = 0;
- ep->saved_mfa[0] = THE_NON_VALUE;
}
#ifdef ERTS_DIRTY_SCHEDULERS
@@ -1740,11 +2460,13 @@ restore_nif_mfa(Process* proc, NifExport* ep, int exception)
static ERL_NIF_TERM
dirty_nif_finalizer(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
- Process* proc = env->proc;
+ Process* proc;
NifExport* ep;
+ execution_state(env, &proc, NULL);
+
ASSERT(argc == 1);
- ASSERT(!ERTS_SCHEDULER_IS_DIRTY(env->proc->scheduler_data));
+ ASSERT(!ERTS_SCHEDULER_IS_DIRTY(erts_proc_sched_data(proc)));
ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc);
ASSERT(ep);
ASSERT(!ep->exception_thrown);
@@ -1759,10 +2481,12 @@ dirty_nif_finalizer(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
static ERL_NIF_TERM
dirty_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
- Process* proc = env->proc;
+ Process* proc;
NifExport* ep;
- ASSERT(!ERTS_SCHEDULER_IS_DIRTY(env->proc->scheduler_data));
+ execution_state(env, &proc, NULL);
+
+ ASSERT(!ERTS_SCHEDULER_IS_DIRTY(erts_proc_sched_data(proc)));
ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc);
ASSERT(ep);
ASSERT(ep->exception_thrown);
@@ -1779,25 +2503,33 @@ dirty_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
static ERL_NIF_TERM
execute_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
- Process* proc = env->proc;
- NativeFunPtr fp = (NativeFunPtr) proc->current[6];
+ Process* proc;
+ NativeFunPtr fp;
NifExport* ep;
ERL_NIF_TERM result;
- ASSERT(ERTS_SCHEDULER_IS_DIRTY(env->proc->scheduler_data));
+ execution_state(env, &proc, NULL);
+
+ fp = (NativeFunPtr) proc->current[6];
+
+ ASSERT(ERTS_SCHEDULER_IS_DIRTY(erts_proc_sched_data(proc)));
/*
* Set ep->fp to NULL before the native call so we know later whether it scheduled another NIF for execution
*/
ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc);
- ASSERT(ep);
+ ASSERT(ep && fp);
+
ep->fp = NULL;
+ erts_smp_atomic32_read_band_mb(&proc->state, ~(ERTS_PSFLG_DIRTY_CPU_PROC
+ | ERTS_PSFLG_DIRTY_IO_PROC));
+
+ erts_smp_proc_unlock(proc, ERTS_PROC_LOCK_MAIN);
+
result = (*fp)(env, argc, argv);
- erts_smp_atomic32_read_band_mb(&proc->state,
- ~(ERTS_PSFLG_DIRTY_CPU_PROC
- |ERTS_PSFLG_DIRTY_IO_PROC
- |ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q
- |ERTS_PSFLG_DIRTY_IO_PROC_IN_Q));
+
+ erts_smp_proc_lock(proc, ERTS_PROC_LOCK_MAIN);
+
if (erts_refc_dectest(&env->mod_nif->rt_dtor_cnt, 0) == 0 && env->mod_nif->mod == NULL)
close_lib(env->mod_nif);
/*
@@ -1834,36 +2566,49 @@ execute_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
static ERTS_INLINE ERL_NIF_TERM
schedule_dirty_nif(ErlNifEnv* env, int flags, int argc, const ERL_NIF_TERM argv[])
{
- erts_aint32_t state, n, a;
- Process* proc = env->proc;
- NativeFunPtr fp = (NativeFunPtr) proc->current[6];
+ ERL_NIF_TERM result;
+ erts_aint32_t act, dirty_flag;
+ Process* proc;
+ NativeFunPtr fp;
NifExport* ep;
- int need_save;
+ int need_save, scheduler;
+
+ execution_state(env, &proc, &scheduler);
+ if (scheduler <= 0) {
+ ASSERT(scheduler < 0);
+ erts_smp_proc_lock(proc, ERTS_PROC_LOCK_MAIN);
+ }
+
+ fp = (NativeFunPtr) proc->current[6];
+
+ ASSERT(fp);
ASSERT(flags==ERL_NIF_DIRTY_JOB_IO_BOUND || flags==ERL_NIF_DIRTY_JOB_CPU_BOUND);
- a = erts_smp_atomic32_read_acqb(&proc->state);
- while (1) {
- n = state = a;
- /*
- * clear any current dirty flags and dirty queue indicators,
- * in case the application is shifting a job from one type
- * of dirty scheduler to the other
- */
- n &= ~(ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC
- |ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q);
+ if (flags == ERL_NIF_DIRTY_JOB_CPU_BOUND)
+ dirty_flag = ERTS_PSFLG_DIRTY_CPU_PROC;
+ else
+ dirty_flag = ERTS_PSFLG_DIRTY_IO_PROC;
+
+ act = erts_smp_atomic32_read_bor_nob(&proc->state, dirty_flag);
+ if (!(act & (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC)))
+ erts_refc_inc(&env->mod_nif->rt_dtor_cnt, 1);
+ else if ((act & (ERTS_PSFLG_DIRTY_CPU_PROC
+ | ERTS_PSFLG_DIRTY_IO_PROC)) & ~dirty_flag) {
+ /* clear other flag... */
if (flags == ERL_NIF_DIRTY_JOB_CPU_BOUND)
- n |= ERTS_PSFLG_DIRTY_CPU_PROC;
+ dirty_flag = ERTS_PSFLG_DIRTY_IO_PROC;
else
- n |= ERTS_PSFLG_DIRTY_IO_PROC;
- a = erts_smp_atomic32_cmpxchg_mb(&proc->state, n, state);
- if (a == state)
- break;
+ dirty_flag = ERTS_PSFLG_DIRTY_CPU_PROC;
+ erts_smp_atomic32_read_band_nob(&proc->state, ~dirty_flag);
}
- erts_refc_inc(&env->mod_nif->rt_dtor_cnt, 1);
+
ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc);
- need_save = (ep == NULL || is_non_value(ep->saved_mfa[0]));
- return init_nif_sched_data(env, execute_dirty_nif, fp, need_save, argc, argv);
+ need_save = (ep == NULL || !ep->saved_current);
+ result = init_nif_sched_data(env, execute_dirty_nif, fp, need_save, argc, argv);
+ if (scheduler <= 0)
+ erts_smp_proc_unlock(proc, ERTS_PROC_LOCK_MAIN);
+ return result;
}
static ERL_NIF_TERM
@@ -1888,11 +2633,14 @@ schedule_dirty_cpu_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
static ERL_NIF_TERM
execute_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
- Process* proc = env->proc;
- NativeFunPtr fp = (NativeFunPtr) proc->current[6];
+ Process* proc;
+ NativeFunPtr fp;
NifExport* ep;
ERL_NIF_TERM result;
+ execution_state(env, &proc, NULL);
+ fp = (NativeFunPtr) proc->current[6];
+
ASSERT(!env->exception_thrown);
ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc);
ASSERT(ep);
@@ -1915,10 +2663,10 @@ enif_schedule_nif(ErlNifEnv* env, const char* fun_name, int flags,
ERL_NIF_TERM (*fp)(ErlNifEnv*, int, const ERL_NIF_TERM[]),
int argc, const ERL_NIF_TERM argv[])
{
- Process* proc = env->proc;
+ Process* proc;
NifExport* ep;
ERL_NIF_TERM fun_name_atom, result;
- int need_save;
+ int need_save, scheduler;
if (argc > MAX_ARG)
return enif_make_badarg(env);
@@ -1926,8 +2674,15 @@ enif_schedule_nif(ErlNifEnv* env, const char* fun_name, int flags,
if (enif_is_exception(env, fun_name_atom))
return fun_name_atom;
+ execution_state(env, &proc, &scheduler);
+ if (scheduler <= 0) {
+ if (scheduler == 0)
+ enif_make_badarg(env);
+ erts_smp_proc_lock(proc, ERTS_PROC_LOCK_MAIN);
+ }
+
ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc);
- need_save = (ep == NULL || is_non_value(ep->saved_mfa[0]));
+ need_save = (ep == NULL || !ep->saved_current);
if (flags) {
#ifdef ERTS_DIRTY_SCHEDULERS
@@ -1937,12 +2692,15 @@ enif_schedule_nif(ErlNifEnv* env, const char* fun_name, int flags,
sched_fun = schedule_dirty_io_nif;
else if (chkflgs == ERL_NIF_DIRTY_JOB_CPU_BOUND)
sched_fun = schedule_dirty_cpu_nif;
- else
- return enif_make_badarg(env);
+ else {
+ result = enif_make_badarg(env);
+ goto done;
+ }
result = init_nif_sched_data(env, sched_fun, fp, need_save, argc, argv);
#else
- return enif_make_badarg(env);
+ result = enif_make_badarg(env);
#endif
+ goto done;
}
else
result = init_nif_sched_data(env, execute_nif, fp, need_save, argc, argv);
@@ -1950,18 +2708,31 @@ enif_schedule_nif(ErlNifEnv* env, const char* fun_name, int flags,
ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc);
ASSERT(ep);
ep->exp.code[1] = (BeamInstr) fun_name_atom;
+
+done:
+ if (scheduler < 0)
+ erts_smp_proc_unlock(proc, ERTS_PROC_LOCK_MAIN);
+
return result;
}
-#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT
-
int
-enif_is_on_dirty_scheduler(ErlNifEnv* env)
+enif_thread_type(void)
{
- return ERTS_SCHEDULER_IS_DIRTY(env->proc->scheduler_data);
-}
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
+
+ if (!esdp)
+ return ERL_NIF_THR_UNDEFINED;
+
+ if (!ERTS_SCHEDULER_IS_DIRTY(esdp))
+ return ERL_NIF_THR_NORMAL_SCHEDULER;
-#endif /* ERL_NIF_DIRTY_SCHEDULER_SUPPORT */
+ if (ERTS_SCHEDULER_IS_DIRTY_CPU(esdp))
+ return ERL_NIF_THR_DIRTY_CPU_SCHEDULER;
+
+ ASSERT(ERTS_SCHEDULER_IS_DIRTY_IO(esdp));
+ return ERL_NIF_THR_DIRTY_IO_SCHEDULER;
+}
/* Maps */
@@ -2055,14 +2826,13 @@ int enif_make_map_remove(ErlNifEnv* env,
Eterm key,
Eterm *map_out)
{
- int res;
if (!is_map(map_in)) {
return 0;
}
flush_env(env);
- res = erts_maps_remove(env->proc, key, map_in, map_out);
+ (void) erts_maps_take(env->proc, key, map_in, map_out, NULL);
cache_env(env);
- return res;
+ return 1;
}
int enif_map_iterator_create(ErlNifEnv *env,
@@ -2251,17 +3021,17 @@ int enif_map_iterator_get_pair(ErlNifEnv *env,
***************************************************************************/
-static BeamInstr** get_func_pp(BeamInstr* mod_code, Eterm f_atom, unsigned arity)
+static BeamInstr** get_func_pp(BeamCodeHeader* mod_code, Eterm f_atom, unsigned arity)
{
- int n = (int) mod_code[MI_NUM_FUNCTIONS];
+ int n = (int) mod_code->num_functions;
int j;
for (j = 0; j < n; ++j) {
- BeamInstr* code_ptr = (BeamInstr*) mod_code[MI_FUNCTIONS+j];
+ BeamInstr* code_ptr = (BeamInstr*) mod_code->functions[j];
ASSERT(code_ptr[0] == (BeamInstr) BeamOp(op_i_func_info_IaaI));
if (f_atom == ((Eterm) code_ptr[3])
&& arity == ((unsigned) code_ptr[4])) {
- return (BeamInstr**) &mod_code[MI_FUNCTIONS+j];
+ return (BeamInstr**) &mod_code->functions[j];
}
}
return NULL;
@@ -2311,16 +3081,16 @@ Eterm erts_nif_taints(Process* p)
return list;
}
-void erts_print_nif_taints(int to, void* to_arg)
+void erts_print_nif_taints(fmtfn_t to, void* to_arg)
{
struct tainted_module_t* t;
const char* delim = "";
for (t=first_tainted_module ; t!=NULL; t=t->next) {
const Atom* atom = atom_tab(atom_val(t->module_atom));
- erts_print(to,to_arg,"%s%.*s", delim, atom->len, atom->name);
+ erts_cbprintf(to,to_arg,"%s%.*s", delim, atom->len, atom->name);
delim = ",";
}
- erts_print(to,to_arg,"\n");
+ erts_cbprintf(to,to_arg,"\n");
}
@@ -2393,7 +3163,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
ErlNifEntry* entry = NULL;
ErlNifEnv env;
int i, err, encoding;
- Module* mod;
+ Module* module_p;
Eterm mod_atom;
const Atom* mod_atomp;
Eterm f_atom;
@@ -2403,6 +3173,8 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
int veto;
struct erl_module_nif* lib = NULL;
int reload_warning = 0;
+ struct erl_module_instance* this_mi;
+ struct erl_module_instance* prev_mi;
encoding = erts_get_native_filename_encoding();
if (encoding == ERL_FILENAME_WIN_WCHAR) {
@@ -2436,21 +3208,31 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
ASSERT(caller != NULL);
mod_atom = caller[0];
ASSERT(is_atom(mod_atom));
- mod=erts_get_module(mod_atom, erts_active_code_ix());
- ASSERT(mod != NULL);
+ module_p = erts_get_module(mod_atom, erts_active_code_ix());
+ ASSERT(module_p != NULL);
mod_atomp = atom_tab(atom_val(mod_atom));
init_func = erts_static_nif_get_nif_init((char*)mod_atomp->name, mod_atomp->len);
if (init_func != NULL)
handle = init_func;
- if (!in_area(caller, mod->curr.code, mod->curr.code_length)) {
- ASSERT(in_area(caller, mod->old.code, mod->old.code_length));
-
+ this_mi = &module_p->curr;
+ prev_mi = &module_p->old;
+ if (in_area(caller, module_p->old.code_hdr, module_p->old.code_length)) {
ret = load_nif_error(BIF_P, "old_code", "Calling load_nif from old "
"module '%T' not allowed", mod_atom);
- }
- else if (init_func == NULL &&
+ goto error;
+ } else if (module_p->on_load) {
+ ASSERT(module_p->on_load->code_hdr->on_load_function_ptr);
+ if (module_p->curr.code_hdr) {
+ prev_mi = &module_p->curr;
+ } else {
+ prev_mi = &module_p->old;
+ }
+ this_mi = module_p->on_load;
+ }
+
+ if (init_func == NULL &&
(err=erts_sys_ddll_open(lib_name, &handle, &errdesc)) != ERL_DE_NO_ERROR) {
const char slogan[] = "Failed to load NIF library";
if (strstr(errdesc.str, lib_name) != NULL) {
@@ -2499,7 +3281,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
for (i=0; i < entry->num_of_funcs && ret==am_ok; i++) {
BeamInstr** code_pp;
if (!erts_atom_get(f->name, sys_strlen(f->name), &f_atom, ERTS_ATOM_ENC_LATIN1)
- || (code_pp = get_func_pp(mod->curr.code, f_atom, f->arity))==NULL) {
+ || (code_pp = get_func_pp(this_mi->code_hdr, f_atom, f->arity))==NULL) {
ret = load_nif_error(BIF_P,bad_lib,"Function not found %T:%s/%u",
mod_atom, f->name, f->arity);
}
@@ -2550,9 +3332,9 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
erts_refc_init(&lib->rt_cnt, 0);
erts_refc_init(&lib->rt_dtor_cnt, 0);
ASSERT(opened_rt_list == NULL);
- lib->mod = mod;
+ lib->mod = module_p;
env.mod_nif = lib;
- if (mod->curr.nif != NULL) { /*************** Reload ******************/
+ if (this_mi->nif != NULL) { /*************** Reload ******************/
/*
* Repeated load_nif calls from same Erlang module instance ("reload")
* is deprecated and was only ment as a development feature not to
@@ -2560,16 +3342,16 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
*/
int k, old_incr = 0;
ErlNifFunc* old_func;
- lib->priv_data = mod->curr.nif->priv_data;
+ lib->priv_data = this_mi->nif->priv_data;
- ASSERT(mod->curr.nif->entry != NULL);
+ ASSERT(this_mi->nif->entry != NULL);
if (entry->reload == NULL) {
ret = load_nif_error(BIF_P,reload,"Reload not supported by this NIF library.");
goto error;
}
/* Check that no NIF is removed */
- old_func = mod->curr.nif->entry->funcs;
- for (k=0; k < mod->curr.nif->entry->num_of_funcs; k++) {
+ old_func = this_mi->nif->entry->funcs;
+ for (k=0; k < this_mi->nif->entry->num_of_funcs; k++) {
int incr = 0;
ErlNifFunc* f = entry->funcs;
for (i=0; i < entry->num_of_funcs; i++) {
@@ -2585,45 +3367,45 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
old_func->name, old_func->arity);
goto error;
}
- old_func = next_func(mod->curr.nif->entry, &old_incr, old_func);
+ old_func = next_func(this_mi->nif->entry, &old_incr, old_func);
}
- erts_pre_nif(&env, BIF_P, lib);
+ erts_pre_nif(&env, BIF_P, lib, NULL);
veto = entry->reload(&env, &lib->priv_data, BIF_ARG_2);
erts_post_nif(&env);
if (veto) {
- ret = load_nif_error(BIF_P, reload, "Library reload-call unsuccessful.");
+ ret = load_nif_error(BIF_P, reload, "Library reload-call unsuccessful (%d).", veto);
}
else {
commit_opened_resource_types(lib);
- mod->curr.nif->entry = NULL; /* to prevent 'unload' callback */
- erts_unload_nif(mod->curr.nif);
+ this_mi->nif->entry = NULL; /* to prevent 'unload' callback */
+ erts_unload_nif(this_mi->nif);
reload_warning = 1;
}
}
else {
lib->priv_data = NULL;
- if (mod->old.nif != NULL) { /**************** Upgrade ***************/
- void* prev_old_data = mod->old.nif->priv_data;
+ if (prev_mi->nif != NULL) { /**************** Upgrade ***************/
+ void* prev_old_data = prev_mi->nif->priv_data;
if (entry->upgrade == NULL) {
ret = load_nif_error(BIF_P, upgrade, "Upgrade not supported by this NIF library.");
goto error;
}
- erts_pre_nif(&env, BIF_P, lib);
- veto = entry->upgrade(&env, &lib->priv_data, &mod->old.nif->priv_data, BIF_ARG_2);
+ erts_pre_nif(&env, BIF_P, lib, NULL);
+ veto = entry->upgrade(&env, &lib->priv_data, &prev_mi->nif->priv_data, BIF_ARG_2);
erts_post_nif(&env);
if (veto) {
- mod->old.nif->priv_data = prev_old_data;
- ret = load_nif_error(BIF_P, upgrade, "Library upgrade-call unsuccessful.");
+ prev_mi->nif->priv_data = prev_old_data;
+ ret = load_nif_error(BIF_P, upgrade, "Library upgrade-call unsuccessful (%d).", veto);
}
else
commit_opened_resource_types(lib);
}
else if (entry->load != NULL) { /********* Initial load ***********/
- erts_pre_nif(&env, BIF_P, lib);
+ erts_pre_nif(&env, BIF_P, lib, NULL);
veto = entry->load(&env, &lib->priv_data, BIF_ARG_2);
erts_post_nif(&env);
if (veto) {
- ret = load_nif_error(BIF_P, "load", "Library load-call unsuccessful.");
+ ret = load_nif_error(BIF_P, "load", "Library load-call unsuccessful (%d).", veto);
}
else
commit_opened_resource_types(lib);
@@ -2637,12 +3419,12 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
int incr = 0;
ErlNifFunc* f = entry->funcs;
- mod->curr.nif = lib;
+ this_mi->nif = lib;
for (i=0; i < entry->num_of_funcs; i++)
{
BeamInstr* code_ptr;
erts_atom_get(f->name, sys_strlen(f->name), &f_atom, ERTS_ATOM_ENC_LATIN1);
- code_ptr = *get_func_pp(mod->curr.code, f_atom, f->arity);
+ code_ptr = *get_func_pp(this_mi->code_hdr, f_atom, f->arity);
if (code_ptr[1] == 0) {
code_ptr[5+0] = (BeamInstr) BeamOp(op_call_nif);
@@ -2653,16 +3435,16 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
(BeamInstr) BeamOp(op_i_generic_breakpoint));
g->orig_instr = (BeamInstr) BeamOp(op_call_nif);
}
+#ifdef ERTS_DIRTY_SCHEDULERS
if ((entry->major > 2 || (entry->major == 2 && entry->minor >= 7))
&& (entry->options & ERL_NIF_DIRTY_NIF_OPTION) && f->flags) {
-#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT
code_ptr[5+3] = (BeamInstr) f->fptr;
code_ptr[5+1] = (f->flags == ERL_NIF_DIRTY_JOB_IO_BOUND) ?
(BeamInstr) schedule_dirty_io_nif :
(BeamInstr) schedule_dirty_cpu_nif;
-#endif
}
else
+#endif
code_ptr[5+1] = (BeamInstr) f->fptr;
code_ptr[5+2] = (BeamInstr) lib;
f = next_func(entry, &incr, f);
@@ -2706,6 +3488,9 @@ erts_unload_nif(struct erl_module_nif* lib)
ASSERT(erts_smp_thr_progress_is_blocking());
ASSERT(lib != NULL);
ASSERT(lib->mod != NULL);
+
+ erts_tracer_nif_clear();
+
for (rt = resource_type_list.next;
rt != &resource_type_list;
rt = next) {
@@ -2748,6 +3533,78 @@ void erl_nif_init()
resource_type_list.owner = NULL;
resource_type_list.module = THE_NON_VALUE;
resource_type_list.name = THE_NON_VALUE;
+
+}
+
+int erts_nif_get_funcs(struct erl_module_nif* mod,
+ ErlNifFunc **funcs)
+{
+ *funcs = mod->entry->funcs;
+ return mod->entry->num_of_funcs;
+}
+
+Eterm erts_nif_call_function(Process *p, Process *tracee,
+ struct erl_module_nif* mod,
+ ErlNifFunc *fun, int argc, Eterm *argv)
+{
+ Eterm nif_result;
+#ifdef DEBUG
+ /* Verify that function is part of this module */
+ int i;
+ for (i = 0; i < mod->entry->num_of_funcs; i++)
+ if (fun == &(mod->entry->funcs[i]))
+ break;
+ ASSERT(i < mod->entry->num_of_funcs);
+ if (p)
+ ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(p) & ERTS_PROC_LOCK_MAIN
+ || erts_smp_thr_progress_is_blocking());
+#endif
+ if (p) {
+ /* This is almost a normal nif call like in beam_emu,
+ except that any heap consumed by the nif will be
+ released without checking if anything in it is live.
+ This is because we cannot do a GC here as we don't know
+ the number of live registers that have to be preserved.
+ This means that any heap part of the returned term may
+ not be used outside this function. */
+ struct enif_environment_t env;
+ ErlHeapFragment *orig_hf = MBUF(p);
+ ErlOffHeap orig_oh = MSO(p);
+ Eterm *orig_htop = HEAP_TOP(p);
+ ASSERT(is_internal_pid(p->common.id));
+ MBUF(p) = NULL;
+ clear_offheap(&MSO(p));
+
+ erts_pre_nif(&env, p, mod, tracee);
+ nif_result = (*fun->fptr)(&env, argc, argv);
+ if (env.exception_thrown)
+ nif_result = THE_NON_VALUE;
+ erts_post_nif(&env);
+
+ /* Free any offheap and heap fragments created in nif */
+ if (MSO(p).first) {
+ erts_cleanup_offheap(&MSO(p));
+ clear_offheap(&MSO(p));
+ }
+ if (MBUF(p))
+ free_message_buffer(MBUF(p));
+
+ /* restore original heap fragment list */
+ MBUF(p) = orig_hf;
+ MSO(p) = orig_oh;
+ HEAP_TOP(p) = orig_htop;
+ } else {
+ /* Nif call was done without a process context,
+ so we create a phony one. */
+ struct enif_msg_environment_t msg_env;
+ pre_nif_noproc(&msg_env, mod, tracee);
+ nif_result = (*fun->fptr)(&msg_env.env, argc, argv);
+ if (msg_env.env.exception_thrown)
+ nif_result = THE_NON_VALUE;
+ post_nif_noproc(&msg_env);
+ }
+
+ return nif_result;
}
#ifdef USE_VM_PROBES
diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h
index 75070ad901..494971e118 100644
--- a/erts/emulator/beam/erl_nif.h
+++ b/erts/emulator/beam/erl_nif.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2014. All Rights Reserved.
+ * Copyright Ericsson AB 2009-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,7 +28,6 @@
# include "config.h"
#endif
-#include "erl_native_features_config.h"
#include "erl_drv_nif.h"
/* Version history:
@@ -50,9 +49,10 @@
** 2.8: 18.0 add enif_has_pending_exception
** 2.9: 18.2 enif_getenv
** 2.10: Time API
+** 2.11: 19.0 enif_snprintf
*/
#define ERL_NIF_MAJOR_VERSION 2
-#define ERL_NIF_MINOR_VERSION 10
+#define ERL_NIF_MINOR_VERSION 11
/*
* The emulator will refuse to load a nif-lib with a major version
@@ -77,13 +77,8 @@ typedef ErlNapiSInt64 ErlNifSInt64;
typedef ErlNapiUInt ErlNifUInt;
typedef ErlNapiSInt ErlNifSInt;
-#ifdef HALFWORD_HEAP_EMULATOR
-# define ERL_NIF_VM_VARIANT "beam.halfword"
-typedef unsigned int ERL_NIF_TERM;
-#else
# define ERL_NIF_VM_VARIANT "beam.vanilla"
typedef ErlNifUInt ERL_NIF_TERM;
-#endif
typedef ERL_NIF_TERM ERL_NIF_UINT;
@@ -92,16 +87,16 @@ typedef ErlNifSInt64 ErlNifTime;
#define ERL_NIF_TIME_ERROR ((ErlNifSInt64) ERTS_NAPI_TIME_ERROR__)
typedef enum {
- ERL_NIF_SEC = ERTS_NAPI_SEC__,
- ERL_NIF_MSEC = ERTS_NAPI_MSEC__,
- ERL_NIF_USEC = ERTS_NAPI_USEC__,
- ERL_NIF_NSEC = ERTS_NAPI_NSEC__
+ ERL_NIF_SEC = ERTS_NAPI_SEC__,
+ ERL_NIF_MSEC = ERTS_NAPI_MSEC__,
+ ERL_NIF_USEC = ERTS_NAPI_USEC__,
+ ERL_NIF_NSEC = ERTS_NAPI_NSEC__
} ErlNifTimeUnit;
struct enif_environment_t;
typedef struct enif_environment_t ErlNifEnv;
-typedef struct
+typedef struct enif_func_t
{
const char* name;
unsigned arity;
@@ -155,7 +150,12 @@ typedef enum
typedef struct
{
ERL_NIF_TERM pid; /* internal, may change */
-}ErlNifPid;
+} ErlNifPid;
+
+typedef struct
+{
+ ERL_NIF_TERM port_id; /* internal, may change */
+}ErlNifPort;
typedef ErlDrvSysInfo ErlNifSysInfo;
@@ -167,13 +167,11 @@ typedef int ErlNifTSDKey;
typedef ErlDrvThreadOpts ErlNifThreadOpts;
-#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT
typedef enum
{
- ERL_NIF_DIRTY_JOB_CPU_BOUND = ERL_DRV_DIRTY_JOB_CPU_BOUND,
- ERL_NIF_DIRTY_JOB_IO_BOUND = ERL_DRV_DIRTY_JOB_IO_BOUND
+ ERL_NIF_DIRTY_JOB_CPU_BOUND = ERL_DIRTY_JOB_CPU_BOUND,
+ ERL_NIF_DIRTY_JOB_IO_BOUND = ERL_DIRTY_JOB_IO_BOUND
}ErlNifDirtyTaskFlags;
-#endif
typedef struct /* All fields all internal and may change */
{
@@ -202,6 +200,26 @@ typedef enum {
ERL_NIF_MAP_ITERATOR_TAIL = ERL_NIF_MAP_ITERATOR_LAST
} ErlNifMapIteratorEntry;
+typedef enum {
+ ERL_NIF_UNIQUE_POSITIVE = (1 << 0),
+ ERL_NIF_UNIQUE_MONOTONIC = (1 << 1)
+} ErlNifUniqueInteger;
+
+typedef enum {
+ ERL_NIF_BIN2TERM_SAFE = 0x20000000
+} ErlNifBinaryToTerm;
+
+/*
+ * Return values from enif_thread_type(). Negative values
+ * reserved for specific types of non-scheduler threads.
+ * Positive values reserved for scheduler thread types.
+ */
+
+#define ERL_NIF_THR_UNDEFINED 0
+#define ERL_NIF_THR_NORMAL_SCHEDULER 1
+#define ERL_NIF_THR_DIRTY_CPU_SCHEDULER 2
+#define ERL_NIF_THR_DIRTY_IO_SCHEDULER 3
+
#if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_))
# define ERL_NIF_API_FUNC_DECL(RET_TYPE, NAME, ARGS) RET_TYPE (*NAME) ARGS
typedef struct {
@@ -226,28 +244,30 @@ extern TWinDynNifCallbacks WinDynNifCallbacks;
#if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_))
# define ERL_NIF_INIT_GLOB TWinDynNifCallbacks WinDynNifCallbacks;
-# ifdef STATIC_ERLANG_NIF
-# define ERL_NIF_INIT_DECL(MODNAME) __declspec(dllexport) ErlNifEntry* MODNAME ## _nif_init(TWinDynNifCallbacks* callbacks)
-# else
-# define ERL_NIF_INIT_DECL(MODNAME) __declspec(dllexport) ErlNifEntry* nif_init(TWinDynNifCallbacks* callbacks)
-# endif
+# define ERL_NIF_INIT_ARGS TWinDynNifCallbacks* callbacks
# define ERL_NIF_INIT_BODY memcpy(&WinDynNifCallbacks,callbacks,sizeof(TWinDynNifCallbacks))
+# define ERL_NIF_INIT_EXPORT __declspec(dllexport)
#else
# define ERL_NIF_INIT_GLOB
+# define ERL_NIF_INIT_ARGS void
# define ERL_NIF_INIT_BODY
-# ifdef STATIC_ERLANG_NIF
-# define ERL_NIF_INIT_DECL(MODNAME) ErlNifEntry* MODNAME ## _nif_init(void)
+# if defined(__GNUC__) && __GNUC__ >= 4
+# define ERL_NIF_INIT_EXPORT __attribute__ ((visibility("default")))
+# elif defined (__SUNPRO_C) && (__SUNPRO_C >= 0x550)
+# define ERL_NIF_INIT_EXPORT __global
# else
-# define ERL_NIF_INIT_DECL(MODNAME) ErlNifEntry* nif_init(void)
+# define ERL_NIF_INIT_EXPORT
# endif
#endif
-#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT
-# define ERL_NIF_ENTRY_OPTIONS ERL_NIF_DIRTY_NIF_OPTION
+#ifdef STATIC_ERLANG_NIF
+# define ERL_NIF_INIT_DECL(MODNAME) ErlNifEntry* MODNAME ## _nif_init(ERL_NIF_INIT_ARGS)
#else
-# define ERL_NIF_ENTRY_OPTIONS 0
+# define ERL_NIF_INIT_DECL(MODNAME) ERL_NIF_INIT_EXPORT ErlNifEntry* nif_init(ERL_NIF_INIT_ARGS)
#endif
+#define ERL_NIF_ENTRY_OPTIONS ERL_NIF_DIRTY_NIF_OPTION
+
#ifdef __cplusplus
}
# define ERL_NIF_INIT_PROLOGUE extern "C" {
diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h
index 1448a508a2..9a8f216773 100644
--- a/erts/emulator/beam/erl_nif_api_funcs.h
+++ b/erts/emulator/beam/erl_nif_api_funcs.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2014. All Rights Reserved.
+ * Copyright Ericsson AB 2009-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -163,20 +163,22 @@ ERL_NIF_API_FUNC_DECL(int,enif_getenv,(const char* key, char* value, size_t* val
ERL_NIF_API_FUNC_DECL(ErlNifTime, enif_monotonic_time, (ErlNifTimeUnit));
ERL_NIF_API_FUNC_DECL(ErlNifTime, enif_time_offset, (ErlNifTimeUnit));
ERL_NIF_API_FUNC_DECL(ErlNifTime, enif_convert_time_unit, (ErlNifTime, ErlNifTimeUnit, ErlNifTimeUnit));
+ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_now_time, (ErlNifEnv *env));
+ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_cpu_time, (ErlNifEnv *env));
+ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_make_unique_integer, (ErlNifEnv *env, ErlNifUniqueInteger properties));
+ERL_NIF_API_FUNC_DECL(int, enif_is_current_process_alive, (ErlNifEnv *env));
+ERL_NIF_API_FUNC_DECL(int, enif_is_process_alive, (ErlNifEnv *env, ErlNifPid *pid));
+ERL_NIF_API_FUNC_DECL(int, enif_is_port_alive, (ErlNifEnv *env, ErlNifPort *port_id));
+ERL_NIF_API_FUNC_DECL(int, enif_get_local_port, (ErlNifEnv* env, ERL_NIF_TERM, ErlNifPort* port_id));
+ERL_NIF_API_FUNC_DECL(int, enif_term_to_binary, (ErlNifEnv *env, ERL_NIF_TERM term, ErlNifBinary *bin));
+ERL_NIF_API_FUNC_DECL(size_t, enif_binary_to_term, (ErlNifEnv *env, const unsigned char* data, size_t sz, ERL_NIF_TERM *term, unsigned int opts));
+ERL_NIF_API_FUNC_DECL(int, enif_port_command, (ErlNifEnv *env, const ErlNifPort* to_port, ErlNifEnv *msg_env, ERL_NIF_TERM msg));
+ERL_NIF_API_FUNC_DECL(int,enif_thread_type,(void));
+ERL_NIF_API_FUNC_DECL(int,enif_snprintf,(char * buffer, size_t size, const char *format, ...));
/*
** ADD NEW ENTRIES HERE (before this comment) !!!
*/
-
-
-/*
- * Conditional EXPERIMENTAL stuff always last.
- * Must be moved up and made unconditional to support binary backward
- * compatibility on Windows.
- */
-#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT
-ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*));
-#endif
#endif /* ERL_NIF_API_FUNC_DECL */
/*
@@ -318,6 +320,18 @@ ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*));
# define enif_monotonic_time ERL_NIF_API_FUNC_MACRO(enif_monotonic_time)
# define enif_time_offset ERL_NIF_API_FUNC_MACRO(enif_time_offset)
# define enif_convert_time_unit ERL_NIF_API_FUNC_MACRO(enif_convert_time_unit)
+# define enif_now_time ERL_NIF_API_FUNC_MACRO(enif_now_time)
+# define enif_cpu_time ERL_NIF_API_FUNC_MACRO(enif_cpu_time)
+# define enif_make_unique_integer ERL_NIF_API_FUNC_MACRO(enif_make_unique_integer)
+# define enif_is_current_process_alive ERL_NIF_API_FUNC_MACRO(enif_is_current_process_alive)
+# define enif_is_process_alive ERL_NIF_API_FUNC_MACRO(enif_is_process_alive)
+# define enif_is_port_alive ERL_NIF_API_FUNC_MACRO(enif_is_port_alive)
+# define enif_get_local_port ERL_NIF_API_FUNC_MACRO(enif_get_local_port)
+# define enif_term_to_binary ERL_NIF_API_FUNC_MACRO(enif_term_to_binary)
+# define enif_binary_to_term ERL_NIF_API_FUNC_MACRO(enif_binary_to_term)
+# define enif_port_command ERL_NIF_API_FUNC_MACRO(enif_port_command)
+# define enif_thread_type ERL_NIF_API_FUNC_MACRO(enif_thread_type)
+# define enif_snprintf ERL_NIF_API_FUNC_MACRO(enif_snprintf)
/*
** ADD NEW ENTRIES HERE (before this comment)
@@ -328,9 +342,6 @@ ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*));
* Must be moved up and made unconditional to support binary backward
* compatibility on Windows.
*/
-#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT
-# define enif_is_on_dirty_scheduler ERL_NIF_API_FUNC_MACRO(enif_is_on_dirty_scheduler)
-#endif
#endif /* ERL_NIF_API_FUNC_MACRO */
diff --git a/erts/emulator/beam/erl_node_container_utils.h b/erts/emulator/beam/erl_node_container_utils.h
index 211b1a0090..0c76c6fe7d 100644
--- a/erts/emulator/beam/erl_node_container_utils.h
+++ b/erts/emulator/beam/erl_node_container_utils.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -255,7 +255,7 @@ extern ErtsPTab erts_port;
* Refs *
\* */
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
#define internal_ref_no_of_numbers(x) \
(internal_ref_data((x))[0])
@@ -328,8 +328,6 @@ extern ErtsPTab erts_port;
: external_ref_channel_no((x)))
#define is_ref(x) (is_internal_ref((x)) \
|| is_external_ref((x)))
-#define is_ref_rel(x,Base) (is_internal_ref_rel((x),Base) \
- || is_external_ref_rel((x),Base))
#define is_not_ref(x) (!is_ref(x))
#endif
diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c
index e69bd49c42..f37393bce4 100644
--- a/erts/emulator/beam/erl_node_tables.c
+++ b/erts/emulator/beam/erl_node_tables.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2013. 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.
@@ -98,7 +98,7 @@ dist_table_alloc(void *dep_tmpl)
dist_entries++;
dep->prev = NULL;
- erts_refc_init(&dep->refc, -1);
+ erts_smp_refc_init(&dep->refc, -1);
erts_smp_rwmtx_init_opt_x(&dep->rwmtx, &rwmtx_opt, "dist_entry", chnl_nr);
dep->sysname = sysname;
dep->cid = NIL;
@@ -188,7 +188,7 @@ dist_table_free(void *vdep)
void
-erts_dist_table_info(int to, void *to_arg)
+erts_dist_table_info(fmtfn_t to, void *to_arg)
{
int lock = !ERTS_IS_CRASH_DUMPING;
if (lock)
@@ -208,7 +208,7 @@ erts_channel_no_to_dist_entry(Uint cno)
* to the node name is used as channel no.
*/
if(cno == ERST_INTERNAL_CHANNEL_NO) {
- erts_refc_inc(&erts_this_dist_entry->refc, 2);
+ erts_smp_refc_inc(&erts_this_dist_entry->refc, 2);
return erts_this_dist_entry;
}
@@ -231,16 +231,16 @@ erts_sysname_to_connected_dist_entry(Eterm sysname)
de.sysname = sysname;
if(erts_this_dist_entry->sysname == sysname) {
- erts_refc_inc(&erts_this_dist_entry->refc, 2);
+ erts_smp_refc_inc(&erts_this_dist_entry->refc, 2);
return erts_this_dist_entry;
}
erts_smp_rwmtx_rlock(&erts_dist_table_rwmtx);
res_dep = (DistEntry *) hash_get(&erts_dist_table, (void *) &de);
if (res_dep) {
- erts_aint_t refc = erts_refc_inctest(&res_dep->refc, 1);
+ erts_aint_t refc = erts_smp_refc_inctest(&res_dep->refc, 1);
if (refc < 2) /* Pending delete */
- erts_refc_inc(&res_dep->refc, 1);
+ erts_smp_refc_inc(&res_dep->refc, 1);
}
erts_smp_rwmtx_runlock(&erts_dist_table_rwmtx);
if (res_dep) {
@@ -267,9 +267,9 @@ DistEntry *erts_find_or_insert_dist_entry(Eterm sysname)
de.sysname = sysname;
erts_smp_rwmtx_rwlock(&erts_dist_table_rwmtx);
res = hash_put(&erts_dist_table, (void *) &de);
- refc = erts_refc_inctest(&res->refc, 0);
+ refc = erts_smp_refc_inctest(&res->refc, 0);
if (refc < 2) /* New or pending delete */
- erts_refc_inc(&res->refc, 1);
+ erts_smp_refc_inc(&res->refc, 1);
erts_smp_rwmtx_rwunlock(&erts_dist_table_rwmtx);
return res;
}
@@ -282,9 +282,9 @@ DistEntry *erts_find_dist_entry(Eterm sysname)
erts_smp_rwmtx_rlock(&erts_dist_table_rwmtx);
res = hash_get(&erts_dist_table, (void *) &de);
if (res) {
- erts_aint_t refc = erts_refc_inctest(&res->refc, 1);
+ erts_aint_t refc = erts_smp_refc_inctest(&res->refc, 1);
if (refc < 2) /* Pending delete */
- erts_refc_inc(&res->refc, 1);
+ erts_smp_refc_inc(&res->refc, 1);
}
erts_smp_rwmtx_runlock(&erts_dist_table_rwmtx);
return res;
@@ -311,7 +311,7 @@ static void try_delete_dist_entry(void *vdep)
*
* If refc > 0, the entry is in use. Keep the entry.
*/
- refc = erts_refc_dectest(&dep->refc, -1);
+ refc = erts_smp_refc_dectest(&dep->refc, -1);
if (refc == -1)
(void) hash_erase(&erts_dist_table, (void *) dep);
erts_smp_rwmtx_rwunlock(&erts_dist_table_rwmtx);
@@ -497,31 +497,7 @@ node_table_hash(void *venp)
Uint32 cre = ((ErlNode *) venp)->creation;
HashValue h = atom_tab(atom_val(((ErlNode *) venp)->sysname))->slot.bucket.hvalue;
- h *= PRIME0;
- h += cre & 0xff;
-
-#if MAX_CREATION >= (1 << 8)
- h *= PRIME1;
- h += (cre >> 8) & 0xff;
-#endif
-
-#if MAX_CREATION >= (1 << 16)
- h *= PRIME2;
- h += (cre >> 16) & 0xff;
-#endif
-
-#if MAX_CREATION >= (1 << 24)
- h *= PRIME3;
- h += (cre >> 24) & 0xff;
-#endif
-
-#if 0
-/* XXX Problems in older versions of GCC */
- #if MAX_CREATION >= (1UL << 32)
- #error "MAX_CREATION larger than size of expected creation storage (Uint32)"
- #endif
-#endif
- return h;
+ return (h + cre) * PRIME0;
}
static int
@@ -542,7 +518,7 @@ node_table_alloc(void *venp_tmpl)
node_entries++;
- erts_refc_init(&enp->refc, -1);
+ erts_smp_refc_init(&enp->refc, -1);
enp->creation = ((ErlNode *) venp_tmpl)->creation;
enp->sysname = ((ErlNode *) venp_tmpl)->sysname;
enp->dist_entry = erts_find_or_insert_dist_entry(((ErlNode *) venp_tmpl)->sysname);
@@ -588,7 +564,7 @@ erts_node_table_size(void)
}
void
-erts_node_table_info(int to, void *to_arg)
+erts_node_table_info(fmtfn_t to, void *to_arg)
{
int lock = !ERTS_IS_CRASH_DUMPING;
if (lock)
@@ -599,7 +575,7 @@ erts_node_table_info(int to, void *to_arg)
}
-ErlNode *erts_find_or_insert_node(Eterm sysname, Uint creation)
+ErlNode *erts_find_or_insert_node(Eterm sysname, Uint32 creation)
{
ErlNode *res;
ErlNode ne;
@@ -609,9 +585,9 @@ ErlNode *erts_find_or_insert_node(Eterm sysname, Uint creation)
erts_smp_rwmtx_rlock(&erts_node_table_rwmtx);
res = hash_get(&erts_node_table, (void *) &ne);
if (res && res != erts_this_node) {
- erts_aint_t refc = erts_refc_inctest(&res->refc, 0);
+ erts_aint_t refc = erts_smp_refc_inctest(&res->refc, 0);
if (refc < 2) /* New or pending delete */
- erts_refc_inc(&res->refc, 1);
+ erts_smp_refc_inc(&res->refc, 1);
}
erts_smp_rwmtx_runlock(&erts_node_table_rwmtx);
if (res)
@@ -621,9 +597,9 @@ ErlNode *erts_find_or_insert_node(Eterm sysname, Uint creation)
res = hash_put(&erts_node_table, (void *) &ne);
ASSERT(res);
if (res != erts_this_node) {
- erts_aint_t refc = erts_refc_inctest(&res->refc, 0);
+ erts_aint_t refc = erts_smp_refc_inctest(&res->refc, 0);
if (refc < 2) /* New or pending delete */
- erts_refc_inc(&res->refc, 1);
+ erts_smp_refc_inc(&res->refc, 1);
}
erts_smp_rwmtx_rwunlock(&erts_node_table_rwmtx);
return res;
@@ -650,7 +626,7 @@ static void try_delete_node(void *venp)
*
* If refc > 0, the entry is in use. Keep the entry.
*/
- refc = erts_refc_dectest(&enp->refc, -1);
+ refc = erts_smp_refc_dectest(&enp->refc, -1);
if (refc == -1)
(void) hash_erase(&erts_node_table, (void *) enp);
erts_smp_rwmtx_rwunlock(&erts_node_table_rwmtx);
@@ -673,7 +649,7 @@ void erts_schedule_delete_node(ErlNode *enp)
}
struct pn_data {
- int to;
+ fmtfn_t to;
void *to_arg;
Eterm sysname;
int no_sysname;
@@ -696,14 +672,14 @@ static void print_node(void *venp, void *vpndp)
erts_print(pndp->to, pndp->to_arg, " %d", enp->creation);
#ifdef DEBUG
erts_print(pndp->to, pndp->to_arg, " (refc=%ld)",
- erts_refc_read(&enp->refc, 0));
+ erts_smp_refc_read(&enp->refc, 0));
#endif
pndp->no_sysname++;
}
pndp->no_total++;
}
-void erts_print_node_info(int to,
+void erts_print_node_info(fmtfn_t to,
void *to_arg,
Eterm sysname,
int *no_sysname,
@@ -739,19 +715,19 @@ void
erts_set_this_node(Eterm sysname, Uint creation)
{
ERTS_SMP_LC_ASSERT(erts_thr_progress_is_blocking());
- ASSERT(erts_refc_read(&erts_this_dist_entry->refc, 2));
+ ASSERT(erts_smp_refc_read(&erts_this_dist_entry->refc, 2));
- if (erts_refc_dectest(&erts_this_node->refc, 0) == 0)
+ if (erts_smp_refc_dectest(&erts_this_node->refc, 0) == 0)
try_delete_node(erts_this_node);
- if (erts_refc_dectest(&erts_this_dist_entry->refc, 0) == 0)
+ if (erts_smp_refc_dectest(&erts_this_dist_entry->refc, 0) == 0)
try_delete_dist_entry(erts_this_dist_entry);
erts_this_node = NULL; /* to make sure refc is bumped for this node */
erts_this_node = erts_find_or_insert_node(sysname, creation);
erts_this_dist_entry = erts_this_node->dist_entry;
- erts_refc_inc(&erts_this_dist_entry->refc, 2);
+ erts_smp_refc_inc(&erts_this_dist_entry->refc, 2);
erts_this_node_sysname = erts_this_node_sysname_BUFFER;
erts_snprintf(erts_this_node_sysname, sizeof(erts_this_node_sysname_BUFFER),
@@ -787,10 +763,13 @@ void erts_init_node_tables(int dd_sec)
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");
- f.hash = (H_FUN) dist_table_hash;
- f.cmp = (HCMP_FUN) dist_table_cmp;
- f.alloc = (HALLOC_FUN) dist_table_alloc;
- f.free = (HFREE_FUN) dist_table_free;
+ f.hash = (H_FUN) dist_table_hash;
+ f.cmp = (HCMP_FUN) dist_table_cmp;
+ f.alloc = (HALLOC_FUN) dist_table_alloc;
+ f.free = (HFREE_FUN) dist_table_free;
+ f.meta_alloc = (HMALLOC_FUN) erts_alloc;
+ f.meta_free = (HMFREE_FUN) erts_free;
+ f.meta_print = (HMPRINT_FUN) erts_print;
hash_init(ERTS_ALC_T_DIST_TABLE, &erts_dist_table, "dist_table", 11, f);
f.hash = (H_FUN) node_table_hash;
@@ -810,13 +789,13 @@ void erts_init_node_tables(int dd_sec)
node_tmpl.creation = 0;
erts_this_node = hash_put(&erts_node_table, &node_tmpl);
/* +1 for erts_this_node */
- erts_refc_init(&erts_this_node->refc, 1);
+ erts_smp_refc_init(&erts_this_node->refc, 1);
ASSERT(erts_this_node->dist_entry != NULL);
erts_this_dist_entry = erts_this_node->dist_entry;
/* +1 for erts_this_dist_entry */
/* +1 for erts_this_node->dist_entry */
- erts_refc_init(&erts_this_dist_entry->refc, 2);
+ erts_smp_refc_init(&erts_this_dist_entry->refc, 2);
erts_this_node_sysname = erts_this_node_sysname_BUFFER;
@@ -1157,23 +1136,11 @@ insert_offheap(ErlOffHeap *oh, int type, Eterm id)
break;
}
if (insert_bin) {
-#if HALFWORD_HEAP
- UWord val = (UWord) u.pb->val;
- DeclareTmpHeapNoproc(id_heap,BIG_UINT_HEAP_SIZE*2); /* extra place allocated */
-#else
DeclareTmpHeapNoproc(id_heap,BIG_UINT_HEAP_SIZE);
-#endif
Uint *hp = &id_heap[0];
InsertedBin *nib;
-#if HALFWORD_HEAP
- int actual_need = BIG_UWORD_HEAP_SIZE(val);
- ASSERT(actual_need <= (BIG_UINT_HEAP_SIZE*2));
- UseTmpHeapNoproc(actual_need);
- a.id = erts_bld_uword(&hp, NULL, (UWord) val);
-#else
UseTmpHeapNoproc(BIG_UINT_HEAP_SIZE);
a.id = erts_bld_uint(&hp, NULL, (Uint) u.pb->val);
-#endif
erts_match_prog_foreach_offheap(u.pb->val,
insert_offheap2,
(void *) &a);
@@ -1181,11 +1148,7 @@ insert_offheap(ErlOffHeap *oh, int type, Eterm id)
nib->bin_val = u.pb->val;
nib->next = inserted_bins;
inserted_bins = nib;
-#if HALFWORD_HEAP
- UnUseTmpHeapNoproc(actual_need);
-#else
UnUseTmpHeapNoproc(BIG_UINT_HEAP_SIZE);
-#endif
}
}
break;
@@ -1371,56 +1334,50 @@ setup_reference_table(void)
for (i = 0; i < max; i++) {
Process *proc = erts_pix2proc(i);
if (proc) {
- ErlMessage *msg;
+ int mli;
+ ErtsMessage *msg_list[] = {
+ proc->msg.first,
+#ifdef ERTS_SMP
+ proc->msg_inq.first,
+#endif
+ proc->msg_frag};
/* Insert Heap */
insert_offheap(&(proc->off_heap),
HEAP_REF,
proc->common.id);
- /* Insert message buffers */
+ /* Insert heap fragments buffers */
for(hfp = proc->mbuf; hfp; hfp = hfp->next)
insert_offheap(&(hfp->off_heap),
HEAP_REF,
proc->common.id);
- /* Insert msg msg buffers */
- for (msg = proc->msg.first; msg; msg = msg->next) {
- ErlHeapFragment *heap_frag = NULL;
- if (msg->data.attached) {
- if (is_value(ERL_MESSAGE_TERM(msg)))
- heap_frag = msg->data.heap_frag;
- else {
- if (msg->data.dist_ext->dep)
- insert_dist_entry(msg->data.dist_ext->dep,
- HEAP_REF, proc->common.id, 0);
- if (is_not_nil(ERL_MESSAGE_TOKEN(msg)))
- heap_frag = erts_dist_ext_trailer(msg->data.dist_ext);
+
+ /* Insert msg buffers */
+ for (mli = 0; mli < sizeof(msg_list)/sizeof(msg_list[0]); mli++) {
+ ErtsMessage *msg;
+ for (msg = msg_list[mli]; msg; msg = msg->next) {
+ ErlHeapFragment *heap_frag = NULL;
+ if (msg->data.attached) {
+ if (msg->data.attached == ERTS_MSG_COMBINED_HFRAG)
+ heap_frag = &msg->hfrag;
+ else if (is_value(ERL_MESSAGE_TERM(msg)))
+ heap_frag = msg->data.heap_frag;
+ else {
+ if (msg->data.dist_ext->dep)
+ insert_dist_entry(msg->data.dist_ext->dep,
+ HEAP_REF, proc->common.id, 0);
+ if (is_not_nil(ERL_MESSAGE_TOKEN(msg)))
+ heap_frag = erts_dist_ext_trailer(msg->data.dist_ext);
+ }
}
- }
- if (heap_frag)
- insert_offheap(&(heap_frag->off_heap),
- HEAP_REF,
- proc->common.id);
- }
-#ifdef ERTS_SMP
- for (msg = proc->msg_inq.first; msg; msg = msg->next) {
- ErlHeapFragment *heap_frag = NULL;
- if (msg->data.attached) {
- if (is_value(ERL_MESSAGE_TERM(msg)))
- heap_frag = msg->data.heap_frag;
- else {
- if (msg->data.dist_ext->dep)
- insert_dist_entry(msg->data.dist_ext->dep,
- HEAP_REF, proc->common.id, 0);
- if (is_not_nil(ERL_MESSAGE_TOKEN(msg)))
- heap_frag = erts_dist_ext_trailer(msg->data.dist_ext);
+ while (heap_frag) {
+ insert_offheap(&(heap_frag->off_heap),
+ HEAP_REF,
+ proc->common.id);
+ heap_frag = heap_frag->next;
}
}
- if (heap_frag)
- insert_offheap(&(heap_frag->off_heap),
- HEAP_REF,
- proc->common.id);
}
-#endif
/* Insert links */
if (ERTS_P_LINKS(proc))
insert_links(ERTS_P_LINKS(proc), proc->common.id);
@@ -1666,7 +1623,7 @@ reference_table_term(Uint **hpp, Uint *szp)
tup = MK_2TUP(referred_nodes[i].node->sysname,
MK_UINT(referred_nodes[i].node->creation));
- tup = MK_3TUP(tup, MK_UINT(erts_refc_read(&referred_nodes[i].node->refc, 0)), nril);
+ tup = MK_3TUP(tup, MK_UINT(erts_smp_refc_read(&referred_nodes[i].node->refc, 0)), nril);
nl = MK_CONS(tup, nl);
}
@@ -1727,7 +1684,7 @@ reference_table_term(Uint **hpp, Uint *szp)
/* DistList = [{Dist, Refc, ReferenceIdList}] */
tup = MK_3TUP(referred_dists[i].dist->sysname,
- MK_UINT(erts_refc_read(&referred_dists[i].dist->refc, 0)),
+ MK_UINT(erts_smp_refc_read(&referred_dists[i].dist->refc, 0)),
dril);
dl = MK_CONS(tup, dl);
}
diff --git a/erts/emulator/beam/erl_node_tables.h b/erts/emulator/beam/erl_node_tables.h
index 694ac84232..489da1ba17 100644
--- a/erts/emulator/beam/erl_node_tables.h
+++ b/erts/emulator/beam/erl_node_tables.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2012. 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.
@@ -41,6 +41,7 @@
#include "sys.h"
#include "hash.h"
+#include "erl_alloc.h"
#include "erl_process.h"
#include "erl_monitors.h"
#include "erl_smp.h"
@@ -66,7 +67,7 @@
#define ERTS_DE_QFLGS_ALL (ERTS_DE_QFLG_BUSY \
| ERTS_DE_QFLG_EXIT)
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
#define ERTS_DIST_OUTPUT_BUF_DBG_PATTERN ((Uint) 0xf713f713f713f713UL)
#else
#define ERTS_DIST_OUTPUT_BUF_DBG_PATTERN ((Uint) 0xf713f713)
@@ -106,7 +107,7 @@ typedef struct dist_entry_ {
HashBucket hash_bucket; /* Hash bucket */
struct dist_entry_ *next; /* Next entry in dist_table (not sorted) */
struct dist_entry_ *prev; /* Previous entry in dist_table (not sorted) */
- erts_refc_t refc; /* Reference count */
+ erts_smp_refc_t refc; /* Reference count */
erts_smp_rwmtx_t rwmtx; /* Protects all fields below until lck_mtx. */
Eterm sysname; /* name@host atom for efficiency */
@@ -148,7 +149,7 @@ typedef struct dist_entry_ {
typedef struct erl_node_ {
HashBucket hash_bucket; /* Hash bucket */
- erts_refc_t refc; /* Reference count */
+ erts_smp_refc_t refc; /* Reference count */
Eterm sysname; /* name@host atom for efficiency */
Uint32 creation; /* Creation */
DistEntry *dist_entry; /* Corresponding dist entry */
@@ -178,16 +179,16 @@ DistEntry *erts_find_or_insert_dist_entry(Eterm);
DistEntry *erts_find_dist_entry(Eterm);
void erts_schedule_delete_dist_entry(DistEntry *);
Uint erts_dist_table_size(void);
-void erts_dist_table_info(int, void *);
+void erts_dist_table_info(fmtfn_t, void *);
void erts_set_dist_entry_not_connected(DistEntry *);
void erts_set_dist_entry_connected(DistEntry *, Eterm, Uint);
-ErlNode *erts_find_or_insert_node(Eterm, Uint);
+ErlNode *erts_find_or_insert_node(Eterm, Uint32);
void erts_schedule_delete_node(ErlNode *);
void erts_set_this_node(Eterm, Uint);
Uint erts_node_table_size(void);
void erts_init_node_tables(int);
-void erts_node_table_info(int, void *);
-void erts_print_node_info(int, void *, Eterm, int*, int*);
+void erts_node_table_info(fmtfn_t, void *);
+void erts_print_node_info(fmtfn_t, void *, Eterm, int*, int*);
Eterm erts_get_node_and_dist_references(struct process *);
#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
int erts_lc_is_de_rwlocked(DistEntry *);
@@ -209,7 +210,7 @@ ERTS_GLB_INLINE void
erts_deref_dist_entry(DistEntry *dep)
{
ASSERT(dep);
- if (erts_refc_dectest(&dep->refc, 0) == 0)
+ if (erts_smp_refc_dectest(&dep->refc, 0) == 0)
erts_schedule_delete_dist_entry(dep);
}
@@ -217,7 +218,7 @@ ERTS_GLB_INLINE void
erts_deref_node_entry(ErlNode *np)
{
ASSERT(np);
- if (erts_refc_dectest(&np->refc, 0) == 0)
+ if (erts_smp_refc_dectest(&np->refc, 0) == 0)
erts_schedule_delete_node(np);
}
diff --git a/erts/emulator/beam/erl_port.h b/erts/emulator/beam/erl_port.h
index acd68ef0ad..c59b42cdae 100644
--- a/erts/emulator/beam/erl_port.h
+++ b/erts/emulator/beam/erl_port.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2012-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2012-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -185,8 +185,13 @@ struct _erl_drv_port {
int control_flags; /* Flags for port_control() */
ErlDrvPDL port_data_lock;
- ErtsPrtSD *psd; /* Port specific data */
+ erts_smp_atomic_t psd; /* Port specific data */
int reds; /* Only used while executing driver callbacks */
+
+ struct {
+ Eterm to;
+ Uint32 ref[ERTS_MAX_REF_NUMBERS];
+ } *async_open_port; /* Reference used with async open port */
};
@@ -247,22 +252,51 @@ ERTS_GLB_INLINE void *erts_prtsd_set(Port *p, int ix, void *new);
ERTS_GLB_INLINE void *
erts_prtsd_get(Port *prt, int ix)
{
- return prt->psd ? prt->psd->data[ix] : NULL;
+ ErtsPrtSD *psd = (ErtsPrtSD *) erts_smp_atomic_read_nob(&prt->psd);
+ if (!psd)
+ return NULL;
+ ERTS_SMP_DATA_DEPENDENCY_READ_MEMORY_BARRIER;
+ return psd->data[ix];
}
ERTS_GLB_INLINE void *
erts_prtsd_set(Port *prt, int ix, void *data)
{
- if (prt->psd) {
- void *old = prt->psd->data[ix];
- prt->psd->data[ix] = data;
+ ErtsPrtSD *psd, *new_psd;
+ void *old;
+ int i;
+
+ psd = (ErtsPrtSD *) erts_smp_atomic_read_nob(&prt->psd);
+
+ if (psd) {
+#ifdef ERTS_SMP
+#ifdef ETHR_ORDERED_READ_DEPEND
+ ETHR_MEMBAR(ETHR_LoadStore|ETHR_StoreStore);
+#else
+ ETHR_MEMBAR(ETHR_LoadLoad|ETHR_LoadStore|ETHR_StoreStore);
+#endif
+#endif
+ old = psd->data[ix];
+ psd->data[ix] = data;
return old;
}
- else {
- prt->psd = erts_alloc(ERTS_ALC_T_PRTSD, sizeof(ErtsPrtSD));
- prt->psd->data[ix] = data;
+
+ if (!data)
return NULL;
- }
+
+ new_psd = erts_alloc(ERTS_ALC_T_PRTSD, sizeof(ErtsPrtSD));
+ for (i = 0; i < ERTS_PRTSD_SIZE; i++)
+ new_psd->data[i] = NULL;
+ psd = (ErtsPrtSD *) erts_smp_atomic_cmpxchg_mb(&prt->psd,
+ (erts_aint_t) new_psd,
+ (erts_aint_t) NULL);
+ if (psd)
+ erts_free(ERTS_ALC_T_PRTSD, new_psd);
+ else
+ psd = new_psd;
+ old = psd->data[ix];
+ psd->data[ix] = data;
+ return old;
}
#endif
@@ -327,13 +361,15 @@ Eterm erts_request_io_bytes(Process *c_p);
#define ERTS_PORT_REDS_CONNECT (CONTEXT_REDS/200)
#define ERTS_PORT_REDS_UNLINK (CONTEXT_REDS/200)
#define ERTS_PORT_REDS_LINK (CONTEXT_REDS/200)
+#define ERTS_PORT_REDS_MONITOR (CONTEXT_REDS/200)
+#define ERTS_PORT_REDS_DEMONITOR (CONTEXT_REDS/200)
#define ERTS_PORT_REDS_BADSIG (CONTEXT_REDS/200)
#define ERTS_PORT_REDS_CONTROL (CONTEXT_REDS/100)
#define ERTS_PORT_REDS_CALL (CONTEXT_REDS/50)
#define ERTS_PORT_REDS_INFO (CONTEXT_REDS/100)
#define ERTS_PORT_REDS_TERMINATE (CONTEXT_REDS/50)
-void print_port_info(Port *, int, void *);
+void print_port_info(Port *, fmtfn_t, void *);
void erts_port_free(Port *);
#ifndef ERTS_SMP
void erts_port_cleanup(Port *);
@@ -453,6 +489,7 @@ ERTS_GLB_INLINE Port*erts_id2port(Eterm id);
ERTS_GLB_INLINE Port *erts_id2port_sflgs(Eterm, Process *, ErtsProcLocks, Uint32);
ERTS_GLB_INLINE void erts_port_release(Port *);
#ifdef ERTS_SMP
+ERTS_GLB_INLINE Port *erts_thr_port_lookup(Eterm id, Uint32 invalid_sflgs);
ERTS_GLB_INLINE Port *erts_thr_id2port_sflgs(Eterm id, Uint32 invalid_sflgs);
ERTS_GLB_INLINE void erts_thr_port_release(Port *prt);
#endif
@@ -592,6 +629,44 @@ erts_port_release(Port *prt)
}
#ifdef ERTS_SMP
+/*
+ * erts_thr_id2port_sflgs() and erts_port_dec_refc(prt) can
+ * be used by unmanaged threads in the SMP case.
+ */
+ERTS_GLB_INLINE Port *
+erts_thr_port_lookup(Eterm id, Uint32 invalid_sflgs)
+{
+ Port *prt;
+ ErtsThrPrgrDelayHandle dhndl;
+
+ if (is_not_internal_port(id))
+ return NULL;
+
+ dhndl = erts_thr_progress_unmanaged_delay();
+
+ prt = (Port *) erts_ptab_pix2intptr_ddrb(&erts_port,
+ internal_port_index(id));
+
+ if (!prt || prt->common.id != id) {
+ erts_thr_progress_unmanaged_continue(dhndl);
+ return NULL;
+ }
+ else {
+ erts_aint32_t state;
+ erts_port_inc_refc(prt);
+
+ if (dhndl != ERTS_THR_PRGR_DHANDLE_MANAGED)
+ erts_thr_progress_unmanaged_continue(dhndl);
+
+ state = erts_atomic32_read_acqb(&prt->state);
+ if (state & invalid_sflgs) {
+ erts_port_dec_refc(prt);
+ return NULL;
+ }
+
+ return prt;
+ }
+}
/*
* erts_thr_id2port_sflgs() and erts_thr_port_release() can
@@ -687,7 +762,7 @@ erts_drvport2port_state(ErlDrvPort drvport, erts_aint32_t *statep)
Port *prt = ERTS_ErlDrvPort2Port(drvport);
erts_aint32_t state;
ASSERT(prt);
- ERTS_LC_ASSERT(erts_lc_is_emu_thr());
+// ERTS_LC_ASSERT(erts_lc_is_emu_thr());
if (prt == ERTS_INVALID_ERL_DRV_PORT)
return ERTS_INVALID_ERL_DRV_PORT;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)
@@ -777,16 +852,20 @@ void erts_port_resume_procs(Port *);
struct binary;
-#define ERTS_P2P_SIG_TYPE_BAD 0
-#define ERTS_P2P_SIG_TYPE_OUTPUT 1
-#define ERTS_P2P_SIG_TYPE_OUTPUTV 2
-#define ERTS_P2P_SIG_TYPE_CONNECT 3
-#define ERTS_P2P_SIG_TYPE_EXIT 4
-#define ERTS_P2P_SIG_TYPE_CONTROL 5
-#define ERTS_P2P_SIG_TYPE_CALL 6
-#define ERTS_P2P_SIG_TYPE_INFO 7
-#define ERTS_P2P_SIG_TYPE_LINK 8
-#define ERTS_P2P_SIG_TYPE_UNLINK 9
+enum {
+ ERTS_P2P_SIG_TYPE_BAD = 0,
+ ERTS_P2P_SIG_TYPE_OUTPUT = 1,
+ ERTS_P2P_SIG_TYPE_OUTPUTV = 2,
+ ERTS_P2P_SIG_TYPE_CONNECT = 3,
+ ERTS_P2P_SIG_TYPE_EXIT = 4,
+ ERTS_P2P_SIG_TYPE_CONTROL = 5,
+ ERTS_P2P_SIG_TYPE_CALL = 6,
+ ERTS_P2P_SIG_TYPE_INFO = 7,
+ ERTS_P2P_SIG_TYPE_LINK = 8,
+ ERTS_P2P_SIG_TYPE_UNLINK = 9,
+ ERTS_P2P_SIG_TYPE_MONITOR = 10,
+ ERTS_P2P_SIG_TYPE_DEMONITOR = 11
+};
#define ERTS_P2P_SIG_TYPE_BITS 4
#define ERTS_P2P_SIG_TYPE_MASK \
@@ -848,6 +927,15 @@ struct ErtsProc2PortSigData_ {
struct {
Eterm from;
} unlink;
+ struct {
+ Eterm origin; /* who receives monitor event, pid */
+ Eterm name; /* either name for named monitor, or port id */
+ } monitor;
+ struct {
+ Eterm origin; /* who is at the other end of the monitor, pid */
+ Eterm name; /* port id */
+ Uint32 ref[ERTS_MAX_REF_NUMBERS]; /* box contents of a ref */
+ } demonitor;
} u;
} ;
@@ -910,7 +998,7 @@ erts_schedule_proc2port_signal(Process *,
ErtsPortTaskHandle *,
ErtsProc2PortSigCallback);
-int erts_deliver_port_exit(Port *, Eterm, Eterm, int);
+int erts_deliver_port_exit(Port *, Eterm, Eterm, int, int);
/*
* Port signal flags
@@ -944,4 +1032,34 @@ ErtsPortOpResult erts_port_control(Process *, Port *, unsigned int, Eterm, Eterm
ErtsPortOpResult erts_port_call(Process *, Port *, unsigned int, Eterm, Eterm *);
ErtsPortOpResult erts_port_info(Process *, Port *, Eterm, Eterm *);
+/* Creates monitor between Origin and Target. Ref must be initialized to
+ * a reference (ref may be rewritten to be used to serve additionally as a
+ * signal id). Name is atom if user monitors port by name or NIL */
+ErtsPortOpResult erts_port_monitor(Process *origin, Port *target, Eterm name,
+ Eterm *ref);
+
+typedef enum {
+ /* Normal demonitor rules apply with locking and reductions bump */
+ ERTS_PORT_DEMONITOR_NORMAL = 1,
+ /* Relaxed demonitor rules when process is about to die, which means that
+ * pid lookup won't work, locks won't work, no reductions bump. */
+ ERTS_PORT_DEMONITOR_ORIGIN_ON_DEATHBED = 2,
+} ErtsDemonitorMode;
+
+/* Removes monitor between origin and target, identified by ref.
+ * origin_is_dying can be 0 (false, normal locking rules and reductions bump
+ * apply) or 1 (true, in case when we avoid origin locking) */
+ErtsPortOpResult erts_port_demonitor(Process *origin, ErtsDemonitorMode mode,
+ Port *target, Eterm ref,
+ Eterm *trap_ref);
+/* defined in erl_bif_port.c */
+Port *erts_sig_lookup_port(Process *c_p, Eterm id_or_name);
+
+int erts_port_output_async(Port *, Eterm, Eterm);
+
+/*
+ * Signals from ports to ports. Used by sys drivers.
+ */
+int erl_drv_port_control(Eterm, char, char*, ErlDrvSizeT);
+
#endif
diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c
index ddcca06b0a..3102e44c11 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-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,6 +35,7 @@
#include "dist.h"
#include "erl_check_io.h"
#include "dtrace-wrapper.h"
+#include "lttng-wrapper.h"
#include <stdarg.h>
/*
@@ -69,6 +70,18 @@ static void chk_task_queues(Port *pp, ErtsPortTask *execq, int processing_busy_q
#else
#define DTRACE_DRIVER(PROBE_NAME, PP) do {} while(0)
#endif
+#ifdef USE_LTTNG_VM_TRACEPOINTS
+#define LTTNG_DRIVER(TRACEPOINT, PP) \
+ if (LTTNG_ENABLED(TRACEPOINT)) { \
+ lttng_decl_portbuf(port_str); \
+ lttng_decl_procbuf(proc_str); \
+ lttng_pid_to_str(ERTS_PORT_GET_CONNECTED(PP), proc_str); \
+ lttng_port_to_str((PP), port_str); \
+ LTTNG3(TRACEPOINT, proc_str, port_str, (PP)->name); \
+ }
+#else
+#define LTTNG_DRIVER(TRACEPOINT, PP) do {} while(0)
+#endif
#define ERTS_SMP_LC_VERIFY_RQ(RQ, PP) \
do { \
@@ -180,10 +193,9 @@ p2p_sig_data_to_task(ErtsProc2PortSigData *sigdp)
return ptp;
}
-ErtsProc2PortSigData *
-erts_port_task_alloc_p2p_sig_data(void)
+static ERTS_INLINE ErtsProc2PortSigData *
+p2p_sig_data_init(ErtsPortTask *ptp)
{
- ErtsPortTask *ptp = port_task_alloc();
ptp->type = ERTS_PORT_TASK_PROC_SIG;
ptp->u.alive.flags = ERTS_PT_FLG_SIG_DEP;
@@ -194,6 +206,31 @@ erts_port_task_alloc_p2p_sig_data(void)
return &ptp->u.alive.td.psig.data;
}
+ErtsProc2PortSigData *
+erts_port_task_alloc_p2p_sig_data(void)
+{
+ ErtsPortTask *ptp = port_task_alloc();
+
+ return p2p_sig_data_init(ptp);
+}
+
+ErtsProc2PortSigData *
+erts_port_task_alloc_p2p_sig_data_extra(size_t extra, void **extra_ptr)
+{
+ ErtsPortTask *ptp = erts_alloc(ERTS_ALC_T_PORT_TASK,
+ sizeof(ErtsPortTask) + extra);
+
+ *extra_ptr = ptp+1;
+
+ return p2p_sig_data_init(ptp);
+}
+
+void
+erts_port_task_free_p2p_sig_data(ErtsProc2PortSigData *sigdp)
+{
+ schedule_port_task_free(p2p_sig_data_to_task(sigdp));
+}
+
static ERTS_INLINE Eterm
task_caller(ErtsPortTask *ptp)
{
@@ -1648,6 +1685,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
int active;
Uint64 start_time = 0;
ErtsSchedulerData *esdp = runq->scheduler;
+ ERTS_MSACC_PUSH_STATE_M();
ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(runq));
@@ -1690,6 +1728,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
state = erts_atomic32_read_nob(&pp->state);
pp->reds = ERTS_PORT_REDS_EXECUTE;
+ ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_PORT);
goto begin_handle_tasks;
while (1) {
@@ -1726,6 +1765,9 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
reds = ERTS_PORT_REDS_TIMEOUT;
if (!(state & ERTS_PORT_SFLGS_DEAD)) {
DTRACE_DRIVER(driver_timeout, pp);
+ LTTNG_DRIVER(driver_timeout, pp);
+ if (IS_TRACED_FL(pp, F_TRACE_RECEIVE))
+ trace_port(pp, am_receive, am_timeout);
(*pp->drv_ptr->timeout)((ErlDrvData) pp->drv_data);
}
}
@@ -1734,7 +1776,8 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
reds = ERTS_PORT_REDS_INPUT;
ASSERT((state & ERTS_PORT_SFLGS_DEAD) == 0);
DTRACE_DRIVER(driver_ready_input, pp);
- /* NOTE some windows/ose drivers use ->ready_input
+ LTTNG_DRIVER(driver_ready_input, pp);
+ /* NOTE some windows drivers use ->ready_input
for input and output */
(*pp->drv_ptr->ready_input)((ErlDrvData) pp->drv_data,
ptp->u.alive.td.io.event);
@@ -1745,6 +1788,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
reds = ERTS_PORT_REDS_OUTPUT;
ASSERT((state & ERTS_PORT_SFLGS_DEAD) == 0);
DTRACE_DRIVER(driver_ready_output, pp);
+ LTTNG_DRIVER(driver_ready_output, pp);
(*pp->drv_ptr->ready_output)((ErlDrvData) pp->drv_data,
ptp->u.alive.td.io.event);
reset_executed_io_task_handle(ptp);
@@ -1754,6 +1798,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
reds = ERTS_PORT_REDS_EVENT;
ASSERT((state & ERTS_PORT_SFLGS_DEAD) == 0);
DTRACE_DRIVER(driver_event, pp);
+ LTTNG_DRIVER(driver_event, pp);
(*pp->drv_ptr->event)((ErlDrvData) pp->drv_data,
ptp->u.alive.td.io.event,
ptp->u.alive.td.io.event_data);
@@ -1822,6 +1867,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
}
erts_unblock_fpe(fpe_was_unmasked);
+ ERTS_MSACC_POP_STATE_M();
if (io_tasks_executed) {
diff --git a/erts/emulator/beam/erl_port_task.h b/erts/emulator/beam/erl_port_task.h
index 335f7a77d5..2a6bd165a3 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-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -269,6 +269,8 @@ int erts_port_task_schedule(Eterm,
void erts_port_task_free_port(Port *);
int erts_port_is_scheduled(Port *);
ErtsProc2PortSigData *erts_port_task_alloc_p2p_sig_data(void);
+ErtsProc2PortSigData *erts_port_task_alloc_p2p_sig_data_extra(size_t extra, void **extra_ptr);
+void erts_port_task_free_p2p_sig_data(ErtsProc2PortSigData *sigdp);
#ifdef ERTS_SMP
void erts_enqueue_port(ErtsRunQueue *rq, Port *pp);
diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c
index 2917d58932..1a579704a8 100644
--- a/erts/emulator/beam/erl_printf_term.c
+++ b/erts/emulator/beam/erl_printf_term.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2014. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -117,13 +117,12 @@ do { \
/* return 0 if list is not a non-empty flat list of printable characters */
static int
-is_printable_string(Eterm list, Eterm* base)
-{
+is_printable_string(Eterm list) {
int len = 0;
int c;
while(is_list(list)) {
- Eterm* consp = list_val_rel(list, base);
+ Eterm* consp = list_val(list);
Eterm hd = CAR(consp);
if (!is_byte(hd))
@@ -260,9 +259,7 @@ static char *format_binary(Uint16 x, char *b) {
#endif
static int
-print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount,
- Eterm* obj_base) /* ignored if !HALFWORD_HEAP */
-{
+print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) {
DECLARE_WSTACK(s);
int res;
int i;
@@ -308,7 +305,7 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount,
obj = (Eterm) popped.word;
L_print_one_cons:
{
- Eterm* cons = list_val_rel(obj, obj_base);
+ Eterm* cons = list_val(obj);
Eterm tl;
obj = CAR(cons);
@@ -344,11 +341,7 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount,
PRINT_CHAR(res, fn, arg, '>');
goto L_done;
}
-#if HALFWORD_HEAP
- wobj = is_immed(obj) ? (Wterm)obj : rterm2wterm(obj, obj_base);
-#else
wobj = (Wterm)obj;
-#endif
switch (tag_val_def(wobj)) {
case NIL_DEF:
PRINT_STRING(res, fn, arg, "[]");
@@ -424,10 +417,10 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount,
PRINT_CHAR(res, fn, arg, '>');
break;
case LIST_DEF:
- if (is_printable_string(obj, obj_base)) {
+ if (is_printable_string(obj)) {
int c;
PRINT_CHAR(res, fn, arg, '"');
- nobj = list_val_rel(obj, obj_base);
+ nobj = list_val(obj);
while (1) {
if ((*dcount)-- <= 0)
goto L_done;
@@ -441,7 +434,7 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount,
}
if (is_not_list(*nobj))
break;
- nobj = list_val_rel(*nobj, obj_base);
+ nobj = list_val(*nobj);
}
PRINT_CHAR(res, fn, arg, '"');
} else {
@@ -469,11 +462,11 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount,
case BINARY_DEF:
{
byte* bytep;
- Uint bytesize = binary_size_rel(obj,obj_base);
+ Uint bytesize = binary_size(obj);
Uint bitoffs;
Uint bitsize;
byte octet;
- ERTS_GET_BINARY_BYTES_REL(obj, bytep, bitoffs, bitsize, obj_base);
+ ERTS_GET_BINARY_BYTES(obj, bytep, bitoffs, bitsize);
if (bitsize || !bytesize
|| !is_printable_ascii(bytep, bytesize, bitoffs)) {
@@ -648,13 +641,11 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount,
int
-erts_printf_term(fmtfn_t fn, void* arg, ErlPfEterm term, long precision,
- ErlPfEterm* term_base)
-{
+erts_printf_term(fmtfn_t fn, void* arg, ErlPfEterm term, long precision) {
int res;
ERTS_CT_ASSERT(sizeof(ErlPfEterm) == sizeof(Eterm));
- res = print_term(fn, arg, (Eterm)term, &precision, (Eterm*)term_base);
+ res = print_term(fn, arg, (Eterm)term, &precision);
if (res < 0)
return res;
if (precision <= 0)
diff --git a/erts/emulator/beam/erl_printf_term.h b/erts/emulator/beam/erl_printf_term.h
index 87618a36d7..8a30286fd8 100644
--- a/erts/emulator/beam/erl_printf_term.h
+++ b/erts/emulator/beam/erl_printf_term.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,6 +22,5 @@
#define ERL_PRINTF_TERM_H__
#include "erl_printf_format.h"
-int erts_printf_term(fmtfn_t fn, void* arg, ErlPfEterm term, long precision,
- ErlPfEterm* term_base);
+int erts_printf_term(fmtfn_t fn, void* arg, ErlPfEterm term, long precision);
#endif
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index 4ec98d5cc7..54724fd62d 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2014. 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.
@@ -43,6 +43,7 @@
#include "erl_thr_queue.h"
#include "erl_async.h"
#include "dtrace-wrapper.h"
+#include "lttng-wrapper.h"
#include "erl_ptab.h"
#include "erl_bif_unique.h"
#define ERTS_WANT_TIMER_WHEEL_API
@@ -58,11 +59,7 @@
#define ERTS_PROC_MIN_CONTEXT_SWITCH_REDS_COST (CONTEXT_REDS/10)
-#ifndef ERTS_SCHED_MIN_SPIN
#define ERTS_SCHED_SPIN_UNTIL_YIELD 100
-#else
-#define ERTS_SCHED_SPIN_UNTIL_YIELD 1
-#endif
#define ERTS_SCHED_SYS_SLEEP_SPINCOUNT_VERY_LONG 40
#define ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_VERY_LONG 1000
@@ -119,7 +116,7 @@
#define RUNQ_SET_RQ(X, RQ) erts_smp_atomic_set_nob((X), (erts_aint_t) (RQ))
#ifdef DEBUG
-# if defined(ARCH_64) && !HALFWORD_HEAP
+# if defined(ARCH_64)
# define ERTS_DBG_SET_INVALID_RUNQP(RQP, N) \
(RUNQ_SET_RQ((RQP), (0xdeadbeefdead0003LL | ((N) << 4)))
# define ERTS_DBG_VERIFY_VALID_RUNQP(RQP) \
@@ -152,19 +149,14 @@ extern BeamInstr beam_apply[];
extern BeamInstr beam_exit[];
extern BeamInstr beam_continue_exit[];
-#ifdef __OSE__
-/* Eager check I/O not supported on OSE yet. */
-int erts_eager_check_io = 0;
-#else
-int erts_eager_check_io = 1;
-#endif
-int erts_sched_compact_load;
-int erts_sched_balance_util = 0;
-Uint erts_no_schedulers;
-#ifdef ERTS_DIRTY_SCHEDULERS
-Uint erts_no_dirty_cpu_schedulers;
-Uint erts_no_dirty_io_schedulers;
-#endif
+int ERTS_WRITE_UNLIKELY(erts_default_spo_flags) = SPO_ON_HEAP_MSGQ;
+int ERTS_WRITE_UNLIKELY(erts_eager_check_io) = 1;
+int ERTS_WRITE_UNLIKELY(erts_sched_compact_load);
+int ERTS_WRITE_UNLIKELY(erts_sched_balance_util) = 0;
+Uint ERTS_WRITE_UNLIKELY(erts_no_schedulers);
+Uint ERTS_WRITE_UNLIKELY(erts_no_total_schedulers);
+Uint ERTS_WRITE_UNLIKELY(erts_no_dirty_cpu_schedulers) = 0;
+Uint ERTS_WRITE_UNLIKELY(erts_no_dirty_io_schedulers) = 0;
static char *erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_NO_FLAGS] = {0};
int erts_aux_work_no_flags = ERTS_SSI_AUX_WORK_NO_FLAGS;
@@ -196,84 +188,176 @@ int erts_disable_proc_not_running_opt;
static ErtsAuxWorkData *aux_thread_aux_work_data;
-#define ERTS_SCHDLR_SSPND_CHNG_WAITER (((erts_aint32_t) 1) << 0)
+#define ERTS_SCHDLR_SSPND_CHNG_NMSB (((erts_aint32_t) 1) << 0)
#define ERTS_SCHDLR_SSPND_CHNG_MSB (((erts_aint32_t) 1) << 1)
#define ERTS_SCHDLR_SSPND_CHNG_ONLN (((erts_aint32_t) 1) << 2)
+#define ERTS_SCHDLR_SSPND_CHNG_DCPU_ONLN (((erts_aint32_t) 1) << 3)
-#ifndef DEBUG
+typedef enum {
+ ERTS_SCHED_NORMAL,
+ ERTS_SCHED_DIRTY_CPU,
+ ERTS_SCHED_DIRTY_IO
+} ErtsSchedType;
-#define ERTS_SCHDLR_SSPND_CHNG_SET(VAL, OLD_VAL) \
- erts_smp_atomic32_set_nob(&schdlr_sspnd.changing, (VAL))
+typedef struct {
+ int ongoing;
+ ErtsProcList *blckrs;
+ ErtsProcList *chngq;
+} ErtsMultiSchedulingBlock;
-#ifdef ERTS_DIRTY_SCHEDULERS
-#define ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET(VAL, OLD_VAL) \
- erts_smp_atomic32_set_nob(&schdlr_sspnd.dirty_cpu_changing, (VAL))
-#define ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET(VAL, OLD_VAL) \
- erts_smp_atomic32_set_nob(&schdlr_sspnd.dirty_io_changing, (VAL))
-#endif
+static struct {
+ erts_smp_mtx_t mtx;
+ Uint32 online;
+ Uint32 curr_online;
+ Uint32 active;
+ erts_smp_atomic32_t changing;
+ ErtsProcList *chngq;
+ Eterm changer;
+ ErtsMultiSchedulingBlock nmsb; /* Normal multi Scheduling Block */
+ ErtsMultiSchedulingBlock msb; /* Multi Scheduling Block */
+} schdlr_sspnd;
-#else
+#define ERTS_SCHDLR_SSPND_S_BITS 10
+#define ERTS_SCHDLR_SSPND_DCS_BITS 11
+#define ERTS_SCHDLR_SSPND_DIS_BITS 11
-#define ERTS_SCHDLR_SSPND_CHNG_SET(VAL, OLD_VAL) \
-do { \
- erts_aint32_t old_val__; \
- old_val__ = erts_smp_atomic32_xchg_nob(&schdlr_sspnd.changing, \
- (VAL)); \
- ASSERT(old_val__ == (OLD_VAL)); \
-} while (0)
+#define ERTS_SCHDLR_SSPND_S_MASK ((1 << ERTS_SCHDLR_SSPND_S_BITS)-1)
+#define ERTS_SCHDLR_SSPND_DCS_MASK ((1 << ERTS_SCHDLR_SSPND_DCS_BITS)-1)
+#define ERTS_SCHDLR_SSPND_DIS_MASK ((1 << ERTS_SCHDLR_SSPND_DIS_BITS)-1)
-#ifdef ERTS_DIRTY_SCHEDULERS
-#define ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET(VAL, OLD_VAL) \
-do { \
- erts_aint32_t old_val__; \
- old_val__ = erts_smp_atomic32_xchg_nob(&schdlr_sspnd.dirty_cpu_changing, \
- (VAL)); \
- ASSERT(old_val__ == (OLD_VAL)); \
-} while (0)
-#define ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET(VAL, OLD_VAL) \
-do { \
- erts_aint32_t old_val__; \
- old_val__ = erts_smp_atomic32_xchg_nob(&schdlr_sspnd.dirty_io_changing, \
- (VAL)); \
- ASSERT(old_val__ == (OLD_VAL)); \
-} while (0)
-#endif
+#define ERTS_SCHDLR_SSPND_S_SHIFT 0
+#define ERTS_SCHDLR_SSPND_DCS_SHIFT (ERTS_SCHDLR_SSPND_S_SHIFT \
+ + ERTS_SCHDLR_SSPND_S_BITS)
+#define ERTS_SCHDLR_SSPND_DIS_SHIFT (ERTS_SCHDLR_SSPND_DCS_SHIFT \
+ + ERTS_SCHDLR_SSPND_DCS_BITS)
+#if (ERTS_SCHDLR_SSPND_S_BITS \
+ + ERTS_SCHDLR_SSPND_DCS_BITS \
+ + ERTS_SCHDLR_SSPND_DIS_BITS) > 32
+# error Wont fit in Uint32
#endif
-
-static struct {
- erts_smp_mtx_t mtx;
- erts_smp_cnd_t cnd;
- int online;
- int curr_online;
- int wait_curr_online;
-#ifdef ERTS_DIRTY_SCHEDULERS
- int dirty_cpu_online;
- int dirty_cpu_curr_online;
- int dirty_cpu_wait_curr_online;
- int dirty_io_online;
- int dirty_io_curr_online;
- int dirty_io_wait_curr_online;
+#if (ERTS_MAX_NO_OF_SCHEDULERS-1) > ERTS_SCHDLR_SSPND_S_MASK
+# error Max no schedulers wont fit in its bit-field
#endif
- erts_smp_atomic32_t changing;
- erts_smp_atomic32_t active;
-#ifdef ERTS_DIRTY_SCHEDULERS
- erts_smp_atomic32_t dirty_cpu_changing;
- erts_smp_atomic32_t dirty_cpu_active;
- erts_smp_atomic32_t dirty_io_changing;
- erts_smp_atomic32_t dirty_io_active;
+#if ERTS_MAX_NO_OF_DIRTY_CPU_SCHEDULERS > ERTS_SCHDLR_SSPND_DCS_MASK
+# error Max no dirty cpu schedulers wont fit in its bit-field
#endif
- struct {
- int ongoing;
- long wait_active;
-#ifdef ERTS_DIRTY_SCHEDULERS
- long dirty_cpu_wait_active;
- long dirty_io_wait_active;
+#if ERTS_MAX_NO_OF_DIRTY_IO_SCHEDULERS > ERTS_SCHDLR_SSPND_DIS_MASK
+# error Max no dirty io schedulers wont fit in its bit-field
#endif
- ErtsProcList *procs;
- } msb; /* Multi Scheduling Block */
-} schdlr_sspnd;
+
+#define ERTS_SCHDLR_SSPND_MAKE_NSCHEDS_VAL(S, DCS, DIS) \
+ ((((Uint32) (((S) & ERTS_SCHDLR_SSPND_S_MASK))-1) \
+ << ERTS_SCHDLR_SSPND_S_SHIFT) \
+ | ((((Uint32) ((DCS) & ERTS_SCHDLR_SSPND_DCS_MASK)) \
+ << ERTS_SCHDLR_SSPND_DCS_SHIFT)) \
+ | ((((Uint32) ((DIS) & ERTS_SCHDLR_SSPND_DIS_MASK)) \
+ << ERTS_SCHDLR_SSPND_DIS_SHIFT)))
+
+static void init_scheduler_suspend(void);
+
+static ERTS_INLINE Uint32
+schdlr_sspnd_get_nscheds(Uint32 *valp, ErtsSchedType type)
+{
+ Uint32 res = (Uint32) (*valp);
+ switch (type) {
+ case ERTS_SCHED_NORMAL:
+ res >>= ERTS_SCHDLR_SSPND_S_SHIFT;
+ res &= (Uint32) ERTS_SCHDLR_SSPND_S_MASK;
+ res++;
+ break;
+ case ERTS_SCHED_DIRTY_CPU:
+ res >>= ERTS_SCHDLR_SSPND_DCS_SHIFT;
+ res &= (Uint32) ERTS_SCHDLR_SSPND_DCS_MASK;
+ break;
+ case ERTS_SCHED_DIRTY_IO:
+ res >>= ERTS_SCHDLR_SSPND_DIS_SHIFT;
+ res &= (Uint32) ERTS_SCHDLR_SSPND_DIS_MASK;
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Invalid scheduler type");
+ return 0;
+ }
+
+ return res;
+}
+
+static ERTS_INLINE void
+schdlr_sspnd_dec_nscheds(Uint32 *valp, ErtsSchedType type)
+{
+ ASSERT(schdlr_sspnd_get_nscheds(valp, type) > 0);
+
+ switch (type) {
+ case ERTS_SCHED_NORMAL:
+ *valp -= ((Uint32) 1) << ERTS_SCHDLR_SSPND_S_SHIFT;
+ break;
+ case ERTS_SCHED_DIRTY_CPU:
+ *valp -= ((Uint32) 1) << ERTS_SCHDLR_SSPND_DCS_SHIFT;
+ break;
+ case ERTS_SCHED_DIRTY_IO:
+ *valp -= ((Uint32) 1) << ERTS_SCHDLR_SSPND_DIS_SHIFT;
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Invalid scheduler type");
+ }
+}
+
+static ERTS_INLINE void
+schdlr_sspnd_inc_nscheds(Uint32 *valp, ErtsSchedType type)
+{
+ switch (type) {
+ case ERTS_SCHED_NORMAL:
+ ASSERT(schdlr_sspnd_get_nscheds(valp, type)
+ < ERTS_MAX_NO_OF_SCHEDULERS-1);
+ *valp += ((Uint32) 1) << ERTS_SCHDLR_SSPND_S_SHIFT;
+ break;
+ case ERTS_SCHED_DIRTY_CPU:
+ ASSERT(schdlr_sspnd_get_nscheds(valp, type)
+ < ERTS_MAX_NO_OF_DIRTY_CPU_SCHEDULERS);
+ *valp += ((Uint32) 1) << ERTS_SCHDLR_SSPND_DCS_SHIFT;
+ break;
+ case ERTS_SCHED_DIRTY_IO:
+ ASSERT(schdlr_sspnd_get_nscheds(valp, type)
+ < ERTS_MAX_NO_OF_DIRTY_IO_SCHEDULERS);
+ *valp += ((Uint32) 1) << ERTS_SCHDLR_SSPND_DIS_SHIFT;
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Invalid scheduler type");
+ }
+}
+
+static ERTS_INLINE void
+schdlr_sspnd_set_nscheds(Uint32 *valp, ErtsSchedType type, Uint32 no)
+{
+ Uint32 val = *valp;
+
+ switch (type) {
+ case ERTS_SCHED_NORMAL:
+ ASSERT(no > 0);
+ val &= ~(((Uint32) ERTS_SCHDLR_SSPND_S_MASK)
+ << ERTS_SCHDLR_SSPND_S_SHIFT);
+ val |= (((no-1) & ((Uint32) ERTS_SCHDLR_SSPND_S_MASK))
+ << ERTS_SCHDLR_SSPND_S_SHIFT);
+ break;
+ case ERTS_SCHED_DIRTY_CPU:
+ val &= ~(((Uint32) ERTS_SCHDLR_SSPND_DCS_MASK)
+ << ERTS_SCHDLR_SSPND_DCS_SHIFT);
+ val |= ((no & ((Uint32) ERTS_SCHDLR_SSPND_DCS_MASK))
+ << ERTS_SCHDLR_SSPND_DCS_SHIFT);
+ break;
+ case ERTS_SCHED_DIRTY_IO:
+ val &= ~(((Uint32) ERTS_SCHDLR_SSPND_DIS_MASK)
+ << ERTS_SCHDLR_SSPND_DIS_SHIFT);
+ val |= ((no & ((Uint32) ERTS_SCHDLR_SSPND_DIS_MASK))
+ << ERTS_SCHDLR_SSPND_DIS_SHIFT);
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Invalid scheduler type");
+ }
+
+ *valp = val;
+}
static struct {
erts_smp_mtx_t update_mtx;
@@ -303,7 +387,7 @@ do { \
erts_sched_stat_t erts_sched_stat;
#ifdef USE_THREADS
-static erts_tsd_key_t sched_data_key;
+static erts_tsd_key_t ERTS_WRITE_UNLIKELY(sched_data_key);
#endif
static erts_smp_atomic32_t function_calls;
@@ -326,6 +410,10 @@ ErtsAlignedSchedulerData *erts_aligned_scheduler_data;
#ifdef ERTS_DIRTY_SCHEDULERS
ErtsAlignedSchedulerData *erts_aligned_dirty_cpu_scheduler_data;
ErtsAlignedSchedulerData *erts_aligned_dirty_io_scheduler_data;
+typedef union {
+ Process dsp;
+ char align[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(Process))];
+} ErtsAlignedDirtyShadowProcess;
#endif
typedef union {
@@ -343,11 +431,10 @@ static ErtsAlignedSchedulerSleepInfo *aligned_dirty_io_sched_sleep_info;
static Uint last_reductions;
static Uint last_exact_reductions;
-Uint erts_default_process_flags;
-Eterm erts_system_monitor;
-Eterm erts_system_monitor_long_gc;
-Uint erts_system_monitor_long_schedule;
-Eterm erts_system_monitor_large_heap;
+Eterm ERTS_WRITE_UNLIKELY(erts_system_monitor);
+Eterm ERTS_WRITE_UNLIKELY(erts_system_monitor_long_gc);
+Uint ERTS_WRITE_UNLIKELY(erts_system_monitor_long_schedule);
+Eterm ERTS_WRITE_UNLIKELY(erts_system_monitor_large_heap);
struct erts_system_monitor_flags_t erts_system_monitor_flags;
/* system performance monitor */
@@ -361,7 +448,12 @@ int erts_system_profile_ts_type = ERTS_TRACE_FLG_NOW_TIMESTAMP;
typedef enum {
ERTS_PSTT_GC, /* Garbage Collect */
- ERTS_PSTT_CPC /* Check Process Code */
+ ERTS_PSTT_CPC, /* Check Process Code */
+#ifdef ERTS_NEW_PURGE_STRATEGY
+ ERTS_PSTT_CLA, /* Copy Literal Area */
+#endif
+ ERTS_PSTT_COHMQ, /* Change off heap message queue */
+ ERTS_PSTT_FTMQ /* Flush trace msg queue */
} ErtsProcSysTaskType;
#define ERTS_MAX_PROC_SYS_TASK_ARGS 2
@@ -434,8 +526,10 @@ do { \
do { \
ErtsRunQueue *RQVAR; \
int ix__; \
+ int online__ = (int) schdlr_sspnd_get_nscheds(&schdlr_sspnd.online, \
+ ERTS_SCHED_NORMAL); \
ERTS_SMP_LC_ASSERT(erts_smp_lc_mtx_is_locked(&schdlr_sspnd.mtx)); \
- for (ix__ = 0; ix__ < schdlr_sspnd.online; ix__++) { \
+ for (ix__ = 0; ix__ < online__; ix__++) { \
RQVAR = ERTS_RUNQ_IX(ix__); \
erts_smp_runq_lock(RQVAR); \
{ DO; } \
@@ -464,8 +558,8 @@ do { \
*/
static void exec_misc_ops(ErtsRunQueue *);
-static void print_function_from_pc(int to, void *to_arg, BeamInstr* x);
-static int stack_element_dump(int to, void *to_arg, Eterm* sp, int yreg);
+static void print_function_from_pc(fmtfn_t to, void *to_arg, BeamInstr* x);
+static int stack_element_dump(fmtfn_t to, void *to_arg, Eterm* sp, int yreg);
static void aux_work_timeout(void *unused);
static void aux_work_timeout_early_init(int no_schedulers);
@@ -503,13 +597,11 @@ dbg_chk_aux_work_val(erts_aint32_t value)
valid |= ERTS_SSI_AUX_WORK_CNCLD_TMRS;
valid |= ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR;
valid |= ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP;
+ valid |= ERTS_SSI_AUX_WORK_PENDING_EXITERS;
#endif
#if HAVE_ERTS_MSEG
valid |= ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK;
#endif
-#ifdef ERTS_SMP_SCHEDULERS_NEED_TO_CHECK_CHILDREN
- valid |= ERTS_SSI_AUX_WORK_CHECK_CHILDREN;
-#endif
#ifdef ERTS_SSI_AUX_WORK_REAP_PORTS
valid |= ERTS_SSI_AUX_WORK_REAP_PORTS;
#endif
@@ -528,7 +620,7 @@ dbg_chk_aux_work_val(erts_aint32_t value)
#endif
#ifdef ERTS_SMP
-static void handle_pending_exiters(ErtsProcList *);
+static void do_handle_pending_exiters(ErtsProcList *);
static void wake_scheduler(ErtsRunQueue *rq);
#endif
@@ -596,8 +688,8 @@ erts_pre_init_process(void)
= "MISC_THR_PRGR";
erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_MISC_IX]
= "MISC";
- erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_CHECK_CHILDREN_IX]
- = "CHECK_CHILDREN";
+ erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_PENDING_EXITERS_IX]
+ = "PENDING_EXITERS";
erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_SET_TMO_IX]
= "SET_TMO";
erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK_IX]
@@ -608,45 +700,36 @@ erts_pre_init_process(void)
= "DEBUG_WAIT_COMPLETED";
#ifdef ERTS_ENABLE_LOCK_CHECK
- {
- int ix;
-
- erts_psd_required_locks[ERTS_PSD_ERROR_HANDLER].get_locks
- = ERTS_PSD_ERROR_HANDLER_BUF_GET_LOCKS;
- erts_psd_required_locks[ERTS_PSD_ERROR_HANDLER].set_locks
- = ERTS_PSD_ERROR_HANDLER_BUF_SET_LOCKS;
- erts_psd_required_locks[ERTS_PSD_SAVED_CALLS_BUF].get_locks
- = ERTS_PSD_SAVED_CALLS_BUF_GET_LOCKS;
- erts_psd_required_locks[ERTS_PSD_SAVED_CALLS_BUF].set_locks
- = ERTS_PSD_SAVED_CALLS_BUF_SET_LOCKS;
+ erts_psd_required_locks[ERTS_PSD_ERROR_HANDLER].get_locks
+ = ERTS_PSD_ERROR_HANDLER_BUF_GET_LOCKS;
+ erts_psd_required_locks[ERTS_PSD_ERROR_HANDLER].set_locks
+ = ERTS_PSD_ERROR_HANDLER_BUF_SET_LOCKS;
- erts_psd_required_locks[ERTS_PSD_SCHED_ID].get_locks
- = ERTS_PSD_SCHED_ID_GET_LOCKS;
- erts_psd_required_locks[ERTS_PSD_SCHED_ID].set_locks
- = ERTS_PSD_SCHED_ID_SET_LOCKS;
+ erts_psd_required_locks[ERTS_PSD_SAVED_CALLS_BUF].get_locks
+ = ERTS_PSD_SAVED_CALLS_BUF_GET_LOCKS;
+ erts_psd_required_locks[ERTS_PSD_SAVED_CALLS_BUF].set_locks
+ = ERTS_PSD_SAVED_CALLS_BUF_SET_LOCKS;
- erts_psd_required_locks[ERTS_PSD_CALL_TIME_BP].get_locks
- = ERTS_PSD_CALL_TIME_BP_GET_LOCKS;
- erts_psd_required_locks[ERTS_PSD_CALL_TIME_BP].set_locks
- = ERTS_PSD_CALL_TIME_BP_SET_LOCKS;
+ erts_psd_required_locks[ERTS_PSD_SCHED_ID].get_locks
+ = ERTS_PSD_SCHED_ID_GET_LOCKS;
+ erts_psd_required_locks[ERTS_PSD_SCHED_ID].set_locks
+ = ERTS_PSD_SCHED_ID_SET_LOCKS;
- erts_psd_required_locks[ERTS_PSD_DELAYED_GC_TASK_QS].get_locks
- = ERTS_PSD_DELAYED_GC_TASK_QS_GET_LOCKS;
- erts_psd_required_locks[ERTS_PSD_DELAYED_GC_TASK_QS].set_locks
- = ERTS_PSD_DELAYED_GC_TASK_QS_SET_LOCKS;
+ erts_psd_required_locks[ERTS_PSD_CALL_TIME_BP].get_locks
+ = ERTS_PSD_CALL_TIME_BP_GET_LOCKS;
+ erts_psd_required_locks[ERTS_PSD_CALL_TIME_BP].set_locks
+ = ERTS_PSD_CALL_TIME_BP_SET_LOCKS;
- erts_psd_required_locks[ERTS_PSD_NIF_TRAP_EXPORT].get_locks
- = ERTS_PSD_NIF_TRAP_EXPORT_GET_LOCKS;
- erts_psd_required_locks[ERTS_PSD_NIF_TRAP_EXPORT].set_locks
- = ERTS_PSD_NIF_TRAP_EXPORT_SET_LOCKS;
+ erts_psd_required_locks[ERTS_PSD_DELAYED_GC_TASK_QS].get_locks
+ = ERTS_PSD_DELAYED_GC_TASK_QS_GET_LOCKS;
+ erts_psd_required_locks[ERTS_PSD_DELAYED_GC_TASK_QS].set_locks
+ = ERTS_PSD_DELAYED_GC_TASK_QS_SET_LOCKS;
- /* Check that we have locks for all entries */
- for (ix = 0; ix < ERTS_PSD_SIZE; ix++) {
- ERTS_SMP_LC_ASSERT(erts_psd_required_locks[ix].get_locks);
- ERTS_SMP_LC_ASSERT(erts_psd_required_locks[ix].set_locks);
- }
- }
+ erts_psd_required_locks[ERTS_PSD_NIF_TRAP_EXPORT].get_locks
+ = ERTS_PSD_NIF_TRAP_EXPORT_GET_LOCKS;
+ erts_psd_required_locks[ERTS_PSD_NIF_TRAP_EXPORT].set_locks
+ = ERTS_PSD_NIF_TRAP_EXPORT_SET_LOCKS;
#endif
}
@@ -685,7 +768,6 @@ erts_init_process(int ncpu, int proc_tab_size, int legacy_proc_tab)
last_reductions = 0;
last_exact_reductions = 0;
- erts_default_process_flags = 0;
}
void
@@ -955,6 +1037,12 @@ sched_wall_time_change(ErtsSchedulerData *esdp, int working)
}
}
}
+ if (!working) {
+ ERTS_MSACC_SET_STATE_M_X(ERTS_MSACC_STATE_BUSY_WAIT);
+ } else {
+ ERTS_MSACC_SET_STATE_M_X(ERTS_MSACC_STATE_OTHER);
+ }
+
}
typedef struct {
@@ -976,7 +1064,6 @@ typedef struct {
} ErtsSystemCheckReq;
-#if !HALFWORD_HEAP
ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(swtreq,
ErtsSchedWallTimeReq,
5,
@@ -986,33 +1073,6 @@ ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(screq,
ErtsSystemCheckReq,
5,
ERTS_ALC_T_SYS_CHECK_REQ)
-#else
-static ERTS_INLINE ErtsSchedWallTimeReq *
-swtreq_alloc(void)
-{
- return erts_alloc(ERTS_ALC_T_SCHED_WTIME_REQ,
- sizeof(ErtsSchedWallTimeReq));
-}
-
-static ERTS_INLINE void
-swtreq_free(ErtsSchedWallTimeReq *ptr)
-{
- erts_free(ERTS_ALC_T_SCHED_WTIME_REQ, ptr);
-}
-
-static ERTS_INLINE ErtsSystemCheckReq *
-screq_alloc(void)
-{
- return erts_alloc(ERTS_ALC_T_SYS_CHECK_REQ,
- sizeof(ErtsSystemCheckReq));
-}
-
-static ERTS_INLINE void
-screq_free(ErtsSystemCheckReq *ptr)
-{
- erts_free(ERTS_ALC_T_SYS_CHECK_REQ, ptr);
-}
-#endif
static void
reply_sched_wall_time(void *vswtrp)
@@ -1029,7 +1089,7 @@ reply_sched_wall_time(void *vswtrp)
Eterm **hpp;
Uint sz, *szp;
ErlOffHeap *ohp = NULL;
- ErlHeapFragment *bp = NULL;
+ ErtsMessage *mp = NULL;
ASSERT(esdp);
#ifdef ERTS_DIRTY_SCHEDULERS
@@ -1085,12 +1145,12 @@ reply_sched_wall_time(void *vswtrp)
if (hpp)
break;
- hp = erts_alloc_message_heap(sz, &bp, &ohp, rp, &rp_locks);
+ mp = erts_alloc_message_heap(rp, &rp_locks, sz, &hp, &ohp);
szp = NULL;
hpp = &hp;
}
- erts_queue_message(rp, &rp_locks, bp, msg, NIL);
+ erts_queue_message(rp, rp_locks, mp, msg, am_system);
if (swtrp->req_sched == esdp->no)
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
@@ -1107,7 +1167,7 @@ reply_sched_wall_time(void *vswtrp)
Eterm
erts_sched_wall_time_request(Process *c_p, int set, int enable)
{
- ErtsSchedulerData *esdp = ERTS_PROC_GET_SCHDATA(c_p);
+ ErtsSchedulerData *esdp = erts_proc_sched_data(c_p);
Eterm ref;
ErtsSchedWallTimeReq *swtrp;
Eterm *hp;
@@ -1157,7 +1217,7 @@ reply_system_check(void *vscrp)
Eterm **hpp;
Uint sz;
ErlOffHeap *ohp = NULL;
- ErlHeapFragment *bp = NULL;
+ ErtsMessage *mp = NULL;
ASSERT(esdp);
#ifdef ERTS_DIRTY_SCHEDULERS
@@ -1165,11 +1225,11 @@ reply_system_check(void *vscrp)
#endif
sz = REF_THING_SIZE;
- hp = erts_alloc_message_heap(sz, &bp, &ohp, rp, &rp_locks);
+ mp = erts_alloc_message_heap(rp, &rp_locks, sz, &hp, &ohp);
hpp = &hp;
msg = STORE_NC(hpp, ohp, scrp->ref);
- erts_queue_message(rp, &rp_locks, bp, msg, NIL);
+ erts_queue_message(rp, rp_locks, mp, msg, am_system);
if (scrp->req_sched == esdp->no)
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
@@ -1185,7 +1245,7 @@ reply_system_check(void *vscrp)
Eterm erts_system_check_request(Process *c_p) {
- ErtsSchedulerData *esdp = ERTS_PROC_GET_SCHDATA(c_p);
+ ErtsSchedulerData *esdp = erts_proc_sched_data(c_p);
Eterm ref;
ErtsSystemCheckReq *scrp;
Eterm *hp;
@@ -1224,6 +1284,15 @@ proclist_create(Process *p)
return plp;
}
+static ERTS_INLINE ErtsProcList *
+proclist_copy(ErtsProcList *plp0)
+{
+ ErtsProcList *plp1 = proclist_alloc();
+ plp1->pid = plp0->pid;
+ plp1->started_interval = plp0->started_interval;
+ return plp1;
+}
+
static ERTS_INLINE void
proclist_destroy(ErtsProcList *plp)
{
@@ -1231,6 +1300,12 @@ proclist_destroy(ErtsProcList *plp)
}
ErtsProcList *
+erts_proclist_copy(ErtsProcList *plp)
+{
+ return proclist_copy(plp);
+}
+
+ErtsProcList *
erts_proclist_create(Process *p)
{
return proclist_create(p);
@@ -1243,46 +1318,25 @@ erts_proclist_destroy(ErtsProcList *plp)
}
void *
-erts_psd_set_init(Process *p, ErtsProcLocks plocks, int ix, void *data)
+erts_psd_set_init(Process *p, int ix, void *data)
{
void *old;
- ErtsProcLocks xplocks;
- int refc = 0;
- ErtsPSD *psd = erts_alloc(ERTS_ALC_T_PSD, sizeof(ErtsPSD));
+ ErtsPSD *psd, *new_psd;
int i;
- for (i = 0; i < ERTS_PSD_SIZE; i++)
- psd->data[i] = NULL;
- ERTS_SMP_LC_ASSERT(plocks);
- ERTS_SMP_LC_ASSERT(plocks == erts_proc_lc_my_proc_locks(p));
+ new_psd = erts_alloc(ERTS_ALC_T_PSD, sizeof(ErtsPSD));
+ for (i = 0; i < ERTS_PSD_SIZE; i++)
+ new_psd->data[i] = NULL;
- xplocks = ERTS_PROC_LOCKS_ALL;
- xplocks &= ~plocks;
- if (xplocks && erts_smp_proc_trylock(p, xplocks) == EBUSY) {
- if (xplocks & ERTS_PROC_LOCK_MAIN) {
- erts_proc_inc_refc(p);
- erts_smp_proc_unlock(p, plocks);
- erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL);
- refc = 1;
- }
- else {
- if (plocks & ERTS_PROC_LOCKS_ALL_MINOR)
- erts_smp_proc_unlock(p, plocks & ERTS_PROC_LOCKS_ALL_MINOR);
- erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR);
- }
- }
- if (!p->psd)
- p->psd = psd;
- if (xplocks)
- erts_smp_proc_unlock(p, xplocks);
- if (refc)
- erts_proc_dec_refc(p);
- ASSERT(p->psd);
- if (p->psd != psd)
- erts_free(ERTS_ALC_T_PSD, psd);
- old = p->psd->data[ix];
- p->psd->data[ix] = data;
- ERTS_SMP_LC_ASSERT(plocks == erts_proc_lc_my_proc_locks(p));
+ psd = (ErtsPSD *) erts_smp_atomic_cmpxchg_mb(&p->psd,
+ (erts_aint_t) new_psd,
+ (erts_aint_t) NULL);
+ if (psd)
+ erts_free(ERTS_ALC_T_PSD, new_psd);
+ else
+ psd = new_psd;
+ old = psd->data[ix];
+ psd->data[ix] = data;
return old;
}
@@ -1821,15 +1875,17 @@ handle_delayed_dealloc(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waitin
int need_thr_progress = 0;
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,
&need_thr_progress,
&wakeup,
&more_work);
+ ERTS_MSACC_POP_STATE_M_X();
if (more_work) {
if (set_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_DD)
& ERTS_SSI_AUX_WORK_DD_THR_PRGR) {
@@ -2004,6 +2060,7 @@ handle_thr_prgr_later_op(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int wait
#endif
for (lops = 0; lops < ERTS_MAX_THR_PRGR_LATER_OPS; lops++) {
ErtsThrPrgrLaterOp *lop = awdp->later_op.first;
+
if (!erts_thr_progress_has_reached_this(current, lop->later))
return aux_work & ~ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP;
awdp->later_op.first = lop->next;
@@ -2138,8 +2195,7 @@ setup_thr_debug_wait_completed(void *vproc)
if (debug_wait_completed_flags & ERTS_DEBUG_WAIT_COMPLETED_DEALLOCATIONS) {
erts_alloc_fix_alloc_shrink(awdp->sched_id, 0);
wait_flags |= (ERTS_SSI_AUX_WORK_DD
- | ERTS_SSI_AUX_WORK_DD_THR_PRGR
- | ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP);
+ | ERTS_SSI_AUX_WORK_DD_THR_PRGR);
#ifdef ERTS_SMP
aux_work_flags |= ERTS_SSI_AUX_WORK_DD;
#endif
@@ -2147,8 +2203,7 @@ setup_thr_debug_wait_completed(void *vproc)
if (debug_wait_completed_flags & ERTS_DEBUG_WAIT_COMPLETED_TIMER_CANCELLATIONS) {
wait_flags |= (ERTS_SSI_AUX_WORK_CNCLD_TMRS
- | ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR
- | ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP);
+ | ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR);
#ifdef ERTS_SMP
if (awdp->esdp && !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp))
aux_work_flags |= ERTS_SSI_AUX_WORK_CNCLD_TMRS;
@@ -2162,26 +2217,42 @@ setup_thr_debug_wait_completed(void *vproc)
awdp->debug.wait_completed.arg = vproc;
}
-static void
-prep_setup_thr_debug_wait_completed(void *vproc)
+struct debug_lop {
+ ErtsThrPrgrLaterOp lop;
+ Process *proc;
+};
+
+static void later_thr_debug_wait_completed(void *vlop)
{
+ struct debug_lop *lop = vlop;
erts_aint32_t count = (erts_aint32_t) erts_no_schedulers;
#ifdef ERTS_SMP
count += 1; /* aux thread */
#endif
if (erts_atomic32_dec_read_mb(&debug_wait_completed_count) == count) {
- /* scheduler threads */
- erts_schedule_multi_misc_aux_work(0,
- erts_no_schedulers,
- setup_thr_debug_wait_completed,
- vproc);
+ /* scheduler threads */
+ erts_schedule_multi_misc_aux_work(0,
+ erts_no_schedulers,
+ setup_thr_debug_wait_completed,
+ lop->proc);
#ifdef ERTS_SMP
- /* aux_thread */
- erts_schedule_misc_aux_work(0,
- setup_thr_debug_wait_completed,
- vproc);
+ /* aux_thread */
+ erts_schedule_misc_aux_work(0,
+ setup_thr_debug_wait_completed,
+ lop->proc);
#endif
}
+ erts_free(ERTS_ALC_T_DEBUG, lop);
+}
+
+
+static void
+init_thr_debug_wait_completed(void *vproc)
+{
+ struct debug_lop* lop = erts_alloc(ERTS_ALC_T_DEBUG,
+ sizeof(struct debug_lop));
+ lop->proc = vproc;
+ erts_schedule_thr_prgr_later_op(later_thr_debug_wait_completed, lop, &lop->lop);
}
@@ -2191,7 +2262,7 @@ erts_debug_wait_completed(Process *c_p, int flags)
/* Only one process at a time can do this */
erts_aint32_t count = (erts_aint32_t) (2*erts_no_schedulers);
#ifdef ERTS_SMP
- count += 2; /* aux thread */
+ count += 1; /* aux thread */
#endif
if (0 == erts_atomic32_cmpxchg_mb(&debug_wait_completed_count,
count,
@@ -2199,51 +2270,18 @@ erts_debug_wait_completed(Process *c_p, int flags)
debug_wait_completed_flags = flags;
erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL);
erts_proc_inc_refc(c_p);
- /* scheduler threads */
+
+ /* First flush later-ops on all scheduler threads */
erts_schedule_multi_misc_aux_work(0,
erts_no_schedulers,
- prep_setup_thr_debug_wait_completed,
+ init_thr_debug_wait_completed,
(void *) c_p);
-#ifdef ERTS_SMP
- /* aux_thread */
- erts_schedule_misc_aux_work(0,
- prep_setup_thr_debug_wait_completed,
- (void *) c_p);
-#endif
return 1;
}
return 0;
}
-#ifdef ERTS_SMP_SCHEDULERS_NEED_TO_CHECK_CHILDREN
-void
-erts_smp_notify_check_children_needed(void)
-{
- int i;
- for (i = 0; i < erts_no_schedulers; i++)
- set_aux_work_flags_wakeup_nob(ERTS_SCHED_SLEEP_INFO_IX(i),
- ERTS_SSI_AUX_WORK_CHECK_CHILDREN);
-#ifdef ERTS_DIRTY_SCHEDULERS
- for (i = 0; i < erts_no_dirty_cpu_schedulers; i++)
- set_aux_work_flags_wakeup_nob(ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(i),
- ERTS_SSI_AUX_WORK_CHECK_CHILDREN);
- for (i = 0; i < erts_no_dirty_io_schedulers; i++)
- set_aux_work_flags_wakeup_nob(ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(i),
- ERTS_SSI_AUX_WORK_CHECK_CHILDREN);
-#endif
-}
-
-static ERTS_INLINE erts_aint32_t
-handle_check_children(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting)
-{
- unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_CHECK_CHILDREN);
- erts_check_children();
- return aux_work & ~ERTS_SSI_AUX_WORK_CHECK_CHILDREN;
-}
-
-#endif
-
static void
notify_reap_ports_relb(void)
{
@@ -2286,7 +2324,7 @@ handle_reap_ports(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting)
ERTS_PORT_SFLG_HALT);
erts_smp_atomic32_inc_nob(&erts_halt_progress);
if (!(state & (ERTS_PORT_SFLG_EXITING|ERTS_PORT_SFLG_CLOSING)))
- erts_deliver_port_exit(prt, prt->common.id, am_killed, 0);
+ erts_deliver_port_exit(prt, prt->common.id, am_killed, 0, 1);
}
erts_port_release(prt);
@@ -2310,6 +2348,30 @@ handle_mseg_cache_check(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiti
#endif
+#ifdef ERTS_SMP
+
+static ERTS_INLINE erts_aint32_t
+handle_pending_exiters(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting)
+{
+ ErtsProcList *pnd_xtrs;
+ ErtsRunQueue *rq;
+
+ rq = awdp->esdp->run_queue;
+ unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_PENDING_EXITERS);
+
+ erts_smp_runq_lock(rq);
+ pnd_xtrs = rq->procs.pending_exiters;
+ rq->procs.pending_exiters = NULL;
+ erts_smp_runq_unlock(rq);
+
+ if (erts_proclist_fetch(&pnd_xtrs, NULL))
+ do_handle_pending_exiters(pnd_xtrs);
+
+ return aux_work & ~ERTS_SSI_AUX_WORK_PENDING_EXITERS;
+}
+
+#endif
+
static ERTS_INLINE erts_aint32_t
handle_setup_aux_work_timer(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting)
{
@@ -2329,12 +2391,15 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work, int waiting)
ERTS_DBG_CHK_AUX_WORK_VAL(aux_work); \
if (!(aux_work & ~ignore)) { \
ERTS_DBG_CHK_AUX_WORK_VAL(aux_work); \
+ ERTS_MSACC_UPDATE_CACHE(); \
+ ERTS_MSACC_POP_STATE_M(); \
return aux_work; \
} \
}
erts_aint32_t aux_work = orig_aux_work;
erts_aint32_t ignore = 0;
+ ERTS_MSACC_PUSH_AND_SET_STATE_M(ERTS_MSACC_STATE_AUX);
ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp));
#ifdef ERTS_SMP
@@ -2398,9 +2463,9 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work, int waiting)
HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_MISC,
handle_misc_aux_work);
-#ifdef ERTS_SMP_SCHEDULERS_NEED_TO_CHECK_CHILDREN
- HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_CHECK_CHILDREN,
- handle_check_children);
+#ifdef ERTS_SMP
+ HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_PENDING_EXITERS,
+ handle_pending_exiters);
#endif
HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_SET_TMO,
@@ -2429,6 +2494,8 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work, int waiting)
haw_thr_prgr_current_check_progress(awdp);
#endif
+ ERTS_MSACC_UPDATE_CACHE();
+ ERTS_MSACC_POP_STATE_M();
return aux_work;
#undef HANDLE_AUX_WORK
@@ -2629,19 +2696,10 @@ try_set_sys_scheduling(void)
#endif
static ERTS_INLINE int
-prepare_for_sys_schedule(ErtsSchedulerData *esdp, int non_blocking)
+prepare_for_sys_schedule(int non_blocking)
{
if (non_blocking && erts_eager_check_io) {
#ifdef ERTS_SMP
-#ifdef ERTS_SCHED_ONLY_POLL_SCHED_1
- if (esdp->no != 1) {
- /* If we are not scheduler 1 and ERTS_SCHED_ONLY_POLL_SCHED_1 is used
- then we make sure to wake scheduler 1 */
- ErtsRunQueue *rq = ERTS_RUNQ_IX(0);
- wake_scheduler(rq);
- return 0;
- }
-#endif
return try_set_sys_scheduling();
#else
return 1;
@@ -2651,16 +2709,6 @@ prepare_for_sys_schedule(ErtsSchedulerData *esdp, int non_blocking)
#ifdef ERTS_SMP
while (!erts_port_task_have_outstanding_io_tasks()
&& try_set_sys_scheduling()) {
-#ifdef ERTS_SCHED_ONLY_POLL_SCHED_1
- if (esdp->no != 1) {
- /* If we are not scheduler 1 and ERTS_SCHED_ONLY_POLL_SCHED_1 is used
- then we make sure to wake scheduler 1 */
- ErtsRunQueue *rq = ERTS_RUNQ_IX(0);
- clear_sys_scheduling();
- wake_scheduler(rq);
- return 0;
- }
-#endif
if (!erts_port_task_have_outstanding_io_tasks())
return 1;
clear_sys_scheduling();
@@ -2712,13 +2760,6 @@ sched_active(Uint no, ErtsRunQueue *rq)
profile_scheduler(make_small(no), am_active);
}
-static int ERTS_INLINE
-ongoing_multi_scheduling_block(void)
-{
- ERTS_SMP_LC_ASSERT(erts_lc_mtx_is_locked(&schdlr_sspnd.mtx));
- return schdlr_sspnd.msb.ongoing;
-}
-
static ERTS_INLINE void
empty_runq_aux(ErtsRunQueue *rq, Uint32 old_flags)
{
@@ -2955,6 +2996,8 @@ aux_thread(void *unused)
ssi->event = erts_tse_fetch();
+ erts_msacc_init_thread("aux", 1, 1);
+
callbacks.arg = (void *) ssi;
callbacks.wakeup = thr_prgr_wakeup;
callbacks.prepare_wait = thr_prgr_prep_wait;
@@ -2965,6 +3008,7 @@ aux_thread(void *unused)
init_aux_work_data(awdp, NULL, NULL);
awdp->ssi = ssi;
+
sched_prep_spin_wait(ssi);
while (1) {
@@ -2984,8 +3028,6 @@ aux_thread(void *unused)
erts_thr_progress_active(NULL, thr_prgr_active = 0);
erts_thr_progress_prepare_wait(NULL);
- ERTS_SCHED_FAIR_YIELD();
-
flgs = sched_spin_wait(ssi, 0);
if (flgs & ERTS_SSI_FLG_SLEEPING) {
@@ -3020,6 +3062,9 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq)
#ifdef ERTS_SMP
int thr_prgr_active = 1;
erts_aint32_t flgs;
+#endif
+ ERTS_MSACC_PUSH_STATE_M();
+#ifdef ERTS_SMP
ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
@@ -3053,7 +3098,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq)
* be waiting in erl_sys_schedule()
*/
- if (ERTS_SCHEDULER_IS_DIRTY(esdp) || !prepare_for_sys_schedule(esdp, 0)) {
+ if (ERTS_SCHEDULER_IS_DIRTY(esdp) || !prepare_for_sys_schedule(0)) {
sched_waiting(esdp->no, rq);
@@ -3067,7 +3112,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq)
sched_wall_time_change(esdp, thr_prgr_active);
while (1) {
- ErtsMonotonicTime current_time;
+ ErtsMonotonicTime current_time = 0;
aux_work = erts_atomic32_read_acqb(&ssi->aux_work);
if (aux_work && !ERTS_SCHEDULER_IS_DIRTY(esdp)) {
@@ -3076,6 +3121,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq)
sched_wall_time_change(esdp, 1);
}
aux_work = handle_aux_work(&esdp->aux_work_data, aux_work, 1);
+ ERTS_MSACC_UPDATE_CACHE();
if (aux_work && erts_thr_progress_update(esdp))
erts_thr_progress_leader_update(esdp);
}
@@ -3100,8 +3146,10 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq)
timeout_time = erts_check_next_timeout_time(esdp);
current_time = erts_get_monotonic_time(esdp);
do_timeout = (current_time >= timeout_time);
- } else
+ } else {
+ current_time = 0;
timeout_time = ERTS_MONOTONIC_TIME_MAX;
+ }
if (do_timeout) {
if (!thr_prgr_active) {
erts_thr_progress_active(esdp, thr_prgr_active = 1);
@@ -3117,8 +3165,6 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq)
erts_thr_progress_prepare_wait(esdp);
}
- ERTS_SCHED_FAIR_YIELD();
-
flgs = sched_spin_wait(ssi, spincount);
if (flgs & ERTS_SSI_FLG_SLEEPING) {
ASSERT(flgs & ERTS_SSI_FLG_WAITING);
@@ -3139,7 +3185,9 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq)
- 1) + 1;
} else
timeout = -1;
+ ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_SLEEP);
res = erts_tse_twait(ssi->event, timeout);
+ ERTS_MSACC_POP_STATE_M();
current_time = ERTS_SCHEDULER_IS_DIRTY(esdp) ? 0 :
erts_get_monotonic_time(esdp);
} while (res == EINTR);
@@ -3189,13 +3237,8 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq)
#ifdef ERTS_DIRTY_SCHEDULERS
ASSERT(!ERTS_SCHEDULER_IS_DIRTY(esdp));
#endif
-
-#ifdef ERTS_SCHED_ONLY_POLL_SCHED_1
- ASSERT(esdp->no == 1);
-#endif
sched_waiting_sys(esdp->no, rq);
-
erts_smp_runq_unlock(rq);
ASSERT(working);
@@ -3213,9 +3256,14 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq)
if (working)
sched_wall_time_change(esdp, working = 0);
+ ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_CHECK_IO);
+
ASSERT(!erts_port_task_have_outstanding_io_tasks());
+ LTTNG2(scheduler_poll, esdp->no, 1);
erl_sys_schedule(1); /* Might give us something to do */
+ ERTS_MSACC_POP_STATE_M();
+
if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) {
current_time = erts_get_monotonic_time(esdp);
if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref))
@@ -3236,6 +3284,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq)
erts_thr_progress_active(esdp, thr_prgr_active = 1);
#endif
aux_work = handle_aux_work(&esdp->aux_work_data, aux_work, 1);
+ ERTS_MSACC_UPDATE_CACHE();
#ifdef ERTS_SMP
if (aux_work && erts_thr_progress_update(esdp))
erts_thr_progress_leader_update(esdp);
@@ -3262,7 +3311,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq)
* Got to check that we still got I/O tasks; otherwise
* we have to continue checking for I/O...
*/
- if (!prepare_for_sys_schedule(esdp, 0)) {
+ if (!prepare_for_sys_schedule(0)) {
spincount *= ERTS_SCHED_TSE_SLEEP_SPINCOUNT_FACT;
goto tse_wait;
}
@@ -3284,7 +3333,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq)
* Got to check that we still got I/O tasks; otherwise
* we have to wait in erl_sys_schedule() after all...
*/
- if (!prepare_for_sys_schedule(esdp, 0)) {
+ if (!prepare_for_sys_schedule(0)) {
/*
* Not allowed to wait in erl_sys_schedule;
* do tse wait instead...
@@ -3333,8 +3382,13 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq)
ASSERT(!erts_port_task_have_outstanding_io_tasks());
+ ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_CHECK_IO);
+ LTTNG2(scheduler_poll, esdp->no, 0);
+
erl_sys_schedule(0);
+ ERTS_MSACC_POP_STATE_M();
+
if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) {
ErtsMonotonicTime current_time = erts_get_monotonic_time(esdp);
if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref))
@@ -3404,7 +3458,8 @@ wake_scheduler(ErtsRunQueue *rq)
* so all code *should* handle this without having
* the lock on the run queue.
*/
- ERTS_SMP_LC_ASSERT(!erts_smp_lc_runq_is_locked(rq));
+ ERTS_SMP_LC_ASSERT(!erts_smp_lc_runq_is_locked(rq)
+ || ERTS_RUNQ_IX_IS_DIRTY(rq->ix));
ssi = rq->scheduler->ssi;
@@ -3459,7 +3514,7 @@ wake_dirty_schedulers(ErtsRunQueue *rq, int one)
#endif
#define ERTS_NO_USED_RUNQS_SHIFT 16
-#define ERTS_NO_RUNQS_MASK 0xffff
+#define ERTS_NO_RUNQS_MASK 0xffffU
#if ERTS_MAX_NO_OF_SCHEDULERS > ERTS_NO_RUNQS_MASK
# error "Too large amount of schedulers allowed"
@@ -3729,6 +3784,13 @@ check_requeue_process(ErtsRunQueue *rq, int prio_q)
return 0;
}
+static ERTS_INLINE void
+free_proxy_proc(Process *proxy)
+{
+ ASSERT(erts_smp_atomic32_read_nob(&proxy->state) & ERTS_PSFLG_PROXY);
+ erts_free(ERTS_ALC_T_PROC, proxy);
+}
+
#ifdef ERTS_SMP
static ErtsRunQueue *
@@ -3900,24 +3962,33 @@ static ERTS_INLINE void
resume_run_queue(ErtsRunQueue *rq)
{
int pix;
+ Uint32 oflgs;
erts_smp_runq_lock(rq);
- (void) ERTS_RUNQ_FLGS_READ_BSET(rq,
- (ERTS_RUNQ_FLG_OUT_OF_WORK
- | ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK
- | ERTS_RUNQ_FLG_SUSPENDED),
- (ERTS_RUNQ_FLG_OUT_OF_WORK
- | ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK));
+ oflgs = ERTS_RUNQ_FLGS_READ_BSET(rq,
+ (ERTS_RUNQ_FLG_OUT_OF_WORK
+ | ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK
+ | ERTS_RUNQ_FLG_SUSPENDED),
+ (ERTS_RUNQ_FLG_OUT_OF_WORK
+ | ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK));
+
+ if (oflgs & ERTS_RUNQ_FLG_SUSPENDED) {
+ erts_aint32_t len;
+
+ rq->check_balance_reds = ERTS_RUNQ_CALL_CHECK_BALANCE_REDS;
+ for (pix = 0; pix < ERTS_NO_PROC_PRIO_LEVELS; pix++) {
+ len = erts_smp_atomic32_read_dirty(&rq->procs.prio_info[pix].len);
+ rq->procs.prio_info[pix].max_len = len;
+ rq->procs.prio_info[pix].reds = 0;
+ }
+ len = erts_smp_atomic32_read_dirty(&rq->ports.info.len);
+ rq->ports.info.max_len = len;
+ rq->ports.info.reds = 0;
+ len = erts_smp_atomic32_read_dirty(&rq->len);
+ rq->max_len = len;
- rq->check_balance_reds = ERTS_RUNQ_CALL_CHECK_BALANCE_REDS;
- for (pix = 0; pix < ERTS_NO_PROC_PRIO_LEVELS; pix++) {
- rq->procs.prio_info[pix].max_len = 0;
- rq->procs.prio_info[pix].reds = 0;
}
- rq->ports.info.max_len = 0;
- rq->ports.info.reds = 0;
- rq->max_len = 0;
erts_smp_runq_unlock(rq);
@@ -3948,6 +4019,33 @@ schedule_bound_processes(ErtsRunQueue *rq,
}
}
+#ifdef ERTS_DIRTY_SCHEDULERS
+
+static ERTS_INLINE void
+clear_proc_dirty_queue_bit(Process *p, ErtsRunQueue *rq, int prio_bit)
+{
+#ifdef DEBUG
+ erts_aint32_t old;
+#endif
+ erts_aint32_t qb = prio_bit;
+ if (rq == ERTS_DIRTY_CPU_RUNQ)
+ qb <<= ERTS_PDSFLGS_IN_CPU_PRQ_MASK_OFFSET;
+ else {
+ ASSERT(rq == ERTS_DIRTY_IO_RUNQ);
+ qb <<= ERTS_PDSFLGS_IN_IO_PRQ_MASK_OFFSET;
+ }
+#ifdef DEBUG
+ old = (int)
+#else
+ (void)
+#endif
+ erts_smp_atomic32_read_band_mb(&p->dirty_state, ~qb);
+ ASSERT(old & qb);
+}
+
+#endif /* ERTS_DIRTY_SCHEDULERS */
+
+
static void
evacuate_run_queue(ErtsRunQueue *rq,
ErtsStuckBoundProcesses *sbpp)
@@ -4043,9 +4141,6 @@ evacuate_run_queue(ErtsRunQueue *rq,
erts_aint32_t state;
Process *proc;
int notify = 0;
-#ifdef ERTS_DIRTY_SCHEDULERS
- int requeue;
-#endif
to_rq = NULL;
#ifdef ERTS_DIRTY_SCHEDULERS
@@ -4060,10 +4155,64 @@ evacuate_run_queue(ErtsRunQueue *rq,
proc = dequeue_process(rq, prio_q, &state);
while (proc) {
+ Process *real_proc;
+ int prio;
+ erts_aint32_t max_qbit, qbit, real_state;
+
+ prio = ERTS_PSFLGS_GET_PRQ_PRIO(state);
+ qbit = ((erts_aint32_t) 1) << prio;
+
+ if (!(state & ERTS_PSFLG_PROXY)) {
+ real_proc = proc;
+ real_state = state;
+ }
+ else {
+ real_proc = erts_proc_lookup_raw(proc->common.id);
+ if (!real_proc) {
+ free_proxy_proc(proc);
+ goto handle_next_proc;
+ }
+ real_state = erts_smp_atomic32_read_acqb(&real_proc->state);
+ }
+
+ max_qbit = (state >> ERTS_PSFLGS_IN_PRQ_MASK_OFFSET);
+ max_qbit &= ERTS_PSFLGS_QMASK;
+ max_qbit |= 1 << ERTS_PSFLGS_QMASK_BITS;
+ max_qbit &= -max_qbit;
+
+ if (qbit > max_qbit) {
+ /* Process already queued with higher prio; drop it... */
+ if (real_proc != proc)
+ free_proxy_proc(proc);
+ else {
+ erts_aint32_t clr_bits;
+#ifdef DEBUG
+ erts_aint32_t old;
+#endif
+
+ clr_bits = ERTS_PSFLG_IN_RUNQ;
+ clr_bits |= qbit << ERTS_PSFLGS_IN_PRQ_MASK_OFFSET;
+
+#ifdef DEBUG
+ old =
+#else
+ (void)
+#endif
+ erts_smp_atomic32_read_band_mb(&proc->state,
+ ~clr_bits);
+ ASSERT((old & clr_bits) == clr_bits);
+
+ }
+
+ goto handle_next_proc;
+ }
+
#ifdef ERTS_DIRTY_SCHEDULERS
- requeue = 1;
+ if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix))
+ clear_proc_dirty_queue_bit(real_proc, rq, qbit);
#endif
- if (ERTS_PSFLG_BOUND & state) {
+
+ if (ERTS_PSFLG_BOUND & real_state) {
/* Bound processes get stuck here... */
proc->next = NULL;
if (sbpp->last)
@@ -4071,38 +4220,8 @@ evacuate_run_queue(ErtsRunQueue *rq,
else
sbpp->first = proc;
sbpp->last = proc;
-#ifdef ERTS_DIRTY_SCHEDULERS
- requeue = 0;
-#endif
- }
-#ifdef ERTS_DIRTY_SCHEDULERS
- else if (state & ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q) {
-#ifdef DEBUG
- erts_aint32_t old =
-#else
- (void)
-#endif
- erts_smp_atomic32_read_band_nob(&proc->state,
- ~(ERTS_PSFLG_DIRTY_CPU_PROC
- | ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q));
- /* assert that no other dirty flags are set */
- ASSERT(!(old & (ERTS_PSFLG_DIRTY_IO_PROC|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q)));
- } else if (state & ERTS_PSFLG_DIRTY_IO_PROC_IN_Q) {
-#ifdef DEBUG
- erts_aint32_t old =
-#else
- (void)
-#endif
- erts_smp_atomic32_read_band_nob(&proc->state,
- ~(ERTS_PSFLG_DIRTY_IO_PROC
- | ERTS_PSFLG_DIRTY_IO_PROC_IN_Q));
- /* assert that no other dirty flags are set */
- ASSERT(!(old & (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q)));
}
- if (requeue) {
-#else
else {
-#endif
int prio = (int) ERTS_PSFLGS_GET_PRQ_PRIO(state);
erts_smp_runq_unlock(rq);
@@ -4125,6 +4244,8 @@ evacuate_run_queue(ErtsRunQueue *rq,
erts_smp_runq_lock(rq);
}
+
+ handle_next_proc:
proc = dequeue_process(rq, prio_q, &state);
}
if (notify)
@@ -5405,17 +5526,11 @@ erts_early_init_scheduling(int no_schedulers)
wakeup_other.threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_MEDIUM;
wakeup_other.type = ERTS_SCHED_WAKEUP_OTHER_TYPE_DEFAULT;
#endif
-#ifndef ERTS_SCHED_MIN_SPIN
sched_busy_wait.sys_schedule = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_MEDIUM;
sched_busy_wait.tse = (ERTS_SCHED_SYS_SLEEP_SPINCOUNT_MEDIUM
* ERTS_SCHED_TSE_SLEEP_SPINCOUNT_FACT);
sched_busy_wait.aux_work = (ERTS_SCHED_SYS_SLEEP_SPINCOUNT_MEDIUM
* ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_MEDIUM);
-#else
- sched_busy_wait.sys_schedule = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_NONE;
- sched_busy_wait.tse = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_NONE;
- sched_busy_wait.aux_work = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_NONE;
-#endif
}
int
@@ -5584,7 +5699,8 @@ static void
init_scheduler_data(ErtsSchedulerData* esdp, int num,
ErtsSchedulerSleepInfo* ssi,
ErtsRunQueue* runq,
- char** daww_ptr, size_t daww_sz)
+ char** daww_ptr, size_t daww_sz,
+ Process *shadow_proc)
{
esdp->timer_wheel = NULL;
#ifdef ERTS_SMP
@@ -5599,9 +5715,6 @@ init_scheduler_data(ErtsSchedulerData* esdp, int num,
esdp->f_reg_array =
erts_alloc_permanent_cache_aligned(ERTS_ALC_T_BEAM_REGISTER,
MAX_REG * sizeof(FloatDef));
-#if !HEAP_ON_C_STACK
- esdp->num_tmp_heap_used = 0;
-#endif
#ifdef ERTS_DIRTY_SCHEDULERS
if (ERTS_RUNQ_IX_IS_DIRTY(runq->ix)) {
esdp->no = 0;
@@ -5611,6 +5724,15 @@ init_scheduler_data(ErtsSchedulerData* esdp, int num,
esdp->no = (Uint) num;
ERTS_DIRTY_SCHEDULER_NO(esdp) = 0;
}
+ esdp->dirty_shadow_process = shadow_proc;
+ if (shadow_proc) {
+ erts_init_empty_process(shadow_proc);
+ erts_smp_atomic32_init_nob(&shadow_proc->state,
+ (ERTS_PSFLG_ACTIVE
+ | ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_PROXY));
+ shadow_proc->static_flags = ERTS_STC_FLG_SHADOW_PROC;
+ }
#else
esdp->no = (Uint) num;
#endif
@@ -5661,6 +5783,9 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online
char *daww_ptr;
size_t daww_sz;
size_t size_runqs;
+#ifdef ERTS_SMP
+ erts_aint32_t set_schdlr_sspnd_change_flags;
+#endif
init_misc_op_list_alloc();
init_proc_sys_task_queues_alloc();
@@ -5785,9 +5910,12 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online
n = (int) no_schedulers;
erts_no_schedulers = n;
+ erts_no_total_schedulers = n;
#ifdef ERTS_DIRTY_SCHEDULERS
erts_no_dirty_cpu_schedulers = no_dirty_cpu_schedulers;
+ erts_no_total_schedulers += no_dirty_cpu_schedulers;
erts_no_dirty_io_schedulers = no_dirty_io_schedulers;
+ erts_no_total_schedulers += no_dirty_io_schedulers;
#endif
/* Create and initialize scheduler sleep info */
@@ -5859,37 +5987,45 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online
for (ix = 0; ix < n; ix++) {
ErtsSchedulerData *esdp = ERTS_SCHEDULER_IX(ix);
init_scheduler_data(esdp, ix+1, ERTS_SCHED_SLEEP_INFO_IX(ix),
- ERTS_RUNQ_IX(ix), &daww_ptr, daww_sz);
+ ERTS_RUNQ_IX(ix), &daww_ptr, daww_sz,
+ NULL);
}
#ifdef ERTS_DIRTY_SCHEDULERS
-#ifdef ERTS_SMP
- erts_aligned_dirty_cpu_scheduler_data =
- erts_alloc_permanent_cache_aligned(
- ERTS_ALC_T_SCHDLR_DATA,
- no_dirty_cpu_schedulers*sizeof(ErtsAlignedSchedulerData));
- for (ix = 0; ix < no_dirty_cpu_schedulers; ix++) {
- ErtsSchedulerData *esdp = ERTS_DIRTY_CPU_SCHEDULER_IX(ix);
- init_scheduler_data(esdp, ix+1, ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix),
- ERTS_DIRTY_CPU_RUNQ, NULL, 0);
- }
- erts_aligned_dirty_io_scheduler_data =
- erts_alloc_permanent_cache_aligned(
- ERTS_ALC_T_SCHDLR_DATA,
- no_dirty_io_schedulers*sizeof(ErtsAlignedSchedulerData));
- for (ix = 0; ix < no_dirty_io_schedulers; ix++) {
- ErtsSchedulerData *esdp = ERTS_DIRTY_IO_SCHEDULER_IX(ix);
- init_scheduler_data(esdp, ix+1, ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(ix),
- ERTS_DIRTY_IO_RUNQ, NULL, 0);
+ {
+ int dirty_scheds = no_dirty_cpu_schedulers + no_dirty_io_schedulers;
+ int adspix = 0;
+ ErtsAlignedDirtyShadowProcess *adsp =
+ erts_alloc_permanent_cache_aligned(
+ ERTS_ALC_T_SCHDLR_DATA,
+ dirty_scheds * sizeof(ErtsAlignedDirtyShadowProcess));
+
+ erts_aligned_dirty_cpu_scheduler_data =
+ erts_alloc_permanent_cache_aligned(
+ ERTS_ALC_T_SCHDLR_DATA,
+ dirty_scheds * sizeof(ErtsAlignedSchedulerData));
+
+ erts_aligned_dirty_io_scheduler_data =
+ &erts_aligned_dirty_cpu_scheduler_data[no_dirty_cpu_schedulers];
+
+ for (ix = 0; ix < no_dirty_cpu_schedulers; ix++) {
+ ErtsSchedulerData *esdp = ERTS_DIRTY_CPU_SCHEDULER_IX(ix);
+ init_scheduler_data(esdp, ix+1, ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix),
+ ERTS_DIRTY_CPU_RUNQ, NULL, 0,
+ &adsp[adspix++].dsp);
+ }
+ for (ix = 0; ix < no_dirty_io_schedulers; ix++) {
+ ErtsSchedulerData *esdp = ERTS_DIRTY_IO_SCHEDULER_IX(ix);
+ init_scheduler_data(esdp, ix+1, ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(ix),
+ ERTS_DIRTY_IO_RUNQ, NULL, 0,
+ &adsp[adspix++].dsp);
+ }
}
#endif
-#endif
init_misc_aux_work();
-#if !HALFWORD_HEAP
init_swtreq_alloc();
init_screq_alloc();
-#endif
erts_atomic32_init_nob(&debug_wait_completed_count, 0); /* debug only */
debug_wait_completed_flags = 0;
@@ -5900,25 +6036,6 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online
erts_alloc_permanent_cache_aligned(ERTS_ALC_T_SCHDLR_DATA,
sizeof(ErtsAuxWorkData));
- erts_smp_mtx_init(&schdlr_sspnd.mtx, "schdlr_sspnd");
- erts_smp_cnd_init(&schdlr_sspnd.cnd);
-
- erts_smp_atomic32_init_nob(&schdlr_sspnd.changing, 0);
- schdlr_sspnd.online = no_schedulers_online;
- schdlr_sspnd.curr_online = no_schedulers;
- schdlr_sspnd.msb.ongoing = 0;
- erts_smp_atomic32_init_nob(&schdlr_sspnd.active, no_schedulers);
-#ifdef ERTS_DIRTY_SCHEDULERS
- erts_smp_atomic32_init_nob(&schdlr_sspnd.dirty_cpu_changing, 0);
- schdlr_sspnd.dirty_cpu_online = no_dirty_cpu_schedulers_online;
- schdlr_sspnd.dirty_cpu_curr_online = no_dirty_cpu_schedulers;
- erts_smp_atomic32_init_nob(&schdlr_sspnd.dirty_cpu_active, no_dirty_cpu_schedulers);
- erts_smp_atomic32_init_nob(&schdlr_sspnd.dirty_io_changing, 0);
- schdlr_sspnd.dirty_io_online = no_dirty_io_schedulers;
- schdlr_sspnd.dirty_io_curr_online = no_dirty_io_schedulers;
- erts_smp_atomic32_init_nob(&schdlr_sspnd.dirty_io_active, no_dirty_io_schedulers);
-#endif
- schdlr_sspnd.msb.procs = NULL;
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");
@@ -5933,32 +6050,66 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online
init_migration_paths();
- if (no_schedulers_online < no_schedulers) {
+ init_scheduler_suspend();
+
+ set_schdlr_sspnd_change_flags = 0;
+
+ schdlr_sspnd_set_nscheds(&schdlr_sspnd.online,
+ ERTS_SCHED_NORMAL,
+ no_schedulers_online);
+ schdlr_sspnd_set_nscheds(&schdlr_sspnd.curr_online,
+ ERTS_SCHED_NORMAL,
+ no_schedulers);
+ schdlr_sspnd_set_nscheds(&schdlr_sspnd.active,
+ ERTS_SCHED_NORMAL,
+ no_schedulers);
+
+ if (no_schedulers_online != no_schedulers) {
+ ASSERT(no_schedulers_online < no_schedulers);
+ set_schdlr_sspnd_change_flags |= ERTS_SCHDLR_SSPND_CHNG_ONLN;
+ schdlr_sspnd.changer = am_init;
change_no_used_runqs(no_schedulers_online);
for (ix = no_schedulers_online; ix < erts_no_run_queues; ix++)
suspend_run_queue(ERTS_RUNQ_IX(ix));
}
- schdlr_sspnd.wait_curr_online = no_schedulers_online;
- schdlr_sspnd.curr_online *= 2; /* Boot strapping... */
- ERTS_SCHDLR_SSPND_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_ONLN
- | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0);
#ifdef ERTS_DIRTY_SCHEDULERS
- schdlr_sspnd.dirty_cpu_wait_curr_online = no_dirty_cpu_schedulers_online;
- schdlr_sspnd.dirty_cpu_curr_online *= 2;
- ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_ONLN
- | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0);
- for (ix = no_dirty_cpu_schedulers_online; ix < no_dirty_cpu_schedulers; ix++) {
- ErtsSchedulerData* esdp = ERTS_DIRTY_CPU_SCHEDULER_IX(ix);
- erts_smp_atomic32_read_bor_nob(&esdp->ssi->flags, ERTS_SSI_FLG_SUSPENDED);
+
+ schdlr_sspnd_set_nscheds(&schdlr_sspnd.online,
+ ERTS_SCHED_DIRTY_CPU,
+ no_dirty_cpu_schedulers_online);
+ schdlr_sspnd_set_nscheds(&schdlr_sspnd.curr_online,
+ ERTS_SCHED_DIRTY_CPU,
+ no_dirty_cpu_schedulers);
+ schdlr_sspnd_set_nscheds(&schdlr_sspnd.active,
+ ERTS_SCHED_DIRTY_CPU,
+ no_dirty_cpu_schedulers);
+
+ if (no_dirty_cpu_schedulers_online != no_dirty_cpu_schedulers) {
+ ASSERT(no_dirty_cpu_schedulers_online < no_dirty_cpu_schedulers);
+ set_schdlr_sspnd_change_flags |= ERTS_SCHDLR_SSPND_CHNG_DCPU_ONLN;
+ for (ix = no_dirty_cpu_schedulers_online; ix < no_dirty_cpu_schedulers; ix++) {
+ ErtsSchedulerData* esdp = ERTS_DIRTY_CPU_SCHEDULER_IX(ix);
+ erts_smp_atomic32_read_bor_nob(&esdp->ssi->flags, ERTS_SSI_FLG_SUSPENDED);
+ }
}
- schdlr_sspnd.dirty_io_wait_curr_online = no_dirty_io_schedulers;
- schdlr_sspnd.dirty_io_curr_online *= 2;
- ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_ONLN
- | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0);
+ schdlr_sspnd_set_nscheds(&schdlr_sspnd.online,
+ ERTS_SCHED_DIRTY_IO,
+ no_dirty_io_schedulers);
+ schdlr_sspnd_set_nscheds(&schdlr_sspnd.curr_online,
+ ERTS_SCHED_DIRTY_IO,
+ no_dirty_io_schedulers);
+ schdlr_sspnd_set_nscheds(&schdlr_sspnd.active,
+ ERTS_SCHED_DIRTY_IO,
+ no_dirty_io_schedulers);
+
#endif
+ if (set_schdlr_sspnd_change_flags)
+ erts_smp_atomic32_set_nob(&schdlr_sspnd.changing,
+ set_schdlr_sspnd_change_flags);
+
erts_smp_atomic32_init_nob(&doing_sys_schedule, 0);
init_misc_aux_work();
@@ -5973,11 +6124,9 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online
#endif
}
erts_no_schedulers = 1;
-#ifdef ERTS_DIRTY_SCHEDULERS
erts_no_dirty_cpu_schedulers = 0;
erts_no_dirty_io_schedulers = 0;
#endif
-#endif
erts_smp_atomic32_init_nob(&function_calls, 0);
@@ -6069,19 +6218,106 @@ make_proxy_proc(Process *prev_proxy, Process *proc, erts_aint32_t prio)
return proxy;
}
-static ERTS_INLINE void
-free_proxy_proc(Process *proxy)
-{
- ASSERT(erts_smp_atomic32_read_nob(&proxy->state) & ERTS_PSFLG_PROXY);
- erts_free(ERTS_ALC_T_PROC, proxy);
-}
-
#define ERTS_ENQUEUE_NOT 0
#define ERTS_ENQUEUE_NORMAL_QUEUE 1
-#ifdef ERTS_DIRTY_SCHEDULERS
#define ERTS_ENQUEUE_DIRTY_CPU_QUEUE 2
#define ERTS_ENQUEUE_DIRTY_IO_QUEUE 3
+
+#ifdef ERTS_DIRTY_SCHEDULERS
+
+static int
+check_dirty_enqueue_in_prio_queue(Process *c_p,
+ erts_aint32_t *newp,
+ erts_aint32_t actual,
+ erts_aint32_t aprio,
+ erts_aint32_t qbit)
+{
+ int queue;
+ erts_aint32_t dact, max_qbit;
+
+ /* Do not enqueue free process... */
+ if (actual & ERTS_PSFLG_FREE) {
+ *newp &= ~ERTS_PSFLGS_DIRTY_WORK;
+ return ERTS_ENQUEUE_NOT;
+ }
+
+ /* Termination should be done on an ordinary scheduler */
+ if ((*newp) & ERTS_PSFLG_EXITING) {
+ *newp &= ~ERTS_PSFLGS_DIRTY_WORK;
+ return ERTS_ENQUEUE_NORMAL_QUEUE;
+ }
+
+ /*
+ * If we have system tasks, we enqueue on ordinary run-queue
+ * and take care of those system tasks first.
+ */
+ if ((*newp) & ERTS_PSFLG_ACTIVE_SYS)
+ return ERTS_ENQUEUE_NORMAL_QUEUE;
+
+ dact = erts_smp_atomic32_read_mb(&c_p->dirty_state);
+ if (actual & (ERTS_PSFLG_DIRTY_ACTIVE_SYS
+ | ERTS_PSFLG_DIRTY_CPU_PROC)) {
+ max_qbit = ((dact >> ERTS_PDSFLGS_IN_CPU_PRQ_MASK_OFFSET)
+ & ERTS_PDSFLGS_QMASK);
+ queue = ERTS_ENQUEUE_DIRTY_CPU_QUEUE;
+ }
+ else {
+ ASSERT(actual & ERTS_PSFLG_DIRTY_IO_PROC);
+ max_qbit = ((dact >> ERTS_PDSFLGS_IN_IO_PRQ_MASK_OFFSET)
+ & ERTS_PDSFLGS_QMASK);
+ queue = ERTS_ENQUEUE_DIRTY_IO_QUEUE;
+ }
+
+ max_qbit |= 1 << ERTS_PSFLGS_QMASK_BITS;
+ max_qbit &= -max_qbit;
+
+ if (qbit >= max_qbit)
+ return ERTS_ENQUEUE_NOT; /* Already queued in higher or equal prio */
+ if ((actual & (ERTS_PSFLG_IN_RUNQ|ERTS_PSFLGS_USR_PRIO_MASK))
+ != (aprio << ERTS_PSFLGS_USR_PRIO_OFFSET)) {
+ /*
+ * Process struct already enqueued, or actual prio not
+ * equal to user prio, i.e., enqueue using proxy.
+ */
+ return -1*queue;
+ }
+
+ /*
+ * Enqueue using process struct.
+ */
+ *newp &= ~ERTS_PSFLGS_PRQ_PRIO_MASK;
+ *newp |= ERTS_PSFLG_IN_RUNQ | (aprio << ERTS_PSFLGS_PRQ_PRIO_OFFSET);
+ return queue;
+}
+
+static ERTS_INLINE int
+fin_dirty_enq_s_change(Process *p,
+ int pstruct_reserved,
+ erts_aint32_t enq_prio,
+ int qmask_offset)
+{
+ erts_aint32_t qbit = 1 << enq_prio;
+ qbit <<= qmask_offset;
+
+ if (qbit & erts_smp_atomic32_read_bor_mb(&p->dirty_state, qbit)) {
+ /* Already enqueue by someone else... */
+ if (pstruct_reserved) {
+ /* We reserved process struct for enqueue; clear it... */
+#ifdef DEBUG
+ erts_aint32_t old =
+#else
+ (void)
#endif
+ erts_smp_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_IN_RUNQ);
+ ASSERT(old & ERTS_PSFLG_IN_RUNQ);
+ }
+ return 0;
+ }
+
+ return !0;
+}
+
+#endif /* ERTS_DIRTY_SCHEDULERS */
static ERTS_INLINE int
check_enqueue_in_prio_queue(Process *c_p,
@@ -6097,61 +6333,14 @@ check_enqueue_in_prio_queue(Process *c_p,
*prq_prio_p = aprio;
#ifdef ERTS_DIRTY_SCHEDULERS
- if (actual & (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC)) {
- /*
- * If we have system tasks of a priority higher
- * or equal to the user priority, we enqueue
- * on ordinary run-queue and take care of
- * those system tasks first.
- */
- if (actual & ERTS_PSFLG_ACTIVE_SYS) {
- erts_aint32_t uprio, stprio, qmask;
- uprio = (actual >> ERTS_PSFLGS_USR_PRIO_OFFSET) & ERTS_PSFLGS_PRIO_MASK;
- if (aprio < uprio)
- goto enqueue_normal_runq; /* system tasks with higher prio */
- erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_STATUS);
- qmask = c_p->sys_task_qs->qmask;
- erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_STATUS);
- switch (qmask & -qmask) {
- case MAX_BIT:
- stprio = PRIORITY_MAX;
- break;
- case HIGH_BIT:
- stprio = PRIORITY_HIGH;
- break;
- case NORMAL_BIT:
- stprio = PRIORITY_NORMAL;
- break;
- case LOW_BIT:
- stprio = PRIORITY_LOW;
- break;
- default:
- stprio = PRIORITY_LOW+1;
- break;
- }
- if (stprio <= uprio)
- goto enqueue_normal_runq; /* system tasks with higher prio */
- }
-
- /* Enqueue in dirty run queue if not already enqueued */
- if (actual & (ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q))
- return ERTS_ENQUEUE_NOT; /* already in queue */
- if (actual & ERTS_PSFLG_DIRTY_CPU_PROC) {
- *newp |= ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q;
- if (actual & ERTS_PSFLG_IN_RUNQ)
- return -ERTS_ENQUEUE_DIRTY_CPU_QUEUE; /* use proxy */
- *newp |= ERTS_PSFLG_IN_RUNQ;
- return ERTS_ENQUEUE_DIRTY_CPU_QUEUE;
- }
- *newp |= ERTS_PSFLG_DIRTY_IO_PROC_IN_Q;
- if (actual & ERTS_PSFLG_IN_RUNQ)
- return -ERTS_ENQUEUE_DIRTY_IO_QUEUE; /* use proxy */
- *newp |= ERTS_PSFLG_IN_RUNQ;
- return ERTS_ENQUEUE_DIRTY_IO_QUEUE;
+ if (actual & ERTS_PSFLGS_DIRTY_WORK) {
+ int res = check_dirty_enqueue_in_prio_queue(c_p, newp, actual,
+ aprio, qbit);
+ if (res != ERTS_ENQUEUE_NORMAL_QUEUE)
+ return res;
}
-
- enqueue_normal_runq:
#endif
+
max_qbit = (actual >> ERTS_PSFLGS_IN_PRQ_MASK_OFFSET) & ERTS_PSFLGS_QMASK;
max_qbit |= 1 << ERTS_PSFLGS_QMASK_BITS;
max_qbit &= -max_qbit;
@@ -6183,31 +6372,95 @@ check_enqueue_in_prio_queue(Process *c_p,
return ERTS_ENQUEUE_NORMAL_QUEUE;
}
+static ERTS_INLINE ErtsRunQueue *
+select_enqueue_run_queue(int enqueue, int enq_prio, Process *p, erts_aint32_t state)
+{
+
+ switch (enqueue) {
+
+ case ERTS_ENQUEUE_NOT:
+
+ return NULL;
+
+#ifdef ERTS_DIRTY_SCHEDULERS
+
+ case ERTS_ENQUEUE_DIRTY_CPU_QUEUE:
+ case -ERTS_ENQUEUE_DIRTY_CPU_QUEUE:
+
+ if (fin_dirty_enq_s_change(p, enqueue > 0, enq_prio,
+ ERTS_PDSFLGS_IN_CPU_PRQ_MASK_OFFSET))
+ return ERTS_DIRTY_CPU_RUNQ;
+
+ return NULL;
+
+
+ case ERTS_ENQUEUE_DIRTY_IO_QUEUE:
+ case -ERTS_ENQUEUE_DIRTY_IO_QUEUE:
+
+ if (fin_dirty_enq_s_change(p, enqueue > 0, enq_prio,
+ ERTS_PDSFLGS_IN_IO_PRQ_MASK_OFFSET))
+ return ERTS_DIRTY_IO_RUNQ;
+
+ return NULL;
+
+#endif
+
+ default: {
+ ErtsRunQueue* runq;
+
+ ASSERT(enqueue == ERTS_ENQUEUE_NORMAL_QUEUE
+ || enqueue == -ERTS_ENQUEUE_NORMAL_QUEUE);
+
+ runq = erts_get_runq_proc(p);
+
+#ifdef ERTS_SMP
+ if (!(ERTS_PSFLG_BOUND & state)) {
+ ErtsRunQueue *new_runq = erts_check_emigration_need(runq, enq_prio);
+ if (new_runq) {
+ RUNQ_SET_RQ(&p->run_queue, new_runq);
+ runq = new_runq;
+ }
+ }
+#endif
+
+ ASSERT(runq);
+
+ return runq;
+ }
+ }
+}
+
+
/*
* schedule_out_process() return with c_rq locked.
+ *
+ * Return non-zero value if caller should decrease
+ * reference count on the process when done with it...
*/
static ERTS_INLINE int
-schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p, Process *proxy)
+schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p,
+ Process *proxy, int is_normal_sched)
{
- erts_aint32_t a, e, n, enq_prio = -1;
+ erts_aint32_t a, e, n, enq_prio = -1, running_flgs;
int enqueue; /* < 0 -> use proxy */
- Process* sched_p;
ErtsRunQueue* runq;
-#ifdef ERTS_SMP
- int check_emigration_need;
-#endif
+
+ if (is_normal_sched)
+ running_flgs = ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS;
+ else
+ running_flgs = ERTS_PSFLG_DIRTY_RUNNING|ERTS_PSFLG_DIRTY_RUNNING_SYS;
a = state;
while (1) {
n = e = a;
- ASSERT(a & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS));
+ ASSERT(a & running_flgs);
enqueue = ERTS_ENQUEUE_NOT;
- n &= ~(ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS);
- if (a & ERTS_PSFLG_ACTIVE_SYS
+ n &= ~running_flgs;
+ if ((a & (ERTS_PSFLG_ACTIVE_SYS|ERTS_PSFLG_DIRTY_ACTIVE_SYS))
|| (a & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_SUSPENDED)) == ERTS_PSFLG_ACTIVE) {
enqueue = check_enqueue_in_prio_queue(p, &enq_prio, &n, a);
}
@@ -6216,16 +6469,17 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p, Proces
break;
}
- switch (enqueue) {
- case ERTS_ENQUEUE_NOT:
+ runq = select_enqueue_run_queue(enqueue, enq_prio, p, n);
+
+ if (!runq) {
+
if (erts_system_profile_flags.runnable_procs) {
/* Status lock prevents out of order "runnable proc" trace msgs */
ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p));
- if (!(a & ERTS_PSFLG_ACTIVE_SYS)
- && (!(a & ERTS_PSFLG_ACTIVE)
- || (a & ERTS_PSFLG_SUSPENDED))) {
+ if (!(a & (ERTS_PSFLG_ACTIVE_SYS|ERTS_PSFLG_DIRTY_ACTIVE_SYS))
+ && (!(a & ERTS_PSFLG_ACTIVE) || (a & ERTS_PSFLG_SUSPENDED))) {
/* Process inactive */
profile_runnable_proc(p, am_inactive);
}
@@ -6236,100 +6490,102 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p, Proces
erts_smp_runq_lock(c_rq);
- return 0;
-
-#ifdef ERTS_DIRTY_SCHEDULERS
-#ifdef ERTS_SMP
- case ERTS_ENQUEUE_DIRTY_CPU_QUEUE:
- case -ERTS_ENQUEUE_DIRTY_CPU_QUEUE:
- runq = ERTS_DIRTY_CPU_RUNQ;
- ASSERT(ERTS_SCHEDULER_IS_DIRTY_CPU(runq->scheduler));
-#ifdef ERTS_SMP
- check_emigration_need = 0;
+#if !defined(ERTS_SMP)
+ /* Decrement refc if process struct is free... */
+ return !!(n & ERTS_PSFLG_FREE);
+#else
+ /* Decrement refc if scheduled out from dirty scheduler... */
+ return !is_normal_sched;
#endif
- break;
+ }
+ else {
+ Process* sched_p;
- case ERTS_ENQUEUE_DIRTY_IO_QUEUE:
- case -ERTS_ENQUEUE_DIRTY_IO_QUEUE:
- runq = ERTS_DIRTY_IO_RUNQ;
- ASSERT(ERTS_SCHEDULER_IS_DIRTY_IO(runq->scheduler));
-#ifdef ERTS_SMP
- check_emigration_need = 0;
-#endif
- break;
-#endif
-#endif
+ ASSERT(!(n & ERTS_PSFLG_FREE));
+ ASSERT(!(n & ERTS_PSFLG_SUSPENDED) || (n & (ERTS_PSFLG_ACTIVE_SYS
+ | ERTS_PSFLG_DIRTY_ACTIVE_SYS)));
- default:
- ASSERT(enqueue == ERTS_ENQUEUE_NORMAL_QUEUE
- || enqueue == -ERTS_ENQUEUE_NORMAL_QUEUE);
+ if (enqueue < 0)
+ sched_p = make_proxy_proc(proxy, p, enq_prio);
+ else {
+ sched_p = p;
+ if (proxy)
+ free_proxy_proc(proxy);
+ }
- runq = erts_get_runq_proc(p);
-#ifdef ERTS_SMP
- check_emigration_need = !(ERTS_PSFLG_BOUND & n);
-#endif
- break;
- }
+ ASSERT(runq);
- ASSERT(!(n & ERTS_PSFLG_SUSPENDED) || (n & ERTS_PSFLG_ACTIVE_SYS));
+ erts_smp_runq_lock(runq);
- if (enqueue < 0)
- sched_p = make_proxy_proc(proxy, p, enq_prio);
- else {
- sched_p = p;
- if (proxy)
- free_proxy_proc(proxy);
- }
+ if (is_normal_sched && sched_p == p && ERTS_RUNQ_IX_IS_DIRTY(runq->ix))
+ erts_proc_inc_refc(p); /* Needs to be done before enqueue_process() */
-#ifdef ERTS_SMP
- if (check_emigration_need) {
- ErtsRunQueue *new_runq = erts_check_emigration_need(runq, enq_prio);
- if (new_runq) {
- RUNQ_SET_RQ(&sched_p->run_queue, new_runq);
- runq = new_runq;
- }
- }
-#endif
+ /* Enqueue the process */
+ enqueue_process(runq, (int) enq_prio, sched_p);
- ASSERT(runq);
+ if (runq == c_rq)
+ return 0;
- erts_smp_runq_lock(runq);
+ erts_smp_runq_unlock(runq);
- /* Enqueue the process */
- enqueue_process(runq, (int) enq_prio, sched_p);
+ smp_notify_inc_runq(runq);
- if (runq == c_rq)
- return 1;
- erts_smp_runq_unlock(runq);
- smp_notify_inc_runq(runq);
- erts_smp_runq_lock(c_rq);
- return 1;
+ erts_smp_runq_lock(c_rq);
+
+ /*
+ * Decrement refc if process is scheduled out by a
+ * dirty scheduler, and we have not just scheduled
+ * the process using the ordinary process struct
+ * on a dirty run-queue again...
+ */
+ return !is_normal_sched && (sched_p != p
+ || !ERTS_RUNQ_IX_IS_DIRTY(runq->ix));
+ }
}
static ERTS_INLINE void
-add2runq(Process *p, erts_aint32_t state, erts_aint32_t prio)
+add2runq(int enqueue, erts_aint32_t prio,
+ Process *proc, erts_aint32_t state,
+ Process **proxy)
{
- ErtsRunQueue *runq = erts_get_runq_proc(p);
+ ErtsRunQueue *runq;
-#ifdef ERTS_SMP
- if (!(ERTS_PSFLG_BOUND & state)) {
- ErtsRunQueue *new_runq = erts_check_emigration_need(runq, (int) prio);
- if (new_runq) {
- RUNQ_SET_RQ(&p->run_queue, new_runq);
- runq = new_runq;
- }
- }
-#endif
- ASSERT(runq);
+ runq = select_enqueue_run_queue(enqueue, prio, proc, state);
- erts_smp_runq_lock(runq);
+ if (runq) {
+ Process *sched_p;
- /* Enqueue the process */
- enqueue_process(runq, (int) prio, p);
+ if (enqueue > 0) {
+ sched_p = proc;
+ /*
+ * Refc on process struct (i.e. true struct,
+ * not proxy-struct) increased while in a
+ * dirty run-queue or executing on a dirty
+ * scheduler.
+ */
+ if (ERTS_RUNQ_IX_IS_DIRTY(runq->ix))
+ erts_proc_inc_refc(proc);
+ }
+ else {
+ Process *pxy;
- erts_smp_runq_unlock(runq);
- smp_notify_inc_runq(runq);
+ if (!proxy)
+ pxy = NULL;
+ else {
+ pxy = *proxy;
+ *proxy = NULL;
+ }
+ sched_p = make_proxy_proc(pxy, proc, prio);
+ }
+
+ erts_smp_runq_lock(runq);
+ /* Enqueue the process */
+ enqueue_process(runq, (int) prio, sched_p);
+
+ erts_smp_runq_unlock(runq);
+ smp_notify_inc_runq(runq);
+ }
}
static ERTS_INLINE int
@@ -6341,8 +6597,9 @@ change_proc_schedule_state(Process *p,
ErtsProcLocks locks)
{
/*
- * NOTE: ERTS_PSFLG_RUNNING, ERTS_PSFLG_RUNNING_SYS and
- * ERTS_PSFLG_ACTIVE_SYS are not allowed to be
+ * NOTE: ERTS_PSFLG_RUNNING, ERTS_PSFLG_RUNNING_SYS,
+ * ERTS_PSFLG_DIRTY_RUNNING, ERTS_PSFLG_DIRTY_RUNNING_SYS
+ * and ERTS_PSFLG_ACTIVE_SYS are not allowed to be
* altered by this function!
*/
erts_aint32_t a = *statep, n;
@@ -6356,9 +6613,13 @@ change_proc_schedule_state(Process *p,
ASSERT(!(a & ERTS_PSFLG_PROXY));
ASSERT((clear_state_flags & (ERTS_PSFLG_RUNNING
| ERTS_PSFLG_RUNNING_SYS
+ | ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS
| ERTS_PSFLG_ACTIVE_SYS)) == 0);
ASSERT((set_state_flags & (ERTS_PSFLG_RUNNING
| ERTS_PSFLG_RUNNING_SYS
+ | ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS
| ERTS_PSFLG_ACTIVE_SYS)) == 0);
if (lock_status)
@@ -6382,8 +6643,16 @@ change_proc_schedule_state(Process *p,
if ((n & (ERTS_PSFLG_SUSPENDED
| ERTS_PSFLG_RUNNING
| ERTS_PSFLG_RUNNING_SYS
+ | ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS
| ERTS_PSFLG_IN_RUNQ
- | ERTS_PSFLG_ACTIVE)) == ERTS_PSFLG_ACTIVE) {
+ | ERTS_PSFLG_ACTIVE)) == ERTS_PSFLG_ACTIVE
+#ifdef ERTS_DIRTY_SCHEDULERS
+ || (n & (ERTS_PSFLG_RUNNING
+ | ERTS_PSFLG_RUNNING_SYS
+ | ERTS_PSFLG_EXITING)) == ERTS_PSFLG_EXITING
+#endif
+ ) {
/*
* Active and seemingly need to be enqueued, but
* process may be in a run queue via proxy, need
@@ -6407,7 +6676,9 @@ change_proc_schedule_state(Process *p,
| ERTS_PSFLG_ACTIVE)) == ERTS_PSFLG_ACTIVE)
&& (!(a & (ERTS_PSFLG_ACTIVE_SYS
| ERTS_PSFLG_RUNNING
- | ERTS_PSFLG_RUNNING_SYS)
+ | ERTS_PSFLG_RUNNING_SYS
+ | ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)
&& (!(a & ERTS_PSFLG_ACTIVE)
|| (a & ERTS_PSFLG_SUSPENDED))))) {
/* We activated a prevously inactive process */
@@ -6435,10 +6706,7 @@ schedule_process(Process *p, erts_aint32_t in_state, ErtsProcLocks locks)
&state,
&enq_prio,
locks);
- if (enqueue != ERTS_ENQUEUE_NOT)
- add2runq(enqueue > 0 ? p : make_proxy_proc(NULL, p, enq_prio),
- state,
- enq_prio);
+ add2runq(enqueue, enq_prio, p, state, NULL);
}
void
@@ -6447,22 +6715,103 @@ erts_schedule_process(Process *p, erts_aint32_t state, ErtsProcLocks locks)
schedule_process(p, state, locks);
}
-static void
-schedule_process_sys_task(Process *p, erts_aint32_t state, Process *proxy)
+static int
+schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st,
+ erts_aint32_t *fail_state_p)
{
- /*
- * Expects status lock to be locked when called, and
- * returns with status lock unlocked...
- */
- erts_aint32_t a = state, n, enq_prio = -1;
+ int res;
+ int locked;
+ ErtsProcSysTaskQs *stqs, *free_stqs;
+ erts_aint32_t fail_state, state, a, n, enq_prio;
int enqueue; /* < 0 -> use proxy */
- unsigned int prof_runnable_procs = erts_system_profile_flags.runnable_procs;
+ unsigned int prof_runnable_procs;
+
+ fail_state = *fail_state_p;
+
+ res = 1; /* prepare for success */
+ st->next = st->prev = st; /* Prep for empty prio queue */
+ state = erts_smp_atomic32_read_nob(&p->state);
+ prof_runnable_procs = erts_system_profile_flags.runnable_procs;
+ locked = 0;
+ free_stqs = NULL;
+ if (state & ERTS_PSFLG_ACTIVE_SYS)
+ stqs = NULL;
+ else {
+ alloc_qs:
+ stqs = proc_sys_task_queues_alloc();
+ stqs->qmask = 1 << prio;
+ stqs->ncount = 0;
+ stqs->q[PRIORITY_MAX] = NULL;
+ stqs->q[PRIORITY_HIGH] = NULL;
+ stqs->q[PRIORITY_NORMAL] = NULL;
+ stqs->q[PRIORITY_LOW] = NULL;
+ stqs->q[prio] = st;
+ }
+
+ if (!locked) {
+ locked = 1;
+ erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
+
+ state = erts_smp_atomic32_read_nob(&p->state);
+ if (state & fail_state) {
+ *fail_state_p = (state & fail_state);
+ free_stqs = stqs;
+ res = 0;
+ goto cleanup;
+ }
+ }
+
+ if (!p->sys_task_qs) {
+ if (stqs)
+ p->sys_task_qs = stqs;
+ else
+ goto alloc_qs;
+ }
+ else {
+ free_stqs = stqs;
+ stqs = p->sys_task_qs;
+ if (!stqs->q[prio]) {
+ stqs->q[prio] = st;
+ stqs->qmask |= 1 << prio;
+ }
+ else {
+ st->next = stqs->q[prio];
+ st->prev = stqs->q[prio]->prev;
+ st->next->prev = st;
+ st->prev->next = st;
+ ASSERT(stqs->qmask & (1 << prio));
+ }
+ }
+
+ if (ERTS_PSFLGS_GET_ACT_PRIO(state) > prio) {
+ erts_aint32_t n, a, e;
+ /* Need to elevate actual prio */
+
+ a = state;
+ do {
+ if (ERTS_PSFLGS_GET_ACT_PRIO(a) <= prio) {
+ n = a;
+ break;
+ }
+ n = e = a;
+ n &= ~ERTS_PSFLGS_ACT_PRIO_MASK;
+ n |= (prio << ERTS_PSFLGS_ACT_PRIO_OFFSET);
+ a = erts_smp_atomic32_cmpxchg_nob(&p->state, n, e);
+ } while (a != e);
+ state = n;
+ }
+
+
+ a = state;
+ enq_prio = -1;
/* Status lock prevents out of order "runnable proc" trace msgs */
ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p));
- if (!prof_runnable_procs)
+ if (!prof_runnable_procs) {
erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ locked = 0;
+ }
ASSERT(!(state & ERTS_PSFLG_PROXY));
@@ -6475,7 +6824,10 @@ schedule_process_sys_task(Process *p, erts_aint32_t state, Process *proxy)
enqueue = ERTS_ENQUEUE_NOT;
n |= ERTS_PSFLG_ACTIVE_SYS;
- if (!(a & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS)))
+ if (!(a & (ERTS_PSFLG_RUNNING
+ | ERTS_PSFLG_RUNNING_SYS
+ | ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)))
enqueue = check_enqueue_in_prio_queue(p, &enq_prio, &n, a);
a = erts_smp_atomic32_cmpxchg_mb(&p->state, n, e);
if (a == e)
@@ -6488,36 +6840,31 @@ schedule_process_sys_task(Process *p, erts_aint32_t state, Process *proxy)
if (!(a & (ERTS_PSFLG_ACTIVE_SYS
| ERTS_PSFLG_RUNNING
- | ERTS_PSFLG_RUNNING_SYS))
+ | ERTS_PSFLG_RUNNING_SYS
+ | ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS))
&& (!(a & ERTS_PSFLG_ACTIVE) || (a & ERTS_PSFLG_SUSPENDED))) {
/* We activated a prevously inactive process */
profile_runnable_proc(p, am_active);
}
erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
- prof_runnable_procs = 0;
+ locked = 0;
}
- if (enqueue != ERTS_ENQUEUE_NOT) {
- Process *sched_p;
- if (enqueue > 0)
- sched_p = p;
- else {
- sched_p = make_proxy_proc(proxy, p, enq_prio);
- proxy = NULL;
- }
- add2runq(sched_p, n, enq_prio);
- }
+ add2runq(enqueue, enq_prio, p, n, NULL);
cleanup:
- if (prof_runnable_procs)
+ if (locked)
erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
- if (proxy)
- free_proxy_proc(proxy);
+ if (free_stqs)
+ proc_sys_task_queues_free(free_stqs);
ERTS_SMP_LC_ASSERT(!(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p)));
+
+ return res;
}
static ERTS_INLINE int
@@ -6535,11 +6882,16 @@ suspend_process(Process *c_p, Process *p)
if (c_p == p) {
state = erts_smp_atomic32_read_bor_relb(&p->state,
ERTS_PSFLG_SUSPENDED);
- ASSERT(state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS));
+ ASSERT(state & (ERTS_PSFLG_RUNNING
+ | ERTS_PSFLG_RUNNING_SYS
+ | ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS));
suspended = (state & ERTS_PSFLG_SUSPENDED) ? -1: 1;
}
else {
- while (!(state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_EXITING))) {
+ while (!(state & (ERTS_PSFLG_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_EXITING))) {
erts_aint32_t n, e;
n = e = state;
@@ -6565,8 +6917,11 @@ suspend_process(Process *c_p, Process *p)
if ((state & (ERTS_PSFLG_ACTIVE
| ERTS_PSFLG_ACTIVE_SYS
+ | ERTS_PSFLG_DIRTY_ACTIVE_SYS
| ERTS_PSFLG_RUNNING
| ERTS_PSFLG_RUNNING_SYS
+ | ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS
| ERTS_PSFLG_SUSPENDED)) == ERTS_PSFLG_ACTIVE) {
/* We made process inactive */
profile_runnable_proc(p, am_inactive);
@@ -6600,23 +6955,7 @@ resume_process(Process *p, ErtsProcLocks locks)
&state,
&enq_prio,
locks);
- if (enqueue)
- add2runq(enqueue > 0 ? p : make_proxy_proc(NULL, p, enq_prio),
- state,
- enq_prio);
-}
-
-int
-erts_get_max_no_executing_schedulers(void)
-{
-#ifdef ERTS_SMP
- if (erts_smp_atomic32_read_nob(&schdlr_sspnd.changing))
- return (int) erts_no_schedulers;
- ERTS_THR_MEMORY_BARRIER;
- return (int) erts_smp_atomic32_read_nob(&schdlr_sspnd.active);
-#else
- return 1;
-#endif
+ add2runq(enqueue, enq_prio, p, state, NULL);
}
#ifdef ERTS_SMP
@@ -6721,42 +7060,91 @@ sched_set_suspended_sleeptype(ErtsSchedulerSleepInfo *ssi)
}
}
-#ifdef ERTS_DIRTY_SCHEDULERS
+static void
+init_scheduler_suspend(void)
+{
+ erts_smp_mtx_init(&schdlr_sspnd.mtx, "schdlr_sspnd");
+ schdlr_sspnd.online = ERTS_SCHDLR_SSPND_MAKE_NSCHEDS_VAL(1, 0, 0);
+ schdlr_sspnd.curr_online = ERTS_SCHDLR_SSPND_MAKE_NSCHEDS_VAL(1, 0, 0);
+ schdlr_sspnd.active = ERTS_SCHDLR_SSPND_MAKE_NSCHEDS_VAL(1, 0, 0);
+ erts_smp_atomic32_init_nob(&schdlr_sspnd.changing, 0);
+ schdlr_sspnd.chngq = NULL;
+ schdlr_sspnd.changer = am_false;
+ schdlr_sspnd.nmsb.ongoing = 0;
+ schdlr_sspnd.nmsb.blckrs = NULL;
+ schdlr_sspnd.nmsb.chngq = NULL;
+ schdlr_sspnd.msb.ongoing = 0;
+ schdlr_sspnd.msb.blckrs = NULL;
+ schdlr_sspnd.msb.chngq = NULL;
+}
+
+typedef struct {
+ struct {
+ Eterm chngr;
+ Eterm nxt;
+ } onln;
+ struct {
+ ErtsProcList *chngrs;
+ } msb;
+} ErtsSchdlrSspndResume;
+
+static void
+schdlr_sspnd_resume_proc(ErtsSchedType sched_type, Eterm pid)
+{
+ Process *p;
+ p = erts_pid2proc_opt(NULL, 0, pid, ERTS_PROC_LOCK_STATUS,
+ (sched_type != ERTS_SCHED_NORMAL
+ ? ERTS_P2P_FLG_INC_REFC
+ : 0));
+ if (p) {
+ resume_process(p, ERTS_PROC_LOCK_STATUS);
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ if (sched_type != ERTS_SCHED_NORMAL)
+ erts_proc_dec_refc(p);
+ }
+}
+
+static ERTS_INLINE void
+schdlr_sspnd_resume_procs(ErtsSchedType sched_type,
+ ErtsSchdlrSspndResume *resume)
+{
+ if (is_internal_pid(resume->onln.chngr)) {
+ schdlr_sspnd_resume_proc(sched_type,
+ resume->onln.chngr);
+ resume->onln.chngr = NIL;
+ }
+ if (is_internal_pid(resume->onln.nxt)) {
+ schdlr_sspnd_resume_proc(sched_type,
+ resume->onln.nxt);
+ resume->onln.nxt = NIL;
+ }
+ while (resume->msb.chngrs) {
+ ErtsProcList *plp = resume->msb.chngrs;
+ resume->msb.chngrs = plp->next;
+ schdlr_sspnd_resume_proc(sched_type,
+ plp->pid);
+ proclist_destroy(plp);
+ }
+}
static void
suspend_scheduler(ErtsSchedulerData *esdp)
{
erts_aint32_t flgs;
erts_aint32_t changing;
-#ifdef ERTS_DIRTY_SCHEDULERS
- long no = (long) (ERTS_SCHEDULER_IS_DIRTY(esdp)
- ? ERTS_DIRTY_SCHEDULER_NO(esdp)
- : esdp->no);
-#else
- long no = (long) esdp->no;
-#endif
+ long no;
ErtsSchedulerSleepInfo *ssi = esdp->ssi;
- long active_schedulers;
int curr_online = 1;
- int wake = 0;
+ ErtsSchdlrSspndResume resume = {{NIL, NIL}, {NULL}};
erts_aint32_t aux_work;
int thr_prgr_active = 1;
ErtsStuckBoundProcesses sbp = {NULL, NULL};
- int* ss_onlinep;
- int* ss_curr_onlinep;
- int* ss_wait_curr_onlinep;
- long* ss_wait_activep;
- long ss_wait_active_target;
- erts_smp_atomic32_t* ss_changingp;
- erts_smp_atomic32_t* ss_activep;
+ ErtsSchedType sched_type;
+ erts_aint32_t online_flag;
/*
* Schedulers may be suspended in two different ways:
* - A scheduler may be suspended since it is not online.
- * All schedulers with scheduler ids greater than
- * schdlr_sspnd.online are suspended; same for dirty
- * schedulers and schdlr_sspnd.dirty_cpu_online and
- * schdlr_sspnd.dirty_io_online.
* - Multi scheduling is blocked. All schedulers except the
* scheduler with scheduler id 1 are suspended, and all
* dirty CPU and dirty I/O schedulers are suspended.
@@ -6764,27 +7152,35 @@ suspend_scheduler(ErtsSchedulerData *esdp)
* Regardless of why a scheduler is suspended, it ends up here.
*/
- ASSERT(ERTS_SCHEDULER_IS_DIRTY(esdp) || no != 1);
-
#ifdef ERTS_DIRTY_SCHEDULERS
if (ERTS_SCHEDULER_IS_DIRTY(esdp)) {
- if (erts_smp_mtx_trylock(&schdlr_sspnd.mtx) == EBUSY) {
- erts_smp_runq_unlock(esdp->run_queue);
- erts_smp_mtx_lock(&schdlr_sspnd.mtx);
- erts_smp_runq_lock(esdp->run_queue);
+ no = ERTS_DIRTY_SCHEDULER_NO(esdp);
+ if (ERTS_RUNQ_IS_DIRTY_CPU_RUNQ(esdp->run_queue)) {
+ online_flag = ERTS_SCHDLR_SSPND_CHNG_DCPU_ONLN;
+ sched_type = ERTS_SCHED_DIRTY_CPU;
}
- if (ongoing_multi_scheduling_block())
- evacuate_run_queue(esdp->run_queue, &sbp);
- } else
+ else {
+ online_flag = 0;
+ sched_type = ERTS_SCHED_DIRTY_IO;
+ }
+ }
+ else
#endif
+ {
+ online_flag = ERTS_SCHDLR_SSPND_CHNG_ONLN;
+ no = esdp->no;
+ sched_type = ERTS_SCHED_NORMAL;
+ }
+
+ ASSERT(sched_type != ERTS_SCHED_NORMAL || no != 1);
+
+ if (sched_type != ERTS_SCHED_NORMAL)
+ erts_smp_runq_unlock(esdp->run_queue);
+ else {
evacuate_run_queue(esdp->run_queue, &sbp);
- erts_smp_runq_unlock(esdp->run_queue);
+ erts_smp_runq_unlock(esdp->run_queue);
-#ifdef ERTS_DIRTY_SCHEDULERS
- if (!ERTS_SCHEDULER_IS_DIRTY(esdp))
-#endif
- {
erts_sched_check_cpu_bind_prep_suspend(esdp);
if (erts_system_profile_flags.scheduler)
@@ -6792,334 +7188,138 @@ suspend_scheduler(ErtsSchedulerData *esdp)
sched_wall_time_change(esdp, 0);
- erts_smp_mtx_lock(&schdlr_sspnd.mtx);
}
+ erts_smp_mtx_lock(&schdlr_sspnd.mtx);
flgs = sched_prep_spin_suspended(ssi, ERTS_SSI_FLG_SUSPENDED);
if (flgs & ERTS_SSI_FLG_SUSPENDED) {
-#ifdef ERTS_DIRTY_SCHEDULERS
- if (ERTS_SCHEDULER_IS_DIRTY(esdp)) {
- if (ERTS_RUNQ_IS_DIRTY_CPU_RUNQ(esdp->run_queue)) {
- active_schedulers = erts_smp_atomic32_dec_read_nob(&schdlr_sspnd.dirty_cpu_active);
- ASSERT(active_schedulers >= 0);
- changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_changing);
- ss_onlinep = &schdlr_sspnd.dirty_cpu_online;
- ss_curr_onlinep = &schdlr_sspnd.dirty_cpu_curr_online;
- ss_wait_curr_onlinep = &schdlr_sspnd.dirty_cpu_wait_curr_online;
- ss_changingp = &schdlr_sspnd.dirty_cpu_changing;
- ss_wait_activep = &schdlr_sspnd.msb.dirty_cpu_wait_active;
- ss_activep = &schdlr_sspnd.dirty_cpu_active;
- } else {
- active_schedulers = erts_smp_atomic32_dec_read_nob(&schdlr_sspnd.dirty_io_active);
- ASSERT(active_schedulers >= 0);
- changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_io_changing);
- ss_onlinep = &schdlr_sspnd.dirty_io_online;
- ss_curr_onlinep = &schdlr_sspnd.dirty_io_curr_online;
- ss_wait_curr_onlinep = &schdlr_sspnd.dirty_io_wait_curr_online;
- ss_changingp = &schdlr_sspnd.dirty_io_changing;
- ss_wait_activep = &schdlr_sspnd.msb.dirty_io_wait_active;
- ss_activep = &schdlr_sspnd.dirty_io_active;
- }
- ss_wait_active_target = 0;
- }
- else
-#endif
- {
- active_schedulers = erts_smp_atomic32_dec_read_nob(&schdlr_sspnd.active);
- ASSERT(active_schedulers >= 1);
- changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing);
- ss_onlinep = &schdlr_sspnd.online;
- ss_curr_onlinep = &schdlr_sspnd.curr_online;
- ss_wait_curr_onlinep = &schdlr_sspnd.wait_curr_online;
- ss_changingp = &schdlr_sspnd.changing;
- ss_wait_activep = &schdlr_sspnd.msb.wait_active;
- ss_activep = &schdlr_sspnd.active;
- ss_wait_active_target = 1;
- }
- if (changing & ERTS_SCHDLR_SSPND_CHNG_MSB) {
- if (active_schedulers == *ss_wait_activep)
- wake = 1;
- if (active_schedulers == ss_wait_active_target) {
- changing = erts_smp_atomic32_read_band_nob(ss_changingp,
- ~ERTS_SCHDLR_SSPND_CHNG_MSB);
- changing &= ~ERTS_SCHDLR_SSPND_CHNG_MSB;
- }
- }
-
- while (1) {
- if (changing & ERTS_SCHDLR_SSPND_CHNG_ONLN) {
- int changed = 0;
- if (no > *ss_onlinep && curr_online) {
- (*ss_curr_onlinep)--;
- curr_online = 0;
- changed = 1;
- }
- else if (no <= *ss_onlinep && !curr_online) {
- (*ss_curr_onlinep)++;
- curr_online = 1;
- changed = 1;
- }
- if (changed
- && *ss_curr_onlinep == *ss_wait_curr_onlinep)
- wake = 1;
- if (*ss_onlinep == *ss_curr_onlinep) {
- changing = erts_smp_atomic32_read_band_nob(ss_changingp,
- ~ERTS_SCHDLR_SSPND_CHNG_ONLN);
- changing &= ~ERTS_SCHDLR_SSPND_CHNG_ONLN;
- }
- }
-
- if (wake) {
- erts_smp_cnd_signal(&schdlr_sspnd.cnd);
- wake = 0;
- }
+ schdlr_sspnd_dec_nscheds(&schdlr_sspnd.active, sched_type);
- if (curr_online && !ongoing_multi_scheduling_block()) {
- flgs = erts_smp_atomic32_read_acqb(&ssi->flags);
- if (!(flgs & ERTS_SSI_FLG_SUSPENDED))
- break;
- }
- erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
+ ASSERT(schdlr_sspnd_get_nscheds(&schdlr_sspnd.active,
+ ERTS_SCHED_NORMAL) >= 1);
- while (1) {
- erts_aint32_t qmask;
- erts_aint32_t flgs;
+ changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing);
- qmask = (ERTS_RUNQ_FLGS_GET(esdp->run_queue)
- & ERTS_RUNQ_FLGS_QMASK);
- aux_work = erts_atomic32_read_acqb(&ssi->aux_work);
- if (aux_work|qmask) {
- if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) {
- if (!thr_prgr_active) {
- erts_thr_progress_active(esdp, thr_prgr_active = 1);
- sched_wall_time_change(esdp, 1);
- }
- if (aux_work)
- aux_work = handle_aux_work(&esdp->aux_work_data,
- aux_work,
- 1);
+ while (1) {
- if (aux_work && erts_thr_progress_update(esdp))
- erts_thr_progress_leader_update(esdp);
+ if (changing & (ERTS_SCHDLR_SSPND_CHNG_NMSB
+ | ERTS_SCHDLR_SSPND_CHNG_MSB)) {
+ int i = 0;
+ ErtsMultiSchedulingBlock *msb[3] = {0};
+ if (changing & ERTS_SCHDLR_SSPND_CHNG_NMSB)
+ msb[i++] = &schdlr_sspnd.nmsb;
+ if (changing & ERTS_SCHDLR_SSPND_CHNG_MSB)
+ msb[i++] = &schdlr_sspnd.msb;
+
+ for (i = 0; msb[i]; i++) {
+ erts_aint32_t clr_flg = 0;
+
+ if (msb[i] == &schdlr_sspnd.nmsb
+ && schdlr_sspnd_get_nscheds(&schdlr_sspnd.active,
+ ERTS_SCHED_NORMAL) == 1) {
+ clr_flg = ERTS_SCHDLR_SSPND_CHNG_NMSB;
}
- if (qmask) {
-#ifdef ERTS_DIRTY_SCHEDULERS
- if (ERTS_SCHEDULER_IS_DIRTY(esdp)) {
- erts_smp_mtx_lock(&schdlr_sspnd.mtx);
- erts_smp_runq_lock(esdp->run_queue);
- if (ongoing_multi_scheduling_block())
- evacuate_run_queue(esdp->run_queue, &sbp);
- erts_smp_runq_unlock(esdp->run_queue);
- erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
- } else
-#endif
- {
- erts_smp_runq_lock(esdp->run_queue);
- evacuate_run_queue(esdp->run_queue, &sbp);
- erts_smp_runq_unlock(esdp->run_queue);
- }
+ else if (schdlr_sspnd.active
+ == ERTS_SCHDLR_SSPND_MAKE_NSCHEDS_VAL(1, 0, 0)) {
+ clr_flg = ERTS_SCHDLR_SSPND_CHNG_MSB;
}
- }
- if (!aux_work) {
-#ifdef ERTS_DIRTY_SCHEDULERS
- if (!ERTS_SCHEDULER_IS_DIRTY(esdp))
-#endif
- {
- if (thr_prgr_active) {
- erts_thr_progress_active(esdp, thr_prgr_active = 0);
- sched_wall_time_change(esdp, 0);
- }
- erts_thr_progress_prepare_wait(esdp);
+ if (clr_flg) {
+ ErtsProcList *plp, *end_plp;
+ changing = erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing,
+ ~clr_flg);
+ changing &= ~clr_flg;
+ (void) erts_proclist_fetch(&msb[i]->chngq, &end_plp);
+ /* resume processes that initiated the multi scheduling block... */
+ plp = msb[i]->chngq;
+ if (plp) {
+ ASSERT(end_plp);
+ ASSERT(msb[i]->ongoing);
+ do {
+ erts_proclist_store_last(&msb[i]->blckrs,
+ proclist_copy(plp));
+ plp = plp->next;
+ } while (plp);
+ end_plp->next = resume.msb.chngrs;
+ resume.msb.chngrs = msb[i]->chngq;
+ msb[i]->chngq = NULL;
+ }
}
- flgs = sched_spin_suspended(ssi,
- ERTS_SCHED_SUSPEND_SLEEP_SPINCOUNT);
- if (flgs == (ERTS_SSI_FLG_SLEEPING
- | ERTS_SSI_FLG_WAITING
- | ERTS_SSI_FLG_SUSPENDED)) {
- flgs = sched_set_suspended_sleeptype(ssi);
- if (flgs == (ERTS_SSI_FLG_SLEEPING
- | ERTS_SSI_FLG_TSE_SLEEPING
- | ERTS_SSI_FLG_WAITING
- | ERTS_SSI_FLG_SUSPENDED)) {
- int res;
-
- do {
- res = erts_tse_twait(ssi->event, -1);
- } while (res == EINTR);
- }
- }
-#ifdef ERTS_DIRTY_SCHEDULERS
- if (!ERTS_SCHEDULER_IS_DIRTY(esdp))
-#endif
- erts_thr_progress_finalize_wait(esdp);
}
-
- flgs = sched_prep_spin_suspended(ssi, (ERTS_SSI_FLG_WAITING
- | ERTS_SSI_FLG_SUSPENDED));
- if (!(flgs & ERTS_SSI_FLG_SUSPENDED))
- break;
- changing = erts_smp_atomic32_read_nob(ss_changingp);
- if (changing & ~ERTS_SCHDLR_SSPND_CHNG_WAITER)
- break;
- }
-
- erts_smp_mtx_lock(&schdlr_sspnd.mtx);
- changing = erts_smp_atomic32_read_nob(ss_changingp);
- }
-
- active_schedulers = erts_smp_atomic32_inc_read_nob(ss_activep);
- changing = erts_smp_atomic32_read_nob(ss_changingp);
- if ((changing & ERTS_SCHDLR_SSPND_CHNG_MSB)
- && *ss_onlinep == active_schedulers) {
- erts_smp_atomic32_read_band_nob(ss_changingp,
- ~ERTS_SCHDLR_SSPND_CHNG_MSB);
- }
-
-#ifdef ERTS_DIRTY_SCHEDULERS
- if (!ERTS_SCHEDULER_IS_DIRTY(esdp))
-#endif
- ASSERT(no <= *ss_onlinep);
- ASSERT(!ongoing_multi_scheduling_block());
-
- }
-
- erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
-
- ASSERT(curr_online);
-
-#ifdef ERTS_DIRTY_SCHEDULERS
- if (!ERTS_SCHEDULER_IS_DIRTY(esdp))
-#endif
- {
- if (erts_system_profile_flags.scheduler)
- profile_scheduler(make_small(esdp->no), am_active);
-
- if (!thr_prgr_active) {
- erts_thr_progress_active(esdp, thr_prgr_active = 1);
- sched_wall_time_change(esdp, 1);
- }
- }
-
- if (!ERTS_SCHEDULER_IS_DIRTY(esdp))
- (void) erts_get_monotonic_time(esdp);
- erts_smp_runq_lock(esdp->run_queue);
- non_empty_runq(esdp->run_queue);
-
-#ifdef ERTS_DIRTY_SCHEDULERS
- if (!ERTS_SCHEDULER_IS_DIRTY(esdp))
-#endif
- {
- schedule_bound_processes(esdp->run_queue, &sbp);
-
- erts_sched_check_cpu_bind_post_suspend(esdp);
- }
-}
-
-#else /* !ERTS_DIRTY_SCHEDULERS */
-
-static void
-suspend_scheduler(ErtsSchedulerData *esdp)
-{
- erts_aint32_t flgs;
- erts_aint32_t changing;
- long no = (long) esdp->no;
- ErtsSchedulerSleepInfo *ssi = esdp->ssi;
- long active_schedulers;
- int curr_online = 1;
- int wake = 0;
- erts_aint32_t aux_work;
- int thr_prgr_active = 1;
- ErtsStuckBoundProcesses sbp = {NULL, NULL};
-
- /*
- * Schedulers may be suspended in two different ways:
- * - A scheduler may be suspended since it is not online.
- * All schedulers with scheduler ids greater than
- * schdlr_sspnd.online are suspended.
- * - Multi scheduling is blocked. All schedulers except the
- * scheduler with scheduler id 1 are suspended.
- *
- * Regardless of why a scheduler is suspended, it ends up here.
- */
-
- ASSERT(no != 1);
-
- evacuate_run_queue(esdp->run_queue, &sbp);
-
- erts_smp_runq_unlock(esdp->run_queue);
-
- erts_sched_check_cpu_bind_prep_suspend(esdp);
-
- if (erts_system_profile_flags.scheduler)
- profile_scheduler(make_small(esdp->no), am_inactive);
-
- sched_wall_time_change(esdp, 0);
-
- erts_smp_mtx_lock(&schdlr_sspnd.mtx);
-
- flgs = sched_prep_spin_suspended(ssi, ERTS_SSI_FLG_SUSPENDED);
- if (flgs & ERTS_SSI_FLG_SUSPENDED) {
-
- active_schedulers = erts_smp_atomic32_dec_read_nob(&schdlr_sspnd.active);
- ASSERT(active_schedulers >= 1);
- changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing);
- if (changing & ERTS_SCHDLR_SSPND_CHNG_MSB) {
- if (active_schedulers == schdlr_sspnd.msb.wait_active)
- wake = 1;
- if (active_schedulers == 1) {
- changing = erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing,
- ~ERTS_SCHDLR_SSPND_CHNG_MSB);
- changing &= ~ERTS_SCHDLR_SSPND_CHNG_MSB;
}
- }
- while (1) {
- if (changing & ERTS_SCHDLR_SSPND_CHNG_ONLN) {
+ if (changing & online_flag) {
int changed = 0;
- if (no > schdlr_sspnd.online && curr_online) {
- schdlr_sspnd.curr_online--;
+ Uint32 st_online;
+
+ st_online = schdlr_sspnd_get_nscheds(&schdlr_sspnd.online,
+ sched_type);
+ if (no > st_online && curr_online) {
+ schdlr_sspnd_dec_nscheds(&schdlr_sspnd.curr_online,
+ sched_type);
curr_online = 0;
changed = 1;
}
- else if (no <= schdlr_sspnd.online && !curr_online) {
- schdlr_sspnd.curr_online++;
+ else if (no <= st_online && !curr_online) {
+ schdlr_sspnd_inc_nscheds(&schdlr_sspnd.curr_online,
+ sched_type);
curr_online = 1;
changed = 1;
}
if (changed
- && schdlr_sspnd.curr_online == schdlr_sspnd.wait_curr_online)
- wake = 1;
- if (schdlr_sspnd.online == schdlr_sspnd.curr_online) {
+ && (schdlr_sspnd_get_nscheds(&schdlr_sspnd.online,
+ sched_type)
+ == schdlr_sspnd_get_nscheds(&schdlr_sspnd.curr_online,
+ sched_type))) {
+ ErtsProcList *plp;
changing = erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing,
- ~ERTS_SCHDLR_SSPND_CHNG_ONLN);
- changing &= ~ERTS_SCHDLR_SSPND_CHNG_ONLN;
+ ~online_flag);
+ changing &= ~online_flag;
+ if (sched_type == ERTS_SCHED_NORMAL) {
+ ASSERT(is_internal_pid(schdlr_sspnd.changer)
+ || schdlr_sspnd.changer == am_init);
+ /* resume process that initiated this change... */
+ resume.onln.chngr = schdlr_sspnd.changer;
+ plp = erts_proclist_peek_first(schdlr_sspnd.chngq);
+ if (!plp)
+ schdlr_sspnd.changer = am_false;
+ else {
+ schdlr_sspnd.changer = am_true; /* change right in transit */
+ /* resume process that is queued for next change... */
+ resume.onln.nxt = plp->pid;
+ ASSERT(is_internal_pid(resume.onln.nxt));
+ }
+ }
}
}
- if (wake) {
- erts_smp_cnd_signal(&schdlr_sspnd.cnd);
- wake = 0;
- }
-
- if (curr_online && !ongoing_multi_scheduling_block()) {
+ if (curr_online
+ && (sched_type == ERTS_SCHED_NORMAL
+ ? !(schdlr_sspnd.msb.ongoing|schdlr_sspnd.nmsb.ongoing)
+ : !schdlr_sspnd.msb.ongoing)) {
flgs = erts_smp_atomic32_read_acqb(&ssi->flags);
if (!(flgs & ERTS_SSI_FLG_SUSPENDED))
break;
}
erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
+ schdlr_sspnd_resume_procs(sched_type, &resume);
+
while (1) {
ErtsMonotonicTime current_time;
- erts_aint32_t qmask;
erts_aint32_t flgs;
- qmask = (ERTS_RUNQ_FLGS_GET(esdp->run_queue)
- & ERTS_RUNQ_FLGS_QMASK);
- aux_work = erts_atomic32_read_acqb(&ssi->aux_work);
- if (aux_work|qmask) {
- if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) {
+ if (sched_type != ERTS_SCHED_NORMAL)
+ aux_work = 0;
+ else {
+ erts_aint32_t qmask;
+ qmask = (ERTS_RUNQ_FLGS_GET(esdp->run_queue)
+ & ERTS_RUNQ_FLGS_QMASK);
+
+ aux_work = erts_atomic32_read_acqb(&ssi->aux_work);
+
+ if (aux_work|qmask) {
if (!thr_prgr_active) {
erts_thr_progress_active(esdp, thr_prgr_active = 1);
sched_wall_time_change(esdp, 1);
@@ -7128,45 +7328,53 @@ suspend_scheduler(ErtsSchedulerData *esdp)
aux_work = handle_aux_work(&esdp->aux_work_data,
aux_work,
1);
+
if (aux_work && erts_thr_progress_update(esdp))
erts_thr_progress_leader_update(esdp);
+ if (qmask) {
+ erts_smp_runq_lock(esdp->run_queue);
+ evacuate_run_queue(esdp->run_queue, &sbp);
+ erts_smp_runq_unlock(esdp->run_queue);
+ }
}
- if (qmask) {
- erts_smp_runq_lock(esdp->run_queue);
- evacuate_run_queue(esdp->run_queue, &sbp);
- erts_smp_runq_unlock(esdp->run_queue);
- }
+
}
if (aux_work) {
- if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) {
- current_time = erts_get_monotonic_time(esdp);
- if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) {
- if (!thr_prgr_active) {
- erts_thr_progress_active(esdp, thr_prgr_active = 1);
- sched_wall_time_change(esdp, 1);
- }
- erts_bump_timers(esdp->timer_wheel, current_time);
+ ASSERT(sched_type == ERTS_SCHED_NORMAL);
+ current_time = erts_get_monotonic_time(esdp);
+ if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) {
+ if (!thr_prgr_active) {
+ erts_thr_progress_active(esdp, thr_prgr_active = 1);
+ sched_wall_time_change(esdp, 1);
}
+ erts_bump_timers(esdp->timer_wheel, current_time);
}
}
else {
ErtsMonotonicTime timeout_time;
- int do_timeout = 0;
- if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) {
+ int do_timeout;
+
+ if (sched_type == ERTS_SCHED_NORMAL) {
timeout_time = erts_check_next_timeout_time(esdp);
current_time = erts_get_monotonic_time(esdp);
do_timeout = (current_time >= timeout_time);
- } else
+ }
+ else {
timeout_time = ERTS_MONOTONIC_TIME_MAX;
+ current_time = 0;
+ do_timeout = 0;
+ }
+
if (do_timeout) {
+ ASSERT(sched_type == ERTS_SCHED_NORMAL);
if (!thr_prgr_active) {
erts_thr_progress_active(esdp, thr_prgr_active = 1);
sched_wall_time_change(esdp, 1);
}
}
else {
- if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) {
+ if (sched_type == ERTS_SCHED_NORMAL) {
if (thr_prgr_active) {
erts_thr_progress_active(esdp, thr_prgr_active = 0);
sched_wall_time_change(esdp, 0);
@@ -7185,30 +7393,39 @@ suspend_scheduler(ErtsSchedulerData *esdp)
| ERTS_SSI_FLG_SUSPENDED)) {
int res;
- current_time = ERTS_SCHEDULER_IS_DIRTY(esdp) ? 0 :
- erts_get_monotonic_time(esdp);
+ if (sched_type == ERTS_SCHED_NORMAL)
+ current_time = erts_get_monotonic_time(esdp);
+ else
+ current_time = 0;
+
do {
Sint64 timeout;
if (current_time >= timeout_time)
break;
- if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) {
+ if (sched_type != ERTS_SCHED_NORMAL)
+ timeout = -1;
+ else
timeout = ERTS_MONOTONIC_TO_NSEC(timeout_time
- current_time
- 1) + 1;
- } else
- timeout = -1;
res = erts_tse_twait(ssi->event, timeout);
- current_time = ERTS_SCHEDULER_IS_DIRTY(esdp) ? 0 :
- erts_get_monotonic_time(esdp);
+
+ if (sched_type == ERTS_SCHED_NORMAL)
+ current_time = erts_get_monotonic_time(esdp);
+ else
+ current_time = 0;
+
} while (res == EINTR);
}
}
- if (!ERTS_SCHEDULER_IS_DIRTY(esdp))
+ if (sched_type == ERTS_SCHED_NORMAL)
erts_thr_progress_finalize_wait(esdp);
}
- if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && current_time >= timeout_time)
+ if (current_time >= timeout_time) {
+ ASSERT(sched_type == ERTS_SCHED_NORMAL);
erts_bump_timers(esdp->timer_wheel, current_time);
+ }
}
flgs = sched_prep_spin_suspended(ssi, (ERTS_SSI_FLG_WAITING
@@ -7216,7 +7433,7 @@ suspend_scheduler(ErtsSchedulerData *esdp)
if (!(flgs & ERTS_SSI_FLG_SUSPENDED))
break;
changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing);
- if (changing & ~ERTS_SCHDLR_SSPND_CHNG_WAITER)
+ if (changing)
break;
}
@@ -7224,612 +7441,507 @@ suspend_scheduler(ErtsSchedulerData *esdp)
changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing);
}
- active_schedulers = erts_smp_atomic32_inc_read_nob(&schdlr_sspnd.active);
+ schdlr_sspnd_inc_nscheds(&schdlr_sspnd.active, sched_type);
changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing);
- if ((changing & ERTS_SCHDLR_SSPND_CHNG_MSB)
- && schdlr_sspnd.online == active_schedulers) {
- erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing,
- ~ERTS_SCHDLR_SSPND_CHNG_MSB);
- }
-
- ASSERT(no <= schdlr_sspnd.online);
- ASSERT(!ongoing_multi_scheduling_block());
-
+ if (changing) {
+ if ((changing & ERTS_SCHDLR_SSPND_CHNG_MSB)
+ && !schdlr_sspnd.msb.ongoing
+ && schdlr_sspnd.online == schdlr_sspnd.active) {
+ erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing,
+ ~ERTS_SCHDLR_SSPND_CHNG_MSB);
+ }
+ if ((changing & ERTS_SCHDLR_SSPND_CHNG_NMSB)
+ && !schdlr_sspnd.nmsb.ongoing
+ && (schdlr_sspnd_get_nscheds(&schdlr_sspnd.online,
+ ERTS_SCHED_NORMAL)
+ == schdlr_sspnd_get_nscheds(&schdlr_sspnd.active,
+ ERTS_SCHED_NORMAL))) {
+ erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing,
+ ~ERTS_SCHDLR_SSPND_CHNG_NMSB);
+ }
+ }
+ ASSERT(no <= schdlr_sspnd_get_nscheds(&schdlr_sspnd.online, sched_type));
+ ASSERT((sched_type == ERTS_SCHED_NORMAL
+ ? !(schdlr_sspnd.msb.ongoing|schdlr_sspnd.nmsb.ongoing)
+ : !schdlr_sspnd.msb.ongoing));
}
erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
+ ASSERT(!resume.msb.chngrs);
+ schdlr_sspnd_resume_procs(sched_type, &resume);
+
ASSERT(curr_online);
- if (erts_system_profile_flags.scheduler)
- profile_scheduler(make_small(esdp->no), am_active);
+ if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) {
+ if (erts_system_profile_flags.scheduler)
+ profile_scheduler(make_small(esdp->no), am_active);
- if (!thr_prgr_active) {
- erts_thr_progress_active(esdp, thr_prgr_active = 1);
- sched_wall_time_change(esdp, 1);
+ if (!thr_prgr_active) {
+ erts_thr_progress_active(esdp, thr_prgr_active = 1);
+ sched_wall_time_change(esdp, 1);
+ }
}
+ if (sched_type == ERTS_SCHED_NORMAL)
+ (void) erts_get_monotonic_time(esdp);
erts_smp_runq_lock(esdp->run_queue);
non_empty_runq(esdp->run_queue);
- schedule_bound_processes(esdp->run_queue, &sbp);
+ if (sched_type == ERTS_SCHED_NORMAL) {
+ schedule_bound_processes(esdp->run_queue, &sbp);
- erts_sched_check_cpu_bind_post_suspend(esdp);
+ erts_sched_check_cpu_bind_post_suspend(esdp);
+ }
}
-#endif
-
-ErtsSchedSuspendResult
+void
erts_schedulers_state(Uint *total,
Uint *online,
Uint *active,
Uint *dirty_cpu,
Uint *dirty_cpu_online,
+ Uint *dirty_cpu_active,
Uint *dirty_io,
- int yield_allowed)
+ Uint *dirty_io_active)
{
- int res = ERTS_SCHDLR_SSPND_EINVAL;
- erts_aint32_t changing;
- erts_smp_mtx_lock(&schdlr_sspnd.mtx);
- changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing);
-#ifdef ERTS_DIRTY_SCHEDULERS
- changing |= (erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_changing)
- | erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_io_changing));
-#endif
- if (yield_allowed && (changing & ~ERTS_SCHDLR_SSPND_CHNG_WAITER))
- res = ERTS_SCHDLR_SSPND_YIELD_RESTART;
- else {
+ if (active || online || dirty_cpu_online
+ || dirty_cpu_active || dirty_io_active) {
+ erts_smp_mtx_lock(&schdlr_sspnd.mtx);
if (active)
- *active = schdlr_sspnd.online;
+ *active = schdlr_sspnd_get_nscheds(&schdlr_sspnd.active,
+ ERTS_SCHED_NORMAL);
if (online)
- *online = schdlr_sspnd.online;
- if (ongoing_multi_scheduling_block() && active)
- *active = 1;
-#ifdef ERTS_DIRTY_SCHEDULERS
+ *online = schdlr_sspnd_get_nscheds(&schdlr_sspnd.curr_online,
+ ERTS_SCHED_NORMAL);
+ if (dirty_cpu_active)
+ *dirty_cpu_active = schdlr_sspnd_get_nscheds(&schdlr_sspnd.active,
+ ERTS_SCHED_DIRTY_CPU);
if (dirty_cpu_online)
- *dirty_cpu_online = schdlr_sspnd.dirty_cpu_online;
-#endif
- res = ERTS_SCHDLR_SSPND_DONE;
+ *dirty_cpu_online = schdlr_sspnd_get_nscheds(&schdlr_sspnd.curr_online,
+ ERTS_SCHED_DIRTY_CPU);
+ if (dirty_io_active)
+ *dirty_io_active = schdlr_sspnd_get_nscheds(&schdlr_sspnd.active,
+ ERTS_SCHED_DIRTY_IO);
+ erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
}
- erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
+
if (total)
*total = erts_no_schedulers;
-#ifdef ERTS_DIRTY_SCHEDULERS
if (dirty_cpu)
*dirty_cpu = erts_no_dirty_cpu_schedulers;
if (dirty_io)
*dirty_io = erts_no_dirty_io_schedulers;
-#endif
- return res;
}
-#ifdef ERTS_DIRTY_SCHEDULERS
+static void
+abort_sched_onln_chng_waitq(Process *p)
+{
+ Eterm resume = NIL;
+
+ erts_smp_mtx_lock(&schdlr_sspnd.mtx);
+
+#ifdef DEBUG
+ {
+ int found_it = 0;
+ ErtsProcList *plp = erts_proclist_peek_first(schdlr_sspnd.chngq);
+ while (plp) {
+ if (erts_proclist_same(plp, p))
+ found_it++;
+ plp = erts_proclist_peek_next(schdlr_sspnd.chngq, plp);
+ }
+ ASSERT(found_it == !!(p->flags & F_SCHDLR_ONLN_WAITQ));
+ }
+#endif
+
+ if (p->flags & F_SCHDLR_ONLN_WAITQ) {
+ ErtsProcList *plp = NULL;
+
+ plp = erts_proclist_peek_first(schdlr_sspnd.chngq);
+ if (plp) {
+ if (erts_proclist_same(plp, p)
+ && schdlr_sspnd.changer == am_true) {
+ p->flags &= ~F_SCHDLR_ONLN_WAITQ;
+ /*
+ * Change right was in transit to us;
+ * transfer it to the next process by
+ * resuming it...
+ */
+ erts_proclist_remove(&schdlr_sspnd.chngq, plp);
+ proclist_destroy(plp);
+ plp = erts_proclist_peek_first(schdlr_sspnd.chngq);
+ if (plp)
+ resume = plp->pid;
+ else
+ schdlr_sspnd.changer = am_false;
+ }
+ else {
+ do {
+ if (erts_proclist_same(plp, p)) {
+ p->flags &= ~F_SCHDLR_ONLN_WAITQ;
+ erts_proclist_remove(&schdlr_sspnd.chngq, plp);
+ proclist_destroy(plp);
+ break;
+ }
+ plp = erts_proclist_peek_next(schdlr_sspnd.chngq, plp);
+ } while (plp);
+ }
+ }
+ }
+
+ erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
+
+ if (is_internal_pid(resume))
+ schdlr_sspnd_resume_proc(ERTS_SCHED_NORMAL, resume);
+}
ErtsSchedSuspendResult
erts_set_schedulers_online(Process *p,
ErtsProcLocks plocks,
Sint new_no,
- Sint *old_no
-#ifdef ERTS_DIRTY_SCHEDULERS
- , int dirty_only
-#endif
- )
+ Sint *old_no,
+ int dirty_only)
{
- ErtsSchedulerData *esdp;
- int ix, res = -1, no, have_unlocked_plocks, end_wait;
- erts_aint32_t changing = 0;
+ int resume_proc, ix, res = -1, no, have_unlocked_plocks;
+ erts_aint32_t changing = 0, change_flags;
+ int online, increase;
+ ErtsProcList *plp;
#ifdef ERTS_DIRTY_SCHEDULERS
- ErtsSchedulerSleepInfo* ssi;
- int dirty_no, change_dirty;
+ int dirty_no, change_dirty, dirty_online;
+#else
+ ASSERT(!dirty_only);
#endif
if (new_no < 1)
return ERTS_SCHDLR_SSPND_EINVAL;
-#ifdef ERTS_DIRTY_SCHEDULERS
else if (dirty_only && erts_no_dirty_cpu_schedulers < new_no)
return ERTS_SCHDLR_SSPND_EINVAL;
-#endif
else if (erts_no_schedulers < new_no)
return ERTS_SCHDLR_SSPND_EINVAL;
- esdp = ERTS_PROC_GET_SCHDATA(p);
- end_wait = 0;
+#ifdef ERTS_DIRTY_SCHEDULERS
+ if (dirty_only)
+ resume_proc = 0;
+ else
+#endif
+ {
+ resume_proc = 1;
+ /*
+ * If we suspend current process we need to suspend before
+ * requesting the change; otherwise, we got a resume/suspend
+ * race...
+ */
+ if (!(plocks & ERTS_PROC_LOCK_STATUS))
+ erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
+ suspend_process(p, p);
+ if (!(plocks & ERTS_PROC_LOCK_STATUS))
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ }
erts_smp_mtx_lock(&schdlr_sspnd.mtx);
+ change_flags = 0;
have_unlocked_plocks = 0;
no = (int) new_no;
#ifdef ERTS_DIRTY_SCHEDULERS
- ASSERT(schdlr_sspnd.dirty_cpu_online <= erts_no_dirty_cpu_schedulers);
+ if (!dirty_only)
+#endif
+ {
+ changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing);
+ if (changing & ERTS_SCHDLR_SSPND_CHNG_ONLN) {
+ enqueue_wait:
+ p->flags |= F_SCHDLR_ONLN_WAITQ;
+ plp = proclist_create(p);
+ erts_proclist_store_last(&schdlr_sspnd.chngq, plp);
+ resume_proc = 0;
+ res = ERTS_SCHDLR_SSPND_YIELD_RESTART;
+ goto done;
+ }
+ plp = erts_proclist_peek_first(schdlr_sspnd.chngq);
+ if (!plp) {
+ ASSERT(schdlr_sspnd.changer == am_false);
+ }
+ else {
+ ASSERT(schdlr_sspnd.changer == am_true);
+ if (!erts_proclist_same(plp, p))
+ goto enqueue_wait;
+ p->flags &= ~F_SCHDLR_ONLN_WAITQ;
+ erts_proclist_remove(&schdlr_sspnd.chngq, plp);
+ proclist_destroy(plp);
+ }
+ }
+
+ *old_no = online = schdlr_sspnd_get_nscheds(&schdlr_sspnd.online,
+ ERTS_SCHED_NORMAL);
+#ifndef ERTS_DIRTY_SCHEDULERS
+ if (no == online) {
+ res = ERTS_SCHDLR_SSPND_DONE;
+ goto done;
+ }
+#else /* ERTS_DIRTY_SCHEDULERS */
+ dirty_online = schdlr_sspnd_get_nscheds(&schdlr_sspnd.online,
+ ERTS_SCHED_DIRTY_CPU);
+ if (dirty_only)
+ *old_no = dirty_online;
+
+ ASSERT(dirty_online <= erts_no_dirty_cpu_schedulers);
+
if (dirty_only) {
- if (no > schdlr_sspnd.online) {
- erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
- return ERTS_SCHDLR_SSPND_EINVAL;
+ if (no > online) {
+ res = ERTS_SCHDLR_SSPND_EINVAL;
+ goto done;
}
dirty_no = no;
+ if (dirty_no == dirty_online) {
+ res = ERTS_SCHDLR_SSPND_DONE;
+ goto done;
+ }
+ change_dirty = 1;
} else {
/*
* Adjust the number of dirty CPU schedulers online relative to the
* adjustment made to the number of normal schedulers online.
*/
int total_pct = erts_no_dirty_cpu_schedulers*100/erts_no_schedulers;
- int onln_pct = no*total_pct/schdlr_sspnd.online;
- dirty_no = schdlr_sspnd.dirty_cpu_online*onln_pct/100;
+ int onln_pct = no*total_pct/online;
+ dirty_no = dirty_online*onln_pct/100;
if (dirty_no == 0)
dirty_no = 1;
ASSERT(dirty_no <= erts_no_dirty_cpu_schedulers);
- }
-#endif
- changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing);
-#ifdef ERTS_DIRTY_SCHEDULERS
- changing |= erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_changing);
-#endif
- if (changing) {
- res = ERTS_SCHDLR_SSPND_YIELD_RESTART;
- }
- else {
- int online = *old_no = schdlr_sspnd.online;
-#ifdef ERTS_DIRTY_SCHEDULERS
- int dirty_online = schdlr_sspnd.dirty_cpu_online;
- if (dirty_only) {
- *old_no = schdlr_sspnd.dirty_cpu_online;
- if (dirty_no == schdlr_sspnd.dirty_cpu_online) {
+ if (no != online)
+ change_dirty = (dirty_no != dirty_online);
+ else {
+ dirty_only = 1;
+ if (dirty_no == dirty_online) {
res = ERTS_SCHDLR_SSPND_DONE;
+ goto done;
}
change_dirty = 1;
- } else {
-#endif
- if (no == schdlr_sspnd.online) {
-#ifdef ERTS_DIRTY_SCHEDULERS
- dirty_only = 1;
- if (dirty_no == schdlr_sspnd.dirty_cpu_online)
-#endif
- res = ERTS_SCHDLR_SSPND_DONE;
-#ifdef ERTS_DIRTY_SCHEDULERS
- else
- change_dirty = 1;
-#endif
- }
-#ifdef ERTS_DIRTY_SCHEDULERS
- else
- change_dirty = (dirty_no != schdlr_sspnd.dirty_cpu_online);
}
-#endif
- if (res == -1)
- {
- int increase = (no > online);
-#ifdef ERTS_DIRTY_SCHEDULERS
- if (!dirty_only) {
-#endif
- ERTS_SCHDLR_SSPND_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_ONLN
- | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0);
- schdlr_sspnd.online = no;
-#ifdef ERTS_DIRTY_SCHEDULERS
- } else
- increase = (dirty_no > dirty_online);
- if (change_dirty) {
- ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_ONLN
- | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0);
- schdlr_sspnd.dirty_cpu_online = dirty_no;
- }
-#endif
- if (increase) {
- int ix;
-#ifdef ERTS_DIRTY_SCHEDULERS
- if (!dirty_only) {
-#endif
- schdlr_sspnd.wait_curr_online = no;
- if (ongoing_multi_scheduling_block()) {
- for (ix = online; ix < no; ix++)
- erts_sched_poke(ERTS_SCHED_SLEEP_INFO_IX(ix));
- }
- else {
- if (plocks) {
- have_unlocked_plocks = 1;
- erts_smp_proc_unlock(p, plocks);
- }
- change_no_used_runqs(no);
+ }
+ if (change_dirty) {
+ change_flags |= ERTS_SCHDLR_SSPND_CHNG_DCPU_ONLN;
+ schdlr_sspnd_set_nscheds(&schdlr_sspnd.online,
+ ERTS_SCHED_DIRTY_CPU,
+ dirty_no);
+ }
- for (ix = online; ix < no; ix++)
- resume_run_queue(ERTS_RUNQ_IX(ix));
+ if (dirty_only)
+ increase = (dirty_no > dirty_online);
+ else
+#endif /* ERTS_DIRTY_SCHEDULERS */
+ {
+ change_flags |= ERTS_SCHDLR_SSPND_CHNG_ONLN;
+ schdlr_sspnd_set_nscheds(&schdlr_sspnd.online,
+ ERTS_SCHED_NORMAL,
+ no);
+ increase = (no > online);
+ }
- for (ix = no; ix < erts_no_run_queues; ix++)
- suspend_run_queue(ERTS_RUNQ_IX(ix));
- }
+ erts_smp_atomic32_read_bor_nob(&schdlr_sspnd.changing, change_flags);
+
+ res = ERTS_SCHDLR_SSPND_DONE;
+ if (increase) {
+ int ix;
#ifdef ERTS_DIRTY_SCHEDULERS
+ if (change_dirty) {
+ ErtsSchedulerSleepInfo* ssi;
+ if (schdlr_sspnd.msb.ongoing) {
+ for (ix = dirty_online; ix < dirty_no; ix++) {
+ ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix);
+ erts_sched_poke(ssi);
}
- if (change_dirty) {
- schdlr_sspnd.dirty_cpu_wait_curr_online = dirty_no;
- ASSERT(schdlr_sspnd.dirty_cpu_curr_online !=
- schdlr_sspnd.dirty_cpu_wait_curr_online);
- if (ongoing_multi_scheduling_block()) {
- for (ix = dirty_online; ix < dirty_no; ix++) {
- ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix);
- erts_sched_poke(ssi);
- }
- } else {
- for (ix = dirty_online; ix < dirty_no; ix++) {
- ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix);
- scheduler_ssi_resume_wake(ssi);
- erts_smp_atomic32_read_band_nob(&ssi->flags,
- ~ERTS_SSI_FLG_SUSPENDED);
- }
- wake_dirty_schedulers(ERTS_DIRTY_CPU_RUNQ, 0);
- }
+ } else {
+ for (ix = dirty_online; ix < dirty_no; ix++) {
+ ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix);
+ scheduler_ssi_resume_wake(ssi);
}
-#endif
- res = ERTS_SCHDLR_SSPND_DONE;
}
- else /* if (no < online) */ {
-#ifdef ERTS_DIRTY_SCHEDULERS
- if (change_dirty) {
- schdlr_sspnd.dirty_cpu_wait_curr_online = dirty_no;
- ASSERT(schdlr_sspnd.dirty_cpu_curr_online !=
- schdlr_sspnd.dirty_cpu_wait_curr_online);
- if (ongoing_multi_scheduling_block()) {
- for (ix = dirty_no; ix < dirty_online; ix++) {
- ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix);
- erts_sched_poke(ssi);
- }
- } else {
- for (ix = dirty_no; ix < dirty_online; ix++) {
- ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix);
- erts_smp_atomic32_read_bor_nob(&ssi->flags,
- ERTS_SSI_FLG_SUSPENDED);
- }
- wake_dirty_schedulers(ERTS_DIRTY_CPU_RUNQ, 0);
- }
- }
- if (dirty_only) {
- res = ERTS_SCHDLR_SSPND_DONE;
- }
- else
+ }
+ if (!dirty_only)
#endif
- {
- if (p->scheduler_data->no <= no) {
- res = ERTS_SCHDLR_SSPND_DONE;
- schdlr_sspnd.wait_curr_online = no;
- }
- else {
- /*
- * Yield! Current process needs to migrate
- * before bif returns.
- */
- res = ERTS_SCHDLR_SSPND_YIELD_DONE;
- schdlr_sspnd.wait_curr_online = no+1;
- }
-
- if (ongoing_multi_scheduling_block()) {
- for (ix = no; ix < online; ix++)
- erts_sched_poke(ERTS_SCHED_SLEEP_INFO_IX(ix));
- }
- else {
- if (plocks) {
- have_unlocked_plocks = 1;
- erts_smp_proc_unlock(p, plocks);
- }
-
- change_no_used_runqs(no);
- for (ix = no; ix < erts_no_run_queues; ix++)
- suspend_run_queue(ERTS_RUNQ_IX(ix));
-
- for (ix = no; ix < online; ix++) {
- ErtsRunQueue *rq = ERTS_RUNQ_IX(ix);
- wake_scheduler(rq);
- }
- }
- }
- }
-
-#ifdef ERTS_DIRTY_SCHEDULERS
- if (change_dirty) {
- while (schdlr_sspnd.dirty_cpu_curr_online != schdlr_sspnd.dirty_cpu_wait_curr_online)
- erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx);
- ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER);
- erts_smp_atomic32_read_band_nob(&schdlr_sspnd.dirty_cpu_changing,
- ~ERTS_SCHDLR_SSPND_CHNG_WAITER);
+ {
+ if (schdlr_sspnd.msb.ongoing|schdlr_sspnd.nmsb.ongoing) {
+ for (ix = online; ix < no; ix++)
+ erts_sched_poke(ERTS_SCHED_SLEEP_INFO_IX(ix));
}
- if (!dirty_only)
-#endif
- {
- if (schdlr_sspnd.curr_online != schdlr_sspnd.wait_curr_online) {
- erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
- if (plocks && !have_unlocked_plocks) {
- have_unlocked_plocks = 1;
- erts_smp_proc_unlock(p, plocks);
- }
- erts_thr_progress_active(esdp, 0);
- erts_thr_progress_prepare_wait(esdp);
- end_wait = 1;
- erts_smp_mtx_lock(&schdlr_sspnd.mtx);
+ else {
+ if (plocks) {
+ have_unlocked_plocks = 1;
+ erts_smp_proc_unlock(p, plocks);
}
+ change_no_used_runqs(no);
- while (schdlr_sspnd.curr_online != schdlr_sspnd.wait_curr_online)
- erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx);
+ for (ix = online; ix < no; ix++)
+ resume_run_queue(ERTS_RUNQ_IX(ix));
- ASSERT(res != ERTS_SCHDLR_SSPND_DONE
- ? (ERTS_SCHDLR_SSPND_CHNG_WAITER
- & erts_smp_atomic32_read_nob(&schdlr_sspnd.changing))
- : (ERTS_SCHDLR_SSPND_CHNG_WAITER
- == erts_smp_atomic32_read_nob(&schdlr_sspnd.changing)));
- erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing,
- ~ERTS_SCHDLR_SSPND_CHNG_WAITER);
+ for (ix = no; ix < erts_no_run_queues; ix++)
+ suspend_run_queue(ERTS_RUNQ_IX(ix));
}
}
}
-
- erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
+ else /* if decrease */ {
#ifdef ERTS_DIRTY_SCHEDULERS
- ASSERT(schdlr_sspnd.dirty_cpu_online <= schdlr_sspnd.online);
- if (!dirty_only)
-#endif
- {
- if (end_wait) {
- erts_thr_progress_finalize_wait(esdp);
- erts_thr_progress_active(esdp, 1);
- }
- if (have_unlocked_plocks)
- erts_smp_proc_lock(p, plocks);
- }
-
- return res;
-}
-
-#else /* !ERTS_DIRTY_SCHEDULERS */
-
-ErtsSchedSuspendResult
-erts_set_schedulers_online(Process *p,
- ErtsProcLocks plocks,
- Sint new_no,
- Sint *old_no)
-{
- ErtsSchedulerData *esdp;
- int ix, res, no, have_unlocked_plocks, end_wait;
- erts_aint32_t changing;
-
- if (new_no < 1 || erts_no_schedulers < new_no)
- return ERTS_SCHDLR_SSPND_EINVAL;
-
- esdp = ERTS_PROC_GET_SCHDATA(p);
- end_wait = 0;
-
- erts_smp_mtx_lock(&schdlr_sspnd.mtx);
-
- have_unlocked_plocks = 0;
- no = (int) new_no;
-
- changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing);
- if (changing) {
- res = ERTS_SCHDLR_SSPND_YIELD_RESTART;
- }
- else {
- int online = *old_no = schdlr_sspnd.online;
- if (no == schdlr_sspnd.online) {
- res = ERTS_SCHDLR_SSPND_DONE;
- }
- else {
- ERTS_SCHDLR_SSPND_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_ONLN
- | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0);
- schdlr_sspnd.online = no;
- if (no > online) {
- int ix;
- schdlr_sspnd.wait_curr_online = no;
- if (ongoing_multi_scheduling_block()) {
- for (ix = online; ix < no; ix++)
- erts_sched_poke(ERTS_SCHED_SLEEP_INFO_IX(ix));
+ if (change_dirty) {
+ ErtsSchedulerSleepInfo* ssi;
+ if (schdlr_sspnd.msb.ongoing) {
+ for (ix = dirty_no; ix < dirty_online; ix++) {
+ ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix);
+ erts_sched_poke(ssi);
}
- else {
- if (plocks) {
- have_unlocked_plocks = 1;
- erts_smp_proc_unlock(p, plocks);
- }
- change_no_used_runqs(no);
-
- for (ix = online; ix < no; ix++)
- resume_run_queue(ERTS_RUNQ_IX(ix));
-
- for (ix = no; ix < erts_no_run_queues; ix++)
- suspend_run_queue(ERTS_RUNQ_IX(ix));
+ } else {
+ for (ix = dirty_no; ix < dirty_online; ix++) {
+ ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix);
+ erts_smp_atomic32_read_bor_nob(&ssi->flags,
+ ERTS_SSI_FLG_SUSPENDED);
}
- res = ERTS_SCHDLR_SSPND_DONE;
+ wake_dirty_schedulers(ERTS_DIRTY_CPU_RUNQ, 0);
}
- else /* if (no < online) */ {
- if (p->scheduler_data->no <= no) {
- res = ERTS_SCHDLR_SSPND_DONE;
- schdlr_sspnd.wait_curr_online = no;
- }
- else {
- /*
- * Yield! Current process needs to migrate
- * before bif returns.
- */
- res = ERTS_SCHDLR_SSPND_YIELD_DONE;
- schdlr_sspnd.wait_curr_online = no+1;
- }
-
- if (ongoing_multi_scheduling_block()) {
- for (ix = no; ix < online; ix++)
- erts_sched_poke(ERTS_SCHED_SLEEP_INFO_IX(ix));
- }
- else {
- if (plocks) {
- have_unlocked_plocks = 1;
- erts_smp_proc_unlock(p, plocks);
- }
-
- change_no_used_runqs(no);
- for (ix = no; ix < erts_no_run_queues; ix++)
- suspend_run_queue(ERTS_RUNQ_IX(ix));
-
- for (ix = no; ix < online; ix++) {
- ErtsRunQueue *rq = ERTS_RUNQ_IX(ix);
- wake_scheduler(rq);
- }
- }
+ }
+ if (!dirty_only)
+#endif
+ {
+ if (schdlr_sspnd.msb.ongoing|schdlr_sspnd.nmsb.ongoing) {
+ for (ix = no; ix < online; ix++)
+ erts_sched_poke(ERTS_SCHED_SLEEP_INFO_IX(ix));
}
-
- if (schdlr_sspnd.curr_online != schdlr_sspnd.wait_curr_online) {
- erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
- if (plocks && !have_unlocked_plocks) {
+ else {
+ if (plocks) {
have_unlocked_plocks = 1;
erts_smp_proc_unlock(p, plocks);
}
- erts_thr_progress_active(esdp, 0);
- erts_thr_progress_prepare_wait(esdp);
- end_wait = 1;
- erts_smp_mtx_lock(&schdlr_sspnd.mtx);
- }
-
- while (schdlr_sspnd.curr_online != schdlr_sspnd.wait_curr_online)
- erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx);
- ASSERT(res != ERTS_SCHDLR_SSPND_DONE
- ? (ERTS_SCHDLR_SSPND_CHNG_WAITER
- & erts_smp_atomic32_read_nob(&schdlr_sspnd.changing))
- : (ERTS_SCHDLR_SSPND_CHNG_WAITER
- == erts_smp_atomic32_read_nob(&schdlr_sspnd.changing)));
- erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing,
- ~ERTS_SCHDLR_SSPND_CHNG_WAITER);
+ change_no_used_runqs(no);
+ for (ix = no; ix < erts_no_run_queues; ix++)
+ suspend_run_queue(ERTS_RUNQ_IX(ix));
+ for (ix = no; ix < online; ix++) {
+ ErtsRunQueue *rq = ERTS_RUNQ_IX(ix);
+ wake_scheduler(rq);
+ }
+ }
}
}
- erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
- if (end_wait) {
- erts_thr_progress_finalize_wait(esdp);
- erts_thr_progress_active(esdp, 1);
+ if (change_flags & ERTS_SCHDLR_SSPND_CHNG_ONLN) {
+ /* Suspend and wait for requested change to complete... */
+ schdlr_sspnd.changer = p->common.id;
+ resume_proc = 0;
+ res = ERTS_SCHDLR_SSPND_YIELD_DONE;
}
+
+done:
+
+ ASSERT(schdlr_sspnd_get_nscheds(&schdlr_sspnd.online,
+ ERTS_SCHED_DIRTY_CPU)
+ <= schdlr_sspnd_get_nscheds(&schdlr_sspnd.online,
+ ERTS_SCHED_NORMAL));
+
+ erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
+
if (have_unlocked_plocks)
erts_smp_proc_lock(p, plocks);
+ if (resume_proc) {
+ if (!(plocks & ERTS_PROC_LOCK_STATUS))
+ erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
+ resume_process(p, plocks|ERTS_PROC_LOCK_STATUS);
+ if (!(plocks & ERTS_PROC_LOCK_STATUS))
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ }
+
return res;
}
-#endif
-
ErtsSchedSuspendResult
-erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all)
+erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int normal, int all)
{
- int ix, res, have_unlocked_plocks = 0, online;
- erts_aint32_t changing;
+ int resume_proc, ix, res, have_unlocked_plocks = 0;
ErtsProcList *plp;
#ifdef ERTS_DIRTY_SCHEDULERS
ErtsSchedulerSleepInfo* ssi;
#endif
+ ErtsMultiSchedulingBlock *msbp;
+ erts_aint32_t chng_flg;
+ int have_blckd_flg;
- erts_smp_mtx_lock(&schdlr_sspnd.mtx);
- changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing);
-#ifdef ERTS_DIRTY_SCHEDULERS
- changing |= (erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_changing)
- | erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_io_changing));
-#endif
- if (changing) {
- res = ERTS_SCHDLR_SSPND_YIELD_RESTART; /* Yield */
+ if (normal) {
+ chng_flg = ERTS_SCHDLR_SSPND_CHNG_NMSB;
+ have_blckd_flg = F_HAVE_BLCKD_NMSCHED;
+ msbp = &schdlr_sspnd.nmsb;
+ }
+ else {
+ chng_flg = ERTS_SCHDLR_SSPND_CHNG_MSB;
+ have_blckd_flg = F_HAVE_BLCKD_MSCHED;
+ msbp = &schdlr_sspnd.msb;
}
- else if (on) { /* ------ BLOCK ------ */
- if (schdlr_sspnd.msb.procs) {
+
+ /*
+ * If we suspend current process we need to suspend before
+ * requesting the change; otherwise, we got a resume/suspend
+ * race...
+ */
+ if (!on) {
+ /* We never suspend current process when unblocking... */
+ resume_proc = 0;
+ }
+ else {
+ resume_proc = 1;
+ if (!(plocks & ERTS_PROC_LOCK_STATUS))
+ erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
+ suspend_process(p, p);
+ if (!(plocks & ERTS_PROC_LOCK_STATUS))
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ }
+
+ erts_smp_mtx_lock(&schdlr_sspnd.mtx);
+ if (on) { /* ------ BLOCK ------ */
+ if (msbp->chngq) {
+ ASSERT(msbp->ongoing);
+ p->flags |= have_blckd_flg;
+ goto wait_until_msb;
+ }
+ else if (msbp->blckrs) {
+ ASSERT(msbp->ongoing);
plp = proclist_create(p);
- erts_proclist_store_last(&schdlr_sspnd.msb.procs, plp);
- p->flags |= F_HAVE_BLCKD_MSCHED;
- ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.active) == 1);
-#ifdef ERTS_DIRTY_SCHEDULERS
- ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_active) == 0);
- ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_io_active) == 0);
-#endif
- ASSERT(p->scheduler_data->no == 1);
- res = ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED;
- } else {
- int online = schdlr_sspnd.online;
- p->flags |= F_HAVE_BLCKD_MSCHED;
+ erts_proclist_store_last(&msbp->blckrs, plp);
+ p->flags |= have_blckd_flg;
+ ASSERT(normal
+ ? 1 == schdlr_sspnd_get_nscheds(&schdlr_sspnd.active, ERTS_SCHED_NORMAL)
+ : schdlr_sspnd.active == ERTS_SCHDLR_SSPND_MAKE_NSCHEDS_VAL(1, 0, 0));
+ ASSERT(erts_proc_sched_data(p)->no == 1);
+ if (schdlr_sspnd.msb.ongoing)
+ res = ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED;
+ else
+ res = ERTS_SCHDLR_SSPND_DONE_NMSCHED_BLOCKED;
+ }
+ else {
+ int online = (int) schdlr_sspnd_get_nscheds(&schdlr_sspnd.online,
+ ERTS_SCHED_NORMAL);
+ ASSERT(!msbp->ongoing);
+ p->flags |= have_blckd_flg;
if (plocks) {
have_unlocked_plocks = 1;
erts_smp_proc_unlock(p, plocks);
}
- ASSERT(!ongoing_multi_scheduling_block());
- schdlr_sspnd.msb.ongoing = 1;
- if (online == 1) {
- res = ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED;
- ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.active) == 1);
-#ifdef ERTS_DIRTY_SCHEDULERS
- ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_active) == 1);
- ASSERT(!(erts_smp_atomic32_read_nob(&ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(0)->flags)
- & ERTS_SSI_FLG_SUSPENDED));
- schdlr_sspnd.msb.dirty_cpu_wait_active = 0;
- ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_MSB
- | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0);
- ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(0);
- erts_smp_atomic32_read_bor_nob(&ssi->flags, ERTS_SSI_FLG_SUSPENDED);
- wake_dirty_schedulers(ERTS_DIRTY_CPU_RUNQ, 0);
- while (erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_active)
- != schdlr_sspnd.msb.dirty_cpu_wait_active)
- erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx);
- ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER);
-
- schdlr_sspnd.msb.dirty_io_wait_active = 0;
- ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_MSB
- | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0);
- for (ix = 0; ix < erts_no_dirty_io_schedulers; ix++) {
- ssi = ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(ix);
- erts_smp_atomic32_read_bor_nob(&ssi->flags,
- ERTS_SSI_FLG_SUSPENDED);
- }
- wake_dirty_schedulers(ERTS_DIRTY_IO_RUNQ, 0);
- while (erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_io_active)
- != schdlr_sspnd.msb.dirty_io_wait_active)
- erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx);
- ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER);
-#endif
- ASSERT(p->scheduler_data->no == 1);
+ ASSERT(!msbp->ongoing);
+ msbp->ongoing = 1;
+ if (schdlr_sspnd.active == ERTS_SCHDLR_SSPND_MAKE_NSCHEDS_VAL(1, 0, 0)
+ || (normal && schdlr_sspnd_get_nscheds(&schdlr_sspnd.active,
+ ERTS_SCHED_NORMAL) == 1)) {
+ ASSERT(erts_proc_sched_data(p)->no == 1);
+ plp = proclist_create(p);
+ erts_proclist_store_last(&msbp->blckrs, plp);
+ if (schdlr_sspnd.msb.ongoing)
+ res = ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED;
+ else
+ res = ERTS_SCHDLR_SSPND_DONE_NMSCHED_BLOCKED;
}
else {
- ERTS_SCHDLR_SSPND_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_MSB
- | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0);
- if (p->scheduler_data->no == 1) {
- res = ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED;
- schdlr_sspnd.msb.wait_active = 1;
- }
- else {
- /*
- * Yield! Current process needs to migrate
- * before bif returns.
- */
- res = ERTS_SCHDLR_SSPND_YIELD_DONE_MSCHED_BLOCKED;
- schdlr_sspnd.msb.wait_active = 2;
- }
-
-#ifdef ERTS_DIRTY_SCHEDULERS
- schdlr_sspnd.msb.dirty_cpu_wait_active = 0;
- ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_MSB
- | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0);
- for (ix = 0; ix < erts_no_dirty_cpu_schedulers; ix++) {
- ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix);
- erts_smp_atomic32_read_bor_nob(&ssi->flags,
- ERTS_SSI_FLG_SUSPENDED);
- }
- wake_dirty_schedulers(ERTS_DIRTY_CPU_RUNQ, 0);
- while (erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_active)
- != schdlr_sspnd.msb.dirty_cpu_wait_active)
- erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx);
- ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER);
- ASSERT(schdlr_sspnd.dirty_cpu_curr_online == schdlr_sspnd.dirty_cpu_online);
-
- schdlr_sspnd.msb.dirty_io_wait_active = 0;
- ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_MSB
- | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0);
- for (ix = 0; ix < erts_no_dirty_io_schedulers; ix++) {
- ssi = ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(ix);
- erts_smp_atomic32_read_bor_nob(&ssi->flags,
- ERTS_SSI_FLG_SUSPENDED);
- }
- wake_dirty_schedulers(ERTS_DIRTY_IO_RUNQ, 0);
- while (erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_io_active)
- != schdlr_sspnd.msb.dirty_io_wait_active)
- erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx);
- ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER);
- ASSERT(schdlr_sspnd.dirty_io_curr_online == schdlr_sspnd.dirty_io_online);
-#endif
+ erts_smp_atomic32_read_bor_nob(&schdlr_sspnd.changing,
+ chng_flg);
change_no_used_runqs(1);
for (ix = 1; ix < erts_no_run_queues; ix++)
suspend_run_queue(ERTS_RUNQ_IX(ix));
@@ -7839,84 +7951,83 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all)
wake_scheduler(rq);
}
- if (erts_smp_atomic32_read_nob(&schdlr_sspnd.active)
- != schdlr_sspnd.msb.wait_active) {
- ErtsSchedulerData *esdp;
-
- erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
-
- if (plocks && !have_unlocked_plocks) {
- have_unlocked_plocks = 1;
- erts_smp_proc_unlock(p, plocks);
+#ifdef ERTS_DIRTY_SCHEDULERS
+ if (!normal) {
+ for (ix = 0; ix < erts_no_dirty_cpu_schedulers; ix++) {
+ ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix);
+ erts_smp_atomic32_read_bor_nob(&ssi->flags,
+ ERTS_SSI_FLG_SUSPENDED);
}
+ wake_dirty_schedulers(ERTS_DIRTY_CPU_RUNQ, 0);
- esdp = ERTS_PROC_GET_SCHDATA(p);
-
- erts_thr_progress_active(esdp, 0);
- erts_thr_progress_prepare_wait(esdp);
-
- erts_smp_mtx_lock(&schdlr_sspnd.mtx);
-
- while (erts_smp_atomic32_read_nob(&schdlr_sspnd.active)
- != schdlr_sspnd.msb.wait_active)
- erts_smp_cnd_wait(&schdlr_sspnd.cnd,
- &schdlr_sspnd.mtx);
-
- erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
-
- erts_thr_progress_active(esdp, 1);
- erts_thr_progress_finalize_wait(esdp);
+ for (ix = 0; ix < erts_no_dirty_io_schedulers; ix++) {
+ ssi = ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(ix);
+ erts_smp_atomic32_read_bor_nob(&ssi->flags,
+ ERTS_SSI_FLG_SUSPENDED);
+ }
+ wake_dirty_schedulers(ERTS_DIRTY_IO_RUNQ, 0);
+ }
+#endif
- erts_smp_mtx_lock(&schdlr_sspnd.mtx);
+ wait_until_msb:
- }
+ ASSERT(chng_flg & erts_smp_atomic32_read_nob(&schdlr_sspnd.changing));
- ASSERT(res != ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED
- ? (ERTS_SCHDLR_SSPND_CHNG_WAITER
- & erts_smp_atomic32_read_nob(&schdlr_sspnd.changing))
- : (ERTS_SCHDLR_SSPND_CHNG_WAITER
- == erts_smp_atomic32_read_nob(&schdlr_sspnd.changing)));
- erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing,
- ~ERTS_SCHDLR_SSPND_CHNG_WAITER);
+ plp = proclist_create(p);
+ erts_proclist_store_last(&msbp->chngq, plp);
+ resume_proc = 0;
+ if (schdlr_sspnd.msb.ongoing)
+ res = ERTS_SCHDLR_SSPND_YIELD_DONE_MSCHED_BLOCKED;
+ else
+ res = ERTS_SCHDLR_SSPND_YIELD_DONE_NMSCHED_BLOCKED;
}
- plp = proclist_create(p);
- erts_proclist_store_last(&schdlr_sspnd.msb.procs, plp);
- ASSERT(p->scheduler_data);
+ ASSERT(erts_proc_sched_data(p));
}
}
- else if (!ongoing_multi_scheduling_block()) {
- /* unblock not ongoing */
- ASSERT(!schdlr_sspnd.msb.procs);
- res = ERTS_SCHDLR_SSPND_DONE;
+ else if (!msbp->ongoing) {
+ ASSERT(!msbp->blckrs);
+ goto unblock_res;
}
else { /* ------ UNBLOCK ------ */
- if (p->flags & F_HAVE_BLCKD_MSCHED) {
- ErtsProcList *plp = erts_proclist_peek_first(schdlr_sspnd.msb.procs);
-
- while (plp) {
- ErtsProcList *tmp_plp = plp;
- plp = erts_proclist_peek_next(schdlr_sspnd.msb.procs, plp);
- if (erts_proclist_same(tmp_plp, p)) {
- erts_proclist_remove(&schdlr_sspnd.msb.procs, tmp_plp);
- proclist_destroy(tmp_plp);
- if (!all)
- break;
+ if (p->flags & have_blckd_flg) {
+ ErtsProcList **plpps[3] = {0};
+ ErtsProcList *plp;
+
+ plpps[0] = &msbp->blckrs;
+ if (all)
+ plpps[1] = &msbp->chngq;
+
+ for (ix = 0; plpps[ix]; ix++) {
+ plp = erts_proclist_peek_first(*plpps[ix]);
+ while (plp) {
+ ErtsProcList *tmp_plp = plp;
+ plp = erts_proclist_peek_next(*plpps[ix], plp);
+ if (erts_proclist_same(tmp_plp, p)) {
+ erts_proclist_remove(plpps[ix], tmp_plp);
+ proclist_destroy(tmp_plp);
+ if (!all)
+ break;
+ }
}
}
}
- if (schdlr_sspnd.msb.procs)
- res = ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED;
- else {
- ERTS_SCHDLR_SSPND_CHNG_SET(ERTS_SCHDLR_SSPND_CHNG_MSB, 0);
- p->flags &= ~F_HAVE_BLCKD_MSCHED;
- schdlr_sspnd.msb.ongoing = 0;
- if (schdlr_sspnd.online == 1) {
+ if (!msbp->blckrs && !msbp->chngq) {
+ int online = (int) schdlr_sspnd_get_nscheds(&schdlr_sspnd.online,
+ ERTS_SCHED_NORMAL);
+ erts_smp_atomic32_read_bor_nob(&schdlr_sspnd.changing,
+ chng_flg);
+ p->flags &= ~have_blckd_flg;
+ msbp->ongoing = 0;
+ if (online == 1) {
/* No normal schedulers to resume */
- ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.active) == 1);
- ERTS_SCHDLR_SSPND_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_MSB);
+ ASSERT(schdlr_sspnd_get_nscheds(&schdlr_sspnd.active,
+ ERTS_SCHED_NORMAL) == 1);
+#ifndef ERTS_DIRTY_SCHEDULERS
+ erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing,
+ ~chng_flg);
+#endif
}
- else {
- online = schdlr_sspnd.online;
+ else if (!(schdlr_sspnd.msb.ongoing|schdlr_sspnd.nmsb.ongoing)) {
if (plocks) {
have_unlocked_plocks = 1;
erts_smp_proc_unlock(p, plocks);
@@ -7932,83 +8043,91 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all)
suspend_run_queue(ERTS_RUNQ_IX(ix));
}
#ifdef ERTS_DIRTY_SCHEDULERS
- ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET(ERTS_SCHDLR_SSPND_CHNG_MSB, 0);
- schdlr_sspnd.msb.dirty_cpu_wait_active = schdlr_sspnd.dirty_cpu_online;
- for (ix = 0; ix < schdlr_sspnd.dirty_cpu_online; ix++) {
- ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix);
- scheduler_ssi_resume_wake(ssi);
- erts_smp_atomic32_read_band_nob(&ssi->flags,
- ~ERTS_SSI_FLG_SUSPENDED);
- }
- wake_dirty_schedulers(ERTS_DIRTY_CPU_RUNQ, 0);
-
- ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET(ERTS_SCHDLR_SSPND_CHNG_MSB, 0);
- schdlr_sspnd.msb.dirty_io_wait_active = erts_no_dirty_io_schedulers;
- for (ix = 0; ix < erts_no_dirty_io_schedulers; ix++) {
- ssi = ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(ix);
- scheduler_ssi_resume_wake(ssi);
- erts_smp_atomic32_read_band_nob(&ssi->flags,
- ~ERTS_SSI_FLG_SUSPENDED);
- }
- wake_dirty_schedulers(ERTS_DIRTY_IO_RUNQ, 0);
+ if (!normal) {
+ ASSERT(!schdlr_sspnd.msb.ongoing);
+ online = (int) schdlr_sspnd_get_nscheds(&schdlr_sspnd.online,
+ ERTS_SCHED_DIRTY_CPU);
+ for (ix = 0; ix < online; ix++) {
+ ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix);
+ scheduler_ssi_resume_wake(ssi);
+ }
+
+ for (ix = 0; ix < erts_no_dirty_io_schedulers; ix++) {
+ ssi = ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(ix);
+ scheduler_ssi_resume_wake(ssi);
+ }
+ }
#endif
- res = ERTS_SCHDLR_SSPND_DONE;
}
+
+ unblock_res:
+ if (schdlr_sspnd.msb.ongoing)
+ res = ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED;
+ else if (schdlr_sspnd.nmsb.ongoing)
+ res = ERTS_SCHDLR_SSPND_DONE_NMSCHED_BLOCKED;
+ else
+ res = ERTS_SCHDLR_SSPND_DONE;
}
erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
+
if (have_unlocked_plocks)
erts_smp_proc_lock(p, plocks);
- return res;
-}
-#ifdef DEBUG
-void
-erts_dbg_multi_scheduling_return_trap(Process *p, Eterm return_value)
-{
- if (return_value == am_blocked) {
- erts_aint32_t active = erts_smp_atomic32_read_nob(&schdlr_sspnd.active);
- ASSERT(1 <= active && active <= 2);
- ASSERT(ERTS_PROC_GET_SCHDATA(p)->no == 1);
+ if (resume_proc) {
+ if (!(plocks & ERTS_PROC_LOCK_STATUS))
+ erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
+ resume_process(p, plocks|ERTS_PROC_LOCK_STATUS);
+ if (!(plocks & ERTS_PROC_LOCK_STATUS))
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
}
+
+ return res;
}
-#endif
int
erts_is_multi_scheduling_blocked(void)
{
int res;
erts_smp_mtx_lock(&schdlr_sspnd.mtx);
- res = schdlr_sspnd.msb.procs != NULL;
+ if (schdlr_sspnd.msb.blckrs)
+ res = 1;
+ else if (schdlr_sspnd.nmsb.blckrs)
+ res = -1;
+ else
+ res = 0;
erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
return res;
}
Eterm
-erts_multi_scheduling_blockers(Process *p)
+erts_multi_scheduling_blockers(Process *p, int normal)
{
Eterm res = NIL;
+ ErtsMultiSchedulingBlock *msbp;
+
+ msbp = normal ? &schdlr_sspnd.nmsb : &schdlr_sspnd.msb;
erts_smp_mtx_lock(&schdlr_sspnd.mtx);
- if (!erts_proclist_is_empty(schdlr_sspnd.msb.procs)) {
+ if (!erts_proclist_is_empty(msbp->blckrs)) {
Eterm *hp, *hp_end;
ErtsProcList *plp1, *plp2;
Uint max_size = 0;
- for (plp1 = erts_proclist_peek_first(schdlr_sspnd.msb.procs);
+ for (plp1 = erts_proclist_peek_first(msbp->blckrs);
plp1;
- plp1 = erts_proclist_peek_next(schdlr_sspnd.msb.procs, plp1)) {
+ plp1 = erts_proclist_peek_next(msbp->blckrs, plp1)) {
max_size += 2;
}
ASSERT(max_size);
hp = HAlloc(p, max_size);
hp_end = hp + max_size;
- for (plp1 = erts_proclist_peek_first(schdlr_sspnd.msb.procs);
+ for (plp1 = erts_proclist_peek_first(msbp->blckrs);
plp1;
- plp1 = erts_proclist_peek_next(schdlr_sspnd.msb.procs, plp1)) {
- for (plp2 = erts_proclist_peek_first(schdlr_sspnd.msb.procs);
+ plp1 = erts_proclist_peek_next(msbp->blckrs, plp1)) {
+ for (plp2 = erts_proclist_peek_first(msbp->blckrs);
plp2->pid != plp1->pid;
- plp2 = erts_proclist_peek_next(schdlr_sspnd.msb.procs, plp2));
+ plp2 = erts_proclist_peek_next(msbp->blckrs, plp2));
if (plp2 == plp1) {
res = CONS(hp, plp1->pid, res);
hp += 2;
@@ -8046,6 +8165,8 @@ sched_thread_func(void *vesdp)
callbacks.wait = thr_prgr_wait;
callbacks.finalize_wait = thr_prgr_fin_wait;
+ erts_msacc_init_thread("scheduler", no, 1);
+
erts_thr_progress_register_managed_thread(esdp, &callbacks, 0);
erts_alloc_register_scheduler(vesdp);
#endif
@@ -8075,39 +8196,6 @@ sched_thread_func(void *vesdp)
#endif
erts_thread_init_float();
- if (no == 1) {
- erts_thr_progress_active(esdp, 0);
- erts_thr_progress_prepare_wait(esdp);
- }
-
- erts_smp_mtx_lock(&schdlr_sspnd.mtx);
-
- ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.changing)
- & ERTS_SCHDLR_SSPND_CHNG_ONLN);
-
- if (--schdlr_sspnd.curr_online == schdlr_sspnd.wait_curr_online) {
- erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing,
- ~ERTS_SCHDLR_SSPND_CHNG_ONLN);
- if (no != 1)
-#ifdef ERTS_DIRTY_SCHEDULERS
- erts_smp_cnd_broadcast(&schdlr_sspnd.cnd);
-#else
- erts_smp_cnd_signal(&schdlr_sspnd.cnd);
-#endif
- }
-
- if (no == 1) {
- while (schdlr_sspnd.curr_online != schdlr_sspnd.wait_curr_online)
- erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx);
- ERTS_SCHDLR_SSPND_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER);
- }
- erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
-
- if (no == 1) {
- erts_thr_progress_finalize_wait(esdp);
- erts_thr_progress_active(esdp, 1);
- }
-
#ifdef ERTS_DO_VERIFY_UNUSED_TEMP_ALLOC
esdp->verify_unused_temp_alloc
= erts_alloc_get_verify_unused_temp_alloc(
@@ -8142,6 +8230,8 @@ sched_dirty_cpu_thread_func(void *vesdp)
esdp->thr_id += erts_no_schedulers;
+ erts_msacc_init_thread("dirty_cpu_scheduler", no, 0);
+
erts_thr_progress_register_unmanaged_thread(&callbacks);
#ifdef ERTS_ENABLE_LOCK_CHECK
{
@@ -8162,25 +8252,7 @@ sched_dirty_cpu_thread_func(void *vesdp)
#endif
erts_thread_init_float();
- erts_smp_mtx_lock(&schdlr_sspnd.mtx);
- ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_changing)
- & ERTS_SCHDLR_SSPND_CHNG_ONLN);
-
- if (--schdlr_sspnd.dirty_cpu_curr_online == schdlr_sspnd.dirty_cpu_wait_curr_online) {
- erts_smp_atomic32_read_band_nob(&schdlr_sspnd.dirty_cpu_changing,
- ~ERTS_SCHDLR_SSPND_CHNG_ONLN);
- if (no != 1)
- erts_smp_cnd_broadcast(&schdlr_sspnd.cnd);
- }
-
- if (no == 1) {
- while (schdlr_sspnd.dirty_cpu_curr_online != schdlr_sspnd.dirty_cpu_wait_curr_online)
- erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx);
- ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER);
- }
- erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
-
- process_main();
+ erts_dirty_process_main(esdp);
/* No schedulers should *ever* terminate */
erts_exit(ERTS_ABORT_EXIT,
"Dirty CPU scheduler thread number %beu terminated\n",
@@ -8205,6 +8277,8 @@ sched_dirty_io_thread_func(void *vesdp)
esdp->thr_id += erts_no_schedulers + erts_no_dirty_cpu_schedulers;
+ erts_msacc_init_thread("dirty_io_scheduler", no, 0);
+
erts_thr_progress_register_unmanaged_thread(&callbacks);
#ifdef ERTS_ENABLE_LOCK_CHECK
{
@@ -8225,25 +8299,7 @@ sched_dirty_io_thread_func(void *vesdp)
#endif
erts_thread_init_float();
- erts_smp_mtx_lock(&schdlr_sspnd.mtx);
- ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_io_changing)
- & ERTS_SCHDLR_SSPND_CHNG_ONLN);
-
- if (--schdlr_sspnd.dirty_io_curr_online == schdlr_sspnd.dirty_io_wait_curr_online) {
- erts_smp_atomic32_read_band_nob(&schdlr_sspnd.dirty_io_changing,
- ~ERTS_SCHDLR_SSPND_CHNG_ONLN);
- if (no != 1)
- erts_smp_cnd_broadcast(&schdlr_sspnd.cnd);
- }
-
- if (no == 1) {
- while (schdlr_sspnd.dirty_io_curr_online != schdlr_sspnd.dirty_io_wait_curr_online)
- erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx);
- ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER);
- }
- erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
-
- process_main();
+ erts_dirty_process_main(esdp);
/* No schedulers should *ever* terminate */
erts_exit(ERTS_ABORT_EXIT,
"Dirty I/O scheduler thread number %beu terminated\n",
@@ -8301,18 +8357,12 @@ erts_start_schedulers(void)
erts_snprintf(opts.name, 16, "%lu_scheduler", actual + 1);
-#ifdef __OSE__
- /* This should be done in the bind strategy */
- opts.coreNo = (actual+1) % ose_num_cpus();
-#endif
-
res = ethr_thr_create(&esdp->tid, sched_thread_func, (void*)esdp, &opts);
if (res != 0) {
break;
}
}
-
erts_no_schedulers = actual;
#ifdef ERTS_DIRTY_SCHEDULERS
@@ -8341,10 +8391,6 @@ erts_start_schedulers(void)
erts_snprintf(opts.name, 16, "aux");
-#ifdef __OSE__
- opts.coreNo = 0;
-#endif /* __OSE__ */
-
res = ethr_thr_create(&aux_tid, aux_thread, NULL, &opts);
if (res != 0)
erts_exit(ERTS_ERROR_EXIT, "Failed to create aux thread\n");
@@ -8364,7 +8410,6 @@ erts_start_schedulers(void)
actual, actual == 1 ? " was" : "s were");
erts_send_error_to_logger_nogl(dsbufp);
}
-
}
#endif /* ERTS_SMP */
@@ -8383,7 +8428,7 @@ add_pend_suspend(Process *suspendee,
sizeof(ErtsPendingSuspend));
psp->next = NULL;
#ifdef DEBUG
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
psp->end = (ErtsPendingSuspend *) 0xdeaddeaddeaddead;
#else
psp->end = (ErtsPendingSuspend *) 0xdeaddead;
@@ -8529,9 +8574,23 @@ pid2proc_not_running(Process *c_p, ErtsProcLocks c_p_locks,
if (!suspend_process(c_p, rp)) {
/* Other process running */
- ASSERT(ERTS_PSFLG_RUNNING
+ ASSERT((ERTS_PSFLG_RUNNING | ERTS_PSFLG_DIRTY_RUNNING)
& erts_smp_atomic32_read_nob(&rp->state));
+#ifdef ERTS_DIRTY_SCHEDULERS
+ if (!suspend
+ && (erts_smp_atomic32_read_nob(&rp->state)
+ & ERTS_PSFLG_DIRTY_RUNNING)) {
+ ErtsProcLocks need_locks = pid_locks & ~ERTS_PROC_LOCK_STATUS;
+ if (need_locks && erts_smp_proc_trylock(rp, need_locks) == EBUSY) {
+ erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_STATUS);
+ rp = erts_pid2proc(c_p, c_p_locks|ERTS_PROC_LOCK_STATUS,
+ pid, pid_locks|ERTS_PROC_LOCK_STATUS);
+ }
+ goto done;
+ }
+#endif
+
running:
/*
@@ -8556,7 +8615,7 @@ pid2proc_not_running(Process *c_p, ErtsProcLocks c_p_locks,
else {
ErtsProcLocks need_locks = pid_locks & ~ERTS_PROC_LOCK_STATUS;
if (need_locks && erts_smp_proc_trylock(rp, need_locks) == EBUSY) {
- if (ERTS_PSFLG_RUNNING_SYS
+ if ((ERTS_PSFLG_RUNNING_SYS|ERTS_PSFLG_DIRTY_RUNNING_SYS)
& erts_smp_atomic32_read_nob(&rp->state)) {
/* Executing system task... */
resume_process(rp, ERTS_PROC_LOCK_STATUS);
@@ -8583,8 +8642,15 @@ pid2proc_not_running(Process *c_p, ErtsProcLocks c_p_locks,
* from being selected for normal execution regardless
* of locks held or not held on it...
*/
- ASSERT(!(ERTS_PSFLG_RUNNING
- & erts_smp_atomic32_read_nob(&rp->state)));
+#ifdef DEBUG
+ {
+ erts_aint32_t state;
+ state = erts_smp_atomic32_read_nob(&rp->state);
+ ASSERT((state & ERTS_PSFLG_PENDING_EXIT)
+ || !(state & (ERTS_PSFLG_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)));
+ }
+#endif
if (!suspend)
resume_process(rp, pid_locks|ERTS_PROC_LOCK_STATUS);
@@ -9086,6 +9152,27 @@ resume_process_1(BIF_ALIST_1)
BIF_ERROR(BIF_P, BADARG);
}
+BIF_RETTYPE
+erts_internal_is_process_executing_dirty_1(BIF_ALIST_1)
+{
+ if (is_not_internal_pid(BIF_ARG_1))
+ BIF_ERROR(BIF_P, BADARG);
+#ifdef ERTS_DIRTY_SCHEDULERS
+ else {
+ Process *rp = erts_proc_lookup(BIF_ARG_1);
+ if (rp) {
+ erts_aint32_t state = erts_smp_atomic32_read_nob(&rp->state);
+ if (state & (ERTS_PSFLG_DIRTY_RUNNING
+ |ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
+ BIF_RET(am_true);
+ }
+ }
+ }
+#endif
+ BIF_RET(am_false);
+}
+
+
Uint
erts_run_queues_len(Uint *qlen, int atomic_queues_read, int incl_active_sched)
{
@@ -9124,28 +9211,43 @@ erts_run_queues_len(Uint *qlen, int atomic_queues_read, int incl_active_sched)
}
Eterm
-erts_process_status(Process *c_p, ErtsProcLocks c_p_locks,
- Process *rp, Eterm rpid)
+erts_process_state2status(erts_aint32_t state)
+{
+ if (state & ERTS_PSFLG_FREE)
+ return am_free;
+
+ if (state & ERTS_PSFLG_EXITING)
+ return am_exiting;
+
+ if (state & ERTS_PSFLG_GC)
+ return am_garbage_collecting;
+
+ if (state & ERTS_PSFLG_SUSPENDED)
+ return am_suspended;
+
+ if (state & (ERTS_PSFLG_RUNNING
+ | ERTS_PSFLG_RUNNING_SYS
+ | ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS))
+ return am_running;
+
+ if (state & (ERTS_PSFLG_ACTIVE
+ | ERTS_PSFLG_ACTIVE_SYS
+ | ERTS_PSFLG_DIRTY_ACTIVE_SYS))
+ return am_runnable;
+
+ return am_waiting;
+}
+
+Eterm
+erts_process_status(Process *rp, Eterm rpid)
{
Eterm res = am_undefined;
Process *p = rp ? rp : erts_proc_lookup_raw(rpid);
if (p) {
erts_aint32_t state = erts_smp_atomic32_read_acqb(&p->state);
- if (state & ERTS_PSFLG_FREE)
- res = am_free;
- else if (state & ERTS_PSFLG_EXITING)
- res = am_exiting;
- else if (state & ERTS_PSFLG_GC)
- res = am_garbage_collecting;
- else if (state & ERTS_PSFLG_SUSPENDED)
- res = am_suspended;
- else if (state & ERTS_PSFLG_RUNNING)
- res = am_running;
- else if (state & ERTS_PSFLG_ACTIVE)
- res = am_runnable;
- else
- res = am_waiting;
+ res = erts_process_state2status(state);
}
#ifdef ERTS_SMP
else {
@@ -9347,6 +9449,27 @@ erts_set_process_priority(Process *p, Eterm value)
}
}
+#ifdef __WIN32__
+Sint64
+erts_time2reds(ErtsMonotonicTime start, ErtsMonotonicTime end)
+{
+ return ERTS_TIME2REDS_IMPL__(start, end);
+}
+#endif
+
+static int
+scheduler_gc_proc(Process *c_p, int reds_left)
+{
+ int fcalls, reds;
+ if (!ERTS_PROC_GET_SAVED_CALLS_BUF(c_p))
+ fcalls = reds_left;
+ else
+ fcalls = reds_left - CONTEXT_REDS;
+ reds = erts_garbage_collect_nobump(c_p, 0, c_p->arg_reg, c_p->arity, fcalls);
+ ASSERT(reds_left >= reds);
+ return reds;
+}
+
/*
* schedule() is called from BEAM (process_main()) or HiPE
* (hipe_mode_switch()) when the current process is to be
@@ -9365,11 +9488,10 @@ erts_set_process_priority(Process *p, Eterm value)
* so that normal processes get to run more frequently.
*/
-Process *schedule(Process *p, int calls)
+Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
{
Process *proxy_p = NULL;
ErtsRunQueue *rq;
- ErtsSchedulerData *esdp;
int context_reds;
int fcalls;
int input_reductions;
@@ -9377,6 +9499,9 @@ Process *schedule(Process *p, int calls)
int reds;
Uint32 flags;
erts_aint32_t state = 0; /* Supress warning... */
+ int is_normal_sched;
+
+ ERTS_MSACC_DECLARE_CACHE();
#ifdef USE_VM_PROBES
if (p != NULL && DTRACE_ENABLED(process_unscheduled)) {
@@ -9403,25 +9528,54 @@ Process *schedule(Process *p, int calls)
* Clean up after the process being scheduled out.
*/
if (!p) { /* NULL in the very first schedule() call */
+#ifdef ERTS_DIRTY_SCHEDULERS
+ is_normal_sched = !esdp;
+ if (is_normal_sched) {
+ esdp = erts_get_scheduler_data();
+ ASSERT(!ERTS_SCHEDULER_IS_DIRTY(esdp));
+ }
+ else {
+ ASSERT(ERTS_SCHEDULER_IS_DIRTY(esdp));
+ }
+#else
esdp = erts_get_scheduler_data();
+ is_normal_sched = 1;
+#endif
rq = erts_get_runq_current(esdp);
ASSERT(esdp);
fcalls = (int) erts_smp_atomic32_read_acqb(&function_calls);
actual_reds = reds = 0;
erts_smp_runq_lock(rq);
} else {
- sched_out_proc:
-
#ifdef ERTS_SMP
- ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
+#ifdef ERTS_DIRTY_SCHEDULERS
+ is_normal_sched = !esdp;
+ if (is_normal_sched) {
+ esdp = p->scheduler_data;
+ ASSERT(!ERTS_SCHEDULER_IS_DIRTY(esdp));
+ }
+ else {
+ ASSERT(ERTS_SCHEDULER_IS_DIRTY(esdp));
+ }
+#else
esdp = p->scheduler_data;
+ is_normal_sched = 1;
+#endif
ASSERT(esdp->current_process == p
|| esdp->free_process == p);
#else
esdp = erts_scheduler_data;
ASSERT(esdp->current_process == p);
+ is_normal_sched = 1;
#endif
+
+ sched_out_proc:
+
+ ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
+
reds = actual_reds = calls - esdp->virtual_reds;
+
+ ASSERT(actual_reds >= 0);
if (reds < ERTS_PROC_MIN_CONTEXT_SWITCH_REDS_COST)
reds = ERTS_PROC_MIN_CONTEXT_SWITCH_REDS_COST;
esdp->virtual_reds = 0;
@@ -9433,61 +9587,92 @@ Process *schedule(Process *p, int calls)
p->reds += actual_reds;
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
-
- state = erts_smp_atomic32_read_acqb(&p->state);
+ state = erts_smp_atomic32_read_nob(&p->state);
if (IS_TRACED(p)) {
if (IS_TRACED_FL(p, F_TRACE_CALLS) && !(state & ERTS_PSFLG_FREE))
erts_schedule_time_break(p, ERTS_BP_CALL_TIME_SCHEDULE_OUT);
- if (state & (ERTS_PSFLG_FREE|ERTS_PSFLG_EXITING)) {
+ if ((state & (ERTS_PSFLG_FREE|ERTS_PSFLG_EXITING)) == ERTS_PSFLG_EXITING) {
if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED_EXIT))
- trace_sched(p, ((state & ERTS_PSFLG_FREE)
- ? am_out_exited
- : am_out_exiting));
+ trace_sched(p, ERTS_PROC_LOCK_MAIN,
+ ((state & ERTS_PSFLG_FREE)
+ ? am_out_exited
+ : am_out_exiting));
}
else {
- if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED))
- trace_sched(p, am_out);
- else if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED_PROCS))
- trace_virtual_sched(p, am_out);
+ if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED) ||
+ ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED_PROCS))
+ trace_sched(p, ERTS_PROC_LOCK_MAIN, am_out);
}
}
+ erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+
+#ifdef ERTS_SMP
+ if (p->trace_msg_q) {
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ erts_schedule_flush_trace_messages(p, 1);
+ erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ }
+#endif
+
+ /* have to re-read state after taking lock */
+ state = erts_smp_atomic32_read_nob(&p->state);
+
#ifdef ERTS_SMP
- if (state & ERTS_PSFLG_PENDING_EXIT)
+ if (is_normal_sched && (state & ERTS_PSFLG_PENDING_EXIT))
erts_handle_pending_exit(p, (ERTS_PROC_LOCK_MAIN
+ | ERTS_PROC_LOCK_TRACE
| ERTS_PROC_LOCK_STATUS));
if (p->pending_suspenders)
handle_pending_suspend(p, (ERTS_PROC_LOCK_MAIN
+ | ERTS_PROC_LOCK_TRACE
| ERTS_PROC_LOCK_STATUS));
#endif
esdp->reductions += reds;
- schedule_out_process(rq, state, p, proxy_p); /* Returns with rq locked! */
- proxy_p = NULL;
+ {
+ int dec_refc;
+
+ /* schedule_out_process() returns with rq locked! */
+ dec_refc = schedule_out_process(rq, state, p,
+ proxy_p, is_normal_sched);
+ proxy_p = NULL;
- ERTS_PROC_REDUCTIONS_EXECUTED(esdp, rq,
- (int) ERTS_PSFLGS_GET_USR_PRIO(state),
- reds,
- actual_reds);
+ ERTS_PROC_REDUCTIONS_EXECUTED(esdp, rq,
+ (int) ERTS_PSFLGS_GET_USR_PRIO(state),
+ reds,
+ actual_reds);
- esdp->current_process = NULL;
+ esdp->current_process = NULL;
#ifdef ERTS_SMP
- p->scheduler_data = NULL;
+ if (is_normal_sched)
+ p->scheduler_data = NULL;
#endif
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS);
+ erts_smp_proc_unlock(p, (ERTS_PROC_LOCK_MAIN
+ | ERTS_PROC_LOCK_STATUS
+ | ERTS_PROC_LOCK_TRACE));
+
+ ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_OTHER);
- if (state & ERTS_PSFLG_FREE) {
#ifdef ERTS_SMP
- ASSERT(esdp->free_process == p);
- esdp->free_process = NULL;
-#else
- erts_proc_dec_refc(p);
+ if (state & ERTS_PSFLG_FREE) {
+ if (!is_normal_sched) {
+ ASSERT(p->flags & F_DELAYED_DEL_PROC);
+ }
+ else {
+ ASSERT(esdp->free_process == p);
+ esdp->free_process = NULL;
+ }
+ }
#endif
- }
+
+ if (dec_refc)
+ erts_proc_dec_refc(p);
+ }
+
#ifdef ERTS_SMP
ASSERT(!esdp->free_process);
#endif
@@ -9495,7 +9680,7 @@ Process *schedule(Process *p, int calls)
ERTS_SMP_CHK_NO_PROC_LOCKS;
- if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) {
+ if (is_normal_sched) {
if (esdp->check_time_reds >= ERTS_CHECK_TIME_REDS)
(void) erts_get_monotonic_time(esdp);
@@ -9505,26 +9690,17 @@ Process *schedule(Process *p, int calls)
erts_smp_runq_lock(rq);
}
}
- BM_STOP_TIMER(system);
-
}
- ERTS_SMP_LC_ASSERT(ERTS_SCHEDULER_IS_DIRTY(esdp)
- || !erts_thr_progress_is_blocking());
+ ERTS_SMP_LC_ASSERT(!is_normal_sched || !erts_thr_progress_is_blocking());
check_activities_to_run: {
+ erts_aint32_t psflg_running, psflg_running_sys;
#ifdef ERTS_SMP
ErtsMigrationPaths *mps;
ErtsMigrationPath *mp;
- ErtsProcList *pnd_xtrs = rq->procs.pending_exiters;
- if (erts_proclist_fetch(&pnd_xtrs, NULL)) {
- rq->procs.pending_exiters = NULL;
- erts_smp_runq_unlock(rq);
- handle_pending_exiters(pnd_xtrs);
- erts_smp_runq_lock(rq);
- }
- if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) {
+ if (is_normal_sched) {
if (rq->check_balance_reds <= 0)
check_balance(rq);
@@ -9541,39 +9717,40 @@ Process *schedule(Process *p, int calls)
continue_check_activities_to_run:
flags = ERTS_RUNQ_FLGS_GET_NOB(rq);
continue_check_activities_to_run_known_flags:
- ASSERT(ERTS_SCHEDULER_IS_DIRTY(esdp)
- || flags & ERTS_RUNQ_FLG_NONEMPTY);
+ ASSERT(!is_normal_sched || (flags & ERTS_RUNQ_FLG_NONEMPTY));
- if (flags & (ERTS_RUNQ_FLG_CHK_CPU_BIND|ERTS_RUNQ_FLG_SUSPENDED)) {
- if (flags & ERTS_RUNQ_FLG_SUSPENDED) {
- (void) ERTS_RUNQ_FLGS_UNSET_NOB(rq, ERTS_RUNQ_FLG_EXEC);
+ if (!is_normal_sched) {
+ if (erts_smp_atomic32_read_acqb(&esdp->ssi->flags)
+ & ERTS_SSI_FLG_SUSPENDED) {
suspend_scheduler(esdp);
- flags = ERTS_RUNQ_FLGS_SET_NOB(rq, ERTS_RUNQ_FLG_EXEC);
- flags |= ERTS_RUNQ_FLG_EXEC;
- }
- if (flags & ERTS_RUNQ_FLG_CHK_CPU_BIND) {
- flags = ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_CHK_CPU_BIND);
- flags &= ~ ERTS_RUNQ_FLG_CHK_CPU_BIND;
- erts_sched_check_cpu_bind(esdp);
}
}
-#ifdef ERTS_DIRTY_SCHEDULERS
- else if (ERTS_SCHEDULER_IS_DIRTY(esdp)
- && (erts_smp_atomic32_read_acqb(&esdp->ssi->flags)
- & ERTS_SSI_FLG_SUSPENDED))
- suspend_scheduler(esdp);
-#endif
-
- if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) {
+ else {
erts_aint32_t aux_work;
- int leader_update = erts_thr_progress_update(esdp);
+ int leader_update;
+
+ ASSERT(is_normal_sched);
+
+ if (flags & (ERTS_RUNQ_FLG_CHK_CPU_BIND|ERTS_RUNQ_FLG_SUSPENDED)) {
+ if (flags & ERTS_RUNQ_FLG_SUSPENDED) {
+ (void) ERTS_RUNQ_FLGS_UNSET_NOB(rq, ERTS_RUNQ_FLG_EXEC);
+ suspend_scheduler(esdp);
+ flags = ERTS_RUNQ_FLGS_SET_NOB(rq, ERTS_RUNQ_FLG_EXEC);
+ flags |= ERTS_RUNQ_FLG_EXEC;
+ }
+ if (flags & ERTS_RUNQ_FLG_CHK_CPU_BIND) {
+ flags = ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_CHK_CPU_BIND);
+ flags &= ~ERTS_RUNQ_FLG_CHK_CPU_BIND;
+ erts_sched_check_cpu_bind(esdp);
+ }
+ }
+
+ leader_update = erts_thr_progress_update(esdp);
aux_work = erts_atomic32_read_acqb(&esdp->ssi->aux_work);
- if (aux_work | leader_update | ERTS_SCHED_FAIR) {
+ if (aux_work | leader_update) {
erts_smp_runq_unlock(rq);
if (leader_update)
erts_thr_progress_leader_update(esdp);
- else if (ERTS_SCHED_FAIR)
- ERTS_SCHED_FAIR_YIELD();
if (aux_work)
handle_aux_work(&esdp->aux_work_data, aux_work, 0);
erts_smp_runq_lock(rq);
@@ -9594,19 +9771,13 @@ Process *schedule(Process *p, int calls)
flags = ERTS_RUNQ_FLGS_GET_NOB(rq);
-#ifdef ERTS_DIRTY_SCHEDULERS
- if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix) && rq->halt_in_progress) {
- /*
- * TODO: if halt in progress, need to put the dirty scheduler
- * to sleep somewhere around here to prevent it from picking up
- * new work
- */
+ if (!is_normal_sched && rq->halt_in_progress) {
+ /* Wait for emulator to terminate... */
+ while (1)
+ erts_milli_sleep(1000*1000);
}
- else
-#endif
-
- if ((!(flags & ERTS_RUNQ_FLGS_QMASK) && !rq->misc.start)
- || (rq->halt_in_progress && ERTS_EMPTY_RUNQ_PORTS(rq))) {
+ else if ((!(flags & ERTS_RUNQ_FLGS_QMASK) && !rq->misc.start)
+ || (rq->halt_in_progress && ERTS_EMPTY_RUNQ_PORTS(rq))) {
/* Prepare for scheduler wait */
#ifdef ERTS_SMP
ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
@@ -9620,7 +9791,7 @@ Process *schedule(Process *p, int calls)
if (flags & ERTS_RUNQ_FLG_INACTIVE)
empty_runq(rq);
else {
- if (!ERTS_RUNQ_IX_IS_DIRTY(rq->ix) && try_steal_task(rq))
+ if (is_normal_sched && try_steal_task(rq))
goto continue_check_activities_to_run;
empty_runq(rq);
@@ -9642,21 +9813,23 @@ Process *schedule(Process *p, int calls)
scheduler_wait(&fcalls, esdp, rq);
flags = ERTS_RUNQ_FLGS_SET_NOB(rq, ERTS_RUNQ_FLG_EXEC);
flags |= ERTS_RUNQ_FLG_EXEC;
-
+ ERTS_MSACC_UPDATE_CACHE();
#ifdef ERTS_SMP
non_empty_runq(rq);
#endif
goto check_activities_to_run;
}
- else if (!ERTS_SCHEDULER_IS_DIRTY(esdp) &&
- (fcalls > input_reductions &&
- prepare_for_sys_schedule(esdp, !0))) {
+ else if (is_normal_sched
+ && (fcalls > input_reductions
+ && prepare_for_sys_schedule(!0))) {
ErtsMonotonicTime current_time;
/*
* Schedule system-level activities.
*/
+ ERTS_MSACC_PUSH_STATE_CACHED_M();
+
erts_smp_atomic32_set_relb(&function_calls, 0);
fcalls = 0;
@@ -9664,7 +9837,12 @@ Process *schedule(Process *p, int calls)
erts_sys_schedule_interrupt(0);
#endif
erts_smp_runq_unlock(rq);
+
+ ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_CHECK_IO);
+ LTTNG2(scheduler_poll, esdp->no, 1);
+
erl_sys_schedule(1);
+ ERTS_MSACC_POP_STATE_M();
current_time = erts_get_monotonic_time(esdp);
if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref))
@@ -9722,11 +9900,12 @@ Process *schedule(Process *p, int calls)
pick_next_process: {
erts_aint32_t psflg_band_mask;
int prio_q;
- int qmask;
+ int qmask, qbit;
flags = ERTS_RUNQ_FLGS_GET_NOB(rq);
qmask = (int) (flags & ERTS_RUNQ_FLGS_PROCS_QMASK);
- switch (qmask & -qmask) {
+ qbit = qmask & -qmask;
+ switch (qbit) {
case MAX_BIT:
prio_q = PRIORITY_MAX;
break;
@@ -9742,11 +9921,10 @@ Process *schedule(Process *p, int calls)
case 0: /* No process at all */
default:
ASSERT(qmask == 0);
+ ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_OTHER);
goto check_activities_to_run;
}
- BM_START_TIMER(system);
-
/*
* Take the chosen process out of the queue.
*/
@@ -9754,26 +9932,27 @@ Process *schedule(Process *p, int calls)
ASSERT(p); /* Wrong qmask in rq->flags? */
- psflg_band_mask = ~(((erts_aint32_t) 1) << (ERTS_PSFLGS_GET_PRQ_PRIO(state)
- + ERTS_PSFLGS_IN_PRQ_MASK_OFFSET));
-
-#ifdef ERTS_DIRTY_SCHEDULERS
- ASSERT((state & (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC)) !=
- (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC));
- if (state & (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC)) {
- ASSERT((ERTS_SCHEDULER_IS_DIRTY_CPU(esdp) && (state & ERTS_PSFLG_DIRTY_CPU_PROC)) ||
- (ERTS_SCHEDULER_IS_DIRTY_IO(esdp) && (state & ERTS_PSFLG_DIRTY_IO_PROC)));
- if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && !(state & ERTS_PSFLG_ACTIVE_SYS))
- goto pick_next_process;
- state &= ~(ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q);
+ if (is_normal_sched) {
+ psflg_running = ERTS_PSFLG_RUNNING;
+ psflg_running_sys = ERTS_PSFLG_RUNNING_SYS;
+ psflg_band_mask = ~(((erts_aint32_t) 1) << (ERTS_PSFLGS_GET_PRQ_PRIO(state)
+ + ERTS_PSFLGS_IN_PRQ_MASK_OFFSET));
+ }
+ else {
+ psflg_running = ERTS_PSFLG_DIRTY_RUNNING;
+ psflg_running_sys = ERTS_PSFLG_DIRTY_RUNNING_SYS;
+ psflg_band_mask = ~((erts_aint32_t) 0);
}
-#endif
if (!(state & ERTS_PSFLG_PROXY))
psflg_band_mask &= ~ERTS_PSFLG_IN_RUNQ;
else {
+ Eterm pid = p->common.id;
proxy_p = p;
- p = erts_proc_lookup_raw(proxy_p->common.id);
+ p = (is_normal_sched
+ ? erts_proc_lookup_raw(pid)
+ : erts_pid2proc_opt(NULL, 0, pid, 0,
+ ERTS_P2P_FLG_INC_REFC));
if (!p) {
free_proxy_proc(proxy_p);
proxy_p = NULL;
@@ -9782,31 +9961,54 @@ Process *schedule(Process *p, int calls)
state = erts_smp_atomic32_read_nob(&p->state);
}
+#ifdef ERTS_DIRTY_SCHEDULERS
+ if (!is_normal_sched)
+ clear_proc_dirty_queue_bit(p, rq, qbit);
+#endif
+
while (1) {
- erts_aint32_t exp, new, tmp;
- tmp = new = exp = state;
+ erts_aint32_t exp, new;
+ int run_process;
+ new = exp = state;
new &= psflg_band_mask;
- if (!(state & (ERTS_PSFLG_RUNNING
- | ERTS_PSFLG_RUNNING_SYS))) {
- tmp = state & (ERTS_PSFLG_SUSPENDED
- | ERTS_PSFLG_PENDING_EXIT
- | ERTS_PSFLG_ACTIVE_SYS);
- if (tmp != ERTS_PSFLG_SUSPENDED) {
- if (state & ERTS_PSFLG_ACTIVE_SYS)
- new |= ERTS_PSFLG_RUNNING_SYS;
- else
- new |= ERTS_PSFLG_RUNNING;
- }
+ /*
+ * Run process if not already running (or free)
+ * or exiting and not running on a normal
+ * scheduler, and not suspended (and not in a
+ * state where suspend should be ignored).
+ */
+ run_process = (((!(state & (ERTS_PSFLG_RUNNING
+ | ERTS_PSFLG_RUNNING_SYS
+ | ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS
+ | ERTS_PSFLG_FREE)))
+#ifdef ERTS_DIRTY_SCHEDULERS
+ | (((state & (ERTS_PSFLG_RUNNING
+
+ | ERTS_PSFLG_FREE
+ | ERTS_PSFLG_RUNNING_SYS
+ | ERTS_PSFLG_EXITING))
+ == ERTS_PSFLG_EXITING)
+ & (!!is_normal_sched))
+#endif
+ )
+ & ((state & (ERTS_PSFLG_SUSPENDED
+ | ERTS_PSFLG_EXITING
+ | ERTS_PSFLG_FREE
+ | ERTS_PSFLG_PENDING_EXIT
+ | ERTS_PSFLG_ACTIVE_SYS
+ | ERTS_PSFLG_DIRTY_ACTIVE_SYS))
+ != ERTS_PSFLG_SUSPENDED));
+ if (run_process) {
+ if (state & (ERTS_PSFLG_ACTIVE_SYS
+ | ERTS_PSFLG_DIRTY_ACTIVE_SYS))
+ new |= psflg_running_sys;
+ else
+ new |= psflg_running;
}
state = erts_smp_atomic32_cmpxchg_relb(&p->state, new, exp);
if (state == exp) {
- if ((state & (ERTS_PSFLG_RUNNING
- | ERTS_PSFLG_RUNNING_SYS
- | ERTS_PSFLG_FREE))
- || ((state & (ERTS_PSFLG_SUSPENDED
- | ERTS_PSFLG_PENDING_EXIT
- | ERTS_PSFLG_ACTIVE_SYS))
- == ERTS_PSFLG_SUSPENDED)) {
+ if (!run_process) {
if (proxy_p) {
free_proxy_proc(proxy_p);
proxy_p = NULL;
@@ -9815,6 +10017,8 @@ Process *schedule(Process *p, int calls)
/* free and not queued by proxy */
erts_proc_dec_refc(p);
}
+ if (!is_normal_sched)
+ erts_proc_dec_refc(p);
goto pick_next_process;
}
state = new;
@@ -9826,10 +10030,16 @@ Process *schedule(Process *p, int calls)
esdp->current_process = p;
+ calls = 0;
+ reds = context_reds;
+
+ erts_smp_runq_unlock(rq);
+
}
+ ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_EMULATOR);
+
#ifdef ERTS_SMP
- erts_smp_runq_unlock(rq);
if (flags & ERTS_RUNQ_FLG_PROTECTED)
(void) ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED);
@@ -9842,15 +10052,10 @@ Process *schedule(Process *p, int calls)
if (erts_sched_stat.enabled) {
int prio;
- UWord old = ERTS_PROC_SCHED_ID(p,
- (ERTS_PROC_LOCK_MAIN
- | ERTS_PROC_LOCK_STATUS),
- (UWord) esdp->no);
+ UWord old = ERTS_PROC_SCHED_ID(p, (UWord) esdp->no);
int migrated = old && old != esdp->no;
-#ifdef ERTS_DIRTY_SCHEDULERS
- ASSERT(!ERTS_SCHEDULER_IS_DIRTY(esdp));
-#endif
+ ASSERT(is_normal_sched);
prio = (int) ERTS_PSFLGS_GET_USR_PRIO(state);
@@ -9864,64 +10069,118 @@ Process *schedule(Process *p, int calls)
erts_smp_spin_unlock(&erts_sched_stat.lock);
}
- if (ERTS_PROC_PENDING_EXIT(p)) {
+ state = erts_smp_atomic32_read_nob(&p->state);
+
+#ifndef ERTS_DIRTY_SCHEDULERS
+ ASSERT(!p->scheduler_data);
+ p->scheduler_data = esdp;
+#else /* ERTS_DIRTY_SCHEDULERS */
+ if (is_normal_sched) {
+ if ((!!(state & ERTS_PSFLGS_DIRTY_WORK))
+ & (!(state & ERTS_PSFLG_ACTIVE_SYS))) {
+ /* Migrate to dirty scheduler... */
+ sunlock_sched_out_proc:
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ goto sched_out_proc;
+ }
+ ASSERT(!p->scheduler_data);
+ p->scheduler_data = esdp;
+ }
+ else {
+ if (state & (ERTS_PSFLG_ACTIVE_SYS
+ | ERTS_PSFLG_PENDING_EXIT
+ | ERTS_PSFLG_EXITING)) {
+ /*
+ * IMPORTANT! We need to take care of
+ * scheduled check-process-code requests
+ * before continuing with dirty execution!
+ */
+ /* Migrate to normal scheduler... */
+ goto sunlock_sched_out_proc;
+ }
+ if ((state & ERTS_PSFLG_DIRTY_ACTIVE_SYS)
+ && rq == ERTS_DIRTY_IO_RUNQ) {
+ /* Migrate to dirty cpu scheduler... */
+ goto sunlock_sched_out_proc;
+ }
+
+ ASSERT((state & ERTS_PSFLG_DIRTY_ACTIVE_SYS)
+ || *p->i == (BeamInstr) em_call_nif);
+
+ ASSERT(rq == ERTS_DIRTY_CPU_RUNQ
+ ? (state & (ERTS_PSFLG_DIRTY_CPU_PROC
+ | ERTS_PSFLG_DIRTY_ACTIVE_SYS))
+ : (rq == ERTS_DIRTY_IO_RUNQ
+ && (state & ERTS_PSFLG_DIRTY_IO_PROC)));
+ }
+#endif
+
+ if (state & ERTS_PSFLG_PENDING_EXIT) {
erts_handle_pending_exit(p,
ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS);
state = erts_smp_atomic32_read_nob(&p->state);
}
- ASSERT(!p->scheduler_data);
- p->scheduler_data = esdp;
-#endif
- reds = context_reds;
- if (IS_TRACED(p)) {
+#endif /* ERTS_SMP */
+
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+
+ /* Clear tracer if it has been removed */
+ if (IS_TRACED(p) && erts_is_tracer_proc_enabled(
+ p, ERTS_PROC_LOCK_MAIN, &p->common)) {
+
if (state & ERTS_PSFLG_EXITING) {
if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED_EXIT))
- trace_sched(p, am_in_exiting);
+ trace_sched(p, ERTS_PROC_LOCK_MAIN, am_in_exiting);
}
else {
- if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED))
- trace_sched(p, am_in);
- else if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED_PROCS))
- trace_virtual_sched(p, am_in);
+ if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED) ||
+ ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED_PROCS))
+ trace_sched(p, ERTS_PROC_LOCK_MAIN, am_in);
}
if (IS_TRACED_FL(p, F_TRACE_CALLS)) {
erts_schedule_time_break(p, ERTS_BP_CALL_TIME_SCHEDULE_IN);
}
}
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
-
-#ifdef ERTS_SMP
- if (is_not_nil(ERTS_TRACER_PROC(p)))
- erts_check_my_tracer_proc(p);
-#endif
-
- if (state & ERTS_PSFLG_RUNNING_SYS) {
- reds -= execute_sys_tasks(p, &state, reds);
- if (reds <= 0
+ if (state & (ERTS_PSFLG_RUNNING_SYS
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
+ /*
+ * GC is normally never delayed when a process
+ * is scheduled out, but might be when executing
+ * hand written beam assembly in
+ * prim_eval:'receive'. If GC is delayed we are
+ * not allowed to execute system tasks.
+ */
+ if (!(p->flags & F_DELAY_GC)) {
+ int cost = execute_sys_tasks(p, &state, reds);
+ calls += cost;
+ reds -= cost;
+ if (reds <= 0
#ifdef ERTS_DIRTY_SCHEDULERS
- || (state & (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC))
+ || !is_normal_sched
+ || (state & ERTS_PSFLGS_DIRTY_WORK)
#endif
- ) {
- p->fcalls = reds;
- goto sched_out_proc;
+ ) {
+ goto sched_out_proc;
+ }
}
- ASSERT(state & ERTS_PSFLG_RUNNING_SYS);
- ASSERT(!(state & ERTS_PSFLG_RUNNING));
+ ASSERT(state & psflg_running_sys);
+ ASSERT(!(state & psflg_running));
while (1) {
erts_aint32_t n, e;
if (((state & (ERTS_PSFLG_SUSPENDED
| ERTS_PSFLG_ACTIVE)) != ERTS_PSFLG_ACTIVE)
- && !(state & ERTS_PSFLG_EXITING))
+ && !(state & ERTS_PSFLG_EXITING)) {
goto sched_out_proc;
+ }
n = e = state;
- n &= ~ERTS_PSFLG_RUNNING_SYS;
- n |= ERTS_PSFLG_RUNNING;
+ n &= ~psflg_running_sys;
+ n |= psflg_running;
state = erts_smp_atomic32_cmpxchg_mb(&p->state, n, e);
if (state == e) {
@@ -9929,18 +10188,18 @@ Process *schedule(Process *p, int calls)
break;
}
- ASSERT(state & ERTS_PSFLG_RUNNING_SYS);
- ASSERT(!(state & ERTS_PSFLG_RUNNING));
+ ASSERT(state & psflg_running_sys);
+ ASSERT(!(state & psflg_running));
}
}
- if (!(state & ERTS_PSFLG_EXITING)
- && ((FLAGS(p) & F_FORCE_GC)
- || (MSO(p).overhead > BIN_VHEAP_SZ(p)))) {
- reds -= erts_garbage_collect(p, 0, p->arg_reg, p->arity);
- if (reds <= 0) {
- p->fcalls = reds;
- goto sched_out_proc;
+ if (ERTS_IS_GC_DESIRED(p) && !ERTS_SCHEDULER_IS_DIRTY_IO(esdp)) {
+ if (!(state & ERTS_PSFLG_EXITING) && !(p->flags & (F_DELAY_GC|F_DISABLE_GC))) {
+ int cost = scheduler_gc_proc(p, reds);
+ calls += cost;
+ reds -= cost;
+ if (reds <= 0)
+ goto sched_out_proc;
}
}
@@ -9948,8 +10207,9 @@ Process *schedule(Process *p, int calls)
free_proxy_proc(proxy_p);
proxy_p = NULL;
}
-
+
p->fcalls = reds;
+
ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
/* Never run a suspended process */
@@ -9980,7 +10240,7 @@ notify_sys_task_executed(Process *c_p, ErtsProcSysTask *st, Eterm st_result)
if (rp) {
ErtsProcLocks rp_locks;
ErlOffHeap *ohp;
- ErlHeapFragment* bp;
+ ErtsMessage *mp;
Eterm *hp, msg, req_id, result;
Uint st_result_sz, hsz;
#ifdef DEBUG
@@ -9992,11 +10252,7 @@ notify_sys_task_executed(Process *c_p, ErtsProcSysTask *st, Eterm st_result)
st_result_sz = is_immed(st_result) ? 0 : size_object(st_result);
hsz = st->req_id_sz + st_result_sz + 4 /* 3-tuple */;
- hp = erts_alloc_message_heap(hsz,
- &bp,
- &ohp,
- rp,
- &rp_locks);
+ mp = erts_alloc_message_heap(rp, &rp_locks, hsz, &hp, &ohp);
#ifdef DEBUG
hp_start = hp;
@@ -10021,7 +10277,7 @@ notify_sys_task_executed(Process *c_p, ErtsProcSysTask *st, Eterm st_result)
ASSERT(hp_start + hsz == hp);
#endif
- erts_queue_message(rp, &rp_locks, bp, msg, NIL);
+ erts_queue_message(rp, rp_locks, mp, msg, c_p->common.id);
if (c_p == rp)
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
@@ -10189,8 +10445,7 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds)
{
int garbage_collected = 0;
erts_aint32_t state = *statep;
- int max_reds = in_reds;
- int reds = 0;
+ int reds = in_reds;
int qmask = 0;
ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == ERTS_PROC_LOCK_MAIN);
@@ -10218,31 +10473,69 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds)
if (c_p->flags & F_DISABLE_GC) {
save_gc_task(c_p, st, st_prio);
st = NULL;
- reds++;
+ reds--;
}
else {
- if (!garbage_collected) {
+ if (!garbage_collected
+ && !(c_p->flags & F_HIBERNATED)) {
FLAGS(c_p) |= F_NEED_FULLSWEEP;
- reds += erts_garbage_collect(c_p,
- 0,
- c_p->arg_reg,
- c_p->arity);
+ reds -= scheduler_gc_proc(c_p, reds);
garbage_collected = 1;
}
st_res = am_true;
}
break;
- case ERTS_PSTT_CPC:
+ case ERTS_PSTT_CPC: {
+ int fcalls;
+ int cpc_reds = 0;
+ if (!ERTS_PROC_GET_SAVED_CALLS_BUF(c_p))
+ fcalls = reds;
+ else
+ fcalls = reds - CONTEXT_REDS;
st_res = erts_check_process_code(c_p,
st->arg[0],
- st->arg[1] == am_true,
- &reds);
+ unsigned_val(st->arg[1]),
+ &cpc_reds,
+ fcalls);
+ reds -= cpc_reds;
+ if (is_non_value(st_res)) {
+ /* Needed gc, but gc was disabled */
+ save_gc_task(c_p, st, st_prio);
+ st = NULL;
+ }
+ break;
+ }
+#ifdef ERTS_NEW_PURGE_STRATEGY
+ case ERTS_PSTT_CLA: {
+ int fcalls;
+ int cla_reds = 0;
+ if (!ERTS_PROC_GET_SAVED_CALLS_BUF(c_p))
+ fcalls = reds;
+ else
+ fcalls = reds - CONTEXT_REDS;
+ st_res = erts_proc_copy_literal_area(c_p,
+ &cla_reds,
+ fcalls,
+ st->arg[0] == am_true);
+ reds -= cla_reds;
if (is_non_value(st_res)) {
/* Needed gc, but gc was disabled */
save_gc_task(c_p, st, st_prio);
st = NULL;
}
break;
+ }
+#endif
+ case ERTS_PSTT_COHMQ:
+ reds -= erts_complete_off_heap_message_queue_change(c_p);
+ st_res = am_true;
+ break;
+#ifdef ERTS_SMP
+ case ERTS_PSTT_FTMQ:
+ reds -= erts_flush_trace_messages(c_p, ERTS_PROC_LOCK_MAIN);
+ st_res = am_true;
+ break;
+#endif
default:
ERTS_INTERNAL_ERROR("Invalid process sys task type");
st_res = am_false;
@@ -10252,11 +10545,14 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds)
reds += notify_sys_task_executed(c_p, st, st_res);
state = erts_smp_atomic32_read_acqb(&c_p->state);
- } while (qmask && reds < max_reds);
+ } while (qmask && reds > 0);
*statep = state;
- return reds;
+ if (in_reds < reds)
+ return in_reds;
+
+ return in_reds - reds;
}
static int
@@ -10280,11 +10576,21 @@ cleanup_sys_tasks(Process *c_p, erts_aint32_t in_state, int in_reds)
switch (st->type) {
case ERTS_PSTT_GC:
- st_res = am_false;
- break;
case ERTS_PSTT_CPC:
+ case ERTS_PSTT_COHMQ:
st_res = am_false;
break;
+#ifdef ERTS_NEW_PURGE_STRATEGY
+ case ERTS_PSTT_CLA:
+ st_res = am_ok;
+ break;
+#endif
+#ifdef ERTS_SMP
+ case ERTS_PSTT_FTMQ:
+ reds -= erts_flush_trace_messages(c_p, ERTS_PROC_LOCK_MAIN);
+ st_res = am_true;
+ break;
+#endif
default:
ERTS_INTERNAL_ERROR("Invalid process sys task type");
st_res = am_false;
@@ -10299,24 +10605,83 @@ cleanup_sys_tasks(Process *c_p, erts_aint32_t in_state, int in_reds)
return reds;
}
-BIF_RETTYPE
-erts_internal_request_system_task_3(BIF_ALIST_3)
+#ifdef ERTS_DIRTY_SCHEDULERS
+
+static BIF_RETTYPE
+dispatch_system_task(Process *c_p, erts_aint_t fail_state,
+ ErtsProcSysTask *st, Eterm target,
+ Eterm prio, Eterm operation)
+{
+ Process *rp;
+ ErtsProcLocks rp_locks = 0;
+ ErlOffHeap *ohp;
+ ErtsMessage *mp;
+ Eterm *hp, msg;
+ Uint hsz, osz;
+ BIF_RETTYPE ret;
+
+ switch (st->type) {
+ case ERTS_PSTT_CPC:
+ rp = erts_dirty_process_code_checker;
+ ASSERT(fail_state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS));
+ if (c_p == rp) {
+ ERTS_BIF_PREP_RET(ret, am_dirty_execution);
+ return ret;
+ }
+ break;
+ default:
+ rp = NULL;
+ ERTS_INTERNAL_ERROR("Non-dispatchable system task");
+ break;
+ }
+
+ ERTS_BIF_PREP_RET(ret, am_ok);
+
+ /*
+ * Send message on the form: {Requester, Target, Operation}
+ */
+
+ ASSERT(is_immed(st->requester));
+ ASSERT(is_immed(target));
+ ASSERT(is_immed(prio));
+
+ osz = size_object(operation);
+ hsz = 5 /* 4-tuple */ + osz;
+
+ mp = erts_alloc_message_heap(rp, &rp_locks, hsz, &hp, &ohp);
+
+ msg = copy_struct(operation, osz, &hp, ohp);
+ msg = TUPLE4(hp, st->requester, target, prio, msg);
+
+ erts_queue_message(rp, rp_locks, mp, msg, st->requester);
+
+ if (rp_locks)
+ erts_smp_proc_unlock(rp, rp_locks);
+
+ return ret;
+}
+
+#endif
+
+static BIF_RETTYPE
+request_system_task(Process *c_p, Eterm requester, Eterm target,
+ Eterm priority, Eterm operation)
{
- Process *rp = erts_proc_lookup(BIF_ARG_1);
- ErtsProcSysTaskQs *stqs, *free_stqs = NULL;
+ BIF_RETTYPE ret;
+ Process *rp = erts_proc_lookup(target);
ErtsProcSysTask *st = NULL;
- erts_aint32_t prio, rp_state;
- int rp_locked;
+ erts_aint32_t prio, fail_state = ERTS_PSFLG_EXITING;
Eterm noproc_res, req_type;
- if (!rp && !is_internal_pid(BIF_ARG_1)) {
- if (!is_external_pid(BIF_ARG_1))
+ if (!rp && !is_internal_pid(target)) {
+ if (!is_external_pid(target))
goto badarg;
- if (external_pid_dist_entry(BIF_ARG_1) != erts_this_dist_entry)
+ if (external_pid_dist_entry(target) != erts_this_dist_entry)
goto badarg;
}
- switch (BIF_ARG_2) {
+ switch (priority) {
case am_max: prio = PRIORITY_MAX; break;
case am_high: prio = PRIORITY_HIGH; break;
case am_normal: prio = PRIORITY_NORMAL; break;
@@ -10324,11 +10689,11 @@ erts_internal_request_system_task_3(BIF_ALIST_3)
default: goto badarg;
}
- if (is_not_tuple(BIF_ARG_3))
+ if (is_not_tuple(operation))
goto badarg;
else {
int i;
- Eterm *tp = tuple_val(BIF_ARG_3);
+ Eterm *tp = tuple_val(operation);
Uint arity = arityval(*tp);
Eterm req_id;
Uint req_id_sz;
@@ -10363,11 +10728,10 @@ erts_internal_request_system_task_3(BIF_ALIST_3)
}
st = erts_alloc(ERTS_ALC_T_PROC_SYS_TSK,
ERTS_PROC_SYS_TASK_SIZE(tot_sz));
- st->next = st->prev = st; /* Prep for empty prio queue */
ERTS_INIT_OFF_HEAP(&st->off_heap);
hp = &st->heap[0];
- st->requester = BIF_P->common.id;
+ st->requester = requester;
st->reply_tag = req_type;
st->req_id_sz = req_id_sz;
st->req_id = req_id_sz == 0 ? req_id : copy_struct(req_id,
@@ -10395,118 +10759,211 @@ erts_internal_request_system_task_3(BIF_ALIST_3)
case am_check_process_code:
if (is_not_atom(st->arg[0]))
goto badarg;
- if (st->arg[1] != am_true && st->arg[1] != am_false)
+ if (is_not_small(st->arg[1]) || (unsigned_val(st->arg[1]) & ~ERTS_CPC_ALL))
goto badarg;
noproc_res = am_false;
st->type = ERTS_PSTT_CPC;
if (!rp)
goto noproc;
+#ifdef ERTS_DIRTY_SCHEDULERS
+ /*
+ * If the process should start executing dirty
+ * code it is important that this task is
+ * aborted. Therefore this strict fail state...
+ */
+ fail_state |= (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS);
+#endif
+ break;
+
+#ifdef ERTS_NEW_PURGE_STRATEGY
+ case am_copy_literals:
+ if (st->arg[0] != am_true && st->arg[0] != am_false)
+ goto badarg;
+ st->type = ERTS_PSTT_CLA;
+ noproc_res = am_ok;
+ if (!rp)
+ goto noproc;
break;
+#endif
default:
goto badarg;
}
- rp_state = erts_smp_atomic32_read_nob(&rp->state);
+ if (!schedule_process_sys_task(rp, prio, st, &fail_state)) {
+ Eterm failure;
+ if (fail_state & ERTS_PSFLG_EXITING) {
+ noproc:
+ failure = noproc_res;
+ }
+#ifdef ERTS_DIRTY_SCHEDULERS
+ else if (fail_state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
+ ret = dispatch_system_task(c_p, fail_state, st,
+ target, priority, operation);
+ goto cleanup_return;
+ }
+#endif
+ else {
+ ERTS_INTERNAL_ERROR("Unknown failure schedule_process_sys_task()");
+ failure = am_internal_error;
+ }
+ notify_sys_task_executed(c_p, st, failure);
+ }
- rp_locked = 0;
+ ERTS_BIF_PREP_RET(ret, am_ok);
- free_stqs = NULL;
- if (rp_state & ERTS_PSFLG_ACTIVE_SYS)
- stqs = NULL;
- else {
- alloc_qs:
- stqs = proc_sys_task_queues_alloc();
- stqs->qmask = 1 << prio;
- stqs->ncount = 0;
- stqs->q[PRIORITY_MAX] = NULL;
- stqs->q[PRIORITY_HIGH] = NULL;
- stqs->q[PRIORITY_NORMAL] = NULL;
- stqs->q[PRIORITY_LOW] = NULL;
- stqs->q[prio] = st;
- }
+ return ret;
- if (!rp_locked) {
- rp_locked = 1;
- erts_smp_proc_lock(rp, ERTS_PROC_LOCK_STATUS);
+badarg:
- rp_state = erts_smp_atomic32_read_nob(&rp->state);
- if (rp_state & ERTS_PSFLG_EXITING) {
- erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_STATUS);
- rp = NULL;
- free_stqs = stqs;
- goto noproc;
- }
- }
+ ERTS_BIF_PREP_ERROR(ret, c_p, BADARG);
- if (!rp->sys_task_qs) {
- if (stqs)
- rp->sys_task_qs = stqs;
- else
- goto alloc_qs;
- }
- else {
- if (stqs)
- free_stqs = stqs;
- stqs = rp->sys_task_qs;
- if (!stqs->q[prio]) {
- stqs->q[prio] = st;
- stqs->qmask |= 1 << prio;
- }
- else {
- st->next = stqs->q[prio];
- st->prev = stqs->q[prio]->prev;
- st->next->prev = st;
- st->prev->next = st;
- ASSERT(stqs->qmask & (1 << prio));
- }
+#ifdef ERTS_DIRTY_SCHEDULERS
+cleanup_return:
+#endif
+
+ if (st) {
+ erts_cleanup_offheap(&st->off_heap);
+ erts_free(ERTS_ALC_T_PROC_SYS_TSK, st);
}
- if (ERTS_PSFLGS_GET_ACT_PRIO(rp_state) > prio) {
- erts_aint32_t n, a, e;
- /* Need to elevate actual prio */
+ return ret;
+}
- a = rp_state;
- do {
- if (ERTS_PSFLGS_GET_ACT_PRIO(a) <= prio) {
- n = a;
- break;
- }
- n = e = a;
- n &= ~ERTS_PSFLGS_ACT_PRIO_MASK;
- n |= (prio << ERTS_PSFLGS_ACT_PRIO_OFFSET);
- a = erts_smp_atomic32_cmpxchg_nob(&rp->state, n, e);
- } while (a != e);
- rp_state = n;
+BIF_RETTYPE
+erts_internal_request_system_task_3(BIF_ALIST_3)
+{
+ return request_system_task(BIF_P, BIF_P->common.id,
+ BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
+}
+
+BIF_RETTYPE
+erts_internal_request_system_task_4(BIF_ALIST_4)
+{
+ return request_system_task(BIF_P, BIF_ARG_1,
+ BIF_ARG_2, BIF_ARG_3, BIF_ARG_4);
+}
+
+static void
+erts_schedule_generic_sys_task(Eterm pid, ErtsProcSysTaskType type)
+{
+ Process *rp = erts_proc_lookup(pid);
+ if (rp) {
+ ErtsProcSysTask *st;
+ erts_aint32_t state, fail_state;
+ int i;
+
+ st = erts_alloc(ERTS_ALC_T_PROC_SYS_TSK,
+ ERTS_PROC_SYS_TASK_SIZE(0));
+ st->type = type;
+ st->requester = NIL;
+ st->reply_tag = NIL;
+ st->req_id = NIL;
+ st->req_id_sz = 0;
+ for (i = 0; i < ERTS_MAX_PROC_SYS_TASK_ARGS; i++)
+ st->arg[i] = NIL;
+ ERTS_INIT_OFF_HEAP(&st->off_heap);
+ state = erts_smp_atomic32_read_nob(&rp->state);
+
+ fail_state = ERTS_PSFLG_EXITING;
+
+ if (!schedule_process_sys_task(rp, ERTS_PSFLGS_GET_USR_PRIO(state),
+ st, &fail_state))
+ erts_free(ERTS_ALC_T_PROC_SYS_TSK, st);
}
+}
- /*
- * schedule_process_sys_task() unlocks status
- * lock on process.
- */
- schedule_process_sys_task(rp, rp_state, NULL);
- if (free_stqs)
- proc_sys_task_queues_free(free_stqs);
+void
+erts_schedule_complete_off_heap_message_queue_change(Eterm pid)
+{
+ erts_schedule_generic_sys_task(pid, ERTS_PSTT_COHMQ);
+}
- BIF_RET(am_ok);
+#ifdef ERTS_DIRTY_SCHEDULERS
-noproc:
+static void
+flush_dirty_trace_messages(void *vpid)
+{
+ Process *proc;
+ Eterm pid;
+#ifdef ARCH_64
+ pid = (Eterm) vpid;
+#else
+ pid = *((Eterm *) vpid);
+ erts_free(ERTS_ALC_T_DIRTY_SL, vpid);
+#endif
- notify_sys_task_executed(BIF_P, st, noproc_res);
- if (free_stqs)
- proc_sys_task_queues_free(free_stqs);
- BIF_RET(am_ok);
+ proc = erts_proc_lookup(pid);
+ if (proc)
+ (void) erts_flush_trace_messages(proc, 0);
+}
-badarg:
+#endif /* ERTS_DIRTY_SCHEDULERS */
- if (st) {
- erts_cleanup_offheap(&st->off_heap);
- erts_free(ERTS_ALC_T_PROC_SYS_TSK, st);
+void
+erts_schedule_flush_trace_messages(Process *proc, int force_on_proc)
+{
+#ifdef ERTS_SMP
+ ErtsThrPrgrDelayHandle dhndl;
+#endif
+ Eterm pid = proc->common.id;
+
+#ifdef ERTS_DIRTY_SCHEDULERS
+ erts_aint32_t state;
+
+ if (!force_on_proc) {
+ state = erts_smp_atomic32_read_nob(&proc->state);
+ if (state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
+ goto sched_flush_dirty;
+ }
}
- if (free_stqs)
- proc_sys_task_queues_free(free_stqs);
- BIF_ERROR(BIF_P, BADARG);
+#endif
+
+#ifdef ERTS_SMP
+ dhndl = erts_thr_progress_unmanaged_delay();
+#endif
+
+ erts_schedule_generic_sys_task(pid, ERTS_PSTT_FTMQ);
+
+#ifdef ERTS_SMP
+ erts_thr_progress_unmanaged_continue(dhndl);
+#endif
+
+#ifdef ERTS_DIRTY_SCHEDULERS
+ if (!force_on_proc) {
+ state = erts_smp_atomic32_read_mb(&proc->state);
+ if (state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
+ void *vargp;
+
+ sched_flush_dirty:
+ /*
+ * We traced 'proc' from another thread than
+ * it is executing on, and it is executing
+ * on a dirty scheduler. It might take a
+ * significant amount of time before it is
+ * scheduled out (where it gets opportunity
+ * to flush messages). We therefore schedule
+ * the flush on the first ordinary scheduler.
+ */
+
+#ifdef ARCH_64
+ vargp = (void *) pid;
+#else
+ {
+ Eterm *argp = erts_alloc(ERTS_ALC_T_DIRTY_SL, sizeof(Eterm));
+ *argp = pid;
+ vargp = (void *) argp;
+ }
+#endif
+ erts_schedule_misc_aux_work(1, flush_dirty_trace_messages, vargp);
+ }
+ }
+#endif
}
static void
@@ -10528,7 +10985,7 @@ save_gc_task(Process *c_p, ErtsProcSysTask *st, int prio)
qs->q[PRIORITY_NORMAL] = NULL;
qs->q[PRIORITY_LOW] = NULL;
qs->q[prio] = st;
- (void) ERTS_PROC_SET_DELAYED_GC_TASK_QS(c_p, ERTS_PROC_LOCK_MAIN, qs);
+ (void) ERTS_PROC_SET_DELAYED_GC_TASK_QS(c_p, qs);
}
else {
if (!qs->q[prio]) {
@@ -10546,7 +11003,10 @@ save_gc_task(Process *c_p, ErtsProcSysTask *st, int prio)
}
state = erts_smp_atomic32_read_nob(&c_p->state);
- ASSERT((ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS) & state);
+ ASSERT((ERTS_PSFLG_RUNNING
+ | ERTS_PSFLG_RUNNING_SYS
+ | ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS) & state);
while (!(state & ERTS_PSFLG_DELAYED_SYS)
|| prio < ERTS_PSFLGS_GET_ACT_PRIO(state)) {
@@ -10675,7 +11135,7 @@ erts_set_gc_state(Process *c_p, int enable)
erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_STATUS);
- (void) ERTS_PROC_SET_DELAYED_GC_TASK_QS(c_p, ERTS_PROC_LOCK_MAIN, NULL);
+ (void) ERTS_PROC_SET_DELAYED_GC_TASK_QS(c_p, NULL);
if (dgc_tsk_qs)
proc_sys_task_queues_free(dgc_tsk_qs);
@@ -10871,6 +11331,8 @@ erts_get_exact_total_reductions(Process *c_p, Uint *redsp, Uint *diffp)
erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
}
+static void delete_process(Process* p);
+
void
erts_free_proc(Process *p)
{
@@ -10879,6 +11341,8 @@ erts_free_proc(Process *p)
#endif
ASSERT(erts_smp_atomic32_read_nob(&p->state) & ERTS_PSFLG_FREE);
ASSERT(0 == erts_proc_read_refc(p));
+ if (p->flags & F_DELAYED_DEL_PROC)
+ delete_process(p);
erts_free(ERTS_ALC_T_PROC, (void *) p);
}
@@ -10894,6 +11358,9 @@ static void early_init_process_struct(void *varg, Eterm data)
Process *proc = arg->proc;
proc->common.id = make_internal_pid(data);
+#ifdef ERTS_DIRTY_SCHEDULERS
+ erts_smp_atomic32_init_nob(&proc->dirty_state, 0);
+#endif
erts_smp_atomic32_init_relb(&proc->state, arg->state);
#ifdef ERTS_SMP
@@ -10954,6 +11421,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
Eterm args, /* Arguments for function (must be well-formed list). */
ErlSpawnOpts* so) /* Options for spawn. */
{
+ Uint flags = 0;
ErtsRunQueue *rq = NULL;
Process *p;
Sint arity; /* Number of arguments. */
@@ -10963,10 +11431,13 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
Eterm res = THE_NON_VALUE;
erts_aint32_t state = 0;
erts_aint32_t prio = (erts_aint32_t) PRIORITY_NORMAL;
+ ErtsProcLocks locks = ERTS_PROC_LOCKS_ALL;
+#ifdef SHCOPY_SPAWN
+ erts_shcopy_t info;
+ INITIALIZE_SHCOPY(info);
+#endif
-#ifdef ERTS_SMP
erts_smp_proc_lock(parent, ERTS_PROC_LOCKS_ALL_MINOR);
-#endif
/*
* Check for errors.
@@ -10991,6 +11462,17 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
state |= (((prio & ERTS_PSFLGS_PRIO_MASK) << ERTS_PSFLGS_ACT_PRIO_OFFSET)
| ((prio & ERTS_PSFLGS_PRIO_MASK) << ERTS_PSFLGS_USR_PRIO_OFFSET));
+ if (so->flags & SPO_OFF_HEAP_MSGQ) {
+ state |= ERTS_PSFLG_OFF_HEAP_MSGQ;
+ flags |= F_OFF_HEAP_MSGQ;
+ }
+ else if (so->flags & SPO_ON_HEAP_MSGQ) {
+ state |= ERTS_PSFLG_ON_HEAP_MSGQ;
+ flags |= F_ON_HEAP_MSGQ;
+ }
+
+ ASSERT((flags & F_ON_HEAP_MSGQ) || (flags & F_OFF_HEAP_MSGQ));
+
if (!rq)
rq = erts_get_runq_proc(parent);
@@ -11003,17 +11485,19 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
goto error;
}
-#ifdef BM_COUNTERS
- processes_busy++;
-#endif
- BM_COUNT(processes_spawned);
+ ASSERT((erts_smp_atomic32_read_nob(&p->state)
+ & ERTS_PSFLG_ON_HEAP_MSGQ)
+ || (erts_smp_atomic32_read_nob(&p->state)
+ & ERTS_PSFLG_OFF_HEAP_MSGQ));
- BM_SWAP_TIMER(system,size);
+#ifdef SHCOPY_SPAWN
+ arg_size = copy_shared_calculate(args, &info);
+#else
arg_size = size_object(args);
- BM_SWAP_TIMER(size,system);
+#endif
heap_need = arg_size;
- p->flags = erts_default_process_flags;
+ p->flags = flags;
p->static_flags = 0;
if (so->flags & SPO_SYSTEM_PROC)
@@ -11022,9 +11506,13 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->min_heap_size = so->min_heap_size;
p->min_vheap_size = so->min_vheap_size;
p->max_gen_gcs = so->max_gen_gcs;
+ MAX_HEAP_SIZE_SET(p, so->max_heap_size);
+ MAX_HEAP_SIZE_FLAGS_SET(p, so->max_heap_flags);
} else {
p->min_heap_size = H_MIN_SIZE;
p->min_vheap_size = BIN_VH_MIN_SIZE;
+ MAX_HEAP_SIZE_SET(p, H_MAX_SIZE);
+ MAX_HEAP_SIZE_FLAGS_SET(p, H_MAX_FLAGS);
p->max_gen_gcs = (Uint16) erts_smp_atomic32_read_nob(&erts_max_gen_gcs);
}
p->schedule_count = 0;
@@ -11062,12 +11550,13 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->stop = p->hend = p->heap + sz;
p->htop = p->heap;
p->heap_sz = sz;
+ p->abandoned_heap = NULL;
+ p->live_hf_end = ERTS_INVALID_HFRAG_PTR;
p->catches = 0;
p->bin_vheap_sz = p->min_vheap_size;
p->bin_old_vheap_sz = p->min_vheap_size;
p->bin_old_vheap = 0;
- p->bin_vheap_mature = 0;
p->sys_task_qs = NULL;
@@ -11082,13 +11571,12 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->max_arg_reg = sizeof(p->def_arg_reg)/sizeof(p->def_arg_reg[0]);
p->arg_reg[0] = mod;
p->arg_reg[1] = func;
- BM_STOP_TIMER(system);
- BM_MESSAGE(args,p,parent);
- BM_START_TIMER(system);
- BM_SWAP_TIMER(system,copy);
+#ifdef SHCOPY_SPAWN
+ p->arg_reg[2] = copy_shared_perform(args, arg_size, &info, &p->htop, &p->off_heap);
+ DESTROY_SHCOPY(info);
+#else
p->arg_reg[2] = copy_struct(args, arg_size, &p->htop, &p->off_heap);
- BM_MESSAGE_COPIED(arg_size);
- BM_SWAP_TIMER(copy,system);
+#endif
p->arity = 3;
p->fvalue = NIL;
@@ -11116,7 +11604,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
: STORE_NC(&p->htop, &p->off_heap, parent->group_leader);
}
- erts_get_default_tracing(&ERTS_TRACE_FLAGS(p), &ERTS_TRACER_PROC(p));
+ erts_get_default_proc_tracing(&ERTS_TRACE_FLAGS(p), &ERTS_TRACER(p));
p->msg.first = NULL;
p->msg.last = &p->msg.first;
@@ -11132,8 +11620,9 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->accessor_bif_timers = NULL;
#endif
p->mbuf = NULL;
+ p->msg_frag = NULL;
p->mbuf_sz = 0;
- p->psd = NULL;
+ erts_smp_atomic_init_nob(&p->psd, (erts_aint_t) NULL);
p->dictionary = NULL;
p->seq_trace_lastcnt = 0;
p->seq_trace_clock = 0;
@@ -11151,21 +11640,60 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->last_old_htop = NULL;
#endif
+#ifdef ERTS_SMP
+ p->trace_msg_q = NULL;
+ p->scheduler_data = NULL;
+ p->suspendee = NIL;
+ p->pending_suspenders = NULL;
+ p->pending_exit.reason = THE_NON_VALUE;
+ p->pending_exit.bp = NULL;
+#endif
+
+#if !defined(NO_FPE_SIGNALS) || defined(HIPE)
+ p->fp_exception = 0;
+#endif
+
if (IS_TRACED(parent)) {
if (ERTS_TRACE_FLAGS(parent) & F_TRACE_SOS) {
ERTS_TRACE_FLAGS(p) |= (ERTS_TRACE_FLAGS(parent) & TRACEE_FLAGS);
- ERTS_TRACER_PROC(p) = ERTS_TRACER_PROC(parent);
- }
- if (ARE_TRACE_FLAGS_ON(parent, F_TRACE_PROCS)) {
- trace_proc_spawn(parent, p->common.id, mod, func, args);
+ erts_tracer_replace(&p->common, ERTS_TRACER(parent));
}
- if (ERTS_TRACE_FLAGS(parent) & F_TRACE_SOS1) {
+ if (ERTS_TRACE_FLAGS(parent) & F_TRACE_SOS1) {
/* Overrides TRACE_CHILDREN */
ERTS_TRACE_FLAGS(p) |= (ERTS_TRACE_FLAGS(parent) & TRACEE_FLAGS);
- ERTS_TRACER_PROC(p) = ERTS_TRACER_PROC(parent);
+ erts_tracer_replace(&p->common, ERTS_TRACER(parent));
ERTS_TRACE_FLAGS(p) &= ~(F_TRACE_SOS1 | F_TRACE_SOS);
ERTS_TRACE_FLAGS(parent) &= ~(F_TRACE_SOS1 | F_TRACE_SOS);
}
+ if (so->flags & SPO_LINK && ERTS_TRACE_FLAGS(parent) & (F_TRACE_SOL|F_TRACE_SOL1)) {
+ ERTS_TRACE_FLAGS(p) |= (ERTS_TRACE_FLAGS(parent)&TRACEE_FLAGS);
+ erts_tracer_replace(&p->common, ERTS_TRACER(parent));
+ if (ERTS_TRACE_FLAGS(parent) & F_TRACE_SOL1) {/*maybe override*/
+ ERTS_TRACE_FLAGS(p) &= ~(F_TRACE_SOL1 | F_TRACE_SOL);
+ ERTS_TRACE_FLAGS(parent) &= ~(F_TRACE_SOL1 | F_TRACE_SOL);
+ }
+ }
+ if (ARE_TRACE_FLAGS_ON(parent, F_TRACE_PROCS)) {
+ locks &= ~(ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ erts_smp_proc_unlock(parent, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ trace_proc_spawn(parent, am_spawn, p->common.id, mod, func, args);
+ if (so->flags & SPO_LINK)
+ trace_proc(parent, locks, parent, am_link, p->common.id);
+ }
+ }
+
+ if (IS_TRACED_FL(p, F_TRACE_PROCS)) {
+ if ((locks & (ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE))
+ == (ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE)) {
+ /* This happens when parent was not traced, but child is */
+ locks &= ~(ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ erts_smp_proc_unlock(parent, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ }
+ trace_proc_spawn(p, am_spawned, parent->common.id, mod, func, args);
+ if (so->flags & SPO_LINK)
+ trace_proc(p, locks, p, am_getting_linked, parent->common.id);
}
/*
@@ -11176,10 +11704,6 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
#ifdef DEBUG
int ret;
#endif
- if (IS_TRACED_FL(parent, F_TRACE_PROCS)) {
- trace_proc(parent, parent, am_link, p->common.id);
- }
-
#ifdef DEBUG
ret = erts_add_link(&ERTS_P_LINKS(parent), LINK_PID, p->common.id);
ASSERT(ret == 0);
@@ -11190,17 +11714,6 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
erts_add_link(&ERTS_P_LINKS(p), LINK_PID, parent->common.id);
#endif
- if (IS_TRACED(parent)) {
- if (ERTS_TRACE_FLAGS(parent) & (F_TRACE_SOL|F_TRACE_SOL1)) {
- ERTS_TRACE_FLAGS(p) |= (ERTS_TRACE_FLAGS(parent)&TRACEE_FLAGS);
- ERTS_TRACER_PROC(p) = ERTS_TRACER_PROC(parent); /*maybe steal*/
-
- if (ERTS_TRACE_FLAGS(parent) & F_TRACE_SOL1) {/*maybe override*/
- ERTS_TRACE_FLAGS(p) &= ~(F_TRACE_SOL1 | F_TRACE_SOL);
- ERTS_TRACE_FLAGS(parent) &= ~(F_TRACE_SOL1 | F_TRACE_SOL);
- }
- }
- }
}
/*
@@ -11215,19 +11728,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
so->mref = mref;
}
-#ifdef ERTS_SMP
- p->scheduler_data = NULL;
- p->suspendee = NIL;
- p->pending_suspenders = NULL;
- p->pending_exit.reason = THE_NON_VALUE;
- p->pending_exit.bp = NULL;
-#endif
-
-#if !defined(NO_FPE_SIGNALS) || defined(HIPE)
- p->fp_exception = 0;
-#endif
-
- erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL);
+ erts_smp_proc_unlock(p, locks);
res = p->common.id;
@@ -11235,6 +11736,8 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
* Schedule process for execution.
*/
+ erts_smp_proc_unlock(parent, locks & ERTS_PROC_LOCKS_ALL_MINOR);
+
schedule_process(p, state, 0);
VERBOSE(DEBUG_PROCESSES, ("Created a new process: %T\n",p->common.id));
@@ -11248,10 +11751,11 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
DTRACE2(process_spawn, process_name, mfa);
}
#endif
+ return res;
error:
- erts_smp_proc_unlock(parent, ERTS_PROC_LOCKS_ALL_MINOR);
+ erts_smp_proc_unlock(parent, locks & ERTS_PROC_LOCKS_ALL_MINOR);
return res;
}
@@ -11267,6 +11771,8 @@ void erts_init_empty_process(Process *p)
p->stop = NULL;
p->hend = NULL;
p->heap = NULL;
+ p->abandoned_heap = NULL;
+ p->live_hf_end = ERTS_INVALID_HFRAG_PTR;
p->gen_gcs = 0;
p->max_gen_gcs = 0;
p->min_heap_size = 0;
@@ -11274,7 +11780,7 @@ void erts_init_empty_process(Process *p)
p->rcount = 0;
p->common.id = ERTS_INVALID_PID;
p->reds = 0;
- ERTS_TRACER_PROC(p) = NIL;
+ ERTS_TRACER(p) = erts_tracer_nil;
ERTS_TRACE_FLAGS(p) = F_INITIAL_TRACE_FLAGS;
p->group_leader = ERTS_INVALID_PID;
p->flags = 0;
@@ -11287,7 +11793,6 @@ void erts_init_empty_process(Process *p)
p->bin_old_vheap_sz = BIN_VH_MIN_SIZE;
p->bin_old_vheap = 0;
p->sys_task_qs = NULL;
- p->bin_vheap_mature = 0;
ERTS_PTMR_INIT(p);
p->next = NULL;
p->off_heap.first = NULL;
@@ -11299,8 +11804,9 @@ void erts_init_empty_process(Process *p)
p->old_htop = NULL;
p->old_heap = NULL;
p->mbuf = NULL;
+ p->msg_frag = NULL;
p->mbuf_sz = 0;
- p->psd = NULL;
+ erts_smp_atomic_init_nob(&p->psd, (erts_aint_t) NULL);
ERTS_P_MONITORS(p) = NULL;
ERTS_P_LINKS(p) = NULL; /* List of links */
p->nodes_monitors = NULL;
@@ -11356,6 +11862,9 @@ void erts_init_empty_process(Process *p)
p->last_old_htop = NULL;
#endif
+#ifdef ERTS_DIRTY_SCHEDULERS
+ erts_smp_atomic32_init_nob(&p->dirty_state, 0);
+#endif
erts_smp_atomic32_init_nob(&p->state, (erts_aint32_t) PRIORITY_NORMAL);
#ifdef ERTS_SMP
@@ -11387,9 +11896,11 @@ erts_debug_verify_clean_empty_process(Process* p)
ASSERT(p->htop == NULL);
ASSERT(p->stop == NULL);
ASSERT(p->hend == NULL);
+ ASSERT(p->abandoned_heap == NULL);
+ ASSERT(p->live_hf_end == ERTS_INVALID_HFRAG_PTR);
ASSERT(p->heap == NULL);
ASSERT(p->common.id == ERTS_INVALID_PID);
- ASSERT(ERTS_TRACER_PROC(p) == NIL);
+ ASSERT(ERTS_TRACER_IS_NIL(ERTS_TRACER(p)));
ASSERT(ERTS_TRACE_FLAGS(p) == F_INITIAL_TRACE_FLAGS);
ASSERT(p->group_leader == ERTS_INVALID_PID);
ASSERT(p->next == NULL);
@@ -11458,20 +11969,41 @@ erts_cleanup_empty_process(Process* p)
#endif
}
-/*
- * p must be the currently executing process.
- */
static void
delete_process(Process* p)
{
- ErlMessage* mp;
+ ErtsPSD *psd;
+ struct saved_calls *scb;
+ process_breakpoint_time_t *pbt;
+ void *nif_export;
VERBOSE(DEBUG_PROCESSES, ("Removing process: %T\n",p->common.id));
+ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] delete process: %p %p %p %p\n", p->common.id,
+ HEAP_START(p), HEAP_END(p), OLD_HEAP(p), OLD_HEND(p)));
+
+ scb = ERTS_PROC_SET_SAVED_CALLS_BUF(p, NULL);
+
+ if (scb) {
+ p->fcalls += CONTEXT_REDS; /* Reduction counting depends on this... */
+ erts_free(ERTS_ALC_T_CALLS_BUF, (void *) scb);
+ }
+
+ pbt = ERTS_PROC_SET_CALL_TIME(p, NULL);
+ if (pbt)
+ erts_free(ERTS_ALC_T_BPD, (void *) pbt);
+
+ nif_export = ERTS_PROC_SET_NIF_TRAP_EXPORT(p, NULL);
+ if (nif_export)
+ erts_destroy_nif_export(nif_export);
/* Cleanup psd */
- if (p->psd)
- erts_free(ERTS_ALC_T_PSD, p->psd);
+ psd = (ErtsPSD *) erts_smp_atomic_read_nob(&p->psd);
+
+ if (psd) {
+ erts_smp_atomic_set_nob(&p->psd, (erts_aint_t) NULL); /* Reduction counting depends on this... */
+ erts_free(ERTS_ALC_T_PSD, psd);
+ }
/* Clean binaries and funs */
erts_cleanup_offheap(&p->off_heap);
@@ -11490,16 +12022,12 @@ delete_process(Process* p)
* Release heaps. Clobber contents in DEBUG build.
*/
-
-#ifdef DEBUG
- sys_memset(p->heap, DEBUG_BAD_BYTE, p->heap_sz*sizeof(Eterm));
-#endif
-
#ifdef HIPE
hipe_delete_process(&p->hipe);
#endif
- ERTS_HEAP_FREE(ERTS_ALC_T_HEAP, (void*) p->heap, p->heap_sz*sizeof(Eterm));
+ erts_deallocate_young_generation(p);
+
if (p->old_heap != NULL) {
#ifdef DEBUG
@@ -11511,34 +12039,11 @@ delete_process(Process* p)
(p->old_hend-p->old_heap)*sizeof(Eterm));
}
- /*
- * Free all pending message buffers.
- */
- if (p->mbuf != NULL) {
- free_message_buffer(p->mbuf);
- }
-
erts_erase_dicts(p);
/* free all pending messages */
- mp = p->msg.first;
- while(mp != NULL) {
- ErlMessage* next_mp = mp->next;
- if (mp->data.attached) {
- if (is_value(mp->m[0]))
- free_message_buffer(mp->data.heap_frag);
- else {
- if (is_not_nil(mp->m[1])) {
- ErlHeapFragment *heap_frag;
- heap_frag = (ErlHeapFragment *) mp->data.dist_ext->ext_endp;
- erts_cleanup_offheap(&heap_frag->off_heap);
- }
- erts_free_dist_ext_copy(mp->data.dist_ext);
- }
- }
- free_message(mp);
- mp = next_mp;
- }
+ erts_cleanup_messages(p->msg.first);
+ p->msg.first = NULL;
ASSERT(!p->nodes_monitors);
ASSERT(!p->suspend_monitors);
@@ -11557,7 +12062,9 @@ set_proc_exiting(Process *p,
ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(p) == ERTS_PROC_LOCKS_ALL);
enqueue = change_proc_schedule_state(p,
- ERTS_PSFLG_SUSPENDED|ERTS_PSFLG_PENDING_EXIT,
+ (ERTS_PSFLG_SUSPENDED
+ | ERTS_PSFLG_PENDING_EXIT
+ | ERTS_PSFLGS_DIRTY_WORK),
ERTS_PSFLG_EXITING|ERTS_PSFLG_ACTIVE,
&state,
&enq_prio,
@@ -11576,7 +12083,8 @@ set_proc_exiting(Process *p,
p->i = (BeamInstr *) beam_exit;
#ifndef ERTS_SMP
- if (state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS)) {
+ if (state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS)
+ && !(state & ERTS_PSFLG_GC)) {
/*
* I non smp case:
*
@@ -11591,10 +12099,7 @@ set_proc_exiting(Process *p,
}
#endif
- if (enqueue)
- add2runq(enqueue > 0 ? p : make_proxy_proc(NULL, p, enq_prio),
- state,
- enq_prio);
+ add2runq(enqueue, enq_prio, p, state, NULL);
}
static ERTS_INLINE erts_aint32_t
@@ -11608,7 +12113,10 @@ set_proc_self_exiting(Process *c_p)
ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == ERTS_PROC_LOCKS_ALL);
state = erts_smp_atomic32_read_nob(&c_p->state);
- ASSERT(state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS));
+ ASSERT(state & (ERTS_PSFLG_RUNNING
+ |ERTS_PSFLG_RUNNING_SYS
+ | ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS));
#ifdef DEBUG
enqueue =
@@ -11658,46 +12166,73 @@ erts_handle_pending_exit(Process *c_p, ErtsProcLocks locks)
erts_smp_proc_unlock(c_p, xlocks);
}
+static void save_pending_exiter(Process *p, ErtsProcList *plp);
+
static void
-handle_pending_exiters(ErtsProcList *pnd_xtrs)
+do_handle_pending_exiters(ErtsProcList *pnd_xtrs)
{
/* 'list' is expected to have been fetched (i.e. not a ring anymore) */
ErtsProcList *plp = pnd_xtrs;
while (plp) {
- ErtsProcList *free_plp;
- Process *p = erts_pid2proc(NULL, 0, plp->pid, ERTS_PROC_LOCKS_ALL);
+ ErtsProcList *next_plp = plp->next;
+ Process *p = erts_proc_lookup(plp->pid);
if (p) {
- if (erts_proclist_same(plp, p)) {
- erts_aint32_t state = erts_smp_atomic32_read_acqb(&p->state);
- if (!(state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS))) {
- ASSERT(state & ERTS_PSFLG_PENDING_EXIT);
- erts_handle_pending_exit(p, ERTS_PROC_LOCKS_ALL);
+ erts_aint32_t state;
+ /*
+ * If the process is running on a normal scheduler, the
+ * pending exit will soon be detected and handled by the
+ * scheduler running the process (at schedule in/out).
+ */
+ if (erts_smp_proc_trylock(p, ERTS_PROC_LOCKS_ALL) != EBUSY) {
+ if (erts_proclist_same(plp, p)) {
+ state = erts_smp_atomic32_read_acqb(&p->state);
+ if (!(state & (ERTS_PSFLG_RUNNING
+ | ERTS_PSFLG_RUNNING_SYS
+ | ERTS_PSFLG_EXITING))) {
+ ASSERT(state & ERTS_PSFLG_PENDING_EXIT);
+ erts_handle_pending_exit(p, ERTS_PROC_LOCKS_ALL);
+ }
+ }
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL);
+ }
+ else {
+ erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
+ if (erts_proclist_same(plp, p)) {
+ state = erts_smp_atomic32_read_acqb(&p->state);
+ if (!(state & (ERTS_PSFLG_RUNNING
+ | ERTS_PSFLG_RUNNING_SYS
+ | ERTS_PSFLG_EXITING))) {
+ /*
+ * Save process and try to acquire all
+ * locks at a later time...
+ */
+ save_pending_exiter(p, plp);
+ plp = NULL;
+ }
}
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
}
- erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL);
}
- free_plp = plp;
- plp = plp->next;
- proclist_destroy(free_plp);
+ if (plp)
+ proclist_destroy(plp);
+ plp = next_plp;
}
}
static void
-save_pending_exiter(Process *p)
+save_pending_exiter(Process *p, ErtsProcList *plp)
{
- ErtsProcList *plp;
+ ErtsSchedulerSleepInfo *ssi;
ErtsRunQueue *rq;
- ErtsSchedulerData *esdp = erts_get_scheduler_data();
ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p));
- if (!esdp)
- rq = RUNQ_READ_RQ(&p->run_queue);
- else
- rq = esdp->run_queue;
+ rq = RUNQ_READ_RQ(&p->run_queue);
+ ASSERT(rq && !ERTS_RUNQ_IX_IS_DIRTY(rq->ix));
- plp = proclist_create(p);
+ if (!plp)
+ plp = proclist_create(p);
erts_smp_runq_lock(rq);
@@ -11705,14 +12240,11 @@ save_pending_exiter(Process *p)
non_empty_runq(rq);
+ ssi = rq->scheduler->ssi;
+
erts_smp_runq_unlock(rq);
-#ifdef ERTS_DIRTY_SCHEDULERS
- if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix))
- wake_dirty_schedulers(rq, 0);
- else
-#endif
- wake_scheduler(rq);
+ set_aux_work_flags_wakeup_nob(ssi, ERTS_SSI_AUX_WORK_PENDING_EXITERS);
}
#endif
@@ -11726,35 +12258,47 @@ static ERTS_INLINE void
send_exit_message(Process *to, ErtsProcLocks *to_locksp,
Eterm exit_term, Uint term_size, Eterm token)
{
- if (token == NIL
-#ifdef USE_VM_PROBES
- || token == am_have_dt_utag
-#endif
- ) {
- Eterm* hp;
- Eterm mess;
- ErlHeapFragment* bp;
- ErlOffHeap *ohp;
-
- hp = erts_alloc_message_heap(term_size, &bp, &ohp, to, to_locksp);
+ ErtsMessage *mp;
+ ErlOffHeap *ohp;
+ Eterm* hp;
+ Eterm mess;
+#ifdef SHCOPY_SEND
+ erts_shcopy_t info;
+#endif
+
+ if (!have_seqtrace(token)) {
+#ifdef SHCOPY_SEND
+ INITIALIZE_SHCOPY(info);
+ term_size = copy_shared_calculate(exit_term, &info);
+ mp = erts_alloc_message_heap(to, to_locksp, term_size, &hp, &ohp);
+ mess = copy_shared_perform(exit_term, term_size, &info, &hp, ohp);
+ DESTROY_SHCOPY(info);
+#else
+ mp = erts_alloc_message_heap(to, to_locksp, term_size, &hp, &ohp);
mess = copy_struct(exit_term, term_size, &hp, ohp);
- erts_queue_message(to, to_locksp, bp, mess, NIL);
+#endif
+ erts_queue_message(to, *to_locksp, mp, mess, am_system);
} else {
- ErlHeapFragment* bp;
- Eterm* hp;
- Eterm mess;
Eterm temp_token;
Uint sz_token;
ASSERT(is_tuple(token));
sz_token = size_object(token);
- bp = new_message_buffer(term_size+sz_token);
- hp = bp->mem;
- mess = copy_struct(exit_term, term_size, &hp, &bp->off_heap);
+#ifdef SHCOPY_SEND
+ INITIALIZE_SHCOPY(info);
+ term_size = copy_shared_calculate(exit_term, &info);
+ mp = erts_alloc_message_heap(to, to_locksp, term_size+sz_token, &hp, &ohp);
+ mess = copy_shared_perform(exit_term, term_size, &info, &hp, ohp);
+ DESTROY_SHCOPY(info);
+#else
+ mp = erts_alloc_message_heap(to, to_locksp, term_size+sz_token, &hp, &ohp);
+ mess = copy_struct(exit_term, term_size, &hp, ohp);
+#endif
/* the trace token must in this case be updated by the caller */
- seq_trace_output(token, mess, SEQ_TRACE_SEND, to->common.id, NULL);
- temp_token = copy_struct(token, sz_token, &hp, &bp->off_heap);
- erts_queue_message(to, to_locksp, bp, mess, temp_token);
+ seq_trace_output(token, mess, SEQ_TRACE_SEND, to->common.id, to);
+ temp_token = copy_struct(token, sz_token, &hp, ohp);
+ ERL_MESSAGE_TOKEN(mp) = temp_token;
+ erts_queue_message(to, *to_locksp, mp, mess, am_system);
}
}
@@ -11863,11 +12407,10 @@ send_exit_signal(Process *c_p, /* current process if and only
if ((state & ERTS_PSFLG_TRAP_EXIT)
&& (reason != am_kill || (flags & ERTS_XSIG_FLG_IGN_KILL))) {
- if (is_not_nil(token)
-#ifdef USE_VM_PROBES
- && token != am_have_dt_utag
-#endif
- && token_update)
+ /* have to release the status lock in order to send the exit message */
+ erts_smp_proc_unlock(rp, *rp_locks & ERTS_PROC_LOCKS_XSIG_SEND);
+ *rp_locks &= ~ERTS_PROC_LOCKS_XSIG_SEND;
+ if (have_seqtrace(token) && token_update)
seq_trace_update_send(token_update);
if (is_value(exit_tuple))
send_exit_message(rp, rp_locks, exit_tuple, exit_tuple_sz, token);
@@ -11898,10 +12441,12 @@ send_exit_signal(Process *c_p, /* current process if and only
else if (!(state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS))) {
/* Process not running ... */
ErtsProcLocks need_locks = ~(*rp_locks) & ERTS_PROC_LOCKS_ALL;
+ ErlHeapFragment *bp = NULL;
+ Eterm rsn_cpy;
if (need_locks
&& erts_smp_proc_trylock(rp, need_locks) == EBUSY) {
/* ... but we havn't got all locks on it ... */
- save_pending_exiter(rp);
+ save_pending_exiter(rp, NULL);
/*
* The pending exit will be discovered when next
* process is scheduled in
@@ -11910,12 +12455,32 @@ send_exit_signal(Process *c_p, /* current process if and only
}
/* ...and we have all locks on it... */
*rp_locks = ERTS_PROC_LOCKS_ALL;
- set_proc_exiting(rp,
- state,
- (is_immed(rsn)
- ? rsn
- : copy_object(rsn, rp)),
- NULL);
+
+ state = erts_smp_atomic32_read_nob(&rp->state);
+
+ if (is_immed(rsn))
+ rsn_cpy = rsn;
+ else {
+ Eterm *hp;
+ ErlOffHeap *ohp;
+ Uint rsn_sz = size_object(rsn);
+#ifdef ERTS_DIRTY_SCHEDULERS
+ if (state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
+ bp = new_message_buffer(rsn_sz);
+ ohp = &bp->off_heap;
+ hp = &bp->mem[0];
+ }
+ else
+#endif
+ {
+ hp = HAlloc(rp, rsn_sz);
+ ohp = &rp->off_heap;
+ }
+ rsn_cpy = copy_struct(rsn, rsn_sz, &hp, ohp);
+ }
+
+ set_proc_exiting(rp, state, rsn_cpy, bp);
}
else { /* Process running... */
@@ -12003,7 +12568,6 @@ static void doit_exit_monitor(ErtsMonitor *mon, void *vpcontext)
ExitMonitorContext *pcontext = vpcontext;
DistEntry *dep;
ErtsMonitor *rmon;
- Process *rp;
switch (mon->type) {
case MON_ORIGIN:
@@ -12032,9 +12596,10 @@ static void doit_exit_monitor(ErtsMonitor *mon, void *vpcontext)
erts_deref_dist_entry(dep);
}
} else {
- ASSERT(is_pid(mon->pid));
- if (is_internal_pid(mon->pid)) { /* local by pid or name */
- rp = erts_pid2proc(NULL, 0, mon->pid, ERTS_PROC_LOCK_LINK);
+ ASSERT(is_pid(mon->pid) || is_port(mon->pid));
+ /* if is local by pid or name */
+ if (is_internal_pid(mon->pid)) {
+ Process *rp = erts_pid2proc(NULL, 0, mon->pid, ERTS_PROC_LOCK_LINK);
if (!rp) {
goto done;
}
@@ -12044,7 +12609,16 @@ static void doit_exit_monitor(ErtsMonitor *mon, void *vpcontext)
goto done;
}
erts_destroy_monitor(rmon);
- } else { /* remote by pid */
+ } else if (is_internal_port(mon->pid)) {
+ /* Is a local port */
+ Port *prt = erts_port_lookup_raw(mon->pid);
+ if (!prt) {
+ goto done;
+ }
+ erts_port_demonitor(pcontext->p,
+ ERTS_PORT_DEMONITOR_ORIGIN_ON_DEATHBED,
+ prt, mon->ref, NULL);
+ } else { /* remote by pid */
ASSERT(is_external_pid(mon->pid));
dep = external_pid_dist_entry(mon->pid);
ASSERT(dep != NULL);
@@ -12082,6 +12656,7 @@ static void doit_exit_monitor(ErtsMonitor *mon, void *vpcontext)
erts_port_release(prt);
} else if (is_internal_pid(mon->pid)) {/* local by name or pid */
Eterm watched;
+ Process *rp;
DeclareTmpHeapNoproc(lhp,3);
ErtsProcLocks rp_locks = (ERTS_PROC_LOCK_LINK
| ERTS_PROC_LOCKS_MSG_SEND);
@@ -12164,6 +12739,7 @@ static void doit_exit_link(ErtsLink *lnk, void *vpcontext)
DistEntry *dep;
Process *rp;
+
switch(lnk->type) {
case LINK_PID:
if(is_internal_port(item)) {
@@ -12211,7 +12787,11 @@ static void doit_exit_link(ErtsLink *lnk, void *vpcontext)
if (xres >= 0 && IS_TRACED_FL(rp, F_TRACE_PROCS)) {
/* We didn't exit the process and it is traced */
if (IS_TRACED_FL(rp, F_TRACE_PROCS)) {
- trace_proc(p, rp, am_getting_unlinked, p->common.id);
+ if (rp_locks & ERTS_PROC_LOCKS_XSIG_SEND) {
+ erts_smp_proc_unlock(rp, ERTS_PROC_LOCKS_XSIG_SEND);
+ rp_locks &= ~ERTS_PROC_LOCKS_XSIG_SEND;
+ }
+ trace_proc(NULL, 0, rp, am_getting_unlinked, p->common.id);
}
}
}
@@ -12328,8 +12908,6 @@ erts_do_exit_process(Process* p, Eterm reason)
if (IS_TRACED_FL(p, F_TRACE_CALLS))
erts_schedule_time_break(p, ERTS_BP_CALL_TIME_SCHEDULE_EXITING);
- if (IS_TRACED_FL(p,F_TRACE_PROCS))
- trace_proc(p, p, am_exit, reason);
}
erts_trace_check_exiting(p->common.id);
@@ -12345,6 +12923,10 @@ erts_do_exit_process(Process* p, Eterm reason)
erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR);
+ if (IS_TRACED_FL(p,F_TRACE_PROCS))
+ trace_proc(p, ERTS_PROC_LOCK_MAIN, p, am_exit, reason);
+
+
/*
* p->u.initial of this process can *not* be used anymore;
* will be overwritten by misc termination data.
@@ -12362,10 +12944,8 @@ erts_continue_exit_process(Process *p)
ErtsProcLocks curr_locks = ERTS_PROC_LOCK_MAIN;
Eterm reason = p->fvalue;
DistEntry *dep;
- struct saved_calls *scb;
- process_breakpoint_time_t *pbt;
erts_aint32_t state;
- void *nif_export;
+ int delay_del_proc = 0;
#ifdef DEBUG
int yield_allowed = 1;
@@ -12399,14 +12979,39 @@ erts_continue_exit_process(Process *p)
#endif
#ifdef ERTS_SMP
+ if (p->flags & F_SCHDLR_ONLN_WAITQ)
+ abort_sched_onln_chng_waitq(p);
+
if (p->flags & F_HAVE_BLCKD_MSCHED) {
ErtsSchedSuspendResult ssr;
- ssr = erts_block_multi_scheduling(p, ERTS_PROC_LOCK_MAIN, 0, 1);
+ ssr = erts_block_multi_scheduling(p, ERTS_PROC_LOCK_MAIN, 0, 0, 1);
switch (ssr) {
case ERTS_SCHDLR_SSPND_YIELD_RESTART:
goto yield;
case ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED:
+ case ERTS_SCHDLR_SSPND_DONE_NMSCHED_BLOCKED:
case ERTS_SCHDLR_SSPND_YIELD_DONE_MSCHED_BLOCKED:
+ case ERTS_SCHDLR_SSPND_YIELD_DONE_NMSCHED_BLOCKED:
+ case ERTS_SCHDLR_SSPND_DONE:
+ case ERTS_SCHDLR_SSPND_YIELD_DONE:
+ p->flags &= ~F_HAVE_BLCKD_MSCHED;
+ break;
+ case ERTS_SCHDLR_SSPND_EINVAL:
+ default:
+ erts_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error: %d\n",
+ __FILE__, __LINE__, (int) ssr);
+ }
+ }
+ if (p->flags & F_HAVE_BLCKD_NMSCHED) {
+ ErtsSchedSuspendResult ssr;
+ ssr = erts_block_multi_scheduling(p, ERTS_PROC_LOCK_MAIN, 0, 1, 1);
+ switch (ssr) {
+ case ERTS_SCHDLR_SSPND_YIELD_RESTART:
+ goto yield;
+ case ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED:
+ case ERTS_SCHDLR_SSPND_DONE_NMSCHED_BLOCKED:
+ case ERTS_SCHDLR_SSPND_YIELD_DONE_MSCHED_BLOCKED:
+ case ERTS_SCHDLR_SSPND_YIELD_DONE_NMSCHED_BLOCKED:
case ERTS_SCHDLR_SSPND_DONE:
case ERTS_SCHDLR_SSPND_YIELD_DONE:
p->flags &= ~F_HAVE_BLCKD_MSCHED;
@@ -12459,6 +13064,9 @@ erts_continue_exit_process(Process *p)
ASSERT(!p->common.u.alive.reg);
}
+ if (IS_TRACED_FL(p, F_TRACE_SCHED_EXIT))
+ trace_sched(p, curr_locks, am_out_exited);
+
erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR);
curr_locks = ERTS_PROC_LOCKS_ALL;
@@ -12480,7 +13088,7 @@ erts_continue_exit_process(Process *p)
{
/* Do *not* use erts_get_runq_proc() */
ErtsRunQueue *rq;
- rq = erts_get_runq_current(ERTS_GET_SCHEDULER_DATA_FROM_PROC(p));
+ rq = erts_get_runq_current(erts_proc_sched_data(p));
erts_smp_runq_lock(rq);
@@ -12526,19 +13134,25 @@ erts_continue_exit_process(Process *p)
break;
}
+#ifdef ERTS_DIRTY_SCHEDULERS
+ if (a & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
+ p->flags |= F_DELAYED_DEL_PROC;
+ delay_del_proc = 1;
+ /*
+ * The dirty scheduler decrease refc
+ * when done with the process...
+ */
+ }
+#endif
+
if (refc_inced && !(n & ERTS_PSFLG_IN_RUNQ))
erts_proc_dec_refc(p);
}
dep = (p->flags & F_DISTRIBUTION) ? erts_this_dist_entry : NULL;
- scb = ERTS_PROC_SET_SAVED_CALLS_BUF(p, ERTS_PROC_LOCKS_ALL, NULL);
- pbt = ERTS_PROC_SET_CALL_TIME(p, ERTS_PROC_LOCKS_ALL, NULL);
- nif_export = ERTS_PROC_SET_NIF_TRAP_EXPORT(p, ERTS_PROC_LOCKS_ALL, NULL);
erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL);
-#ifdef BM_COUNTERS
- processes_busy--;
-#endif
if (dep) {
erts_do_net_exits(dep, reason);
@@ -12573,16 +13187,14 @@ erts_continue_exit_process(Process *p)
have none here */
}
- if (scb)
- erts_free(ERTS_ALC_T_CALLS_BUF, (void *) scb);
-
- if (pbt)
- erts_free(ERTS_ALC_T_BPD, (void *) pbt);
+#ifdef ERTS_SMP
+ erts_flush_trace_messages(p, 0);
+#endif
- if (nif_export)
- erts_destroy_nif_export(nif_export);
+ ERTS_TRACER_CLEAR(&ERTS_TRACER(p));
- delete_process(p);
+ if (!delay_del_proc)
+ delete_process(p);
#ifdef ERTS_SMP
erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN);
@@ -12612,6 +13224,7 @@ erts_continue_exit_process(Process *p)
ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(p));
+ BUMP_ALL_REDS(p);
}
/*
@@ -12619,7 +13232,7 @@ erts_continue_exit_process(Process *p)
*/
void
-erts_stack_dump(int to, void *to_arg, Process *p)
+erts_stack_dump(fmtfn_t to, void *to_arg, Process *p)
{
Eterm* sp;
int yreg = -1;
@@ -12634,7 +13247,7 @@ erts_stack_dump(int to, void *to_arg, Process *p)
}
void
-erts_program_counter_info(int to, void *to_arg, Process *p)
+erts_program_counter_info(fmtfn_t to, void *to_arg, Process *p)
{
erts_aint32_t state;
int i;
@@ -12664,7 +13277,7 @@ erts_program_counter_info(int to, void *to_arg, Process *p)
}
static void
-print_function_from_pc(int to, void *to_arg, BeamInstr* x)
+print_function_from_pc(fmtfn_t to, void *to_arg, BeamInstr* x)
{
BeamInstr* addr = find_function_from_pc(x);
if (addr == NULL) {
@@ -12686,7 +13299,7 @@ print_function_from_pc(int to, void *to_arg, BeamInstr* x)
}
static int
-stack_element_dump(int to, void *to_arg, Eterm* sp, int yreg)
+stack_element_dump(fmtfn_t to, void *to_arg, Eterm* sp, int yreg)
{
Eterm x = *sp;
@@ -12700,7 +13313,7 @@ stack_element_dump(int to, void *to_arg, Eterm* sp, int yreg)
}
if (is_CP(x)) {
- erts_print(to, to_arg, "Return addr %p (", (Eterm *) EXPAND_POINTER(x));
+ erts_print(to, to_arg, "Return addr %p (", (Eterm *) x);
print_function_from_pc(to, to_arg, cp_val(x));
erts_print(to, to_arg, ")\n");
yreg = 0;
@@ -12718,7 +13331,7 @@ stack_element_dump(int to, void *to_arg, Eterm* sp, int yreg)
* Print scheduler information
*/
void
-erts_print_scheduler_info(int to, void *to_arg, ErtsSchedulerData *esdp) {
+erts_print_scheduler_info(fmtfn_t to, void *to_arg, ErtsSchedulerData *esdp) {
int i;
erts_aint32_t flg;
Process *p;
@@ -12931,11 +13544,13 @@ void erts_halt(int code)
int
erts_dbg_check_halloc_lock(Process *p)
{
+ ErtsSchedulerData *esdp;
if (ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(p))
return 1;
if (p->common.id == ERTS_INVALID_PID)
return 1;
- if (p->scheduler_data && p == p->scheduler_data->match_pseudo_process)
+ esdp = erts_proc_sched_data(p);
+ if (esdp && p == esdp->match_pseudo_process)
return 1;
if (erts_thr_progress_is_blocking())
return 1;
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index b536426b0f..b8755fe948 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2014. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -79,10 +79,8 @@ struct ErtsNodesMonitor_;
#define ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT 0
#define ERTS_MAX_NO_OF_SCHEDULERS 1024
-#ifdef ERTS_DIRTY_SCHEDULERS
#define ERTS_MAX_NO_OF_DIRTY_CPU_SCHEDULERS ERTS_MAX_NO_OF_SCHEDULERS
#define ERTS_MAX_NO_OF_DIRTY_IO_SCHEDULERS ERTS_MAX_NO_OF_SCHEDULERS
-#endif
#define ERTS_DEFAULT_MAX_PROCESSES (1 << 18)
@@ -113,6 +111,7 @@ extern int erts_eager_check_io;
extern int erts_sched_compact_load;
extern int erts_sched_balance_util;
extern Uint erts_no_schedulers;
+extern Uint erts_no_total_schedulers;
#ifdef ERTS_DIRTY_SCHEDULERS
extern Uint erts_no_dirty_cpu_schedulers;
extern Uint erts_no_dirty_io_schedulers;
@@ -246,7 +245,9 @@ extern int erts_sched_thread_suggested_stack_size;
typedef enum {
ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED,
+ ERTS_SCHDLR_SSPND_DONE_NMSCHED_BLOCKED,
ERTS_SCHDLR_SSPND_YIELD_DONE_MSCHED_BLOCKED,
+ ERTS_SCHDLR_SSPND_YIELD_DONE_NMSCHED_BLOCKED,
ERTS_SCHDLR_SSPND_DONE,
ERTS_SCHDLR_SSPND_YIELD_RESTART,
ERTS_SCHDLR_SSPND_YIELD_DONE,
@@ -304,7 +305,7 @@ typedef enum {
ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN_IX,
ERTS_SSI_AUX_WORK_MISC_THR_PRGR_IX,
ERTS_SSI_AUX_WORK_MISC_IX,
- ERTS_SSI_AUX_WORK_CHECK_CHILDREN_IX,
+ ERTS_SSI_AUX_WORK_PENDING_EXITERS_IX,
ERTS_SSI_AUX_WORK_SET_TMO_IX,
ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK_IX,
ERTS_SSI_AUX_WORK_REAP_PORTS_IX,
@@ -337,8 +338,8 @@ typedef enum {
(((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_MISC_THR_PRGR_IX)
#define ERTS_SSI_AUX_WORK_MISC \
(((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_MISC_IX)
-#define ERTS_SSI_AUX_WORK_CHECK_CHILDREN \
- (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_CHECK_CHILDREN_IX)
+#define ERTS_SSI_AUX_WORK_PENDING_EXITERS \
+ (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_PENDING_EXITERS_IX)
#define ERTS_SSI_AUX_WORK_SET_TMO \
(((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_SET_TMO_IX)
#define ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK \
@@ -643,17 +644,12 @@ struct ErtsSchedulerData_ {
Process *free_process;
ErtsThrPrgrData thr_progress_data;
#endif
-#if !HEAP_ON_C_STACK
- Eterm tmp_heap[TMP_HEAP_SIZE];
- int num_tmp_heap_used;
- Eterm beam_emu_tmp_heap[BEAM_EMU_TMP_HEAP_SIZE];
- Eterm erl_arith_tmp_heap[ERL_ARITH_TMP_HEAP_SIZE];
-#endif
ErtsSchedulerSleepInfo *ssi;
Process *current_process;
Uint no; /* Scheduler number for normal schedulers */
#ifdef ERTS_DIRTY_SCHEDULERS
ErtsDirtySchedId dirty_no; /* Scheduler number for dirty schedulers */
+ Process *dirty_shadow_process;
#endif
Port *current_port;
ErtsRunQueue *run_queue;
@@ -702,13 +698,6 @@ extern ErtsAlignedSchedulerData *erts_aligned_dirty_io_scheduler_data;
extern ErtsSchedulerData *erts_scheduler_data;
#endif
-#ifdef ERTS_SCHED_FAIR
-#define ERTS_SCHED_FAIR_YIELD() ETHR_YIELD()
-#else
-#define ERTS_SCHED_FAIR 0
-#define ERTS_SCHED_FAIR_YIELD()
-#endif
-
#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
int erts_smp_lc_runq_is_locked(ErtsRunQueue *);
#endif
@@ -821,8 +810,15 @@ erts_smp_reset_max_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi)
#define ERTS_PSD_CALL_TIME_BP 3
#define ERTS_PSD_DELAYED_GC_TASK_QS 4
#define ERTS_PSD_NIF_TRAP_EXPORT 5
+#define ERTS_PSD_SUSPENDED_SAVED_CALLS_BUF 6
-#define ERTS_PSD_SIZE 6
+#define ERTS_PSD_SIZE 7
+
+#if !defined(HIPE)
+# undef ERTS_PSD_SUSPENDED_SAVED_CALLS_BUF
+# undef ERTS_PSD_SIZE
+# define ERTS_PSD_SIZE 6
+#endif
typedef struct {
void *data[ERTS_PSD_SIZE];
@@ -834,8 +830,8 @@ typedef struct {
#define ERTS_PSD_ERROR_HANDLER_BUF_GET_LOCKS ERTS_PROC_LOCK_MAIN
#define ERTS_PSD_ERROR_HANDLER_BUF_SET_LOCKS ERTS_PROC_LOCK_MAIN
-#define ERTS_PSD_SAVED_CALLS_BUF_GET_LOCKS ERTS_PROC_LOCK_MAIN
-#define ERTS_PSD_SAVED_CALLS_BUF_SET_LOCKS ERTS_PROC_LOCK_MAIN
+#define ERTS_PSD_SAVED_CALLS_BUF_GET_LOCKS ((ErtsProcLocks) 0)
+#define ERTS_PSD_SAVED_CALLS_BUF_SET_LOCKS ((ErtsProcLocks) 0)
#define ERTS_PSD_SCHED_ID_GET_LOCKS ERTS_PROC_LOCK_STATUS
#define ERTS_PSD_SCHED_ID_SET_LOCKS ERTS_PROC_LOCK_STATUS
@@ -846,8 +842,8 @@ typedef struct {
#define ERTS_PSD_DELAYED_GC_TASK_QS_GET_LOCKS ERTS_PROC_LOCK_MAIN
#define ERTS_PSD_DELAYED_GC_TASK_QS_SET_LOCKS ERTS_PROC_LOCK_MAIN
-#define ERTS_PSD_NIF_TRAP_EXPORT_GET_LOCKS ERTS_PROC_LOCK_MAIN
-#define ERTS_PSD_NIF_TRAP_EXPORT_SET_LOCKS ERTS_PROC_LOCK_MAIN
+#define ERTS_PSD_NIF_TRAP_EXPORT_GET_LOCKS ((ErtsProcLocks) 0)
+#define ERTS_PSD_NIF_TRAP_EXPORT_SET_LOCKS ((ErtsProcLocks) 0)
typedef struct {
ErtsProcLocks get_locks;
@@ -924,10 +920,18 @@ struct ErtsPendingSuspend_ {
# define MIN_VHEAP_SIZE(p) (p)->min_vheap_size
# define BIN_VHEAP_SZ(p) (p)->bin_vheap_sz
-# define BIN_VHEAP_MATURE(p) (p)->bin_vheap_mature
# define BIN_OLD_VHEAP_SZ(p) (p)->bin_old_vheap_sz
# define BIN_OLD_VHEAP(p) (p)->bin_old_vheap
+# define MAX_HEAP_SIZE_GET(p) ((p)->max_heap_size >> 2)
+# define MAX_HEAP_SIZE_SET(p, sz) ((p)->max_heap_size = ((sz) << 2) | \
+ MAX_HEAP_SIZE_FLAGS_GET(p))
+# define MAX_HEAP_SIZE_FLAGS_GET(p) ((p)->max_heap_size & 0x3)
+# define MAX_HEAP_SIZE_FLAGS_SET(p, flags) ((p)->max_heap_size = flags | \
+ ((p)->max_heap_size & ~0x3))
+# define MAX_HEAP_SIZE_KILL 1
+# define MAX_HEAP_SIZE_LOG 2
+
struct process {
ErtsPTabElementCommon common; /* *Need* to be first in struct */
@@ -957,6 +961,16 @@ struct process {
#endif
/*
+ * Moved to after "struct hipe_process_state hipe", as a temporary fix for
+ * LLVM hard-coding offsetof(struct process, hipe.nstack) (sic!)
+ * (see void X86FrameLowering::adjustForHiPEPrologue(...) in
+ * lib/Target/X86/X86FrameLowering.cpp).
+ *
+ * Used to be below "Eterm* hend".
+ */
+ Eterm* abandoned_heap;
+
+ /*
* Saved x registers.
*/
Uint arity; /* Number of live argument registers (only valid
@@ -976,8 +990,7 @@ struct process {
Uint32 rcount; /* suspend count */
int schedule_count; /* Times left to reschedule a low prio process */
Uint reds; /* No of reductions for this process */
- Eterm group_leader; /* Pid in charge
- (can be boxed) */
+ Eterm group_leader; /* Pid in charge (can be boxed) */
Uint flags; /* Trap exit, etc (no trace flags anymore) */
Eterm fvalue; /* Exit & Throw value (failure reason) */
Uint freason; /* Reason for detected failure */
@@ -1035,24 +1048,30 @@ struct process {
Eterm *old_hend; /* Heap pointers for generational GC. */
Eterm *old_htop;
Eterm *old_heap;
+ Uint max_heap_size; /* Maximum size of heap (in words). */
Uint16 gen_gcs; /* Number of (minor) generational GCs. */
Uint16 max_gen_gcs; /* Max minor gen GCs before fullsweep. */
ErlOffHeap off_heap; /* Off-heap data updated by copy_struct(). */
- ErlHeapFragment* mbuf; /* Pointer to message buffer list */
- Uint mbuf_sz; /* Size of all message buffers */
- ErtsPSD *psd; /* Rarely used process specific data */
+ ErlHeapFragment* mbuf; /* Pointer to heap fragment list */
+ ErlHeapFragment* live_hf_end;
+ ErtsMessage *msg_frag; /* Pointer to message fragment list */
+ Uint mbuf_sz; /* Total size of heap fragments and message fragments */
+ erts_smp_atomic_t psd; /* Rarely used process specific data */
Uint64 bin_vheap_sz; /* Virtual heap block size for binaries */
- Uint64 bin_vheap_mature; /* Virtual heap block size for binaries */
Uint64 bin_old_vheap_sz; /* Virtual old heap block size for binaries */
Uint64 bin_old_vheap; /* Virtual old heap size for binaries */
ErtsProcSysTaskQs *sys_task_qs;
erts_smp_atomic32_t state; /* Process state flags (see ERTS_PSFLG_*) */
+#ifdef ERTS_DIRTY_SCHEDULERS
+ erts_smp_atomic32_t dirty_state; /* Process dirty state flags (see ERTS_PDSFLG_*) */
+#endif
#ifdef ERTS_SMP
ErlMessageInQueue msg_inq;
+ ErlTraceMessageQueue *trace_msg_q;
ErtsPendExit pending_exit;
erts_proc_lock_t lock;
ErtsSchedulerData *scheduler_data;
@@ -1067,6 +1086,7 @@ struct process {
#ifdef CHECK_FOR_HOLES
Eterm* last_htop; /* No need to scan the heap below this point. */
ErlHeapFragment* last_mbuf; /* No need to scan beyond this mbuf. */
+ ErlHeapFragment* heap_hfrag; /* Heap abandoned, htop now lives in this frag */
#endif
#ifdef DEBUG
@@ -1081,6 +1101,10 @@ struct process {
Uint space_verified; /* Avoid HAlloc forcing heap fragments when */
Eterm* space_verified_from; /* we rely on available heap space (TestHeap) */
#endif
+
+#ifdef DEBUG
+ Uint debug_reds_in;
+#endif
};
extern const Process erts_invalid_process;
@@ -1090,6 +1114,7 @@ extern const Process erts_invalid_process;
do { \
(p)->last_htop = 0; \
(p)->last_mbuf = 0; \
+ (p)->heap_hfrag = NULL; \
} while (0)
# define ERTS_HOLE_CHECK(p) erts_check_for_holes((p))
@@ -1142,6 +1167,9 @@ void erts_check_for_holes(Process* p);
* USR_PRIO -> User prio. i.e., prio the user has set.
* PRQ_PRIO -> Prio queue prio, i.e., prio queue currently
* enqueued in.
+ *
+ * Update etp-proc-state-int in $ERL_TOP/erts/etc/unix/etp-commands.in
+ * when changing ERTS_PSFLG_*.
*/
#define ERTS_PSFLGS_ACT_PRIO_MASK \
(ERTS_PSFLGS_PRIO_MASK << ERTS_PSFLGS_ACT_PRIO_OFFSET)
@@ -1167,21 +1195,30 @@ void erts_check_for_holes(Process* p);
#define ERTS_PSFLG_RUNNING_SYS ERTS_PSFLG_BIT(15)
#define ERTS_PSFLG_PROXY ERTS_PSFLG_BIT(16)
#define ERTS_PSFLG_DELAYED_SYS ERTS_PSFLG_BIT(17)
-#ifdef ERTS_DIRTY_SCHEDULERS
-#define ERTS_PSFLG_DIRTY_CPU_PROC ERTS_PSFLG_BIT(18)
-#define ERTS_PSFLG_DIRTY_IO_PROC ERTS_PSFLG_BIT(19)
-#define ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q ERTS_PSFLG_BIT(20)
-#define ERTS_PSFLG_DIRTY_IO_PROC_IN_Q ERTS_PSFLG_BIT(21)
-#define ERTS_PSFLG_MAX (ERTS_PSFLGS_ZERO_BIT_OFFSET + 22)
-#else
-#define ERTS_PSFLG_MAX (ERTS_PSFLGS_ZERO_BIT_OFFSET + 18)
-#endif
+#define ERTS_PSFLG_OFF_HEAP_MSGQ ERTS_PSFLG_BIT(18)
+#define ERTS_PSFLG_ON_HEAP_MSGQ ERTS_PSFLG_BIT(19)
+#define ERTS_PSFLG_DIRTY_CPU_PROC ERTS_PSFLG_BIT(20)
+#define ERTS_PSFLG_DIRTY_IO_PROC ERTS_PSFLG_BIT(21)
+#define ERTS_PSFLG_DIRTY_ACTIVE_SYS ERTS_PSFLG_BIT(22)
+#define ERTS_PSFLG_DIRTY_RUNNING ERTS_PSFLG_BIT(23)
+#define ERTS_PSFLG_DIRTY_RUNNING_SYS ERTS_PSFLG_BIT(24)
+
+#define ERTS_PSFLG_MAX (ERTS_PSFLGS_ZERO_BIT_OFFSET + 24)
+
+#define ERTS_PSFLGS_DIRTY_WORK (ERTS_PSFLG_DIRTY_CPU_PROC \
+ | ERTS_PSFLG_DIRTY_IO_PROC \
+ | ERTS_PSFLG_DIRTY_ACTIVE_SYS)
#define ERTS_PSFLGS_IN_PRQ_MASK (ERTS_PSFLG_IN_PRQ_MAX \
| ERTS_PSFLG_IN_PRQ_HIGH \
| ERTS_PSFLG_IN_PRQ_NORMAL \
| ERTS_PSFLG_IN_PRQ_LOW)
+#define ERTS_PSFLGS_VOLATILE_HEAP (ERTS_PSFLG_EXITING \
+ | ERTS_PSFLG_PENDING_EXIT \
+ | ERTS_PSFLG_DIRTY_RUNNING \
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)
+
#define ERTS_PSFLGS_GET_ACT_PRIO(PSFLGS) \
(((PSFLGS) >> ERTS_PSFLGS_ACT_PRIO_OFFSET) & ERTS_PSFLGS_PRIO_MASK)
#define ERTS_PSFLGS_GET_USR_PRIO(PSFLGS) \
@@ -1189,10 +1226,41 @@ void erts_check_for_holes(Process* p);
#define ERTS_PSFLGS_GET_PRQ_PRIO(PSFLGS) \
(((PSFLGS) >> ERTS_PSFLGS_PRQ_PRIO_OFFSET) & ERTS_PSFLGS_PRIO_MASK)
+#ifdef ERTS_DIRTY_SCHEDULERS
+
+/*
+ * Flags in the dirty_state field.
+ */
+
+#define ERTS_PDSFLG_IN_CPU_PRQ_MAX (((erts_aint32_t) 1) << 0)
+#define ERTS_PDSFLG_IN_CPU_PRQ_HIGH (((erts_aint32_t) 1) << 1)
+#define ERTS_PDSFLG_IN_CPU_PRQ_NORMAL (((erts_aint32_t) 1) << 2)
+#define ERTS_PDSFLG_IN_CPU_PRQ_LOW (((erts_aint32_t) 1) << 3)
+#define ERTS_PDSFLG_IN_IO_PRQ_MAX (((erts_aint32_t) 1) << 4)
+#define ERTS_PDSFLG_IN_IO_PRQ_HIGH (((erts_aint32_t) 1) << 5)
+#define ERTS_PDSFLG_IN_IO_PRQ_NORMAL (((erts_aint32_t) 1) << 6)
+#define ERTS_PDSFLG_IN_IO_PRQ_LOW (((erts_aint32_t) 1) << 7)
+
+#define ERTS_PDSFLGS_QMASK ERTS_PSFLGS_QMASK
+#define ERTS_PDSFLGS_IN_CPU_PRQ_MASK_OFFSET 0
+#define ERTS_PDSFLGS_IN_IO_PRQ_MASK_OFFSET ERTS_PSFLGS_QMASK_BITS
+
+#define ERTS_PDSFLG_IN_CPU_PRQ_MASK (ERTS_PDSFLG_IN_CPU_PRQ_MAX \
+ | ERTS_PDSFLG_IN_CPU_PRQ_HIGH \
+ | ERTS_PDSFLG_IN_CPU_PRQ_NORMAL\
+ | ERTS_PDSFLG_IN_CPU_PRQ_LOW)
+#define ERTS_PDSFLG_IN_IO_PRQ_MASK (ERTS_PDSFLG_IN_CPU_PRQ_MAX \
+ | ERTS_PDSFLG_IN_CPU_PRQ_HIGH \
+ | ERTS_PDSFLG_IN_CPU_PRQ_NORMAL\
+ | ERTS_PDSFLG_IN_CPU_PRQ_LOW)
+#endif
+
+
/*
* Static flags that do not change after process creation.
*/
#define ERTS_STC_FLG_SYSTEM_PROC (((Uint32) 1) << 0)
+#define ERTS_STC_FLG_SHADOW_PROC (((Uint32) 1) << 1)
/* The sequential tracing token is a tuple of size 5:
*
@@ -1222,12 +1290,16 @@ void erts_check_for_holes(Process* p);
#define SPO_USE_ARGS 2
#define SPO_MONITOR 4
#define SPO_SYSTEM_PROC 8
+#define SPO_OFF_HEAP_MSGQ 16
+#define SPO_ON_HEAP_MSGQ 32
+
+extern int erts_default_spo_flags;
/*
* The following struct contains options for a process to be spawned.
*/
typedef struct {
- Uint flags;
+ int flags;
int error_code; /* Error code returned from create_process(). */
Eterm mref; /* Monitor ref returned (if SPO_MONITOR was given). */
@@ -1239,6 +1311,8 @@ typedef struct {
Uint min_vheap_size; /* Minimum virtual heap size */
int priority; /* Priority for process. */
Uint16 max_gen_gcs; /* Maximum number of gen GCs before fullsweep. */
+ Uint max_heap_size; /* Maximum heap size in words */
+ Uint max_heap_flags; /* Maximum heap flags (kill | log) */
int scheduler;
} ErlSpawnOpts;
@@ -1255,10 +1329,13 @@ ERTS_GLB_INLINE void erts_heap_frag_shrink(Process* p, Eterm* hp);
ERTS_GLB_INLINE void erts_heap_frag_shrink(Process* p, Eterm* hp)
{
ErlHeapFragment* hf = MBUF(p);
+ Uint sz;
- ASSERT(hf!=NULL && (hp - hf->mem < (unsigned long)hf->alloc_size));
+ ASSERT(hf!=NULL && (hp - hf->mem < hf->alloc_size));
- hf->used_size = hp - hf->mem;
+ sz = hp - hf->mem;
+ p->mbuf_sz -= hf->used_size - sz;
+ hf->used_size = sz;
}
#endif /* inline */
@@ -1267,7 +1344,6 @@ Eterm* erts_heap_alloc(Process* p, Uint need, Uint xtra);
Eterm* erts_set_hole_marker(Eterm* ptr, Uint sz);
#endif
-extern Uint erts_default_process_flags;
extern erts_smp_rwmtx_t erts_cpu_bind_rwmtx;
/* If any of the erts_system_monitor_* variables are set (enabled),
** erts_system_monitor must be != NIL, to allow testing on just
@@ -1309,14 +1385,34 @@ extern int erts_system_profile_ts_type;
#define F_HAVE_BLCKD_MSCHED (1 << 8) /* Process has blocked multi-scheduling */
#define F_P2PNR_RESCHED (1 << 9) /* Process has been rescheduled via erts_pid2proc_not_running() */
#define F_FORCE_GC (1 << 10) /* Force gc at process in-scheduling */
-#define F_DISABLE_GC (1 << 11) /* Disable GC */
+#define F_DISABLE_GC (1 << 11) /* Disable GC (see below) */
+#define F_OFF_HEAP_MSGQ (1 << 12) /* Off heap msg queue */
+#define F_ON_HEAP_MSGQ (1 << 13) /* Off heap msg queue */
+#define F_OFF_HEAP_MSGQ_CHNG (1 << 14) /* Off heap msg queue changing */
+#define F_ABANDONED_HEAP_USE (1 << 15) /* Have usage of abandoned heap */
+#define F_DELAY_GC (1 << 16) /* Similar to disable GC (see below) */
+#define F_SCHDLR_ONLN_WAITQ (1 << 17) /* Process enqueued waiting to change schedulers online */
+#define F_HAVE_BLCKD_NMSCHED (1 << 18) /* Process has blocked normal multi-scheduling */
+#define F_HIPE_MODE (1 << 19)
+#define F_DELAYED_DEL_PROC (1 << 20) /* Delay delete process (dirty proc exit case) */
+#define F_HIBERNATED (1 << 21) /* Hibernated */
+
+/*
+ * F_DISABLE_GC and F_DELAY_GC are similar. Both will prevent
+ * GC of the process, but it is important to use the right
+ * one:
+ * - F_DISABLE_GC should *only* be used by BIFs. This when
+ * the BIF needs to yield while preventig a GC.
+ * - F_DELAY_GC should only be used when GC is temporarily
+ * disabled while the process is scheduled. A process must
+ * not be scheduled out while F_DELAY_GC is set.
+ */
#define ERTS_TRACE_FLAGS_TS_TYPE_SHIFT 0
#define F_TRACE_FLAG(N) (1 << (ERTS_TRACE_TS_TYPE_BITS + (N)))
/* process trace_flags */
-
#define F_NOW_TS (ERTS_TRACE_FLG_NOW_TIMESTAMP \
<< ERTS_TRACE_FLAGS_TS_TYPE_SHIFT)
#define F_STRICT_MON_TS (ERTS_TRACE_FLG_STRICT_MONOTONIC_TIMESTAMP \
@@ -1338,8 +1434,7 @@ extern int erts_system_profile_ts_type;
#define F_TRACE_ARITY_ONLY F_TRACE_FLAG(12)
#define F_TRACE_RETURN_TO F_TRACE_FLAG(13) /* Return_to trace when breakpoint tracing */
#define F_TRACE_SILENT F_TRACE_FLAG(14) /* No call trace msg suppress */
-#define F_TRACER F_TRACE_FLAG(15) /* May be (has been) tracer */
-#define F_EXCEPTION_TRACE F_TRACE_FLAG(16) /* May have exception trace on stack */
+#define F_EXCEPTION_TRACE F_TRACE_FLAG(15) /* May have exception trace on stack */
/* port trace flags, currently the same as process trace flags */
#define F_TRACE_SCHED_PORTS F_TRACE_FLAG(17) /* Trace of port scheduling */
@@ -1368,12 +1463,14 @@ extern int erts_system_profile_ts_type;
| F_TRACE_SCHED_PORTS | F_TRACE_SCHED_NO \
| F_TRACE_SCHED_EXIT)
+
#define ERTS_TRACEE_MODIFIER_FLAGS \
- (F_TRACE_SILENT | F_TIMESTAMP_MASK | F_TRACE_SCHED_NO)
-#define ERTS_PORT_TRACEE_FLAGS \
- (ERTS_TRACEE_MODIFIER_FLAGS | F_TRACE_PORTS | F_TRACE_SCHED_PORTS)
+ (F_TRACE_SILENT | F_TIMESTAMP_MASK | F_TRACE_SCHED_NO \
+ | F_TRACE_RECEIVE | F_TRACE_SEND)
+#define ERTS_PORT_TRACEE_FLAGS \
+ (ERTS_TRACEE_MODIFIER_FLAGS | F_TRACE_PORTS | F_TRACE_SCHED_PORTS)
#define ERTS_PROC_TRACEE_FLAGS \
- ((TRACEE_FLAGS & ~ERTS_PORT_TRACEE_FLAGS) | ERTS_TRACEE_MODIFIER_FLAGS)
+ ((TRACEE_FLAGS & ~ERTS_PORT_TRACEE_FLAGS) | ERTS_TRACEE_MODIFIER_FLAGS)
#define SEQ_TRACE_FLAG(N) (1 << (ERTS_TRACE_TS_TYPE_BITS + (N)))
@@ -1457,9 +1554,9 @@ extern int erts_system_profile_ts_type;
#define ERTS_SCHEDULER_IS_DIRTY(ESDP) \
((ESDP)->dirty_no.s.num != 0)
#define ERTS_SCHEDULER_IS_DIRTY_CPU(ESDP) \
- ((ESDP)->dirty_no.s.type == 0)
+ (ERTS_SCHEDULER_IS_DIRTY((ESDP)) & ((ESDP)->dirty_no.s.type == 0))
#define ERTS_SCHEDULER_IS_DIRTY_IO(ESDP) \
- ((ESDP)->dirty_no.s.type == 1)
+ (ERTS_SCHEDULER_IS_DIRTY((ESDP)) & ((ESDP)->dirty_no.s.type == 1))
#else
#define ERTS_SCHEDULER_IS_DIRTY(ESDP) 0
#define ERTS_SCHEDULER_IS_DIRTY_CPU(ESDP) 0
@@ -1491,8 +1588,10 @@ Uint64 erts_step_proc_interval(void);
int erts_setup_nif_gc(Process* proc, Eterm** objv, int* nobj); /* see erl_nif.c */
void erts_destroy_nif_export(void *); /* see erl_nif.c */
+int erts_check_nif_export_in_area(Process *p, char *start, Uint size);
ErtsProcList *erts_proclist_create(Process *);
+ErtsProcList *erts_proclist_copy(ErtsProcList *);
void erts_proclist_destroy(ErtsProcList *);
ERTS_GLB_INLINE int erts_proclist_same(ErtsProcList *, Process *);
@@ -1586,7 +1685,7 @@ ERTS_GLB_INLINE ErtsProcList *erts_proclist_fetch_first(ErtsProcList **list)
return NULL;
else {
ErtsProcList *res = *list;
- if (res == *list)
+ if (res->next == *list)
*list = NULL;
else
*list = res->next;
@@ -1673,40 +1772,33 @@ void erts_schedule_thr_prgr_later_cleanup_op(void (*)(void *),
void *,
ErtsThrPrgrLaterOp *,
UWord);
+void erts_schedule_complete_off_heap_message_queue_change(Eterm pid);
+void erts_schedule_flush_trace_messages(Process *proc, int force_on_proc);
+int erts_flush_trace_messages(Process *c_p, ErtsProcLocks locks);
#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
int erts_dbg_check_halloc_lock(Process *p);
#endif
-#ifdef DEBUG
-void erts_dbg_multi_scheduling_return_trap(Process *, Eterm);
-#endif
-int erts_get_max_no_executing_schedulers(void);
#if defined(ERTS_SMP) || defined(ERTS_DIRTY_SCHEDULERS)
-ErtsSchedSuspendResult
-erts_schedulers_state(Uint *, Uint *, Uint *, Uint *, Uint *, Uint *, int);
+void
+erts_schedulers_state(Uint *, Uint *, Uint *, Uint *, Uint *, Uint *, Uint *, Uint *);
#endif
#ifdef ERTS_SMP
ErtsSchedSuspendResult
erts_set_schedulers_online(Process *p,
ErtsProcLocks plocks,
Sint new_no,
- Sint *old_no
-#ifdef ERTS_DIRTY_SCHEDULERS
- , int dirty_only
-#endif
- );
+ Sint *old_no,
+ int dirty_only);
ErtsSchedSuspendResult
-erts_block_multi_scheduling(Process *, ErtsProcLocks, int, int);
+erts_block_multi_scheduling(Process *, ErtsProcLocks, int, int, int);
int erts_is_multi_scheduling_blocked(void);
-Eterm erts_multi_scheduling_blockers(Process *);
+Eterm erts_multi_scheduling_blockers(Process *, int);
void erts_start_schedulers(void);
void erts_alloc_notify_delayed_dealloc(int);
void erts_alloc_ensure_handle_delayed_dealloc_call(int);
-#ifdef ERTS_SMP
void erts_notify_canceled_timer(ErtsSchedulerData *, int);
#endif
-void erts_smp_notify_check_children_needed(void);
-#endif
#if ERTS_USE_ASYNC_READY_Q
void erts_notify_check_async_ready_queue(void *);
#endif
@@ -1725,7 +1817,8 @@ erts_aint32_t erts_set_aux_work_timeout(int, erts_aint32_t, int);
void erts_sched_notify_check_cpu_bind(void);
Uint erts_active_schedulers(void);
void erts_init_process(int, int, int);
-Eterm erts_process_status(Process *, ErtsProcLocks, Process *, Eterm);
+Eterm erts_process_state2status(erts_aint32_t);
+Eterm erts_process_status(Process *, Eterm);
Uint erts_run_queues_len(Uint *, int, int);
void erts_add_to_runq(Process *);
Eterm erts_bound_schedulers_term(Process *c_p);
@@ -1734,7 +1827,7 @@ Eterm erts_get_schedulers_binds(Process *c_p);
Eterm erts_set_cpu_topology(Process *c_p, Eterm term);
Eterm erts_bind_schedulers(Process *c_p, Eterm how);
ErtsRunQueue *erts_schedid2runq(Uint);
-Process *schedule(Process*, int);
+Process *erts_schedule(ErtsSchedulerData *, Process*, int);
void erts_schedule_misc_op(void (*)(void *), void *);
Eterm erl_create_process(Process*, Eterm, Eterm, Eterm, ErlSpawnOpts*);
void erts_do_exit_process(Process*, Eterm);
@@ -1747,12 +1840,12 @@ void erts_cleanup_empty_process(Process* p);
#ifdef DEBUG
void erts_debug_verify_clean_empty_process(Process* p);
#endif
-void erts_stack_dump(int to, void *to_arg, Process *);
-void erts_limited_stack_trace(int to, void *to_arg, Process *);
-void erts_program_counter_info(int to, void *to_arg, Process *);
-void erts_print_scheduler_info(int to, void *to_arg, ErtsSchedulerData *esdp);
-void erts_dump_extended_process_state(int to, void *to_arg, erts_aint32_t psflg);
-void erts_dump_process_state(int to, void *to_arg, erts_aint32_t psflg);
+void erts_stack_dump(fmtfn_t to, void *to_arg, Process *);
+void erts_limited_stack_trace(fmtfn_t to, void *to_arg, Process *);
+void erts_program_counter_info(fmtfn_t to, void *to_arg, Process *);
+void erts_print_scheduler_info(fmtfn_t to, void *to_arg, ErtsSchedulerData *esdp);
+void erts_dump_extended_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg);
+void erts_dump_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg);
Eterm erts_get_process_priority(Process *p);
Eterm erts_set_process_priority(Process *p, Eterm prio);
@@ -1788,7 +1881,7 @@ void erts_handle_pending_exit(Process *, ErtsProcLocks);
#define ERTS_PROC_PENDING_EXIT(P) 0
#endif
-void erts_deep_process_dump(int, void *);
+void erts_deep_process_dump(fmtfn_t, void *);
Eterm erts_get_reader_groups_map(Process *c_p);
Eterm erts_debug_reader_groups_map(Process *c_p, int groups);
@@ -1800,21 +1893,13 @@ Uint erts_debug_nbalance(void);
int erts_debug_wait_completed(Process *c_p, int flags);
-Uint erts_process_memory(Process *c_p);
-
-#ifdef ERTS_SMP
-# define ERTS_GET_SCHEDULER_DATA_FROM_PROC(PROC) ((PROC)->scheduler_data)
-# define ERTS_PROC_GET_SCHDATA(PROC) ((PROC)->scheduler_data)
-#else
-# define ERTS_GET_SCHEDULER_DATA_FROM_PROC(PROC) (erts_scheduler_data)
-# define ERTS_PROC_GET_SCHDATA(PROC) (erts_scheduler_data)
-#endif
+Uint erts_process_memory(Process *c_p, int incl_msg_inq);
#ifdef ERTS_DO_VERIFY_UNUSED_TEMP_ALLOC
# define ERTS_VERIFY_UNUSED_TEMP_ALLOC(P) \
do { \
ErtsSchedulerData *esdp__ = ((P) \
- ? ERTS_PROC_GET_SCHDATA((Process *) (P)) \
+ ? erts_proc_sched_data((Process *) (P)) \
: erts_get_scheduler_data()); \
if (esdp__ && !ERTS_SCHEDULER_IS_DIRTY(esdp__)) \
esdp__->verify_unused_temp_alloc( \
@@ -1869,18 +1954,19 @@ do { \
#define ERTS_SMP_LC_CHK_RUNQ_LOCK(RQ, L)
#endif
-void *erts_psd_set_init(Process *p, ErtsProcLocks plocks, int ix, void *data);
+void *erts_psd_set_init(Process *p, int ix, void *data);
ERTS_GLB_INLINE void *
erts_psd_get(Process *p, int ix);
ERTS_GLB_INLINE void *
-erts_psd_set(Process *p, ErtsProcLocks plocks, int ix, void *new);
+erts_psd_set(Process *p, int ix, void *new);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
ERTS_GLB_INLINE void *
erts_psd_get(Process *p, int ix)
{
+ ErtsPSD *psd;
#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
ErtsProcLocks locks = erts_proc_lc_my_proc_locks(p);
if (ERTS_LC_PSD_ANY_LOCK == erts_psd_required_locks[ix].get_locks)
@@ -1891,71 +1977,88 @@ erts_psd_get(Process *p, int ix)
|| erts_thr_progress_is_blocking());
}
#endif
+
+ psd = (ErtsPSD *) erts_smp_atomic_read_nob(&p->psd);
ASSERT(0 <= ix && ix < ERTS_PSD_SIZE);
- return p->psd ? p->psd->data[ix] : NULL;
+ if (!psd)
+ return NULL;
+ ERTS_SMP_DATA_DEPENDENCY_READ_MEMORY_BARRIER;
+ return psd->data[ix];
}
-
-/*
- * NOTE: erts_psd_set() might release and reacquire locks on 'p'.
- */
ERTS_GLB_INLINE void *
-erts_psd_set(Process *p, ErtsProcLocks plocks, int ix, void *data)
+erts_psd_set(Process *p, int ix, void *data)
{
+ ErtsPSD *psd;
#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
ErtsProcLocks locks = erts_proc_lc_my_proc_locks(p);
- if (ERTS_LC_PSD_ANY_LOCK == erts_psd_required_locks[ix].set_locks)
- ERTS_SMP_LC_ASSERT(locks || erts_thr_progress_is_blocking());
- else {
- locks &= erts_psd_required_locks[ix].set_locks;
- ERTS_SMP_LC_ASSERT(erts_psd_required_locks[ix].set_locks == locks
- || erts_thr_progress_is_blocking());
+ erts_aint32_t state = state = erts_smp_atomic32_read_nob(&p->state);
+ if (!(state & ERTS_PSFLG_FREE)) {
+ if (ERTS_LC_PSD_ANY_LOCK == erts_psd_required_locks[ix].set_locks)
+ ERTS_SMP_LC_ASSERT(locks || erts_thr_progress_is_blocking());
+ else {
+ locks &= erts_psd_required_locks[ix].set_locks;
+ ERTS_SMP_LC_ASSERT(erts_psd_required_locks[ix].set_locks == locks
+ || erts_thr_progress_is_blocking());
+ }
}
#endif
+ psd = (ErtsPSD *) erts_smp_atomic_read_nob(&p->psd);
ASSERT(0 <= ix && ix < ERTS_PSD_SIZE);
- if (p->psd) {
- void *old = p->psd->data[ix];
- p->psd->data[ix] = data;
+ if (psd) {
+ void *old;
+#ifdef ERTS_SMP
+#ifdef ETHR_ORDERED_READ_DEPEND
+ ETHR_MEMBAR(ETHR_LoadStore|ETHR_StoreStore);
+#else
+ ETHR_MEMBAR(ETHR_LoadLoad|ETHR_LoadStore|ETHR_StoreStore);
+#endif
+#endif
+ old = psd->data[ix];
+ psd->data[ix] = data;
return old;
}
- else {
- if (!data)
- return NULL;
- else
- return erts_psd_set_init(p, plocks, ix, data);
- }
+
+ if (!data)
+ return NULL;
+
+ return erts_psd_set_init(p, ix, data);
}
#endif
-#define ERTS_PROC_SCHED_ID(P, L, ID) \
- ((UWord) erts_psd_set((P), (L), ERTS_PSD_SCHED_ID, (void *) (ID)))
+#define ERTS_PROC_SCHED_ID(P, ID) \
+ ((UWord) erts_psd_set((P), ERTS_PSD_SCHED_ID, (void *) (ID)))
#define ERTS_PROC_GET_SAVED_CALLS_BUF(P) \
((struct saved_calls *) erts_psd_get((P), ERTS_PSD_SAVED_CALLS_BUF))
-#define ERTS_PROC_SET_SAVED_CALLS_BUF(P, L, SCB) \
- ((struct saved_calls *) erts_psd_set((P), (L), ERTS_PSD_SAVED_CALLS_BUF, (void *) (SCB)))
+#define ERTS_PROC_SET_SAVED_CALLS_BUF(P, SCB) \
+ ((struct saved_calls *) erts_psd_set((P), ERTS_PSD_SAVED_CALLS_BUF, (void *) (SCB)))
#define ERTS_PROC_GET_CALL_TIME(P) \
((process_breakpoint_time_t *) erts_psd_get((P), ERTS_PSD_CALL_TIME_BP))
-#define ERTS_PROC_SET_CALL_TIME(P, L, PBT) \
- ((process_breakpoint_time_t *) erts_psd_set((P), (L), ERTS_PSD_CALL_TIME_BP, (void *) (PBT)))
+#define ERTS_PROC_SET_CALL_TIME(P, PBT) \
+ ((process_breakpoint_time_t *) erts_psd_set((P), ERTS_PSD_CALL_TIME_BP, (void *) (PBT)))
#define ERTS_PROC_GET_DELAYED_GC_TASK_QS(P) \
((ErtsProcSysTaskQs *) erts_psd_get((P), ERTS_PSD_DELAYED_GC_TASK_QS))
-#define ERTS_PROC_SET_DELAYED_GC_TASK_QS(P, L, PBT) \
- ((ErtsProcSysTaskQs *) erts_psd_set((P), (L), ERTS_PSD_DELAYED_GC_TASK_QS, (void *) (PBT)))
+#define ERTS_PROC_SET_DELAYED_GC_TASK_QS(P, PBT) \
+ ((ErtsProcSysTaskQs *) erts_psd_set((P), ERTS_PSD_DELAYED_GC_TASK_QS, (void *) (PBT)))
#define ERTS_PROC_GET_NIF_TRAP_EXPORT(P) \
erts_psd_get((P), ERTS_PSD_NIF_TRAP_EXPORT)
-#define ERTS_PROC_SET_NIF_TRAP_EXPORT(P, L, NTE) \
- erts_psd_set((P), (L), ERTS_PSD_NIF_TRAP_EXPORT, (void *) (NTE))
+#define ERTS_PROC_SET_NIF_TRAP_EXPORT(P, NTE) \
+ erts_psd_set((P), ERTS_PSD_NIF_TRAP_EXPORT, (void *) (NTE))
+#ifdef HIPE
+#define ERTS_PROC_GET_SUSPENDED_SAVED_CALLS_BUF(P) \
+ ((struct saved_calls *) erts_psd_get((P), ERTS_PSD_SUSPENDED_SAVED_CALLS_BUF))
+#define ERTS_PROC_SET_SUSPENDED_SAVED_CALLS_BUF(P, SCB) \
+ ((struct saved_calls *) erts_psd_set((P), ERTS_PSD_SUSPENDED_SAVED_CALLS_BUF, (void *) (SCB)))
+#endif
ERTS_GLB_INLINE Eterm erts_proc_get_error_handler(Process *p);
-ERTS_GLB_INLINE Eterm erts_proc_set_error_handler(Process *p,
- ErtsProcLocks plocks,
- Eterm handler);
+ERTS_GLB_INLINE Eterm erts_proc_set_error_handler(Process *p, Eterm handler);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
ERTS_GLB_INLINE Eterm
@@ -1971,13 +2074,13 @@ erts_proc_get_error_handler(Process *p)
}
ERTS_GLB_INLINE Eterm
-erts_proc_set_error_handler(Process *p, ErtsProcLocks plocks, Eterm handler)
+erts_proc_set_error_handler(Process *p, Eterm handler)
{
void *old_val;
void *new_val;
ASSERT(is_atom(handler));
new_val = (handler == am_error_handler) ? NULL : (void *) (UWord) handler;
- old_val = erts_psd_set(p, plocks, ERTS_PSD_ERROR_HANDLER, new_val);
+ old_val = erts_psd_set(p, ERTS_PSD_ERROR_HANDLER, new_val);
if (!old_val)
return am_error_handler;
else {
@@ -2096,6 +2199,7 @@ erts_check_emigration_need(ErtsRunQueue *c_rq, int prio)
#endif
+ERTS_GLB_INLINE ErtsSchedulerData *erts_proc_sched_data(Process *c_p);
ERTS_GLB_INLINE int erts_is_scheduler_bound(ErtsSchedulerData *esdp);
ERTS_GLB_INLINE Process *erts_get_current_process(void);
ERTS_GLB_INLINE Eterm erts_get_current_pid(void);
@@ -2110,9 +2214,50 @@ ERTS_GLB_INLINE void erts_smp_xrunq_unlock(ErtsRunQueue *rq, ErtsRunQueue *xrq);
ERTS_GLB_INLINE void erts_smp_runqs_lock(ErtsRunQueue *rq1, ErtsRunQueue *rq2);
ERTS_GLB_INLINE void erts_smp_runqs_unlock(ErtsRunQueue *rq1, ErtsRunQueue *rq2);
+ERTS_GLB_INLINE ErtsMessage *erts_alloc_message_heap_state(Process *pp,
+ erts_aint32_t *psp,
+ ErtsProcLocks *plp,
+ Uint sz,
+ Eterm **hpp,
+ ErlOffHeap **ohpp);
+ERTS_GLB_INLINE ErtsMessage *erts_alloc_message_heap(Process *pp,
+ ErtsProcLocks *plp,
+ Uint sz,
+ Eterm **hpp,
+ ErlOffHeap **ohpp);
+
+ERTS_GLB_INLINE void erts_shrink_message_heap(ErtsMessage **msgpp, Process *pp,
+ Eterm *start_hp, Eterm *used_hp, Eterm *end_hp,
+ Eterm *brefs, Uint brefs_size);
+
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
ERTS_GLB_INLINE
+ErtsSchedulerData *erts_proc_sched_data(Process *c_p)
+{
+ ErtsSchedulerData *esdp;
+ ASSERT(c_p);
+#if !defined(ERTS_SMP)
+ esdp = erts_get_scheduler_data();
+#else
+ esdp = c_p->scheduler_data;
+# if defined(ERTS_DIRTY_SCHEDULERS)
+ if (esdp) {
+ ASSERT(esdp == erts_get_scheduler_data());
+ ASSERT(!ERTS_SCHEDULER_IS_DIRTY(esdp));
+ }
+ else {
+ esdp = erts_get_scheduler_data();
+ ASSERT(esdp);
+ ASSERT(ERTS_SCHEDULER_IS_DIRTY(esdp));
+ }
+# endif
+#endif
+ ASSERT(esdp);
+ return esdp;
+}
+
+ERTS_GLB_INLINE
int erts_is_scheduler_bound(ErtsSchedulerData *esdp)
{
if (!esdp)
@@ -2258,6 +2403,66 @@ erts_smp_runqs_unlock(ErtsRunQueue *rq1, ErtsRunQueue *rq2)
#endif
}
+ERTS_GLB_INLINE ErtsMessage *
+erts_alloc_message_heap_state(Process *pp,
+ erts_aint32_t *psp,
+ ErtsProcLocks *plp,
+ Uint sz,
+ Eterm **hpp,
+ ErlOffHeap **ohpp)
+{
+ int on_heap;
+ ErtsMessage *mp;
+
+ if ((*psp) & ERTS_PSFLG_OFF_HEAP_MSGQ) {
+ mp = erts_alloc_message(sz, hpp);
+ *ohpp = sz == 0 ? NULL : &mp->hfrag.off_heap;
+ return mp;
+ }
+
+ mp = erts_try_alloc_message_on_heap(pp, psp, plp, sz, hpp, ohpp, &on_heap);
+ ASSERT(pp || !on_heap);
+ return mp;
+}
+
+ERTS_GLB_INLINE ErtsMessage *
+erts_alloc_message_heap(Process *pp,
+ ErtsProcLocks *plp,
+ Uint sz,
+ Eterm **hpp,
+ ErlOffHeap **ohpp)
+{
+ erts_aint32_t state = pp ? erts_smp_atomic32_read_nob(&pp->state) : 0;
+ return erts_alloc_message_heap_state(pp, &state, plp, sz, hpp, ohpp);
+}
+
+ERTS_GLB_INLINE void
+erts_shrink_message_heap(ErtsMessage **msgpp, Process *pp,
+ Eterm *start_hp, Eterm *used_hp, Eterm *end_hp,
+ Eterm *brefs, Uint brefs_size)
+{
+ ASSERT(start_hp <= used_hp && used_hp <= end_hp);
+ if ((*msgpp)->data.attached == ERTS_MSG_COMBINED_HFRAG)
+ *msgpp = erts_shrink_message(*msgpp, used_hp - start_hp,
+ brefs, brefs_size);
+ else if (!(*msgpp)->data.attached) {
+ ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN
+ & erts_proc_lc_my_proc_locks(pp));
+ HRelease(pp, end_hp, used_hp);
+ }
+ else {
+ ErlHeapFragment *hfrag = (*msgpp)->data.heap_frag;
+ if (start_hp != used_hp)
+ hfrag = erts_resize_message_buffer(hfrag, used_hp - start_hp,
+ brefs, brefs_size);
+ else {
+ free_message_buffer(hfrag);
+ hfrag = NULL;
+ }
+ (*msgpp)->data.heap_frag = hfrag;
+ }
+}
+
#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
ERTS_GLB_INLINE ErtsAtomCacheMap *erts_get_atom_cache_map(Process *c_p);
@@ -2267,13 +2472,42 @@ ERTS_GLB_INLINE ErtsAtomCacheMap *
erts_get_atom_cache_map(Process *c_p)
{
ErtsSchedulerData *esdp = (c_p
- ? ERTS_PROC_GET_SCHDATA(c_p)
+ ? erts_proc_sched_data(c_p)
: erts_get_scheduler_data());
ASSERT(esdp);
return &esdp->atom_cache_map;
}
#endif
+#ifdef __WIN32__
+/*
+ * Don't want erts_time2reds() inlined in beam_emu.c on windows since
+ * it is compiled with gcc which fails on it. Implementation is in
+ * erl_process.c on windows.
+ */
+# define ERTS_TIME2REDS_IMPL__ erts_time2reds__
+#else
+# define ERTS_TIME2REDS_IMPL__ erts_time2reds
+#endif
+
+ERTS_GLB_INLINE Sint64 ERTS_TIME2REDS_IMPL__(ErtsMonotonicTime start,
+ ErtsMonotonicTime end);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+ERTS_GLB_INLINE Sint64
+ERTS_TIME2REDS_IMPL__(ErtsMonotonicTime start, ErtsMonotonicTime end)
+{
+ ErtsMonotonicTime time = end - start;
+ ASSERT(time >= 0);
+ time = ERTS_MONOTONIC_TO_USEC(time);
+ if (time == 0)
+ return (Sint64) 1; /* At least one reduction */
+ /* Currently two reductions per micro second */
+ time *= (CONTEXT_REDS-1)/1000 + 1;
+ return (Sint64) time;
+}
+#endif
+
Process *erts_pid2proc_suspend(Process *,
ErtsProcLocks,
Eterm,
diff --git a/erts/emulator/beam/erl_process_dict.c b/erts/emulator/beam/erl_process_dict.c
index 3253ee6b70..42654604cb 100644
--- a/erts/emulator/beam/erl_process_dict.c
+++ b/erts/emulator/beam/erl_process_dict.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -51,16 +51,19 @@
#define INITIAL_SIZE (erts_pd_initial_size)
/* Hash utility macros */
-#define HASH_RANGE(PDict) ((PDict)->homeSize + (PDict)->splitPosition)
+#define HASH_RANGE(PDict) ((PDict)->usedSlots)
-#define MAKE_HASH(Term) \
-((is_small(Term)) ? unsigned_val(Term) : \
- ((is_atom(Term)) ? \
- (atom_tab(atom_val(term))->slot.bucket.hvalue) : \
- make_hash2(Term)))
+#define MAKE_HASH(Term) \
+ ((is_small(Term)) ? unsigned_val(Term) : \
+ ((is_atom(Term)) ? \
+ (atom_tab(atom_val(Term))->slot.bucket.hvalue) : \
+ make_internal_hash(Term)))
#define PD_SZ2BYTES(Sz) (sizeof(ProcDict) + ((Sz) - 1)*sizeof(Eterm))
+#define pd_hash_value(Pdict, Key) \
+ pd_hash_value_to_ix(Pdict, MAKE_HASH((Key)))
+
/* Memory allocation macros */
#define PD_ALLOC(Sz) \
erts_alloc(ERTS_ALC_T_PROC_DICT, (Sz))
@@ -74,14 +77,19 @@
#define TCDR(Term) CDR(list_val(Term))
/* Array access macro */
-#define ARRAY_GET(PDict, Index) (((PDict)->size > (Index)) ? \
- (PDict)->data[Index] : NIL)
+#define ARRAY_GET(PDict, Index) (ASSERT((Index) < (PDict)->arraySize), \
+ (PDict)->data[Index])
+#define ARRAY_PUT(PDict, Index, Val) (ASSERT((Index) < (PDict)->arraySize), \
+ (PDict)->data[Index] = (Val))
+
+#define IS_POW2(X) ((X) && !((X) & ((X)-1)))
/*
* Forward decalarations
*/
static void pd_hash_erase(Process *p, Eterm id, Eterm *ret);
static void pd_hash_erase_all(Process *p);
+static Eterm pd_hash_get_with_hval(Process *p, Eterm bucket, Eterm id);
static Eterm pd_hash_get_keys(Process *p, Eterm value);
static Eterm pd_hash_get_all_keys(Process *p, ProcDict *pd);
static Eterm pd_hash_get_all(Process *p, ProcDict *pd);
@@ -91,9 +99,9 @@ static void shrink(Process *p, Eterm* ret);
static void grow(Process *p);
static void array_shrink(ProcDict **ppd, unsigned int need);
-static Eterm array_put(ProcDict **ppdict, unsigned int ndx, Eterm term);
+static void ensure_array_size(ProcDict**, unsigned int size);
-static unsigned int pd_hash_value(ProcDict *pdict, Eterm term);
+static unsigned int pd_hash_value_to_ix(ProcDict *pdict, Uint32 hx);
static unsigned int next_array_size(unsigned int need);
/*
@@ -134,11 +142,21 @@ static void pd_check(ProcDict *pd);
** External interface
*/
+int
+erts_pd_set_initial_size(int size)
+{
+ if (size <= 0)
+ return 0;
+
+ erts_pd_initial_size = 1 << erts_fit_in_bits_uint(size-1);
+ return 1;
+}
+
/*
* Called from break handler
*/
void
-erts_dictionary_dump(int to, void *to_arg, ProcDict *pd)
+erts_dictionary_dump(fmtfn_t to, void *to_arg, ProcDict *pd)
{
unsigned int i;
#ifdef DEBUG
@@ -146,9 +164,9 @@ erts_dictionary_dump(int to, void *to_arg, ProcDict *pd)
/*PD_CHECK(pd);*/
if (pd == NULL)
return;
- erts_print(to, to_arg, "(size = %d, used = %d, homeSize = %d, "
+ erts_print(to, to_arg, "(size = %d, usedSlots = %d, "
"splitPosition = %d, numElements = %d)\n",
- pd->size, pd->used, pd->homeSize,
+ pd->arraySize, pd->usedSlots,
pd->splitPosition, (unsigned int) pd->numElements);
for (i = 0; i < HASH_RANGE(pd); ++i) {
erts_print(to, to_arg, "%d: %T\n", i, ARRAY_GET(pd, i));
@@ -178,8 +196,8 @@ erts_dictionary_dump(int to, void *to_arg, ProcDict *pd)
}
void
-erts_deep_dictionary_dump(int to, void *to_arg,
- ProcDict* pd, void (*cb)(int, void *, Eterm))
+erts_deep_dictionary_dump(fmtfn_t to, void *to_arg,
+ ProcDict* pd, void (*cb)(fmtfn_t, void *, Eterm))
{
unsigned int i;
Eterm t;
@@ -203,7 +221,7 @@ erts_dicts_mem_size(Process *p)
{
Uint size = 0;
if (p->dictionary)
- size += PD_SZ2BYTES(p->dictionary->size);
+ size += PD_SZ2BYTES(p->dictionary->arraySize);
return size;
}
@@ -345,7 +363,7 @@ static void pd_hash_erase(Process *p, Eterm id, Eterm *ret)
if (is_boxed(old)) { /* Tuple */
ASSERT(is_tuple(old));
if (EQ(tuple_val(old)[1], id)) {
- array_put(&(p->dictionary), hval, NIL);
+ ARRAY_PUT(p->dictionary, hval, NIL);
--(p->dictionary->numElements);
*ret = tuple_val(old)[2];
}
@@ -365,7 +383,7 @@ static void pd_hash_erase(Process *p, Eterm id, Eterm *ret)
old = ARRAY_GET(p->dictionary, hval);
ASSERT(is_list(old));
if (is_nil(TCDR(old))) {
- array_put(&p->dictionary, hval, TCAR(old));
+ ARRAY_PUT(p->dictionary, hval, TCAR(old));
}
} else if (is_not_nil(old)) {
#ifdef DEBUG
@@ -390,40 +408,56 @@ static void pd_hash_erase_all(Process *p)
}
}
+Eterm erts_pd_hash_get_with_hx(Process *p, Uint32 hx, Eterm id)
+{
+ unsigned int hval;
+ ProcDict *pd = p->dictionary;
+
+ ASSERT(hx == MAKE_HASH(id));
+ if (pd == NULL)
+ return am_undefined;
+ hval = pd_hash_value_to_ix(pd, hx);
+ return pd_hash_get_with_hval(p, ARRAY_GET(pd, hval), id);
+}
+
Eterm erts_pd_hash_get(Process *p, Eterm id)
{
unsigned int hval;
- Eterm tmp;
ProcDict *pd = p->dictionary;
if (pd == NULL)
return am_undefined;
hval = pd_hash_value(pd, id);
- tmp = ARRAY_GET(pd, hval);
- if (is_boxed(tmp)) { /* Tuple */
- ASSERT(is_tuple(tmp));
- if (EQ(tuple_val(tmp)[1], id)) {
- return tuple_val(tmp)[2];
+ return pd_hash_get_with_hval(p, ARRAY_GET(pd, hval), id);
+}
+
+Eterm pd_hash_get_with_hval(Process *p, Eterm bucket, Eterm id)
+{
+ if (is_boxed(bucket)) { /* Tuple */
+ ASSERT(is_tuple(bucket));
+ if (EQ(tuple_val(bucket)[1], id)) {
+ return tuple_val(bucket)[2];
}
- } else if (is_list(tmp)) {
- for (; tmp != NIL && !EQ(tuple_val(TCAR(tmp))[1], id); tmp = TCDR(tmp)) {
+ } else if (is_list(bucket)) {
+ for (; bucket != NIL && !EQ(tuple_val(TCAR(bucket))[1], id); bucket = TCDR(bucket)) {
;
}
- if (tmp != NIL) {
- return tuple_val(TCAR(tmp))[2];
+ if (bucket != NIL) {
+ return tuple_val(TCAR(bucket))[2];
}
- } else if (is_not_nil(tmp)) {
+ } else if (is_not_nil(bucket)) {
#ifdef DEBUG
erts_fprintf(stderr,
"Process dictionary for process %T is broken, trying to "
"display term found in line %d:\n"
- "%T\n", p->common.id, __LINE__, tmp);
+ "%T\n", p->common.id, __LINE__, bucket);
#endif
erts_exit(ERTS_ERROR_EXIT, "Damaged process dictionary found during get/1.");
}
return am_undefined;
}
+
#define PD_GET_TKEY(Dst,Src) \
do { \
ASSERT(is_tuple((Src))); \
@@ -549,8 +583,11 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value)
if (p->dictionary == NULL) {
/* Create it */
- array_put(&(p->dictionary), INITIAL_SIZE - 1, NIL);
- p->dictionary->homeSize = INITIAL_SIZE;
+ ensure_array_size(&p->dictionary, INITIAL_SIZE);
+ p->dictionary->usedSlots = INITIAL_SIZE;
+ p->dictionary->sizeMask = INITIAL_SIZE*2 - 1;
+ p->dictionary->splitPosition = 0;
+ p->dictionary->numElements = 0;
}
hval = pd_hash_value(p->dictionary, id);
old = ARRAY_GET(p->dictionary, hval);
@@ -583,7 +620,7 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value)
root[0] = id;
root[1] = value;
root[2] = old;
- BUMP_REDS(p, erts_garbage_collect(p, needed, root, 3));
+ erts_garbage_collect(p, needed, root, 3);
id = root[0];
value = root[1];
old = root[2];
@@ -602,19 +639,19 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value)
* Update the dictionary.
*/
if (is_nil(old)) {
- array_put(&(p->dictionary), hval, tpl);
+ ARRAY_PUT(p->dictionary, hval, tpl);
++(p->dictionary->numElements);
} else if (is_boxed(old)) {
ASSERT(is_tuple(old));
if (EQ(tuple_val(old)[1],id)) {
- array_put(&(p->dictionary), hval, tpl);
+ ARRAY_PUT(p->dictionary, hval, tpl);
return tuple_val(old)[2];
} else {
hp = HeapOnlyAlloc(p, 4);
tmp = CONS(hp, old, NIL);
hp += 2;
++(p->dictionary->numElements);
- array_put(&(p->dictionary), hval, CONS(hp, tpl, tmp));
+ ARRAY_PUT(p->dictionary, hval, CONS(hp, tpl, tmp));
hp += 2;
ASSERT(hp <= hp_limit);
}
@@ -624,7 +661,7 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value)
* New key. Simply prepend the tuple to the beginning of the list.
*/
hp = HeapOnlyAlloc(p, 2);
- array_put(&(p->dictionary), hval, CONS(hp, tpl, old));
+ ARRAY_PUT(p->dictionary, hval, CONS(hp, tpl, old));
hp += 2;
ASSERT(hp <= hp_limit);
++(p->dictionary->numElements);
@@ -659,7 +696,7 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value)
nlist = CONS(hp, tpl, nlist);
hp += 2;
ASSERT(hp <= hp_limit);
- array_put(&(p->dictionary), hval, nlist);
+ ARRAY_PUT(p->dictionary, hval, nlist);
return tuple_val(TCAR(tmp))[2];
}
} else {
@@ -684,6 +721,7 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value)
static void shrink(Process *p, Eterm* ret)
{
+ ProcDict *pd = p->dictionary;
unsigned int range = HASH_RANGE(p->dictionary);
unsigned int steps = (range*3) / 10;
Eterm hi, lo, tmp;
@@ -698,25 +736,26 @@ static void shrink(Process *p, Eterm* ret)
}
for (i = 0; i < steps; ++i) {
- ProcDict *pd = p->dictionary;
if (pd->splitPosition == 0) {
- pd->homeSize /= 2;
- pd->splitPosition = pd->homeSize;
+ ASSERT(IS_POW2(pd->usedSlots));
+ pd->sizeMask = pd->usedSlots - 1;
+ pd->splitPosition = pd->usedSlots / 2;
}
--(pd->splitPosition);
- hi = ARRAY_GET(pd, (pd->splitPosition + pd->homeSize));
+ /* Must wait to decrement 'usedSlots' for GC rootset below */
+ hi = ARRAY_GET(pd, pd->usedSlots - 1);
lo = ARRAY_GET(pd, pd->splitPosition);
if (hi != NIL) {
if (lo == NIL) {
- array_put(&(p->dictionary), pd->splitPosition, hi);
+ ARRAY_PUT(pd, pd->splitPosition, hi);
} else {
- int needed = 4;
+ Sint needed = 4;
if (is_list(hi) && is_list(lo)) {
needed = 2*erts_list_length(hi);
}
if (HeapWordsLeft(p) < needed) {
- BUMP_REDS(p, erts_garbage_collect(p, needed, ret, 1));
- hi = pd->data[(pd->splitPosition + pd->homeSize)];
+ erts_garbage_collect(p, needed, ret, 1);
+ hi = pd->data[pd->usedSlots - 1];
lo = pd->data[pd->splitPosition];
}
#ifdef DEBUG
@@ -727,13 +766,13 @@ static void shrink(Process *p, Eterm* ret)
hp = HeapOnlyAlloc(p, 4);
tmp = CONS(hp, hi, NIL);
hp += 2;
- array_put(&(p->dictionary), pd->splitPosition,
+ ARRAY_PUT(pd, pd->splitPosition,
CONS(hp,lo,tmp));
hp += 2;
ASSERT(hp <= hp_limit);
} else { /* hi is a list */
hp = HeapOnlyAlloc(p, 2);
- array_put(&(p->dictionary), pd->splitPosition,
+ ARRAY_PUT(pd, pd->splitPosition,
CONS(hp, lo, hi));
hp += 2;
ASSERT(hp <= hp_limit);
@@ -741,7 +780,7 @@ static void shrink(Process *p, Eterm* ret)
} else { /* lo is a list */
if (is_tuple(hi)) {
hp = HeapOnlyAlloc(p, 2);
- array_put(&(p->dictionary), pd->splitPosition,
+ ARRAY_PUT(pd, pd->splitPosition,
CONS(hp, hi, lo));
hp += 2;
ASSERT(hp <= hp_limit);
@@ -753,14 +792,15 @@ static void shrink(Process *p, Eterm* ret)
hp += 2;
}
ASSERT(hp <= hp_limit);
- array_put(&(p->dictionary), pd->splitPosition, lo);
+ ARRAY_PUT(pd, pd->splitPosition, lo);
}
}
}
}
- array_put(&(p->dictionary), (pd->splitPosition + pd->homeSize), NIL);
+ --pd->usedSlots;
+ ARRAY_PUT(pd, pd->usedSlots, NIL);
}
- if (HASH_RANGE(p->dictionary) <= (p->dictionary->size / 4)) {
+ if (HASH_RANGE(p->dictionary) <= (p->dictionary->arraySize / 4)) {
array_shrink(&(p->dictionary), (HASH_RANGE(p->dictionary) * 3) / 2);
}
}
@@ -768,14 +808,14 @@ static void shrink(Process *p, Eterm* ret)
static void grow(Process *p)
{
unsigned int i,j;
- unsigned int steps = p->dictionary->homeSize / 5;
+ unsigned int steps = (p->dictionary->usedSlots / 4) & 0xf;
Eterm l1,l2;
Eterm l;
Eterm *hp;
unsigned int pos;
unsigned int homeSize;
- int needed = 0;
- ProcDict *pd;
+ Sint needed = 0;
+ ProcDict *pd = p->dictionary;
#ifdef DEBUG
Eterm *hp_limit;
#endif
@@ -784,18 +824,20 @@ static void grow(Process *p)
if (steps == 0)
steps = 1;
/* Dont grow over MAX_HASH */
- if ((MAX_HASH - steps) <= HASH_RANGE(p->dictionary)) {
+ if ((MAX_HASH - steps) <= HASH_RANGE(pd)) {
return;
}
+ ensure_array_size(&p->dictionary, HASH_RANGE(pd) + steps);
+ pd = p->dictionary;
+
/*
* Calculate total number of heap words needed, and garbage collect
* if necessary.
*/
- pd = p->dictionary;
pos = pd->splitPosition;
- homeSize = pd->homeSize;
+ homeSize = pd->usedSlots - pd->splitPosition;
for (i = 0; i < steps; ++i) {
if (pos == homeSize) {
homeSize *= 2;
@@ -811,7 +853,7 @@ static void grow(Process *p)
}
}
if (HeapWordsLeft(p) < needed) {
- BUMP_REDS(p, erts_garbage_collect(p, needed, 0, 0));
+ erts_garbage_collect(p, needed, 0, 0);
}
#ifdef DEBUG
hp_limit = p->htop + needed;
@@ -820,21 +862,22 @@ static void grow(Process *p)
/*
* Now grow.
*/
-
+ homeSize = pd->usedSlots - pd->splitPosition;
for (i = 0; i < steps; ++i) {
- ProcDict *pd = p->dictionary;
- if (pd->splitPosition == pd->homeSize) {
- pd->homeSize *= 2;
- pd->splitPosition = 0;
+ if (pd->splitPosition == homeSize) {
+ homeSize *= 2;
+ pd->sizeMask = homeSize*2 - 1;
+ pd->splitPosition = 0;
}
pos = pd->splitPosition;
++pd->splitPosition; /* For the hashes */
+ ++pd->usedSlots;
+ ASSERT(pos + homeSize == pd->usedSlots - 1);
l = ARRAY_GET(pd, pos);
if (is_tuple(l)) {
if (pd_hash_value(pd, tuple_val(l)[1]) != pos) {
- array_put(&(p->dictionary), pos +
- p->dictionary->homeSize, l);
- array_put(&(p->dictionary), pos, NIL);
+ ARRAY_PUT(pd, pos + homeSize, l);
+ ARRAY_PUT(pd, pos, NIL);
}
} else {
l2 = NIL;
@@ -856,10 +899,8 @@ static void grow(Process *p)
if (l2 != NIL && TCDR(l2) == NIL)
l2 = TCAR(l2);
ASSERT(hp <= hp_limit);
- /* After array_put pd is no longer valid */
- array_put(&(p->dictionary), pos, l1);
- array_put(&(p->dictionary), pos +
- p->dictionary->homeSize, l2);
+ ARRAY_PUT(pd, pos, l1);
+ ARRAY_PUT(pd, pos + homeSize, l2);
}
}
@@ -876,73 +917,65 @@ static void array_shrink(ProcDict **ppd, unsigned int need)
{
unsigned int siz = next_array_size(need);
- HDEBUGF(("array_shrink: size = %d, used = %d, need = %d",
- (*ppd)->size, (*ppd)->used, need));
+ HDEBUGF(("array_shrink: size = %d, need = %d",
+ (*ppd)->arraySize, need));
- if (siz > (*ppd)->size)
+ if (siz >= (*ppd)->arraySize)
return; /* Only shrink */
*ppd = PD_REALLOC(((void *) *ppd),
- PD_SZ2BYTES((*ppd)->size),
+ PD_SZ2BYTES((*ppd)->arraySize),
PD_SZ2BYTES(siz));
- (*ppd)->size = siz;
- if ((*ppd)->size < (*ppd)->used)
- (*ppd)->used = (*ppd)->size;
+ (*ppd)->arraySize = siz;
}
-static Eterm array_put(ProcDict **ppdict, unsigned int ndx, Eterm term)
+static void ensure_array_size(ProcDict **ppdict, unsigned int size)
{
+ ProcDict *pd = *ppdict;
unsigned int i;
- Eterm ret;
- if (*ppdict == NULL) {
- Uint siz = next_array_size(ndx+1);
- ProcDict *p;
- p = PD_ALLOC(PD_SZ2BYTES(siz));
+ if (pd == NULL) {
+ Uint siz = next_array_size(size);
+
+ pd = PD_ALLOC(PD_SZ2BYTES(siz));
for (i = 0; i < siz; ++i)
- p->data[i] = NIL;
- p->size = siz;
- p->homeSize = p->splitPosition = p->numElements = p->used = 0;
- *ppdict = p;
- } else if (ndx >= (*ppdict)->size) {
- Uint osize = (*ppdict)->size;
- Uint nsize = next_array_size(ndx+1);
- *ppdict = PD_REALLOC(((void *) *ppdict),
+ pd->data[i] = NIL;
+ pd->arraySize = siz;
+ *ppdict = pd;
+ } else if (size > pd->arraySize) {
+ Uint osize = pd->arraySize;
+ Uint nsize = next_array_size(size);
+ pd = PD_REALLOC(((void *) pd),
PD_SZ2BYTES(osize),
PD_SZ2BYTES(nsize));
for (i = osize; i < nsize; ++i)
- (*ppdict)->data[i] = NIL;
- (*ppdict)->size = nsize;
+ pd->data[i] = NIL;
+ pd->arraySize = nsize;
+ *ppdict = pd;
}
- ret = (*ppdict)->data[ndx];
- (*ppdict)->data[ndx] = term;
- if ((ndx + 1) > (*ppdict)->used)
- (*ppdict)->used = ndx + 1;
-#ifdef HARDDEBUG
- HDEBUGF(("array_put: (*ppdict)->size = %d, (*ppdict)->used = %d, ndx = %d",
- (*ppdict)->size, (*ppdict)->used, ndx));
- erts_fprintf(stderr, "%T", term);
-#endif /* HARDDEBUG */
- return ret;
}
/*
** Basic utilities
*/
-static unsigned int pd_hash_value(ProcDict *pdict, Eterm term)
+static unsigned int pd_hash_value_to_ix(ProcDict *pdict, Uint32 hx)
{
- Uint hash, high;
+ Uint high;
- hash = MAKE_HASH(term);
- high = hash % (pdict->homeSize*2);
+ ASSERT(IS_POW2(pdict->sizeMask+1));
+ ASSERT(HASH_RANGE(pdict) >= (pdict->sizeMask >> 1));
+ ASSERT(HASH_RANGE(pdict) <= (pdict->sizeMask + 1));
+
+ high = hx & pdict->sizeMask;
if (high >= HASH_RANGE(pdict))
- return hash % pdict->homeSize;
+ return hx & (pdict->sizeMask >> 1);
return high;
}
+
static unsigned int next_array_size(unsigned int need)
{
static unsigned int tab[] =
@@ -1002,24 +1035,28 @@ static unsigned int next_array_size(unsigned int need)
static void pd_check(ProcDict *pd)
{
unsigned int i;
+ unsigned int used;
Uint num;
if (pd == NULL)
return;
- ASSERT(pd->size >= pd->used);
+ used = HASH_RANGE(pd);
+ ASSERT(pd->arraySize >= used);
ASSERT(HASH_RANGE(pd) <= MAX_HASH);
- for (i = 0, num = 0; i < pd->used; ++i) {
+ for (i = 0, num = 0; i < used; ++i) {
Eterm t = pd->data[i];
if (is_nil(t)) {
continue;
} else if (is_tuple(t)) {
++num;
ASSERT(arityval(*tuple_val(t)) == 2);
+ ASSERT(pd_hash_value(pd, tuple_val(t)[1]) == i);
continue;
} else if (is_list(t)) {
while (t != NIL) {
++num;
ASSERT(is_tuple(TCAR(t)));
ASSERT(arityval(*(tuple_val(TCAR(t)))) == 2);
+ ASSERT(pd_hash_value(pd, tuple_val(TCAR(t))[1]) == i);
t = TCDR(t);
}
continue;
@@ -1030,7 +1067,7 @@ static void pd_check(ProcDict *pd)
}
}
ASSERT(num == pd->numElements);
- ASSERT(pd->splitPosition <= pd->homeSize);
+ ASSERT(pd->usedSlots >= pd->splitPosition*2);
}
#endif /* DEBUG */
diff --git a/erts/emulator/beam/erl_process_dict.h b/erts/emulator/beam/erl_process_dict.h
index cc53800eb5..b50a2af72c 100644
--- a/erts/emulator/beam/erl_process_dict.h
+++ b/erts/emulator/beam/erl_process_dict.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,21 +23,26 @@
#include "sys.h"
typedef struct proc_dict {
- unsigned int size;
- unsigned int used;
- unsigned int homeSize;
+ unsigned int sizeMask;
+ unsigned int usedSlots;
+ unsigned int arraySize;
unsigned int splitPosition;
Uint numElements;
Eterm data[1]; /* The beginning of an array of erlang terms */
} ProcDict;
+#define ERTS_PD_START(PD) ((PD)->data)
+#define ERTS_PD_SIZE(PD) ((PD)->usedSlots)
+
+int erts_pd_set_initial_size(int size);
Uint erts_dicts_mem_size(struct process *p);
void erts_erase_dicts(struct process *p);
-void erts_dictionary_dump(int to, void *to_arg, ProcDict *pd);
-void erts_deep_dictionary_dump(int to, void *to_arg,
- ProcDict* pd, void (*cb)(int, void *, Eterm obj));
+void erts_dictionary_dump(fmtfn_t to, void *to_arg, ProcDict *pd);
+void erts_deep_dictionary_dump(fmtfn_t to, void *to_arg,
+ ProcDict* pd, void (*cb)(fmtfn_t, void *, Eterm obj));
Eterm erts_dictionary_copy(struct process *p, ProcDict *pd);
Eterm erts_pd_hash_get(struct process *p, Eterm id);
+Eterm erts_pd_hash_get_with_hx(Process *p, Uint32 hx, Eterm id);
#endif
diff --git a/erts/emulator/beam/erl_process_dump.c b/erts/emulator/beam/erl_process_dump.c
index 25f0b1ed38..8d66391d55 100644
--- a/erts/emulator/beam/erl_process_dump.c
+++ b/erts/emulator/beam/erl_process_dump.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2013. 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.
@@ -40,17 +40,17 @@
#define OUR_NIL _make_header(0,_TAG_HEADER_FLOAT)
-static void dump_process_info(int to, void *to_arg, Process *p);
-static void dump_element(int to, void *to_arg, Eterm x);
-static void dump_dist_ext(int to, void *to_arg, ErtsDistExternal *edep);
-static void dump_element_nl(int to, void *to_arg, Eterm x);
-static int stack_element_dump(int to, void *to_arg, Eterm* sp,
+static void dump_process_info(fmtfn_t to, void *to_arg, Process *p);
+static void dump_element(fmtfn_t to, void *to_arg, Eterm x);
+static void dump_dist_ext(fmtfn_t to, void *to_arg, ErtsDistExternal *edep);
+static void dump_element_nl(fmtfn_t to, void *to_arg, Eterm x);
+static int stack_element_dump(fmtfn_t to, void *to_arg, Eterm* sp,
int yreg);
-static void stack_trace_dump(int to, void *to_arg, Eterm* sp);
-static void print_function_from_pc(int to, void *to_arg, BeamInstr* x);
-static void heap_dump(int to, void *to_arg, Eterm x);
-static void dump_binaries(int to, void *to_arg, Binary* root);
-static void dump_externally(int to, void *to_arg, Eterm term);
+static void stack_trace_dump(fmtfn_t to, void *to_arg, Eterm* sp);
+static void print_function_from_pc(fmtfn_t to, void *to_arg, BeamInstr* x);
+static void heap_dump(fmtfn_t to, void *to_arg, Eterm x);
+static void dump_binaries(fmtfn_t to, void *to_arg, Binary* root);
+static void dump_externally(fmtfn_t to, void *to_arg, Eterm term);
static Binary* all_binaries;
@@ -60,7 +60,7 @@ extern BeamInstr beam_continue_exit[];
void
-erts_deep_process_dump(int to, void *to_arg)
+erts_deep_process_dump(fmtfn_t to, void *to_arg)
{
int i, max = erts_ptab_max(&erts_proc);
@@ -78,21 +78,25 @@ erts_deep_process_dump(int to, void *to_arg)
dump_binaries(to, to_arg, all_binaries);
}
-Uint erts_process_memory(Process *p) {
- ErlMessage *mp;
+Uint erts_process_memory(Process *p, int incl_msg_inq) {
+ ErtsMessage *mp;
Uint size = 0;
struct saved_calls *scb;
size += sizeof(Process);
- ERTS_SMP_MSGQ_MV_INQ2PRIVQ(p);
+ if (incl_msg_inq)
+ ERTS_SMP_MSGQ_MV_INQ2PRIVQ(p);
erts_doforall_links(ERTS_P_LINKS(p), &erts_one_link_size, &size);
erts_doforall_monitors(ERTS_P_MONITORS(p), &erts_one_mon_size, &size);
size += (p->heap_sz + p->mbuf_sz) * sizeof(Eterm);
+ if (p->abandoned_heap)
+ size += (p->hend - p->heap) * sizeof(Eterm);
if (p->old_hend && p->old_heap)
size += (p->old_hend - p->old_heap) * sizeof(Eterm);
- size += p->msg.len * sizeof(ErlMessage);
+
+ size += p->msg.len * sizeof(ErtsMessage);
for (mp = p->msg.first; mp; mp = mp->next)
if (mp->data.attached)
@@ -102,7 +106,7 @@ Uint erts_process_memory(Process *p) {
size += p->arity * sizeof(p->arg_reg[0]);
}
- if (p->psd)
+ if (erts_smp_atomic_read_nob(&p->psd) != (erts_aint_t) NULL)
size += sizeof(ErtsPSD);
scb = ERTS_PROC_GET_SAVED_CALLS_BUF(p);
@@ -116,10 +120,10 @@ Uint erts_process_memory(Process *p) {
}
static void
-dump_process_info(int to, void *to_arg, Process *p)
+dump_process_info(fmtfn_t to, void *to_arg, Process *p)
{
Eterm* sp;
- ErlMessage* mp;
+ ErtsMessage* mp;
int yreg = -1;
ERTS_SMP_MSGQ_MV_INQ2PRIVQ(p);
@@ -175,7 +179,7 @@ dump_process_info(int to, void *to_arg, Process *p)
}
static void
-dump_dist_ext(int to, void *to_arg, ErtsDistExternal *edep)
+dump_dist_ext(fmtfn_t to, void *to_arg, ErtsDistExternal *edep)
{
if (!edep)
erts_print(to, to_arg, "D0:E0:");
@@ -209,7 +213,7 @@ dump_dist_ext(int to, void *to_arg, ErtsDistExternal *edep)
}
static void
-dump_element(int to, void *to_arg, Eterm x)
+dump_element(fmtfn_t to, void *to_arg, Eterm x)
{
if (is_list(x)) {
erts_print(to, to_arg, "H" PTR_FMT, list_val(x));
@@ -239,14 +243,14 @@ dump_element(int to, void *to_arg, Eterm x)
}
static void
-dump_element_nl(int to, void *to_arg, Eterm x)
+dump_element_nl(fmtfn_t to, void *to_arg, Eterm x)
{
dump_element(to, to_arg, x);
erts_putc(to, to_arg, '\n');
}
static void
-stack_trace_dump(int to, void *to_arg, Eterm *sp) {
+stack_trace_dump(fmtfn_t to, void *to_arg, Eterm *sp) {
Eterm x = *sp;
if (is_CP(x)) {
erts_print(to, to_arg, "%p:", sp);
@@ -257,7 +261,7 @@ stack_trace_dump(int to, void *to_arg, Eterm *sp) {
}
void
-erts_limited_stack_trace(int to, void *to_arg, Process *p)
+erts_limited_stack_trace(fmtfn_t to, void *to_arg, Process *p)
{
Eterm* sp;
@@ -303,7 +307,7 @@ erts_limited_stack_trace(int to, void *to_arg, Process *p)
}
static int
-stack_element_dump(int to, void *to_arg, Eterm* sp, int yreg)
+stack_element_dump(fmtfn_t to, void *to_arg, Eterm* sp, int yreg)
{
Eterm x = *sp;
@@ -331,7 +335,7 @@ stack_element_dump(int to, void *to_arg, Eterm* sp, int yreg)
}
static void
-print_function_from_pc(int to, void *to_arg, BeamInstr* x)
+print_function_from_pc(fmtfn_t to, void *to_arg, BeamInstr* x)
{
BeamInstr* addr = find_function_from_pc(x);
if (addr == NULL) {
@@ -351,7 +355,7 @@ print_function_from_pc(int to, void *to_arg, BeamInstr* x)
}
static void
-heap_dump(int to, void *to_arg, Eterm x)
+heap_dump(fmtfn_t to, void *to_arg, Eterm x)
{
DeclareTmpHeapNoproc(last,1);
Eterm* next = last;
@@ -365,7 +369,7 @@ heap_dump(int to, void *to_arg, Eterm x)
while (x != OUR_NIL) {
if (is_CP(x)) {
- next = (Eterm *) EXPAND_POINTER(x);
+ next = (Eterm *) x;
} else if (is_list(x)) {
ptr = list_val(x);
if (ptr[0] != OUR_NIL) {
@@ -378,7 +382,7 @@ heap_dump(int to, void *to_arg, Eterm x)
ptr[1] = make_small(0);
}
x = ptr[0];
- ptr[0] = (Eterm) COMPRESS_POINTER(next);
+ ptr[0] = (Eterm) next;
next = ptr + 1;
continue;
}
@@ -408,7 +412,7 @@ heap_dump(int to, void *to_arg, Eterm x)
ptr[0] = OUR_NIL;
} else {
x = ptr[arity];
- ptr[0] = (Eterm) COMPRESS_POINTER(next);
+ ptr[0] = (Eterm) next;
next = ptr + arity - 1;
continue;
}
@@ -442,7 +446,7 @@ heap_dump(int to, void *to_arg, Eterm x)
ProcBin* pb = (ProcBin *) binary_val(x);
Binary* val = pb->val;
- if (erts_smp_atomic_xchg_nob(&val->refc, 0) != 0) {
+ if (erts_atomic_xchg_nob(&val->refc, 0) != 0) {
val->flags = (UWord) all_binaries;
all_binaries = val;
}
@@ -511,7 +515,7 @@ heap_dump(int to, void *to_arg, Eterm x)
}
static void
-dump_binaries(int to, void *to_arg, Binary* current)
+dump_binaries(fmtfn_t to, void *to_arg, Binary* current)
{
while (current) {
long i;
@@ -529,7 +533,7 @@ dump_binaries(int to, void *to_arg, Binary* current)
}
static void
-dump_externally(int to, void *to_arg, Eterm term)
+dump_externally(fmtfn_t to, void *to_arg, Eterm term)
{
byte sbuf[1024]; /* encode and hope for the best ... */
byte* s;
@@ -559,6 +563,11 @@ dump_externally(int to, void *to_arg, Eterm term)
}
}
+ /* Do not handle maps */
+ if (is_map(term)) {
+ term = am_undefined;
+ }
+
s = p = sbuf;
erts_encode_ext(term, &p);
erts_print(to, to_arg, "E%X:", p-s);
@@ -567,27 +576,25 @@ dump_externally(int to, void *to_arg, Eterm term)
}
}
-void erts_dump_process_state(int to, void *to_arg, erts_aint32_t psflg) {
- if (psflg & ERTS_PSFLG_FREE)
- erts_print(to, to_arg, "Non Existing\n"); /* Should never happen */
- else if (psflg & ERTS_PSFLG_EXITING)
- erts_print(to, to_arg, "Exiting\n");
- else if (psflg & ERTS_PSFLG_GC) {
- erts_print(to, to_arg, "Garbing\n");
- }
- else if (psflg & ERTS_PSFLG_SUSPENDED)
- erts_print(to, to_arg, "Suspended\n");
- else if (psflg & ERTS_PSFLG_RUNNING) {
- erts_print(to, to_arg, "Running\n");
+void erts_dump_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg)
+{
+ char *s;
+ switch (erts_process_state2status(psflg)) {
+ case am_free: s = "Non Existing"; break; /* Should never happen */
+ case am_exiting: s = "Exiting"; break;
+ case am_garbage_collecting: s = "Garbing"; break;
+ case am_suspended: s = "Suspended"; break;
+ case am_running: s = "Running"; break;
+ case am_runnable: s = "Scheduled"; break;
+ case am_waiting: s = "Waiting"; break;
+ default: s = "Undefined"; break; /* Should never happen */
}
- else if (psflg & ERTS_PSFLG_ACTIVE)
- erts_print(to, to_arg, "Scheduled\n");
- else
- erts_print(to, to_arg, "Waiting\n");
+
+ erts_print(to, to_arg, "%s\n", s);
}
void
-erts_dump_extended_process_state(int to, void *to_arg, erts_aint32_t psflg) {
+erts_dump_extended_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg) {
int i;
@@ -617,7 +624,7 @@ erts_dump_extended_process_state(int to, void *to_arg, erts_aint32_t psflg) {
if (psflg)
erts_print(to, to_arg, " | ");
- for (i = 0; i < ERTS_PSFLG_MAX && psflg; i++) {
+ for (i = 0; i <= ERTS_PSFLG_MAX && psflg; i++) {
erts_aint32_t chk = (1 << i);
if (psflg & chk) {
switch (chk) {
@@ -657,16 +664,20 @@ erts_dump_extended_process_state(int to, void *to_arg, erts_aint32_t psflg) {
erts_print(to, to_arg, "PROXY"); break;
case ERTS_PSFLG_DELAYED_SYS:
erts_print(to, to_arg, "DELAYED_SYS"); break;
-#ifdef ERTS_DIRTY_SCHEDULERS
+ case ERTS_PSFLG_OFF_HEAP_MSGQ:
+ erts_print(to, to_arg, "OFF_HEAP_MSGQ"); break;
+ case ERTS_PSFLG_ON_HEAP_MSGQ:
+ erts_print(to, to_arg, "ON_HEAP_MSGQ"); break;
case ERTS_PSFLG_DIRTY_CPU_PROC:
erts_print(to, to_arg, "DIRTY_CPU_PROC"); break;
case ERTS_PSFLG_DIRTY_IO_PROC:
erts_print(to, to_arg, "DIRTY_IO_PROC"); break;
- case ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q:
- erts_print(to, to_arg, "DIRTY_CPU_PROC_IN_Q"); break;
- case ERTS_PSFLG_DIRTY_IO_PROC_IN_Q:
- erts_print(to, to_arg, "DIRTY_IO_PROC_IN_Q"); break;
-#endif
+ case ERTS_PSFLG_DIRTY_ACTIVE_SYS:
+ erts_print(to, to_arg, "DIRTY_ACTIVE_SYS"); break;
+ case ERTS_PSFLG_DIRTY_RUNNING:
+ erts_print(to, to_arg, "DIRTY_RUNNING"); break;
+ case ERTS_PSFLG_DIRTY_RUNNING_SYS:
+ erts_print(to, to_arg, "DIRTY_RUNNING_SYS"); break;
default:
erts_print(to, to_arg, "UNKNOWN(%d)", chk); break;
}
diff --git a/erts/emulator/beam/erl_process_lock.c b/erts/emulator/beam/erl_process_lock.c
index 0bee2c848c..a69185bc5c 100644
--- a/erts/emulator/beam/erl_process_lock.c
+++ b/erts/emulator/beam/erl_process_lock.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2007-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2007-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -106,6 +106,7 @@ static struct {
Sint16 proc_lock_msgq;
Sint16 proc_lock_btm;
Sint16 proc_lock_status;
+ Sint16 proc_lock_trace;
} lc_id;
#endif
@@ -152,6 +153,7 @@ erts_init_proc_lock(int cpus)
lc_id.proc_lock_msgq = erts_lc_get_lock_order_id("proc_msgq");
lc_id.proc_lock_btm = erts_lc_get_lock_order_id("proc_btm");
lc_id.proc_lock_status = erts_lc_get_lock_order_id("proc_status");
+ lc_id.proc_lock_trace = erts_lc_get_lock_order_id("proc_trace");
#endif
}
@@ -1057,6 +1059,11 @@ erts_proc_lock_init(Process *p)
#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, do_lock_count);
+ ethr_mutex_lock(&p->lock.trace.mtx);
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ erts_lc_trylock(1, &p->lock.trace.lc);
+#endif
#endif
#ifdef ERTS_PROC_LOCK_DEBUG
for (i = 0; i <= ERTS_PROC_LOCK_MAX_BIT; i++)
@@ -1078,6 +1085,7 @@ erts_proc_lock_fin(Process *p)
erts_mtx_destroy(&p->lock.msgq);
erts_mtx_destroy(&p->lock.btm);
erts_mtx_destroy(&p->lock.status);
+ erts_mtx_destroy(&p->lock.trace);
#endif
#if defined(ERTS_ENABLE_LOCK_COUNT) && defined(ERTS_SMP)
erts_lcnt_proc_lock_destroy(p);
@@ -1100,26 +1108,29 @@ void erts_lcnt_enable_proc_lock_count(int enable) {
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_link));
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_link), "proc_link", 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 */
} /* 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_link));
erts_lcnt_destroy_lock(&(p->lock.lcnt_status));
+ erts_lcnt_destroy_lock(&(p->lock.lcnt_trace));
}
static void lcnt_enable_proc_lock_count(Process *proc, int enable) {
@@ -1138,10 +1149,11 @@ static void lcnt_enable_proc_lock_count(Process *proc, int enable) {
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_LINK) { erts_lcnt_lock(&(lock->lcnt_link)); }
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_proc_lock_post_x(erts_proc_lock_t *lock, ErtsProcLocks locks,
@@ -1150,44 +1162,50 @@ void erts_lcnt_proc_lock_post_x(erts_proc_lock_t *lock, ErtsProcLocks locks,
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_LINK) {
- erts_lcnt_lock_post_x(&(lock->lcnt_link), 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);
+ }
}
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_LINK) { erts_lcnt_lock_unaquire(&(lock->lcnt_link)); }
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)); }
}
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_LINK) { erts_lcnt_unlock(&(lock->lcnt_link)); }
if (locks & ERTS_PROC_LOCK_STATUS) { erts_lcnt_unlock(&(lock->lcnt_status)); }
+ if (locks & ERTS_PROC_LOCK_TRACE) { erts_lcnt_unlock(&(lock->lcnt_trace)); }
}
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_LINK) { erts_lcnt_trylock(&(lock->lcnt_link), 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 */
@@ -1224,6 +1242,10 @@ erts_proc_lc_lock(Process *p, ErtsProcLocks locks, char *file, unsigned int line
lck.id = lc_id.proc_lock_status;
erts_lc_lock_x(&lck,file,line);
}
+ if (locks & ERTS_PROC_LOCK_TRACE) {
+ lck.id = lc_id.proc_lock_trace;
+ erts_lc_lock_x(&lck,file,line);
+ }
}
void
@@ -1253,6 +1275,10 @@ erts_proc_lc_trylock(Process *p, ErtsProcLocks locks, int locked,
lck.id = lc_id.proc_lock_status;
erts_lc_trylock_x(locked, &lck, file, line);
}
+ if (locks & ERTS_PROC_LOCK_TRACE) {
+ lck.id = lc_id.proc_lock_trace;
+ erts_lc_trylock_x(locked, &lck, file, line);
+ }
}
void
@@ -1261,6 +1287,10 @@ erts_proc_lc_unlock(Process *p, ErtsProcLocks locks)
erts_lc_lock_t lck = ERTS_LC_LOCK_INIT(-1,
p->common.id,
ERTS_LC_FLG_LT_PROCLOCK);
+ if (locks & ERTS_PROC_LOCK_TRACE) {
+ lck.id = lc_id.proc_lock_trace;
+ erts_lc_unlock(&lck);
+ }
if (locks & ERTS_PROC_LOCK_STATUS) {
lck.id = lc_id.proc_lock_status;
erts_lc_unlock(&lck);
@@ -1292,6 +1322,10 @@ erts_proc_lc_might_unlock(Process *p, ErtsProcLocks locks)
erts_lc_lock_t lck = ERTS_LC_LOCK_INIT(-1,
p->common.id,
ERTS_LC_FLG_LT_PROCLOCK);
+ if (locks & ERTS_PROC_LOCK_TRACE) {
+ lck.id = lc_id.proc_lock_trace;
+ erts_lc_might_unlock(&lck);
+ }
if (locks & ERTS_PROC_LOCK_STATUS) {
lck.id = lc_id.proc_lock_status;
erts_lc_might_unlock(&lck);
@@ -1323,6 +1357,8 @@ erts_proc_lc_might_unlock(Process *p, ErtsProcLocks locks)
erts_lc_might_unlock(&p->lock.btm.lc);
if (locks & ERTS_PROC_LOCK_STATUS)
erts_lc_might_unlock(&p->lock.status.lc);
+ if (locks & ERTS_PROC_LOCK_TRACE)
+ erts_lc_might_unlock(&p->lock.trace.lc);
#endif
}
@@ -1354,6 +1390,10 @@ erts_proc_lc_require_lock(Process *p, ErtsProcLocks locks, char *file,
lck.id = lc_id.proc_lock_status;
erts_lc_require_lock(&lck, file, line);
}
+ if (locks & ERTS_PROC_LOCK_TRACE) {
+ lck.id = lc_id.proc_lock_trace;
+ erts_lc_require_lock(&lck, file, line);
+ }
#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
if (locks & ERTS_PROC_LOCK_MAIN)
erts_lc_require_lock(&p->lock.main.lc, file, line);
@@ -1365,6 +1405,8 @@ erts_proc_lc_require_lock(Process *p, ErtsProcLocks locks, char *file,
erts_lc_require_lock(&p->lock.btm.lc, file, line);
if (locks & ERTS_PROC_LOCK_STATUS)
erts_lc_require_lock(&p->lock.status.lc, file, line);
+ if (locks & ERTS_PROC_LOCK_TRACE)
+ erts_lc_require_lock(&p->lock.trace.lc, file, line);
#endif
}
@@ -1375,6 +1417,10 @@ erts_proc_lc_unrequire_lock(Process *p, ErtsProcLocks locks)
erts_lc_lock_t lck = ERTS_LC_LOCK_INIT(-1,
p->common.id,
ERTS_LC_FLG_LT_PROCLOCK);
+ if (locks & ERTS_PROC_LOCK_TRACE) {
+ lck.id = lc_id.proc_lock_trace;
+ erts_lc_unrequire_lock(&lck);
+ }
if (locks & ERTS_PROC_LOCK_STATUS) {
lck.id = lc_id.proc_lock_status;
erts_lc_unrequire_lock(&lck);
@@ -1406,6 +1452,8 @@ erts_proc_lc_unrequire_lock(Process *p, ErtsProcLocks locks)
erts_lc_unrequire_lock(&p->lock.btm.lc);
if (locks & ERTS_PROC_LOCK_STATUS)
erts_lc_unrequire_lock(&p->lock.status.lc);
+ if (locks & ERTS_PROC_LOCK_TRACE)
+ erts_lc_unrequire_lock(&p->lock.trace.lc);
#endif
}
@@ -1429,6 +1477,8 @@ erts_proc_lc_trylock_force_busy(Process *p, ErtsProcLocks locks)
lck.id = lc_id.proc_lock_btm;
else if (locks & ERTS_PROC_LOCK_STATUS)
lck.id = lc_id.proc_lock_status;
+ else if (locks & ERTS_PROC_LOCK_TRACE)
+ lck.id = lc_id.proc_lock_trace;
else
erts_lc_fail("Unknown proc lock found");
@@ -1441,14 +1491,7 @@ erts_proc_lc_trylock_force_busy(Process *p, ErtsProcLocks locks)
void erts_proc_lc_chk_only_proc_main(Process *p)
{
-#if ERTS_PROC_LOCK_OWN_IMPL
- erts_lc_lock_t proc_main = ERTS_LC_LOCK_INIT(lc_id.proc_lock_main,
- p->common.id,
- ERTS_LC_FLG_LT_PROCLOCK);
- erts_lc_check_exact(&proc_main, 1);
-#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
- erts_lc_check_exact(&p->lock.main.lc, 1);
-#endif
+ erts_proc_lc_chk_only_proc(p, ERTS_PROC_LOCK_MAIN);
}
#if ERTS_PROC_LOCK_OWN_IMPL
@@ -1456,14 +1499,67 @@ void erts_proc_lc_chk_only_proc_main(Process *p)
ERTS_LC_LOCK_INIT(-1, THE_NON_VALUE, ERTS_LC_FLG_LT_PROCLOCK)
#endif /* ERTS_PROC_LOCK_OWN_IMPL */
+void erts_proc_lc_chk_only_proc(Process *p, ErtsProcLocks locks)
+{
+ int have_locks_len = 0;
+#if ERTS_PROC_LOCK_OWN_IMPL
+ erts_lc_lock_t have_locks[6] = {ERTS_PROC_LC_EMPTY_LOCK_INIT,
+ ERTS_PROC_LC_EMPTY_LOCK_INIT,
+ ERTS_PROC_LC_EMPTY_LOCK_INIT,
+ ERTS_PROC_LC_EMPTY_LOCK_INIT,
+ ERTS_PROC_LC_EMPTY_LOCK_INIT,
+ ERTS_PROC_LC_EMPTY_LOCK_INIT};
+ if (locks & ERTS_PROC_LOCK_MAIN) {
+ have_locks[have_locks_len].id = lc_id.proc_lock_main;
+ have_locks[have_locks_len++].extra = p->common.id;
+ }
+ if (locks & ERTS_PROC_LOCK_LINK) {
+ have_locks[have_locks_len].id = lc_id.proc_lock_link;
+ have_locks[have_locks_len++].extra = p->common.id;
+ }
+ if (locks & ERTS_PROC_LOCK_MSGQ) {
+ have_locks[have_locks_len].id = lc_id.proc_lock_msgq;
+ have_locks[have_locks_len++].extra = p->common.id;
+ }
+ if (locks & ERTS_PROC_LOCK_BTM) {
+ have_locks[have_locks_len].id = lc_id.proc_lock_btm;
+ have_locks[have_locks_len++].extra = p->common.id;
+ }
+ if (locks & ERTS_PROC_LOCK_STATUS) {
+ have_locks[have_locks_len].id = lc_id.proc_lock_status;
+ have_locks[have_locks_len++].extra = p->common.id;
+ }
+ if (locks & ERTS_PROC_LOCK_TRACE) {
+ have_locks[have_locks_len].id = lc_id.proc_lock_trace;
+ have_locks[have_locks_len++].extra = p->common.id;
+ }
+#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
+ erts_lc_lock_t have_locks[6];
+ if (locks & ERTS_PROC_LOCK_MAIN)
+ have_locks[have_locks_len++] = p->lock.main.lc;
+ if (locks & ERTS_PROC_LOCK_LINK)
+ have_locks[have_locks_len++] = p->lock.link.lc;
+ if (locks & ERTS_PROC_LOCK_MSGQ)
+ have_locks[have_locks_len++] = p->lock.msgq.lc;
+ if (locks & ERTS_PROC_LOCK_BTM)
+ have_locks[have_locks_len++] = p->lock.btm.lc;
+ if (locks & ERTS_PROC_LOCK_STATUS)
+ have_locks[have_locks_len++] = p->lock.status.lc;
+ if (locks & ERTS_PROC_LOCK_TRACE)
+ have_locks[have_locks_len++] = p->lock.trace.lc;
+#endif
+ erts_lc_check_exact(have_locks, have_locks_len);
+}
+
void
erts_proc_lc_chk_have_proc_locks(Process *p, ErtsProcLocks locks)
{
int have_locks_len = 0;
#if ERTS_PROC_LOCK_OWN_IMPL
- erts_lc_lock_t have_locks[5] = {ERTS_PROC_LC_EMPTY_LOCK_INIT,
+ erts_lc_lock_t have_locks[6] = {ERTS_PROC_LC_EMPTY_LOCK_INIT,
ERTS_PROC_LC_EMPTY_LOCK_INIT,
ERTS_PROC_LC_EMPTY_LOCK_INIT,
+ ERTS_PROC_LC_EMPTY_LOCK_INIT,
ERTS_PROC_LC_EMPTY_LOCK_INIT,
ERTS_PROC_LC_EMPTY_LOCK_INIT};
if (locks & ERTS_PROC_LOCK_MAIN) {
@@ -1486,8 +1582,12 @@ erts_proc_lc_chk_have_proc_locks(Process *p, ErtsProcLocks locks)
have_locks[have_locks_len].id = lc_id.proc_lock_status;
have_locks[have_locks_len++].extra = p->common.id;
}
+ if (locks & ERTS_PROC_LOCK_TRACE) {
+ have_locks[have_locks_len].id = lc_id.proc_lock_trace;
+ have_locks[have_locks_len++].extra = p->common.id;
+ }
#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
- erts_lc_lock_t have_locks[5];
+ erts_lc_lock_t have_locks[6];
if (locks & ERTS_PROC_LOCK_MAIN)
have_locks[have_locks_len++] = p->lock.main.lc;
if (locks & ERTS_PROC_LOCK_LINK)
@@ -1498,6 +1598,8 @@ erts_proc_lc_chk_have_proc_locks(Process *p, ErtsProcLocks locks)
have_locks[have_locks_len++] = p->lock.btm.lc;
if (locks & ERTS_PROC_LOCK_STATUS)
have_locks[have_locks_len++] = p->lock.status.lc;
+ if (locks & ERTS_PROC_LOCK_TRACE)
+ have_locks[have_locks_len++] = p->lock.trace.lc;
#endif
erts_lc_check(have_locks, have_locks_len, NULL, 0);
}
@@ -1508,12 +1610,14 @@ erts_proc_lc_chk_proc_locks(Process *p, ErtsProcLocks locks)
int have_locks_len = 0;
int have_not_locks_len = 0;
#if ERTS_PROC_LOCK_OWN_IMPL
- erts_lc_lock_t have_locks[5] = {ERTS_PROC_LC_EMPTY_LOCK_INIT,
+ erts_lc_lock_t have_locks[6] = {ERTS_PROC_LC_EMPTY_LOCK_INIT,
ERTS_PROC_LC_EMPTY_LOCK_INIT,
+ ERTS_PROC_LC_EMPTY_LOCK_INIT,
ERTS_PROC_LC_EMPTY_LOCK_INIT,
ERTS_PROC_LC_EMPTY_LOCK_INIT};
- erts_lc_lock_t have_not_locks[5] = {ERTS_PROC_LC_EMPTY_LOCK_INIT,
+ erts_lc_lock_t have_not_locks[6] = {ERTS_PROC_LC_EMPTY_LOCK_INIT,
ERTS_PROC_LC_EMPTY_LOCK_INIT,
+ ERTS_PROC_LC_EMPTY_LOCK_INIT,
ERTS_PROC_LC_EMPTY_LOCK_INIT,
ERTS_PROC_LC_EMPTY_LOCK_INIT};
@@ -1557,9 +1661,17 @@ erts_proc_lc_chk_proc_locks(Process *p, ErtsProcLocks locks)
have_not_locks[have_not_locks_len].id = lc_id.proc_lock_status;
have_not_locks[have_not_locks_len++].extra = p->common.id;
}
+ if (locks & ERTS_PROC_LOCK_TRACE) {
+ have_locks[have_locks_len].id = lc_id.proc_lock_trace;
+ have_locks[have_locks_len++].extra = p->common.id;
+ }
+ else {
+ have_not_locks[have_not_locks_len].id = lc_id.proc_lock_trace;
+ have_not_locks[have_not_locks_len++].extra = p->common.id;
+ }
#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
- erts_lc_lock_t have_locks[5];
- erts_lc_lock_t have_not_locks[5];
+ erts_lc_lock_t have_locks[6];
+ erts_lc_lock_t have_not_locks[6];
if (locks & ERTS_PROC_LOCK_MAIN)
have_locks[have_locks_len++] = p->lock.main.lc;
@@ -1581,6 +1693,10 @@ erts_proc_lc_chk_proc_locks(Process *p, ErtsProcLocks locks)
have_locks[have_locks_len++] = p->lock.status.lc;
else
have_not_locks[have_not_locks_len++] = p->lock.status.lc;
+ if (locks & ERTS_PROC_LOCK_TRACE)
+ have_locks[have_locks_len++] = p->lock.trace.lc;
+ else
+ have_not_locks[have_not_locks_len++] = p->lock.trace.lc;
#endif
erts_lc_check(have_locks, have_locks_len,
@@ -1590,10 +1706,10 @@ erts_proc_lc_chk_proc_locks(Process *p, ErtsProcLocks locks)
ErtsProcLocks
erts_proc_lc_my_proc_locks(Process *p)
{
- int resv[5];
+ int resv[6];
ErtsProcLocks res = 0;
#if ERTS_PROC_LOCK_OWN_IMPL
- erts_lc_lock_t locks[5] = {ERTS_LC_LOCK_INIT(lc_id.proc_lock_main,
+ erts_lc_lock_t locks[6] = {ERTS_LC_LOCK_INIT(lc_id.proc_lock_main,
p->common.id,
ERTS_LC_FLG_LT_PROCLOCK),
ERTS_LC_LOCK_INIT(lc_id.proc_lock_link,
@@ -1607,16 +1723,20 @@ erts_proc_lc_my_proc_locks(Process *p)
ERTS_LC_FLG_LT_PROCLOCK),
ERTS_LC_LOCK_INIT(lc_id.proc_lock_status,
p->common.id,
+ ERTS_LC_FLG_LT_PROCLOCK),
+ ERTS_LC_LOCK_INIT(lc_id.proc_lock_trace,
+ p->common.id,
ERTS_LC_FLG_LT_PROCLOCK)};
#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
- erts_lc_lock_t locks[5] = {p->lock.main.lc,
+ erts_lc_lock_t locks[6] = {p->lock.main.lc,
p->lock.link.lc,
p->lock.msgq.lc,
p->lock.btm.lc,
- p->lock.status.lc};
+ p->lock.status.lc,
+ p->lock.trace.lc};
#endif
- erts_lc_have_locks(resv, locks, 5);
+ erts_lc_have_locks(resv, locks, 6);
if (resv[0])
res |= ERTS_PROC_LOCK_MAIN;
if (resv[1])
@@ -1627,6 +1747,8 @@ erts_proc_lc_my_proc_locks(Process *p)
res |= ERTS_PROC_LOCK_BTM;
if (resv[4])
res |= ERTS_PROC_LOCK_STATUS;
+ if (resv[5])
+ res |= ERTS_PROC_LOCK_TRACE;
return res;
}
@@ -1634,14 +1756,15 @@ erts_proc_lc_my_proc_locks(Process *p)
void
erts_proc_lc_chk_no_proc_locks(char *file, int line)
{
- int resv[5];
- int ids[5] = {lc_id.proc_lock_main,
+ int resv[6];
+ int ids[6] = {lc_id.proc_lock_main,
lc_id.proc_lock_link,
lc_id.proc_lock_msgq,
lc_id.proc_lock_btm,
- lc_id.proc_lock_status};
- erts_lc_have_lock_ids(resv, ids, 5);
- if (!ERTS_IS_CRASH_DUMPING && (resv[0] || resv[1] || resv[2] || resv[3] || resv[4])) {
+ lc_id.proc_lock_status,
+ lc_id.proc_lock_trace};
+ erts_lc_have_lock_ids(resv, ids, 6);
+ if (!ERTS_IS_CRASH_DUMPING && (resv[0] || resv[1] || resv[2] || resv[3] || resv[4] || resv[5])) {
erts_lc_fail("%s:%d: Thread has process locks locked when expected "
"not to have any process locks locked",
file, line);
diff --git a/erts/emulator/beam/erl_process_lock.h b/erts/emulator/beam/erl_process_lock.h
index 9c59301086..2cccf0697a 100644
--- a/erts/emulator/beam/erl_process_lock.h
+++ b/erts/emulator/beam/erl_process_lock.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2007-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2007-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -66,7 +66,7 @@
#endif
-#define ERTS_PROC_LOCK_MAX_BIT 4
+#define ERTS_PROC_LOCK_MAX_BIT 5
typedef erts_aint32_t ErtsProcLocks;
@@ -84,6 +84,7 @@ typedef struct erts_proc_lock_t_ {
erts_lcnt_lock_t lcnt_msgq;
erts_lcnt_lock_t lcnt_btm;
erts_lcnt_lock_t lcnt_status;
+ erts_lcnt_lock_t lcnt_trace;
#endif
#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
erts_mtx_t main;
@@ -91,6 +92,7 @@ typedef struct erts_proc_lock_t_ {
erts_mtx_t msgq;
erts_mtx_t btm;
erts_mtx_t status;
+ erts_mtx_t trace;
#else
# error "no implementation"
#endif
@@ -137,9 +139,18 @@ typedef struct erts_proc_lock_t_ {
* Protects the following fields in the process structure:
* * pending_suspenders
* * suspendee
+ * * sys_tasks
* * ...
*/
-#define ERTS_PROC_LOCK_STATUS (((ErtsProcLocks) 1) << ERTS_PROC_LOCK_MAX_BIT)
+#define ERTS_PROC_LOCK_STATUS (((ErtsProcLocks) 1) << 4)
+
+/*
+ * Trace message lock:
+ * Protects the order in which messages are sent
+ * from trace nifs. This lock is taken inside enif_send.
+ *
+ */
+#define ERTS_PROC_LOCK_TRACE (((ErtsProcLocks) 1) << ERTS_PROC_LOCK_MAX_BIT)
/*
* Special fields:
@@ -154,8 +165,8 @@ typedef struct erts_proc_lock_t_ {
* all process locks are held, and are allowed to be read if
* at least one process lock (whichever one doesn't matter)
* is held:
- * * tracer_proc
- * * tracer_flags
+ * * common.tracer
+ * * common.trace_flags
*
* The following fields are only allowed to be accessed if
* both the schedule queue lock and at least one process lock
@@ -198,17 +209,15 @@ typedef struct erts_proc_lock_t_ {
/* ERTS_PROC_LOCKS_* are combinations of process locks */
-#define ERTS_PROC_LOCKS_MSG_RECEIVE (ERTS_PROC_LOCK_MSGQ \
- | ERTS_PROC_LOCK_STATUS)
-#define ERTS_PROC_LOCKS_MSG_SEND (ERTS_PROC_LOCK_MSGQ \
- | ERTS_PROC_LOCK_STATUS)
+#define ERTS_PROC_LOCKS_MSG_RECEIVE ERTS_PROC_LOCK_MSGQ
+#define ERTS_PROC_LOCKS_MSG_SEND ERTS_PROC_LOCK_MSGQ
#define ERTS_PROC_LOCKS_XSIG_SEND ERTS_PROC_LOCK_STATUS
#define ERTS_PROC_LOCKS_ALL \
((((ErtsProcLocks) 1) << (ERTS_PROC_LOCK_MAX_BIT + 1)) - 1)
#define ERTS_PROC_LOCKS_ALL_MINOR (ERTS_PROC_LOCKS_ALL \
- & ~ERTS_PROC_LOCK_MAIN)
+ & ~ERTS_PROC_LOCK_MAIN)
#define ERTS_PIX_LOCKS_BITS 10
@@ -260,6 +269,7 @@ void erts_proc_lc_might_unlock(Process *p, ErtsProcLocks locks);
void erts_proc_lc_chk_have_proc_locks(Process *p, ErtsProcLocks locks);
void erts_proc_lc_chk_proc_locks(Process *p, ErtsProcLocks locks);
void erts_proc_lc_chk_only_proc_main(Process *p);
+void erts_proc_lc_chk_only_proc(Process *p, ErtsProcLocks locks);
void erts_proc_lc_chk_no_proc_locks(char *file, int line);
ErtsProcLocks erts_proc_lc_my_proc_locks(Process *p);
int erts_proc_lc_trylock_force_busy(Process *p, ErtsProcLocks locks);
@@ -477,9 +487,15 @@ erts_smp_proc_raw_trylock__(Process *p, ErtsProcLocks locks)
if (locks & ERTS_PROC_LOCK_STATUS)
if (erts_mtx_trylock(&p->lock.status) == EBUSY)
goto busy_status;
+ if (locks & ERTS_PROC_LOCK_TRACE)
+ if (erts_mtx_trylock(&p->lock.trace) == EBUSY)
+ goto busy_trace;
return 0;
+busy_trace:
+ if (locks & ERTS_PROC_LOCK_TRACE)
+ erts_mtx_unlock(&p->lock.trace);
busy_status:
if (locks & ERTS_PROC_LOCK_BTM)
erts_mtx_unlock(&p->lock.btm);
@@ -568,6 +584,8 @@ erts_smp_proc_lock__(Process *p,
erts_mtx_lock(&p->lock.btm);
if (locks & ERTS_PROC_LOCK_STATUS)
erts_mtx_lock(&p->lock.status);
+ if (locks & ERTS_PROC_LOCK_TRACE)
+ erts_mtx_lock(&p->lock.trace);
#ifdef ERTS_PROC_LOCK_DEBUG
erts_proc_lock_op_debug(p, locks, 1);
@@ -653,6 +671,8 @@ erts_smp_proc_unlock__(Process *p,
erts_proc_lock_op_debug(p, locks, 0);
#endif
+ if (locks & ERTS_PROC_LOCK_TRACE)
+ erts_mtx_unlock(&p->lock.trace);
if (locks & ERTS_PROC_LOCK_STATUS)
erts_mtx_unlock(&p->lock.status);
if (locks & ERTS_PROC_LOCK_BTM)
diff --git a/erts/emulator/beam/erl_ptab.c b/erts/emulator/beam/erl_ptab.c
index 9ed175fe01..22830a19c4 100644
--- a/erts/emulator/beam/erl_ptab.c
+++ b/erts/emulator/beam/erl_ptab.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2012-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2012-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erl_ptab.h b/erts/emulator/beam/erl_ptab.h
index 8fd961e3ce..fecfd96ab0 100644
--- a/erts/emulator/beam/erl_ptab.h
+++ b/erts/emulator/beam/erl_ptab.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2012-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2012-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,14 +37,16 @@
#include "erl_alloc.h"
#include "erl_monitors.h"
-#define ERTS_TRACER_PROC(P) ((P)->common.tracer_proc)
+#define ERTS_TRACER(P) ((P)->common.tracer)
+#define ERTS_TRACER_MODULE(T) (CAR(list_val(T)))
+#define ERTS_TRACER_STATE(T) (CDR(list_val(T)))
#define ERTS_TRACE_FLAGS(P) ((P)->common.trace_flags)
#define ERTS_P_LINKS(P) ((P)->common.u.alive.links)
#define ERTS_P_MONITORS(P) ((P)->common.u.alive.monitors)
#define IS_TRACED(p) \
- (ERTS_TRACER_PROC((p)) != NIL)
+ (ERTS_TRACER(p) != NIL)
#define ARE_TRACE_FLAGS_ON(p,tf) \
((ERTS_TRACE_FLAGS((p)) & (tf|F_SENSITIVE)) == (tf))
#define IS_TRACED_FL(p,tf) \
@@ -56,7 +58,7 @@ typedef struct {
erts_atomic_t atmc;
Sint sint;
} refc;
- Eterm tracer_proc;
+ ErtsTracer tracer;
Uint trace_flags;
erts_smp_atomic_t timer;
union {
@@ -166,7 +168,7 @@ typedef struct {
#define ERTS_PTAB_INVALID_ID(TAG) \
((Eterm) \
- ((((1 << ERTS_PTAB_ID_DATA_SIZE) - 1) << ERTS_PTAB_ID_DATA_SHIFT) \
+ ((((1U << ERTS_PTAB_ID_DATA_SIZE) - 1) << ERTS_PTAB_ID_DATA_SHIFT) \
| (TAG)))
#define erts_ptab_is_valid_id(ID) \
diff --git a/erts/emulator/beam/erl_sched_spec_pre_alloc.c b/erts/emulator/beam/erl_sched_spec_pre_alloc.c
index caec24bc03..cab4bd73db 100644
--- a/erts/emulator/beam/erl_sched_spec_pre_alloc.c
+++ b/erts/emulator/beam/erl_sched_spec_pre_alloc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2011-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2011-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erl_sched_spec_pre_alloc.h b/erts/emulator/beam/erl_sched_spec_pre_alloc.h
index 4d07b0f674..7808d7d438 100644
--- a/erts/emulator/beam/erl_sched_spec_pre_alloc.h
+++ b/erts/emulator/beam/erl_sched_spec_pre_alloc.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2011-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2011-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erl_smp.h b/erts/emulator/beam/erl_smp.h
index 5fc5e989a6..713ed50b86 100644
--- a/erts/emulator/beam/erl_smp.h
+++ b/erts/emulator/beam/erl_smp.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erl_sock.h b/erts/emulator/beam/erl_sock.h
index 7be6062115..3429a52d7e 100644
--- a/erts/emulator/beam/erl_sock.h
+++ b/erts/emulator/beam/erl_sock.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2003-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erl_sys_driver.h b/erts/emulator/beam/erl_sys_driver.h
index 4031eef0aa..d46e88cb05 100644
--- a/erts/emulator/beam/erl_sys_driver.h
+++ b/erts/emulator/beam/erl_sys_driver.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erl_term.c b/erts/emulator/beam/erl_term.c
index bae3385d3f..7d857ad326 100644
--- a/erts/emulator/beam/erl_term.c
+++ b/erts/emulator/beam/erl_term.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2000-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,94 +28,35 @@
#include <stdlib.h>
#include <stdio.h>
-__decl_noreturn static void __noreturn
-et_abort(const char *expr, const char *file, unsigned line)
+void
+erts_set_literal_tag(Eterm *term, Eterm *hp_start, Eterm hsz)
{
-#ifdef EXIT_ON_ET_ABORT
- static int have_been_called = 0;
+#ifdef TAG_LITERAL_PTR
+ Eterm *hp_end, *hp;
+
+ hp_end = hp_start + hsz;
+ hp = hp_start;
- if (have_been_called) {
- abort();
- } else {
- /*
- * Prevent infinite loop.
- */
- have_been_called = 1;
- erts_exit(ERTS_ERROR_EXIT, "TYPE ASSERTION FAILED, file %s, line %u: %s\n", file, line, expr);
- }
-#else
- erts_fprintf(stderr, "TYPE ASSERTION FAILED, file %s, line %u: %s\n", file, line, expr);
- abort();
-#endif
-}
-
-#if ET_DEBUG
-#define ET_ASSERT(expr,file,line) \
-do { \
- if (!(expr)) \
- et_abort(#expr, file, line); \
-} while(0)
-#else
-#define ET_ASSERT(expr,file,line) do { } while(0)
-#endif
-
-#if ET_DEBUG
-unsigned tag_val_def_debug(Wterm x, const char *file, unsigned line)
-#else
-unsigned tag_val_def(Wterm x)
-#define file __FILE__
-#define line __LINE__
-#endif
-{
- static char msg[32];
-
- switch (x & _TAG_PRIMARY_MASK) {
- case TAG_PRIMARY_LIST:
- ET_ASSERT(_list_precond(x),file,line);
- return LIST_DEF;
- case TAG_PRIMARY_BOXED: {
- Eterm hdr = *boxed_val(x);
- ET_ASSERT(is_header(hdr),file,line);
- switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) {
- case (_TAG_HEADER_ARITYVAL >> _TAG_PRIMARY_SIZE): return TUPLE_DEF;
- case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE): return BIG_DEF;
- case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE): return BIG_DEF;
- case (_TAG_HEADER_REF >> _TAG_PRIMARY_SIZE): return REF_DEF;
- case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): return FLOAT_DEF;
- case (_TAG_HEADER_EXPORT >> _TAG_PRIMARY_SIZE): return EXPORT_DEF;
- case (_TAG_HEADER_FUN >> _TAG_PRIMARY_SIZE): return FUN_DEF;
- case (_TAG_HEADER_EXTERNAL_PID >> _TAG_PRIMARY_SIZE): return EXTERNAL_PID_DEF;
- case (_TAG_HEADER_EXTERNAL_PORT >> _TAG_PRIMARY_SIZE): return EXTERNAL_PORT_DEF;
- case (_TAG_HEADER_EXTERNAL_REF >> _TAG_PRIMARY_SIZE): return EXTERNAL_REF_DEF;
- case (_TAG_HEADER_MAP >> _TAG_PRIMARY_SIZE): return MAP_DEF;
- case (_TAG_HEADER_REFC_BIN >> _TAG_PRIMARY_SIZE): return BINARY_DEF;
- case (_TAG_HEADER_HEAP_BIN >> _TAG_PRIMARY_SIZE): return BINARY_DEF;
- case (_TAG_HEADER_SUB_BIN >> _TAG_PRIMARY_SIZE): return BINARY_DEF;
- case (_TAG_HEADER_BIN_MATCHSTATE >> _TAG_PRIMARY_SIZE): return MATCHSTATE_DEF;
- }
-
- break;
- }
- case TAG_PRIMARY_IMMED1: {
- switch ((x & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
- case (_TAG_IMMED1_PID >> _TAG_PRIMARY_SIZE): return PID_DEF;
- case (_TAG_IMMED1_PORT >> _TAG_PRIMARY_SIZE): return PORT_DEF;
- case (_TAG_IMMED1_IMMED2 >> _TAG_PRIMARY_SIZE): {
- switch ((x & _TAG_IMMED2_MASK) >> _TAG_IMMED1_SIZE) {
- case (_TAG_IMMED2_ATOM >> _TAG_IMMED1_SIZE): return ATOM_DEF;
- case (_TAG_IMMED2_NIL >> _TAG_IMMED1_SIZE): return NIL_DEF;
- }
- break;
+ while (hp < hp_end) {
+ switch (primary_tag(*hp)) {
+ case TAG_PRIMARY_BOXED:
+ case TAG_PRIMARY_LIST:
+ *hp |= TAG_LITERAL_PTR;
+ break;
+ case TAG_PRIMARY_HEADER:
+ if (header_is_thing(*hp)) {
+ hp += thing_arityval(*hp);
}
- case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE): return SMALL_DEF;
- }
- break;
- }
+ break;
+ default:
+ break;
+ }
+
+ hp++;
}
- erts_snprintf(msg, sizeof(msg), "tag_val_def: %#lx", (unsigned long) x);
- et_abort(msg, file, line);
-#undef file
-#undef line
+ if (is_boxed(*term) || is_list(*term))
+ *term |= TAG_LITERAL_PTR;
+#endif
}
/*
@@ -174,9 +115,7 @@ ET_DEFINE_CHECKED(Uint,external_thing_data_words,ExternalThing*,is_thing_ptr);
ET_DEFINE_CHECKED(Eterm,make_cp,UWord *,_is_taggable_pointer);
ET_DEFINE_CHECKED(UWord *,cp_val,Eterm,is_CP);
ET_DEFINE_CHECKED(Uint,catch_val,Eterm,is_catch);
-ET_DEFINE_CHECKED(Uint,x_reg_offset,Uint,_is_xreg);
-ET_DEFINE_CHECKED(Uint,y_reg_offset,Uint,_is_yreg);
-ET_DEFINE_CHECKED(Uint,x_reg_index,Uint,_is_xreg);
-ET_DEFINE_CHECKED(Uint,y_reg_index,Uint,_is_yreg);
+ET_DEFINE_CHECKED(Uint,loader_x_reg_index,Uint,_is_loader_x_reg);
+ET_DEFINE_CHECKED(Uint,loader_y_reg_index,Uint,_is_loader_y_reg);
#endif /* ET_DEBUG */
diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h
index d5e91d80d9..c3234ee349 100644
--- a/erts/emulator/beam/erl_term.h
+++ b/erts/emulator/beam/erl_term.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2014. All Rights Reserved.
+ * Copyright Ericsson AB 2000-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,34 +21,10 @@
#ifndef __ERL_TERM_H
#define __ERL_TERM_H
-#include "sys.h" /* defines HALFWORD_HEAP */
+#include "erl_mmap.h"
typedef UWord Wterm; /* Full word terms */
-#if HALFWORD_HEAP
-# define HEAP_ON_C_STACK 0
-# if HALFWORD_ASSERT
-# ifdef ET_DEBUG
-# undef ET_DEBUG
-# endif
-# define ET_DEBUG 1
-# endif
-# if 1
-# define CHECK_POINTER_MASK 0xFFFFFFFF00000000UL
-# define COMPRESS_POINTER(APointer) ((Eterm) (UWord) (APointer))
-# define EXPAND_POINTER(AnEterm) ((UWord) (AnEterm))
-# else
-# define CHECK_POINTER_MASK 0x0UL
-# define COMPRESS_POINTER(AnUint) (AnUint)
-# define EXPAND_POINTER(APointer) (APointer)
-# endif
-#else
-# define HEAP_ON_C_STACK 1
-# define CHECK_POINTER_MASK 0x0UL
-# define COMPRESS_POINTER(AnUint) (AnUint)
-# define EXPAND_POINTER(APointer) (APointer)
-#endif
-
struct erl_node_; /* Declared in erl_node_tables.h */
/*
@@ -74,6 +50,24 @@ struct erl_node_; /* Declared in erl_node_tables.h */
#define _ET_APPLY(F,X) _unchecked_##F(X)
#endif
+#if defined(ARCH_64)
+# define TAG_PTR_MASK__ 0x7
+# if !defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
+# ifdef HIPE
+# error Hipe on 64-bit needs a real mmap as it does not support the literal tag
+# endif
+# define TAG_LITERAL_PTR 0x4
+# else
+# undef TAG_LITERAL_PTR
+# endif
+#elif defined(ARCH_32)
+# define TAG_PTR_MASK__ 0x3
+# undef TAG_LITERAL_PTR
+#else
+# error Not supported arch
+#endif
+
+
#define _TAG_PRIMARY_SIZE 2
#define _TAG_PRIMARY_MASK 0x3
#define TAG_PRIMARY_HEADER 0x0
@@ -190,15 +184,13 @@ struct erl_node_; /* Declared in erl_node_tables.h */
/* boxed object access methods */
-#if HALFWORD_HEAP
-#define _is_taggable_pointer(x) (((UWord)(x) & (CHECK_POINTER_MASK | 0x3)) == 0)
-#define _boxed_precond(x) (is_boxed(x))
-#else
-#define _is_taggable_pointer(x) (((Uint)(x) & 0x3) == 0)
+
+#define _is_taggable_pointer(x) (((Uint)(x) & TAG_PTR_MASK__) == 0)
+
#define _boxed_precond(x) (is_boxed(x))
-#endif
-#define _is_aligned(x) (((Uint)(x) & 0x3) == 0)
-#define _unchecked_make_boxed(x) ((Uint) COMPRESS_POINTER(x) + TAG_PRIMARY_BOXED)
+
+#define _is_aligned(x) (((Uint)(x) & TAG_PTR_MASK__) == 0)
+#define _unchecked_make_boxed(x) ((Uint)(x) + TAG_PRIMARY_BOXED)
_ET_DECLARE_CHECKED(Eterm,make_boxed,const Eterm*)
#define make_boxed(x) _ET_APPLY(make_boxed,(x))
#if 1
@@ -209,12 +201,16 @@ _ET_DECLARE_CHECKED(int,is_boxed,Eterm)
#else
#define is_boxed(x) (((x) & _TAG_PRIMARY_MASK) == TAG_PRIMARY_BOXED)
#endif
-#define _unchecked_boxed_val(x) ((Eterm*) EXPAND_POINTER(((x) - TAG_PRIMARY_BOXED)))
+#ifdef TAG_LITERAL_PTR
+#define _unchecked_boxed_val(x) _unchecked_ptr_val(x)
+#else
+#define _unchecked_boxed_val(x) ((Eterm*) ((x) - TAG_PRIMARY_BOXED))
+#endif
_ET_DECLARE_CHECKED(Eterm*,boxed_val,Wterm)
#define boxed_val(x) _ET_APPLY(boxed_val,(x))
/* cons cell ("list") access methods */
-#define _unchecked_make_list(x) ((Uint) COMPRESS_POINTER(x) + TAG_PRIMARY_LIST)
+#define _unchecked_make_list(x) ((Uint)(x) + TAG_PRIMARY_LIST)
_ET_DECLARE_CHECKED(Eterm,make_list,const Eterm*)
#define make_list(x) _ET_APPLY(make_list,(x))
#if 1
@@ -226,12 +222,12 @@ _ET_DECLARE_CHECKED(int,is_not_list,Eterm)
#define is_list(x) (((x) & _TAG_PRIMARY_MASK) == TAG_PRIMARY_LIST)
#define is_not_list(x) (!is_list((x)))
#endif
-#if HALFWORD_HEAP
#define _list_precond(x) (is_list(x))
+#ifdef TAG_LITERAL_PTR
+#define _unchecked_list_val(x) _unchecked_ptr_val(x)
#else
-#define _list_precond(x) (is_list(x))
+#define _unchecked_list_val(x) ((Eterm*) ((x) - TAG_PRIMARY_LIST))
#endif
-#define _unchecked_list_val(x) ((Eterm*) EXPAND_POINTER((x) - TAG_PRIMARY_LIST))
_ET_DECLARE_CHECKED(Eterm*,list_val,Wterm)
#define list_val(x) _ET_APPLY(list_val,(x))
@@ -242,15 +238,22 @@ _ET_DECLARE_CHECKED(Eterm*,list_val,Wterm)
#define CDR(x) ((x)[1])
/* generic tagged pointer (boxed or list) access methods */
-#define _unchecked_ptr_val(x) ((Eterm*) EXPAND_POINTER((x) & ~((Uint) 0x3)))
+#define _unchecked_ptr_val(x) ((Eterm*) ((x) & ~((Uint) TAG_PTR_MASK__)))
#define ptr_val(x) _unchecked_ptr_val((x)) /*XXX*/
#define _unchecked_offset_ptr(x,offs) ((x)+((offs)*sizeof(Eterm)))
#define offset_ptr(x,offs) _unchecked_offset_ptr(x,offs) /*XXX*/
#define _unchecked_byte_offset_ptr(x,byte_offs) ((x)+(offs))
#define byte_offset_ptr(x,offs) _unchecked_byte_offset_ptr(x,offs) /*XXX*/
+#ifdef TAG_LITERAL_PTR
+#define _unchecked_is_not_literal_ptr(x) (!((x) & TAG_LITERAL_PTR))
+#define is_not_literal_ptr(x) _unchecked_is_not_literal_ptr((x)) /*XXX*/
+#define is_literal_ptr(x) (!is_not_literal_ptr((x))) /*XXX*/
+#endif
+
+
/* fixnum ("small") access methods */
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
#define SMALL_BITS (64-4)
#define SMALL_DIGITS (17)
#else
@@ -396,11 +399,7 @@ _ET_DECLARE_CHECKED(Eterm*,fun_val,Wterm)
_ET_DECLARE_CHECKED(Eterm*,export_val,Wterm)
#define export_val(x) _ET_APPLY(export_val,(x))
#define is_export_header(x) ((x) == HEADER_EXPORT)
-#if HALFWORD_HEAP
-#define HEADER_EXPORT _make_header(2,_TAG_HEADER_EXPORT)
-#else
#define HEADER_EXPORT _make_header(1,_TAG_HEADER_EXPORT)
-#endif
/* bignum access methods */
#define make_pos_bignum_header(sz) _make_header((sz),_TAG_HEADER_POS_BIG)
@@ -424,7 +423,7 @@ _ET_DECLARE_CHECKED(Eterm*,big_val,Wterm)
#define big_val(x) _ET_APPLY(big_val,(x))
/* flonum ("float") access methods */
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
#define HEADER_FLONUM _make_header(1,_TAG_HEADER_FLOAT)
#else
#define HEADER_FLONUM _make_header(2,_TAG_HEADER_FLOAT)
@@ -445,12 +444,12 @@ typedef union float_def
byte fb[sizeof(ieee754_8)];
Uint16 fs[sizeof(ieee754_8) / sizeof(Uint16)];
Uint32 fw[sizeof(ieee754_8) / sizeof(Uint32)];
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
Uint fdw;
#endif
} FloatDef;
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
#define FLOAT_VAL_GET_DOUBLE(fval, f) (f).fdw = *((fval)+1)
@@ -559,14 +558,6 @@ _ET_DECLARE_CHECKED(Eterm*,tuple_val,Wterm)
#define _GETBITS(X,Pos,Size) (((X) >> (Pos)) & ~(~((Uint) 0) << (Size)))
-/*
- * Creation in node specific data (pids, ports, refs)
- */
-
-#define _CRE_SIZE 2
-
-/* MAX value for the creation field in pid, port and reference */
-#define MAX_CREATION (1 << _CRE_SIZE)
/*
* PID layout (internal pids):
@@ -580,7 +571,7 @@ _ET_DECLARE_CHECKED(Eterm*,tuple_val,Wterm)
*
* n : number
*
- * Old pid layout:
+ * Very old pid layout:
*
* |3 3 2 2 2 2 2 2|2 2 2 2 1 1 1 1|1 1 1 1 1 1 | |
* |1 0 9 8 7 6 5 4|3 2 1 0 9 8 7 6|5 4 3 2 1 0 9 8|7 6 5 4 3 2 1 0|
@@ -727,7 +718,7 @@ _ET_DECLARE_CHECKED(struct erl_node_*,internal_port_node,Eterm)
#define ERTS_MAX_REF_NUMBERS 3
#define ERTS_REF_NUMBERS ERTS_MAX_REF_NUMBERS
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
# define ERTS_REF_WORDS (ERTS_REF_NUMBERS/2 + 1)
# define ERTS_REF_32BIT_WORDS (ERTS_REF_NUMBERS+1)
#else
@@ -749,7 +740,7 @@ typedef struct {
#define make_ref_thing_header(DW) \
_make_header((DW)+REF_THING_HEAD_SIZE-1,_TAG_HEADER_REF)
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
/*
* Ref layout on a 64-bit little endian machine:
@@ -1009,27 +1000,20 @@ _ET_DECLARE_CHECKED(struct erl_node_*,external_ref_node,Eterm)
#define MAP_HEADER_VAL(Hdr) (((Hdr) >> (_HEADER_ARITY_OFFS + MAP_HEADER_TAG_SZ + MAP_HEADER_ARITY_SZ)) & (0xffff))
#define make_hashmap(x) make_boxed((Eterm*)(x))
-#define make_hashmap_rel make_boxed_rel
#define is_hashmap(x) (is_boxed((x)) && is_hashmap_header(*boxed_val((x))))
#define is_not_hashmap(x) (!is_hashmap(x))
-#define is_hashmap_rel(RTERM,BASE) is_hashmap(rterm2wterm(RTERM,BASE))
#define is_hashmap_header(x) (((x) & (_HEADER_MAP_HASHMAP_HEAD_MASK)) == HAMT_SUBTAG_HEAD_ARRAY)
#define hashmap_val(x) _unchecked_boxed_val((x))
-#define hashmap_val_rel(RTERM, BASE) hashmap_val(rterm2wterm(RTERM, BASE))
#define make_flatmap(x) make_boxed((Eterm*)(x))
-#define make_flatmap_rel(x, BASE) make_boxed_rel((Eterm*)(x),(BASE))
#define is_flatmap(x) (is_boxed((x)) && is_flatmap_header(*boxed_val((x))))
-#define is_flatmap_rel(RTERM,BASE) is_flatmap(rterm2wterm(RTERM,BASE))
#define is_not_flatmap(x) (!is_flatmap((x)))
#define is_flatmap_header(x) (((x) & (_HEADER_MAP_SUBTAG_MASK)) == HAMT_SUBTAG_HEAD_FLATMAP)
#define flatmap_val(x) (_unchecked_boxed_val((x)))
-#define flatmap_val_rel(RTERM, BASE) flatmap_val(rterm2wterm(RTERM, BASE))
#define is_map_header(x) (((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_MAP)
#define is_map(x) (is_boxed((x)) && is_map_header(*boxed_val(x)))
#define is_not_map(x) (!is_map(x))
-#define is_map_rel(RTERM,BASE) is_map(rterm2wterm(RTERM,BASE))
/* number tests */
@@ -1048,14 +1032,14 @@ _ET_DECLARE_CHECKED(struct erl_node_*,external_ref_node,Eterm)
#error "fix yer arch, like"
#endif
-#define _unchecked_make_cp(x) ((Eterm) COMPRESS_POINTER(x))
+#define _unchecked_make_cp(x) ((Eterm)(x))
_ET_DECLARE_CHECKED(Eterm,make_cp,BeamInstr*)
#define make_cp(x) _ET_APPLY(make_cp,(x))
#define is_not_CP(x) ((x) & _CPMASK)
#define is_CP(x) (!is_not_CP(x))
-#define _unchecked_cp_val(x) ((BeamInstr*) EXPAND_POINTER(x))
+#define _unchecked_cp_val(x) ((BeamInstr*) (x))
_ET_DECLARE_CHECKED(BeamInstr*,cp_val,Eterm)
#define cp_val(x) _ET_APPLY(cp_val,(x))
@@ -1071,44 +1055,40 @@ _ET_DECLARE_CHECKED(Uint,catch_val,Eterm)
/*
* Overloaded tags.
*
- * SMALL = 15
- * ATOM/NIL=7
+ * In the loader, we want to tag a term in a way so that it can
+ * be any literal (atom/integer/float/tuple/list/binary) or a
+ * register.
*
- * Note that the two least significant bits in SMALL/ATOM/NIL always are 3;
- * thus, we can distinguish register from literals by looking at only these
- * two bits.
+ * We can achive that by overloading the PID and PORT tags to
+ * mean X and Y registers. That works because there are no
+ * pid or port literals.
*/
-#define X_REG_DEF 0
-#define Y_REG_DEF 1
-#define R_REG_DEF 2
+#define _LOADER_TAG_XREG _TAG_IMMED1_PID
+#define _LOADER_TAG_YREG _TAG_IMMED1_PORT
+#define _LOADER_TAG_SIZE _TAG_IMMED1_SIZE
+#define _LOADER_MASK _TAG_IMMED1_MASK
-#define beam_reg_tag(x) ((x) & 3)
+#define LOADER_X_REG _LOADER_TAG_XREG
+#define LOADER_Y_REG _LOADER_TAG_YREG
-#define make_rreg() R_REG_DEF
-#define make_xreg(ix) (((ix) * sizeof(Eterm)) | X_REG_DEF)
-#define make_yreg(ix) (((ix) * sizeof(Eterm)) | Y_REG_DEF)
+#define make_loader_x_reg(R) (((R) << _LOADER_TAG_SIZE) | _LOADER_TAG_XREG)
+#define make_loader_y_reg(R) (((R) << _LOADER_TAG_SIZE) | _LOADER_TAG_YREG)
-#define _is_xreg(x) (beam_reg_tag(x) == X_REG_DEF)
-#define _is_yreg(x) (beam_reg_tag(x) == Y_REG_DEF)
+#define loader_reg_index(R) ((R) >> _LOADER_TAG_SIZE)
-#define _unchecked_x_reg_offset(R) ((R) - X_REG_DEF)
-_ET_DECLARE_CHECKED(Uint,x_reg_offset,Uint)
-#define x_reg_offset(R) _ET_APPLY(x_reg_offset,(R))
+#define loader_tag(T) ((T) & _LOADER_MASK)
-#define _unchecked_y_reg_offset(R) ((R) - Y_REG_DEF)
-_ET_DECLARE_CHECKED(Uint,y_reg_offset,Uint)
-#define y_reg_offset(R) _ET_APPLY(y_reg_offset,(R))
+#define _is_loader_x_reg(x) (loader_tag(x) == _LOADER_TAG_XREG)
+#define _is_loader_y_reg(x) (loader_tag(x) == _LOADER_TAG_YREG)
-#define reg_index(R) ((R) / sizeof(Eterm))
+#define _unchecked_loader_x_reg_index(R) ((R) >> _LOADER_TAG_SIZE)
+_ET_DECLARE_CHECKED(Uint,loader_x_reg_index,Uint)
+#define loader_x_reg_index(R) _ET_APPLY(loader_x_reg_index,(R))
-#define _unchecked_x_reg_index(R) ((R) >> 2)
-_ET_DECLARE_CHECKED(Uint,x_reg_index,Uint)
-#define x_reg_index(R) _ET_APPLY(x_reg_index,(R))
-
-#define _unchecked_y_reg_index(R) ((R) >> 2)
-_ET_DECLARE_CHECKED(Uint,y_reg_index,Uint)
-#define y_reg_index(R) _ET_APPLY(y_reg_index,(R))
+#define _unchecked_loader_y_reg_index(R) ((R) >> _LOADER_TAG_SIZE)
+_ET_DECLARE_CHECKED(Uint,loader_y_reg_index,Uint)
+#define loader_y_reg_index(R) _ET_APPLY(loader_y_reg_index,(R))
/*
* Backwards compatibility definitions:
@@ -1141,11 +1121,11 @@ _ET_DECLARE_CHECKED(Uint,y_reg_index,Uint)
#define FIRST_VACANT_TAG_DEF 0x12
#if ET_DEBUG
-extern unsigned tag_val_def_debug(Wterm, const char*, unsigned);
-#define tag_val_def(x) tag_val_def_debug((x),__FILE__,__LINE__)
+ERTS_GLB_INLINE unsigned tag_val_def(Wterm, const char*, unsigned);
#else
-extern unsigned tag_val_def(Wterm);
+ERTS_GLB_INLINE unsigned tag_val_def(Wterm);
#endif
+
#define not_eq_tags(X,Y) (tag_val_def((X)) ^ tag_val_def((Y)))
#define NUMBER_CODE(x,y) ((tag_val_def(x) << 5) | tag_val_def(y))
@@ -1160,81 +1140,83 @@ extern unsigned tag_val_def(Wterm);
#define FLOAT_BIG _NUMBER_CODE(FLOAT_DEF,BIG_DEF)
#define FLOAT_FLOAT _NUMBER_CODE(FLOAT_DEF,FLOAT_DEF)
-#if HALFWORD_HEAP
-#define ptr2rel(PTR,BASE) ((Eterm*)((char*)(PTR) - (char*)(BASE)))
-#define rterm2wterm(REL,BASE) ((Wterm)(REL) + (Wterm)(BASE))
-
-#else /* HALFWORD_HEAP */
-
-#define ptr2rel(PTR,BASE) (PTR)
-#define rterm2wterm(REL,BASE) (REL)
-
-#endif /* !HALFWORD_HEAP */
-
-#define make_list_rel(PTR, BASE) make_list(ptr2rel(PTR,BASE))
-#define make_boxed_rel(PTR, BASE) make_boxed(ptr2rel(PTR,BASE))
-#define make_fun_rel make_boxed_rel
-#define make_binary_rel make_boxed_rel
-#define make_tuple_rel make_boxed_rel
-#define make_external_rel make_boxed_rel
-#define make_internal_ref_rel make_boxed_rel
-#define make_big_rel make_boxed_rel
-
-#define binary_val_rel(RTERM, BASE) binary_val(rterm2wterm(RTERM, BASE))
-#define list_val_rel(RTERM, BASE) list_val(rterm2wterm(RTERM, BASE))
-#define boxed_val_rel(RTERM, BASE) boxed_val(rterm2wterm(RTERM, BASE))
-#define tuple_val_rel(RTERM, BASE) tuple_val(rterm2wterm(RTERM, BASE))
-#define export_val_rel(RTERM, BASE) export_val(rterm2wterm(RTERM, BASE))
-#define fun_val_rel(RTERM, BASE) fun_val(rterm2wterm(RTERM, BASE))
-#define big_val_rel(RTERM,BASE) big_val(rterm2wterm(RTERM,BASE))
-#define float_val_rel(RTERM,BASE) float_val(rterm2wterm(RTERM,BASE))
-#define internal_ref_val_rel(RTERM,BASE) internal_ref_val(rterm2wterm(RTERM,BASE))
-
-#define external_thing_ptr_rel(RTERM, BASE) external_thing_ptr(rterm2wterm(RTERM, BASE))
-#define external_data_words_rel(RTERM,BASE) external_data_words(rterm2wterm(RTERM,BASE))
-
-#define external_port_node_rel(RTERM,BASE) external_port_node(rterm2wterm(RTERM,BASE))
-#define external_port_data_rel(RTERM,BASE) external_port_data(rterm2wterm(RTERM,BASE))
-
-#define is_external_pid_rel(RTERM,BASE) is_external_pid(rterm2wterm(RTERM,BASE))
-#define external_pid_node_rel(RTERM,BASE) external_pid_node(rterm2wterm(RTERM,BASE))
-#define external_pid_data_rel(RTERM,BASE) external_pid_data(rterm2wterm(RTERM,BASE))
+#define is_same(A,B) ((A)==(B))
-#define is_binary_rel(RTERM,BASE) is_binary(rterm2wterm(RTERM,BASE))
-#define is_float_rel(RTERM,BASE) is_float(rterm2wterm(RTERM,BASE))
-#define is_fun_rel(RTERM,BASE) is_fun(rterm2wterm(RTERM,BASE))
-#define is_big_rel(RTERM,BASE) is_big(rterm2wterm(RTERM,BASE))
-#define is_export_rel(RTERM,BASE) is_export(rterm2wterm(RTERM,BASE))
-#define is_tuple_rel(RTERM,BASE) is_tuple(rterm2wterm(RTERM,BASE))
+void erts_set_literal_tag(Eterm *term, Eterm *hp_start, Eterm hsz);
-#define GET_DOUBLE_REL(RTERM, f, BASE) GET_DOUBLE(rterm2wterm(RTERM,BASE), f)
-
-#define ref_thing_ptr_rel(RTERM,BASE) ref_thing_ptr(rterm2wterm(RTERM,BASE))
-#define is_internal_ref_rel(RTERM,BASE) is_internal_ref(rterm2wterm(RTERM,BASE))
-#define is_external_rel(RTERM,BASE) is_external(rterm2wterm(RTERM,BASE))
-#define is_external_port_rel(RTERM,BASE) is_external_port(rterm2wterm(RTERM,BASE))
-#define is_external_ref_rel(RTERM,BASE) is_external_ref(rterm2wterm(RTERM,BASE))
-
-#define external_node_rel(RTERM,BASE) external_node(rterm2wterm(RTERM,BASE))
-
-
-#if HALFWORD_HEAP
-ERTS_GLB_INLINE int is_same(Eterm a, Eterm* a_base, Eterm b, Eterm* b_base);
+#if ET_DEBUG
+#define ET_ASSERT(expr,file,line) \
+do { \
+ if (!(expr)) \
+ erl_assert_error("TYPE ASSERTION: " #expr, __FUNCTION__, file, line); \
+} while(0)
+#else
+#define ET_ASSERT(expr,file,line) do { } while(0)
+#endif
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-ERTS_GLB_INLINE int is_same(Eterm a, Eterm* a_base, Eterm b, Eterm* b_base)
-{
- /* If bases differ, assume a and b are on different "heaps",
- ie can only be same if immed */
- ASSERT(a_base == b_base || is_immed(a) || is_immed(b)
- || rterm2wterm(a,a_base) != rterm2wterm(b,b_base));
- return a == b && (a_base == b_base || is_immed(a));
+#if ET_DEBUG
+ERTS_GLB_INLINE unsigned tag_val_def(Wterm x, const char *file, unsigned line)
+#else
+ERTS_GLB_INLINE unsigned tag_val_def(Wterm x)
+#define file __FILE__
+#define line __LINE__
+#endif
+{
+ static char *msg = "tag_val_def error";
+
+ switch (x & _TAG_PRIMARY_MASK) {
+ case TAG_PRIMARY_LIST:
+ ET_ASSERT(_list_precond(x),file,line);
+ return LIST_DEF;
+ case TAG_PRIMARY_BOXED: {
+ Eterm hdr = *boxed_val(x);
+ ET_ASSERT(is_header(hdr),file,line);
+ switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_HEADER_ARITYVAL >> _TAG_PRIMARY_SIZE): return TUPLE_DEF;
+ case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE): return BIG_DEF;
+ case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE): return BIG_DEF;
+ case (_TAG_HEADER_REF >> _TAG_PRIMARY_SIZE): return REF_DEF;
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): return FLOAT_DEF;
+ case (_TAG_HEADER_EXPORT >> _TAG_PRIMARY_SIZE): return EXPORT_DEF;
+ case (_TAG_HEADER_FUN >> _TAG_PRIMARY_SIZE): return FUN_DEF;
+ case (_TAG_HEADER_EXTERNAL_PID >> _TAG_PRIMARY_SIZE): return EXTERNAL_PID_DEF;
+ case (_TAG_HEADER_EXTERNAL_PORT >> _TAG_PRIMARY_SIZE): return EXTERNAL_PORT_DEF;
+ case (_TAG_HEADER_EXTERNAL_REF >> _TAG_PRIMARY_SIZE): return EXTERNAL_REF_DEF;
+ case (_TAG_HEADER_MAP >> _TAG_PRIMARY_SIZE): return MAP_DEF;
+ case (_TAG_HEADER_REFC_BIN >> _TAG_PRIMARY_SIZE): return BINARY_DEF;
+ case (_TAG_HEADER_HEAP_BIN >> _TAG_PRIMARY_SIZE): return BINARY_DEF;
+ case (_TAG_HEADER_SUB_BIN >> _TAG_PRIMARY_SIZE): return BINARY_DEF;
+ case (_TAG_HEADER_BIN_MATCHSTATE >> _TAG_PRIMARY_SIZE): return MATCHSTATE_DEF;
+ }
+
+ break;
+ }
+ case TAG_PRIMARY_IMMED1: {
+ switch ((x & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_PID >> _TAG_PRIMARY_SIZE): return PID_DEF;
+ case (_TAG_IMMED1_PORT >> _TAG_PRIMARY_SIZE): return PORT_DEF;
+ case (_TAG_IMMED1_IMMED2 >> _TAG_PRIMARY_SIZE): {
+ switch ((x & _TAG_IMMED2_MASK) >> _TAG_IMMED1_SIZE) {
+ case (_TAG_IMMED2_ATOM >> _TAG_IMMED1_SIZE): return ATOM_DEF;
+ case (_TAG_IMMED2_NIL >> _TAG_IMMED1_SIZE): return NIL_DEF;
+ }
+ break;
+ }
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE): return SMALL_DEF;
+ }
+ break;
+ }
+ }
+ erl_assert_error(msg, __FUNCTION__, file, line);
+#undef file
+#undef line
}
#endif
-#else /* !HALFWORD_HEAP */
-#define is_same(A,A_BASE,B,B_BASE) ((A)==(B))
+#if ET_DEBUG
+#define tag_val_def(X) tag_val_def(X, __FILE__, __LINE__)
#endif
#endif /* __ERL_TERM_H */
diff --git a/erts/emulator/beam/erl_thr_progress.c b/erts/emulator/beam/erl_thr_progress.c
index 7b06fd840f..700ed90def 100644
--- a/erts/emulator/beam/erl_thr_progress.c
+++ b/erts/emulator/beam/erl_thr_progress.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2011-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2011-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -95,9 +95,9 @@
#define ERTS_THR_PRGR_FTL_ERR_BLCK_POLL_INTERVAL 100
-#define ERTS_THR_PRGR_LFLG_BLOCK (((erts_aint32_t) 1) << 31)
-#define ERTS_THR_PRGR_LFLG_NO_LEADER (((erts_aint32_t) 1) << 30)
-#define ERTS_THR_PRGR_LFLG_WAITING_UM (((erts_aint32_t) 1) << 29)
+#define ERTS_THR_PRGR_LFLG_BLOCK ((erts_aint32_t) (1U << 31))
+#define ERTS_THR_PRGR_LFLG_NO_LEADER ((erts_aint32_t) (1U << 30))
+#define ERTS_THR_PRGR_LFLG_WAITING_UM ((erts_aint32_t) (1U << 29))
#define ERTS_THR_PRGR_LFLG_ACTIVE_MASK (~(ERTS_THR_PRGR_LFLG_NO_LEADER \
| ERTS_THR_PRGR_LFLG_BLOCK \
| ERTS_THR_PRGR_LFLG_WAITING_UM))
@@ -142,8 +142,8 @@ init_nob(ERTS_THR_PRGR_ATOMIC *atmc, ErtsThrPrgrVal val)
#warning "Thread progress state debug is on"
#endif
-#define ERTS_THR_PROGRESS_STATE_DEBUG_LEADER (((erts_aint32_t) 1) << 0)
-#define ERTS_THR_PROGRESS_STATE_DEBUG_ACTIVE (((erts_aint32_t) 1) << 1)
+#define ERTS_THR_PROGRESS_STATE_DEBUG_LEADER ((erts_aint32_t) (1U << 0))
+#define ERTS_THR_PROGRESS_STATE_DEBUG_ACTIVE ((erts_aint32_t) (1U << 1))
#define ERTS_THR_PROGRESS_STATE_DEBUG_INIT(ID) \
erts_atomic32_init_nob(&intrnl->thr[(ID)].data.state_debug, \
@@ -179,10 +179,10 @@ do { \
#endif /* ERTS_THR_PROGRESS_STATE_DEBUG */
-#define ERTS_THR_PRGR_BLCKR_INVALID (~((erts_aint32_t) 0))
-#define ERTS_THR_PRGR_BLCKR_UNMANAGED (((erts_aint32_t) 1) << 31)
+#define ERTS_THR_PRGR_BLCKR_INVALID ((erts_aint32_t) (~0U))
+#define ERTS_THR_PRGR_BLCKR_UNMANAGED ((erts_aint32_t) (1U << 31))
-#define ERTS_THR_PRGR_BC_FLG_NOT_BLOCKING (((erts_aint32_t) 1) << 31)
+#define ERTS_THR_PRGR_BC_FLG_NOT_BLOCKING ((erts_aint32_t) (1U << 31))
#define ERTS_THR_PRGR_BM_BITS 32
#define ERTS_THR_PRGR_BM_SHIFT 5
@@ -700,6 +700,7 @@ leader_update(ErtsThrPrgrData *tpd)
tpd->leader_state.chk_next_ix = no_managed;
erts_atomic32_set_nob(&intrnl->misc.data.umrefc_ix.current,
(erts_aint32_t) new_umrefc_ix);
+ tpd->leader_state.umrefc_ix.current = new_umrefc_ix;
ETHR_MEMBAR(ETHR_StoreLoad);
refc = erts_atomic_read_nob(&intrnl->umrefc[umrefc_ix].refc);
ASSERT(refc >= 0);
@@ -969,8 +970,10 @@ erts_thr_progress_unmanaged_continue__(ErtsThrPrgrDelayHandle handle)
#ifdef ERTS_ENABLE_LOCK_CHECK
ErtsThrPrgrData *tpd = perhaps_thr_prgr_data(NULL);
ERTS_LC_ASSERT(tpd && tpd->is_delaying);
- tpd->is_delaying = 0;
- return_tmp_thr_prgr_data(tpd);
+ tpd->is_delaying--;
+ ASSERT(tpd->is_delaying >= 0);
+ if (!tpd->is_delaying)
+ return_tmp_thr_prgr_data(tpd);
#endif
ASSERT(!erts_thr_progress_is_managed_thread());
@@ -995,7 +998,7 @@ erts_thr_progress_unmanaged_delay__(void)
#ifdef ERTS_ENABLE_LOCK_CHECK
{
ErtsThrPrgrData *tpd = tmp_thr_prgr_data(NULL);
- tpd->is_delaying = 1;
+ tpd->is_delaying++;
}
#endif
return (ErtsThrPrgrDelayHandle) umrefc_ix;
@@ -1186,7 +1189,7 @@ wakeup_unmanaged_threads(ErtsThrPrgrUnmanagedWakeupData *umwd)
int hbase = hix << ERTS_THR_PRGR_BM_SHIFT;
int hbit;
for (hbit = 0; hbit < ERTS_THR_PRGR_BM_BITS; hbit++) {
- if (hmask & (1 << hbit)) {
+ if (hmask & (1U << hbit)) {
erts_aint_t lmask;
int lix = hbase + hbit;
ASSERT(0 <= lix && lix < umwd->low_sz);
@@ -1195,7 +1198,7 @@ wakeup_unmanaged_threads(ErtsThrPrgrUnmanagedWakeupData *umwd)
int lbase = lix << ERTS_THR_PRGR_BM_SHIFT;
int lbit;
for (lbit = 0; lbit < ERTS_THR_PRGR_BM_BITS; lbit++) {
- if (lmask & (1 << lbit)) {
+ if (lmask & (1U << lbit)) {
int id = lbase + lbit;
wakeup_unmanaged(id);
}
diff --git a/erts/emulator/beam/erl_thr_progress.h b/erts/emulator/beam/erl_thr_progress.h
index b89cc4c267..b2894ba1fe 100644
--- a/erts/emulator/beam/erl_thr_progress.h
+++ b/erts/emulator/beam/erl_thr_progress.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2011-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2011-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erl_thr_queue.c b/erts/emulator/beam/erl_thr_queue.c
index 7ff456b915..f56d0828dd 100644
--- a/erts/emulator/beam/erl_thr_queue.c
+++ b/erts/emulator/beam/erl_thr_queue.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2011-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2011-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -780,3 +780,35 @@ erts_thr_q_dequeue(ErtsThrQ_t *q)
return res;
#endif
}
+
+#ifdef USE_LTTNG_VM_TRACEPOINTS
+int
+erts_thr_q_length_dirty(ErtsThrQ_t *q)
+{
+ int n = 0;
+#ifndef USE_THREADS
+ void *res;
+ ErtsThrQElement_t *tmp;
+
+ for (tmp = q->first; tmp != NULL; tmp = tmp->next) {
+ n++;
+ }
+#else
+ ErtsThrQElement_t *e;
+ erts_aint_t inext;
+
+ e = ErtsThrQDirtyReadEl(&q->head.head);
+ inext = erts_atomic_read_acqb(&e->next);
+
+ while (inext != ERTS_AINT_NULL) {
+ e = (ErtsThrQElement_t *) inext;
+ if (e != &q->tail.data.marker) {
+ /* don't count marker */
+ n++;
+ }
+ inext = erts_atomic_read_acqb(&e->next);
+ }
+#endif
+ return n;
+}
+#endif
diff --git a/erts/emulator/beam/erl_thr_queue.h b/erts/emulator/beam/erl_thr_queue.h
index 27a6d03224..705a67af4c 100644
--- a/erts/emulator/beam/erl_thr_queue.h
+++ b/erts/emulator/beam/erl_thr_queue.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2011-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2011-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -190,6 +190,10 @@ void erts_thr_q_append_finalize_dequeue_data(ErtsThrQFinDeQ_t *,
int erts_thr_q_finalize_dequeue(ErtsThrQFinDeQ_t *);
void erts_thr_q_finalize_dequeue_state_init(ErtsThrQFinDeQ_t *);
+#ifdef USE_LTTNG_VM_TRACEPOINTS
+int erts_thr_q_length_dirty(ErtsThrQ_t *);
+#endif
+
#ifdef ERTS_SMP
ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_q_need_thr_progress(ErtsThrQ_t *q);
#endif
diff --git a/erts/emulator/beam/erl_threads.h b/erts/emulator/beam/erl_threads.h
index 34f91e2ec8..eccd49f2a9 100644
--- a/erts/emulator/beam/erl_threads.h
+++ b/erts/emulator/beam/erl_threads.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -2056,6 +2056,8 @@ erts_atomic64_read_dirty(erts_atomic64_t *var)
#endif /* !USE_THREADS */
+#include "erl_msacc.h"
+
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
ERTS_GLB_INLINE void
@@ -2414,6 +2416,7 @@ erts_cnd_wait(erts_cnd_t *cnd, erts_mtx_t *mtx)
{
#ifdef USE_THREADS
int res;
+ ERTS_MSACC_PUSH_AND_SET_STATE(ERTS_MSACC_STATE_SLEEP);
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_lc_unlock(&mtx->lc);
#endif
@@ -2432,6 +2435,7 @@ erts_cnd_wait(erts_cnd_t *cnd, erts_mtx_t *mtx)
#endif
if (res != 0 && res != EINTR)
erts_thr_fatal_error(res, "wait on condition variable");
+ ERTS_MSACC_POP_STATE();
#endif
}
@@ -3488,7 +3492,11 @@ ERTS_GLB_INLINE void erts_tse_reset(erts_tse_t *ep)
ERTS_GLB_INLINE int erts_tse_wait(erts_tse_t *ep)
{
#ifdef USE_THREADS
- return ethr_event_wait(&((ethr_ts_event *) ep)->event);
+ int res;
+ ERTS_MSACC_PUSH_AND_SET_STATE(ERTS_MSACC_STATE_SLEEP);
+ res = ethr_event_wait(&((ethr_ts_event *) ep)->event);
+ ERTS_MSACC_POP_STATE();
+ return res;
#else
return ENOTSUP;
#endif
@@ -3497,7 +3505,11 @@ ERTS_GLB_INLINE int erts_tse_wait(erts_tse_t *ep)
ERTS_GLB_INLINE int erts_tse_swait(erts_tse_t *ep, int spincount)
{
#ifdef USE_THREADS
- return ethr_event_swait(&((ethr_ts_event *) ep)->event, spincount);
+ int res;
+ ERTS_MSACC_PUSH_AND_SET_STATE(ERTS_MSACC_STATE_SLEEP);
+ res = ethr_event_swait(&((ethr_ts_event *) ep)->event, spincount);
+ ERTS_MSACC_POP_STATE();
+ return res;
#else
return ENOTSUP;
#endif
@@ -3506,8 +3518,12 @@ ERTS_GLB_INLINE int erts_tse_swait(erts_tse_t *ep, int spincount)
ERTS_GLB_INLINE int erts_tse_twait(erts_tse_t *ep, Sint64 tmo)
{
#ifdef USE_THREADS
- return ethr_event_twait(&((ethr_ts_event *) ep)->event,
- (ethr_sint64_t) tmo);
+ int res;
+ ERTS_MSACC_PUSH_AND_SET_STATE(ERTS_MSACC_STATE_SLEEP);
+ res = ethr_event_twait(&((ethr_ts_event *) ep)->event,
+ (ethr_sint64_t) tmo);
+ ERTS_MSACC_POP_STATE();
+ return res;
#else
return ENOTSUP;
#endif
@@ -3516,9 +3532,13 @@ ERTS_GLB_INLINE int erts_tse_twait(erts_tse_t *ep, Sint64 tmo)
ERTS_GLB_INLINE int erts_tse_stwait(erts_tse_t *ep, int spincount, Sint64 tmo)
{
#ifdef USE_THREADS
- return ethr_event_stwait(&((ethr_ts_event *) ep)->event,
- spincount,
- (ethr_sint64_t) tmo);
+ int res;
+ ERTS_MSACC_PUSH_AND_SET_STATE(ERTS_MSACC_STATE_SLEEP);
+ res = ethr_event_stwait(&((ethr_ts_event *) ep)->event,
+ spincount,
+ (ethr_sint64_t) tmo);
+ ERTS_MSACC_POP_STATE();
+ return res;
#else
return ENOTSUP;
#endif
diff --git a/erts/emulator/beam/erl_time.h b/erts/emulator/beam/erl_time.h
index 93a0d556bf..a1c4220633 100644
--- a/erts/emulator/beam/erl_time.h
+++ b/erts/emulator/beam/erl_time.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -142,6 +142,8 @@ erts_time_unit_conversion(Uint64 value,
Uint32 from_time_unit,
Uint32 to_time_unit);
+ErtsSysPerfCounter erts_perf_counter_unit(void);
+
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
ERTS_GLB_INLINE Uint64
diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c
index 99005356ed..6aa2a7500f 100644
--- a/erts/emulator/beam/erl_time_sup.c
+++ b/erts/emulator/beam/erl_time_sup.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2015. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1749,7 +1749,7 @@ erts_get_monotonic_time(ErtsSchedulerData *esdp)
{
ErtsMonotonicTime mtime = time_sup.r.o.get_time();
update_last_mtime(esdp, mtime);
- return mtime;
+ return mtime;
}
ErtsMonotonicTime
@@ -1957,15 +1957,16 @@ send_time_offset_changed_notifications(void *new_offsetp)
ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK;
erts_smp_proc_lock(rp, ERTS_PROC_LOCK_LINK);
if (erts_lookup_monitor(ERTS_P_MONITORS(rp), ref)) {
- ErlHeapFragment *bp;
+ ErtsMessage *mp;
ErlOffHeap *ohp;
Eterm message;
- hp = erts_alloc_message_heap(hsz, &bp, &ohp, rp, &rp_locks);
+ mp = erts_alloc_message_heap(rp, &rp_locks,
+ hsz, &hp, &ohp);
*patch_refp = ref;
ASSERT(hsz == size_object(message_template));
message = copy_struct(message_template, hsz, &hp, ohp);
- erts_queue_message(rp, &rp_locks, bp, message, NIL);
+ erts_queue_message(rp, rp_locks, mp, message, am_clock_service);
}
erts_smp_proc_unlock(rp, rp_locks);
}
@@ -2132,22 +2133,26 @@ time_unit_conversion(Process *c_p, Eterm term, ErtsMonotonicTime val, ErtsMonoto
/* Convert to common user specified time units */
switch (term) {
+ case am_second:
case am_seconds:
case make_small(1):
result = ERTS_MONOTONIC_TO_SEC(val) + muloff*ERTS_MONOTONIC_OFFSET_SEC;
ERTS_BIF_PREP_RET(ret, make_time_val(c_p, result));
break;
+ case am_millisecond:
case am_milli_seconds:
case make_small(1000):
result = ERTS_MONOTONIC_TO_MSEC(val) + muloff*ERTS_MONOTONIC_OFFSET_MSEC;
ERTS_BIF_PREP_RET(ret, make_time_val(c_p, result));
break;
+ case am_microsecond:
case am_micro_seconds:
case make_small(1000*1000):
result = ERTS_MONOTONIC_TO_USEC(val) + muloff*ERTS_MONOTONIC_OFFSET_USEC;
ERTS_BIF_PREP_RET(ret, make_time_val(c_p, result));
break;
#ifdef ARCH_64
+ case am_nanosecond:
case am_nano_seconds:
case make_small(1000*1000*1000):
result = ERTS_MONOTONIC_TO_NSEC(val) + muloff*ERTS_MONOTONIC_OFFSET_NSEC;
@@ -2158,7 +2163,7 @@ time_unit_conversion(Process *c_p, Eterm term, ErtsMonotonicTime val, ErtsMonoto
Eterm value, native_res;
#ifndef ARCH_64
Sint user_res;
- if (term == am_nano_seconds)
+ if (term == am_nanosecond || term == am_nano_seconds)
goto to_nano_seconds;
if (term_to_Sint(term, &user_res)) {
if (user_res == 1000*1000*1000) {
@@ -2347,7 +2352,7 @@ erts_napi_convert_time_unit(ErtsMonotonicTime val, int from, int to)
BIF_RETTYPE monotonic_time_0(BIF_ALIST_0)
{
ErtsMonotonicTime mtime = time_sup.r.o.get_time();
- update_last_mtime(ERTS_PROC_GET_SCHDATA(BIF_P), mtime);
+ update_last_mtime(erts_proc_sched_data(BIF_P), mtime);
mtime += ERTS_MONOTONIC_OFFSET_NATIVE;
BIF_RET(make_time_val(BIF_P, mtime));
}
@@ -2355,7 +2360,7 @@ BIF_RETTYPE monotonic_time_0(BIF_ALIST_0)
BIF_RETTYPE monotonic_time_1(BIF_ALIST_1)
{
ErtsMonotonicTime mtime = time_sup.r.o.get_time();
- update_last_mtime(ERTS_PROC_GET_SCHDATA(BIF_P), mtime);
+ update_last_mtime(erts_proc_sched_data(BIF_P), mtime);
BIF_RET(time_unit_conversion(BIF_P, BIF_ARG_1, mtime, 1));
}
@@ -2364,7 +2369,7 @@ BIF_RETTYPE system_time_0(BIF_ALIST_0)
ErtsMonotonicTime mtime, offset;
mtime = time_sup.r.o.get_time();
offset = get_time_offset();
- update_last_mtime(ERTS_PROC_GET_SCHDATA(BIF_P), mtime);
+ update_last_mtime(erts_proc_sched_data(BIF_P), mtime);
BIF_RET(make_time_val(BIF_P, mtime + offset));
}
@@ -2373,7 +2378,7 @@ BIF_RETTYPE system_time_1(BIF_ALIST_0)
ErtsMonotonicTime mtime, offset;
mtime = time_sup.r.o.get_time();
offset = get_time_offset();
- update_last_mtime(ERTS_PROC_GET_SCHDATA(BIF_P), mtime);
+ update_last_mtime(erts_proc_sched_data(BIF_P), mtime);
BIF_RET(time_unit_conversion(BIF_P, BIF_ARG_1, mtime + offset, 0));
}
@@ -2403,7 +2408,7 @@ BIF_RETTYPE timestamp_0(BIF_ALIST_0)
mtime = time_sup.r.o.get_time();
offset = get_time_offset();
- update_last_mtime(ERTS_PROC_GET_SCHDATA(BIF_P), mtime);
+ update_last_mtime(erts_proc_sched_data(BIF_P), mtime);
make_timestamp_value(&mega_sec, &sec, &micro_sec, mtime, offset);
@@ -2434,9 +2439,19 @@ BIF_RETTYPE os_system_time_0(BIF_ALIST_0)
BIF_RET(make_time_val(BIF_P, stime));
}
-BIF_RETTYPE os_system_time_1(BIF_ALIST_0)
+BIF_RETTYPE os_system_time_1(BIF_ALIST_1)
{
ErtsSystemTime stime = erts_os_system_time();
BIF_RET(time_unit_conversion(BIF_P, BIF_ARG_1, stime, 0));
}
+BIF_RETTYPE
+os_perf_counter_0(BIF_ALIST_0)
+{
+ BIF_RET(make_time_val(BIF_P, erts_sys_perf_counter()));
+}
+
+BIF_RETTYPE erts_internal_perf_counter_unit_0(BIF_ALIST_0)
+{
+ BIF_RET(make_time_val(BIF_P, erts_sys_perf_counter_unit()));
+}
diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c
index 114853cac4..00bb114c26 100644
--- a/erts/emulator/beam/erl_trace.c
+++ b/erts/emulator/beam/erl_trace.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2014. 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.
@@ -20,6 +20,19 @@
/*
* Support functions for tracing.
+ *
+ * Ideas for future speed improvements in tracing framework:
+ * * Move ErtsTracerNif into ErtsTracer
+ * + Removes need for locking
+ * + Removes hash lookup overhead
+ * + Use a refc on the ErtsTracerNif to know when it can
+ * be freed. We don't want to allocate a separate
+ * ErtsTracerNif for each module used.
+ * * Optimize GenericBp for cache locality by reusing equivalent
+ * GenericBp and GenericBpData in multiple tracer points.
+ * + Possibly we want to use specialized instructions for different
+ * types of trace so that the knowledge of which struct is used
+ * can be in the instruction.
*/
#ifdef HAVE_CONFIG_H
@@ -39,6 +52,7 @@
#include "erl_bits.h"
#include "erl_thr_progress.h"
#include "erl_bif_unique.h"
+#include "erl_map.h"
#if 0
#define DEBUG_PRINTOUTS
@@ -46,17 +60,15 @@
#undef DEBUG_PRINTOUTS
#endif
-extern BeamInstr beam_return_to_trace[1]; /* OpCode(i_return_to_trace) */
-extern BeamInstr beam_return_trace[1]; /* OpCode(i_return_trace) */
-extern BeamInstr beam_return_time_trace[1]; /* OpCode(i_return_time_trace) */
-
/* Pseudo export entries. Never filled in with data, only used to
yield unique pointers of the correct type. */
Export exp_send, exp_receive, exp_timeout;
-static Eterm system_seq_tracer;
-static Uint default_trace_flags;
-static Eterm default_tracer;
+static ErtsTracer system_seq_tracer;
+static Uint default_proc_trace_flags;
+static ErtsTracer default_proc_tracer;
+static Uint default_port_trace_flags;
+static ErtsTracer default_port_tracer;
static Eterm system_monitor;
static Eterm system_profile;
@@ -70,8 +82,6 @@ static erts_smp_rwmtx_t sys_trace_rwmtx;
enum ErtsSysMsgType {
SYS_MSG_TYPE_UNDEFINED,
- SYS_MSG_TYPE_TRACE,
- SYS_MSG_TYPE_SEQTRACE,
SYS_MSG_TYPE_SYSMON,
SYS_MSG_TYPE_ERRLGR,
SYS_MSG_TYPE_PROC_MSG,
@@ -176,7 +186,7 @@ take_timestamp(ErtsTraceTimeStamp *tsp, int ts_type)
hsz += 3; /* 2-tuple */
raw_unique = erts_raw_get_unique_monotonic_integer();
tsp->u.monotonic.raw_unique = raw_unique;
- hsz += erts_raw_unique_monotonic_integer_heap_size(raw_unique);
+ hsz += erts_raw_unique_monotonic_integer_heap_size(raw_unique, 0);
}
return hsz;
}
@@ -216,8 +226,7 @@ write_timestamp(ErtsTraceTimeStamp *tsp, Eterm **hpp)
return emtime;
raw = tsp->u.monotonic.raw_unique;
- unique = erts_raw_make_unique_monotonic_integer_value(hpp,
- raw);
+ unique = erts_raw_make_unique_monotonic_integer_value(hpp, raw, 0);
res = TUPLE2(*hpp, emtime, unique);
*hpp += 3;
return res;
@@ -228,7 +237,7 @@ write_timestamp(ErtsTraceTimeStamp *tsp, Eterm **hpp)
}
}
-#define PATCH_TS_SIZE(p) patch_ts_size(TFLGS_TS_TYPE(p))
+#ifdef ERTS_SMP
static ERTS_INLINE Uint
patch_ts_size(int ts_type)
@@ -248,6 +257,7 @@ patch_ts_size(int ts_type)
return 0;
}
}
+#endif /* ERTS_SMP */
/*
* Write a timestamp. The timestamp MUST be the last
@@ -299,43 +309,6 @@ write_ts(int ts_type, Eterm *hp, ErlHeapFragment *bp, Process *tracer)
return res;
}
-/*
- * Patch a timestamp into a tuple. The tuple MUST be the last thing
- * built on the heap before the call, and the timestamp MUST be
- * the last thing after the call. This since patch_ts() might adjust
- * the size of the used area.
- */
-
-#define PATCH_TS__(Type, Tuple, Hp, Bp, Tracer) \
- do { \
- int ts_type__ = (Type); \
- if (ts_type__) \
- patch_ts(ts_type__, (Tuple), (Hp), (Bp), (Tracer)); \
- } while (0)
-
-#ifdef ERTS_SMP
-#define PATCH_TS(Type, Tuple, Hp, Bp, Tracer) \
- PATCH_TS__((Type), (Tuple), (Hp), (Bp), NULL)
-#else
-#define PATCH_TS(Type, Tuple, Hp, Bp, Tracer) \
- PATCH_TS__((Type), (Tuple), (Hp), (Bp), (Tracer))
-#endif
-
-static ERTS_INLINE void
-patch_ts(int ts_type, Eterm tuple, Eterm* hp, ErlHeapFragment *bp, Process *tracer)
-{
- Eterm *tptr = tuple_val(tuple);
- int arity = arityval(*tptr);
-
- ASSERT(ts_type);
- ASSERT((tptr+arity+1) == hp);
-
- tptr[0] = make_arityval(arity+1);
- tptr[1] = am_trace_ts;
-
- *hp = write_ts(ts_type, hp+1, bp, tracer);
-}
-
#ifdef ERTS_SMP
static void enqueue_sys_msg_unlocked(enum ErtsSysMsgType type,
Eterm from,
@@ -350,6 +323,14 @@ static void enqueue_sys_msg(enum ErtsSysMsgType type,
static void init_sys_msg_dispatcher(void);
#endif
+static void init_tracer_nif(void);
+static int tracer_cmp_fun(void*, void*);
+static HashValue tracer_hash_fun(void*);
+static void *tracer_alloc_fun(void*);
+static void tracer_free_fun(void*);
+
+typedef struct ErtsTracerNif_ ErtsTracerNif;
+
void erts_init_trace(void) {
erts_smp_rwmtx_opt_t rwmtx_opts = ERTS_SMP_RWMTX_OPT_DEFAULT_INITER;
rwmtx_opts.type = ERTS_SMP_RWMTX_TYPE_EXTREMELY_FREQUENT_READ;
@@ -363,81 +344,67 @@ void erts_init_trace(void) {
erts_bif_trace_init();
erts_system_monitor_clear(NULL);
erts_system_profile_clear(NULL);
- default_trace_flags = F_INITIAL_TRACE_FLAGS;
- default_tracer = NIL;
- system_seq_tracer = am_false;
+ default_proc_trace_flags = F_INITIAL_TRACE_FLAGS;
+ default_proc_tracer = erts_tracer_nil;
+ default_port_trace_flags = F_INITIAL_TRACE_FLAGS;
+ default_port_tracer = erts_tracer_nil;
+ system_seq_tracer = erts_tracer_nil;
#ifdef ERTS_SMP
init_sys_msg_dispatcher();
#endif
+ init_tracer_nif();
}
-static Eterm system_seq_tracer;
-
-#ifdef ERTS_SMP
#define ERTS_ALLOC_SYSMSG_HEAP(SZ, BPP, OHPP, UNUSED) \
(*(BPP) = new_message_buffer((SZ)), \
*(OHPP) = &(*(BPP))->off_heap, \
(*(BPP))->mem)
-#else
-#define ERTS_ALLOC_SYSMSG_HEAP(SZ, BPP, OHPP, RPP) \
- erts_alloc_message_heap((SZ), (BPP), (OHPP), (RPP), 0)
-#endif
-#ifdef ERTS_SMP
-#define ERTS_ENQ_TRACE_MSG(FPID, TPID, MSG, BP) \
-do { \
- ERTS_LC_ASSERT(erts_smp_lc_mtx_is_locked(&smq_mtx)); \
- enqueue_sys_msg_unlocked(SYS_MSG_TYPE_TRACE, (FPID), (TPID), (MSG), (BP)); \
-} while(0)
-#else
-#define ERTS_ENQ_TRACE_MSG(FPID, TPROC, MSG, BP) \
- erts_queue_message((TPROC), NULL, (BP), (MSG), NIL)
-#endif
+enum ErtsTracerOpt {
+ TRACE_FUN_DEFAULT = 0,
+ TRACE_FUN_ENABLED = 1,
+ TRACE_FUN_T_SEND = 2,
+ TRACE_FUN_T_RECEIVE = 3,
+ TRACE_FUN_T_CALL = 4,
+ TRACE_FUN_T_SCHED_PROC = 5,
+ TRACE_FUN_T_SCHED_PORT = 6,
+ TRACE_FUN_T_GC = 7,
+ TRACE_FUN_T_PROCS = 8,
+ TRACE_FUN_T_PORTS = 9,
+ TRACE_FUN_E_SEND = 10,
+ TRACE_FUN_E_RECEIVE = 11,
+ TRACE_FUN_E_CALL = 12,
+ TRACE_FUN_E_SCHED_PROC = 13,
+ TRACE_FUN_E_SCHED_PORT = 14,
+ TRACE_FUN_E_GC = 15,
+ TRACE_FUN_E_PROCS = 16,
+ TRACE_FUN_E_PORTS = 17
+};
-/*
- * NOTE that the ERTS_GET_TRACER_REF() returns from the function (!!!)
- * using it, and resets the parameters used if the tracer is invalid, i.e.,
- * use it with extreme care!
- */
-#ifdef ERTS_SMP
-#define ERTS_NULL_TRACER_REF NIL
-#define ERTS_TRACER_REF_TYPE Eterm
- /* In the smp case, we never find the tracer invalid here (the sys
- message dispatcher thread takes care of that). */
-#define ERTS_GET_TRACER_REF(RES, TPID, TRACEE_FLGS) \
-do { (RES) = (TPID); } while(0)
-int
-erts_is_tracer_proc_valid(Process* p)
-{
- return 1;
-}
-#else
-#define ERTS_NULL_TRACER_REF NULL
-#define ERTS_TRACER_REF_TYPE Process *
-#define ERTS_GET_TRACER_REF(RES, TPID, TRACEE_FLGS) \
-do { \
- (RES) = erts_proc_lookup((TPID)); \
- if (!(RES) || !(ERTS_TRACE_FLAGS((RES)) & F_TRACER)) { \
- (TPID) = NIL; \
- (TRACEE_FLGS) &= ~TRACEE_FLAGS; \
- return; \
- } \
-} while (0)
-int
-erts_is_tracer_proc_valid(Process* p)
-{
- Process* tracer;
+#define NIF_TRACER_TYPES (18)
- tracer = erts_proc_lookup(ERTS_TRACER_PROC(p));
- if (tracer && ERTS_TRACE_FLAGS(tracer) & F_TRACER) {
- return 1;
- } else {
- ERTS_TRACER_PROC(p) = NIL;
- ERTS_TRACE_FLAGS(p) = ~TRACEE_FLAGS;
- return 0;
- }
-}
-#endif
+
+static ERTS_INLINE int
+send_to_tracer_nif_raw(Process *c_p, Process *tracee, const ErtsTracer tracer,
+ Uint trace_flags, Eterm t_p_id, ErtsTracerNif *tnif,
+ enum ErtsTracerOpt topt,
+ Eterm tag, Eterm msg, Eterm extra, Eterm pam_result);
+static ERTS_INLINE int
+send_to_tracer_nif(Process *c_p, ErtsPTabElementCommon *t_p,
+ Eterm t_p_id, ErtsTracerNif *tnif,
+ enum ErtsTracerOpt topt,
+ Eterm tag, Eterm msg, Eterm extra,
+ Eterm pam_result);
+static ERTS_INLINE Eterm
+call_enabled_tracer(const ErtsTracer tracer,
+ ErtsTracerNif **tnif_ref,
+ enum ErtsTracerOpt topt,
+ Eterm tag, Eterm t_p_id);
+static int
+is_tracer_enabled(Process* c_p, ErtsProcLocks c_p_locks,
+ ErtsPTabElementCommon *t_p,
+ ErtsTracerNif **tnif_ret,
+ enum ErtsTracerOpt topt, Eterm tag);
static Uint active_sched;
@@ -452,19 +419,6 @@ static void
exiting_reset(Eterm exiting)
{
erts_smp_rwmtx_rwlock(&sys_trace_rwmtx);
- if (exiting == default_tracer) {
- default_tracer = NIL;
- default_trace_flags &= TRACEE_FLAGS;
-#ifdef DEBUG
- default_trace_flags |= F_INITIAL_TRACE_FLAGS;
-#endif
- }
- if (exiting == system_seq_tracer) {
-#ifdef DEBUG_PRINTOUTS
- erts_fprintf(stderr, "seq tracer %T exited\n", exiting);
-#endif
- system_seq_tracer = am_false;
- }
if (exiting == system_monitor) {
#ifdef ERTS_SMP
system_monitor = NIL;
@@ -489,11 +443,7 @@ erts_trace_check_exiting(Eterm exiting)
{
int reset = 0;
erts_smp_rwmtx_rlock(&sys_trace_rwmtx);
- if (exiting == default_tracer)
- reset = 1;
- else if (exiting == system_seq_tracer)
- reset = 1;
- else if (exiting == system_monitor)
+ if (exiting == system_monitor)
reset = 1;
else if (exiting == system_profile)
reset = 1;
@@ -502,23 +452,25 @@ erts_trace_check_exiting(Eterm exiting)
exiting_reset(exiting);
}
-static ERTS_INLINE int
-is_valid_tracer(Eterm tracer)
-{
- return erts_proc_lookup(tracer) || erts_is_valid_tracer_port(tracer);
-}
-
-Eterm
-erts_set_system_seq_tracer(Process *c_p, ErtsProcLocks c_p_locks, Eterm new)
+ErtsTracer
+erts_set_system_seq_tracer(Process *c_p, ErtsProcLocks c_p_locks, ErtsTracer new)
{
- Eterm old;
+ ErtsTracer old;
- if (new != am_false && !is_valid_tracer(new))
- return THE_NON_VALUE;
+ if (!ERTS_TRACER_IS_NIL(new)) {
+ Eterm nif_result = call_enabled_tracer(
+ new, NULL, TRACE_FUN_ENABLED, am_trace_status, am_undefined);
+ switch (nif_result) {
+ case am_trace: break;
+ default:
+ return THE_NON_VALUE;
+ }
+ }
erts_smp_rwmtx_rwlock(&sys_trace_rwmtx);
old = system_seq_tracer;
- system_seq_tracer = new;
+ system_seq_tracer = erts_tracer_nil;
+ erts_tracer_update(&system_seq_tracer, new);
#ifdef DEBUG_PRINTOUTS
erts_fprintf(stderr, "set seq tracer new=%T old=%T\n", new, old);
@@ -527,66 +479,134 @@ erts_set_system_seq_tracer(Process *c_p, ErtsProcLocks c_p_locks, Eterm new)
return old;
}
-Eterm
+ErtsTracer
erts_get_system_seq_tracer(void)
{
- Eterm st;
+ ErtsTracer st;
erts_smp_rwmtx_rlock(&sys_trace_rwmtx);
st = system_seq_tracer;
#ifdef DEBUG_PRINTOUTS
erts_fprintf(stderr, "get seq tracer %T\n", st);
#endif
erts_smp_rwmtx_runlock(&sys_trace_rwmtx);
+
+ if (st != erts_tracer_nil &&
+ call_enabled_tracer(st, NULL, TRACE_FUN_ENABLED,
+ am_trace_status, am_undefined) == am_remove) {
+ st = erts_set_system_seq_tracer(NULL, 0, erts_tracer_nil);
+ ERTS_TRACER_CLEAR(&st);
+ }
+
return st;
}
static ERTS_INLINE void
-get_default_tracing(Uint *flagsp, Eterm *tracerp)
-{
- if (!(default_trace_flags & TRACEE_FLAGS))
- default_tracer = NIL;
-
- if (is_nil(default_tracer)) {
- default_trace_flags &= ~TRACEE_FLAGS;
- } else if (is_internal_pid(default_tracer)) {
- if (!erts_proc_lookup(default_tracer)) {
- reset_tracer:
- default_trace_flags &= ~TRACEE_FLAGS;
- default_tracer = NIL;
- }
+get_default_tracing(Uint *flagsp, ErtsTracer *tracerp,
+ Uint *default_trace_flags,
+ ErtsTracer *default_tracer)
+{
+ if (!(*default_trace_flags & TRACEE_FLAGS))
+ ERTS_TRACER_CLEAR(default_tracer);
+
+ if (ERTS_TRACER_IS_NIL(*default_tracer)) {
+ *default_trace_flags &= ~TRACEE_FLAGS;
} else {
- ASSERT(is_internal_port(default_tracer));
- if (!erts_is_valid_tracer_port(default_tracer))
- goto reset_tracer;
+ Eterm nif_res;
+ nif_res = call_enabled_tracer(*default_tracer,
+ NULL, TRACE_FUN_ENABLED,
+ am_trace_status, am_undefined);
+ switch (nif_res) {
+ case am_trace: break;
+ default: {
+ ErtsTracer curr_default_tracer = *default_tracer;
+ if (tracerp) {
+ /* we only have a rlock, so we have to unlock and then rwlock */
+ erts_smp_rwmtx_runlock(&sys_trace_rwmtx);
+ erts_smp_rwmtx_rwlock(&sys_trace_rwmtx);
+ }
+ /* check if someone else changed default tracer
+ while we got the write lock, if so we don't do
+ anything. */
+ if (curr_default_tracer == *default_tracer) {
+ *default_trace_flags &= ~TRACEE_FLAGS;
+ ERTS_TRACER_CLEAR(default_tracer);
+ }
+ if (tracerp) {
+ erts_smp_rwmtx_rwunlock(&sys_trace_rwmtx);
+ erts_smp_rwmtx_rlock(&sys_trace_rwmtx);
+ }
+ }
+ }
}
if (flagsp)
- *flagsp = default_trace_flags;
- if (tracerp)
- *tracerp = default_tracer;
+ *flagsp = *default_trace_flags;
+ if (tracerp) {
+ erts_tracer_update(tracerp,*default_tracer);
+ }
+}
+
+static ERTS_INLINE void
+erts_change_default_tracing(int setflags, Uint flags,
+ const ErtsTracer tracer,
+ Uint *default_trace_flags,
+ ErtsTracer *default_tracer)
+{
+ if (setflags)
+ *default_trace_flags |= flags;
+ else
+ *default_trace_flags &= ~flags;
+
+ erts_tracer_update(default_tracer, tracer);
+
+ get_default_tracing(NULL, NULL, default_trace_flags, default_tracer);
}
void
-erts_change_default_tracing(int setflags, Uint *flagsp, Eterm *tracerp)
+erts_change_default_proc_tracing(int setflags, Uint flagsp,
+ const ErtsTracer tracer)
{
erts_smp_rwmtx_rwlock(&sys_trace_rwmtx);
- if (flagsp) {
- if (setflags)
- default_trace_flags |= *flagsp;
- else
- default_trace_flags &= ~(*flagsp);
- }
- if (tracerp)
- default_tracer = *tracerp;
- get_default_tracing(flagsp, tracerp);
+ erts_change_default_tracing(
+ setflags, flagsp, tracer,
+ &default_proc_trace_flags,
+ &default_proc_tracer);
+ erts_smp_rwmtx_rwunlock(&sys_trace_rwmtx);
+}
+
+void
+erts_change_default_port_tracing(int setflags, Uint flagsp,
+ const ErtsTracer tracer)
+{
+ erts_smp_rwmtx_rwlock(&sys_trace_rwmtx);
+ erts_change_default_tracing(
+ setflags, flagsp, tracer,
+ &default_port_trace_flags,
+ &default_port_tracer);
erts_smp_rwmtx_rwunlock(&sys_trace_rwmtx);
}
void
-erts_get_default_tracing(Uint *flagsp, Eterm *tracerp)
+erts_get_default_proc_tracing(Uint *flagsp, ErtsTracer *tracerp)
+{
+ erts_smp_rwmtx_rlock(&sys_trace_rwmtx);
+ *tracerp = erts_tracer_nil; /* initialize */
+ get_default_tracing(
+ flagsp, tracerp,
+ &default_proc_trace_flags,
+ &default_proc_tracer);
+ erts_smp_rwmtx_runlock(&sys_trace_rwmtx);
+}
+
+void
+erts_get_default_port_tracing(Uint *flagsp, ErtsTracer *tracerp)
{
erts_smp_rwmtx_rlock(&sys_trace_rwmtx);
- get_default_tracing(flagsp, tracerp);
+ *tracerp = erts_tracer_nil; /* initialize */
+ get_default_tracing(
+ flagsp, tracerp,
+ &default_port_trace_flags,
+ &default_port_tracer);
erts_smp_rwmtx_runlock(&sys_trace_rwmtx);
}
@@ -624,29 +644,22 @@ erts_get_system_profile(void) {
return profile;
}
-#ifdef ERTS_SMP
-static void
-do_send_to_port(Eterm to,
- Port* unused_port,
- Eterm from,
- enum ErtsSysMsgType type,
- Eterm message)
-{
- Uint sz = size_object(message);
- ErlHeapFragment *bp = new_message_buffer(sz);
- Uint *hp = bp->mem;
- Eterm msg = copy_struct(message, sz, &hp, &bp->off_heap);
- enqueue_sys_msg_unlocked(type, from, to, msg, bp);
-}
-
-#define WRITE_SYS_MSG_TO_PORT write_sys_msg_to_port
+#ifdef HAVE_ERTS_NOW_CPU
+# define GET_NOW(m, s, u) \
+do { \
+ if (erts_cpu_timestamp) \
+ erts_get_now_cpu(m, s, u); \
+ else \
+ get_now(m, s, u); \
+} while (0)
#else
-#define WRITE_SYS_MSG_TO_PORT do_send_to_port
+# define GET_NOW(m, s, u) do {get_now(m, s, u);} while (0)
#endif
+
static void
-WRITE_SYS_MSG_TO_PORT(Eterm unused_to,
+write_sys_msg_to_port(Eterm unused_to,
Port* trace_port,
Eterm unused_from,
enum ErtsSysMsgType unused_type,
@@ -673,150 +686,6 @@ WRITE_SYS_MSG_TO_PORT(Eterm unused_to,
erts_free(ERTS_ALC_T_TMP, (void *) buffer);
}
-
-#ifndef ERTS_SMP
-/* Send {trace_ts, Pid, out, 0, Timestamp}
- * followed by {trace_ts, Pid, in, 0, NewTimestamp}
- *
- * 'NewTimestamp' through patch_ts().
- */
-static void
-do_send_schedfix_to_port(Port *trace_port, Eterm pid, Eterm timestamp, int ts_type) {
-#define LOCAL_HEAP_SIZE (5+5+ERTS_TRACE_PATCH_TS_MAX_SIZE)
- DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE);
- Eterm message;
- Eterm *hp;
- Eterm mfarity;
-
- UseTmpHeapNoproc(LOCAL_HEAP_SIZE);
- ASSERT(is_pid(pid));
- ASSERT(is_tuple(timestamp));
- ASSERT(*tuple_val(timestamp) == make_arityval(3));
-
- hp = local_heap;
- mfarity = make_small(0);
- message = TUPLE5(hp, am_trace_ts, pid, am_out, mfarity, timestamp);
- /* Note, hp is deliberately NOT incremented since it will be reused */
-
- do_send_to_port(trace_port->common.id,
- trace_port,
- pid,
- SYS_MSG_TYPE_UNDEFINED,
- message);
-
-
- message = TUPLE5(hp, am_trace_ts, pid, am_in, mfarity,
- NIL /* Will be overwritten by timestamp */);
- hp += 6;
- hp[-1] = write_ts(ts_type, hp, NULL, NULL);
-
- do_send_to_port(trace_port->common.id,
- trace_port,
- pid,
- SYS_MSG_TYPE_UNDEFINED,
- message);
- UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
-#undef LOCAL_HEAP_SIZE
-}
-#endif
-
-/* If (c_p != NULL), a fake schedule out/in message pair will be sent,
- * if the driver so requests.
- * It is assumed that 'message' is not an 'out' message.
- *
- * 'c_p' is the currently executing process, "tracee" is the traced process
- * which 'message' concerns => if (*tracee_flags & F_TIMESTAMP_MASK),
- * 'message' must contain a timestamp.
- */
-static void
-send_to_port(Process *c_p, Eterm message,
- Eterm *tracer_pid, Uint *tracee_flags) {
- Port* trace_port;
-#ifndef ERTS_SMP
- int ts_type;
-#define LOCAL_HEAP_SIZE ERTS_TRACE_PATCH_TS_MAX_SIZE
- Eterm ts;
- DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE);
-#endif
-
- ASSERT(is_internal_port(*tracer_pid));
-#ifdef ERTS_SMP
- if (is_not_internal_port(*tracer_pid))
- return;
-
- trace_port = NULL;
-#else
-
- trace_port = erts_id2port_sflgs(*tracer_pid,
- NULL,
- 0,
- ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP);
-
- if (!trace_port) {
- *tracee_flags &= ~TRACEE_FLAGS;
- *tracer_pid = NIL;
- return;
- }
-
- /*
- * Make a fake schedule only if the current process is traced
- * with 'running' and 'timestamp'.
- */
-
- if ( c_p == NULL ||
- (! IS_TRACED_FL(c_p, F_TRACE_SCHED | F_TIMESTAMP_MASK))) {
-#endif
- do_send_to_port(*tracer_pid,
- trace_port,
- c_p ? c_p->common.id : NIL,
- SYS_MSG_TYPE_TRACE,
- message);
-#ifndef ERTS_SMP
- erts_port_release(trace_port);
- return;
- }
-
- /*
- * Note that the process being traced for some type of trace messages
- * (e.g. getting_linked) need not be the current process. That other
- * process might not have timestamps enabled.
- */
- UseTmpHeapNoproc(LOCAL_HEAP_SIZE);
-
- /* A fake schedule might be needed.
- * Create a dummy trace message with timestamp to be
- * passed to do_send_schedfix_to_port().
- */
- ts_type = TFLGS_TS_TYPE(c_p);
- ts = write_ts(ts_type, local_heap, NULL, NULL);
-
- trace_port->control_flags &= ~PORT_CONTROL_FLAG_HEAVY;
- do_send_to_port(*tracer_pid,
- trace_port,
- c_p ? c_p->common.id : NIL,
- SYS_MSG_TYPE_TRACE,
- message);
-
- if (trace_port->control_flags & PORT_CONTROL_FLAG_HEAVY) {
- /* The driver has just informed us that the last write took a
- * non-neglectible amount of time.
- *
- * We need to fake some trace messages to compensate for the time the
- * current process had to sacrifice for the writing of the previous
- * trace message. We pretend that the process got scheduled out
- * just after writning the real trace message, and now gets scheduled
- * in again.
- */
- do_send_schedfix_to_port(trace_port, c_p->common.id, ts, ts_type);
- }
-
- erts_port_release(trace_port);
-
- UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
-#undef LOCAL_HEAP_SIZE
-#endif
-}
-
#ifndef ERTS_SMP
/* Profile send
* Checks if profiler is port or process
@@ -826,11 +695,9 @@ send_to_port(Process *c_p, Eterm message,
static void
profile_send(Eterm from, Eterm message) {
Uint sz = 0;
- ErlHeapFragment *bp = NULL;
Uint *hp = NULL;
Eterm msg = NIL;
Process *profile_p = NULL;
- ErlOffHeap *off_heap = NULL;
Eterm profiler = erts_get_system_profile();
@@ -847,15 +714,16 @@ profile_send(Eterm from, Eterm message) {
0,
ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP);
if (profiler_port) {
- do_send_to_port(profiler,
- profiler_port,
- NIL, /* or current process->common.id */
- SYS_MSG_TYPE_SYSPROF,
- message);
+ write_sys_msg_to_port(profiler,
+ profiler_port,
+ NIL, /* or current process->common.id */
+ SYS_MSG_TYPE_SYSPROF,
+ message);
erts_port_release(profiler_port);
}
} else {
+ ErtsMessage *mp;
ASSERT(is_internal_pid(profiler));
profile_p = erts_proc_lookup(profiler);
@@ -864,157 +732,32 @@ profile_send(Eterm from, Eterm message) {
return;
sz = size_object(message);
- hp = erts_alloc_message_heap(sz, &bp, &off_heap, profile_p, 0);
- msg = copy_struct(message, sz, &hp, &bp->off_heap);
-
- erts_queue_message(profile_p, NULL, bp, msg, NIL);
- }
-}
-
-#endif
-
-
-/* A fake schedule out/in message pair will be sent,
- * if the driver so requests.
- *
- * 'c_p' is the currently executing process, may be NULL.
- */
-static void
-seq_trace_send_to_port(Process *c_p,
- Eterm seq_tracer,
- Eterm message)
-{
- Port* trace_port;
-#ifndef ERTS_SMP
- int ts_type;
- Eterm ts;
-#define LOCAL_HEAP_SIZE ERTS_TRACE_PATCH_TS_MAX_SIZE
- DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE);
- UseTmpHeapNoproc(LOCAL_HEAP_SIZE);
-#endif
-
- ASSERT(is_internal_port(seq_tracer));
-#ifdef ERTS_SMP
- if (is_not_internal_port(seq_tracer))
- return;
-
- trace_port = NULL;
-#else
- trace_port = erts_id2port_sflgs(seq_tracer,
- NULL,
- 0,
- ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP);
- if (!trace_port) {
- system_seq_tracer = am_false;
-#ifndef ERTS_SMP
- UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
-#endif
- return;
- }
-
- if (c_p == NULL
- || (! IS_TRACED_FL(c_p, F_TRACE_SCHED | F_TIMESTAMP_MASK))) {
-#endif
- do_send_to_port(seq_tracer,
- trace_port,
- c_p ? c_p->common.id : NIL,
- SYS_MSG_TYPE_SEQTRACE,
- message);
-
-#ifndef ERTS_SMP
- erts_port_release(trace_port);
- UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
- return;
- }
- /* Make a fake schedule only if the current process is traced
- * with 'running' and 'timestamp'.
- */
+ mp = erts_alloc_message(sz, &hp);
+ if (sz == 0)
+ msg = message;
+ else
+ msg = copy_struct(message, sz, &hp, &mp->hfrag.off_heap);
- /* A fake schedule might be needed.
- * Create a dummy trace message with timestamp to be
- * passed to do_send_schedfix_to_port().
- */
- ts_type = TFLGS_TS_TYPE(c_p);
- ts = write_ts(ts_type, local_heap, NULL, NULL);
-
- trace_port->control_flags &= ~PORT_CONTROL_FLAG_HEAVY;
- do_send_to_port(seq_tracer,
- trace_port,
- c_p ? c_p->common.id : NIL,
- SYS_MSG_TYPE_SEQTRACE,
- message);
-
- if (trace_port->control_flags & PORT_CONTROL_FLAG_HEAVY) {
- /* The driver has just informed us that the last write took a
- * non-neglectible amount of time.
- *
- * We need to fake some trace messages to compensate for the time the
- * current process had to sacrifice for the writing of the previous
- * trace message. We pretend that the process got scheduled out
- * just after writing the real trace message, and now gets scheduled
- * in again.
- */
- do_send_schedfix_to_port(trace_port, c_p->common.id, ts, ts_type);
+ erts_queue_message(profile_p, 0, mp, msg, from);
}
-
- erts_port_release(trace_port);
-
- UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
-#undef LOCAL_HEAP_SIZE
-#endif
}
-static ERTS_INLINE void
-send_to_tracer(Process *tracee,
- ERTS_TRACER_REF_TYPE tracer_ref,
- Eterm msg,
- Eterm **hpp,
- ErlHeapFragment *bp,
- int no_fake_sched)
-{
- ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(tracee));
-
- erts_smp_mtx_lock(&smq_mtx);
-
- if (is_internal_pid(ERTS_TRACER_PROC(tracee))) {
- PATCH_TS(TFLGS_TS_TYPE(tracee), msg, *hpp, bp, tracer_ref);
- ERTS_ENQ_TRACE_MSG(tracee->common.id, tracer_ref, msg, bp);
- }
- else {
- ASSERT(is_internal_port(ERTS_TRACER_PROC(tracee)));
- PATCH_TS(TFLGS_TS_TYPE(tracee), msg, *hpp, NULL, NULL);
- send_to_port(no_fake_sched ? NULL : tracee,
- msg,
- &ERTS_TRACER_PROC(tracee),
- &ERTS_TRACE_FLAGS(tracee));
- }
-
- erts_smp_mtx_unlock(&smq_mtx);
-
-}
+#endif
static void
-trace_sched_aux(Process *p, Eterm what, int never_fake_sched)
+trace_sched_aux(Process *p, ErtsProcLocks locks, Eterm what)
{
-#define LOCAL_HEAP_SIZE (5+4+1+ERTS_TRACE_PATCH_TS_MAX_SIZE)
- DeclareTmpHeap(local_heap,LOCAL_HEAP_SIZE,p);
- Eterm tmp, mess, *hp;
- ErlHeapFragment *bp = NULL;
- ErlOffHeap *off_heap;
- ERTS_TRACER_REF_TYPE tracer_ref = ERTS_NULL_TRACER_REF;
- int sched_no, curr_func, to_port, no_fake_sched;
+ Eterm tmp, *hp;
+ int curr_func;
+ ErtsTracerNif *tnif = NULL;
- if (is_nil(ERTS_TRACER_PROC(p)))
+ if (ERTS_TRACER_IS_NIL(ERTS_TRACER(p)))
return;
- no_fake_sched = never_fake_sched;
-
switch (what) {
case am_out:
case am_out_exiting:
case am_out_exited:
- no_fake_sched = 1;
- break;
case am_in:
case am_in_exiting:
break;
@@ -1023,16 +766,8 @@ trace_sched_aux(Process *p, Eterm what, int never_fake_sched)
break;
}
- sched_no = IS_TRACED_FL(p, F_TRACE_SCHED_NO);
- to_port = is_internal_port(ERTS_TRACER_PROC(p));
-
- if (!to_port) {
- ASSERT(is_internal_pid(ERTS_TRACER_PROC(p)));
-
- ERTS_GET_TRACER_REF(tracer_ref,
- ERTS_TRACER_PROC(p),
- ERTS_TRACE_FLAGS(p));
- }
+ if (!is_tracer_enabled(p, locks, &p->common, &tnif, TRACE_FUN_E_SCHED_PROC, what))
+ return;
if (ERTS_PROC_IS_EXITING(p))
curr_func = 0;
@@ -1042,44 +777,16 @@ trace_sched_aux(Process *p, Eterm what, int never_fake_sched)
curr_func = p->current != NULL;
}
- UseTmpHeap(LOCAL_HEAP_SIZE,p);
-
- if (to_port)
- hp = local_heap;
- else {
- Uint size = 5;
- if (curr_func)
- size += 4;
- if (sched_no)
- size += 1;
- size += PATCH_TS_SIZE(p);
- hp = ERTS_ALLOC_SYSMSG_HEAP(size, &bp, &off_heap, tracer_ref);
- }
-
if (!curr_func) {
tmp = make_small(0);
} else {
+ hp = HAlloc(p, 4);
tmp = TUPLE3(hp,p->current[0],p->current[1],make_small(p->current[2]));
hp += 4;
}
- if (!sched_no) {
- mess = TUPLE4(hp, am_trace, p->common.id, what, tmp);
- hp += 5;
- }
- else {
-#ifdef ERTS_SMP
- Eterm sched_id = make_small(p->scheduler_data->no);
-#else
- Eterm sched_id = make_small(1);
-#endif
- mess = TUPLE5(hp, am_trace, p->common.id, what, sched_id, tmp);
- hp += 6;
- }
-
- send_to_tracer(p, tracer_ref, mess, &hp, bp, no_fake_sched);
- UnUseTmpHeap(LOCAL_HEAP_SIZE,p);
-#undef LOCAL_HEAP_SIZE
+ send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_SCHED_PROC,
+ what, tmp, THE_NON_VALUE, am_true);
}
/* Send {trace_ts, Pid, What, {Mod, Func, Arity}, Timestamp}
@@ -1089,9 +796,9 @@ trace_sched_aux(Process *p, Eterm what, int never_fake_sched)
* 'out_exiting', or 'out_exited'.
*/
void
-trace_sched(Process *p, Eterm what)
+trace_sched(Process *p, ErtsProcLocks locks, Eterm what)
{
- trace_sched_aux(p, what, 0);
+ trace_sched_aux(p, locks, what);
}
/* Send {trace_ts, Pid, Send, Msg, DestPid, Timestamp}
@@ -1102,140 +809,126 @@ trace_sched(Process *p, Eterm what)
void
trace_send(Process *p, Eterm to, Eterm msg)
{
- Eterm operation;
- unsigned sz_msg;
- unsigned sz_to;
- Eterm* hp;
- Eterm mess;
-
- if (!ARE_TRACE_FLAGS_ON(p, F_TRACE_SEND)) {
+ Eterm operation = am_send;
+ ErtsTracerNif *tnif = NULL;
+ ErtsTracingEvent* te;
+ Eterm pam_result;
+#ifdef ERTS_SMP
+ ErtsThrPrgrDelayHandle dhndl;
+#endif
+
+ ASSERT(ARE_TRACE_FLAGS_ON(p, F_TRACE_SEND));
+
+ te = &erts_send_tracing[erts_active_bp_ix()];
+ if (!te->on) {
return;
}
+ if (te->match_spec) {
+ Eterm args[2];
+ Uint32 return_flags;
+ args[0] = to;
+ args[1] = msg;
+ pam_result = erts_match_set_run_trace(p, p,
+ te->match_spec, args, 2,
+ ERTS_PAM_TMP_RESULT, &return_flags);
+ if (pam_result == am_false)
+ return;
+ if (ERTS_TRACE_FLAGS(p) & F_TRACE_SILENT) {
+ erts_match_set_release_result_trace(p, pam_result);
+ return;
+ }
+ } else
+ pam_result = am_true;
+
+#ifdef ERTS_SMP
+ dhndl = erts_thr_progress_unmanaged_delay();
+#endif
- operation = am_send;
if (is_internal_pid(to)) {
if (!erts_proc_lookup(to))
goto send_to_non_existing_process;
}
else if(is_external_pid(to)
&& external_pid_dist_entry(to) == erts_this_dist_entry) {
- char *s;
send_to_non_existing_process:
- s = "send_to_non_existing_process";
- operation = am_atom_put(s, sys_strlen(s));
+ operation = am_send_to_non_existing_process;
}
- if (is_internal_port(ERTS_TRACER_PROC(p))) {
-#define LOCAL_HEAP_SIZE (6 + ERTS_TRACE_PATCH_TS_MAX_SIZE)
- DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE);
- UseTmpHeapNoproc(LOCAL_HEAP_SIZE);
-
- hp = local_heap;
- mess = TUPLE5(hp, am_trace, p->common.id, operation, msg, to);
- hp += 6;
- erts_smp_mtx_lock(&smq_mtx);
- PATCH_TS(TFLGS_TS_TYPE(p), mess, hp, NULL, NULL);
- send_to_port(p, mess, &ERTS_TRACER_PROC(p), &ERTS_TRACE_FLAGS(p));
- UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
-#undef LOCAL_HEAP_SIZE
- erts_smp_mtx_unlock(&smq_mtx);
- } else {
- Uint need;
- ErlHeapFragment *bp;
- ErlOffHeap *off_heap;
- ERTS_TRACER_REF_TYPE tracer_ref;
-
- ASSERT(is_internal_pid(ERTS_TRACER_PROC(p)));
-
- ERTS_GET_TRACER_REF(tracer_ref,
- ERTS_TRACER_PROC(p),
- ERTS_TRACE_FLAGS(p));
-
- sz_msg = size_object(msg);
- sz_to = size_object(to);
- need = sz_msg + sz_to + 6 + PATCH_TS_SIZE(p);
-
- hp = ERTS_ALLOC_SYSMSG_HEAP(need, &bp, &off_heap, tracer_ref);
-
- to = copy_struct(to,
- sz_to,
- &hp,
- off_heap);
- msg = copy_struct(msg,
- sz_msg,
- &hp,
- off_heap);
- mess = TUPLE5(hp, am_trace, p->common.id, operation, msg, to);
- hp += 6;
+ if (is_tracer_enabled(p, ERTS_PROC_LOCK_MAIN, &p->common, &tnif,
+ TRACE_FUN_E_SEND, operation)) {
+ send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_SEND,
+ operation, msg, to, pam_result);
+ }
- erts_smp_mtx_lock(&smq_mtx);
+#ifdef ERTS_SMP
+ erts_thr_progress_unmanaged_continue(dhndl);
+#endif
- PATCH_TS(TFLGS_TS_TYPE(p), mess, hp, bp, tracer_ref);
- ERTS_ENQ_TRACE_MSG(p->common.id, tracer_ref, mess, bp);
- erts_smp_mtx_unlock(&smq_mtx);
- }
+ erts_match_set_release_result_trace(p, pam_result);
}
/* Send {trace_ts, Pid, receive, Msg, Timestamp}
* or {trace, Pid, receive, Msg}
*/
void
-trace_receive(Process *rp, Eterm msg)
+trace_receive(Process* receiver,
+ Eterm from,
+ Eterm msg, ErtsTracingEvent* te)
{
- Eterm mess;
- size_t sz_msg;
- Eterm* hp;
-
- if (is_internal_port(ERTS_TRACER_PROC(rp))) {
-#define LOCAL_HEAP_SIZE (5+ERTS_TRACE_PATCH_TS_MAX_SIZE)
- DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE);
- UseTmpHeapNoproc(LOCAL_HEAP_SIZE);
-
- hp = local_heap;
- mess = TUPLE4(hp, am_trace, rp->common.id, am_receive, msg);
- hp += 5;
- erts_smp_mtx_lock(&smq_mtx);
- PATCH_TS(TFLGS_TS_TYPE(rp), mess, hp, NULL, NULL);
- send_to_port(rp, mess, &ERTS_TRACER_PROC(rp), &ERTS_TRACE_FLAGS(rp));
- UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
-#undef LOCAL_HEAP_SIZE
- erts_smp_mtx_unlock(&smq_mtx);
- } else {
- Uint hsz;
- ErlHeapFragment *bp;
- ErlOffHeap *off_heap;
- ERTS_TRACER_REF_TYPE tracer_ref;
-
- ASSERT(is_internal_pid(ERTS_TRACER_PROC(rp)));
-
- ERTS_GET_TRACER_REF(tracer_ref,
- ERTS_TRACER_PROC(rp),
- ERTS_TRACE_FLAGS(rp));
-
- sz_msg = size_object(msg);
-
- hsz = sz_msg + 5 + PATCH_TS_SIZE(rp);
-
- hp = ERTS_ALLOC_SYSMSG_HEAP(hsz, &bp, &off_heap, tracer_ref);
-
- msg = copy_struct(msg, sz_msg, &hp, off_heap);
- mess = TUPLE4(hp, am_trace, rp->common.id, am_receive, msg);
- hp += 5;
-
- erts_smp_mtx_lock(&smq_mtx);
-
- PATCH_TS(TFLGS_TS_TYPE(rp), mess, hp, bp, tracer_ref);
- ERTS_ENQ_TRACE_MSG(rp->common.id, tracer_ref, mess, bp);
- erts_smp_mtx_unlock(&smq_mtx);
- }
+ ErtsTracerNif *tnif = NULL;
+ Eterm pam_result;
+
+ if (!te) {
+ te = &erts_receive_tracing[erts_active_bp_ix()];
+ if (!te->on)
+ return;
+ }
+ else ASSERT(te->on);
+
+ if (te->match_spec) {
+ Eterm args[3];
+ Uint32 return_flags;
+ if (is_pid(from)) {
+ args[0] = pid_node_name(from);
+ args[1] = from;
+ }
+ else {
+ ASSERT(is_atom(from));
+ args[0] = from; /* node name or other atom (e.g 'system') */
+ args[1] = am_undefined;
+ }
+ args[2] = msg;
+ pam_result = erts_match_set_run_trace(NULL, receiver,
+ te->match_spec, args, 3,
+ ERTS_PAM_TMP_RESULT, &return_flags);
+ if (pam_result == am_false)
+ return;
+ if (ERTS_TRACE_FLAGS(receiver) & F_TRACE_SILENT) {
+ erts_match_set_release_result_trace(NULL, pam_result);
+ return;
+ }
+ } else
+ pam_result = am_true;
+
+ if (is_tracer_enabled(NULL, 0, &receiver->common, &tnif,
+ TRACE_FUN_E_RECEIVE, am_receive)) {
+ send_to_tracer_nif(NULL, &receiver->common, receiver->common.id,
+ tnif, TRACE_FUN_T_RECEIVE,
+ am_receive, msg, THE_NON_VALUE, pam_result);
+ }
+ erts_match_set_release_result_trace(NULL, pam_result);
}
int
seq_trace_update_send(Process *p)
{
- Eterm seq_tracer = erts_get_system_seq_tracer();
+ ErtsTracer seq_tracer = erts_get_system_seq_tracer();
ASSERT((is_tuple(SEQ_TRACE_TOKEN(p)) || is_nil(SEQ_TRACE_TOKEN(p))));
- if ( (p->common.id == seq_tracer) || (SEQ_TRACE_TOKEN(p) == NIL)
+ if (have_no_seqtrace(SEQ_TRACE_TOKEN(p)) ||
+ (seq_tracer != NIL &&
+ call_enabled_tracer(seq_tracer, NULL,
+ TRACE_FUN_ENABLED, am_seq_trace,
+ p ? p->common.id : am_undefined) != am_trace)
#ifdef USE_VM_PROBES
|| (SEQ_TRACE_TOKEN(p) == am_have_dt_utag)
#endif
@@ -1267,20 +960,29 @@ seq_trace_output_generic(Eterm token, Eterm msg, Uint type,
Eterm receiver, Process *process, Eterm exitfrom)
{
Eterm mess;
- ErlHeapFragment* bp;
Eterm* hp;
Eterm label;
Eterm lastcnt_serial;
Eterm type_atom;
- int sz_exit;
- Eterm seq_tracer;
- int ts_type;
+ ErtsTracer seq_tracer;
+ int seq_tracer_flags = 0;
+#define LOCAL_HEAP_SIZE (64)
+ DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE);
seq_tracer = erts_get_system_seq_tracer();
ASSERT(is_tuple(token) || is_nil(token));
- if (SEQ_TRACE_T_SENDER(token) == seq_tracer || token == NIL ||
- (process && ERTS_TRACE_FLAGS(process) & F_SENSITIVE)) {
+ if (token == NIL || (process && ERTS_TRACE_FLAGS(process) & F_SENSITIVE) ||
+ ERTS_TRACER_IS_NIL(seq_tracer) ||
+ call_enabled_tracer(seq_tracer,
+ NULL, TRACE_FUN_ENABLED,
+ am_seq_trace,
+ process ? process->common.id : am_undefined) != am_trace) {
+ return;
+ }
+
+ if ((unsigned_val(SEQ_TRACE_T_FLAGS(token)) & type) == 0) {
+ /* No flags set, nothing to do */
return;
}
@@ -1293,147 +995,28 @@ seq_trace_output_generic(Eterm token, Eterm msg, Uint type,
return; /* To avoid warning */
}
- if ((unsigned_val(SEQ_TRACE_T_FLAGS(token)) & type) == 0) {
- /* No flags set, nothing to do */
- return;
- }
+ UseTmpHeapNoproc(LOCAL_HEAP_SIZE);
- if (seq_tracer == am_false) {
- return; /* no need to send anything */
+ hp = local_heap;
+ label = SEQ_TRACE_T_LABEL(token);
+ lastcnt_serial = TUPLE2(hp, SEQ_TRACE_T_LASTCNT(token),
+ SEQ_TRACE_T_SERIAL(token));
+ hp += 3;
+ if (exitfrom != NIL) {
+ msg = TUPLE3(hp, am_EXIT, exitfrom, msg);
+ hp += 4;
}
+ mess = TUPLE5(hp, type_atom, lastcnt_serial, SEQ_TRACE_T_SENDER(token), receiver, msg);
+ hp += 6;
- ts_type = ERTS_SEQTFLGS2TSTYPE(unsigned_val(SEQ_TRACE_T_FLAGS(token)));
-
- if (is_internal_port(seq_tracer)) {
-#define LOCAL_HEAP_SIZE (60 + ERTS_TRACE_PATCH_TS_MAX_SIZE)
- DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE);
- UseTmpHeapNoproc(LOCAL_HEAP_SIZE);
+ seq_tracer_flags |= ERTS_SEQTFLGS2TFLGS(unsigned_val(SEQ_TRACE_T_FLAGS(token)));
- hp = local_heap;
- label = SEQ_TRACE_T_LABEL(token);
- lastcnt_serial = TUPLE2(hp, SEQ_TRACE_T_LASTCNT(token),
- SEQ_TRACE_T_SERIAL(token));
- hp += 3;
- if (exitfrom != NIL) {
- msg = TUPLE3(hp, am_EXIT, exitfrom, msg);
- hp += 4;
- }
- mess = TUPLE5(hp, type_atom, lastcnt_serial, SEQ_TRACE_T_SENDER(token),
- receiver, msg);
- hp += 6;
+ send_to_tracer_nif_raw(NULL, process, seq_tracer, seq_tracer_flags,
+ label, NULL, TRACE_FUN_DEFAULT, am_seq_trace, mess,
+ THE_NON_VALUE, am_true);
- erts_smp_mtx_lock(&smq_mtx);
- if (!ts_type) {
- mess = TUPLE3(hp, am_seq_trace, label, mess);
- seq_trace_send_to_port(NULL, seq_tracer, mess);
- } else {
- mess = TUPLE4(hp, am_seq_trace, label, mess,
- NIL /* Will be overwritten by timestamp */);
- hp += 5;
- hp[-1] = write_ts(ts_type, hp, NULL, NULL);
- seq_trace_send_to_port(process, seq_tracer, mess);
- }
- UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
+ UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
#undef LOCAL_HEAP_SIZE
- erts_smp_mtx_unlock(&smq_mtx);
- } else {
-#ifndef ERTS_SMP
- Process* tracer;
-#endif
- Eterm sender_copy;
- Eterm receiver_copy;
- Eterm m2;
- Uint sz_label, sz_lastcnt_serial, sz_msg, sz_ts, sz_sender,
- sz_exitfrom, sz_receiver;
-
- ASSERT(is_internal_pid(seq_tracer));
-
-#ifndef ERTS_SMP
-
- tracer = erts_proc_lookup(seq_tracer);
- if (!tracer) {
- system_seq_tracer = am_false;
- return; /* no need to send anything */
- }
-#endif
- if (receiver == seq_tracer) {
- return; /* no need to send anything */
- }
-
- sz_label = size_object(SEQ_TRACE_T_LABEL(token));
- sz_sender = size_object(SEQ_TRACE_T_SENDER(token));
- sz_receiver = size_object(receiver);
- sz_lastcnt_serial = 3; /* TUPLE2 */
- sz_msg = size_object(msg);
-
- sz_ts = patch_ts_size(ts_type);
- if (exitfrom != NIL) {
- sz_exit = 4; /* create {'EXIT',exitfrom,msg} */
- sz_exitfrom = size_object(exitfrom);
- }
- else {
- sz_exit = 0;
- sz_exitfrom = 0;
- }
- bp = new_message_buffer(4 /* TUPLE3 */ + sz_ts + 6 /* TUPLE5 */
- + sz_lastcnt_serial + sz_label + sz_msg
- + sz_exit + sz_exitfrom
- + sz_sender + sz_receiver);
- hp = bp->mem;
- label = copy_struct(SEQ_TRACE_T_LABEL(token), sz_label, &hp, &bp->off_heap);
- lastcnt_serial = TUPLE2(hp,SEQ_TRACE_T_LASTCNT(token),SEQ_TRACE_T_SERIAL(token));
- hp += 3;
- m2 = copy_struct(msg, sz_msg, &hp, &bp->off_heap);
- if (sz_exit) {
- Eterm exitfrom_copy = copy_struct(exitfrom,
- sz_exitfrom,
- &hp,
- &bp->off_heap);
- m2 = TUPLE3(hp, am_EXIT, exitfrom_copy, m2);
- hp += 4;
- }
- sender_copy = copy_struct(SEQ_TRACE_T_SENDER(token),
- sz_sender,
- &hp,
- &bp->off_heap);
- receiver_copy = copy_struct(receiver,
- sz_receiver,
- &hp,
- &bp->off_heap);
- mess = TUPLE5(hp,
- type_atom,
- lastcnt_serial,
- sender_copy,
- receiver_copy,
- m2);
- hp += 6;
-
- erts_smp_mtx_lock(&smq_mtx);
-
- if (!ts_type)
- mess = TUPLE3(hp, am_seq_trace, label, mess);
- else {
- mess = TUPLE4(hp, am_seq_trace, label, mess,
- NIL /* Will be overwritten by timestamp */);
- hp += 5;
- /* Write timestamp in element 6 of the 'msg' tuple */
- hp[-1] = write_ts(ts_type, hp, bp,
-#ifndef ERTS_SMP
- tracer
-#else
- NULL
-#endif
- );
- }
-
-#ifdef ERTS_SMP
- enqueue_sys_msg_unlocked(SYS_MSG_TYPE_SEQTRACE, NIL, NIL, mess, bp);
- erts_smp_mtx_unlock(&smq_mtx);
-#else
- /* trace_token must be NIL here */
- erts_queue_message(tracer, NULL, bp, mess, NIL);
-#endif
- }
}
/* Send {trace_ts, Pid, return_to, {Mod, Func, Arity}, Timestamp}
@@ -1442,63 +1025,20 @@ seq_trace_output_generic(Eterm token, Eterm msg, Uint type,
void
erts_trace_return_to(Process *p, BeamInstr *pc)
{
-#define LOCAL_HEAP_SIZE (4+5+ERTS_TRACE_PATCH_TS_MAX_SIZE)
- Eterm* hp;
Eterm mfa;
- Eterm mess;
- DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE);
BeamInstr *code_ptr = find_function_from_pc(pc);
- UseTmpHeapNoproc(LOCAL_HEAP_SIZE);
-
- hp = local_heap;
-
if (!code_ptr) {
mfa = am_undefined;
} else {
+ Eterm *hp = HAlloc(p, 4);
mfa = TUPLE3(hp, code_ptr[0], code_ptr[1], make_small(code_ptr[2]));
- hp += 4;
}
-
- mess = TUPLE4(hp, am_trace, p->common.id, am_return_to, mfa);
- hp += 5;
-
- erts_smp_mtx_lock(&smq_mtx);
-
- PATCH_TS(TFLGS_TS_TYPE(p), mess, hp, NULL, NULL);
- if (is_internal_port(ERTS_TRACER_PROC(p))) {
- send_to_port(p, mess, &ERTS_TRACER_PROC(p), &ERTS_TRACE_FLAGS(p));
- } else {
- ErlHeapFragment *bp;
- ErlOffHeap *off_heap;
- ERTS_TRACER_REF_TYPE tracer_ref;
- unsigned size;
-
- /*
- * Find the tracer.
- */
- ASSERT(is_internal_pid(ERTS_TRACER_PROC(p)));
-
- ERTS_GET_TRACER_REF(tracer_ref,
- ERTS_TRACER_PROC(p),
- ERTS_TRACE_FLAGS(p));
-
- size = size_object(mess);
-
- hp = ERTS_ALLOC_SYSMSG_HEAP(size, &bp, &off_heap, tracer_ref);
-
- /*
- * Copy the trace message into the buffer and enqueue it.
- */
- mess = copy_struct(mess, size, &hp, off_heap);
- ERTS_ENQ_TRACE_MSG(p->common.id, tracer_ref, mess, bp);
- }
- UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
-#undef LOCAL_HEAP_SIZE
- erts_smp_mtx_unlock(&smq_mtx);
+ send_to_tracer_nif(p, &p->common, p->common.id, NULL, TRACE_FUN_T_CALL,
+ am_return_to, mfa, THE_NON_VALUE, am_true);
}
@@ -1506,114 +1046,53 @@ erts_trace_return_to(Process *p, BeamInstr *pc)
* or {trace, Pid, return_from, {Mod, Name, Arity}, Retval}
*/
void
-erts_trace_return(Process* p, BeamInstr* fi, Eterm retval, Eterm *tracer_pid)
+erts_trace_return(Process* p, BeamInstr* fi, Eterm retval, ErtsTracer *tracer)
{
Eterm* hp;
- Eterm mfa;
- Eterm mess;
- Eterm mod, name;
+ Eterm mfa, mod, name;
int arity;
Uint meta_flags, *tracee_flags;
- int ts_type;
-#ifdef ERTS_SMP
- Eterm tracee;
-#endif
-
- ASSERT(tracer_pid);
- if (*tracer_pid == am_true) {
+
+ ASSERT(tracer);
+ if (ERTS_TRACER_COMPARE(*tracer, erts_tracer_true)) {
/* Breakpoint trace enabled without specifying tracer =>
* use process tracer and flags
*/
- tracer_pid = &ERTS_TRACER_PROC(p);
+ tracer = &ERTS_TRACER(p);
}
- if (is_nil(*tracer_pid)) {
+ if (ERTS_TRACER_IS_NIL(*tracer)) {
/* Trace disabled */
return;
}
- ASSERT(is_internal_pid(*tracer_pid) || is_internal_port(*tracer_pid));
- if (*tracer_pid == p->common.id) {
- /* Do not generate trace messages to oneself */
- return;
- }
- if (tracer_pid == &ERTS_TRACER_PROC(p)) {
+ ASSERT(IS_TRACER_VALID(*tracer));
+ if (tracer == &ERTS_TRACER(p)) {
/* Tracer specified in process structure =>
* non-breakpoint trace =>
* use process flags
*/
tracee_flags = &ERTS_TRACE_FLAGS(p);
-#ifdef ERTS_SMP
- tracee = p->common.id;
-#endif
+ if (! (*tracee_flags & F_TRACE_CALLS)) {
+ return;
+ }
} else {
/* Tracer not specified in process structure =>
* tracer specified in breakpoint =>
* meta trace =>
* use fixed flag set instead of process flags
- */
+ */
meta_flags = F_TRACE_CALLS | F_NOW_TS;
tracee_flags = &meta_flags;
-#ifdef ERTS_SMP
- tracee = NIL;
-#endif
}
- if (! (*tracee_flags & F_TRACE_CALLS)) {
- return;
- }
-
+
mod = fi[0];
name = fi[1];
arity = fi[2];
-
- ts_type = ERTS_TFLGS2TSTYPE(*tracee_flags);
-
- if (is_internal_port(*tracer_pid)) {
-#define LOCAL_HEAP_SIZE (4+6+ERTS_TRACE_PATCH_TS_MAX_SIZE)
- DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE);
- UseTmpHeapNoproc(LOCAL_HEAP_SIZE);
- hp = local_heap;
- mfa = TUPLE3(hp, mod, name, make_small(arity));
- hp += 4;
- mess = TUPLE5(hp, am_trace, p->common.id, am_return_from, mfa, retval);
- hp += 6;
- erts_smp_mtx_lock(&smq_mtx);
- PATCH_TS(ts_type, mess, hp, NULL, NULL);
- send_to_port(p, mess, tracer_pid, tracee_flags);
- UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
-#undef LOCAL_HEAP_SIZE
- erts_smp_mtx_unlock(&smq_mtx);
- } else {
- ErlHeapFragment *bp;
- ErlOffHeap *off_heap;
- ERTS_TRACER_REF_TYPE tracer_ref;
- unsigned size;
- unsigned retval_size;
-
- ASSERT(is_internal_pid(*tracer_pid));
- ERTS_GET_TRACER_REF(tracer_ref, *tracer_pid, *tracee_flags);
-
- retval_size = size_object(retval);
- size = 6 + 4 + retval_size + patch_ts_size(ts_type);
-
- hp = ERTS_ALLOC_SYSMSG_HEAP(size, &bp, &off_heap, tracer_ref);
-
- /*
- * Build the trace tuple and put it into receive queue of the tracer process.
- */
-
- mfa = TUPLE3(hp, mod, name, make_small(arity));
- hp += 4;
- retval = copy_struct(retval, retval_size, &hp, off_heap);
- mess = TUPLE5(hp, am_trace, p->common.id, am_return_from, mfa, retval);
- hp += 6;
-
- erts_smp_mtx_lock(&smq_mtx);
-
- PATCH_TS(ts_type, mess, hp, bp, tracer_ref);
-
- ERTS_ENQ_TRACE_MSG(tracee, tracer_ref, mess, bp);
- erts_smp_mtx_unlock(&smq_mtx);
- }
+ hp = HAlloc(p, 4);
+ mfa = TUPLE3(hp, mod, name, make_small(arity));
+ hp += 4;
+ send_to_tracer_nif_raw(p, NULL, *tracer, *tracee_flags, p->common.id,
+ NULL, TRACE_FUN_T_CALL, am_return_from, mfa, retval, am_true);
}
/* Send {trace_ts, Pid, exception_from, {Mod, Name, Arity}, {Class,Value},
@@ -1625,116 +1104,50 @@ erts_trace_return(Process* p, BeamInstr* fi, Eterm retval, Eterm *tracer_pid)
*/
void
erts_trace_exception(Process* p, BeamInstr mfa[3], Eterm class, Eterm value,
- Eterm *tracer_pid)
+ ErtsTracer *tracer)
{
Eterm* hp;
- Eterm mfa_tuple;
- Eterm cv;
- Eterm mess;
+ Eterm mfa_tuple, cv;
Uint meta_flags, *tracee_flags;
- int ts_type;
-#ifdef ERTS_SMP
- Eterm tracee;
-#endif
-
- ASSERT(tracer_pid);
- if (*tracer_pid == am_true) {
+
+ ASSERT(tracer);
+ if (ERTS_TRACER_COMPARE(*tracer, erts_tracer_true)) {
/* Breakpoint trace enabled without specifying tracer =>
* use process tracer and flags
*/
- tracer_pid = &ERTS_TRACER_PROC(p);
+ tracer = &ERTS_TRACER(p);
}
- if (is_nil(*tracer_pid)) {
+ if (ERTS_TRACER_IS_NIL(*tracer)) {
/* Trace disabled */
return;
}
- ASSERT(is_internal_pid(*tracer_pid) || is_internal_port(*tracer_pid));
- if (*tracer_pid == p->common.id) {
- /* Do not generate trace messages to oneself */
- return;
- }
- if (tracer_pid == &ERTS_TRACER_PROC(p)) {
+ ASSERT(IS_TRACER_VALID(*tracer));
+ if (tracer == &ERTS_TRACER(p)) {
/* Tracer specified in process structure =>
* non-breakpoint trace =>
* use process flags
*/
tracee_flags = &ERTS_TRACE_FLAGS(p);
-#ifdef ERTS_SMP
- tracee = p->common.id;
-#endif
- if (! (*tracee_flags & F_TRACE_CALLS)) {
- return;
- }
+ if (! (*tracee_flags & F_TRACE_CALLS)) {
+ return;
+ }
} else {
/* Tracer not specified in process structure =>
* tracer specified in breakpoint =>
* meta trace =>
* use fixed flag set instead of process flags
- */
+ */
meta_flags = F_TRACE_CALLS | F_NOW_TS;
tracee_flags = &meta_flags;
-#ifdef ERTS_SMP
- tracee = NIL;
-#endif
}
-
- ts_type = ERTS_TFLGS2TSTYPE(*tracee_flags);
-
- if (is_internal_port(*tracer_pid)) {
-#define LOCAL_HEAP_SIZE (4+3+6+ERTS_TRACE_PATCH_TS_MAX_SIZE)
- DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE);
- UseTmpHeapNoproc(LOCAL_HEAP_SIZE);
- hp = local_heap;
- mfa_tuple = TUPLE3(hp, (Eterm) mfa[0], (Eterm) mfa[1], make_small((Eterm)mfa[2]));
- hp += 4;
- cv = TUPLE2(hp, class, value);
- hp += 3;
- mess = TUPLE5(hp, am_trace, p->common.id, am_exception_from, mfa_tuple, cv);
- hp += 6;
- ASSERT((hp - local_heap) <= LOCAL_HEAP_SIZE);
- erts_smp_mtx_lock(&smq_mtx);
- PATCH_TS(ts_type, mess, hp, NULL, NULL);
- send_to_port(p, mess, tracer_pid, tracee_flags);
- UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
-#undef LOCAL_HEAP_SIZE
- erts_smp_mtx_unlock(&smq_mtx);
- } else {
- ErlHeapFragment *bp;
- ErlOffHeap *off_heap;
- ERTS_TRACER_REF_TYPE tracer_ref;
- unsigned size;
- unsigned value_size;
-
- ASSERT(is_internal_pid(*tracer_pid));
-
- ERTS_GET_TRACER_REF(tracer_ref, *tracer_pid, *tracee_flags);
-
- value_size = size_object(value);
- size = 6 + 4 + 3 + value_size + patch_ts_size(ts_type);
-
- hp = ERTS_ALLOC_SYSMSG_HEAP(size, &bp, &off_heap, tracer_ref);
-
- /*
- * Build the trace tuple and put it into receive queue of the tracer process.
- */
-
- mfa_tuple = TUPLE3(hp, (Eterm) mfa[0], (Eterm) mfa[1], make_small((Eterm) mfa[2]));
- hp += 4;
- value = copy_struct(value, value_size, &hp, off_heap);
- cv = TUPLE2(hp, class, value);
- hp += 3;
- mess = TUPLE5(hp, am_trace, p->common.id,
- am_exception_from, mfa_tuple, cv);
- hp += 6;
-
- erts_smp_mtx_lock(&smq_mtx);
-
- PATCH_TS(ts_type, mess, hp, bp, tracer_ref);
-
- ERTS_ENQ_TRACE_MSG(tracee, tracer_ref, mess, bp);
- erts_smp_mtx_unlock(&smq_mtx);
- }
+ hp = HAlloc(p, 7);;
+ mfa_tuple = TUPLE3(hp, (Eterm) mfa[0], (Eterm) mfa[1], make_small((Eterm)mfa[2]));
+ hp += 4;
+ cv = TUPLE2(hp, class, value);
+ hp += 3;
+ send_to_tracer_nif_raw(p, NULL, *tracer, *tracee_flags, p->common.id,
+ NULL, TRACE_FUN_T_CALL, am_exception_from, mfa_tuple, cv, am_true);
}
/*
@@ -1753,7 +1166,7 @@ erts_trace_exception(Process* p, BeamInstr mfa[3], Eterm class, Eterm value,
*/
Uint32
erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec,
- Eterm* args, int local, Eterm *tracer_pid)
+ Eterm* args, int local, ErtsTracer *tracer)
{
Eterm* hp;
Eterm mfa_tuple;
@@ -1761,55 +1174,67 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec,
int i;
Uint32 return_flags;
Eterm pam_result = am_true;
- Eterm mess;
Uint meta_flags, *tracee_flags;
- int ts_type;
-#ifdef ERTS_SMP
- Eterm tracee;
-#endif
+ ErtsTracerNif *tnif = NULL;
Eterm transformed_args[MAX_ARG];
- DeclareTypedTmpHeap(ErlSubBin,sub_bin_heap,p);
+ ErtsTracer pre_ms_tracer = erts_tracer_nil;
- ASSERT(tracer_pid);
- if (*tracer_pid == am_true) {
- /* Breakpoint trace enabled without specifying tracer =>
+ ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(p) & ERTS_PROC_LOCK_MAIN);
+
+ ASSERT(tracer);
+ if (ERTS_TRACER_COMPARE(*tracer, erts_tracer_true)) {
+ /* Breakpoint trace enabled without specifying tracer =>
* use process tracer and flags
*/
- tracer_pid = &ERTS_TRACER_PROC(p);
- }
- if (is_nil(*tracer_pid)) {
- /* Trace disabled */
- return 0;
+ tracer = &ERTS_TRACER(p);
}
- ASSERT(is_internal_pid(*tracer_pid) || is_internal_port(*tracer_pid));
- if (*tracer_pid == p->common.id) {
- /* Do not generate trace messages to oneself */
+ if (ERTS_TRACER_IS_NIL(*tracer)) {
+ /* Trace disabled */
return 0;
}
- if (tracer_pid == &ERTS_TRACER_PROC(p)) {
+ ASSERT(IS_TRACER_VALID(*tracer));
+ if (tracer == &ERTS_TRACER(p)) {
/* Tracer specified in process structure =>
* non-breakpoint trace =>
* use process flags
*/
tracee_flags = &ERTS_TRACE_FLAGS(p);
-#ifdef ERTS_SMP
- tracee = p->common.id;
-#endif
+ /* Is is not ideal at all to call this check twice,
+ it should be optimized so that only one call is made. */
+ if (!is_tracer_enabled(p, ERTS_PROC_LOCK_MAIN, &p->common, &tnif,
+ TRACE_FUN_ENABLED, am_trace_status)
+ || !is_tracer_enabled(p, ERTS_PROC_LOCK_MAIN, &p->common, &tnif,
+ TRACE_FUN_E_CALL, am_call)) {
+ return 0;
+ }
} else {
/* Tracer not specified in process structure =>
* tracer specified in breakpoint =>
* meta trace =>
* use fixed flag set instead of process flags
- */
- if (ERTS_TRACE_FLAGS(p) & F_SENSITIVE) {
- /* No trace messages for sensitive processes. */
- return 0;
- }
+ */
+ if (ERTS_TRACE_FLAGS(p) & F_SENSITIVE) {
+ /* No trace messages for sensitive processes. */
+ return 0;
+ }
meta_flags = F_TRACE_CALLS | F_NOW_TS;
tracee_flags = &meta_flags;
-#ifdef ERTS_SMP
- tracee = NIL;
-#endif
+ switch (call_enabled_tracer(*tracer,
+ &tnif, TRACE_FUN_ENABLED,
+ am_trace_status, p->common.id)) {
+ default:
+ case am_remove: *tracer = erts_tracer_nil;
+ case am_discard: return 0;
+ case am_trace:
+ switch (call_enabled_tracer(*tracer,
+ &tnif, TRACE_FUN_T_CALL,
+ am_call, p->common.id)) {
+ default:
+ case am_discard: return 0;
+ case am_trace: break;
+ }
+ break;
+ }
}
/*
@@ -1820,18 +1245,13 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec,
* temporarily convert any match contexts to sub binaries.
*/
arity = (Eterm) mfa[2];
- UseTmpHeap(ERL_SUB_BIN_SIZE,p);
-#ifdef DEBUG
- sub_bin_heap->thing_word = 0;
-#endif
for (i = 0; i < arity; i++) {
Eterm arg = args[i];
if (is_boxed(arg) && header_is_bin_matchstate(*boxed_val(arg))) {
ErlBinMatchState* ms = (ErlBinMatchState *) boxed_val(arg);
ErlBinMatchBuffer* mb = &ms->mb;
Uint bit_size;
-
- ASSERT(sub_bin_heap->thing_word == 0); /* At most one of match context */
+ ErlSubBin *sub_bin_heap = (ErlSubBin *)HAlloc(p, ERL_SUB_BIN_SIZE);
bit_size = mb->size - mb->offset;
sub_bin_heap->thing_word = HEADER_SUB_BIN;
@@ -1848,298 +1268,94 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec,
}
args = transformed_args;
- ts_type = ERTS_TFLGS2TSTYPE(*tracee_flags);
-
- if (is_internal_port(*tracer_pid)) {
-#if HEAP_ON_C_STACK
- Eterm local_heap[60+ERTS_TRACE_PATCH_TS_MAX_SIZE+MAX_ARG];
-#else
- Eterm *local_heap = erts_alloc(ERTS_ALC_T_TEMP_TERM,
- sizeof(Eterm)*(60+ERTS_TRACE_PATCH_TS_MAX_SIZE+MAX_ARG));
-#endif
- hp = local_heap;
-
- if (!erts_is_valid_tracer_port(*tracer_pid)) {
-#ifdef ERTS_SMP
- ASSERT(is_nil(tracee) || tracer_pid == &ERTS_TRACER_PROC(p));
- if (is_not_nil(tracee))
- erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR);
-#endif
- *tracee_flags &= ~TRACEE_FLAGS;
- *tracer_pid = NIL;
-#ifdef ERTS_SMP
- if (is_not_nil(tracee))
- erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR);
-#endif
-#if !HEAP_ON_C_STACK
- erts_free(ERTS_ALC_T_TEMP_TERM,local_heap);
-#endif
- UnUseTmpHeap(ERL_SUB_BIN_SIZE,p);
- return 0;
- }
-
- /*
- * If there is a PAM program, run it. Return if it fails.
- *
- * Some precedence rules:
- *
- * - No proc flags, e.g 'silent' or 'return_to'
- * has any effect on meta trace.
- * - The 'silent' process trace flag silences all call
- * related messages, e.g 'call', 'return_to' and 'return_from'.
- * - The {message,_} PAM function does not affect {return_trace}.
- * - The {message,false} PAM function shall give the same
- * 'call' trace message as no PAM match.
- * - The {message,true} PAM function shall give the same
- * 'call' trace message as a nonexistent PAM program.
- */
-
- /* BEGIN this code should be the same for port and pid trace */
- return_flags = 0;
- if (match_spec) {
- pam_result = erts_match_set_run(p, match_spec, args, arity,
- ERTS_PAM_TMP_RESULT, &return_flags);
- if (is_non_value(pam_result)) {
- erts_match_set_release_result(p);
-#if !HEAP_ON_C_STACK
- erts_free(ERTS_ALC_T_TEMP_TERM,local_heap);
-#endif
- UnUseTmpHeap(ERL_SUB_BIN_SIZE,p);
- return 0;
- }
- }
- if (tracee_flags == &meta_flags) {
- /* Meta trace */
- if (pam_result == am_false) {
- erts_match_set_release_result(p);
-#if !HEAP_ON_C_STACK
- erts_free(ERTS_ALC_T_TEMP_TERM,local_heap);
-#endif
- UnUseTmpHeap(ERL_SUB_BIN_SIZE,p);
- return return_flags;
- }
- } else {
- /* Non-meta trace */
- if (*tracee_flags & F_TRACE_SILENT) {
- erts_match_set_release_result(p);
-#if !HEAP_ON_C_STACK
- erts_free(ERTS_ALC_T_TEMP_TERM,local_heap);
-#endif
- UnUseTmpHeap(ERL_SUB_BIN_SIZE,p);
- return 0;
- }
- if (pam_result == am_false) {
- erts_match_set_release_result(p);
-#if !HEAP_ON_C_STACK
- erts_free(ERTS_ALC_T_TEMP_TERM,local_heap);
-#endif
- UnUseTmpHeap(ERL_SUB_BIN_SIZE,p);
- return return_flags;
- }
- if (local && (*tracee_flags & F_TRACE_RETURN_TO)) {
- return_flags |= MATCH_SET_RETURN_TO_TRACE;
- }
- }
- /* END this code should be the same for port and pid trace */
-
- /*
- * Build the the {M,F,A} tuple in the local heap.
- * (A is arguments or arity.)
- */
-
- if (*tracee_flags & F_TRACE_ARITY_ONLY) {
- mfa_tuple = make_small(arity);
- } else {
- mfa_tuple = NIL;
- for (i = arity-1; i >= 0; i--) {
- mfa_tuple = CONS(hp, args[i], mfa_tuple);
- hp += 2;
- }
- }
- mfa_tuple = TUPLE3(hp, (Eterm) mfa[0], (Eterm) mfa[1], mfa_tuple);
- hp += 4;
-
- /*
- * Build the trace tuple and send it to the port.
- */
-
- mess = TUPLE4(hp, am_trace, p->common.id, am_call, mfa_tuple);
- hp += 5;
- if (pam_result != am_true) {
- hp[-5] = make_arityval(5);
- *hp++ = pam_result;
- }
- erts_smp_mtx_lock(&smq_mtx);
- PATCH_TS(ts_type, mess, hp, NULL, NULL);
- send_to_port(p, mess, tracer_pid, tracee_flags);
- erts_smp_mtx_unlock(&smq_mtx);
- erts_match_set_release_result(p);
-#if !HEAP_ON_C_STACK
- erts_free(ERTS_ALC_T_TEMP_TERM,local_heap);
-#endif
- UnUseTmpHeap(ERL_SUB_BIN_SIZE,p);
- return *tracer_pid == NIL ? 0 : return_flags;
+ /*
+ * If there is a PAM program, run it. Return if it fails.
+ *
+ * Some precedence rules:
+ *
+ * - No proc flags, e.g 'silent' or 'return_to'
+ * has any effect on meta trace.
+ * - The 'silent' process trace flag silences all call
+ * related messages, e.g 'call', 'return_to' and 'return_from'.
+ * - The {message,_} PAM function does not affect {return_trace}.
+ * - The {message,false} PAM function shall give the same
+ * 'call' trace message as no PAM match.
+ * - The {message,true} PAM function shall give the same
+ * 'call' trace message as a nonexistent PAM program.
+ */
+ return_flags = 0;
+ if (match_spec) {
+ /* we have to make a copy of the tracer here as the match spec
+ may remove it, and we still want to generate a trace message */
+ erts_tracer_update(&pre_ms_tracer, *tracer);
+ tracer = &pre_ms_tracer;
+ pam_result = erts_match_set_run_trace(p, p,
+ match_spec, args, arity,
+ ERTS_PAM_TMP_RESULT, &return_flags);
+ }
+
+ if (tracee_flags == &meta_flags) {
+ /* Meta trace */
+ if (pam_result == am_false) {
+ UnUseTmpHeap(ERL_SUB_BIN_SIZE,p);
+ ERTS_TRACER_CLEAR(&pre_ms_tracer);
+ return return_flags;
+ }
} else {
- ErlHeapFragment *bp;
- ErlOffHeap *off_heap;
- Process *tracer;
- ERTS_TRACER_REF_TYPE tracer_ref;
-#ifdef ERTS_SMP
- Eterm tpid;
-#endif
- unsigned size;
- unsigned sizes[MAX_ARG];
- unsigned pam_result_size = 0;
- int invalid_tracer;
-
- ASSERT(is_internal_pid(*tracer_pid));
-
- tracer = erts_pid2proc(p, ERTS_PROC_LOCK_MAIN,
- *tracer_pid, ERTS_PROC_LOCK_STATUS);
- if (!tracer)
- invalid_tracer = 1;
- else {
- invalid_tracer = !(ERTS_TRACE_FLAGS(tracer) & F_TRACER);
- erts_smp_proc_unlock(tracer, ERTS_PROC_LOCK_STATUS);
- }
-
- if (invalid_tracer) {
-#ifdef ERTS_SMP
- ASSERT(is_nil(tracee)
- || tracer_pid == &ERTS_TRACER_PROC(p));
- if (is_not_nil(tracee))
- erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR);
-#endif
- *tracee_flags &= ~TRACEE_FLAGS;
- *tracer_pid = NIL;
-#ifdef ERTS_SMP
- if (is_not_nil(tracee))
- erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR);
-#endif
- UnUseTmpHeap(ERL_SUB_BIN_SIZE,p);
- return 0;
- }
+ /* Non-meta trace */
+ if (*tracee_flags & F_TRACE_SILENT) {
+ erts_match_set_release_result_trace(p, pam_result);
+ UnUseTmpHeap(ERL_SUB_BIN_SIZE,p);
+ ERTS_TRACER_CLEAR(&pre_ms_tracer);
+ return 0;
+ }
+ if (pam_result == am_false) {
+ UnUseTmpHeap(ERL_SUB_BIN_SIZE,p);
+ ERTS_TRACER_CLEAR(&pre_ms_tracer);
+ return return_flags;
+ }
+ if (local && (*tracee_flags & F_TRACE_RETURN_TO)) {
+ return_flags |= MATCH_SET_RETURN_TO_TRACE;
+ }
+ }
+
+ ASSERT(!ERTS_TRACER_IS_NIL(*tracer));
-#ifdef ERTS_SMP
- tpid = *tracer_pid; /* Need to save tracer pid,
- since *tracer_pid might
- be reset by erts_match_set_run() */
- tracer_ref = tpid;
-#else
- tracer_ref = tracer;
-#endif
-
- /*
- * If there is a PAM program, run it. Return if it fails.
- *
- * See the rules above in the port trace code.
- */
-
- /* BEGIN this code should be the same for port and pid trace */
- return_flags = 0;
- if (match_spec) {
- pam_result = erts_match_set_run(p, match_spec, args, arity,
- ERTS_PAM_TMP_RESULT, &return_flags);
- if (is_non_value(pam_result)) {
- erts_match_set_release_result(p);
- UnUseTmpHeap(ERL_SUB_BIN_SIZE,p);
- return 0;
- }
- }
- if (tracee_flags == &meta_flags) {
- /* Meta trace */
- if (pam_result == am_false) {
- erts_match_set_release_result(p);
- UnUseTmpHeap(ERL_SUB_BIN_SIZE,p);
- return return_flags;
- }
- } else {
- /* Non-meta trace */
- if (*tracee_flags & F_TRACE_SILENT) {
- erts_match_set_release_result(p);
- UnUseTmpHeap(ERL_SUB_BIN_SIZE,p);
- return 0;
- }
- if (pam_result == am_false) {
- erts_match_set_release_result(p);
- UnUseTmpHeap(ERL_SUB_BIN_SIZE,p);
- return return_flags;
- }
- if (local && (*tracee_flags & F_TRACE_RETURN_TO)) {
- return_flags |= MATCH_SET_RETURN_TO_TRACE;
- }
- }
- /* END this code should be the same for port and pid trace */
-
- /*
- * Calculate number of words needed on heap.
- */
-
- size = 4 + 5; /* Trace tuple + MFA tuple. */
- if (! (*tracee_flags & F_TRACE_ARITY_ONLY)) {
- size += 2*arity;
- for (i = arity-1; i >= 0; i--) {
- sizes[i] = size_object(args[i]);
- size += sizes[i];
- }
- }
- size += patch_ts_size(ts_type);
- if (pam_result != am_true) {
- pam_result_size = size_object(pam_result);
- size += 1 + pam_result_size;
- /* One element in trace tuple + term size. */
- }
-
- hp = ERTS_ALLOC_SYSMSG_HEAP(size, &bp, &off_heap, tracer_ref);
-
- /*
- * Build the the {M,F,A} tuple in the message buffer.
- * (A is arguments or arity.)
- */
-
- if (*tracee_flags & F_TRACE_ARITY_ONLY) {
- mfa_tuple = make_small(arity);
- } else {
- mfa_tuple = NIL;
- for (i = arity-1; i >= 0; i--) {
- Eterm term = copy_struct(args[i], sizes[i], &hp, off_heap);
- mfa_tuple = CONS(hp, term, mfa_tuple);
- hp += 2;
- }
- }
- mfa_tuple = TUPLE3(hp, (Eterm) mfa[0], (Eterm) mfa[1], mfa_tuple);
- hp += 4;
-
- /*
- * Copy the PAM result (if any) onto the heap.
- */
-
- if (pam_result != am_true) {
- pam_result = copy_struct(pam_result, pam_result_size, &hp, off_heap);
- }
+ /*
+ * Build the the {M,F,A} tuple in the local heap.
+ * (A is arguments or arity.)
+ */
- erts_match_set_release_result(p);
- /*
- * Build the trace tuple and enqueue it.
- */
-
- mess = TUPLE4(hp, am_trace, p->common.id, am_call, mfa_tuple);
- hp += 5;
- if (pam_result != am_true) {
- hp[-5] = make_arityval(5);
- *hp++ = pam_result;
- }
+ if (*tracee_flags & F_TRACE_ARITY_ONLY) {
+ hp = HAlloc(p, 4);
+ mfa_tuple = make_small(arity);
+ } else {
+ hp = HAlloc(p, 4 + arity * 2);
+ mfa_tuple = NIL;
+ for (i = arity-1; i >= 0; i--) {
+ mfa_tuple = CONS(hp, args[i], mfa_tuple);
+ hp += 2;
+ }
+ }
+ mfa_tuple = TUPLE3(hp, (Eterm) mfa[0], (Eterm) mfa[1], mfa_tuple);
+ hp += 4;
- erts_smp_mtx_lock(&smq_mtx);
+ /*
+ * Build the trace tuple and send it to the port.
+ */
+ send_to_tracer_nif_raw(p, NULL, *tracer, *tracee_flags, p->common.id,
+ tnif, TRACE_FUN_T_CALL, am_call, mfa_tuple,
+ THE_NON_VALUE, pam_result);
- PATCH_TS(ts_type, mess, hp, bp, tracer_ref);
- ERTS_ENQ_TRACE_MSG(tracee, tracer_ref, mess, bp);
- erts_smp_mtx_unlock(&smq_mtx);
- UnUseTmpHeap(ERL_SUB_BIN_SIZE,p);
- return return_flags;
+ if (match_spec) {
+ erts_match_set_release_result_trace(p, pam_result);
+ if (tracer == &pre_ms_tracer)
+ ERTS_TRACER_CLEAR(&pre_ms_tracer);
}
+
+ return return_flags;
}
/* Sends trace message:
@@ -2151,69 +1367,14 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec,
* 't_p' is the traced process.
*/
void
-trace_proc(Process *c_p, Process *t_p, Eterm what, Eterm data)
+trace_proc(Process *c_p, ErtsProcLocks c_p_locks,
+ Process *t_p, Eterm what, Eterm data)
{
- Eterm mess;
- Eterm* hp;
- int need;
-
- ERTS_SMP_LC_ASSERT((erts_proc_lc_my_proc_locks(t_p) != 0)
- || erts_thr_progress_is_blocking());
- if (is_internal_port(ERTS_TRACER_PROC(t_p))) {
-#define LOCAL_HEAP_SIZE (5+5)
- DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE);
- UseTmpHeapNoproc(LOCAL_HEAP_SIZE);
-
-
- hp = local_heap;
- mess = TUPLE4(hp, am_trace, t_p->common.id, what, data);
- hp += 5;
- erts_smp_mtx_lock(&smq_mtx);
- PATCH_TS(TFLGS_TS_TYPE(t_p), mess, hp, NULL, NULL);
- send_to_port(
-#ifndef ERTS_SMP
- /* No fake schedule out and in again after an exit */
- what == am_exit ? NULL : c_p,
-#else
- /* Fake schedule out and in are never sent when smp enabled */
- c_p,
-#endif
- mess,
- &ERTS_TRACER_PROC(t_p),
- &ERTS_TRACE_FLAGS(t_p));
- UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
-#undef LOCAL_HEAP_SIZE
- erts_smp_mtx_unlock(&smq_mtx);
- } else {
- Eterm tmp;
- ErlHeapFragment *bp;
- ErlOffHeap *off_heap;
- ERTS_TRACER_REF_TYPE tracer_ref;
- size_t sz_data;
-
- ASSERT(is_internal_pid(ERTS_TRACER_PROC(t_p)));
-
- ERTS_GET_TRACER_REF(tracer_ref,
- ERTS_TRACER_PROC(t_p),
- ERTS_TRACE_FLAGS(t_p));
-
- sz_data = size_object(data);
-
- need = sz_data + 5 + PATCH_TS_SIZE(t_p);
-
- hp = ERTS_ALLOC_SYSMSG_HEAP(need, &bp, &off_heap, tracer_ref);
-
- tmp = copy_struct(data, sz_data, &hp, off_heap);
- mess = TUPLE4(hp, am_trace, t_p->common.id, what, tmp);
- hp += 5;
-
- erts_smp_mtx_lock(&smq_mtx);
-
- PATCH_TS(TFLGS_TS_TYPE(t_p), mess, hp, bp, tracer_ref);
-
- ERTS_ENQ_TRACE_MSG(t_p->common.id, tracer_ref, mess, bp);
- erts_smp_mtx_unlock(&smq_mtx);
- }
+ ErtsTracerNif *tnif = NULL;
+ if (is_tracer_enabled(NULL, 0, &t_p->common, &tnif,
+ TRACE_FUN_E_PROCS, what))
+ send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, TRACE_FUN_T_PROCS,
+ what, data, THE_NON_VALUE, am_true);
}
@@ -2225,77 +1386,37 @@ trace_proc(Process *c_p, Process *t_p, Eterm what, Eterm data)
* and 'args' may be a deep term.
*/
void
-trace_proc_spawn(Process *p, Eterm pid,
+trace_proc_spawn(Process *p, Eterm what, Eterm pid,
Eterm mod, Eterm func, Eterm args)
{
- Eterm mfa;
- Eterm mess;
- Eterm* hp;
+ ErtsTracerNif *tnif = NULL;
+ if (is_tracer_enabled(NULL, 0,
+ &p->common, &tnif, TRACE_FUN_E_PROCS, what)) {
+ Eterm mfa;
+ Eterm* hp;
- if (is_internal_port(ERTS_TRACER_PROC(p))) {
-#define LOCAL_HEAP_SIZE (4+6+5)
- DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE);
- UseTmpHeapNoproc(LOCAL_HEAP_SIZE);
-
- hp = local_heap;
- mfa = TUPLE3(hp, mod, func, args);
- hp += 4;
- mess = TUPLE5(hp, am_trace, p->common.id, am_spawn, pid, mfa);
- hp += 6;
- erts_smp_mtx_lock(&smq_mtx);
- PATCH_TS(TFLGS_TS_TYPE(p), mess, hp, NULL, NULL);
- send_to_port(p, mess, &ERTS_TRACER_PROC(p), &ERTS_TRACE_FLAGS(p));
- UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
-#undef LOCAL_HEAP_SIZE
- erts_smp_mtx_unlock(&smq_mtx);
- } else {
- Eterm tmp;
- ErlHeapFragment *bp;
- ErlOffHeap *off_heap;
- ERTS_TRACER_REF_TYPE tracer_ref;
- size_t sz_args, sz_pid;
- Uint need;
-
- ASSERT(is_internal_pid(ERTS_TRACER_PROC(p)));
-
- ERTS_GET_TRACER_REF(tracer_ref,
- ERTS_TRACER_PROC(p),
- ERTS_TRACE_FLAGS(p));
-
- sz_args = size_object(args);
- sz_pid = size_object(pid);
- need = sz_args + 4 + 6 + PATCH_TS_SIZE(p);
-
- hp = ERTS_ALLOC_SYSMSG_HEAP(need, &bp, &off_heap, tracer_ref);
-
- tmp = copy_struct(args, sz_args, &hp, off_heap);
- mfa = TUPLE3(hp, mod, func, tmp);
- hp += 4;
- tmp = copy_struct(pid, sz_pid, &hp, off_heap);
- mess = TUPLE5(hp, am_trace, p->common.id, am_spawn, tmp, mfa);
- hp += 6;
-
- erts_smp_mtx_lock(&smq_mtx);
-
- PATCH_TS(TFLGS_TS_TYPE(p), mess, hp, bp, tracer_ref);
-
- ERTS_ENQ_TRACE_MSG(p->common.id, tracer_ref, mess, bp);
- erts_smp_mtx_unlock(&smq_mtx);
+ hp = HAlloc(p, 4);
+ mfa = TUPLE3(hp, mod, func, args);
+ hp += 4;
+ send_to_tracer_nif(NULL, &p->common, p->common.id, tnif, TRACE_FUN_T_PROCS,
+ what, pid, mfa, am_true);
}
}
void save_calls(Process *p, Export *e)
{
- struct saved_calls *scb = ERTS_PROC_GET_SAVED_CALLS_BUF(p);
- if (scb) {
- Export **ct = &scb->ct[0];
- int len = scb->len;
-
- ct[scb->cur] = e;
- if (++scb->cur >= len)
- scb->cur = 0;
- if (scb->n < len)
- scb->n++;
+ if ((ERTS_TRACE_FLAGS(p) & F_SENSITIVE) == 0) {
+ struct saved_calls *scb = ERTS_PROC_GET_SAVED_CALLS_BUF(p);
+ if (scb) {
+ Export **ct = &scb->ct[0];
+ int len = scb->len;
+
+ ct[scb->cur] = e;
+ if (++scb->cur >= len)
+ scb->cur = 0;
+ if (scb->n < len)
+ scb->n++;
+ }
}
}
@@ -2312,113 +1433,32 @@ void save_calls(Process *p, Export *e)
* are all small (atomic) integers.
*/
void
-trace_gc(Process *p, Eterm what)
+trace_gc(Process *p, Eterm what, Uint size, Eterm msg)
{
- ERTS_DECL_AM(bin_vheap_size);
- ERTS_DECL_AM(bin_vheap_block_size);
- ERTS_DECL_AM(bin_old_vheap_size);
- ERTS_DECL_AM(bin_old_vheap_block_size);
-
- ErlHeapFragment *bp = NULL;
- ErlOffHeap *off_heap;
- ERTS_TRACER_REF_TYPE tracer_ref = ERTS_NULL_TRACER_REF; /* Initialized
- to eliminate
- compiler
- warning */
+ ErtsTracerNif *tnif = NULL;
+ Eterm* o_hp = NULL;
Eterm* hp;
- Eterm msg = NIL;
- Uint size;
-
- Eterm tags[] = {
- am_old_heap_block_size,
- am_heap_block_size,
- am_mbuf_size,
- am_recent_size,
- am_stack_size,
- am_old_heap_size,
- am_heap_size,
- AM_bin_vheap_size,
- AM_bin_vheap_block_size,
- AM_bin_old_vheap_size,
- AM_bin_old_vheap_block_size
- };
-
- UWord values[] = {
- OLD_HEAP(p) ? OLD_HEND(p) - OLD_HEAP(p) : 0,
- HEAP_SIZE(p),
- MBUF_SIZE(p),
- HIGH_WATER(p) - HEAP_START(p),
- STACK_START(p) - p->stop,
- OLD_HEAP(p) ? OLD_HTOP(p) - OLD_HEAP(p) : 0,
- HEAP_TOP(p) - HEAP_START(p),
- MSO(p).overhead,
- BIN_VHEAP_SZ(p),
- BIN_OLD_VHEAP(p),
- BIN_OLD_VHEAP_SZ(p)
- };
-#define LOCAL_HEAP_SIZE \
- (sizeof(values)/sizeof(*values)) * \
- (2/*cons*/ + 3/*2-tuple*/ + BIG_UINT_HEAP_SIZE) + \
- 5/*4-tuple */ + ERTS_TRACE_PATCH_TS_MAX_SIZE
- DeclareTmpHeap(local_heap,LOCAL_HEAP_SIZE,p);
-
- ERTS_CT_ASSERT(sizeof(values)/sizeof(*values) == sizeof(tags)/sizeof(Eterm));
-
- UseTmpHeap(LOCAL_HEAP_SIZE,p);
-
- if (is_internal_port(ERTS_TRACER_PROC(p))) {
- hp = local_heap;
-#ifdef DEBUG
- size = 0;
- (void) erts_bld_atom_uword_2tup_list(NULL,
- &size,
- sizeof(values)/sizeof(*values),
- tags,
- values);
- size += 5/*4-tuple*/ + PATCH_TS_SIZE(p);
-#endif
- } else {
- ASSERT(is_internal_pid(ERTS_TRACER_PROC(p)));
-
- ERTS_GET_TRACER_REF(tracer_ref,
- ERTS_TRACER_PROC(p),
- ERTS_TRACE_FLAGS(p));
-
- size = 0;
- (void) erts_bld_atom_uword_2tup_list(NULL,
- &size,
- sizeof(values)/sizeof(*values),
- tags,
- values);
- size += 5/*4-tuple*/ + PATCH_TS_SIZE(p);
+ Uint sz = 0;
+ Eterm tup;
- hp = ERTS_ALLOC_SYSMSG_HEAP(size, &bp, &off_heap, tracer_ref);
- }
+ if (is_tracer_enabled(p, ERTS_PROC_LOCK_MAIN, &p->common, &tnif,
+ TRACE_FUN_E_GC, what)) {
- ASSERT(size <= LOCAL_HEAP_SIZE);
+ if (is_non_value(msg)) {
- msg = erts_bld_atom_uword_2tup_list(&hp,
- NULL,
- sizeof(values)/sizeof(*values),
- tags,
- values);
+ (void) erts_process_gc_info(p, &sz, NULL, 0, 0);
+ o_hp = hp = erts_alloc(ERTS_ALC_T_TMP, (sz + 3 + 2) * sizeof(Eterm));
- msg = TUPLE4(hp, am_trace, p->common.id, what, msg);
- hp += 5;
+ msg = erts_process_gc_info(p, NULL, &hp, 0, 0);
+ tup = TUPLE2(hp, am_wordsize, make_small(size)); hp += 3;
+ msg = CONS(hp, tup, msg); hp += 2;
+ }
- erts_smp_mtx_lock(&smq_mtx);
-
- if (is_internal_port(ERTS_TRACER_PROC(p))) {
- PATCH_TS(TFLGS_TS_TYPE(p), msg, hp, NULL, NULL);
- send_to_port(p, msg, &ERTS_TRACER_PROC(p), &ERTS_TRACE_FLAGS(p));
- }
- else {
- PATCH_TS(TFLGS_TS_TYPE(p), msg, hp, bp, tracer_ref);
- ERTS_ENQ_TRACE_MSG(p->common.id, tracer_ref, msg, bp);
+ send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_GC,
+ what, msg, THE_NON_VALUE, am_true);
+ if (o_hp)
+ erts_free(ERTS_ALC_T_TMP, o_hp);
}
- erts_smp_mtx_unlock(&smq_mtx);
- UnUseTmpHeap(LOCAL_HEAP_SIZE,p);
-#undef LOCAL_HEAP_SIZE
}
void
@@ -2477,7 +1517,11 @@ monitor_long_schedule_proc(Process *p, BeamInstr *in_fp, BeamInstr *out_fp, Uint
#ifdef ERTS_SMP
enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, p->common.id, NIL, msg, bp);
#else
- erts_queue_message(monitor_p, NULL, bp, msg, NIL);
+ {
+ ErtsMessage *mp = erts_alloc_message(0, NULL);
+ mp->data.heap_frag = bp;
+ erts_queue_message(monitor_p, 0, mp, msg, am_system);
+ }
#endif
}
void
@@ -2538,7 +1582,11 @@ monitor_long_schedule_port(Port *pp, ErtsPortTaskType type, Uint time)
#ifdef ERTS_SMP
enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, pp->common.id, NIL, msg, bp);
#else
- erts_queue_message(monitor_p, NULL, bp, msg, NIL);
+ {
+ ErtsMessage *mp = erts_alloc_message(0, NULL);
+ mp->data.heap_frag = bp;
+ erts_queue_message(monitor_p, 0, mp, msg, am_system);
+ }
#endif
}
@@ -2609,7 +1657,11 @@ monitor_long_gc(Process *p, Uint time) {
#ifdef ERTS_SMP
enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, p->common.id, NIL, msg, bp);
#else
- erts_queue_message(monitor_p, NULL, bp, msg, NIL);
+ {
+ ErtsMessage *mp = erts_alloc_message(0, NULL);
+ mp->data.heap_frag = bp;
+ erts_queue_message(monitor_p, 0, mp, msg, am_system);
+ }
#endif
}
@@ -2680,7 +1732,11 @@ monitor_large_heap(Process *p) {
#ifdef ERTS_SMP
enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, p->common.id, NIL, msg, bp);
#else
- erts_queue_message(monitor_p, NULL, bp, msg, NIL);
+ {
+ ErtsMessage *mp = erts_alloc_message(0, NULL);
+ mp->data.heap_frag = bp;
+ erts_queue_message(monitor_p, 0, mp, msg, am_system);
+ }
#endif
}
@@ -2708,7 +1764,11 @@ monitor_generic(Process *p, Eterm type, Eterm spec) {
#ifdef ERTS_SMP
enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, p->common.id, NIL, msg, bp);
#else
- erts_queue_message(monitor_p, NULL, bp, msg, NIL);
+ {
+ ErtsMessage *mp = erts_alloc_message(0, NULL);
+ mp->data.heap_frag = bp;
+ erts_queue_message(monitor_p, 0, mp, msg, am_system);
+ }
#endif
}
@@ -2769,106 +1829,15 @@ profile_scheduler(Eterm scheduler_id, Eterm state) {
}
-void
-profile_scheduler_q(Eterm scheduler_id, Eterm state, Eterm no_schedulers, Uint Ms, Uint s, Uint us) {
- Eterm *hp, msg, timestamp;
-
-#ifndef ERTS_SMP
-#define LOCAL_HEAP_SIZE (4 + 7)
- DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE);
- UseTmpHeapNoproc(LOCAL_HEAP_SIZE);
-
- hp = local_heap;
-#else
- ErlHeapFragment *bp;
- Uint hsz;
-
- hsz = 4 + 7;
-
- bp = new_message_buffer(hsz);
- hp = bp->mem;
-#endif
-
- erts_smp_mtx_lock(&smq_mtx);
-
- timestamp = TUPLE3(hp, make_small(Ms), make_small(s), make_small(us)); hp += 4;
- msg = TUPLE6(hp, am_profile, am_scheduler, scheduler_id, state, no_schedulers, timestamp); hp += 7;
-#ifndef ERTS_SMP
- profile_send(NIL, msg);
- UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
-#undef LOCAL_HEAP_SIZE
-#else
- enqueue_sys_msg_unlocked(SYS_MSG_TYPE_SYSPROF, NIL, NIL, msg, bp);
-#endif
- erts_smp_mtx_unlock(&smq_mtx);
-
-}
-
-
-/* Send {trace_ts, Pid, What, {Mod, Func, Arity}, Timestamp}
- * or {trace, Pid, What, {Mod, Func, Arity}}
- *
- * where 'What' is supposed to be 'in' or 'out'.
- *
- * Virtual scheduling do not fake scheduling for ports.
- */
-
-
-void trace_virtual_sched(Process *p, Eterm what)
-{
- trace_sched_aux(p, what, 1);
-}
-
/* Port profiling */
void
trace_port_open(Port *p, Eterm calling_pid, Eterm drv_name) {
- Eterm mess;
- Eterm* hp;
-
- if (is_internal_port(ERTS_TRACER_PROC(p))) {
-#define LOCAL_HEAP_SIZE (6+ERTS_TRACE_PATCH_TS_MAX_SIZE)
- DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE);
- UseTmpHeapNoproc(LOCAL_HEAP_SIZE);
-
- hp = local_heap;
-
- mess = TUPLE5(hp, am_trace, calling_pid, am_open, p->common.id, drv_name);
- hp += 6;
- erts_smp_mtx_lock(&smq_mtx);
- PATCH_TS(TFLGS_TS_TYPE(p), mess, hp, NULL, NULL);
- /* No fake schedule */
- send_to_port(NULL, mess, &ERTS_TRACER_PROC(p), &ERTS_TRACE_FLAGS(p));
- UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
-#undef LOCAL_HEAP_SIZE
- erts_smp_mtx_unlock(&smq_mtx);
- } else {
- ErlHeapFragment *bp;
- ErlOffHeap *off_heap;
- size_t sz_data;
- ERTS_TRACER_REF_TYPE tracer_ref;
-
- ASSERT(is_internal_pid(ERTS_TRACER_PROC(p)));
-
- sz_data = 6 + PATCH_TS_SIZE(p);
-
- ERTS_GET_TRACER_REF(tracer_ref,
- ERTS_TRACER_PROC(p),
- ERTS_TRACE_FLAGS(p));
-
- hp = ERTS_ALLOC_SYSMSG_HEAP(sz_data, &bp, &off_heap, tracer_ref);
-
- mess = TUPLE5(hp, am_trace, calling_pid, am_open, p->common.id, drv_name);
- hp += 6;
-
- erts_smp_mtx_lock(&smq_mtx);
-
- PATCH_TS(TFLGS_TS_TYPE(p), mess, hp, bp, tracer_ref);
-
- ERTS_ENQ_TRACE_MSG(p->common.id, tracer_ref, mess, bp);
- erts_smp_mtx_unlock(&smq_mtx);
- }
-
+ ErtsTracerNif *tnif = NULL;
+ ERTS_SMP_CHK_NO_PROC_LOCKS;
+ if (is_tracer_enabled(NULL, 0, &p->common, &tnif, TRACE_FUN_E_PORTS, am_open))
+ send_to_tracer_nif(NULL, &p->common, p->common.id, tnif, TRACE_FUN_T_PORTS,
+ am_open, calling_pid, drv_name, am_true);
}
/* Sends trace message:
@@ -2880,52 +1849,211 @@ trace_port_open(Port *p, Eterm calling_pid, Eterm drv_name) {
*/
void
trace_port(Port *t_p, Eterm what, Eterm data) {
- Eterm mess;
- Eterm* hp;
+ ErtsTracerNif *tnif = NULL;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(t_p)
|| erts_thr_progress_is_blocking());
+ ERTS_SMP_CHK_NO_PROC_LOCKS;
+ if (is_tracer_enabled(NULL, 0, &t_p->common, &tnif, TRACE_FUN_E_PORTS, what))
+ send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, TRACE_FUN_T_PORTS,
+ what, data, THE_NON_VALUE, am_true);
+}
- if (is_internal_port(ERTS_TRACER_PROC(t_p))) {
-#define LOCAL_HEAP_SIZE (5+ERTS_TRACE_PATCH_TS_MAX_SIZE)
- DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE);
- UseTmpHeapNoproc(LOCAL_HEAP_SIZE);
- hp = local_heap;
- mess = TUPLE4(hp, am_trace, t_p->common.id, what, data);
- hp += 5;
- erts_smp_mtx_lock(&smq_mtx);
- PATCH_TS(TFLGS_TS_TYPE(t_p), mess, hp, NULL, NULL);
- /* No fake schedule */
- send_to_port(NULL,mess,&ERTS_TRACER_PROC(t_p),&ERTS_TRACE_FLAGS(t_p));
- UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
-#undef LOCAL_HEAP_SIZE
- erts_smp_mtx_unlock(&smq_mtx);
+static Eterm
+trace_port_tmp_binary(char *bin, Sint sz, Binary **bptrp, Eterm **hp)
+{
+ if (sz <= ERL_ONHEAP_BIN_LIMIT) {
+ ErlHeapBin *hb = (ErlHeapBin *)*hp;
+ hb->thing_word = header_heap_bin(sz);
+ hb->size = sz;
+ sys_memcpy(hb->data, bin, sz);
+ *hp += heap_bin_size(sz);
+ return make_binary(hb);
} else {
- ErlHeapFragment *bp;
- ErlOffHeap *off_heap;
- size_t sz_data;
- ERTS_TRACER_REF_TYPE tracer_ref;
+ ProcBin* pb = (ProcBin *)*hp;
+ Binary *bptr = erts_bin_nrml_alloc(sz);
+ erts_refc_init(&bptr->refc, 1);
+ sys_memcpy(bptr->orig_bytes, bin, sz);
+ pb->thing_word = HEADER_PROC_BIN;
+ pb->size = sz;
+ pb->next = NULL;
+ pb->val = bptr;
+ pb->bytes = (byte*) bptr->orig_bytes;
+ pb->flags = 0;
+ *bptrp = bptr;
+ *hp += PROC_BIN_SIZE;
+ return make_binary(pb);
+ }
+}
- ASSERT(is_internal_pid(ERTS_TRACER_PROC(t_p)));
+/* Sends trace message:
+ * {trace, PortPid, 'receive', {pid(), {command, iolist()}}}
+ * {trace, PortPid, 'receive', {pid(), {control, pid()}}}
+ * {trace, PortPid, 'receive', {pid(), exit}}
+ *
+ */
+void
+trace_port_receive(Port *t_p, Eterm caller, Eterm what, ...)
+{
+ ErtsTracerNif *tnif = NULL;
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(t_p)
+ || erts_thr_progress_is_blocking());
+ ERTS_SMP_CHK_NO_PROC_LOCKS;
+ if (is_tracer_enabled(NULL, 0, &t_p->common, &tnif, TRACE_FUN_E_RECEIVE, am_receive)) {
+ /* We can use a stack heap here, as the nif is called in the
+ context of a port */
+#define LOCAL_HEAP_SIZE (3 + 3 + heap_bin_size(ERL_ONHEAP_BIN_LIMIT) + 3)
+ DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE);
+
+ Eterm *hp, data, *orig_hp = NULL;
+ Binary *bptr = NULL;
+ va_list args;
+ UseTmpHeapNoproc(LOCAL_HEAP_SIZE);
+ hp = local_heap;
+
+ if (what == am_close) {
+ data = what;
+ } else {
+ Eterm arg;
+ va_start(args, what);
+ if (what == am_command) {
+ char *bin = va_arg(args, char *);
+ Sint sz = va_arg(args, Sint);
+ va_end(args);
+ arg = trace_port_tmp_binary(bin, sz, &bptr, &hp);
+ } else if (what == am_call || what == am_control) {
+ unsigned int command = va_arg(args, unsigned int);
+ char *bin = va_arg(args, char *);
+ Sint sz = va_arg(args, Sint);
+ Eterm cmd;
+ va_end(args);
+ arg = trace_port_tmp_binary(bin, sz, &bptr, &hp);
+#if defined(ARCH_32)
+ if (!IS_USMALL(0, command)) {
+ *hp = make_pos_bignum_header(1);
+ BIG_DIGIT(hp, 0) = (Uint)command;
+ cmd = make_big(hp);
+ hp += 2;
+ } else
+#endif
+ {
+ cmd = make_small((Sint)command);
+ }
+ arg = TUPLE2(hp, cmd, arg);
+ hp += 3;
+ } else if (what == am_commandv) {
+ ErlIOVec *evp = va_arg(args, ErlIOVec*);
+ int i;
+ va_end(args);
+ if ((6 + evp->vsize * (2+PROC_BIN_SIZE+ERL_SUB_BIN_SIZE)) > LOCAL_HEAP_SIZE) {
+ hp = erts_alloc(ERTS_ALC_T_TMP,
+ (6 + evp->vsize * (2+PROC_BIN_SIZE+ERL_SUB_BIN_SIZE)) * sizeof(Eterm));
+ orig_hp = hp;
+ }
+ arg = NIL;
+ /* Convert each element in the ErlIOVec to a sub bin that points
+ to a procbin. We don't have to increment the proc bin refc as
+ the port task keeps the reference alive. */
+ for (i = evp->vsize-1; i >= 0; i--) {
+ if (evp->iov[i].iov_len) {
+ ProcBin* pb = (ProcBin*)hp;
+ ErlSubBin *sb;
+ ASSERT(evp->binv[i]);
+ pb->thing_word = HEADER_PROC_BIN;
+ pb->val = ErlDrvBinary2Binary(evp->binv[i]);
+ pb->size = pb->val->orig_size;
+ pb->next = NULL;
+ pb->bytes = (byte*) pb->val->orig_bytes;
+ pb->flags = 0;
+ hp += PROC_BIN_SIZE;
+
+ sb = (ErlSubBin*) hp;
+ sb->thing_word = HEADER_SUB_BIN;
+ sb->size = evp->iov[i].iov_len;
+ sb->offs = (byte*)(evp->iov[i].iov_base) - pb->bytes;
+ sb->orig = make_binary(pb);
+ sb->bitoffs = 0;
+ sb->bitsize = 0;
+ sb->is_writable = 0;
+ hp += ERL_SUB_BIN_SIZE;
+
+ arg = CONS(hp, make_binary(sb), arg);
+ hp += 2;
+ }
+ }
+ what = am_command;
+ } else {
+ arg = va_arg(args, Eterm);
+ va_end(args);
+ }
+ data = TUPLE2(hp, what, arg);
+ hp += 3;
+ }
+
+ data = TUPLE2(hp, caller, data);
+ hp += 3;
+ ASSERT(hp <= (local_heap + LOCAL_HEAP_SIZE) || orig_hp);
+ send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif,
+ TRACE_FUN_T_RECEIVE,
+ am_receive, data, THE_NON_VALUE, am_true);
+
+ if (bptr && erts_refc_dectest(&bptr->refc, 1) == 0)
+ erts_bin_free(bptr);
+
+ if (orig_hp)
+ erts_free(ERTS_ALC_T_TMP, orig_hp);
+
+ UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
+ }
+#undef LOCAL_HEAP_SIZE
+}
- sz_data = 5 + PATCH_TS_SIZE(t_p);
+void
+trace_port_send(Port *t_p, Eterm receiver, Eterm msg, int exists)
+{
+ ErtsTracerNif *tnif = NULL;
+ Eterm op = exists ? am_send : am_send_to_non_existing_process;
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(t_p)
+ || erts_thr_progress_is_blocking());
+ ERTS_SMP_CHK_NO_PROC_LOCKS;
+ if (is_tracer_enabled(NULL, 0, &t_p->common, &tnif, TRACE_FUN_E_SEND, op))
+ send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, TRACE_FUN_T_SEND,
+ op, msg, receiver, am_true);
+}
- ERTS_GET_TRACER_REF(tracer_ref,
- ERTS_TRACER_PROC(t_p),
- ERTS_TRACE_FLAGS(t_p));
+void trace_port_send_binary(Port *t_p, Eterm to, Eterm what, char *bin, Sint sz)
+{
+ ErtsTracerNif *tnif = NULL;
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(t_p)
+ || erts_thr_progress_is_blocking());
+ ERTS_SMP_CHK_NO_PROC_LOCKS;
+ if (is_tracer_enabled(NULL, 0, &t_p->common, &tnif, TRACE_FUN_E_SEND, am_send)) {
+ Eterm msg;
+ Binary* bptr = NULL;
+#define LOCAL_HEAP_SIZE (3 + 3 + heap_bin_size(ERL_ONHEAP_BIN_LIMIT))
+ DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE);
- hp = ERTS_ALLOC_SYSMSG_HEAP(sz_data, &bp, &off_heap, tracer_ref);
+ Eterm *hp;
- mess = TUPLE4(hp, am_trace, t_p->common.id, what, data);
- hp += 5;
+ ERTS_CT_ASSERT(heap_bin_size(ERL_ONHEAP_BIN_LIMIT) >= PROC_BIN_SIZE);
+ UseTmpHeapNoproc(LOCAL_HEAP_SIZE);
+ hp = local_heap;
- erts_smp_mtx_lock(&smq_mtx);
+ msg = trace_port_tmp_binary(bin, sz, &bptr, &hp);
- PATCH_TS(TFLGS_TS_TYPE(t_p), mess, hp, bp, tracer_ref);
+ msg = TUPLE2(hp, what, msg);
+ hp += 3;
+ msg = TUPLE2(hp, t_p->common.id, msg);
+ hp += 3;
- ERTS_ENQ_TRACE_MSG(t_p->common.id, tracer_ref, mess, bp);
- erts_smp_mtx_unlock(&smq_mtx);
+ send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, TRACE_FUN_T_SEND,
+ am_send, msg, to, am_true);
+ if (bptr && erts_refc_dectest(&bptr->refc, 1) == 0)
+ erts_bin_free(bptr);
+
+ UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
+#undef LOCAL_HEAP_SIZE
}
}
@@ -2939,83 +2067,19 @@ trace_port(Port *t_p, Eterm what, Eterm data) {
void
trace_sched_ports(Port *p, Eterm what) {
- trace_sched_ports_where(p,what, make_small(0));
+ trace_sched_ports_where(p, what, make_small(0));
}
-void
-trace_sched_ports_where(Port *p, Eterm what, Eterm where) {
- Eterm mess;
- Eterm* hp;
- int ws = 5;
- Eterm sched_id = am_undefined;
-
- if (is_internal_port(ERTS_TRACER_PROC(p))) {
-#define LOCAL_HEAP_SIZE (6+ERTS_TRACE_PATCH_TS_MAX_SIZE)
- DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE);
- UseTmpHeapNoproc(LOCAL_HEAP_SIZE);
-
- hp = local_heap;
-
- if (IS_TRACED_FL(p, F_TRACE_SCHED_NO)) {
-#ifdef ERTS_SMP
- ErtsSchedulerData *esd = erts_get_scheduler_data();
- if (esd) sched_id = make_small(esd->no);
- else sched_id = am_undefined;
-#else
- sched_id = make_small(1);
-#endif
- mess = TUPLE5(hp, am_trace, p->common.id, what, sched_id, where);
- ws = 6;
- } else {
- mess = TUPLE4(hp, am_trace, p->common.id, what, where);
- ws = 5;
- }
- hp += ws;
-
- erts_smp_mtx_lock(&smq_mtx);
-
- PATCH_TS(TFLGS_TS_TYPE(p), mess, hp, NULL, NULL);
-
- /* No fake scheduling */
- send_to_port(NULL, mess, &ERTS_TRACER_PROC(p), &ERTS_TRACE_FLAGS(p));
- UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
-#undef LOCAL_HEAP_SIZE
- erts_smp_mtx_unlock(&smq_mtx);
- } else {
- ErlHeapFragment *bp;
- ErlOffHeap *off_heap;
- ERTS_TRACER_REF_TYPE tracer_ref;
-
- ASSERT(is_internal_pid(ERTS_TRACER_PROC(p)));
-
- if (IS_TRACED_FL(p, F_TRACE_SCHED_NO)) ws = 6; /* Make place for scheduler id */
-
- ERTS_GET_TRACER_REF(tracer_ref,
- ERTS_TRACER_PROC(p),
- ERTS_TRACE_FLAGS(p));
-
- hp = ERTS_ALLOC_SYSMSG_HEAP(ws+PATCH_TS_SIZE(p), &bp, &off_heap, tracer_ref);
-
- if (IS_TRACED_FL(p, F_TRACE_SCHED_NO)) {
-#ifdef ERTS_SMP
- ErtsSchedulerData *esd = erts_get_scheduler_data();
- if (esd) sched_id = make_small(esd->no);
- else sched_id = am_undefined;
-#else
- sched_id = make_small(1);
-#endif
- mess = TUPLE5(hp, am_trace, p->common.id, what, sched_id, where);
- } else {
- mess = TUPLE4(hp, am_trace, p->common.id, what, where);
- }
- hp += ws;
-
- erts_smp_mtx_lock(&smq_mtx);
-
- PATCH_TS(TFLGS_TS_TYPE(p), mess, hp, bp, tracer_ref);
- ERTS_ENQ_TRACE_MSG(p->common.id, tracer_ref, mess, bp);
- erts_smp_mtx_unlock(&smq_mtx);
- }
+void
+trace_sched_ports_where(Port *t_p, Eterm what, Eterm where) {
+ ErtsTracerNif *tnif = NULL;
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(t_p)
+ || erts_thr_progress_is_blocking());
+ ERTS_SMP_CHK_NO_PROC_LOCKS;
+ if (is_tracer_enabled(NULL, 0, &t_p->common, &tnif, TRACE_FUN_E_SCHED_PORT, what))
+ send_to_tracer_nif(NULL, &t_p->common, t_p->common.id,
+ tnif, TRACE_FUN_T_SCHED_PORT,
+ what, where, THE_NON_VALUE, am_true);
}
/* Port profiling */
@@ -3068,24 +2132,36 @@ profile_runnable_proc(Process *p, Eterm status){
Eterm *hp, msg;
Eterm where = am_undefined;
ErlHeapFragment *bp = NULL;
+ BeamInstr *current = NULL;
#ifndef ERTS_SMP
#define LOCAL_HEAP_SIZE (4 + 6 + ERTS_TRACE_PATCH_TS_MAX_SIZE)
-
DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE);
UseTmpHeapNoproc(LOCAL_HEAP_SIZE);
hp = local_heap;
#else
+ ErtsThrPrgrDelayHandle dhndl;
Uint hsz = 4 + 6 + patch_ts_size(erts_system_profile_ts_type)-1;
#endif
-
- if (!p->current) {
- p->current = find_function_from_pc(p->i);
+ /* Assumptions:
+ * We possibly don't have the MAIN_LOCK for the process p here.
+ * We assume that we can read from p->current and p->i atomically
+ */
+#ifdef ERTS_SMP
+ dhndl = erts_thr_progress_unmanaged_delay(); /* suspend purge operations */
+#endif
+
+ if (!ERTS_PROC_IS_EXITING(p)) {
+ if (p->current) {
+ current = p->current;
+ } else {
+ current = find_function_from_pc(p->i);
+ }
}
#ifdef ERTS_SMP
- if (!p->current) {
+ if (!current) {
hsz -= 4;
}
@@ -3093,11 +2169,15 @@ profile_runnable_proc(Process *p, Eterm status){
hp = bp->mem;
#endif
- if (p->current) {
- where = TUPLE3(hp, p->current[0], p->current[1], make_small(p->current[2])); hp += 4;
+ if (current) {
+ where = TUPLE3(hp, current[0], current[1], make_small(current[2])); hp += 4;
} else {
where = make_small(0);
}
+
+#ifdef ERTS_SMP
+ erts_thr_progress_unmanaged_continue(dhndl);
+#endif
erts_smp_mtx_lock(&smq_mtx);
@@ -3123,28 +2203,6 @@ profile_runnable_proc(Process *p, Eterm status){
#ifdef ERTS_SMP
-void
-erts_check_my_tracer_proc(Process *p)
-{
- if (is_internal_pid(ERTS_TRACER_PROC(p))) {
- Process *tracer = erts_pid2proc(p,
- ERTS_PROC_LOCK_MAIN,
- ERTS_TRACER_PROC(p),
- ERTS_PROC_LOCK_STATUS);
- int invalid_tracer = (!tracer
- || !(ERTS_TRACE_FLAGS(tracer) & F_TRACER));
- if (tracer)
- erts_smp_proc_unlock(tracer, ERTS_PROC_LOCK_STATUS);
- if (invalid_tracer) {
- erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR);
- ERTS_TRACE_FLAGS(p) &= ~TRACEE_FLAGS;
- ERTS_TRACER_PROC(p) = NIL;
- erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR);
- }
- }
-}
-
-
typedef struct ErtsSysMsgQ_ ErtsSysMsgQ;
struct ErtsSysMsgQ_ {
ErtsSysMsgQ *next;
@@ -3222,12 +2280,6 @@ static void
print_msg_type(ErtsSysMsgQ *smqp)
{
switch (smqp->type) {
- case SYS_MSG_TYPE_TRACE:
- erts_fprintf(stderr, "TRACE ");
- break;
- case SYS_MSG_TYPE_SEQTRACE:
- erts_fprintf(stderr, "SEQTRACE ");
- break;
case SYS_MSG_TYPE_SYSMON:
erts_fprintf(stderr, "SYSMON ");
break;
@@ -3238,8 +2290,8 @@ print_msg_type(ErtsSysMsgQ *smqp)
erts_fprintf(stderr, "ERRLGR ");
break;
case SYS_MSG_TYPE_PROC_MSG:
- erts_fprintf(stderr, "PROC_MSG ");
- break;
+ erts_fprintf(stderr, "PROC_MSG ");
+ break;
default:
erts_fprintf(stderr, "??? ");
break;
@@ -3251,17 +2303,6 @@ static void
sys_msg_disp_failure(ErtsSysMsgQ *smqp, Eterm receiver)
{
switch (smqp->type) {
- case SYS_MSG_TYPE_TRACE:
- /* Invalid tracer_proc's are removed when processes
- are scheduled in. */
- break;
- case SYS_MSG_TYPE_SEQTRACE:
- /* Reset seq_tracer if it hasn't changed */
- erts_smp_rwmtx_rwlock(&sys_trace_rwmtx);
- if (system_seq_tracer == receiver)
- system_seq_tracer = am_false;
- erts_smp_rwmtx_rwunlock(&sys_trace_rwmtx);
- break;
case SYS_MSG_TYPE_SYSMON:
if (receiver == NIL
&& !erts_system_monitor_long_gc
@@ -3322,7 +2363,7 @@ sys_msg_disp_failure(ErtsSysMsgQ *smqp, Eterm receiver)
break;
}
case SYS_MSG_TYPE_PROC_MSG:
- break;
+ break;
default:
ASSERT(0);
}
@@ -3436,19 +2477,13 @@ sys_msg_dispatcher_func(void *unused)
if (erts_thr_progress_update(NULL))
erts_thr_progress_leader_update(NULL);
- ERTS_SCHED_FAIR_YIELD();
-
#ifdef DEBUG_PRINTOUTS
print_msg_type(smqp);
#endif
switch (smqp->type) {
- case SYS_MSG_TYPE_TRACE:
- case SYS_MSG_TYPE_PROC_MSG:
- receiver = smqp->to;
- break;
- case SYS_MSG_TYPE_SEQTRACE:
- receiver = erts_get_system_seq_tracer();
- break;
+ case SYS_MSG_TYPE_PROC_MSG:
+ receiver = smqp->to;
+ break;
case SYS_MSG_TYPE_SYSMON:
receiver = erts_get_system_monitor();
if (smqp->from == receiver) {
@@ -3483,21 +2518,16 @@ sys_msg_dispatcher_func(void *unused)
if (is_internal_pid(receiver)) {
proc = erts_pid2proc(NULL, 0, receiver, proc_locks);
- if (!proc
- || (smqp->type == SYS_MSG_TYPE_TRACE
- && !(ERTS_TRACE_FLAGS(proc) & F_TRACER))) {
+ if (!proc) {
/* Bad tracer */
-#ifdef DEBUG_PRINTOUTS
- if (smqp->type == SYS_MSG_TYPE_TRACE && proc)
- erts_fprintf(stderr,
- "<tracer alive but missing "
- "F_TRACER flag> ");
-#endif
goto failure;
}
else {
+ ErtsMessage *mp;
queue_proc_msg:
- erts_queue_message(proc,&proc_locks,smqp->bp,smqp->msg,NIL);
+ mp = erts_alloc_message(0, NULL);
+ mp->data.heap_frag = smqp->bp;
+ erts_queue_message(proc,proc_locks,mp,smqp->msg,am_system);
#ifdef DEBUG_PRINTOUTS
erts_fprintf(stderr, "delivered\n");
#endif
@@ -3563,12 +2593,6 @@ erts_foreach_sys_msg_in_q(void (*func)(Eterm,
for (sm = sys_message_queue; sm; sm = sm->next) {
Eterm to;
switch (sm->type) {
- case SYS_MSG_TYPE_TRACE:
- to = sm->to;
- break;
- case SYS_MSG_TYPE_SEQTRACE:
- to = erts_get_system_seq_tracer();
- break;
case SYS_MSG_TYPE_SYSMON:
to = erts_get_system_monitor();
break;
@@ -3592,9 +2616,6 @@ static void
init_sys_msg_dispatcher(void)
{
erts_smp_thr_opts_t thr_opts = ERTS_SMP_THR_OPTS_DEFAULT_INITER;
-#ifdef __OSE__
- thr_opts.coreNo = 0;
-#endif
thr_opts.detached = 1;
thr_opts.name = "sys_msg_dispatcher";
init_smq_element_alloc();
@@ -3602,7 +2623,6 @@ init_sys_msg_dispatcher(void)
sys_message_queue_end = NULL;
erts_smp_cnd_init(&smq_cnd);
erts_smp_mtx_init(&smq_mtx, "sys_msg_q");
-
erts_smp_thr_create(&sys_msg_dispatcher_tid,
sys_msg_dispatcher_func,
NULL,
@@ -3610,3 +2630,624 @@ init_sys_msg_dispatcher(void)
}
#endif
+
+#include "erl_nif.h"
+
+typedef struct {
+ char *name;
+ Uint arity;
+ ErlNifFunc *cb;
+} ErtsTracerType;
+
+struct ErtsTracerNif_ {
+ HashBucket hb;
+ Eterm module;
+ struct erl_module_nif* nif_mod;
+ ErtsTracerType tracers[NIF_TRACER_TYPES];
+};
+
+static void init_tracer_template(ErtsTracerNif *tnif) {
+
+ /* default tracer functions */
+ tnif->tracers[TRACE_FUN_DEFAULT].name = "trace";
+ tnif->tracers[TRACE_FUN_DEFAULT].arity = 5;
+ tnif->tracers[TRACE_FUN_DEFAULT].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_ENABLED].name = "enabled";
+ tnif->tracers[TRACE_FUN_ENABLED].arity = 3;
+ tnif->tracers[TRACE_FUN_ENABLED].cb = NULL;
+
+ /* specific tracer functions */
+ tnif->tracers[TRACE_FUN_T_SEND].name = "trace_send";
+ tnif->tracers[TRACE_FUN_T_SEND].arity = 5;
+ tnif->tracers[TRACE_FUN_T_SEND].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_T_RECEIVE].name = "trace_receive";
+ tnif->tracers[TRACE_FUN_T_RECEIVE].arity = 5;
+ tnif->tracers[TRACE_FUN_T_RECEIVE].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_T_CALL].name = "trace_call";
+ tnif->tracers[TRACE_FUN_T_CALL].arity = 5;
+ tnif->tracers[TRACE_FUN_T_CALL].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_T_SCHED_PROC].name = "trace_running_procs";
+ tnif->tracers[TRACE_FUN_T_SCHED_PROC].arity = 5;
+ tnif->tracers[TRACE_FUN_T_SCHED_PROC].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_T_SCHED_PORT].name = "trace_running_ports";
+ tnif->tracers[TRACE_FUN_T_SCHED_PORT].arity = 5;
+ tnif->tracers[TRACE_FUN_T_SCHED_PORT].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_T_GC].name = "trace_garbage_collection";
+ tnif->tracers[TRACE_FUN_T_GC].arity = 5;
+ tnif->tracers[TRACE_FUN_T_GC].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_T_PROCS].name = "trace_procs";
+ tnif->tracers[TRACE_FUN_T_PROCS].arity = 5;
+ tnif->tracers[TRACE_FUN_T_PROCS].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_T_PORTS].name = "trace_ports";
+ tnif->tracers[TRACE_FUN_T_PORTS].arity = 5;
+ tnif->tracers[TRACE_FUN_T_PORTS].cb = NULL;
+
+ /* specific enabled functions */
+ tnif->tracers[TRACE_FUN_E_SEND].name = "enabled_send";
+ tnif->tracers[TRACE_FUN_E_SEND].arity = 3;
+ tnif->tracers[TRACE_FUN_E_SEND].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_E_RECEIVE].name = "enabled_receive";
+ tnif->tracers[TRACE_FUN_E_RECEIVE].arity = 3;
+ tnif->tracers[TRACE_FUN_E_RECEIVE].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_E_CALL].name = "enabled_call";
+ tnif->tracers[TRACE_FUN_E_CALL].arity = 3;
+ tnif->tracers[TRACE_FUN_E_CALL].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_E_SCHED_PROC].name = "enabled_running_procs";
+ tnif->tracers[TRACE_FUN_E_SCHED_PROC].arity = 3;
+ tnif->tracers[TRACE_FUN_E_SCHED_PROC].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_E_SCHED_PORT].name = "enabled_running_ports";
+ tnif->tracers[TRACE_FUN_E_SCHED_PORT].arity = 3;
+ tnif->tracers[TRACE_FUN_E_SCHED_PORT].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_E_GC].name = "enabled_garbage_collection";
+ tnif->tracers[TRACE_FUN_E_GC].arity = 3;
+ tnif->tracers[TRACE_FUN_E_GC].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_E_PROCS].name = "enabled_procs";
+ tnif->tracers[TRACE_FUN_E_PROCS].arity = 3;
+ tnif->tracers[TRACE_FUN_E_PROCS].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_E_PORTS].name = "enabled_ports";
+ tnif->tracers[TRACE_FUN_E_PORTS].arity = 3;
+ tnif->tracers[TRACE_FUN_E_PORTS].cb = NULL;
+}
+
+static Hash *tracer_hash = NULL;
+static erts_smp_rwmtx_t tracer_mtx;
+
+static ErtsTracerNif *
+load_tracer_nif(const ErtsTracer tracer)
+{
+ Module* mod = erts_get_module(ERTS_TRACER_MODULE(tracer),
+ erts_active_code_ix());
+ struct erl_module_instance *instance;
+ ErlNifFunc *funcs;
+ int num_of_funcs;
+ ErtsTracerNif tnif_tmpl, *tnif;
+ ErtsTracerType *tracers;
+ int i,j;
+
+ if (!mod || !mod->curr.nif) {
+ return NULL;
+ }
+
+ instance = &mod->curr;
+
+ init_tracer_template(&tnif_tmpl);
+ tnif_tmpl.nif_mod = instance->nif;
+ tnif_tmpl.module = ERTS_TRACER_MODULE(tracer);
+ tracers = tnif_tmpl.tracers;
+
+ num_of_funcs = erts_nif_get_funcs(instance->nif, &funcs);
+
+ for(i = 0; i < num_of_funcs; i++) {
+ for (j = 0; j < NIF_TRACER_TYPES; j++) {
+ if (strcmp(tracers[j].name, funcs[i].name) == 0 && tracers[j].arity == funcs[i].arity) {
+ tracers[j].cb = &(funcs[i]);
+ break;
+ }
+ }
+ }
+
+ if (tracers[TRACE_FUN_DEFAULT].cb == NULL || tracers[TRACE_FUN_ENABLED].cb == NULL ) {
+ return NULL;
+ }
+
+ erts_smp_rwmtx_rwlock(&tracer_mtx);
+ tnif = hash_put(tracer_hash, &tnif_tmpl);
+ erts_smp_rwmtx_rwunlock(&tracer_mtx);
+
+ return tnif;
+}
+
+static ERTS_INLINE ErtsTracerNif *
+lookup_tracer_nif(const ErtsTracer tracer)
+{
+ ErtsTracerNif tnif_tmpl;
+ ErtsTracerNif *tnif;
+ tnif_tmpl.module = ERTS_TRACER_MODULE(tracer);
+ erts_smp_rwmtx_rlock(&tracer_mtx);
+ if ((tnif = hash_get(tracer_hash, &tnif_tmpl)) == NULL) {
+ erts_smp_rwmtx_runlock(&tracer_mtx);
+ tnif = load_tracer_nif(tracer);
+ ASSERT(!tnif || tnif->nif_mod);
+ return tnif;
+ }
+ erts_smp_rwmtx_runlock(&tracer_mtx);
+ ASSERT(tnif->nif_mod);
+ return tnif;
+}
+
+/* This function converts an Erlang tracer term to ErtsTracer.
+ It returns THE_NON_VALUE if an invalid tracer term was given.
+ Accepted input is:
+ pid() || port() || {prefix, pid()} || {prefix, port()} ||
+ {prefix, atom(), term()} || {atom(), term()}
+ */
+ErtsTracer
+erts_term_to_tracer(Eterm prefix, Eterm t)
+{
+ ErtsTracer tracer = erts_tracer_nil;
+ ASSERT(is_atom(prefix) || prefix == THE_NON_VALUE);
+ if (!is_nil(t)) {
+ Eterm module = am_erl_tracer, state = THE_NON_VALUE;
+ Eterm hp[2];
+ if (is_tuple(t)) {
+ Eterm *tp = tuple_val(t);
+ if (prefix != THE_NON_VALUE) {
+ if (arityval(tp[0]) == 2 && tp[1] == prefix)
+ t = tp[2];
+ else if (arityval(tp[0]) == 3 && tp[1] == prefix && is_atom(tp[2])) {
+ module = tp[2];
+ state = tp[3];
+ }
+ } else {
+ if (arityval(tp[0]) == 2 && is_atom(tp[2])) {
+ module = tp[1];
+ state = tp[2];
+ }
+ }
+ }
+ if (state == THE_NON_VALUE && (is_internal_pid(t) || is_internal_port(t)))
+ state = t;
+ if (state == THE_NON_VALUE)
+ return THE_NON_VALUE;
+ erts_tracer_update(&tracer, CONS(hp, module, state));
+ }
+ if (!lookup_tracer_nif(tracer)) {
+ ASSERT(ERTS_TRACER_MODULE(tracer) != am_erl_tracer);
+ ERTS_TRACER_CLEAR(&tracer);
+ return THE_NON_VALUE;
+ }
+ return tracer;
+}
+
+Eterm
+erts_tracer_to_term(Process *p, ErtsTracer tracer)
+{
+ if (ERTS_TRACER_IS_NIL(tracer))
+ return am_false;
+ if (ERTS_TRACER_MODULE(tracer) == am_erl_tracer)
+ /* Have to manage these specifically in order to be
+ backwards compatible */
+ return ERTS_TRACER_STATE(tracer);
+ else {
+ Eterm *hp = HAlloc(p, 3);
+ return TUPLE2(hp, ERTS_TRACER_MODULE(tracer),
+ copy_object(ERTS_TRACER_STATE(tracer), p));
+ }
+}
+
+
+static ERTS_INLINE int
+send_to_tracer_nif_raw(Process *c_p, Process *tracee,
+ const ErtsTracer tracer, Uint tracee_flags,
+ Eterm t_p_id, ErtsTracerNif *tnif,
+ enum ErtsTracerOpt topt,
+ Eterm tag, Eterm msg, Eterm extra, Eterm pam_result)
+{
+ if (tnif || (tnif = lookup_tracer_nif(tracer)) != NULL) {
+#define MAP_SIZE 4
+ Eterm argv[5], local_heap[3+MAP_SIZE /* values */ + (MAP_SIZE+1 /* keys */)];
+ flatmap_t *map = (flatmap_t*)(local_heap+(MAP_SIZE+1));
+ Eterm *map_values = flatmap_get_values(map);
+ Eterm *map_keys = local_heap + 1;
+ Uint map_elem_count = 0;
+
+ topt = (tnif->tracers[topt].cb) ? topt : TRACE_FUN_DEFAULT;
+ ASSERT(topt < NIF_TRACER_TYPES);
+
+ argv[0] = tag;
+ argv[1] = ERTS_TRACER_STATE(tracer);
+ argv[2] = t_p_id;
+ argv[3] = msg;
+ argv[4] = make_flatmap(map);
+
+ map->thing_word = MAP_HEADER_FLATMAP;
+
+ if (extra != THE_NON_VALUE) {
+ map_keys[map_elem_count] = am_extra;
+ map_values[map_elem_count++] = extra;
+ }
+
+ if (pam_result != am_true) {
+ map_keys[map_elem_count] = am_match_spec_result;
+ map_values[map_elem_count++] = pam_result;
+ }
+
+ if (tracee_flags & F_TRACE_SCHED_NO) {
+ map_keys[map_elem_count] = am_scheduler_id;
+ map_values[map_elem_count++] = make_small(erts_get_scheduler_id());
+ }
+ map_keys[map_elem_count] = am_timestamp;
+ if (tracee_flags & F_NOW_TS)
+#ifdef HAVE_ERTS_NOW_CPU
+ if (erts_cpu_timestamp)
+ map_values[map_elem_count++] = am_cpu_timestamp;
+ else
+#endif
+ map_values[map_elem_count++] = am_timestamp;
+ else if (tracee_flags & F_STRICT_MON_TS)
+ map_values[map_elem_count++] = am_strict_monotonic;
+ else if (tracee_flags & F_MON_TS)
+ map_values[map_elem_count++] = am_monotonic;
+
+ map->size = map_elem_count;
+ map->keys = make_tuple(local_heap);
+ local_heap[0] = make_arityval(map_elem_count);
+
+#undef MAP_SIZE
+ erts_nif_call_function(c_p, tracee ? tracee : c_p,
+ tnif->nif_mod,
+ tnif->tracers[topt].cb,
+ tnif->tracers[topt].arity,
+ argv);
+ }
+ return 1;
+}
+
+static ERTS_INLINE int
+send_to_tracer_nif(Process *c_p, ErtsPTabElementCommon *t_p,
+ Eterm t_p_id, ErtsTracerNif *tnif, enum ErtsTracerOpt topt,
+ Eterm tag, Eterm msg, Eterm extra, Eterm pam_result)
+{
+#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
+ if (c_p) {
+ /* We have to hold the main lock of the currently executing process */
+ erts_proc_lc_chk_have_proc_locks(c_p, ERTS_PROC_LOCK_MAIN);
+ }
+ if (is_internal_pid(t_p->id)) {
+ /* We have to have at least one lock */
+ ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks((Process*)t_p) & ERTS_PROC_LOCKS_ALL);
+ } else {
+ ASSERT(is_internal_port(t_p->id));
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked((Port*)t_p));
+ }
+#endif
+
+ return send_to_tracer_nif_raw(c_p,
+ is_internal_pid(t_p->id) ? (Process*)t_p : NULL,
+ t_p->tracer, t_p->trace_flags,
+ t_p_id, tnif, topt, tag, msg, extra,
+ pam_result);
+}
+
+static ERTS_INLINE Eterm
+call_enabled_tracer(const ErtsTracer tracer,
+ ErtsTracerNif **tnif_ret,
+ enum ErtsTracerOpt topt,
+ Eterm tag, Eterm t_p_id) {
+ ErtsTracerNif *tnif = lookup_tracer_nif(tracer);
+ if (tnif) {
+ Eterm argv[] = {tag, ERTS_TRACER_STATE(tracer), t_p_id},
+ ret;
+ topt = (tnif->tracers[topt].cb) ? topt : TRACE_FUN_ENABLED;
+ ASSERT(topt < NIF_TRACER_TYPES);
+ ASSERT(tnif->tracers[topt].cb != NULL);
+ if (tnif_ret) *tnif_ret = tnif;
+ ret = erts_nif_call_function(NULL, NULL, tnif->nif_mod,
+ tnif->tracers[topt].cb,
+ tnif->tracers[topt].arity,
+ argv);
+ if (tag == am_trace_status && ret != am_remove)
+ return am_trace;
+ ASSERT(tag == am_trace_status || ret != am_remove);
+ return ret;
+ }
+ return tag == am_trace_status ? am_remove : am_discard;
+}
+
+static int
+is_tracer_enabled(Process* c_p, ErtsProcLocks c_p_locks,
+ ErtsPTabElementCommon *t_p,
+ ErtsTracerNif **tnif_ret,
+ enum ErtsTracerOpt topt, Eterm tag) {
+ Eterm nif_result;
+
+#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
+ if (c_p)
+ ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == c_p_locks
+ || erts_thr_progress_is_blocking());
+ if (is_internal_pid(t_p->id)) {
+ /* We have to have at least one lock */
+ ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks((Process*)t_p) & ERTS_PROC_LOCKS_ALL
+ || erts_thr_progress_is_blocking());
+ } else {
+ ASSERT(is_internal_port(t_p->id));
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked((Port*)t_p)
+ || erts_thr_progress_is_blocking());
+ }
+#endif
+
+ nif_result = call_enabled_tracer(t_p->tracer, tnif_ret, topt, tag, t_p->id);
+ switch (nif_result) {
+ case am_discard: return 0;
+ case am_trace: return 1;
+ case THE_NON_VALUE:
+ case am_remove: ASSERT(tag == am_trace_status); break;
+ default:
+ /* only am_remove should be returned, but if
+ something else is returned we fall-through
+ and remove the tracer. */
+ ASSERT(0);
+ }
+
+ /* Only remove tracer on self() and ports */
+ if (is_internal_port(t_p->id) || (c_p && c_p->common.id == t_p->id)) {
+ ErtsProcLocks c_p_xlocks = 0;
+ if (is_internal_pid(t_p->id)) {
+ ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) & ERTS_PROC_LOCK_MAIN);
+ if (c_p_locks != ERTS_PROC_LOCKS_ALL) {
+ c_p_xlocks = ~c_p_locks & ERTS_PROC_LOCKS_ALL;
+ if (erts_smp_proc_trylock(c_p, c_p_xlocks) == EBUSY) {
+ erts_smp_proc_unlock(c_p, c_p_locks & ~ERTS_PROC_LOCK_MAIN);
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
+ }
+ }
+ }
+ erts_tracer_replace(t_p, erts_tracer_nil);
+ t_p->trace_flags &= ~TRACEE_FLAGS;
+
+ if (c_p_xlocks)
+ erts_smp_proc_unlock(c_p, c_p_xlocks);
+ }
+
+ return 0;
+}
+
+int erts_is_tracer_enabled(const ErtsTracer tracer, ErtsPTabElementCommon *t_p)
+{
+ ErtsTracerNif *tnif = lookup_tracer_nif(tracer);
+ if (tnif) {
+ Eterm nif_result = call_enabled_tracer(tracer, &tnif,
+ TRACE_FUN_ENABLED,
+ am_trace_status,
+ t_p->id);
+ switch (nif_result) {
+ case am_discard:
+ case am_trace: return 1;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+int erts_is_tracer_proc_enabled(Process* c_p, ErtsProcLocks c_p_locks,
+ ErtsPTabElementCommon *t_p)
+{
+ return is_tracer_enabled(c_p, c_p_locks, t_p, NULL, TRACE_FUN_ENABLED,
+ am_trace_status);
+}
+
+int erts_is_tracer_proc_enabled_send(Process* c_p, ErtsProcLocks c_p_locks,
+ ErtsPTabElementCommon *t_p)
+{
+ return is_tracer_enabled(c_p, c_p_locks, t_p, NULL, TRACE_FUN_T_SEND, am_send);
+}
+
+
+void erts_tracer_replace(ErtsPTabElementCommon *t_p, const ErtsTracer tracer)
+{
+#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
+ if (is_internal_pid(t_p->id) && !erts_thr_progress_is_blocking()) {
+ erts_proc_lc_chk_have_proc_locks((Process*)t_p, ERTS_PROC_LOCKS_ALL);
+ } else if (is_internal_port(t_p->id)) {
+ ERTS_LC_ASSERT(erts_lc_is_port_locked((Port*)t_p)
+ || erts_thr_progress_is_blocking());
+ }
+#endif
+ if (ERTS_TRACER_COMPARE(t_p->tracer, tracer))
+ return;
+
+ erts_tracer_update(&t_p->tracer, tracer);
+}
+
+static void free_tracer(void *p)
+{
+ ErtsTracer tracer = (ErtsTracer)p;
+
+ if (is_immed(ERTS_TRACER_STATE(tracer))) {
+ erts_free(ERTS_ALC_T_HEAP_FRAG, ptr_val(tracer));
+ } else {
+ ErlHeapFragment *hf = (void*)((char*)(ptr_val(tracer)) - offsetof(ErlHeapFragment, mem));
+ free_message_buffer(hf);
+ }
+}
+
+/* un-define erts_tracer_update before implementation */
+#ifdef erts_tracer_update
+#undef erts_tracer_update
+#endif
+
+/*
+ * ErtsTracer is either NIL, 'true' or [Mod | State]
+ *
+ * - If State is immediate then the memory for
+ * the cons cell is just two words + sizeof(ErtsThrPrgrLaterOp) large.
+ * - If State is a complex term then the cons cell
+ * is allocated in an ErlHeapFragment where the cons
+ * ptr points to the mem field. So in order to get the
+ * ptr to the fragment you do this:
+ * (char*)(ptr_val(tracer)) - offsetof(ErlHeapFragment, mem)
+ * Normally you shouldn't have to care about this though
+ * as erts_tracer_update takes care of it for you.
+ *
+ * When ErtsTracer is stored in the stack as part of a
+ * return trace, the cons cell is stored on the heap of
+ * the process.
+ *
+ * The cons cell is not always stored on the heap as:
+ * 1) for port/meta tracing there is no heap
+ * 2) we would need the main lock in order to
+ * read the tracer which is undesirable.
+ *
+ * One way to optimize this (memory wise) is to keep an refc and only bump
+ * the refc when *tracer is NIL.
+ */
+void
+erts_tracer_update(ErtsTracer *tracer, const ErtsTracer new_tracer)
+{
+ ErlHeapFragment *hf;
+
+ if (is_not_nil(*tracer)) {
+ Uint offs = 2;
+ UWord size = 2 * sizeof(Eterm) + sizeof(ErtsThrPrgrLaterOp);
+ ErtsThrPrgrLaterOp *lop;
+ ASSERT(is_list(*tracer));
+ if (is_not_immed(ERTS_TRACER_STATE(*tracer))) {
+ hf = (void*)(((char*)(ptr_val(*tracer)) - offsetof(ErlHeapFragment, mem)));
+ offs = hf->used_size;
+ size = hf->alloc_size * sizeof(Eterm) + sizeof(ErlHeapFragment);
+ ASSERT(offs == size_object(*tracer));
+ }
+
+ /* sparc assumes that all structs are double word aligned, so we
+ have to align the ErtsThrPrgrLaterOp struct otherwise it may
+ segfault.*/
+ if ((UWord)(ptr_val(*tracer) + offs) % (sizeof(UWord)*2) == sizeof(UWord))
+ offs += 1;
+
+ lop = (ErtsThrPrgrLaterOp*)(ptr_val(*tracer) + offs);
+ ASSERT((UWord)lop % (sizeof(UWord)*2) == 0);
+
+ /* We schedule the free:ing of the tracer until after a thread progress
+ has been made so that we know that no schedulers have any references
+ to it. Because we do this, it is possible to release all locks of a
+ process/port and still use the ErtsTracer of that port/process
+ without having to worry if it is free'd.
+ */
+ erts_schedule_thr_prgr_later_cleanup_op(
+ free_tracer, (void*)(*tracer), lop, size);
+ }
+
+ if (is_nil(new_tracer)) {
+ *tracer = new_tracer;
+ } else if (is_immed(ERTS_TRACER_STATE(new_tracer))) {
+ /* If tracer state is an immediate we only allocate a 2 Eterm heap.
+ Not sure if it is worth it, we save 4 words (sizeof(ErlHeapFragment))
+ per tracer. */
+ Eterm *hp = erts_alloc(ERTS_ALC_T_HEAP_FRAG,
+ 3*sizeof(Eterm) + sizeof(ErtsThrPrgrLaterOp));
+ *tracer = CONS(hp, ERTS_TRACER_MODULE(new_tracer),
+ ERTS_TRACER_STATE(new_tracer));
+ } else {
+ Eterm *hp, tracer_state = ERTS_TRACER_STATE(new_tracer),
+ tracer_module = ERTS_TRACER_MODULE(new_tracer);
+ Uint sz = size_object(tracer_state);
+ hf = new_message_buffer(sz + 2 /* cons cell */ +
+ (sizeof(ErtsThrPrgrLaterOp)+sizeof(Eterm)-1)/sizeof(Eterm) + 1);
+ hp = hf->mem + 2;
+ hf->used_size -= (sizeof(ErtsThrPrgrLaterOp)+sizeof(Eterm)-1)/sizeof(Eterm) + 1;
+ *tracer = copy_struct(tracer_state, sz, &hp, &hf->off_heap);
+ *tracer = CONS(hf->mem, tracer_module, *tracer);
+ ASSERT((void*)(((char*)(ptr_val(*tracer)) - offsetof(ErlHeapFragment, mem))) == hf);
+ }
+}
+
+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_tracer_nif_clear();
+
+}
+
+int erts_tracer_nif_clear()
+{
+
+ erts_smp_rwmtx_rlock(&tracer_mtx);
+ if (!tracer_hash || tracer_hash->nobjs) {
+
+ HashFunctions hf;
+ hf.hash = tracer_hash_fun;
+ hf.cmp = tracer_cmp_fun;
+ hf.alloc = tracer_alloc_fun;
+ hf.free = tracer_free_fun;
+ hf.meta_alloc = (HMALLOC_FUN) erts_alloc;
+ hf.meta_free = (HMFREE_FUN) erts_free;
+ hf.meta_print = (HMPRINT_FUN) erts_print;
+
+ erts_smp_rwmtx_runlock(&tracer_mtx);
+ erts_smp_rwmtx_rwlock(&tracer_mtx);
+
+ if (tracer_hash)
+ hash_delete(tracer_hash);
+
+ tracer_hash = hash_new(ERTS_ALC_T_TRACER_NIF, "tracer_hash", 10, hf);
+
+ erts_smp_rwmtx_rwunlock(&tracer_mtx);
+ return 1;
+ }
+
+ erts_smp_rwmtx_runlock(&tracer_mtx);
+ return 0;
+}
+
+static int tracer_cmp_fun(void* a, void* b)
+{
+ return ((ErtsTracerNif*)a)->module != ((ErtsTracerNif*)b)->module;
+}
+
+static HashValue tracer_hash_fun(void* obj)
+{
+ return make_internal_hash(((ErtsTracerNif*)obj)->module);
+}
+
+static void *tracer_alloc_fun(void* tmpl)
+{
+ ErtsTracerNif *obj = erts_alloc(ERTS_ALC_T_TRACER_NIF,
+ sizeof(ErtsTracerNif) +
+ sizeof(ErtsThrPrgrLaterOp));
+ memcpy(obj, tmpl, sizeof(*obj));
+ return obj;
+}
+
+static void tracer_free_fun_cb(void* obj)
+{
+ erts_free(ERTS_ALC_T_TRACER_NIF, obj);
+}
+
+static void tracer_free_fun(void* obj)
+{
+ ErtsTracerNif *tnif = obj;
+ erts_schedule_thr_prgr_later_op(
+ tracer_free_fun_cb, obj,
+ (ErtsThrPrgrLaterOp*)(tnif + 1));
+
+}
diff --git a/erts/emulator/beam/erl_trace.h b/erts/emulator/beam/erl_trace.h
index a0058264d7..0095d4386b 100644
--- a/erts/emulator/beam/erl_trace.h
+++ b/erts/emulator/beam/erl_trace.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2012-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2012-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -46,6 +46,8 @@
#define ERTS_SEQTFLGS2TSTYPE(SEQTFLGS) \
((int) (((SEQTFLGS) >> ERTS_SEQ_TRACE_FLAGS_TS_TYPE_SHIFT) \
& ERTS_TRACE_TS_TYPE_MASK))
+#define ERTS_SEQTFLGS2TFLGS(SEQTFLGS) \
+ (ERTS_SEQTFLGS2TSTYPE(SEQTFLGS) << ERTS_TRACE_FLAGS_TS_TYPE_SHIFT)
#endif /* ERL_TRACE_H__FLAGS__ */
@@ -54,6 +56,15 @@
struct binary;
+typedef struct
+{
+ int on;
+ struct binary* match_spec;
+} ErtsTracingEvent;
+
+extern ErtsTracingEvent erts_send_tracing[];
+extern ErtsTracingEvent erts_receive_tracing[];
+
/* erl_bif_trace.c */
Eterm erl_seq_trace_info(Process *p, Eterm arg1);
void erts_system_monitor_clear(Process *c_p);
@@ -62,15 +73,19 @@ void erts_system_profile_clear(Process *c_p);
/* erl_trace.c */
void erts_init_trace(void);
void erts_trace_check_exiting(Eterm exiting);
-Eterm erts_set_system_seq_tracer(Process *c_p,
- ErtsProcLocks c_p_locks,
- Eterm new);
-Eterm erts_get_system_seq_tracer(void);
-void erts_change_default_tracing(int setflags, Uint *flagsp, Eterm *tracerp);
-void erts_get_default_tracing(Uint *flagsp, Eterm *tracerp);
+ErtsTracer erts_set_system_seq_tracer(Process *c_p,
+ ErtsProcLocks c_p_locks,
+ ErtsTracer new);
+ErtsTracer erts_get_system_seq_tracer(void);
+void erts_change_default_proc_tracing(int setflags, Uint flagsp,
+ const ErtsTracer tracerp);
+void erts_get_default_proc_tracing(Uint *flagsp, ErtsTracer *tracerp);
+void erts_change_default_port_tracing(int setflags, Uint flagsp,
+ const ErtsTracer tracerp);
+void erts_get_default_port_tracing(Uint *flagsp, ErtsTracer *tracerp);
void erts_set_system_monitor(Eterm monitor);
Eterm erts_get_system_monitor(void);
-int erts_is_tracer_proc_valid(Process* p);
+int erts_is_tracer_valid(Process* p);
#ifdef ERTS_SMP
void erts_check_my_tracer_proc(Process *);
@@ -81,28 +96,32 @@ void erts_foreach_sys_msg_in_q(void (*func)(Eterm,
Eterm,
ErlHeapFragment *));
void erts_queue_error_logger_message(Eterm, Eterm, ErlHeapFragment *);
+void erts_send_sys_msg_proc(Eterm, Eterm, Eterm, ErlHeapFragment *);
#endif
-void erts_send_sys_msg_proc(Eterm, Eterm, Eterm, ErlHeapFragment *);
void trace_send(Process*, Eterm, Eterm);
-void trace_receive(Process*, Eterm);
-Uint32 erts_call_trace(Process *p, BeamInstr mfa[], struct binary *match_spec, Eterm* args,
- int local, Eterm *tracer_pid);
-void erts_trace_return(Process* p, BeamInstr* fi, Eterm retval, Eterm *tracer_pid);
+void trace_receive(Process*, Eterm, Eterm, ErtsTracingEvent*);
+Uint32 erts_call_trace(Process *p, BeamInstr mfa[], struct binary *match_spec,
+ Eterm* args, int local, ErtsTracer *tracer);
+void erts_trace_return(Process* p, BeamInstr* fi, Eterm retval,
+ ErtsTracer *tracer);
void erts_trace_exception(Process* p, BeamInstr mfa[], Eterm class, Eterm value,
- Eterm *tracer);
+ ErtsTracer *tracer);
void erts_trace_return_to(Process *p, BeamInstr *pc);
-void trace_sched(Process*, Eterm);
-void trace_proc(Process*, Process*, Eterm, Eterm);
-void trace_proc_spawn(Process*, Eterm pid, Eterm mod, Eterm func, Eterm args);
+void trace_sched(Process*, ErtsProcLocks, Eterm);
+void trace_proc(Process*, ErtsProcLocks, Process*, Eterm, Eterm);
+void trace_proc_spawn(Process*, Eterm what, Eterm pid, Eterm mod, Eterm func, Eterm args);
void save_calls(Process *p, Export *);
-void trace_gc(Process *p, Eterm what);
+void trace_gc(Process *p, Eterm what, Uint size, Eterm msg);
/* port tracing */
-void trace_virtual_sched(Process*, Eterm);
+void trace_virtual_sched(Process*, ErtsProcLocks, Eterm);
void trace_sched_ports(Port *pp, Eterm);
void trace_sched_ports_where(Port *pp, Eterm, Eterm);
void trace_port(Port *, Eterm what, Eterm data);
void trace_port_open(Port *, Eterm calling_pid, Eterm drv_name);
+void trace_port_receive(Port *, Eterm calling_pid, Eterm tag, ...);
+void trace_port_send(Port *, Eterm to, Eterm msg, int exists);
+void trace_port_send_binary(Port *, Eterm to, Eterm what, char *bin, Sint sz);
/* system_profile */
void erts_set_system_profile(Eterm profile);
@@ -121,7 +140,7 @@ void monitor_large_heap(Process *p);
void monitor_generic(Process *p, Eterm type, Eterm spec);
Uint erts_trace_flag2bit(Eterm flag);
int erts_trace_flags(Eterm List,
- Uint *pMask, Eterm *pTracer, int *pCpuTimestamp);
+ Uint *pMask, ErtsTracer *pTracer, int *pCpuTimestamp);
Eterm erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr *I);
#ifdef ERTS_SMP
@@ -161,15 +180,50 @@ int erts_set_trace_pattern(Process*p, Eterm* mfa, int specified,
struct binary* match_prog_set,
struct binary *meta_match_prog_set,
int on, struct trace_pattern_flags,
- Eterm meta_tracer_pid, int is_blocking);
+ ErtsTracer meta_tracer, int is_blocking);
void
erts_get_default_trace_pattern(int *trace_pattern_is_on,
struct binary **match_spec,
struct binary **meta_match_spec,
struct trace_pattern_flags *trace_pattern_flags,
- Eterm *meta_tracer_pid);
+ ErtsTracer *meta_tracer);
int erts_is_default_trace_enabled(void);
void erts_bif_trace_init(void);
int erts_finish_breakpointing(void);
+/* Nif tracer functions */
+int erts_is_tracer_proc_enabled(Process *c_p, ErtsProcLocks c_p_locks,
+ ErtsPTabElementCommon *t_p);
+int erts_is_tracer_proc_enabled_send(Process* c_p, ErtsProcLocks c_p_locks,
+ ErtsPTabElementCommon *t_p);
+int erts_is_tracer_enabled(const ErtsTracer tracer, ErtsPTabElementCommon *t_p);
+Eterm erts_tracer_to_term(Process *p, ErtsTracer tracer);
+ErtsTracer erts_term_to_tracer(Eterm prefix, Eterm term);
+void erts_tracer_replace(ErtsPTabElementCommon *t_p,
+ const ErtsTracer new_tracer);
+void erts_tracer_update(ErtsTracer *tracer, const ErtsTracer new_tracer);
+int erts_tracer_nif_clear(void);
+
+#define erts_tracer_update(t,n) do { if (*(t) != (n)) erts_tracer_update(t,n); } while(0)
+#define ERTS_TRACER_CLEAR(t) erts_tracer_update(t, erts_tracer_nil)
+
+static const ErtsTracer
+ERTS_DECLARE_DUMMY(erts_tracer_true) = am_true;
+
+static const ErtsTracer
+ERTS_DECLARE_DUMMY(erts_tracer_nil) = NIL;
+
+#define ERTS_TRACER_COMPARE(t1, t2) \
+ (EQ((t1), (t2)))
+
+#define ERTS_TRACER_IS_NIL(t1) ERTS_TRACER_COMPARE(t1, erts_tracer_nil)
+
+#define IS_TRACER_VALID(tracer) \
+ (ERTS_TRACER_COMPARE(tracer,erts_tracer_true) \
+ || ERTS_TRACER_IS_NIL(tracer) \
+ || (is_list(tracer) && is_atom(CAR(list_val(tracer)))))
+
+#define ERTS_TRACER_FROM_ETERM(termp) \
+ ((ErtsTracer*)(termp))
+
#endif /* ERL_TRACE_H__ */
diff --git a/erts/emulator/beam/erl_unicode.c b/erts/emulator/beam/erl_unicode.c
index 36d85b0a22..bd5e1482fb 100644
--- a/erts/emulator/beam/erl_unicode.c
+++ b/erts/emulator/beam/erl_unicode.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -55,7 +55,7 @@ static BIF_RETTYPE finalize_list_to_list(Process *p,
Uint num_processed_bytes,
Uint num_bytes_to_process,
Uint num_resulting_chars,
- int state, int left,
+ int state, Sint left,
Eterm tail);
static BIF_RETTYPE characters_to_utf8_trap(BIF_ALIST_3);
static BIF_RETTYPE characters_to_list_trap_1(BIF_ALIST_3);
@@ -173,12 +173,13 @@ static ERTS_INLINE int allowed_iterations(Process *p)
else
return tmp;
}
-static ERTS_INLINE int cost_to_proc(Process *p, int cost)
+
+static ERTS_INLINE void cost_to_proc(Process *p, Sint cost)
{
- int x = (cost / LOOP_FACTOR);
+ Sint x = (cost / LOOP_FACTOR);
BUMP_REDS(p,x);
- return x;
}
+
static ERTS_INLINE int simple_loops_to_common(int cost)
{
int factor = (LOOP_FACTOR_SIMPLE / LOOP_FACTOR);
@@ -243,14 +244,15 @@ static int utf8_len(byte first)
return -1;
}
-static int copy_utf8_bin(byte *target, byte *source, Uint size,
- byte *leftover, int *num_leftovers,
- byte **err_pos, Uint *characters) {
- int copied = 0;
+static Uint copy_utf8_bin(byte *target, byte *source, Uint size,
+ byte *leftover, int *num_leftovers,
+ byte **err_pos, Uint *characters)
+{
+ Uint copied = 0;
if (leftover != NULL && *num_leftovers) {
int need = utf8_len(leftover[0]);
int from_source = need - (*num_leftovers);
- int c;
+ Uint c;
byte *tmp_err_pos = NULL;
ASSERT(need > 0);
ASSERT(from_source > 0);
@@ -502,8 +504,8 @@ L_Again: /* Restart with sublist, old listend was pushed on stack */
}
-static Eterm do_build_utf8(Process *p, Eterm ioterm, int *left, int latin1,
- byte *target, int *pos, Uint *characters, int *err,
+static Eterm do_build_utf8(Process *p, Eterm ioterm, Sint *left, int latin1,
+ byte *target, Uint *pos, Uint *characters, int *err,
byte *leftover, int *num_leftovers)
{
int c;
@@ -573,7 +575,7 @@ static Eterm do_build_utf8(Process *p, Eterm ioterm, int *left, int latin1,
}
if (!latin1) {
- int num;
+ Uint num;
byte *err_pos = NULL;
num = copy_utf8_bin(target + (*pos), bytes,
size, leftover, num_leftovers,&err_pos,characters);
@@ -804,7 +806,7 @@ static int check_leftovers(byte *source, int size)
-static BIF_RETTYPE build_utf8_return(Process *p,Eterm bin,int pos,
+static BIF_RETTYPE build_utf8_return(Process *p,Eterm bin,Uint pos,
Eterm rest_term,int err,
byte *leftover,int num_leftovers,Eterm latin1)
{
@@ -859,8 +861,8 @@ static BIF_RETTYPE characters_to_utf8_trap(BIF_ALIST_3)
#endif
byte* bytes;
Eterm rest_term;
- int left, sleft;
- int pos;
+ Sint left, sleft;
+ Uint pos;
int err;
byte leftover[4]; /* used for temp buffer too,
otherwise 3 bytes would have been enough */
@@ -874,7 +876,7 @@ static BIF_RETTYPE characters_to_utf8_trap(BIF_ALIST_3)
real_bin = binary_val(BIF_ARG_1);
ASSERT(*real_bin == HEADER_PROC_BIN);
#endif
- pos = (int) binary_size(BIF_ARG_1);
+ pos = binary_size(BIF_ARG_1);
bytes = binary_bytes(BIF_ARG_1);
sleft = left = allowed_iterations(BIF_P);
err = 0;
@@ -934,9 +936,9 @@ BIF_RETTYPE unicode_characters_to_binary_2(BIF_ALIST_2)
int latin1;
Eterm bin;
byte *bytes;
- int pos;
+ Uint pos;
int err;
- int left, sleft;
+ Sint left, sleft;
Eterm rest_term, subject;
byte leftover[4]; /* used for temp buffer too, o
therwise 3 bytes would have been enough */
@@ -999,7 +1001,7 @@ BIF_RETTYPE unicode_characters_to_binary_2(BIF_ALIST_2)
byte *t = NULL;
Uint sz = binary_size(bin);
byte *by = erts_get_aligned_binary_bytes(bin,&t);
- int i;
+ Uint i;
erts_printf("<<");
for (i = 0;i < sz; ++i) {
erts_printf((i == sz -1) ? "0x%X" : "0x%X, ", (unsigned) by[i]);
@@ -1007,7 +1009,7 @@ BIF_RETTYPE unicode_characters_to_binary_2(BIF_ALIST_2)
erts_printf(">>: ");
erts_free_aligned_binary_bytes(t);
}
- erts_printf("%d - %d = %d\n",sleft,left,sleft - left);
+ erts_printf("%ld - %ld = %ld\n", sleft, left, sleft - left);
}
#endif
cost_to_proc(BIF_P, sleft - left);
@@ -1015,10 +1017,10 @@ BIF_RETTYPE unicode_characters_to_binary_2(BIF_ALIST_2)
leftover,num_leftovers,BIF_ARG_2);
}
-static BIF_RETTYPE build_list_return(Process *p, byte *bytes, int pos, Uint characters,
+static BIF_RETTYPE build_list_return(Process *p, byte *bytes, Uint pos, Uint characters,
Eterm rest_term, int err,
byte *leftover, int num_leftovers,
- Eterm latin1, int left)
+ Eterm latin1, Sint left)
{
Eterm *hp;
@@ -1070,11 +1072,11 @@ static BIF_RETTYPE characters_to_list_trap_1(BIF_ALIST_3)
{
RestartContext *rc;
byte* bytes;
- int pos;
+ Uint pos;
Uint characters;
int err;
Eterm rest_term;
- int left, sleft;
+ Sint left, sleft;
int latin1 = 0;
byte leftover[4]; /* used for temp buffer too,
@@ -1107,9 +1109,9 @@ BIF_RETTYPE unicode_characters_to_list_2(BIF_ALIST_2)
int latin1;
Uint characters = 0;
byte *bytes;
- int pos;
+ Uint pos;
int err;
- int left, sleft;
+ Sint left, sleft;
Eterm rest_term;
byte leftover[4]; /* used for temp buffer too, o
therwise 3 bytes would have been enough */
@@ -1541,7 +1543,7 @@ static BIF_RETTYPE finalize_list_to_list(Process *p,
Uint num_processed_bytes,
Uint num_bytes_to_process,
Uint num_resulting_chars,
- int state, int left,
+ int state, Sint left,
Eterm tail)
{
Uint num_built; /* characters */
@@ -2016,7 +2018,7 @@ char *erts_convert_filename_to_encoding(Eterm name, char *statbuf, size_t statbu
++need;
}
if (used)
- *used = (Sint) need;
+ *used = need;
if (need+extra > statbuf_size) {
name_buf = (char *) erts_alloc(alloc_type, need+extra);
} else {
diff --git a/erts/emulator/beam/erl_unicode.h b/erts/emulator/beam/erl_unicode.h
index 4c25d89b7c..e01eaa787e 100644
--- a/erts/emulator/beam/erl_unicode.h
+++ b/erts/emulator/beam/erl_unicode.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erl_unicode_normalize.h b/erts/emulator/beam/erl_unicode_normalize.h
index 16c62db50e..21e2a52544 100644
--- a/erts/emulator/beam/erl_unicode_normalize.h
+++ b/erts/emulator/beam/erl_unicode_normalize.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2010. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h
index 6dab3bf297..81800752f0 100644
--- a/erts/emulator/beam/erl_utils.h
+++ b/erts/emulator/beam/erl_utils.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2012-2014. All Rights Reserved.
+ * Copyright Ericsson AB 2012-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -115,7 +115,7 @@ void erts_silence_warn_unused_result(long unused);
int erts_fit_in_bits_int64(Sint64);
int erts_fit_in_bits_int32(Sint32);
int erts_fit_in_bits_uint(Uint);
-int erts_list_length(Eterm);
+Sint erts_list_length(Eterm);
int erts_is_builtin(Eterm, Eterm, int);
Uint32 make_broken_hash(Eterm);
Uint32 block_hash(byte *, unsigned, Uint32);
@@ -157,51 +157,46 @@ void erts_init_utils_mem(void);
erts_dsprintf_buf_t *erts_create_tmp_dsbuf(Uint);
void erts_destroy_tmp_dsbuf(erts_dsprintf_buf_t *);
-#if HALFWORD_HEAP
-int eq_rel(Eterm a, Eterm* a_base, Eterm b, Eterm* b_base);
-# define eq(A,B) eq_rel(A,NULL,B,NULL)
-#else
int eq(Eterm, Eterm);
-# define eq_rel(A,A_BASE,B,B_BASE) eq(A,B)
-#endif
#define EQ(x,y) (((x) == (y)) || (is_not_both_immed((x),(y)) && eq((x),(y))))
-#if HALFWORD_HEAP
-Sint erts_cmp_rel_opt(Eterm, Eterm*, Eterm, Eterm*, int, int);
-#define cmp_rel(A,A_BASE,B,B_BASE) erts_cmp_rel_opt(A,A_BASE,B,B_BASE,0,0)
-#define cmp_rel_term(A,A_BASE,B,B_BASE) erts_cmp_rel_opt(A,A_BASE,B,B_BASE,1,0)
-#define CMP(A,B) erts_cmp_rel_opt(A,NULL,B,NULL,0,0)
-#define CMP_TERM(A,B) erts_cmp_rel_opt(A,NULL,B,NULL,1,0)
-#define CMP_EQ_ONLY(A,B) erts_cmp_rel_opt(A,NULL,B,NULL,0,1)
-#else
+int erts_cmp_atoms(Eterm a, Eterm b);
Sint erts_cmp(Eterm, Eterm, int, int);
+Sint erts_cmp_compound(Eterm, Eterm, int, int);
Sint cmp(Eterm a, Eterm b);
-#define cmp_rel(A,A_BASE,B,B_BASE) erts_cmp(A,B,0,0)
-#define cmp_rel_term(A,A_BASE,B,B_BASE) erts_cmp(A,B,1,0)
#define CMP(A,B) erts_cmp(A,B,0,0)
#define CMP_TERM(A,B) erts_cmp(A,B,1,0)
#define CMP_EQ_ONLY(A,B) erts_cmp(A,B,0,1)
-#endif
-#define cmp_lt(a,b) (CMP((a),(b)) < 0)
-#define cmp_le(a,b) (CMP((a),(b)) <= 0)
-#define cmp_eq(a,b) (CMP_EQ_ONLY((a),(b)) == 0)
-#define cmp_ne(a,b) (CMP_EQ_ONLY((a),(b)) != 0)
-#define cmp_ge(a,b) (CMP((a),(b)) >= 0)
-#define cmp_gt(a,b) (CMP((a),(b)) > 0)
-
-#define cmp_lt_term(a,b) (CMP_TERM((a),(b)) < 0)
-#define cmp_le_term(a,b) (CMP_TERM((a),(b)) <= 0)
-#define cmp_ge_term(a,b) (CMP_TERM((a),(b)) >= 0)
-#define cmp_gt_term(a,b) (CMP_TERM((a),(b)) > 0)
-
-#define CMP_LT(a,b) ((a) != (b) && cmp_lt((a),(b)))
-#define CMP_GE(a,b) ((a) == (b) || cmp_ge((a),(b)))
-#define CMP_EQ(a,b) ((a) == (b) || cmp_eq((a),(b)))
-#define CMP_NE(a,b) ((a) != (b) && cmp_ne((a),(b)))
-
-#define CMP_LT_TERM(a,b) ((a) != (b) && cmp_lt_term((a),(b)))
-#define CMP_GE_TERM(a,b) ((a) == (b) || cmp_ge_term((a),(b)))
+#define CMP_LT(a,b) ((a) != (b) && CMP((a),(b)) < 0)
+#define CMP_LE(a,b) ((a) == (b) || CMP((a),(b)) <= 0)
+#define CMP_EQ(a,b) ((a) == (b) || CMP_EQ_ONLY((a),(b)) == 0)
+#define CMP_NE(a,b) ((a) != (b) && CMP_EQ_ONLY((a),(b)) != 0)
+#define CMP_GE(a,b) ((a) == (b) || CMP((a),(b)) >= 0)
+#define CMP_GT(a,b) ((a) != (b) && CMP((a),(b)) > 0)
+
+#define CMP_EQ_ACTION(X,Y,Action) \
+ if ((X) != (Y)) { CMP_SPEC((X),(Y),!=,Action,1); }
+#define CMP_NE_ACTION(X,Y,Action) \
+ if ((X) == (Y)) { Action; } else { CMP_SPEC((X),(Y),==,Action,1); }
+#define CMP_GE_ACTION(X,Y,Action) \
+ if ((X) != (Y)) { CMP_SPEC((X),(Y),<,Action,0); }
+#define CMP_LT_ACTION(X,Y,Action) \
+ if ((X) == (Y)) { Action; } else { CMP_SPEC((X),(Y),>=,Action,0); }
+
+#define CMP_SPEC(X,Y,Op,Action,EqOnly) \
+ if (is_atom(X) && is_atom(Y)) { \
+ if (erts_cmp_atoms(X, Y) Op 0) { Action; }; \
+ } else if (is_both_small(X, Y)) { \
+ if (signed_val(X) Op signed_val(Y)) { Action; }; \
+ } else if (is_float(X) && is_float(Y)) { \
+ FloatDef af, bf; \
+ GET_DOUBLE(X, af); \
+ GET_DOUBLE(Y, bf); \
+ if (af.fd Op bf.fd) { Action; }; \
+ } else { \
+ if (erts_cmp_compound(X,Y,0,EqOnly) Op 0) { Action; }; \
+ }
#endif
diff --git a/erts/emulator/beam/erl_vm.h b/erts/emulator/beam/erl_vm.h
index 8948ca2ea9..3d8c647386 100644
--- a/erts/emulator/beam/erl_vm.h
+++ b/erts/emulator/beam/erl_vm.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2014. 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.
@@ -40,14 +40,6 @@
#define MAX_ARG 255 /* Max number of arguments allowed */
#define MAX_REG 1024 /* Max number of x(N) registers used */
-/* Scheduler stores data for temporary heaps if
- !HEAP_ON_C_STACK. Macros (*TmpHeap*) in global.h selects if we put temporary
- heap data on the C stack or if we use the buffers in the scheduler data. */
-#define TMP_HEAP_SIZE 128 /* Number of Eterm in the schedulers
- small heap for transient heap data */
-#define ERL_ARITH_TMP_HEAP_SIZE 4 /* as does erl_arith... */
-#define BEAM_EMU_TMP_HEAP_SIZE 2 /* and beam_emu... */
-
/*
* The new arithmetic operations need some extra X registers in the register array.
* so does the gc_bif's (i_gc_bif3 need 3 extra).
@@ -58,6 +50,7 @@
#define H_DEFAULT_SIZE 233 /* default (heap + stack) min size */
#define VH_DEFAULT_SIZE 32768 /* default virtual (bin) heap min size (words) */
+#define H_DEFAULT_MAX_SIZE 0 /* default max heap size is off */
#define CP_SIZE 1
@@ -117,11 +110,20 @@
#define HeapWordsLeft(p) (HEAP_LIMIT(p) - HEAP_TOP(p))
#if defined(DEBUG) || defined(CHECK_FOR_HOLES)
-#if HALFWORD_HEAP
-# define ERTS_HOLE_MARKER (0xdeadbeef)
-#else
-# define ERTS_HOLE_MARKER (((0xdeadbeef << 24) << 8) | 0xdeadbeef)
-#endif
+
+/*
+ * ERTS_HOLE_MARKER must *not* be mistaken for a valid term
+ * on the heap...
+ */
+# ifdef ARCH_64
+# define ERTS_HOLE_MARKER \
+ make_catch(UWORD_CONSTANT(0xdeadbeaf00000000) >> _TAG_IMMED2_SIZE)
+/* Will (at the time of writing) appear as 0xdeadbeaf0000001b */
+# else
+# define ERTS_HOLE_MARKER \
+ make_catch(UWORD_CONSTANT(0xdead0000) >> _TAG_IMMED2_SIZE)
+/* Will (at the time of writing) appear as 0xdead001b */
+# endif
#endif
/*
@@ -153,6 +155,7 @@
typedef struct op_entry {
char* name; /* Name of instruction. */
Uint32 mask[3]; /* Signature mask. */
+ unsigned involves_r; /* Needs special attention when matching. */
int sz; /* Number of loaded words. */
char* pack; /* Instructions for packing engine. */
char* sign; /* Signature string. */
@@ -171,12 +174,13 @@ extern int num_instructions; /* Number of instruction in opc[]. */
extern int H_MIN_SIZE; /* minimum (heap + stack) */
extern int BIN_VH_MIN_SIZE; /* minimum virtual (bin) heap */
+extern int H_MAX_SIZE; /* maximum (heap + stack) */
+extern int H_MAX_FLAGS; /* maximum heap flags */
extern int erts_atom_table_size;/* Atom table size */
extern int erts_pd_initial_size;/* Initial Process dictionary table size */
#define ORIG_CREATION 0
-#define INTERNAL_CREATION 255
/* macros for extracting bytes from uint16's */
diff --git a/erts/emulator/beam/erl_zlib.c b/erts/emulator/beam/erl_zlib.c
index e44e86c39c..944ff2e35f 100644
--- a/erts/emulator/beam/erl_zlib.c
+++ b/erts/emulator/beam/erl_zlib.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2009-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erl_zlib.h b/erts/emulator/beam/erl_zlib.h
index 2ca1e3735e..c83c6f291f 100644
--- a/erts/emulator/beam/erl_zlib.h
+++ b/erts/emulator/beam/erl_zlib.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2009-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/erlang_dtrace.d b/erts/emulator/beam/erlang_dtrace.d
index c682f76e3a..237889e0f5 100644
--- a/erts/emulator/beam/erlang_dtrace.d
+++ b/erts/emulator/beam/erlang_dtrace.d
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Dustin Sallings, Michal Ptaszek, Scott Lystig Fritchie 2011-2012.
+ * Copyright Dustin Sallings, Michal Ptaszek, Scott Lystig Fritchie 2011-2016.
* All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -700,6 +700,35 @@ provider erlang {
*/
probe efile_drv__return(int, int, char *, int, int, int);
+
+/*
+ * The set of probes called by the erlang tracer nif backend. In order
+ * to receive events on these you both have to enable tracing in erlang
+ * using the trace bifs and also from dtrace/systemtap.
+ */
+
+
+ /**
+ * A trace message of type `event` was triggered by process `p`.
+ *
+ *
+ * @param p the PID (string form) of the process
+ * @param event the event that was triggered (i.e. call or spawn)
+ * @param state the state of the tracer nif as a string
+ * @param arg1 first argument to the trace event
+ * @param arg2 second argument to the trace event
+ */
+ probe trace(char *p, char *event, char *state, char *arg1, char *arg2);
+
+ /**
+ * A sequence trace message of type `label` was triggered.
+ *
+ * @param state the state of the tracer nif as a string
+ * @param label the seq trace label
+ * @param seq_info the seq trace info tuple as a string
+ */
+ probe trace_seq(char *state, char *label, char *seq_info);
+
/*
* NOTE:
* For formatting int64_t arguments within a D script, see:
diff --git a/erts/etc/ose/run_erl.h b/erts/emulator/beam/erlang_lttng.c
index bdc8b6c355..fce40eedc1 100644
--- a/erts/etc/ose/run_erl.h
+++ b/erts/emulator/beam/erlang_lttng.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2013. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,14 +17,16 @@
*
* %CopyrightEnd%
*/
-#ifndef ERL_RUN_ERL_H
-#define ERL_RUN_ERL_H
-
-#include "ose.h"
-
-#include "erts.sig"
-
-int run_erl(int argc, char **argv);
-OS_PROCESS(run_erl_process);
+#ifdef HAVE_CONFIG_H
+# include "config.h"
#endif
+
+#ifdef USE_LTTNG
+#define TRACEPOINT_CREATE_PROBES
+/*
+ * The header containing our TRACEPOINT_EVENTs.
+ */
+#define TRACEPOINT_DEFINE
+#include "erlang_lttng.h"
+#endif /* USE_LTTNG */
diff --git a/erts/emulator/beam/erlang_lttng.h b/erts/emulator/beam/erlang_lttng.h
new file mode 100644
index 0000000000..4e869671f7
--- /dev/null
+++ b/erts/emulator/beam/erlang_lttng.h
@@ -0,0 +1,424 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#ifdef USE_LTTNG
+#undef TRACEPOINT_PROVIDER
+#define TRACEPOINT_PROVIDER org_erlang_otp
+
+#undef TRACEPOINT_INCLUDE
+#define TRACEPOINT_INCLUDE "erlang_lttng.h"
+
+#if !defined(__ERLANG_LTTNG_H__) || defined(TRACEPOINT_HEADER_MULTI_READ)
+#define __ERLANG_LTTNG_H__
+
+#include <lttng/tracepoint.h>
+
+/* Schedulers */
+
+TRACEPOINT_EVENT(
+ org_erlang_otp,
+ scheduler_poll,
+ TP_ARGS(
+ int, id,
+ int, runnable
+ ),
+ TP_FIELDS(
+ ctf_integer(int, scheduler, id)
+ ctf_integer(int, runnable, runnable)
+ )
+)
+
+#ifndef LTTNG_CARRIER_STATS
+#define LTTNG_CARRIER_STATS
+typedef struct {
+ unsigned long no;
+ unsigned long size;
+} lttng_stat_values_t;
+
+typedef struct {
+ lttng_stat_values_t carriers;
+ lttng_stat_values_t blocks;
+} lttng_carrier_stats_t;
+#endif
+
+
+/* Port and Driver Scheduling */
+
+TRACEPOINT_EVENT(
+ org_erlang_otp,
+ driver_start,
+ TP_ARGS(
+ char*, pid,
+ char*, driver,
+ char*, port
+ ),
+ TP_FIELDS(
+ ctf_string(pid, pid)
+ ctf_string(driver, driver)
+ ctf_string(port, port)
+ )
+)
+
+TRACEPOINT_EVENT(
+ org_erlang_otp,
+ driver_init,
+ TP_ARGS(
+ char*, driver,
+ int, major,
+ int, minor,
+ int, flags
+ ),
+ TP_FIELDS(
+ ctf_string(driver, driver)
+ ctf_integer(int, major, major)
+ ctf_integer(int, minor, minor)
+ ctf_integer(int, flags, flags)
+ )
+)
+
+TRACEPOINT_EVENT(
+ org_erlang_otp,
+ driver_outputv,
+ TP_ARGS(
+ char*, pid,
+ char*, port,
+ char*, driver,
+ size_t, bytes
+ ),
+ TP_FIELDS(
+ ctf_string(pid, pid)
+ ctf_string(port, port)
+ ctf_string(driver, driver)
+ ctf_integer(size_t, bytes, bytes)
+ )
+)
+
+TRACEPOINT_EVENT(
+ org_erlang_otp,
+ driver_output,
+ TP_ARGS(
+ char*, pid,
+ char*, port,
+ char*, driver,
+ size_t, bytes
+ ),
+ TP_FIELDS(
+ ctf_string(pid, pid)
+ ctf_string(port, port)
+ ctf_string(driver, driver)
+ ctf_integer(size_t, bytes, bytes)
+ )
+)
+
+TRACEPOINT_EVENT(
+ org_erlang_otp,
+ driver_ready_input,
+ TP_ARGS(
+ char*, pid,
+ char*, port,
+ char*, driver
+ ),
+ TP_FIELDS(
+ ctf_string(pid, pid)
+ ctf_string(port, port)
+ ctf_string(driver, driver)
+ )
+)
+
+TRACEPOINT_EVENT(
+ org_erlang_otp,
+ driver_ready_output,
+ TP_ARGS(
+ char*, pid,
+ char*, port,
+ char*, driver
+ ),
+ TP_FIELDS(
+ ctf_string(pid, pid)
+ ctf_string(port, port)
+ ctf_string(driver, driver)
+ )
+)
+
+TRACEPOINT_EVENT(
+ org_erlang_otp,
+ driver_event,
+ TP_ARGS(
+ char*, pid,
+ char*, port,
+ char*, driver
+ ),
+ TP_FIELDS(
+ ctf_string(pid, pid)
+ ctf_string(port, port)
+ ctf_string(driver, driver)
+ )
+)
+
+TRACEPOINT_EVENT(
+ org_erlang_otp,
+ driver_timeout,
+ TP_ARGS(
+ char*, pid,
+ char*, port,
+ char*, driver
+ ),
+ TP_FIELDS(
+ ctf_string(pid, pid)
+ ctf_string(port, port)
+ ctf_string(driver, driver)
+ )
+)
+
+TRACEPOINT_EVENT(
+ org_erlang_otp,
+ driver_stop_select,
+ TP_ARGS(
+ char*, driver
+ ),
+ TP_FIELDS(
+ ctf_string(driver, driver)
+ )
+)
+
+TRACEPOINT_EVENT(
+ org_erlang_otp,
+ driver_flush,
+ TP_ARGS(
+ char*, pid,
+ char*, port,
+ char*, driver
+ ),
+ TP_FIELDS(
+ ctf_string(pid, pid)
+ ctf_string(port, port)
+ ctf_string(driver, driver)
+ )
+)
+
+TRACEPOINT_EVENT(
+ org_erlang_otp,
+ driver_stop,
+ TP_ARGS(
+ char*, pid,
+ char*, port,
+ char*, driver
+ ),
+ TP_FIELDS(
+ ctf_string(pid, pid)
+ ctf_string(port, port)
+ ctf_string(driver, driver)
+ )
+)
+
+TRACEPOINT_EVENT(
+ org_erlang_otp,
+ driver_process_exit,
+ TP_ARGS(
+ char*, pid,
+ char*, port,
+ char*, driver
+ ),
+ TP_FIELDS(
+ ctf_string(pid, pid)
+ ctf_string(port, port)
+ ctf_string(driver, driver)
+ )
+)
+
+TRACEPOINT_EVENT(
+ org_erlang_otp,
+ driver_ready_async,
+ TP_ARGS(
+ char*, pid,
+ char*, port,
+ char*, driver
+ ),
+ TP_FIELDS(
+ ctf_string(pid, pid)
+ ctf_string(port, port)
+ ctf_string(driver, driver)
+ )
+)
+
+TRACEPOINT_EVENT(
+ org_erlang_otp,
+ driver_finish,
+ TP_ARGS(
+ char*, driver
+ ),
+ TP_FIELDS(
+ ctf_string(driver, driver)
+ )
+)
+
+TRACEPOINT_EVENT(
+ org_erlang_otp,
+ driver_call,
+ TP_ARGS(
+ char*, pid,
+ char*, port,
+ char*, driver,
+ unsigned int, command,
+ size_t, bytes
+ ),
+ TP_FIELDS(
+ ctf_string(pid, pid)
+ ctf_string(port, port)
+ ctf_string(driver, driver)
+ ctf_integer(unsigned int, command, command)
+ ctf_integer(size_t, bytes, bytes)
+ )
+)
+
+TRACEPOINT_EVENT(
+ org_erlang_otp,
+ driver_control,
+ TP_ARGS(
+ char*, pid,
+ char*, port,
+ char*, driver,
+ unsigned int, command,
+ size_t, bytes
+ ),
+ TP_FIELDS(
+ ctf_string(pid, pid)
+ ctf_string(port, port)
+ ctf_string(driver, driver)
+ ctf_integer(unsigned int, command, command)
+ ctf_integer(size_t, bytes, bytes)
+ )
+)
+
+/* Async pool */
+
+TRACEPOINT_EVENT(
+ org_erlang_otp,
+ aio_pool_get,
+ TP_ARGS(
+ char*, port,
+ int, length
+ ),
+ TP_FIELDS(
+ ctf_string(port, port)
+ ctf_integer(int, length, length)
+ )
+)
+
+TRACEPOINT_EVENT(
+ org_erlang_otp,
+ aio_pool_put,
+ TP_ARGS(
+ char*, port,
+ int, length
+ ),
+ TP_FIELDS(
+ ctf_string(port, port)
+ ctf_integer(int, length, length)
+ )
+)
+
+
+/* Memory Allocator */
+
+TRACEPOINT_EVENT(
+ org_erlang_otp,
+ carrier_create,
+ TP_ARGS(
+ const char*, type,
+ int, instance,
+ unsigned long, size,
+ lttng_carrier_stats_t *, mbcs,
+ lttng_carrier_stats_t *, sbcs
+ ),
+ TP_FIELDS(
+ ctf_string(type, type)
+ ctf_integer(int, instance, instance)
+ ctf_integer(unsigned long, size, size)
+ ctf_integer(unsigned long, mbc_carriers, mbcs->carriers.no)
+ ctf_integer(unsigned long, mbc_carriers_size, mbcs->carriers.size)
+ ctf_integer(unsigned long, mbc_blocks, mbcs->blocks.no)
+ ctf_integer(unsigned long, mbc_blocks_size, mbcs->blocks.size)
+ ctf_integer(unsigned long, sbc_carriers, sbcs->carriers.no)
+ ctf_integer(unsigned long, sbc_carriers_size, sbcs->carriers.size)
+ ctf_integer(unsigned long, sbc_blocks, sbcs->blocks.no)
+ ctf_integer(unsigned long, sbc_blocks_size, sbcs->blocks.size)
+ )
+)
+
+
+TRACEPOINT_EVENT(
+ org_erlang_otp,
+ carrier_destroy,
+ TP_ARGS(
+ const char*, type,
+ int, instance,
+ unsigned long, size,
+ lttng_carrier_stats_t *, mbcs,
+ lttng_carrier_stats_t *, sbcs
+ ),
+ TP_FIELDS(
+ ctf_string(type, type)
+ ctf_integer(int, instance, instance)
+ ctf_integer(unsigned long, size, size)
+ ctf_integer(unsigned long, mbc_carriers, mbcs->carriers.no)
+ ctf_integer(unsigned long, mbc_carriers_size, mbcs->carriers.size)
+ ctf_integer(unsigned long, mbc_blocks, mbcs->blocks.no)
+ ctf_integer(unsigned long, mbc_blocks_size, mbcs->blocks.size)
+ ctf_integer(unsigned long, sbc_carriers, sbcs->carriers.no)
+ ctf_integer(unsigned long, sbc_carriers_size, sbcs->carriers.size)
+ ctf_integer(unsigned long, sbc_blocks, sbcs->blocks.no)
+ ctf_integer(unsigned long, sbc_blocks_size, sbcs->blocks.size)
+ )
+)
+
+TRACEPOINT_EVENT(
+ org_erlang_otp,
+ carrier_pool_put,
+ TP_ARGS(
+ const char*, name,
+ int, instance,
+ unsigned long, size
+ ),
+ TP_FIELDS(
+ ctf_string(type, name)
+ ctf_integer(int, instance, instance)
+ ctf_integer(unsigned long, size, size)
+ )
+)
+
+TRACEPOINT_EVENT(
+ org_erlang_otp,
+ carrier_pool_get,
+ TP_ARGS(
+ const char*, name,
+ int, instance,
+ unsigned long, size
+ ),
+ TP_FIELDS(
+ ctf_string(type, name)
+ ctf_integer(int, instance, instance)
+ ctf_integer(unsigned long, size, size)
+ )
+)
+
+#endif /* __ERLANG_LTTNG_H__ */
+#include <lttng/tracepoint-event.h>
+#endif /* USE_LTTNG */
diff --git a/erts/emulator/beam/error.h b/erts/emulator/beam/error.h
index cba8672c68..6c33b12dd0 100644
--- a/erts/emulator/beam/error.h
+++ b/erts/emulator/beam/error.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2010. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/export.c b/erts/emulator/beam/export.c
index 2420df36b5..6cf6fed31a 100644
--- a/erts/emulator/beam/export.c
+++ b/erts/emulator/beam/export.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2013. 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.
@@ -31,7 +31,7 @@
#define EXPORT_INITIAL_SIZE 4000
#define EXPORT_LIMIT (512*1024)
-#define EXPORT_HASH(m,f,a) ((m)*(f)+(a))
+#define EXPORT_HASH(m,f,a) ((atom_val(m) * atom_val(f)) ^ (a))
#ifdef DEBUG
# define IF_DEBUG(x) x
@@ -79,12 +79,11 @@ struct export_templ
static struct export_blob* entry_to_blob(struct export_entry* ee)
{
- return (struct export_blob*)
- ((char*)ee->ep - offsetof(struct export_blob,exp));
+ return ErtsContainerStruct(ee->ep, struct export_blob, exp);
}
void
-export_info(int to, void *to_arg)
+export_info(fmtfn_t to, void *to_arg)
{
#ifdef ERTS_SMP
int lock = !ERTS_IS_CRASH_DUMPING;
@@ -184,6 +183,9 @@ init_export_table(void)
f.cmp = (HCMP_FUN) export_cmp;
f.alloc = (HALLOC_FUN) export_alloc;
f.free = (HFREE_FUN) export_free;
+ f.meta_alloc = (HMALLOC_FUN) erts_alloc;
+ f.meta_free = (HMFREE_FUN) erts_free;
+ f.meta_print = (HMPRINT_FUN) erts_print;
for (i=0; i<ERTS_NUM_CODE_IX; i++) {
erts_index_init(ERTS_ALC_T_EXPORT_TABLE, &export_tables[i], "export_list",
@@ -346,7 +348,7 @@ Export *export_list(int i, ErtsCodeIndex code_ix)
int export_list_size(ErtsCodeIndex code_ix)
{
- return export_tables[code_ix].entries;
+ return erts_index_num_entries(&export_tables[code_ix]);
}
int export_table_sz(void)
diff --git a/erts/emulator/beam/export.h b/erts/emulator/beam/export.h
index a8bc9d2f66..17fc4828ca 100644
--- a/erts/emulator/beam/export.h
+++ b/erts/emulator/beam/export.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,14 +21,8 @@
#ifndef __EXPORT_H__
#define __EXPORT_H__
-#ifndef __SYS_H__
#include "sys.h"
-#endif
-
-#ifndef __INDEX_H__
#include "index.h"
-#endif
-
#include "code_ix.h"
/*
@@ -59,7 +53,7 @@ typedef struct export
void init_export_table(void);
-void export_info(int, void *);
+void export_info(fmtfn_t, void *);
ERTS_GLB_INLINE Export* erts_active_export_entry(Eterm m, Eterm f, unsigned a);
Export* erts_export_put(Eterm mod, Eterm func, unsigned int arity);
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index ffe3303796..9436259011 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2014. 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.
@@ -51,7 +51,18 @@
#define MAX_STRING_LEN 0xffff
-#define is_valid_creation(Cre) ((unsigned)(Cre) < MAX_CREATION || (Cre) == INTERNAL_CREATION)
+/* MAX value for the creation field in pid, port and reference
+ for the local node and for the current external format.
+
+ Larger creation values than this are allowed in external pid, port and refs
+ encoded with NEW_PID_EXT, NEW_PORT_EXT and NEWER_REFERENCE_EXT.
+ The point here is to prepare for future upgrade to 32-bit creation.
+ OTP-19 (erts-8.0) can handle big creation values from other (newer) nodes,
+ but do not use big creation values for the local node yet,
+ as we still may have to communicate with older nodes.
+*/
+#define ERTS_MAX_LOCAL_CREATION (3)
+#define is_valid_creation(Cre) ((unsigned)(Cre) <= ERTS_MAX_LOCAL_CREATION)
#undef ERTS_DEBUG_USE_DIST_SEP
#ifdef DEBUG
@@ -97,7 +108,7 @@ static byte* enc_pid(ErtsAtomCacheMap *, Eterm, byte*, Uint32);
struct B2TContext_t;
static byte* dec_term(ErtsDistExternal*, ErtsHeapFactory*, byte*, Eterm*, struct B2TContext_t*);
static byte* dec_atom(ErtsDistExternal *, byte*, Eterm*);
-static byte* dec_pid(ErtsDistExternal *, ErtsHeapFactory*, byte*, Eterm*);
+static byte* dec_pid(ErtsDistExternal *, ErtsHeapFactory*, byte*, Eterm*, byte tag);
static Sint decoded_size(byte *ep, byte* endp, int internal_tags, struct B2TContext_t*);
static BIF_RETTYPE term_to_binary_trap_1(BIF_ALIST_1);
@@ -605,7 +616,7 @@ erts_make_dist_ext_copy(ErtsDistExternal *edep, Uint xsize)
sys_memcpy((void *) ep, (void *) edep, dist_ext_sz);
ep += dist_ext_sz;
if (new_edep->dep)
- erts_refc_inc(&new_edep->dep->refc, 1);
+ erts_smp_refc_inc(&new_edep->dep->refc, 1);
new_edep->extp = ep;
new_edep->ext_endp = ep + ext_sz;
new_edep->heap_size = -1;
@@ -967,19 +978,24 @@ erts_decode_dist_ext(ErtsHeapFactory* factory,
return THE_NON_VALUE;
}
-Eterm erts_decode_ext(ErtsHeapFactory* factory, byte **ext)
+Eterm erts_decode_ext(ErtsHeapFactory* factory, byte **ext, Uint32 flags)
{
+ ErtsDistExternal ede, *edep;
Eterm obj;
byte *ep = *ext;
if (*ep++ != VERSION_MAGIC) {
erts_factory_undo(factory);
return THE_NON_VALUE;
}
- ep = dec_term(NULL, factory, ep, &obj, NULL);
+ if (flags) {
+ ASSERT(flags == ERTS_DIST_EXT_BTT_SAFE);
+ ede.flags = flags; /* a dummy struct just for the flags */
+ edep = &ede;
+ } else {
+ edep = NULL;
+ }
+ ep = dec_term(edep, factory, ep, &obj, NULL);
if (!ep) {
-#ifdef DEBUG
- bin_write(ERTS_PRINT_STDERR,NULL,*ext,500);
-#endif
return THE_NON_VALUE;
}
*ext = ep;
@@ -1206,6 +1222,7 @@ typedef struct B2TContext_t {
} u;
} B2TContext;
+static B2TContext* b2t_export_context(Process*, B2TContext* src);
static uLongf binary2term_uncomp_size(byte* data, Sint size)
{
@@ -1238,7 +1255,7 @@ static uLongf binary2term_uncomp_size(byte* data, Sint size)
static ERTS_INLINE int
binary2term_prepare(ErtsBinary2TermState *state, byte *data, Sint data_size,
- B2TContext* ctx)
+ B2TContext** ctxp, Process* p)
{
byte *bytes = data;
Sint size = data_size;
@@ -1252,8 +1269,8 @@ binary2term_prepare(ErtsBinary2TermState *state, byte *data, Sint data_size,
size--;
if (size < 5 || *bytes != COMPRESSED) {
state->extp = bytes;
- if (ctx)
- ctx->state = B2TSizeInit;
+ if (ctxp)
+ (*ctxp)->state = B2TSizeInit;
}
else {
uLongf dest_len = (Uint32) get_int32(bytes+1);
@@ -1270,16 +1287,26 @@ binary2term_prepare(ErtsBinary2TermState *state, byte *data, Sint data_size,
return -1;
}
state->extp = erts_alloc(ERTS_ALC_T_EXT_TERM_DATA, dest_len);
- ctx->reds -= dest_len;
+ if (ctxp)
+ (*ctxp)->reds -= dest_len;
}
state->exttmp = 1;
- if (ctx) {
+ if (ctxp) {
+ /*
+ * Start decompression by exporting trap context
+ * so we don't have to deal with deep-copying z_stream.
+ */
+ B2TContext* ctx = b2t_export_context(p, *ctxp);
+ ASSERT(state = &(*ctxp)->b2ts);
+ state = &ctx->b2ts;
+
if (erl_zlib_inflate_start(&ctx->u.uc.stream, bytes, size) != Z_OK)
return -1;
ctx->u.uc.dbytes = state->extp;
ctx->u.uc.dleft = dest_len;
ctx->state = B2TUncompressChunk;
+ *ctxp = ctx;
}
else {
uLongf dlen = dest_len;
@@ -1323,7 +1350,7 @@ erts_binary2term_prepare(ErtsBinary2TermState *state, byte *data, Sint data_size
{
Sint res;
- if (binary2term_prepare(state, data, data_size, NULL) < 0 ||
+ if (binary2term_prepare(state, data, data_size, NULL, NULL) < 0 ||
(res=decoded_size(state->extp, state->extp + state->extsize, 0, NULL)) < 0) {
if (state->exttmp)
@@ -1469,7 +1496,7 @@ static BIF_RETTYPE binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binar
if (ctx->aligned_alloc) {
ctx->reds -= bin_size / 8;
}
- if (binary2term_prepare(&ctx->b2ts, bytes, bin_size, ctx) < 0) {
+ if (binary2term_prepare(&ctx->b2ts, bytes, bin_size, &ctx, p) < 0) {
ctx->state = B2TBadArg;
}
break;
@@ -2143,16 +2170,28 @@ enc_atom(ErtsAtomCacheMap *acmp, Eterm atom, byte *ep, Uint32 dflags)
return ep;
}
+/*
+ * We use this atom as sysname in local pid/port/refs
+ * for the ETS compressed format (DFLAG_INTERNAL_TAGS).
+ *
+ * We used atom '' earlier but that turned out to cause problems
+ * for buggy erl_interface/ic usage of c-nodes with empty node names.
+ * A long atom reduces risk of nodes actually called this and the length
+ * does not matter anyway as it's encoded with atom index (ATOM_INTERNAL_REF2).
+ */
+#define INTERNAL_LOCAL_SYSNAME am_await_microstate_accounting_modifications
+
static byte*
enc_pid(ErtsAtomCacheMap *acmp, Eterm pid, byte* ep, Uint32 dflags)
{
Uint on, os;
+ Eterm sysname = ((is_internal_pid(pid) && (dflags & DFLAG_INTERNAL_TAGS))
+ ? INTERNAL_LOCAL_SYSNAME : pid_node_name(pid));
+ Uint32 creation = pid_creation(pid);
+ byte* tagp = ep++;
- *ep++ = PID_EXT;
/* insert atom here containing host and sysname */
- ep = enc_atom(acmp, pid_node_name(pid), ep, dflags);
-
- /* two bytes for each number and serial */
+ ep = enc_atom(acmp, sysname, ep, dflags);
on = pid_number(pid);
os = pid_serial(pid);
@@ -2161,8 +2200,15 @@ enc_pid(ErtsAtomCacheMap *acmp, Eterm pid, byte* ep, Uint32 dflags)
ep += 4;
put_int32(os, ep);
ep += 4;
- *ep++ = (is_internal_pid(pid) && (dflags & DFLAG_INTERNAL_TAGS)) ?
- INTERNAL_CREATION : pid_creation(pid);
+ if (creation <= ERTS_MAX_LOCAL_CREATION) {
+ *tagp = PID_EXT;
+ *ep++ = creation;
+ } else {
+ ASSERT(is_external_pid(pid));
+ *tagp = NEW_PID_EXT;
+ put_int32(creation, ep);
+ ep += 4;
+ }
return ep;
}
@@ -2242,27 +2288,27 @@ dec_atom(ErtsDistExternal *edep, byte* ep, Eterm* objp)
return ep;
}
-static ERTS_INLINE ErlNode* dec_get_node(Eterm sysname, Uint creation)
+static ERTS_INLINE ErlNode* dec_get_node(Eterm sysname, Uint32 creation)
{
- switch (creation) {
- case INTERNAL_CREATION:
+ if (sysname == INTERNAL_LOCAL_SYSNAME) /* && DFLAG_INTERNAL_TAGS */
return erts_this_node;
- case ORIG_CREATION:
- if (sysname == erts_this_node->sysname) {
- creation = erts_this_node->creation;
- }
- }
+
+ if (sysname == erts_this_node->sysname
+ && (creation == erts_this_node->creation || creation == ORIG_CREATION))
+ return erts_this_node;
+
return erts_find_or_insert_node(sysname,creation);
}
static byte*
-dec_pid(ErtsDistExternal *edep, ErtsHeapFactory* factory, byte* ep, Eterm* objp)
+dec_pid(ErtsDistExternal *edep, ErtsHeapFactory* factory, byte* ep,
+ Eterm* objp, byte tag)
{
Eterm sysname;
Uint data;
Uint num;
Uint ser;
- Uint cre;
+ Uint32 cre;
ErlNode *node;
*objp = NIL; /* In case we fail, don't leave a hole in the heap */
@@ -2278,12 +2324,19 @@ dec_pid(ErtsDistExternal *edep, ErtsHeapFactory* factory, byte* ep, Eterm* objp)
ep += 4;
if (ser > ERTS_MAX_PID_SERIAL)
return NULL;
- cre = get_int8(ep);
- ep += 1;
- if (!is_valid_creation(cre)) {
- return NULL;
+ if (tag == PID_EXT) {
+ cre = get_int8(ep);
+ ep += 1;
+ if (!is_valid_creation(cre)) {
+ return NULL;
+ }
+ } else {
+ ASSERT(tag == NEW_PID_EXT);
+ cre = get_int32(ep);
+ ep += 4;
}
+
data = make_pid_data(ser, num);
/*
@@ -2339,10 +2392,6 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
Eterm val;
FloatDef f;
Sint r = 0;
-#if HALFWORD_HEAP
- UWord wobj;
-#endif
-
if (ctx) {
WSTACK_CHANGE_ALLOCATOR(s, ERTS_ALC_T_SAVED_ESTACK);
@@ -2362,11 +2411,8 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
outer_loop:
while (!WSTACK_ISEMPTY(s)) {
-#if HALFWORD_HEAP
- obj = (Eterm) (wobj = WSTACK_POP(s));
-#else
obj = WSTACK_POP(s);
-#endif
+
switch (val = WSTACK_POP(s)) {
case ENC_TERM:
break;
@@ -2384,11 +2430,7 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
break;
case ENC_PATCH_FUN_SIZE:
{
-#if HALFWORD_HEAP
- byte* size_p = (byte *) wobj;
-#else
byte* size_p = (byte *) obj;
-#endif
put_int32(ep - size_p, size_p);
}
goto outer_loop;
@@ -2435,21 +2477,13 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
case ENC_LAST_ARRAY_ELEMENT:
/* obj is the tuple */
{
-#if HALFWORD_HEAP
- Eterm* ptr = (Eterm *) wobj;
-#else
Eterm* ptr = (Eterm *) obj;
-#endif
obj = *ptr;
}
break;
default: /* ENC_LAST_ARRAY_ELEMENT+1 and upwards */
{
-#if HALFWORD_HEAP
- Eterm* ptr = (Eterm *) wobj;
-#else
Eterm* ptr = (Eterm *) obj;
-#endif
obj = *ptr++;
WSTACK_PUSH2(s, val-1, (UWord)ptr);
}
@@ -2542,16 +2576,26 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
case REF_DEF:
case EXTERNAL_REF_DEF: {
Uint32 *ref_num;
+ Eterm sysname = (((dflags & DFLAG_INTERNAL_TAGS) && is_internal_ref(obj))
+ ? INTERNAL_LOCAL_SYSNAME : ref_node_name(obj));
+ Uint32 creation = ref_creation(obj);
+ byte* tagp = ep++;
ASSERT(dflags & DFLAG_EXTENDED_REFERENCES);
- *ep++ = NEW_REFERENCE_EXT;
i = ref_no_of_numbers(obj);
put_int16(i, ep);
ep += 2;
- ep = enc_atom(acmp,ref_node_name(obj),ep,dflags);
- *ep++ = ((dflags & DFLAG_INTERNAL_TAGS) && is_internal_ref(obj)) ?
- INTERNAL_CREATION : ref_creation(obj);
+ ep = enc_atom(acmp, sysname, ep, dflags);
+ if (creation <= ERTS_MAX_LOCAL_CREATION) {
+ *tagp = NEW_REFERENCE_EXT;
+ *ep++ = creation;
+ } else {
+ ASSERT(is_external_ref(obj));
+ *tagp = NEWER_REFERENCE_EXT;
+ put_int32(creation, ep);
+ ep += 4;
+ }
ref_num = ref_numbers(obj);
for (j = 0; j < i; j++) {
put_int32(ref_num[j], ep);
@@ -2560,17 +2604,27 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
break;
}
case PORT_DEF:
- case EXTERNAL_PORT_DEF:
+ case EXTERNAL_PORT_DEF: {
+ Eterm sysname = (((dflags & DFLAG_INTERNAL_TAGS) && is_internal_port(obj))
+ ? INTERNAL_LOCAL_SYSNAME : port_node_name(obj));
+ Uint32 creation = port_creation(obj);
+ byte* tagp = ep++;
- *ep++ = PORT_EXT;
- ep = enc_atom(acmp,port_node_name(obj),ep,dflags);
+ ep = enc_atom(acmp, sysname, ep, dflags);
j = port_number(obj);
put_int32(j, ep);
ep += 4;
- *ep++ = ((dflags & DFLAG_INTERNAL_TAGS) && is_internal_port(obj)) ?
- INTERNAL_CREATION : port_creation(obj);
+ if (creation <= ERTS_MAX_LOCAL_CREATION) {
+ *tagp = PORT_EXT;
+ *ep++ = creation;
+ } else {
+ ASSERT(is_external_port(obj));
+ *tagp = NEW_PORT_EXT;
+ put_int32(creation, ep);
+ ep += 4;
+ }
break;
-
+ }
case LIST_DEF:
{
int is_str;
@@ -2976,7 +3030,7 @@ dec_term(ErtsDistExternal *edep,
case B2TDecodeList:
objp = next - 2;
while (n > 0) {
- objp[0] = (Eterm) COMPRESS_POINTER(next);
+ objp[0] = (Eterm) next;
objp[1] = make_list(next);
next = objp;
objp -= 2;
@@ -2987,7 +3041,7 @@ dec_term(ErtsDistExternal *edep,
case B2TDecodeTuple:
objp = next - 1;
while (n-- > 0) {
- objp[0] = (Eterm) COMPRESS_POINTER(next);
+ objp[0] = (Eterm) next;
next = objp;
objp--;
}
@@ -3043,7 +3097,7 @@ dec_term(ErtsDistExternal *edep,
while (next != NULL) {
objp = next;
- next = (Eterm *) EXPAND_POINTER(*objp);
+ next = (Eterm *) *objp;
switch (*ep++) {
case INTEGER_EXT:
@@ -3051,7 +3105,7 @@ dec_term(ErtsDistExternal *edep,
Sint sn = get_int32(ep);
ep += 4;
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
*objp = make_small(sn);
#else
if (MY_IS_SSMALL(sn)) {
@@ -3174,7 +3228,7 @@ dec_term_atom_common:
reds -= n;
}
while (n-- > 0) {
- objp[0] = (Eterm) COMPRESS_POINTER(next);
+ objp[0] = (Eterm) next;
next = objp;
objp--;
}
@@ -3192,8 +3246,8 @@ dec_term_atom_common:
*objp = make_list(hp);
hp += 2 * n;
objp = hp - 2;
- objp[0] = (Eterm) COMPRESS_POINTER((objp+1));
- objp[1] = (Eterm) COMPRESS_POINTER(next);
+ objp[0] = (Eterm) (objp+1);
+ objp[1] = (Eterm) next;
next = objp;
objp -= 2;
n--;
@@ -3206,7 +3260,7 @@ dec_term_atom_common:
reds -= n;
}
while (n > 0) {
- objp[0] = (Eterm) COMPRESS_POINTER(next);
+ objp[0] = (Eterm) next;
objp[1] = make_list(next);
next = objp;
objp -= 2;
@@ -3274,20 +3328,23 @@ dec_term_atom_common:
hp += FLOAT_SIZE_OBJECT;
break;
}
- case PID_EXT:
+ case PID_EXT:
+ case NEW_PID_EXT:
factory->hp = hp;
- ep = dec_pid(edep, factory, ep, objp);
+ ep = dec_pid(edep, factory, ep, objp, ep[-1]);
hp = factory->hp;
if (ep == NULL) {
goto error;
}
break;
- case PORT_EXT:
+ case PORT_EXT:
+ case NEW_PORT_EXT:
{
Eterm sysname;
ErlNode *node;
Uint num;
- Uint cre;
+ Uint32 cre;
+ byte tag = ep[-1];
if ((ep = dec_atom(edep, ep, &sysname)) == NULL) {
goto error;
@@ -3296,12 +3353,17 @@ dec_term_atom_common:
goto error;
}
ep += 4;
- cre = get_int8(ep);
- ep++;
- if (!is_valid_creation(cre)) {
- goto error;
- }
-
+ if (tag == PORT_EXT) {
+ cre = get_int8(ep);
+ ep++;
+ if (!is_valid_creation(cre)) {
+ goto error;
+ }
+ }
+ else {
+ cre = get_int32(ep);
+ ep += 4;
+ }
node = dec_get_node(sysname, cre);
if(node == erts_this_node) {
*objp = make_internal_port(num);
@@ -3326,7 +3388,7 @@ dec_term_atom_common:
Eterm sysname;
ErlNode *node;
int i;
- Uint cre;
+ Uint32 cre;
Uint32 *ref_num;
Uint32 r0;
Uint ref_words;
@@ -3350,9 +3412,6 @@ dec_term_atom_common:
ref_words = get_int16(ep);
ep += 2;
- if (ref_words > ERTS_MAX_REF_NUMBERS)
- goto error;
-
if ((ep = dec_atom(edep, ep, &sysname)) == NULL)
goto error;
@@ -3365,15 +3424,30 @@ dec_term_atom_common:
ep += 4;
if (r0 >= MAX_REFERENCE)
goto error;
+ goto ref_ext_common;
+
+ case NEWER_REFERENCE_EXT:
+ ref_words = get_int16(ep);
+ ep += 2;
+
+ if ((ep = dec_atom(edep, ep, &sysname)) == NULL)
+ goto error;
+
+ cre = get_int32(ep);
+ ep += 4;
+ r0 = get_int32(ep); /* allow full word */
+ ep += 4;
ref_ext_common:
+ if (ref_words > ERTS_MAX_REF_NUMBERS)
+ goto error;
node = dec_get_node(sysname, cre);
if(node == erts_this_node) {
RefThing *rtp = (RefThing *) hp;
ref_num = (Uint32 *) (hp + REF_THING_HEAD_SIZE);
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
hp += REF_THING_HEAD_SIZE + ref_words/2 + 1;
rtp->header = make_ref_thing_header(ref_words/2 + 1);
#else
@@ -3384,13 +3458,13 @@ dec_term_atom_common:
}
else {
ExternalThing *etp = (ExternalThing *) hp;
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
hp += EXTERNAL_THING_HEAD_SIZE + ref_words/2 + 1;
#else
hp += EXTERNAL_THING_HEAD_SIZE + ref_words;
#endif
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
etp->header = make_external_ref_header(ref_words/2 + 1);
#else
etp->header = make_external_ref_header(ref_words);
@@ -3403,7 +3477,7 @@ dec_term_atom_common:
ref_num = &(etp->data.ui32[0]);
}
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
*(ref_num++) = ref_words /* 32-bit arity */;
#endif
ref_num[0] = r0;
@@ -3411,7 +3485,7 @@ dec_term_atom_common:
ref_num[i] = get_int32(ep);
ep += 4;
}
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
if ((1 + ref_words) % 2)
ref_num[ref_words] = 0;
#endif
@@ -3563,12 +3637,7 @@ dec_term_atom_common:
}
*objp = make_export(hp);
*hp++ = HEADER_EXPORT;
-#if HALFWORD_HEAP
- *((UWord *) (UWord) hp) = (UWord) erts_export_get_or_make_stub(mod, name, arity);
- hp += 2;
-#else
*hp++ = (Eterm) erts_export_get_or_make_stub(mod, name, arity);
-#endif
break;
}
break;
@@ -3604,8 +3673,8 @@ dec_term_atom_common:
*objp = make_flatmap(mp);
for (n = size; n; n--) {
- *vptr = (Eterm) COMPRESS_POINTER(next);
- *kptr = (Eterm) COMPRESS_POINTER(vptr);
+ *vptr = (Eterm) next;
+ *kptr = (Eterm) vptr;
next = kptr;
vptr--;
kptr--;
@@ -3619,8 +3688,8 @@ dec_term_atom_common:
hamt->leaf_array = hp;
for (n = size; n; n--) {
- CDR(hp) = (Eterm) COMPRESS_POINTER(next);
- CAR(hp) = (Eterm) COMPRESS_POINTER(&CDR(hp));
+ CDR(hp) = (Eterm) next;
+ CAR(hp) = (Eterm) &CDR(hp);
next = &CAR(hp);
hp += 2;
}
@@ -3697,11 +3766,11 @@ dec_term_atom_common:
/* Environment */
for (i = num_free-1; i >= 0; i--) {
- funp->env[i] = (Eterm) COMPRESS_POINTER(next);
+ funp->env[i] = (Eterm) next;
next = funp->env + i;
}
/* Creator */
- funp->creator = (Eterm) COMPRESS_POINTER(next);
+ funp->creator = (Eterm) next;
next = &(funp->creator);
break;
}
@@ -3725,9 +3794,9 @@ dec_term_atom_common:
*objp = make_fun(funp);
/* Creator pid */
- if (*ep != PID_EXT
- || (ep = dec_pid(edep, factory, ++ep,
- &funp->creator))==NULL) {
+ if ((*ep != PID_EXT && *ep != NEW_PID_EXT)
+ || (ep = dec_pid(edep, factory, ep+1,
+ &funp->creator, *ep))==NULL) {
goto error;
}
@@ -3770,7 +3839,7 @@ dec_term_atom_common:
/* Environment */
for (i = num_free-1; i >= 0; i--) {
- funp->env[i] = (Eterm) COMPRESS_POINTER(next);
+ funp->env[i] = (Eterm) next;
next = funp->env + i;
}
break;
@@ -3897,7 +3966,7 @@ dec_term_atom_common:
}
WSTACK_DESTROY(flat_maps);
- ASSERT((Eterm*)EXPAND_POINTER(*dbg_resultp) != NULL);
+ ASSERT((Eterm*)*dbg_resultp != NULL);
if (ctx) {
ctx->state = B2TDone;
@@ -3911,9 +3980,13 @@ error:
* Must unlink all off-heap objects that may have been
* linked into the process.
*/
- if (factory->hp < hp) { /* Sometimes we used hp and sometimes factory->hp */
- factory->hp = hp; /* the largest must be the freshest */
+ if (factory->mode != FACTORY_CLOSED) {
+ if (factory->hp < hp) { /* Sometimes we used hp and sometimes factory->hp */
+ factory->hp = hp; /* the largest must be the freshest */
+ }
}
+ else ASSERT(factory->hp == hp);
+
error_hamt:
erts_factory_undo(factory);
PSTACK_DESTROY(hamt_array);
@@ -4029,20 +4102,29 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
else
result += 1 + 4 + 1 + i; /* tag,size,sign,digits */
break;
+ case EXTERNAL_PID_DEF:
+ if (external_pid_creation(obj) > ERTS_MAX_LOCAL_CREATION)
+ result += 3;
+ /*fall through*/
case PID_DEF:
- case EXTERNAL_PID_DEF:
result += (1 + encode_size_struct2(acmp, pid_node_name(obj), dflags) +
4 + 4 + 1);
break;
+ case EXTERNAL_REF_DEF:
+ if (external_ref_creation(obj) > ERTS_MAX_LOCAL_CREATION)
+ result += 3;
+ /*fall through*/
case REF_DEF:
- case EXTERNAL_REF_DEF:
ASSERT(dflags & DFLAG_EXTENDED_REFERENCES);
i = ref_no_of_numbers(obj);
result += (1 + 2 + encode_size_struct2(acmp, ref_node_name(obj), dflags) +
1 + 4*i);
break;
- case PORT_DEF:
- case EXTERNAL_PORT_DEF:
+ case EXTERNAL_PORT_DEF:
+ if (external_port_creation(obj) > ERTS_MAX_LOCAL_CREATION)
+ result += 3;
+ /*fall through*/
+ case PORT_DEF:
result += (1 + encode_size_struct2(acmp, port_node_name(obj), dflags) +
4 + 1);
break;
@@ -4190,11 +4272,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
case EXPORT_DEF:
{
Export* ep = *((Export **) (export_val(obj) + 1));
-#if HALFWORD_HEAP
- result += 2;
-#else
result += 1;
-#endif
result += encode_size_struct2(acmp, ep->code[0], dflags);
result += encode_size_struct2(acmp, ep->code[1], dflags);
result += encode_size_struct2(acmp, make_small(ep->code[2]), dflags);
@@ -4309,7 +4387,7 @@ init_done:
switch (tag) {
case INTEGER_EXT:
SKIP(4);
-#if !defined(ARCH_64) || HALFWORD_HEAP
+#if !defined(ARCH_64)
heap_size += BIG_UINT_HEAP_SIZE;
#endif
break;
@@ -4373,19 +4451,32 @@ init_done:
SKIP(1+atom_extra_skip);
atom_extra_skip = 0;
break;
- case PID_EXT:
+ case NEW_PID_EXT:
+ atom_extra_skip = 12;
+ goto case_PID;
+ case PID_EXT:
atom_extra_skip = 9;
+ case_PID:
/* In case it is an external pid */
heap_size += EXTERNAL_THING_HEAD_SIZE + 1;
terms++;
break;
- case PORT_EXT:
+ case NEW_PORT_EXT:
+ atom_extra_skip = 8;
+ goto case_PORT;
+ case PORT_EXT:
atom_extra_skip = 5;
+ case_PORT:
/* In case it is an external port */
heap_size += EXTERNAL_THING_HEAD_SIZE + 1;
terms++;
break;
- case NEW_REFERENCE_EXT:
+ case NEWER_REFERENCE_EXT:
+ atom_extra_skip = 4;
+ goto case_NEW_REFERENCE;
+ case NEW_REFERENCE_EXT:
+ atom_extra_skip = 1;
+ case_NEW_REFERENCE:
{
int id_words;
@@ -4396,9 +4487,9 @@ init_done:
goto error;
ep += 2;
- atom_extra_skip = 1 + 4*id_words;
+ atom_extra_skip += 4*id_words;
/* In case it is an external ref */
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
heap_size += EXTERNAL_THING_HEAD_SIZE + id_words/2 + 1;
#else
heap_size += EXTERNAL_THING_HEAD_SIZE + id_words;
@@ -4484,11 +4575,7 @@ init_done:
break;
case EXPORT_EXT:
terms += 3;
-#if HALFWORD_HEAP
- heap_size += 3;
-#else
heap_size += 2;
-#endif
break;
case NEW_FUN_EXT:
{
diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h
index d12051c6b4..f00426cc16 100644
--- a/erts/emulator/beam/external.h
+++ b/erts/emulator/beam/external.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2014. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,8 +18,6 @@
* %CopyrightEnd%
*/
-/* Same order as the ordering of terms in erlang */
-
/* Since there are 255 different External tag values to choose from
There is no reason to not be extravagant.
Hence, the different tags for large/small tuple e.t.c
@@ -37,9 +35,12 @@
#define SMALL_ATOM_EXT 's'
#define REFERENCE_EXT 'e'
#define NEW_REFERENCE_EXT 'r'
+#define NEWER_REFERENCE_EXT 'Z'
#define PORT_EXT 'f'
+#define NEW_PORT_EXT 'Y'
#define NEW_FLOAT_EXT 'F'
#define PID_EXT 'g'
+#define NEW_PID_EXT 'X'
#define SMALL_TUPLE_EXT 'h'
#define LARGE_TUPLE_EXT 'i'
#define NIL_EXT 'j'
@@ -191,7 +192,7 @@ Eterm erts_decode_dist_ext(ErtsHeapFactory* factory, ErtsDistExternal *);
Sint erts_decode_ext_size(byte*, Uint);
Sint erts_decode_ext_size_ets(byte*, Uint);
-Eterm erts_decode_ext(ErtsHeapFactory*, byte**);
+Eterm erts_decode_ext(ErtsHeapFactory*, byte**, Uint32 flags);
Eterm erts_decode_ext_ets(ErtsHeapFactory*, byte*);
Eterm erts_term_to_binary(Process* p, Eterm Term, int level, Uint flags);
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 1b8595fe57..19286e1310 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2014. 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.
@@ -35,7 +35,6 @@
#include "register.h"
#include "erl_fun.h"
#include "erl_node_tables.h"
-#include "benchmark.h"
#include "erl_process.h"
#include "erl_sys_driver.h"
#include "erl_debug.h"
@@ -44,6 +43,8 @@
#include "erl_port.h"
#include "erl_gc.h"
+struct enif_func_t;
+
struct enif_environment_t /* ErlNifEnv */
{
struct erl_module_nif* mod_nif;
@@ -54,14 +55,28 @@ struct enif_environment_t /* ErlNifEnv */
int fpe_was_unmasked;
struct enif_tmp_obj_t* tmp_obj_list;
int exception_thrown; /* boolean */
+ Process *tracee;
+ int exiting; /* boolean (dirty nifs might return in exiting state) */
};
extern void erts_pre_nif(struct enif_environment_t*, Process*,
- struct erl_module_nif*);
+ struct erl_module_nif*, Process* tracee);
extern void erts_post_nif(struct enif_environment_t* env);
+#ifdef ERTS_DIRTY_SCHEDULERS
+extern void erts_pre_dirty_nif(ErtsSchedulerData *,
+ struct enif_environment_t*, Process*,
+ struct erl_module_nif*);
+extern void erts_post_dirty_nif(struct enif_environment_t* env);
+#endif
extern Eterm erts_nif_taints(Process* p);
-extern void erts_print_nif_taints(int to, void* to_arg);
+extern void erts_print_nif_taints(fmtfn_t to, void* to_arg);
void erts_unload_nif(struct erl_module_nif* nif);
extern void erl_nif_init(void);
+extern int erts_nif_get_funcs(struct erl_module_nif*,
+ struct enif_func_t **funcs);
+extern Eterm erts_nif_call_function(Process *p, Process *tracee,
+ struct erl_module_nif*,
+ struct enif_func_t *,
+ int argc, Eterm *argv);
/* Driver handle (wrapper for old plain handle) */
#define ERL_DE_OK 0
@@ -112,7 +127,7 @@ typedef struct {
void *handle; /* Handle for DLL or SO (for dyn. drivers). */
DE_ProcEntry *procs; /* List of pids that have loaded this driver,
or that wait for it to change state */
- erts_refc_t refc; /* Number of ports/processes having
+ erts_smp_refc_t refc; /* Number of ports/processes having
references to the driver */
erts_smp_atomic32_t port_count; /* Number of ports using the driver */
Uint flags; /* ERL_DE_FL_KILL_PORTS */
@@ -310,9 +325,6 @@ typedef union {
typedef struct proc_bin {
Eterm thing_word; /* Subtag REFC_BINARY_SUBTAG. */
Uint size; /* Binary size in bytes. */
-#if HALFWORD_HEAP
- void* dummy_ptr_padding__;
-#endif
struct erl_off_heap_header *next;
Binary *val; /* Pointer to Binary structure. */
byte *bytes; /* Pointer to the actual data bytes. */
@@ -383,7 +395,7 @@ extern int bif_reductions; /* reductions + fcalls (when doing call_bif) */
extern int stackdump_on_exit;
/*
- * Here is an implementation of a lightweiht stack.
+ * Here is an implementation of a lightweight stack.
*
* Use it like this:
*
@@ -848,11 +860,99 @@ do {\
} while(0)
+/*
+ * An implementation of lightweight unbounded queues,
+ * using a circular dynamic array.
+ * It does not include support for change_allocator.
+ *
+ * Use it like this:
+ *
+ * DECLARE_EQUEUE(Queue) (At the start of a block)
+ * ...
+ * EQUEUE_PUT(Queue, Term)
+ * ...
+ * if (EQUEUE_ISEMPTY(Queue)) {
+ * Queue is empty
+ * } else {
+ * Term = EQUEUE_GET(Stack);
+ * Process popped Term here
+ * }
+ * ...
+ * DESTROY_EQUEUE(Queue)
+ */
+
+typedef struct {
+ Eterm* start;
+ Eterm* front;
+ Eterm* back;
+ int possibly_empty;
+ Eterm* end;
+ ErtsAlcType_t alloc_type;
+} ErtsEQueue;
+
+#define DEF_EQUEUE_SIZE (16)
+
+void erl_grow_equeue(ErtsEQueue*, Eterm* def_queue);
+#define EQUE_CONCAT(a,b) a##b
+#define EQUE_DEF_QUEUE(q) EQUE_CONCAT(q,_default_equeue)
+
+#define DECLARE_EQUEUE(q) \
+ UWord EQUE_DEF_QUEUE(q)[DEF_EQUEUE_SIZE]; \
+ ErtsEQueue q = { \
+ EQUE_DEF_QUEUE(q), /* start */ \
+ EQUE_DEF_QUEUE(q), /* front */ \
+ EQUE_DEF_QUEUE(q), /* back */ \
+ 1, /* possibly_empty */ \
+ EQUE_DEF_QUEUE(q) + DEF_EQUEUE_SIZE, /* end */ \
+ ERTS_ALC_T_ESTACK /* alloc_type */ \
+ }
+
+#define DESTROY_EQUEUE(q) \
+do { \
+ if (q.start != EQUE_DEF_QUEUE(q)) { \
+ erts_free(q.alloc_type, q.start); \
+ } \
+} while(0)
+
+#define EQUEUE_PUT_UNCHECKED(q, x) \
+do { \
+ q.possibly_empty = 0; \
+ *(q.back) = (x); \
+ if (++(q.back) == q.end) { \
+ q.back = q.start; \
+ } \
+} while(0)
+
+#define EQUEUE_PUT(q, x) \
+do { \
+ if (q.back == q.front && !q.possibly_empty) { \
+ erl_grow_equeue(&q, EQUE_DEF_QUEUE(q)); \
+ } \
+ EQUEUE_PUT_UNCHECKED(q, x); \
+} while(0)
+
+#define EQUEUE_ISEMPTY(q) (q.back == q.front && q.possibly_empty)
+
+ERTS_GLB_INLINE Eterm erts_equeue_get(ErtsEQueue *q);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+ERTS_GLB_INLINE Eterm erts_equeue_get(ErtsEQueue *q) {
+ Eterm x;
+ q->possibly_empty = 1;
+ x = *(q->front);
+ if (++(q->front) == q->end) {
+ q->front = q->start;
+ }
+ return x;
+}
+#endif
+#define EQUEUE_GET(q) erts_equeue_get(&(q));
+
/* binary.c */
void erts_emasculate_writable_binary(ProcBin* pb);
Eterm erts_new_heap_binary(Process *p, byte *buf, int len, byte** datap);
-Eterm erts_new_mso_binary(Process*, byte*, int);
+Eterm erts_new_mso_binary(Process*, byte*, Uint);
Eterm new_binary(Process*, byte*, Uint);
Eterm erts_realloc_binary(Eterm bin, size_t size);
@@ -900,8 +1000,34 @@ Eterm erl_send(Process *p, Eterm to, Eterm msg);
Eterm erl_is_function(Process* p, Eterm arg1, Eterm arg2);
/* beam_bif_load.c */
-Eterm erts_check_process_code(Process *c_p, Eterm module, int allow_gc, int *redsp);
+#define ERTS_CPC_ALLOW_GC (1 << 0)
+#define ERTS_CPC_ALL ERTS_CPC_ALLOW_GC
+Eterm erts_check_process_code(Process *c_p, Eterm module, Uint flags, int *redsp, int fcalls);
+#ifdef ERTS_NEW_PURGE_STRATEGY
+Eterm erts_proc_copy_literal_area(Process *c_p, int *redsp, int fcalls, int gc_allowed);
+#endif
+
+typedef struct ErtsLiteralArea_ {
+ struct erl_off_heap_header *off_heap;
+ Eterm *end;
+ Eterm start[1]; /* beginning of area */
+} ErtsLiteralArea;
+
+#define ERTS_LITERAL_AREA_ALLOC_SIZE(N) \
+ (sizeof(ErtsLiteralArea) + sizeof(Eterm)*((N) - 1))
+
+extern erts_smp_atomic_t erts_copy_literal_area__;
+#define ERTS_COPY_LITERAL_AREA() \
+ ((ErtsLiteralArea *) erts_smp_atomic_read_nob(&erts_copy_literal_area__))
+
+#ifdef ERTS_NEW_PURGE_STRATEGY
+extern Process *erts_literal_area_collector;
+#endif
+#ifdef ERTS_DIRTY_SCHEDULERS
+extern Process *erts_dirty_process_code_checker;
+#endif
+extern Process *erts_code_purger;
/* beam_load.c */
typedef struct {
@@ -913,6 +1039,7 @@ typedef struct {
Binary* erts_alloc_loader_state(void);
Eterm erts_module_for_prepared_code(Binary* magic);
+Eterm erts_has_code_on_load(Binary* magic);
Eterm erts_prepare_loading(Binary* loader_state, Process *c_p,
Eterm group_leader, Eterm* modp,
byte* code, Uint size);
@@ -931,7 +1058,7 @@ Eterm erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info);
/* beam_ranges.c */
void erts_init_ranges(void);
-void erts_start_staging_ranges(void);
+void erts_start_staging_ranges(int num_new);
void erts_end_staging_ranges(int commit);
void erts_update_ranges(BeamInstr* code, Uint size);
void erts_remove_from_ranges(BeamInstr* code);
@@ -942,56 +1069,100 @@ void erts_lookup_function_info(FunctionInfo* fi, BeamInstr* pc, int full_info);
void init_break_handler(void);
void erts_set_ignore_break(void);
void erts_replace_intr(void);
-void process_info(int, void *);
-void print_process_info(int, void *, Process*);
-void info(int, void *);
-void loaded(int, void *);
+void process_info(fmtfn_t, void *);
+void print_process_info(fmtfn_t, void *, Process*);
+void info(fmtfn_t, void *);
+void loaded(fmtfn_t, void *);
/* erl_arith.c */
double erts_get_positive_zero_float(void);
/* config.c */
+__decl_noreturn void __noreturn erts_exit_epilogue(void);
__decl_noreturn void __noreturn erts_exit(int n, char*, ...);
__decl_noreturn void __noreturn erts_flush_async_exit(int n, char*, ...);
void erl_error(char*, va_list);
-/* copy.c */
-Eterm copy_object(Eterm, Process*);
+/* This controls whether sharing-preserving copy is used by Erlang */
-#if HALFWORD_HEAP
-Uint size_object_rel(Eterm, Eterm*);
-# define size_object(A) size_object_rel(A,NULL)
+#ifdef SHCOPY
+#define SHCOPY_SEND
+#define SHCOPY_SPAWN
+#endif
-Eterm copy_struct_rel(Eterm, Uint, Eterm**, ErlOffHeap*, Eterm* src_base, Eterm* dst_base);
-# define copy_struct(OBJ,SZ,HPP,OH) copy_struct_rel(OBJ,SZ,HPP,OH, NULL,NULL)
+/* The persistent state while the sharing-preserving copier works */
-Eterm copy_shallow_rel(Eterm*, Uint, Eterm**, ErlOffHeap*, Eterm* src_base);
-# define copy_shallow(A,B,C,D) copy_shallow_rel(A,B,C,D,NULL)
+typedef struct {
+ Eterm queue_default[DEF_EQUEUE_SIZE];
+ Eterm* queue_start;
+ Eterm* queue_end;
+ ErtsAlcType_t queue_alloc_type;
+ UWord bitstore_default[DEF_WSTACK_SIZE];
+ UWord* bitstore_start;
+ ErtsAlcType_t bitstore_alloc_type;
+ Eterm shtable_default[DEF_ESTACK_SIZE];
+ Eterm* shtable_start;
+ ErtsAlcType_t shtable_alloc_type;
+ Uint literal_size;
+ Eterm *range_ptr;
+ Uint range_sz;
+} erts_shcopy_t;
+
+#define INITIALIZE_SHCOPY(info) \
+do { \
+ ErtsLiteralArea *larea__ = ERTS_COPY_LITERAL_AREA();\
+ info.queue_start = info.queue_default; \
+ info.bitstore_start = info.bitstore_default; \
+ info.shtable_start = info.shtable_default; \
+ info.literal_size = 0; \
+ if (larea__) { \
+ info.range_ptr = &larea__->start[0]; \
+ info.range_sz = larea__->end - info.range_ptr; \
+ } \
+ else { \
+ info.range_ptr = NULL; \
+ info.range_sz = 0; \
+ } \
+} while(0)
-#else /* !HALFWORD_HEAP */
+#define DESTROY_SHCOPY(info) \
+do { \
+ if (info.queue_start != info.queue_default) { \
+ erts_free(info.queue_alloc_type, info.queue_start); \
+ } \
+ if (info.bitstore_start != info.bitstore_default) { \
+ erts_free(info.bitstore_alloc_type, info.bitstore_start); \
+ } \
+ if (info.shtable_start != info.shtable_default) { \
+ erts_free(info.shtable_alloc_type, info.shtable_start); \
+ } \
+} while(0)
+
+/* copy.c */
+Eterm copy_object_x(Eterm, Process*, Uint);
+#define copy_object(Term, Proc) copy_object_x(Term,Proc,0)
Uint size_object(Eterm);
-# define size_object_rel(A,B) size_object(A)
+Uint copy_shared_calculate(Eterm, erts_shcopy_t*);
+Eterm copy_shared_perform(Eterm, Uint, erts_shcopy_t*, Eterm**, ErlOffHeap*);
-Eterm copy_struct(Eterm, Uint, Eterm**, ErlOffHeap*);
-# define copy_struct_rel(OBJ,SZ,HPP,OH, SB,DB) copy_struct(OBJ,SZ,HPP,OH)
+Uint size_shared(Eterm);
+Eterm copy_struct_x(Eterm, Uint, Eterm**, ErlOffHeap*, Uint* bsz);
+#define copy_struct(Obj,Sz,HPP,OH) \
+ copy_struct_x(Obj,Sz,HPP,OH,NULL)
Eterm copy_shallow(Eterm*, Uint, Eterm**, ErlOffHeap*);
-# define copy_shallow_rel(A,B,C,D, BASE) copy_shallow(A,B,C,D)
-
-#endif
-
-void move_multi_frags(Eterm** hpp, ErlOffHeap*, ErlHeapFragment* first,
- Eterm* refs, unsigned nrefs);
+void erts_move_multi_frags(Eterm** hpp, ErlOffHeap*, ErlHeapFragment* first,
+ Eterm* refs, unsigned nrefs, int literals);
/* Utilities */
extern void erts_delete_nodes_monitors(Process *, ErtsProcLocks);
extern Eterm erts_monitor_nodes(Process *, Eterm, Eterm);
extern Eterm erts_processes_monitoring_nodes(Process *);
extern int erts_do_net_exits(DistEntry*, Eterm);
-extern int distribution_info(int, void *);
+extern int distribution_info(fmtfn_t, void *);
extern int is_node_name_atom(Eterm a);
extern int erts_net_message(Port *, DistEntry *,
@@ -1010,6 +1181,7 @@ void print_pass_through(int, byte*, int);
int catchlevel(Process*);
void init_emulator(void);
void process_main(void);
+void erts_dirty_process_main(ErtsSchedulerData *);
Eterm build_stacktrace(Process* c_p, Eterm exc);
Eterm expand_error_value(Process* c_p, Uint freason, Eterm Value);
void erts_save_stacktrace(Process* p, struct StackTrace* s, int depth);
@@ -1037,6 +1209,9 @@ extern ErtsModifiedTimings erts_modified_timings[];
extern int erts_no_line_info;
extern Eterm erts_error_logger_warnings;
extern int erts_initialized;
+#if defined(USE_THREADS) && !defined(ERTS_SMP)
+extern erts_tid_t erts_main_thread;
+#endif
extern int erts_compat_rel;
extern int erts_use_sender_punish;
void erts_short_init(void);
@@ -1176,11 +1351,11 @@ int erts_utf8_to_latin1(byte* dest, const byte* source, int slen);
#define ERTS_UTF8_ANALYZE_MORE 3
#define ERTS_UTF8_OK_MAX_CHARS 4
-void bin_write(int, void*, byte*, size_t);
-int intlist_to_buf(Eterm, char*, int); /* most callers pass plain char*'s */
+void bin_write(fmtfn_t, void*, byte*, size_t);
+Sint intlist_to_buf(Eterm, char*, Sint); /* most callers pass plain char*'s */
struct Sint_buf {
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
char s[22];
#else
char s[12];
@@ -1250,7 +1425,7 @@ ErlDrvSizeT erts_iolist_to_buf(Eterm, char*, ErlDrvSizeT);
ErlDrvSizeT erts_iolist_to_buf_yielding(ErtsIOList2BufState *);
int erts_iolist_size_yielding(ErtsIOListState *state);
int erts_iolist_size(Eterm, ErlDrvSizeT *);
-int is_string(Eterm);
+Sint is_string(Eterm);
void erl_at_exit(void (*) (void*), void*);
Eterm collect_memory(Process *);
void dump_memory_to_fd(int);
@@ -1292,10 +1467,33 @@ Eterm erts_gc_binary_part_2(Process* p, Eterm* reg, Uint live);
Uint erts_current_reductions(Process* current, Process *p);
-int erts_print_system_version(int to, void *arg, Process *c_p);
+int erts_print_system_version(fmtfn_t to, void *arg, Process *c_p);
int erts_hibernate(Process* c_p, Eterm module, Eterm function, Eterm args, Eterm* reg);
+ERTS_GLB_FORCE_INLINE int erts_is_literal(Eterm tptr, Eterm *ptr);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_FORCE_INLINE int erts_is_literal(Eterm tptr, Eterm *ptr)
+{
+ ASSERT(is_boxed(tptr) || is_list(tptr));
+ ASSERT(ptr == ptr_val(tptr));
+
+#if defined(ERTS_HAVE_IS_IN_LITERAL_RANGE)
+ return erts_is_in_literal_range(ptr);
+#elif defined(TAG_LITERAL_PTR)
+ return is_literal_ptr(tptr);
+#else
+# error Not able to detect literals...
+#endif
+
+}
+
+#endif
+
+Eterm erts_msacc_request(Process *c_p, int action, Eterm *threads);
+
/*
** Call_trace uses this API for the parameter matching functions
*/
@@ -1316,9 +1514,19 @@ do { \
#define MatchSetGetSource(MPSP) erts_match_set_get_source(MPSP)
-extern Binary *erts_match_set_compile(Process *p, Eterm matchexpr);
+extern Binary *erts_match_set_compile(Process *p, Eterm matchexpr, Eterm MFA);
Eterm erts_match_set_lint(Process *p, Eterm matchexpr);
extern void erts_match_set_release_result(Process* p);
+ERTS_GLB_INLINE void erts_match_set_release_result_trace(Process* p, Eterm);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+ERTS_GLB_INLINE
+void erts_match_set_release_result_trace(Process* p, Eterm pam_result)
+{
+ if (is_not_immed(pam_result))
+ erts_match_set_release_result(p);
+}
+#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
enum erts_pam_run_flags {
ERTS_PAM_TMP_RESULT=1,
@@ -1326,10 +1534,12 @@ enum erts_pam_run_flags {
ERTS_PAM_CONTIGUOUS_TUPLE=4,
ERTS_PAM_IGNORE_TRACE_SILENT=8
};
-extern Eterm erts_match_set_run(Process *p, Binary *mpsp,
- Eterm *args, int num_args,
- enum erts_pam_run_flags in_flags,
- Uint32 *return_flags);
+extern Eterm erts_match_set_run_trace(Process *p,
+ Process *self,
+ Binary *mpsp,
+ Eterm *args, int num_args,
+ enum erts_pam_run_flags in_flags,
+ Uint32 *return_flags);
extern Eterm erts_match_set_get_source(Binary *mpsp);
extern void erts_match_prog_foreach_offheap(Binary *b,
void (*)(ErlOffHeap *, void *),
@@ -1344,191 +1554,21 @@ extern void erts_match_prog_foreach_offheap(Binary *b,
extern erts_driver_t vanilla_driver;
extern erts_driver_t spawn_driver;
+extern erts_driver_t forker_driver;
extern erts_driver_t fd_driver;
int erts_beam_jump_table(void);
-/* Should maybe be placed in erl_message.h, but then we get an include mess. */
-ERTS_GLB_INLINE Eterm *
-erts_alloc_message_heap_state(Uint size,
- ErlHeapFragment **bpp,
- ErlOffHeap **ohpp,
- Process *receiver,
- ErtsProcLocks *receiver_locks,
- erts_aint32_t *statep);
-
-ERTS_GLB_INLINE Eterm *
-erts_alloc_message_heap(Uint size,
- ErlHeapFragment **bpp,
- ErlOffHeap **ohpp,
- Process *receiver,
- ErtsProcLocks *receiver_locks);
-
-#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-
-/*
- * NOTE: erts_alloc_message_heap() releases msg q and status
- * lock on receiver without ensuring that other locks are
- * held. User is responsible to ensure that the receiver
- * pointer cannot become invalid until after message has
- * been passed. This is normal done either by increasing
- * reference count on process (preferred) or by holding
- * main or link lock over the whole message passing
- * operation.
- */
-
-ERTS_GLB_INLINE Eterm *
-erts_alloc_message_heap_state(Uint size,
- ErlHeapFragment **bpp,
- ErlOffHeap **ohpp,
- Process *receiver,
- ErtsProcLocks *receiver_locks,
- erts_aint32_t *statep)
-{
- Eterm *hp;
- erts_aint32_t state;
-#ifdef ERTS_SMP
- int locked_main = 0;
- state = erts_smp_atomic32_read_acqb(&receiver->state);
- if (statep)
- *statep = state;
- if (state & (ERTS_PSFLG_EXITING
- | ERTS_PSFLG_PENDING_EXIT))
- goto allocate_in_mbuf;
-#endif
-
- if (size > (Uint) INT_MAX)
- erts_exit(ERTS_ABORT_EXIT, "HUGE size (%beu)\n", size);
-
- if (
-#if defined(ERTS_SMP)
- *receiver_locks & ERTS_PROC_LOCK_MAIN
-#else
- 1
-#endif
- ) {
-#ifdef ERTS_SMP
- try_allocate_on_heap:
-#endif
- state = erts_smp_atomic32_read_nob(&receiver->state);
- if (statep)
- *statep = state;
- if ((state & (ERTS_PSFLG_EXITING
- | ERTS_PSFLG_PENDING_EXIT))
- || (receiver->flags & F_DISABLE_GC)
- || HEAP_LIMIT(receiver) - HEAP_TOP(receiver) <= size) {
- /*
- * The heap is either potentially in an inconsistent
- * state, or not large enough.
- */
-#ifdef ERTS_SMP
- if (locked_main) {
- *receiver_locks &= ~ERTS_PROC_LOCK_MAIN;
- erts_smp_proc_unlock(receiver, ERTS_PROC_LOCK_MAIN);
- }
-#endif
- goto allocate_in_mbuf;
- }
- hp = HEAP_TOP(receiver);
- HEAP_TOP(receiver) = hp + size;
- *bpp = NULL;
- *ohpp = &MSO(receiver);
- }
-#ifdef ERTS_SMP
- else if (erts_smp_proc_trylock(receiver, ERTS_PROC_LOCK_MAIN) == 0) {
- locked_main = 1;
- *receiver_locks |= ERTS_PROC_LOCK_MAIN;
- goto try_allocate_on_heap;
- }
-#endif
- else {
- ErlHeapFragment *bp;
- allocate_in_mbuf:
- bp = new_message_buffer(size);
- hp = bp->mem;
- *bpp = bp;
- *ohpp = &bp->off_heap;
- }
-
- return hp;
-}
-
-ERTS_GLB_INLINE Eterm *
-erts_alloc_message_heap(Uint size,
- ErlHeapFragment **bpp,
- ErlOffHeap **ohpp,
- Process *receiver,
- ErtsProcLocks *receiver_locks)
-{
- return erts_alloc_message_heap_state(size, bpp, ohpp, receiver,
- receiver_locks, NULL);
-}
-
-#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
-
-#if !HEAP_ON_C_STACK
-# if defined(DEBUG)
-# define DeclareTmpHeap(VariableName,Size,Process) \
- Eterm *VariableName = erts_debug_allocate_tmp_heap(Size,Process)
-# define DeclareTypedTmpHeap(Type,VariableName,Process) \
- Type *VariableName = (Type *) erts_debug_allocate_tmp_heap(sizeof(Type)/sizeof(Eterm),Process)
-# define DeclareTmpHeapNoproc(VariableName,Size) \
- Eterm *VariableName = erts_debug_allocate_tmp_heap(Size,NULL)
-# define UseTmpHeap(Size,Proc) \
- do { \
- erts_debug_use_tmp_heap((Size),(Proc)); \
- } while (0)
-# define UnUseTmpHeap(Size,Proc) \
- do { \
- erts_debug_unuse_tmp_heap((Size),(Proc)); \
- } while (0)
-# define UseTmpHeapNoproc(Size) \
- do { \
- erts_debug_use_tmp_heap(Size,NULL); \
- } while (0)
-# define UnUseTmpHeapNoproc(Size) \
- do { \
- erts_debug_unuse_tmp_heap(Size,NULL); \
- } while (0)
-# else
-# define DeclareTmpHeap(VariableName,Size,Process) \
- Eterm *VariableName = (ERTS_PROC_GET_SCHDATA(Process)->tmp_heap)+(ERTS_PROC_GET_SCHDATA(Process)->num_tmp_heap_used)
-# define DeclareTypedTmpHeap(Type,VariableName,Process) \
- Type *VariableName = (Type *) (ERTS_PROC_GET_SCHDATA(Process)->tmp_heap)+(ERTS_PROC_GET_SCHDATA(Process)->num_tmp_heap_used)
-# define DeclareTmpHeapNoproc(VariableName,Size) \
- Eterm *VariableName = (erts_get_scheduler_data()->tmp_heap)+(erts_get_scheduler_data()->num_tmp_heap_used)
-# define UseTmpHeap(Size,Proc) \
- do { \
- ERTS_PROC_GET_SCHDATA(Proc)->num_tmp_heap_used += (Size); \
- } while (0)
-# define UnUseTmpHeap(Size,Proc) \
- do { \
- ERTS_PROC_GET_SCHDATA(Proc)->num_tmp_heap_used -= (Size); \
- } while (0)
-# define UseTmpHeapNoproc(Size) \
- do { \
- erts_get_scheduler_data()->num_tmp_heap_used += (Size); \
- } while (0)
-# define UnUseTmpHeapNoproc(Size) \
- do { \
- erts_get_scheduler_data()->num_tmp_heap_used -= (Size); \
- } while (0)
-
-
-# endif
-
-#else
-# define DeclareTmpHeap(VariableName,Size,Process) \
+#define DeclareTmpHeap(VariableName,Size,Process) \
Eterm VariableName[Size]
-# define DeclareTypedTmpHeap(Type,VariableName,Process) \
+#define DeclareTypedTmpHeap(Type,VariableName,Process) \
Type VariableName[1]
-# define DeclareTmpHeapNoproc(VariableName,Size) \
+#define DeclareTmpHeapNoproc(VariableName,Size) \
Eterm VariableName[Size]
-# define UseTmpHeap(Size,Proc) /* Nothing */
-# define UnUseTmpHeap(Size,Proc) /* Nothing */
-# define UseTmpHeapNoproc(Size) /* Nothing */
-# define UnUseTmpHeapNoproc(Size) /* Nothing */
-#endif /* HEAP_ON_C_STACK */
+#define UseTmpHeap(Size,Proc) /* Nothing */
+#define UnUseTmpHeap(Size,Proc) /* Nothing */
+#define UseTmpHeapNoproc(Size) /* Nothing */
+#define UnUseTmpHeapNoproc(Size) /* Nothing */
ERTS_GLB_INLINE void dtrace_pid_str(Eterm pid, char *process_buf);
ERTS_GLB_INLINE void dtrace_proc_str(Process *process, char *process_buf);
@@ -1544,10 +1584,15 @@ ERTS_GLB_INLINE void dtrace_fun_decode(Process *process,
ERTS_GLB_INLINE void
dtrace_pid_str(Eterm pid, char *process_buf)
{
- erts_snprintf(process_buf, DTRACE_TERM_BUF_SIZE, "<%lu.%lu.%lu>",
- pid_channel_no(pid),
- pid_number(pid),
- pid_serial(pid));
+ if (is_pid(pid))
+ erts_snprintf(process_buf, DTRACE_TERM_BUF_SIZE, "<%lu.%lu.%lu>",
+ pid_channel_no(pid),
+ pid_number(pid),
+ pid_serial(pid));
+ else if (is_port(pid))
+ erts_snprintf(process_buf, DTRACE_TERM_BUF_SIZE, "#Port<%lu.%lu>",
+ port_channel_no(pid),
+ port_number(pid));
}
ERTS_GLB_INLINE void
@@ -1559,9 +1604,7 @@ dtrace_proc_str(Process *process, char *process_buf)
ERTS_GLB_INLINE void
dtrace_port_str(Port *port, char *port_buf)
{
- erts_snprintf(port_buf, DTRACE_TERM_BUF_SIZE, "#Port<%lu.%lu>",
- port_channel_no(port->common.id),
- port_number(port->common.id));
+ dtrace_pid_str(port->common.id, port_buf);
}
ERTS_GLB_INLINE void
@@ -1576,6 +1619,7 @@ dtrace_fun_decode(Process *process,
erts_snprintf(mfa_buf, DTRACE_TERM_BUF_SIZE, "%T:%T/%d",
module, function, arity);
}
+
#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
#endif /* !__GLOBAL_H__ */
diff --git a/erts/emulator/beam/hash.c b/erts/emulator/beam/hash.c
index 895fe657d1..8548e30e8b 100644
--- a/erts/emulator/beam/hash.c
+++ b/erts/emulator/beam/hash.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,8 +27,6 @@
#endif
#include "sys.h"
-#include "erl_vm.h"
-#include "global.h"
#include "hash.h"
/*
@@ -37,9 +35,9 @@
static const int h_size_table[] = {
2, 5, 11, 23, 47, 97, 197, 397, 797, /* double upto here */
1201, 1597,
- 2411, 3203,
+ 2411, 3203,
4813, 6421,
- 9643, 12853,
+ 9643, 12853,
19289, 25717,
51437,
102877,
@@ -51,8 +49,8 @@ static const int h_size_table[] = {
6584983,
13169977,
26339969,
- 52679969,
- -1
+ 52679969,
+ -1
};
/*
@@ -66,24 +64,29 @@ void hash_get_info(HashInfo *hi, Hash *h)
int i;
int max_depth = 0;
int objects = 0;
+ int used = 0;
for (i = 0; i < size; i++) {
int depth = 0;
HashBucket* b = h->bucket[i];
-
+
while (b != (HashBucket*) 0) {
objects++;
depth++;
b = b->next;
}
- if (depth > max_depth)
- max_depth = depth;
+ if (depth) {
+ used++;
+ if (depth > max_depth)
+ max_depth = depth;
+ }
}
+ ASSERT(objects == h->nobjs);
hi->name = h->name;
hi->size = h->size;
- hi->used = h->used;
- hi->objs = objects;
+ hi->used = used;
+ hi->objs = h->nobjs;
hi->depth = max_depth;
}
@@ -92,24 +95,24 @@ void hash_get_info(HashInfo *hi, Hash *h)
**
*/
-void hash_info(int to, void *arg, Hash* h)
+void hash_info(fmtfn_t to, void *arg, Hash* h)
{
HashInfo hi;
hash_get_info(&hi, h);
- erts_print(to, arg, "=hash_table:%s\n", hi.name);
- erts_print(to, arg, "size: %d\n", hi.size);
- erts_print(to, arg, "used: %d\n", hi.used);
- erts_print(to, arg, "objs: %d\n", hi.objs);
- erts_print(to, arg, "depth: %d\n", hi.depth);
+ h->fun.meta_print(to, arg, "=hash_table:%s\n", hi.name);
+ h->fun.meta_print(to, arg, "size: %d\n", hi.size);
+ h->fun.meta_print(to, arg, "used: %d\n", hi.used);
+ h->fun.meta_print(to, arg, "objs: %d\n", hi.objs);
+ h->fun.meta_print(to, arg, "depth: %d\n", hi.depth);
}
/*
* Returns size of table in bytes. Stored objects not included.
*/
-int
+int
hash_table_sz(Hash *h)
{
int i;
@@ -119,47 +122,56 @@ hash_table_sz(Hash *h)
}
+static ERTS_INLINE void set_thresholds(Hash* h)
+{
+ h->grow_threshold = (8*h->size)/5; /* grow at 160% load */
+ if (h->size_ix > h->min_size_ix)
+ h->shrink_threshold = h->size / 5; /* shrink at 20% load */
+ else
+ h->shrink_threshold = -1; /* never shrink below inital size */
+}
+
/*
** init a pre allocated or static hash structure
** and allocate buckets.
*/
-Hash* hash_init(ErtsAlcType_t type, Hash* h, char* name, int size, HashFunctions fun)
+Hash* hash_init(int type, Hash* h, char* name, int size, HashFunctions fun)
{
int sz;
int ix = 0;
- h->type = type;
+ h->meta_alloc_type = type;
while (h_size_table[ix] != -1 && h_size_table[ix] < size)
ix++;
if (h_size_table[ix] == -1)
- erts_exit(ERTS_ERROR_EXIT, "panic: too large hash table size (%d)\n", size);
+ return NULL;
size = h_size_table[ix];
sz = size*sizeof(HashBucket*);
- h->bucket = (HashBucket**) erts_alloc(h->type, sz);
+ h->bucket = (HashBucket**) fun.meta_alloc(h->meta_alloc_type, sz);
sys_memzero(h->bucket, sz);
h->is_allocated = 0;
h->name = name;
h->fun = fun;
h->size = size;
- h->size20percent = h->size/5;
- h->size80percent = (4*h->size)/5;
- h->ix = ix;
- h->used = 0;
+ h->size_ix = ix;
+ h->min_size_ix = ix;
+ h->nobjs = 0;
+ set_thresholds(h);
return h;
}
/*
** Create a new hash table
*/
-Hash* hash_new(ErtsAlcType_t type, char* name, int size, HashFunctions fun)
+Hash* hash_new(int type, char* name, int size, HashFunctions fun)
{
Hash* h;
- h = erts_alloc(type, sizeof(Hash));
+ h = fun.meta_alloc(type, sizeof(Hash));
h = hash_init(type, h, name, size, fun);
h->is_allocated = 1;
@@ -178,14 +190,14 @@ void hash_delete(Hash* h)
HashBucket* b = h->bucket[i];
while (b != (HashBucket*) 0) {
HashBucket* b_next = b->next;
-
+
h->fun.free((void*) b);
b = b_next;
}
}
- erts_free(h->type, h->bucket);
+ h->fun.meta_free(h->meta_alloc_type, h->bucket);
if (h->is_allocated)
- erts_free(h->type, (void*) h);
+ h->fun.meta_free(h->meta_alloc_type, (void*) h);
}
/*
@@ -199,39 +211,34 @@ static void rehash(Hash* h, int grow)
int i;
if (grow) {
- if ((h_size_table[h->ix+1]) == -1)
+ if ((h_size_table[h->size_ix+1]) == -1)
return;
- h->ix++;
+ h->size_ix++;
}
else {
- if (h->ix == 0)
+ if (h->size_ix == 0)
return;
- h->ix--;
+ h->size_ix--;
}
- h->size = h_size_table[h->ix];
- h->size20percent = h->size/5;
- h->size80percent = (4*h->size)/5;
+ h->size = h_size_table[h->size_ix];
sz = h->size*sizeof(HashBucket*);
- new_bucket = (HashBucket **) erts_alloc(h->type, sz);
+ new_bucket = (HashBucket **) h->fun.meta_alloc(h->meta_alloc_type, sz);
sys_memzero(new_bucket, sz);
- h->used = 0;
-
for (i = 0; i < old_size; i++) {
HashBucket* b = h->bucket[i];
while (b != (HashBucket*) 0) {
HashBucket* b_next = b->next;
int ix = b->hvalue % h->size;
- if (new_bucket[ix] == NULL)
- h->used++;
b->next = new_bucket[ix];
new_bucket[ix] = b;
b = b_next;
}
}
- erts_free(h->type, (void *) h->bucket);
+ h->fun.meta_free(h->meta_alloc_type, (void *) h->bucket);
h->bucket = new_bucket;
+ set_thresholds(h);
}
/*
@@ -243,7 +250,7 @@ void* hash_get(Hash* h, void* tmpl)
HashValue hval = h->fun.hash(tmpl);
int ix = hval % h->size;
HashBucket* b = h->bucket[ix];
-
+
while(b != (HashBucket*) 0) {
if ((b->hvalue == hval) && (h->fun.cmp(tmpl, (void*)b) == 0))
return (void*) b;
@@ -268,68 +275,15 @@ void* hash_put(Hash* h, void* tmpl)
}
b = (HashBucket*) h->fun.alloc(tmpl);
- if (h->bucket[ix] == NULL)
- h->used++;
-
b->hvalue = hval;
b->next = h->bucket[ix];
h->bucket[ix] = b;
- if (h->used > h->size80percent) /* rehash at 80% */
+ if (++h->nobjs > h->grow_threshold)
rehash(h, 1);
return (void*) b;
}
-static void
-hash_insert_entry(Hash* h, HashBucket* entry)
-{
- HashValue hval = entry->hvalue;
- int ix = hval % h->size;
- HashBucket* b = h->bucket[ix];
-
- while (b != (HashBucket*) 0) {
- if ((b->hvalue == hval) && (h->fun.cmp((void*)entry, (void*)b) == 0)) {
- abort(); /* Should not happen */
- }
- b = b->next;
- }
-
- if (h->bucket[ix] == NULL)
- h->used++;
-
- entry->next = h->bucket[ix];
- h->bucket[ix] = entry;
-
- if (h->used > h->size80percent) /* rehash at 80% */
- rehash(h, 1);
-}
-
-
-/*
- * Move all entries in src into dst; empty src.
- * Entries in src must not exist in dst.
- */
-void
-erts_hash_merge(Hash* src, Hash* dst)
-{
- int limit = src->size;
- HashBucket** bucket = src->bucket;
- int i;
-
- src->used = 0;
- for (i = 0; i < limit; i++) {
- HashBucket* b = bucket[i];
- HashBucket* next;
-
- bucket[i] = NULL;
- while (b) {
- next = b->next;
- hash_insert_entry(dst, b);
- b = next;
- }
- }
-}
-
/*
** Erase hash entry return template if erased
** return 0 if not erased
@@ -340,7 +294,7 @@ void* hash_erase(Hash* h, void* tmpl)
int ix = hval % h->size;
HashBucket* b = h->bucket[ix];
HashBucket* prev = 0;
-
+
while(b != 0) {
if ((b->hvalue == hval) && (h->fun.cmp(tmpl, (void*)b) == 0)) {
if (prev != 0)
@@ -348,9 +302,7 @@ void* hash_erase(Hash* h, void* tmpl)
else
h->bucket[ix] = b->next;
h->fun.free((void*)b);
- if (h->bucket[ix] == NULL)
- h->used--;
- if (h->used < h->size20percent) /* rehash at 20% */
+ if (--h->nobjs < h->shrink_threshold)
rehash(h, 0);
return tmpl;
}
@@ -374,16 +326,14 @@ hash_remove(Hash *h, void *tmpl)
int ix = hval % h->size;
HashBucket *b = h->bucket[ix];
HashBucket *prev = NULL;
-
+
while (b) {
if ((b->hvalue == hval) && (h->fun.cmp(tmpl, (void*)b) == 0)) {
if (prev)
prev->next = b->next;
else
h->bucket[ix] = b->next;
- if (h->bucket[ix] == NULL)
- h->used--;
- if (h->used < h->size20percent) /* rehash at 20% */
+ if (--h->nobjs < h->shrink_threshold)
rehash(h, 0);
return (void *) b;
}
@@ -405,4 +355,3 @@ void hash_foreach(Hash* h, void (*func)(void *, void *), void *func_arg2)
}
}
}
-
diff --git a/erts/emulator/beam/hash.h b/erts/emulator/beam/hash.h
index 87fdb360e3..d319aaca83 100644
--- a/erts/emulator/beam/hash.h
+++ b/erts/emulator/beam/hash.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,18 +25,19 @@
#ifndef __HASH_H__
#define __HASH_H__
-#ifndef __SYS_H__
#include "sys.h"
-#endif
-
-#include "erl_alloc.h"
typedef unsigned long HashValue;
+typedef struct hash Hash;
typedef int (*HCMP_FUN)(void*, void*);
typedef HashValue (*H_FUN)(void*);
typedef void* (*HALLOC_FUN)(void*);
typedef void (*HFREE_FUN)(void*);
+/* Meta functions */
+typedef void* (*HMALLOC_FUN)(int,size_t);
+typedef void (*HMFREE_FUN)(int,void*);
+typedef int (*HMPRINT_FUN)(fmtfn_t,void*,char*, ...);
/*
** This bucket must be placed in top of
@@ -55,6 +56,9 @@ typedef struct hash_functions
HCMP_FUN cmp;
HALLOC_FUN alloc;
HFREE_FUN free;
+ HMALLOC_FUN meta_alloc;
+ HMFREE_FUN meta_free;
+ HMPRINT_FUN meta_print;
} HashFunctions;
typedef struct {
@@ -65,26 +69,27 @@ typedef struct {
int depth;
} HashInfo;
-typedef struct hash
+struct hash
{
HashFunctions fun; /* Function block */
int is_allocated; /* 0 iff hash structure is on stack or is static */
- ErtsAlcType_t type;
+ int meta_alloc_type; /* argument to pass to meta_alloc and meta_free */
char* name; /* Table name (static string, for debugging) */
int size; /* Number of slots */
- int size20percent; /* 20 percent of number of slots */
- int size80percent; /* 80 percent of number of slots */
- int ix; /* Size index in size table */
- int used; /* Number of slots used */
+ int shrink_threshold;
+ int grow_threshold;
+ int size_ix; /* Size index in size table */
+ int min_size_ix; /* Never shrink table smaller than this */
+ int nobjs; /* Number of objects in table */
HashBucket** bucket; /* Vector of bucket pointers (objects) */
-} Hash;
+};
-Hash* hash_new(ErtsAlcType_t, char*, int, HashFunctions);
-Hash* hash_init(ErtsAlcType_t, Hash*, char*, int, HashFunctions);
+Hash* hash_new(int, char*, int, HashFunctions);
+Hash* hash_init(int, Hash*, char*, int, HashFunctions);
void hash_delete(Hash*);
void hash_get_info(HashInfo*, Hash*);
-void hash_info(int, void *, Hash*);
+void hash_info(fmtfn_t, void *, Hash*);
int hash_table_sz(Hash *);
void* hash_get(Hash*, void*);
@@ -93,6 +98,4 @@ void* hash_erase(Hash*, void*);
void* hash_remove(Hash*, void*);
void hash_foreach(Hash*, void (*func)(void *, void *), void *);
-void erts_hash_merge(Hash* src, Hash* dst);
-
#endif
diff --git a/erts/emulator/beam/index.c b/erts/emulator/beam/index.c
index 5f6ea14732..a1f6f54543 100644
--- a/erts/emulator/beam/index.c
+++ b/erts/emulator/beam/index.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2012. 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.
@@ -27,7 +27,7 @@
#include "global.h"
#include "index.h"
-void index_info(int to, void *arg, IndexTable *t)
+void index_info(fmtfn_t to, void *arg, IndexTable *t)
{
hash_info(to, arg, &t->htable);
erts_print(to, arg, "=index_table:%s\n", t->htable.name);
@@ -91,9 +91,16 @@ index_put_entry(IndexTable* t, void* tmpl)
t->seg_table[ix>>INDEX_PAGE_SHIFT] = erts_alloc(t->type, sz);
t->size += INDEX_PAGE_SIZE;
}
- t->entries++;
p->index = ix;
t->seg_table[ix>>INDEX_PAGE_SHIFT][ix&INDEX_PAGE_MASK] = p;
+
+ /*
+ * Do a write barrier here to allow readers to do lock free iteration.
+ * erts_index_num_entries() does matching read barrier.
+ */
+ ERTS_SMP_WRITE_MEMORY_BARRIER;
+ t->entries++;
+
return p;
}
diff --git a/erts/emulator/beam/index.h b/erts/emulator/beam/index.h
index 14fab41026..6c07571df6 100644
--- a/erts/emulator/beam/index.h
+++ b/erts/emulator/beam/index.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2013. 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.
@@ -26,9 +26,8 @@
#ifndef __INDEX_H__
#define __INDEX_H__
-#ifndef __HASH_H__
#include "hash.h"
-#endif
+#include "erl_alloc.h"
typedef struct index_slot
{
@@ -52,7 +51,7 @@ typedef struct index_table
#define INDEX_PAGE_MASK ((1 << INDEX_PAGE_SHIFT)-1)
IndexTable *erts_index_init(ErtsAlcType_t,IndexTable*,char*,int,int,HashFunctions);
-void index_info(int, void *, IndexTable*);
+void index_info(fmtfn_t, void *, IndexTable*);
int index_table_sz(IndexTable *);
int index_get(IndexTable*, void*);
@@ -66,6 +65,7 @@ void index_erase_latest_from(IndexTable*, Uint ix);
ERTS_GLB_INLINE int index_put(IndexTable*, void*);
ERTS_GLB_INLINE IndexSlot* erts_index_lookup(IndexTable*, Uint);
+ERTS_GLB_INLINE int erts_index_num_entries(IndexTable* t);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
@@ -79,6 +79,19 @@ erts_index_lookup(IndexTable* t, Uint ix)
{
return t->seg_table[ix>>INDEX_PAGE_SHIFT][ix&INDEX_PAGE_MASK];
}
+
+ERTS_GLB_INLINE int erts_index_num_entries(IndexTable* t)
+{
+ int ret = t->entries;
+ /*
+ * Do a read barrier here to allow lock free iteration
+ * on tables where entries are never erased.
+ * index_put_entry() does matching write barrier.
+ */
+ ERTS_SMP_READ_MEMORY_BARRIER;
+ return ret;
+}
+
#endif
#endif
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index fbcb0c31fc..c3eb610fdc 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2014. 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.
@@ -47,16 +47,18 @@
#define ERTS_WANT_EXTERNAL_TAGS
#include "external.h"
#include "dtrace-wrapper.h"
+#include "lttng-wrapper.h"
#include "erl_map.h"
#include "erl_bif_unique.h"
#include "erl_hl_timer.h"
#include "erl_time.h"
extern ErlDrvEntry fd_driver_entry;
-#ifndef __OSE__
extern ErlDrvEntry vanilla_driver_entry;
-#endif
extern ErlDrvEntry spawn_driver_entry;
+#ifndef __WIN32__
+extern ErlDrvEntry forker_driver_entry;
+#endif
extern ErlDrvEntry *driver_tab[]; /* table of static drivers, only used during initialization */
erts_driver_t *driver_list; /* List of all drivers, static and dynamic. */
@@ -74,6 +76,9 @@ const Port erts_invalid_port = {{ERTS_INVALID_PORT}};
erts_driver_t vanilla_driver;
erts_driver_t spawn_driver;
+#ifndef __WIN32__
+erts_driver_t forker_driver;
+#endif
erts_driver_t fd_driver;
int erts_port_synchronous_ops = 0;
@@ -83,10 +88,11 @@ int erts_port_parallelism = 0;
static erts_atomic64_t bytes_in;
static erts_atomic64_t bytes_out;
-static void deliver_result(Eterm sender, Eterm pid, Eterm res);
+static void deliver_result(Port *p, Eterm sender, Eterm pid, Eterm res);
static int init_driver(erts_driver_t *, ErlDrvEntry *, DE_Handle *);
static void terminate_port(Port *p);
static void pdl_init(void);
+static int driver_failure_term(ErlDrvPort ix, Eterm term, int eof);
#ifdef ERTS_SMP
static void driver_monitor_lock_pdl(Port *p);
static void driver_monitor_unlock_pdl(Port *p);
@@ -206,6 +212,7 @@ static ERTS_INLINE void
kill_port(Port *pp)
{
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(pp));
+ ERTS_TRACER_CLEAR(&ERTS_TRACER(pp));
erts_ptab_delete_element(&erts_port, &pp->common); /* Time of death */
erts_port_task_free_port(pp);
/* In non-smp case the port structure may have been deallocated now */
@@ -308,12 +315,9 @@ static Port *create_port(char *name,
size_t port_size, busy_port_queue_size, size;
erts_aint32_t state = ERTS_PORT_SFLG_CONNECTED;
erts_aint32_t x_pts_flgs = 0;
-#ifdef DEBUG
- /* Make sure the debug flags survives until port is freed */
- state |= ERTS_PORT_SFLG_PORT_DEBUG;
-#endif
#ifdef ERTS_SMP
+ ErtsRunQueue *runq;
if (!driver_lock) {
/* Align size for mutex following port struct */
port_size = size = ERTS_ALC_DATA_ALIGN_SIZE(sizeof(Port));
@@ -323,6 +327,12 @@ static Port *create_port(char *name,
#endif
port_size = size = ERTS_ALC_DATA_ALIGN_SIZE(sizeof(Port));
+#ifdef DEBUG
+ /* Make sure the debug flags survives until port is freed */
+ state |= ERTS_PORT_SFLG_PORT_DEBUG;
+#endif
+
+
busy_port_queue_size
= ((driver->flags & ERL_DRV_FLAG_NO_BUSY_MSGQ)
? 0
@@ -358,8 +368,12 @@ static Port *create_port(char *name,
p += sizeof(erts_mtx_t);
state |= ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK;
}
- erts_smp_atomic_set_nob(&prt->run_queue,
- (erts_aint_t) erts_get_runq_current(NULL));
+ if (erts_get_scheduler_data())
+ runq = erts_get_runq_current(NULL);
+ else
+ runq = ERTS_RUNQ_IX(0);
+ erts_smp_atomic_set_nob(&prt->run_queue, (erts_aint_t) runq);
+
prt->xports = NULL;
#else
erts_atomic32_init_nob(&prt->refc, 1);
@@ -385,12 +399,13 @@ static Port *create_port(char *name,
prt->common.u.alive.reg = NULL;
ERTS_PTMR_INIT(prt);
erts_port_task_handle_init(&prt->timeout_task);
- prt->psd = NULL;
+ erts_smp_atomic_init_nob(&prt->psd, (erts_aint_t) NULL);
+ prt->async_open_port = NULL;
prt->drv_data = (SWord) 0;
prt->os_pid = -1;
/* Set default tracing */
- erts_get_default_tracing(&ERTS_TRACE_FLAGS(prt), &ERTS_TRACER_PROC(prt));
+ erts_get_default_port_tracing(&ERTS_TRACE_FLAGS(prt), &ERTS_TRACER(prt));
ERTS_CT_ASSERT(offsetof(Port,common) == 0);
@@ -467,6 +482,11 @@ erts_port_free(Port *prt)
erts_port_task_fini_sched(&prt->sched);
+ if (prt->async_open_port) {
+ erts_free(ERTS_ALC_T_PRTSD, prt->async_open_port);
+ prt->async_open_port = NULL;
+ }
+
#ifdef ERTS_SMP
ASSERT(prt->lock);
if (state & ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK)
@@ -688,8 +708,9 @@ erts_open_driver(erts_driver_t* driver, /* Pointer to driver. */
error_number = error_type = 0;
if (driver->start) {
+ ERTS_MSACC_PUSH_STATE_M();
if (IS_TRACED_FL(port, F_TRACE_SCHED_PORTS)) {
- trace_sched_ports_where(port, am_in, am_start);
+ trace_sched_ports_where(port, am_in, am_open);
}
port->caller = pid;
#ifdef USE_VM_PROBES
@@ -698,6 +719,19 @@ erts_open_driver(erts_driver_t* driver, /* Pointer to driver. */
DTRACE3(driver_start, process_str, driver->name, port_str);
}
#endif
+
+ ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_PORT);
+
+#ifdef USE_LTTNG_VM_TRACEPOINTS
+ if (LTTNG_ENABLED(driver_start)) {
+ lttng_decl_portbuf(port_str);
+ lttng_decl_procbuf(proc_str);
+ lttng_pid_to_str(pid, proc_str);
+ lttng_port_to_str(port, port_str);
+ LTTNG3(driver_start, proc_str, driver->name, port_str);
+ }
+#endif
+
fpe_was_unmasked = erts_block_fpe();
drv_data = (*driver->start)(ERTS_Port2ErlDrvPort(port), name, opts);
if (((SWord) drv_data) == -1)
@@ -717,9 +751,10 @@ erts_open_driver(erts_driver_t* driver, /* Pointer to driver. */
}
erts_unblock_fpe(fpe_was_unmasked);
+ ERTS_MSACC_POP_STATE_M();
port->caller = NIL;
if (IS_TRACED_FL(port, F_TRACE_SCHED_PORTS)) {
- trace_sched_ports_where(port, am_out, am_start);
+ trace_sched_ports_where(port, am_out, am_open);
}
#ifdef ERTS_SMP
if (port->xports)
@@ -903,18 +938,32 @@ int erts_port_handle_xports(Port *prt)
** -2 on type error
*/
-#define SET_VEC(iov, bv, bin, ptr, len, vlen) do { \
- (iov)->iov_base = (ptr); \
- (iov)->iov_len = (len); \
- if (sizeof((iov)->iov_len) < sizeof(len) \
- /* Check if (len) overflowed (iov)->iov_len */ \
- && (iov)->iov_len != (len)) { \
- goto L_overflow; \
- } \
- *(bv)++ = (bin); \
- (iov)++; \
- (vlen)++; \
-} while(0)
+#ifdef DEBUG
+#define MAX_SYSIOVEC_IOVLEN (1ull << (32 - 1))
+#else
+#define MAX_SYSIOVEC_IOVLEN (1ull << (sizeof(((SysIOVec*)0)->iov_len) * 8 - 1))
+#endif
+
+static ERTS_INLINE void
+io_list_to_vec_set_vec(SysIOVec **iov, ErlDrvBinary ***binv,
+ ErlDrvBinary *bin, byte *ptr, Uint len,
+ int *vlen)
+{
+ while (len > MAX_SYSIOVEC_IOVLEN) {
+ (*iov)->iov_base = ptr;
+ (*iov)->iov_len = MAX_SYSIOVEC_IOVLEN;
+ ptr += MAX_SYSIOVEC_IOVLEN;
+ len -= MAX_SYSIOVEC_IOVLEN;
+ (*iov)++;
+ (*vlen)++;
+ *(*binv)++ = bin;
+ }
+ (*iov)->iov_base = ptr;
+ (*iov)->iov_len = len;
+ *(*binv)++ = bin;
+ (*iov)++;
+ (*vlen)++;
+}
static int
io_list_to_vec(Eterm obj, /* io-list */
@@ -925,11 +974,11 @@ io_list_to_vec(Eterm obj, /* io-list */
{
DECLARE_ESTACK(s);
Eterm* objp;
- char *buf = cbin->orig_bytes;
+ byte *buf = (byte*)cbin->orig_bytes;
Uint len = cbin->orig_size;
Uint csize = 0;
int vlen = 0;
- char* cptr = buf;
+ byte* cptr = buf;
goto L_jump_start; /* avoid push */
@@ -997,15 +1046,17 @@ io_list_to_vec(Eterm obj, /* io-list */
len -= size;
} else {
if (csize != 0) {
- SET_VEC(iov, binv, cbin, cptr, csize, vlen);
+ io_list_to_vec_set_vec(&iov, &binv, cbin,
+ cptr, csize, &vlen);
cptr = buf;
csize = 0;
}
if (pb->flags) {
erts_emasculate_writable_binary(pb);
}
- SET_VEC(iov, binv, Binary2ErlDrvBinary(pb->val),
- pb->bytes+offset, size, vlen);
+ io_list_to_vec_set_vec(
+ &iov, &binv, Binary2ErlDrvBinary(pb->val),
+ pb->bytes+offset, size, &vlen);
}
} else {
ErlHeapBin* hb = (ErlHeapBin *) bptr;
@@ -1025,7 +1076,7 @@ io_list_to_vec(Eterm obj, /* io-list */
}
if (csize != 0) {
- SET_VEC(iov, binv, cbin, cptr, csize, vlen);
+ io_list_to_vec_set_vec(&iov, &binv, cbin, cptr, csize, &vlen);
}
DESTROY_ESTACK(s);
@@ -1051,10 +1102,13 @@ do { \
if (_bitsize != 0) goto L_type_error; \
if (thing_subtag(*binary_val(_real)) == REFC_BINARY_SUBTAG && \
_bitoffs == 0) { \
- b_size += _size; \
+ b_size += _size; \
if (b_size < _size) goto L_overflow_error; \
in_clist = 0; \
- v_size++; \
+ v_size++; \
+ /* If iov_len is smaller then Uint we split the binary into*/ \
+ /* multiple smaller (2GB) elements in the iolist.*/ \
+ v_size += _size / MAX_SYSIOVEC_IOVLEN; \
if (_size >= ERL_SMALL_IO_BIN_LIMIT) { \
p_in_clist = 0; \
p_v_size++; \
@@ -1106,7 +1160,7 @@ io_list_vec_len(Eterm obj, int* vsize, Uint* csize,
Uint p_v_size = 0;
Uint p_c_size = 0;
Uint p_in_clist = 0;
- Uint total; /* Uint due to halfword emulator */
+ Uint total;
goto L_jump_start; /* avoid a push */
@@ -1206,7 +1260,7 @@ typedef struct {
/*
* Try doing an immediate driver callback call from a process. If
* this fail, the operation should be scheduled in the normal case...
- *
+ * Returns: ok to do the call, or error (lock busy, does not exist, etc)
*/
static ERTS_INLINE ErtsTryImmDrvCallResult
try_imm_drv_call(ErtsTryImmDrvCallState *sp)
@@ -1266,7 +1320,7 @@ try_imm_drv_call(ErtsTryImmDrvCallState *sp)
reds_left_in = CONTEXT_REDS/10;
else {
if (IS_TRACED_FL(c_p, F_TRACE_SCHED_PROCS))
- trace_virtual_sched(c_p, am_out);
+ trace_sched(c_p, ERTS_PROC_LOCK_MAIN, am_out);
/*
* No status lock held while sending runnable
* proc trace messages. It is however not needed
@@ -1350,7 +1404,7 @@ finalize_imm_drv_call(ErtsTryImmDrvCallState *sp)
}
if (IS_TRACED_FL(c_p, F_TRACE_SCHED_PROCS))
- trace_virtual_sched(c_p, am_in);
+ trace_sched(c_p, ERTS_PROC_LOCK_MAIN, am_in);
/*
* No status lock held while sending runnable
* proc trace messages. It is however not needed
@@ -1397,10 +1451,11 @@ finalize_force_imm_drv_call(ErtsTryImmDrvCallState *sp)
static ERTS_INLINE void
queue_port_sched_op_reply(Process *rp,
- ErtsProcLocks *rp_locksp,
+ ErtsProcLocks rp_locks,
ErtsHeapFactory* factory,
Uint32 *ref_num,
- Eterm msg)
+ Eterm msg,
+ Port* prt)
{
Eterm* hp = erts_produce_heap(factory, ERTS_QUEUE_PORT_SCHED_OP_REPLY_SIZE, 0);
Eterm ref;
@@ -1413,20 +1468,18 @@ queue_port_sched_op_reply(Process *rp,
erts_factory_trim_and_close(factory, &msg, 1);
- erts_queue_message(rp, rp_locksp, factory->heap_frags, msg, NIL);
+ erts_queue_message(rp, rp_locks, factory->message, msg,
+ prt ? prt->common.id : am_undefined);
}
static void
-port_sched_op_reply(Eterm to, Uint32 *ref_num, Eterm msg)
+port_sched_op_reply(Eterm to, Uint32 *ref_num, Eterm msg, Port* prt)
{
Process *rp = erts_proc_lookup_raw(to);
if (rp) {
- ErlOffHeap *ohp;
- ErlHeapFragment* bp;
ErtsHeapFactory factory;
Eterm msg_copy;
Uint hsz, msg_sz;
- Eterm *hp;
ErtsProcLocks rp_locks = 0;
hsz = ERTS_QUEUE_PORT_SCHED_OP_REPLY_SIZE;
@@ -1437,24 +1490,20 @@ port_sched_op_reply(Eterm to, Uint32 *ref_num, Eterm msg)
hsz += msg_sz;
}
- hp = erts_alloc_message_heap(hsz,
- &bp,
- &ohp,
- rp,
- &rp_locks);
- erts_factory_message_init(&factory, rp, hp, bp);
- if (is_immed(msg))
- msg_copy = msg;
- else {
- msg_copy = copy_struct(msg, msg_sz, &hp, ohp);
- factory.hp = hp;
- }
+ (void) erts_factory_message_create(&factory, rp,
+ &rp_locks, hsz);
+ msg_copy = (is_immed(msg)
+ ? msg
+ : copy_struct(msg, msg_sz,
+ &factory.hp,
+ factory.off_heap));
queue_port_sched_op_reply(rp,
- &rp_locks,
+ rp_locks,
&factory,
ref_num,
- msg_copy);
+ msg_copy,
+ prt);
if (rp_locks)
erts_smp_proc_unlock(rp, rp_locks);
@@ -1509,9 +1558,8 @@ erts_schedule_proc2port_signal(Process *c_p,
ERTS_SMP_MSGQ_MV_INQ2PRIVQ(c_p);
c_p->msg.save = c_p->msg.last;
- erts_smp_proc_unlock(c_p,
- (ERTS_PROC_LOCK_MAIN
- | ERTS_PROC_LOCKS_MSG_RECEIVE));
+ erts_smp_proc_unlock(c_p, (ERTS_PROC_LOCKS_MSG_RECEIVE
+ | ERTS_PROC_LOCK_MAIN));
}
@@ -1547,6 +1595,26 @@ erts_schedule_proc2port_signal(Process *c_p,
return ERTS_PORT_OP_SCHEDULED;
}
+static int
+erts_schedule_port2port_signal(Eterm port_num, ErtsProc2PortSigData *sigdp,
+ int task_flags,
+ ErtsProc2PortSigCallback callback)
+{
+ Port *prt = erts_port_lookup_raw(port_num);
+
+ if (!prt)
+ return -1;
+
+ sigdp->caller = ERTS_INVALID_PID;
+
+ return erts_port_task_schedule(prt->common.id,
+ NULL,
+ ERTS_PORT_TASK_PROC_SIG,
+ sigdp,
+ callback,
+ task_flags);
+}
+
static ERTS_INLINE void
send_badsig(Port *prt) {
ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
@@ -1605,7 +1673,7 @@ port_badsig(Port *prt, erts_aint32_t state, int op,
state,
sigdp->flags & ERTS_P2P_SIG_DATA_FLG_BAD_OUTPUT);
if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
- port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg, prt);
return ERTS_PORT_REDS_BADSIG;
} /* port_badsig */
/* bad_port_signal() will
@@ -1691,16 +1759,30 @@ call_driver_outputv(int bang_op,
else {
ErtsSchedulerData *esdp = erts_get_scheduler_data();
ErlDrvSizeT size = evp->size;
+ ERTS_MSACC_PUSH_AND_SET_STATE_M(ERTS_MSACC_STATE_PORT);
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)
|| ERTS_IS_CRASH_DUMPING);
+
+ if (IS_TRACED_FL(prt, F_TRACE_RECEIVE))
+ trace_port_receive(prt, caller, am_commandv, evp);
+
#ifdef USE_VM_PROBES
if (DTRACE_ENABLED(driver_outputv)) {
DTRACE_FORMAT_COMMON_PID_AND_PORT(caller, prt);
DTRACE4(driver_outputv, process_str, port_str, prt->name, size);
}
#endif
+#ifdef USE_LTTNG_VM_TRACEPOINTS
+ if (LTTNG_ENABLED(driver_outputv)) {
+ lttng_decl_portbuf(port_str);
+ lttng_decl_procbuf(proc_str);
+ lttng_pid_to_str(caller, proc_str);
+ lttng_port_to_str(prt, port_str);
+ LTTNG4(driver_outputv, proc_str, port_str, prt->name, size);
+ }
+#endif
prt->caller = caller;
(*drv->outputv)((ErlDrvData) prt->drv_data, evp);
@@ -1711,6 +1793,8 @@ call_driver_outputv(int bang_op,
esdp->io.out += (Uint64) size;
else
erts_atomic64_add_nob(&bytes_out, (erts_aint64_t) size);
+
+ ERTS_MSACC_POP_STATE_M();
}
}
@@ -1724,7 +1808,6 @@ cleanup_scheduled_outputv(ErlIOVec *ev, ErlDrvBinary *cbinp)
driver_free_binary(ev->binv[i]);
if (cbinp)
driver_free_binary(cbinp);
- erts_free(ERTS_ALC_T_DRV_CMD_DATA, ev);
}
static int
@@ -1759,7 +1842,7 @@ port_sig_outputv(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *s
}
if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
- port_sched_op_reply(sigdp->caller, sigdp->ref, reply);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, reply, prt);
cleanup_scheduled_outputv(sigdp->u.outputv.evp,
sigdp->u.outputv.cbinp);
@@ -1791,6 +1874,7 @@ call_driver_output(int bang_op,
send_badsig(prt);
else {
ErtsSchedulerData *esdp = erts_get_scheduler_data();
+ ERTS_MSACC_PUSH_AND_SET_STATE_M(ERTS_MSACC_STATE_PORT);
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)
|| ERTS_IS_CRASH_DUMPING);
@@ -1800,6 +1884,18 @@ call_driver_output(int bang_op,
DTRACE4(driver_output, process_str, port_str, prt->name, size);
}
#endif
+#ifdef USE_LTTNG_VM_TRACEPOINTS
+ if (LTTNG_ENABLED(driver_output)) {
+ lttng_decl_portbuf(port_str);
+ lttng_decl_procbuf(proc_str);
+ lttng_pid_to_str(caller, proc_str);
+ lttng_port_to_str(prt, port_str);
+ LTTNG4(driver_output, proc_str, port_str, prt->name, size);
+ }
+#endif
+
+ if (IS_TRACED_FL(prt, F_TRACE_RECEIVE))
+ trace_port_receive(prt, caller, am_command, bufp, size);
prt->caller = caller;
(*drv->output)((ErlDrvData) prt->drv_data, bufp, size);
@@ -1810,6 +1906,8 @@ call_driver_output(int bang_op,
esdp->io.out += (Uint64) size;
else
erts_atomic64_add_nob(&bytes_out, (erts_aint64_t) size);
+
+ ERTS_MSACC_POP_STATE_M();
}
}
@@ -1852,13 +1950,195 @@ port_sig_output(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *si
}
if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
- port_sched_op_reply(sigdp->caller, sigdp->ref, reply);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, reply, prt);
cleanup_scheduled_output(sigdp->u.output.bufp);
return ERTS_PORT_REDS_CMD_OUTPUT;
}
+
+/*
+ * This erts_port_output will always create a port task.
+ * The call is treated as a port_command call, i.e. no
+ * badsig i generated if the input in invalid. However
+ * an error_logger message is generated.
+ */
+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;
+ int task_flags;
+ ErtsProc2PortSigCallback port_sig_callback;
+ ErlDrvBinary *cbin = NULL;
+ ErlIOVec *evp = NULL;
+ char *buf = NULL;
+ ErtsPortTaskHandle *ns_pthp;
+
+ if (drv->outputv) {
+ ErlIOVec ev;
+ SysIOVec* ivp;
+ ErlDrvBinary** bvp;
+ int vsize;
+ Uint csize;
+ Uint pvsize;
+ Uint pcsize;
+ size_t iov_offset, binv_offset, alloc_size;
+ Uint blimit = 0;
+ char *ptr;
+ int i;
+
+ Eterm* bptr = NULL;
+ Uint offset;
+
+ if (is_binary(list)) {
+ /* We optimize for when we get a procbin without offset */
+ Eterm real_bin;
+ int bitoffs;
+ int bitsize;
+ ERTS_GET_REAL_BIN(list, real_bin, offset, bitoffs, bitsize);
+ bptr = binary_val(real_bin);
+ if (*bptr == HEADER_PROC_BIN && bitoffs == 0) {
+ size = binary_size(list);
+ vsize = 1;
+ } else
+ bptr = NULL;
+ }
+
+ if (!bptr) {
+ if (io_list_vec_len(list, &vsize, &csize, &pvsize, &pcsize, &size))
+ goto bad_value;
+
+ /* To pack or not to pack (small binaries) ...? */
+ if (vsize >= SMALL_WRITE_VEC) {
+ /* Do pack */
+ vsize = pvsize + 1;
+ csize = pcsize;
+ blimit = ERL_SMALL_IO_BIN_LIMIT;
+ }
+ cbin = driver_alloc_binary(csize);
+ if (!cbin)
+ erts_alloc_enomem(ERTS_ALC_T_DRV_BINARY, ERTS_SIZEOF_Binary(csize));
+ }
+
+
+ iov_offset = ERTS_ALC_DATA_ALIGN_SIZE(sizeof(ErlIOVec));
+ binv_offset = iov_offset;
+ binv_offset += ERTS_ALC_DATA_ALIGN_SIZE((vsize+1)*sizeof(SysIOVec));
+ alloc_size = binv_offset;
+ alloc_size += (vsize+1)*sizeof(ErlDrvBinary *);
+
+ sigdp = erts_port_task_alloc_p2p_sig_data_extra(alloc_size, (void**)&ptr);
+
+ evp = (ErlIOVec *) ptr;
+ ivp = evp->iov = (SysIOVec *) (ptr + iov_offset);
+ bvp = evp->binv = (ErlDrvBinary **) (ptr + binv_offset);
+
+ ivp[0].iov_base = NULL;
+ ivp[0].iov_len = 0;
+ bvp[0] = NULL;
+
+ if (bptr) {
+ ProcBin* pb = (ProcBin *) bptr;
+
+ ivp[1].iov_base = pb->bytes+offset;
+ ivp[1].iov_len = size;
+ bvp[1] = Binary2ErlDrvBinary(pb->val);
+
+ evp->vsize = 1;
+ } else {
+
+ evp->vsize = io_list_to_vec(list, ivp+1, bvp+1, cbin, blimit);
+ if (evp->vsize < 0) {
+ if (evp != &ev)
+ erts_free(ERTS_ALC_T_DRV_CMD_DATA, evp);
+ driver_free_binary(cbin);
+ goto bad_value;
+ }
+ }
+#if 0
+ /* This assertion may say something useful, but it can
+ be falsified during the emulator test suites. */
+ ASSERT(evp->vsize == vsize);
+#endif
+ evp->vsize++;
+ evp->size = size; /* total size */
+
+ /* Need to increase refc on all binaries */
+ for (i = 1; i < evp->vsize; i++)
+ if (bvp[i])
+ driver_binary_inc_refc(bvp[i]);
+
+ sigdp->flags = ERTS_P2P_SIG_TYPE_OUTPUTV;
+ sigdp->u.outputv.from = from;
+ sigdp->u.outputv.evp = evp;
+ sigdp->u.outputv.cbinp = cbin;
+ port_sig_callback = port_sig_outputv;
+ } else {
+ ErlDrvSizeT ERTS_DECLARE_DUMMY(r);
+
+ /*
+ * Apperently there exist code that write 1 byte to
+ * much in buffer. Where it resides I don't know, but
+ * we can live with one byte extra allocated...
+ */
+
+ if (erts_iolist_size(list, &size))
+ goto bad_value;
+
+ buf = erts_alloc(ERTS_ALC_T_DRV_CMD_DATA, size + 1);
+
+ r = erts_iolist_to_buf(list, buf, size);
+ ASSERT(ERTS_IOLIST_TO_BUF_SUCCEEDED(r));
+
+ sigdp = erts_port_task_alloc_p2p_sig_data();
+ sigdp->flags = ERTS_P2P_SIG_TYPE_OUTPUT;
+ sigdp->u.output.from = from;
+ sigdp->u.output.bufp = buf;
+ 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);
+
+ 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:
+
+ /*
+ * We call badsig directly here as this function is called with
+ * the main lock of the calling process still held.
+ * At the moment this operation is always not a bang_op, so
+ * only an error_logger message should be generated, no badsig.
+ */
+
+ badsig_received(0, prt, erts_atomic32_read_nob(&prt->state), 1);
+
+ return 0;
+
+}
+
ErtsPortOpResult
erts_port_output(Process *c_p,
int flags,
@@ -1868,7 +2148,7 @@ erts_port_output(Process *c_p,
Eterm *refp)
{
ErtsPortOpResult res;
- ErtsProc2PortSigData *sigdp;
+ ErtsProc2PortSigData *sigdp = NULL;
erts_driver_t *drv = prt->drv_ptr;
size_t size;
int try_call;
@@ -1921,7 +2201,6 @@ erts_port_output(Process *c_p,
DTRACE4(port_command, process_str, port_str, prt->name, "command");
}
#endif
-
if (drv->outputv) {
ErlIOVec ev;
SysIOVec iv[SMALL_WRITE_VEC];
@@ -1950,10 +2229,13 @@ erts_port_output(Process *c_p,
evp = &ev;
}
else {
- char *ptr = erts_alloc((try_call
- ? ERTS_ALC_T_TMP
- : ERTS_ALC_T_DRV_CMD_DATA), alloc_size);
-
+ char *ptr;
+ if (try_call) {
+ ptr = erts_alloc(ERTS_ALC_T_TMP, alloc_size);
+ } else {
+ sigdp = erts_port_task_alloc_p2p_sig_data_extra(
+ alloc_size, (void**)&ptr);
+ }
evp = (ErlIOVec *) ptr;
ivp = evp->iov = (SysIOVec *) (ptr + iov_offset);
bvp = evp->binv = (ErlDrvBinary **) (ptr + binv_offset);
@@ -1982,9 +2264,12 @@ erts_port_output(Process *c_p,
bvp[0] = NULL;
evp->vsize = io_list_to_vec(list, ivp+1, bvp+1, cbin, blimit);
if (evp->vsize < 0) {
- if (evp != &ev)
- erts_free(try_call ? ERTS_ALC_T_TMP : ERTS_ALC_T_DRV_CMD_DATA,
- evp);
+ if (evp != &ev) {
+ if (try_call)
+ erts_free(ERTS_ALC_T_TMP, evp);
+ else
+ erts_port_task_free_p2p_sig_data(sigdp);
+ }
driver_free_binary(cbin);
goto bad_value;
}
@@ -2036,8 +2321,10 @@ erts_port_output(Process *c_p,
/* Fall through... */
case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT:
driver_free_binary(cbin);
- if (evp != &ev)
+ if (evp != &ev) {
+ ASSERT(!sigdp);
erts_free(ERTS_ALC_T_TMP, evp);
+ }
if (try_call_res != ERTS_TRY_IMM_DRV_CALL_OK)
return ERTS_PORT_OP_DROPPED;
if (c_p)
@@ -2048,8 +2335,10 @@ erts_port_output(Process *c_p,
if (async_nosuspend
&& (sched_flags & (busy_flgs|ERTS_PTS_FLG_EXIT))) {
driver_free_binary(cbin);
- if (evp != &ev)
+ if (evp != &ev) {
+ ASSERT(!sigdp);
erts_free(ERTS_ALC_T_TMP, evp);
+ }
return ((sched_flags & ERTS_PTS_FLG_EXIT)
? ERTS_PORT_OP_DROPPED
: ERTS_PORT_OP_BUSY);
@@ -2064,9 +2353,16 @@ erts_port_output(Process *c_p,
if (bvp[i])
driver_binary_inc_refc(bvp[i]);
- new_evp = erts_alloc(ERTS_ALC_T_DRV_CMD_DATA, alloc_size);
+ /* The port task and iovec is allocated in the
+ same structure as an optimization. This
+ is especially important in erts_port_output_async
+ of when !try_call */
+ ASSERT(sigdp == NULL);
+ sigdp = erts_port_task_alloc_p2p_sig_data_extra(
+ alloc_size, (void**)&new_evp);
if (evp != &ev) {
+ /* Copy from TMP alloc to port task */
sys_memcpy((void *) new_evp, (void *) evp, alloc_size);
new_evp->iov = (SysIOVec *) (((char *) new_evp)
+ iov_offset);
@@ -2114,7 +2410,6 @@ erts_port_output(Process *c_p,
evp = new_evp;
}
- sigdp = erts_port_task_alloc_p2p_sig_data();
sigdp->flags = ERTS_P2P_SIG_TYPE_OUTPUTV;
sigdp->u.outputv.from = from;
sigdp->u.outputv.evp = evp;
@@ -2236,7 +2531,7 @@ erts_port_output(Process *c_p,
sigdp->flags &= ~ERTS_P2P_SIG_DATA_FLG_NOSUSPEND;
else if (async_nosuspend) {
ErtsSchedulerData *esdp = (c_p
- ? ERTS_PROC_GET_SCHDATA(c_p)
+ ? erts_proc_sched_data(c_p)
: erts_get_scheduler_data());
ASSERT(esdp);
ns_pthp = &esdp->nosuspend_port_task_handle;
@@ -2323,7 +2618,10 @@ call_deliver_port_exit(int bang_op,
return ERTS_PORT_OP_DROPPED;
}
- if (!erts_deliver_port_exit(prt, from, reason, bang_op))
+ if (IS_TRACED_FL(prt, F_TRACE_RECEIVE))
+ trace_port_receive(prt, from, am_close);
+
+ if (!erts_deliver_port_exit(prt, from, reason, bang_op, broken_link))
return ERTS_PORT_OP_DROPPED;
#ifdef USE_VM_PROBES
@@ -2360,7 +2658,7 @@ port_sig_exit(Port *prt,
if (sigdp->u.exit.bp)
free_message_buffer(sigdp->u.exit.bp);
if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
- port_sched_op_reply(sigdp->caller, sigdp->ref, msg);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, msg, prt);
return ERTS_PORT_REDS_EXIT;
}
@@ -2382,6 +2680,11 @@ erts_port_exit(Process *c_p,
| ERTS_PORT_SIG_FLG_BROKEN_LINK
| ERTS_PORT_SIG_FLG_FORCE_SCHED)) == 0);
+#ifndef __WIN32__
+ if (prt->drv_ptr == &forker_driver)
+ return ERTS_PORT_OP_DROPPED;
+#endif
+
if (!(flags & ERTS_PORT_SIG_FLG_FORCE_SCHED)) {
ErtsTryImmDrvCallState try_call_state
= ERTS_INIT_TRY_IMM_DRV_CALL_STATE(c_p,
@@ -2389,13 +2692,13 @@ erts_port_exit(Process *c_p,
ERTS_PORT_SFLGS_INVALID_LOOKUP,
0,
!refp,
- am_exit);
+ am_close);
switch (try_imm_drv_call(&try_call_state)) {
case ERTS_TRY_IMM_DRV_CALL_OK: {
res = call_deliver_port_exit(flags & ERTS_PORT_SIG_FLG_BANG_OP,
- from,
+ c_p ? c_p->common.id : from,
prt,
try_call_state.state,
reason,
@@ -2474,8 +2777,11 @@ set_port_connected(int bang_op,
return ERTS_PORT_OP_DROPPED;
}
+ if (IS_TRACED_FL(prt, F_TRACE_RECEIVE))
+ trace_port_receive(prt, from, am_connect, connect);
+
ERTS_PORT_SET_CONNECTED(prt, connect);
- deliver_result(prt->common.id, from, am_connected);
+ deliver_result(prt, prt->common.id, from, am_connected);
#ifdef USE_VM_PROBES
if(DTRACE_ENABLED(port_command)) {
@@ -2497,10 +2803,22 @@ set_port_connected(int bang_op,
erts_add_link(&ERTS_P_LINKS(rp), LINK_PID, prt->common.id);
erts_add_link(&ERTS_P_LINKS(prt), LINK_PID, connect);
+ if (IS_TRACED_FL(rp, F_TRACE_PROCS))
+ trace_proc(NULL, 0, rp, am_getting_linked, prt->common.id);
+
ERTS_PORT_SET_CONNECTED(prt, connect);
erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
+ if (IS_TRACED_FL(prt, F_TRACE_PORTS))
+ trace_port(prt, am_getting_linked, connect);
+ if (IS_TRACED_FL(prt, F_TRACE_RECEIVE))
+ trace_port_receive(prt, from, am_connect, connect);
+ if (IS_TRACED_FL(prt, F_TRACE_SEND)) {
+ Eterm hp[3];
+ trace_port_send(prt, from, TUPLE2(hp, prt->common.id, am_connected), 1);
+ }
+
#ifdef USE_VM_PROBES
if (DTRACE_ENABLED(port_connect)) {
DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE);
@@ -2533,7 +2851,7 @@ port_sig_connect(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *s
msg = am_true;
}
if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
- port_sched_op_reply(sigdp->caller, sigdp->ref, msg);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, msg, prt);
return ERTS_PORT_REDS_CONNECT;
}
@@ -2603,8 +2921,11 @@ static void
port_unlink(Port *prt, Eterm from)
{
ErtsLink *lnk = erts_remove_link(&ERTS_P_LINKS(prt), from);
- if (lnk)
+ if (lnk) {
+ if (IS_TRACED_FL(prt, F_TRACE_PORTS))
+ trace_port(prt, am_getting_unlinked, from);
erts_destroy_link(lnk);
+ }
}
static int
@@ -2613,7 +2934,7 @@ port_sig_unlink(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *si
if (op == ERTS_PROC2PORT_SIG_EXEC)
port_unlink(prt, sigdp->u.unlink.from);
if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
- port_sched_op_reply(sigdp->caller, sigdp->ref, am_true);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, am_true, prt);
return ERTS_PORT_REDS_UNLINK;
}
@@ -2674,10 +2995,10 @@ port_link_failure(Eterm port_id, Eterm linker)
NIL,
NULL,
0);
- if (xres >= 0 && IS_TRACED_FL(rp, F_TRACE_PROCS)) {
+ if (xres >= 0) {
/* We didn't exit the process and it is traced */
if (IS_TRACED_FL(rp, F_TRACE_PROCS))
- trace_proc(NULL, rp, am_getting_unlinked, port_id);
+ trace_proc(NULL, 0, rp, am_getting_unlinked, port_id);
}
if (rp_locks)
erts_smp_proc_unlock(rp, rp_locks);
@@ -2688,10 +3009,15 @@ port_link_failure(Eterm port_id, Eterm linker)
static void
port_link(Port *prt, erts_aint32_t state, Eterm to)
{
- if (!(state & ERTS_PORT_SFLGS_INVALID_LOOKUP))
+ if (IS_TRACED_FL(prt, F_TRACE_PORTS))
+ trace_port(prt, am_getting_linked, to);
+ if (!(state & ERTS_PORT_SFLGS_INVALID_LOOKUP)) {
erts_add_link(&ERTS_P_LINKS(prt), LINK_PID, to);
- else
+ } else {
port_link_failure(prt->common.id, to);
+ if (IS_TRACED_FL(prt, F_TRACE_PORTS))
+ trace_port(prt, am_unlink, to);
+ }
}
static int
@@ -2699,10 +3025,11 @@ port_sig_link(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *sigd
{
if (op == ERTS_PROC2PORT_SIG_EXEC)
port_link(prt, state, sigdp->u.link.to);
- else
+ else {
port_link_failure(sigdp->u.link.port, sigdp->u.link.to);
+ }
if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
- port_sched_op_reply(sigdp->caller, sigdp->ref, am_true);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, am_true, prt);
return ERTS_PORT_REDS_LINK;
}
@@ -2746,6 +3073,317 @@ erts_port_link(Process *c_p, Port *prt, Eterm to, Eterm *refp)
port_sig_link);
}
+static void
+port_monitor_failure(Eterm port_id, Eterm origin, Eterm ref_DOWN)
+{
+ Process *origin_p;
+ ErtsProcLocks p_locks = ERTS_PROC_LOCK_LINK;
+ ASSERT(is_internal_pid(origin));
+
+ origin_p = erts_pid2proc(NULL, 0, origin, p_locks);
+ if (! origin_p) { return; }
+
+ /* Send the DOWN message immediately. Ref is made on the fly because
+ * caller has never seen it yet. */
+ erts_queue_monitor_message(origin_p, &p_locks, ref_DOWN,
+ am_port, port_id, am_noproc);
+ erts_smp_proc_unlock(origin_p, p_locks);
+}
+
+/* Origin wants to monitor port Prt. State contains possible error, which has
+ * happened just before. Name is either NIL or an atom, if user monitors
+ * a port by name. Ref is premade reference that will be returned to user */
+static void
+port_monitor(Port *prt, erts_aint32_t state, Eterm origin,
+ Eterm name, Eterm ref)
+{
+ Eterm name_or_nil = is_atom(name) ? name : NIL;
+
+ ASSERT(is_pid(origin));
+ ASSERT(is_atom(name) || is_port(name) || name == NIL);
+ ASSERT(is_internal_ref(ref));
+
+ if (!(state & ERTS_PORT_SFLGS_INVALID_LOOKUP)) {
+ ErtsProcLocks p_locks = ERTS_PROC_LOCK_LINK;
+
+ Process *origin_p = erts_pid2proc(NULL, 0, origin, p_locks);
+ if (! origin_p) {
+ goto failure;
+ }
+ erts_add_monitor(&ERTS_P_MONITORS(origin_p), MON_ORIGIN, ref,
+ prt->common.id, name_or_nil);
+ erts_add_monitor(&ERTS_P_MONITORS(prt), MON_TARGET, ref,
+ origin, name_or_nil);
+
+ erts_smp_proc_unlock(origin_p, p_locks);
+ } else {
+failure:
+ port_monitor_failure(prt->common.id, origin, ref);
+ }
+}
+
+static int
+port_sig_monitor(Port *prt, erts_aint32_t state, int op,
+ ErtsProc2PortSigData *sigdp)
+{
+ Eterm hp[REF_THING_SIZE];
+ Eterm ref = make_internal_ref(&hp);
+ write_ref_thing(hp, sigdp->ref[0], sigdp->ref[1], sigdp->ref[2]);
+
+ if (op == ERTS_PROC2PORT_SIG_EXEC) {
+ /* erts_add_monitor call inside port_monitor will copy ref from hp */
+ port_monitor(prt, state,
+ sigdp->u.monitor.origin,
+ sigdp->u.monitor.name,
+ ref);
+ } else {
+ port_monitor_failure(sigdp->u.monitor.name,
+ sigdp->u.monitor.origin,
+ ref);
+ }
+ if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY) {
+ port_sched_op_reply(sigdp->caller, sigdp->ref, am_true, prt);
+ }
+ return ERTS_PORT_REDS_MONITOR;
+}
+
+/* Creates monitor between Origin and Target. Ref must be initialized to
+ * a reference (ref may be rewritten to be used to serve additionally as a
+ * signal id). Name is atom if user monitors port by name or NIL */
+ErtsPortOpResult
+erts_port_monitor(Process *origin, Port *port, Eterm name, Eterm *refp)
+{
+ ErtsProc2PortSigData *sigdp;
+ ErtsTryImmDrvCallState try_call_state
+ = ERTS_INIT_TRY_IMM_DRV_CALL_STATE(
+ origin, port, ERTS_PORT_SFLGS_INVALID_LOOKUP,
+ 0,
+ 0, /* trap_ref is always set so !trap_ref always is false */
+ am_monitor);
+
+ ASSERT(origin);
+ ASSERT(port);
+ ASSERT(is_atom(name) || is_port(name));
+ ASSERT(refp);
+
+ switch (try_imm_drv_call(&try_call_state)) {
+ case ERTS_TRY_IMM_DRV_CALL_OK:
+ port_monitor(port, try_call_state.state, origin->common.id, name, *refp);
+ finalize_imm_drv_call(&try_call_state);
+ BUMP_REDS(origin, ERTS_PORT_REDS_MONITOR);
+ return ERTS_PORT_OP_DONE;
+ case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT:
+ return ERTS_PORT_OP_BADARG;
+ default:
+ break; /* Schedule call instead... */
+ }
+
+ sigdp = erts_port_task_alloc_p2p_sig_data();
+ sigdp->flags = ERTS_P2P_SIG_TYPE_MONITOR;
+ sigdp->u.monitor.origin = origin->common.id;
+ sigdp->u.monitor.name = name; /* either named monitor, or port id */
+
+ /* Ref contents will be initialized here */
+ return erts_schedule_proc2port_signal(origin, port, origin->common.id,
+ refp, sigdp, 0, NULL,
+ port_sig_monitor);
+}
+
+static void
+port_demonitor_failure(Eterm port_id, Eterm origin, Eterm ref)
+{
+ Process *origin_p;
+ ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK;
+ ErtsMonitor *mon1;
+ ASSERT(is_internal_pid(origin));
+
+ origin_p = erts_pid2proc(NULL, 0, origin, rp_locks);
+ if (! origin_p) { return; }
+
+ /* do not send any DOWN messages, drop monitors on process */
+ mon1 = erts_remove_monitor(&ERTS_P_MONITORS(origin_p), ref);
+ if (mon1 != NULL) {
+ erts_destroy_monitor(mon1);
+ }
+
+ erts_smp_proc_unlock(origin_p, rp_locks);
+}
+
+/* Origin wants to demonitor port Prt. State contains possible error, which has
+ * happened just before. Ref is reference to monitor */
+static void
+port_demonitor(Port *port, erts_aint32_t state, Eterm origin, Eterm ref)
+{
+ ASSERT(port);
+ ASSERT(is_pid(origin));
+ ASSERT(is_internal_ref(ref));
+
+ if (!(state & ERTS_PORT_SFLGS_INVALID_LOOKUP)) {
+ ErtsProcLocks p_locks = ERTS_PROC_LOCK_LINK;
+ Process *origin_p = erts_pid2proc(NULL, 0, origin, p_locks);
+ if (origin_p) {
+ ErtsMonitor *mon1 = erts_remove_monitor(&ERTS_P_MONITORS(origin_p),
+ ref);
+ if (mon1 != NULL) {
+ erts_destroy_monitor(mon1);
+ }
+ }
+ if (1) {
+ ErtsMonitor *mon2 = erts_remove_monitor(&ERTS_P_MONITORS(port),
+ ref);
+ if (mon2 != NULL) {
+ erts_destroy_monitor(mon2);
+ }
+ }
+ if (origin_p) { /* when origin is dying, it won't be found */
+ erts_smp_proc_unlock(origin_p, p_locks);
+ }
+ } else {
+ port_demonitor_failure(port->common.id, origin, ref);
+ }
+}
+
+static int
+port_sig_demonitor(Port *prt, erts_aint32_t state, int op,
+ ErtsProc2PortSigData *sigdp)
+{
+ Eterm hp[REF_THING_SIZE];
+ Eterm ref = make_internal_ref(&hp);
+ write_ref_thing(hp, sigdp->u.demonitor.ref[0],
+ sigdp->u.demonitor.ref[1],
+ sigdp->u.demonitor.ref[2]);
+ if (op == ERTS_PROC2PORT_SIG_EXEC) {
+ port_demonitor(prt, state, sigdp->u.demonitor.origin, ref);
+ } else {
+ port_demonitor_failure(sigdp->u.demonitor.name,
+ sigdp->u.demonitor.origin,
+ ref);
+ }
+ if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY) {
+ port_sched_op_reply(sigdp->caller, sigdp->ref, am_true, prt);
+ }
+ return ERTS_PORT_REDS_DEMONITOR;
+}
+
+/* Removes monitor between origin and target, identified by ref.
+ * Mode defines normal or relaxed demonitor rules (process is at death) */
+ErtsPortOpResult erts_port_demonitor(Process *origin, ErtsDemonitorMode mode,
+ Port *target, Eterm ref,
+ Eterm *trap_ref)
+{
+ Process *c_p = mode == ERTS_PORT_DEMONITOR_NORMAL ? origin : NULL;
+ ErtsProc2PortSigData *sigdp;
+ ErtsTryImmDrvCallState try_call_state
+ = ERTS_INIT_TRY_IMM_DRV_CALL_STATE(
+ c_p,
+ target, ERTS_PORT_SFLGS_INVALID_LOOKUP,
+ 0,
+ !trap_ref,
+ am_demonitor);
+
+ ASSERT(origin);
+ ASSERT(target);
+ ASSERT(is_internal_ref(ref));
+
+ switch (try_imm_drv_call(&try_call_state)) {
+ case ERTS_TRY_IMM_DRV_CALL_OK:
+ port_demonitor(target, try_call_state.state, origin->common.id, ref);
+ finalize_imm_drv_call(&try_call_state);
+ if (mode == ERTS_PORT_DEMONITOR_NORMAL) {
+ BUMP_REDS(origin, ERTS_PORT_REDS_DEMONITOR);
+ }
+ return ERTS_PORT_OP_DONE;
+ case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT:
+ return ERTS_PORT_OP_BADARG;
+ default:
+ break; /* Schedule call instead... */
+ }
+
+ sigdp = erts_port_task_alloc_p2p_sig_data();
+ sigdp->flags = ERTS_P2P_SIG_TYPE_DEMONITOR;
+ sigdp->u.demonitor.origin = origin->common.id;
+ sigdp->u.demonitor.name = target->common.id;
+ {
+ RefThing *reft = ref_thing_ptr(ref);
+ /* Start from 1 skip ref arity */
+ sys_memcpy(sigdp->u.demonitor.ref,
+ internal_thing_ref_numbers(reft),
+ sizeof(sigdp->u.demonitor.ref));
+ }
+
+ /* Ref contents will be initialized here */
+ return erts_schedule_proc2port_signal(c_p, target, origin->common.id,
+ trap_ref, sigdp, 0, NULL,
+ port_sig_demonitor);
+}
+
+static void
+init_ack_send_reply(Port *port, Eterm resp)
+{
+
+ if (!is_internal_port(resp)) {
+ Process *rp = erts_proc_lookup_raw(port->async_open_port->to);
+ erts_smp_proc_lock(rp, ERTS_PROC_LOCK_LINK);
+ erts_remove_link(&ERTS_P_LINKS(port), port->async_open_port->to);
+ erts_remove_link(&ERTS_P_LINKS(rp), port->common.id);
+ erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
+ }
+ port_sched_op_reply(port->async_open_port->to,
+ port->async_open_port->ref,
+ resp,
+ port);
+
+ erts_free(ERTS_ALC_T_PRTSD, port->async_open_port);
+ port->async_open_port = NULL;
+}
+
+void
+erl_drv_init_ack(ErlDrvPort ix, ErlDrvData res) {
+ Port *port = erts_drvport2port(ix);
+ SWord err_type = (SWord)res;
+ Eterm resp;
+
+ if (port == ERTS_INVALID_ERL_DRV_PORT && port->async_open_port)
+ return;
+
+ if (port->async_open_port) {
+ switch(err_type) {
+ case -3:
+ resp = am_badarg;
+ break;
+ case -2: {
+ char *str = erl_errno_id(errno);
+ resp = erts_atom_put((byte *) str, strlen(str),
+ ERTS_ATOM_ENC_LATIN1, 1);
+ break;
+ }
+ case -1:
+ resp = am_einval;
+ break;
+ default:
+ resp = port->common.id;
+ break;
+ }
+
+ init_ack_send_reply(port, resp);
+
+ if (err_type == -1 || err_type == -2 || err_type == -3)
+ driver_failure_term(ix, am_normal, 0);
+ port->drv_data = err_type;
+ }
+}
+
+void
+erl_drv_set_os_pid(ErlDrvPort ix, ErlDrvSInt pid) {
+ Port *port = erts_drvport2port(ix);
+
+ if (port == ERTS_INVALID_ERL_DRV_PORT)
+ return;
+
+ port->os_pid = (SWord)pid;
+
+}
+
void erts_init_io(int port_tab_size,
int port_tab_size_ignore_files,
int legacy_port_tab)
@@ -2806,10 +3444,11 @@ void erts_init_io(int port_tab_size,
erts_smp_rwmtx_rwlock(&erts_driver_list_lock);
init_driver(&fd_driver, &fd_driver_entry, NULL);
-#ifndef __OSE__
init_driver(&vanilla_driver, &vanilla_driver_entry, NULL);
-#endif
init_driver(&spawn_driver, &spawn_driver_entry, NULL);
+#ifndef __WIN32__
+ init_driver(&forker_driver, &forker_driver_entry, NULL);
+#endif
erts_init_static_drivers();
for (dp = driver_tab; *dp != NULL; dp++)
erts_add_driver_entry(*dp, NULL, 1);
@@ -2871,6 +3510,9 @@ void erts_lcnt_enable_io_lock_count(int enable) {
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) {
@@ -3050,7 +3692,7 @@ static int read_linebuf(LineBufContext *bp)
}
static void
-deliver_result(Eterm sender, Eterm pid, Eterm res)
+deliver_result(Port *prt, Eterm sender, Eterm pid, Eterm res)
{
Process *rp;
ErtsProcLocks rp_locks = 0;
@@ -3058,24 +3700,35 @@ deliver_result(Eterm sender, Eterm pid, Eterm res)
ERTS_SMP_CHK_NO_PROC_LOCKS;
+ ASSERT(!prt || prt->common.id == sender);
+#ifdef ERTS_SMP
+ ASSERT(!prt || erts_lc_is_port_locked(prt));
+#endif
+
ASSERT(is_internal_port(sender) && is_internal_pid(pid));
rp = (scheduler
? erts_proc_lookup(pid)
: erts_pid2proc_opt(NULL, 0, pid, 0, ERTS_P2P_FLG_INC_REFC));
+ if (prt && IS_TRACED_FL(prt, F_TRACE_SEND)) {
+ Eterm hp[3];
+ trace_port_send(prt, pid, TUPLE2(hp, sender, res), !!rp);
+ }
+
if (rp) {
Eterm tuple;
- ErlHeapFragment *bp;
+ ErtsMessage *mp;
ErlOffHeap *ohp;
Eterm* hp;
Uint sz_res;
sz_res = size_object(res);
- hp = erts_alloc_message_heap(sz_res + 3, &bp, &ohp, rp, &rp_locks);
+ mp = erts_alloc_message_heap(rp, &rp_locks,
+ sz_res + 3, &hp, &ohp);
res = copy_struct(res, sz_res, &hp, ohp);
tuple = TUPLE2(hp, sender, res);
- erts_queue_message(rp, &rp_locks, bp, tuple, NIL);
+ erts_queue_message(rp, rp_locks, mp, tuple, sender);
if (rp_locks)
erts_smp_proc_unlock(rp, rp_locks);
@@ -3103,10 +3756,11 @@ static void deliver_read_message(Port* prt, erts_aint32_t state, Eterm to,
Eterm tuple;
Process* rp;
Eterm* hp;
- ErlHeapFragment *bp;
+ ErtsMessage *mp;
ErlOffHeap *ohp;
ErtsProcLocks rp_locks = 0;
int scheduler = erts_get_scheduler_id() != 0;
+ int trace_send = IS_TRACED_FL(prt, F_TRACE_SEND);
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
ERTS_SMP_CHK_NO_PROC_LOCKS;
@@ -3129,7 +3783,7 @@ static void deliver_read_message(Port* prt, erts_aint32_t state, Eterm to,
if (!rp)
return;
- hp = erts_alloc_message_heap(need, &bp, &ohp, rp, &rp_locks);
+ mp = erts_alloc_message_heap(trace_send ? NULL : rp, &rp_locks, need, &hp, &ohp);
listp = NIL;
if ((state & ERTS_PORT_SFLG_BINARY_IO) == 0) {
@@ -3171,7 +3825,11 @@ static void deliver_read_message(Port* prt, erts_aint32_t state, Eterm to,
tuple = TUPLE2(hp, prt->common.id, tuple);
hp += 3;
- erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined);
+ if (trace_send)
+ trace_port_send(prt, to, tuple, 1);
+
+ ERL_MESSAGE_TOKEN(mp) = am_undefined;
+ erts_queue_message(rp, rp_locks, mp, tuple, prt->common.id);
if (rp_locks)
erts_smp_proc_unlock(rp, rp_locks);
if (!scheduler)
@@ -3245,11 +3903,12 @@ deliver_vec_message(Port* prt, /* Port */
Eterm tuple;
Process* rp;
Eterm* hp;
- ErlHeapFragment *bp;
+ ErtsMessage *mp;
ErlOffHeap *ohp;
ErtsProcLocks rp_locks = 0;
int scheduler = erts_get_scheduler_id() != 0;
erts_aint32_t state;
+ int trace_send = IS_TRACED_FL(prt, F_TRACE_SEND);
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
ERTS_SMP_CHK_NO_PROC_LOCKS;
@@ -3277,7 +3936,7 @@ deliver_vec_message(Port* prt, /* Port */
need += (hlen+csize)*2;
}
- hp = erts_alloc_message_heap(need, &bp, &ohp, rp, &rp_locks);
+ mp = erts_alloc_message_heap(trace_send ? NULL : rp, &rp_locks, need, &hp, &ohp);
listp = NIL;
iov += vsize;
@@ -3338,7 +3997,11 @@ deliver_vec_message(Port* prt, /* Port */
tuple = TUPLE2(hp, prt->common.id, tuple);
hp += 3;
- erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined);
+ if (IS_TRACED_FL(prt, F_TRACE_SEND))
+ trace_port_send(prt, to, tuple, 1);
+
+ ERL_MESSAGE_TOKEN(mp) = am_undefined;
+ erts_queue_message(rp, rp_locks, mp, tuple, prt->common.id);
erts_smp_proc_unlock(rp, rp_locks);
if (!scheduler)
erts_proc_dec_refc(rp);
@@ -3378,18 +4041,32 @@ static void flush_port(Port *p)
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(p));
if (p->drv_ptr->flush != NULL) {
+ ERTS_MSACC_PUSH_STATE_M();
#ifdef USE_VM_PROBES
if (DTRACE_ENABLED(driver_flush)) {
DTRACE_FORMAT_COMMON_PID_AND_PORT(ERTS_PORT_GET_CONNECTED(p), p)
DTRACE3(driver_flush, process_str, port_str, p->name);
}
#endif
+#ifdef USE_LTTNG_VM_TRACEPOINTS
+ if (LTTNG_ENABLED(driver_flush)) {
+ lttng_decl_portbuf(port_str);
+ lttng_decl_procbuf(proc_str);
+ lttng_pid_to_str(ERTS_PORT_GET_CONNECTED(p), proc_str);
+ lttng_port_to_str(p, port_str);
+ LTTNG3(driver_flush, proc_str, port_str, p->name);
+ }
+#endif
+
+
if (IS_TRACED_FL(p, F_TRACE_SCHED_PORTS)) {
trace_sched_ports_where(p, am_in, am_flush);
}
+ ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_PORT);
fpe_was_unmasked = erts_block_fpe();
(*p->drv_ptr->flush)((ErlDrvData)p->drv_data);
erts_unblock_fpe(fpe_was_unmasked);
+ ERTS_MSACC_POP_STATE_M();
if (IS_TRACED_FL(p, F_TRACE_SCHED_PORTS)) {
trace_sched_ports_where(p, am_out, am_flush);
}
@@ -3413,6 +4090,7 @@ terminate_port(Port *prt)
Eterm connected_id = NIL /* Initialize to silence compiler */;
erts_driver_t *drv;
erts_aint32_t state;
+ ErtsPrtSD *psd;
ERTS_SMP_CHK_NO_PROC_LOCKS;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
@@ -3437,20 +4115,37 @@ terminate_port(Port *prt)
drv = prt->drv_ptr;
if ((drv != NULL) && (drv->stop != NULL)) {
int fpe_was_unmasked = erts_block_fpe();
+ ERTS_MSACC_PUSH_AND_SET_STATE_M(ERTS_MSACC_STATE_PORT);
#ifdef USE_VM_PROBES
if (DTRACE_ENABLED(driver_stop)) {
DTRACE_FORMAT_COMMON_PID_AND_PORT(connected_id, prt)
DTRACE3(driver_stop, process_str, drv->name, port_str);
}
#endif
+#ifdef USE_LTTNG_VM_TRACEPOINTS
+ if (LTTNG_ENABLED(driver_stop)) {
+ lttng_decl_portbuf(port_str);
+ lttng_decl_procbuf(proc_str);
+ lttng_pid_to_str(connected_id, proc_str);
+ lttng_port_to_str(prt, port_str);
+ LTTNG3(driver_stop, proc_str, port_str, drv->name);
+ }
+#endif
+
(*drv->stop)((ErlDrvData)prt->drv_data);
erts_unblock_fpe(fpe_was_unmasked);
+ ERTS_MSACC_POP_STATE_M();
#ifdef ERTS_SMP
if (prt->xports)
erts_port_handle_xports(prt);
ASSERT(!prt->xports);
#endif
}
+
+ if (is_internal_port(send_closed_port_id)
+ && IS_TRACED_FL(prt, F_TRACE_SEND))
+ trace_port_send(prt, connected_id, am_closed, 1);
+
if(drv->handle != NULL) {
erts_smp_rwmtx_rlock(&erts_driver_list_lock);
erts_ddll_decrement_port_count(drv->handle);
@@ -3464,8 +4159,9 @@ terminate_port(Port *prt)
erts_cleanup_port_data(prt);
- if (prt->psd)
- erts_free(ERTS_ALC_T_PRTSD, prt->psd);
+ psd = (ErtsPrtSD *) erts_smp_atomic_read_nob(&prt->psd);
+ if (psd)
+ erts_free(ERTS_ALC_T_PRTSD, psd);
ASSERT(prt->dist_entry == NULL);
@@ -3481,7 +4177,7 @@ terminate_port(Port *prt)
erts_flush_async_exit(erts_halt_code, "");
}
if (is_internal_port(send_closed_port_id))
- deliver_result(send_closed_port_id, connected_id, am_closed);
+ deliver_result(NULL, send_closed_port_id, connected_id, am_closed);
}
void
@@ -3490,23 +4186,30 @@ erts_terminate_port(Port *pp)
terminate_port(pp);
}
+static void port_fire_one_monitor(ErtsMonitor *mon, void *ctx0);
static void sweep_one_monitor(ErtsMonitor *mon, void *vpsc)
{
- ErtsMonitor *rmon;
- Process *rp;
+ switch (mon->type) {
+ case MON_ORIGIN: {
+ ErtsMonitor *rmon;
+ Process *rp;
- ASSERT(mon->type == MON_ORIGIN);
- ASSERT(is_internal_pid(mon->pid));
- rp = erts_pid2proc(NULL, 0, mon->pid, ERTS_PROC_LOCK_LINK);
- if (!rp) {
- goto done;
- }
- rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref);
- erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- if (rmon == NULL) {
- goto done;
+ ASSERT(is_internal_pid(mon->pid));
+ rp = erts_pid2proc(NULL, 0, mon->pid, ERTS_PROC_LOCK_LINK);
+ if (!rp) {
+ goto done;
+ }
+ rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref);
+ erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
+ if (rmon == NULL) {
+ goto done;
+ }
+ erts_destroy_monitor(rmon);
+ } break;
+ case MON_TARGET: {
+ port_fire_one_monitor(mon, vpsc); /* forward call */
+ } break;
}
- erts_destroy_monitor(rmon);
done:
erts_destroy_monitor(mon);
}
@@ -3514,7 +4217,7 @@ static void sweep_one_monitor(ErtsMonitor *mon, void *vpsc)
typedef struct {
- Eterm port;
+ Port *port;
Eterm reason;
} SweepContext;
@@ -3523,10 +4226,13 @@ static void sweep_one_link(ErtsLink *lnk, void *vpsc)
SweepContext *psc = vpsc;
DistEntry *dep;
Process *rp;
-
+ Eterm port_id = psc->port->common.id;
ASSERT(lnk->type == LINK_PID);
-
+
+ if (IS_TRACED_FL(psc->port, F_TRACE_PORTS))
+ trace_port(psc->port, am_unlink, lnk->pid);
+
if (is_external_pid(lnk->pid)) {
dep = external_pid_dist_entry(lnk->pid);
if(dep != erts_this_dist_entry) {
@@ -3539,9 +4245,9 @@ static void sweep_one_link(ErtsLink *lnk, void *vpsc)
case ERTS_DSIG_PREP_NOT_CONNECTED:
break;
case ERTS_DSIG_PREP_CONNECTED:
- erts_remove_dist_link(&dld, psc->port, lnk->pid, dep);
+ erts_remove_dist_link(&dld, port_id, lnk->pid, dep);
erts_destroy_dist_link(&dld);
- code = erts_dsig_send_exit(&dsd, psc->port, lnk->pid,
+ code = erts_dsig_send_exit(&dsd, port_id, lnk->pid,
psc->reason);
ASSERT(code == ERTS_DSIG_SEND_OK);
break;
@@ -3555,23 +4261,25 @@ static void sweep_one_link(ErtsLink *lnk, void *vpsc)
ASSERT(is_internal_pid(lnk->pid));
rp = erts_pid2proc(NULL, 0, lnk->pid, rp_locks);
if (rp) {
- ErtsLink *rlnk = erts_remove_link(&ERTS_P_LINKS(rp), psc->port);
+ ErtsLink *rlnk = erts_remove_link(&ERTS_P_LINKS(rp), port_id);
if (rlnk) {
int xres = erts_send_exit_signal(NULL,
- psc->port,
+ port_id,
rp,
&rp_locks,
psc->reason,
NIL,
NULL,
0);
- if (xres >= 0 && IS_TRACED_FL(rp, F_TRACE_PROCS)) {
+ if (xres >= 0) {
+ if (rp_locks & ERTS_PROC_LOCKS_XSIG_SEND) {
+ erts_smp_proc_unlock(rp, ERTS_PROC_LOCKS_XSIG_SEND);
+ rp_locks &= ~ERTS_PROC_LOCKS_XSIG_SEND;
+ }
/* We didn't exit the process and it is traced */
- if (IS_TRACED_FL(rp, F_TRACE_PROCS)) {
- trace_proc(NULL, rp, am_getting_unlinked,
- psc->port);
- }
+ if (IS_TRACED_FL(rp, F_TRACE_PROCS))
+ trace_proc(NULL, 0, rp, am_getting_unlinked, port_id);
}
erts_destroy_link(rlnk);
}
@@ -3582,6 +4290,43 @@ static void sweep_one_link(ErtsLink *lnk, void *vpsc)
erts_destroy_link(lnk);
}
+static void
+port_fire_one_monitor(ErtsMonitor *mon, void *ctx0)
+{
+ Process *origin;
+ ErtsProcLocks origin_locks;
+
+ if (mon->type != MON_TARGET || ! is_pid(mon->pid)) {
+ return;
+ }
+ /*
+ * Proceed here if someone monitors us, we (port) are the target and
+ * origin is some process
+ */
+ origin_locks = ERTS_PROC_LOCKS_MSG_SEND | ERTS_PROC_LOCK_LINK;
+
+ origin = erts_pid2proc(NULL, 0, mon->pid, origin_locks);
+ if (origin) {
+ DeclareTmpHeapNoproc(lhp,3);
+ SweepContext *ctx = (SweepContext *)ctx0;
+ ErtsMonitor *rmon;
+ Eterm watched = (is_atom(mon->name)
+ ? TUPLE2(lhp, mon->name, erts_this_dist_entry->sysname)
+ : ctx->port->common.id);
+
+ erts_queue_monitor_message(origin, &origin_locks, mon->ref, am_port,
+ watched, ctx->reason);
+ UnUseTmpHeapNoproc(3);
+
+ rmon = erts_remove_monitor(&ERTS_P_MONITORS(origin), mon->ref);
+ erts_smp_proc_unlock(origin, origin_locks);
+
+ if (rmon) {
+ erts_destroy_monitor(rmon);
+ }
+ }
+}
+
/* 'from' is sending 'this_port' an exit signal, (this_port must be internal).
* If reason is normal we don't do anything, *unless* from is our connected
* process in which case we close the port. Any other reason kills the port.
@@ -3593,91 +4338,95 @@ static void sweep_one_link(ErtsLink *lnk, void *vpsc)
*/
int
-erts_deliver_port_exit(Port *p, Eterm from, Eterm reason, int send_closed)
+erts_deliver_port_exit(Port *prt, Eterm from, Eterm reason, int send_closed,
+ int drop_normal)
{
ErtsLink *lnk;
- Eterm rreason;
+ Eterm modified_reason;
erts_aint32_t state, set_state_flags;
ERTS_SMP_CHK_NO_PROC_LOCKS;
- ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(p));
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
- rreason = (reason == am_kill) ? am_killed : reason;
+ modified_reason = (reason == am_kill) ? am_killed : reason;
#ifdef USE_VM_PROBES
if (DTRACE_ENABLED(port_exit)) {
DTRACE_CHARBUF(from_str, DTRACE_TERM_BUF_SIZE);
DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE);
- DTRACE_CHARBUF(rreason_str, 64);
+ DTRACE_CHARBUF(reason_str, 64);
erts_snprintf(from_str, sizeof(DTRACE_CHARBUF_NAME(from_str)), "%T", from);
- dtrace_port_str(p, port_str);
- erts_snprintf(rreason_str, sizeof(DTRACE_CHARBUF_NAME(rreason_str)), "%T", rreason);
- DTRACE4(port_exit, from_str, port_str, p->name, rreason_str);
+ dtrace_port_str(prt, port_str);
+ erts_snprintf(reason_str, sizeof(DTRACE_CHARBUF_NAME(reason_str)), "%T",
+ modified_reason);
+ DTRACE4(port_exit, from_str, port_str, prt->name, reason_str);
}
#endif
- state = erts_atomic32_read_nob(&p->state);
+ state = erts_atomic32_read_nob(&prt->state);
if (state & (ERTS_PORT_SFLGS_DEAD
| ERTS_PORT_SFLG_EXITING
| ERTS_PORT_SFLG_CLOSING))
return 0;
- if (reason == am_normal && from != ERTS_PORT_GET_CONNECTED(p) && from != p->common.id)
+ if (reason == am_normal && from != ERTS_PORT_GET_CONNECTED(prt)
+ && from != prt->common.id && drop_normal) {
return 0;
+ }
set_state_flags = ERTS_PORT_SFLG_EXITING;
if (send_closed)
set_state_flags |= ERTS_PORT_SFLG_SEND_CLOSED;
- erts_port_task_sched_enter_exiting_state(&p->sched);
+ erts_port_task_sched_enter_exiting_state(&prt->sched);
- state = erts_atomic32_read_bor_mb(&p->state, set_state_flags);
+ state = erts_atomic32_read_bor_mb(&prt->state, set_state_flags);
state |= set_state_flags;
- if (IS_TRACED_FL(p, F_TRACE_PORTS)) {
- trace_port(p, am_closed, reason);
- }
+ if (IS_TRACED_FL(prt, F_TRACE_PORTS))
+ trace_port(prt, am_closed, reason);
- erts_trace_check_exiting(p->common.id);
+ erts_trace_check_exiting(prt->common.id);
- set_busy_port(ERTS_Port2ErlDrvPort(p), 0);
+ set_busy_port(ERTS_Port2ErlDrvPort(prt), 0);
- if (p->common.u.alive.reg != NULL)
- (void) erts_unregister_name(NULL, 0, p, p->common.u.alive.reg->name);
+ if (prt->common.u.alive.reg != NULL)
+ (void) erts_unregister_name(NULL, 0, prt, prt->common.u.alive.reg->name);
{
- SweepContext sc = {p->common.id, rreason};
- lnk = ERTS_P_LINKS(p);
- ERTS_P_LINKS(p) = NULL;
+ SweepContext sc = {prt, modified_reason};
+ lnk = ERTS_P_LINKS(prt);
+ ERTS_P_LINKS(prt) = NULL;
erts_sweep_links(lnk, &sweep_one_link, &sc);
}
- DRV_MONITOR_LOCK_PDL(p);
+ DRV_MONITOR_LOCK_PDL(prt);
{
- ErtsMonitor *moni = ERTS_P_MONITORS(p);
- ERTS_P_MONITORS(p) = NULL;
- erts_sweep_monitors(moni, &sweep_one_monitor, NULL);
+ SweepContext ctx = {prt, modified_reason};
+ ErtsMonitor *moni = ERTS_P_MONITORS(prt);
+ ERTS_P_MONITORS(prt) = NULL;
+ erts_sweep_monitors(moni, &sweep_one_monitor, &ctx);
}
- DRV_MONITOR_UNLOCK_PDL(p);
+ DRV_MONITOR_UNLOCK_PDL(prt);
- if ((state & ERTS_PORT_SFLG_DISTRIBUTION) && p->dist_entry) {
- erts_do_net_exits(p->dist_entry, rreason);
- erts_deref_dist_entry(p->dist_entry);
- p->dist_entry = NULL;
- erts_atomic32_read_band_relb(&p->state,
+ if ((state & ERTS_PORT_SFLG_DISTRIBUTION) && prt->dist_entry) {
+ erts_do_net_exits(prt->dist_entry, modified_reason);
+ erts_deref_dist_entry(prt->dist_entry);
+ prt->dist_entry = NULL;
+ erts_atomic32_read_band_relb(&prt->state,
~ERTS_PORT_SFLG_DISTRIBUTION);
}
- if ((reason != am_kill) && !is_port_ioq_empty(p)) {
+ if ((reason != am_kill) && !is_port_ioq_empty(prt)) {
/* must turn exiting flag off */
- erts_atomic32_read_bset_relb(&p->state,
+ erts_atomic32_read_bset_relb(&prt->state,
(ERTS_PORT_SFLG_EXITING
| ERTS_PORT_SFLG_CLOSING),
ERTS_PORT_SFLG_CLOSING);
- flush_port(p);
+ flush_port(prt);
}
else {
- terminate_port(p);
+ terminate_port(prt);
}
return 1;
@@ -3755,6 +4504,7 @@ call_driver_control(Eterm caller,
ErlDrvSizeT *from_size)
{
ErlDrvSSizeT cres;
+ ERTS_MSACC_PUSH_STATE_M();
if (!prt->drv_ptr->control)
return ERTS_PORT_OP_BADARG;
@@ -3769,6 +4519,21 @@ call_driver_control(Eterm caller,
}
#endif
+ if (IS_TRACED_FL(prt, F_TRACE_RECEIVE))
+ trace_port_receive(prt, caller, am_control, command, bufp, size);
+
+ ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_PORT);
+
+#ifdef USE_LTTNG_VM_TRACEPOINTS
+ if (LTTNG_ENABLED(driver_control)) {
+ lttng_decl_procbuf(proc_str);
+ lttng_decl_portbuf(port_str);
+ lttng_pid_to_str(caller, proc_str);
+ lttng_port_to_str(prt, port_str);
+ LTTNG5(driver_control, proc_str, port_str, prt->name, command, size);
+ }
+#endif
+
prt->caller = caller;
cres = prt->drv_ptr->control((ErlDrvData) prt->drv_data,
command,
@@ -3778,9 +4543,14 @@ call_driver_control(Eterm caller,
*from_size);
prt->caller = NIL;
+ ERTS_MSACC_POP_STATE_M();
+
if (cres < 0)
return ERTS_PORT_OP_BADARG;
+ if (IS_TRACED_FL(prt, F_TRACE_SEND))
+ trace_port_send_binary(prt, caller, am_control, *resp_bufp, cres);
+
*from_size = (ErlDrvSizeT) cres;
return ERTS_PORT_OP_DONE;
@@ -3829,7 +4599,6 @@ write_port_control_result(int control_flags,
ErlDrvSizeT resp_size,
char *pre_alloc_buf,
Eterm **hpp,
- ErlHeapFragment *bp,
ErlOffHeap *ohp)
{
Eterm res;
@@ -3903,16 +4672,13 @@ port_sig_control(Port *prt,
if (res == ERTS_PORT_OP_DONE) {
Eterm msg;
- Eterm *hp;
- ErlHeapFragment *bp;
- ErlOffHeap *ohp;
ErtsHeapFactory factory;
Process *rp;
ErtsProcLocks rp_locks = 0;
Uint hsz, rsz;
int control_flags;
- rp = erts_proc_lookup_raw(sigdp->caller);
+ rp = sigdp->caller == ERTS_INVALID_PID ? NULL : erts_proc_lookup_raw(sigdp->caller);
if (!rp)
goto done;
@@ -3925,27 +4691,21 @@ port_sig_control(Port *prt,
hsz = rsz + ERTS_QUEUE_PORT_SCHED_OP_REPLY_SIZE;
- hp = erts_alloc_message_heap(hsz,
- &bp,
- &ohp,
- rp,
- &rp_locks);
- erts_factory_message_init(&factory, rp, hp, bp);
+ (void) erts_factory_message_create(&factory, rp,
+ &rp_locks, hsz);
msg = write_port_control_result(control_flags,
resp_bufp,
resp_size,
&resp_buf[0],
- &hp,
- bp,
- ohp);
- factory.hp = hp;
-
+ &factory.hp,
+ factory.off_heap);
queue_port_sched_op_reply(rp,
- &rp_locks,
+ rp_locks,
&factory,
sigdp->ref,
- msg);
+ msg,
+ prt);
if (rp_locks)
erts_smp_proc_unlock(rp, rp_locks);
@@ -3955,7 +4715,8 @@ port_sig_control(Port *prt,
/* failure */
- port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg);
+ if (sigdp->caller != ERTS_INVALID_PID)
+ port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg, prt);
done:
@@ -3965,6 +4726,23 @@ done:
return ERTS_PORT_REDS_CONTROL;
}
+/*
+ * This is an asynchronous control call. I.e. it will not return anything
+ * to the caller.
+ */
+int
+erl_drv_port_control(Eterm port_num, char cmd, char* buff, ErlDrvSizeT size)
+{
+ ErtsProc2PortSigData *sigdp = erts_port_task_alloc_p2p_sig_data();
+
+ sigdp->flags = ERTS_P2P_SIG_TYPE_CONTROL | ERTS_P2P_SIG_DATA_FLG_REPLY;
+ sigdp->u.control.binp = NULL;
+ sigdp->u.control.command = cmd;
+ sigdp->u.control.bufp = buff;
+ sigdp->u.control.size = size;
+
+ return erts_schedule_port2port_signal(port_num, sigdp, 0, port_sig_control);
+}
ErtsPortOpResult
erts_port_control(Process* c_p,
@@ -4081,7 +4859,6 @@ erts_port_control(Process* c_p,
resp_size,
&resp_buf[0],
&hp,
- NULL,
&c_p->off_heap);
BUMP_REDS(c_p, ERTS_PORT_REDS_CONTROL);
return ERTS_PORT_OP_DONE;
@@ -4103,14 +4880,28 @@ erts_port_control(Process* c_p,
binp = NULL;
if (is_binary(data) && binary_bitoffset(data) == 0) {
- Eterm *ebinp = binary_val_rel(data, NULL);
+ Eterm *ebinp = binary_val(data);
ASSERT(!tmp_alloced);
if (*ebinp == HEADER_SUB_BIN)
- ebinp = binary_val_rel(((ErlSubBin *) ebinp)->orig, NULL);
+ ebinp = binary_val(((ErlSubBin *) ebinp)->orig);
+
if (*ebinp != HEADER_PROC_BIN)
copy = 1;
else {
- binp = ((ProcBin *) ebinp)->val;
+ ProcBin *pb = (ProcBin *) ebinp;
+ int offset = bufp - pb->val->orig_bytes;
+
+ ASSERT(pb->val->orig_bytes <= bufp
+ && bufp + size <= pb->val->orig_bytes + pb->val->orig_size);
+
+ if (pb->flags) {
+ erts_emasculate_writable_binary(pb);
+
+ /* The procbin may have been reallocated, so update bufp */
+ bufp = pb->val->orig_bytes + offset;
+ }
+
+ binp = pb->val;
ASSERT(bufp <= bufp + size);
ASSERT(binp->orig_bytes <= bufp
&& bufp + size <= binp->orig_bytes + binp->orig_size);
@@ -4159,6 +4950,7 @@ call_driver_call(Eterm caller,
unsigned *ret_flagsp)
{
ErlDrvSSizeT cres;
+ ERTS_MSACC_PUSH_STATE_M();
if (!prt->drv_ptr->call)
return ERTS_PORT_OP_BADARG;
@@ -4173,6 +4965,20 @@ call_driver_call(Eterm caller,
DTRACE5(driver_call, process_str, port_str, prt->name, command, size);
}
#endif
+#ifdef USE_LTTNG_VM_TRACEPOINTS
+ if (LTTNG_ENABLED(driver_call)) {
+ lttng_decl_procbuf(proc_str);
+ lttng_decl_portbuf(port_str);
+ lttng_pid_to_str(caller,proc_str);
+ lttng_port_to_str(prt, port_str);
+ LTTNG5(driver_call, proc_str, port_str, prt->name, command, size);
+ }
+#endif
+
+ if (IS_TRACED_FL(prt, F_TRACE_RECEIVE))
+ trace_port_receive(prt, caller, am_call, command, bufp, size);
+
+ ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_PORT);
prt->caller = caller;
cres = prt->drv_ptr->call((ErlDrvData) prt->drv_data,
@@ -4184,10 +4990,15 @@ call_driver_call(Eterm caller,
ret_flagsp);
prt->caller = NIL;
+ ERTS_MSACC_POP_STATE_M();
+
if (cres <= 0
|| ((byte) (*resp_bufp)[0]) != VERSION_MAGIC)
return ERTS_PORT_OP_BADARG;
+ if (IS_TRACED_FL(prt, F_TRACE_SEND))
+ trace_port_send_binary(prt, caller, am_call, *resp_bufp, cres);
+
*from_size = (ErlDrvSizeT) cres;
return ERTS_PORT_OP_DONE;
@@ -4240,22 +5051,15 @@ port_sig_call(Port *prt,
hsz = erts_decode_ext_size((byte *) resp_bufp, resp_size);
if (hsz >= 0) {
- ErlHeapFragment* bp;
- ErlOffHeap* ohp;
ErtsHeapFactory factory;
byte *endp;
hsz += 3; /* ok tuple */
hsz += ERTS_QUEUE_PORT_SCHED_OP_REPLY_SIZE;
- hp = erts_alloc_message_heap(hsz,
- &bp,
- &ohp,
- rp,
- &rp_locks);
+ (void) erts_factory_message_create(&factory, rp, &rp_locks, hsz);
endp = (byte *) resp_bufp;
- erts_factory_message_init(&factory, rp, hp, bp);
- msg = erts_decode_ext(&factory, &endp);
+ msg = erts_decode_ext(&factory, &endp, 0);
if (is_value(msg)) {
hp = erts_produce_heap(&factory,
3,
@@ -4263,10 +5067,11 @@ port_sig_call(Port *prt,
msg = TUPLE2(hp, am_ok, msg);
queue_port_sched_op_reply(rp,
- &rp_locks,
+ rp_locks,
&factory,
sigdp->ref,
- msg);
+ msg,
+ prt);
if (rp_locks)
erts_smp_proc_unlock(rp, rp_locks);
@@ -4278,7 +5083,7 @@ port_sig_call(Port *prt,
}
}
- port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg, prt);
done:
@@ -4374,7 +5179,7 @@ erts_port_call(Process* c_p,
hsz += 3;
erts_factory_proc_prealloc_init(&factory, c_p, hsz);
endp = (byte *) resp_bufp;
- term = erts_decode_ext(&factory, &endp);
+ term = erts_decode_ext(&factory, &endp, 0);
if (term == THE_NON_VALUE)
return ERTS_PORT_OP_BADARG;
hp = erts_produce_heap(&factory,3,0);
@@ -4493,7 +5298,7 @@ port_sig_info(Port *prt,
{
ASSERT(sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY);
if (op != ERTS_PROC2PORT_SIG_EXEC)
- port_sched_op_reply(sigdp->caller, sigdp->ref, am_undefined);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, am_undefined, prt);
else {
Eterm *hp, *hp_start;
Uint hsz;
@@ -4515,12 +5320,15 @@ port_sig_info(Port *prt,
sigdp->u.info.item);
if (is_value(value)) {
ErtsHeapFactory factory;
- erts_factory_message_init(&factory, NULL, hp, bp);
+ ErtsMessage *mp = erts_alloc_message(0, NULL);
+ mp->data.heap_frag = bp;
+ erts_factory_selfcontained_message_init(&factory, mp, hp);
queue_port_sched_op_reply(rp,
- &rp_locks,
+ rp_locks,
&factory,
sigdp->ref,
- value);
+ value,
+ prt);
}
if (rp_locks)
erts_smp_proc_unlock(rp, rp_locks);
@@ -4603,8 +5411,8 @@ reply_io_bytes(void *vreq)
rp = erts_proc_lookup(req->pid);
if (rp) {
- ErlOffHeap *ohp = NULL;
- ErlHeapFragment *bp = NULL;
+ ErlOffHeap *ohp;
+ ErtsMessage *mp;
ErtsProcLocks rp_locks;
Eterm ref, msg, ein, eout, *hp;
Uint64 in, out;
@@ -4626,7 +5434,7 @@ reply_io_bytes(void *vreq)
erts_bld_uint64(NULL, &hsz, in);
erts_bld_uint64(NULL, &hsz, out);
- hp = erts_alloc_message_heap(hsz, &bp, &ohp, rp, &rp_locks);
+ mp = erts_alloc_message_heap(rp, &rp_locks, hsz, &hp, &ohp);
ref = make_internal_ref(hp);
write_ref_thing(hp, req->refn[0], req->refn[1], req->refn[2]);
@@ -4636,7 +5444,8 @@ reply_io_bytes(void *vreq)
eout = erts_bld_uint64(&hp, NULL, out);
msg = TUPLE4(hp, ref, make_small(sched_id), ein, eout);
- erts_queue_message(rp, &rp_locks, bp, msg, NIL);
+
+ erts_queue_message(rp, rp_locks, mp, msg, am_system);
if (req->sched_id == sched_id)
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
@@ -4654,7 +5463,7 @@ erts_request_io_bytes(Process *c_p)
Uint *hp;
Eterm ref;
Uint32 *refn;
- ErtsSchedulerData *esdp = ERTS_PROC_GET_SCHDATA(c_p);
+ ErtsSchedulerData *esdp = erts_proc_sched_data(c_p);
ErtsIOBytesReq *req = erts_alloc(ERTS_ALC_T_IOB_REQ,
sizeof(ErtsIOBytesReq));
@@ -4685,7 +5494,7 @@ erts_request_io_bytes(Process *c_p)
typedef struct {
- int to;
+ fmtfn_t to;
void *arg;
} prt_one_lnk_data;
@@ -4702,7 +5511,7 @@ static void prt_one_lnk(ErtsLink *lnk, void *vprtd)
}
void
-print_port_info(Port *p, int to, void *arg)
+print_port_info(Port *p, fmtfn_t to, void *arg)
{
erts_aint32_t state = erts_atomic32_read_nob(&p->state);
@@ -4742,6 +5551,10 @@ print_port_info(Port *p, int to, void *arg)
erts_print(to, arg, "Port is a file: %s\n",p->name);
} else if (p->drv_ptr == &spawn_driver) {
erts_print(to, arg, "Port controls external process: %s\n",p->name);
+#ifndef __WIN32__
+ } else if (p->drv_ptr == &forker_driver) {
+ erts_print(to, arg, "Port controls forker process: %s\n",p->name);
+#endif
} else {
erts_print(to, arg, "Port controls linked-in driver: %s\n",p->name);
}
@@ -4888,6 +5701,7 @@ int get_port_flags(ErlDrvPort ix)
void erts_raw_port_command(Port* p, byte* buf, Uint len)
{
int fpe_was_unmasked;
+ ERTS_MSACC_PUSH_STATE_M();
ERTS_SMP_CHK_NO_PROC_LOCKS;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(p));
@@ -4908,9 +5722,11 @@ void erts_raw_port_command(Port* p, byte* buf, Uint len)
DTRACE4(driver_output, "-raw-", port_str, p->name, len);
}
#endif
+ ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_PORT);
fpe_was_unmasked = erts_block_fpe();
(*p->drv_ptr->output)((ErlDrvData)p->drv_data, (char*) buf, (int) len);
erts_unblock_fpe(fpe_was_unmasked);
+ ERTS_MSACC_POP_STATE_M();
}
int async_ready(Port *p, void* data)
@@ -4922,14 +5738,25 @@ int async_ready(Port *p, void* data)
if (p) {
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(p));
if (p->drv_ptr->ready_async != NULL) {
+ ERTS_MSACC_PUSH_AND_SET_STATE_M(ERTS_MSACC_STATE_PORT);
#ifdef USE_VM_PROBES
if (DTRACE_ENABLED(driver_ready_async)) {
DTRACE_FORMAT_COMMON_PID_AND_PORT(ERTS_PORT_GET_CONNECTED(p), p)
DTRACE3(driver_ready_async, process_str, port_str, p->name);
}
#endif
+#ifdef USE_LTTNG_VM_TRACEPOINTS
+ if (LTTNG_ENABLED(driver_ready_async)) {
+ lttng_decl_portbuf(port_str);
+ lttng_decl_procbuf(proc_str);
+ lttng_pid_to_str(ERTS_PORT_GET_CONNECTED(p), proc_str);
+ lttng_port_to_str(p, port_str);
+ LTTNG3(driver_ready_async, proc_str, port_str, p->name);
+ }
+#endif
(*p->drv_ptr->ready_async)((ErlDrvData)p->drv_data, data);
need_free = 0;
+ ERTS_MSACC_POP_STATE_M();
}
erts_port_driver_callback_epilogue(p, NULL);
@@ -5081,14 +5908,15 @@ ErlDrvTermData driver_mk_term_nil(void)
void driver_report_exit(ErlDrvPort ix, int status)
{
Eterm* hp;
+ ErlOffHeap *ohp;
Eterm tuple;
Process *rp;
Eterm pid;
- ErlHeapFragment *bp = NULL;
- ErlOffHeap *ohp;
+ ErtsMessage *mp;
ErtsProcLocks rp_locks = 0;
int scheduler = erts_get_scheduler_id() != 0;
Port* prt = erts_drvport2port(ix);
+ int trace_send = IS_TRACED_FL(prt, F_TRACE_SEND);
if (prt == ERTS_INVALID_ERL_DRV_PORT)
return;
@@ -5105,13 +5933,17 @@ void driver_report_exit(ErlDrvPort ix, int status)
if (!rp)
return;
- hp = erts_alloc_message_heap(3+3, &bp, &ohp, rp, &rp_locks);
+ mp = erts_alloc_message_heap(trace_send ? NULL : rp, &rp_locks, 3+3, &hp, &ohp);
tuple = TUPLE2(hp, am_exit_status, make_small(status));
hp += 3;
tuple = TUPLE2(hp, prt->common.id, tuple);
- erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined);
+ if (IS_TRACED_FL(prt, F_TRACE_SEND))
+ trace_port_send(prt, pid, tuple, 1);
+
+ ERL_MESSAGE_TOKEN(mp) = am_undefined;
+ erts_queue_message(rp, rp_locks, mp, tuple, prt->common.id);
erts_smp_proc_unlock(rp, rp_locks);
if (!scheduler)
@@ -5205,13 +6037,13 @@ cleanup_b2t_states(struct b2t_states__ *b2tsp)
*/
static int
-driver_deliver_term(Eterm to, ErlDrvTermData* data, int len)
+driver_deliver_term(Port *prt, Eterm to, ErlDrvTermData* data, int len)
{
#define HEAP_EXTRA 200
#define ERTS_DDT_FAIL do { res = -1; goto done; } while (0)
Uint need = 0;
int depth = 0;
- int res;
+ int res = 0;
ErlDrvTermData* ptr;
ErlDrvTermData* ptr_end;
DECLARE_ESTACK(stack);
@@ -5221,7 +6053,6 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len)
ErtsProcLocks rp_locks = 0;
struct b2t_states__ b2t;
int scheduler;
- int is_heap_need_limited = 1;
ErtsSchedulerData *esdp = erts_get_scheduler_data();
ERTS_UNDEF(mess,NIL);
@@ -5260,25 +6091,17 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len)
break;
case ERL_DRV_INT: /* signed int argument */
ERTS_DDT_CHK_ENOUGH_ARGS(1);
-#if HALFWORD_HEAP
- erts_bld_sint64(NULL, &need, (Sint64)ptr[0]);
-#else
/* check for bignum */
if (!IS_SSMALL((Sint)ptr[0]))
need += BIG_UINT_HEAP_SIZE; /* use small_to_big */
-#endif
ptr++;
depth++;
break;
case ERL_DRV_UINT: /* unsigned int argument */
ERTS_DDT_CHK_ENOUGH_ARGS(1);
-#if HALFWORD_HEAP
- erts_bld_uint64(NULL, &need, (Uint64)ptr[0]);
-#else
/* check for bignum */
if (!IS_USMALL(0, (Uint)ptr[0]))
need += BIG_UINT_HEAP_SIZE; /* use small_to_big */
-#endif
ptr++;
depth++;
break;
@@ -5398,9 +6221,6 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len)
need += hsz;
ptr += 2;
depth++;
- if (size > MAP_SMALL_MAP_LIMIT*3) { /* may contain big map */
- is_heap_need_limited = 0;
- }
break;
}
case ERL_DRV_MAP: { /* int */
@@ -5408,7 +6228,6 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len)
if ((int) ptr[0] < 0) ERTS_DDT_FAIL;
if (ptr[0] > MAP_SMALL_MAP_LIMIT) {
need += HASHMAP_ESTIMATED_HEAP_SIZE(ptr[0]);
- is_heap_need_limited = 0;
} else {
need += MAP_HEADER_FLATMAP_SZ + 1 + 2*ptr[0];
}
@@ -5443,20 +6262,25 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len)
? erts_proc_lookup(to)
: erts_pid2proc_opt(NULL, 0, to, 0, ERTS_P2P_FLG_INC_REFC));
if (!rp) {
- res = 0;
- goto done;
- }
+ if (!prt || !IS_TRACED_FL(prt, F_TRACE_SEND))
+ goto done;
+ if (!erts_is_tracer_proc_enabled_send(NULL, 0, &prt->common))
+ goto done;
- /* Try copy directly to destination heap if we know there are no big maps */
- if (is_heap_need_limited) {
- ErlOffHeap *ohp;
- ErlHeapFragment* bp;
- Eterm* hp = erts_alloc_message_heap(need, &bp, &ohp, rp, &rp_locks);
- erts_factory_message_init(&factory, rp, hp, bp);
- }
- else {
- erts_factory_message_init(&factory, NULL, NULL,
- new_message_buffer(need));
+ res = -2;
+
+ /* We allocate a temporary heap to be used to create
+ the message that may be sent using tracing */
+ erts_factory_tmp_init(&factory, erts_alloc(ERTS_ALC_T_DRIVER, need*sizeof(Eterm)),
+ need, ERTS_ALC_T_DRIVER);
+
+ } else {
+ /* We force the creation of a heap fragment (rp == NULL) when send
+ tracing so that we don't have the main lock of the process while
+ tracing */
+ Process *trace_rp = prt && IS_TRACED_FL(prt, F_TRACE_SEND) ? NULL : rp;
+ (void) erts_factory_message_create(&factory, trace_rp, &rp_locks, need);
+ res = 1;
}
/*
@@ -5477,10 +6301,6 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len)
break;
case ERL_DRV_INT: /* signed int argument */
-#if HALFWORD_HEAP
- erts_reserve_heap(&factory, BIG_NEED_SIZE(2));
- mess = erts_bld_sint64(&factory.hp, NULL, (Sint64)ptr[0]);
-#else
erts_reserve_heap(&factory, BIG_UINT_HEAP_SIZE);
if (IS_SSMALL((Sint)ptr[0]))
mess = make_small((Sint)ptr[0]);
@@ -5488,15 +6308,10 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len)
mess = small_to_big((Sint)ptr[0], factory.hp);
factory.hp += BIG_UINT_HEAP_SIZE;
}
-#endif
ptr++;
break;
case ERL_DRV_UINT: /* unsigned int argument */
-#if HALFWORD_HEAP
- erts_reserve_heap(&factory, BIG_NEED_FOR_BITS(64));
- mess = erts_bld_uint64(&factory.hp, NULL, (Uint64)ptr[0]);
-#else
erts_reserve_heap(&factory, BIG_UINT_HEAP_SIZE);
if (IS_USMALL(0, (Uint)ptr[0]))
mess = make_small((Uint)ptr[0]);
@@ -5504,7 +6319,6 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len)
mess = uint_to_big((Uint)ptr[0], factory.hp);
factory.hp += BIG_UINT_HEAP_SIZE;
}
-#endif
ptr++;
break;
@@ -5730,22 +6544,45 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len)
ESTACK_PUSH(stack, mess);
}
- res = 1;
-
done:
if (res > 0) {
+ Eterm from = am_undefined;
mess = ESTACK_POP(stack); /* get resulting value */
- erts_factory_close(&factory);
+ erts_factory_trim_and_close(&factory, &mess, 1);
+
+ if (prt) {
+ if (IS_TRACED_FL(prt, F_TRACE_SEND)) {
+ trace_port_send(prt, to, mess, 1);
+ }
+ from = prt->common.id;
+ }
+
/* send message */
- erts_queue_message(rp, &rp_locks, factory.heap_frags, mess, am_undefined);
+ ERL_MESSAGE_TOKEN(factory.message) = am_undefined;
+ erts_queue_message(rp, rp_locks, factory.message, mess, from);
+ }
+ else if (res == -2) {
+ /* this clause only happens when we were requested to
+ generate a send trace, but the process to send to
+ did not exist any more */
+ mess = ESTACK_POP(stack); /* get resulting value */
+
+ trace_port_send(prt, to, mess, 0);
+
+ erts_factory_trim_and_close(&factory, &mess, 1);
+ erts_free(ERTS_ALC_T_DRIVER, factory.hp_start);
+ res = 0;
}
else {
if (b2t.ix > b2t.used)
b2t.used = b2t.ix;
for (b2t.ix = 0; b2t.ix < b2t.used; b2t.ix++)
erts_binary2term_abort(&b2t.state[b2t.ix]);
- erts_factory_undo(&factory);
+ if (factory.mode != FACTORY_CLOSED) {
+ ERL_MESSAGE_TERM(factory.message) = am_undefined;
+ erts_factory_undo(&factory);
+ }
}
if (rp) {
if (rp_locks)
@@ -5761,22 +6598,27 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len)
}
static ERTS_INLINE int
-deliver_term_check_port(ErlDrvTermData port_id, Eterm *connected_p)
+deliver_term_check_port(ErlDrvTermData port_id, Eterm *connected_p,
+ Port **trace_prt)
{
#ifdef ERTS_SMP
ErtsThrPrgrDelayHandle dhndl = erts_thr_progress_unmanaged_delay();
#endif
erts_aint32_t state;
+ int res = 1;
Port *prt = erts_port_lookup_raw((Eterm) port_id);
- if (!prt)
- return -1;
+ if (!prt) {
+ res = -1;
+ goto done;
+ }
state = erts_atomic32_read_nob(&prt->state);
if (state & (ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP
| ERTS_PORT_SFLG_CLOSING)) {
if (state & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP)
- return -1;
+ res = -1;
else
- return 0;
+ res = 0;
+ goto done;
}
if (connected_p) {
#ifdef ERTS_SMP
@@ -5785,26 +6627,32 @@ deliver_term_check_port(ErlDrvTermData port_id, Eterm *connected_p)
#endif
*connected_p = ERTS_PORT_GET_CONNECTED(prt);
}
+
+done:
+
#ifdef ERTS_SMP
if (dhndl != ERTS_THR_PRGR_DHANDLE_MANAGED) {
+ ERTS_SMP_LC_ASSERT(!prt || !erts_lc_is_port_locked(prt));
erts_thr_progress_unmanaged_continue(dhndl);
ETHR_MEMBAR(ETHR_LoadLoad|ETHR_LoadStore);
- }
+ } else
#endif
- ERTS_SMP_LC_ASSERT(dhndl == ERTS_THR_PRGR_DHANDLE_MANAGED
- ? erts_lc_is_port_locked(prt)
- : !erts_lc_is_port_locked(prt));
- return 1;
+ if (res == 1) {
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
+ *trace_prt = prt;
+ }
+ return res;
}
int erl_drv_output_term(ErlDrvTermData port_id, ErlDrvTermData* data, int len)
{
/* May be called from arbitrary thread */
- Eterm connected;
- int res = deliver_term_check_port(port_id, &connected);
+ Eterm connected = NIL; /* Shut up faulty warning... */
+ Port *prt = NULL;
+ int res = deliver_term_check_port(port_id, &connected, &prt);
if (res <= 0)
return res;
- return driver_deliver_term(connected, data, len);
+ return driver_deliver_term(prt, connected, data, len);
}
/*
@@ -5828,7 +6676,7 @@ driver_output_term(ErlDrvPort drvport, ErlDrvTermData* data, int len)
if (state & ERTS_PORT_SFLG_CLOSING)
return 0;
- return driver_deliver_term(ERTS_PORT_GET_CONNECTED(prt), data, len);
+ return driver_deliver_term(prt, ERTS_PORT_GET_CONNECTED(prt), data, len);
}
int erl_drv_send_term(ErlDrvTermData port_id,
@@ -5837,10 +6685,11 @@ int erl_drv_send_term(ErlDrvTermData port_id,
int len)
{
/* May be called from arbitrary thread */
- int res = deliver_term_check_port(port_id, NULL);
+ Port *prt = NULL;
+ int res = deliver_term_check_port(port_id, NULL, &prt);
if (res <= 0)
return res;
- return driver_deliver_term(to, data, len);
+ return driver_deliver_term(prt, to, data, len);
}
/*
@@ -5859,20 +6708,21 @@ driver_send_term(ErlDrvPort drvport,
* to make this access safe without using a less efficient
* internal data representation for ErlDrvPort.
*/
+ Port* prt = NULL;
ERTS_SMP_CHK_NO_PROC_LOCKS;
#ifdef ERTS_SMP
if (erts_thr_progress_is_managed_thread())
#endif
{
erts_aint32_t state;
- Port* prt = erts_drvport2port_state(drvport, &state);
+ prt = erts_drvport2port_state(drvport, &state);
if (prt == ERTS_INVALID_ERL_DRV_PORT)
return -1; /* invalid (dead) */
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
if (state & ERTS_PORT_SFLG_CLOSING)
return 0;
}
- return driver_deliver_term(to, data, len);
+ return driver_deliver_term(prt, to, data, len);
}
@@ -6054,12 +6904,6 @@ ErlDrvSizeT driver_vec_to_buf(ErlIOVec *vec, char *buf, ErlDrvSizeT len)
return (orig_len - len);
}
-
-/*
- * - driver_alloc_binary() is thread safe (efile driver depend on it).
- * - driver_realloc_binary(), and driver_free_binary() are *not* thread safe.
- */
-
/*
* reference count on driver binaries...
*/
@@ -6102,26 +6946,15 @@ driver_alloc_binary(ErlDrvSizeT size)
return Binary2ErlDrvBinary(bin);
}
-/* Reallocate space hold by binary */
+/* Reallocate space held by binary */
ErlDrvBinary* driver_realloc_binary(ErlDrvBinary* bin, ErlDrvSizeT size)
{
Binary* oldbin;
Binary* newbin;
- if (!bin) {
- erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
- erts_dsprintf(dsbufp,
- "Bad use of driver_realloc_binary(%p, %lu): "
- "called with ",
- bin, (unsigned long)size);
- if (!bin) {
- erts_dsprintf(dsbufp, "NULL pointer as first argument");
- }
- erts_send_warning_to_logger_nogl(dsbufp);
- if (!bin)
- return driver_alloc_binary(size);
- }
+ if (!bin)
+ return driver_alloc_binary(size);
oldbin = ErlDrvBinary2Binary(bin);
newbin = (Binary *) erts_bin_realloc_fnf(oldbin, size);
@@ -6135,14 +6968,8 @@ ErlDrvBinary* driver_realloc_binary(ErlDrvBinary* bin, ErlDrvSizeT size)
void driver_free_binary(ErlDrvBinary* dbin)
{
Binary *bin;
- if (!dbin) {
- erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
- erts_dsprintf(dsbufp,
- "Bad use of driver_free_binary(%p): called with "
- "NULL pointer as argument", dbin);
- erts_send_warning_to_logger_nogl(dsbufp);
+ if (!dbin)
return;
- }
bin = ErlDrvBinary2Binary(dbin);
if (erts_refc_dectest(&bin->refc, 0) == 0)
@@ -6843,7 +7670,7 @@ int driver_monitor_process(ErlDrvPort drvport,
{
Port *prt;
int ret;
-#if !HEAP_ON_C_STACK || (defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK))
+#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
ErtsSchedulerData *sched = erts_get_scheduler_data();
#endif
@@ -6854,16 +7681,6 @@ int driver_monitor_process(ErlDrvPort drvport,
/* Now (in SMP) we should have either the port lock (if we have a scheduler) or the port data lock
(if we're a driver thread) */
ERTS_SMP_LC_ASSERT((sched != NULL || prt->port_data_lock));
-
-#if !HEAP_ON_C_STACK
- if (!sched) {
- /* Need a separate allocation for the ref :( */
- Eterm *buf = erts_alloc(ERTS_ALC_T_TEMP_TERM,
- sizeof(Eterm)*REF_THING_SIZE);
- ret = do_driver_monitor_process(prt,buf,process,monitor);
- erts_free(ERTS_ALC_T_TEMP_TERM,buf);
- } else
-#endif
{
DeclareTmpHeapNoproc(buf,REF_THING_SIZE);
UseTmpHeapNoproc(REF_THING_SIZE);
@@ -6916,7 +7733,7 @@ int driver_demonitor_process(ErlDrvPort drvport,
{
Port *prt;
int ret;
-#if !HEAP_ON_C_STACK || (defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK))
+#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
ErtsSchedulerData *sched = erts_get_scheduler_data();
#endif
@@ -6927,15 +7744,6 @@ int driver_demonitor_process(ErlDrvPort drvport,
/* Now we should have either the port lock (if we have a scheduler) or the port data lock
(if we're a driver thread) */
ERTS_SMP_LC_ASSERT((sched != NULL || prt->port_data_lock));
-#if !HEAP_ON_C_STACK
- if (!sched) {
- /* Need a separate allocation for the ref :( */
- Eterm *buf = erts_alloc(ERTS_ALC_T_TEMP_TERM,
- sizeof(Eterm)*REF_THING_SIZE);
- ret = do_driver_demonitor_process(prt,buf,monitor);
- erts_free(ERTS_ALC_T_TEMP_TERM,buf);
- } else
-#endif
{
DeclareTmpHeapNoproc(buf,REF_THING_SIZE);
UseTmpHeapNoproc(REF_THING_SIZE);
@@ -6971,7 +7779,7 @@ ErlDrvTermData driver_get_monitored_process(ErlDrvPort drvport,
{
Port *prt;
ErlDrvTermData ret;
-#if !HEAP_ON_C_STACK || (defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK))
+#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
ErtsSchedulerData *sched = erts_get_scheduler_data();
#endif
@@ -6982,16 +7790,6 @@ ErlDrvTermData driver_get_monitored_process(ErlDrvPort drvport,
/* Now we should have either the port lock (if we have a scheduler) or the port data lock
(if we're a driver thread) */
ERTS_SMP_LC_ASSERT((sched != NULL || prt->port_data_lock));
-
-#if !HEAP_ON_C_STACK
- if (!sched) {
- /* Need a separate allocation for the ref :( */
- Eterm *buf = erts_alloc(ERTS_ALC_T_TEMP_TERM,
- sizeof(Eterm)*REF_THING_SIZE);
- ret = do_driver_get_monitored_process(prt,buf,monitor);
- erts_free(ERTS_ALC_T_TEMP_TERM,buf);
- } else
-#endif
{
DeclareTmpHeapNoproc(buf,REF_THING_SIZE);
UseTmpHeapNoproc(REF_THING_SIZE);
@@ -7015,6 +7813,7 @@ void erts_fire_port_monitor(Port *prt, Eterm ref)
void (*callback)(ErlDrvData drv_data, ErlDrvMonitor *monitor);
ErlDrvMonitor drv_monitor;
int fpe_was_unmasked;
+ ERTS_MSACC_PUSH_STATE_M();
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
ASSERT(prt->drv_ptr != NULL);
@@ -7026,6 +7825,7 @@ void erts_fire_port_monitor(Port *prt, Eterm ref)
callback = prt->drv_ptr->process_exit;
ASSERT(callback != NULL);
ref_to_driver_monitor(ref,&drv_monitor);
+ ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_PORT);
DRV_MONITOR_UNLOCK_PDL(prt);
#ifdef USE_VM_PROBES
if (DTRACE_ENABLED(driver_process_exit)) {
@@ -7033,10 +7833,20 @@ void erts_fire_port_monitor(Port *prt, Eterm ref)
DTRACE3(driver_process_exit, process_str, port_str, prt->name);
}
#endif
+#ifdef USE_LTTNG_VM_TRACEPOINTS
+ if (LTTNG_ENABLED(driver_process_exit)) {
+ lttng_decl_portbuf(port_str);
+ lttng_decl_procbuf(proc_str);
+ lttng_pid_to_str(ERTS_PORT_GET_CONNECTED(prt), proc_str);
+ lttng_port_to_str(prt, port_str);
+ LTTNG3(driver_process_exit, proc_str, port_str, prt->name);
+ }
+#endif
fpe_was_unmasked = erts_block_fpe();
(*callback)((ErlDrvData) (prt->drv_data), &drv_monitor);
erts_unblock_fpe(fpe_was_unmasked);
DRV_MONITOR_LOCK_PDL(prt);
+ ERTS_MSACC_POP_STATE_M();
/* remove monitor *after* callback */
rmon = erts_remove_monitor(&ERTS_P_MONITORS(prt), ref);
DRV_MONITOR_UNLOCK_PDL(prt);
@@ -7057,12 +7867,15 @@ driver_failure_term(ErlDrvPort ix, Eterm term, int eof)
if (prt == ERTS_INVALID_ERL_DRV_PORT)
return -1;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
+
+ if (prt->async_open_port)
+ init_ack_send_reply(prt, prt->common.id);
if (eof)
flush_linebuf_messages(prt, state);
if (state & ERTS_PORT_SFLG_CLOSING) {
terminate_port(prt);
} else if (eof && (state & ERTS_PORT_SFLG_SOFT_EOF)) {
- deliver_result(prt->common.id, ERTS_PORT_GET_CONNECTED(prt), am_eof);
+ deliver_result(prt, prt->common.id, ERTS_PORT_GET_CONNECTED(prt), am_eof);
} else {
/* XXX UGLY WORK AROUND, Let erts_deliver_port_exit() terminate the port */
if (prt->port_data_lock)
@@ -7070,7 +7883,7 @@ driver_failure_term(ErlDrvPort ix, Eterm term, int eof)
prt->ioq.size = 0;
if (prt->port_data_lock)
driver_pdl_unlock(prt->port_data_lock);
- erts_deliver_port_exit(prt, prt->common.id, eof ? am_normal : term, 0);
+ erts_deliver_port_exit(prt, prt->common.id, eof ? am_normal : term, 0, 0);
}
return 0;
}
@@ -7374,11 +8187,13 @@ driver_system_info(ErlDrvSysInfo *sip, size_t si_size)
* (driver version 3.1, NIF version 2.7)
*/
if (si_size >= ERL_DRV_SYS_INFO_SIZE(dirty_scheduler_support)) {
-#if defined(ERL_NIF_DIRTY_SCHEDULER_SUPPORT) && defined(USE_THREADS)
- sip->dirty_scheduler_support = 1;
+ sip->dirty_scheduler_support =
+#ifdef ERTS_DIRTY_SCHEDULERS
+ 1
#else
- sip->dirty_scheduler_support = 0;
+ 0
#endif
+ ;
}
}
@@ -7506,6 +8321,8 @@ init_driver(erts_driver_t *drv, ErlDrvEntry *de, DE_Handle *handle)
int fpe_was_unmasked = erts_block_fpe();
DTRACE4(driver_init, drv->name, drv->version.major, drv->version.minor,
drv->flags);
+ LTTNG4(driver_init, drv->name, drv->version.major, drv->version.minor,
+ drv->flags);
res = (*de->init)();
erts_unblock_fpe(fpe_was_unmasked);
return res;
diff --git a/erts/emulator/beam/lttng-wrapper.h b/erts/emulator/beam/lttng-wrapper.h
new file mode 100644
index 0000000000..0bc75c1552
--- /dev/null
+++ b/erts/emulator/beam/lttng-wrapper.h
@@ -0,0 +1,107 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#ifndef __LTTNG_WRAPPER_H__
+#define __LTTNG_WRAPPER_H__
+
+#ifdef USE_LTTNG
+
+#include "erlang_lttng.h"
+#define USE_LTTNG_VM_TRACEPOINTS
+
+#define LTTNG_BUFFER_SZ (256)
+#define LTTNG_PROC_BUFFER_SZ (16)
+#define LTTNG_PORT_BUFFER_SZ (20)
+#define LTTNG_MFA_BUFFER_SZ (256)
+
+#define lttng_decl_procbuf(Name) \
+ char Name[LTTNG_PROC_BUFFER_SZ]
+
+#define lttng_decl_portbuf(Name) \
+ char Name[LTTNG_PORT_BUFFER_SZ]
+
+#define lttng_decl_mfabuf(Name) \
+ char Name[LTTNG_MFA_BUFFER_SZ]
+
+#define lttng_decl_carrier_stats(Name) \
+ lttng_carrier_stats_t Name##_STATSTRUCT, *Name = &Name##_STATSTRUCT
+
+#define lttng_pid_to_str(pid, name) \
+ erts_snprintf(name, LTTNG_PROC_BUFFER_SZ, "%T", (pid))
+
+#define lttng_portid_to_str(pid, name) \
+ erts_snprintf(name, LTTNG_PORT_BUFFER_SZ, "%T", (pid))
+
+#define lttng_proc_to_str(p, name) \
+ lttng_pid_to_str(((p) ? (p)->common.id : ERTS_INVALID_PID), name)
+
+#define lttng_port_to_str(p, name) \
+ lttng_portid_to_str(((p) ? (p)->common.id : ERTS_INVALID_PORT), name)
+
+#define lttng_mfa_to_str(m,f,a, Name) \
+ erts_snprintf(Name, LTTNG_MFA_BUFFER_SZ, "%T:%T/%lu", (Eterm)(m), (Eterm)(f), (Uint)(a))
+
+#define lttng_proc_to_mfa_str(p, Name) \
+ do { \
+ if (ERTS_PROC_IS_EXITING((p))) { \
+ strcpy(Name, "<exiting>"); \
+ } else { \
+ BeamInstr *_fptr = find_function_from_pc((p)->i); \
+ if (_fptr) { \
+ lttng_mfa_to_str(_fptr[0],_fptr[1],_fptr[2], Name); \
+ } else { \
+ strcpy(Name, "<unknown>"); \
+ } \
+ } \
+ } while(0)
+
+/* ErtsRunQueue->ErtsSchedulerData->Uint */
+#define lttng_rq_to_id(RQ) \
+ (RQ)->scheduler->no
+
+#define LTTNG_ENABLED(Name) \
+ tracepoint_enabled(org_erlang_otp, Name)
+
+/* include a special LTTNG_DO for do_tracepoint ? */
+#define LTTNG1(Name, Arg1) \
+ tracepoint(org_erlang_otp, Name, (Arg1))
+
+#define LTTNG2(Name, Arg1, Arg2) \
+ tracepoint(org_erlang_otp, Name, (Arg1), (Arg2))
+
+#define LTTNG3(Name, Arg1, Arg2, Arg3) \
+ tracepoint(org_erlang_otp, Name, (Arg1), (Arg2), (Arg3))
+
+#define LTTNG4(Name, Arg1, Arg2, Arg3, Arg4) \
+ tracepoint(org_erlang_otp, Name, (Arg1), (Arg2), (Arg3), (Arg4))
+
+#define LTTNG5(Name, Arg1, Arg2, Arg3, Arg4, Arg5) \
+ tracepoint(org_erlang_otp, Name, (Arg1), (Arg2), (Arg3), (Arg4), (Arg5))
+
+#else /* USE_LTTNG */
+
+#define LTTNG1(Name, Arg1) do {} while(0)
+#define LTTNG2(Name, Arg1, Arg2) do {} while(0)
+#define LTTNG3(Name, Arg1, Arg2, Arg3) do {} while(0)
+#define LTTNG4(Name, Arg1, Arg2, Arg3, Arg4) do {} while(0)
+#define LTTNG5(Name, Arg1, Arg2, Arg3, Arg4, Arg5) do {} while(0)
+
+#endif /* USE_LTTNG */
+#endif /* __LTTNG_WRAPPER_H__ */
diff --git a/erts/emulator/beam/module.c b/erts/emulator/beam/module.c
index 86dd3b5aac..5978478323 100644
--- a/erts/emulator/beam/module.c
+++ b/erts/emulator/beam/module.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -50,7 +50,7 @@ static erts_smp_atomic_t tot_module_bytes;
#include "erl_smp.h"
-void module_info(int to, void *to_arg)
+void module_info(fmtfn_t to, void *to_arg)
{
index_info(to, to_arg, &module_tables[erts_active_code_ix()]);
}
@@ -74,8 +74,8 @@ static Module* module_alloc(Module* tmpl)
erts_smp_atomic_add_nob(&tot_module_bytes, sizeof(Module));
obj->module = tmpl->module;
- obj->curr.code = 0;
- obj->old.code = 0;
+ obj->curr.code_hdr = 0;
+ obj->old.code_hdr = 0;
obj->curr.code_length = 0;
obj->old.code_length = 0;
obj->slot.index = -1;
@@ -85,6 +85,7 @@ static Module* module_alloc(Module* tmpl)
obj->old.num_breakpoints = 0;
obj->curr.num_traced_exports = 0;
obj->old.num_traced_exports = 0;
+ obj->on_load = 0;
return obj;
}
@@ -103,6 +104,9 @@ void init_module_table(void)
f.cmp = (HCMP_FUN) module_cmp;
f.alloc = (HALLOC_FUN) module_alloc;
f.free = (HFREE_FUN) module_free;
+ f.meta_alloc = (HMALLOC_FUN) erts_alloc;
+ f.meta_free = (HMFREE_FUN) erts_free;
+ f.meta_print = (HMPRINT_FUN) erts_print;
for (i = 0; i < ERTS_NUM_CODE_IX; i++) {
erts_index_init(ERTS_ALC_T_MODULE_TABLE, &module_tables[i], "module_code",
@@ -198,6 +202,7 @@ void module_start_staging(void)
dst_mod->curr = src_mod->curr;
dst_mod->old = src_mod->old;
+ dst_mod->on_load = src_mod->on_load;
}
/*
@@ -211,6 +216,7 @@ void module_start_staging(void)
dst_mod->curr = src_mod->curr;
dst_mod->old = src_mod->old;
+ dst_mod->on_load = src_mod->on_load;
}
newsz = index_table_sz(dst);
erts_smp_atomic_add_nob(&tot_module_bytes, (newsz - oldsz));
diff --git a/erts/emulator/beam/module.h b/erts/emulator/beam/module.h
index c8a6351b04..f105b3f401 100644
--- a/erts/emulator/beam/module.h
+++ b/erts/emulator/beam/module.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,12 +21,10 @@
#ifndef __MODULE_H__
#define __MODULE_H__
-#ifndef __INDEX_H__
#include "index.h"
-#endif
struct erl_module_instance {
- BeamInstr* code;
+ BeamCodeHeader* code_hdr;
int code_length; /* Length of loaded code in bytes. */
unsigned catches;
struct erl_module_nif* nif;
@@ -37,9 +35,11 @@ struct erl_module_instance {
typedef struct erl_module {
IndexSlot slot; /* Must be located at top of struct! */
int module; /* Atom index for module (not tagged). */
+ int seen; /* Used by finish_loading() */
struct erl_module_instance curr;
struct erl_module_instance old; /* protected by "old_code" rwlock */
+ struct erl_module_instance* on_load;
} Module;
Module* erts_get_module(Eterm mod, ErtsCodeIndex code_ix);
@@ -48,7 +48,7 @@ Module* erts_put_module(Eterm mod);
void init_module_table(void);
void module_start_staging(void);
void module_end_staging(int commit);
-void module_info(int, void *);
+void module_info(fmtfn_t, void *);
Module *module_code(int, ErtsCodeIndex);
int module_code_size(ErtsCodeIndex);
diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab
index 1d32e72247..879daaca0a 100644
--- a/erts/emulator/beam/ops.tab
+++ b/erts/emulator/beam/ops.tab
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2013. All Rights Reserved.
+# Copyright Ericsson AB 1997-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -39,8 +39,8 @@ too_old_compiler | never() =>
# necessary.) Since the instructions don't work correctly in R12B, simply
# refuse to load the module.
-func_info M=a a==am_module_info A=u==0 | label L | move n r => too_old_compiler
-func_info M=a a==am_module_info A=u==1 | label L | move n r => too_old_compiler
+func_info M=a a==am_module_info A=u==0 | label L | move n x==0 => too_old_compiler
+func_info M=a a==am_module_info A=u==1 | label L | move n x==0 => too_old_compiler
# The undocumented and unsupported guard BIF is_constant/1 was removed
# in R13. The is_constant/2 operation is marked as obsolete in genop.tab,
@@ -76,17 +76,6 @@ return
# with the following call instruction, we need to make sure that
# there is no line/1 instruction between the move and the call.
#
-
-move S r | line Loc | call_ext Ar Func => \
- line Loc | move S r | call_ext Ar Func
-move S r | line Loc | call_ext_last Ar Func=u$is_bif D => \
- line Loc | move S r | call_ext_last Ar Func D
-move S r | line Loc | call_ext_only Ar Func=u$is_bif => \
- line Loc | move S r | call_ext_only Ar Func
-move S r | line Loc | call Ar Func => \
- line Loc | move S r | call Ar Func
-
-#
# A tail-recursive call to an external function (non-BIF) will
# never be saved on the stack, so there is no reason to keep
# the line instruction. (The compiler did not remove the line
@@ -94,10 +83,14 @@ move S r | line Loc | call Ar Func => \
# BIFs and ordinary Erlang functions.)
#
-line Loc | call_ext_last Ar Func=u$is_not_bif D => \
- call_ext_last Ar Func D
-line Loc | call_ext_only Ar Func=u$is_not_bif => \
- call_ext_only Ar Func
+move S X0=x==0 | line Loc | call_ext Ar Func => \
+ line Loc | move S X0 | call_ext Ar Func
+move S X0=x==0 | line Loc | call_ext_last Ar Func=u$is_not_bif D => \
+ move S X0 | call_ext_last Ar Func D
+move S X0=x==0 | line Loc | call_ext_only Ar Func=u$is_not_bif => \
+ move S X0 | call_ext_only Ar Func
+move S X0=x==0 | line Loc | call Ar Func => \
+ line Loc | move S X0 | call Ar Func
line Loc | func_info M F A => func_info M F A | line Loc
@@ -167,66 +160,45 @@ 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 r f I
i_select_val_bins x f I
i_select_val_bins y f I
-i_select_val_lins r f I
i_select_val_lins x f I
i_select_val_lins y f I
-i_select_val2 r f c c f f
i_select_val2 x f c c f f
i_select_val2 y f c c f f
-i_select_tuple_arity r f I
i_select_tuple_arity x f I
i_select_tuple_arity y f I
-i_select_tuple_arity2 r f A A f f
i_select_tuple_arity2 x f A A f f
i_select_tuple_arity2 y f A A f f
-i_jump_on_val_zero r f I
i_jump_on_val_zero x f I
i_jump_on_val_zero y f I
-i_jump_on_val r f I I
i_jump_on_val x f I I
i_jump_on_val y f I I
-jump Target | label Lbl | same_label(Target, Lbl) => label Lbl
-
-is_ne_exact L1 S1 S2 | jump Fail | label L2 | same_label(L1, L2) => \
- is_eq_exact Fail S1 S2 | label L2
-
%macro: get_list GetList -pack
get_list x x x
get_list x x y
-get_list x x r
get_list x y x
get_list x y y
-get_list x y r
-get_list x r x
-get_list x r y
get_list y x x
get_list y x y
-get_list y x r
get_list y y x
get_list y y y
-get_list y y r
-get_list y r x
-get_list y r y
+# The following get_list instructions using x(0) are frequently used.
get_list r x x
+get_list r r y
+get_list x r x
get_list r x y
-get_list r x r
-get_list r y x
-get_list r y y
get_list r y r
-get_list r r x
-get_list r r y
+get_list r x r
# Old-style catch.
catch y f
@@ -237,33 +209,34 @@ try Y F => catch Y F
try_case Y => try_end Y
try_end y
-try_case_end Literal=q => move Literal x | try_case_end x
try_case_end s
# Destructive set tuple element
-set_tuple_element Lit=q Tuple Pos => move Lit x | set_tuple_element x Tuple Pos
set_tuple_element s d P
# Get tuple element
%macro: i_get_tuple_element GetTupleElement -pack
i_get_tuple_element x P x
-i_get_tuple_element r P x
i_get_tuple_element y P x
-i_get_tuple_element x P r
-i_get_tuple_element y P r
%cold
-i_get_tuple_element r P r
i_get_tuple_element x P y
-i_get_tuple_element r P y
i_get_tuple_element y P y
%hot
+%macro: i_get_tuple_element2 GetTupleElement2 -pack
+i_get_tuple_element2 x P x
+
+%macro: i_get_tuple_element2y GetTupleElement2Y -pack
+i_get_tuple_element2y x P y y
+
+%macro: i_get_tuple_element3 GetTupleElement3 -pack
+i_get_tuple_element3 x P x
+
%macro: is_number IsNumber -fail_action
%cold
-is_number f r
is_number f x
is_number f y
%hot
@@ -273,25 +246,28 @@ is_number Fail Literal=q => move Literal x | is_number Fail x
jump f
-case_end Literal=cq => move Literal x | case_end x
-badmatch Literal=cq => move Literal x | badmatch x
+case_end NotInX=cy => move NotInX x | case_end x
+badmatch NotInX=cy => move NotInX x | badmatch x
-case_end r
case_end x
-case_end y
-badmatch r
badmatch x
-badmatch y
if_end
-raise s s
+
+# Operands for raise/2 are almost always in x(2) and x(1).
+# Optimize for that case.
+raise x==2 x==1 => i_raise
+raise Trace=y Value=y => move Trace x=2 | move Value x=1 | i_raise
+raise Trace Value => move Trace x=3 | move Value x=1 | move x=3 x=2 | i_raise
+
+i_raise
# Internal now, but could be useful to make known to the compiler.
badarg j
system_limit j
-move C=cxy r | jump Lbl => move_jump Lbl C
+move C=cxy x==0 | jump Lbl => move_jump Lbl C
%macro: move_jump MoveJump -nonext
move_jump f n
@@ -309,8 +285,6 @@ move_window/6
# x -> y
-move S1=r S2=y | move X1=x Y1=y => move2 S1 S2 X1 Y1
-
move X1=x Y1=y | move X2=x Y2=y | move X3=x Y3=y | succ(Y1,Y2) | succ(Y2,Y3) => \
move_window X1 X2 X3 Y1 Y3
@@ -323,24 +297,76 @@ move_window X1=x X2=x X3=x X4=x Y1=y Y4=y | move X5=x Y5=y | succ(Y4,Y5) => \
move_window X1=x X2=x X3=x Y1=y Y3=y => move_window3 X1 X2 X3 Y1
move_window X1=x X2=x X3=x X4=x Y1=y Y4=y => move_window4 X1 X2 X3 X4 Y1
+%macro: move_window3 MoveWindow3 -pack
+%macro: move_window4 MoveWindow4 -pack
+%macro: move_window5 MoveWindow5 -pack
+
move_window3 x x x y
move_window4 x x x x y
move_window5 x x x x x y
-move X1=x Y1=y | move X2=x Y2=y => move2 X1 Y1 X2 Y2
-move Y1=y X1=x | move Y2=y X2=x => move2 Y1 X1 Y2 X2
-move X1=x X2=x | move X3=x X4=x => move2 X1 X2 X3 X4
+# Swap registers.
+move R1=x Tmp=x | move R2=xy R1 | move Tmp R2 => swap_temp R1 R2 Tmp
+
+swap_temp R1 R2 Tmp | line Loc | apply Live | is_killed_apply(Tmp, Live) => \
+ swap R1 R2 | line Loc | apply Live
+
+swap_temp R1 R2 Tmp | line Loc | call Live Addr | is_killed(Tmp, Live) => \
+ swap R1 R2 | line Loc | call Live Addr
+swap_temp R1 R2 Tmp | call_only Live Addr | \
+ is_killed(Tmp, Live) => swap R1 R2 | call_only Live Addr
+swap_temp R1 R2 Tmp | call_last Live Addr D | \
+ is_killed(Tmp, Live) => swap R1 R2 | call_last Live Addr D
+
+swap_temp R1 R2 Tmp | line Loc | call_ext Live Addr | is_killed(Tmp, Live) => \
+ swap R1 R2 | line Loc | call_ext Live Addr
+swap_temp R1 R2 Tmp | line Loc | call_ext_only Live Addr | \
+ is_killed(Tmp, Live) => swap R1 R2 | line Loc | call_ext_only Live Addr
+swap_temp R1 R2 Tmp | line Loc | call_ext_last Live Addr D | \
+ is_killed(Tmp, Live) => swap R1 R2 | line Loc | call_ext_last Live Addr D
+
+%macro: swap_temp SwapTemp -pack
+swap_temp x x x
+swap_temp x y x
+
+%macro: swap Swap -pack
+swap x x
+swap x y
+
+move Src=x D1=x | move Src=x D2=x => move_dup Src D1 D2
+move Src=x SD=x | move SD=x D=x => move_dup Src SD D
+move Src=x D1=x | move Src=x D2=y => move_dup Src D1 D2
+move Src=y SD=x | move SD=x D=y => move_dup Src SD D
+move Src=x SD=x | move SD=x D=y => move_dup Src SD D
+move Src=y SD=x | move SD=x D=x => move_dup Src SD D
+
+move SD=x D=x | move Src=xy SD=x => move_shift Src SD D
+move SD=y D=x | move Src=x SD=y => move_shift Src SD D
+move SD=x D=y | move Src=x SD=x => move_shift Src SD D
+
+# The transformations above guarantee that the source for
+# the second move is not the same as the destination for
+# the first move. That means that we can do the moves in
+# parallel (fetch both values, then store them) which could
+# be faster.
+
+move X1=x Y1=y | move X2=x Y2=y => move2_par X1 Y1 X2 Y2
+move Y1=y X1=x | move Y2=y X2=x => move2_par Y1 X1 Y2 X2
+
+move X1=x X2=x | move X3=x X4=x => move2_par X1 X2 X3 X4
+
+move X1=x X2=x | move X3=x Y1=y => move2_par X1 X2 X3 Y1
+
+move S1=x S2=x | move X1=x Y1=y => move2_par S1 S2 X1 Y1
-move S1=x S2=r | move S3=x S4=x => move2 S1 S2 S3 S4
-move S1=x S2=r | move X1=x Y1=y => move2 S1 S2 X1 Y1
-move S1=y S2=r | move X1=x Y1=y => move2 S1 S2 X1 Y1
+move S1=y S2=x | move X1=x Y1=y => move2_par S1 S2 X1 Y1
-move Y1=y X1=x | move S1=r D1=x => move2 Y1 X1 S1 D1
-move S1=r D1=x | move Y1=y X1=x => move2 S1 D1 Y1 X1
+move Y1=y X1=x | move S1=x D1=x => move2_par Y1 X1 S1 D1
+move S1=x D1=x | move Y1=y X1=x => move2_par S1 D1 Y1 X1
-move2 X1=x Y1=y X2=x Y2=y | move X3=x Y3=y => move3 X1 Y1 X2 Y2 X3 Y3
-move2 Y1=y X1=x Y2=y X2=x | move Y3=y X3=x => move3 Y1 X1 Y2 X2 Y3 X3
-move2 X1=x X2=x X3=x X4=x | move X5=x X6=x => move3 X1 X2 X3 X4 X5 X6
+move2_par X1=x Y1=y X2=x Y2=y | move X3=x Y3=y => move3 X1 Y1 X2 Y2 X3 Y3
+move2_par Y1=y X1=x Y2=y X2=x | move Y3=y X3=x => move3 Y1 X1 Y2 X2 Y3 X3
+move2_par X1=x X2=x X3=x X4=x | move X5=x X6=x => move3 X1 X2 X3 X4 X5 X6
move C=aiq X=x==1 => move_x1 C
move C=aiq X=x==2 => move_x2 C
@@ -348,21 +374,32 @@ move C=aiq X=x==2 => move_x2 C
move_x1 c
move_x2 c
-%macro: move2 Move2 -pack
-move2 x y x y
-move2 y x y x
-move2 x x x x
+%macro: move_shift MoveShift -pack
+move_shift x x x
+move_shift y x x
+move_shift x y x
+move_shift x x y
-move2 x r x x
+%macro: move_dup MoveDup -pack
+move_dup x x x
+move_dup x x y
+move_dup y x x
+move_dup y x y
-move2 x r x y
-move2 r y x y
-move2 y r x y
+%macro: move2_par Move2Par -pack
-move2 r x y x
-move2 y x r x
+move2_par x y x y
+move2_par y x y x
+move2_par x x x x
-%macro: move3 Move3
+move2_par x x x y
+
+move2_par y x x y
+
+move2_par x x y x
+move2_par y x x x
+
+%macro: move3 Move3 -pack
move3 x y x y x y
move3 y x y x y x
move3 x x x x x x
@@ -375,20 +412,22 @@ move S=c D=y => move S x | move x D
%macro:move Move -pack -gen_dest
move x x
move x y
-move x r
move y x
-move y r
-move r x
-move r y
-move c r
move c x
move n x
-move n r
move y y
+# The following move instructions using x(0) are frequently used.
+
+move x r
+move r x
+move y r
+move c r
+move r y
+
# Receive operations.
-loop_rec Fail Src | smp_mark_target_label(Fail) => i_loop_rec Fail Src
+loop_rec Fail x==0 | smp_mark_target_label(Fail) => i_loop_rec Fail
label L | wait_timeout Fail Src | smp_already_locked(L) => label L | i_wait_timeout_locked Fail Src
wait_timeout Fail Src => i_wait_timeout Fail Src
@@ -403,7 +442,7 @@ label L | timeout | smp_already_locked(L) => label L | timeout_locked
remove_message
timeout
timeout_locked
-i_loop_rec f r
+i_loop_rec f
loop_rec_end f
wait f
wait_locked f
@@ -421,93 +460,57 @@ send
# Optimized comparisons with one immediate/literal operand.
#
-is_eq_exact Lbl R=rxy C=ian => i_is_eq_exact_immed Lbl R C
-is_eq_exact Lbl R=rxy C=q => i_is_eq_exact_literal R Lbl C
+is_eq_exact Lbl R=xy C=ian => i_is_eq_exact_immed Lbl R C
+is_eq_exact Lbl R=xy C=q => i_is_eq_exact_literal Lbl R C
-is_ne_exact Lbl R=rxy C=ian => i_is_ne_exact_immed Lbl R C
-is_ne_exact Lbl R=rxy C=q => i_is_ne_exact_literal R Lbl C
+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 r f c
-i_is_eq_exact_literal x f c
-i_is_eq_exact_literal y f c
+i_is_eq_exact_literal f x c
+i_is_eq_exact_literal f y c
%macro: i_is_ne_exact_immed NotEqualImmed -fail_action
-i_is_ne_exact_immed f r c
i_is_ne_exact_immed f x c
i_is_ne_exact_immed f y c
-i_is_ne_exact_literal r f c
-i_is_ne_exact_literal x f c
-i_is_ne_exact_literal y f c
+i_is_ne_exact_literal f x c
+i_is_ne_exact_literal f y c
-#
-# Common Compare Specializations
-# We don't do all of them since we want
-# to keep the instruction set small-ish
-#
+is_eq_exact Lbl Y=y X=x => is_eq_exact Lbl X Y
+%macro: is_eq_exact EqualExact -fail_action -pack
+is_eq_exact f x x
+is_eq_exact f x y
+is_eq_exact f s s
-is_eq_exact Lbl S1=xy S2=r => is_eq_exact Lbl S2 S1
-is_eq_exact Lbl S1=rx S2=xy => i_is_eq_exact_spec Lbl S1 S2
-%macro: i_is_eq_exact_spec EqualExact -fail_action
-
-i_is_eq_exact_spec f x x
-i_is_eq_exact_spec f x y
-i_is_eq_exact_spec f r x
-i_is_eq_exact_spec f r y
+%macro: is_lt IsLessThan -fail_action
+is_lt f x x
+is_lt f x c
+is_lt f c x
%cold
-i_is_eq_exact_spec f r r
+is_lt f s s
%hot
-is_lt Lbl S1=rxc S2=rxc => i_is_lt_spec Lbl S1 S2
-
-%macro: i_is_lt_spec IsLessThan -fail_action
-
-i_is_lt_spec f x x
-i_is_lt_spec f x r
-i_is_lt_spec f x c
-i_is_lt_spec f r x
-i_is_lt_spec f r c
-i_is_lt_spec f c x
-i_is_lt_spec f c r
+%macro: is_ge IsGreaterEqual -fail_action
+is_ge f x x
+is_ge f x c
+is_ge f c x
%cold
-i_is_lt_spec f r r
-i_is_lt_spec f c c
+is_ge f s s
%hot
-is_ge Lbl S1=xc S2=xc => i_is_ge_spec Lbl S1 S2
+%macro: is_ne_exact NotEqualExact -fail_action
+is_ne_exact f s s
-%macro: i_is_ge_spec IsGreaterEqual -fail_action
+%macro: is_eq Equal -fail_action
+is_eq f s s
-i_is_ge_spec f x x
-i_is_ge_spec f x c
-i_is_ge_spec f c x
-%cold
-i_is_ge_spec f c c
-%hot
-
-#
-# All other comparisons.
-#
-
-is_eq_exact Lbl S1 S2 => i_fetch S1 S2 | i_is_eq_exact Lbl
-is_ne_exact Lbl S1 S2 => i_fetch S1 S2 | i_is_ne_exact Lbl
-
-is_lt Lbl S1 S2 => i_fetch S1 S2 | i_is_lt Lbl
-is_ge Lbl S1 S2 => i_fetch S1 S2 | i_is_ge Lbl
-is_eq Lbl S1 S2 => i_fetch S1 S2 | i_is_eq Lbl
-is_ne Lbl S1 S2 => i_fetch S1 S2 | i_is_ne Lbl
-
-i_is_eq_exact f
-i_is_ne_exact f
-i_is_lt f
-i_is_ge f
-i_is_eq f
-i_is_ne f
+%macro: is_ne NotEqual -fail_action
+is_ne f s s
#
# Putting things.
@@ -525,14 +528,13 @@ 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 r I
i_put_tuple x I
i_put_tuple y I
#
-# The instruction "put_list Const [] Dst" will not be generated by
-# the current BEAM compiler. But until R15A, play it safe by handling
-# that instruction with the following transformation.
+# The instruction "put_list Const [] Dst" were generated in rare
+# circumstances up to and including OTP 18. Starting with OTP 19,
+# AFAIK, it should never be generated.
#
put_list Const=c n Dst => move Const x | put_list x n Dst
@@ -542,74 +544,36 @@ put_list x n x
put_list y n x
put_list x x x
put_list y x x
-put_list x x r
-put_list y r r
put_list y y x
put_list x y x
-put_list r x x
-put_list r y x
-put_list r x r
-put_list y y r
-put_list y r x
-put_list r n x
-put_list x r x
-put_list x y r
-put_list y x r
put_list y x x
-put_list x r r
-
# put_list SrcReg Constant Dst
-put_list r c r
-put_list r c x
-put_list r c y
-put_list x c r
put_list x c x
put_list x c y
-put_list y c r
put_list y c x
-put_list y c y
# put_list Constant SrcReg Dst
-put_list c r r
-put_list c r x
-put_list c r y
-put_list c x r
put_list c x x
-put_list c x y
-
-put_list c y r
put_list c y x
-put_list c y y
-%cold
-put_list s s d
-%hot
+# The following put_list instructions using x(0) are frequently used.
-%macro: i_fetch FetchArgs -pack
-i_fetch c r
-i_fetch c x
-i_fetch c y
-i_fetch r c
-i_fetch r x
-i_fetch r y
-i_fetch x c
-i_fetch x r
-i_fetch x x
-i_fetch x y
-i_fetch y c
-i_fetch y r
-i_fetch y x
-i_fetch y y
+put_list y r r
+put_list x r r
+put_list r n r
+put_list r n x
+put_list r x x
+put_list r x r
+put_list x x r
%cold
-i_fetch c c
-i_fetch s s
+put_list s s d
%hot
#
@@ -631,27 +595,27 @@ return_trace
# Note: There is no 'move_return y r', since there never are any y registers
# when we do move_return (if we have y registers, we must do move_deallocate_return).
-move S r | return => move_return S r
+move S x==0 | return => move_return S
%macro: move_return MoveReturn -nonext
-move_return x r
-move_return c r
-move_return n r
+move_return x
+move_return c
+move_return n
-move S r | deallocate D | return => move_deallocate_return S r D
+move S x==0 | deallocate D | return => move_deallocate_return S D
%macro: move_deallocate_return MoveDeallocateReturn -pack -nonext
-move_deallocate_return x r Q
-move_deallocate_return y r Q
-move_deallocate_return c r Q
-move_deallocate_return n r Q
+move_deallocate_return x Q
+move_deallocate_return y Q
+move_deallocate_return c Q
+move_deallocate_return n Q
deallocate D | return => deallocate_return D
%macro: deallocate_return DeallocateReturn -nonext
deallocate_return Q
-test_heap Need u==1 | put_list Y=y r r => test_heap_1_put_list Need Y
+test_heap Need u==1 | put_list Y=y x==0 x==0 => test_heap_1_put_list Need Y
%macro: test_heap_1_put_list TestHeapPutList -pack
test_heap_1_put_list I y
@@ -660,18 +624,18 @@ test_heap_1_put_list I y
is_tuple Fail Literal=q => move Literal x | is_tuple Fail x
is_tuple Fail=f c => jump Fail
-is_tuple Fail=f S=rxy | test_arity Fail=f S=rxy Arity => is_tuple_of_arity Fail S Arity
+is_tuple Fail=f S=xy | test_arity Fail=f S=xy Arity => is_tuple_of_arity Fail S Arity
%macro:is_tuple_of_arity IsTupleOfArity -fail_action
+is_tuple_of_arity f r A
is_tuple_of_arity f x A
is_tuple_of_arity f y A
-is_tuple_of_arity f r A
%macro: is_tuple IsTuple -fail_action
+is_tuple f r
is_tuple f x
is_tuple f y
-is_tuple f r
test_arity Fail Literal=q Arity => move Literal x | test_arity Fail x Arity
test_arity Fail=f c Arity => jump Fail
@@ -679,95 +643,66 @@ test_arity Fail=f c Arity => jump Fail
%macro: test_arity IsArity -fail_action
test_arity f x A
test_arity f y A
-test_arity f r A
-
-is_tuple_of_arity Fail=f Reg Arity | get_tuple_element Reg P=u==0 Dst=xy => \
- is_tuple_of_arity Fail Reg Arity | extract_next_element Dst | original_reg Reg P
-
-test_arity Fail Reg Arity | get_tuple_element Reg P=u==0 Dst=xy => \
- test_arity Fail Reg Arity | extract_next_element Dst | original_reg Reg P
-
-original_reg Reg P1 | get_tuple_element Reg P2 Dst=xy | succ(P1, P2) => \
- extract_next_element Dst | original_reg Reg P2
-get_tuple_element Reg P Dst => i_get_tuple_element Reg P Dst | original_reg Reg P
+get_tuple_element Reg=x P1 D1=x | get_tuple_element Reg=x P2 D2=x | \
+ get_tuple_element Reg=x P3 D3=x | \
+ succ(P1, P2) | succ(P2, P3) | \
+ succ(D1, D2) | succ(D2, D3) => i_get_tuple_element3 Reg P1 D1
-original_reg Reg Pos =>
+get_tuple_element Reg=x P1 D1=x | get_tuple_element Reg=x P2 D2=x | \
+ succ(P1, P2) | succ(D1, D2) => i_get_tuple_element2 Reg P1 D1
-original_reg/2
+get_tuple_element Reg=x P1 D1=y | get_tuple_element Reg=x P2 D2=y | \
+ succ(P1, P2) => i_get_tuple_element2y Reg P1 D1 D2
-extract_next_element D1=xy | original_reg Reg P1 | get_tuple_element Reg P2 D2=xy | \
-succ(P1, P2) | succ(D1, D2) => \
- extract_next_element2 D1 | original_reg Reg P2
-
-extract_next_element2 D1=xy | original_reg Reg P1 | get_tuple_element Reg P2 D2=xy | \
-succ(P1, P2) | succ2(D1, D2) => \
- extract_next_element3 D1 | original_reg Reg P2
-
-#extract_next_element3 D1=xy | original_reg Reg P1 | get_tuple_element Reg P2 D2=xy | \
-#succ(P1, P2) | succ3(D1, D2) => \
-# extract_next_element4 D1 | original_reg Reg P2
-
-%macro: extract_next_element ExtractNextElement -pack
-extract_next_element x
-extract_next_element y
-
-%macro: extract_next_element2 ExtractNextElement2 -pack
-extract_next_element2 x
-extract_next_element2 y
-
-%macro: extract_next_element3 ExtractNextElement3 -pack
-extract_next_element3 x
-extract_next_element3 y
-
-#%macro: extract_next_element4 ExtractNextElement4 -pack
-#extract_next_element4 x
-#extract_next_element4 y
+get_tuple_element Reg P Dst => i_get_tuple_element Reg P Dst
is_integer Fail=f i =>
is_integer Fail=f an => jump Fail
is_integer Fail Literal=q => move Literal x | is_integer Fail x
-is_integer Fail=f S=rx | allocate Need Regs => is_integer_allocate Fail S Need Regs
+is_integer Fail=f S=x | allocate Need Regs => is_integer_allocate Fail S Need Regs
%macro: is_integer_allocate IsIntegerAllocate -fail_action
is_integer_allocate f x I I
-is_integer_allocate f r I I
%macro: is_integer IsInteger -fail_action
is_integer f x
is_integer f y
-is_integer f r
is_list Fail=f n =>
is_list Fail Literal=q => move Literal x | is_list Fail x
is_list Fail=f c => jump Fail
%macro: is_list IsList -fail_action
-is_list f r
is_list f x
%cold
is_list f y
%hot
-is_nonempty_list Fail=f S=rx | allocate Need Rs => is_nonempty_list_allocate Fail S Need Rs
+is_nonempty_list Fail=f S=x | allocate Need Rs => is_nonempty_list_allocate Fail S Need Rs
%macro:is_nonempty_list_allocate IsNonemptyListAllocate -fail_action -pack
-is_nonempty_list_allocate f x I t
is_nonempty_list_allocate f r I t
+is_nonempty_list_allocate f x I t
-is_nonempty_list F=f r | test_heap I1 I2 => is_non_empty_list_test_heap F r I1 I2
+is_nonempty_list F=f x==0 | test_heap I1 I2 => is_non_empty_list_test_heap F I1 I2
%macro: is_non_empty_list_test_heap IsNonemptyListTestHeap -fail_action -pack
-is_non_empty_list_test_heap f r I t
+is_non_empty_list_test_heap f I t
+
+is_nonempty_list Fail=f S=x | get_list S D1=x D2=x => \
+ is_nonempty_list_get_list Fail S D1 D2
+
+%macro: is_nonempty_list_get_list IsNonemptyListGetList -fail_action -pack
+is_nonempty_list_get_list f r x x
+is_nonempty_list_get_list f x x x
%macro: is_nonempty_list IsNonemptyList -fail_action
is_nonempty_list f x
is_nonempty_list f y
-is_nonempty_list f r
%macro: is_atom IsAtom -fail_action
is_atom f x
-is_atom f r
%cold
is_atom f y
%hot
@@ -775,7 +710,6 @@ is_atom Fail=f a =>
is_atom Fail=f niq => jump Fail
%macro: is_float IsFloat -fail_action
-is_float f r
is_float f x
%cold
is_float f y
@@ -789,12 +723,10 @@ is_nil Fail=f qia => jump Fail
%macro: is_nil IsNil -fail_action
is_nil f x
is_nil f y
-is_nil f r
is_binary Fail Literal=q => move Literal x | is_binary Fail x
is_binary Fail=f c => jump Fail
%macro: is_binary IsBinary -fail_action
-is_binary f r
is_binary f x
%cold
is_binary f y
@@ -806,7 +738,6 @@ is_bitstr Fail Term => is_bitstring Fail Term
is_bitstring Fail Literal=q => move Literal x | is_bitstring Fail x
is_bitstring Fail=f c => jump Fail
%macro: is_bitstring IsBitstring -fail_action
-is_bitstring f r
is_bitstring f x
%cold
is_bitstring f y
@@ -814,7 +745,6 @@ is_bitstring f y
is_reference Fail=f cq => jump Fail
%macro: is_reference IsRef -fail_action
-is_reference f r
is_reference f x
%cold
is_reference f y
@@ -822,7 +752,6 @@ is_reference f y
is_pid Fail=f cq => jump Fail
%macro: is_pid IsPid -fail_action
-is_pid f r
is_pid f x
%cold
is_pid f y
@@ -830,7 +759,6 @@ is_pid f y
is_port Fail=f cq => jump Fail
%macro: is_port IsPort -fail_action
-is_port f r
is_port f x
%cold
is_port f y
@@ -842,14 +770,12 @@ is_boolean Fail=f ac => jump Fail
%cold
%macro: is_boolean IsBoolean -fail_action
-is_boolean f r
is_boolean f x
is_boolean f y
%hot
is_function2 Fail=f acq Arity => jump Fail
is_function2 Fail=f Fun a => jump Fail
-is_function2 Fail Fun Literal=q => move Literal x | is_function2 Fail Fun x
is_function2 f s s
%macro: is_function2 IsFunction2 -fail_action
@@ -989,76 +915,76 @@ call_ext_only u==3 u$func:erlang:hibernate/3 => i_hibernate
%unless USE_VM_PROBES
call_ext Arity u$func:erlang:dt_get_tag/0 => \
- move a=am_undefined r
+ move a=am_undefined x=0
call_ext_last Arity u$func:erlang:dt_get_tag/0 D => \
- move a=am_undefined r | deallocate D | return
+ move a=am_undefined x=0 | deallocate D | return
call_ext_only Arity u$func:erlang:dt_get_tag/0 => \
- move a=am_undefined r | return
-
-move Any r | call_ext Arity u$func:erlang:dt_put_tag/1 => \
- move a=am_undefined r
-move Any r | call_ext_last Arity u$func:erlang:dt_put_tag/1 D => \
- move a=am_undefined r | deallocate D | return
-move Any r | call_ext_only Arity u$func:erlang:dt_put_tag/1 => \
- move a=am_undefined r | return
+ move a=am_undefined x=0 | return
+
+move Any x==0 | call_ext Arity u$func:erlang:dt_put_tag/1 => \
+ move a=am_undefined x=0
+move Any x==0 | call_ext_last Arity u$func:erlang:dt_put_tag/1 D => \
+ move a=am_undefined x=0 | deallocate D | return
+move Any x==0 | call_ext_only Arity u$func:erlang:dt_put_tag/1 => \
+ move a=am_undefined x=0 | return
call_ext Arity u$func:erlang:dt_put_tag/1 => \
- move a=am_undefined r
+ move a=am_undefined x=0
call_ext_last Arity u$func:erlang:dt_put_tag/1 D => \
- move a=am_undefined r | deallocate D | return
+ move a=am_undefined x=0 | deallocate D | return
call_ext_only Arity u$func:erlang:dt_put_tag/1 => \
- move a=am_undefined r | return
+ move a=am_undefined x=0 | return
call_ext Arity u$func:erlang:dt_get_tag_data/0 => \
- move a=am_undefined r
+ move a=am_undefined x=0
call_ext_last Arity u$func:erlang:dt_get_tag_data/0 D => \
- move a=am_undefined r | deallocate D | return
+ move a=am_undefined x=0 | deallocate D | return
call_ext_only Arity u$func:erlang:dt_get_tag_data/0 => \
- move a=am_undefined r | return
-
-move Any r | call_ext Arity u$func:erlang:dt_spread_tag/1 => \
- move a=am_true r
-move Any r | call_ext_last Arity u$func:erlang:dt_spread_tag/1 D => \
- move a=am_true r | deallocate D | return
-move Any r | call_ext_only Arity u$func:erlang:dt_spread_tag/1 => \
- move a=am_true r | return
+ move a=am_undefined x=0 | return
+
+move Any x==0 | call_ext Arity u$func:erlang:dt_spread_tag/1 => \
+ move a=am_true x=0
+move Any x==0 | call_ext_last Arity u$func:erlang:dt_spread_tag/1 D => \
+ move a=am_true x=0 | deallocate D | return
+move Any x==0 | call_ext_only Arity u$func:erlang:dt_spread_tag/1 => \
+ move a=am_true x=0 | return
call_ext Arity u$func:erlang:dt_spread_tag/1 => \
- move a=am_true r
+ move a=am_true x=0
call_ext_last Arity u$func:erlang:dt_spread_tag/1 D => \
- move a=am_true r | deallocate D | return
+ move a=am_true x=0 | deallocate D | return
call_ext_only Arity u$func:erlang:dt_spread_tag/1 => \
- move a=am_true r | return
-
-move Any r | call_ext Arity u$func:erlang:dt_restore_tag/1 => \
- move a=am_true r
-move Any r | call_ext_last Arity u$func:erlang:dt_restore_tag/1 D => \
- move a=am_true r | deallocate D | return
-move Any r | call_ext_only Arity u$func:erlang:dt_restore_tag/1 => \
- move a=am_true r | return
+ move a=am_true x=0 | return
+
+move Any x==0 | call_ext Arity u$func:erlang:dt_restore_tag/1 => \
+ move a=am_true x=0
+move Any x==0 | call_ext_last Arity u$func:erlang:dt_restore_tag/1 D => \
+ move a=am_true x=0 | deallocate D | return
+move Any x==0 | call_ext_only Arity u$func:erlang:dt_restore_tag/1 => \
+ move a=am_true x=0 | return
call_ext Arity u$func:erlang:dt_restore_tag/1 => \
- move a=am_true r
+ move a=am_true x=0
call_ext_last Arity u$func:erlang:dt_restore_tag/1 D => \
- move a=am_true r | deallocate D | return
+ move a=am_true x=0 | deallocate D | return
call_ext_only Arity u$func:erlang:dt_restore_tag/1 => \
- move a=am_true r | return
-
-move Any r | call_ext Arity u$func:erlang:dt_prepend_vm_tag_data/1 => \
- move Any r
-move Any r | call_ext_last Arity u$func:erlang:dt_prepend_vm_tag_data/1 D => \
- move Any r | deallocate D | return
-move Any r | call_ext_only Arity u$func:erlang:dt_prepend_vm_tag_data/1 => \
- move Any r | return
+ move a=am_true x=0 | return
+
+move Any x==0 | call_ext Arity u$func:erlang:dt_prepend_vm_tag_data/1 => \
+ move Any x=0
+move Any x==0 | call_ext_last Arity u$func:erlang:dt_prepend_vm_tag_data/1 D => \
+ move Any x=0 | deallocate D | return
+move Any x==0 | call_ext_only Arity u$func:erlang:dt_prepend_vm_tag_data/1 => \
+ move Any x=0 | return
call_ext Arity u$func:erlang:dt_prepend_vm_tag_data/1 =>
call_ext_last Arity u$func:erlang:dt_prepend_vm_tag_data/1 D => \
deallocate D | return
call_ext_only Arity u$func:erlang:dt_prepend_vm_tag_data/1 => \
return
-move Any r | call_ext Arity u$func:erlang:dt_append_vm_tag_data/1 => \
- move Any r
-move Any r | call_ext_last Arity u$func:erlang:dt_append_vm_tag_data/1 D => \
- move Any r | deallocate D | return
-move Any r | call_ext_only Arity u$func:erlang:dt_append_vm_tag_data/1 => \
- move Any r | return
+move Any x==0 | call_ext Arity u$func:erlang:dt_append_vm_tag_data/1 => \
+ move Any x=0
+move Any x==0 | call_ext_last Arity u$func:erlang:dt_append_vm_tag_data/1 D => \
+ move Any x=0 | deallocate D | return
+move Any x==0 | call_ext_only Arity u$func:erlang:dt_append_vm_tag_data/1 => \
+ move Any x=0 | return
call_ext Arity u$func:erlang:dt_append_vm_tag_data/1 =>
call_ext_last Arity u$func:erlang:dt_append_vm_tag_data/1 D => \
deallocate D | return
@@ -1066,10 +992,17 @@ call_ext_only Arity u$func:erlang:dt_append_vm_tag_data/1 => \
return
# Can happen after one of the transformations above.
-move Discarded r | move Something r => move Something r
+move Discarded x==0 | move Something x==0 => move Something x=0
%endif
+call_ext u==0 u$func:os:perf_counter/0 => \
+ i_perf_counter
+call_ext_last u==0 u$func:os:perf_counter/0 D => \
+ i_perf_counter | deallocate_return D
+call_ext_only u==0 u$func:os:perf_counter/0 => \
+ i_perf_counter | return
+
#
# The general case for BIFs that have no special instructions.
# A BIF used in the tail must be followed by a return instruction.
@@ -1091,9 +1024,9 @@ call_ext_only Ar=u Bif=u$is_bif => \
# with call instructions.
#
-move S=c r | call_ext Ar=u Func=u$is_not_bif => i_move_call_ext S r Func
-move S=c r | call_ext_last Ar=u Func=u$is_not_bif D => i_move_call_ext_last Func D S r
-move S=c r | call_ext_only Ar=u Func=u$is_not_bif => i_move_call_ext_only Func S r
+move S=c x==0 | call_ext Ar=u Func=u$is_not_bif => i_move_call_ext S Func
+move S=c x==0 | call_ext_last Ar=u Func=u$is_not_bif D => i_move_call_ext_last Func D S
+move S=c x==0 | call_ext_only Ar=u Func=u$is_not_bif => i_move_call_ext_only Func S
call_ext Ar Func => i_call_ext Func
call_ext_last Ar Func D => i_call_ext_last Func D
@@ -1109,6 +1042,8 @@ i_apply_fun_only
i_hibernate
+i_perf_counter
+
call_bif e
#
@@ -1118,80 +1053,74 @@ call_bif e
bif0 u$bif:erlang:self/0 Dst=d => self Dst
bif0 u$bif:erlang:node/0 Dst=d => node Dst
-bif1 Fail Bif=u$bif:erlang:get/1 Src=s Dst=d => i_get Src Dst
+bif1 Fail Bif=u$bif:erlang:get/1 Src=s Dst=d => gen_get(Src, Dst)
-bif2 Jump=j u$bif:erlang:element/2 S1=s S2=rxy Dst=d => gen_element(Jump, S1, S2, Dst)
+bif2 Jump=j u$bif:erlang:element/2 S1=s S2=xy Dst=d => gen_element(Jump, S1, S2, Dst)
-bif1 Fail Bif Literal=q Dst => move Literal x | bif1 Fail Bif x Dst
bif1 p Bif S1 Dst => bif1_body Bif S1 Dst
-bif1_body Bif Literal=q Dst => move Literal x | bif1_body Bif x Dst
-
-bif2 p Bif S1 S2 Dst => i_fetch S1 S2 | i_bif2_body Bif Dst
-bif2 Fail Bif S1 S2 Dst => i_fetch S1 S2 | i_bif2 Fail Bif Dst
+bif2 p Bif S1 S2 Dst => i_bif2_body Bif S1 S2 Dst
+bif2 Fail Bif S1 S2 Dst => i_bif2 Fail Bif S1 S2 Dst
+i_get_hash c I d
i_get s d
%macro: self Self
-self r
self x
self y
%macro: node Node
-node r
node x
%cold
node y
%hot
-i_fast_element r j I d
-i_fast_element x j I d
-i_fast_element y j I d
+i_fast_element j x I d
+i_fast_element j y I d
-i_element r j s d
-i_element x j s d
-i_element y j s d
+i_element j x s d
+i_element j y s d
bif1 f b s d
bif1_body b s d
-i_bif2 f b d
-i_bif2_body b d
+i_bif2 f b s s d
+i_bif2_body b s s d
#
# Internal calls.
#
-move S=c r | call Ar P=f => i_move_call S r P
-move S=s r | call Ar P=f => move_call S r P
+move S=c x==0 | call Ar P=f => i_move_call S P
+move S=s x==0 | call Ar P=f => move_call S P
-i_move_call c r f
+i_move_call c f
%macro:move_call MoveCall -arg_f -size -nonext
-move_call/3
+move_call/2
-move_call x r f
-move_call y r f
+move_call x f
+move_call y f
-move S=c r | call_last Ar P=f D => i_move_call_last P D S r
-move S r | call_last Ar P=f D => move_call_last S r P D
+move S=c x==0 | call_last Ar P=f D => i_move_call_last P D S
+move S x==0 | call_last Ar P=f D => move_call_last S P D
-i_move_call_last f P c r
+i_move_call_last f P c
%macro:move_call_last MoveCallLast -arg_f -nonext -pack
-move_call_last/4
-move_call_last x r f Q
-move_call_last y r f Q
+move_call_last/3
+move_call_last x f Q
+move_call_last y f Q
-move S=c r | call_only Ar P=f => i_move_call_only P S r
-move S=x r | call_only Ar P=f => move_call_only S r P
+move S=c x==0 | call_only Ar P=f => i_move_call_only P S
+move S=x x==0 | call_only Ar P=f => move_call_only S P
-i_move_call_only f c r
+i_move_call_only f c
%macro:move_call_only MoveCallOnly -arg_f -nonext
-move_call_only/3
+move_call_only/2
-move_call_only x r f
+move_call_only x f
call Ar Func => i_call Func
call_last Ar Func D => i_call_last Func D
@@ -1205,9 +1134,9 @@ i_call_ext e
i_call_ext_last e P
i_call_ext_only e
-i_move_call_ext c r e
-i_move_call_ext_last e P c r
-i_move_call_ext_only e c r
+i_move_call_ext c e
+i_move_call_ext_last e P c
+i_move_call_ext_only e c
# Fun calls.
@@ -1227,7 +1156,6 @@ i_make_fun I t
%macro: is_function IsFunction -fail_action
is_function f x
is_function f y
-is_function f r
is_function Fail=f c => jump Fail
func_info M F A => i_func_info u M F A
@@ -1239,131 +1167,105 @@ 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 r f I I d
i_bs_start_match2 x f I I d
i_bs_start_match2 y f I I d
bs_save2 Reg Index => gen_bs_save(Reg, Index)
-i_bs_save2 r I
i_bs_save2 x I
bs_restore2 Reg Index => gen_bs_restore(Reg, Index)
-i_bs_restore2 r I
i_bs_restore2 x I
# Matching integers
bs_match_string Fail Ms Bits Val => i_bs_match_string Ms Fail Bits Val
-i_bs_match_string r f I I
i_bs_match_string x f I I
# Fetching integers from binaries.
-bs_get_integer2 Fail=f Ms=rx Live=u Sz=sq Unit=u Flags=u Dst=d => \
+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 r I f I d
i_bs_get_integer_small_imm x I f I d
-i_bs_get_integer_imm r I I f I d
i_bs_get_integer_imm x I I f I d
-i_bs_get_integer f I I d
-i_bs_get_integer_8 r f d
+i_bs_get_integer f I I s s d
i_bs_get_integer_8 x f d
-i_bs_get_integer_16 r f d
i_bs_get_integer_16 x f d
-i_bs_get_integer_32 r f I d
i_bs_get_integer_32 x f I d
# Fetching binaries from binaries.
-bs_get_binary2 Fail=f Ms=rx Live=u Sz=sq Unit=u Flags=u Dst=d => \
+bs_get_binary2 Fail=f Ms=x Live=u Sz=sq Unit=u Flags=u Dst=d => \
gen_get_binary2(Fail, Ms, Live, Sz, Unit, Flags, Dst)
%macro: i_bs_get_binary_imm2 BsGetBinaryImm_2 -fail_action -gen_dest
%macro: i_bs_get_binary2 BsGetBinary_2 -fail_action -gen_dest
%macro: i_bs_get_binary_all2 BsGetBinaryAll_2 -fail_action -gen_dest
-i_bs_get_binary_imm2 f r I I I d
i_bs_get_binary_imm2 f x I I I d
-i_bs_get_binary2 f r I s I d
i_bs_get_binary2 f x I s I d
-i_bs_get_binary_all2 f r I I d
i_bs_get_binary_all2 f x I I d
-i_bs_get_binary_all_reuse r f I
i_bs_get_binary_all_reuse x f I
# Fetching float from binaries.
-bs_get_float2 Fail=f Ms=rx Live=u Sz=s Unit=u Flags=u Dst=d => \
+bs_get_float2 Fail=f Ms=x Live=u Sz=s Unit=u Flags=u Dst=d => \
gen_get_float2(Fail, Ms, Live, Sz, Unit, Flags, Dst)
-bs_get_float2 Fail=f Ms=rx Live=u Sz=q Unit=u Flags=u Dst=d => jump Fail
+bs_get_float2 Fail=f Ms=x Live=u Sz=q Unit=u Flags=u Dst=d => jump Fail
%macro: i_bs_get_float2 BsGetFloat2 -fail_action -gen_dest
-i_bs_get_float2 f r I s I d
i_bs_get_float2 f x I s I d
# Miscellanous
-bs_skip_bits2 Fail=f Ms=rx Sz=s Unit=u Flags=u => \
- gen_skip_bits2(Fail, Ms, Sz, Unit, Flags)
-bs_skip_bits2 Fail=f Ms=rx Sz=q Unit=u Flags=u => \
+bs_skip_bits2 Fail=f Ms=x Sz=sq Unit=u Flags=u => \
gen_skip_bits2(Fail, Ms, Sz, Unit, Flags)
%macro: i_bs_skip_bits_imm2 BsSkipBitsImm2 -fail_action
-i_bs_skip_bits_imm2 f r I
i_bs_skip_bits_imm2 f x I
%macro: i_bs_skip_bits2 BsSkipBits2 -fail_action
-i_bs_skip_bits2 f r x I
-i_bs_skip_bits2 f r y I
i_bs_skip_bits2 f x x I
-i_bs_skip_bits2 f x r I
i_bs_skip_bits2 f x y I
%macro: i_bs_skip_bits_all2 BsSkipBitsAll2 -fail_action
-i_bs_skip_bits_all2 f r I
i_bs_skip_bits_all2 f x I
-bs_test_tail2 Fail=f Ms=rx Bits=u==0 => bs_test_zero_tail2 Fail Ms
-bs_test_tail2 Fail=f Ms=rx Bits=u => bs_test_tail_imm2 Fail Ms Bits
-bs_test_zero_tail2 f r
+bs_test_tail2 Fail=f Ms=x Bits=u==0 => bs_test_zero_tail2 Fail Ms
+bs_test_tail2 Fail=f Ms=x Bits=u => bs_test_tail_imm2 Fail Ms Bits
bs_test_zero_tail2 f x
-bs_test_tail_imm2 f r I
bs_test_tail_imm2 f x I
bs_test_unit F Ms Unit=u==8 => bs_test_unit8 F Ms
-bs_test_unit f r I
bs_test_unit f x I
-bs_test_unit8 f r
bs_test_unit8 f x
-bs_context_to_binary r
+# An y register operand for bs_context_to_binary is rare,
+# but can happen because of inlining.
+
+bs_context_to_binary Y=y => move Y x | bs_context_to_binary x
+
bs_context_to_binary x
-bs_context_to_binary y
#
# Utf8/utf16/utf32 support. (R12B-5)
#
-bs_get_utf8 Fail=f Ms=rx u u Dst=d => i_bs_get_utf8 Ms Fail Dst
-i_bs_get_utf8 r f d
+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
-bs_skip_utf8 Fail=f Ms=rx u u => i_bs_get_utf8 Ms Fail x
+bs_skip_utf8 Fail=f Ms=x u u => i_bs_get_utf8 Ms Fail x
-bs_get_utf16 Fail=f Ms=rx u Flags=u Dst=d => i_bs_get_utf16 Ms Fail Flags Dst
-bs_skip_utf16 Fail=f Ms=rx u Flags=u => i_bs_get_utf16 Ms Fail Flags 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 r f I d
i_bs_get_utf16 x f I d
-bs_get_utf32 Fail=f Ms=rx Live=u Flags=u Dst=d => \
+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 | \
- i_fetch Dst Ms | \
- i_bs_validate_unicode_retract Fail
-bs_skip_utf32 Fail=f Ms=rx Live=u Flags=u => \
+ i_bs_validate_unicode_retract Fail Dst Ms
+bs_skip_utf32 Fail=f Ms=x Live=u Flags=u => \
bs_get_integer2 Fail Ms Live i=32 u=1 Flags x | \
- i_fetch x Ms | \
- i_bs_validate_unicode_retract Fail
+ i_bs_validate_unicode_retract Fail x Ms
-i_bs_validate_unicode_retract j
+i_bs_validate_unicode_retract j s s
%hot
#
@@ -1385,13 +1287,12 @@ bs_init2 Fail Sz=u Words Regs Flags Dst => \
bs_init2 Fail Sz Words=u==0 Regs Flags Dst => \
i_bs_init_fail Sz Fail Regs Dst
bs_init2 Fail Sz Words Regs Flags Dst => \
- i_fetch Sz r | i_bs_init_fail_heap Words Fail Regs Dst
+ i_bs_init_fail_heap Sz Words Fail Regs Dst
-i_bs_init_fail r j I d
i_bs_init_fail x j I d
i_bs_init_fail y j I d
-i_bs_init_fail_heap I j I d
+i_bs_init_fail_heap s I j I d
i_bs_init I I d
i_bs_init_heap_bin I I d
@@ -1408,39 +1309,35 @@ bs_init_bits Fail Sz=u Words Regs Flags Dst => i_bs_init_bits_heap Sz Words Reg
bs_init_bits Fail Sz Words=u==0 Regs Flags Dst => \
i_bs_init_bits_fail Sz Fail Regs Dst
bs_init_bits Fail Sz Words Regs Flags Dst => \
- i_fetch Sz r | i_bs_init_bits_fail_heap Words Fail Regs Dst
+ i_bs_init_bits_fail_heap Sz Words Fail Regs Dst
-i_bs_init_bits_fail r j I d
i_bs_init_bits_fail x j I d
i_bs_init_bits_fail y j I d
-i_bs_init_bits_fail_heap I j I d
+i_bs_init_bits_fail_heap s I j I d
i_bs_init_bits I I d
i_bs_init_bits_heap I I I d
bs_add Fail S1=i==0 S2 Unit=u==1 D => move S2 D
-bs_add Fail S1 S2 Unit D => i_fetch S1 S2 | i_bs_add Fail Unit D
-i_bs_add j I d
+bs_add j s s I d
bs_append Fail Size Extra Live Unit Bin Flags Dst => \
- i_fetch Size Bin | i_bs_append Fail Extra Live Unit Dst
+ move Bin x | i_bs_append Fail Extra Live Unit Size Dst
bs_private_append Fail Size Unit Bin Flags Dst => \
- i_fetch Size Bin | i_bs_private_append Fail Unit Dst
+ i_bs_private_append Fail Unit Size Bin Dst
bs_init_writable
-i_bs_append j I I I d
-i_bs_private_append j I d
+i_bs_append j I I I s d
+i_bs_private_append j I s s d
#
# Storing integers into binaries.
#
-bs_put_integer Fail=j Sz=s Unit=u Flags=u Literal=q => \
- move Literal x | bs_put_integer Fail Sz Unit Flags x
bs_put_integer Fail=j Sz=sq Unit=u Flags=u Src=s => \
gen_put_integer(Fail, Sz, Unit, Flags, Src)
@@ -1454,32 +1351,20 @@ i_new_bs_put_integer_imm j I I s
# Utf8/utf16/utf32 support. (R12B-5)
#
-bs_utf8_size Fail Literal=q Dst=d => \
- move Literal x | bs_utf8_size Fail x Dst
bs_utf8_size j Src=s Dst=d => i_bs_utf8_size Src Dst
i_bs_utf8_size s d
-bs_utf16_size Fail Literal=q Dst=d => \
- move Literal x | bs_utf16_size Fail x Dst
bs_utf16_size j Src=s Dst=d => i_bs_utf16_size Src Dst
i_bs_utf16_size s d
-bs_put_utf8 Fail=j Flags=u Literal=q => \
- move Literal x | bs_put_utf8 Fail Flags x
bs_put_utf8 Fail u Src=s => i_bs_put_utf8 Fail Src
i_bs_put_utf8 j s
-bs_put_utf16 Fail=j Flags=u Literal=q => \
- move Literal x | bs_put_utf16 Fail Flags x
-bs_put_utf16 Fail Flags=u Src=s => i_bs_put_utf16 Fail Flags Src
-
-i_bs_put_utf16 j I s
+bs_put_utf16 j I s
-bs_put_utf32 Fail=j Flags=u Literal=q => \
- move Literal x | bs_put_utf32 Fail Flags x
bs_put_utf32 Fail=j Flags=u Src=s => \
i_bs_validate_unicode Fail Src | bs_put_integer Fail i=32 u=1 Flags Src
@@ -1490,9 +1375,6 @@ i_bs_validate_unicode j s
#
bs_put_float Fail Sz=q Unit Flags Val => badarg Fail
-bs_put_float Fail=j Sz Unit=u Flags=u Literal=q => \
- move Literal x | bs_put_float Fail Sz Unit Flags x
-
bs_put_float Fail=j Sz=s Unit=u Flags=u Src=s => \
gen_put_float(Fail, Sz, Unit, Flags, Src)
@@ -1506,8 +1388,6 @@ i_new_bs_put_float_imm j I I s
# Storing binaries into binaries.
#
-bs_put_binary Fail Sz Unit Flags Literal=q => \
- move Literal x | bs_put_binary Fail Sz Unit Flags x
bs_put_binary Fail=j Sz=s Unit=u Flags=u Src=s => \
gen_put_binary(Fail, Sz, Unit, Flags, Src)
@@ -1600,7 +1480,6 @@ is_map Fail Lit=q | literal_is_map(Lit) =>
is_map Fail cq => jump Fail
%macro: is_map IsMap -fail_action
-is_map f r
is_map f x
is_map f y
@@ -1611,101 +1490,119 @@ has_map_fields Fail Src Size Rest=* => \
## Transform get_map_elements(s) #{ K1 := V1, K2 := V2 }
-get_map_elements Fail Src=rxy Size=u==2 Rest=* => \
+get_map_elements Fail Src=xy Size=u==2 Rest=* => \
gen_get_map_element(Fail, Src, Size, Rest)
get_map_elements Fail Src Size Rest=* | map_key_sort(Size, Rest) => \
gen_get_map_elements(Fail, Src, Size, Rest)
i_get_map_elements f s I
-i_get_map_element Fail Src=rxy Key=ry Dst => \
+i_get_map_element Fail Src=xy Key=y Dst => \
move Key x | i_get_map_element Fail Src x Dst
%macro: i_get_map_element_hash GetMapElementHash -fail_action
-i_get_map_element_hash f r c I r
-i_get_map_element_hash f x c I r
-i_get_map_element_hash f y c I r
-i_get_map_element_hash f r c I x
i_get_map_element_hash f x c I x
i_get_map_element_hash f y c I x
-i_get_map_element_hash f r c I y
i_get_map_element_hash f x c I y
i_get_map_element_hash f y c I y
%macro: i_get_map_element GetMapElement -fail_action
-i_get_map_element f r x r
-i_get_map_element f x x r
-i_get_map_element f y x r
-i_get_map_element f r x x
i_get_map_element f x x x
i_get_map_element f y x x
-i_get_map_element f r x y
i_get_map_element f x x y
i_get_map_element f y x y
#
+# Convert the plus operations to a generic plus instruction.
+#
+gen_plus/5
+gen_minus/5
+
+gc_bif1 Fail Live u$bif:erlang:splus/1 Src Dst => \
+ gen_plus Fail Live Src i Dst
+gc_bif2 Fail Live u$bif:erlang:splus/2 S1 S2 Dst => \
+ gen_plus Fail Live S1 S2 Dst
+
+gc_bif1 Fail Live u$bif:erlang:sminus/1 Src Dst => \
+ gen_minus Fail Live i Src Dst
+gc_bif2 Fail Live u$bif:erlang:sminus/2 S1 S2 Dst => \
+ gen_minus Fail Live S1 S2 Dst
+
+#
# Optimize addition and subtraction of small literals using
# the i_increment/4 instruction (in bodies, not in guards).
#
-gc_bif2 p Live u$bif:erlang:splus/2 Int=i Reg=d Dst => \
+gen_plus p Live Int=i Reg=d Dst => \
gen_increment(Reg, Int, Live, Dst)
-gc_bif2 p Live u$bif:erlang:splus/2 Reg=d Int=i Dst => \
+gen_plus p Live Reg=d Int=i Dst => \
gen_increment(Reg, Int, Live, Dst)
-gc_bif2 p Live u$bif:erlang:sminus/2 Reg=d Int=i Dst | \
- negation_is_small(Int) => \
+gen_minus p Live Reg=d Int=i Dst | negation_is_small(Int) => \
gen_increment_from_minus(Reg, Int, Live, Dst)
#
# GCing arithmetic instructions.
#
-gc_bif2 Fail I u$bif:erlang:splus/2 S1=x S2=x Dst=d => i_plus Fail I S1 S2 Dst
-gc_bif2 Fail I u$bif:erlang:splus/2 S1 S2 Dst=d => i_fetch S1 S2 | i_plus Fail I Dst
-gc_bif2 Fail I u$bif:erlang:sminus/2 S1=x S2=x Dst=d => i_minus Fail I S1 S2 Dst
-gc_bif2 Fail I u$bif:erlang:sminus/2 S1 S2 Dst=d => i_fetch S1 S2 | i_minus Fail I Dst
-gc_bif2 Fail I u$bif:erlang:stimes/2 S1 S2 Dst=d => i_fetch S1 S2 | i_times Fail I Dst
-gc_bif2 Fail I u$bif:erlang:div/2 S1 S2 Dst=d => i_fetch S1 S2 | i_m_div Fail I Dst
+gen_plus Fail Live S1 S2 Dst => i_plus Fail Live S1 S2 Dst
-gc_bif2 Fail I u$bif:erlang:intdiv/2 S1 S2 Dst=d => i_fetch S1 S2 | i_int_div Fail I Dst
-gc_bif2 Fail I u$bif:erlang:rem/2 S1=x S2=x Dst=d => i_rem Fail I S1 S2 Dst
-gc_bif2 Fail I u$bif:erlang:rem/2 S1 S2 Dst=d => i_fetch S1 S2 | i_rem Fail I Dst
+gen_minus Fail Live S1 S2 Dst => i_minus Fail Live S1 S2 Dst
-gc_bif2 Fail I u$bif:erlang:bsl/2 S1 S2 Dst=d => i_fetch S1 S2 | i_bsl Fail I Dst
-gc_bif2 Fail I u$bif:erlang:bsr/2 S1 S2 Dst=d => i_fetch S1 S2 | i_bsr Fail I Dst
+gc_bif2 Fail Live u$bif:erlang:stimes/2 S1 S2 Dst => \
+ i_times Fail Live S1 S2 Dst
-gc_bif2 Fail I u$bif:erlang:band/2 S1=x S2=c Dst=d => i_band Fail I S1 S2 Dst
-gc_bif2 Fail I u$bif:erlang:band/2 S1 S2 Dst=d => i_fetch S1 S2 | i_band Fail I Dst
-gc_bif2 Fail I u$bif:erlang:bor/2 S1 S2 Dst=d => i_fetch S1 S2 | i_bor Fail I Dst
-gc_bif2 Fail I u$bif:erlang:bxor/2 S1 S2 Dst=d => i_fetch S1 S2 | i_bxor Fail I Dst
+gc_bif2 Fail Live u$bif:erlang:div/2 S1 S2 Dst => \
+ i_m_div Fail Live S1 S2 Dst
+gc_bif2 Fail Live u$bif:erlang:intdiv/2 S1 S2 Dst => \
+ i_int_div Fail Live S1 S2 Dst
-gc_bif1 Fail I u$bif:erlang:bnot/1 Src Dst=d => i_int_bnot Fail Src I Dst
+gc_bif2 Fail Live u$bif:erlang:rem/2 S1 S2 Dst => \
+ i_rem Fail Live S1 S2 Dst
+
+gc_bif2 Fail Live u$bif:erlang:bsl/2 S1 S2 Dst => \
+ i_bsl Fail Live S1 S2 Dst
+gc_bif2 Fail Live u$bif:erlang:bsr/2 S1 S2 Dst => \
+ i_bsr Fail Live S1 S2 Dst
+
+gc_bif2 Fail Live u$bif:erlang:band/2 S1 S2 Dst => \
+ i_band Fail Live S1 S2 Dst
-gc_bif1 Fail I u$bif:erlang:sminus/1 Src Dst=d => i_fetch i Src | i_minus Fail I Dst
-gc_bif1 Fail I u$bif:erlang:splus/1 Src Dst=d => i_fetch i Src | i_plus Fail I Dst
+gc_bif2 Fail Live u$bif:erlang:bor/2 S1 S2 Dst => \
+ i_bor Fail Live S1 S2 Dst
+
+gc_bif2 Fail Live u$bif:erlang:bxor/2 S1 S2 Dst => \
+ i_bxor Fail Live S1 S2 Dst
+
+gc_bif1 Fail I u$bif:erlang:bnot/1 Src Dst=d => i_int_bnot Fail Src I Dst
i_increment r I I d
i_increment x I I d
i_increment y I I d
i_plus j I x x d
-i_plus j I d
+i_plus j I x y d
+i_plus j I s s d
+
i_minus j I x x d
-i_minus j I d
-i_times j I d
-i_m_div j I d
-i_int_div j I d
+i_minus j I s s d
+
+i_times j I s s d
+
+i_m_div j I s s d
+i_int_div j I s s d
+
i_rem j I x x d
-i_rem j I d
+i_rem j I s s d
-i_bsl j I d
-i_bsr j I d
+i_bsl j I s s d
+i_bsr j I s s d
i_band j I x c d
-i_band j I d
-i_bor j I d
-i_bxor j I d
+i_band j I s s d
+
+i_bor j I s s d
+i_bxor j I s s d
i_int_bnot j s I d
@@ -1731,21 +1628,18 @@ gc_bif2 Fail I Bif S1 S2 Dst => \
gc_bif3 Fail I Bif S1 S2 S3 Dst => \
gen_guard_bif3(Fail, I, Bif, S1, S2, S3, Dst)
-i_gc_bif1 Fail Bif V=q Live D => move V x | i_gc_bif1 Fail Bif x Live D
-
i_gc_bif1 j I s I d
-ii_gc_bif2/6
-
-ii_gc_bif2 Fail Bif S1 S2 Live D => i_fetch S1 S2 | i_gc_bif2 Fail Bif Live D
-
-i_gc_bif2 j I I d
+i_gc_bif2 j I I s s d
ii_gc_bif3/7
-ii_gc_bif3 Fail Bif S1 S2 S3 Live D => move S1 x | i_fetch S2 S3 | i_gc_bif3 Fail Bif x Live D
+# A specific instruction can only have 6 operands, so we must
+# pass one of the arguments in an x register.
+ii_gc_bif3 Fail Bif Live S1 S2 S3 Dst => \
+ move S1 x | i_gc_bif3 Fail Bif Live S2 S3 Dst
-i_gc_bif3 j I s I d
+i_gc_bif3 j I I s s d
#
# The following instruction is specially handled in beam_load.c
diff --git a/erts/emulator/beam/packet_parser.c b/erts/emulator/beam/packet_parser.c
index a737a86f14..f14910bc72 100644
--- a/erts/emulator/beam/packet_parser.c
+++ b/erts/emulator/beam/packet_parser.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/packet_parser.h b/erts/emulator/beam/packet_parser.h
index 717d905fad..358d650804 100644
--- a/erts/emulator/beam/packet_parser.h
+++ b/erts/emulator/beam/packet_parser.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/register.c b/erts/emulator/beam/register.c
index 020e61d136..acda51c9fc 100644
--- a/erts/emulator/beam/register.c
+++ b/erts/emulator/beam/register.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -102,7 +102,7 @@ is_proc_alive(Process *p)
return !ERTS_PROC_IS_EXITING(p);
}
-void register_info(int to, void *to_arg)
+void register_info(fmtfn_t to, void *to_arg)
{
int lock = !ERTS_IS_CRASH_DUMPING;
if (lock)
@@ -151,6 +151,9 @@ void init_register_table(void)
f.cmp = (HCMP_FUN) reg_cmp;
f.alloc = (HALLOC_FUN) reg_alloc;
f.free = (HFREE_FUN) reg_free;
+ f.meta_alloc = (HMALLOC_FUN) erts_alloc;
+ f.meta_free = (HMFREE_FUN) erts_free;
+ f.meta_print = (HMPRINT_FUN) erts_print;
hash_init(ERTS_ALC_T_REG_TABLE, &process_reg, "process_reg",
PREG_HASH_SIZE, f);
@@ -223,7 +226,8 @@ int erts_register_name(Process *c_p, Eterm name, Eterm id)
rp = (RegProc*) hash_put(&process_reg, (void*) &r);
if (proc && rp->p == proc) {
if (IS_TRACED_FL(proc, F_TRACE_PROCS)) {
- trace_proc(c_p, proc, am_register, name);
+ trace_proc(proc, ERTS_PROC_LOCK_MAIN,
+ proc, am_register, name);
}
proc->common.u.alive.reg = rp;
}
@@ -319,7 +323,8 @@ erts_whereis_name(Process *c_p,
Process** proc,
ErtsProcLocks need_locks,
int flags,
- Port** port)
+ Port** port,
+ int lock_port)
{
RegProc* rp = NULL;
HashValue hval;
@@ -402,31 +407,33 @@ erts_whereis_name(Process *c_p,
*port = NULL;
else {
#ifdef ERTS_SMP
- if (pending_port == rp->pt)
- pending_port = NULL;
- else {
- if (pending_port) {
- /* Ahh! Registered port changed while reg lock
- was unlocked... */
- erts_port_release(pending_port);
- pending_port = NULL;
- }
+ if (lock_port) {
+ if (pending_port == rp->pt)
+ pending_port = NULL;
+ else {
+ if (pending_port) {
+ /* Ahh! Registered port changed while reg lock
+ was unlocked... */
+ erts_port_release(pending_port);
+ pending_port = NULL;
+ }
- if (erts_smp_port_trylock(rp->pt) == EBUSY) {
- Eterm id = rp->pt->common.id; /* id read only... */
- /* Unlock all locks, acquire port lock, and restart... */
- if (current_c_p_locks) {
- erts_smp_proc_unlock(c_p, current_c_p_locks);
- current_c_p_locks = 0;
- }
- reg_read_unlock();
- pending_port = erts_id2port(id);
- goto restart;
- }
- }
+ if (erts_smp_port_trylock(rp->pt) == EBUSY) {
+ Eterm id = rp->pt->common.id; /* id read only... */
+ /* Unlock all locks, acquire port lock, and restart... */
+ if (current_c_p_locks) {
+ erts_smp_proc_unlock(c_p, current_c_p_locks);
+ current_c_p_locks = 0;
+ }
+ reg_read_unlock();
+ pending_port = erts_id2port(id);
+ goto restart;
+ }
+ }
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(rp->pt));
+ }
#endif
*port = rp->pt;
- ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(*port));
}
}
@@ -448,7 +455,7 @@ erts_whereis_process(Process *c_p,
int flags)
{
Process *proc;
- erts_whereis_name(c_p, c_p_locks, name, &proc, need_locks, flags, NULL);
+ erts_whereis_name(c_p, c_p_locks, name, &proc, need_locks, flags, NULL, 0);
return proc;
}
@@ -467,8 +474,8 @@ int erts_unregister_name(Process *c_p,
int res = 0;
RegProc r, *rp;
Port *port = c_prt;
+ ErtsProcLocks current_c_p_locks = 0;
#ifdef ERTS_SMP
- ErtsProcLocks current_c_p_locks;
/*
* SMP note: If 'c_prt != NULL' and 'c_prt->reg->name == name',
@@ -534,8 +541,12 @@ int erts_unregister_name(Process *c_p,
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(port));
rp->pt->common.u.alive.reg = NULL;
-
+
if (IS_TRACED_FL(port, F_TRACE_PORTS)) {
+ if (current_c_p_locks) {
+ erts_smp_proc_unlock(c_p, current_c_p_locks);
+ current_c_p_locks = 0;
+ }
trace_port(port, am_unregister, r.name);
}
@@ -552,7 +563,8 @@ int erts_unregister_name(Process *c_p,
#endif
rp->p->common.u.alive.reg = NULL;
if (IS_TRACED_FL(rp->p, F_TRACE_PROCS)) {
- trace_proc(c_p, rp->p, am_unregister, r.name);
+ trace_proc(rp->p, (c_p == rp->p) ? c_p_locks : ERTS_PROC_LOCK_MAIN,
+ rp->p, am_unregister, r.name);
}
#ifdef ERTS_SMP
if (rp->p != c_p) {
diff --git a/erts/emulator/beam/register.h b/erts/emulator/beam/register.h
index 144536f34b..27a314ca78 100644
--- a/erts/emulator/beam/register.h
+++ b/erts/emulator/beam/register.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2012. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -44,12 +44,12 @@ typedef struct reg_proc
int process_reg_size(void);
int process_reg_sz(void);
void init_register_table(void);
-void register_info(int, void *);
+void register_info(fmtfn_t, void *);
int erts_register_name(Process *, Eterm, Eterm);
Eterm erts_whereis_name_to_id(Process *, Eterm);
void erts_whereis_name(Process *, ErtsProcLocks,
Eterm, Process**, ErtsProcLocks, int,
- Port**);
+ Port**, int);
Process *erts_whereis_process(Process *,
ErtsProcLocks,
Eterm,
diff --git a/erts/emulator/beam/safe_hash.c b/erts/emulator/beam/safe_hash.c
index 3f039c8dfd..30b26a7296 100644
--- a/erts/emulator/beam/safe_hash.c
+++ b/erts/emulator/beam/safe_hash.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/beam/safe_hash.h b/erts/emulator/beam/safe_hash.h
index a11370813c..285103cb17 100644
--- a/erts/emulator/beam/safe_hash.h
+++ b/erts/emulator/beam/safe_hash.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,14 +26,9 @@
#ifndef __SAFE_HASH_H__
#define __SAFE_HASH_H__
-
-#ifndef __SYS_H__
#include "sys.h"
-#endif
-
#include "erl_alloc.h"
-
typedef unsigned long SafeHashValue;
typedef int (*SHCMP_FUN)(void*, void*);
diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h
index 6497a1e648..dd4f05686b 100644
--- a/erts/emulator/beam/sys.h
+++ b/erts/emulator/beam/sys.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2013. 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.
@@ -21,6 +21,23 @@
#ifndef __SYS_H__
#define __SYS_H__
+#if !defined(__GNUC__)
+# define ERTS_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) 0
+#elif !defined(__GNUC_MINOR__)
+# define ERTS_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) \
+ ((__GNUC__ << 24) >= (((MAJ) << 24) | ((MIN) << 12) | (PL)))
+#elif !defined(__GNUC_PATCHLEVEL__)
+# define ERTS_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) \
+ (((__GNUC__ << 24) | (__GNUC_MINOR__ << 12)) >= (((MAJ) << 24) | ((MIN) << 12) | (PL)))
+#else
+# define ERTS_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) \
+ (((__GNUC__ << 24) | (__GNUC_MINOR__ << 12) | __GNUC_PATCHLEVEL__) >= (((MAJ) << 24) | ((MIN) << 12) | (PL)))
+#endif
+
+#if defined(ERTS_DIRTY_SCHEDULERS) && !defined(ERTS_SMP)
+# error "Dirty schedulers not supported without smp support"
+#endif
+
#ifdef ERTS_INLINE
# ifndef ERTS_CAN_INLINE
# define ERTS_CAN_INLINE 1
@@ -38,6 +55,17 @@
# endif
#endif
+#ifndef ERTS_FORCE_INLINE
+# if ERTS_AT_LEAST_GCC_VSN__(3,1,1)
+# define ERTS_FORCE_INLINE __inline__ __attribute__((__always_inline__))
+# elif defined(__WIN32__)
+# define ERTS_FORCE_INLINE __forceinline
+# endif
+# ifndef ERTS_FORCE_INLINE
+# define ERTS_FORCE_INLINE ERTS_INLINE
+# endif
+#endif
+
#if defined(DEBUG) || defined(ERTS_ENABLE_LOCK_CHECK)
# undef ERTS_CAN_INLINE
# define ERTS_CAN_INLINE 0
@@ -46,8 +74,10 @@
#endif
#if ERTS_CAN_INLINE
+#define ERTS_GLB_FORCE_INLINE static ERTS_FORCE_INLINE
#define ERTS_GLB_INLINE static ERTS_INLINE
#else
+#define ERTS_GLB_FORCE_INLINE
#define ERTS_GLB_INLINE
#endif
@@ -61,22 +91,17 @@
# define NO_FPE_SIGNALS
#endif
-#ifdef DISABLE_CHILD_WAITER_THREAD
-#undef ENABLE_CHILD_WAITER_THREAD
-#endif
+#define ERTS_I64_LITERAL(X) X##LL
-#if defined(ERTS_SMP) && !defined(DISABLE_CHILD_WAITER_THREAD)
-#undef ENABLE_CHILD_WAITER_THREAD
-#define ENABLE_CHILD_WAITER_THREAD 1
-#endif
+#define ErtsInArea(ptr,start,nbytes) \
+ ((UWord)((char*)(ptr) - (char*)(start)) < (nbytes))
-#define ERTS_I64_LITERAL(X) X##LL
+#define ErtsContainerStruct(ptr, type, member) \
+ ((type *)((char *)(1 ? (ptr) : &((type *)0)->member) - offsetof(type, member)))
#if defined (__WIN32__)
# include "erl_win_sys.h"
-#elif defined (__OSE__)
-# include "erl_ose_sys.h"
-#else
+#else
# include "erl_unix_sys.h"
#ifndef UNIX
# define UNIX 1
@@ -111,19 +136,6 @@ typedef int ErtsSysFdType;
typedef ERTS_SYS_FD_TYPE ErtsSysFdType;
#endif
-#if !defined(__GNUC__)
-# define ERTS_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) 0
-#elif !defined(__GNUC_MINOR__)
-# define ERTS_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) \
- ((__GNUC__ << 24) >= (((MAJ) << 24) | ((MIN) << 12) | (PL)))
-#elif !defined(__GNUC_PATCHLEVEL__)
-# define ERTS_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) \
- (((__GNUC__ << 24) | (__GNUC_MINOR__ << 12)) >= (((MAJ) << 24) | ((MIN) << 12) | (PL)))
-#else
-# define ERTS_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) \
- (((__GNUC__ << 24) | (__GNUC_MINOR__ << 12) | __GNUC_PATCHLEVEL__) >= (((MAJ) << 24) | ((MIN) << 12) | (PL)))
-#endif
-
#if ERTS_AT_LEAST_GCC_VSN__(2, 96, 0)
# define ERTS_LIKELY(BOOL) __builtin_expect((BOOL), !0)
# define ERTS_UNLIKELY(BOOL) __builtin_expect((BOOL), 0)
@@ -131,8 +143,20 @@ typedef ERTS_SYS_FD_TYPE ErtsSysFdType;
# define ERTS_LIKELY(BOOL) (BOOL)
# define ERTS_UNLIKELY(BOOL) (BOOL)
#endif
+
+#if ERTS_AT_LEAST_GCC_VSN__(2, 96, 0)
+#if (defined(__APPLE__) && defined(__MACH__)) || defined(__DARWIN__)
+# define ERTS_WRITE_UNLIKELY(X) X __attribute__ ((section ("__DATA,ERTS_LOW_WRITE") ))
+#else
+# define ERTS_WRITE_UNLIKELY(X) X __attribute__ ((section ("ERTS_LOW_WRITE") ))
+#endif
+#else
+# define ERTS_WRITE_UNLIKELY(X) X
+#endif
+
+/* clang may have too low __GNUC__ versions but can handle it */
#ifdef __GNUC__
-# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5)
+# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5) || defined(__clang__)
# define ERTS_DECLARE_DUMMY(X) X __attribute__ ((unused))
# else
# define ERTS_DECLARE_DUMMY(X) X
@@ -191,6 +215,12 @@ __decl_noreturn void __noreturn erl_assert_error(const char* expr, const char *f
# define ASSERT(e) ((void) 1)
#endif
+#ifdef ERTS_SMP
+# define ERTS_SMP_ASSERT(e) ASSERT(e)
+#else
+# define ERTS_SMP_ASSERT(e) ((void)1)
+#endif
+
/* ERTS_UNDEF can be used to silence false warnings about
* "variable may be used uninitialized" while keeping the variable
* marked as undefined by valgrind.
@@ -285,62 +315,11 @@ __decl_noreturn void __noreturn erl_assert_error(const char* expr, const char *f
#else
#error Neither 32 nor 64 bit architecture
#endif
-#if defined(ARCH_64) && defined(HALFWORD_HEAP_EMULATOR)
-# define HALFWORD_HEAP 1
-# define HALFWORD_ASSERT 0
-# define ASSERT_HALFWORD(COND) ASSERT(COND)
-# undef ERTS_SIZEOF_TERM
-# define ERTS_SIZEOF_TERM 4
-#else
-# define HALFWORD_HEAP 0
-# define HALFWORD_ASSERT 0
-# define ASSERT_HALFWORD(COND)
-#endif
#if SIZEOF_VOID_P != SIZEOF_SIZE_T
#error sizeof(void*) != sizeof(size_t)
#endif
-#if HALFWORD_HEAP
-
-#if SIZEOF_INT == 4
-typedef unsigned int Eterm;
-typedef unsigned int Uint;
-typedef int Sint;
-#define ERTS_UINT_MAX UINT_MAX
-#define ERTS_SIZEOF_ETERM SIZEOF_INT
-#define ErtsStrToSint strtol
-#else
-#error Found no appropriate type to use for 'Eterm', 'Uint' and 'Sint'
-#endif
-
-#if SIZEOF_VOID_P == SIZEOF_LONG
-typedef unsigned long UWord;
-typedef long SWord;
-#define SWORD_CONSTANT(Const) Const##L
-#define UWORD_CONSTANT(Const) Const##UL
-#define ERTS_UWORD_MAX ULONG_MAX
-#define ERTS_SWORD_MAX LONG_MAX
-#elif SIZEOF_VOID_P == SIZEOF_INT
-typedef unsigned int UWord;
-typedef int SWord;
-#define SWORD_CONSTANT(Const) Const
-#define UWORD_CONSTANT(Const) Const##U
-#define ERTS_UWORD_MAX UINT_MAX
-#define ERTS_SWORD_MAX INT_MAX
-#elif SIZEOF_VOID_P == SIZEOF_LONG_LONG
-typedef unsigned long long UWord;
-typedef long long SWord;
-#define SWORD_CONSTANT(Const) Const##LL
-#define UWORD_CONSTANT(Const) Const##ULL
-#define ERTS_UWORD_MAX ULLONG_MAX
-#define ERTS_SWORD_MAX LLONG_MAX
-#else
-#error Found no appropriate type to use for 'Eterm', 'Uint' and 'Sint'
-#endif
-
-#else /* !HALFWORD_HEAP */
-
#if SIZEOF_VOID_P == SIZEOF_LONG
typedef unsigned long Eterm;
typedef unsigned long Uint;
@@ -383,8 +362,6 @@ typedef Uint UWord;
typedef Sint SWord;
#define ERTS_UINT_MAX ERTS_UWORD_MAX
-#endif /* HALFWORD_HEAP */
-
typedef UWord BeamInstr;
#ifndef HAVE_INT64
@@ -401,6 +378,7 @@ typedef long Sint64;
# ifdef LONG_MIN
# define ERTS_SINT64_MIN LONG_MIN
# endif
+# define ErtsStrToSint64 strtol
# elif SIZEOF_LONG_LONG == 8
# define HAVE_INT64 1
typedef unsigned long long Uint64;
@@ -414,6 +392,7 @@ typedef long long Sint64;
# ifdef LLONG_MIN
# define ERTS_SINT64_MIN LLONG_MIN
# endif
+# define ErtsStrToSint64 strtoll
# else
# error "No 64-bit integer type found"
# endif
@@ -502,6 +481,12 @@ extern volatile int erts_break_requested;
void erts_do_break_handling(void);
#endif
+#if !defined(ERTS_SMP) && !defined(__WIN32__)
+extern volatile Uint erts_signal_sigterm;
+#define ERTS_SIGNAL_SIGTERM erts_signal_sigterm
+void erts_handle_signal_sigterm(void);
+#endif
+
#ifdef ERTS_WANT_GOT_SIGUSR1
# ifndef UNIX
# define ERTS_GOT_SIGUSR1 0
@@ -633,22 +618,21 @@ Uint erts_sys_misc_mem_sz(void);
#include "erl_printf.h"
/* Io constants to erts_print and erts_putc */
-#define ERTS_PRINT_STDERR (2)
-#define ERTS_PRINT_STDOUT (1)
-#define ERTS_PRINT_FILE (-1)
-#define ERTS_PRINT_SBUF (-2)
-#define ERTS_PRINT_SNBUF (-3)
-#define ERTS_PRINT_DSBUF (-4)
-
-#define ERTS_PRINT_MIN ERTS_PRINT_DSBUF
+#define ERTS_PRINT_STDERR ((fmtfn_t)0)
+#define ERTS_PRINT_STDOUT ((fmtfn_t)1)
+#define ERTS_PRINT_FILE ((fmtfn_t)2)
+#define ERTS_PRINT_SBUF ((fmtfn_t)3)
+#define ERTS_PRINT_SNBUF ((fmtfn_t)4)
+#define ERTS_PRINT_DSBUF ((fmtfn_t)5)
+#define ERTS_PRINT_FD ((fmtfn_t)6)
typedef struct {
char *buf;
size_t size;
} erts_print_sn_buf;
-int erts_print(int to, void *arg, char *format, ...); /* in utils.c */
-int erts_putc(int to, void *arg, char); /* in utils.c */
+int erts_print(fmtfn_t to, void *arg, char *format, ...); /* in utils.c */
+int erts_putc(fmtfn_t to, void *arg, char); /* in utils.c */
/* logger stuff is declared here instead of in global.h, so sys files
won't have to include global.h */
@@ -675,6 +659,15 @@ typedef struct preload {
unsigned char* code; /* Code pointer */
} Preload;
+/*
+ * ErtsTracer is either NIL, 'true' or [Mod | State]
+ *
+ * If set to NIL, it means no tracer.
+ * If set to 'true' it means the current process' tracer.
+ * If set to [Mod | State], there is a tracer.
+ * See erts_tracer_update for more details
+ */
+typedef Eterm ErtsTracer;
/*
* This structure contains options to all built in drivers.
@@ -786,6 +779,7 @@ void erts_sys_main_thread(void);
extern int erts_sys_prepare_crash_dump(int secs);
extern void erts_sys_pre_init(void);
extern void erl_sys_init(void);
+extern void erl_sys_late_init(void);
extern void erl_sys_args(int *argc, char **argv);
extern void erl_sys_schedule(int);
void sys_tty_reset(int);
@@ -871,9 +865,12 @@ int erts_sys_unsetenv(char *key);
char *erts_read_env(char *key);
void erts_free_read_env(void *value);
+#if defined(ERTS_SMP)
#if defined(ERTS_THR_HAVE_SIG_FUNCS) && !defined(ETHR_UNUSABLE_SIGUSRX)
extern void sys_thr_resume(erts_tid_t tid);
extern void sys_thr_suspend(erts_tid_t tid);
+#define ERTS_SYS_SUSPEND_SIGNAL SIGUSR2
+#endif
#endif
/* utils.c */
@@ -906,7 +903,7 @@ void sys_alloc_stat(SysAllocStat *);
#define ERTS_REFC_DEBUG
#endif
-typedef erts_smp_atomic_t erts_refc_t;
+typedef erts_atomic_t erts_refc_t;
ERTS_GLB_INLINE void erts_refc_init(erts_refc_t *refcp, erts_aint_t val);
ERTS_GLB_INLINE void erts_refc_inc(erts_refc_t *refcp, erts_aint_t min_val);
@@ -925,27 +922,27 @@ ERTS_GLB_INLINE erts_aint_t erts_refc_read(erts_refc_t *refcp,
ERTS_GLB_INLINE void
erts_refc_init(erts_refc_t *refcp, erts_aint_t val)
{
- erts_smp_atomic_init_nob((erts_smp_atomic_t *) refcp, val);
+ erts_atomic_init_nob((erts_atomic_t *) refcp, val);
}
ERTS_GLB_INLINE void
erts_refc_inc(erts_refc_t *refcp, erts_aint_t min_val)
{
#ifdef ERTS_REFC_DEBUG
- erts_aint_t val = erts_smp_atomic_inc_read_nob((erts_smp_atomic_t *) refcp);
+ erts_aint_t val = erts_atomic_inc_read_nob((erts_atomic_t *) refcp);
if (val < min_val)
erts_exit(ERTS_ABORT_EXIT,
"erts_refc_inc(): Bad refc found (refc=%ld < %ld)!\n",
val, min_val);
#else
- erts_smp_atomic_inc_nob((erts_smp_atomic_t *) refcp);
+ erts_atomic_inc_nob((erts_atomic_t *) refcp);
#endif
}
ERTS_GLB_INLINE erts_aint_t
erts_refc_inctest(erts_refc_t *refcp, erts_aint_t min_val)
{
- erts_aint_t val = erts_smp_atomic_inc_read_nob((erts_smp_atomic_t *) refcp);
+ erts_aint_t val = erts_atomic_inc_read_nob((erts_atomic_t *) refcp);
#ifdef ERTS_REFC_DEBUG
if (val < min_val)
erts_exit(ERTS_ABORT_EXIT,
@@ -959,20 +956,20 @@ ERTS_GLB_INLINE void
erts_refc_dec(erts_refc_t *refcp, erts_aint_t min_val)
{
#ifdef ERTS_REFC_DEBUG
- erts_aint_t val = erts_smp_atomic_dec_read_nob((erts_smp_atomic_t *) refcp);
+ erts_aint_t val = erts_atomic_dec_read_nob((erts_atomic_t *) refcp);
if (val < min_val)
erts_exit(ERTS_ABORT_EXIT,
"erts_refc_dec(): Bad refc found (refc=%ld < %ld)!\n",
val, min_val);
#else
- erts_smp_atomic_dec_nob((erts_smp_atomic_t *) refcp);
+ erts_atomic_dec_nob((erts_atomic_t *) refcp);
#endif
}
ERTS_GLB_INLINE erts_aint_t
erts_refc_dectest(erts_refc_t *refcp, erts_aint_t min_val)
{
- erts_aint_t val = erts_smp_atomic_dec_read_nob((erts_smp_atomic_t *) refcp);
+ erts_aint_t val = erts_atomic_dec_read_nob((erts_atomic_t *) refcp);
#ifdef ERTS_REFC_DEBUG
if (val < min_val)
erts_exit(ERTS_ABORT_EXIT,
@@ -986,20 +983,20 @@ ERTS_GLB_INLINE void
erts_refc_add(erts_refc_t *refcp, erts_aint_t diff, erts_aint_t min_val)
{
#ifdef ERTS_REFC_DEBUG
- erts_aint_t val = erts_smp_atomic_add_read_nob((erts_smp_atomic_t *) refcp, diff);
+ erts_aint_t val = erts_atomic_add_read_nob((erts_atomic_t *) refcp, diff);
if (val < min_val)
erts_exit(ERTS_ABORT_EXIT,
"erts_refc_add(%ld): Bad refc found (refc=%ld < %ld)!\n",
diff, val, min_val);
#else
- erts_smp_atomic_add_nob((erts_smp_atomic_t *) refcp, diff);
+ erts_atomic_add_nob((erts_atomic_t *) refcp, diff);
#endif
}
ERTS_GLB_INLINE erts_aint_t
erts_refc_read(erts_refc_t *refcp, erts_aint_t min_val)
{
- erts_aint_t val = erts_smp_atomic_read_nob((erts_smp_atomic_t *) refcp);
+ erts_aint_t val = erts_atomic_read_nob((erts_atomic_t *) refcp);
#ifdef ERTS_REFC_DEBUG
if (val < min_val)
erts_exit(ERTS_ABORT_EXIT,
@@ -1011,6 +1008,112 @@ erts_refc_read(erts_refc_t *refcp, erts_aint_t min_val)
#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
+typedef erts_smp_atomic_t erts_smp_refc_t;
+
+ERTS_GLB_INLINE void erts_smp_refc_init(erts_smp_refc_t *refcp, erts_aint_t val);
+ERTS_GLB_INLINE void erts_smp_refc_inc(erts_smp_refc_t *refcp, erts_aint_t min_val);
+ERTS_GLB_INLINE erts_aint_t erts_smp_refc_inctest(erts_smp_refc_t *refcp,
+ erts_aint_t min_val);
+ERTS_GLB_INLINE void erts_smp_refc_dec(erts_smp_refc_t *refcp, erts_aint_t min_val);
+ERTS_GLB_INLINE erts_aint_t erts_smp_refc_dectest(erts_smp_refc_t *refcp,
+ erts_aint_t min_val);
+ERTS_GLB_INLINE void erts_smp_refc_add(erts_smp_refc_t *refcp, erts_aint_t diff,
+ erts_aint_t min_val);
+ERTS_GLB_INLINE erts_aint_t erts_smp_refc_read(erts_smp_refc_t *refcp,
+ erts_aint_t min_val);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE void
+erts_smp_refc_init(erts_smp_refc_t *refcp, erts_aint_t val)
+{
+ erts_smp_atomic_init_nob((erts_smp_atomic_t *) refcp, val);
+}
+
+ERTS_GLB_INLINE void
+erts_smp_refc_inc(erts_smp_refc_t *refcp, erts_aint_t min_val)
+{
+#ifdef ERTS_REFC_DEBUG
+ erts_aint_t val = erts_smp_atomic_inc_read_nob((erts_smp_atomic_t *) refcp);
+ if (val < min_val)
+ erts_exit(ERTS_ABORT_EXIT,
+ "erts_smp_refc_inc(): Bad refc found (refc=%ld < %ld)!\n",
+ val, min_val);
+#else
+ erts_smp_atomic_inc_nob((erts_smp_atomic_t *) refcp);
+#endif
+}
+
+ERTS_GLB_INLINE erts_aint_t
+erts_smp_refc_inctest(erts_smp_refc_t *refcp, erts_aint_t min_val)
+{
+ erts_aint_t val = erts_smp_atomic_inc_read_nob((erts_smp_atomic_t *) refcp);
+#ifdef ERTS_REFC_DEBUG
+ if (val < min_val)
+ erts_exit(ERTS_ABORT_EXIT,
+ "erts_smp_refc_inctest(): Bad refc found (refc=%ld < %ld)!\n",
+ val, min_val);
+#endif
+ return val;
+}
+
+ERTS_GLB_INLINE void
+erts_smp_refc_dec(erts_smp_refc_t *refcp, erts_aint_t min_val)
+{
+#ifdef ERTS_REFC_DEBUG
+ erts_aint_t val = erts_smp_atomic_dec_read_nob((erts_smp_atomic_t *) refcp);
+ if (val < min_val)
+ erts_exit(ERTS_ABORT_EXIT,
+ "erts_smp_refc_dec(): Bad refc found (refc=%ld < %ld)!\n",
+ val, min_val);
+#else
+ erts_smp_atomic_dec_nob((erts_smp_atomic_t *) refcp);
+#endif
+}
+
+ERTS_GLB_INLINE erts_aint_t
+erts_smp_refc_dectest(erts_smp_refc_t *refcp, erts_aint_t min_val)
+{
+ erts_aint_t val = erts_smp_atomic_dec_read_nob((erts_smp_atomic_t *) refcp);
+#ifdef ERTS_REFC_DEBUG
+ if (val < min_val)
+ erts_exit(ERTS_ABORT_EXIT,
+ "erts_smp_refc_dectest(): Bad refc found (refc=%ld < %ld)!\n",
+ val, min_val);
+#endif
+ return val;
+}
+
+ERTS_GLB_INLINE void
+erts_smp_refc_add(erts_smp_refc_t *refcp, erts_aint_t diff, erts_aint_t min_val)
+{
+#ifdef ERTS_REFC_DEBUG
+ erts_aint_t val = erts_smp_atomic_add_read_nob((erts_smp_atomic_t *) refcp, diff);
+ if (val < min_val)
+ erts_exit(ERTS_ABORT_EXIT,
+ "erts_smp_refc_add(%ld): Bad refc found (refc=%ld < %ld)!\n",
+ diff, val, min_val);
+#else
+ erts_smp_atomic_add_nob((erts_smp_atomic_t *) refcp, diff);
+#endif
+}
+
+ERTS_GLB_INLINE erts_aint_t
+erts_smp_refc_read(erts_smp_refc_t *refcp, erts_aint_t min_val)
+{
+ erts_aint_t val = erts_smp_atomic_read_nob((erts_smp_atomic_t *) refcp);
+#ifdef ERTS_REFC_DEBUG
+ if (val < min_val)
+ erts_exit(ERTS_ABORT_EXIT,
+ "erts_smp_refc_read(): Bad refc found (refc=%ld < %ld)!\n",
+ val, min_val);
+#endif
+ return val;
+}
+
+#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
+
#ifdef ERTS_ENABLE_KERNEL_POLL
extern int erts_use_kernel_poll;
#endif
@@ -1103,7 +1206,6 @@ extern int erts_use_kernel_poll;
#define put_int8(i, s) do {((unsigned char*)(s))[0] = (i) & 0xff;} while (0)
-
/*
* Use DEBUGF as you would use printf, but use double parentheses:
*
diff --git a/erts/emulator/beam/time.c b/erts/emulator/beam/time.c
index 988338ed81..6f15082130 100644
--- a/erts/emulator/beam/time.c
+++ b/erts/emulator/beam/time.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -278,9 +278,14 @@ ErtsMonotonicTime
erts_check_next_timeout_time(ErtsSchedulerData *esdp)
{
ErtsTimerWheel *tiw = esdp->timer_wheel;
+ ErtsMonotonicTime time;
+ ERTS_MSACC_DECLARE_CACHE_X();
if (tiw->true_next_timeout_time)
return tiw->next_timeout_time;
- return find_next_timeout(esdp, tiw, 1, 0, 0);
+ ERTS_MSACC_PUSH_AND_SET_STATE_CACHED_X(ERTS_MSACC_STATE_TIMERS);
+ time = find_next_timeout(esdp, tiw, 1, 0, 0);
+ ERTS_MSACC_POP_STATE_M_X();
+ return time;
}
#ifndef ERTS_TW_DEBUG
@@ -336,6 +341,7 @@ erts_bump_timers(ErtsTimerWheel *tiw, ErtsMonotonicTime curr_time)
{
int tiw_pos_ix, slots, yielded_slot_restarted, yield_count;
ErtsMonotonicTime bump_to, tmp_slots, old_pos;
+ ERTS_MSACC_PUSH_AND_SET_STATE_M_X(ERTS_MSACC_STATE_TIMERS);
yield_count = ERTS_TWHEEL_BUMP_YIELD_LIMIT;
@@ -371,6 +377,7 @@ erts_bump_timers(ErtsTimerWheel *tiw, ErtsMonotonicTime curr_time)
tiw->next_timeout_time = curr_time + ERTS_MONOTONIC_DAY;
tiw->pos = bump_to;
tiw->yield_slot = ERTS_TWHEEL_SLOT_INACTIVE;
+ ERTS_MSACC_POP_STATE_M_X();
return;
}
@@ -382,6 +389,7 @@ erts_bump_timers(ErtsTimerWheel *tiw, ErtsMonotonicTime curr_time)
tiw->yield_slot = ERTS_TWHEEL_SLOT_AT_ONCE;
tiw->true_next_timeout_time = 1;
tiw->next_timeout_time = ERTS_CLKTCKS_TO_MONOTONIC(old_pos);
+ ERTS_MSACC_POP_STATE_M_X();
return;
}
@@ -400,8 +408,10 @@ erts_bump_timers(ErtsTimerWheel *tiw, ErtsMonotonicTime curr_time)
p = tiw->at_once.head;
}
- if (tiw->pos >= bump_to)
+ if (tiw->pos >= bump_to) {
+ ERTS_MSACC_POP_STATE_M_X();
break;
+ }
if (tiw->nto == 0)
goto empty_wheel;
@@ -478,6 +488,7 @@ erts_bump_timers(ErtsTimerWheel *tiw, ErtsMonotonicTime curr_time)
tiw->yield_slot = tiw_pos_ix;
tiw->yield_slots_left = slots;
tiw->yield_start_pos = old_pos;
+ ERTS_MSACC_POP_STATE_M_X();
return; /* Yield! */
}
@@ -500,6 +511,7 @@ erts_bump_timers(ErtsTimerWheel *tiw, ErtsMonotonicTime curr_time)
/* Search at most two seconds ahead... */
(void) find_next_timeout(NULL, tiw, 0, curr_time, ERTS_SEC_TO_MONOTONIC(2));
+ ERTS_MSACC_POP_STATE_M_X();
}
Uint
@@ -569,6 +581,7 @@ erts_twheel_set_timer(ErtsTimerWheel *tiw,
ErtsMonotonicTime timeout_pos)
{
ErtsMonotonicTime timeout_time;
+ ERTS_MSACC_PUSH_AND_SET_STATE_M_X(ERTS_MSACC_STATE_TIMERS);
p->u.func.timeout = timeout;
p->u.func.cancel = cancel;
@@ -612,6 +625,7 @@ erts_twheel_set_timer(ErtsTimerWheel *tiw,
tiw->true_next_timeout_time = 1;
tiw->next_timeout_time = timeout_time;
}
+ ERTS_MSACC_POP_STATE_M_X();
}
void
@@ -620,11 +634,13 @@ erts_twheel_cancel_timer(ErtsTimerWheel *tiw, ErtsTWheelTimer *p)
if (p->slot != ERTS_TWHEEL_SLOT_INACTIVE) {
ErlCancelProc cancel;
void *arg;
+ ERTS_MSACC_PUSH_AND_SET_STATE_M_X(ERTS_MSACC_STATE_TIMERS);
remove_timer(tiw, p);
cancel = p->u.func.cancel;
arg = p->u.func.arg;
if (cancel)
(*cancel)(arg);
+ ERTS_MSACC_POP_STATE_M_X();
}
}
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index b9ce70e364..d90c282c7e 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2014. 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.
@@ -71,46 +71,10 @@
#define HAVE_MALLOPT 0
#endif
-/* profile_scheduler mini message queue */
-
-typedef struct {
- Uint scheduler_id;
- Uint no_schedulers;
- Uint Ms;
- Uint s;
- Uint us;
- Eterm state;
-} profile_sched_msg;
-
-typedef struct {
- profile_sched_msg msg[2];
- Uint n;
-} profile_sched_msg_q;
-
-#ifdef ERTS_SMP
-
-#if 0 /* Unused */
-static void
-dispatch_profile_msg_q(profile_sched_msg_q *psmq)
-{
- int i = 0;
- profile_sched_msg *msg = NULL;
- ASSERT(psmq != NULL);
- for (i = 0; i < psmq->n; i++) {
- msg = &(psmq->msg[i]);
- profile_scheduler_q(make_small(msg->scheduler_id), msg->state, am_undefined, msg->Ms, msg->s, msg->us);
- }
-}
-#endif
-
-#endif
-
-
Eterm*
erts_heap_alloc(Process* p, Uint need, Uint xtra)
{
ErlHeapFragment* bp;
- Eterm* htop;
Uint n;
#if defined(DEBUG) || defined(CHECK_FOR_HOLES)
Uint i;
@@ -121,7 +85,7 @@ erts_heap_alloc(Process* p, Uint need, Uint xtra)
&& HEAP_TOP(p) >= p->space_verified_from
&& HEAP_TOP(p) + need <= p->space_verified_from + p->space_verified
&& HEAP_LIMIT(p) - HEAP_TOP(p) >= need) {
-
+
Uint consumed = need + (HEAP_TOP(p) - p->space_verified_from);
ASSERT(consumed <= p->space_verified);
p->space_verified -= consumed;
@@ -138,6 +102,7 @@ erts_heap_alloc(Process* p, Uint need, Uint xtra)
if (bp != NULL && need <= (bp->alloc_size - bp->used_size)) {
Eterm* ret = bp->mem + bp->used_size;
bp->used_size += need;
+ p->mbuf_sz += need;
return ret;
}
#ifdef DEBUG
@@ -156,21 +121,11 @@ erts_heap_alloc(Process* p, Uint need, Uint xtra)
n--;
#endif
- /*
- * When we have created a heap fragment, we are no longer allowed
- * to store anything more on the heap.
- */
- htop = HEAP_TOP(p);
- if (htop < HEAP_LIMIT(p)) {
- *htop = make_pos_bignum_header(HEAP_LIMIT(p)-htop-1);
- HEAP_TOP(p) = HEAP_LIMIT(p);
- }
-
bp->next = MBUF(p);
MBUF(p) = bp;
bp->alloc_size = n;
bp->used_size = need;
- MBUF_SIZE(p) += n;
+ MBUF_SIZE(p) += need;
bp->off_heap.first = NULL;
bp->off_heap.overhead = 0;
return bp->mem;
@@ -269,6 +224,31 @@ erl_grow_pstack(ErtsPStack* s, void* default_pstack, unsigned need_bytes)
s->psp = s->pstart + sp_offs;
}
+/*
+ * Helper function for the EQUEUE macros defined in global.h.
+ */
+
+void
+erl_grow_equeue(ErtsEQueue* q, Eterm* default_equeue)
+{
+ Uint old_size = (q->end - q->start);
+ Uint new_size = old_size * 2;
+ Uint first_part = (q->end - q->front);
+ Uint second_part = (q->back - q->start);
+ Eterm* new_ptr = erts_alloc(q->alloc_type, new_size*sizeof(Eterm));
+ ASSERT(q->back == q->front); // of course the queue is full now!
+ if (first_part > 0)
+ sys_memcpy(new_ptr, q->front, first_part*sizeof(Eterm));
+ if (second_part > 0)
+ sys_memcpy(new_ptr+first_part, q->start, second_part*sizeof(Eterm));
+ if (q->start != default_equeue)
+ erts_free(q->alloc_type, q->start);
+ q->start = new_ptr;
+ q->end = q->start + new_size;
+ q->front = q->start;
+ q->back = q->start + old_size;
+}
+
/* CTYPE macros */
#define LATIN1
@@ -303,10 +283,10 @@ erl_grow_pstack(ErtsPStack* s, void* default_pstack, unsigned need_bytes)
* Calculate length of a list.
* Returns -1 if not a proper list (i.e. not terminated with NIL)
*/
-int
+Sint
erts_list_length(Eterm list)
{
- int i = 0;
+ Sint i = 0;
while(is_list(list)) {
i++;
@@ -368,40 +348,41 @@ int erts_fit_in_bits_uint(Uint value)
}
int
-erts_print(int to, void *arg, char *format, ...)
+erts_print(fmtfn_t to, void *arg, char *format, ...)
{
int res;
va_list arg_list;
va_start(arg_list, format);
- if (to < ERTS_PRINT_MIN)
- res = -EINVAL;
- else {
- switch (to) {
- case ERTS_PRINT_STDOUT:
+ {
+ switch ((UWord)to) {
+ case (UWord)ERTS_PRINT_STDOUT:
res = erts_vprintf(format, arg_list);
break;
- case ERTS_PRINT_STDERR:
+ case (UWord)ERTS_PRINT_STDERR:
res = erts_vfprintf(stderr, format, arg_list);
break;
- case ERTS_PRINT_FILE:
+ case (UWord)ERTS_PRINT_FILE:
res = erts_vfprintf((FILE *) arg, format, arg_list);
break;
- case ERTS_PRINT_SBUF:
+ case (UWord)ERTS_PRINT_SBUF:
res = erts_vsprintf((char *) arg, format, arg_list);
break;
- case ERTS_PRINT_SNBUF:
+ case (UWord)ERTS_PRINT_SNBUF:
res = erts_vsnprintf(((erts_print_sn_buf *) arg)->buf,
((erts_print_sn_buf *) arg)->size,
format,
arg_list);
break;
- case ERTS_PRINT_DSBUF:
+ case (UWord)ERTS_PRINT_DSBUF:
res = erts_vdsprintf((erts_dsprintf_buf_t *) arg, format, arg_list);
break;
- default:
- res = erts_vfdprintf((int) to, format, arg_list);
+ case (UWord)ERTS_PRINT_FD:
+ res = erts_vfdprintf((int)(SWord) arg, format, arg_list);
break;
+ default:
+ res = erts_vcbprintf(to, arg, format, arg_list);
+ break;
}
}
@@ -410,7 +391,7 @@ erts_print(int to, void *arg, char *format, ...)
}
int
-erts_putc(int to, void *arg, char c)
+erts_putc(fmtfn_t to, void *arg, char c)
{
return erts_print(to, arg, "%c", c);
}
@@ -659,7 +640,7 @@ erts_bld_atom_uword_2tup_list(Uint **hpp, Uint *szp,
ui = uint_to_big(uints[i], *hpp);
*hpp += BIG_UINT_HEAP_SIZE;
}
-
+
res = CONS(*hpp+3, TUPLE2(*hpp, atoms[i], ui), res);
*hpp += 5;
}
@@ -697,14 +678,14 @@ erts_bld_atom_2uint_3tup_list(Uint **hpp, Uint *szp, Sint length,
ui1 = uint_to_big(uints1[i], *hpp);
*hpp += BIG_UINT_HEAP_SIZE;
}
-
+
if (IS_USMALL(0, uints2[i]))
ui2 = make_small(uints2[i]);
else {
ui2 = uint_to_big(uints2[i], *hpp);
*hpp += BIG_UINT_HEAP_SIZE;
}
-
+
res = CONS(*hpp+4, TUPLE3(*hpp, atoms[i], ui1, ui2), res);
*hpp += 6;
}
@@ -815,7 +796,7 @@ hash_binary_bytes(Eterm bin, Uint sz, Uint32 hash)
Uint b;
Uint lshift = bitoffs;
Uint rshift = 8 - lshift;
-
+
while (sz--) {
b = (previous << lshift) & 0xFF;
previous = *ptr++;
@@ -826,7 +807,7 @@ hash_binary_bytes(Eterm bin, Uint sz, Uint32 hash)
b = (previous << lshift) & 0xFF;
previous = *ptr++;
b |= previous >> rshift;
-
+
b >>= 8 - bitsize;
hash = (hash*FUNNY_NUMBER1 + b) * FUNNY_NUMBER12 + bitsize;
}
@@ -856,21 +837,21 @@ Uint32 make_hash(Eterm term_arg)
do { \
Uint32 x = (Uint32) (Expr); \
hash = \
- (((((hash)*(Prime1) + (x & 0xFF)) * (Prime1) + \
- ((x >> 8) & 0xFF)) * (Prime1) + \
- ((x >> 16) & 0xFF)) * (Prime1) + \
+ (((((hash)*(Prime1) + (x & 0xFF)) * (Prime1) + \
+ ((x >> 8) & 0xFF)) * (Prime1) + \
+ ((x >> 16) & 0xFF)) * (Prime1) + \
(x >> 24)); \
} while(0)
-#define UINT32_HASH_RET(Expr, Prime1, Prime2) \
+#define UINT32_HASH_RET(Expr, Prime1, Prime2) \
UINT32_HASH_STEP(Expr, Prime1); \
hash = hash * (Prime2); \
- break
-
-
+ break
+
+
/*
* Significant additions needed for real 64 bit port with larger fixnums.
- */
+ */
/*
* Note, for the simple 64bit port, not utilizing the
@@ -885,7 +866,7 @@ tail_recur:
hash = hash*FUNNY_NUMBER3 + 1;
break;
case ATOM_DEF:
- hash = hash*FUNNY_NUMBER1 +
+ hash = hash*FUNNY_NUMBER1 +
(atom_tab(atom_val(term))->slot.bucket.hvalue);
break;
case SMALL_DEF:
@@ -894,7 +875,7 @@ tail_recur:
Uint y2 = y1 < 0 ? -(Uint)y1 : y1;
UINT32_HASH_STEP(y2, FUNNY_NUMBER2);
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
if (y2 >> 32)
UINT32_HASH_STEP(y2 >> 32, FUNNY_NUMBER2);
#endif
@@ -914,9 +895,9 @@ tail_recur:
Export* ep = *((Export **) (export_val(term) + 1));
hash = hash * FUNNY_NUMBER11 + ep->code[2];
- hash = hash*FUNNY_NUMBER1 +
+ hash = hash*FUNNY_NUMBER1 +
(atom_tab(atom_val(ep->code[0]))->slot.bucket.hvalue);
- hash = hash*FUNNY_NUMBER1 +
+ hash = hash*FUNNY_NUMBER1 +
(atom_tab(atom_val(ep->code[1]))->slot.bucket.hvalue);
break;
}
@@ -927,7 +908,7 @@ tail_recur:
Uint num_free = funp->num_free;
hash = hash * FUNNY_NUMBER10 + num_free;
- hash = hash*FUNNY_NUMBER1 +
+ hash = hash*FUNNY_NUMBER1 +
(atom_tab(atom_val(funp->fe->module))->slot.bucket.hvalue);
hash = hash*FUNNY_NUMBER2 + funp->fe->old_index;
hash = hash*FUNNY_NUMBER2 + funp->fe->old_uniq;
@@ -952,7 +933,7 @@ tail_recur:
UINT32_HASH_RET(internal_ref_numbers(term)[0],FUNNY_NUMBER9,FUNNY_NUMBER10);
case EXTERNAL_REF_DEF:
UINT32_HASH_RET(external_ref_numbers(term)[0],FUNNY_NUMBER9,FUNNY_NUMBER10);
- case FLOAT_DEF:
+ case FLOAT_DEF:
{
FloatDef ff;
GET_DOUBLE(term, ff);
@@ -979,12 +960,12 @@ tail_recur:
** as multiplications on a Sparc is so slow.
*/
hash = hash*FUNNY_NUMBER2 + unsigned_val(*list);
-
+
if (is_not_list(CDR(list))) {
WSTACK_PUSH(stack, MAKE_HASH_CDR_POST_OP);
term = CDR(list);
goto tail_recur;
- }
+ }
list = list_val(CDR(list));
}
WSTACK_PUSH2(stack, CDR(list), MAKE_HASH_CDR_PRE_OP);
@@ -1015,7 +996,7 @@ tail_recur:
}
d = BIG_DIGIT(ptr, k);
k = sizeof(ErtsDigit);
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
if (!(d >> 32))
k /= 2;
#endif
@@ -1025,17 +1006,17 @@ tail_recur:
}
hash *= is_neg ? FUNNY_NUMBER4 : FUNNY_NUMBER3;
break;
- }
+ }
case MAP_DEF:
hash = hash*FUNNY_NUMBER13 + FUNNY_NUMBER14 + make_hash2(term);
break;
- case TUPLE_DEF:
+ case TUPLE_DEF:
{
Eterm* ptr = tuple_val(term);
Uint arity = arityval(*ptr);
WSTACK_PUSH3(stack, (UWord) arity, (UWord)(ptr+1), (UWord) arity);
- op = MAKE_HASH_TUPLE_OP;
+ op = MAKE_HASH_TUPLE_OP;
}/*fall through*/
case MAKE_HASH_TUPLE_OP:
case MAKE_HASH_TERM_ARRAY_OP:
@@ -1052,8 +1033,8 @@ tail_recur:
hash = hash*FUNNY_NUMBER9 + arity;
}
break;
- }
-
+ }
+
default:
erts_exit(ERTS_ERROR_EXIT, "Invalid tag in make_hash(0x%X,0x%X)\n", term, op);
return 0;
@@ -1180,8 +1161,8 @@ make_hash2(Eterm term)
if (y < 0) { \
UINT32_HASH(-y, AConst); \
/* Negative numbers are unnecessarily mixed twice. */ \
- } \
- UINT32_HASH(y, AConst); \
+ } \
+ UINT32_HASH(y, AConst); \
} while(0)
#define IS_SSMALL28(x) (((Uint) (((x) >> (28-1)) + 1)) < 2)
@@ -1263,7 +1244,7 @@ make_hash2(Eterm term)
int arity = header_arity(hdr);
Eterm* elem = tuple_val(term);
UINT32_HASH(arity, HCONST_9);
- if (arity == 0) /* Empty tuple */
+ if (arity == 0) /* Empty tuple */
goto hash2_common;
for (i = arity; ; i--) {
term = elem[i];
@@ -1350,7 +1331,7 @@ make_hash2(Eterm term)
{
Export* ep = *((Export **) (export_val(term) + 1));
UINT32_HASH_2
- (ep->code[2],
+ (ep->code[2],
atom_tab(atom_val(ep->code[0]))->slot.bucket.hvalue,
HCONST);
UINT32_HASH
@@ -1364,7 +1345,7 @@ make_hash2(Eterm term)
ErlFunThing* funp = (ErlFunThing *) fun_val(term);
Uint num_free = funp->num_free;
UINT32_HASH_2
- (num_free,
+ (num_free,
atom_tab(atom_val(funp->fe->module))->slot.bucket.hvalue,
HCONST);
UINT32_HASH_2
@@ -1489,7 +1470,7 @@ make_hash2(Eterm term)
goto hash2_common;
}
break;
-
+
default:
erts_exit(ERTS_ERROR_EXIT, "Invalid tag in make_hash2(0x%X)\n", term);
}
@@ -1562,7 +1543,7 @@ make_hash2(Eterm term)
}
case HASH_MAP_PAIR:
hash_xor_pairs ^= hash;
- hash = 0;
+ hash = 0;
goto hash2_common;
default:
break;
@@ -1699,17 +1680,22 @@ make_internal_hash(Eterm term)
* the order in which keys and values are encountered.
* We therefore calculate context independent hashes for all .
* key-value pairs and then xor them together.
+ *
+ * We *do* need to use an initial seed for each pair, i.e. the
+ * hash value, so the hash value is reset for each pair with 'hash'.
+ * If we don't, no additional entropy is given to the system and the
+ * hash collision resolution in maps:from_list/1 would fail.
*/
ESTACK_PUSH(s, hash_xor_pairs);
ESTACK_PUSH(s, hash);
ESTACK_PUSH(s, HASH_MAP_TAIL);
- hash = 0;
- hash_xor_pairs = 0;
for (i = size - 1; i >= 0; i--) {
+ ESTACK_PUSH(s, hash); /* initial seed for all pairs */
ESTACK_PUSH(s, HASH_MAP_PAIR);
ESTACK_PUSH(s, vs[i]);
ESTACK_PUSH(s, ks[i]);
}
+ hash_xor_pairs = 0;
goto pop_next;
}
case HAMT_SUBTAG_HEAD_ARRAY:
@@ -1721,7 +1707,6 @@ make_internal_hash(Eterm term)
ESTACK_PUSH(s, hash_xor_pairs);
ESTACK_PUSH(s, hash);
ESTACK_PUSH(s, HASH_MAP_TAIL);
- hash = 0;
hash_xor_pairs = 0;
}
switch (hdr & _HEADER_MAP_SUBTAG_MASK) {
@@ -1738,6 +1723,7 @@ make_internal_hash(Eterm term)
while (i) {
if (is_list(*ptr)) {
Eterm* cons = list_val(*ptr);
+ ESTACK_PUSH(s, hash); /* initial seed for all pairs */
ESTACK_PUSH(s, HASH_MAP_PAIR);
ESTACK_PUSH(s, CDR(cons));
ESTACK_PUSH(s, CAR(cons));
@@ -1927,6 +1913,7 @@ make_internal_hash(Eterm term)
pop_next:
if (ESTACK_ISEMPTY(s)) {
DESTROY_ESTACK(s);
+
return hash;
}
@@ -1941,7 +1928,7 @@ make_internal_hash(Eterm term)
}
case HASH_MAP_PAIR:
hash_xor_pairs ^= hash;
- hash = 0;
+ hash = (Uint32) ESTACK_POP(s); /* initial seed for all pairs */
goto pop_next;
case HASH_CDR:
@@ -1974,8 +1961,8 @@ Uint32 make_broken_hash(Eterm term)
DECLARE_WSTACK(stack);
unsigned op;
tail_recur:
- op = tag_val_def(term);
- for (;;) {
+ op = tag_val_def(term);
+ for (;;) {
switch (op) {
case NIL_DEF:
hash = hash*FUNNY_NUMBER3 + 1;
@@ -1985,7 +1972,7 @@ tail_recur:
(atom_tab(atom_val(term))->slot.bucket.hvalue);
break;
case SMALL_DEF:
-#if defined(ARCH_64) && !HALFWORD_HEAP
+#if defined(ARCH_64)
{
Sint y1 = signed_val(term);
Uint y2 = y1 < 0 ? -(Uint)y1 : y1;
@@ -1997,8 +1984,7 @@ tail_recur:
{ /* like a bignum */
Uint32 y4 = (Uint32) y2;
hash = hash*FUNNY_NUMBER2 + ((y4 << 16) | (y4 >> 16));
- if (y3)
- {
+ if (y3) {
hash = hash*FUNNY_NUMBER2 + ((y3 << 16) | (y3 >> 16));
arity++;
}
@@ -2041,9 +2027,9 @@ tail_recur:
Export* ep = *((Export **) (export_val(term) + 1));
hash = hash * FUNNY_NUMBER11 + ep->code[2];
- hash = hash*FUNNY_NUMBER1 +
+ hash = hash*FUNNY_NUMBER1 +
(atom_tab(atom_val(ep->code[0]))->slot.bucket.hvalue);
- hash = hash*FUNNY_NUMBER1 +
+ hash = hash*FUNNY_NUMBER1 +
(atom_tab(atom_val(ep->code[1]))->slot.bucket.hvalue);
break;
}
@@ -2054,7 +2040,7 @@ tail_recur:
Uint num_free = funp->num_free;
hash = hash * FUNNY_NUMBER10 + num_free;
- hash = hash*FUNNY_NUMBER1 +
+ hash = hash*FUNNY_NUMBER1 +
(atom_tab(atom_val(funp->fe->module))->slot.bucket.hvalue);
hash = hash*FUNNY_NUMBER2 + funp->fe->old_index;
hash = hash*FUNNY_NUMBER2 + funp->fe->old_uniq;
@@ -2086,7 +2072,7 @@ tail_recur:
case EXTERNAL_REF_DEF:
hash = hash*FUNNY_NUMBER9 + external_ref_numbers(term)[0];
break;
- case FLOAT_DEF:
+ case FLOAT_DEF:
{
FloatDef ff;
GET_DOUBLE(term, ff);
@@ -2170,7 +2156,7 @@ tail_recur:
}
#else
-#error "unsupported D_EXP size"
+#error "unsupported D_EXP size"
#endif
hash = hash * (is_neg ? FUNNY_NUMBER3 : FUNNY_NUMBER2) + arity;
}
@@ -2179,14 +2165,14 @@ tail_recur:
case MAP_DEF:
hash = hash*FUNNY_NUMBER13 + FUNNY_NUMBER14 + make_hash2(term);
break;
- case TUPLE_DEF:
+ case TUPLE_DEF:
{
Eterm* ptr = tuple_val(term);
Uint arity = arityval(*ptr);
WSTACK_PUSH3(stack, (UWord) arity, (UWord) (ptr+1), (UWord) arity);
op = MAKE_HASH_TUPLE_OP;
- }/*fall through*/
+ }/*fall through*/
case MAKE_HASH_TUPLE_OP:
case MAKE_HASH_TERM_ARRAY_OP:
{
@@ -2214,7 +2200,7 @@ tail_recur:
DESTROY_WSTACK(stack);
return hash;
-
+
#undef MAKE_HASH_TUPLE_OP
#undef MAKE_HASH_TERM_ARRAY_OP
#undef MAKE_HASH_CDR_PRE_OP
@@ -2231,7 +2217,9 @@ do_allocate_logger_message(Eterm gleader, Eterm **hp, ErlOffHeap **ohp,
#ifndef ERTS_SMP
#ifdef USE_THREADS
- if (erts_get_scheduler_data()) /* Must be scheduler thread */
+ if (!erts_get_scheduler_data()) /* Must be scheduler thread */
+ *p = NULL;
+ else
#endif
{
*p = erts_whereis_process(NULL, 0, am_error_logger, 0, 0);
@@ -2247,18 +2235,10 @@ do_allocate_logger_message(Eterm gleader, Eterm **hp, ErlOffHeap **ohp,
}
/* So we have an error logger, lets build the message */
- if (sz <= HeapWordsLeft(*p)) {
- *ohp = &MSO(*p);
- *hp = HEAP_TOP(*p);
- HEAP_TOP(*p) += sz;
- } else {
-#endif
- *bp = new_message_buffer(sz);
- *ohp = &(*bp)->off_heap;
- *hp = (*bp)->mem;
-#ifndef ERTS_SMP
- }
#endif
+ *bp = new_message_buffer(sz);
+ *ohp = &(*bp)->off_heap;
+ *hp = (*bp)->mem;
return (is_nil(gleader)
? am_noproc
@@ -2281,7 +2261,11 @@ static void do_send_logger_message(Eterm *hp, ErlOffHeap *ohp, ErlHeapFragment *
erts_queue_error_logger_message(from, message, bp);
}
#else
- erts_queue_message(p, NULL /* only used for smp build */, bp, message, NIL);
+ {
+ ErtsMessage *mp = erts_alloc_message(0, NULL);
+ mp->data.heap_frag = bp;
+ erts_queue_message(p, 0, mp, message, am_system);
+ }
#endif
}
@@ -2376,13 +2360,13 @@ static int do_send_term_to_logger(Eterm tag, Eterm gleader,
}
static ERTS_INLINE int
-send_info_to_logger(Eterm gleader, char *buf, int len)
+send_info_to_logger(Eterm gleader, char *buf, int len)
{
return do_send_to_logger(am_info_msg, gleader, buf, len);
}
static ERTS_INLINE int
-send_warning_to_logger(Eterm gleader, char *buf, int len)
+send_warning_to_logger(Eterm gleader, char *buf, int len)
{
Eterm tag;
switch (erts_error_logger_warnings) {
@@ -2394,7 +2378,7 @@ send_warning_to_logger(Eterm gleader, char *buf, int len)
}
static ERTS_INLINE int
-send_error_to_logger(Eterm gleader, char *buf, int len)
+send_error_to_logger(Eterm gleader, char *buf, int len)
{
return do_send_to_logger(am_error, gleader, buf, len);
}
@@ -2594,11 +2578,7 @@ erts_destroy_tmp_dsbuf(erts_dsprintf_buf_t *dsbufp)
* Test for equality of two terms.
* Returns 0 if not equal, or a non-zero value otherwise.
*/
-#if HALFWORD_HEAP
-int eq_rel(Eterm a, Eterm* a_base, Eterm b, Eterm* b_base)
-#else
int eq(Eterm a, Eterm b)
-#endif
{
DECLARE_WSTACK(stack);
Sint sz;
@@ -2606,18 +2586,18 @@ int eq(Eterm a, Eterm b)
Eterm* bb;
tailrecur:
- if (is_same(a, a_base, b, b_base)) goto pop_next;
+ if (is_same(a, b)) goto pop_next;
tailrecur_ne:
switch (primary_tag(a)) {
case TAG_PRIMARY_LIST:
if (is_list(b)) {
- Eterm* aval = list_val_rel(a, a_base);
- Eterm* bval = list_val_rel(b, b_base);
+ Eterm* aval = list_val(a);
+ Eterm* bval = list_val(b);
while (1) {
Eterm atmp = CAR(aval);
Eterm btmp = CAR(bval);
- if (!is_same(atmp,a_base,btmp,b_base)) {
+ if (!is_same(atmp,btmp)) {
WSTACK_PUSH2(stack,(UWord) CDR(bval),(UWord) CDR(aval));
a = atmp;
b = btmp;
@@ -2625,7 +2605,7 @@ tailrecur_ne:
}
atmp = CDR(aval);
btmp = CDR(bval);
- if (is_same(atmp,a_base,btmp,b_base)) {
+ if (is_same(atmp,btmp)) {
goto pop_next;
}
if (is_not_list(atmp) || is_not_list(btmp)) {
@@ -2633,22 +2613,22 @@ tailrecur_ne:
b = btmp;
goto tailrecur_ne;
}
- aval = list_val_rel(atmp, a_base);
- bval = list_val_rel(btmp, b_base);
+ aval = list_val(atmp);
+ bval = list_val(btmp);
}
}
break; /* not equal */
case TAG_PRIMARY_BOXED:
- {
- Eterm hdr = *boxed_val_rel(a,a_base);
+ {
+ Eterm hdr = *boxed_val(a);
switch (hdr & _TAG_HEADER_MASK) {
case ARITYVAL_SUBTAG:
{
- aa = tuple_val_rel(a, a_base);
- if (!is_boxed(b) || *boxed_val_rel(b,b_base) != *aa)
+ aa = tuple_val(a);
+ if (!is_boxed(b) || *boxed_val(b) != *aa)
goto not_equal;
- bb = tuple_val_rel(b,b_base);
+ bb = tuple_val(b);
if ((sz = arityval(*aa)) == 0) goto pop_next;
++aa;
++bb;
@@ -2666,17 +2646,17 @@ tailrecur_ne:
Uint b_bitsize;
Uint a_bitoffs;
Uint b_bitoffs;
-
- if (!is_binary_rel(b,b_base)) {
+
+ if (!is_binary(b)) {
goto not_equal;
}
- a_size = binary_size_rel(a,a_base);
- b_size = binary_size_rel(b,b_base);
+ a_size = binary_size(a);
+ b_size = binary_size(b);
if (a_size != b_size) {
goto not_equal;
}
- ERTS_GET_BINARY_BYTES_REL(a, a_ptr, a_bitoffs, a_bitsize, a_base);
- ERTS_GET_BINARY_BYTES_REL(b, b_ptr, b_bitoffs, b_bitsize, b_base);
+ ERTS_GET_BINARY_BYTES(a, a_ptr, a_bitoffs, a_bitsize);
+ ERTS_GET_BINARY_BYTES(b, b_ptr, b_bitoffs, b_bitsize);
if ((a_bitsize | b_bitsize | a_bitoffs | b_bitoffs) == 0) {
if (sys_memcmp(a_ptr, b_ptr, a_size) == 0) goto pop_next;
} else if (a_bitsize == b_bitsize) {
@@ -2687,9 +2667,9 @@ tailrecur_ne:
}
case EXPORT_SUBTAG:
{
- if (is_export_rel(b,b_base)) {
- Export* a_exp = *((Export **) (export_val_rel(a,a_base) + 1));
- Export* b_exp = *((Export **) (export_val_rel(b,b_base) + 1));
+ if (is_export(b)) {
+ Export* a_exp = *((Export **) (export_val(a) + 1));
+ Export* b_exp = *((Export **) (export_val(b) + 1));
if (a_exp == b_exp) goto pop_next;
}
break; /* not equal */
@@ -2698,11 +2678,11 @@ tailrecur_ne:
{
ErlFunThing* f1;
ErlFunThing* f2;
-
- if (!is_fun_rel(b,b_base))
+
+ if (!is_fun(b))
goto not_equal;
- f1 = (ErlFunThing *) fun_val_rel(a,a_base);
- f2 = (ErlFunThing *) fun_val_rel(b,b_base);
+ f1 = (ErlFunThing *) fun_val(a);
+ f2 = (ErlFunThing *) fun_val(b);
if (f1->fe->module != f2->fe->module ||
f1->fe->old_index != f2->fe->old_index ||
f1->fe->old_uniq != f2->fe->old_uniq ||
@@ -2720,16 +2700,16 @@ tailrecur_ne:
ExternalThing *ap;
ExternalThing *bp;
- if(!is_external_rel(b,b_base))
+ if(!is_external(b))
goto not_equal;
- ap = external_thing_ptr_rel(a,a_base);
- bp = external_thing_ptr_rel(b,b_base);
+ ap = external_thing_ptr(a);
+ bp = external_thing_ptr(b);
if(ap->header == bp->header && ap->node == bp->node) {
- ASSERT(1 == external_data_words_rel(a,a_base));
- ASSERT(1 == external_data_words_rel(b,b_base));
-
+ ASSERT(1 == external_data_words(a));
+ ASSERT(1 == external_data_words(b));
+
if (ap->data.ui[0] == bp->data.ui[0]) goto pop_next;
}
break; /* not equal */
@@ -2749,11 +2729,11 @@ tailrecur_ne:
ExternalThing* athing;
ExternalThing* bthing;
- if(!is_external_ref_rel(b,b_base))
+ if(!is_external_ref(b))
goto not_equal;
- athing = external_thing_ptr_rel(a,a_base);
- bthing = external_thing_ptr_rel(b,b_base);
+ athing = external_thing_ptr(a);
+ bthing = external_thing_ptr(b);
if(athing->node != bthing->node)
goto not_equal;
@@ -2765,12 +2745,12 @@ tailrecur_ne:
goto ref_common;
case REF_SUBTAG:
- if (!is_internal_ref_rel(b,b_base))
+ if (!is_internal_ref(b))
goto not_equal;
{
- RefThing* athing = ref_thing_ptr_rel(a,a_base);
- RefThing* bthing = ref_thing_ptr_rel(b,b_base);
+ RefThing* athing = ref_thing_ptr(a);
+ RefThing* bthing = ref_thing_ptr(b);
alen = internal_thing_ref_no_of_numbers(athing);
blen = internal_thing_ref_no_of_numbers(bthing);
anum = internal_thing_ref_numbers(athing);
@@ -2786,7 +2766,7 @@ tailrecur_ne:
if (alen == 3 && blen == 3) {
/* Most refs are of length 3 */
if (anum[1] == bnum[1] && anum[2] == bnum[2]) {
- goto pop_next;
+ goto pop_next;
} else {
goto not_equal;
}
@@ -2811,7 +2791,7 @@ tailrecur_ne:
for (i = common_len; i < blen; i++)
if (bnum[i] != 0)
goto not_equal;
- }
+ }
}
goto pop_next;
}
@@ -2819,11 +2799,11 @@ tailrecur_ne:
case NEG_BIG_SUBTAG:
{
int i;
-
- if (!is_big_rel(b,b_base))
+
+ if (!is_big(b))
goto not_equal;
- aa = big_val_rel(a,a_base);
- bb = big_val_rel(b,b_base);
+ aa = big_val(a);
+ bb = big_val(b);
if (*aa != *bb)
goto not_equal;
i = BIG_ARITY(aa);
@@ -2837,20 +2817,20 @@ tailrecur_ne:
{
FloatDef af;
FloatDef bf;
-
- if (is_float_rel(b,b_base)) {
- GET_DOUBLE_REL(a, af, a_base);
- GET_DOUBLE_REL(b, bf, b_base);
+
+ if (is_float(b)) {
+ GET_DOUBLE(a, af);
+ GET_DOUBLE(b, bf);
if (af.fd == bf.fd) goto pop_next;
}
break; /* not equal */
}
case MAP_SUBTAG:
- if (is_flatmap_rel(a, a_base)) {
- aa = flatmap_val_rel(a, a_base);
- if (!is_boxed(b) || *boxed_val_rel(b,b_base) != *aa)
+ if (is_flatmap(a)) {
+ aa = flatmap_val(a);
+ if (!is_boxed(b) || *boxed_val(b) != *aa)
goto not_equal;
- bb = flatmap_val_rel(b,b_base);
+ bb = flatmap_val(b);
sz = flatmap_get_size((flatmap_t*)aa);
if (sz != flatmap_get_size((flatmap_t*)bb)) goto not_equal;
@@ -2862,11 +2842,11 @@ tailrecur_ne:
goto term_array;
} else {
- if (!is_boxed(b) || *boxed_val_rel(b,b_base) != hdr)
+ if (!is_boxed(b) || *boxed_val(b) != hdr)
goto not_equal;
- aa = hashmap_val_rel(a, a_base) + 1;
- bb = hashmap_val_rel(b, b_base) + 1;
+ aa = hashmap_val(a) + 1;
+ bb = hashmap_val(b) + 1;
switch (hdr & _HEADER_MAP_SUBTAG_MASK) {
case HAMT_SUBTAG_HEAD_ARRAY:
aa++; bb++;
@@ -2899,7 +2879,7 @@ term_array: /* arrays in 'aa' and 'bb', length in 'sz' */
Eterm* bp = bb;
Sint i = sz;
for (;;) {
- if (!is_same(*ap,a_base,*bp,b_base)) break;
+ if (!is_same(*ap,*bp)) break;
if (--i == 0) goto pop_next;
++ap;
++bp;
@@ -2916,7 +2896,7 @@ term_array: /* arrays in 'aa' and 'bb', length in 'sz' */
}
goto tailrecur_ne;
}
-
+
pop_next:
if (!WSTACK_ISEMPTY(stack)) {
UWord something = WSTACK_POP(stack);
@@ -2977,7 +2957,7 @@ static int cmpbytes(byte *s1, int l1, byte *s2, int l2)
#define float_comp(x,y) (((x)<(y)) ? -1 : (((x)==(y)) ? 0 : 1))
-static int cmp_atoms(Eterm a, Eterm b)
+int erts_cmp_atoms(Eterm a, Eterm b)
{
Atom *aa = atom_tab(atom_val(a));
Atom *bb = atom_tab(atom_val(b));
@@ -2988,7 +2968,6 @@ static int cmp_atoms(Eterm a, Eterm b)
bb->name+3, bb->len-3);
}
-#if !HALFWORD_HEAP
/* cmp(Eterm a, Eterm b)
* For compatibility with HiPE - arith-based compare.
*/
@@ -2996,39 +2975,22 @@ Sint cmp(Eterm a, Eterm b)
{
return erts_cmp(a, b, 0, 0);
}
-#endif
-#if HALFWORD_HEAP
-static Sint erts_cmp_compound_rel_opt(Eterm a, Eterm* a_base,
- Eterm b, Eterm* b_base,
- int exact, int eq_only);
-#else
-static Sint erts_cmp_compound(Eterm a, Eterm b, int exact, int eq_only);
-#endif
+Sint erts_cmp_compound(Eterm a, Eterm b, int exact, int eq_only);
-#if HALFWORD_HEAP
-Sint erts_cmp_rel_opt(Eterm a, Eterm* a_base,
- Eterm b, Eterm* b_base,
- int exact, int eq_only)
-#else
Sint erts_cmp(Eterm a, Eterm b, int exact, int eq_only)
-#endif
{
if (is_atom(a) && is_atom(b)) {
- return cmp_atoms(a, b);
+ return erts_cmp_atoms(a, b);
} else if (is_both_small(a, b)) {
return (signed_val(a) - signed_val(b));
- } else if (is_float_rel(a, a_base) && is_float_rel(b, b_base)) {
+ } else if (is_float(a) && is_float(b)) {
FloatDef af, bf;
- GET_DOUBLE_REL(a, af, a_base);
- GET_DOUBLE_REL(b, bf, b_base);
+ GET_DOUBLE(a, af);
+ GET_DOUBLE(b, bf);
return float_comp(af.fd, bf.fd);
}
-#if HALFWORD_HEAP
- return erts_cmp_compound_rel_opt(a,a_base,b,b_base,exact,eq_only);
-#else
return erts_cmp_compound(a,b,exact,eq_only);
-#endif
}
@@ -3036,13 +2998,7 @@ Sint erts_cmp(Eterm a, Eterm b, int exact, int eq_only)
* exact = 1 -> term-based compare
* exact = 0 -> arith-based compare
*/
-#if HALFWORD_HEAP
-static Sint erts_cmp_compound_rel_opt(Eterm a, Eterm* a_base,
- Eterm b, Eterm* b_base,
- int exact, int eq_only)
-#else
-static Sint erts_cmp_compound(Eterm a, Eterm b, int exact, int eq_only)
-#endif
+Sint erts_cmp_compound(Eterm a, Eterm b, int exact, int eq_only)
{
#define PSTACK_TYPE struct erts_cmp_hashmap_state
struct erts_cmp_hashmap_state {
@@ -3099,7 +3055,7 @@ static Sint erts_cmp_compound(Eterm a, Eterm b, int exact, int eq_only)
do { \
if((AN) != (BN)) { \
if((AN)->sysname != (BN)->sysname) \
- RETURN_NEQ(cmp_atoms((AN)->sysname, (BN)->sysname)); \
+ RETURN_NEQ(erts_cmp_atoms((AN)->sysname, (BN)->sysname)); \
ASSERT((AN)->creation != (BN)->creation); \
RETURN_NEQ(((AN)->creation < (BN)->creation) ? -1 : 1); \
} \
@@ -3109,7 +3065,7 @@ static Sint erts_cmp_compound(Eterm a, Eterm b, int exact, int eq_only)
bodyrecur:
j = 0;
tailrecur:
- if (is_same(a,a_base,b,b_base)) { /* Equal values or pointers. */
+ if (is_same(a,b)) { /* Equal values or pointers. */
goto pop_next;
}
tailrecur_ne:
@@ -3117,7 +3073,7 @@ tailrecur_ne:
/* deal with majority (?) cases by brute-force */
if (is_atom(a)) {
if (is_atom(b)) {
- ON_CMP_GOTO(cmp_atoms(a, b));
+ ON_CMP_GOTO(erts_cmp_atoms(a, b));
}
} else if (is_both_small(a, b)) {
ON_CMP_GOTO(signed_val(a) - signed_val(b));
@@ -3135,16 +3091,16 @@ tailrecur_ne:
if (is_internal_port(b)) {
bnode = erts_this_node;
bdata = internal_port_data(b);
- } else if (is_external_port_rel(b,b_base)) {
- bnode = external_port_node_rel(b,b_base);
- bdata = external_port_data_rel(b,b_base);
+ } else if (is_external_port(b)) {
+ bnode = external_port_node(b);
+ bdata = external_port_data(b);
} else {
a_tag = PORT_DEF;
goto mixed_types;
}
anode = erts_this_node;
adata = internal_port_data(a);
-
+
port_common:
CMP_NODES(anode, bnode);
ON_CMP_GOTO((Sint)(adata - bdata));
@@ -3153,16 +3109,16 @@ tailrecur_ne:
if (is_internal_pid(b)) {
bnode = erts_this_node;
bdata = internal_pid_data(b);
- } else if (is_external_pid_rel(b,b_base)) {
- bnode = external_pid_node_rel(b,b_base);
- bdata = external_pid_data_rel(b,b_base);
+ } else if (is_external_pid(b)) {
+ bnode = external_pid_node(b);
+ bdata = external_pid_data(b);
} else {
a_tag = PID_DEF;
goto mixed_types;
}
anode = erts_this_node;
adata = internal_pid_data(a);
-
+
pid_common:
if (adata != bdata) {
RETURN_NEQ(adata < bdata ? -1 : 1);
@@ -3188,12 +3144,12 @@ tailrecur_ne:
a_tag = LIST_DEF;
goto mixed_types;
}
- aa = list_val_rel(a,a_base);
- bb = list_val_rel(b,b_base);
+ aa = list_val(a);
+ bb = list_val(b);
while (1) {
Eterm atmp = CAR(aa);
Eterm btmp = CAR(bb);
- if (!is_same(atmp,a_base,btmp,b_base)) {
+ if (!is_same(atmp,btmp)) {
WSTACK_PUSH2(stack,(UWord) CDR(bb),(UWord) CDR(aa));
a = atmp;
b = btmp;
@@ -3201,7 +3157,7 @@ tailrecur_ne:
}
atmp = CDR(aa);
btmp = CDR(bb);
- if (is_same(atmp,a_base,btmp,b_base)) {
+ if (is_same(atmp,btmp)) {
goto pop_next;
}
if (is_not_list(atmp) || is_not_list(btmp)) {
@@ -3209,20 +3165,20 @@ tailrecur_ne:
b = btmp;
goto tailrecur_ne;
}
- aa = list_val_rel(atmp,a_base);
- bb = list_val_rel(btmp,b_base);
+ aa = list_val(atmp);
+ bb = list_val(btmp);
}
case TAG_PRIMARY_BOXED:
{
- Eterm ahdr = *boxed_val_rel(a,a_base);
+ Eterm ahdr = *boxed_val(a);
switch ((ahdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) {
case (_TAG_HEADER_ARITYVAL >> _TAG_PRIMARY_SIZE):
- if (!is_tuple_rel(b,b_base)) {
+ if (!is_tuple(b)) {
a_tag = TUPLE_DEF;
goto mixed_types;
}
- aa = tuple_val_rel(a,a_base);
- bb = tuple_val_rel(b,b_base);
+ aa = tuple_val(a);
+ bb = tuple_val(b);
/* compare the arities */
i = arityval(ahdr); /* get the arity*/
if (i != arityval(*bb)) {
@@ -3238,18 +3194,18 @@ tailrecur_ne:
{
struct erts_cmp_hashmap_state* sp;
if (is_flatmap_header(ahdr)) {
- if (!is_flatmap_rel(b,b_base)) {
- if (is_hashmap_rel(b,b_base)) {
- aa = (Eterm *)flatmap_val_rel(a,a_base);
- i = flatmap_get_size((flatmap_t*)aa) - hashmap_size_rel(b,b_base);
+ if (!is_flatmap(b)) {
+ if (is_hashmap(b)) {
+ aa = (Eterm *)flatmap_val(a);
+ i = flatmap_get_size((flatmap_t*)aa) - hashmap_size(b);
ASSERT(i != 0);
RETURN_NEQ(i);
}
a_tag = MAP_DEF;
goto mixed_types;
}
- aa = (Eterm *)flatmap_val_rel(a,a_base);
- bb = (Eterm *)flatmap_val_rel(b,b_base);
+ aa = (Eterm *)flatmap_val(a);
+ bb = (Eterm *)flatmap_val(b);
i = flatmap_get_size((flatmap_t*)aa);
if (i != flatmap_get_size((flatmap_t*)bb)) {
@@ -3276,21 +3232,21 @@ tailrecur_ne:
goto bodyrecur;
}
}
- if (!is_hashmap_rel(b,b_base)) {
- if (is_flatmap_rel(b,b_base)) {
- bb = (Eterm *)flatmap_val_rel(b,b_base);
- i = hashmap_size_rel(a,a_base) - flatmap_get_size((flatmap_t*)bb);
+ if (!is_hashmap(b)) {
+ if (is_flatmap(b)) {
+ bb = (Eterm *)flatmap_val(b);
+ i = hashmap_size(a) - flatmap_get_size((flatmap_t*)bb);
ASSERT(i != 0);
RETURN_NEQ(i);
}
a_tag = MAP_DEF;
goto mixed_types;
}
- i = hashmap_size_rel(a,a_base) - hashmap_size_rel(b,b_base);
+ i = hashmap_size(a) - hashmap_size(b);
if (i) {
RETURN_NEQ(i);
}
- if (hashmap_size_rel(a,a_base) == 0) {
+ if (hashmap_size(a) == 0) {
goto pop_next;
}
@@ -3325,48 +3281,48 @@ tailrecur_ne:
goto bodyrecur;
}
case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
- if (!is_float_rel(b,b_base)) {
+ if (!is_float(b)) {
a_tag = FLOAT_DEF;
goto mixed_types;
} else {
FloatDef af;
FloatDef bf;
- GET_DOUBLE_REL(a, af, a_base);
- GET_DOUBLE_REL(b, bf, b_base);
+ GET_DOUBLE(a, af);
+ GET_DOUBLE(b, bf);
ON_CMP_GOTO(float_comp(af.fd, bf.fd));
}
case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE):
case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE):
- if (!is_big_rel(b,b_base)) {
+ if (!is_big(b)) {
a_tag = BIG_DEF;
goto mixed_types;
}
- ON_CMP_GOTO(big_comp(rterm2wterm(a,a_base), rterm2wterm(b,b_base)));
+ ON_CMP_GOTO(big_comp(a, b));
case (_TAG_HEADER_EXPORT >> _TAG_PRIMARY_SIZE):
- if (!is_export_rel(b,b_base)) {
+ if (!is_export(b)) {
a_tag = EXPORT_DEF;
goto mixed_types;
} else {
- Export* a_exp = *((Export **) (export_val_rel(a,a_base) + 1));
- Export* b_exp = *((Export **) (export_val_rel(b,b_base) + 1));
+ Export* a_exp = *((Export **) (export_val(a) + 1));
+ Export* b_exp = *((Export **) (export_val(b) + 1));
- if ((j = cmp_atoms(a_exp->code[0], b_exp->code[0])) != 0) {
+ if ((j = erts_cmp_atoms(a_exp->code[0], b_exp->code[0])) != 0) {
RETURN_NEQ(j);
}
- if ((j = cmp_atoms(a_exp->code[1], b_exp->code[1])) != 0) {
+ if ((j = erts_cmp_atoms(a_exp->code[1], b_exp->code[1])) != 0) {
RETURN_NEQ(j);
}
ON_CMP_GOTO((Sint) a_exp->code[2] - (Sint) b_exp->code[2]);
}
break;
case (_TAG_HEADER_FUN >> _TAG_PRIMARY_SIZE):
- if (!is_fun_rel(b,b_base)) {
+ if (!is_fun(b)) {
a_tag = FUN_DEF;
goto mixed_types;
} else {
- ErlFunThing* f1 = (ErlFunThing *) fun_val_rel(a,a_base);
- ErlFunThing* f2 = (ErlFunThing *) fun_val_rel(b,b_base);
+ ErlFunThing* f1 = (ErlFunThing *) fun_val(a);
+ ErlFunThing* f2 = (ErlFunThing *) fun_val(b);
Sint diff;
diff = cmpbytes(atom_tab(atom_val(f1->fe->module))->name,
@@ -3387,7 +3343,7 @@ tailrecur_ne:
diff = f1->num_free - f2->num_free;
if (diff != 0) {
RETURN_NEQ(diff);
- }
+ }
i = f1->num_free;
if (i == 0) goto pop_next;
aa = f1->env;
@@ -3398,29 +3354,29 @@ tailrecur_ne:
if (is_internal_pid(b)) {
bnode = erts_this_node;
bdata = internal_pid_data(b);
- } else if (is_external_pid_rel(b,b_base)) {
- bnode = external_pid_node_rel(b,b_base);
- bdata = external_pid_data_rel(b,b_base);
+ } else if (is_external_pid(b)) {
+ bnode = external_pid_node(b);
+ bdata = external_pid_data(b);
} else {
a_tag = EXTERNAL_PID_DEF;
goto mixed_types;
}
- anode = external_pid_node_rel(a,a_base);
- adata = external_pid_data_rel(a,a_base);
+ anode = external_pid_node(a);
+ adata = external_pid_data(a);
goto pid_common;
case (_TAG_HEADER_EXTERNAL_PORT >> _TAG_PRIMARY_SIZE):
if (is_internal_port(b)) {
bnode = erts_this_node;
bdata = internal_port_data(b);
- } else if (is_external_port_rel(b,b_base)) {
- bnode = external_port_node_rel(b,b_base);
- bdata = external_port_data_rel(b,b_base);
+ } else if (is_external_port(b)) {
+ bnode = external_port_node(b);
+ bdata = external_port_data(b);
} else {
a_tag = EXTERNAL_PORT_DEF;
goto mixed_types;
}
- anode = external_port_node_rel(a,a_base);
- adata = external_port_data_rel(a,a_base);
+ anode = external_port_node(a);
+ adata = external_port_data(a);
goto port_common;
case (_TAG_HEADER_REF >> _TAG_PRIMARY_SIZE):
/*
@@ -3428,14 +3384,13 @@ tailrecur_ne:
* (32-bit words), *not* ref data words.
*/
-
- if (is_internal_ref_rel(b,b_base)) {
- RefThing* bthing = ref_thing_ptr_rel(b,b_base);
+ if (is_internal_ref(b)) {
+ RefThing* bthing = ref_thing_ptr(b);
bnode = erts_this_node;
bnum = internal_thing_ref_numbers(bthing);
blen = internal_thing_ref_no_of_numbers(bthing);
- } else if(is_external_ref_rel(b,b_base)) {
- ExternalThing* bthing = external_thing_ptr_rel(b,b_base);
+ } else if(is_external_ref(b)) {
+ ExternalThing* bthing = external_thing_ptr(b);
bnode = bthing->node;
bnum = external_thing_ref_numbers(bthing);
blen = external_thing_ref_no_of_numbers(bthing);
@@ -3444,15 +3399,15 @@ tailrecur_ne:
goto mixed_types;
}
{
- RefThing* athing = ref_thing_ptr_rel(a,a_base);
+ RefThing* athing = ref_thing_ptr(a);
anode = erts_this_node;
anum = internal_thing_ref_numbers(athing);
alen = internal_thing_ref_no_of_numbers(athing);
}
-
+
ref_common:
CMP_NODES(anode, bnode);
-
+
ASSERT(alen > 0 && blen > 0);
if (alen != blen) {
if (alen > blen) {
@@ -3470,20 +3425,20 @@ tailrecur_ne:
} while (alen < blen);
}
}
-
+
ASSERT(alen == blen);
for (i = (Sint) alen - 1; i >= 0; i--)
if (anum[i] != bnum[i])
RETURN_NEQ((Sint32) (anum[i] - bnum[i]));
goto pop_next;
case (_TAG_HEADER_EXTERNAL_REF >> _TAG_PRIMARY_SIZE):
- if (is_internal_ref_rel(b,b_base)) {
- RefThing* bthing = ref_thing_ptr_rel(b,b_base);
+ if (is_internal_ref(b)) {
+ RefThing* bthing = ref_thing_ptr(b);
bnode = erts_this_node;
bnum = internal_thing_ref_numbers(bthing);
blen = internal_thing_ref_no_of_numbers(bthing);
- } else if (is_external_ref_rel(b,b_base)) {
- ExternalThing* bthing = external_thing_ptr_rel(b,b_base);
+ } else if (is_external_ref(b)) {
+ ExternalThing* bthing = external_thing_ptr(b);
bnode = bthing->node;
bnum = external_thing_ref_numbers(bthing);
blen = external_thing_ref_no_of_numbers(bthing);
@@ -3492,7 +3447,7 @@ tailrecur_ne:
goto mixed_types;
}
{
- ExternalThing* athing = external_thing_ptr_rel(a,a_base);
+ ExternalThing* athing = external_thing_ptr(a);
anode = athing->node;
anum = external_thing_ref_numbers(athing);
alen = external_thing_ref_no_of_numbers(athing);
@@ -3500,13 +3455,13 @@ tailrecur_ne:
goto ref_common;
default:
/* Must be a binary */
- ASSERT(is_binary_rel(a,a_base));
- if (!is_binary_rel(b,b_base)) {
+ ASSERT(is_binary(a));
+ if (!is_binary(b)) {
a_tag = BINARY_DEF;
goto mixed_types;
} else {
- Uint a_size = binary_size_rel(a,a_base);
- Uint b_size = binary_size_rel(b,b_base);
+ Uint a_size = binary_size(a);
+ Uint b_size = binary_size(b);
Uint a_bitsize;
Uint b_bitsize;
Uint a_bitoffs;
@@ -3515,8 +3470,8 @@ tailrecur_ne:
int cmp;
byte* a_ptr;
byte* b_ptr;
- ERTS_GET_BINARY_BYTES_REL(a, a_ptr, a_bitoffs, a_bitsize, a_base);
- ERTS_GET_BINARY_BYTES_REL(b, b_ptr, b_bitoffs, b_bitsize, b_base);
+ ERTS_GET_BINARY_BYTES(a, a_ptr, a_bitoffs, a_bitsize);
+ ERTS_GET_BINARY_BYTES(b, b_ptr, b_bitoffs, b_bitsize);
if ((a_bitsize | b_bitsize | a_bitoffs | b_bitoffs) == 0) {
min_size = (a_size < b_size) ? a_size : b_size;
if ((cmp = sys_memcmp(a_ptr, b_ptr, min_size)) != 0) {
@@ -3547,13 +3502,8 @@ tailrecur_ne:
{
FloatDef f1, f2;
Eterm big;
-#if HALFWORD_HEAP
- Wterm aw = is_immed(a) ? a : rterm2wterm(a,a_base);
- Wterm bw = is_immed(b) ? b : rterm2wterm(b,b_base);
-#else
Eterm aw = a;
Eterm bw = b;
-#endif
#define MAX_LOSSLESS_FLOAT ((double)((1LL << 53) - 2))
#define MIN_LOSSLESS_FLOAT ((double)(((1LL << 53) - 2)*-1))
#define BIG_ARITY_FLOAT_MAX (1024 / D_EXP) /* arity of max float as a bignum */
@@ -3625,7 +3575,7 @@ tailrecur_ne:
}
} else {
big = double_to_big(f2.fd, big_buf, sizeof(big_buf)/sizeof(Eterm));
- j = big_comp(aw, rterm2wterm(big,big_buf));
+ j = big_comp(aw, big);
}
if (_NUMBER_CODE(a_tag, b_tag) == FLOAT_BIG) {
j = -j;
@@ -3673,9 +3623,9 @@ term_array: /* arrays in 'aa' and 'bb', length in 'i' */
while (--i) {
a = *aa++;
b = *bb++;
- if (!is_same(a,a_base, b,b_base)) {
+ if (!is_same(a, b)) {
if (is_atom(a) && is_atom(b)) {
- if ((j = cmp_atoms(a, b)) != 0) {
+ if ((j = erts_cmp_atoms(a, b)) != 0) {
goto not_equal;
}
} else if (is_both_small(a, b)) {
@@ -3690,8 +3640,8 @@ term_array: /* arrays in 'aa' and 'bb', length in 'i' */
}
a = *aa;
b = *bb;
- goto tailrecur;
-
+ goto tailrecur;
+
pop_next:
if (!WSTACK_ISEMPTY(stack)) {
UWord something = WSTACK_POP(stack);
@@ -3892,7 +3842,7 @@ store_external_or_ref_(Uint **hpp, ErlOffHeap* oh, Eterm ns)
for(i = 0; i < size; i++)
to_hp[i] = from_hp[i];
- erts_refc_inc(&((ExternalThing *) to_hp)->node->refc, 2);
+ erts_smp_refc_inc(&((ExternalThing *) to_hp)->node->refc, 2);
((struct erl_off_heap_header*) to_hp)->next = oh->first;
oh->first = (struct erl_off_heap_header*) to_hp;
@@ -3927,7 +3877,7 @@ store_external_or_ref_in_proc_(Process *proc, Eterm ns)
return store_external_or_ref_(&hp, &MSO(proc), ns);
}
-void bin_write(int to, void *to_arg, byte* buf, size_t sz)
+void bin_write(fmtfn_t to, void *to_arg, byte* buf, size_t sz)
{
size_t i;
@@ -3944,31 +3894,33 @@ void bin_write(int to, void *to_arg, byte* buf, size_t sz)
}
/* Fill buf with the contents of bytelist list
- return number of chars in list or -1 for error */
-
-int
-intlist_to_buf(Eterm list, char *buf, int len)
+ * return number of chars in list
+ * or -1 for type error
+ * or -2 for not enough buffer space (buffer contains truncated result)
+ */
+Sint
+intlist_to_buf(Eterm list, char *buf, Sint len)
{
Eterm* listptr;
- int sz = 0;
+ Sint sz = 0;
- if (is_nil(list))
+ if (is_nil(list))
return 0;
if (is_not_list(list))
return -1;
listptr = list_val(list);
while (sz < len) {
- if (!is_byte(*listptr))
+ if (!is_byte(*listptr))
return -1;
buf[sz++] = unsigned_val(*listptr);
if (is_nil(*(listptr + 1)))
return(sz);
- if (is_not_list(*(listptr + 1)))
+ if (is_not_list(*(listptr + 1)))
return -1;
listptr = list_val(*(listptr + 1));
}
- return -1; /* not enough space */
+ return -2; /* not enough space */
}
/*
@@ -4212,10 +4164,10 @@ do { \
} else if (yield_support && --yield_count <= 0)
goto L_yield;
}
-
+
res = len;
- L_return:
+ L_return:
DESTROY_ESTACK(s);
@@ -4365,7 +4317,7 @@ iolist_size(const int yield_support, ErtsIOListState *state, Eterm obj, ErlDrvSi
{
int res, init_yield_count, yield_count;
Eterm* objp;
- Uint size = (Uint) *sizep; /* Intentionally Uint due to halfword heap */
+ Uint size = (Uint) *sizep;
DECLARE_ESTACK(s);
if (!yield_support)
@@ -4497,11 +4449,12 @@ int erts_iolist_size(Eterm obj, ErlDrvSizeT* sizep)
return iolist_size(0, NULL, obj, sizep);
}
-/* return 0 if item is not a non-empty flat list of bytes */
-int
+/* return 0 if item is not a non-empty flat list of bytes
+ otherwise return the nonzero length of the list */
+Sint
is_string(Eterm list)
{
- int len = 0;
+ Sint len = 0;
while(is_list(list)) {
Eterm* consp = list_val(list);
@@ -5107,7 +5060,7 @@ Process *p;
if(p)
print_process_info(ERTS_PRINT_STDERR, NULL, p);
}
-
+
void ppi(Eterm pid)
{
pp(erts_proc_lookup(pid));
@@ -5133,5 +5086,3 @@ ps(Process* p, Eterm* stop)
}
}
#endif
-
-
diff --git a/erts/emulator/beam/version.h b/erts/emulator/beam/version.h
index 004725eaf5..0fa775fe8c 100644
--- a/erts/emulator/beam/version.h
+++ b/erts/emulator/beam/version.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c
index 5e25747ddf..3adb8db661 100644
--- a/erts/emulator/drivers/common/efile_drv.c
+++ b/erts/emulator/drivers/common/efile_drv.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -101,15 +101,9 @@
# include "config.h"
#endif
-#ifndef __OSE__
#include <ctype.h>
#include <sys/types.h>
#include <stdlib.h>
-#else
-#include "ctype.h"
-#include "sys/types.h"
-#include "stdlib.h"
-#endif
/* Need (NON)BLOCKING macros for sendfile */
#ifndef WANT_NONBLOCKING
@@ -882,7 +876,7 @@ static void reply_Uint_posix_error(file_descriptor *desc, Uint num,
TRACE_C('N');
response[0] = FILE_RESP_NUMERR;
-#if SIZEOF_VOID_P == 4 || HALFWORD_HEAP
+#if SIZEOF_VOID_P == 4
put_int32(0, response+1);
#else
put_int32(num>>32, response+1);
@@ -951,7 +945,7 @@ static int reply_Uint(file_descriptor *desc, Uint result) {
TRACE_C('R');
tmp[0] = FILE_RESP_NUMBER;
-#if SIZEOF_VOID_P == 4 || HALFWORD_HEAP
+#if SIZEOF_VOID_P == 4
put_int32(0, tmp+1);
#else
put_int32(result>>32, tmp+1);
@@ -2906,12 +2900,12 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1
+ FILENAME_BYTELEN(buf + 9*4) + FILENAME_CHARSIZE);
- d->info.mode = get_int32(buf + 0 * 4);
- d->info.uid = get_int32(buf + 1 * 4);
- d->info.gid = get_int32(buf + 2 * 4);
- d->info.accessTime = (time_t)((Sint64)get_int64(buf + 3 * 4));
- d->info.modifyTime = (time_t)((Sint64)get_int64(buf + 5 * 4));
- d->info.cTime = (time_t)((Sint64)get_int64(buf + 7 * 4));
+ d->info.mode = get_int32(buf + 0 * 4);
+ d->info.uid = get_int32(buf + 1 * 4);
+ d->info.gid = get_int32(buf + 2 * 4);
+ d->info.accessTime = get_int64(buf + 3 * 4);
+ d->info.modifyTime = get_int64(buf + 5 * 4);
+ d->info.cTime = get_int64(buf + 7 * 4);
FILENAME_COPY(d->b, buf + 9*4);
#ifdef USE_VM_PROBES
diff --git a/erts/emulator/drivers/common/erl_efile.h b/erts/emulator/drivers/common/erl_efile.h
index be5a891486..b7f063b4f2 100644
--- a/erts/emulator/drivers/common/erl_efile.h
+++ b/erts/emulator/drivers/common/erl_efile.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -105,9 +105,9 @@ typedef struct _Efile_info {
Uint32 inode; /* Inode number. */
Uint32 uid; /* User id of owner. */
Uint32 gid; /* Group id of owner. */
- time_t accessTime; /* Last time the file was accessed. */
- time_t modifyTime; /* Last time the file was modified. */
- time_t cTime; /* Creation time (Windows) or last
+ Sint64 accessTime; /* Last time the file was accessed. */
+ Sint64 modifyTime; /* Last time the file was modified. */
+ Sint64 cTime; /* Creation time (Windows) or last
* inode change (Unix).
*/
} Efile_info;
diff --git a/erts/emulator/drivers/common/gzio.h b/erts/emulator/drivers/common/gzio.h
index a6fe2fb6f5..ee0ebe7bd8 100644
--- a/erts/emulator/drivers/common/gzio.h
+++ b/erts/emulator/drivers/common/gzio.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/drivers/common/gzio_zutil.h b/erts/emulator/drivers/common/gzio_zutil.h
index 9eefb86637..b229ae4efd 100644
--- a/erts/emulator/drivers/common/gzio_zutil.h
+++ b/erts/emulator/drivers/common/gzio_zutil.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009. All Rights Reserved.
+ * Copyright Ericsson AB 2009-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c
index 2d2bd80783..ebd13e6f05 100644
--- a/erts/emulator/drivers/common/inet_drv.c
+++ b/erts/emulator/drivers/common/inet_drv.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2015. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,6 +34,7 @@
#include <stdio.h>
#include <stdlib.h>
+#include <stddef.h>
#include <ctype.h>
#include <sys/types.h>
#include <errno.h>
@@ -58,6 +59,9 @@
#ifdef HAVE_NETPACKET_PACKET_H
#include <netpacket/packet.h>
#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
/* All platforms fail on malloc errors. */
#define FATAL_MALLOC
@@ -94,10 +98,6 @@ typedef unsigned long long llu_t;
#define INT16_MAX (32767)
#endif
-#ifdef __OSE__
-#include "inet.h"
-#endif
-
#ifdef __WIN32__
#define STRNCASECMP strncasecmp
@@ -298,139 +298,7 @@ static unsigned long one_value = 1;
#define TCP_SHUT_RD SD_RECEIVE
#define TCP_SHUT_RDWR SD_BOTH
-#elif defined (__OSE__)
-
-/*
- * Some notes about how inet (currently only tcp) works on OSE.
- * The driver uses OSE signals to communicate with the one_inet
- * process. Because of the difference in how signals and file descriptors
- * work the whole select/deselect mechanic is very different.
- * In ose when a sock_select is done a function is called. That function
- * notes the changes that the driver want to do, but does not act on it.
- * later when the function returns the new desired state is compared
- * to the previous state and the apprioriate actions are taken. The action
- * is usually to either request more data from the stack or stop requesting
- * data.
- *
- * One thing to note is that the driver never does select/deselect. It always
- * listens for the signals. Flow of data is regulated by sending or not sending
- * signals to the ose inet process.
- *
- * The interesting functions to look at are:
- * * inet_driver_select : called when sock_select is called
- * * tcp_inet_ose_dispatch_signal : checks state changes and sends new signals
- * * tcp_inet_drv_output_ose : ready output callback, reads signals and calls
- * dispatch_signal
- * * tcp_inet_drv_input_ose : ready input callback.
- */
-
-#include "efs.h"
-#include "sys/socket.h"
-#include "sys/uio.h"
-#include "sfk/sys/sfk_uio.h"
-#include "netinet/in.h"
-#include "netinet/tcp.h"
-#include "netdb.h"
-#include "ose_spi/socket.sig"
-
-
-static ssize_t writev_fallback(int fd, const struct iovec *iov, int iovcnt, int max_sz);
-
-#define INVALID_SOCKET -1
-#define INVALID_EVENT -1
-#define SOCKET_ERROR -1
-
-#define SOCKET int
-#define HANDLE int
-#define FD_READ ERL_DRV_READ
-#define FD_WRITE ERL_DRV_WRITE
-#define FD_CLOSE 0
-#define FD_CONNECT (1<<4)
-#define FD_ACCEPT (1<<5)
-#define SOCK_FD_ERROR (1<<6)
-
-#define sock_connect(s, addr, len) connect((s), (addr), (len))
-#define sock_listen(s, b) listen((s), (b))
-#define sock_bind(s, addr, len) bind((s), (addr), (len))
-#define sock_getopt(s,t,n,v,l) getsockopt((s),(t),(n),(v),(l))
-#define sock_setopt(s,t,n,v,l) setsockopt((s),(t),(n),(v),(l))
-#define sock_name(s, addr, len) getsockname((s), (addr), (len))
-#define sock_peer(s, addr, len) getpeername((s), (addr), (len))
-#define sock_ntohs(x) ntohs((x))
-#define sock_ntohl(x) ntohl((x))
-#define sock_htons(x) htons((x))
-#define sock_htonl(x) htonl((x))
-
-#define sock_accept(s, addr, len) accept((s), (addr), (len))
-#define sock_send(s,buf,len,flag) inet_send((s),(buf),(len),(flag))
-#define sock_sendto(s,buf,blen,flag,addr,alen) \
- sendto((s),(buf),(blen),(flag),(addr),(alen))
-#define sock_sendv(s, vec, size, np, flag) \
- (*(np) = writev_fallback((s), (struct iovec*)(vec), (size), (*(np))))
-#define sock_sendmsg(s,msghdr,flag) sendmsg((s),(msghdr),(flag))
-
-#define sock_open(af, type, proto) socket((af), (type), (proto))
-#define sock_close(s) close((s))
-#define sock_dup(s) dup((s))
-#define sock_shutdown(s, how) shutdown((s), (how))
-
-#define sock_hostname(buf, len) gethostname((buf), (len))
-#define sock_getservbyname(name,proto) getservbyname((name), (proto))
-#define sock_getservbyport(port,proto) getservbyport((port), (proto))
-
-#define sock_recv(s,buf,len,flag) recv((s),(buf),(len),(flag))
-#define sock_recvfrom(s,buf,blen,flag,addr,alen) \
- recvfrom((s),(buf),(blen),(flag),(addr),(alen))
-#define sock_recvmsg(s,msghdr,flag) recvmsg((s),(msghdr),(flag))
-
-#define sock_errno() errno
-#define sock_create_event(d) ((d)->s) /* return file descriptor */
-#define sock_close_event(e) /* do nothing */
-
-#ifndef WANT_NONBLOCKING
-#define WANT_NONBLOCKING
-#endif
-#include "sys.h"
-
-typedef unsigned long u_long;
-#define IN_CLASSA(a) ((((in_addr_t)(a)) & 0x80000000) == 0)
-#define IN_CLASSA_NET 0xff000000
-#define IN_CLASSA_NSHIFT 24
-#define IN_CLASSA_HOST (0xffffffff & ~IN_CLASSA_NET)
-#define IN_CLASSA_MAX 128
-
-#define IN_CLASSB(a) ((((in_addr_t)(a)) & 0xc0000000) == 0x80000000)
-#define IN_CLASSB_NET 0xffff0000
-#define IN_CLASSB_NSHIFT 16
-#define IN_CLASSB_HOST (0xffffffff & ~IN_CLASSB_NET)
-#define IN_CLASSB_MAX 65536
-
-#define IN_CLASSC(a) ((((in_addr_t)(a)) & 0xe0000000) == 0xc0000000)
-#define IN_CLASSC_NET 0xffffff00
-#define IN_CLASSC_NSHIFT 8
-#define IN_CLASSC_HOST (0xffffffff & ~IN_CLASSC_NET)
-
-#define IN_CLASSD(a) ((((in_addr_t)(a)) & 0xf0000000) == 0xe0000000)
-#define IN_MULTICAST(a) IN_CLASSD(a)
-
-#define IN_EXPERIMENTAL(a) ((((in_addr_t)(a)) & 0xe0000000) == 0xe0000000)
-#define IN_BADCLASS(a) ((((in_addr_t)(a)) & 0xf0000000) == 0xf0000000)
-
-#define sock_select(d, flags, onoff) do { \
- ASSERT(!(d)->is_ignored); \
- (d)->event_mask = (onoff) ? \
- ((d)->event_mask | (flags)) : \
- ((d)->event_mask & ~(flags)); \
- DEBUGF(("(%s / %d) sock_select(%ld): flags=%02X, onoff=%d, event_mask=%02lX, s=%d\r\n", \
- __FILE__, __LINE__, (long) (d)->port, (flags), (onoff), (unsigned long) (d)->event_mask, (d)->s)); \
- inet_driver_select((d), (flags), (onoff)); \
- } while(0)
-
-#define TCP_SHUT_WR SHUT_WR
-#define TCP_SHUT_RD SHUT_RD
-#define TCP_SHUT_RDWR SHUT_RDWR
-
-#else /* !__OSE__ && !__WIN32__ */
+#else /* !__WIN32__ */
#include <sys/time.h>
#ifdef NETDB_H_NEEDS_IN_H
@@ -704,12 +572,12 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
#define TCP_SHUT_RD SHUT_RD
#define TCP_SHUT_RDWR SHUT_RDWR
-#endif /* !__WIN32__ && !__OSE__ */
+#endif /* !__WIN32__ */
#ifdef HAVE_SOCKLEN_T
# define SOCKLEN_T socklen_t
#else
-# define SOCKLEN_T int
+# define SOCKLEN_T size_t
#endif
#include "packet_parser.h"
@@ -723,6 +591,26 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
(((unsigned char*) (s))[1] << 8) | \
(((unsigned char*) (s))[0]))
+#ifdef HAVE_SYS_UN_H
+
+/* strnlen doesn't exist everywhere */
+static size_t my_strnlen(const char *s, size_t maxlen)
+{
+ size_t i = 0;
+ while (i < maxlen && s[i] != '\0')
+ i++;
+ return i;
+}
+
+/* Check that some character in the buffer != '\0' */
+static int is_nonzero(const char *s, size_t n)
+{
+ size_t i;
+ for (i = 0; i < n; i++) if (s[i] != '\0') return !0;
+ return 0;
+}
+
+#endif
#ifdef VALGRIND
# include <valgrind/memcheck.h>
@@ -743,10 +631,13 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
*/
/* general address encode/decode tag */
+#define INET_AF_UNSPEC 0
#define INET_AF_INET 1
#define INET_AF_INET6 2
#define INET_AF_ANY 3 /* INADDR_ANY or IN6ADDR_ANY_INIT */
#define INET_AF_LOOPBACK 4 /* INADDR_LOOPBACK or IN6ADDR_LOOPBACK_INIT */
+#define INET_AF_LOCAL 5
+#define INET_AF_UNDEFINED 6 /* Unknown */
/* open and INET_REQ_GETTYPE enumeration */
#define INET_TYPE_STREAM 1
@@ -769,7 +660,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
/* INET_REQ_GETSTATUS enumeration */
#define INET_F_OPEN 0x0001
-#define INET_F_BOUND 0x0002
+/* INET_F_BOUND removed - renumber when there comes a bigger rewrite */
#define INET_F_ACTIVE 0x0004
#define INET_F_LISTEN 0x0008
#define INET_F_CON 0x0010
@@ -886,6 +777,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
#define INET_LOPT_NETNS 38 /* Network namespace pathname */
#define INET_LOPT_TCP_SHOW_ECONNRESET 39 /* tell user about incoming RST */
#define INET_LOPT_LINE_DELIM 40 /* Line delimiting char */
+#define INET_OPT_TCLASS 41 /* IPv6 transport class */
/* SCTP options: a separate range, from 100: */
#define SCTP_OPT_RTOINFO 100
#define SCTP_OPT_ASSOCINFO 101
@@ -967,19 +859,15 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
#define INET_STATE_CLOSED (0)
#define INET_STATE_OPEN (INET_F_OPEN)
-#define INET_STATE_BOUND (INET_STATE_OPEN | INET_F_BOUND)
-#define INET_STATE_CONNECTED (INET_STATE_BOUND | INET_F_ACTIVE)
-#define INET_STATE_LISTENING (INET_STATE_BOUND | INET_F_LISTEN)
-#define INET_STATE_CONNECTING (INET_STATE_BOUND | INET_F_CON)
+#define INET_STATE_CONNECTED (INET_STATE_OPEN | INET_F_ACTIVE)
+#define INET_STATE_LISTENING (INET_STATE_OPEN | INET_F_LISTEN)
+#define INET_STATE_CONNECTING (INET_STATE_OPEN | INET_F_CON)
#define INET_STATE_ACCEPTING (INET_STATE_LISTENING | INET_F_ACC)
#define INET_STATE_MULTI_ACCEPTING (INET_STATE_ACCEPTING | INET_F_MULTI_CLIENT)
#define IS_OPEN(d) \
(((d)->state & INET_F_OPEN) == INET_F_OPEN)
-#define IS_BOUND(d) \
- (((d)->state & INET_F_BOUND) == INET_F_BOUND)
-
#define IS_CONNECTED(d) \
(((d)->state & INET_STATE_CONNECTED) == INET_STATE_CONNECTED)
@@ -1032,19 +920,35 @@ typedef union {
#ifdef HAVE_IN6
struct sockaddr_in6 sai6;
#endif
+#ifdef HAVE_SYS_UN_H
+ struct sockaddr_un sal;
+#endif
} inet_address;
-/* for AF_INET & AF_INET6 */
-#define inet_address_port(x) ((x)->sai.sin_port)
+#define inet_address_port(x) \
+ ((((x)->sai.sin_family == AF_INET) || \
+ ((x)->sai.sin_family == AF_INET6)) ? \
+ ((x)->sai.sin_port) : -1)
+
+#ifdef HAVE_SYS_UN_H
+#define localaddrlen(data) \
+ ((((unsigned char*)(data))[0] == INET_AF_LOCAL) ? \
+ (1 + 1 + ((unsigned char*)(data))[1]) : 1)
+#else
+#define localaddrlen(data) (1)
+#endif
#if defined(HAVE_IN6) && defined(AF_INET6)
-#define addrlen(family) \
- ((family == AF_INET) ? sizeof(struct in_addr) : \
- ((family == AF_INET6) ? sizeof(struct in6_addr) : 0))
+#define addrlen(data) \
+ ((((unsigned char*)(data))[0] == INET_AF_INET) ? \
+ (1 + 2 + 4) : \
+ ((((unsigned char*)(data))[0] == INET_AF_INET6) ? \
+ (1 + 2 + 16) : localaddrlen(data)))
#else
-#define addrlen(family) \
- ((family == AF_INET) ? sizeof(struct in_addr) : 0)
+#define addrlen(data) \
+ ((((unsigned char*)(data))[0] == INET_AF_INET) ? \
+ (1 + 2 + 4) : localaddrlen(data))
#endif
typedef struct _multi_timer_data {
@@ -1135,8 +1039,10 @@ typedef struct {
inet_address peer_addr; /* fake peer address */
inet_address name_addr; /* fake local address */
- inet_address* peer_ptr; /* fake peername or NULL */
- inet_address* name_ptr; /* fake sockname or NULL */
+ inet_address* peer_ptr; /* fake peername or NULL */
+ inet_address* name_ptr; /* fake sockname or NULL */
+ SOCKLEN_T peer_addr_len; /* fake peername size */
+ SOCKLEN_T name_addr_len; /* fake sockname size */
int bufsz; /* minimum buffer constraint */
unsigned int hsz; /* the list header size, -1 is large !!! */
@@ -1168,13 +1074,6 @@ typedef struct {
char *netns; /* Socket network namespace name
as full file path */
#endif
-#ifdef __OSE__
- int select_state; /* state to keep track of whether we
- should trigger another read/write
- request at end of ready_input/output */
- ErlDrvEvent events[6];
-#endif
-
} inet_descriptor;
@@ -1190,10 +1089,8 @@ static void tcp_inet_stop(ErlDrvData);
static void tcp_inet_command(ErlDrvData, char*, ErlDrvSizeT);
static void tcp_inet_commandv(ErlDrvData, ErlIOVec*);
static void tcp_inet_flush(ErlDrvData drv_data);
-#ifndef __OSE__
static void tcp_inet_drv_input(ErlDrvData, ErlDrvEvent);
static void tcp_inet_drv_output(ErlDrvData data, ErlDrvEvent event);
-#endif
static ErlDrvData tcp_inet_start(ErlDrvPort, char* command);
static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData, unsigned int,
char*, ErlDrvSizeT, char**, ErlDrvSizeT);
@@ -1206,71 +1103,6 @@ static void tcp_inet_event(ErlDrvData, ErlDrvEvent);
static void find_dynamic_functions(void);
#endif
-#ifdef __OSE__
-/* The structure of the signal used for requesting asynchronous
- * notification from the stack. Under normal circumstances the network stack
- * shouldn't overwrite the value set in the fd field by the sender
- * of the request */
-struct OseAsyncSig {
- struct FmEvent event;
- int fd;
-};
-
-union SIGNAL {
- SIGSELECT signo;
- struct OseAsyncSig async;
-};
-
-static ErlDrvSSizeT tcp_inet_ctl_ose(ErlDrvData e, unsigned int cmd,
- char* buf, ErlDrvSizeT len,
- char** rbuf, ErlDrvSizeT rsize);
-static void tcp_inet_commandv_ose(ErlDrvData e, ErlIOVec* ev);
-static void tcp_inet_drv_output_ose(ErlDrvData data, ErlDrvEvent event);
-static void tcp_inet_drv_input_ose(ErlDrvData data, ErlDrvEvent event);
-static ErlDrvOseEventId inet_resolve_signal(union SIGNAL *sig);
-
-#ifdef INET_DRV_DEBUG
-
-static char *read_req = "SO_EVENT_READ_REQUEST";
-static char *read_rep = "SO_EVENT_READ_REPLY";
-static char *write_req = "SO_EVENT_WRITE_REQUEST";
-static char *write_rep = "SO_EVENT_WRITE_REPLY";
-static char *eof_req = "SO_EVENT_EOF_REQUEST";
-static char *eof_rep = "SO_EVENT_EOF_REPLY";
-static char *accept_req = "SO_EVENT_ACCEPT_REQUEST";
-static char *accept_rep = "SO_EVENT_ACCEPT_REPLY";
-static char *connect_req = "SO_EVENT_CONNECT_REQUEST";
-static char *connect_rep = "SO_EVENT_CONNECT_REPLY";
-static char *error_req = "SO_EVENT_ERROR_REQUEST";
-static char *error_rep = "SO_EVENT_ERROR_REPLY";
-static char signo_tmp[32];
-
-static char *signo_to_string(SIGSELECT signo) {
- switch (signo) {
- case SO_EVENT_READ_REQUEST: { return read_req; }
- case SO_EVENT_READ_REPLY: { return read_rep; }
- case SO_EVENT_WRITE_REQUEST: { return write_req; }
- case SO_EVENT_WRITE_REPLY: { return write_rep; }
- case SO_EVENT_EOF_REQUEST: { return eof_req; }
- case SO_EVENT_EOF_REPLY: { return eof_rep; }
- case SO_EVENT_ACCEPT_REQUEST: { return accept_req; }
- case SO_EVENT_ACCEPT_REPLY: { return accept_rep; }
- case SO_EVENT_CONNECT_REQUEST: { return connect_req; }
- case SO_EVENT_CONNECT_REPLY: { return connect_rep; }
- case SO_EVENT_ERROR_REQUEST: { return error_req; }
- case SO_EVENT_ERROR_REPLY: { return error_rep; }
- }
-
- snprintf(signo_tmp,32,"0x%x",signo);
-
- return signo_tmp;
-}
-
-#endif
-
-#endif /* __OSE__ */
-
-
static struct erl_drv_entry tcp_inet_driver_entry =
{
tcp_inet_init, /* inet_init will add this driver !! */
@@ -1280,9 +1112,6 @@ static struct erl_drv_entry tcp_inet_driver_entry =
#ifdef __WIN32__
tcp_inet_event,
NULL,
-#elif defined(__OSE__)
- tcp_inet_drv_input_ose, /*ready_input*/
- tcp_inet_drv_output_ose, /*ready_output*/
#else
tcp_inet_drv_input,
tcp_inet_drv_output,
@@ -1290,17 +1119,9 @@ static struct erl_drv_entry tcp_inet_driver_entry =
"tcp_inet",
NULL,
NULL,
-#ifdef __OSE__
- tcp_inet_ctl_ose,
-#else
tcp_inet_ctl,
-#endif
tcp_inet_timeout,
-#ifdef __OSE__
- tcp_inet_commandv_ose,
-#else
tcp_inet_commandv,
-#endif
NULL,
tcp_inet_flush,
NULL,
@@ -1452,14 +1273,6 @@ static int packet_inet_output(udp_descriptor* udesc, HANDLE event);
/* convert descriptor pointer to inet_descriptor pointer */
#define INETP(d) (&(d)->inet)
-#ifdef __OSE__
-static void inet_driver_select(inet_descriptor* desc,
- int flags, int onoff);
-static void tcp_inet_ose_dispatch_signals(tcp_descriptor *desc,
- int prev_select_state,
- union SIGNAL *sig);
-#endif
-
static int async_ref = 0; /* async reference id generator */
#define NEW_ASYNC_ID() ((async_ref++) & 0xffff)
@@ -1475,6 +1288,8 @@ static int async_ref = 0; /* async reference id generator */
} while (0)
static ErlDrvTermData am_ok;
+static ErlDrvTermData am_undefined;
+static ErlDrvTermData am_unspec;
static ErlDrvTermData am_tcp;
static ErlDrvTermData am_error;
static ErlDrvTermData am_einval;
@@ -1492,6 +1307,9 @@ static ErlDrvTermData am_udp;
static ErlDrvTermData am_udp_passive;
static ErlDrvTermData am_udp_error;
#endif
+#ifdef HAVE_SYS_UN_H
+static ErlDrvTermData am_local;
+#endif
#ifdef HAVE_SCTP
static ErlDrvTermData am_sctp;
static ErlDrvTermData am_sctp_passive;
@@ -1513,11 +1331,15 @@ static ErlDrvTermData am_reuseaddr;
static ErlDrvTermData am_dontroute;
static ErlDrvTermData am_priority;
static ErlDrvTermData am_tos;
+static ErlDrvTermData am_tclass;
static ErlDrvTermData am_ipv6_v6only;
static ErlDrvTermData am_netns;
#endif
-/* speical errors for bad ports and sequences */
+static char str_eafnosupport[] = "eafnosupport";
+static char str_einval[] = "einval";
+
+/* special errors for bad ports and sequences */
#define EXBADPORT "exbadport"
#define EXBADSEQ "exbadseq"
@@ -1700,40 +1522,67 @@ static void *realloc_wrapper(void *current, ErlDrvSizeT size){
#endif
#ifdef HAVE_UDP
-static int load_ip_port(ErlDrvTermData* spec, int i, char* buf)
-{
- spec[i++] = ERL_DRV_INT;
- spec[i++] = (ErlDrvTermData) get_int16(buf);
- return i;
-}
-
-static int load_ip_address(ErlDrvTermData* spec, int i, int family, char* buf)
+static int load_address(ErlDrvTermData* spec, int i, char* buf)
{
int n;
- if (family == AF_INET) {
- for (n = 0; n < 4; n++) {
+ switch (*buf++) { /* Family */
+ case INET_AF_INET: {
+ for (n = 2; n < 2+4; n++) {
spec[i++] = ERL_DRV_INT;
spec[i++] = (ErlDrvTermData) ((unsigned char)buf[n]);
}
spec[i++] = ERL_DRV_TUPLE;
spec[i++] = 4;
+ spec[i++] = ERL_DRV_INT;
+ spec[i++] = (ErlDrvTermData) get_int16(buf);
+ break;
}
#if defined(HAVE_IN6) && defined(AF_INET6)
- else if (family == AF_INET6) {
- for (n = 0; n < 16; n += 2) {
+ case INET_AF_INET6: {
+ for (n = 2; n < 2+16; n += 2) {
spec[i++] = ERL_DRV_INT;
spec[i++] = (ErlDrvTermData) get_int16(buf+n);
}
spec[i++] = ERL_DRV_TUPLE;
spec[i++] = 8;
+ spec[i++] = ERL_DRV_INT;
+ spec[i++] = (ErlDrvTermData) get_int16(buf);
+ break;
}
#endif
- else {
+#ifdef HAVE_SYS_UN_H
+ case INET_AF_LOCAL: {
+ int len = *(unsigned char*)buf++;
+ i = LOAD_ATOM(spec, i, am_local);
+ i = LOAD_BUF2BINARY(spec, i, buf, len);
+ spec[i++] = ERL_DRV_TUPLE;
+ spec[i++] = 2;
+ spec[i++] = ERL_DRV_INT;
+ spec[i++] = 0;
+ break;
+ }
+#endif
+ case INET_AF_UNSPEC: {
+ i = LOAD_ATOM(spec, i, am_unspec);
+ i = LOAD_BUF2BINARY(spec, i, buf, 0);
+ spec[i++] = ERL_DRV_TUPLE;
+ spec[i++] = 2;
+ spec[i++] = ERL_DRV_INT;
+ spec[i++] = 0;
+ break;
+ }
+ default: { /* INET_AF_UNDEFINED */
+ i = LOAD_ATOM(spec, i, am_undefined);
+ i = LOAD_BUF2BINARY(spec, i, buf, 0);
spec[i++] = ERL_DRV_TUPLE;
+ spec[i++] = 2;
+ spec[i++] = ERL_DRV_INT;
spec[i++] = 0;
+ break;
+ }
}
return i;
-}
+ }
#endif
@@ -1741,10 +1590,13 @@ static int load_ip_address(ErlDrvTermData* spec, int i, int family, char* buf)
/* For SCTP, we often need to return {IP, Port} tuples: */
static int inet_get_address(char* dst, inet_address* src, unsigned int* len);
-#define LOAD_IP_AND_PORT_CNT \
+/* Max of {{int()*8},int()} | {{int()*4},int()} |
+ * {{'local',binary()},int()}
+ */
+#define LOAD_INET_GET_ADDRESS_CNT \
(8*LOAD_INT_CNT + LOAD_TUPLE_CNT + LOAD_INT_CNT + LOAD_TUPLE_CNT)
-static int load_ip_and_port
+static int load_inet_get_address
(ErlDrvTermData* spec, int i, inet_descriptor* desc,
struct sockaddr_storage* addr)
{
@@ -1762,8 +1614,7 @@ static int load_ip_and_port
/* NB: the following functions are safe to use, as they create tuples
of copied Ints on the "spec", and do not install any String pts --
a ptr to "abuf" would be dangling upon exiting this function: */
- i = load_ip_address(spec, i, desc->sfamily, abuf+3);
- i = load_ip_port (spec, i, abuf+1);
+ i = load_address(spec, i, abuf); /* IP,Port | Family,Addr */
i = LOAD_TUPLE (spec, i, 2);
return i;
}
@@ -2455,7 +2306,6 @@ static ErlDrvTermData am_http_error;
static ErlDrvTermData am_abs_path;
static ErlDrvTermData am_absoluteURI;
static ErlDrvTermData am_star;
-static ErlDrvTermData am_undefined;
static ErlDrvTermData am_http;
static ErlDrvTermData am_https;
static ErlDrvTermData am_scheme;
@@ -3146,7 +2996,7 @@ static int sctp_parse_async_event
ASSERT(sptr->spc_length <= sz); /* No buffer overrun */
i = LOAD_ATOM (spec, i, am_sctp_paddr_change);
- i = load_ip_and_port(spec, i, desc, &sptr->spc_aaddr);
+ i = load_inet_get_address(spec, i, desc, &sptr->spc_aaddr);
switch (sptr->spc_state)
{
@@ -3573,10 +3423,11 @@ static int tcp_error_message(tcp_descriptor* desc, int err)
#ifdef HAVE_UDP
/*
** active mode message:
-** {udp, S, IP, Port, [H1,...Hsz | Data]} or
-** {sctp, S, IP, Port, {[AncilData], Event_or_Data}}
+** {udp, S, IP, Port, [H1,...Hsz | Data]} or
+** {sctp, S, IP, Port, {[AncilData], Event_or_Data}}
** where
** [H1,...,HSz] are msg headers (without IP/Port, UDP only),
+** [AddrLen, H2,...,HSz] are msg headers for UDP AF_UNIX only
** Data : List() | Binary()
*/
static int packet_binary_message
@@ -3586,6 +3437,7 @@ static int packet_binary_message
ErlDrvTermData spec [PACKET_ERL_DRV_TERM_DATA_LEN];
int i = 0;
int alen;
+ char* data = bin->orig_bytes+offs;
DEBUGF(("packet_binary_message(%ld): len = %d\r\n",
(long)desc->port, len));
@@ -3595,13 +3447,12 @@ static int packet_binary_message
i = LOAD_ATOM(spec, i, am_udp ); /* UDP only */
# endif
i = LOAD_PORT(spec, i, desc->dport); /* S */
-
- alen = addrlen(desc->sfamily);
- i = load_ip_address(spec, i, desc->sfamily, bin->orig_bytes+offs+3);
- i = load_ip_port(spec, i, bin->orig_bytes+offs+1); /* IP, Port */
-
- offs += (alen + 3);
- len -= (alen + 3);
+
+ alen = addrlen(data);
+ i = load_address(spec, i, data); /* IP,Port | Family,Addr */
+
+ offs += alen;
+ len -= alen;
# ifdef HAVE_SCTP
if (!IS_SCTP(desc))
@@ -3871,6 +3722,7 @@ static void inet_init_sctp(void) {
INIT_ATOM(dontroute);
INIT_ATOM(priority);
INIT_ATOM(tos);
+ INIT_ATOM(tclass);
INIT_ATOM(ipv6_v6only);
INIT_ATOM(netns);
@@ -3988,6 +3840,8 @@ static int inet_init()
# endif
INIT_ATOM(ok);
+ INIT_ATOM(undefined);
+ INIT_ATOM(unspec);
INIT_ATOM(tcp);
#ifdef HAVE_UDP
INIT_ATOM(udp);
@@ -4005,6 +3859,9 @@ static int inet_init()
INIT_ATOM(udp_passive);
INIT_ATOM(udp_error);
#endif
+#ifdef HAVE_SYS_UN_H
+ INIT_ATOM(local);
+#endif
INIT_ATOM(empty_out_q);
INIT_ATOM(ssl_tls);
@@ -4016,7 +3873,6 @@ static int inet_init()
INIT_ATOM(abs_path);
INIT_ATOM(absoluteURI);
am_star = driver_mk_atom("*");
- INIT_ATOM(undefined);
INIT_ATOM(http);
INIT_ATOM(https);
INIT_ATOM(scheme);
@@ -4112,63 +3968,89 @@ static int inet_init()
/*
-** Set a inaddr structure:
-** src = [P1,P0,X1,X2,.....]
+** Set an inaddr structure:
+** *src = [P1,P0,X1,X2,.....]
** dst points to a structure large enugh to keep any kind
** of inaddr.
** *len is set to length of src on call
** and is set to actual length of dst on return
-** return NULL on error and ptr after port address on success
+** return NULL if ok or ptr to errno string for error
*/
static char* inet_set_address(int family, inet_address* dst,
- char* src, ErlDrvSizeT* len)
+ char* *src, ErlDrvSizeT* len)
{
short port;
- if ((family == AF_INET) && (*len >= 2+4)) {
+ switch (family) {
+ case AF_INET: {
+ if (*len < 2+4) return str_einval;
sys_memzero((char*)dst, sizeof(struct sockaddr_in));
- port = get_int16(src);
+ port = get_int16(*src);
#ifndef NO_SA_LEN
dst->sai.sin_len = sizeof(struct sockaddr_in);
#endif
dst->sai.sin_family = family;
dst->sai.sin_port = sock_htons(port);
- sys_memcpy(&dst->sai.sin_addr, src+2, 4);
+ sys_memcpy(&dst->sai.sin_addr, (*src)+2, 4);
*len = sizeof(struct sockaddr_in);
- return src + 2+4;
+ *src += 2 + 4;
+ return NULL;
}
#if defined(HAVE_IN6) && defined(AF_INET6)
- else if ((family == AF_INET6) && (*len >= 2+16)) {
+ case AF_INET6: {
+ if (*len < 2+16) return str_einval;
sys_memzero((char*)dst, sizeof(struct sockaddr_in6));
- port = get_int16(src);
+ port = get_int16(*src);
#ifndef NO_SA_LEN
dst->sai6.sin6_len = sizeof(struct sockaddr_in6);
#endif
dst->sai6.sin6_family = family;
dst->sai6.sin6_port = sock_htons(port);
dst->sai6.sin6_flowinfo = 0; /* XXX this may be set as well ?? */
- sys_memcpy(&dst->sai6.sin6_addr, src+2, 16);
- *len = sizeof(struct sockaddr_in6);
- return src + 2+16;
+ sys_memcpy(&dst->sai6.sin6_addr, (*src)+2, 16);
+ *len = sizeof(struct sockaddr_in6);
+ *src += 2 + 16;
+ return NULL;
}
#endif
- return NULL;
+#ifdef HAVE_SYS_UN_H
+ case AF_UNIX: {
+ int n;
+ if (*len == 0) return str_einval;
+ n = *((unsigned char*)(*src)); /* Length field */
+ if ((*len < 1+n) || (sizeof(dst->sal.sun_path) < n+1)) {
+ return str_einval;
+ }
+ sys_memzero((char*)dst, sizeof(struct sockaddr_un));
+ dst->sal.sun_family = family;
+ sys_memcpy(dst->sal.sun_path, (*src)+1, n);
+ *len = offsetof(struct sockaddr_un, sun_path) + n;
+ *src += 1 + n;
+ return NULL;
+ }
+#endif
+ }
+ return str_eafnosupport;
}
/*
** Set an inaddr structure, address family comes from source data,
** or from argument if source data specifies constant address.
**
-** src = [TAG,P1,P0] when TAG = INET_AF_ANY | INET_AF_LOOPBACK
-** src = [TAG,P1,P0,X1,X2,...] when TAG = INET_AF_INET | INET_AF_INET6
+** *src = [TAG,P1,P0]
+** when TAG = INET_AF_ANY | INET_AF_LOOPBACK
+** *src = [TAG,P1,P0,X1,X2,...]
+** when TAG = INET_AF_INET | INET_AF_INET6 | INET_AF_LOCAL
+** *src = [TAG,Len,...]
+** when TAG = INET_AF_LOCAL
*/
static char *inet_set_faddress(int family, inet_address* dst,
- char *src, ErlDrvSizeT* len) {
+ char* *src, ErlDrvSizeT* len) {
int tag;
- if (*len < 1) return NULL;
+ if (*len < 1) return str_einval;
(*len) --;
- tag = *(src ++);
+ tag = *((*src) ++);
switch (tag) {
case INET_AF_INET:
family = AF_INET;
@@ -4178,12 +4060,18 @@ static char *inet_set_faddress(int family, inet_address* dst,
family = AF_INET6;
break;
# endif
+# ifdef HAVE_SYS_UN_H
+ case INET_AF_LOCAL: {
+ family = AF_UNIX;
+ break;
+ }
+# endif
case INET_AF_ANY:
case INET_AF_LOOPBACK: {
int port;
- if (*len < 2) return NULL;
- port = get_int16(src);
+ if (*len < 2) return str_einval;
+ port = get_int16(*src);
switch (family) {
case AF_INET: {
struct in_addr addr;
@@ -4195,7 +4083,7 @@ static char *inet_set_faddress(int family, inet_address* dst,
addr.s_addr = sock_htonl(INADDR_LOOPBACK);
break;
default:
- return NULL;
+ return str_einval;
}
sys_memzero((char*)dst, sizeof(struct sockaddr_in));
#ifndef NO_SA_LEN
@@ -4217,7 +4105,7 @@ static char *inet_set_faddress(int family, inet_address* dst,
paddr = &in6addr_loopback;
break;
default:
- return NULL;
+ return str_einval;
}
sys_memzero((char*)dst, sizeof(struct sockaddr_in6));
#ifndef NO_SA_LEN
@@ -4231,17 +4119,17 @@ static char *inet_set_faddress(int family, inet_address* dst,
} break;
# endif
default:
- return NULL;
+ return str_einval;
}
- return src + 2;
+ *src += 2;
+ return NULL;
} break;
default:
- return NULL;
+ return str_eafnosupport;
}
return inet_set_address(family, dst, src, len);
}
-
/* Get a inaddr structure
** src = inaddr structure
** *len is the lenght of structure
@@ -4252,6 +4140,7 @@ static char *inet_set_faddress(int family, inet_address* dst,
*/
static int inet_get_address(char* dst, inet_address* src, unsigned int* len)
{
+ /* Compare the code with inet_address_to_erlang() */
int family;
short port;
@@ -4274,6 +4163,36 @@ static int inet_get_address(char* dst, inet_address* src, unsigned int* len)
return 0;
}
#endif
+#ifdef HAVE_SYS_UN_H
+ else if (family == AF_UNIX) {
+ size_t n, m;
+ if (*len < offsetof(struct sockaddr_un, sun_path)) return -1;
+ n = *len - offsetof(struct sockaddr_un, sun_path);
+ if (255 < n) return -1;
+ /* Portability fix: Assume that the address is a zero terminated
+ * string, except when the first byte is \0 i.e the
+ * string length is 0. Then use the reported length instead.
+ * This fix handles Linux's abstract socket address
+ * nonportable extension.
+ */
+ m = my_strnlen(src->sal.sun_path, n);
+ if ((m == 0) && is_nonzero(src->sal.sun_path, n))
+ m = n;
+ dst[0] = INET_AF_LOCAL;
+ dst[1] = (char) ((unsigned char) m);
+ sys_memcpy(dst+2, src->sal.sun_path, m);
+ *len = 1 + 1 + m;
+ return 0;
+ }
+#endif
+ else if (family == AF_UNSPEC) {
+ dst[0] = INET_AF_UNSPEC;
+ *len = 1;
+ }
+ else {
+ dst[0] = INET_AF_UNDEFINED;
+ *len = 1;
+ }
return -1;
}
@@ -4282,7 +4201,9 @@ static int inet_get_address(char* dst, inet_address* src, unsigned int* len)
** according to the size of the current,
** and return the resulting encoded size
*/
-static int inet_address_to_erlang(char *dst, inet_address **src) {
+static int
+inet_address_to_erlang(char *dst, inet_address **src, SOCKLEN_T sz) {
+ /* Compare the code with inet_get_address() */
short port;
switch ((*src)->sa.sa_family) {
@@ -4307,6 +4228,30 @@ static int inet_address_to_erlang(char *dst, inet_address **src) {
(*src) = (inet_address *) (&(*src)->sai6 + 1);
return 1 + 2 + 16;
#endif
+#ifdef HAVE_SYS_UN_H
+ case AF_UNIX: {
+ size_t n, m;
+ if (sz < offsetof(struct sockaddr_un, sun_path)) return -1;
+ n = sz - offsetof(struct sockaddr_un, sun_path);
+ if (255 < n) return -1;
+ /* Portability fix: Assume that the address is a zero terminated
+ * string, except when the first byte is \0 i.e the
+ * string length is 0. Then use the reported length instead.
+ * This fix handles Linux's abstract socket address
+ * nonportable extension.
+ */
+ m = my_strnlen((*src)->sal.sun_path, n);
+ if ((m == 0) && is_nonzero((*src)->sal.sun_path, n))
+ m = n;
+ if (dst) {
+ dst[0] = INET_AF_LOCAL;
+ dst[1] = (char) ((unsigned char) m);
+ sys_memcpy(dst+2, (*src)->sal.sun_path, m);
+ }
+ (*src) = (inet_address *) (&(*src)->sal + 1);
+ return 1 + 1 + m;
+ }
+#endif
default:
return -1;
}
@@ -4315,7 +4260,7 @@ static int inet_address_to_erlang(char *dst, inet_address **src) {
/* Encode n encoded addresses from addrs in the result buffer
*/
static ErlDrvSizeT reply_inet_addrs
-(int n, inet_address *addrs, char **rbuf, ErlDrvSizeT rsize) {
+(int n, inet_address *addrs, char **rbuf, ErlDrvSizeT rsize, SOCKLEN_T sz) {
inet_address *ia;
int i, s;
ErlDrvSizeT rlen;
@@ -4323,11 +4268,19 @@ static ErlDrvSizeT reply_inet_addrs
if (IS_SOCKET_ERROR(n)) return ctl_error(sock_errno(), rbuf, rsize);
if (n == 0) return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize);
+ /* The sz argument is only used when we have got an actual size
+ * of addrs[0] from e.g getsockname() and then n == 1
+ * so we will loop over 1 element below. Otherwise sz
+ * would be expected to differ between addresses but that
+ * can only happen for AF_UNIX and we will only be called with
+ * n > 1 for SCTP and that will never (?) happen with AF_UNIX
+ */
+
/* Calculate result length */
rlen = 1;
ia = addrs;
for (i = 0; i < n; i++) {
- s = inet_address_to_erlang(NULL, &ia);
+ s = inet_address_to_erlang(NULL, &ia, sz);
if (s < 0) break;
rlen += s;
}
@@ -4338,7 +4291,7 @@ static ErlDrvSizeT reply_inet_addrs
rlen = 1;
ia = addrs;
for (i = 0; i < n; i++) {
- s = inet_address_to_erlang((*rbuf)+rlen, &ia);
+ s = inet_address_to_erlang((*rbuf)+rlen, &ia, sz);
if (s < 0) break;
rlen += s;
}
@@ -4355,16 +4308,6 @@ static void desc_close(inet_descriptor* desc)
desc->forced_events = 0;
desc->send_would_block = 0;
#endif
-#ifdef __OSE__
- if (desc->events[0]) {
- driver_select(desc->port,desc->events[0],FD_READ|FD_WRITE|ERL_DRV_USE,0);
- driver_select(desc->port,desc->events[1],FD_READ|FD_WRITE|ERL_DRV_USE,0);
- driver_select(desc->port,desc->events[2],FD_READ|FD_WRITE|ERL_DRV_USE,0);
- driver_select(desc->port,desc->events[3],FD_READ|FD_WRITE|ERL_DRV_USE,0);
- driver_select(desc->port,desc->events[4],FD_READ|FD_WRITE|ERL_DRV_USE,0);
- driver_select(desc->port,desc->events[5],FD_READ|FD_WRITE|ERL_DRV_USE,0);
- }
-#else
/*
* We should close the fd here, but the other driver might still
* be selecting on it.
@@ -4374,7 +4317,6 @@ static void desc_close(inet_descriptor* desc)
ERL_DRV_USE, 0);
else
inet_stop_select((ErlDrvEvent)(long)desc->event,NULL);
-#endif
desc->event = INVALID_EVENT; /* closed by stop_select callback */
desc->s = INVALID_SOCKET;
desc->event_mask = 0;
@@ -4416,69 +4358,11 @@ static int erl_inet_close(inet_descriptor* desc)
return 0;
}
-#ifdef __OSE__
-static void inet_select_init(inet_descriptor* desc)
-{
- desc->events[0] =
- erl_drv_ose_event_alloc(SO_EVENT_READ_REPLY,
- desc->s,
- inet_resolve_signal,
- NULL);
- driver_select(desc->port, desc->events[0],
- ERL_DRV_READ|ERL_DRV_USE, 1);
-
- desc->events[1] =
- erl_drv_ose_event_alloc(SO_EVENT_EOF_REPLY,
- desc->s,
- inet_resolve_signal,
- NULL);
- driver_select(desc->port, desc->events[1],
- ERL_DRV_READ|ERL_DRV_USE, 1);
-
- desc->events[2] =
- erl_drv_ose_event_alloc(SO_EVENT_ACCEPT_REPLY,
- desc->s,
- inet_resolve_signal,
- NULL);
- driver_select(desc->port, desc->events[2],
- ERL_DRV_READ|ERL_DRV_USE, 1);
-
- /* trigger tcp_inet_input */
- desc->events[3] =
- erl_drv_ose_event_alloc(SO_EVENT_WRITE_REPLY,
- desc->s,
- inet_resolve_signal,
- NULL);
- driver_select(desc->port, desc->events[3],
- ERL_DRV_WRITE|ERL_DRV_USE, 1);
-
- desc->events[4] =
- erl_drv_ose_event_alloc(SO_EVENT_CONNECT_REPLY,
- desc->s,
- inet_resolve_signal,
- NULL);
- driver_select(desc->port, desc->events[4],
- ERL_DRV_WRITE|ERL_DRV_USE, 1);
-
- desc->events[5] =
- erl_drv_ose_event_alloc(SO_EVENT_ERROR_REPLY,
- desc->s,
- inet_resolve_signal,
- NULL);
- driver_select(desc->port, desc->events[5],
- ERL_DRV_WRITE|ERL_DRV_USE, 1);
-
- /* Issue a select on error event before any other select to be sure we are
- prepared to receive error notifications from the stack, even in the
- situations when select isn't issued */
- sock_select(desc, SOCK_FD_ERROR, 1);
-}
-#endif
-
static ErlDrvSSizeT inet_ctl_open(inet_descriptor* desc, int domain, int type,
char** rbuf, ErlDrvSizeT rsize)
{
int save_errno;
+ int protocol;
#ifdef HAVE_SETNS
int current_ns, new_ns;
current_ns = new_ns = 0;
@@ -4517,7 +4401,11 @@ static ErlDrvSSizeT inet_ctl_open(inet_descriptor* desc, int domain, int type,
}
}
#endif
- if ((desc->s = sock_open(domain, type, desc->sprotocol)) == INVALID_SOCKET)
+ protocol = desc->sprotocol;
+#ifdef HAVE_SYS_UN_H
+ if (domain == AF_UNIX) protocol = 0;
+#endif
+ if ((desc->s = sock_open(domain, type, protocol)) == INVALID_SOCKET)
save_errno = sock_errno();
#ifdef HAVE_SETNS
if (desc->netns != NULL) {
@@ -4557,9 +4445,6 @@ static ErlDrvSSizeT inet_ctl_open(inet_descriptor* desc, int domain, int type,
#ifdef __WIN32__
driver_select(desc->port, desc->event, ERL_DRV_READ, 1);
#endif
-#ifdef __OSE__
- inet_select_init(desc);
-#endif
desc->state = INET_STATE_OPEN;
desc->stype = type;
@@ -4574,22 +4459,18 @@ static ErlDrvSSizeT inet_ctl_fdopen(inet_descriptor* desc, int domain, int type,
char** rbuf, ErlDrvSizeT rsize)
{
inet_address name;
- unsigned int sz = sizeof(name);
+ unsigned int sz;
if (bound) {
/* check that it is a socket and that the socket is bound */
+ sz = sizeof(name);
+ sys_memzero((char *) &name, sz);
if (IS_SOCKET_ERROR(sock_name(s, (struct sockaddr*) &name, &sz)))
return ctl_error(sock_errno(), rbuf, rsize);
if (name.sa.sa_family != domain)
return ctl_error(EINVAL, rbuf, rsize);
}
-#ifdef __OSE__
- /* for fdopen duplicating the sd will allow to uniquely identify
- the signal from OSE with erlang port */
- desc->s = sock_dup(s);
-#else
desc->s = s;
-#endif
if ((desc->event = sock_create_event(desc)) == INVALID_EVENT)
return ctl_error(sock_errno(), rbuf, rsize);
@@ -4598,21 +4479,12 @@ static ErlDrvSSizeT inet_ctl_fdopen(inet_descriptor* desc, int domain, int type,
driver_select(desc->port, desc->event, ERL_DRV_READ, 1);
#endif
- if (bound)
- desc->state = INET_STATE_BOUND;
- else
- desc->state = INET_STATE_OPEN;
+ desc->state = INET_STATE_OPEN;
if (type == SOCK_STREAM) { /* check if connected */
sz = sizeof(name);
if (!IS_SOCKET_ERROR(sock_peer(s, (struct sockaddr*) &name, &sz))) {
desc->state = INET_STATE_CONNECTED;
-#ifdef __OSE__
- /* since we are dealing with different descriptors (i.e. inet and
- socket) the select part should be initialized with the right
- values */
- inet_select_init(desc);
-#endif
}
}
@@ -6359,6 +6231,15 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len)
#else
continue;
#endif
+#if defined(IPV6_TCLASS) && defined(SOL_IPV6)
+ case INET_OPT_TCLASS:
+ proto = SOL_IPV6;
+ type = IPV6_TCLASS;
+ propagate = 1;
+ DEBUGF(("inet_set_opts(%ld): s=%d, IPV6_TCLASS=%d\r\n",
+ (long)desc->port, desc->s, ival));
+ break;
+#endif
case TCP_OPT_NODELAY:
proto = IPPROTO_TCP;
@@ -6792,6 +6673,19 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len)
continue; /* Option not supported -- ignore it */
# endif
+# if defined(IPV6_TCLASS) && defined(SOL_IPV6)
+ case INET_OPT_TCLASS:
+ {
+ arg.ival= get_int32 (curr); curr += 4;
+ proto = SOL_IPV6;
+ type = IPV6_TCLASS;
+ arg_ptr = (char*) (&arg.ival);
+ arg_sz = sizeof ( arg.ival);
+ break;
+ }
+# endif
+
+
case INET_OPT_IPV6_V6ONLY:
# if HAVE_DECL_IPV6_V6ONLY
{
@@ -6848,7 +6742,6 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len)
case SCTP_OPT_SET_PEER_PRIMARY_ADDR:
{
ErlDrvSizeT alen;
- char *after;
CHKLEN(curr, ASSOC_ID_LEN);
/* XXX: These 2 opts have isomorphic value data structures,
@@ -6859,12 +6752,9 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len)
/* Fill in "arg.prim.sspp_addr": */
alen = ptr + len - curr;
- after = inet_set_faddress(desc->sfamily,
- (inet_address*) (&arg.prim.sspp_addr),
- curr, &alen);
- if (after == NULL)
- return -1;
- curr = after;
+ if (inet_set_faddress
+ (desc->sfamily, (inet_address*) (&arg.prim.sspp_addr),
+ &curr, &alen) != NULL) return -1;
proto = IPPROTO_SCTP;
if (eopt == SCTP_OPT_PRIMARY_ADDR)
@@ -6890,7 +6780,6 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len)
case SCTP_OPT_PEER_ADDR_PARAMS:
{
ErlDrvSizeT alen;
- char *after;
# ifdef HAVE_STRUCT_SCTP_PADDRPARAMS_SPP_FLAGS
int eflags, cflags, hb_enable, hb_disable,
pmtud_enable, pmtud_disable;
@@ -6905,12 +6794,9 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len)
/* Fill in "pap.spp_address": */
alen = ptr + len - curr;
- after = inet_set_faddress(desc->sfamily,
- (inet_address*) (&arg.pap.spp_address),
- curr, &alen);
- if (after == NULL)
- return -1;
- curr = after;
+ if (inet_set_faddress
+ (desc->sfamily, (inet_address*) (&arg.pap.spp_address),
+ &curr, &alen) != NULL) return -1;
CHKLEN(curr, 4 + 2 + 3*4);
@@ -7301,6 +7187,15 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc,
put_int32(0, ptr);
continue;
#endif
+ case INET_OPT_TCLASS:
+#if defined(IPV6_TCLASS) && defined(SOL_IPV6)
+ proto = SOL_IPV6;
+ type = IPV6_TCLASS;
+ break;
+#else
+ TRUNCATE_TO(0,ptr);
+ continue;
+#endif
case INET_OPT_REUSEADDR:
type = SO_REUSEADDR;
break;
@@ -7433,14 +7328,14 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc,
#ifdef HAVE_SCTP
#define LOAD_PADDRINFO_CNT \
- (2*LOAD_ATOM_CNT + LOAD_ASSOC_ID_CNT + LOAD_IP_AND_PORT_CNT + \
+ (2*LOAD_ATOM_CNT + LOAD_ASSOC_ID_CNT + LOAD_INET_GET_ADDRESS_CNT + \
4*LOAD_INT_CNT + LOAD_TUPLE_CNT)
static int load_paddrinfo (ErlDrvTermData * spec, int i,
inet_descriptor* desc, struct sctp_paddrinfo* pai)
{
i = LOAD_ATOM (spec, i, am_sctp_paddrinfo);
i = LOAD_ASSOC_ID (spec, i, pai->spinfo_assoc_id);
- i = load_ip_and_port(spec, i, desc, &pai->spinfo_address);
+ i = load_inet_get_address(spec, i, desc, &pai->spinfo_address);
switch(pai->spinfo_state)
{
case SCTP_ACTIVE:
@@ -7695,6 +7590,7 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc,
case INET_OPT_DONTROUTE:
case INET_OPT_PRIORITY :
case INET_OPT_TOS :
+ case INET_OPT_TCLASS :
case INET_OPT_IPV6_V6ONLY:
case SCTP_OPT_AUTOCLOSE:
case SCTP_OPT_MAXSEG :
@@ -7768,6 +7664,19 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc,
continue;
# endif
}
+ case INET_OPT_TCLASS:
+ {
+# if defined(IPV6_TCLASS) && defined(SOL_IPV6)
+ proto = SOL_IPV6;
+ type = IPV6_TCLASS;
+ is_int = 1;
+ tag = am_tclass;
+ break;
+# else
+ /* Not supported -- ignore */
+ continue;
+# endif
+ }
case INET_OPT_IPV6_V6ONLY:
# if HAVE_DECL_IPV6_V6ONLY
{
@@ -7856,7 +7765,7 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc,
/* Fill in the response: */
PLACE_FOR(spec, i,
2*LOAD_ATOM_CNT + LOAD_ASSOC_ID_CNT +
- LOAD_IP_AND_PORT_CNT + 2*LOAD_TUPLE_CNT);
+ LOAD_INET_GET_ADDRESS_CNT + 2*LOAD_TUPLE_CNT);
switch (eopt) {
case SCTP_OPT_PRIMARY_ADDR:
i = LOAD_ATOM(spec, i, am_sctp_primary_addr);
@@ -7870,7 +7779,7 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc,
ASSERT(0);
}
i = LOAD_ASSOC_ID (spec, i, sp.sspp_assoc_id);
- i = load_ip_and_port(spec, i, desc, &sp.sspp_addr);
+ i = load_inet_get_address(spec, i, desc, &sp.sspp_addr);
i = LOAD_TUPLE (spec, i, 3);
i = LOAD_TUPLE (spec, i, 2);
break;
@@ -7897,7 +7806,7 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc,
struct sctp_paddrparams ap;
unsigned int sz = sizeof(ap);
int n;
- char *after;
+ char *before, *xerror;
ErlDrvSizeT alen;
if (buflen < ASSOC_ID_LEN) RETURN_ERROR(spec, -EINVAL);
@@ -7905,23 +7814,32 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc,
buf += ASSOC_ID_LEN;
buflen -= ASSOC_ID_LEN;
alen = buflen;
- after = inet_set_faddress(desc->sfamily,
- (inet_address*) (&ap.spp_address),
- buf, &alen);
- if (after == NULL) RETURN_ERROR(spec, -EINVAL);
- buflen -= after - buf;
- buf = after;
-
+ before = buf;
+ xerror =
+ inet_set_faddress
+ (desc->sfamily, (inet_address*) (&ap.spp_address),
+ &buf, &alen);
+ if (xerror != NULL) {
+#ifdef EAFNOSUPPORT
+ if (xerror == str_eafnosupport) {
+ RETURN_ERROR(spec, -EAFNOSUPPORT);
+ }
+#else
+ RETURN_ERROR(spec, -EINVAL);
+#endif
+ }
+ buflen -= buf - before;
+
if (sock_getopt(desc->s, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS,
&ap, &sz) < 0) continue;
/* Fill in the response: */
PLACE_FOR(spec, i,
2*LOAD_ATOM_CNT + LOAD_ASSOC_ID_CNT +
- LOAD_IP_AND_PORT_CNT + 4*LOAD_INT_CNT);
+ LOAD_INET_GET_ADDRESS_CNT + 4*LOAD_INT_CNT);
i = LOAD_ATOM (spec, i, am_sctp_peer_addr_params);
i = LOAD_ATOM (spec, i, am_sctp_paddrparams);
i = LOAD_ASSOC_ID (spec, i, ap.spp_assoc_id);
- i = load_ip_and_port(spec, i, desc, &ap.spp_address);
+ i = load_inet_get_address(spec, i, desc, &ap.spp_address);
i = LOAD_INT (spec, i, ap.spp_hbinterval);
i = LOAD_INT (spec, i, ap.spp_pathmaxrxt);
@@ -8137,7 +8055,7 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc,
{
struct sctp_paddrinfo pai;
unsigned int sz = sizeof(pai);
- char *after;
+ char *before, *xerror;
ErlDrvSizeT alen;
if (buflen < ASSOC_ID_LEN) RETURN_ERROR(spec, -EINVAL);
@@ -8145,13 +8063,22 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc,
buf += ASSOC_ID_LEN;
buflen -= ASSOC_ID_LEN;
alen = buflen;
- after = inet_set_faddress(desc->sfamily,
- (inet_address*) (&pai.spinfo_address),
- buf, &alen);
- if (after == NULL) RETURN_ERROR(spec, -EINVAL);
- buflen -= after - buf;
- buf = after;
-
+ before = buf;
+ xerror =
+ inet_set_faddress
+ (desc->sfamily, (inet_address*) (&pai.spinfo_address),
+ &buf, &alen);
+ if (xerror != NULL) {
+#ifdef EAFNOSUPPORT
+ if (xerror == str_eafnosupport) {
+ RETURN_ERROR(spec, -EAFNOSUPPORT);
+ }
+#else
+ RETURN_ERROR(spec, -EINVAL);
+#endif
+ }
+ buflen -= buf - before;
+
if (sock_getopt(desc->s, IPPROTO_SCTP, SCTP_GET_PEER_ADDR_INFO,
&pai, &sz) < 0) continue;
/* Fill in the response: */
@@ -8414,15 +8341,6 @@ static ErlDrvData inet_start(ErlDrvPort port, int size, int protocol)
#ifdef HAVE_SETNS
desc->netns = NULL;
#endif
-#ifdef __OSE__
- desc->select_state = 0;
- desc->events[0] = NULL;
- desc->events[1] = NULL;
- desc->events[2] = NULL;
- desc->events[3] = NULL;
- desc->events[4] = NULL;
- desc->events[5] = NULL;
-#endif
return (ErlDrvData)desc;
}
@@ -8571,6 +8489,11 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
put_int32(INET_AF_INET6, &tbuf[0]);
}
#endif
+#ifdef HAVE_SYS_UN_H
+ else if (desc->sfamily == AF_UNIX) {
+ put_int32(INET_AF_LOCAL, &tbuf[0]);
+ }
+#endif
else
return ctl_error(EINVAL, rbuf, rsize);
@@ -8619,7 +8542,6 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
if (len != 4) return ctl_error(EINVAL, rbuf, rsize);
if (! IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize);
- if (! IS_BOUND(desc)) return ctl_xerror(EXBADSEQ, rbuf, rsize);
#ifdef HAVE_SCTP
if (IS_SCTP(desc) && p_sctp_getpaddrs) {
@@ -8630,19 +8552,19 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
assoc_id = get_int32(buf);
n = p_sctp_getpaddrs(desc->s, assoc_id, &sa);
- rlen = reply_inet_addrs(n, (inet_address *) sa, rbuf, rsize);
+ rlen = reply_inet_addrs(n, (inet_address *) sa, rbuf, rsize, 0);
if (n > 0) p_sctp_freepaddrs(sa);
return rlen;
}
#endif
{ /* Fallback to sock_peer */
inet_address addr;
- unsigned int sz;
+ SOCKLEN_T sz;
int i;
sz = sizeof(addr);
i = sock_peer(desc->s, (struct sockaddr *) &addr, &sz);
- return reply_inet_addrs(i >= 0 ? 1 : i, &addr, rbuf, rsize);
+ return reply_inet_addrs(i >= 0 ? 1 : i, &addr, rbuf, rsize, sz);
}
}
@@ -8650,15 +8572,21 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
char tbuf[sizeof(inet_address)];
inet_address peer;
inet_address* ptr;
- unsigned int sz = sizeof(peer);
+ unsigned int sz;
DEBUGF(("inet_ctl(%ld): PEER\r\n", (long)desc->port));
if (!(desc->state & INET_F_ACTIVE))
return ctl_error(ENOTCONN, rbuf, rsize);
- if ((ptr = desc->peer_ptr) == NULL) {
+ if ((ptr = desc->peer_ptr) != NULL) {
+ sz = desc->peer_addr_len;
+ }
+ else {
ptr = &peer;
- if (IS_SOCKET_ERROR(sock_peer(desc->s, (struct sockaddr*)ptr,&sz)))
+ sz = sizeof(peer);
+ if (IS_SOCKET_ERROR
+ (sock_peer
+ (desc->s, (struct sockaddr*)ptr, &sz)))
return ctl_error(sock_errno(), rbuf, rsize);
}
if (inet_get_address(tbuf, ptr, &sz) < 0)
@@ -8667,17 +8595,19 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
}
case INET_REQ_SETPEER: { /* set fake peername Port Address */
+ char *xerror;
if (len == 0) {
desc->peer_ptr = NULL;
return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize);
}
else if (len < 2)
return ctl_error(EINVAL, rbuf, rsize);
- else if (inet_set_address(desc->sfamily, &desc->peer_addr,
- buf, &len) == NULL)
- return ctl_error(EINVAL, rbuf, rsize);
+ else if ((xerror = inet_set_faddress
+ (desc->sfamily, &desc->peer_addr, &buf, &len)) != NULL)
+ return ctl_xerror(xerror, rbuf, rsize);
else {
desc->peer_ptr = &desc->peer_addr;
+ desc->peer_addr_len = (SOCKLEN_T) len;
return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize);
}
}
@@ -8688,7 +8618,6 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
if (len != 4) return ctl_error(EINVAL, rbuf, rsize);
if (! IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize);
- if (! IS_BOUND(desc)) return ctl_xerror(EXBADSEQ, rbuf, rsize);
#ifdef HAVE_SCTP
if (IS_SCTP(desc) && p_sctp_getladdrs) {
@@ -8699,19 +8628,20 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
assoc_id = get_int32(buf);
n = p_sctp_getladdrs(desc->s, assoc_id, &sa);
- rlen = reply_inet_addrs(n, (inet_address *) sa, rbuf, rsize);
+ rlen = reply_inet_addrs(n, (inet_address *) sa, rbuf, rsize, 0);
if (n > 0) p_sctp_freeladdrs(sa);
return rlen;
}
#endif
{ /* Fallback to sock_name */
inet_address addr;
- unsigned int sz;
+ SOCKLEN_T sz;
int i;
sz = sizeof(addr);
+ sys_memzero((char *) &addr, sz);
i = sock_name(desc->s, (struct sockaddr *) &addr, &sz);
- return reply_inet_addrs(i >= 0 ? 1 : i, &addr, rbuf, rsize);
+ return reply_inet_addrs(i >= 0 ? 1 : i, &addr, rbuf, rsize, sz);
}
}
@@ -8719,16 +8649,19 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
char tbuf[sizeof(inet_address)];
inet_address name;
inet_address* ptr;
- unsigned int sz = sizeof(name);
+ unsigned int sz;
DEBUGF(("inet_ctl(%ld): NAME\r\n", (long)desc->port));
- if (!IS_BOUND(desc))
- return ctl_error(EINVAL, rbuf, rsize); /* address is not valid */
-
- if ((ptr = desc->name_ptr) == NULL) {
+ if ((ptr = desc->name_ptr) != NULL) {
+ sz = desc->name_addr_len;
+ }
+ else {
ptr = &name;
- if (IS_SOCKET_ERROR(sock_name(desc->s, (struct sockaddr*)ptr, &sz)))
+ sz = sizeof(name);
+ sys_memzero((char *) &name, sz);
+ if (IS_SOCKET_ERROR
+ (sock_name(desc->s, (struct sockaddr*)ptr, &sz)))
return ctl_error(sock_errno(), rbuf, rsize);
}
if (inet_get_address(tbuf, ptr, &sz) < 0)
@@ -8736,26 +8669,28 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
return ctl_reply(INET_REP_OK, tbuf, sz, rbuf, rsize);
}
- case INET_REQ_SETNAME: { /* set fake peername Port Address */
+ case INET_REQ_SETNAME: { /* set fake sockname Port Address */
+ char *xerror;
if (len == 0) {
desc->name_ptr = NULL;
return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize);
}
else if (len < 2)
return ctl_error(EINVAL, rbuf, rsize);
- else if (inet_set_address(desc->sfamily, &desc->name_addr,
- buf, &len) == NULL)
- return ctl_error(EINVAL, rbuf, rsize);
+ else if ((xerror = inet_set_faddress
+ (desc->sfamily, &desc->name_addr, &buf, &len)) != NULL)
+ return ctl_xerror(xerror, rbuf, rsize);
else {
desc->name_ptr = &desc->name_addr;
+ desc->name_addr_len = (SOCKLEN_T) len;
return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize);
}
}
case INET_REQ_BIND: { /* bind socket */
- char tbuf[2];
+ char tbuf[2], *xerror;
inet_address local;
- short port;
+ int port;
DEBUGF(("inet_ctl(%ld): BIND\r\n", (long)desc->port));
@@ -8764,21 +8699,24 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
if (desc->state != INET_STATE_OPEN)
return ctl_xerror(EXBADPORT, rbuf, rsize);
- if (inet_set_faddress(desc->sfamily, &local, buf, &len) == NULL)
- return ctl_error(EINVAL, rbuf, rsize);
+ if ((xerror = inet_set_faddress
+ (desc->sfamily, &local, &buf, &len)) != NULL)
+ return ctl_xerror(xerror, rbuf, rsize);
if (IS_SOCKET_ERROR(sock_bind(desc->s,(struct sockaddr*) &local, len)))
return ctl_error(sock_errno(), rbuf, rsize);
- desc->state = INET_STATE_BOUND;
+ desc->state = INET_STATE_OPEN;
- if ((port = inet_address_port(&local)) == 0) {
+ port = inet_address_port(&local);
+ if (port == 0) {
SOCKLEN_T adrlen = sizeof(local);
+ sys_memzero((char *) &local, adrlen);
sock_name(desc->s, &local.sa, &adrlen);
port = inet_address_port(&local);
}
- port = sock_ntohs(port);
- put_int16(port, tbuf);
+ else if (port == -1) port = 0;
+ put_int16(sock_ntohs((Uint16) port), tbuf);
return ctl_reply(INET_REP_OK, tbuf, 2, rbuf, rsize);
}
@@ -9149,10 +9087,6 @@ static tcp_descriptor* tcp_inet_copy(tcp_descriptor* desc,SOCKET s,
copy_desc->inet.port = port;
copy_desc->inet.dport = driver_mk_port(port);
-#ifdef __OSE__
- inet_select_init(&copy_desc->inet);
-#endif
-
*err = 0;
return copy_desc;
}
@@ -9214,23 +9148,6 @@ static void tcp_inet_stop(ErlDrvData e)
inet_stop(INETP(desc));
}
-#ifdef __OSE__
-
-static ErlDrvSSizeT tcp_inet_ctl_ose(ErlDrvData e, unsigned int cmd,
- char* buf, ErlDrvSizeT len,
- char** rbuf, ErlDrvSizeT rsize) {
-
- tcp_descriptor* desc = (tcp_descriptor*)e;
- int prev_select_state = INETP(desc)->select_state;
-
- ErlDrvSSizeT res = tcp_inet_ctl(e,cmd,buf,len,rbuf,rsize);
-
- tcp_inet_ose_dispatch_signals((tcp_descriptor*)e,prev_select_state,NULL);
-
- return res;
-}
-#endif
-
/* TCP requests from Erlang */
static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd,
char* buf, ErlDrvSizeT len,
@@ -9251,13 +9168,14 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd,
case INET_AF_INET6:
domain = AF_INET6;
break;
-#else
- case INET_AF_INET6:
- return ctl_xerror("eafnosupport", rbuf, rsize);
+#endif
+#ifdef HAVE_SYS_UN_H
+ case INET_AF_LOCAL:
+ domain = AF_UNIX;
break;
#endif
default:
- return ctl_error(EINVAL, rbuf, rsize);
+ return ctl_xerror(str_eafnosupport, rbuf, rsize);
}
if (buf[1] != INET_TYPE_STREAM) return ctl_error(EINVAL, rbuf, rsize);
return inet_ctl_open(INETP(desc), domain, SOCK_STREAM, rbuf, rsize);
@@ -9277,13 +9195,14 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd,
case INET_AF_INET6:
domain = AF_INET6;
break;
-#else
- case INET_AF_INET6:
- return ctl_xerror("eafnosupport", rbuf, rsize);
+#endif
+#ifdef HAVE_SYS_UN_H
+ case INET_AF_LOCAL:
+ domain = AF_UNIX;
break;
#endif
default:
- return ctl_error(EINVAL, rbuf, rsize);
+ return ctl_xerror(str_eafnosupport, rbuf, rsize);
}
if (buf[1] != INET_TYPE_STREAM) return ctl_error(EINVAL, rbuf, rsize);
@@ -9304,8 +9223,6 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd,
return ctl_xerror(EXBADPORT, rbuf, rsize);
if (!IS_OPEN(INETP(desc)))
return ctl_xerror(EXBADPORT, rbuf, rsize);
- if (!IS_BOUND(INETP(desc)))
- return ctl_xerror(EXBADSEQ, rbuf, rsize);
if (len != 2)
return ctl_error(EINVAL, rbuf, rsize);
backlog = get_int16(buf);
@@ -9318,7 +9235,7 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd,
case INET_REQ_CONNECT: { /* do async connect */
int code;
- char tbuf[2];
+ char tbuf[2], *xerror;
unsigned timeout;
DEBUGF(("tcp_inet_ctl(%ld): CONNECT\r\n", (long)desc->inet.port));
@@ -9328,8 +9245,6 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd,
return ctl_xerror(EXBADPORT, rbuf, rsize);
if (IS_CONNECTED(INETP(desc)))
return ctl_error(EISCONN, rbuf, rsize);
- if (!IS_BOUND(INETP(desc)))
- return ctl_xerror(EXBADSEQ, rbuf, rsize);
if (IS_CONNECTING(INETP(desc)))
return ctl_error(EINVAL, rbuf, rsize);
if (len < 6)
@@ -9337,9 +9252,9 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd,
timeout = get_int32(buf);
buf += 4;
len -= 4;
- if (inet_set_address(desc->inet.sfamily, &desc->inet.remote,
- buf, &len) == NULL)
- return ctl_error(EINVAL, rbuf, rsize);
+ if ((xerror = inet_set_faddress
+ (desc->inet.sfamily, &desc->inet.remote, &buf, &len)) != NULL)
+ return ctl_xerror(xerror, rbuf, rsize);
code = sock_connect(desc->inet.s,
(struct sockaddr*) &desc->inet.remote, len);
@@ -9430,6 +9345,7 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd,
return ctl_reply(INET_REP_OK, tbuf, 2, rbuf, rsize);
} else {
n = sizeof(desc->inet.remote);
+ sys_memzero((char *) &remote, n);
s = sock_accept(desc->inet.s, (struct sockaddr*) &remote, &n);
if (s == INVALID_SOCKET) {
if (sock_errno() == ERRNO_BLOCK) {
@@ -9672,17 +9588,6 @@ static void tcp_inet_command(ErlDrvData e, char *buf, ErlDrvSizeT len)
DEBUGF(("tcp_inet_command(%ld) }\r\n", (long)desc->inet.port));
}
-#ifdef __OSE__
-
-static void tcp_inet_commandv_ose(ErlDrvData e, ErlIOVec* ev) {
- int prev_select_state = INETP((tcp_descriptor*)e)->select_state;
- tcp_inet_commandv(e, ev);
- tcp_inet_ose_dispatch_signals((tcp_descriptor*)e,prev_select_state,NULL);
-}
-
-#endif
-
-
static void tcp_inet_commandv(ErlDrvData e, ErlIOVec* ev)
{
tcp_descriptor* desc = (tcp_descriptor*)e;
@@ -9755,22 +9660,6 @@ static void inet_stop_select(ErlDrvEvent event, void* _)
{
#ifdef __WIN32__
WSACloseEvent((HANDLE)event);
-#elif defined(__OSE__)
- ErlDrvOseEventId id;
- union SIGNAL *sig;
- erl_drv_ose_event_fetch(event, NULL, &id,NULL);
- DEBUGF(("inet_stop_select(?#?) {s=%d\n",id));
- sock_close((int)id);
- /* On socket close all the signals waiting to be processed as part of the
- select should be deallocated */
- while((sig = erl_drv_ose_get_signal(event))) {
- DEBUGF(("inet_stop_select(?#?): Freeing signal %s\n",
- signo_to_string(sig->signo)));
- free_buf(&sig);
- }
- erl_drv_ose_event_free(event);
- DEBUGF(("inet_stop_select(?#?) }\n"));
-
#else
sock_close((SOCKET)(long)event);
#endif
@@ -10319,146 +10208,7 @@ static void tcp_inet_event(ErlDrvData e, ErlDrvEvent event)
return;
}
-#elif defined(__OSE__) /* !__WIN32__ */
-/* The specific resolve signal function. It will return the socket descriptor
- for which the select was issued */
-static ErlDrvOseEventId inet_resolve_signal(union SIGNAL *sig) {
- DEBUGF(("%s(?#?): s=%d got signal %s, status = %d, extra = %d, sender = 0x%x\n",
- __FUNCTION__,sig->async.fd,signo_to_string(sig->signo),
- sig->async.event.status,
- sig->async.event.extra,sender(&sig)));
- if (sig->signo == SO_EVENT_READ_REPLY ||
- sig->signo == SO_EVENT_ACCEPT_REPLY ||
- sig->signo == SO_EVENT_EOF_REPLY ||
- sig->signo == SO_EVENT_WRITE_REPLY ||
- sig->signo == SO_EVENT_ERROR_REPLY ||
- sig->signo == SO_EVENT_CONNECT_REPLY ) {
- return sig->async.fd;
- }
-
- return -1;
-}
-
-static void inet_driver_select(inet_descriptor* desc,
- int flags, int onoff) {
- ASSERT(!desc->is_ignored);
-
- if(onoff) {
- desc->select_state |= flags;
- } else {
- desc->select_state &= ~flags;
- }
-}
-
-static ssize_t writev_fallback(int fd, const struct iovec *iov, int iovcnt, int max_sz)
-{
- size_t data_len = 0;
- size_t sent = 0;
- ssize_t n;
- int i;
-
- for(i = 0; i < iovcnt; i++)
- {
- data_len = iov[i].iov_len;
-tryagain:
- n = sock_send(fd, iov[i].iov_base, data_len, 0);
- if (IS_SOCKET_ERROR(n)) {
- /* If buffer length is greater than the amount stack is able to
- * send out then try to send at least max_sz (this comes with
- * SO_EVENT_WRITE_REPLY signal*/
- if ((errno == EMSGSIZE) && (max_sz > 0) && (data_len > max_sz)) {
- data_len = max_sz;
- goto tryagain;
- }
- break;
- }
- sent += n;
- }
- return sent;
-}
-
-#define OSE_EVENT_REQ(TCP_DESC,EVENT) do { \
- union SIGNAL *sig = alloc(sizeof(struct OseAsyncSig), EVENT); \
- sig->async.fd = INETP(TCP_DESC)->s; \
- ose_request_event(INETP(TCP_DESC)->s, &sig, 1); \
- DEBUGF(("%s(%ld): s=%d sent %s\r\n",__FUNCTION__, \
- INETP(TCP_DESC)->port,INETP(TCP_DESC)->s,signo_to_string(EVENT))); \
- } while(0)
-
-static void tcp_inet_ose_dispatch_signals(tcp_descriptor *desc,
- int prev_select_state,
- union SIGNAL *sig) {
- if (sig) {
- DEBUGF(("tcp_inet_ose_dispatch_signals(%ld) {s=%d resend\r\n",
- (long)INETP(desc)->port,INETP(desc)->s));
- /* We are reacting to a signal, which means that if
- the select_state for that signal is still activated
- we should send a new signal */
- switch (sig->signo) {
- case SO_EVENT_READ_REPLY: {
- if (INETP(desc)->select_state & FD_READ)
- OSE_EVENT_REQ(desc,SO_EVENT_READ_REQUEST);
- break;
- }
- case SO_EVENT_WRITE_REPLY: {
- if (INETP(desc)->select_state & FD_WRITE)
- OSE_EVENT_REQ(desc,SO_EVENT_WRITE_REQUEST);
- break;
- }
- case SO_EVENT_CONNECT_REPLY: {
- if (INETP(desc)->select_state & FD_CONNECT)
- OSE_EVENT_REQ(desc,SO_EVENT_CONNECT_REQUEST);
- break;
- }
- case SO_EVENT_ACCEPT_REPLY: {
- if (INETP(desc)->select_state & FD_ACCEPT)
- OSE_EVENT_REQ(desc,SO_EVENT_ACCEPT_REQUEST);
- break;
- }
- case SO_EVENT_ERROR_REPLY: {
- if (INETP(desc)->select_state & SOCK_FD_ERROR)
- OSE_EVENT_REQ(desc,SO_EVENT_ERROR_REQUEST);
- break;
- }
-
- }
- DEBUGF(("tcp_inet_ose_dispatch_signals(%ld) }\r\n",
- (long)INETP(desc)->port));
- }
-
- if (INETP(desc)->select_state != prev_select_state) {
- /* If the select state has changed we have to issue signals for
- the state parts that have changed. */
- int xor_select_state = INETP(desc)->select_state ^ prev_select_state;
- DEBUGF(("tcp_inet_ose_dispatch_signals(%ld) {s=%d select change\r\n",
- (long)INETP(desc)->port,INETP(desc)->s));
- if ((xor_select_state & FD_READ) &&
- (INETP(desc)->select_state & FD_READ)) {
- OSE_EVENT_REQ(desc,SO_EVENT_READ_REQUEST);
- }
- if ((xor_select_state & FD_WRITE) &&
- (INETP(desc)->select_state & FD_WRITE)) {
- OSE_EVENT_REQ(desc,SO_EVENT_WRITE_REQUEST);
- }
- if ((xor_select_state & FD_CONNECT) &&
- (INETP(desc)->select_state & FD_CONNECT)) {
- OSE_EVENT_REQ(desc,SO_EVENT_CONNECT_REQUEST);
- }
- if ((xor_select_state & FD_ACCEPT) &&
- (INETP(desc)->select_state & FD_ACCEPT)) {
- OSE_EVENT_REQ(desc,SO_EVENT_ACCEPT_REQUEST);
- }
- if ((xor_select_state & SOCK_FD_ERROR) &&
- (INETP(desc)->select_state & SOCK_FD_ERROR)) {
- OSE_EVENT_REQ(desc,SO_EVENT_ERROR_REQUEST);
- }
-
- DEBUGF(("tcp_inet_ose_dispatch_signals(%ld) }\r\n",
- (long)INETP(desc)->port));
- }
-}
-
-#endif /* __OSE__ */
+#endif /* __WIN32__ */
/* socket has input:
@@ -10481,6 +10231,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event)
inet_async_op *this_op = desc->inet.opt;
len = sizeof(desc->inet.remote);
+ sys_memzero((char *) &remote, len);
s = sock_accept(desc->inet.s, (struct sockaddr*) &remote, &len);
if (s == INVALID_SOCKET && sock_errno() == ERRNO_BLOCK) {
/* Just try again, no real error, just a ghost trigger from poll,
@@ -10547,6 +10298,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event)
while (desc->inet.state == INET_STATE_MULTI_ACCEPTING) {
len = sizeof(desc->inet.remote);
+ sys_memzero((char *) &remote, len);
s = sock_accept(desc->inet.s, (struct sockaddr*) &remote, &len);
if (s == INVALID_SOCKET && sock_errno() == ERRNO_BLOCK) {
/* Just try again, no real error, keep the last return code */
@@ -10634,6 +10386,9 @@ static int tcp_send_or_shutdown_error(tcp_descriptor* desc, int err)
set_busy_port(desc->inet.port, 0);
}
+ tcp_clear_output(desc);
+ tcp_clear_input(desc);
+
/*
* We used to handle "expected errors" differently from unexpected ones.
* Now we handle all errors in the same way (unless the show_econnreset
@@ -10656,8 +10411,6 @@ static int tcp_send_or_shutdown_error(tcp_descriptor* desc, int err)
else
desc_close(INETP(desc));
} else {
- tcp_clear_output(desc);
- tcp_clear_input(desc);
tcp_close_check(desc);
erl_inet_close(INETP(desc));
@@ -10945,49 +10698,6 @@ static void tcp_shutdown_async(tcp_descriptor* desc)
desc->tcp_add_flags |= TCP_ADDF_SHUTDOWN_WR_DONE;
}
-#ifdef __OSE__
-
-static void tcp_inet_drv_output_ose(ErlDrvData data, ErlDrvEvent event)
-{
- union SIGNAL *event_sig = erl_drv_ose_get_signal(event);
-
- while (event_sig) {
- int prev_select_state = INETP((tcp_descriptor*)data)->select_state;
- int res = tcp_inet_output((tcp_descriptor*)data, (HANDLE)event_sig);
- if (res != -1) {
- tcp_inet_ose_dispatch_signals((tcp_descriptor*)data,
- prev_select_state,event_sig);
- free_buf(&event_sig);
- event_sig = erl_drv_ose_get_signal(event);
- } else {
- /* NOTE: here the event object could have been deallocated!!!!
- inet_stop_select is called when doing driver_select(ERL_DRV_USE,0)
- */
- free_buf(&event_sig);
- return;
- }
- }
-}
-
-static void tcp_inet_drv_input_ose(ErlDrvData data, ErlDrvEvent event)
-{
- union SIGNAL *event_sig = erl_drv_ose_get_signal(event);
-
- while (event_sig) {
- int prev_select_state = INETP((tcp_descriptor*)data)->select_state;
- int res = tcp_inet_input((tcp_descriptor*)data, (HANDLE)event);
- if (res != -1) {
- tcp_inet_ose_dispatch_signals((tcp_descriptor*)data, prev_select_state,
- event_sig);
- free_buf(&event_sig);
- event_sig = erl_drv_ose_get_signal(event);
- } else {
- free_buf(&event_sig);
- return;
- }
- }
-}
-#else
static void tcp_inet_drv_output(ErlDrvData data, ErlDrvEvent event)
{
(void)tcp_inet_output((tcp_descriptor*)data, (HANDLE)event);
@@ -10997,7 +10707,6 @@ static void tcp_inet_drv_input(ErlDrvData data, ErlDrvEvent event)
{
(void)tcp_inet_input((tcp_descriptor*)data, (HANDLE)event);
}
-#endif
/* socket ready for ouput:
** 1. INET_STATE_CONNECTING => non block connect ?
@@ -11031,7 +10740,7 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event)
(struct sockaddr*) &desc->inet.remote, &sz);
if (IS_SOCKET_ERROR(code)) {
- desc->inet.state = INET_STATE_BOUND; /* restore state */
+ desc->inet.state = INET_STATE_OPEN; /* restore state */
ret = async_error(INETP(desc), sock_errno());
goto done;
}
@@ -11044,7 +10753,7 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event)
(void *)&error, &sz);
if ((code < 0) || error) {
- desc->inet.state = INET_STATE_BOUND; /* restore state */
+ desc->inet.state = INET_STATE_OPEN; /* restore state */
ret = async_error(INETP(desc), error);
goto done;
}
@@ -11063,13 +10772,6 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event)
ssize_t n;
SysIOVec* iov;
-#ifdef __OSE__
- /* For large size buffers case the amount of data that the stack is
- able to send out (received in the .extra field) should be passed
- down to writev_fallback */
- n = event ? ((union SIGNAL*)event)->async.event.extra : 0;
-#endif
-
if ((iov = driver_peekq(ix, &vsize)) == NULL) {
sock_select(INETP(desc), FD_WRITE, 0);
send_empty_out_q_msgs(INETP(desc));
@@ -11097,12 +10799,6 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event)
sizes > (max 32 bit signed int) */
size_t howmuch = 0x7FFFFFFF; /* max signed 32 bit */
int x;
-#ifdef __OSE__
- /* For EWOULDBLOCK sock_sendv returns 0 so we have to be sure it
- wasn't the case */
- if(sock_errno() == ERRNO_BLOCK)
- goto done;
-#endif
for(x = 0; x < vsize && iov[x].iov_len == 0; ++x)
;
if (x < vsize) {
@@ -11339,13 +11035,12 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf,
case INET_AF_INET: af = AF_INET; break;
#if defined(HAVE_IN6) && defined(AF_INET6)
case INET_AF_INET6: af = AF_INET6; break;
-#else
- case INET_AF_INET6:
- return ctl_xerror("eafnosupport", rbuf, rsize);
- break;
+#endif
+#ifdef HAVE_SYS_UN_H
+ case INET_AF_LOCAL: af = AF_UNIX; break;
#endif
default:
- return ctl_error(EINVAL, rbuf, rsize);
+ return ctl_xerror(str_eafnosupport, rbuf, rsize);
}
switch (buf[1]) {
case INET_TYPE_STREAM: type = SOCK_STREAM; break;
@@ -11389,13 +11084,12 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf,
case INET_AF_INET: af = AF_INET; break;
#if defined(HAVE_IN6) && defined(AF_INET6)
case INET_AF_INET6: af = AF_INET6; break;
-#else
- case INET_AF_INET6:
- return ctl_xerror("eafnosupport", rbuf, rsize);
- break;
+#endif
+#ifdef HAVE_SYS_UN_H
+ case INET_AF_LOCAL: af = AF_UNIX; break;
#endif
default:
- return ctl_error(EINVAL, rbuf, rsize);
+ return ctl_xerror(str_eafnosupport, rbuf, rsize);
}
switch (buf[1]) {
case INET_TYPE_STREAM: type = SOCK_STREAM; break;
@@ -11460,11 +11154,10 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf,
if (!IS_OPEN(desc))
return ctl_xerror(EXBADPORT, rbuf, rsize);
- if (!IS_BOUND(desc))
- return ctl_xerror(EXBADSEQ, rbuf, rsize);
#ifdef HAVE_SCTP
if (IS_SCTP(desc)) {
inet_address remote;
+ char *xerror;
if (IS_CONNECTING(desc))
return ctl_error(EINVAL, rbuf, rsize);
@@ -11476,8 +11169,9 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf,
/* For SCTP, we do not set the peer's addr in desc->remote, as
multiple peers are possible: */
- if (inet_set_address(desc->sfamily, &remote, buf, &len) == NULL)
- return ctl_error(EINVAL, rbuf, rsize);
+ if ((xerror = inet_set_faddress
+ (desc->sfamily, &remote, &buf, &len)) != NULL)
+ return ctl_xerror(xerror, rbuf, rsize);
sock_select(desc, FD_CONNECT, 1);
code = sock_connect(desc->s, &remote.sa, len);
@@ -11513,12 +11207,13 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf,
else if (len < 6)
return ctl_error(EINVAL, rbuf, rsize);
else {
+ char *xerror;
/* Ignore timeout */
buf += 4;
len -= 4;
- if (inet_set_address(desc->sfamily,
- &desc->remote, buf, &len) == NULL)
- return ctl_error(EINVAL, rbuf, rsize);
+ if ((xerror = inet_set_faddress
+ (desc->sfamily, &desc->remote, &buf, &len)) != NULL)
+ return ctl_xerror(xerror, rbuf, rsize);
code = sock_connect(desc->s,
(struct sockaddr*) &desc->remote, len);
@@ -11548,8 +11243,6 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf,
return ctl_xerror(EXBADPORT, rbuf, rsize);
if (!IS_OPEN(desc))
return ctl_xerror(EXBADPORT, rbuf, rsize);
- if (!IS_BOUND(desc))
- return ctl_xerror(EXBADSEQ, rbuf, rsize);
if (len != 2)
return ctl_error(EINVAL, rbuf, rsize);
@@ -11584,11 +11277,13 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf,
while (curr < buf+len)
{
+ char *xerror;
/* List item format: see "inet_set_faddress": */
ErlDrvSizeT alen = buf + len - curr;
- curr = inet_set_faddress(desc->sfamily, &addr, curr, &alen);
- if (curr == NULL)
- return ctl_error(EINVAL, rbuf, rsize);
+ xerror = inet_set_faddress
+ (desc->sfamily, &addr, &curr, &alen);
+ if (xerror != NULL)
+ return ctl_xerror(xerror, rbuf, rsize);
/* Invoke the call: */
if (p_sctp_bindx(desc->s, (struct sockaddr *)&addr, 1,
@@ -11596,7 +11291,7 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf,
return ctl_error(sock_errno(), rbuf, rsize);
}
- desc->state = INET_STATE_BOUND;
+ desc->state = INET_STATE_OPEN;
return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize);
}
@@ -11613,8 +11308,6 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf,
return ctl_xerror(EXBADPORT, rbuf, rsize);
if (!IS_OPEN(desc))
return ctl_xerror(EXBADPORT, rbuf, rsize);
- if (!IS_BOUND(desc))
- return ctl_xerror(EXBADSEQ, rbuf, rsize);
if (! p_sctp_peeloff)
return ctl_error(ENOTSUP, rbuf, rsize);
@@ -11655,8 +11348,6 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf,
/* INPUT: Timeout(4), Length(4) */
if (!IS_OPEN(desc))
return ctl_xerror(EXBADPORT, rbuf, rsize);
- if (!IS_BOUND(desc))
- return ctl_error(EINVAL, rbuf, rsize);
if (desc->active || (len != 8))
return ctl_error(EINVAL, rbuf, rsize);
timeout = get_int32(buf);
@@ -11697,12 +11388,12 @@ static void packet_inet_timeout(ErlDrvData e)
/* THIS IS A "send*" REQUEST; on the Erlang side: "port_command".
-** input should be: P1 P0 Address buffer .
+** input should be: Family Address buffer .
** For UDP, buffer (after Address) is just data to be sent.
** For SCTP, buffer contains a list representing 2 items:
** (1) 6 parms for sctp_sndrcvinfo, as in sctp_get_sendparams();
** (2) 0+ real data bytes.
-** There is no destination address -- SCTYP send is performed over
+** There is no destination address -- SCTP send is performed over
** an existing association, using "sctp_sndrcvinfo" specified.
*/
static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len)
@@ -11711,6 +11402,7 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len)
inet_descriptor* desc = INETP(udesc);
char* ptr = buf;
char* qtr;
+ char* xerror;
ErlDrvSizeT sz;
int code;
inet_address other;
@@ -11721,10 +11413,6 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len)
inet_reply_error(desc, EINVAL);
return;
}
- if (!IS_BOUND(desc)) {
- inet_reply_error(desc, EINVAL);
- return;
- }
#ifdef HAVE_SCTP
if (IS_SCTP(desc))
@@ -11768,6 +11456,7 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len)
VALGRIND_MAKE_MEM_DEFINED(mhdr.msg_control, mhdr.msg_controllen); /*suppress "uninitialised bytes"*/
mhdr.msg_flags = 0; /* Not used with "sendmsg" */
+ inet_output_count(desc, data_len);
/* Now do the actual sending. NB: "flags" in "sendmsg" itself are NOT
used: */
code = sock_sendmsg(desc->s, &mhdr, 0);
@@ -11777,9 +11466,10 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len)
/* UDP socket. Even if it is connected, there is an address prefix
here -- ignored for connected sockets: */
sz = len;
- qtr = inet_set_address(desc->sfamily, &other, ptr, &sz);
- if (qtr == NULL) {
- inet_reply_error(desc, EINVAL);
+ qtr = ptr;
+ xerror = inet_set_faddress(desc->sfamily, &other, &qtr, &sz);
+ if (xerror != NULL) {
+ inet_reply_error_am(desc, driver_mk_atom(xerror));
return;
}
len -= (qtr - ptr);
@@ -11855,6 +11545,8 @@ static int packet_inet_input(udp_descriptor* udesc, HANDLE event)
while(packet_count--) {
unsigned int len = sizeof(other);
+ sys_memzero((char *) &other, sizeof(other));
+
/* udesc->i_buf is only kept between SCTP fragments */
if (udesc->i_buf == NULL) {
udesc->i_bufsz = desc->bufsz + len;
@@ -12050,7 +11742,7 @@ static int packet_inet_output(udp_descriptor* udesc, HANDLE event)
(struct sockaddr*) &desc->remote, &sz);
if (IS_SOCKET_ERROR(code)) {
- desc->state = INET_STATE_BOUND; /* restore state */
+ desc->state = INET_STATE_OPEN; /* restore state */
ret = async_error(desc, sock_errno());
goto done;
}
@@ -12063,7 +11755,7 @@ static int packet_inet_output(udp_descriptor* udesc, HANDLE event)
(void *)&error, &sz);
if ((code < 0) || error) {
- desc->state = INET_STATE_BOUND; /* restore state */
+ desc->state = INET_STATE_OPEN; /* restore state */
ret = async_error(desc, error);
goto done;
}
@@ -12363,7 +12055,7 @@ void erts_sock_close(erts_sock_t socket)
int erts_sock_connect(erts_sock_t socket, byte *ip_addr, int len, Uint16 port)
{
SOCKET s = (SOCKET) socket;
- char buf[2 + 4];
+ char buf[2 + 4], *p;
ErlDrvSizeT blen = 6;
inet_address addr;
@@ -12373,12 +12065,12 @@ int erts_sock_connect(erts_sock_t socket, byte *ip_addr, int len, Uint16 port)
put_int16(port, buf);
memcpy((void *) (buf + 2), (void *) ip_addr, 4);
- if (!inet_set_address(AF_INET, &addr, buf, &blen))
+ p = buf;
+ if (inet_set_address(AF_INET, &addr, &p, &blen) != NULL)
return 0;
- if (IS_SOCKET_ERROR(sock_connect(s,
- (struct sockaddr *) &addr,
- sizeof(struct sockaddr_in))))
+ if (IS_SOCKET_ERROR
+ (sock_connect(s, (struct sockaddr *) &addr, blen)))
return 0;
return 1;
}
diff --git a/erts/emulator/drivers/common/ram_file_drv.c b/erts/emulator/drivers/common/ram_file_drv.c
index 9cb44d0b7e..bcdfe6a186 100644
--- a/erts/emulator/drivers/common/ram_file_drv.c
+++ b/erts/emulator/drivers/common/ram_file_drv.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/drivers/common/zlib_drv.c b/erts/emulator/drivers/common/zlib_drv.c
index 364048174c..440ba956d8 100644
--- a/erts/emulator/drivers/common/zlib_drv.c
+++ b/erts/emulator/drivers/common/zlib_drv.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2003-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/drivers/ose/ose_efile.c b/erts/emulator/drivers/ose/ose_efile.c
deleted file mode 100644
index c8337a95d5..0000000000
--- a/erts/emulator/drivers/ose/ose_efile.c
+++ /dev/null
@@ -1,1125 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1997-2012. 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%
- */
-/*
- * Purpose: Provides file and directory operations for OSE.
- */
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-#if defined(HAVE_POSIX_FALLOCATE) && !defined(__sun) && !defined(__sun__)
-#define _XOPEN_SOURCE 600
-#endif
-#if !defined(_GNU_SOURCE) && defined(HAVE_LINUX_FALLOC_H)
-#define _GNU_SOURCE
-#endif
-#include "sys.h"
-#include "erl_driver.h"
-#include "erl_efile.h"
-#if defined(DARWIN) || defined(HAVE_LINUX_FALLOC_H) || defined(HAVE_POSIX_FALLOCATE)
-#include "fcntl.h"
-#endif
-#include "ose.h"
-#include "unistd.h"
-#include "sys/stat.h"
-#include "dirent.h"
-#include "sys/time.h"
-#include "time.h"
-#include "assert.h"
-
-/* Find a definition of MAXIOV, that is used in the code later. */
-#if defined IOV_MAX
-#define MAXIOV IOV_MAX
-#elif defined UIO_MAXIOV
-#define MAXIOV UIO_MAXIOV
-#else
-#define MAXIOV 16
-#endif
-
-/*
- * Macros for testing file types.
- */
-
-#define ISDIR(st) (((st).st_mode & S_IFMT) == S_IFDIR)
-#define ISREG(st) (((st).st_mode & S_IFMT) == S_IFREG)
-#define ISDEV(st) \
- (((st).st_mode&S_IFMT) == S_IFCHR || ((st).st_mode&S_IFMT) == S_IFBLK)
-#define ISLNK(st) (((st).st_mode & S_IFLNK) == S_IFLNK)
-#ifdef NO_UMASK
-#define FILE_MODE 0644
-#define DIR_MODE 0755
-#else
-#define FILE_MODE 0666
-#define DIR_MODE 0777
-#endif
-
-#define IS_DOT_OR_DOTDOT(s) \
- (s[0] == '.' && (s[1] == '\0' || (s[1] == '.' && s[2] == '\0')))
-
-/*
- * Macros for handling local file descriptors
- * and mutexes.
- *
- * Handling of files like this is necessary because OSE
- * does not allow seeking after the end of a file. So
- * what we do it emulate this by keeping track of the size
- * of the file and where the file's positions is. If a
- * write happens after eof then we pad it.
- *
- * Given time this should be rewritten to get rid of the
- * mutex and use the port lock to protect the data. This
- * could be done be done by adapting the efile api for some
- * calls to allow some meta-data to be associated with the
- * open file.
- */
-
-#define L_FD_IS_VALID(fd_data) ((fd_data)->beyond_eof > 0)
-#define L_FD_INVALIDATE(fd_data) (fd_data)->beyond_eof = 0
-#define L_FD_CUR(fd_data) (fd_data)->pos
-#define L_FD_OFFS_BEYOND_EOF(fd_data, offs) \
- (((fd_data)->size > offs) ? 0 : 1)
-
-#define L_FD_FAIL -1
-#define L_FD_SUCCESS 1
-#define L_FD_PAD_SIZE 255
-
-struct fd_meta {
- ErlDrvMutex *meta_mtx;
- struct fd_data *fd_data_list;
-};
-
-struct fd_data {
- int fd;
- struct fd_data *next;
- struct fd_data *prev;
- int pos;
- int beyond_eof;
- size_t size;
-#ifdef DEBUG
- PROCESS owner;
-#endif
-};
-
-static int l_invalidate_local_fd(int fd);
-static int l_pad_file(struct fd_data *fd_data, off_t offset);
-static int check_error(int result, Efile_error* errInfo);
-static struct fd_data* l_new_fd(void);
-static int l_remove_local_fd(int fd);
-static struct fd_data* l_find_local_fd(int fd);
-static int l_update_local_fd(int fd, int pos, int size);
-
-static struct fd_meta* fdm = NULL;
-
-
-/***************************************************************************/
-
-static int
-l_remove_local_fd(int fd)
-{
- struct fd_data *fd_data;
- fd_data = l_find_local_fd(fd);
-
- if (fd_data == NULL) {
- return L_FD_FAIL;
- }
-#ifdef DEBUG
- assert(fd_data->owner == current_process());
-#endif
- erl_drv_mutex_lock(fdm->meta_mtx);
- /* head ? */
- if (fd_data == fdm->fd_data_list) {
- if (fd_data->next != NULL) {
- /* remove link to head */
- fd_data->next->prev = NULL;
- /* set new head */
- fdm->fd_data_list = fd_data->next;
- }
- else {
- /* head is lonely */
- fdm->fd_data_list = NULL;
- }
- }
- else { /* not head */
- if (fd_data->prev == NULL) {
- erl_drv_mutex_unlock(fdm->meta_mtx);
- return L_FD_FAIL;
- }
- else {
- if (fd_data->next != NULL) {
- fd_data->next->prev = fd_data->prev;
- fd_data->prev->next = fd_data->next;
- }
- else {
- fd_data->prev->next = NULL;
- }
- }
- }
-
- /* scramble values */
- fd_data->beyond_eof = -1;
- fd_data->next = NULL;
- fd_data->prev = NULL;
- fd_data->fd = -1;
-
- /* unlock and clean */
- driver_free(fd_data);
- erl_drv_mutex_unlock(fdm->meta_mtx);
-
- return L_FD_SUCCESS;
-}
-
-/***************************************************************************/
-
-static int
-l_invalidate_local_fd(int fd) {
- struct fd_data *fd_data;
-
- if ((fd_data = l_find_local_fd(fd)) == NULL) {
- return L_FD_FAIL;
- }
-
- fd_data->beyond_eof = 0;
- return L_FD_SUCCESS;
-}
-
-/****************************************************************************/
-
-static struct fd_data*
-l_find_local_fd(int fd) {
- struct fd_data *fd_data;
-
- fd_data = NULL;
- erl_drv_mutex_lock(fdm->meta_mtx);
- for (fd_data = fdm->fd_data_list; fd_data != NULL; ) {
- if (fd_data->fd == fd) {
-#ifdef DEBUG
- assert(fd_data->owner == current_process());
-#endif
- break;
- }
- fd_data = fd_data->next;
- }
- erl_drv_mutex_unlock(fdm->meta_mtx);
- return fd_data;
-}
-
-/***************************************************************************/
-
-static struct fd_data*
-l_new_fd(void) {
- struct fd_data *fd_data;
-
- fd_data = driver_alloc(sizeof(struct fd_data));
- if (fd_data == NULL) {
- return NULL;
- }
- erl_drv_mutex_lock(fdm->meta_mtx);
- if (fdm->fd_data_list == NULL) {
- fdm->fd_data_list = fd_data;
- fdm->fd_data_list->prev = NULL;
- fdm->fd_data_list->next = NULL;
- }
- else {
- fd_data->next = fdm->fd_data_list;
- fdm->fd_data_list = fd_data;
- fdm->fd_data_list->prev = NULL;
- }
-#ifdef DEBUG
- fd_data->owner = current_process();
-#endif
- erl_drv_mutex_unlock(fdm->meta_mtx);
- return fd_data;
-}
-
-/***************************************************************************/
-
-static int
-l_update_local_fd(int fd, int pos, int size) {
- struct fd_data *fd_data = NULL;
-
- fd_data = l_find_local_fd(fd);
- /* new fd to handle? */
- if (fd_data == NULL) {
- fd_data = l_new_fd();
- if (fd_data == NULL) {
- /* out of memory */
- return L_FD_FAIL;
- }
- }
- fd_data->size = size;
- fd_data->pos = pos;
- fd_data->fd = fd;
- fd_data->beyond_eof = 1;
-
- return L_FD_SUCCESS;
-}
-
-/***************************************************************************/
-
-static int
-l_pad_file(struct fd_data *fd_data, off_t offset) {
- int size_dif;
- int written = 0;
- int ret_val = L_FD_SUCCESS;
- char padding[L_FD_PAD_SIZE];
-
- size_dif = (offset - fd_data->size);
- memset(&padding, '\0', L_FD_PAD_SIZE);
-
- while (size_dif > 0) {
- written = write(fd_data->fd, padding,
- (size_dif < L_FD_PAD_SIZE) ?
- size_dif : L_FD_PAD_SIZE);
- if (written < 0 && errno != EINTR && errno != EAGAIN) {
- ret_val = -1;
- break;
- }
- size_dif -= written;
- }
- L_FD_INVALIDATE(fd_data);
- return ret_val;
-}
-
-/***************************************************************************/
-
-static int
-check_error(int result, Efile_error *errInfo) {
- if (result < 0) {
- errInfo->posix_errno = errInfo->os_errno = errno;
- return 0;
- }
- return 1;
-}
-
-/***************************************************************************/
-
-int
-efile_init() {
- fdm = driver_alloc(sizeof(struct fd_meta));
- if (fdm == NULL) {
- return L_FD_FAIL;
- }
- fdm->meta_mtx = erl_drv_mutex_create("ose_efile local fd mutex\n");
- erl_drv_mutex_lock(fdm->meta_mtx);
- fdm->fd_data_list = NULL;
- erl_drv_mutex_unlock(fdm->meta_mtx);
- return L_FD_SUCCESS;
-}
-
-/***************************************************************************/
-
-int
-efile_mkdir(Efile_error* errInfo, /* Where to return error codes. */
- char* name) /* Name of directory to create. */
-{
-#ifdef NO_MKDIR_MODE
- return check_error(mkdir(name), errInfo);
-#else
- int res = mkdir(name, DIR_MODE);
- if (res < 0 && errno == EINVAL) {
- errno = ENOENT;
- }
- return check_error(res, errInfo);
-#endif
-}
-
-/***************************************************************************/
-
-int
-efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */
- char* name) /* Name of directory to delete. */
-{
- if (rmdir(name) == 0) {
- return 1;
- }
- if (errno == ENOTEMPTY) {
- errno = EEXIST;
- }
- if (errno == EEXIST || errno == EINVAL) {
- int saved_errno = errno;
- struct stat file_stat;
- struct stat cwd_stat;
-
- if(stat(name, &file_stat) != 0) {
- errno = ENOENT;
- return check_error(-1, errInfo);
- }
- /*
- * The error code might be wrong if this is the current directory.
- */
- if (stat(name, &file_stat) == 0 && stat(".", &cwd_stat) == 0 &&
- file_stat.st_ino == cwd_stat.st_ino &&
- file_stat.st_dev == cwd_stat.st_dev) {
- saved_errno = EACCES;
- }
- errno = saved_errno;
- }
- return check_error(-1, errInfo);
-}
-
-/***************************************************************************/
-
-int
-efile_delete_file(Efile_error* errInfo, /* Where to return error codes. */
- char* name) /* Name of file to delete. */
-{
- struct stat statbuf;
-
- if (stat(name, &statbuf) >= 0) {
- /* Do not let unlink() remove directories */
- if (ISDIR(statbuf)) {
- errno = EPERM;
- return check_error(-1, errInfo);
- }
-
- if (unlink(name) == 0) {
- return 1;
- }
-
- if (errno == EISDIR) {
- errno = EPERM;
- return check_error(-1, errInfo);
- }
- }
- else {
- if (errno == EINVAL) {
- errno = ENOENT;
- return check_error(-1, errInfo);
- }
- }
- return check_error(-1, errInfo);
-}
-
-/*
- *---------------------------------------------------------------------------
- *
- * Changes the name of an existing file or directory, from src to dst.
- * If src and dst refer to the same file or directory, does nothing
- * and returns success. Otherwise if dst already exists, it will be
- * deleted and replaced by src subject to the following conditions:
- * If src is a directory, dst may be an empty directory.
- * If src is a file, dst may be a file.
- * In any other situation where dst already exists, the rename will
- * fail.
- *
- * Results:
- * If the directory was successfully created, returns 1.
- * Otherwise the return value is 0 and errno is set to
- * indicate the error. Some possible values for errno are:
- *
- * EACCES: src or dst parent directory can't be read and/or written.
- * EEXIST: dst is a non-empty directory.
- * EINVAL: src is a root directory or dst is a subdirectory of src.
- * EISDIR: dst is a directory, but src is not.
- * ENOENT: src doesn't exist, or src or dst is "".
- * ENOTDIR: src is a directory, but dst is not.
- * EXDEV: src and dst are on different filesystems.
- *
- * Side effects:
- * The implementation of rename may allow cross-filesystem renames,
- * but the caller should be prepared to emulate it with copy and
- * delete if errno is EXDEV.
- *
- *---------------------------------------------------------------------------
- */
-
-int
-efile_rename(Efile_error* errInfo, /* Where to return error codes. */
- char* src, /* Original name. */
- char* dst) /* New name. */
-{
-
- /* temporary fix AFM does not recognize ./<file name>
- * in destination remove pending on adaption of AFM fix
- */
-
- char *dot_str;
- if (dst != NULL) {
- dot_str = strchr(dst, '.');
- if (dot_str && dot_str == dst && dot_str[1] == '/') {
- dst = dst+2;
- }
- }
-
- if (rename(src, dst) == 0) {
- return 1;
- }
- if (errno == ENOTEMPTY) {
- errno = EEXIST;
- }
- if (errno == EINVAL) {
- struct stat file_stat;
-
- if (stat(dst, &file_stat)== 0) {
- if (ISDIR(file_stat)) {
- errno = EISDIR;
- }
- else if (ISREG(file_stat)) {
- errno = ENOTDIR;
- }
- else {
- errno = EINVAL;
- }
- }
- else {
- errno = EINVAL;
- }
- }
-
- if (strcmp(src, "/") == 0) {
- errno = EINVAL;
- }
- return check_error(-1, errInfo);
-}
-
-/***************************************************************************/
-
-int
-efile_chdir(Efile_error* errInfo, /* Where to return error codes. */
- char* name) /* Name of directory to make current. */
-{
- return check_error(chdir(name), errInfo);
-}
-
-/***************************************************************************/
-
-int
-efile_getdcwd(Efile_error* errInfo, /* Where to return error codes. */
- int drive, /* 0 - current, 1 - A, 2 - B etc. */
- char* buffer, /* Where to return the current
- directory. */
- size_t size) /* Size of buffer. */
-{
- if (drive == 0) {
- if (getcwd(buffer, size) == NULL)
- return check_error(-1, errInfo);
-
- return 1;
- }
-
- /*
- * Drives other than 0 is not supported on Unix.
- */
-
- errno = ENOTSUP;
- return check_error(-1, errInfo);
-}
-
-/***************************************************************************/
-
-int
-efile_readdir(Efile_error* errInfo, /* Where to return error codes. */
- char* name, /* Name of directory to open. */
- EFILE_DIR_HANDLE* p_dir_handle, /* Pointer to directory
- handle of
- open directory.*/
- char* buffer, /* Pointer to buffer for
- one filename. */
- size_t *size) /* in-out Size of buffer, length
- of name. */
-{
- DIR *dp; /* Pointer to directory structure. */
- struct dirent* dirp; /* Pointer to directory entry. */
-
- /*
- * If this is the first call, we must open the directory.
- */
-
- if (*p_dir_handle == NULL) {
- dp = opendir(name);
- if (dp == NULL)
- return check_error(-1, errInfo);
- *p_dir_handle = (EFILE_DIR_HANDLE) dp;
- }
-
- /*
- * Retrieve the name of the next file using the directory handle.
- */
-
- dp = *((DIR **)((void *)p_dir_handle));
- for (;;) {
- dirp = readdir(dp);
- if (dirp == NULL) {
- closedir(dp);
- return 0;
- }
- if (IS_DOT_OR_DOTDOT(dirp->d_name))
- continue;
- buffer[0] = '\0';
- strncat(buffer, dirp->d_name, (*size)-1);
- *size = strlen(dirp->d_name);
- return 1;
- }
-}
-
-/***************************************************************************/
-
-int
-efile_openfile(Efile_error* errInfo, /* Where to return error codes. */
- char* name, /* Name of directory to open. */
- int flags, /* Flags to user for opening. */
- int* pfd, /* Where to store the file
- descriptor. */
- Sint64 *pSize) /* Where to store the size of the
- file. */
-{
- struct stat statbuf;
- int fd;
- int mode; /* Open mode. */
-
- if (stat(name, &statbuf) >= 0 && !ISREG(statbuf)) {
- errno = EISDIR;
- return check_error(-1, errInfo);
- }
-
- switch (flags & (EFILE_MODE_READ|EFILE_MODE_WRITE)) {
- case EFILE_MODE_READ:
- mode = O_RDONLY;
- break;
- case EFILE_MODE_WRITE:
- if (flags & EFILE_NO_TRUNCATE)
- mode = O_WRONLY | O_CREAT;
- else
- mode = O_WRONLY | O_CREAT | O_TRUNC;
- break;
- case EFILE_MODE_READ_WRITE:
- mode = O_RDWR | O_CREAT;
- break;
- default:
- errno = EINVAL;
- return check_error(-1, errInfo);
- }
-
-
- if (flags & EFILE_MODE_APPEND) {
- mode &= ~O_TRUNC;
- mode |= O_APPEND;
- }
-
- if (flags & EFILE_MODE_EXCL) {
- mode |= O_EXCL;
- }
-
- fd = open(name, mode, FILE_MODE);
-
- if (!check_error(fd, errInfo))
- return 0;
-
- *pfd = fd;
- if (pSize) {
- *pSize = statbuf.st_size;
- }
- return 1;
-}
-
-/***************************************************************************/
-
-int
-efile_may_openfile(Efile_error* errInfo, char *name) {
- struct stat statbuf; /* Information about the file */
- int result;
-
- result = stat(name, &statbuf);
- if (!check_error(result, errInfo))
- return 0;
- if (!ISREG(statbuf)) {
- errno = EISDIR;
- return check_error(-1, errInfo);
- }
- return 1;
-}
-
-/***************************************************************************/
-
-void
-efile_closefile(int fd)
-{
- if (l_find_local_fd(fd) != NULL) {
- l_remove_local_fd(fd);
- }
- close(fd);
-}
-
-/***************************************************************************/
-
-int
-efile_fdatasync(Efile_error *errInfo, /* Where to return error codes. */
- int fd) /* File descriptor for file to sync data. */
-{
- return efile_fsync(errInfo, fd);
-}
-
-/***************************************************************************/
-
-int
-efile_fsync(Efile_error *errInfo, /* Where to return error codes. */
- int fd) /* File descriptor for file to sync. */
-{
- return check_error(fsync(fd), errInfo);
-}
-
-/***************************************************************************/
-
-int
-efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo,
- char* name, int info_for_link)
-{
- struct stat statbuf; /* Information about the file */
- int result;
-
- result = stat(name, &statbuf);
- if (!check_error(result, errInfo)) {
- return 0;
- }
-
-#if SIZEOF_OFF_T == 4
- pInfo->size_high = 0;
-#else
- pInfo->size_high = (Uint32)(statbuf.st_size >> 32);
-#endif
- pInfo->size_low = (Uint32)statbuf.st_size;
-
-#ifdef NO_ACCESS
- /* Just look at read/write access for owner. */
-
- pInfo->access = ((statbuf.st_mode >> 6) & 07) >> 1;
-
-#else
- pInfo->access = FA_NONE;
- if (access(name, R_OK) == 0)
- pInfo->access |= FA_READ;
- if (access(name, W_OK) == 0)
- pInfo->access |= FA_WRITE;
-
-#endif
-
- if (ISDEV(statbuf))
- pInfo->type = FT_DEVICE;
- else if (ISDIR(statbuf))
- pInfo->type = FT_DIRECTORY;
- else if (ISREG(statbuf))
- pInfo->type = FT_REGULAR;
- else if (ISLNK(statbuf))
- pInfo->type = FT_SYMLINK;
- else
- pInfo->type = FT_OTHER;
-
- pInfo->accessTime = statbuf.st_atime;
- pInfo->modifyTime = statbuf.st_mtime;
- pInfo->cTime = statbuf.st_ctime;
-
- pInfo->mode = statbuf.st_mode;
- pInfo->links = statbuf.st_nlink;
- pInfo->major_device = statbuf.st_dev;
- pInfo->inode = statbuf.st_ino;
- pInfo->uid = statbuf.st_uid;
- pInfo->gid = statbuf.st_gid;
-
- return 1;
-}
-
-/***************************************************************************/
-
-int
-efile_write_info(Efile_error *errInfo, Efile_info *pInfo, char *name)
-{
- /*
- * On some systems chown will always fail for a non-root user unless
- * POSIX_CHOWN_RESTRICTED is not set. Others will succeed as long as
- * you don't try to chown a file to someone besides youself.
- */
- if (pInfo->mode != -1) {
- mode_t newMode = pInfo->mode & (S_ISUID | S_ISGID |
- S_IRWXU | S_IRWXG | S_IRWXO);
- if (chmod(name, newMode)) {
- newMode &= ~(S_ISUID | S_ISGID);
- if (chmod(name, newMode)) {
- return check_error(-1, errInfo);
- }
- }
- }
-
- return 1;
-}
-
-/***************************************************************************/
-
-int
-efile_write(Efile_error* errInfo, /* Where to return error codes. */
- int flags, /* Flags given when file was
- opened. */
- int fd, /* File descriptor to write to. */
- char* buf, /* Buffer to write. */
- size_t count) /* Number of bytes to write. */
-{
- ssize_t written; /* Bytes written in last operation. */
- struct fd_data *fd_data;
-
- if ((fd_data = l_find_local_fd(fd)) != NULL) {
- if (L_FD_IS_VALID(fd_data)) {
- /* we are beyond eof and need to pad*/
- if (l_pad_file(fd_data, L_FD_CUR(fd_data)) < 0) {
- return check_error(-1, errInfo);
- }
- }
- }
-
- while (count > 0) {
- if ((written = write(fd, buf, count)) < 0) {
- if (errno != EINTR) {
- return check_error(-1, errInfo);
- }
- else {
- written = 0;
- }
- }
- ASSERT(written <= count);
- buf += written;
- count -= written;
- }
- return 1;
-}
-
-/***************************************************************************/
-
-int
-efile_writev(Efile_error* errInfo, /* Where to return error codes */
- int flags, /* Flags given when file was
- * opened */
- int fd, /* File descriptor to write to */
- SysIOVec* iov, /* Vector of buffer structs.
- * The structs may be changed i.e.
- * due to incomplete writes */
- int iovcnt) /* Number of structs in vector */
-{
- struct fd_data *fd_data;
- int cnt = 0; /* Buffers so far written */
-
- ASSERT(iovcnt >= 0);
- if ((fd_data = l_find_local_fd(fd)) != NULL) {
- if (L_FD_IS_VALID(fd_data)) {
- /* we are beyond eof and need to pad*/
- if (l_pad_file(fd_data, L_FD_CUR(fd_data)) < 0) {
- return check_error(-1, errInfo);
- }
- }
- }
- while (cnt < iovcnt) {
- if ((! iov[cnt].iov_base) || (iov[cnt].iov_len <= 0)) {
- /* Empty buffer - skip */
- cnt++;
- }
- else { /* Non-empty buffer */
- ssize_t w; /* Bytes written in this call */
- do {
- w = write(fd, iov[cnt].iov_base, iov[cnt].iov_len);
- } while (w < 0 && errno == EINTR);
-
- ASSERT(w <= iov[cnt].iov_len || w == -1);
-
- if (w < 0) {
- return check_error(-1, errInfo);
- }
- /* Move forward to next buffer to write */
- for (; cnt < iovcnt && w > 0; cnt++) {
- if (iov[cnt].iov_base && iov[cnt].iov_len > 0) {
- if (w < iov[cnt].iov_len) {
- /* Adjust the buffer for next write */
- iov[cnt].iov_len -= w;
- iov[cnt].iov_base += w;
- w = 0;
- break;
- }
- else {
- w -= iov[cnt].iov_len;
- }
- }
- }
- ASSERT(w == 0);
- } /* else Non-empty buffer */
- } /* while (cnt< iovcnt) */
- return 1;
-}
-
-/***************************************************************************/
-
-int
-efile_read(Efile_error* errInfo, /* Where to return error codes. */
- int flags, /* Flags given when file was opened. */
- int fd, /* File descriptor to read from. */
- char* buf, /* Buffer to read into. */
- size_t count, /* Number of bytes to read. */
- size_t *pBytesRead) /* Where to return number of
- bytes read. */
-{
- ssize_t n;
- struct fd_data *fd_data;
-
- if ((fd_data = l_find_local_fd(fd)) != NULL) {
- if (L_FD_IS_VALID(fd_data)) {
- *pBytesRead = 0;
- return 1;
- }
- }
- for (;;) {
- if ((n = read(fd, buf, count)) >= 0) {
- break;
- }
- else if (errno != EINTR) {
- return check_error(-1, errInfo);
- }
- }
- if (fd_data != NULL && L_FD_IS_VALID(fd_data)) {
- L_FD_INVALIDATE(fd_data);
- }
- *pBytesRead = (size_t) n;
- return 1;
-}
-
-/* pread() and pwrite() */
-/* Some unix systems, notably Solaris has these syscalls */
-/* It is especially nice for i.e. the dets module to have support */
-/* for this, even if the underlying OS dosn't support it, it is */
-/* reasonably easy to work around by first calling seek, and then */
-/* calling read(). */
-/* This later strategy however changes the file pointer, which pread() */
-/* does not do. We choose to ignore this and say that the location */
-/* of the file pointer is undefined after a call to any of the p functions*/
-
-
-int
-efile_pread(Efile_error* errInfo, /* Where to return error codes. */
- int fd, /* File descriptor to read from. */
- Sint64 offset, /* Offset in bytes from BOF. */
- char* buf, /* Buffer to read into. */
- size_t count, /* Number of bytes to read. */
- size_t *pBytesRead) /* Where to return
- number of bytes read. */
-{
- int res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL);
- if (res) {
- return efile_read(errInfo, 0, fd, buf, count, pBytesRead);
- } else {
- return res;
- }
-}
-
-
-/***************************************************************************/
-
-int
-efile_pwrite(Efile_error* errInfo, /* Where to return error codes. */
- int fd, /* File descriptor to write to. */
- char* buf, /* Buffer to write. */
- size_t count, /* Number of bytes to write. */
- Sint64 offset) /* where to write it */
-{
- int res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL);
-
- if (res) {
- return efile_write(errInfo, 0, fd, buf, count);
- } else {
- return res;
- }
-}
-
-/***************************************************************************/
-
-int
-efile_seek(Efile_error* errInfo, /* Where to return error codes. */
- int fd, /* File descriptor to do the seek on. */
- Sint64 offset, /* Offset in bytes from the given
- origin. */
- int origin, /* Origin of seek (SEEK_SET, SEEK_CUR,
- SEEK_END). */
- Sint64 *new_location) /* Resulting new location in file. */
-{
- off_t off, result;
- off = (off_t) offset;
-
- switch (origin) {
- case EFILE_SEEK_SET:
- origin = SEEK_SET;
- break;
- case EFILE_SEEK_CUR:
- origin = SEEK_CUR;
- break;
- case EFILE_SEEK_END:
- origin = SEEK_END;
- break;
- default:
- errno = EINVAL;
- return check_error(-1, errInfo);
- }
-
- if (off != offset) {
- errno = EINVAL;
- return check_error(-1, errInfo);
- }
-
- errno = 0;
- result = lseek(fd, off, origin);
-
- if (result >= 0) {
- l_invalidate_local_fd(fd);
- }
-
- if (result < 0)
- {
- if (errno == ENOSYS) {
- int size, cur_pos;
-
- if (off < 0) {
- errno = EINVAL;
- return check_error(-1, errInfo);
- }
-
- cur_pos = lseek(fd, 0, SEEK_CUR);
- size = lseek(fd, 0, SEEK_END);
-
- if (origin == SEEK_SET) {
- result = offset;
- }
- else if (origin == SEEK_CUR) {
- result = offset + cur_pos;
- }
- else if (origin == SEEK_END) {
- result = size + offset;
- }
-
- /* sanity check our result */
- if (size > result) {
- return check_error(-1, errInfo);
- }
-
- /* store the data localy */
- l_update_local_fd(fd, result, size);
-
- /* reset the original file position */
- if (origin != SEEK_END) {
- lseek(fd, cur_pos, SEEK_SET);
- }
- }
- else if (errno == 0) {
- errno = EINVAL;
- }
- }
-
- if (new_location) {
- *new_location = result;
- }
-
- return 1;
-}
-
-/***************************************************************************/
-
-int
-efile_truncate_file(Efile_error* errInfo, int *fd, int flags)
-{
- off_t offset;
- struct fd_data *fd_data;
-
- if ((fd_data = l_find_local_fd(*fd)) != NULL && L_FD_IS_VALID(fd_data)) {
- offset = L_FD_CUR(fd_data);
- }
- else {
- offset = lseek(*fd, 0, SEEK_CUR);
- }
-
- return check_error(((offset >= 0) &&
- (ftruncate(*fd, offset) == 0)) ? 1 : -1, errInfo);
-}
-
-/***************************************************************************/
-
-int
-efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size)
-{
- errno = ENOTSUP;
- return check_error(-1, errInfo);
-}
-
-/***************************************************************************/
-
-int
-efile_altname(Efile_error* errInfo, char* name, char* buffer, size_t size)
-{
- errno = ENOTSUP;
- return check_error(-1, errInfo);
-}
-
-/***************************************************************************/
-
-int
-efile_link(Efile_error* errInfo, char* old, char* new)
-{
- errno = ENOTSUP;
- return check_error(-1, errInfo);
-}
-
-/***************************************************************************/
-
-int
-efile_symlink(Efile_error* errInfo, char* old, char* new)
-{
- errno = ENOTSUP;
- return check_error(-1, errInfo);
-}
-
-/***************************************************************************/
-
-int
-efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset,
- Sint64 length, int advise)
-{
- return check_error(posix_fadvise(fd, offset, length, advise), errInfo);
-}
-
-/***************************************************************************/
-
-static int
-call_posix_fallocate(int fd, Sint64 offset, Sint64 length)
-{
- int ret;
-
- /*
- * On Linux and Solaris for example, posix_fallocate() returns
- * a positive error number on error and it does not set errno.
- * On FreeBSD however (9.0 at least), it returns -1 on error
- * and it sets errno.
- */
- do {
- ret = posix_fallocate(fd, (off_t) offset, (off_t) length);
- if (ret > 0) {
- errno = ret;
- ret = -1;
- }
- } while (ret != 0 && errno == EINTR);
-
- return ret;
-}
-
-/***************************************************************************/
-
-int
-efile_fallocate(Efile_error* errInfo, int fd, Sint64 offset, Sint64 length)
-{
- return check_error(call_posix_fallocate(fd, offset, length), errInfo);
-}
diff --git a/erts/emulator/drivers/ose/ose_signal_drv.c b/erts/emulator/drivers/ose/ose_signal_drv.c
deleted file mode 100644
index 2be9462a47..0000000000
--- a/erts/emulator/drivers/ose/ose_signal_drv.c
+++ /dev/null
@@ -1,897 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2013-2013. 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 "errno.h"
-#include "stdio.h"
-#include "string.h"
-#include "stddef.h"
-
-#include "sys.h"
-#include "erl_driver.h"
-#include "ose.h"
-
-
-#ifdef HAVE_OSE_SPI_H
-#include "ose_spi/ose_spi.h"
-#endif
-
-#define DEBUG_ATTACH 0
-#define DEBUG_HUNT 0
-#define DEBUG_SEND 0
-#define DEBUG_LISTEN 0
-
-#if 0
-#define DEBUGP(FMT,...) printf(FMT, __VA_ARGS__)
-#else
-#define DEBUGP(FMT,...)
-#endif
-
-#if DEBUG_ATTACH
-#define DEBUGP_ATTACH(...) DEBUGP( __VA_ARGS__)
-#else
-#define DEBUGP_ATTACH(...)
-#endif
-
-#if DEBUG_HUNT
-#define DEBUGP_HUNT(...) DEBUGP( __VA_ARGS__)
-#else
-#define DEBUGP_HUNT(...)
-#endif
-
-#if DEBUG_LISTEN
-#define DEBUGP_LISTEN(...) DEBUGP( __VA_ARGS__)
-#else
-#define DEBUGP_LISTEN(...)
-#endif
-
-#if DEBUG_SEND
-#define DEBUGP_SEND(...) DEBUGP( __VA_ARGS__)
-#else
-#define DEBUGP_SEND(...)
-#endif
-
-
-#define DRIVER_NAME "ose_signal_drv"
-#define GET_SPID 1
-#define GET_NAME 2
-#define HUNT 100
-#define DEHUNT 101
-#define ATTACH 102
-#define DETACH 103
-#define SEND 104
-#define SEND_W_S 105
-#define LISTEN 106
-#define OPEN 200
-
-#define REF_SEGMENT_SIZE 8
-
-struct async {
- SIGSELECT signo;
- ErlDrvTermData port;
- ErlDrvTermData proc;
- PROCESS spid;
- PROCESS target;
- Uint32 ref;
-};
-
-/**
- * OSE signals
- **/
-union SIGNAL {
- SIGSELECT signo;
- struct async async;
-};
-
-/**
- * The driver's context
- **/
-typedef struct _driver_context {
- ErlDrvPort port;
- PROCESS spid;
- ErlDrvEvent perm_events[2];
- ErlDrvEvent *events;
- Uint32 event_cnt;
- Uint32 ref;
- Uint32 *outstanding_refs;
- Uint32 outstanding_refs_max;
- Uint32 outstanding_refs_cnt;
-} driver_context_t;
-
-/**
- * Global variables
- **/
-static ErlDrvTermData a_ok;
-static ErlDrvTermData a_error;
-static ErlDrvTermData a_enomem;
-static ErlDrvTermData a_enoent;
-static ErlDrvTermData a_badarg;
-static ErlDrvTermData a_mailbox_up;
-static ErlDrvTermData a_mailbox_down;
-static ErlDrvTermData a_ose_drv_reply;
-static ErlDrvTermData a_message;
-static PROCESS proxy_proc;
-
-
-/**
- * Serialize/unserialize unsigned 32-bit values
- **/
-static char *put_u32(unsigned int value, char *ptr) {
- *ptr++ = (value & 0xff000000) >> 24;
- *ptr++ = (value & 0x00ff0000) >> 16;
- *ptr++ = (value & 0x0000ff00) >> 8;
- *ptr++ = (value & 0xff);
-
- return ptr;
-}
-
-static unsigned int get_u32(char *ptr) {
- unsigned int result = 0;
- result += (ptr[0] & 0xff) << 24;
- result += (ptr[1] & 0xff) << 16;
- result += (ptr[2] & 0xff) << 8;
- result += (ptr[3] & 0xff);
-
- return result;
-}
-
-
-/* Stolen from efile_drv.c */
-
-/* char EV_CHAR_P(ErlIOVec *ev, int p, int q) */
-#define EV_CHAR_P(ev, p, q) \
- (((char *)(ev)->iov[(q)].iov_base) + (p))
-
-/* int EV_GET_CHAR(ErlIOVec *ev, char *p, int *pp, int *qp) */
-#define EV_GET_CHAR(ev, p, pp, qp) ev_get_char(ev, p ,pp, qp)
-static int
-ev_get_char(ErlIOVec *ev, char *p, int *pp, int *qp) {
- if (*(pp)+1 <= (ev)->iov[*(qp)].iov_len) {
- *(p) = *EV_CHAR_P(ev, *(pp), *(qp));
- if (*(pp)+1 < (ev)->iov[*(qp)].iov_len)
- *(pp) = *(pp)+1;
- else {
- (*(qp))++;
- *pp = 0;
- }
- return !0;
- }
- return 0;
-}
-
-/* Uint32 EV_UINT32(ErlIOVec *ev, int p, int q)*/
-#define EV_UINT32(ev, p, q) \
- ((Uint32) *(((unsigned char *)(ev)->iov[(q)].iov_base) + (p)))
-
-/* int EV_GET_UINT32(ErlIOVec *ev, Uint32 *p, int *pp, int *qp) */
-#define EV_GET_UINT32(ev, p, pp, qp) ev_get_uint32(ev,(Uint32*)(p),pp,qp)
-static int
-ev_get_uint32(ErlIOVec *ev, Uint32 *p, int *pp, int *qp) {
- if (*(pp)+4 <= (ev)->iov[*(qp)].iov_len) {
- *(p) = (EV_UINT32(ev, *(pp), *(qp)) << 24)
- | (EV_UINT32(ev, *(pp)+1, *(qp)) << 16)
- | (EV_UINT32(ev, *(pp)+2, *(qp)) << 8)
- | (EV_UINT32(ev, *(pp)+3, *(qp)));
- if (*(pp)+4 < (ev)->iov[*(qp)].iov_len)
- *(pp) = *(pp)+4;
- else {
- (*(qp))++;
- *pp = 0;
- }
- return !0;
- }
- return 0;
-}
-
-/**
- * Convinience macros
- **/
-#define send_response(port,output) erl_drv_send_term(driver_mk_port(port),\
- driver_caller(port), output, sizeof(output) / sizeof(output[0]));
-
-void iov_memcpy(void *dest,ErlIOVec *ev,int ind,int off);
-void iov_memcpy(void *dest,ErlIOVec *ev,int ind,int off) {
- int i;
- memcpy(dest,ev->iov[ind].iov_base+off,ev->iov[ind].iov_len-off);
- for (i = ind+1; i < ev->vsize; i++)
- memcpy(dest,ev->iov[i].iov_base,ev->iov[i].iov_len);
-}
-
-/**
- * Reference handling
- **/
-
-static int add_reference(driver_context_t *ctxt, Uint32 ref) {
-
- /*
- * Premature optimizations may be evil, but they sure are fun.
- */
-
- if (ctxt->outstanding_refs == NULL) {
- /* First ref to be ignored */
- ctxt->outstanding_refs = driver_alloc(REF_SEGMENT_SIZE*sizeof(Uint32));
- if (!ctxt->outstanding_refs)
- return 1;
-
- memset(ctxt->outstanding_refs,0,REF_SEGMENT_SIZE*sizeof(Uint32));
- ctxt->outstanding_refs_max += REF_SEGMENT_SIZE;
- ctxt->outstanding_refs[ctxt->outstanding_refs_cnt++] = ref;
- } else if (ctxt->outstanding_refs_cnt == ctxt->outstanding_refs_max) {
- /* Expand ref array */
- Uint32 *new_array;
- ctxt->outstanding_refs_max += REF_SEGMENT_SIZE;
- new_array = driver_realloc(ctxt->outstanding_refs,
- ctxt->outstanding_refs_max*sizeof(Uint32));
-
- if (!new_array) {
- ctxt->outstanding_refs_max -= REF_SEGMENT_SIZE;
- return 1;
- }
-
- ctxt->outstanding_refs = new_array;
-
- memset(ctxt->outstanding_refs+ctxt->outstanding_refs_cnt,0,
- REF_SEGMENT_SIZE*sizeof(Uint32));
- ctxt->outstanding_refs[ctxt->outstanding_refs_cnt++] = ref;
-
- } else {
- /* Find an empty slot:
- * First we try current index,
- * then we scan for a slot.
- */
- if (!ctxt->outstanding_refs[ctxt->outstanding_refs_cnt]) {
- ctxt->outstanding_refs[ctxt->outstanding_refs_cnt++] = ref;
- } else {
- int i;
- ASSERT(ctxt->outstanding_refs_cnt < ctxt->outstanding_refs_max);
- for (i = 0; i < ctxt->outstanding_refs_max; i++)
- if (!ctxt->outstanding_refs[i])
- break;
- ASSERT(ctxt->outstanding_refs[i] == 0);
- ctxt->outstanding_refs[i] = ref;
- ctxt->outstanding_refs_cnt++;
- }
- }
- return 0;
-}
-
-/* Return 0 if removed, 1 if does not exist, */
-static int remove_reference(driver_context_t *ctxt, Uint32 ref) {
- int i,j;
-
- if (ctxt->outstanding_refs_max == 0 && ctxt->outstanding_refs_cnt == 0) {
- ASSERT(ctxt->outstanding_refs == NULL);
- return 1;
- }
-
- for (i = 0; i < ctxt->outstanding_refs_max; i++) {
- if (ctxt->outstanding_refs[i] == ref) {
- ctxt->outstanding_refs[i] = 0;
- ctxt->outstanding_refs_cnt--;
- i = -1;
- break;
- }
- }
-
- if (i != -1)
- return 1;
-
- if (ctxt->outstanding_refs_cnt == 0) {
- driver_free(ctxt->outstanding_refs);
- ctxt->outstanding_refs = NULL;
- ctxt->outstanding_refs_max = 0;
- } else if (ctxt->outstanding_refs_cnt == (ctxt->outstanding_refs_max - REF_SEGMENT_SIZE)) {
- Uint32 *new_array;
- for (i = 0, j = 0; i < ctxt->outstanding_refs_cnt; i++) {
- if (ctxt->outstanding_refs[i] == 0) {
- for (j = i+1; j < ctxt->outstanding_refs_max; j++)
- if (ctxt->outstanding_refs[j]) {
- ctxt->outstanding_refs[i] = ctxt->outstanding_refs[j];
- ctxt->outstanding_refs[j] = 0;
- break;
- }
- }
- }
- ctxt->outstanding_refs_max -= REF_SEGMENT_SIZE;
- new_array = driver_realloc(ctxt->outstanding_refs,
- ctxt->outstanding_refs_max*sizeof(Uint32));
- if (!new_array) {
- ctxt->outstanding_refs_max += REF_SEGMENT_SIZE;
- return 2;
- }
-
- ctxt->outstanding_refs = new_array;
-
- }
-
- return 0;
-}
-
-/**
- * The OSE proxy process. This only handles ERTS_SIGNAL_OSE_DRV_ATTACH.
- * The process is needed because signals triggered by attach ignore
- * redir tables.
- *
- * We have one global proxy process to save memory. An attempt to make each
- * port phantom into a proxy was made, but that used way to much memory.
- */
-static OS_PROCESS(driver_proxy_process) {
- SIGSELECT sigs[] = {1,ERTS_SIGNAL_OSE_DRV_ATTACH};
- PROCESS master = 0;
-
- while (1) {
- union SIGNAL *sig = receive(sigs);
-
- if (sig->signo == ERTS_SIGNAL_OSE_DRV_ATTACH) {
-
- /* The first message is used to determine who to send messages to. */
- if (master == 0)
- master = sender(&sig);
-
- if (sig->async.target == 0) {
- PROCESS from = sender(&sig);
- restore(sig);
- DEBUGP_ATTACH("0x%x: got attach 0x%x, sending to 0x%x\n",
- current_process(),from,master);
- sig->async.target = from;
- send(&sig,master);
- } else {
- PROCESS target = sig->async.target;
- restore(sig);
- sig->async.target = 0;
- DEBUGP_ATTACH("0x%x: doing attach on 0x%x\n",current_process(),target);
- attach(&sig,target);
- }
- }
- }
-}
-
-
-/**
- * Init routine for the driver
- **/
-static int drv_init(void) {
-
- a_ok = driver_mk_atom("ok");
- a_error = driver_mk_atom("error");
- a_enomem = driver_mk_atom("enomem");
- a_enoent = driver_mk_atom("enoent");
- a_badarg = driver_mk_atom("badarg");
- a_mailbox_up = driver_mk_atom("mailbox_up");
- a_mailbox_down = driver_mk_atom("mailbox_down");
- a_ose_drv_reply = driver_mk_atom("ose_drv_reply");
- a_message = driver_mk_atom("message");
-
- proxy_proc = create_process(get_ptype(current_process()),
- "ose_signal_driver_proxy",
- driver_proxy_process, 10000,
- get_pri(current_process()),
- 0, 0, NULL, 0, 0);
-
-#ifdef DEBUG
- efs_clone(proxy_proc);
-#endif
- start(proxy_proc);
-
- return 0;
-}
-
-/* Signal resolution callback */
-static ErlDrvOseEventId resolve_signal(union SIGNAL* osig) {
- union SIGNAL *sig = osig;
- if (sig->signo == ERTS_SIGNAL_OSE_DRV_HUNT ||
- sig->signo == ERTS_SIGNAL_OSE_DRV_ATTACH) {
- return sig->async.spid;
- }
- DEBUGP("%p: Got signal %d sent to %p from 0x%p\n",
- current_process(),sig->signo,addressee(&sig),sender(&sig));
- return addressee(&sig);
-}
-
-
-/**
- * Start routine for the driver
- **/
-static ErlDrvData drv_start(ErlDrvPort port, char *command)
-{
- driver_context_t *ctxt = driver_alloc(sizeof(driver_context_t));
-
- ctxt->perm_events[0] = NULL;
- ctxt->perm_events[1] = NULL;
-
- ctxt->spid = 0;
- ctxt->port = port;
- ctxt->event_cnt = 0;
- ctxt->events = NULL;
- ctxt->ref = 0;
- ctxt->outstanding_refs = NULL;
- ctxt->outstanding_refs_max = 0;
- ctxt->outstanding_refs_cnt = 0;
-
-
- /* Set the communication protocol to Erlang to be binary */
- set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);
-
- /* Everything ok */
- return (ErlDrvData)ctxt;
-}
-
-/**
- * Stop routine for the driver
- **/
-static void drv_stop(ErlDrvData driver_data)
-{
- driver_context_t *ctxt = (driver_context_t *)driver_data;
- int i;
-
- /* HUNT + ATTACH */
- if (ctxt->perm_events[0])
- driver_select(ctxt->port, ctxt->perm_events[0],
- ERL_DRV_USE|ERL_DRV_READ, 0);
- if (ctxt->perm_events[1])
- driver_select(ctxt->port, ctxt->perm_events[1],
- ERL_DRV_USE|ERL_DRV_READ, 0);
-
- for (i = 0; i < ctxt->event_cnt; i++) {
- driver_select(ctxt->port, ctxt->events[i], ERL_DRV_USE|ERL_DRV_READ, 0);
- }
-
- if (ctxt->spid != 0)
- kill_proc(ctxt->spid);
- DEBUGP("0x%x: stopped\n",ctxt->spid);
- if (ctxt->events)
- driver_free(ctxt->events);
- if (ctxt->outstanding_refs)
- driver_free(ctxt->outstanding_refs);
-
- driver_free(ctxt);
-}
-
-/**
- * Output from Erlang
- **/
-static void outputv(ErlDrvData driver_data, ErlIOVec *ev)
-{
- driver_context_t *ctxt = (driver_context_t *)driver_data;
- int p = 0, q = 1;
- char cmd;
-
- if (! EV_GET_CHAR(ev,&cmd,&p,&q)) {
- ErlDrvTermData output[] = {
- ERL_DRV_ATOM, a_ose_drv_reply,
- ERL_DRV_PORT, driver_mk_port(ctxt->port),
- ERL_DRV_ATOM, a_badarg,
- ERL_DRV_TUPLE, 3};
- send_response(ctxt->port, output);
- return;
- }
-
- /* Command is in the buffer's first byte */
- switch(cmd) {
-
- case OPEN: {
- char *name = driver_alloc(ev->size - 1+1);
- struct OS_redir_entry redir[2];
-
- redir[0].sig = 1;
- redir[0].pid = current_process();
-
- iov_memcpy(name,ev,q,p);
- name[ev->size-1] = '\0';
-
- ctxt->spid = create_process(OS_PHANTOM, name, NULL, 0,
- 0, 0, 0, redir, 0, 0);
-
- DEBUGP("0x%x: open\n",ctxt->spid);
-
- ctxt->perm_events[1] =
- erl_drv_ose_event_alloc(ERTS_SIGNAL_OSE_DRV_ATTACH,(int)ctxt->spid,
- resolve_signal, NULL);
- driver_select(ctxt->port,ctxt->perm_events[1],ERL_DRV_READ|ERL_DRV_USE,1);
-
- ctxt->perm_events[0] =
- erl_drv_ose_event_alloc(ERTS_SIGNAL_OSE_DRV_HUNT,(int)ctxt->spid,
- resolve_signal, NULL);
- driver_select(ctxt->port,ctxt->perm_events[0],ERL_DRV_READ|ERL_DRV_USE,1);
-
- start(ctxt->spid);
-
- {
- ErlDrvTermData output[] = {
- ERL_DRV_ATOM, a_ose_drv_reply,
- ERL_DRV_PORT, driver_mk_port(ctxt->port),
- ERL_DRV_ATOM, a_ok,
- ERL_DRV_TUPLE, 3};
-
- send_response(ctxt->port, output);
- }
-
- break;
-
- }
-
- case ATTACH:
- case HUNT:
- {
- union SIGNAL *sig = alloc(sizeof(union SIGNAL),
- cmd == HUNT ? ERTS_SIGNAL_OSE_DRV_HUNT:ERTS_SIGNAL_OSE_DRV_ATTACH);
-
- sig->async.port = driver_mk_port(ctxt->port);
- sig->async.proc = driver_caller(ctxt->port);
- sig->async.spid = ctxt->spid;
- sig->async.ref = ++ctxt->ref;
-
- if (add_reference(ctxt,ctxt->ref)) {
- ErlDrvTermData output[] = {
- ERL_DRV_ATOM, a_ose_drv_reply,
- ERL_DRV_PORT, driver_mk_port(ctxt->port),
- ERL_DRV_ATOM, a_enomem,
- ERL_DRV_TUPLE, 3};
- send_response(ctxt->port, output);
- free_buf(&sig);
- } else {
- ErlDrvTermData output[] = {
- ERL_DRV_ATOM, a_ose_drv_reply,
- ERL_DRV_PORT, driver_mk_port(ctxt->port),
- ERL_DRV_PORT, driver_mk_port(ctxt->port),
- ERL_DRV_INT, (ErlDrvUInt)ctxt->ref,
- ERL_DRV_TUPLE, 2,
- ERL_DRV_TUPLE, 3};
- send_response(ctxt->port, output);
-
- if (cmd == HUNT) {
- char *huntname = driver_alloc(sizeof(char)*((ev->size-1)+1));
-
- iov_memcpy(huntname,ev,q,p);
- huntname[ev->size-1] = '\0';
-
- DEBUGP_HUNT("0x%x: hunt %s -> %u (%u,%u)\n",
- ctxt->spid,huntname,ctxt->ref,
- ctxt->outstanding_refs_cnt,
- ctxt->outstanding_refs_max);
-
- hunt(huntname, 0, NULL, &sig);
-
- driver_free(huntname);
- } else {
- EV_GET_UINT32(ev,&sig->async.target,&p,&q);
- DEBUGP_ATTACH("0x%x: attach %u -> %u (%u,%u)\n",
- ctxt->spid,sig->async.target,
- ctxt->ref,
- ctxt->outstanding_refs_cnt,
- ctxt->outstanding_refs_max);
-
- send(&sig,proxy_proc);
- }
-
- }
-
- break;
- }
-
- case DETACH:
- case DEHUNT:
- {
-
- Uint32 ref;
-
- EV_GET_UINT32(ev,&ref,&p,&q);
- if (cmd == DETACH) {
- DEBUGP_ATTACH("0x%x: detach %u (%u,%u)\n",ctxt->spid,ref,
- ctxt->outstanding_refs_cnt,
- ctxt->outstanding_refs_max);
- } else {
- DEBUGP_HUNT("0x%x: dehunt %u (%u,%u)\n",ctxt->spid,ref,
- ctxt->outstanding_refs_cnt,
- ctxt->outstanding_refs_max);
- }
-
- if (remove_reference(ctxt,ref)) {
- ErlDrvTermData output[] = {
- ERL_DRV_ATOM, a_ose_drv_reply,
- ERL_DRV_PORT, driver_mk_port(ctxt->port),
- ERL_DRV_ATOM, a_error,
- ERL_DRV_ATOM, a_enoent,
- ERL_DRV_TUPLE, 2,
- ERL_DRV_TUPLE, 3};
-
- send_response(ctxt->port, output);
- } else {
- ErlDrvTermData output[] = {
- ERL_DRV_ATOM, a_ose_drv_reply,
- ERL_DRV_PORT, driver_mk_port(ctxt->port),
- ERL_DRV_ATOM, a_ok,
- ERL_DRV_TUPLE, 3};
-
- send_response(ctxt->port, output);
- }
-
- break;
- }
-
- case SEND:
- case SEND_W_S:
- {
- PROCESS spid;
- PROCESS sender;
- SIGSELECT signo;
- OSBUFSIZE size = ev->size-9;
- union SIGNAL *sig;
-
- EV_GET_UINT32(ev,&spid,&p,&q);
-
- if (cmd == SEND_W_S) {
- EV_GET_UINT32(ev,&sender,&p,&q);
- size -= 4;
- } else {
- sender = ctxt->spid;
- }
-
- EV_GET_UINT32(ev,&signo,&p,&q);
-
- sig = alloc(size + sizeof(SIGSELECT),signo);
-
- if (cmd == SEND_W_S) {
- DEBUGP_SEND("0x%x: send_w_s(%u,%u,%u)\n",ctxt->spid,spid,signo,sender);
- } else {
- DEBUGP_SEND("0x%x: send(%u,%u)\n",ctxt->spid,spid,signo);
- }
-
- iov_memcpy(((char *)&sig->signo) + sizeof(SIGSELECT),ev,q,p);
-
- send_w_s(&sig, sender, spid);
-
- break;
- }
-
- case LISTEN:
- {
- int i,j,event_cnt = (ev->size - 1)/4;
- ErlDrvEvent *events = NULL;
- SIGSELECT signo,tmp_signo;
-
- if (event_cnt == 0) {
- for (i = 0; i < ctxt->event_cnt; i++)
- driver_select(ctxt->port,ctxt->events[i],ERL_DRV_READ|ERL_DRV_USE,0);
- if (ctxt->events)
- driver_free(ctxt->events);
- } else {
- events = driver_alloc(sizeof(ErlDrvEvent)*event_cnt);
- EV_GET_UINT32(ev,&signo,&p,&q);
- for (i = 0, j = 0; i < event_cnt || j < ctxt->event_cnt; ) {
-
- if (ctxt->events)
- erl_drv_ose_event_fetch(ctxt->events[j],&tmp_signo,NULL,NULL);
-
- if (signo == tmp_signo) {
- events[i++] = ctxt->events[j++];
- EV_GET_UINT32(ev,&signo,&p,&q);
- } else if (signo < tmp_signo || !ctxt->events) {
- /* New signal to select on */
- events[i] = erl_drv_ose_event_alloc(signo,(int)ctxt->spid,
- resolve_signal, NULL);
- driver_select(ctxt->port,events[i++],ERL_DRV_READ|ERL_DRV_USE,1);
- EV_GET_UINT32(ev,&signo,&p,&q);
- } else {
- /* Remove old signal to select on */
- driver_select(ctxt->port,ctxt->events[j++],ERL_DRV_READ|ERL_DRV_USE,0);
- }
- }
- if (ctxt->events)
- driver_free(ctxt->events);
- }
- ctxt->events = events;
- ctxt->event_cnt = event_cnt;
-
- {
- ErlDrvTermData output[] = {
- ERL_DRV_ATOM, a_ose_drv_reply,
- ERL_DRV_PORT, driver_mk_port(ctxt->port),
- ERL_DRV_ATOM, a_ok,
- ERL_DRV_TUPLE, 3};
- send_response(ctxt->port, output);
- }
- break;
- }
-
- default:
- {
- DEBUGP("Warning: 'ose_signal_drv' unknown command '%d'\n", cmd);
- break;
- }
- }
-}
-
-/**
- * Handler for when OSE signal arrives
- **/
-static void ready_input(ErlDrvData driver_data, ErlDrvEvent event)
-{
- driver_context_t *ctxt = (driver_context_t *)driver_data;
- union SIGNAL *sig = erl_drv_ose_get_signal(event);
-
- while (sig != NULL) {
-
- switch(sig->signo)
- {
- /* Remote process is available */
- case ERTS_SIGNAL_OSE_DRV_HUNT:
- {
- const PROCESS spid = sender(&sig);
-
- if (remove_reference(ctxt,sig->async.ref)) {
- DEBUGP_HUNT("0x%x: Got hunt from 0x%x -> %u (CANCELLED) (%u,%u)\n",
- ctxt->spid,spid,sig->async.ref,
- ctxt->outstanding_refs_cnt,
- ctxt->outstanding_refs_max);
- /* Already removed by dehunt */
- } else {
- ErlDrvTermData reply[] = {
- ERL_DRV_ATOM, a_mailbox_up,
- ERL_DRV_PORT, sig->async.port,
- ERL_DRV_PORT, sig->async.port,
- ERL_DRV_UINT, (ErlDrvUInt)sig->async.ref,
- ERL_DRV_TUPLE, 2,
- ERL_DRV_UINT, (ErlDrvUInt)spid,
- ERL_DRV_TUPLE, 4};
- DEBUGP_HUNT("0x%x: Got hunt from 0x%x -> %u (%u,%u)\n",
- ctxt->spid,spid,sig->async.ref,
- ctxt->outstanding_refs_cnt,
- ctxt->outstanding_refs_max);
- erl_drv_send_term(sig->async.port, sig->async.proc, reply,
- sizeof(reply) / sizeof(reply[0]));
- }
- break;
- }
-
- /* Remote process is down */
- case ERTS_SIGNAL_OSE_DRV_ATTACH:
- {
- PROCESS spid = sig->async.target;
-
- if (remove_reference(ctxt,sig->async.ref)) {
- DEBUGP_ATTACH("0x%x: Got attach from 0x%x -> %u (CANCELLED) (%u,%u)\n",
- ctxt->spid,spid,sig->async.ref,
- ctxt->outstanding_refs_cnt,
- ctxt->outstanding_refs_max);
- /* Already removed by detach */
- } else {
- ErlDrvTermData reply[] = {
- ERL_DRV_ATOM, a_mailbox_down,
- ERL_DRV_PORT, sig->async.port,
- ERL_DRV_PORT, sig->async.port,
- ERL_DRV_UINT, sig->async.ref,
- ERL_DRV_TUPLE, 2,
- ERL_DRV_UINT, (ErlDrvUInt)spid,
- ERL_DRV_TUPLE, 4};
- DEBUGP_ATTACH("0x%x: Got attach from 0x%x -> %u (%u,%u)\n",
- ctxt->spid,spid,sig->async.ref,
- ctxt->outstanding_refs_cnt,
- ctxt->outstanding_refs_max);
- erl_drv_send_term(sig->async.port, sig->async.proc, reply,
- sizeof(reply) / sizeof(reply[0]));
- }
- break;
- }
-
- /* Received user defined signal */
- default:
- {
- const PROCESS spid = sender(&sig);
- const OSBUFSIZE size = sigsize(&sig) - sizeof(SIGSELECT);
- const char *sig_data = ((char *)&sig->signo) + sizeof(SIGSELECT);
-
- ErlDrvTermData reply[] = {
- ERL_DRV_ATOM, a_message,
- ERL_DRV_PORT, driver_mk_port(ctxt->port),
- ERL_DRV_UINT, (ErlDrvUInt)spid,
- ERL_DRV_UINT, (ErlDrvUInt)ctxt->spid,
- ERL_DRV_UINT, (ErlDrvUInt)sig->signo,
- ERL_DRV_BUF2BINARY, (ErlDrvTermData)sig_data, (ErlDrvUInt)size,
- ERL_DRV_TUPLE, 4,
- ERL_DRV_TUPLE, 3};
-
- DEBUGP_SEND("0x%x: Got 0x%u\r\n", spid, sig->signo);
-
- erl_drv_output_term(driver_mk_port(ctxt->port), reply,
- sizeof(reply) / sizeof(reply[0]));
- break;
- }
- }
-
- free_buf(&sig);
- sig = erl_drv_ose_get_signal(event);
- }
-}
-
-/**
- * Handler for 'port_control'
- **/
-static ErlDrvSSizeT control(ErlDrvData driver_data, unsigned int cmd,
- char *buf, ErlDrvSizeT len,
- char **rbuf, ErlDrvSizeT rlen)
-{
- driver_context_t *ctxt = (driver_context_t *)driver_data;
-
- switch(cmd)
- {
- case GET_SPID:
- {
- const PROCESS spid = ctxt->spid;
- put_u32(spid, *rbuf);
- return sizeof(PROCESS);
- }
-
-#ifdef HAVE_OSE_SPI_H
- case GET_NAME:
- {
- const PROCESS spid = get_u32(buf);
- char *name = (char*)get_pid_info(spid,OSE_PI_NAME);
- int n;
- if (!name) {
- *rbuf = NULL;
- return 0;
- }
-
- if (rlen < (n = strlen(name))) {
- ErlDrvBinary *bin = driver_alloc_binary(n);
- strncpy(bin->orig_bytes,name,n);
- *rbuf = (char*)bin;
- } else
- strncpy(*rbuf,name,n);
- free_buf((union SIGNAL**)&name);
-
- return n;
- }
-#endif
- default:
- {
- /* Unknown command */
- return (ErlDrvSSizeT)ERL_DRV_ERROR_GENERAL;
- break;
- }
- }
-}
-
-static void stop_select(ErlDrvEvent event, void *reserved)
-{
- erl_drv_ose_event_free(event);
-}
-
-/**
- * Setup the driver entry for the Erlang runtime
- **/
-ErlDrvEntry ose_signal_driver_entry = {
- .init = drv_init,
- .start = drv_start,
- .stop = drv_stop,
- .outputv = outputv,
- .ready_input = ready_input,
- .driver_name = DRIVER_NAME,
- .control = control,
- .extended_marker = ERL_DRV_EXTENDED_MARKER,
- .major_version = ERL_DRV_EXTENDED_MAJOR_VERSION,
- .minor_version = ERL_DRV_EXTENDED_MINOR_VERSION,
- .driver_flags = ERL_DRV_FLAG_USE_PORT_LOCKING,
- .stop_select = stop_select
-};
-
diff --git a/erts/emulator/drivers/ose/ttsl_drv.c b/erts/emulator/drivers/ose/ttsl_drv.c
deleted file mode 100644
index f759b47984..0000000000
--- a/erts/emulator/drivers/ose/ttsl_drv.c
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2013. 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%
- */
-/*
- * Stub tty driver because group/user depend on this.
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include "erl_driver.h"
-
-static int ttysl_init(void);
-static ErlDrvData ttysl_start(ErlDrvPort, char*);
-
-/* Define the driver table entry. */
-struct erl_drv_entry ttsl_driver_entry = {
- ttysl_init,
- ttysl_start,
- NULL,
- NULL,
- NULL,
- NULL,
- "tty_sl",
- NULL,
- NULL,
- NULL,
- NULL, /* timeout */
- NULL, /* outputv */
- NULL, /* ready_async */
- NULL, /* flush */
- NULL, /* call */
- NULL, /* event */
- ERL_DRV_EXTENDED_MARKER,
- ERL_DRV_EXTENDED_MAJOR_VERSION,
- ERL_DRV_EXTENDED_MINOR_VERSION,
- 0, /* ERL_DRV_FLAGs */
- NULL,
- NULL, /* process_exit */
- NULL
-};
-
-
-static int ttysl_init(void)
-{
- return 0;
-}
-
-static ErlDrvData ttysl_start(ErlDrvPort port, char* buf)
-{
- return ERL_DRV_ERROR_GENERAL;
-}
diff --git a/erts/emulator/drivers/unix/bin_drv.c b/erts/emulator/drivers/unix/bin_drv.c
index 21fb398907..4b633bb0cf 100644
--- a/erts/emulator/drivers/unix/bin_drv.c
+++ b/erts/emulator/drivers/unix/bin_drv.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/drivers/unix/multi_drv.c b/erts/emulator/drivers/unix/multi_drv.c
index 7f8c2d9a0d..eddc57d4d4 100644
--- a/erts/emulator/drivers/unix/multi_drv.c
+++ b/erts/emulator/drivers/unix/multi_drv.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/drivers/unix/sig_drv.c b/erts/emulator/drivers/unix/sig_drv.c
index e6f2ecc494..68ad6b9156 100644
--- a/erts/emulator/drivers/unix/sig_drv.c
+++ b/erts/emulator/drivers/unix/sig_drv.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/drivers/unix/ttsl_drv.c b/erts/emulator/drivers/unix/ttsl_drv.c
index 25cad37e25..e425b99f16 100644
--- a/erts/emulator/drivers/unix/ttsl_drv.c
+++ b/erts/emulator/drivers/unix/ttsl_drv.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -199,7 +199,7 @@ static void my_debug_printf(char *fmt, ...)
erts_vsnprintf(buffer,1024,fmt,args);
va_end(args);
erts_fprintf(debuglog,"%s\n",buffer);
- //erts_printf("Debuglog = %s\n",buffer);
+ /*erts_printf("Debuglog = %s\n",buffer);*/
}
#else
@@ -261,7 +261,7 @@ static int ttysl_init(void)
if (debuglog != NULL)
setbuf(debuglog,NULL);
}
- DEBUGLOG(("Debuglog = %s(0x%ld)\n",dl,(long) debuglog));
+ DEBUGLOG(("ttysl_init: Debuglog = %s(0x%ld)\n",dl,(long) debuglog));
}
#endif
return 0;
@@ -277,36 +277,46 @@ static ErlDrvData ttysl_start(ErlDrvPort port, char* buf)
int flag;
extern int using_oldshell; /* set this to let the rest of erts know */
+ DEBUGLOG(("ttysl_start: driver input \"%s\", ttysl_port = %d (-1 expected)", buf, ttysl_port));
utf8buf_size = 0;
- if (ttysl_port != (ErlDrvPort)-1)
- return ERL_DRV_ERROR_GENERAL;
+ if (ttysl_port != (ErlDrvPort)-1) {
+ DEBUGLOG(("ttysl_start: failure with ttysl_port = %d, not initialized properly?\n", ttysl_port));
+ return ERL_DRV_ERROR_GENERAL;
+ }
- if (!isatty(0) || !isatty(1))
+ DEBUGLOG(("ttysl_start: isatty(0) = %d (1 expected), isatty(1) = %d (1 expected)", isatty(0), isatty(1)));
+ if (!isatty(0) || !isatty(1)) {
+ DEBUGLOG(("ttysl_start: failure in isatty, isatty(0) = %d, isatty(1) = %d", isatty(0), isatty(1)));
return ERL_DRV_ERROR_GENERAL;
+ }
/* Set the terminal modes to default leave as is. */
canon = echo = sig = 0;
/* Parse the input parameters. */
for (s = strchr(buf, ' '); s; s = t) {
- s++;
- /* Find end of this argument (start of next) and insert NUL. */
- if ((t = strchr(s, ' '))) {
- *t = '\0';
- }
- if ((flag = ((*s == '+') ? 1 : ((*s == '-') ? -1 : 0)))) {
- if (s[1] == 'c') canon = flag;
- if (s[1] == 'e') echo = flag;
- if (s[1] == 's') sig = flag;
- }
- else if ((ttysl_fd = open(s, O_RDWR, 0)) < 0)
- return ERL_DRV_ERROR_GENERAL;
+ s++;
+ /* Find end of this argument (start of next) and insert NUL. */
+ if ((t = strchr(s, ' '))) {
+ *t = '\0';
+ }
+ if ((flag = ((*s == '+') ? 1 : ((*s == '-') ? -1 : 0)))) {
+ if (s[1] == 'c') canon = flag;
+ if (s[1] == 'e') echo = flag;
+ if (s[1] == 's') sig = flag;
+ }
+ else if ((ttysl_fd = open(s, O_RDWR, 0)) < 0) {
+ DEBUGLOG(("ttysl_start: failed to open ttysl_fd, open(%s, O_RDWR, 0)) = %d\n", s, ttysl_fd));
+ return ERL_DRV_ERROR_GENERAL;
+ }
}
+
if (ttysl_fd < 0)
ttysl_fd = 0;
if (tty_init(ttysl_fd, canon, echo, sig) < 0 ||
- tty_set(ttysl_fd) < 0) {
+ tty_set(ttysl_fd) < 0) {
+ DEBUGLOG(("ttysl_start: failed init tty or set tty\n"));
ttysl_port = (ErlDrvPort)-1;
tty_reset(ttysl_fd);
return ERL_DRV_ERROR_GENERAL;
@@ -314,6 +324,7 @@ static ErlDrvData ttysl_start(ErlDrvPort port, char* buf)
/* Set up smart line and termcap stuff. */
if (!start_lbuf() || !start_termcap()) {
+ DEBUGLOG(("ttysl_start: failed to start_lbuf or start_termcap\n"));
stop_lbuf(); /* Must free this */
tty_reset(ttysl_fd);
return ERL_DRV_ERROR_GENERAL;
@@ -335,10 +346,10 @@ static ErlDrvData ttysl_start(ErlDrvPort port, char* buf)
l = setlocale(LC_CTYPE, ""); /* Set international environment */
if (l != NULL) {
utf8_mode = (strcmp(nl_langinfo(CODESET), "UTF-8") == 0);
- DEBUGLOG(("setlocale: %s\n",l));
+ DEBUGLOG(("ttysl_start: setlocale: %s",l));
}
#endif
- DEBUGLOG(("utf8_mode is %s\n",(utf8_mode) ? "on" : "off"));
+ DEBUGLOG(("ttysl_start: utf8_mode is %s",(utf8_mode) ? "on" : "off"));
sys_signal(SIGCONT, cont);
sys_signal(SIGWINCH, winch);
@@ -348,6 +359,7 @@ static ErlDrvData ttysl_start(ErlDrvPort port, char* buf)
/* we need to know this when we enter the break handler */
using_oldshell = 0;
+ DEBUGLOG(("ttysl_start: successful start\n"));
return (ErlDrvData)ttysl_port; /* Nothing important to return */
#endif /* HAVE_TERMCAP */
}
@@ -418,6 +430,7 @@ static ErlDrvSSizeT ttysl_control(ErlDrvData drv_data,
static void ttysl_stop(ErlDrvData ttysl_data)
{
+ DEBUGLOG(("ttysl_stop: ttysl_port = %d\n",ttysl_port));
if (ttysl_port != (ErlDrvPort)-1) {
stop_lbuf();
stop_termcap();
@@ -617,12 +630,13 @@ static int check_buf_size(byte *s, int n)
int ch;
int size = 10;
+ DEBUGLOG(("check_buf_size: n = %d",n));
while(pos < n) {
/* Indata is always UTF-8 */
if ((ch = pick_utf8(s,n,&pos)) < 0) {
/* XXX temporary allow invalid chars */
ch = (int) s[pos];
- DEBUGLOG(("Invalid UTF8:%d",ch));
+ DEBUGLOG(("check_buf_size: Invalid UTF8:%d",ch));
++pos;
}
if (utf8_mode) { /* That is, terminal is UTF8 compliant */
@@ -630,7 +644,7 @@ static int check_buf_size(byte *s, int n)
#ifdef HAVE_WCWIDTH
int width;
#endif
- DEBUGLOG(("Printable(UTF-8:%d):%d",pos,ch));
+ DEBUGLOG(("check_buf_size: Printable(UTF-8:%d):%d",pos,ch));
size++;
#ifdef HAVE_WCWIDTH
if ((width = wcwidth(ch)) > 1) {
@@ -640,21 +654,21 @@ static int check_buf_size(byte *s, int n)
} else if (ch == '\t') {
size += 8;
} else {
- DEBUGLOG(("Magic(UTF-8:%d):%d",pos,ch));
+ DEBUGLOG(("check_buf_size: Magic(UTF-8:%d):%d",pos,ch));
size += 2;
}
} else {
if (ch <= 255 && isprint(ch)) {
- DEBUGLOG(("Printable:%d",ch));
+ DEBUGLOG(("check_buf_size: Printable:%d",ch));
size++;
} else if (ch == '\t')
size += 8;
else if (ch >= 128) {
- DEBUGLOG(("Non printable:%d",ch));
+ DEBUGLOG(("check_buf_size: Non printable:%d",ch));
size += (octal_or_hex_positions(ch) + 1);
}
else {
- DEBUGLOG(("Magic:%d",ch));
+ DEBUGLOG(("check_buf_size: Magic:%d",ch));
size += 2;
}
}
@@ -664,10 +678,12 @@ static int check_buf_size(byte *s, int n)
lbuf_size = size + lpos + BUFSIZ;
if ((lbuf = driver_realloc(lbuf, lbuf_size * sizeof(Uint32))) == NULL) {
+ DEBUGLOG(("check_buf_size: alloc failure of %d bytes", lbuf_size * sizeof(Uint32)));
driver_failure(ttysl_port, -1);
return(0);
}
}
+ DEBUGLOG(("check_buf_size: success\n"));
return(1);
}
@@ -685,6 +701,8 @@ static void ttysl_from_erlang(ErlDrvData ttysl_data, char* buf, ErlDrvSizeT coun
if (lpos > MAXSIZE)
put_chars((byte*)"\n", 1);
+ DEBUGLOG(("ttysl_from_erlang: OP = %d", buf[0]));
+
switch (buf[0]) {
case OP_PUTC_SYNC:
/* Using sync means that we have to send an ok to the
@@ -695,7 +713,7 @@ static void ttysl_from_erlang(ErlDrvData ttysl_data, char* buf, ErlDrvSizeT coun
the port_command. */
/* fall through */
case OP_PUTC:
- DEBUGLOG(("OP: Putc(%lu)",(unsigned long) count-1));
+ DEBUGLOG(("ttysl_from_erlang: OP: Putc(%lu)",(unsigned long) count-1));
if (check_buf_size((byte*)buf+1, count-1) == 0)
return;
put_chars((byte*)buf+1, count-1);
@@ -738,6 +756,7 @@ static void ttysl_from_erlang(ErlDrvData ttysl_data, char* buf, ErlDrvSizeT coun
ERL_DRV_USE|ERL_DRV_WRITE,1);
break;
} else {
+ DEBUGLOG(("ttysl_from_erlang: driver failure in writev(%d,..) = %d (errno = %d)\n", ttysl_fd, written, errno));
driver_failure_posix(ttysl_port, errno);
return;
}
@@ -774,6 +793,9 @@ static void ttysl_to_tty(ErlDrvData ttysl_data, ErlDrvEvent fd) {
ErlDrvSizeT sz;
iov = driver_peekq(ttysl_port,&qlen);
+
+ DEBUGLOG(("ttysl_to_tty: qlen = %d", qlen));
+
if (iov)
written = writev(ttysl_fd, iov, qlen > MAXIOV ? MAXIOV : qlen);
else
@@ -782,6 +804,7 @@ static void ttysl_to_tty(ErlDrvData ttysl_data, ErlDrvEvent fd) {
if (errno == EINTR) {
continue;
} else if (errno != ERRNO_BLOCK){
+ DEBUGLOG(("ttysl_to_tty: driver failure in writev(%d,..) = %d (errno = %d)\n", ttysl_fd, written, errno));
driver_failure_posix(ttysl_port, errno);
}
break;
@@ -800,11 +823,13 @@ static void ttysl_to_tty(ErlDrvData ttysl_data, ErlDrvEvent fd) {
if (sz == 0) {
driver_select(ttysl_port,(ErlDrvEvent)(long)ttysl_fd,
ERL_DRV_WRITE,0);
- if (ttysl_terminate)
+ if (ttysl_terminate) {
/* flush has been called, which means we should terminate
when queue is empty. This will not send any exit
message */
+ DEBUGLOG(("ttysl_to_tty: ttysl_terminate normal\n"));
driver_failure_atom(ttysl_port, "normal");
+ }
break;
}
}
@@ -814,6 +839,7 @@ static void ttysl_to_tty(ErlDrvData ttysl_data, ErlDrvEvent fd) {
}
static void ttysl_flush_tty(ErlDrvData ttysl_data) {
+ DEBUGLOG(("ttysl_flush_tty: .."));
ttysl_terminate = 1;
return;
}
@@ -834,6 +860,8 @@ static void ttysl_from_tty(ErlDrvData ttysl_data, ErlDrvEvent fd)
p += utf8buf_size;
utf8buf_size = 0;
}
+
+ DEBUGLOG(("ttysl_from_tty: remainder = %d", left));
if ((i = read((int)(SWord)fd, (char *) p, left)) >= 0) {
if (p != b) {
@@ -847,7 +875,7 @@ static void ttysl_from_tty(ErlDrvData ttysl_data, ErlDrvEvent fd)
utf8buf_size = i -pos;
memcpy(utf8buf,b+pos,utf8buf_size);
} else if (ch == -1) {
- DEBUGLOG(("Giving up on UTF8 mode, invalid character"));
+ DEBUGLOG(("ttysl_from_tty: Giving up on UTF8 mode, invalid character"));
utf8_mode = 0;
goto latin_terminal;
}
@@ -864,6 +892,7 @@ static void ttysl_from_tty(ErlDrvData ttysl_data, ErlDrvEvent fd)
}
}
} else {
+ DEBUGLOG(("ttysl_from_tty: driver failure in read(%d,..) = %d\n", (int)(SWord)fd, i));
driver_failure(ttysl_port, -1);
}
}
@@ -1155,7 +1184,7 @@ static int write_buf(Uint32 *s, int n)
byte *octbuff;
byte octtmp[256];
int octbytes;
- DEBUGLOG(("Escaped: %d", ch));
+ DEBUGLOG(("write_buf: Escaped: %d", ch));
octbytes = octal_or_hex_positions(ch);
if (octbytes > 256) {
octbuff = driver_alloc(octbytes);
@@ -1164,11 +1193,11 @@ static int write_buf(Uint32 *s, int n)
}
octbytes = 0;
octal_or_hex_format(ch, octbuff, &octbytes);
- DEBUGLOG(("octbytes: %d", octbytes));
+ DEBUGLOG(("write_buf: octbytes: %d", octbytes));
outc('\\');
for (i = 0; i < octbytes; ++i) {
outc(lastput = octbuff[i]);
- DEBUGLOG(("outc: %d", (int) lastput));
+ DEBUGLOG(("write_buf: outc: %d", (int) lastput));
}
n -= octbytes+1;
s += octbytes+1;
@@ -1180,7 +1209,7 @@ static int write_buf(Uint32 *s, int n)
--n; s++;
#endif
} else {
- DEBUGLOG(("Very unexpected character %d",(int) *s));
+ DEBUGLOG(("write_buf: Very unexpected character %d",(int) *s));
++n;
--s;
}
@@ -1242,6 +1271,9 @@ static int start_termcap(void)
size_t envsz = 1024;
char *env = NULL;
char *c;
+ int tres;
+
+ DEBUGLOG(("start_termcap: .."));
capbuf = driver_alloc(1024);
if (!capbuf)
@@ -1249,9 +1281,10 @@ static int start_termcap(void)
eres = erl_drv_getenv("TERM", capbuf, &envsz);
if (eres == 0)
env = capbuf;
- else if (eres < 0)
+ else if (eres < 0) {
+ DEBUGLOG(("start_termcap: failure in erl_drv_getenv(\"TERM\", ..) = %d\n", eres));
goto false;
- else /* if (eres > 1) */ {
+ } else /* if (eres > 1) */ {
char *envbuf = driver_alloc(envsz);
if (!envbuf)
goto false;
@@ -1261,7 +1294,8 @@ static int start_termcap(void)
if (eres == 0)
break;
newenvbuf = driver_realloc(envbuf, envsz);
- if (eres < 0 || !newenvbuf) {
+ if (eres < 0 || !newenvbuf) {
+ DEBUGLOG(("start_termcap: failure in erl_drv_getenv(\"TERM\", ..) = %d or realloc buf == %p\n", eres, newenvbuf));
env = newenvbuf ? newenvbuf : envbuf;
goto false;
}
@@ -1269,8 +1303,10 @@ static int start_termcap(void)
}
env = envbuf;
}
- if (tgetent((char*)lbuf, env) <= 0)
- goto false;
+ if ((tres = tgetent((char*)lbuf, env)) <= 0) {
+ DEBUGLOG(("start_termcap: failure in tgetent(..) = %d\n", tres));
+ goto false;
+ }
if (env != capbuf) {
env = NULL;
driver_free(env);
@@ -1286,8 +1322,11 @@ static int start_termcap(void)
if (!(left = tgetflag("bs") ? "\b" : tgetstr("bc", &c)))
left = "\b"; /* Can't happen - but does on Solaris 2 */
right = tgetstr("nd", &c);
- if (up && down && left && right)
- return TRUE;
+ if (up && down && left && right) {
+ DEBUGLOG(("start_termcap: successful start\n"));
+ return TRUE;
+ }
+ DEBUGLOG(("start_termcap: failed start\n"));
false:
if (env && env != capbuf)
driver_free(env);
@@ -1364,10 +1403,13 @@ static void update_cols(void)
static struct termios tty_smode, tty_rmode;
-static int tty_init(int fd, int canon, int echo, int sig)
-{
- if (tcgetattr(fd, &tty_rmode) < 0)
- return -1;
+static int tty_init(int fd, int canon, int echo, int sig) {
+ int tres;
+ DEBUGLOG(("tty_init: fd = %d, canon = %d, echo = %d, sig = %d", fd, canon, echo, sig));
+ if ((tres = tcgetattr(fd, &tty_rmode)) < 0) {
+ DEBUGLOG(("tty_init: failure in tcgetattr(%d,..) = %d\n", fd, tres));
+ return -1;
+ }
tty_smode = tty_rmode;
/* Default characteristics for all usage including termcap output. */
@@ -1420,6 +1462,7 @@ static int tty_init(int fd, int canon, int echo, int sig)
#endif
tty_smode.c_lflag &= ~(ISIG|IEXTEN);
}
+ DEBUGLOG(("tty_init: successful init\n"));
return 0;
}
@@ -1430,20 +1473,25 @@ static int tty_init(int fd, int canon, int echo, int sig)
static int tty_set(int fd)
{
- DEBUGF(("Setting tty...\n"));
+ int tres;
+ DEBUGF(("tty_set: Setting tty...\n"));
- if (tcsetattr(fd, TCSANOW, &tty_smode) < 0)
+ if ((tres = tcsetattr(fd, TCSANOW, &tty_smode)) < 0) {
+ DEBUGLOG(("tty_set: failure in tcgetattr(%d,..) = %d\n", fd, tres));
return(-1);
+ }
return(0);
}
static int tty_reset(int fd) /* of terminal device */
{
- DEBUGF(("Resetting tty...\n"));
+ int tres;
+ DEBUGF(("tty_reset: Resetting tty...\n"));
- if (tcsetattr(fd, TCSANOW, &tty_rmode) < 0)
+ if ((tres = tcsetattr(fd, TCSANOW, &tty_rmode)) < 0) {
+ DEBUGLOG(("tty_reset: failure in tcsetattr(%d,..) = %d\n", fd, tres));
return(-1);
-
+ }
return(0);
}
@@ -1458,6 +1506,7 @@ static int tty_reset(int fd) /* of terminal device */
static RETSIGTYPE suspend(int sig)
{
if (tty_reset(ttysl_fd) < 0) {
+ DEBUGLOG(("signal: failure in suspend(%d), can't reset tty %d\n", sig, ttysl_fd));
fprintf(stderr,"Can't reset tty \n");
exit(1);
}
@@ -1469,6 +1518,7 @@ static RETSIGTYPE suspend(int sig)
sys_signal(sig, suspend); /* Reset signal handler */
if (tty_set(ttysl_fd) < 0) {
+ DEBUGLOG(("signal: failure in suspend(%d), can't set tty %d\n", sig, ttysl_fd));
fprintf(stderr,"Can't set tty raw \n");
exit(1);
}
@@ -1479,6 +1529,7 @@ static RETSIGTYPE suspend(int sig)
static RETSIGTYPE cont(int sig)
{
if (tty_set(ttysl_fd) < 0) {
+ DEBUGLOG(("signal: failure in cont(%d), can't set tty raw %d\n", sig, ttysl_fd));
fprintf(stderr,"Can't set tty raw\n");
exit(1);
}
diff --git a/erts/emulator/drivers/unix/unix_efile.c b/erts/emulator/drivers/unix/unix_efile.c
index 00da48b107..0acc2432a7 100644
--- a/erts/emulator/drivers/unix/unix_efile.c
+++ b/erts/emulator/drivers/unix/unix_efile.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2013. 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.
@@ -79,11 +79,10 @@
* Macros for testing file types.
*/
-#define ISDIR(st) (((st).st_mode & S_IFMT) == S_IFDIR)
-#define ISREG(st) (((st).st_mode & S_IFMT) == S_IFREG)
-#define ISDEV(st) \
- (((st).st_mode&S_IFMT) == S_IFCHR || ((st).st_mode&S_IFMT) == S_IFBLK)
-#define ISLNK(st) (((st).st_mode & S_IFLNK) == S_IFLNK)
+#define ISDIR(st) (S_ISDIR((st).st_mode))
+#define ISREG(st) (S_ISREG((st).st_mode))
+#define ISDEV(st) (S_ISCHR((st).st_mode) || S_ISBLK((st).st_mode))
+#define ISLNK(st) (S_ISLNK((st).st_mode))
#ifdef NO_UMASK
#define FILE_MODE 0644
#define DIR_MODE 0755
@@ -366,33 +365,6 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */
int fd;
int mode; /* Open mode. */
- if (stat(name, &statbuf) < 0) {
- /* statbuf is undefined: if the caller depends on it,
- i.e. invoke_read_file(), fail the call immediately */
- if (pSize && flags == EFILE_MODE_READ)
- return check_error(-1, errInfo);
- } else if (!ISREG(statbuf)) {
- /*
- * For UNIX only, here is some ugly code to allow
- * /dev/null to be opened as a file.
- *
- * Assumption: The i-node number for /dev/null cannot be zero.
- */
- static ino_t dev_null_ino = 0;
-
- if (dev_null_ino == 0) {
- struct stat nullstatbuf;
-
- if (stat("/dev/null", &nullstatbuf) >= 0) {
- dev_null_ino = nullstatbuf.st_ino;
- }
- }
- if (!(dev_null_ino && statbuf.st_ino == dev_null_ino)) {
- errno = EISDIR;
- return check_error(-1, errInfo);
- }
- }
-
switch (flags & (EFILE_MODE_READ|EFILE_MODE_WRITE)) {
case EFILE_MODE_READ:
mode = O_RDONLY;
@@ -411,16 +383,13 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */
return check_error(-1, errInfo);
}
-
if (flags & EFILE_MODE_APPEND) {
mode &= ~O_TRUNC;
mode |= O_APPEND;
}
-
if (flags & EFILE_MODE_EXCL) {
mode |= O_EXCL;
}
-
if (flags & EFILE_MODE_SYNC) {
#ifdef O_SYNC
mode |= O_SYNC;
@@ -430,15 +399,52 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */
#endif
}
- fd = open(name, mode, FILE_MODE);
+#ifdef HAVE_FSTAT
+ while (((fd = open(name, mode, FILE_MODE)) < 0) && (errno == EINTR));
+ if (!check_error(fd, errInfo)) return 0;
+#endif
- if (!check_error(fd, errInfo))
- return 0;
+ if (
+#ifdef HAVE_FSTAT
+ fstat(fd, &statbuf) < 0
+#else
+ stat(name, &statbuf) < 0
+#endif
+ ) {
+ /* statbuf is undefined: if the caller depends on it,
+ i.e. invoke_read_file(), fail the call immediately */
+ if (pSize && flags == EFILE_MODE_READ) {
+ check_error(-1, errInfo);
+#ifdef HAVE_FSTAT
+ efile_closefile(fd);
+#endif
+ return 0;
+ }
+ }
+ else if (! ISREG(statbuf)) {
+ struct stat nullstatbuf;
+ /*
+ * For UNIX only, here is some ugly code to allow
+ * /dev/null to be opened as a file.
+ */
+ if ( (stat("/dev/null", &nullstatbuf) < 0)
+ || (statbuf.st_ino != nullstatbuf.st_ino)
+ || (statbuf.st_dev != nullstatbuf.st_dev) ) {
+#ifdef HAVE_FSTAT
+ efile_closefile(fd);
+#endif
+ errno = EISDIR;
+ return check_error(-1, errInfo);
+ }
+ }
+
+#ifndef HAVE_FSTAT
+ while (((fd = open(name, mode, FILE_MODE)) < 0) && (errno == EINTR));
+ if (!check_error(fd, errInfo)) return 0;
+#endif
*pfd = fd;
- if (pSize) {
- *pSize = statbuf.st_size;
- }
+ if (pSize) *pSize = statbuf.st_size;
return 1;
}
@@ -460,14 +466,14 @@ efile_may_openfile(Efile_error* errInfo, char *name) {
void
efile_closefile(int fd)
{
- close(fd);
+ while((close(fd) < 0) && (errno == EINTR));
}
int
efile_fdatasync(Efile_error *errInfo, /* Where to return error codes. */
int fd) /* File descriptor for file to sync data. */
{
-#ifdef HAVE_FDATASYNC
+#if defined(HAVE_FDATASYNC) && !defined(__DARWIN__)
return check_error(fdatasync(fd), errInfo);
#else
return efile_fsync(errInfo, fd);
@@ -537,9 +543,9 @@ efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo,
else
pInfo->type = FT_OTHER;
- pInfo->accessTime = statbuf.st_atime;
- pInfo->modifyTime = statbuf.st_mtime;
- pInfo->cTime = statbuf.st_ctime;
+ pInfo->accessTime = (Sint64)statbuf.st_atime;
+ pInfo->modifyTime = (Sint64)statbuf.st_mtime;
+ pInfo->cTime = (Sint64)statbuf.st_ctime;
pInfo->mode = statbuf.st_mode;
pInfo->links = statbuf.st_nlink;
@@ -578,8 +584,8 @@ efile_write_info(Efile_error *errInfo, Efile_info *pInfo, char *name)
}
}
- tval.actime = pInfo->accessTime;
- tval.modtime = pInfo->modifyTime;
+ tval.actime = (time_t)pInfo->accessTime;
+ tval.modtime = (time_t)pInfo->modifyTime;
return check_error(utime(name, &tval), errInfo);
}
@@ -638,12 +644,21 @@ efile_writev(Efile_error* errInfo, /* Where to return error codes */
do {
w = writev(fd, &iov[cnt], b);
} while (w < 0 && errno == EINTR);
+ if (w < 0 && errno == EINVAL) {
+ goto single_write;
+ }
} else
+ single_write:
/* Degenerated io vector - use regular write */
#endif
{
do {
- w = write(fd, iov[cnt].iov_base, iov[cnt].iov_len);
+ size_t iov_len = iov[cnt].iov_len;
+ size_t limit = 1024*1024*1024; /* 1GB */
+ if (iov_len > limit) {
+ iov_len = limit;
+ }
+ w = write(fd, iov[cnt].iov_base, iov_len);
} while (w < 0 && errno == EINTR);
ASSERT(w <= iov[cnt].iov_len ||
(w == -1 && errno != EINTR));
diff --git a/erts/emulator/drivers/vxworks/vxworks_resolv.c b/erts/emulator/drivers/vxworks/vxworks_resolv.c
index a7de53c692..c2cdf4a90b 100644
--- a/erts/emulator/drivers/vxworks/vxworks_resolv.c
+++ b/erts/emulator/drivers/vxworks/vxworks_resolv.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/drivers/win32/registry_drv.c b/erts/emulator/drivers/win32/registry_drv.c
index 4e396aa8d9..a03a030df8 100644
--- a/erts/emulator/drivers/win32/registry_drv.c
+++ b/erts/emulator/drivers/win32/registry_drv.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/drivers/win32/ttsl_drv.c b/erts/emulator/drivers/win32/ttsl_drv.c
index 4bd766a8a8..99e7fb25a4 100644
--- a/erts/emulator/drivers/win32/ttsl_drv.c
+++ b/erts/emulator/drivers/win32/ttsl_drv.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/drivers/win32/win_con.c b/erts/emulator/drivers/win32/win_con.c
index 7fe708dc7b..0bcccfe405 100644
--- a/erts/emulator/drivers/win32/win_con.c
+++ b/erts/emulator/drivers/win32/win_con.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2012. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/drivers/win32/win_con.h b/erts/emulator/drivers/win32/win_con.h
index 3f4e89a195..7a642cd7ed 100644
--- a/erts/emulator/drivers/win32/win_con.h
+++ b/erts/emulator/drivers/win32/win_con.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2007-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2007-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c
index d95f29f49d..2d366b5833 100644
--- a/erts/emulator/drivers/win32/win_efile.c
+++ b/erts/emulator/drivers/win32/win_efile.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2014. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/TODO b/erts/emulator/hipe/TODO
index dd9760ea1c..0c28616a30 100644
--- a/erts/emulator/hipe/TODO
+++ b/erts/emulator/hipe/TODO
@@ -1,7 +1,7 @@
%CopyrightBegin%
- Copyright Ericsson AB 2004-2009. All Rights Reserved.
+ Copyright Ericsson AB 2004-2016. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/elf64ppc.x b/erts/emulator/hipe/elf64ppc.x
index 3cd4010cb3..46d2632970 100644
--- a/erts/emulator/hipe/elf64ppc.x
+++ b/erts/emulator/hipe/elf64ppc.x
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_amd64.c b/erts/emulator/hipe/hipe_amd64.c
index 1bb336637d..62739d2a78 100644
--- a/erts/emulator/hipe/hipe_amd64.c
+++ b/erts/emulator/hipe/hipe_amd64.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -83,21 +83,6 @@ int hipe_patch_call(void *callAddress, void *destAddress, void *trampoline)
return 0;
}
-/*
- * Memory allocator for executable code.
- *
- * This is required on AMD64 because some Linux kernels
- * (including 2.6.10-rc1 and newer www.kernel.org ones)
- * default to non-executable memory mappings, causing
- * ordinary malloc() memory to be non-executable.
- *
- * Implementing this properly also allows us to ensure that
- * executable code ends up in the low 2GB of the address space,
- * as required by HiPE/AMD64's small code model.
- */
-static unsigned int code_bytes;
-static char *code_next;
-
#if 0 /* change to non-zero to get allocation statistics at exit() */
static unsigned int total_mapped, nr_joins, nr_splits, total_alloc, nr_allocs, nr_large, total_lost;
static unsigned int atexit_done;
@@ -121,101 +106,20 @@ static void atexit_alloc_code_stats(void)
#define ALLOC_CODE_STATS(X) do{}while(0)
#endif
-/* FreeBSD 6.1 breakage */
-#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
-#define MAP_ANONYMOUS MAP_ANON
-#endif
-
-static int morecore(unsigned int alloc_bytes)
-{
- unsigned int map_bytes;
- char *map_hint, *map_start;
-
- /* Page-align the amount to allocate. */
- map_bytes = (alloc_bytes + 4095) & ~4095;
-
- /* Round up small allocations. */
- if (map_bytes < 1024*1024)
- map_bytes = 1024*1024;
- else
- ALLOC_CODE_STATS(++nr_large);
-
- /* Create a new memory mapping, ensuring it is executable
- and in the low 2GB of the address space. Also attempt
- to make it adjacent to the previous mapping. */
- map_hint = code_next + code_bytes;
-#if !defined(MAP_32BIT)
- /* FreeBSD doesn't have MAP_32BIT, and it doesn't respect
- a plain map_hint (returns high mappings even though the
- hint refers to a free area), so we have to use both map_hint
- and MAP_FIXED to get addresses below the 2GB boundary.
- This is even worse than the Linux/ppc64 case.
- Similarly, Solaris 10 doesn't have MAP_32BIT,
- and it doesn't respect a plain map_hint. */
- if (!map_hint) /* first call */
- map_hint = (char*)(512*1024*1024); /* 0.5GB */
-#endif
- if ((unsigned long)map_hint & 4095)
- abort();
- map_start = mmap(map_hint, map_bytes,
- PROT_EXEC|PROT_READ|PROT_WRITE,
- MAP_PRIVATE|MAP_ANONYMOUS
-#if defined(MAP_32BIT)
- |MAP_32BIT
-#elif defined(__FreeBSD__) || defined(__sun__)
- |MAP_FIXED
-#endif
- ,
- -1, 0);
- ALLOC_CODE_STATS(fprintf(stderr, "%s: mmap(%p,%u,...) == %p\r\n", __FUNCTION__, map_hint, map_bytes, map_start));
-#if !defined(MAP_32BIT)
- if (map_start != MAP_FAILED &&
- (((unsigned long)map_start + (map_bytes-1)) & ~0x7FFFFFFFUL)) {
- fprintf(stderr, "mmap with hint %p returned code memory %p\r\n", map_hint, map_start);
- abort();
- }
-#endif
- if (map_start == MAP_FAILED)
- return -1;
-
- ALLOC_CODE_STATS(total_mapped += map_bytes);
-
- /* Merge adjacent mappings, so the trailing portion of the previous
- mapping isn't lost. In practice this is quite successful. */
- if (map_start == map_hint) {
- ALLOC_CODE_STATS(++nr_joins);
- code_bytes += map_bytes;
-#if !defined(MAP_32BIT)
- if (!code_next) /* first call */
- code_next = map_start;
-#endif
- } else {
- ALLOC_CODE_STATS(++nr_splits);
- ALLOC_CODE_STATS(total_lost += code_bytes);
- code_next = map_start;
- code_bytes = map_bytes;
- }
-
- ALLOC_CODE_STATS(atexit_alloc_code_stats());
-
- return 0;
-}
-
+/*
+ * Memory allocator for executable code.
+ *
+ * We use a dedicated allocator for executable code (from OTP 19.0)
+ * to make sure the memory we get is executable (PROT_EXEC)
+ * and to ensure that executable code ends up in the low 2GB
+ * of the address space, as required by HiPE/AMD64's small code model.
+ */
static void *alloc_code(unsigned int alloc_bytes)
{
- void *res;
-
- /* Align function entries. */
- alloc_bytes = (alloc_bytes + 3) & ~3;
-
- if (code_bytes < alloc_bytes && morecore(alloc_bytes) != 0)
- return NULL;
ALLOC_CODE_STATS(++nr_allocs);
ALLOC_CODE_STATS(total_alloc += alloc_bytes);
- res = code_next;
- code_next += alloc_bytes;
- code_bytes -= alloc_bytes;
- return res;
+
+ return erts_alloc(ERTS_ALC_T_HIPE_EXEC, alloc_bytes);
}
void *hipe_alloc_code(Uint nrbytes, Eterm callees, Eterm *trampolines, Process *p)
diff --git a/erts/emulator/hipe/hipe_amd64.h b/erts/emulator/hipe/hipe_amd64.h
index 09b55a1243..750a26fcb9 100644
--- a/erts/emulator/hipe/hipe_amd64.h
+++ b/erts/emulator/hipe/hipe_amd64.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_amd64.tab b/erts/emulator/hipe/hipe_amd64.tab
index 70287034a0..e5ff31b985 100644
--- a/erts/emulator/hipe/hipe_amd64.tab
+++ b/erts/emulator/hipe/hipe_amd64.tab
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2011. All Rights Reserved.
+# Copyright Ericsson AB 2004-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_amd64_asm.m4 b/erts/emulator/hipe/hipe_amd64_asm.m4
index ecb25ec5c8..409fd0ef89 100644
--- a/erts/emulator/hipe/hipe_amd64_asm.m4
+++ b/erts/emulator/hipe/hipe_amd64_asm.m4
@@ -2,7 +2,7 @@ changecom(`/*', `*/')dnl
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -121,6 +121,22 @@ define(NSP,%rsp)dnl
/*
+ * Debugging macros
+ *
+ * Keeps track of whether context has been saved in the debug build, allowing us
+ * to detect when the garbage collector is called when it shouldn't.
+ */
+`#ifdef DEBUG
+# define SET_GC_UNSAFE \
+ movq $1, P_GCUNSAFE(P)
+# define SET_GC_SAFE \
+ movq $0, P_GCUNSAFE(P)
+#else
+# define SET_GC_UNSAFE
+# define SET_GC_SAFE
+#endif'
+
+/*
* Context switching macros.
*/
`#define SWITCH_C_TO_ERLANG_QUICK \
@@ -133,12 +149,14 @@ define(NSP,%rsp)dnl
`#define SAVE_CACHED_STATE \
SAVE_HP; \
- SAVE_FCALLS'
+ SAVE_FCALLS; \
+ SET_GC_SAFE'
`#define RESTORE_CACHED_STATE \
RESTORE_HP; \
RESTORE_HEAP_LIMIT; \
- RESTORE_FCALLS'
+ RESTORE_FCALLS; \
+ SET_GC_UNSAFE'
`#define SWITCH_C_TO_ERLANG \
RESTORE_CACHED_STATE; \
diff --git a/erts/emulator/hipe/hipe_amd64_bifs.m4 b/erts/emulator/hipe/hipe_amd64_bifs.m4
index 602e71c195..21739726bb 100644
--- a/erts/emulator/hipe/hipe_amd64_bifs.m4
+++ b/erts/emulator/hipe/hipe_amd64_bifs.m4
@@ -2,7 +2,7 @@ changecom(`/*', `*/')dnl
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -600,10 +600,11 @@ noproc_primop_interface_0(nbif_handle_fp_exception, erts_restore_fpu)
define(gc_bif_interface_0,`nofail_primop_interface_0($1, $2)')
/*
- * Implement gc_bif_interface_N as standard_bif_interface_N (N=1,2).
+ * Implement gc_bif_interface_N as standard_bif_interface_N (N=1,2,3).
*/
define(gc_bif_interface_1,`standard_bif_interface_1($1, $2)')
define(gc_bif_interface_2,`standard_bif_interface_2($1, $2)')
+define(gc_bif_interface_3,`standard_bif_interface_3($1, $2)')
/*
* Implement gc_nofail_primop_interface_1 as nofail_primop_interface_1.
diff --git a/erts/emulator/hipe/hipe_amd64_gc.h b/erts/emulator/hipe/hipe_amd64_gc.h
index e7a156c910..6b95d54904 100644
--- a/erts/emulator/hipe/hipe_amd64_gc.h
+++ b/erts/emulator/hipe/hipe_amd64_gc.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_amd64_glue.S b/erts/emulator/hipe/hipe_amd64_glue.S
index 6fb93bce53..f3404888d5 100644
--- a/erts/emulator/hipe/hipe_amd64_glue.S
+++ b/erts/emulator/hipe/hipe_amd64_glue.S
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -94,6 +94,7 @@ ASYM(nbif_return):
.nosave_exit:
/* switch to C stack */
SWITCH_ERLANG_TO_C_QUICK
+ SET_GC_SAFE
/* restore C callee-save registers, drop frame, return */
movq (%rsp), %rbp # kills P
movq 8(%rsp), %rbx
@@ -398,6 +399,7 @@ nbif_4_simple_exception:
movl %eax, P_NARITY(P) # Note: narity is a 32-bit field
/* find and prepare to invoke the handler */
SWITCH_ERLANG_TO_C_QUICK # The cached state is clean and need not be saved.
+ SET_GC_SAFE
movq P, %rdi
call CSYM(hipe_handle_exception) # Note: hipe_handle_exception() conses
SWITCH_C_TO_ERLANG # %rsp updated by hipe_find_handler()
diff --git a/erts/emulator/hipe/hipe_amd64_glue.h b/erts/emulator/hipe/hipe_amd64_glue.h
index e54b8b3a3f..089658f4b5 100644
--- a/erts/emulator/hipe/hipe_amd64_glue.h
+++ b/erts/emulator/hipe/hipe_amd64_glue.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_amd64_primops.h b/erts/emulator/hipe/hipe_amd64_primops.h
index 23d8202e46..94c39a9433 100644
--- a/erts/emulator/hipe/hipe_amd64_primops.h
+++ b/erts/emulator/hipe/hipe_amd64_primops.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_arch.h b/erts/emulator/hipe/hipe_arch.h
index 21dabc7cb6..6f959815bb 100644
--- a/erts/emulator/hipe/hipe_arch.h
+++ b/erts/emulator/hipe/hipe_arch.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2003-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_arm.c b/erts/emulator/hipe/hipe_arm.c
index 01512e9aa1..f8ef468341 100644
--- a/erts/emulator/hipe/hipe_arm.c
+++ b/erts/emulator/hipe/hipe_arm.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_arm.h b/erts/emulator/hipe/hipe_arm.h
index a0e07cd6ce..b5fe6317c9 100644
--- a/erts/emulator/hipe/hipe_arm.h
+++ b/erts/emulator/hipe/hipe_arm.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_arm.tab b/erts/emulator/hipe/hipe_arm.tab
index bcac2cd79d..152319edbd 100644
--- a/erts/emulator/hipe/hipe_arm.tab
+++ b/erts/emulator/hipe/hipe_arm.tab
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2011. All Rights Reserved.
+# Copyright Ericsson AB 2005-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_arm_asm.m4 b/erts/emulator/hipe/hipe_arm_asm.m4
index 8ce8600f97..68a6faa70b 100644
--- a/erts/emulator/hipe/hipe_arm_asm.m4
+++ b/erts/emulator/hipe/hipe_arm_asm.m4
@@ -2,7 +2,7 @@ changecom(`/*', `*/')dnl
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -48,6 +48,24 @@ define(NR_ARG_REGS,3)dnl admissible values are 0 to 6, inclusive
`#define TEMP_LR r8'
/*
+ * Debugging macros
+ *
+ * Keeps track of whether context has been saved in the debug build, allowing us
+ * to detect when the garbage collector is called when it shouldn't.
+ */
+`#ifdef DEBUG
+# define SET_GC_UNSAFE(SCRATCH) \
+ mov SCRATCH, #1; \
+ str SCRATCH, [P, #P_GCUNSAFE]
+# define SET_GC_SAFE(SCRATCH) \
+ mov SCRATCH, #0; \
+ str SCRATCH, [P, #P_GCUNSAFE]
+#else
+# define SET_GC_UNSAFE(SCRATCH)
+# define SET_GC_SAFE(SCRATCH)
+#endif'
+
+/*
* Context switching macros.
*
* RESTORE_CONTEXT and RESTORE_CONTEXT_QUICK do not affect
@@ -59,12 +77,14 @@ define(NR_ARG_REGS,3)dnl admissible values are 0 to 6, inclusive
`#define RESTORE_CONTEXT_QUICK \
mov lr, TEMP_LR'
-`#define SAVE_CACHED_STATE \
- str HP, [P, #P_HP]; \
- str NSP, [P, #P_NSP]'
+`#define SAVE_CACHED_STATE \
+ str HP, [P, #P_HP]; \
+ str NSP, [P, #P_NSP]; \
+ SET_GC_SAFE(HP)'
-`#define RESTORE_CACHED_STATE \
- ldr HP, [P, #P_HP]; \
+`#define RESTORE_CACHED_STATE \
+ SET_GC_UNSAFE(HP); \
+ ldr HP, [P, #P_HP]; \
ldr NSP, [P, #P_NSP]'
`#define SAVE_CONTEXT_BIF \
@@ -75,12 +95,14 @@ define(NR_ARG_REGS,3)dnl admissible values are 0 to 6, inclusive
ldr HP, [P, #P_HP]'
`#define SAVE_CONTEXT_GC \
+ SET_GC_SAFE(TEMP_LR); \
mov TEMP_LR, lr; \
str lr, [P, #P_NRA]; \
str NSP, [P, #P_NSP]; \
str HP, [P, #P_HP]'
`#define RESTORE_CONTEXT_GC \
+ SET_GC_UNSAFE(HP); \
ldr HP, [P, #P_HP]'
/*
diff --git a/erts/emulator/hipe/hipe_arm_bifs.m4 b/erts/emulator/hipe/hipe_arm_bifs.m4
index 3efe961964..d7a2fec04a 100644
--- a/erts/emulator/hipe/hipe_arm_bifs.m4
+++ b/erts/emulator/hipe/hipe_arm_bifs.m4
@@ -2,7 +2,7 @@ changecom(`/*', `*/')dnl
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -198,8 +198,9 @@ $1:
* gc_bif_interface_0(nbif_name, cbif_name)
* gc_bif_interface_1(nbif_name, cbif_name)
* gc_bif_interface_2(nbif_name, cbif_name)
+ * gc_bif_interface_3(nbif_name, cbif_name)
*
- * Generate native interface for a BIF with 0-2 parameters and
+ * Generate native interface for a BIF with 0-3 parameters and
* standard failure mode.
* The BIF may do a GC.
*/
@@ -279,6 +280,36 @@ $1:
.type $1, %function
#endif')
+define(gc_bif_interface_3,
+`
+#ifndef HAVE_$1
+#`define' HAVE_$1
+ .global $1
+$1:
+ /* Set up C argument registers. */
+ mov r0, P
+ NBIF_ARG(r1,3,0)
+ NBIF_ARG(r2,3,1)
+ NBIF_ARG(r3,3,2)
+
+ /* Save caller-save registers and call the C function. */
+ SAVE_CONTEXT_GC
+ str r1, [r0, #P_ARG0] /* Store BIF__ARGS in def_arg_reg[] */
+ str r2, [r0, #P_ARG1]
+ str r3, [r0, #P_ARG2]
+ add r1, r0, #P_ARG0
+ CALL_BIF($2)
+ TEST_GOT_MBUF(3)
+
+ /* Restore registers. Check for exception. */
+ cmp r0, #THE_NON_VALUE
+ RESTORE_CONTEXT_GC
+ beq nbif_3_simple_exception
+ NBIF_RET(3)
+ .size $1, .-$1
+ .type $1, %function
+#endif')
+
/*
* gc_nofail_primop_interface_1(nbif_name, cbif_name)
*
diff --git a/erts/emulator/hipe/hipe_arm_gc.h b/erts/emulator/hipe/hipe_arm_gc.h
index dfffd6c630..e8eb900978 100644
--- a/erts/emulator/hipe/hipe_arm_gc.h
+++ b/erts/emulator/hipe/hipe_arm_gc.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_arm_glue.S b/erts/emulator/hipe/hipe_arm_glue.S
index e4ce9b75ef..5b7f8ad52d 100644
--- a/erts/emulator/hipe/hipe_arm_glue.S
+++ b/erts/emulator/hipe/hipe_arm_glue.S
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -342,6 +342,7 @@ nbif_4_gc_after_bif:
str r1, [P, #P_NARITY]
str TEMP_LR, [P, #P_NRA]
str NSP, [P, #P_NSP]
+ SET_GC_SAFE(TEMP_LR)
mov TEMP_LR, lr
mov r3, #0 /* Pass 0 in arity */
mov r2, #0 /* Pass NULL in regs */
@@ -349,6 +350,7 @@ nbif_4_gc_after_bif:
mov r0, P
bl erts_gc_after_bif_call
mov lr, TEMP_LR
+ SET_GC_UNSAFE(TEMP_LR)
ldr TEMP_LR, [P, #P_NRA]
mov r1, #0
str r1, [P, #P_NARITY]
@@ -404,6 +406,7 @@ nbif_4_simple_exception:
str NSP, [P, #P_NSP]
str TEMP_LR, [P, #P_NRA]
str r1, [P, #P_NARITY]
+ SET_GC_SAFE(r0)
/* find and prepare to invoke the handler */
mov r0, P
bl hipe_handle_exception /* Note: hipe_handle_exception() conses */
@@ -423,6 +426,7 @@ nbif_4_simple_exception:
str NSP, [P, #P_NSP]
str r1, [P, #P_NARITY]
str TEMP_LR, [P, #P_NRA]
+ SET_GC_SAFE(NSP)
b .nosave_exit
/*
diff --git a/erts/emulator/hipe/hipe_arm_glue.h b/erts/emulator/hipe/hipe_arm_glue.h
index 39bc27ec5f..dffc5aef03 100644
--- a/erts/emulator/hipe/hipe_arm_glue.h
+++ b/erts/emulator/hipe/hipe_arm_glue.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_arm_primops.h b/erts/emulator/hipe/hipe_arm_primops.h
index 0e8f84dfb9..c080b67699 100644
--- a/erts/emulator/hipe/hipe_arm_primops.h
+++ b/erts/emulator/hipe/hipe_arm_primops.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c
index c838150db8..c0ebc34771 100644
--- a/erts/emulator/hipe/hipe_bif0.c
+++ b/erts/emulator/hipe/hipe_bif0.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2013. 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.
@@ -418,6 +418,8 @@ BIF_RETTYPE hipe_bifs_enter_code_2(BIF_ALIST_2)
BIF_RET(make_tuple(hp));
}
+#define IS_POWER_OF_TWO(Val) (((Val) > 0) && (((Val) & ((Val)-1)) == 0))
+
/*
* Allocate memory for arbitrary non-Erlang data.
*/
@@ -427,16 +429,18 @@ BIF_RETTYPE hipe_bifs_alloc_data_2(BIF_ALIST_2)
void *block;
if (is_not_small(BIF_ARG_1) || is_not_small(BIF_ARG_2) ||
- (align = unsigned_val(BIF_ARG_1),
- align != sizeof(long) && align != sizeof(double)))
+ (align = unsigned_val(BIF_ARG_1), !IS_POWER_OF_TWO(align)))
BIF_ERROR(BIF_P, BADARG);
nrbytes = unsigned_val(BIF_ARG_2);
if (nrbytes == 0)
BIF_RET(make_small(0));
block = erts_alloc(ERTS_ALC_T_HIPE, nrbytes);
- if ((unsigned long)block & (align-1))
+ if ((unsigned long)block & (align-1)) {
fprintf(stderr, "%s: erts_alloc(%lu) returned %p which is not %lu-byte aligned\r\n",
__FUNCTION__, (unsigned long)nrbytes, block, (unsigned long)align);
+ erts_free(ERTS_ALC_T_HIPE, block);
+ BIF_ERROR(BIF_P, EXC_NOTSUP);
+ }
BIF_RET(address_to_term(block, BIF_P));
}
@@ -488,7 +492,7 @@ static void *const_term_alloc(void *tmpl)
alloc_size = size + (offsetof(struct const_term, mem)/sizeof(Eterm));
hipe_constants_size += alloc_size;
- p = (struct const_term*)erts_alloc(ERTS_ALC_T_HIPE, alloc_size * sizeof(Eterm));
+ p = (struct const_term*)erts_alloc(ERTS_ALC_T_LITERAL, alloc_size * sizeof(Eterm));
/* I have absolutely no idea if having a private 'off_heap'
works or not. _Some_ off_heap object is required for
@@ -497,6 +501,8 @@ static void *const_term_alloc(void *tmpl)
hp = &p->mem[0];
p->val = copy_struct(obj, size, &hp, &const_term_table_off_heap);
+ erts_set_literal_tag(&p->val, &p->mem[0], size);
+
return &p->bucket;
}
@@ -507,6 +513,9 @@ static void init_const_term_table(void)
f.cmp = (HCMP_FUN) const_term_cmp;
f.alloc = (HALLOC_FUN) const_term_alloc;
f.free = (HFREE_FUN) NULL;
+ f.meta_alloc = (HMALLOC_FUN) erts_alloc;
+ f.meta_free = (HMFREE_FUN) erts_free;
+ f.meta_print = (HMPRINT_FUN) erts_print;
hash_init(ERTS_ALC_T_HIPE, &const_term_table, "const_term_table", 97, f);
}
@@ -528,13 +537,13 @@ BIF_RETTYPE hipe_bifs_merge_term_1(BIF_ALIST_1)
BIF_RET(val);
}
-struct mfa {
+struct mfa_t {
Eterm mod;
Eterm fun;
Uint ari;
};
-static int term_to_mfa(Eterm term, struct mfa *mfa)
+static int term_to_mfa(Eterm term, struct mfa_t *mfa)
{
Eterm mod, fun, a;
Uint ari;
@@ -574,15 +583,15 @@ static void print_mfa(Eterm mod, Eterm fun, unsigned int ari)
static Uint *hipe_find_emu_address(Eterm mod, Eterm name, unsigned int arity)
{
Module *modp;
- Uint *code_base;
+ BeamCodeHeader* code_hdr;
int i, n;
modp = erts_get_module(mod, erts_active_code_ix());
- if (modp == NULL || (code_base = modp->curr.code) == NULL)
+ if (modp == NULL || (code_hdr = modp->curr.code_hdr) == NULL)
return NULL;
- n = code_base[MI_NUM_FUNCTIONS];
+ n = code_hdr->num_functions;
for (i = 0; i < n; ++i) {
- Uint *code_ptr = (Uint*)code_base[MI_FUNCTIONS+i];
+ Uint *code_ptr = (Uint*)code_hdr->functions[i];
ASSERT(code_ptr[0] == BeamOpCode(op_i_func_info_IaaI));
if (code_ptr[3] == name && code_ptr[4] == arity)
return code_ptr+5;
@@ -592,7 +601,7 @@ static Uint *hipe_find_emu_address(Eterm mod, Eterm name, unsigned int arity)
Uint *hipe_bifs_find_pc_from_mfa(Eterm term)
{
- struct mfa mfa;
+ struct mfa_t mfa;
if (!term_to_mfa(term, &mfa))
return NULL;
@@ -612,7 +621,7 @@ BIF_RETTYPE hipe_bifs_set_native_address_3(BIF_ALIST_3)
Eterm *pc;
void *address;
int is_closure;
- struct mfa mfa;
+ struct mfa_t mfa;
switch (BIF_ARG_3) {
case am_false:
@@ -688,7 +697,7 @@ static struct nbif nbifs[BIF_SIZE] = {
#undef BIF_LIST
};
-#define NBIF_HASH(m,f,a) ((m)*(f)+(a))
+#define NBIF_HASH(m,f,a) (atom_val(m) ^ atom_val(f) ^ (a))
static Hash nbif_table;
static HashValue nbif_hash(struct nbif *x)
@@ -715,6 +724,9 @@ static void init_nbif_table(void)
f.cmp = (HCMP_FUN) nbif_cmp;
f.alloc = (HALLOC_FUN) nbif_alloc;
f.free = NULL;
+ f.meta_alloc = (HMALLOC_FUN) erts_alloc;
+ f.meta_free = (HMFREE_FUN) erts_free;
+ f.meta_print = (HMPRINT_FUN) erts_print;
hash_init(ERTS_ALC_T_NBIF_TABLE, &nbif_table, "nbif_table", 500, f);
@@ -808,6 +820,9 @@ static void init_primop_table(void)
f.cmp = (HCMP_FUN) primop_cmp;
f.alloc = (HALLOC_FUN) primop_alloc;
f.free = NULL;
+ f.meta_alloc = (HMALLOC_FUN) erts_alloc;
+ f.meta_free = (HMFREE_FUN) erts_free;
+ f.meta_print = (HMPRINT_FUN) erts_print;
hash_init(ERTS_ALC_T_HIPE, &primop_table, "primop_table", 50, f);
@@ -977,7 +992,7 @@ BIF_RETTYPE hipe_bifs_set_native_address_in_fe_2(BIF_ALIST_2)
BIF_ERROR(BIF_P, BADARG);
fe->native_address = native_address;
- if (erts_refc_dectest(&fe->refc, 0) == 0)
+ if (erts_smp_refc_dectest(&fe->refc, 0) == 0)
erts_erase_fun_entry(fe);
BIF_RET(am_true);
}
@@ -1048,7 +1063,7 @@ static inline void hipe_mfa_info_table_rwunlock(void)
erts_smp_rwmtx_rwunlock(&hipe_mfa_info_table.lock);
}
-#define HIPE_MFA_HASH(M,F,A) ((M) * (F) + (A))
+#define HIPE_MFA_HASH(M,F,A) (atom_val(M) ^ atom_val(F) ^ (A))
static struct hipe_mfa_info **hipe_mfa_info_table_alloc_bucket(unsigned int size)
{
@@ -1129,10 +1144,13 @@ static inline struct hipe_mfa_info *hipe_mfa_info_table_get_locked(Eterm m, Eter
h = HIPE_MFA_HASH(m, f, arity);
i = h & hipe_mfa_info_table.mask;
p = hipe_mfa_info_table.bucket[i];
- for (; p; p = p->bucket.next)
- /* XXX: do we want to compare p->bucket.hvalue as well? */
- if (p->m == m && p->f == f && p->a == arity)
- return p;
+ for (; p; p = p->bucket.next) {
+ if (p->bucket.hvalue == h) {
+ if (p->m == m && p->f == f && p->a == arity)
+ return p;
+ }
+ else ASSERT(!(p->m == m && p->f == f && p->a == arity));
+ }
return NULL;
}
@@ -1156,10 +1174,13 @@ static struct hipe_mfa_info *hipe_mfa_info_table_put_rwlocked(Eterm m, Eterm f,
h = HIPE_MFA_HASH(m, f, arity);
i = h & hipe_mfa_info_table.mask;
p = hipe_mfa_info_table.bucket[i];
- for (; p; p = p->bucket.next)
- /* XXX: do we want to compare p->bucket.hvalue as well? */
- if (p->m == m && p->f == f && p->a == arity)
- return p;
+ for (; p; p = p->bucket.next) {
+ if (p->bucket.hvalue == h) {
+ if (p->m == m && p->f == f && p->a == arity)
+ return p;
+ }
+ else ASSERT(!(p->m == m && p->f == f && p->a == arity));
+ }
p = hipe_mfa_info_table_alloc(m, f, arity);
p->bucket.hvalue = h;
p->bucket.next = hipe_mfa_info_table.bucket[i];
@@ -1214,7 +1235,7 @@ void hipe_mfa_set_trampoline(Eterm m, Eterm f, unsigned int arity, void *trampol
BIF_RETTYPE hipe_bifs_set_funinfo_native_address_3(BIF_ALIST_3)
{
- struct mfa mfa;
+ struct mfa_t mfa;
void *address;
int is_exported;
@@ -1236,7 +1257,7 @@ BIF_RETTYPE hipe_bifs_set_funinfo_native_address_3(BIF_ALIST_3)
BIF_RETTYPE hipe_bifs_invalidate_funinfo_native_addresses_1(BIF_ALIST_1)
{
Eterm lst;
- struct mfa mfa;
+ struct mfa_t mfa;
struct hipe_mfa_info *p;
hipe_mfa_info_table_rwlock();
@@ -1405,7 +1426,7 @@ BIF_RETTYPE hipe_find_na_or_make_stub(BIF_ALIST_3)
BIF_RETTYPE hipe_bifs_find_na_or_make_stub_2(BIF_ALIST_2)
{
- struct mfa mfa;
+ struct mfa_t mfa;
void *address;
int is_remote;
@@ -1455,6 +1476,10 @@ int hipe_find_mfa_from_ra(const void *ra, Eterm *m, Eterm *f, unsigned int *a)
struct hipe_mfa_info **bucket;
unsigned int i, nrbuckets;
+ if (hipe_is_ra_mode_switch(ra)) {
+ return 0;
+ }
+
/* Note about locking: the table is only updated from the
loader, which runs with the rest of the system suspended. */
/* XXX: alas not true; see comment at hipe_mfa_info_table.lock */
@@ -1507,9 +1532,9 @@ struct ref {
*/
BIF_RETTYPE hipe_bifs_add_ref_2(BIF_ALIST_2)
{
- struct mfa callee;
+ struct mfa_t callee;
Eterm *tuple;
- struct mfa caller;
+ struct mfa_t caller;
void *address;
void *trampoline;
unsigned int flags;
@@ -1586,7 +1611,7 @@ BIF_RETTYPE hipe_bifs_add_ref_2(BIF_ALIST_2)
*/
BIF_RETTYPE hipe_bifs_mark_referred_from_1(BIF_ALIST_1) /* get_refs_from */
{
- struct mfa mfa;
+ struct mfa_t mfa;
const struct hipe_mfa_info *p;
struct ref *ref;
@@ -1638,7 +1663,7 @@ static void hipe_purge_all_refs(void)
BIF_RETTYPE hipe_bifs_remove_refs_from_1(BIF_ALIST_1)
{
- struct mfa mfa;
+ struct mfa_t mfa;
struct hipe_mfa_info *caller_mfa, *callee_mfa;
struct hipe_mfa_info_list *refers_to, *tmp_refers_to;
struct ref **prev, *ref;
@@ -1692,7 +1717,7 @@ BIF_RETTYPE hipe_bifs_remove_refs_from_1(BIF_ALIST_1)
*/
BIF_RETTYPE hipe_bifs_redirect_referred_from_1(BIF_ALIST_1)
{
- struct mfa mfa;
+ struct mfa_t mfa;
struct hipe_mfa_info *p;
struct ref **prev, *ref;
int is_remote, res;
@@ -1826,6 +1851,9 @@ static void init_modinfo_table(void)
f.cmp = (HCMP_FUN) modinfo_cmp;
f.alloc = (HALLOC_FUN) modinfo_alloc;
f.free = (HFREE_FUN) NULL;
+ f.meta_alloc = (HMALLOC_FUN) erts_alloc;
+ f.meta_free = (HMFREE_FUN) erts_free;
+ f.meta_print = (HMPRINT_FUN) erts_print;
hash_init(ERTS_ALC_T_HIPE, &modinfo_table, "modinfo_table", 11, f);
}
diff --git a/erts/emulator/hipe/hipe_bif0.h b/erts/emulator/hipe/hipe_bif0.h
index 5527c6c3ed..c9a8216368 100644
--- a/erts/emulator/hipe/hipe_bif0.h
+++ b/erts/emulator/hipe/hipe_bif0.h
@@ -2,7 +2,7 @@
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_bif0.tab b/erts/emulator/hipe/hipe_bif0.tab
index 5ce254314a..99237aae05 100644
--- a/erts/emulator/hipe/hipe_bif0.tab
+++ b/erts/emulator/hipe/hipe_bif0.tab
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2011. All Rights Reserved.
+# Copyright Ericsson AB 2001-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_bif1.c b/erts/emulator/hipe/hipe_bif1.c
index a2682992a4..5e127755c6 100644
--- a/erts/emulator/hipe/hipe_bif1.c
+++ b/erts/emulator/hipe/hipe_bif1.c
@@ -2,7 +2,7 @@
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -124,757 +124,3 @@ BIF_RETTYPE hipe_bifs_trap_count_clear_0(BIF_ALIST_0)
hipe_trap_count = 0;
BIF_RET(make_small(count));
}
-
-/*****************************************************************************
- * BIFs for benchmarking. These only do useful things if
- * __BENCHMARK__ is defined in beam/benchmark.h. For documentation
- * about how to add new counters or maintain the existing counters,
- * see benchmark.h.
- *
- * If benchmarking is not enabled all BIFs will return false. If the
- * required benchmark feature is not enabled, the counter will remain
- * zero.
- *
- * process_info/0 -> { Number of live processes,
- * Processes spawned in total }
- *
- * Live processes are increased when a new process is created, and
- * decreased when a process dies. Processes spawned is increased
- * when a process is created.
- *
- *
- * process_info_clear/0 -> true
- *
- * Will reset the processes spawned-counters to zero. If this is
- * done at some improper time, live processes may become a negative
- * value. This is not a problem in itself, just as long as you know
- * about it.
- *
- *
- * message_info/0 -> { Messages sent,
- * Messages copied,
- * Ego messages (sender = receiver),
- * Words sent,
- * Words copied,
- * Words preallocated }
- *
- * Counting the words sent in a shared heap system will affect
- * runtime performance since it means that we have to calculate the
- * size of the mesage. With private heaps, this is done anyway and
- * will not affect performance.
- *
- *
- * message_info_clear/0 -> true
- *
- * Reset the message counters to zero.
- *
- *
- * message_sizes/0 -> true
- *
- * Displays a text-mode bar diagram with message sizes. There are no
- * guaranties that this is printed in a way the Erlang system is
- * supposed to print things.
- *
- *
- * gc_info/0 -> { Minor collections,
- * Major collections,
- * Used heap,
- * Allocated heap,
- * Max used heap,
- * Max allocated heap }
- *
- * Information about private heap garbage collections. Number of
- * minor and major collections, how much heap is used and allocated
- * and how much heap has been in use and allocated at most since the
- * counters were reset.
- *
- *
- * shared_gc_info/0 -> { Minor collections of the shared heap,
- * Major collections of the shared heap,
- * Used shared heap,
- * Allocated shared heap,
- * Max used shared heap,
- * Max allocated shared heap }
- *
- * The same as above, but for the shared heap / message area. Note,
- * that in a shared heap system the max used heap and max allocated
- * heap are mostly the same, since the heap allways is filled before
- * a garbage collection, and most garbage collections do not enlarge
- * the heap. The private heap numbers are much more interesting.
- *
- *
- * incremental_gc_info/0 -> { Complete minor GC cycles,
- * Complete major GC cycles,
- * Minor GC stages,
- * Major GC stages }
- *
- *
- * gc_info_clear/0 -> true
- *
- * Reset counters for both private and shared garbage collection.
- *
- *
- * BM Timers
- * ---------
- *
- * All timers returns tuples of the kind: { Minutes, Seconds, Milliseconds }
- * except for the max times in garbage collection where times are normally
- * small. The tuple is therefor: { Seconds, Milliseconds, Microseconds }
- *
- * system_timer/0 -> Mutator time
- *
- * This timer is not a real-time clock, it only runs when a process
- * is scheduled to run. You can not find out the accual time a
- * program has taken to run using this timer.
- *
- *
- * system_timer_clear/0 -> true
- *
- * Reset system timer to zero.
- *
- *
- * send_timer/0 -> { Send time,
- * Copy time,
- * Size time }
- *
- * Time spent in sending messages. The copy time and size time are
- * only active if the copying is needed in send. Copying of data
- * into ETS-tables etc is not timed with this timer.
- *
- *
- * send_timer_clear/0 -> true
- *
- * Reset send timers to zero.
- *
- *
- * gc_timer/0 -> { Time in minor collection,
- * Time in major collection,
- * Max time in minor collection (�s),
- * Max time in major collection (�s) }
- *
- * Total time spent in garbage collection of the private heaps. The
- * max times are for one separate collection.
- *
- *
- * shared_gc_timer/0 -> { Time in minor collection,
- * Time in major collection,
- * Max time in minor collection (�s),
- * Max time in major collection (�s) }
- *
- * Total time spent in garbage collection of the shared heap /
- * message area. The max times are for one separate collection.
- *
- *
- * gc_timer_clear/0 -> true
- *
- * Reset private and shared garbage collection timers to zero. Note,
- * that the max-times are also reset.
- *
- *
- * misc_timer/0 -> { Misc 0, Misc 1, Misc 2 }
- *
- * Timers for debug purposes. In a normal system, these timers are
- * never used. Add these timers at places where you want to time
- * something not covered here. Use BM_SWAP_TIMER(from,to) to start
- * one of the misc timers.
- *
- * ... code timed by the system timer ...
- * BM_SWAP_TIMER(system,misc1);
- * ... code we want to time ...
- * BM_SWAP_TIMER(misc1,system);
- * ... back on system time ...
- *
- *
- * misc_timer_clear/0 -> true
- *
- * Reset misc timers to zero.
- */
-
-BIF_RETTYPE hipe_bifs_process_info_0(BIF_ALIST_0)
-{
-#ifdef __BENCHMARK__
-#ifndef BM_COUNTERS
- Uint processes_busy = 0;
- Uint processes_spawned = 0;
-#endif
- Eterm *hp;
-
- hp = HAlloc(BIF_P, 3);
- BIF_RET(TUPLE2(hp,
- make_small(processes_busy),
- make_small(processes_spawned)));
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_process_info_clear_0(BIF_ALIST_0)
-{
-#ifdef __BENCHMARK__
-#ifdef BM_COUNTERS
- processes_spawned = 0;
-#endif
- BIF_RET(am_true);
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_message_info_0(BIF_ALIST_0)
-{
-#ifdef __BENCHMARK__
- Eterm *hp;
-#ifndef BM_COUNTERS
- unsigned long messages_sent = 0;
- unsigned long messages_copied = 0;
- unsigned long messages_ego = 0;
-#endif
-#ifndef BM_MESSAGE_SIZES
- unsigned long words_sent = 0;
- unsigned long words_copied = 0;
- unsigned long words_prealloc = 0;
-#endif
-
- hp = HAlloc(BIF_P, 7);
- BIF_RET(TUPLE6(hp,
- make_small(messages_sent),
- make_small(messages_copied),
- make_small(messages_ego),
- make_small(words_sent),
- make_small(words_copied),
- make_small(words_prealloc)));
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_message_info_clear_0(BIF_ALIST_0)
-{
-#ifdef __BENCHMARK__
-#ifdef BM_COUNTERS
- messages_sent = 0;
- messages_copied = 0;
- messages_ego = 0;
-#endif
-#ifdef BM_MESSAGE_SIZES
- words_sent = 0;
- words_copied = 0;
- words_prealloc = 0;
- {
- int i;
- for (i = 0; i < 1000; i++)
- message_sizes[i] = 0;
- }
-#endif
- BIF_RET(am_true);
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_message_sizes_0(BIF_ALIST_0)
-{
-#ifdef BM_MESSAGE_SIZES
- int i, j, max = 0;
- int tmp[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
-
- for (i = 0; i < 65; i++) {
- tmp[0] += message_sizes[i];
- if (tmp[0] > max)
- max = tmp[0];
- }
- for (i = 65; i < 999; i++) {
- tmp[i / 100 + 1] += message_sizes[i];
- if (tmp[i / 100 + 1] > max)
- max = tmp[i / 100 + 1];
- }
- tmp[11] = message_sizes[999];
- if (tmp[11] > max)
- max = tmp[11];
- for (i = -1; i < 11; i++) {
- int num = (tmp[i + 1] * 50) / max;
- if (i == -1)
- printf("\n\r 0 - 64: (%6d) |", tmp[0]);
- else if (i == 0)
- printf("\n\r 65 - 99: (%6d) |", tmp[1]);
- else if (i == 10)
- printf("\n\r >= 1000: (%6d) |", tmp[11]);
- else
- printf("\n\r%3d - %3d: (%6d) |", i * 100, i * 100 + 99,
- tmp[i + 1]);
-
- for (j = 0; j < num; j++)
- printf(".");
- }
- printf("\n\r");
-
- BIF_RET(am_true);
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_gc_info_0(BIF_ALIST_0)
-{
-#ifdef __BENCHMARK__
-#ifndef BM_COUNTERS
- Uint minor_gc = 0;
- Uint major_gc = 0;
-#endif
-#ifndef BM_HEAP_SIZES
- Uint max_used_heap = 0;
- Uint max_allocated_heap = 0;
-#endif
- Eterm *hp;
- Uint used_heap = (BIF_P->htop - BIF_P->heap) +
- (OLD_HTOP(BIF_P) - OLD_HEAP(BIF_P)) +
- MBUF_SIZE(BIF_P);
-
- Uint alloc_heap = (BIF_P->hend - BIF_P->heap) +
- (OLD_HEND(BIF_P) - OLD_HEAP(BIF_P)) +
- MBUF_SIZE(BIF_P);
-
- hp = HAlloc(BIF_P, 7);
- BIF_RET(TUPLE6(hp,
- make_small((Uint)minor_gc),
- make_small((Uint)major_gc),
- make_small((Uint)used_heap),
- make_small((Uint)alloc_heap),
- make_small(max_used_heap),
- make_small(max_allocated_heap)));
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_shared_gc_info_0(BIF_ALIST_0)
-{
-#ifdef __BENCHMARK__
-#if !(defined(BM_COUNTERS))
- Uint minor_global_gc = 0;
- Uint major_global_gc = 0;
-#endif
-#ifndef BM_HEAP_SIZES
- Uint max_used_global_heap = 0;
- Uint max_allocated_global_heap = 0;
-#endif
- Eterm *hp;
-
- Uint tmp_used_heap = 0;
- Uint tmp_allocated_heap = 0;
-
- hp = HAlloc(BIF_P, 7);
- BIF_RET(TUPLE6(hp,
- make_small((uint)minor_global_gc),
- make_small((uint)major_global_gc),
- make_small(tmp_used_heap),
- make_small(tmp_allocated_heap),
- make_small(max_used_global_heap),
- make_small(max_allocated_global_heap)));
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_incremental_gc_info_0(BIF_ALIST_0)
-{
-#ifdef __BENCHMARK__
-#if !defined(BM_COUNTERS)
- Uint minor_gc_cycles = 0;
- Uint major_gc_cycles = 0;
- Uint minor_gc_stages = 0;
- Uint major_gc_stages = 0;
-#endif
- Eterm *hp;
-
- hp = HAlloc(BIF_P, 5);
- BIF_RET(TUPLE4(hp,
- make_small(minor_gc_cycles),
- make_small(major_gc_cycles),
- make_small(minor_gc_stages),
- make_small(major_gc_stages)));
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_gc_info_clear_0(BIF_ALIST_0)
-{
-#ifdef __BENCHMARK__
-
-#ifdef BM_COUNTERS
- minor_gc = 0;
- major_gc = 0;
-#endif
-
-#ifdef BM_HEAP_SIZES
- max_used_heap = 0;
- max_allocated_heap = 0;
- max_used_global_heap = 0;
- max_allocated_global_heap = 0;
-#endif
-
- BIF_RET(am_true);
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_pause_times_0(BIF_ALIST_0)
-{
-#ifdef BM_TIMERS
- int i;
- int total_time = 0, n = 0;
- int left = 0, right = 0, mid = 0;
-
- printf("Pause times in minor collection:\r\n");
- for (i = 0; i < MAX_PAUSE_TIME; i++) {
- if (pause_times[i] > 0) {
- printf("%d: %ld\r\n", i, pause_times[i]);
- total_time += pause_times[i] * i;
- n += pause_times[i];
-
- if (i > mid)
- right += pause_times[i];
-
- while (right > left) {
- left += pause_times[mid++];
- right -= pause_times[mid];
- }
- }
- }
-
- printf("Number of collections: %d\r\n", n);
- printf("Total collection time: %d\r\n", total_time);
- if (n > 0)
- printf("Mean pause time: %d\r\n", total_time / n);
-
- printf("Geometrical mean: %d\r\n", mid);
-
- total_time = 0; n = 0;
- left = 0; right = 0; mid = 0;
- printf("Pause times in major collection:\r\n");
- for (i = 0; i < MAX_PAUSE_TIME; i++) {
- if (pause_times_old[i] > 0) {
- printf("%d: %ld\r\n", i, pause_times_old[i]);
- total_time += pause_times_old[i] * i;
- n += pause_times_old[i];
- }
- }
-
- printf("Number of collections: %d\r\n", n);
- printf("Total collection time: %d\r\n", total_time);
- if (n > 0)
- printf("Mean pause time: %d\r\n", total_time / n);
-
- BIF_RET(am_true);
-#else
- BIF_RET(am_false);
-#endif
-}
-
-/* XXX: these macros have free variables */
-#ifdef BM_TIMERS
-#define MAKE_TIME(_timer_) { \
- BM_TIMER_T tmp = _timer_##_time / 1000000; \
- milli = tmp % 1000; \
- tmp /= 1000; \
- sec = tmp % 60; \
- min = tmp / 60; }
-
-#define MAKE_MICRO_TIME(_timer_) { \
- BM_TIMER_T tmp = _timer_##_time / 1000; \
- micro = tmp % 1000; \
- tmp /= 1000; \
- milli = tmp % 1000; \
- sec = tmp / 1000; }
-
-#else
-#define MAKE_TIME(_timer_)
-#define MAKE_MICRO_TIME(_timer_)
-#endif
-
-BIF_RETTYPE hipe_bifs_system_timer_0(BIF_ALIST_0)
-{
-#ifdef __BENCHMARK__
- uint min = 0;
- uint sec = 0;
- uint milli = 0;
- Eterm *hp;
-
- hp = HAlloc(BIF_P, 4);
- MAKE_TIME(system);
- BIF_RET(TUPLE3(hp,
- make_small(min),
- make_small(sec),
- make_small(milli)));
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_system_timer_clear_0(BIF_ALIST_0)
-{
-#ifdef BM_TIMERS
- system_time = 0;
- BIF_RET(am_true);
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_send_timer_0(BIF_ALIST_0)
-{
-#ifdef __BENCHMARK__
- uint min = 0;
- uint sec = 0;
- uint milli = 0;
- Eterm *hp;
- Eterm sendtime, copytime, sizetime;
-
- hp = HAlloc(BIF_P, 4 * 4);
-
- MAKE_TIME(send);
- sendtime = TUPLE3(hp,
- make_small(min),
- make_small(sec),
- make_small(milli));
- hp += 4;
-
- MAKE_TIME(copy);
- copytime = TUPLE3(hp,
- make_small(min),
- make_small(sec),
- make_small(milli));
- hp += 4;
-
- MAKE_TIME(size);
- sizetime = TUPLE3(hp,
- make_small(min),
- make_small(sec),
- make_small(milli));
- hp += 4;
- BIF_RET(TUPLE3(hp, sendtime, copytime, sizetime));
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_send_timer_clear_0(BIF_ALIST_0)
-{
-#ifdef BM_TIMERS
- send_time = 0;
- copy_time = 0;
- size_time = 0;
- BIF_RET(am_true);
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_gc_timer_0(BIF_ALIST_0)
-{
-#ifdef __BENCHMARK__
- Eterm *hp;
- uint min = 0;
- uint sec = 0;
- uint milli = 0;
- uint micro = 0;
- Eterm minor, major, max_min, max_maj;
-
- hp = HAlloc(BIF_P, 4 * 4 + 5);
-
- MAKE_TIME(minor_gc);
- minor = TUPLE3(hp,
- make_small(min),
- make_small(sec),
- make_small(milli));
- hp += 4;
-
- MAKE_TIME(major_gc);
- major = TUPLE3(hp,
- make_small(min),
- make_small(sec),
- make_small(milli));
- hp += 4;
-
- MAKE_MICRO_TIME(max_minor);
- max_min = TUPLE3(hp,
- make_small(sec),
- make_small(milli),
- make_small(micro));
- hp += 4;
-
- MAKE_MICRO_TIME(max_major);
- max_maj = TUPLE3(hp,
- make_small(sec),
- make_small(milli),
- make_small(micro));
- hp += 4;
-
- BIF_RET(TUPLE4(hp, minor, major, max_min, max_maj));
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_shared_gc_timer_0(BIF_ALIST_0)
-{
-#ifdef __BENCHMARK__
- Eterm *hp;
- uint min = 0;
- uint sec = 0;
- uint milli = 0;
- uint micro = 0;
- Eterm minor, major, max_min, max_maj;
-
- hp = HAlloc(BIF_P, 4 * 4 + 5);
-
- MAKE_TIME(minor_global_gc);
- minor = TUPLE3(hp,
- make_small(min),
- make_small(sec),
- make_small(milli));
- hp += 4;
-
- MAKE_TIME(major_global_gc);
- major = TUPLE3(hp,
- make_small(min),
- make_small(sec),
- make_small(milli));
- hp += 4;
-
- MAKE_MICRO_TIME(max_global_minor);
- max_min = TUPLE3(hp,
- make_small(sec),
- make_small(milli),
- make_small(micro));
- hp += 4;
-
- MAKE_MICRO_TIME(max_global_major);
- max_maj = TUPLE3(hp,
- make_small(sec),
- make_small(milli),
- make_small(micro));
- hp += 4;
-
- BIF_RET(TUPLE4(hp, minor, major, max_min, max_maj));
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_gc_timer_clear_0(BIF_ALIST_0)
-{
-#ifdef BM_TIMERS
- minor_gc_time = 0;
- major_gc_time = 0;
- max_minor_time = 0;
- max_major_time = 0;
- minor_global_gc_time = 0;
- major_global_gc_time = 0;
- max_global_minor_time = 0;
- max_global_major_time = 0;
- BIF_RET(am_true);
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_misc_timer_0(BIF_ALIST_0)
-{
-#ifdef __BENCHMARK__
- uint min = 0;
- uint sec = 0;
- uint milli = 0;
- Eterm *hp;
- Eterm misctime1, misctime2, misctime3;
-
- hp = HAlloc(BIF_P, 4 * 4);
-
- MAKE_TIME(misc0);
- misctime1 = TUPLE3(hp,
- make_small(min),
- make_small(sec),
- make_small(milli));
- hp += 4;
-
- MAKE_TIME(misc1);
- misctime2 = TUPLE3(hp,
- make_small(min),
- make_small(sec),
- make_small(milli));
- hp += 4;
-
- MAKE_TIME(misc2);
- misctime3 = TUPLE3(hp,
- make_small(min),
- make_small(sec),
- make_small(milli));
- hp += 4;
- BIF_RET(TUPLE3(hp, misctime1, misctime2, misctime3));
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_misc_timer_clear_0(BIF_ALIST_0)
-{
-#ifdef BM_TIMERS
- misc0_time = 0;
- misc1_time = 0;
- misc2_time = 0;
- BIF_RET(am_true);
-#else
- BIF_RET(am_false);
-#endif
-}
-
-#undef MAKE_TIME
-#undef MAKE_MICRO_TIME
-
-/*
- * HiPE hrvtime().
- * These implementations are currently available:
- * + The fallback, which is the same as {X,_} = runtime(statistics).
- */
-
-static double fallback_get_hrvtime(void)
-{
- unsigned long ms_user;
-
- elapsed_time_both(&ms_user, NULL, NULL, NULL);
- return (double)ms_user;
-}
-
-/*
- * Fallback, if nothing better exists.
- * This is the same as {X,_} = statistics(runtime), which uses
- * times(2) on Unix systems.
- */
-
-#define hrvtime_is_started() 1
-#define start_hrvtime() do{}while(0)
-#define stop_hrvtime() do{}while(0)
-#define get_hrvtime() fallback_get_hrvtime()
-
-BIF_RETTYPE hipe_bifs_get_hrvtime_0(BIF_ALIST_0)
-{
- Eterm *hp;
- Eterm res;
- FloatDef f;
-
- if (!hrvtime_is_started())
- start_hrvtime();
- f.fd = get_hrvtime();
- hp = HAlloc(BIF_P, FLOAT_SIZE_OBJECT);
- res = make_float(hp);
- PUT_DOUBLE(f, hp);
- BIF_RET(res);
-}
-
-BIF_RETTYPE hipe_bifs_stop_hrvtime_0(BIF_ALIST_0)
-{
- stop_hrvtime();
- BIF_RET(am_true);
-}
diff --git a/erts/emulator/hipe/hipe_bif1.h b/erts/emulator/hipe/hipe_bif1.h
index 80cdeb4a82..e12bb1a1f3 100644
--- a/erts/emulator/hipe/hipe_bif1.h
+++ b/erts/emulator/hipe/hipe_bif1.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_bif1.tab b/erts/emulator/hipe/hipe_bif1.tab
index 6c4e05afdb..4be0ad0e9c 100644
--- a/erts/emulator/hipe/hipe_bif1.tab
+++ b/erts/emulator/hipe/hipe_bif1.tab
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2009. All Rights Reserved.
+# Copyright Ericsson AB 2001-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -27,24 +27,3 @@ bif hipe_bifs:call_count_get/1
bif hipe_bifs:call_count_clear/1
bif hipe_bifs:trap_count_get/0
bif hipe_bifs:trap_count_clear/0
-bif hipe_bifs:process_info/0
-bif hipe_bifs:process_info_clear/0
-bif hipe_bifs:message_info/0
-bif hipe_bifs:message_info_clear/0
-bif hipe_bifs:message_sizes/0
-bif hipe_bifs:gc_info/0
-bif hipe_bifs:shared_gc_info/0
-bif hipe_bifs:incremental_gc_info/0
-bif hipe_bifs:gc_info_clear/0
-bif hipe_bifs:pause_times/0
-bif hipe_bifs:system_timer/0
-bif hipe_bifs:system_timer_clear/0
-bif hipe_bifs:send_timer/0
-bif hipe_bifs:send_timer_clear/0
-bif hipe_bifs:gc_timer/0
-bif hipe_bifs:shared_gc_timer/0
-bif hipe_bifs:gc_timer_clear/0
-bif hipe_bifs:misc_timer/0
-bif hipe_bifs:misc_timer_clear/0
-bif hipe_bifs:get_hrvtime/0
-bif hipe_bifs:stop_hrvtime/0
diff --git a/erts/emulator/hipe/hipe_bif2.c b/erts/emulator/hipe/hipe_bif2.c
index 2328e69886..dfd34e31d4 100644
--- a/erts/emulator/hipe/hipe_bif2.c
+++ b/erts/emulator/hipe/hipe_bif2.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_bif2.tab b/erts/emulator/hipe/hipe_bif2.tab
index a29e1fbdbb..bbcb577be0 100644
--- a/erts/emulator/hipe/hipe_bif2.tab
+++ b/erts/emulator/hipe/hipe_bif2.tab
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2012. All Rights Reserved.
+# Copyright Ericsson AB 2001-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_bif64.c b/erts/emulator/hipe/hipe_bif64.c
index b97cf2b7ee..a1a3c81bfe 100644
--- a/erts/emulator/hipe/hipe_bif64.c
+++ b/erts/emulator/hipe/hipe_bif64.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_bif64.h b/erts/emulator/hipe/hipe_bif64.h
index 856b4003dc..a97ce311d3 100644
--- a/erts/emulator/hipe/hipe_bif64.h
+++ b/erts/emulator/hipe/hipe_bif64.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_bif64.tab b/erts/emulator/hipe/hipe_bif64.tab
index 3062ab5848..eefca379a0 100644
--- a/erts/emulator/hipe/hipe_bif64.tab
+++ b/erts/emulator/hipe/hipe_bif64.tab
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2011. All Rights Reserved.
+# Copyright Ericsson AB 2004-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_bif_list.m4 b/erts/emulator/hipe/hipe_bif_list.m4
index 7240280345..dcf3447af9 100644
--- a/erts/emulator/hipe/hipe_bif_list.m4
+++ b/erts/emulator/hipe/hipe_bif_list.m4
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2014. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -96,6 +96,7 @@
* gc_bif_interface_0(nbif_name, cbif_name)
* gc_bif_interface_1(nbif_name, cbif_name)
* gc_bif_interface_2(nbif_name, cbif_name)
+ * gc_bif_interface_3(nbif_name, cbif_name)
*
* A BIF which may do a GC or walk the native stack.
* May read NSP, NSP_LIMIT, NRA, HP, HP_LIMIT, and FCALLS.
@@ -263,32 +264,34 @@ noproc_primop_interface_1(nbif_atomic_inc, hipe_atomic_inc)
',)dnl
/*
- * Standard BIFs.
- * BIF_LIST(ModuleAtom,FunctionAtom,Arity,CFun,Index)
+ * BIFs that disable GC while trapping are called via a wrapper
+ * to reserve stack space for the "trap frame".
+ * They occasionally need to call the garbage collector in order to make room
+ * for the trap frame on the BEAM stack.
*/
+gc_bif_interface_1(nbif_term_to_binary_1, hipe_wrapper_term_to_binary_1)
+gc_bif_interface_2(nbif_term_to_binary_2, hipe_wrapper_term_to_binary_2)
+gc_bif_interface_1(nbif_binary_to_term_1, hipe_wrapper_binary_to_term_1)
+gc_bif_interface_2(nbif_binary_to_term_2, hipe_wrapper_binary_to_term_2)
+gc_bif_interface_1(nbif_binary_to_list_1, hipe_wrapper_binary_to_list_1)
+gc_bif_interface_3(nbif_binary_to_list_3, hipe_wrapper_binary_to_list_3)
+gc_bif_interface_1(nbif_bitstring_to_list_1, hipe_wrapper_bitstring_to_list_1)
+gc_bif_interface_1(nbif_list_to_binary_1, hipe_wrapper_list_to_binary_1)
+gc_bif_interface_1(nbif_iolist_to_binary_1, hipe_wrapper_iolist_to_binary_1)
+gc_bif_interface_1(nbif_binary_list_to_bin_1, hipe_wrapper_binary_list_to_bin_1)
+gc_bif_interface_1(nbif_list_to_bitstring_1, hipe_wrapper_list_to_bitstring_1)
+gc_bif_interface_2(nbif_send_2, hipe_wrapper_send_2)
+gc_bif_interface_3(nbif_send_3, hipe_wrapper_send_3)
+gc_bif_interface_2(nbif_ebif_bang_2, hipe_wrapper_ebif_bang_2)
+gc_bif_interface_2(nbif_maps_merge_2, hipe_wrapper_maps_merge_2)
-/* BIFs that disable GC while trapping are called via a wrapper
- * to reserve stack space for the "trap frame".
+
+/*
+ * Standard BIFs.
+ * BIF_LIST(ModuleAtom,FunctionAtom,Arity,CFun,Index)
*/
-define(CFUN,`ifelse(
-$1, term_to_binary_1, hipe_wrapper_$1,
-$1, term_to_binary_2, hipe_wrapper_$1,
-$1, binary_to_term_1, hipe_wrapper_$1,
-$1, binary_to_term_2, hipe_wrapper_$1,
-$1, binary_to_list_1, hipe_wrapper_$1,
-$1, binary_to_list_3, hipe_wrapper_$1,
-$1, bitstring_to_list_1, hipe_wrapper_$1,
-$1, list_to_binary_1, hipe_wrapper_$1,
-$1, iolist_to_binary_1, hipe_wrapper_$1,
-$1, binary_list_to_bin_1, hipe_wrapper_$1,
-$1, list_to_bitstring_1, hipe_wrapper_$1,
-$1, send_2, hipe_wrapper_$1,
-$1, send_3, hipe_wrapper_$1,
-$1, ebif_bang_2, hipe_wrapper_$1,
-$1, maps_merge_2, hipe_wrapper_$1,
-$1)')
-define(BIF_LIST,`standard_bif_interface_$3(nbif_$4, CFUN($4))')
+define(BIF_LIST,`standard_bif_interface_$3(nbif_$4, $4)')
include(TARGET/`erl_bif_list.h')
/*
diff --git a/erts/emulator/hipe/hipe_debug.c b/erts/emulator/hipe/hipe_debug.c
index a48578c468..ace489452f 100644
--- a/erts/emulator/hipe/hipe_debug.c
+++ b/erts/emulator/hipe/hipe_debug.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -195,7 +195,7 @@ void hipe_print_pcb(Process *p)
U("rcount ", rcount);
U("id ", common.id);
U("reds ", reds);
- U("tracer_pr..", common.tracer_proc);
+ U("tracer ", common.tracer);
U("trace_fla..", common.trace_flags);
U("group_lea..", group_leader);
U("flags ", flags);
diff --git a/erts/emulator/hipe/hipe_debug.h b/erts/emulator/hipe/hipe_debug.h
index bf10d5a2a8..c510eac949 100644
--- a/erts/emulator/hipe/hipe_debug.h
+++ b/erts/emulator/hipe/hipe_debug.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_gbif_list.h b/erts/emulator/hipe/hipe_gbif_list.h
index dc93741a1d..424b001298 100644
--- a/erts/emulator/hipe/hipe_gbif_list.h
+++ b/erts/emulator/hipe/hipe_gbif_list.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_gc.c b/erts/emulator/hipe/hipe_gc.c
index 2c747771ac..68c65dea27 100644
--- a/erts/emulator/hipe/hipe_gc.c
+++ b/erts/emulator/hipe/hipe_gc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -46,9 +46,7 @@ Eterm *fullsweep_nstack(Process *p, Eterm *n_htop)
/* arch-specific nstack walk state */
struct nstack_walk_state walk_state;
- /* fullsweep-specific state */
- char *src, *oh;
- Uint src_size, oh_size;
+ ASSERT(!p->hipe.gc_is_unsafe);
if (!p->hipe.nstack) {
ASSERT(!p->hipe.nsp && !p->hipe.nstend);
@@ -66,11 +64,6 @@ Eterm *fullsweep_nstack(Process *p, Eterm *n_htop)
sdesc = nstack_walk_init_sdesc(p, &walk_state);
- src = (char*)HEAP_START(p);
- src_size = (char*)HEAP_TOP(p) - src;
- oh = (char*)OLD_HEAP(p);
- oh_size = (char*)OLD_HTOP(p) - oh;
-
for (;;) {
if (nstack_walk_nsp_reached_end(nsp, nsp_end)) {
if (nsp == nsp_end) {
@@ -97,8 +90,7 @@ Eterm *fullsweep_nstack(Process *p, Eterm *n_htop)
if (IS_MOVED_BOXED(val)) {
ASSERT(is_boxed(val));
*nsp_i = val;
- } else if (in_area(ptr, src, src_size) ||
- in_area(ptr, oh, oh_size)) {
+ } else if (!erts_is_literal(gval, ptr)) {
MOVE_BOXED(ptr, val, n_htop, nsp_i);
}
} else if (is_list(gval)) {
@@ -106,8 +98,7 @@ Eterm *fullsweep_nstack(Process *p, Eterm *n_htop)
Eterm val = *ptr;
if (IS_MOVED_CONS(val)) {
*nsp_i = ptr[1];
- } else if (in_area(ptr, src, src_size) ||
- in_area(ptr, oh, oh_size)) {
+ } else if (!erts_is_literal(gval, ptr)) {
ASSERT(within(ptr, p));
MOVE_CONS(ptr, val, n_htop, nsp_i);
}
@@ -139,11 +130,15 @@ void gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop)
unsigned int mask;
/* arch-specific nstack walk state */
struct nstack_walk_state walk_state;
+ char *oh;
+ Uint oh_size;
/* gensweep-specific state */
Eterm *old_htop, *n_htop;
- char *heap;
- Uint heap_size, mature_size;
+ char *mature;
+ Uint mature_size;
+
+ ASSERT(!p->hipe.gc_is_unsafe);
if (!p->hipe.nstack) {
ASSERT(!p->hipe.nsp && !p->hipe.nstend);
@@ -168,9 +163,10 @@ void gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop)
old_htop = *ptr_old_htop;
n_htop = *ptr_n_htop;
- heap = (char*)HEAP_START(p);
- heap_size = (char*)HEAP_TOP(p) - heap;
- mature_size = (char*)HIGH_WATER(p) - heap;
+ mature = (char *) (p->abandoned_heap ? p->abandoned_heap : p->heap);
+ mature_size = (char*)HIGH_WATER(p) - mature;
+ oh = (char*)OLD_HEAP(p);
+ oh_size = (char*)OLD_HTOP(p) - oh;
for (;;) {
if (nstack_walk_nsp_reached_end(nsp, nsp_end)) {
@@ -209,9 +205,9 @@ void gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop)
if (IS_MOVED_BOXED(val)) {
ASSERT(is_boxed(val));
*nsp_i = val;
- } else if (in_area(ptr, heap, mature_size)) {
+ } else if (ErtsInArea(ptr, mature, mature_size)) {
MOVE_BOXED(ptr, val, old_htop, nsp_i);
- } else if (in_area(ptr, heap, heap_size)) {
+ } else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) {
ASSERT(within(ptr, p));
MOVE_BOXED(ptr, val, n_htop, nsp_i);
}
@@ -220,9 +216,9 @@ void gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop)
Eterm val = *ptr;
if (IS_MOVED_CONS(val)) {
*nsp_i = ptr[1];
- } else if (in_area(ptr, heap, mature_size)) {
+ } else if (ErtsInArea(ptr, mature, mature_size)) {
MOVE_CONS(ptr, val, old_htop, nsp_i);
- } else if (in_area(ptr, heap, heap_size)) {
+ } else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) {
ASSERT(within(ptr, p));
MOVE_CONS(ptr, val, n_htop, nsp_i);
}
@@ -241,3 +237,122 @@ void gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop)
}
abort();
}
+
+Eterm *sweep_literals_nstack(Process *p, Eterm *old_htop, char *area,
+ Uint area_size)
+{
+ /* known nstack walk state */
+ Eterm *nsp;
+ Eterm *nsp_end;
+ const struct sdesc *sdesc;
+ /* arch-specific nstack walk state */
+ struct nstack_walk_state walk_state;
+
+ ASSERT(!p->hipe.gc_is_unsafe);
+
+ if (!p->hipe.nstack) {
+ ASSERT(!p->hipe.nsp && !p->hipe.nstend);
+ return old_htop;
+ }
+ if (!nstack_walk_init_check(p))
+ return old_htop;
+
+ ASSERT(p->hipe.nsp && p->hipe.nstend);
+ nsp = nstack_walk_nsp_begin(p);
+ nsp_end = nstack_walk_nsp_end(p);
+ sdesc = nstack_walk_init_sdesc_ignore_trap(p, &walk_state);
+
+ while (!nstack_walk_nsp_reached_end(nsp, nsp_end)) {
+ unsigned long ra;
+ unsigned sdesc_size = nstack_walk_frame_size(sdesc);
+ unsigned i = 0;
+ unsigned mask = sdesc->livebits[0];
+ for (;;) {
+ if (mask & 1) {
+ Eterm *nsp_i = nstack_walk_frame_index(nsp, i);
+ Eterm gval = *nsp_i;
+ if (is_boxed(gval)) {
+ Eterm *ptr = boxed_val(gval);
+ Eterm val = *ptr;
+ if (IS_MOVED_BOXED(val)) {
+ ASSERT(is_boxed(val));
+ *nsp_i = val;
+ } else if (ErtsInArea(ptr, area, area_size)) {
+ MOVE_BOXED(ptr, val, old_htop, nsp_i);
+ }
+ } else if (is_list(gval)) {
+ Eterm *ptr = list_val(gval);
+ Eterm val = *ptr;
+ if (IS_MOVED_CONS(val)) {
+ *nsp_i = ptr[1];
+ } else if (ErtsInArea(ptr, area, area_size)) {
+ MOVE_CONS(ptr, val, old_htop, nsp_i);
+ }
+ }
+ }
+ if (++i >= sdesc_size)
+ break;
+ if (i & 31)
+ mask >>= 1;
+ else
+ mask = sdesc->livebits[i >> 5];
+ }
+ ra = nstack_walk_frame_ra(nsp, sdesc);
+ if (ra == (unsigned long)nbif_stack_trap_ra)
+ ra = (unsigned long)p->hipe.ngra;
+ sdesc = hipe_find_sdesc(ra);
+ nsp = nstack_walk_next_frame(nsp, sdesc_size);
+ }
+ return old_htop;
+}
+
+int
+nstack_any_heap_ref_ptrs(Process *rp, char* mod_start, Uint mod_size)
+{
+ Eterm *nsp;
+ Eterm *nsp_end;
+ const struct sdesc *sdesc;
+ /* arch-specific nstack walk state */
+ struct nstack_walk_state walk_state;
+
+ ASSERT(!rp->hipe.gc_is_unsafe);
+
+ if (!rp->hipe.nstack || !nstack_walk_init_check(rp)) return 0;
+ ASSERT(rp->hipe.nsp && rp->hipe.nstend);
+ nsp = nstack_walk_nsp_begin(rp);
+ nsp_end = nstack_walk_nsp_end(rp);
+ sdesc = nstack_walk_init_sdesc_ignore_trap(rp, &walk_state);
+
+ while (!nstack_walk_nsp_reached_end(nsp, nsp_end)) {
+ unsigned long ra;
+ unsigned sdesc_size = nstack_walk_frame_size(sdesc);
+ unsigned i = 0;
+ unsigned mask = sdesc->livebits[0];
+ for (;;) {
+ if (mask & 1) {
+ Eterm *nsp_i = nstack_walk_frame_index(nsp, i);
+ Eterm val = *nsp_i;
+ switch (primary_tag(val)) {
+ case TAG_PRIMARY_BOXED:
+ case TAG_PRIMARY_LIST:
+ if (ErtsInArea(val, mod_start, mod_size)) {
+ return 1;
+ }
+ break;
+ }
+ }
+ if (++i >= sdesc_size)
+ break;
+ if (i & 31)
+ mask >>= 1;
+ else
+ mask = sdesc->livebits[i >> 5];
+ }
+ ra = nstack_walk_frame_ra(nsp, sdesc);
+ if (ra == (unsigned long)nbif_stack_trap_ra)
+ ra = (unsigned long)rp->hipe.ngra;
+ sdesc = hipe_find_sdesc(ra);
+ nsp = nstack_walk_next_frame(nsp, sdesc_size);
+ }
+ return 0;
+}
diff --git a/erts/emulator/hipe/hipe_gc.h b/erts/emulator/hipe/hipe_gc.h
index e373324cdd..5b5eb29175 100644
--- a/erts/emulator/hipe/hipe_gc.h
+++ b/erts/emulator/hipe/hipe_gc.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_mkliterals.c b/erts/emulator/hipe/hipe_mkliterals.c
index dfa5313739..b9d4226705 100644
--- a/erts/emulator/hipe/hipe_mkliterals.c
+++ b/erts/emulator/hipe/hipe_mkliterals.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -525,6 +525,12 @@ static const struct rts_param rts_params[] = {
{ 51, "P_CALLEE_EXP", 1, offsetof(struct process, hipe.u.callee_exp) },
{ 52, "THE_NON_VALUE", 1, (int)THE_NON_VALUE },
+
+ { 53, "P_GCUNSAFE",
+#ifdef DEBUG
+ 1, offsetof(struct process, hipe.gc_is_unsafe)
+#endif
+ },
};
#define NR_PARAMS ARRAY_SIZE(rts_params)
diff --git a/erts/emulator/hipe/hipe_mode_switch.c b/erts/emulator/hipe/hipe_mode_switch.c
index 9243e029f6..ed95045292 100644
--- a/erts/emulator/hipe/hipe_mode_switch.c
+++ b/erts/emulator/hipe/hipe_mode_switch.c
@@ -2,7 +2,7 @@
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2014. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -59,6 +59,7 @@
* TODO: check PCB consistency at native BIF calls
*/
int hipe_modeswitch_debug = 0;
+extern BeamInstr beam_exit[];
#define HIPE_DEBUG 0
@@ -173,6 +174,33 @@ void hipe_mode_switch_init(void)
make_catch(beam_catches_cons(hipe_beam_pc_throw, BEAM_CATCHES_NIL));
hipe_mfa_info_table_init();
+
+#if (defined(__i386__) || defined(__x86_64__)) && defined(__linux__)
+ /* Verify that the offset of c-p->hipe does not change.
+ The ErLLVM hipe backend depends on it being in a specific
+ position. Kostis et al has promised to fix this in upstream
+ llvm by OTP 20, so it should be possible to remove these asserts
+ after that. */
+ ERTS_CT_ASSERT(sizeof(ErtsPTabElementCommon) ==
+ (sizeof(Eterm) + /* id */
+ sizeof(((ErtsPTabElementCommon*)0)->refc) +
+ sizeof(ErtsTracer) + /* tracer */
+ sizeof(Uint) + /* trace_flags */
+ sizeof(erts_smp_atomic_t) + /* timer */
+ sizeof(((ErtsPTabElementCommon*)0)->u)));
+
+ ERTS_CT_ASSERT(offsetof(Process, hipe) ==
+ (sizeof(ErtsPTabElementCommon) + /* common */
+ sizeof(Eterm*) + /* htop */
+ sizeof(Eterm*) + /* stop */
+ sizeof(Eterm*) + /* heap */
+ sizeof(Eterm*) + /* hend */
+ sizeof(Uint) + /* heap_sz */
+ sizeof(Uint) + /* min_heap_size */
+ sizeof(Uint) + /* min_vheap_size */
+ sizeof(volatile unsigned long))); /* fp_exception */
+#endif
+
}
void hipe_set_call_trap(Uint *bfun, void *nfun, int is_closure)
@@ -196,7 +224,7 @@ hipe_push_beam_trap_frame(Process *p, Eterm reg[], unsigned arity)
ASSERT(!(p->flags & F_DISABLE_GC));
if ((p->stop - 2) < p->htop) {
DPRINTF("calling gc to increase BEAM stack size");
- p->fcalls -= erts_garbage_collect(p, 2, reg, arity);
+ erts_garbage_collect(p, 2, reg, arity);
ASSERT(!((p->stop - 2) < p->htop));
}
p->stop -= 2;
@@ -218,15 +246,37 @@ static __inline__ void hipe_pop_beam_trap_frame(Process *p)
Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])
{
unsigned result;
-#if NR_ARG_REGS > 5
- /* When NR_ARG_REGS > 5, we need to protect the process' input
- reduction count (which BEAM stores in def_arg_reg[5]) from
- being clobbered by the arch glue code. */
Eterm reds_in = p->def_arg_reg[5];
-#endif
-#if NR_ARG_REGS > 4
- Eterm o_reds = p->def_arg_reg[4];
-#endif
+ /*
+ * Process is in the normal case scheduled out when reduction
+ * count reach zero. When "save calls" is enabled reduction
+ * count is subtracted with CONTEXT_REDS, i.e. initial reduction
+ * count will be zero or less and process is scheduled out
+ * when -CONTEXT_REDS is reached.
+ *
+ * HiPE does not support the "save calls" feature, so we switch
+ * to using a positive reduction counter when executing in
+ * hipe mode, but need to restore the "save calls" when
+ * returning to beam. We also need to hide the save calls buffer
+ * from BIFs. We do that by moving the saved calls buf to
+ * suspended saved calls buf.
+ *
+ * Beam has initial reduction count in stored in p->def_arg_reg[5].
+ *
+ * Beam expects -neg_o_reds to be found in p->def_arg_reg[4]
+ * on return to beam.
+ */
+
+ {
+ struct saved_calls *scb = ERTS_PROC_SET_SAVED_CALLS_BUF(p, NULL);
+ if (scb) {
+ reds_in += CONTEXT_REDS;
+ p->fcalls += CONTEXT_REDS;
+ ERTS_PROC_SET_SUSPENDED_SAVED_CALLS_BUF(p, scb);
+ }
+ }
+
+ p->flags |= F_HIPE_MODE; /* inform bifs where we are comming from... */
p->i = NULL;
/* Set current_function to undefined. stdlib hibernate tests rely on it. */
@@ -481,12 +531,25 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])
erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_MSG_RECEIVE);
do_schedule:
{
-#if !(NR_ARG_REGS > 5)
- int reds_in = p->def_arg_reg[5];
+ struct saved_calls *scb;
+
+ scb = ERTS_PROC_SET_SUSPENDED_SAVED_CALLS_BUF(p, NULL);
+ if (scb)
+ ERTS_PROC_SET_SAVED_CALLS_BUF(p, scb);
+
+ /* The process may have died while it was executing,
+ if so we return out from native code to the interpreter */
+ if (erts_smp_atomic32_read_nob(&p->state) & ERTS_PSFLG_EXITING)
+ p->i = beam_exit;
+#ifdef DEBUG
+ ASSERT(p->debug_reds_in == reds_in);
#endif
+ p->flags &= ~F_HIPE_MODE;
+
ERTS_SMP_UNREQ_PROC_MAIN_LOCK(p);
- p = schedule(p, reds_in - p->fcalls);
+ p = erts_schedule(NULL, p, reds_in - p->fcalls);
ERTS_SMP_REQ_PROC_MAIN_LOCK(p);
+ ASSERT(!(p->flags & F_HIPE_MODE));
#ifdef ERTS_SMP
p->hipe_smp.have_receive_locks = 0;
reg = p->scheduler_data->x_reg_array;
@@ -501,28 +564,32 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])
reg[i] = argp[i];
}
{
-#if !(NR_ARG_REGS > 5)
- Eterm reds_in;
-#endif
-#if !(NR_ARG_REGS > 4)
- Eterm o_reds;
-#endif
+ struct saved_calls *scb;
reds_in = p->fcalls;
- o_reds = 0;
- if (ERTS_PROC_GET_SAVED_CALLS_BUF(p)) {
- o_reds = reds_in;
- reds_in = 0;
- p->fcalls = 0;
- }
- p->def_arg_reg[4] = o_reds;
p->def_arg_reg[5] = reds_in;
+#ifdef DEBUG
+ p->debug_reds_in = reds_in;
+#endif
if (p->i == hipe_beam_pc_resume) {
+ p->flags |= F_HIPE_MODE; /* inform bifs where we are comming from... */
+ scb = ERTS_PROC_SET_SAVED_CALLS_BUF(p, NULL);
+ if (scb)
+ ERTS_PROC_SET_SUSPENDED_SAVED_CALLS_BUF(p, scb);
p->i = NULL;
p->arity = 0;
goto do_resume;
}
+
+ scb = ERTS_PROC_GET_SAVED_CALLS_BUF(p);
+ if (!scb)
+ p->def_arg_reg[4] = 0;
+ else {
+ p->def_arg_reg[4] = CONTEXT_REDS;
+ p->fcalls = -CONTEXT_REDS + reds_in;
+ }
}
+
HIPE_CHECK_PCB(p);
result = HIPE_MODE_SWITCH_RES_CALL_BEAM;
p->def_arg_reg[3] = result;
@@ -562,14 +629,29 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])
default:
erts_exit(ERTS_ERROR_EXIT, "hipe_mode_switch: result %#x\r\n", result);
}
+
+ {
+ struct saved_calls *scb = ERTS_PROC_SET_SUSPENDED_SAVED_CALLS_BUF(p, NULL);
+ if (!scb)
+ p->def_arg_reg[4] = 0;
+ else {
+ p->def_arg_reg[4] = CONTEXT_REDS;
+ p->fcalls -= CONTEXT_REDS;
+ ERTS_PROC_SET_SAVED_CALLS_BUF(p, scb);
+ }
+ }
+
HIPE_CHECK_PCB(p);
p->def_arg_reg[3] = result;
-#if NR_ARG_REGS > 4
- p->def_arg_reg[4] = o_reds;
-#endif
#if NR_ARG_REGS > 5
+ /*
+ * When NR_ARG_REGS > 5, we need to protect the process' input
+ * reduction count (which BEAM stores in def_arg_reg[5]) from
+ * being clobbered by the arch glue code.
+ */
p->def_arg_reg[5] = reds_in;
#endif
+ p->flags &= ~F_HIPE_MODE;
return p;
}
diff --git a/erts/emulator/hipe/hipe_mode_switch.h b/erts/emulator/hipe/hipe_mode_switch.h
index bc863a4f36..e54b81cf78 100644
--- a/erts/emulator/hipe/hipe_mode_switch.h
+++ b/erts/emulator/hipe/hipe_mode_switch.h
@@ -2,7 +2,7 @@
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -64,6 +64,7 @@ Eterm hipe_build_stacktrace(Process *p, struct StackTrace *s);
ERTS_GLB_INLINE void hipe_reserve_beam_trap_frame(Process*, Eterm reg[], unsigned arity);
ERTS_GLB_INLINE void hipe_unreserve_beam_trap_frame(Process*);
+ERTS_GLB_INLINE int hipe_is_ra_mode_switch(const void* ra);
extern Uint hipe_beam_pc_return[];
extern Uint hipe_beam_pc_throw[];
@@ -95,7 +96,7 @@ ERTS_GLB_INLINE void hipe_reserve_beam_trap_frame(Process *p, Eterm reg[], unsig
/* ensure that at least 2 words are available on the BEAM stack */
if ((p->stop - 2) < p->htop) {
- p->fcalls -= erts_garbage_collect(p, 2, reg, arity);
+ erts_garbage_collect(p, 2, reg, arity);
ASSERT(!((p->stop - 2) < p->htop));
}
p->stop -= 2;
@@ -112,6 +113,11 @@ ERTS_GLB_INLINE void hipe_unreserve_beam_trap_frame(Process *p)
p->stop += 2;
}
+ERTS_GLB_INLINE int hipe_is_ra_mode_switch(const void* ra)
+{
+ return ra == nbif_return;
+}
+
#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
#endif /* ASM */
diff --git a/erts/emulator/hipe/hipe_native_bif.c b/erts/emulator/hipe/hipe_native_bif.c
index 994f963ece..860791074d 100644
--- a/erts/emulator/hipe/hipe_native_bif.c
+++ b/erts/emulator/hipe/hipe_native_bif.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2013. 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.
@@ -80,7 +80,7 @@ Eterm hipe_show_nstack_1(BIF_ALIST_1)
void hipe_gc(Process *p, Eterm need)
{
hipe_set_narity(p, 1);
- p->fcalls -= erts_garbage_collect(p, unsigned_val(need), NULL, 0);
+ erts_garbage_collect(p, unsigned_val(need), NULL, 0);
hipe_set_narity(p, 0);
}
@@ -157,13 +157,22 @@ BIF_RETTYPE hipe_set_timeout(BIF_ALIST_1)
*/
void hipe_select_msg(Process *p)
{
- ErlMessage *msgp;
+ ErtsMessage *msgp;
msgp = PEEK_MESSAGE(p);
UNLINK_MESSAGE(p, msgp); /* decrements global 'erts_proc_tot_mem' variable */
JOIN_MESSAGE(p);
CANCEL_TIMER(p); /* calls erts_cancel_proc_timer() */
- free_message(msgp);
+ erts_save_message_in_proc(p, msgp);
+ p->flags &= ~F_DELAY_GC;
+ if (ERTS_IS_GC_DESIRED(p)) {
+ /*
+ * We want to GC soon but we leave a few
+ * reductions giving the message some time
+ * to turn into garbage.
+ */
+ ERTS_VBUMP_LEAVE_REDS(p, 5);
+ }
}
void hipe_fclearerror_error(Process *p)
@@ -217,11 +226,6 @@ void hipe_handle_exception(Process *c_p)
ASSERT(c_p->freason != TRAP); /* Should have been handled earlier. */
- if (c_p->mbuf) {
- erts_printf("%s line %u: p==%p, p->mbuf==%p\n", __FUNCTION__, __LINE__, c_p, c_p->mbuf);
- /* erts_garbage_collect(c_p, 0, NULL, 0); */
- }
-
/*
* Check if we have an arglist for the top level call. If so, this
* is encoded in Value, so we have to dig out the real Value as well
@@ -250,11 +254,6 @@ void hipe_handle_exception(Process *c_p)
/* Synthesized to avoid having to generate code for it. */
c_p->def_arg_reg[0] = exception_tag[GET_EXC_CLASS(c_p->freason)];
- if (c_p->mbuf) {
- /* erts_printf("%s line %u: p==%p, p->mbuf==%p, p->lastbif==%p\n", __FUNCTION__, __LINE__, c_p, c_p->mbuf, c_p->hipe.lastbif); */
- erts_garbage_collect(c_p, 0, NULL, 0);
- }
-
hipe_find_handler(c_p);
}
@@ -322,7 +321,7 @@ char *hipe_bs_allocate(int len)
Binary *bptr;
bptr = erts_bin_nrml_alloc(len);
- erts_smp_atomic_init_nob(&bptr->refc, 1);
+ erts_refc_init(&bptr->refc, 1);
return bptr->orig_bytes;
}
@@ -520,8 +519,9 @@ BIF_RETTYPE hipe_is_divisible(BIF_ALIST_2)
*/
Eterm hipe_check_get_msg(Process *c_p)
{
- Eterm ret;
- ErlMessage *msgp;
+ ErtsMessage *msgp;
+
+ c_p->flags |= F_DELAY_GC;
next_message:
@@ -543,25 +543,29 @@ Eterm hipe_check_get_msg(Process *c_p)
/* XXX: BEAM doesn't need this */
c_p->hipe_smp.have_receive_locks = 1;
#endif
+ c_p->flags &= ~F_DELAY_GC;
return THE_NON_VALUE;
#ifdef ERTS_SMP
}
#endif
}
- ErtsMoveMsgAttachmentIntoProc(msgp, c_p, c_p->stop, HEAP_TOP(c_p),
- c_p->fcalls, (void) 0, (void) 0);
- ret = ERL_MESSAGE_TERM(msgp);
- if (is_non_value(ret)) {
+
+ if (is_non_value(ERL_MESSAGE_TERM(msgp))
+ && !erts_decode_dist_message(c_p, ERTS_PROC_LOCK_MAIN, msgp, 0)) {
/*
* A corrupt distribution message that we weren't able to decode;
* remove it...
*/
ASSERT(!msgp->data.attached);
UNLINK_MESSAGE(c_p, msgp);
- free_message(msgp);
+ msgp->next = NULL;
+ erts_cleanup_messages(msgp);
goto next_message;
}
- return ret;
+
+ ASSERT(is_value(ERL_MESSAGE_TERM(msgp)));
+
+ return ERL_MESSAGE_TERM(msgp);
}
/*
@@ -587,7 +591,7 @@ void hipe_clear_timeout(Process *c_p)
}
#endif
if (IS_TRACED_FL(c_p, F_TRACE_RECEIVE)) {
- trace_receive(c_p, am_timeout);
+ trace_receive(c_p, am_clock_service, am_timeout, NULL);
}
c_p->flags &= ~F_TIMO;
JOIN_MESSAGE(c_p);
diff --git a/erts/emulator/hipe/hipe_native_bif.h b/erts/emulator/hipe/hipe_native_bif.h
index 55a0d3bb1b..a02d26087b 100644
--- a/erts/emulator/hipe/hipe_native_bif.h
+++ b/erts/emulator/hipe/hipe_native_bif.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_ops.tab b/erts/emulator/hipe/hipe_ops.tab
index d021c72ac9..96e4c0da91 100644
--- a/erts/emulator/hipe/hipe_ops.tab
+++ b/erts/emulator/hipe/hipe_ops.tab
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2011. All Rights Reserved.
+# Copyright Ericsson AB 2001-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_ppc.c b/erts/emulator/hipe/hipe_ppc.c
index 3f86de626d..9b2048c457 100644
--- a/erts/emulator/hipe/hipe_ppc.c
+++ b/erts/emulator/hipe/hipe_ppc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_ppc.h b/erts/emulator/hipe/hipe_ppc.h
index 777c9384d5..e86c122fb1 100644
--- a/erts/emulator/hipe/hipe_ppc.h
+++ b/erts/emulator/hipe/hipe_ppc.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_ppc.tab b/erts/emulator/hipe/hipe_ppc.tab
index 5bf2320cbe..274612d9ce 100644
--- a/erts/emulator/hipe/hipe_ppc.tab
+++ b/erts/emulator/hipe/hipe_ppc.tab
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2011. All Rights Reserved.
+# Copyright Ericsson AB 2004-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_ppc64.tab b/erts/emulator/hipe/hipe_ppc64.tab
index 301a9752f7..a291185b57 100644
--- a/erts/emulator/hipe/hipe_ppc64.tab
+++ b/erts/emulator/hipe/hipe_ppc64.tab
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2011. All Rights Reserved.
+# Copyright Ericsson AB 2005-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_ppc_asm.m4 b/erts/emulator/hipe/hipe_ppc_asm.m4
index ea74923d1f..be25d65725 100644
--- a/erts/emulator/hipe/hipe_ppc_asm.m4
+++ b/erts/emulator/hipe/hipe_ppc_asm.m4
@@ -2,7 +2,7 @@ changecom(`/*', `*/')dnl
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_ppc_bifs.m4 b/erts/emulator/hipe/hipe_ppc_bifs.m4
index 28518827ec..b540562185 100644
--- a/erts/emulator/hipe/hipe_ppc_bifs.m4
+++ b/erts/emulator/hipe/hipe_ppc_bifs.m4
@@ -2,7 +2,7 @@ changecom(`/*', `*/')dnl
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -212,8 +212,9 @@ ASYM($1):
* gc_bif_interface_0(nbif_name, cbif_name)
* gc_bif_interface_1(nbif_name, cbif_name)
* gc_bif_interface_2(nbif_name, cbif_name)
+ * gc_bif_interface_3(nbif_name, cbif_name)
*
- * Generate native interface for a BIF with 0-2 parameters and
+ * Generate native interface for a BIF with 0-3 parameters and
* standard failure mode.
* The BIF may do a GC.
*/
@@ -300,6 +301,39 @@ ASYM($1):
TYPE_FUNCTION(ASYM($1))
#endif')
+define(gc_bif_interface_3,
+`
+#ifndef HAVE_$1
+#`define' HAVE_$1
+ GLOBAL(ASYM($1))
+ASYM($1):
+ /* Set up C argument registers. */
+ mr r3, P
+ NBIF_ARG(r4,3,0)
+ NBIF_ARG(r5,3,1)
+ NBIF_ARG(r6,3,2)
+
+ /* Save caller-save registers and call the C function. */
+ SAVE_CONTEXT_GC
+ STORE r4, P_ARG0(r3) /* Store BIF__ARGS in def_arg_reg[] */
+ STORE r5, P_ARG1(r3)
+ STORE r6, P_ARG2(r3)
+ addi r4, r3, P_ARG0
+ CALL_BIF($2)
+ TEST_GOT_MBUF
+
+ /* Restore registers. Check for exception. */
+ CMPI r3, THE_NON_VALUE
+ RESTORE_CONTEXT_GC
+ beq- 1f
+ NBIF_RET(3)
+1: /* workaround for bc:s small offset operand */
+ b CSYM(nbif_3_simple_exception)
+ HANDLE_GOT_MBUF(3)
+ SET_SIZE(ASYM($1))
+ TYPE_FUNCTION(ASYM($1))
+#endif')
+
/*
* gc_nofail_primop_interface_1(nbif_name, cbif_name)
*
diff --git a/erts/emulator/hipe/hipe_ppc_gc.h b/erts/emulator/hipe/hipe_ppc_gc.h
index 0854d9e950..a6ec1338ff 100644
--- a/erts/emulator/hipe/hipe_ppc_gc.h
+++ b/erts/emulator/hipe/hipe_ppc_gc.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_ppc_glue.S b/erts/emulator/hipe/hipe_ppc_glue.S
index 6e2022603c..44351dc06c 100644
--- a/erts/emulator/hipe/hipe_ppc_glue.S
+++ b/erts/emulator/hipe/hipe_ppc_glue.S
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_ppc_glue.h b/erts/emulator/hipe/hipe_ppc_glue.h
index 93ca39fcb3..2bfa10298c 100644
--- a/erts/emulator/hipe/hipe_ppc_glue.h
+++ b/erts/emulator/hipe/hipe_ppc_glue.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_ppc_primops.h b/erts/emulator/hipe/hipe_ppc_primops.h
index ce3dda9eb3..540605de9f 100644
--- a/erts/emulator/hipe/hipe_ppc_primops.h
+++ b/erts/emulator/hipe/hipe_ppc_primops.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_primops.h b/erts/emulator/hipe/hipe_primops.h
index 0bec677574..4fcbc9df38 100644
--- a/erts/emulator/hipe/hipe_primops.h
+++ b/erts/emulator/hipe/hipe_primops.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_process.h b/erts/emulator/hipe/hipe_process.h
index 60d09ea1c9..a8d5972280 100644
--- a/erts/emulator/hipe/hipe_process.h
+++ b/erts/emulator/hipe/hipe_process.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -52,6 +52,9 @@ struct hipe_process_state {
#if defined(ERTS_ENABLE_LOCK_CHECK) && defined(ERTS_SMP)
void (*bif_callee)(void); /* When calling BIF's via debug wrapper */
#endif
+#ifdef DEBUG
+ UWord gc_is_unsafe; /* Nonzero when GC-required state is on stack */
+#endif
};
extern void hipe_arch_print_pcb(struct hipe_process_state *p);
@@ -68,6 +71,9 @@ static __inline__ void hipe_init_process(struct hipe_process_state *p)
p->nra = NULL;
#endif
p->narity = 0;
+#ifdef DEBUG
+ p->gc_is_unsafe = 0;
+#endif
}
static __inline__ void hipe_delete_process(struct hipe_process_state *p)
diff --git a/erts/emulator/hipe/hipe_risc_gc.h b/erts/emulator/hipe/hipe_risc_gc.h
index 770ec93459..09568c140e 100644
--- a/erts/emulator/hipe/hipe_risc_gc.h
+++ b/erts/emulator/hipe/hipe_risc_gc.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -51,6 +51,19 @@ nstack_walk_init_sdesc(const Process *p, struct nstack_walk_state *state)
return sdesc;
}
+static inline const struct sdesc*
+nstack_walk_init_sdesc_ignore_trap(const Process *p,
+ struct nstack_walk_state *state)
+{
+ unsigned long ra = (unsigned long)p->hipe.nra;
+ const struct sdesc *sdesc;
+ if (ra == (unsigned long)&nbif_stack_trap_ra)
+ ra = (unsigned long)p->hipe.ngra;
+ sdesc = hipe_find_sdesc(ra);
+ state->sdesc0 = sdesc;
+ return sdesc;
+}
+
static inline void nstack_walk_update_trap(Process *p, const struct sdesc *sdesc0)
{
Eterm *nsp = p->hipe.nsp;
diff --git a/erts/emulator/hipe/hipe_risc_glue.h b/erts/emulator/hipe/hipe_risc_glue.h
index 51db7eac01..0284265307 100644
--- a/erts/emulator/hipe/hipe_risc_glue.h
+++ b/erts/emulator/hipe/hipe_risc_glue.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_risc_stack.c b/erts/emulator/hipe/hipe_risc_stack.c
index fd04421381..dc98c96b8f 100644
--- a/erts/emulator/hipe/hipe_risc_stack.c
+++ b/erts/emulator/hipe/hipe_risc_stack.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_signal.h b/erts/emulator/hipe/hipe_signal.h
index b2f461797d..5d8621135b 100644
--- a/erts/emulator/hipe/hipe_signal.h
+++ b/erts/emulator/hipe/hipe_signal.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2002-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_sparc.c b/erts/emulator/hipe/hipe_sparc.c
index bd95bc5d98..23020f34ee 100644
--- a/erts/emulator/hipe/hipe_sparc.c
+++ b/erts/emulator/hipe/hipe_sparc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2003-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_sparc.h b/erts/emulator/hipe/hipe_sparc.h
index 0b74b2db26..25d513e988 100644
--- a/erts/emulator/hipe/hipe_sparc.h
+++ b/erts/emulator/hipe/hipe_sparc.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2003-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_sparc.tab b/erts/emulator/hipe/hipe_sparc.tab
index 2f528c0607..bc90d38168 100644
--- a/erts/emulator/hipe/hipe_sparc.tab
+++ b/erts/emulator/hipe/hipe_sparc.tab
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2011. All Rights Reserved.
+# Copyright Ericsson AB 2004-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_sparc_asm.m4 b/erts/emulator/hipe/hipe_sparc_asm.m4
index 227a1658b4..8a9a516eab 100644
--- a/erts/emulator/hipe/hipe_sparc_asm.m4
+++ b/erts/emulator/hipe/hipe_sparc_asm.m4
@@ -2,7 +2,7 @@ changecom(`/*', `*/')dnl
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2007-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2007-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_sparc_bifs.m4 b/erts/emulator/hipe/hipe_sparc_bifs.m4
index a7bbe5b2cc..1389beaa61 100644
--- a/erts/emulator/hipe/hipe_sparc_bifs.m4
+++ b/erts/emulator/hipe/hipe_sparc_bifs.m4
@@ -2,7 +2,7 @@ changecom(`/*', `*/')dnl
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -210,8 +210,9 @@ $1:
* gc_bif_interface_0(nbif_name, cbif_name)
* gc_bif_interface_1(nbif_name, cbif_name)
* gc_bif_interface_2(nbif_name, cbif_name)
+ * gc_bif_interface_3(nbif_name, cbif_name)
*
- * Generate native interface for a BIF with 0-2 parameters and
+ * Generate native interface for a BIF with 0-3 parameters and
* standard failure mode.
* The BIF may do a GC.
*/
@@ -295,6 +296,37 @@ $1:
.type $1, #function
#endif')
+define(gc_bif_interface_3,
+`
+#ifndef HAVE_$1
+#`define' HAVE_$1
+ .global $1
+$1:
+ /* Set up C argument registers. */
+ mov P, %o0
+ NBIF_ARG(%o1,3,0)
+ NBIF_ARG(%o2,3,1)
+ NBIF_ARG(%o3,3,2)
+
+ /* Save caller-save registers and call the C function. */
+ SAVE_CONTEXT_GC
+ st %o1, [%o0+P_ARG0] ! Store BIF__ARGS in def_arg_reg
+ st %o2, [%o0+P_ARG1]
+ st %o3, [%o0+P_ARG2]
+ add %o0, P_ARG0, %o1
+ CALL_BIF($2)
+ nop
+ TEST_GOT_MBUF
+
+ /* Restore registers. Check for exception. */
+ TEST_GOT_EXN(3)
+ RESTORE_CONTEXT_GC
+ NBIF_RET(3)
+ HANDLE_GOT_MBUF(3)
+ .size $1, .-$1
+ .type $1, #function
+#endif')
+
/*
* gc_nofail_primop_interface_1(nbif_name, cbif_name)
*
diff --git a/erts/emulator/hipe/hipe_sparc_gc.h b/erts/emulator/hipe/hipe_sparc_gc.h
index 99e55df1ce..eea0268ed5 100644
--- a/erts/emulator/hipe/hipe_sparc_gc.h
+++ b/erts/emulator/hipe/hipe_sparc_gc.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_sparc_glue.S b/erts/emulator/hipe/hipe_sparc_glue.S
index 7e47ffb52f..077db9b8ad 100644
--- a/erts/emulator/hipe/hipe_sparc_glue.S
+++ b/erts/emulator/hipe/hipe_sparc_glue.S
@@ -2,7 +2,7 @@
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_sparc_glue.h b/erts/emulator/hipe/hipe_sparc_glue.h
index 95ce1e5b4a..3a1795f408 100644
--- a/erts/emulator/hipe/hipe_sparc_glue.h
+++ b/erts/emulator/hipe/hipe_sparc_glue.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_sparc_primops.h b/erts/emulator/hipe/hipe_sparc_primops.h
index e42bcbad62..2ecfa1293e 100644
--- a/erts/emulator/hipe/hipe_sparc_primops.h
+++ b/erts/emulator/hipe/hipe_sparc_primops.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_stack.c b/erts/emulator/hipe/hipe_stack.c
index e088b45c57..e2e6eb74b1 100644
--- a/erts/emulator/hipe/hipe_stack.c
+++ b/erts/emulator/hipe/hipe_stack.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2003-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_stack.h b/erts/emulator/hipe/hipe_stack.h
index f575b97ce3..afa0ed4256 100644
--- a/erts/emulator/hipe/hipe_stack.h
+++ b/erts/emulator/hipe/hipe_stack.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -131,5 +131,8 @@ static __inline__ void hipe_check_nstack(Process *p, unsigned nwords)
*/
extern Eterm *fullsweep_nstack(Process *p, Eterm *n_htop);
extern void gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop);
+extern Eterm *sweep_literals_nstack(Process *p, Eterm *n_htop, char *area,
+ Uint area_size);
+extern int nstack_any_heap_ref_ptrs(Process *, char* mod_start, Uint mod_size);
#endif /* HIPE_STACK_H */
diff --git a/erts/emulator/hipe/hipe_x86.c b/erts/emulator/hipe/hipe_x86.c
index 19b70afced..5f6c8c200e 100644
--- a/erts/emulator/hipe/hipe_x86.c
+++ b/erts/emulator/hipe/hipe_x86.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2003-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,7 +37,7 @@
void hipe_patch_load_fe(Uint32 *address, Uint32 value)
{
/* address points to a disp32 or imm32 operand */
- *address = value;
+ *address += value;
}
int hipe_patch_insn(void *address, Uint32 value, Eterm type)
@@ -54,7 +54,7 @@ int hipe_patch_insn(void *address, Uint32 value, Eterm type)
default:
return -1;
}
- *(Uint32*)address = value;
+ *(Uint32*)address += value;
return 0;
}
diff --git a/erts/emulator/hipe/hipe_x86.h b/erts/emulator/hipe/hipe_x86.h
index 30dc5666ae..8967793171 100644
--- a/erts/emulator/hipe/hipe_x86.h
+++ b/erts/emulator/hipe/hipe_x86.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2003-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_x86.tab b/erts/emulator/hipe/hipe_x86.tab
index 55fb03cde8..b922f05165 100644
--- a/erts/emulator/hipe/hipe_x86.tab
+++ b/erts/emulator/hipe/hipe_x86.tab
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2011. All Rights Reserved.
+# Copyright Ericsson AB 2004-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_x86_abi.txt b/erts/emulator/hipe/hipe_x86_abi.txt
index b74fcef127..31a4a0e121 100644
--- a/erts/emulator/hipe/hipe_x86_abi.txt
+++ b/erts/emulator/hipe/hipe_x86_abi.txt
@@ -1,7 +1,7 @@
%CopyrightBegin%
- Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ Copyright Ericsson AB 2001-2016. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_x86_asm.m4 b/erts/emulator/hipe/hipe_x86_asm.m4
index 3457574622..91c60382eb 100644
--- a/erts/emulator/hipe/hipe_x86_asm.m4
+++ b/erts/emulator/hipe/hipe_x86_asm.m4
@@ -2,7 +2,7 @@ changecom(`/*', `*/')dnl
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2002-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_x86_bifs.m4 b/erts/emulator/hipe/hipe_x86_bifs.m4
index 0c0a8da6eb..c0c149733c 100644
--- a/erts/emulator/hipe/hipe_x86_bifs.m4
+++ b/erts/emulator/hipe/hipe_x86_bifs.m4
@@ -2,7 +2,7 @@ changecom(`/*', `*/')dnl
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -671,10 +671,11 @@ noproc_primop_interface_0(nbif_handle_fp_exception, erts_restore_fpu)
define(gc_bif_interface_0,`nofail_primop_interface_0($1, $2)')
/*
- * Implement gc_bif_interface_N as standard_bif_interface_N (N=1,2).
+ * Implement gc_bif_interface_N as standard_bif_interface_N (N=1,2,3).
*/
define(gc_bif_interface_1,`standard_bif_interface_1($1, $2)')
define(gc_bif_interface_2,`standard_bif_interface_2($1, $2)')
+define(gc_bif_interface_3,`standard_bif_interface_3($1, $2)')
/*
* Implement gc_nofail_primop_interface_1 as nofail_primop_interface_1.
diff --git a/erts/emulator/hipe/hipe_x86_gc.h b/erts/emulator/hipe/hipe_x86_gc.h
index a8770ef4f9..00fe03d8f9 100644
--- a/erts/emulator/hipe/hipe_x86_gc.h
+++ b/erts/emulator/hipe/hipe_x86_gc.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -81,6 +81,23 @@ nstack_walk_init_sdesc(const Process *p, struct nstack_walk_state *state)
#endif
}
+static inline const struct sdesc*
+nstack_walk_init_sdesc_ignore_trap(const Process *p,
+ struct nstack_walk_state *state)
+{
+#ifdef SKIP_YOUNGEST_FRAME
+ unsigned long ra = p->hipe.nsp[0];
+ const struct sdesc *sdesc;
+ if (ra == (unsigned long)nbif_stack_trap_ra)
+ ra = (unsigned long)p->hipe.ngra;
+ sdesc = hipe_find_sdesc(ra);
+ state->sdesc0 = sdesc;
+ return sdesc;
+#else
+ return nstack_walk_init_sdesc(p, state);
+#endif
+}
+
static inline void nstack_walk_update_trap(Process *p, const struct sdesc *sdesc0)
{
#ifdef SKIP_YOUNGEST_FRAME
diff --git a/erts/emulator/hipe/hipe_x86_glue.S b/erts/emulator/hipe/hipe_x86_glue.S
index cf382c480d..8d6e377730 100644
--- a/erts/emulator/hipe/hipe_x86_glue.S
+++ b/erts/emulator/hipe/hipe_x86_glue.S
@@ -2,7 +2,7 @@
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_x86_glue.h b/erts/emulator/hipe/hipe_x86_glue.h
index 6c123e8938..818d7444e2 100644
--- a/erts/emulator/hipe/hipe_x86_glue.h
+++ b/erts/emulator/hipe/hipe_x86_glue.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_x86_primops.h b/erts/emulator/hipe/hipe_x86_primops.h
index 3e057059bc..68a284203e 100644
--- a/erts/emulator/hipe/hipe_x86_primops.h
+++ b/erts/emulator/hipe/hipe_x86_primops.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/hipe/hipe_x86_signal.c b/erts/emulator/hipe/hipe_x86_signal.c
index b7dae88417..50d08b96d3 100644
--- a/erts/emulator/hipe/hipe_x86_signal.c
+++ b/erts/emulator/hipe/hipe_x86_signal.c
@@ -2,7 +2,7 @@
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2014. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,9 +38,6 @@
*
* Our solution is to override the C library's signal handler setup
* procedure with our own which enforces the SA_ONSTACK flag.
- *
- * XXX: This code only supports Linux with glibc-2.1 or above,
- * and Solaris 8.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
@@ -55,27 +52,6 @@
#include "hipe_signal.h"
#if __GLIBC__ == 2 && (__GLIBC_MINOR__ >= 3)
-/* See comment below for glibc 2.2. */
-#ifndef __USE_GNU
-#define __USE_GNU /* to un-hide RTLD_NEXT */
-#endif
-#include <dlfcn.h>
-static int (*__next_sigaction)(int, const struct sigaction*, struct sigaction*);
-#define init_done() (__next_sigaction != 0)
-extern int __sigaction(int, const struct sigaction*, struct sigaction*);
-#define __SIGACTION __sigaction
-static void do_init(void)
-{
- __next_sigaction = dlsym(RTLD_NEXT, "__sigaction");
- if (__next_sigaction != 0)
- return;
- perror("dlsym");
- abort();
-}
-#define INIT() do { if (!init_done()) do_init(); } while (0)
-#endif /* glibc 2.3 */
-
-#if __GLIBC__ == 2 && (__GLIBC_MINOR__ == 2 /*|| __GLIBC_MINOR__ == 3*/)
/*
* __libc_sigaction() is the core routine.
* Without libpthread, sigaction() and __sigaction() are both aliases
@@ -100,65 +76,14 @@ static void do_init(void)
* old BSD or SysV interfaces.
* glibc's internal calls to __sigaction() appear to be mostly safe.
* hipe_signal_init() fixes some unsafe ones, e.g. the SIGPROF handler.
- *
- * Tested with glibc-2.1.92 on RedHat 7.0, glibc-2.2.2 on RedHat 7.1,
- * glibc-2.2.4 on RedHat 7.2, and glibc-2.2.5 on RedHat 7.3.
*/
-#if 0
-/* works with 2.2.5 and 2.2.4, but not 2.2.2 or 2.1.92 */
+#ifndef __USE_GNU
#define __USE_GNU /* to un-hide RTLD_NEXT */
-#include <dlfcn.h>
-static int (*__next_sigaction)(int, const struct sigaction*, struct sigaction*);
-#define init_done() (__next_sigaction != 0)
-#define __SIGACTION __sigaction
-static void do_init(void)
-{
- __next_sigaction = dlsym(RTLD_NEXT, "__sigaction");
- if (__next_sigaction != 0)
- return;
- perror("dlsym");
- abort();
-}
-#define INIT() do { if (!init_done()) do_init(); } while (0)
-#else
-/* semi-works with all 2.2 versions so far */
-extern int __sigaction(int, const struct sigaction*, struct sigaction*);
-#define __next_sigaction __sigaction /* pthreads-aware version */
-#undef __SIGACTION /* we can't override __sigaction() */
-#define INIT() do{}while(0)
#endif
-#endif /* glibc 2.2 */
-
-#if __GLIBC__ == 2 && __GLIBC_MINOR__ == 1
-/*
- * __sigaction() is the core routine.
- * Without libpthread, sigaction() is an alias for __sigaction().
- * libpthread redefines sigaction() as a non-trivial wrapper around
- * __sigaction().
- * glibc has internal calls to both sigaction() and __sigaction().
- *
- * Overriding __sigaction() would be ideal, but doing so breaks
- * libpthread (threads hang). Instead we override sigaction() and
- * use dlsym RTLD_NEXT to find glibc's version of sigaction().
- * glibc's internal calls to __sigaction() appear to be mostly safe.
- * hipe_signal_init() fixes some unsafe ones, e.g. the SIGPROF handler.
- *
- * Tested with glibc-2.1.3 on RedHat 6.2.
- */
-#include <dlfcn.h>
-static int (*__next_sigaction)(int, const struct sigaction*, struct sigaction*);
-#define init_done() (__next_sigaction != 0)
-#undef __SIGACTION
-static void do_init(void)
-{
- __next_sigaction = dlsym(RTLD_NEXT, "sigaction");
- if (__next_sigaction != 0)
- return;
- perror("dlsym");
- abort();
-}
-#define INIT() do { if (!init_done()) do_init(); } while (0)
-#endif /* glibc 2.1 */
+#define NEXT_SIGACTION "__sigaction"
+#define LIBC_SIGACTION __sigaction
+#define OVERRIDE_SIGACTION
+#endif /* glibc >= 2.3 */
/* Is there no standard identifier for Darwin/MacOSX ? */
#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__)
@@ -181,21 +106,10 @@ static void do_init(void)
* The other _sigaction, _sigaction_no_bind I don't understand the purpose
* of and don't modify.
*/
-#include <dlfcn.h>
-static int (*__next_sigaction)(int, const struct sigaction*, struct sigaction*);
-#define init_done() (__next_sigaction != 0)
-extern int _sigaction(int, const struct sigaction*, struct sigaction*);
-#define __SIGACTION _sigaction
-static void do_init(void)
-{
- __next_sigaction = dlsym(RTLD_NEXT, "sigaction");
- if (__next_sigaction != 0)
- return;
- perror("dlsym_darwin");
- abort();
-}
+#define NEXT_SIGACTION "sigaction"
+#define LIBC_SIGACTION _sigaction
+#undef OVERRIDE_SIGACTION
#define _NSIG NSIG
-#define INIT() do { if (!init_done()) do_init(); } while (0)
#endif /* __DARWIN__ */
#if defined(__sun__)
@@ -218,20 +132,10 @@ static void do_init(void)
* our init routine has had a chance to find _sigaction()'s address.
* This forces us to initialise at the first call.
*/
-#include <dlfcn.h>
-static int (*__next_sigaction)(int, const struct sigaction*, struct sigaction*);
-#define init_done() (__next_sigaction != 0)
-#define __SIGACTION _sigaction
-static void do_init(void)
-{
- __next_sigaction = dlsym(RTLD_NEXT, "_sigaction");
- if (__next_sigaction != 0)
- return;
- perror("dlsym");
- abort();
-}
+#define NEXT_SIGACTION "_sigaction"
+#define LIBC_SIGACTION _sigaction
+#define OVERRIDE_SIGACTION
#define _NSIG NSIG
-#define INIT() do { if (!init_done()) do_init(); } while (0)
#endif /* __sun__ */
#if defined(__FreeBSD__)
@@ -239,23 +143,22 @@ static void do_init(void)
* This is a copy of Darwin code for FreeBSD.
* CAVEAT: detailed semantics are not verified yet.
*/
-#include <dlfcn.h>
-static int (*__next_sigaction)(int, const struct sigaction*, struct sigaction*);
-#define init_done() (__next_sigaction != 0)
-extern int _sigaction(int, const struct sigaction*, struct sigaction*);
-#define __SIGACTION _sigaction
-static void do_init(void)
-{
- __next_sigaction = dlsym(RTLD_NEXT, "sigaction");
- if (__next_sigaction != 0)
- return;
- perror("dlsym_freebsd");
- abort();
-}
+#define NEXT_SIGACTION "sigaction"
+#define LIBC_SIGACTION _sigaction
+#undef OVERRIDE_SIGACTION
#define _NSIG NSIG
-#define INIT() do { if (!init_done()) do_init(); } while (0)
#endif /* __FreeBSD__ */
+#if defined(__NetBSD__)
+/*
+ * Note: This is only stub code to allow the build to succeed.
+ * Whether this actually provides the needed overrides for safe
+ * signal delivery or not is unknown.
+ */
+#undef NEXT_SIGACTION
+#undef OVERRIDE_SIGACTION
+#endif /* __NetBSD__ */
+
#if !(defined(__GLIBC__) || defined(__DARWIN__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__sun__))
/*
* Unknown libc -- assume musl. Note: musl deliberately does not provide a musl-specific
@@ -265,30 +168,40 @@ static void do_init(void)
* There are libc-internal calls to __libc_sigaction which install handlers, so we must
* override __libc_sigaction rather than __sigaction.
*/
+#define NEXT_SIGACTION "__libc_sigaction"
+#define LIBC_SIGACTION __libc_sigaction
+#define OVERRIDE_SIGACTION
+#ifndef _NSIG
+#define _NSIG NSIG
+#endif
+#endif /* !(__GLIBC__ || __DARWIN__ || __NetBSD__ || __FreeBSD__ || __sun__) */
+
+#if defined(NEXT_SIGACTION)
+/*
+ * Initialize a function pointer to the libc core sigaction routine,
+ * to be used by our wrappers.
+ */
#include <dlfcn.h>
-static int (*__next_sigaction)(int, const struct sigaction*, struct sigaction*);
-#define init_done() (__next_sigaction != 0)
-#define __SIGACTION __libc_sigaction
+static int (*next_sigaction)(int, const struct sigaction*, struct sigaction*);
static void do_init(void)
{
- __next_sigaction = dlsym(RTLD_NEXT, "__libc_sigaction");
- if (__next_sigaction != 0)
+ next_sigaction = dlsym(RTLD_NEXT, NEXT_SIGACTION);
+ if (next_sigaction != 0)
return;
perror("dlsym");
abort();
}
-#ifndef _NSIG
-#define _NSIG NSIG
-#endif
-#define INIT() do { if (!init_done()) do_init(); } while (0)
-#endif /* !(__GLIBC__ || __DARWIN__ || __NetBSD__ || __FreeBSD__ || __sun__) */
+#define INIT() do { if (!next_sigaction) do_init(); } while (0)
+#else /* !defined(NEXT_SIGACTION) */
+#define INIT() do { } while (0)
+#endif /* !defined(NEXT_SIGACTION) */
-#if !defined(__NetBSD__)
+#if defined(NEXT_SIGACTION)
/*
* This is our wrapper for sigaction(). sigaction() can be called before
* hipe_signal_init() has been executed, especially when threads support
* has been linked with the executable. Therefore, we must initialise
- * __next_sigaction() dynamically, the first time it's needed.
+ * next_sigaction() dynamically, the first time it's needed.
*/
static int my_sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)
{
@@ -304,24 +217,26 @@ static int my_sigaction(int signum, const struct sigaction *act, struct sigactio
newact.sa_flags |= SA_ONSTACK;
act = &newact;
}
- return __next_sigaction(signum, act, oldact);
+ return next_sigaction(signum, act, oldact);
}
#endif
+
+#if defined(LIBC_SIGACTION)
/*
* This overrides the C library's core sigaction() procedure, catching
* all its internal calls.
*/
-#ifdef __SIGACTION
-int __SIGACTION(int signum, const struct sigaction *act, struct sigaction *oldact)
+extern int LIBC_SIGACTION(int, const struct sigaction*, struct sigaction*);
+int LIBC_SIGACTION(int signum, const struct sigaction *act, struct sigaction *oldact)
{
return my_sigaction(signum, act, oldact);
}
#endif
+#if defined(OVERRIDE_SIGACTION)
/*
* This catches the application's own sigaction() calls.
*/
-#if !defined(__DARWIN__) && !defined(__NetBSD__) && !defined(__FreeBSD__)
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)
{
return my_sigaction(signum, act, oldact);
@@ -336,15 +251,11 @@ static void hipe_sigaltstack(void *ss_sp)
stack_t ss;
ss.ss_sp = ss_sp;
- ss.ss_flags = SS_ONSTACK;
+ ss.ss_flags = 0;
ss.ss_size = SIGSTKSZ;
if (sigaltstack(&ss, NULL) < 0) {
- /* might be a broken pre-2.4 Linux kernel, try harder */
- ss.ss_flags = 0;
- if (sigaltstack(&ss, NULL) < 0) {
- perror("sigaltstack");
- abort();
- }
+ perror("sigaltstack");
+ abort();
}
}
@@ -381,9 +292,7 @@ void hipe_signal_init(void)
struct sigaction sa;
int i;
-#ifndef __NetBSD__
INIT();
-#endif
hipe_sigaltstack_init();
diff --git a/erts/emulator/hipe/hipe_x86_stack.c b/erts/emulator/hipe/hipe_x86_stack.c
index 6629713a05..f1559b1451 100644
--- a/erts/emulator/hipe/hipe_x86_stack.c
+++ b/erts/emulator/hipe/hipe_x86_stack.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/internal_doc/Tracing.md b/erts/emulator/internal_doc/Tracing.md
index 30bc5327a7..728f315263 100644
--- a/erts/emulator/internal_doc/Tracing.md
+++ b/erts/emulator/internal_doc/Tracing.md
@@ -57,7 +57,7 @@ generations of breakpoints are kept and indentified by index of 0 and
1. The global atomic variables `erts_active_bp_index` will determine
which generation of breakpoints running code will use.
-### Atomicy Without Atomic Operations
+### Atomicity Without Atomic Operations
Not using the code loading generations (or any other code duplication)
means that `trace_pattern` must at some point write to the active beam
diff --git a/erts/emulator/internal_doc/dec.erl b/erts/emulator/internal_doc/dec.erl
index e64e37a7d9..8ce83fa402 100644
--- a/erts/emulator/internal_doc/dec.erl
+++ b/erts/emulator/internal_doc/dec.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/emulator/internal_doc/erl_ext_dist.txt b/erts/emulator/internal_doc/erl_ext_dist.txt
index 23b7d0d8e5..0b9a783069 100644
--- a/erts/emulator/internal_doc/erl_ext_dist.txt
+++ b/erts/emulator/internal_doc/erl_ext_dist.txt
@@ -1,7 +1,7 @@
%CopyrightBegin%
- Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ Copyright Ericsson AB 1997-2016. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/erts/emulator/nifs/common/erl_tracer_nif.c b/erts/emulator/nifs/common/erl_tracer_nif.c
new file mode 100644
index 0000000000..0bde60d057
--- /dev/null
+++ b/erts/emulator/nifs/common/erl_tracer_nif.c
@@ -0,0 +1,257 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson 2015-2016. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Purpose: NIF library for process/port tracer
+ *
+ */
+
+
+#define STATIC_ERLANG_NIF 1
+
+#include "erl_nif.h"
+#include "config.h"
+#include "sys.h"
+
+#ifdef VALGRIND
+# include <valgrind/memcheck.h>
+#endif
+
+/* NIF interface declarations */
+static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info);
+static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info);
+static void unload(ErlNifEnv* env, void* priv_data);
+
+/* The NIFs: */
+static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+
+static ErlNifFunc nif_funcs[] = {
+ {"enabled", 3, enabled},
+ {"trace", 5, trace}
+};
+
+
+ERL_NIF_INIT(erl_tracer, nif_funcs, load, NULL, upgrade, unload)
+
+#define ATOMS \
+ ATOM_DECL(call); \
+ ATOM_DECL(command); \
+ ATOM_DECL(cpu_timestamp); \
+ ATOM_DECL(discard); \
+ ATOM_DECL(exception_from); \
+ ATOM_DECL(extra); \
+ ATOM_DECL(match_spec_result); \
+ ATOM_DECL(monotonic); \
+ ATOM_DECL(ok); \
+ ATOM_DECL(remove); \
+ ATOM_DECL(return_from); \
+ ATOM_DECL(scheduler_id); \
+ ATOM_DECL(send); \
+ ATOM_DECL(send_to_non_existing_process); \
+ ATOM_DECL(seq_trace); \
+ ATOM_DECL(spawn); \
+ ATOM_DECL(strict_monotonic); \
+ ATOM_DECL(timestamp); \
+ ATOM_DECL(trace); \
+ ATOM_DECL(trace_status); \
+ ATOM_DECL(trace_ts); \
+ ATOM_DECL(true); \
+ ATOM_DECL(gc_minor_start); \
+ ATOM_DECL(gc_minor_end); \
+ ATOM_DECL(gc_major_start); \
+ ATOM_DECL(gc_major_end);
+
+#define ATOM_DECL(A) static ERL_NIF_TERM atom_##A
+ATOMS
+#undef ATOM_DECL
+
+static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
+{
+
+#define ATOM_DECL(A) atom_##A = enif_make_atom(env, #A)
+ATOMS
+#undef ATOM_DECL
+
+ *priv_data = NULL;
+
+ return 0;
+}
+
+static void unload(ErlNifEnv* env, void* priv_data)
+{
+
+}
+
+static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data,
+ ERL_NIF_TERM load_info)
+{
+ if (*old_priv_data != NULL) {
+ return -1; /* Don't know how to do that */
+ }
+ if (*priv_data != NULL) {
+ return -1; /* Don't know how to do that */
+ }
+ if (load(env, priv_data, load_info)) {
+ return -1;
+ }
+ return 0;
+}
+
+static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifPid to_pid;
+ ErlNifPort to_port;
+ ERL_NIF_TERM ret = enif_is_identical(argv[0], atom_trace_status) ?
+ atom_remove : atom_discard;
+
+ ASSERT(argc == 3);
+
+ if (enif_get_local_pid(env, argv[1], &to_pid)) {
+ if (!enif_is_process_alive(env, &to_pid))
+ /* tracer is dead so we should remove this trace point */
+ return ret;
+ } else if (enif_get_local_port(env, argv[1], &to_port)) {
+ if (!enif_is_port_alive(env, &to_port))
+ /* tracer is dead so we should remove this trace point */
+ return ret;
+ } else {
+ /* The state was not a pid or a port */
+ return ret;
+ }
+
+ /* Only generate trace for when tracer != tracee */
+ if (enif_is_identical(argv[1], argv[2])) {
+ return atom_discard;
+ }
+
+ return atom_trace;
+}
+
+/*
+ -spec trace(seq_trace, TracerState :: pid() | port(),
+ Label :: non_neg_integer(),
+ Msg :: term(),
+ Opts :: map()) -> ignored();
+ trace(Tag :: atom(), TracerState :: pid() | port(),
+ Tracee :: pid() || port() || undefined,
+ Msg :: term(),
+ Opts :: map()) -> ignored().
+*/
+static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ERL_NIF_TERM value, msg, tt[8], opts;
+ ErlNifPid to_pid;
+ ErlNifPort to_port;
+ size_t tt_sz = 0;
+ int is_port = 0;
+ size_t opts_sz = 0;
+ ASSERT(argc == 5);
+
+ if (!enif_get_local_pid(env, argv[1], &to_pid)) {
+ if (!enif_get_local_port(env, argv[1], &to_port)) {
+ /* This only fails if argv[1] is a not a local port/pid
+ which should not happen as it is checked in enabled */
+ ASSERT(0);
+ return atom_ok;
+ }
+ is_port = 1;
+ }
+
+ opts = argv[4];
+
+ if (!enif_get_map_size(env, opts, &opts_sz))
+ opts_sz = 0;
+
+ if (opts_sz && enif_get_map_value(env, opts, atom_extra, &value)) {
+ tt[tt_sz++] = atom_trace;
+ tt[tt_sz++] = argv[2];
+ tt[tt_sz++] = argv[0];
+ tt[tt_sz++] = argv[3];
+ tt[tt_sz++] = value;
+ } else {
+ if (enif_is_identical(argv[0], atom_seq_trace)) {
+ tt[tt_sz++] = atom_seq_trace;
+ tt[tt_sz++] = argv[2];
+ tt[tt_sz++] = argv[3];
+ } else {
+ tt[tt_sz++] = atom_trace;
+ tt[tt_sz++] = argv[2];
+ tt[tt_sz++] = argv[0];
+ tt[tt_sz++] = argv[3];
+ }
+ }
+
+
+ if (opts_sz && enif_get_map_value(env, opts, atom_match_spec_result, &value)) {
+ tt[tt_sz++] = value;
+ }
+
+ if (opts_sz && enif_get_map_value(env, opts, atom_scheduler_id, &value)) {
+ tt[tt_sz++] = value;
+ }
+
+ if (opts_sz && enif_get_map_value(env, opts, atom_timestamp, &value)) {
+ ERL_NIF_TERM ts;
+ if (enif_is_identical(value, atom_monotonic)) {
+ ErlNifTime mon = enif_monotonic_time(ERL_NIF_NSEC);
+ ts = enif_make_int64(env, mon);
+ } else if (enif_is_identical(value, atom_strict_monotonic)) {
+ ErlNifTime mon = enif_monotonic_time(ERL_NIF_NSEC);
+ ERL_NIF_TERM unique = enif_make_unique_integer(
+ env, ERL_NIF_UNIQUE_MONOTONIC);
+ ts = enif_make_tuple2(env, enif_make_int64(env, mon), unique);
+ } else if (enif_is_identical(value, atom_timestamp)) {
+ ts = enif_now_time(env);
+ } else if (enif_is_identical(value, atom_cpu_timestamp)) {
+ ts = enif_cpu_time(env);
+ } else {
+ ASSERT(0);
+ goto error;
+ }
+ tt[tt_sz++] = ts;
+ if (tt[0] == atom_trace)
+ tt[0] = atom_trace_ts;
+ }
+
+ msg = enif_make_tuple_from_array(env, tt, tt_sz);
+
+ if (is_port) {
+ ErlNifBinary bin;
+
+ if (!enif_term_to_binary(env, msg, &bin))
+ goto error;
+
+ msg = enif_make_binary(env, &bin);
+
+ if (!enif_port_command(env, &to_port, NULL, msg))
+ /* port has probably died, enabled will clean up */;
+
+ enif_release_binary(&bin);
+ } else {
+
+ if (!enif_send(env, &to_pid, NULL, msg))
+ /* process has probably died, enabled will clean up */;
+ }
+
+error:
+
+ return atom_ok;
+}
diff --git a/erts/emulator/pcre/pcre.mk b/erts/emulator/pcre/pcre.mk
index b844b77214..38b91237a2 100644
--- a/erts/emulator/pcre/pcre.mk
+++ b/erts/emulator/pcre/pcre.mk
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2012. All Rights Reserved.
+# Copyright Ericsson AB 2012-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c
index 105b129065..8f2f1f9521 100644
--- a/erts/emulator/sys/common/erl_check_io.c
+++ b/erts/emulator/sys/common/erl_check_io.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,6 +39,7 @@
#include "erl_check_io.h"
#include "erl_thr_progress.h"
#include "dtrace-wrapper.h"
+#include "lttng-wrapper.h"
#define ERTS_WANT_TIMER_WHEEL_API
#include "erl_time.h"
@@ -395,6 +396,7 @@ forget_removed(struct pollset_info* psi)
if (drv_ptr) {
int was_unmasked = erts_block_fpe();
DTRACE1(driver_stop_select, drv_ptr->name);
+ LTTNG1(driver_stop_select, drv_ptr->name);
(*drv_ptr->stop_select) ((ErlDrvEvent) fd, NULL);
erts_unblock_fpe(was_unmasked);
if (drv_ptr->handle) {
@@ -1055,6 +1057,7 @@ done_unknown:
if (stop_select_fn) {
int was_unmasked = erts_block_fpe();
DTRACE1(driver_stop_select, name);
+ LTTNG1(driver_stop_select, "unknown");
(*stop_select_fn)(e, NULL);
erts_unblock_fpe(was_unmasked);
}
@@ -1337,11 +1340,7 @@ print_select_op(erts_dsprintf_buf_t *dsbufp,
{
Port *pp = erts_drvport2port(ix);
erts_dsprintf(dsbufp,
-#ifdef __OSE__
- "driver_select(%p, %d,%s%s%s%s | %d, %d) "
-#else
"driver_select(%p, %d,%s%s%s%s, %d) "
-#endif
"by ",
ix,
(int) GET_FD(fd),
@@ -1618,6 +1617,11 @@ ERTS_CIO_EXPORT(erts_check_io)(int do_wait)
erts_do_break_handling();
#endif
+#ifdef ERTS_SIGNAL_SIGTERM
+ if (ERTS_SIGNAL_SIGTERM)
+ erts_handle_signal_sigterm();
+#endif
+
/* Figure out timeout value */
timeout_time = (do_wait
? erts_check_next_timeout_time(esdp)
@@ -1655,6 +1659,11 @@ ERTS_CIO_EXPORT(erts_check_io)(int do_wait)
erts_do_break_handling();
#endif
+#ifdef ERTS_SIGNAL_SIGTERM
+ if (ERTS_SIGNAL_SIGTERM)
+ erts_handle_signal_sigterm();
+#endif
+
if (poll_ret != 0) {
erts_smp_atomic_set_nob(&pollset.in_poll_wait, 0);
forget_removed(&pollset);
@@ -1861,25 +1870,6 @@ stale_drv_select(Eterm id, ErtsDrvEventState *state, int mode)
#ifndef ERTS_SYS_CONTINOUS_FD_NUMBERS
-#ifdef __OSE__
-static SafeHashValue drv_ev_state_hash(void *des)
-{
- ErtsSysFdType fd = ((ErtsDrvEventState *) des)->fd;
- /* We use hash on signo ^ id in order for steal to happen when the
- same signo + fd is selected on by two different ports */
- SafeHashValue val = (SafeHashValue)(fd->signo ^ fd->id);
- return val ^ (val >> 8);
-}
-
-static int drv_ev_state_cmp(void *des1, void *des2)
-{
- ErtsSysFdType fd1 = ((ErtsDrvEventState *) des1)->fd;
- ErtsSysFdType fd2 = ((ErtsDrvEventState *) des2)->fd;
- if (fd1->signo == fd2->signo && fd1->id == fd2->id)
- return 0;
- return 1;
-}
-#else /* !__OSE__ && !ERTS_SYS_CONTINOUS_FD_NUMBERS i.e. probably windows */
static SafeHashValue drv_ev_state_hash(void *des)
{
SafeHashValue val = (SafeHashValue) ((ErtsDrvEventState *) des)->fd;
@@ -1891,7 +1881,6 @@ static int drv_ev_state_cmp(void *des1, void *des2)
return ( ((ErtsDrvEventState *) des1)->fd == ((ErtsDrvEventState *) des2)->fd
? 0 : 1);
}
-#endif
static void *drv_ev_state_alloc(void *des_tmpl)
{
diff --git a/erts/emulator/sys/common/erl_check_io.h b/erts/emulator/sys/common/erl_check_io.h
index c8675a7d9d..14f1ea3f43 100644
--- a/erts/emulator/sys/common/erl_check_io.h
+++ b/erts/emulator/sys/common/erl_check_io.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c
index 4180e65fdc..a5714f8325 100644
--- a/erts/emulator/sys/common/erl_mmap.c
+++ b/erts/emulator/sys/common/erl_mmap.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2002-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,6 +28,8 @@
#include "erl_mmap.h"
#include <stddef.h>
+#if HAVE_ERTS_MMAP
+
/* #define ERTS_MMAP_OP_RINGBUF_SZ 100 */
#if defined(DEBUG) || 0
@@ -51,23 +53,22 @@
#endif
/*
- * `mmap_state.sa.bot` and `mmap_state.sua.top` are read only after
+ * `mm->sa.bot` and `mm->sua.top` are read only after
* initialization, but the other pointers are not; i.e., only
* ERTS_MMAP_IN_SUPERCARRIER() is allowed without the mutex held.
*/
#define ERTS_MMAP_IN_SUPERCARRIER(PTR) \
- (((UWord) (PTR)) - ((UWord) mmap_state.sa.bot) \
- < ((UWord) mmap_state.sua.top) - ((UWord) mmap_state.sa.bot))
+ (((UWord) (PTR)) - ((UWord) mm->sa.bot) \
+ < ((UWord) mm->sua.top) - ((UWord) mm->sa.bot))
#define ERTS_MMAP_IN_SUPERALIGNED_AREA(PTR) \
- (ERTS_SMP_LC_ASSERT(erts_lc_mtx_is_locked(&mmap_state.mtx)), \
- (((UWord) (PTR)) - ((UWord) mmap_state.sa.bot) \
- < ((UWord) mmap_state.sa.top) - ((UWord) mmap_state.sa.bot)))
+ (ERTS_SMP_LC_ASSERT(erts_lc_mtx_is_locked(&mm->mtx)), \
+ (((UWord) (PTR)) - ((UWord) mm->sa.bot) \
+ < ((UWord) mm->sa.top) - ((UWord) mm->sa.bot)))
#define ERTS_MMAP_IN_SUPERUNALIGNED_AREA(PTR) \
- (ERTS_SMP_LC_ASSERT(erts_lc_mtx_is_locked(&mmap_state.mtx)), \
- (((UWord) (PTR)) - ((UWord) mmap_state.sua.bot) \
- < ((UWord) mmap_state.sua.top) - ((UWord) mmap_state.sua.bot)))
+ (ERTS_SMP_LC_ASSERT(erts_lc_mtx_is_locked(&mm->mtx)), \
+ (((UWord) (PTR)) - ((UWord) mm->sua.bot) \
+ < ((UWord) mm->sua.top) - ((UWord) mm->sua.bot)))
-int erts_have_erts_mmap;
UWord erts_page_inv_mask;
#if defined(DEBUG) || defined(ERTS_MMAP_DEBUG)
@@ -197,10 +198,10 @@ static ErtsMMapOp mmap_ops[ERTS_MMAP_OP_RINGBUF_SZ];
#define ERTS_MMAP_OP_LCK(RES, IN_SZ, OUT_SZ) \
do { \
- erts_smp_mtx_lock(&mmap_state.mtx); \
+ erts_smp_mtx_lock(&mm->mtx); \
ERTS_MMAP_OP_START((IN_SZ)); \
ERTS_MMAP_OP_END((RES), (OUT_SZ)); \
- erts_smp_mtx_unlock(&mmap_state.mtx); \
+ erts_smp_mtx_unlock(&mm->mtx); \
} while (0)
#define ERTS_MUNMAP_OP(PTR, SZ) \
@@ -219,9 +220,9 @@ static ErtsMMapOp mmap_ops[ERTS_MMAP_OP_RINGBUF_SZ];
#define ERTS_MUNMAP_OP_LCK(PTR, SZ) \
do { \
- erts_smp_mtx_lock(&mmap_state.mtx); \
+ erts_smp_mtx_lock(&mm->mtx); \
ERTS_MUNMAP_OP((PTR), (SZ)); \
- erts_smp_mtx_unlock(&mmap_state.mtx); \
+ erts_smp_mtx_unlock(&mm->mtx); \
} while (0)
#define ERTS_MREMAP_OP_START(OLD_PTR, OLD_SZ, IN_SZ) \
@@ -247,10 +248,10 @@ static ErtsMMapOp mmap_ops[ERTS_MMAP_OP_RINGBUF_SZ];
#define ERTS_MREMAP_OP_LCK(RES, OLD_PTR, OLD_SZ, IN_SZ, OUT_SZ) \
do { \
- erts_smp_mtx_lock(&mmap_state.mtx); \
+ erts_smp_mtx_lock(&mm->mtx); \
ERTS_MREMAP_OP_START((OLD_PTR), (OLD_SZ), (IN_SZ)); \
ERTS_MREMAP_OP_END((RES), (OUT_SZ)); \
- erts_smp_mtx_unlock(&mmap_state.mtx); \
+ erts_smp_mtx_unlock(&mm->mtx); \
} while (0)
#define ERTS_MMAP_OP_ABORT() \
@@ -294,13 +295,14 @@ typedef struct {
Uint nseg;
}ErtsFreeSegMap;
-static struct {
- int (*reserve_physical)(char *, UWord);
+struct ErtsMemMapper_ {
+ int (*reserve_physical)(char *, UWord, int exec);
void (*unreserve_physical)(char *, UWord);
int supercarrier;
int no_os_mmap;
+ int executable; /* is client a native code allocator? */
/*
- * Super unaligend area is located above super aligned
+ * Super unaligned area is located above super aligned
* area. That is, `sa.bot` is beginning of the super
* carrier, `sua.top` is the end of the super carrier,
* and sa.top and sua.bot moves towards eachother.
@@ -346,54 +348,68 @@ static struct {
UWord used;
} os;
} size;
-} mmap_state;
+};
+
+ErtsMemMapper erts_dflt_mmapper;
+
+#if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
+ErtsMemMapper erts_literal_mmapper;
+char* erts_literals_start;
+UWord erts_literals_size;
+#endif
+
+#ifdef ERTS_ALC_A_EXEC
+ErtsMemMapper erts_exec_mmapper;
+#endif
+
+
#define ERTS_MMAP_SIZE_SC_SA_INC(SZ) \
do { \
- mmap_state.size.supercarrier.used.total += (SZ); \
- mmap_state.size.supercarrier.used.sa += (SZ); \
- ERTS_MMAP_ASSERT(mmap_state.size.supercarrier.used.total \
- <= mmap_state.size.supercarrier.total); \
- ERTS_MMAP_ASSERT(mmap_state.size.supercarrier.used.sa \
- <= mmap_state.size.supercarrier.used.total); \
+ mm->size.supercarrier.used.total += (SZ); \
+ mm->size.supercarrier.used.sa += (SZ); \
+ ERTS_MMAP_ASSERT(mm->size.supercarrier.used.total \
+ <= mm->size.supercarrier.total); \
+ ERTS_MMAP_ASSERT(mm->size.supercarrier.used.sa \
+ <= mm->size.supercarrier.used.total); \
} while (0)
#define ERTS_MMAP_SIZE_SC_SA_DEC(SZ) \
do { \
- ERTS_MMAP_ASSERT(mmap_state.size.supercarrier.used.total >= (SZ)); \
- mmap_state.size.supercarrier.used.total -= (SZ); \
- ERTS_MMAP_ASSERT(mmap_state.size.supercarrier.used.sa >= (SZ)); \
- mmap_state.size.supercarrier.used.sa -= (SZ); \
+ ERTS_MMAP_ASSERT(mm->size.supercarrier.used.total >= (SZ)); \
+ mm->size.supercarrier.used.total -= (SZ); \
+ ERTS_MMAP_ASSERT(mm->size.supercarrier.used.sa >= (SZ)); \
+ mm->size.supercarrier.used.sa -= (SZ); \
} while (0)
#define ERTS_MMAP_SIZE_SC_SUA_INC(SZ) \
do { \
- mmap_state.size.supercarrier.used.total += (SZ); \
- mmap_state.size.supercarrier.used.sua += (SZ); \
- ERTS_MMAP_ASSERT(mmap_state.size.supercarrier.used.total \
- <= mmap_state.size.supercarrier.total); \
- ERTS_MMAP_ASSERT(mmap_state.size.supercarrier.used.sua \
- <= mmap_state.size.supercarrier.used.total); \
+ mm->size.supercarrier.used.total += (SZ); \
+ mm->size.supercarrier.used.sua += (SZ); \
+ ERTS_MMAP_ASSERT(mm->size.supercarrier.used.total \
+ <= mm->size.supercarrier.total); \
+ ERTS_MMAP_ASSERT(mm->size.supercarrier.used.sua \
+ <= mm->size.supercarrier.used.total); \
} while (0)
#define ERTS_MMAP_SIZE_SC_SUA_DEC(SZ) \
do { \
- ERTS_MMAP_ASSERT(mmap_state.size.supercarrier.used.total >= (SZ)); \
- mmap_state.size.supercarrier.used.total -= (SZ); \
- ERTS_MMAP_ASSERT(mmap_state.size.supercarrier.used.sua >= (SZ)); \
- mmap_state.size.supercarrier.used.sua -= (SZ); \
+ ERTS_MMAP_ASSERT(mm->size.supercarrier.used.total >= (SZ)); \
+ mm->size.supercarrier.used.total -= (SZ); \
+ ERTS_MMAP_ASSERT(mm->size.supercarrier.used.sua >= (SZ)); \
+ mm->size.supercarrier.used.sua -= (SZ); \
} while (0)
#define ERTS_MMAP_SIZE_OS_INC(SZ) \
do { \
- ERTS_MMAP_ASSERT(mmap_state.size.os.used + (SZ) >= (SZ)); \
- mmap_state.size.os.used += (SZ); \
+ ERTS_MMAP_ASSERT(mm->size.os.used + (SZ) >= (SZ)); \
+ mm->size.os.used += (SZ); \
} while (0)
#define ERTS_MMAP_SIZE_OS_DEC(SZ) \
do { \
- ERTS_MMAP_ASSERT(mmap_state.size.os.used >= (SZ)); \
- mmap_state.size.os.used -= (SZ); \
+ ERTS_MMAP_ASSERT(mm->size.os.used >= (SZ)); \
+ mm->size.os.used -= (SZ); \
} while (0)
static void
-add_free_desc_area(char *start, char *end)
+add_free_desc_area(ErtsMemMapper* mm, char *start, char *end)
{
ERTS_MMAP_ASSERT(end == (void *) 0 || end > start);
if (sizeof(ErtsFreeSegDesc) <= ((UWord) end) - ((UWord) start)) {
@@ -403,7 +419,7 @@ add_free_desc_area(char *start, char *end)
no = 1;
prev_desc = (ErtsFreeSegDesc *) start;
- prev_desc->start = mmap_state.desc.free_list;
+ prev_desc->start = mm->desc.free_list;
desc = (ErtsFreeSegDesc *) (start + sizeof(ErtsFreeSegDesc));
desc_end = start + 2*sizeof(ErtsFreeSegDesc);
@@ -414,59 +430,59 @@ add_free_desc_area(char *start, char *end)
desc_end += sizeof(ErtsFreeSegDesc);
no++;
}
- mmap_state.desc.free_list = (char *) prev_desc;
- mmap_state.no.free_seg_descs += no;
+ mm->desc.free_list = (char *) prev_desc;
+ mm->no.free_seg_descs += no;
}
}
static ErtsFreeSegDesc *
-add_unused_free_desc_area(void)
+add_unused_free_desc_area(ErtsMemMapper* mm)
{
char *ptr;
- if (!mmap_state.desc.unused_start)
+ if (!mm->desc.unused_start)
return NULL;
- ERTS_MMAP_ASSERT(mmap_state.desc.unused_end);
+ ERTS_MMAP_ASSERT(mm->desc.unused_end);
ERTS_MMAP_ASSERT(ERTS_PAGEALIGNED_SIZE
- <= mmap_state.desc.unused_end - mmap_state.desc.unused_start);
+ <= mm->desc.unused_end - mm->desc.unused_start);
- ptr = mmap_state.desc.unused_start + ERTS_PAGEALIGNED_SIZE;
- add_free_desc_area(mmap_state.desc.unused_start, ptr);
+ ptr = mm->desc.unused_start + ERTS_PAGEALIGNED_SIZE;
+ add_free_desc_area(mm, mm->desc.unused_start, ptr);
- if ((mmap_state.desc.unused_end - ptr) >= ERTS_PAGEALIGNED_SIZE)
- mmap_state.desc.unused_start = ptr;
+ if ((mm->desc.unused_end - ptr) >= ERTS_PAGEALIGNED_SIZE)
+ mm->desc.unused_start = ptr;
else
- mmap_state.desc.unused_end = mmap_state.desc.unused_start = NULL;
+ mm->desc.unused_end = mm->desc.unused_start = NULL;
- ERTS_MMAP_ASSERT(mmap_state.desc.free_list);
- return (ErtsFreeSegDesc *) mmap_state.desc.free_list;
+ ERTS_MMAP_ASSERT(mm->desc.free_list);
+ return (ErtsFreeSegDesc *) mm->desc.free_list;
}
static ERTS_INLINE ErtsFreeSegDesc *
-alloc_desc(void)
+alloc_desc(ErtsMemMapper* mm)
{
ErtsFreeSegDesc *res;
- res = (ErtsFreeSegDesc *) mmap_state.desc.free_list;
+ res = (ErtsFreeSegDesc *) mm->desc.free_list;
if (!res) {
- res = add_unused_free_desc_area();
+ res = add_unused_free_desc_area(mm);
if (!res)
return NULL;
}
- mmap_state.desc.free_list = res->start;
- ASSERT(mmap_state.no.free_segs.curr < mmap_state.no.free_seg_descs);
- mmap_state.no.free_segs.curr++;
- if (mmap_state.no.free_segs.max < mmap_state.no.free_segs.curr)
- mmap_state.no.free_segs.max = mmap_state.no.free_segs.curr;
+ mm->desc.free_list = res->start;
+ ASSERT(mm->no.free_segs.curr < mm->no.free_seg_descs);
+ mm->no.free_segs.curr++;
+ if (mm->no.free_segs.max < mm->no.free_segs.curr)
+ mm->no.free_segs.max = mm->no.free_segs.curr;
return res;
}
static ERTS_INLINE void
-free_desc(ErtsFreeSegDesc *desc)
+free_desc(ErtsMemMapper* mm, ErtsFreeSegDesc *desc)
{
- desc->start = mmap_state.desc.free_list;
- mmap_state.desc.free_list = (char *) desc;
- ERTS_MMAP_ASSERT(mmap_state.no.free_segs.curr > 0);
- mmap_state.no.free_segs.curr--;
+ desc->start = mm->desc.free_list;
+ mm->desc.free_list = (char *) desc;
+ ERTS_MMAP_ASSERT(mm->no.free_segs.curr > 0);
+ mm->no.free_segs.curr--;
}
static ERTS_INLINE ErtsFreeSegDesc* anode_to_desc(RBTNode* anode)
@@ -1225,6 +1241,7 @@ Eterm build_free_seg_list(Process* p, ErtsFreeSegMap* map)
#if HAVE_MMAP
# define ERTS_MMAP_PROT (PROT_READ|PROT_WRITE)
+# define ERTS_MMAP_PROT_EXEC (PROT_READ|PROT_WRITE|PROT_EXEC)
# if defined(MAP_ANONYMOUS)
# define ERTS_MMAP_FLAGS (MAP_ANON|MAP_PRIVATE)
# define ERTS_MMAP_FD (-1)
@@ -1233,29 +1250,31 @@ Eterm build_free_seg_list(Process* p, ErtsFreeSegMap* map)
# define ERTS_MMAP_FD (-1)
# else
# define ERTS_MMAP_FLAGS (MAP_PRIVATE)
-# define ERTS_MMAP_FD mmap_state.mmap_fd
+# define ERTS_MMAP_FD mm->mmap_fd
# endif
#endif
static ERTS_INLINE void *
-os_mmap(void *hint_ptr, UWord size, int try_superalign)
+os_mmap(void *hint_ptr, UWord size, int try_superalign, int executable)
{
#if HAVE_MMAP
+ const int prot = executable ? ERTS_MMAP_PROT_EXEC : ERTS_MMAP_PROT;
void *res;
#ifdef MAP_ALIGN
if (try_superalign)
- res = mmap((void *) ERTS_SUPERALIGNED_SIZE, size, ERTS_MMAP_PROT,
+ res = mmap((void *) ERTS_SUPERALIGNED_SIZE, size, prot,
ERTS_MMAP_FLAGS|MAP_ALIGN, ERTS_MMAP_FD, 0);
else
#endif
- res = mmap((void *) hint_ptr, size, ERTS_MMAP_PROT,
+ res = mmap((void *) hint_ptr, size, prot,
ERTS_MMAP_FLAGS, ERTS_MMAP_FD, 0);
if (res == MAP_FAILED)
return NULL;
return res;
#elif HAVE_VIRTUALALLOC
+ const DWORD prot = executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
return (void *) VirtualAlloc(NULL, (SIZE_T) size,
- MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
+ MEM_COMMIT|MEM_RESERVE, prot);
#else
# error "missing mmap() or similar"
#endif
@@ -1312,16 +1331,26 @@ os_mremap(void *ptr, UWord old_size, UWord new_size, int try_superalign)
#if HAVE_MMAP
#define ERTS_MMAP_RESERVE_PROT (ERTS_MMAP_PROT)
+#define ERTS_MMAP_RESERVE_PROT_EXEC (ERTS_MMAP_PROT_EXEC)
#define ERTS_MMAP_RESERVE_FLAGS (ERTS_MMAP_FLAGS|MAP_FIXED)
#define ERTS_MMAP_UNRESERVE_PROT (PROT_NONE)
+#if defined(__FreeBSD__)
+#define ERTS_MMAP_UNRESERVE_FLAGS (ERTS_MMAP_FLAGS|MAP_FIXED)
+#else
#define ERTS_MMAP_UNRESERVE_FLAGS (ERTS_MMAP_FLAGS|MAP_NORESERVE|MAP_FIXED)
+#endif /* __FreeBSD__ */
#define ERTS_MMAP_VIRTUAL_PROT (PROT_NONE)
+#if defined(__FreeBSD__)
+#define ERTS_MMAP_VIRTUAL_FLAGS (ERTS_MMAP_FLAGS)
+#else
#define ERTS_MMAP_VIRTUAL_FLAGS (ERTS_MMAP_FLAGS|MAP_NORESERVE)
+#endif /* __FreeBSD__ */
static int
-os_reserve_physical(char *ptr, UWord size)
+os_reserve_physical(char *ptr, UWord size, int exec)
{
- void *res = mmap((void *) ptr, (size_t) size, ERTS_MMAP_RESERVE_PROT,
+ const int prot = exec ? ERTS_MMAP_RESERVE_PROT_EXEC : ERTS_MMAP_RESERVE_PROT;
+ void *res = mmap((void *) ptr, (size_t) size, prot,
ERTS_MMAP_RESERVE_FLAGS, ERTS_MMAP_FD, 0);
if (res == (void *) MAP_FAILED)
return 0;
@@ -1338,10 +1367,39 @@ os_unreserve_physical(char *ptr, UWord size)
}
static void *
-os_mmap_virtual(char *ptr, UWord size)
+os_mmap_virtual(char *ptr, UWord size, int exec)
{
- void *res = mmap((void *) ptr, (size_t) size, ERTS_MMAP_VIRTUAL_PROT,
- ERTS_MMAP_VIRTUAL_FLAGS, ERTS_MMAP_FD, 0);
+ int flags = ERTS_MMAP_VIRTUAL_FLAGS;
+ void* res;
+
+#ifdef ERTS_ALC_A_EXEC
+ if (exec) {
+ ASSERT(!ptr);
+ /* OTP-19.0: Nice hack below cut-and-pasted from hipe_amd64.c */
+
+# ifdef MAP_32BIT
+ /* If we got MAP_32BIT (Linux), then use that to ask for low memory */
+ flags |= MAP_32BIT;
+# else
+ /* FreeBSD doesn't have MAP_32BIT, and it doesn't respect
+ a plain map_hint (returns high mappings even though the
+ hint refers to a free area), so we have to use both map_hint
+ and MAP_FIXED to get addresses below the 2GB boundary.
+ This is even worse than the Linux/ppc64 case.
+ Similarly, Solaris 10 doesn't have MAP_32BIT,
+ and it doesn't respect a plain map_hint. */
+ ptr = (char*)(512*1024*1024); /* 0.5GB */
+
+# if defined(__FreeBSD__) || defined(__sun__)
+ flags |= MAP_FIXED;
+# endif
+# endif /* !MAP_32BIT */
+ }
+#else /* !ERTS_ALC_A_EXEC */
+ ASSERT(!exec);
+#endif
+ res = mmap((void *) ptr, (size_t) size, ERTS_MMAP_VIRTUAL_PROT,
+ flags, ERTS_MMAP_FD, 0);
if (res == (void *) MAP_FAILED)
return NULL;
return res;
@@ -1354,7 +1412,7 @@ os_mmap_virtual(char *ptr, UWord size)
#endif /* ERTS_HAVE_OS_MMAP */
-static int reserve_noop(char *ptr, UWord size)
+static int reserve_noop(char *ptr, UWord size, int exec)
{
#ifdef ERTS_MMAP_DEBUG_FILL_AREAS
Uint32 *uip, *end = (Uint32 *) (ptr + size);
@@ -1378,11 +1436,12 @@ static void unreserve_noop(char *ptr, UWord size)
}
static UWord
-alloc_desc_insert_free_seg(ErtsFreeSegMap *map, char* start, char* end)
+alloc_desc_insert_free_seg(ErtsMemMapper* mm,
+ ErtsFreeSegMap *map, char* start, char* end)
{
char *ptr;
ErtsFreeSegMap *da_map;
- ErtsFreeSegDesc *desc = alloc_desc();
+ ErtsFreeSegDesc *desc = alloc_desc(mm);
if (desc) {
insert_free_seg(map, desc, start, end);
return 0;
@@ -1395,13 +1454,13 @@ alloc_desc_insert_free_seg(ErtsFreeSegMap *map, char* start, char* end)
*/
#if ERTS_HAVE_OS_MMAP
- if (!mmap_state.no_os_mmap) {
- ptr = os_mmap(mmap_state.desc.new_area_hint, ERTS_PAGEALIGNED_SIZE, 0);
+ if (!mm->no_os_mmap) {
+ ptr = os_mmap(mm->desc.new_area_hint, ERTS_PAGEALIGNED_SIZE, 0, 0);
if (ptr) {
- mmap_state.desc.new_area_hint = ptr+ERTS_PAGEALIGNED_SIZE;
+ mm->desc.new_area_hint = ptr+ERTS_PAGEALIGNED_SIZE;
ERTS_MMAP_SIZE_OS_INC(ERTS_PAGEALIGNED_SIZE);
- add_free_desc_area(ptr, ptr+ERTS_PAGEALIGNED_SIZE);
- desc = alloc_desc();
+ add_free_desc_area(mm, ptr, ptr+ERTS_PAGEALIGNED_SIZE);
+ desc = alloc_desc(mm);
ERTS_MMAP_ASSERT(desc);
insert_free_seg(map, desc, start, end);
return 0;
@@ -1412,20 +1471,20 @@ alloc_desc_insert_free_seg(ErtsFreeSegMap *map, char* start, char* end)
/*
* ...then try to find a good place in the supercarrier...
*/
- da_map = &mmap_state.sua.map;
+ da_map = &mm->sua.map;
desc = lookup_free_seg(da_map, ERTS_PAGEALIGNED_SIZE);
if (desc) {
- if (mmap_state.reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE))
+ if (mm->reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE, 0))
ERTS_MMAP_SIZE_SC_SUA_INC(ERTS_PAGEALIGNED_SIZE);
else
desc = NULL;
}
else {
- da_map = &mmap_state.sa.map;
+ da_map = &mm->sa.map;
desc = lookup_free_seg(da_map, ERTS_PAGEALIGNED_SIZE);
if (desc) {
- if (mmap_state.reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE))
+ if (mm->reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE, 0))
ERTS_MMAP_SIZE_SC_SA_INC(ERTS_PAGEALIGNED_SIZE);
else
desc = NULL;
@@ -1433,15 +1492,15 @@ alloc_desc_insert_free_seg(ErtsFreeSegMap *map, char* start, char* end)
}
if (desc) {
char *da_end = desc->start + ERTS_PAGEALIGNED_SIZE;
- add_free_desc_area(desc->start, da_end);
+ add_free_desc_area(mm, desc->start, da_end);
if (da_end != desc->end)
resize_free_seg(da_map, desc, da_end, desc->end);
else {
delete_free_seg(da_map, desc);
- free_desc(desc);
+ free_desc(mm, desc);
}
- desc = alloc_desc();
+ desc = alloc_desc(mm);
ERTS_MMAP_ASSERT(desc);
insert_free_seg(map, desc, start, end);
return 0;
@@ -1454,10 +1513,10 @@ alloc_desc_insert_free_seg(ErtsFreeSegMap *map, char* start, char* end)
ptr = start + ERTS_PAGEALIGNED_SIZE;
ERTS_MMAP_ASSERT(ptr <= end);
- add_free_desc_area(start, ptr);
+ add_free_desc_area(mm, start, ptr);
if (ptr != end) {
- desc = alloc_desc();
+ desc = alloc_desc(mm);
ERTS_MMAP_ASSERT(desc);
insert_free_seg(map, desc, ptr, end);
}
@@ -1466,46 +1525,46 @@ alloc_desc_insert_free_seg(ErtsFreeSegMap *map, char* start, char* end)
}
void *
-erts_mmap(Uint32 flags, UWord *sizep)
+erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep)
{
char *seg;
UWord asize = ERTS_PAGEALIGNED_CEILING(*sizep);
/* Map in premapped supercarrier */
- if (mmap_state.supercarrier && !(ERTS_MMAPFLG_OS_ONLY & flags)) {
+ if (mm->supercarrier && !(ERTS_MMAPFLG_OS_ONLY & flags)) {
char *end;
ErtsFreeSegDesc *desc;
Uint32 superaligned = (ERTS_MMAPFLG_SUPERALIGNED & flags);
- erts_smp_mtx_lock(&mmap_state.mtx);
+ erts_smp_mtx_lock(&mm->mtx);
ERTS_MMAP_OP_START(*sizep);
if (!superaligned) {
- desc = lookup_free_seg(&mmap_state.sua.map, asize);
+ desc = lookup_free_seg(&mm->sua.map, asize);
if (desc) {
seg = desc->start;
end = seg+asize;
- if (!mmap_state.reserve_physical(seg, asize))
+ if (!mm->reserve_physical(seg, asize, mm->executable))
goto supercarrier_reserve_failure;
if (desc->end == end) {
- delete_free_seg(&mmap_state.sua.map, desc);
- free_desc(desc);
+ delete_free_seg(&mm->sua.map, desc);
+ free_desc(mm, desc);
}
else {
ERTS_MMAP_ASSERT(end < desc->end);
- resize_free_seg(&mmap_state.sua.map, desc, end, desc->end);
+ resize_free_seg(&mm->sua.map, desc, end, desc->end);
}
ERTS_MMAP_SIZE_SC_SUA_INC(asize);
goto supercarrier_success;
}
- if (asize <= mmap_state.sua.bot - mmap_state.sa.top) {
- if (!mmap_state.reserve_physical(mmap_state.sua.bot - asize,
- asize))
+ if (asize <= mm->sua.bot - mm->sa.top) {
+ if (!mm->reserve_physical(mm->sua.bot - asize, asize,
+ mm->executable))
goto supercarrier_reserve_failure;
- mmap_state.sua.bot -= asize;
- seg = mmap_state.sua.bot;
+ mm->sua.bot -= asize;
+ seg = mm->sua.bot;
ERTS_MMAP_SIZE_SC_SUA_INC(asize);
goto supercarrier_success;
}
@@ -1513,84 +1572,87 @@ erts_mmap(Uint32 flags, UWord *sizep)
asize = ERTS_SUPERALIGNED_CEILING(asize);
- desc = lookup_free_seg(&mmap_state.sa.map, asize);
+ desc = lookup_free_seg(&mm->sa.map, asize);
if (desc) {
char *start = seg = desc->start;
seg = (char *) ERTS_SUPERALIGNED_CEILING(seg);
end = seg+asize;
- if (!mmap_state.reserve_physical(start, (UWord) (end - start)))
+ if (!mm->reserve_physical(start, (UWord) (end - start),
+ mm->executable))
goto supercarrier_reserve_failure;
ERTS_MMAP_SIZE_SC_SA_INC(asize);
if (desc->end == end) {
if (start != seg)
- resize_free_seg(&mmap_state.sa.map, desc, start, seg);
+ resize_free_seg(&mm->sa.map, desc, start, seg);
else {
- delete_free_seg(&mmap_state.sa.map, desc);
- free_desc(desc);
+ delete_free_seg(&mm->sa.map, desc);
+ free_desc(mm, desc);
}
}
else {
ERTS_MMAP_ASSERT(end < desc->end);
- resize_free_seg(&mmap_state.sa.map, desc, end, desc->end);
+ resize_free_seg(&mm->sa.map, desc, end, desc->end);
if (start != seg) {
UWord ad_sz;
- ad_sz = alloc_desc_insert_free_seg(&mmap_state.sua.map,
+ ad_sz = alloc_desc_insert_free_seg(mm, &mm->sua.map,
start, seg);
start += ad_sz;
if (start != seg)
- mmap_state.unreserve_physical(start, (UWord) (seg - start));
+ mm->unreserve_physical(start, (UWord) (seg - start));
}
}
goto supercarrier_success;
}
if (superaligned) {
- char *start = mmap_state.sa.top;
+ char *start = mm->sa.top;
seg = (char *) ERTS_SUPERALIGNED_CEILING(start);
- if (asize + (seg - start) <= mmap_state.sua.bot - start) {
+ if (asize + (seg - start) <= mm->sua.bot - start) {
end = seg + asize;
- if (!mmap_state.reserve_physical(start, (UWord) (end - start)))
+ if (!mm->reserve_physical(start, (UWord) (end - start),
+ mm->executable))
goto supercarrier_reserve_failure;
- mmap_state.sa.top = end;
+ mm->sa.top = end;
ERTS_MMAP_SIZE_SC_SA_INC(asize);
if (start != seg) {
UWord ad_sz;
- ad_sz = alloc_desc_insert_free_seg(&mmap_state.sua.map,
+ ad_sz = alloc_desc_insert_free_seg(mm, &mm->sua.map,
start, seg);
start += ad_sz;
if (start != seg)
- mmap_state.unreserve_physical(start, (UWord) (seg - start));
+ mm->unreserve_physical(start, (UWord) (seg - start));
}
goto supercarrier_success;
}
- desc = lookup_free_seg(&mmap_state.sua.map, asize + ERTS_SUPERALIGNED_SIZE);
+ desc = lookup_free_seg(&mm->sua.map, asize + ERTS_SUPERALIGNED_SIZE);
if (desc) {
char *org_start = desc->start;
char *org_end = desc->end;
seg = (char *) ERTS_SUPERALIGNED_CEILING(org_start);
end = seg + asize;
- if (!mmap_state.reserve_physical(seg, (UWord) (org_end - seg)))
+ if (!mm->reserve_physical(seg, (UWord) (org_end - seg),
+ mm->executable))
goto supercarrier_reserve_failure;
ERTS_MMAP_SIZE_SC_SUA_INC(asize);
if (org_start != seg) {
ERTS_MMAP_ASSERT(org_start < seg);
- resize_free_seg(&mmap_state.sua.map, desc, org_start, seg);
+ resize_free_seg(&mm->sua.map, desc, org_start, seg);
desc = NULL;
}
if (end != org_end) {
UWord ad_sz = 0;
ERTS_MMAP_ASSERT(end < org_end);
if (desc)
- resize_free_seg(&mmap_state.sua.map, desc, end, org_end);
+ resize_free_seg(&mm->sua.map, desc, end, org_end);
else
- ad_sz = alloc_desc_insert_free_seg(&mmap_state.sua.map,
+ ad_sz = alloc_desc_insert_free_seg(mm, &mm->sua.map,
end, org_end);
end += ad_sz;
if (end != org_end)
- mmap_state.unreserve_physical(end,
+ mm->unreserve_physical(end,
(UWord) (org_end - end));
}
goto supercarrier_success;
@@ -1598,20 +1660,20 @@ erts_mmap(Uint32 flags, UWord *sizep)
}
ERTS_MMAP_OP_ABORT();
- erts_smp_mtx_unlock(&mmap_state.mtx);
+ erts_smp_mtx_unlock(&mm->mtx);
}
#if ERTS_HAVE_OS_MMAP
/* Map using OS primitives */
- if (!(ERTS_MMAPFLG_SUPERCARRIER_ONLY & flags) && !mmap_state.no_os_mmap) {
+ if (!(ERTS_MMAPFLG_SUPERCARRIER_ONLY & flags) && !mm->no_os_mmap) {
if (!(ERTS_MMAPFLG_SUPERALIGNED & flags)) {
- seg = os_mmap(NULL, asize, 0);
+ seg = os_mmap(NULL, asize, 0, mm->executable);
if (!seg)
goto failure;
}
else {
asize = ERTS_SUPERALIGNED_CEILING(*sizep);
- seg = os_mmap(NULL, asize, 1);
+ seg = os_mmap(NULL, asize, 1, mm->executable);
if (!seg)
goto failure;
@@ -1621,7 +1683,8 @@ erts_mmap(Uint32 flags, UWord *sizep)
os_munmap(seg, asize);
- ptr = os_mmap(NULL, asize + ERTS_SUPERALIGNED_SIZE, 1);
+ ptr = os_mmap(NULL, asize + ERTS_SUPERALIGNED_SIZE, 1,
+ mm->executable);
if (!ptr)
goto failure;
@@ -1661,25 +1724,25 @@ supercarrier_success:
#endif
ERTS_MMAP_OP_END(seg, asize);
- erts_smp_mtx_unlock(&mmap_state.mtx);
+ erts_smp_mtx_unlock(&mm->mtx);
*sizep = asize;
return (void *) seg;
supercarrier_reserve_failure:
- erts_smp_mtx_unlock(&mmap_state.mtx);
+ erts_smp_mtx_unlock(&mm->mtx);
*sizep = 0;
return NULL;
}
void
-erts_munmap(Uint32 flags, void *ptr, UWord size)
+erts_munmap(ErtsMemMapper* mm, Uint32 flags, void *ptr, UWord size)
{
ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(ptr));
ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(size));
if (!ERTS_MMAP_IN_SUPERCARRIER(ptr)) {
- ERTS_MMAP_ASSERT(!mmap_state.no_os_mmap);
+ ERTS_MMAP_ASSERT(!mm->no_os_mmap);
#if ERTS_HAVE_OS_MMAP
ERTS_MUNMAP_OP_LCK(ptr, size);
ERTS_MMAP_SIZE_OS_DEC(size);
@@ -1692,45 +1755,45 @@ erts_munmap(Uint32 flags, void *ptr, UWord size)
ErtsFreeSegDesc *prev, *next, *desc;
UWord ad_sz = 0;
- ERTS_MMAP_ASSERT(mmap_state.supercarrier);
+ ERTS_MMAP_ASSERT(mm->supercarrier);
start = (char *) ptr;
end = start + size;
- erts_smp_mtx_lock(&mmap_state.mtx);
+ erts_smp_mtx_lock(&mm->mtx);
ERTS_MUNMAP_OP(ptr, size);
if (ERTS_MMAP_IN_SUPERALIGNED_AREA(ptr)) {
- map = &mmap_state.sa.map;
+ map = &mm->sa.map;
adjacent_free_seg(map, start, end, &prev, &next);
ERTS_MMAP_SIZE_SC_SA_DEC(size);
- if (end == mmap_state.sa.top) {
+ if (end == mm->sa.top) {
ERTS_MMAP_ASSERT(!next);
if (prev) {
start = prev->start;
delete_free_seg(map, prev);
- free_desc(prev);
+ free_desc(mm, prev);
}
- mmap_state.sa.top = start;
+ mm->sa.top = start;
goto supercarrier_success;
}
}
else {
- map = &mmap_state.sua.map;
+ map = &mm->sua.map;
adjacent_free_seg(map, start, end, &prev, &next);
ERTS_MMAP_SIZE_SC_SUA_DEC(size);
- if (start == mmap_state.sua.bot) {
+ if (start == mm->sua.bot) {
ERTS_MMAP_ASSERT(!prev);
if (next) {
end = next->end;
delete_free_seg(map, next);
- free_desc(next);
+ free_desc(mm, next);
}
- mmap_state.sua.bot = end;
+ mm->sua.bot = end;
goto supercarrier_success;
}
}
@@ -1742,7 +1805,7 @@ erts_munmap(Uint32 flags, void *ptr, UWord size)
end = next->end;
if (prev) {
delete_free_seg(map, next);
- free_desc(next);
+ free_desc(mm, next);
goto save_prev;
}
desc = next;
@@ -1756,7 +1819,7 @@ erts_munmap(Uint32 flags, void *ptr, UWord size)
if (desc)
resize_free_seg(map, desc, start, end);
else
- ad_sz = alloc_desc_insert_free_seg(map, start, end);
+ ad_sz = alloc_desc_insert_free_seg(mm, map, start, end);
supercarrier_success: {
UWord unres_sz;
@@ -1764,30 +1827,32 @@ erts_munmap(Uint32 flags, void *ptr, UWord size)
ERTS_MMAP_ASSERT(size >= ad_sz);
unres_sz = size - ad_sz;
if (unres_sz)
- mmap_state.unreserve_physical(((char *) ptr) + ad_sz, unres_sz);
+ mm->unreserve_physical(((char *) ptr) + ad_sz, unres_sz);
- erts_smp_mtx_unlock(&mmap_state.mtx);
+ erts_smp_mtx_unlock(&mm->mtx);
}
}
}
static void *
-remap_move(Uint32 flags, void *ptr, UWord old_size, UWord *sizep)
+remap_move(ErtsMemMapper* mm,
+ Uint32 flags, void *ptr, UWord old_size, UWord *sizep)
{
UWord size = *sizep;
- void *new_ptr = erts_mmap(flags, &size);
+ void *new_ptr = erts_mmap(mm, flags, &size);
if (!new_ptr)
return NULL;
*sizep = size;
if (old_size < size)
size = old_size;
sys_memcpy(new_ptr, ptr, (size_t) size);
- erts_munmap(flags, ptr, old_size);
+ erts_munmap(mm, flags, ptr, old_size);
return new_ptr;
}
void *
-erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep)
+erts_mremap(ErtsMemMapper* mm,
+ Uint32 flags, void *ptr, UWord old_size, UWord *sizep)
{
void *new_ptr;
Uint32 superaligned;
@@ -1799,11 +1864,11 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep)
if (!ERTS_MMAP_IN_SUPERCARRIER(ptr)) {
- ERTS_MMAP_ASSERT(!mmap_state.no_os_mmap);
+ ERTS_MMAP_ASSERT(!mm->no_os_mmap);
- if (!(ERTS_MMAPFLG_OS_ONLY & flags) && mmap_state.supercarrier) {
- new_ptr = remap_move(ERTS_MMAPFLG_SUPERCARRIER_ONLY|flags, ptr,
- old_size, sizep);
+ if (!(ERTS_MMAPFLG_OS_ONLY & flags) && mm->supercarrier) {
+ new_ptr = remap_move(mm, ERTS_MMAPFLG_SUPERCARRIER_ONLY|flags,
+ ptr, old_size, sizep);
if (new_ptr)
return new_ptr;
}
@@ -1850,7 +1915,7 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep)
#endif
#if ERTS_HAVE_OS_MREMAP
if (superaligned)
- return remap_move(flags, new_ptr, old_size, sizep);
+ return remap_move(mm, flags, new_ptr, old_size, sizep);
else {
new_ptr = os_mremap(ptr, old_size, asize, 0);
if (!new_ptr)
@@ -1872,10 +1937,10 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep)
ErtsFreeSegDesc *prev, *next;
UWord ad_sz = 0;
- ERTS_MMAP_ASSERT(mmap_state.supercarrier);
+ ERTS_MMAP_ASSERT(mm->supercarrier);
if (ERTS_MMAPFLG_OS_ONLY & flags)
- return remap_move(flags, ptr, old_size, sizep);
+ return remap_move(mm, flags, ptr, old_size, sizep);
superaligned = (ERTS_MMAPFLG_SUPERALIGNED & flags);
@@ -1883,19 +1948,19 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep)
? ERTS_SUPERALIGNED_CEILING(*sizep)
: ERTS_PAGEALIGNED_CEILING(*sizep));
- erts_smp_mtx_lock(&mmap_state.mtx);
+ erts_smp_mtx_lock(&mm->mtx);
if (ERTS_MMAP_IN_SUPERALIGNED_AREA(ptr)
- ? (!superaligned && lookup_free_seg(&mmap_state.sua.map, asize))
- : (superaligned && lookup_free_seg(&mmap_state.sa.map, asize))) {
- erts_smp_mtx_unlock(&mmap_state.mtx);
+ ? (!superaligned && lookup_free_seg(&mm->sua.map, asize))
+ : (superaligned && lookup_free_seg(&mm->sa.map, asize))) {
+ erts_smp_mtx_unlock(&mm->mtx);
/*
* Segment currently in wrong area (due to a previous memory
* shortage), move it to the right area.
* (remap_move() will succeed)
*/
- return remap_move(ERTS_MMAPFLG_SUPERCARRIER_ONLY|flags, ptr,
- old_size, sizep);
+ return remap_move(mm, ERTS_MMAPFLG_SUPERCARRIER_ONLY|flags,
+ ptr, old_size, sizep);
}
ERTS_MREMAP_OP_START(ptr, old_size, *sizep);
@@ -1917,18 +1982,18 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep)
UWord unres_sz;
new_ptr = ptr;
if (!ERTS_MMAP_IN_SUPERALIGNED_AREA(ptr)) {
- map = &mmap_state.sua.map;
+ map = &mm->sua.map;
ERTS_MMAP_SIZE_SC_SUA_DEC(old_size - asize);
}
else {
- if (end == mmap_state.sa.top) {
- mmap_state.sa.top = new_end;
- mmap_state.unreserve_physical(((char *) ptr) + asize,
+ if (end == mm->sa.top) {
+ mm->sa.top = new_end;
+ mm->unreserve_physical(((char *) ptr) + asize,
old_size - asize);
goto supercarrier_resize_success;
}
ERTS_MMAP_SIZE_SC_SA_DEC(old_size - asize);
- map = &mmap_state.sa.map;
+ map = &mm->sa.map;
}
adjacent_free_seg(map, start, end, &prev, &next);
@@ -1936,11 +2001,11 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep)
if (next)
resize_free_seg(map, next, new_end, next->end);
else
- ad_sz = alloc_desc_insert_free_seg(map, new_end, end);
+ ad_sz = alloc_desc_insert_free_seg(mm, map, new_end, end);
ERTS_MMAP_ASSERT(old_size - asize >= ad_sz);
unres_sz = old_size - asize - ad_sz;
if (unres_sz)
- mmap_state.unreserve_physical(((char *) ptr) + asize + ad_sz,
+ mm->unreserve_physical(((char *) ptr) + asize + ad_sz,
unres_sz);
goto supercarrier_resize_success;
}
@@ -1950,17 +2015,18 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep)
ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(old_size));
ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(asize));
- adjacent_free_seg(&mmap_state.sua.map, start, end, &prev, &next);
+ adjacent_free_seg(&mm->sua.map, start, end, &prev, &next);
if (next && new_end <= next->end) {
- if (!mmap_state.reserve_physical(((char *) ptr) + old_size,
- asize - old_size))
+ if (!mm->reserve_physical(((char *) ptr) + old_size,
+ asize - old_size,
+ mm->executable))
goto supercarrier_reserve_failure;
if (new_end < next->end)
- resize_free_seg(&mmap_state.sua.map, next, new_end, next->end);
+ resize_free_seg(&mm->sua.map, next, new_end, next->end);
else {
- delete_free_seg(&mmap_state.sua.map, next);
- free_desc(next);
+ delete_free_seg(&mm->sua.map, next);
+ free_desc(mm, next);
}
new_ptr = ptr;
ERTS_MMAP_SIZE_SC_SUA_INC(asize - old_size);
@@ -1969,28 +2035,30 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep)
}
else { /* Superaligned area */
- if (end == mmap_state.sa.top) {
- if (new_end <= mmap_state.sua.bot) {
- if (!mmap_state.reserve_physical(((char *) ptr) + old_size,
- asize - old_size))
+ if (end == mm->sa.top) {
+ if (new_end <= mm->sua.bot) {
+ if (!mm->reserve_physical(((char *) ptr) + old_size,
+ asize - old_size,
+ mm->executable))
goto supercarrier_reserve_failure;
- mmap_state.sa.top = new_end;
+ mm->sa.top = new_end;
new_ptr = ptr;
ERTS_MMAP_SIZE_SC_SA_INC(asize - old_size);
goto supercarrier_resize_success;
}
}
else {
- adjacent_free_seg(&mmap_state.sa.map, start, end, &prev, &next);
+ adjacent_free_seg(&mm->sa.map, start, end, &prev, &next);
if (next && new_end <= next->end) {
- if (!mmap_state.reserve_physical(((char *) ptr) + old_size,
- asize - old_size))
+ if (!mm->reserve_physical(((char *) ptr) + old_size,
+ asize - old_size,
+ mm->executable))
goto supercarrier_reserve_failure;
if (new_end < next->end)
- resize_free_seg(&mmap_state.sa.map, next, new_end, next->end);
+ resize_free_seg(&mm->sa.map, next, new_end, next->end);
else {
- delete_free_seg(&mmap_state.sa.map, next);
- free_desc(next);
+ delete_free_seg(&mm->sa.map, next);
+ free_desc(mm, next);
}
new_ptr = ptr;
ERTS_MMAP_SIZE_SC_SA_INC(asize - old_size);
@@ -2000,12 +2068,12 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep)
}
ERTS_MMAP_OP_ABORT();
- erts_smp_mtx_unlock(&mmap_state.mtx);
+ erts_smp_mtx_unlock(&mm->mtx);
/* Failed to resize... */
}
- return remap_move(flags, ptr, old_size, sizep);
+ return remap_move(mm, flags, ptr, old_size, sizep);
supercarrier_resize_success:
@@ -2022,26 +2090,26 @@ supercarrier_resize_success:
#endif
ERTS_MREMAP_OP_END(new_ptr, asize);
- erts_smp_mtx_unlock(&mmap_state.mtx);
+ erts_smp_mtx_unlock(&mm->mtx);
*sizep = asize;
return new_ptr;
supercarrier_reserve_failure:
ERTS_MREMAP_OP_END(NULL, old_size);
- erts_smp_mtx_unlock(&mmap_state.mtx);
+ erts_smp_mtx_unlock(&mm->mtx);
*sizep = old_size;
return NULL;
}
-int erts_mmap_in_supercarrier(void *ptr)
+int erts_mmap_in_supercarrier(ErtsMemMapper* mm, void *ptr)
{
return ERTS_MMAP_IN_SUPERCARRIER(ptr);
}
-
static struct {
+ Eterm options;
Eterm total;
Eterm total_sa;
Eterm total_sua;
@@ -2075,6 +2143,7 @@ static void init_atoms(void)
erts_mtx_lock(&am.init_mutex);
if (!am.is_initialized) {
+ AM_INIT(options);
AM_INIT(total);
AM_INIT(total_sa);
AM_INIT(total_sua);
@@ -2103,8 +2172,9 @@ static void hard_dbg_mseg_init(void);
#endif
void
-erts_mmap_init(ErtsMMapInit *init)
+erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable)
{
+ static int is_first_call = 1;
int virtual_map = 0;
char *start = NULL, *end = NULL;
UWord pagesize;
@@ -2131,20 +2201,21 @@ erts_mmap_init(ErtsMMapInit *init)
ERTS_MMAP_OP_RINGBUF_INIT();
- erts_have_erts_mmap = 0;
-
- mmap_state.supercarrier = 0;
- mmap_state.reserve_physical = reserve_noop;
- mmap_state.unreserve_physical = unreserve_noop;
+ mm->supercarrier = 0;
+ mm->reserve_physical = reserve_noop;
+ mm->unreserve_physical = unreserve_noop;
+ mm->executable = executable;
#if HAVE_MMAP && !defined(MAP_ANON)
- mmap_state.mmap_fd = open("/dev/zero", O_RDWR);
- if (mmap_state.mmap_fd < 0)
+ mm->mmap_fd = open("/dev/zero", O_RDWR);
+ if (mm->mmap_fd < 0)
erts_exit(1, "erts_mmap: Failed to open /dev/zero\n");
#endif
- erts_smp_mtx_init(&mmap_state.mtx, "erts_mmap");
- erts_mtx_init(&am.init_mutex, "mmap_init_atoms");
+ erts_smp_mtx_init(&mm->mtx, "erts_mmap");
+ if (is_first_call) {
+ erts_mtx_init(&am.init_mutex, "mmap_init_atoms");
+ }
#ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION
if (init->virtual_range.start) {
@@ -2153,15 +2224,15 @@ erts_mmap_init(ErtsMMapInit *init)
ptr = (char *) ERTS_PAGEALIGNED_CEILING(init->virtual_range.start);
end = (char *) ERTS_PAGEALIGNED_FLOOR(init->virtual_range.end);
sz = end - ptr;
- start = os_mmap_virtual(ptr, sz);
+ start = os_mmap_virtual(ptr, sz, executable);
if (!start || start > ptr || start >= end)
erts_exit(1,
"erts_mmap: Failed to create virtual range for super carrier\n");
sz = start - ptr;
if (sz)
os_munmap(end, sz);
- mmap_state.reserve_physical = os_reserve_physical;
- mmap_state.unreserve_physical = os_unreserve_physical;
+ mm->reserve_physical = os_reserve_physical;
+ mm->unreserve_physical = os_unreserve_physical;
virtual_map = 1;
}
else
@@ -2178,9 +2249,9 @@ erts_mmap_init(ErtsMMapInit *init)
sz = ERTS_PAGEALIGNED_CEILING(init->scs);
#ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION
if (!init->scrpm) {
- start = os_mmap_virtual(NULL, sz);
- mmap_state.reserve_physical = os_reserve_physical;
- mmap_state.unreserve_physical = os_unreserve_physical;
+ start = os_mmap_virtual(NULL, sz, executable);
+ mm->reserve_physical = os_reserve_physical;
+ mm->unreserve_physical = os_unreserve_physical;
virtual_map = 1;
}
else
@@ -2190,7 +2261,7 @@ erts_mmap_init(ErtsMMapInit *init)
* The whole supercarrier will by physically
* reserved all the time.
*/
- start = os_mmap(NULL, sz, 1);
+ start = os_mmap(NULL, sz, 1, executable);
}
if (!start)
erts_exit(1,
@@ -2206,34 +2277,32 @@ erts_mmap_init(ErtsMMapInit *init)
}
#endif
}
- if (!mmap_state.no_os_mmap)
- erts_have_erts_mmap |= ERTS_HAVE_ERTS_OS_MMAP;
#endif
- mmap_state.no.free_seg_descs = 0;
- mmap_state.no.free_segs.curr = 0;
- mmap_state.no.free_segs.max = 0;
+ mm->no.free_seg_descs = 0;
+ mm->no.free_segs.curr = 0;
+ mm->no.free_segs.max = 0;
- mmap_state.size.supercarrier.total = 0;
- mmap_state.size.supercarrier.used.total = 0;
- mmap_state.size.supercarrier.used.sa = 0;
- mmap_state.size.supercarrier.used.sua = 0;
- mmap_state.size.os.used = 0;
+ mm->size.supercarrier.total = 0;
+ mm->size.supercarrier.used.total = 0;
+ mm->size.supercarrier.used.sa = 0;
+ mm->size.supercarrier.used.sua = 0;
+ mm->size.os.used = 0;
- mmap_state.desc.new_area_hint = NULL;
+ mm->desc.new_area_hint = NULL;
if (!start) {
- mmap_state.sa.bot = NULL;
- mmap_state.sua.top = NULL;
- mmap_state.sa.bot = NULL;
- mmap_state.sua.top = NULL;
- mmap_state.no_os_mmap = 0;
- mmap_state.supercarrier = 0;
+ mm->sa.bot = NULL;
+ mm->sua.top = NULL;
+ mm->sa.bot = NULL;
+ mm->sua.top = NULL;
+ mm->no_os_mmap = 0;
+ mm->supercarrier = 0;
}
else {
size_t desc_size;
- mmap_state.no_os_mmap = init->sco;
+ mm->no_os_mmap = init->sco;
desc_size = init->scrfsd;
if (desc_size < 100)
@@ -2244,66 +2313,73 @@ erts_mmap_init(ErtsMMapInit *init)
+ ERTS_PAGEALIGNED_SIZE) > end - start)
erts_exit(1, "erts_mmap: No space for segments in super carrier\n");
- mmap_state.sa.bot = start;
- mmap_state.sa.bot += desc_size;
- mmap_state.sa.bot = (char *) ERTS_SUPERALIGNED_CEILING(mmap_state.sa.bot);
- mmap_state.sa.top = mmap_state.sa.bot;
- mmap_state.sua.top = end;
- mmap_state.sua.bot = mmap_state.sua.top;
+ mm->sa.bot = start;
+ mm->sa.bot += desc_size;
+ mm->sa.bot = (char *) ERTS_SUPERALIGNED_CEILING(mm->sa.bot);
+ mm->sa.top = mm->sa.bot;
+ mm->sua.top = end;
+ mm->sua.bot = mm->sua.top;
- mmap_state.size.supercarrier.used.total += (UWord) (mmap_state.sa.bot - start);
+ mm->size.supercarrier.used.total += (UWord) (mm->sa.bot - start);
- mmap_state.desc.free_list = NULL;
- mmap_state.desc.reserved = 0;
+ mm->desc.free_list = NULL;
+ mm->desc.reserved = 0;
if (end == (void *) 0) {
/*
* Very unlikely, but we need a guarantee
- * that `mmap_state.sua.top` always will
+ * that `mm->sua.top` always will
* compare as larger than all segment pointers
* into the super carrier...
*/
- mmap_state.sua.top -= ERTS_PAGEALIGNED_SIZE;
- mmap_state.size.supercarrier.used.total += ERTS_PAGEALIGNED_SIZE;
+ mm->sua.top -= ERTS_PAGEALIGNED_SIZE;
+ mm->size.supercarrier.used.total += ERTS_PAGEALIGNED_SIZE;
#ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION
- if (!virtual_map || os_reserve_physical(mmap_state.sua.top, ERTS_PAGEALIGNED_SIZE))
+ if (!virtual_map || os_reserve_physical(mm->sua.top, ERTS_PAGEALIGNED_SIZE, 0))
#endif
- add_free_desc_area(mmap_state.sua.top, end);
- mmap_state.desc.reserved += (end - mmap_state.sua.top) / sizeof(ErtsFreeSegDesc);
+ add_free_desc_area(mm, mm->sua.top, end);
+ mm->desc.reserved += (end - mm->sua.top) / sizeof(ErtsFreeSegDesc);
}
- mmap_state.size.supercarrier.total = (UWord) (mmap_state.sua.top - start);
+ mm->size.supercarrier.total = (UWord) (mm->sua.top - start);
/*
* Area before (and after) super carrier
* will be used for free segment descritors.
*/
#ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION
- if (virtual_map && !os_reserve_physical(start, mmap_state.sa.bot - start))
+ if (virtual_map && !os_reserve_physical(start, mm->sa.bot - start, 0))
erts_exit(1, "erts_mmap: Failed to reserve physical memory for descriptors\n");
#endif
- mmap_state.desc.unused_start = start;
- mmap_state.desc.unused_end = mmap_state.sa.bot;
- mmap_state.desc.reserved += ((mmap_state.desc.unused_end - start)
+ mm->desc.unused_start = start;
+ mm->desc.unused_end = mm->sa.bot;
+ mm->desc.reserved += ((mm->desc.unused_end - start)
/ sizeof(ErtsFreeSegDesc));
- init_free_seg_map(&mmap_state.sa.map, SA_SZ_ADDR_ORDER);
- init_free_seg_map(&mmap_state.sua.map, SZ_REVERSE_ADDR_ORDER);
+ init_free_seg_map(&mm->sa.map, SA_SZ_ADDR_ORDER);
+ init_free_seg_map(&mm->sua.map, SZ_REVERSE_ADDR_ORDER);
- mmap_state.supercarrier = 1;
- erts_have_erts_mmap |= ERTS_HAVE_ERTS_SUPERCARRIER_MMAP;
+ mm->supercarrier = 1;
- mmap_state.desc.new_area_hint = end;
+ mm->desc.new_area_hint = end;
}
#if !ERTS_HAVE_OS_MMAP
- mmap_state.no_os_mmap = 1;
+ mm->no_os_mmap = 1;
#endif
#ifdef HARD_DEBUG_MSEG
hard_dbg_mseg_init();
#endif
+
+#if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
+ if (mm == &erts_literal_mmapper) {
+ erts_literals_start = erts_literal_mmapper.sa.bot;
+ erts_literals_size = erts_literal_mmapper.sua.top - erts_literals_start;
+ }
+#endif
+ is_first_call = 0;
}
@@ -2313,7 +2389,8 @@ add_2tup(Uint **hpp, Uint *szp, Eterm *lp, Eterm el1, Eterm el2)
*lp = erts_bld_cons(hpp, szp, erts_bld_tuple(hpp, szp, 2, el1, el2), *lp);
}
-Eterm erts_mmap_info(int *print_to_p,
+Eterm erts_mmap_info(ErtsMemMapper* mm,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Eterm** hpp, Uint* szp,
struct erts_mmap_info_struct* emis)
@@ -2322,35 +2399,41 @@ Eterm erts_mmap_info(int *print_to_p,
Eterm seg_tags[] = { am.used, am.max, am.allocated, am.reserved, am.used_sa, am.used_sua };
Eterm group[2];
Eterm group_tags[] = { am.sizes, am.free_segs };
- Eterm list[2];
- Eterm list_tags[2]; /* { am.supercarrier, am.os } */
- int lix;
+ Eterm list[3];
+ Eterm list_tags[3]; /* { am.options, am.supercarrier, am.os } */
+ int lix = 0;
Eterm res = THE_NON_VALUE;
if (!hpp) {
- erts_smp_mtx_lock(&mmap_state.mtx);
- emis->sizes[0] = mmap_state.size.supercarrier.total;
- emis->sizes[1] = mmap_state.sa.top - mmap_state.sa.bot;
- emis->sizes[2] = mmap_state.sua.top - mmap_state.sua.bot;
- emis->sizes[3] = mmap_state.size.supercarrier.used.total;
- emis->sizes[4] = mmap_state.size.supercarrier.used.sa;
- emis->sizes[5] = mmap_state.size.supercarrier.used.sua;
+ erts_smp_mtx_lock(&mm->mtx);
+ emis->sizes[0] = mm->size.supercarrier.total;
+ emis->sizes[1] = mm->sa.top - mm->sa.bot;
+ emis->sizes[2] = mm->sua.top - mm->sua.bot;
+ emis->sizes[3] = mm->size.supercarrier.used.total;
+ emis->sizes[4] = mm->size.supercarrier.used.sa;
+ emis->sizes[5] = mm->size.supercarrier.used.sua;
- emis->segs[0] = mmap_state.no.free_segs.curr;
- emis->segs[1] = mmap_state.no.free_segs.max;
- emis->segs[2] = mmap_state.no.free_seg_descs;
- emis->segs[3] = mmap_state.desc.reserved;
- emis->segs[4] = mmap_state.sa.map.nseg;
- emis->segs[5] = mmap_state.sua.map.nseg;
+ emis->segs[0] = mm->no.free_segs.curr;
+ emis->segs[1] = mm->no.free_segs.max;
+ emis->segs[2] = mm->no.free_seg_descs;
+ emis->segs[3] = mm->desc.reserved;
+ emis->segs[4] = mm->sa.map.nseg;
+ emis->segs[5] = mm->sua.map.nseg;
- emis->os_used = mmap_state.size.os.used;
- erts_smp_mtx_unlock(&mmap_state.mtx);
+ emis->os_used = mm->size.os.used;
+ erts_smp_mtx_unlock(&mm->mtx);
}
+ list[lix] = erts_mmap_info_options(mm, "option ", print_to_p, print_to_arg,
+ hpp, szp);
+ list_tags[lix] = am.options;
+ lix++;
+
+
if (print_to_p) {
- int to = *print_to_p;
+ fmtfn_t to = *print_to_p;
void *arg = print_to_arg;
- if (mmap_state.supercarrier) {
+ if (mm->supercarrier) {
const char* prefix = "supercarrier ";
erts_print(to, arg, "%stotal size: %bpu\n", prefix, emis->sizes[0]);
erts_print(to, arg, "%stotal sa size: %bpu\n", prefix, emis->sizes[1]);
@@ -2365,7 +2448,7 @@ Eterm erts_mmap_info(int *print_to_p,
erts_print(to, arg, "%ssa free segs: %bpu\n", prefix, emis->segs[4]);
erts_print(to, arg, "%ssua free segs: %bpu\n", prefix, emis->segs[5]);
}
- if (!mmap_state.no_os_mmap) {
+ if (!mm->no_os_mmap) {
erts_print(to, arg, "os mmap size used: %bpu\n", emis->os_used);
}
}
@@ -2376,8 +2459,7 @@ Eterm erts_mmap_info(int *print_to_p,
init_atoms();
}
- lix = 0;
- if (mmap_state.supercarrier) {
+ if (mm->supercarrier) {
group[0] = erts_bld_atom_uword_2tup_list(hpp, szp,
sizeof(size_tags)/sizeof(Eterm),
size_tags, emis->sizes);
@@ -2389,7 +2471,7 @@ Eterm erts_mmap_info(int *print_to_p,
lix++;
}
- if (!mmap_state.no_os_mmap) {
+ if (!mm->no_os_mmap) {
group[0] = erts_bld_atom_uword_2tup_list(hpp, szp,
1, &am.used, &emis->os_used);
list[lix] = erts_bld_2tup_list(hpp, szp, 1, group_tags, group);
@@ -2401,25 +2483,26 @@ Eterm erts_mmap_info(int *print_to_p,
return res;
}
-Eterm erts_mmap_info_options(char *prefix,
- int *print_to_p,
+Eterm erts_mmap_info_options(ErtsMemMapper* mm,
+ char *prefix,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
{
- const UWord scs = mmap_state.sua.top - mmap_state.sa.bot;
- const Eterm sco = mmap_state.no_os_mmap ? am_true : am_false;
- const Eterm scrpm = (mmap_state.reserve_physical == reserve_noop) ? am_true : am_false;
+ const UWord scs = mm->sua.top - mm->sa.bot;
+ const Eterm sco = mm->no_os_mmap ? am_true : am_false;
+ const Eterm scrpm = (mm->reserve_physical == reserve_noop) ? am_true : am_false;
Eterm res = THE_NON_VALUE;
if (print_to_p) {
- int to = *print_to_p;
+ fmtfn_t to = *print_to_p;
void *arg = print_to_arg;
erts_print(to, arg, "%sscs: %bpu\n", prefix, scs);
- if (mmap_state.supercarrier) {
+ if (mm->supercarrier) {
erts_print(to, arg, "%ssco: %T\n", prefix, sco);
erts_print(to, arg, "%sscrpm: %T\n", prefix, scrpm);
- erts_print(to, arg, "%sscrfsd: %beu\n", prefix, mmap_state.desc.reserved);
+ erts_print(to, arg, "%sscrfsd: %beu\n", prefix, mm->desc.reserved);
}
}
@@ -2429,9 +2512,9 @@ Eterm erts_mmap_info_options(char *prefix,
}
res = NIL;
- if (mmap_state.supercarrier) {
+ if (mm->supercarrier) {
add_2tup(hpp, szp, &res, am.scrfsd,
- erts_bld_uint(hpp,szp, mmap_state.desc.reserved));
+ erts_bld_uint(hpp,szp, mm->desc.reserved));
add_2tup(hpp, szp, &res, am.scrpm, scrpm);
add_2tup(hpp, szp, &res, am.sco, sco);
}
@@ -2440,10 +2523,14 @@ Eterm erts_mmap_info_options(char *prefix,
return res;
}
+#endif /* HAVE_ERTS_MMAP */
Eterm erts_mmap_debug_info(Process* p)
{
- if (mmap_state.supercarrier) {
+#if HAVE_ERTS_MMAP
+ ErtsMemMapper* mm = &erts_dflt_mmapper;
+
+ if (mm->supercarrier) {
ERTS_DECL_AM(sabot);
ERTS_DECL_AM(satop);
ERTS_DECL_AM(suabot);
@@ -2453,18 +2540,17 @@ Eterm erts_mmap_debug_info(Process* p)
UWord values[4];
Eterm *hp, *hp_end;
Uint may_need;
- const Uint PTR_BIG_SZ = HALFWORD_HEAP ? 3 : 2;
-
- erts_smp_mtx_lock(&mmap_state.mtx);
- values[0] = (UWord)mmap_state.sa.bot;
- values[1] = (UWord)mmap_state.sa.top;
- values[2] = (UWord)mmap_state.sua.bot;
- values[3] = (UWord)mmap_state.sua.top;
- sa_list = build_free_seg_list(p, &mmap_state.sa.map);
- sua_list = build_free_seg_list(p, &mmap_state.sua.map);
- erts_smp_mtx_unlock(&mmap_state.mtx);
-
- may_need = 4*(2+3+PTR_BIG_SZ) + 2*(2+3);
+
+ erts_smp_mtx_lock(&mm->mtx);
+ values[0] = (UWord)mm->sa.bot;
+ values[1] = (UWord)mm->sa.top;
+ values[2] = (UWord)mm->sua.bot;
+ values[3] = (UWord)mm->sua.top;
+ sa_list = build_free_seg_list(p, &mm->sa.map);
+ sua_list = build_free_seg_list(p, &mm->sua.map);
+ erts_smp_mtx_unlock(&mm->mtx);
+
+ may_need = 4*(2+3+2) + 2*(2+3);
hp = HAlloc(p, may_need);
hp_end = hp + may_need;
@@ -2481,9 +2567,8 @@ Eterm erts_mmap_debug_info(Process* p)
HRelease(p, hp_end, hp);
return list;
}
- else {
- return am_undefined;
- }
+#endif
+ return am_undefined;
}
diff --git a/erts/emulator/sys/common/erl_mmap.h b/erts/emulator/sys/common/erl_mmap.h
index 66619c5161..623b5188ac 100644
--- a/erts/emulator/sys/common/erl_mmap.h
+++ b/erts/emulator/sys/common/erl_mmap.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2013. All Rights Reserved.
+ * Copyright Ericsson AB 2013-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,17 +22,52 @@
#define ERL_MMAP_H__
#include "sys.h"
+#include "erl_printf.h"
#define ERTS_MMAP_SUPERALIGNED_BITS (18)
/* Affects hard limits for sbct and lmbcs documented in erts_alloc.xml */
-#define ERTS_MMAPFLG_OS_ONLY (((Uint32) 1) << 0)
-#define ERTS_MMAPFLG_SUPERCARRIER_ONLY (((Uint32) 1) << 1)
-#define ERTS_MMAPFLG_SUPERALIGNED (((Uint32) 1) << 2)
+#ifndef HAVE_MMAP
+# define HAVE_MMAP 0
+#endif
+#ifndef HAVE_MREMAP
+# define HAVE_MREMAP 0
+#endif
+#if HAVE_MMAP
+# define ERTS_HAVE_OS_MMAP 1
+# define ERTS_HAVE_GENUINE_OS_MMAP 1
+# if HAVE_MREMAP
+# define ERTS_HAVE_OS_MREMAP 1
+# endif
+/*
+ * MAP_NORESERVE is undefined in FreeBSD 10.x and later.
+ * This is to enable 64bit HiPE experimentally on FreeBSD.
+ * Note that on FreeBSD MAP_NORESERVE was "never implemented"
+ * even before 11.x (and the flag does not exist in /usr/src/sys/vm/mmap.c
+ * of 10.3-STABLE r301478 either), and HiPE was working on OTP 18.3.3,
+ * so mandating MAP_NORESERVE on FreeBSD might not be needed.
+ * See the following message on how MAP_NORESERVE was treated on FreeBSD:
+ * <http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20150202/122958.html>
+ */
+# if defined(MAP_FIXED) && (defined(MAP_NORESERVE) || defined(__FreeBSD__))
+# define ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION 1
+# endif
+#endif
+
+#ifndef HAVE_VIRTUALALLOC
+# define HAVE_VIRTUALALLOC 0
+#endif
+#if HAVE_VIRTUALALLOC
+# define ERTS_HAVE_OS_MMAP 1
+#endif
+
+#ifdef ERTS_HAVE_GENUINE_OS_MMAP
+# define HAVE_ERTS_MMAP 1
+#else
+# define HAVE_ERTS_MMAP 0
+#endif
+
-#define ERTS_HAVE_ERTS_OS_MMAP (1 << 0)
-#define ERTS_HAVE_ERTS_SUPERCARRIER_MMAP (1 << 1)
-extern int erts_have_erts_mmap;
extern UWord erts_page_inv_mask;
typedef struct {
@@ -53,23 +88,16 @@ typedef struct {
#define ERTS_MMAP_INIT_DEFAULT_INITER \
{{NULL, NULL}, {NULL, NULL}, 0, 1, (1 << 16), 1}
-void *erts_mmap(Uint32 flags, UWord *sizep);
-void erts_munmap(Uint32 flags, void *ptr, UWord size);
-void *erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep);
-int erts_mmap_in_supercarrier(void *ptr);
-void erts_mmap_init(ErtsMMapInit*);
-struct erts_mmap_info_struct
-{
- UWord sizes[6];
- UWord segs[6];
- UWord os_used;
-};
-Eterm erts_mmap_info(int *print_to_p, void *print_to_arg,
- Eterm** hpp, Uint* szp, struct erts_mmap_info_struct*);
-Eterm erts_mmap_info_options(char *prefix, int *print_to_p, void *print_to_arg,
- Uint **hpp, Uint *szp);
-struct process;
-Eterm erts_mmap_debug_info(struct process*);
+#define ERTS_LITERAL_VIRTUAL_AREA_SIZE (UWORD_CONSTANT(1)*1024*1024*1024)
+
+#define ERTS_MMAP_INIT_LITERAL_INITER \
+ {{NULL, NULL}, {NULL, NULL}, ERTS_LITERAL_VIRTUAL_AREA_SIZE, 1, (1 << 10), 0}
+
+#define ERTS_HIPE_EXEC_VIRTUAL_AREA_SIZE (UWORD_CONSTANT(512)*1024*1024)
+
+#define ERTS_MMAP_INIT_HIPE_EXEC_INITER \
+ {{NULL, NULL}, {NULL, NULL}, ERTS_HIPE_EXEC_VIRTUAL_AREA_SIZE, 1, (1 << 10), 0}
+
#define ERTS_SUPERALIGNED_SIZE \
(1 << ERTS_MMAP_SUPERALIGNED_BITS)
@@ -97,29 +125,46 @@ Eterm erts_mmap_debug_info(struct process*);
#define ERTS_PAGEALIGNED_SIZE \
(ERTS_INV_PAGEALIGNED_MASK + 1)
-#ifndef HAVE_MMAP
-# define HAVE_MMAP 0
-#endif
-#ifndef HAVE_MREMAP
-# define HAVE_MREMAP 0
-#endif
-#if HAVE_MMAP
-# define ERTS_HAVE_OS_MMAP 1
-# define ERTS_HAVE_GENUINE_OS_MMAP 1
-# if HAVE_MREMAP
-# define ERTS_HAVE_OS_MREMAP 1
+struct process;
+Eterm erts_mmap_debug_info(struct process*);
+
+#if HAVE_ERTS_MMAP
+
+typedef struct ErtsMemMapper_ ErtsMemMapper;
+
+#define ERTS_MMAPFLG_OS_ONLY (((Uint32) 1) << 0)
+#define ERTS_MMAPFLG_SUPERCARRIER_ONLY (((Uint32) 1) << 1)
+#define ERTS_MMAPFLG_SUPERALIGNED (((Uint32) 1) << 2)
+
+void *erts_mmap(ErtsMemMapper*, Uint32 flags, UWord *sizep);
+void erts_munmap(ErtsMemMapper*, Uint32 flags, void *ptr, UWord size);
+void *erts_mremap(ErtsMemMapper*, Uint32 flags, void *ptr, UWord old_size, UWord *sizep);
+int erts_mmap_in_supercarrier(ErtsMemMapper*, void *ptr);
+void erts_mmap_init(ErtsMemMapper*, ErtsMMapInit*, int executable);
+struct erts_mmap_info_struct
+{
+ UWord sizes[6];
+ UWord segs[6];
+ UWord os_used;
+};
+Eterm erts_mmap_info(ErtsMemMapper*, fmtfn_t *print_to_p, void *print_to_arg,
+ Eterm** hpp, Uint* szp, struct erts_mmap_info_struct*);
+Eterm erts_mmap_info_options(ErtsMemMapper*,
+ char *prefix, fmtfn_t *print_to_p, void *print_to_arg,
+ Uint **hpp, Uint *szp);
+
+
+#ifdef ERTS_WANT_MEM_MAPPERS
+# include "erl_alloc_types.h"
+
+extern ErtsMemMapper erts_dflt_mmapper;
+# if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
+extern ErtsMemMapper erts_literal_mmapper;
# endif
-# if defined(MAP_FIXED) && defined(MAP_NORESERVE)
-# define ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION 1
+# ifdef ERTS_ALC_A_EXEC
+extern ErtsMemMapper erts_exec_mmapper;
# endif
-#endif
-
-#ifndef HAVE_VIRTUALALLOC
-# define HAVE_VIRTUALALLOC 0
-#endif
-#if HAVE_VIRTUALALLOC
-# define ERTS_HAVE_OS_MMAP 1
-#endif
+#endif /* ERTS_WANT_MEM_MAPPERS */
/*#define HARD_DEBUG_MSEG*/
#ifdef HARD_DEBUG_MSEG
@@ -132,4 +177,6 @@ void hard_dbg_remove_mseg(void* seg, UWord sz);
# define HARD_DBG_REMOVE_MSEG(SEG,SZ)
#endif
+#endif /* HAVE_ERTS_MMAP */
+
#endif /* ERL_MMAP_H__ */
diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c
index 3398be2b07..968f71211c 100644
--- a/erts/emulator/sys/common/erl_mseg.c
+++ b/erts/emulator/sys/common/erl_mseg.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2002-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@
# include "config.h"
#endif
+#define ERTS_WANT_MEM_MAPPERS
#include "sys.h"
#include "erl_mseg.h"
#include "global.h"
@@ -86,6 +87,7 @@ static const int debruijn[32] = {
#define CACHE_AREAS (32 - MSEG_ALIGN_BITS)
+/* FIXME: segment sizes > 2 GB result in bogus negative indices */
#define SIZE_TO_CACHE_AREA_IDX(S) (LOG2((S)) - MSEG_ALIGN_BITS)
#define MAX_CACHE_SIZE (30)
@@ -99,17 +101,12 @@ static const int debruijn[32] = {
static int atoms_initialized;
-typedef struct mem_kind_t MemKind;
-
const ErtsMsegOpt_t erts_mseg_default_opt = {
1, /* Use cache */
1, /* Preserv data */
0, /* Absolute shrink threshold */
0, /* Relative shrink threshold */
0 /* Scheduler specific */
-#if HALFWORD_HEAP
- ,0 /* need low memory */
-#endif
};
@@ -142,7 +139,14 @@ struct cache_t_ {
typedef struct ErtsMsegAllctr_t_ ErtsMsegAllctr_t;
-struct mem_kind_t {
+struct ErtsMsegAllctr_t_ {
+ int ix;
+
+ int is_init_done;
+ int is_thread_safe;
+ erts_mtx_t mtx;
+
+ int is_cache_check_scheduled;
cache_t cache[MAX_CACHE_SIZE];
cache_t cache_unpowered_node;
@@ -168,29 +172,6 @@ struct mem_kind_t {
} max_ever;
} segments;
- ErtsMsegAllctr_t *ma;
- const char* name;
- MemKind* next;
-};/*MemKind*/
-
-struct ErtsMsegAllctr_t_ {
- int ix;
-
- int is_init_done;
- int is_thread_safe;
- erts_mtx_t mtx;
-
- int is_cache_check_scheduled;
-
- MemKind* mk_list;
-
-#if HALFWORD_HEAP
- MemKind low_mem;
- MemKind hi_mem;
-#else
- MemKind the_mem;
-#endif
-
Uint max_cache_size;
Uint abs_max_cache_bad_fit;
Uint rel_max_cache_bad_fit;
@@ -302,22 +283,17 @@ schedule_cache_check(ErtsMsegAllctr_t *ma) {
/* #define ERTS_PRINT_ERTS_MMAP */
static ERTS_INLINE void *
-mseg_create(ErtsMsegAllctr_t *ma, Uint flags, MemKind* mk, UWord *sizep)
+mseg_create(ErtsMsegAllctr_t *ma, Uint flags, UWord *sizep)
{
#ifdef ERTS_PRINT_ERTS_MMAP
UWord req_size = *sizep;
#endif
void *seg;
Uint32 mmap_flags = 0;
-#if HALFWORD_HEAP
- mmap_flags |= ((mk == &ma->low_mem)
- ? ERTS_MMAPFLG_SUPERCARRIER_ONLY
- : ERTS_MMAPFLG_OS_ONLY);
-#endif
if (MSEG_FLG_IS_2POW(flags))
mmap_flags |= ERTS_MMAPFLG_SUPERALIGNED;
- seg = erts_mmap(mmap_flags, sizep);
+ seg = erts_mmap(&erts_dflt_mmapper, mmap_flags, sizep);
#ifdef ERTS_PRINT_ERTS_MMAP
erts_fprintf(stderr, "%p = erts_mmap(%s, {%bpu, %bpu});\n", seg,
@@ -331,18 +307,13 @@ mseg_create(ErtsMsegAllctr_t *ma, Uint flags, MemKind* mk, UWord *sizep)
}
static ERTS_INLINE void
-mseg_destroy(ErtsMsegAllctr_t *ma, Uint flags, MemKind* mk, void *seg_p, UWord size) {
+mseg_destroy(ErtsMsegAllctr_t *ma, Uint flags, void *seg_p, UWord size) {
Uint32 mmap_flags = 0;
-#if HALFWORD_HEAP
- mmap_flags |= ((mk == &ma->low_mem)
- ? ERTS_MMAPFLG_SUPERCARRIER_ONLY
- : ERTS_MMAPFLG_OS_ONLY);
-#endif
if (MSEG_FLG_IS_2POW(flags))
mmap_flags |= ERTS_MMAPFLG_SUPERALIGNED;
- erts_munmap(mmap_flags, seg_p, size);
+ erts_munmap(&erts_dflt_mmapper, mmap_flags, seg_p, size);
#ifdef ERTS_PRINT_ERTS_MMAP
erts_fprintf(stderr, "erts_munmap(%s, %p, %bpu);\n",
(mmap_flags & ERTS_MMAPFLG_SUPERALIGNED) ? "sa" : "sua",
@@ -353,22 +324,17 @@ mseg_destroy(ErtsMsegAllctr_t *ma, Uint flags, MemKind* mk, void *seg_p, UWord s
}
static ERTS_INLINE void *
-mseg_recreate(ErtsMsegAllctr_t *ma, Uint flags, MemKind* mk, void *old_seg, UWord old_size, UWord *sizep)
+mseg_recreate(ErtsMsegAllctr_t *ma, Uint flags, void *old_seg, UWord old_size, UWord *sizep)
{
#ifdef ERTS_PRINT_ERTS_MMAP
UWord req_size = *sizep;
#endif
void *new_seg;
Uint32 mmap_flags = 0;
-#if HALFWORD_HEAP
- mmap_flags |= ((mk == &ma->low_mem)
- ? ERTS_MMAPFLG_SUPERCARRIER_ONLY
- : ERTS_MMAPFLG_OS_ONLY);
-#endif
if (MSEG_FLG_IS_2POW(flags))
mmap_flags |= ERTS_MMAPFLG_SUPERALIGNED;
- new_seg = erts_mremap(mmap_flags, old_seg, old_size, sizep);
+ new_seg = erts_mremap(&erts_dflt_mmapper, mmap_flags, old_seg, old_size, sizep);
#ifdef ERTS_PRINT_ERTS_MMAP
erts_fprintf(stderr, "%p = erts_mremap(%s, %p, %bpu, {%bpu, %bpu});\n",
@@ -392,11 +358,8 @@ do { \
|| erts_smp_thr_progress_is_blocking() \
|| ERTS_IS_CRASH_DUMPING); \
} while (0)
-#define ERTS_DBG_MK_CHK_THR_ACCESS(MK) \
- ERTS_DBG_MA_CHK_THR_ACCESS((MK)->ma)
#else
#define ERTS_DBG_MA_CHK_THR_ACCESS(MA)
-#define ERTS_DBG_MK_CHK_THR_ACCESS(MK)
#endif
/* Cache interface */
@@ -409,10 +372,10 @@ static ERTS_INLINE void mseg_cache_clear_node(cache_t *c) {
c->prev = c;
}
-static ERTS_INLINE int cache_bless_segment(MemKind *mk, void *seg, UWord size, Uint flags) {
+static ERTS_INLINE int cache_bless_segment(ErtsMsegAllctr_t *ma, void *seg, UWord size, Uint flags) {
cache_t *c;
- ERTS_DBG_MK_CHK_THR_ACCESS(mk);
+ ERTS_DBG_MA_CHK_THR_ACCESS(ma);
ASSERT(!MSEG_FLG_IS_2POW(flags) || (MSEG_FLG_IS_2POW(flags) && MAP_IS_ALIGNED(seg) && IS_2POW(size)));
@@ -421,11 +384,11 @@ static ERTS_INLINE int cache_bless_segment(MemKind *mk, void *seg, UWord size, U
* Large blocks has no such cache and it is up to mseg to cache them to speed things up.
*/
- if (!erts_circleq_is_empty(&(mk->cache_free))) {
+ if (!erts_circleq_is_empty(&(ma->cache_free))) {
/* We have free slots, use one to cache the segment */
- c = erts_circleq_head(&(mk->cache_free));
+ c = erts_circleq_head(&(ma->cache_free));
erts_circleq_remove(c);
c->seg = seg;
@@ -434,32 +397,34 @@ static ERTS_INLINE int cache_bless_segment(MemKind *mk, void *seg, UWord size, U
if (MSEG_FLG_IS_2POW(flags)) {
int ix = SIZE_TO_CACHE_AREA_IDX(size);
+ if (ix < 0)
+ return 0;
+
ASSERT(ix < CACHE_AREAS);
ASSERT((1 << (ix + MSEG_ALIGN_BITS)) == size);
- erts_circleq_push_head(&(mk->cache_powered_node[ix]), c);
+ erts_circleq_push_head(&(ma->cache_powered_node[ix]), c);
} else
- erts_circleq_push_head(&(mk->cache_unpowered_node), c);
+ erts_circleq_push_head(&(ma->cache_unpowered_node), c);
- mk->cache_size++;
- ASSERT(mk->cache_size <= mk->ma->max_cache_size);
+ ma->cache_size++;
return 1;
- } else if (!MSEG_FLG_IS_2POW(flags) && !erts_circleq_is_empty(&(mk->cache_unpowered_node))) {
+ } else if (!MSEG_FLG_IS_2POW(flags) && !erts_circleq_is_empty(&(ma->cache_unpowered_node))) {
/* No free slots.
* Evict oldest slot from unpowered cache so we can cache an unpowered (sbc) segment */
- c = erts_circleq_tail(&(mk->cache_unpowered_node));
+ c = erts_circleq_tail(&(ma->cache_unpowered_node));
erts_circleq_remove(c);
- mseg_destroy(mk->ma, ERTS_MSEG_FLG_NONE, mk, c->seg, c->size);
+ mseg_destroy(ma, ERTS_MSEG_FLG_NONE, c->seg, c->size);
mseg_cache_clear_node(c);
c->seg = seg;
c->size = size;
- erts_circleq_push_head(&(mk->cache_unpowered_node), c);
+ erts_circleq_push_head(&(ma->cache_unpowered_node), c);
return 1;
} else if (!MSEG_FLG_IS_2POW(flags)) {
@@ -473,20 +438,20 @@ static ERTS_INLINE int cache_bless_segment(MemKind *mk, void *seg, UWord size, U
int i;
for( i = 0; i < CACHE_AREAS; i++) {
- if (erts_circleq_is_empty(&(mk->cache_powered_node[i])))
+ if (erts_circleq_is_empty(&(ma->cache_powered_node[i])))
continue;
- c = erts_circleq_tail(&(mk->cache_powered_node[i]));
+ c = erts_circleq_tail(&(ma->cache_powered_node[i]));
erts_circleq_remove(c);
- mseg_destroy(mk->ma, ERTS_MSEG_FLG_2POW, mk, c->seg, c->size);
+ mseg_destroy(ma, ERTS_MSEG_FLG_2POW, c->seg, c->size);
mseg_cache_clear_node(c);
c->seg = seg;
c->size = size;
- erts_circleq_push_head(&(mk->cache_unpowered_node), c);
+ erts_circleq_push_head(&(ma->cache_unpowered_node), c);
return 1;
}
@@ -495,11 +460,11 @@ static ERTS_INLINE int cache_bless_segment(MemKind *mk, void *seg, UWord size, U
return 0;
}
-static ERTS_INLINE void *cache_get_segment(MemKind *mk, UWord *size_p, Uint flags) {
+static ERTS_INLINE void *cache_get_segment(ErtsMsegAllctr_t *ma, UWord *size_p, Uint flags) {
UWord size = *size_p;
- ERTS_DBG_MK_CHK_THR_ACCESS(mk);
+ ERTS_DBG_MA_CHK_THR_ACCESS(ma);
if (MSEG_FLG_IS_2POW(flags)) {
@@ -510,12 +475,15 @@ static ERTS_INLINE void *cache_get_segment(MemKind *mk, UWord *size_p, Uint flag
ASSERT(IS_2POW(size));
+ if (ix < 0)
+ return NULL;
+
for( i = ix; i < CACHE_AREAS; i++) {
- if (erts_circleq_is_empty(&(mk->cache_powered_node[i])))
+ if (erts_circleq_is_empty(&(ma->cache_powered_node[i])))
continue;
- c = erts_circleq_head(&(mk->cache_powered_node[i]));
+ c = erts_circleq_head(&(ma->cache_powered_node[i]));
erts_circleq_remove(c);
ASSERT(IS_2POW(c->size));
@@ -524,31 +492,31 @@ static ERTS_INLINE void *cache_get_segment(MemKind *mk, UWord *size_p, Uint flag
csize = c->size;
seg = (char*) c->seg;
- mk->cache_size--;
- mk->cache_hits++;
+ ma->cache_size--;
+ ma->cache_hits++;
/* link to free cache list */
mseg_cache_clear_node(c);
- erts_circleq_push_head(&(mk->cache_free), c);
+ erts_circleq_push_head(&(ma->cache_free), c);
- ASSERT(!(mk->cache_size < 0));
+ ASSERT(!(ma->cache_size < 0));
if (csize != size)
- mseg_destroy(mk->ma, ERTS_MSEG_FLG_2POW, mk, seg + size, csize - size);
+ mseg_destroy(ma, ERTS_MSEG_FLG_2POW, seg + size, csize - size);
return seg;
}
}
- else if (!erts_circleq_is_empty(&(mk->cache_unpowered_node))) {
+ else if (!erts_circleq_is_empty(&(ma->cache_unpowered_node))) {
void *seg;
cache_t *c;
cache_t *best = NULL;
UWord bdiff = 0;
UWord csize;
- UWord bad_max_abs = mk->ma->abs_max_cache_bad_fit;
- UWord bad_max_rel = mk->ma->rel_max_cache_bad_fit;
+ UWord bad_max_abs = ma->abs_max_cache_bad_fit;
+ UWord bad_max_rel = ma->rel_max_cache_bad_fit;
- erts_circleq_foreach(c, &(mk->cache_unpowered_node)) {
+ erts_circleq_foreach(c, &(ma->cache_unpowered_node)) {
csize = c->size;
if (csize >= size) {
if (((csize - size)*100 < bad_max_rel*size) && (csize - size) < bad_max_abs ) {
@@ -557,11 +525,11 @@ static ERTS_INLINE void *cache_get_segment(MemKind *mk, UWord *size_p, Uint flag
erts_circleq_remove(c);
- mk->cache_size--;
- mk->cache_hits++;
+ ma->cache_size--;
+ ma->cache_hits++;
mseg_cache_clear_node(c);
- erts_circleq_push_head(&(mk->cache_free), c);
+ erts_circleq_push_head(&(ma->cache_free), c);
*size_p = csize;
@@ -584,7 +552,7 @@ static ERTS_INLINE void *cache_get_segment(MemKind *mk, UWord *size_p, Uint flag
ASSERT(best->seg);
ASSERT(best->size > 0);
- mk->cache_hits++;
+ ma->cache_hits++;
/* Use current cache placement for remaining segment space */
@@ -608,7 +576,7 @@ static ERTS_INLINE void *cache_get_segment(MemKind *mk, UWord *size_p, Uint flag
* using callbacks from aux-work in the scheduler.
*/
-static ERTS_INLINE Uint mseg_drop_one_memkind_cache_size(MemKind *mk, Uint flags, cache_t *head) {
+static ERTS_INLINE Uint mseg_drop_one_cache_size(ErtsMsegAllctr_t *ma, Uint flags, cache_t *head) {
cache_t *c = NULL;
c = erts_circleq_tail(head);
@@ -617,19 +585,19 @@ static ERTS_INLINE Uint mseg_drop_one_memkind_cache_size(MemKind *mk, Uint flags
if (erts_mtrace_enabled)
erts_mtrace_crr_free(SEGTYPE, SEGTYPE, c->seg);
- mseg_destroy(mk->ma, flags, mk, c->seg, c->size);
+ mseg_destroy(ma, flags, c->seg, c->size);
mseg_cache_clear_node(c);
- erts_circleq_push_head(&(mk->cache_free), c);
+ erts_circleq_push_head(&(ma->cache_free), c);
- mk->segments.current.watermark--;
- mk->cache_size--;
+ ma->segments.current.watermark--;
+ ma->cache_size--;
- ASSERT( mk->cache_size >= 0 );
+ ASSERT(ma->cache_size >= 0);
- return mk->cache_size;
+ return ma->cache_size;
}
-static ERTS_INLINE Uint mseg_drop_memkind_cache_size(MemKind *mk, Uint flags, cache_t *head) {
+static ERTS_INLINE Uint mseg_drop_cache_size(ErtsMsegAllctr_t *ma, Uint flags, cache_t *head) {
cache_t *c = NULL;
while (!erts_circleq_is_empty(head)) {
@@ -640,58 +608,52 @@ static ERTS_INLINE Uint mseg_drop_memkind_cache_size(MemKind *mk, Uint flags, ca
if (erts_mtrace_enabled)
erts_mtrace_crr_free(SEGTYPE, SEGTYPE, c->seg);
- mseg_destroy(mk->ma, flags, mk, c->seg, c->size);
+ mseg_destroy(ma, flags, c->seg, c->size);
mseg_cache_clear_node(c);
- erts_circleq_push_head(&(mk->cache_free), c);
-
- mk->segments.current.watermark--;
- mk->cache_size--;
+ erts_circleq_push_head(&(ma->cache_free), c);
+ ma->segments.current.watermark--;
+ ma->cache_size--;
}
- ASSERT( mk->cache_size >= 0 );
+ ASSERT(ma->cache_size >= 0);
- return mk->cache_size;
+ return ma->cache_size;
}
-/* mseg_check_memkind_cache
- * - Check if we can empty some cached segments in this
- * MemKind.
+/* mseg_check_cache
+ * - Check if we can empty some cached segments in this allocator
*/
-static Uint mseg_check_memkind_cache(MemKind *mk) {
+static Uint mseg_check_cache(ErtsMsegAllctr_t *ma) {
int i;
- ERTS_DBG_MK_CHK_THR_ACCESS(mk);
+ ERTS_DBG_MA_CHK_THR_ACCESS(ma);
for (i = 0; i < CACHE_AREAS; i++) {
- if (!erts_circleq_is_empty(&(mk->cache_powered_node[i])))
- return mseg_drop_one_memkind_cache_size(mk, ERTS_MSEG_FLG_2POW, &(mk->cache_powered_node[i]));
+ if (!erts_circleq_is_empty(&(ma->cache_powered_node[i])))
+ return mseg_drop_one_cache_size(ma, ERTS_MSEG_FLG_2POW, &(ma->cache_powered_node[i]));
}
- if (!erts_circleq_is_empty(&(mk->cache_unpowered_node)))
- return mseg_drop_one_memkind_cache_size(mk, ERTS_MSEG_FLG_NONE, &(mk->cache_unpowered_node));
+ if (!erts_circleq_is_empty(&(ma->cache_unpowered_node)))
+ return mseg_drop_one_cache_size(ma, ERTS_MSEG_FLG_NONE, &(ma->cache_unpowered_node));
return 0;
}
/* mseg_cache_check
* - Check if we have some cache we can purge
- * in any of the memkinds.
*/
static void mseg_cache_check(ErtsMsegAllctr_t *ma) {
- MemKind* mk;
Uint empty_cache = 1;
ERTS_MSEG_LOCK(ma);
- for (mk = ma->mk_list; mk; mk = mk->next) {
- if (mseg_check_memkind_cache(mk))
- empty_cache = 0;
- }
+ if (mseg_check_cache(ma))
+ empty_cache = 0;
/* If all MemKinds caches are empty,
* remove aux-work callback
@@ -709,7 +671,7 @@ static void mseg_cache_check(ErtsMsegAllctr_t *ma) {
/* erts_mseg_cache_check
* - This is a callback that is scheduled as aux-work from
* schedulers and is called at some interval if we have a cache
- * on this mseg-allocator and memkind.
+ * on this mseg-allocator.
* - Purpose: Empty cache slowly so we don't collect mapped areas
* and bloat memory.
*/
@@ -719,42 +681,32 @@ void erts_mseg_cache_check(void) {
}
-/* *_mseg_clear_*_cache
+/* mseg_clear_cache
* Remove cached segments from the allocator completely
*/
-static void mseg_clear_memkind_cache(MemKind *mk) {
+
+static void mseg_clear_cache(ErtsMsegAllctr_t *ma) {
int i;
+ ERTS_MSEG_LOCK(ma);
+ ERTS_DBG_MA_CHK_THR_ACCESS(ma);
/* drop pow2 caches */
for (i = 0; i < CACHE_AREAS; i++) {
- if (erts_circleq_is_empty(&(mk->cache_powered_node[i])))
+ if (erts_circleq_is_empty(&(ma->cache_powered_node[i])))
continue;
- mseg_drop_memkind_cache_size(mk, ERTS_MSEG_FLG_2POW, &(mk->cache_powered_node[i]));
- ASSERT(erts_circleq_is_empty(&(mk->cache_powered_node[i])));
+ mseg_drop_cache_size(ma, ERTS_MSEG_FLG_2POW, &(ma->cache_powered_node[i]));
+ ASSERT(erts_circleq_is_empty(&(ma->cache_powered_node[i])));
}
/* drop varied caches */
- if (!erts_circleq_is_empty(&(mk->cache_unpowered_node)))
- mseg_drop_memkind_cache_size(mk, ERTS_MSEG_FLG_NONE, &(mk->cache_unpowered_node));
-
- ASSERT(erts_circleq_is_empty(&(mk->cache_unpowered_node)));
- ASSERT(mk->cache_size == 0);
-}
-
-static void mseg_clear_cache(ErtsMsegAllctr_t *ma) {
- MemKind* mk;
-
- ERTS_MSEG_LOCK(ma);
- ERTS_DBG_MA_CHK_THR_ACCESS(ma);
-
+ if (!erts_circleq_is_empty(&(ma->cache_unpowered_node)))
+ mseg_drop_cache_size(ma, ERTS_MSEG_FLG_NONE, &(ma->cache_unpowered_node));
- for (mk = ma->mk_list; mk; mk = mk->next) {
- mseg_clear_memkind_cache(mk);
- }
+ ASSERT(erts_circleq_is_empty(&(ma->cache_unpowered_node)));
+ ASSERT(ma->cache_size == 0);
INC_CC(ma, clear_cache);
-
ERTS_MSEG_UNLOCK(ma);
}
@@ -763,25 +715,12 @@ void erts_mseg_clear_cache(void) {
mseg_clear_cache(ERTS_MSEG_ALLCTR_IX(0));
}
-
-
-static ERTS_INLINE MemKind* memkind(ErtsMsegAllctr_t *ma,
- const ErtsMsegOpt_t *opt)
-{
-#if HALFWORD_HEAP
- return opt->low_mem ? &ma->low_mem : &ma->hi_mem;
-#else
- return &ma->the_mem;
-#endif
-}
-
static void *
mseg_alloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, UWord *size_p,
Uint flags, const ErtsMsegOpt_t *opt)
{
UWord size;
void *seg;
- MemKind* mk = memkind(ma, opt);
INC_CC(ma, alloc);
@@ -795,10 +734,10 @@ mseg_alloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, UWord *size_p,
}
}
- if (opt->cache && mk->cache_size > 0 && (seg = cache_get_segment(mk, &size, flags)) != NULL)
+ if (opt->cache && ma->cache_size > 0 && (seg = cache_get_segment(ma, &size, flags)) != NULL)
goto done;
- seg = mseg_create(ma, flags, mk, &size);
+ seg = mseg_create(ma, flags, &size);
if (!seg)
*size_p = 0;
@@ -808,7 +747,7 @@ done:
if (erts_mtrace_enabled)
erts_mtrace_crr_alloc(seg, atype, ERTS_MTRACE_SEGMENT_ID, size);
- ERTS_MSEG_ALLOC_STAT(mk,size);
+ ERTS_MSEG_ALLOC_STAT(ma,size);
}
return seg;
@@ -819,11 +758,9 @@ static void
mseg_dealloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg, UWord size,
Uint flags, const ErtsMsegOpt_t *opt)
{
- MemKind* mk = memkind(ma, opt);
-
- ERTS_MSEG_DEALLOC_STAT(mk,size);
+ ERTS_MSEG_DEALLOC_STAT(ma,size);
- if (opt->cache && cache_bless_segment(mk, seg, size, flags)) {
+ if (opt->cache && cache_bless_segment(ma, seg, size, flags)) {
schedule_cache_check(ma);
goto done;
}
@@ -831,7 +768,7 @@ mseg_dealloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg, UWord size,
if (erts_mtrace_enabled)
erts_mtrace_crr_free(atype, SEGTYPE, seg);
- mseg_destroy(ma, flags, mk, seg, size);
+ mseg_destroy(ma, flags, seg, size);
done:
@@ -842,7 +779,6 @@ static void *
mseg_realloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg,
UWord old_size, UWord *new_size_p, Uint flags, const ErtsMsegOpt_t *opt)
{
- MemKind* mk;
void *new_seg;
UWord new_size;
@@ -861,7 +797,6 @@ mseg_realloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg,
return NULL;
}
- mk = memkind(ma, opt);
new_seg = seg;
if (!MSEG_FLG_IS_2POW(flags))
@@ -876,7 +811,7 @@ mseg_realloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg,
if (new_size > old_size) {
if (opt->preserv) {
- new_seg = mseg_recreate(ma, flags, mk, (void *) seg, old_size, &new_size);
+ new_seg = mseg_recreate(ma, flags, (void *) seg, old_size, &new_size);
if (!new_seg)
new_size = old_size;
}
@@ -896,7 +831,7 @@ mseg_realloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg,
new_size = old_size;
}
else {
- new_seg = mseg_recreate(ma, flags, mk, (void *) seg, old_size, &new_size);
+ new_seg = mseg_recreate(ma, flags, (void *) seg, old_size, &new_size);
if (!new_seg)
new_size = old_size;
}
@@ -910,7 +845,7 @@ mseg_realloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg,
ASSERT(!MSEG_FLG_IS_2POW(flags) || IS_2POW(new_size));
*new_size_p = new_size;
- ERTS_MSEG_REALLOC_STAT(mk, old_size, new_size);
+ ERTS_MSEG_REALLOC_STAT(ma, old_size, new_size);
return new_seg;
}
@@ -1063,17 +998,15 @@ add_4tup(Uint **hpp, Uint *szp, Eterm *lp,
static Eterm
info_options(ErtsMsegAllctr_t *ma,
char *prefix,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
{
- Eterm res;
-
- res = erts_mmap_info_options(prefix, print_to_p, print_to_arg, hpp, szp);
+ Eterm res = NIL;
if (print_to_p) {
- int to = *print_to_p;
+ fmtfn_t to = *print_to_p;
void *arg = print_to_arg;
erts_print(to, arg, "%samcbf: %beu\n", prefix, ma->abs_max_cache_bad_fit);
erts_print(to, arg, "%srmcbf: %beu\n", prefix, ma->rel_max_cache_bad_fit);
@@ -1101,7 +1034,7 @@ info_options(ErtsMsegAllctr_t *ma,
}
static Eterm
-info_calls(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg, Uint **hpp, Uint *szp)
+info_calls(ErtsMsegAllctr_t *ma, fmtfn_t *print_to_p, void *print_to_arg, Uint **hpp, Uint *szp)
{
Eterm res = THE_NON_VALUE;
@@ -1114,7 +1047,7 @@ info_calls(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg, Uint **hpp
erts_print(TO, TOA, "mseg_%s calls: %b32u%09b32u\n", #CC, \
ma->calls.CC.giga_no, ma->calls.CC.no)
- int to = *print_to_p;
+ fmtfn_t to = *print_to_p;
void *arg = print_to_arg;
PRINT_CC(to, arg, alloc);
@@ -1180,90 +1113,97 @@ info_calls(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg, Uint **hpp
}
static Eterm
-info_status(ErtsMsegAllctr_t *ma, MemKind* mk, int *print_to_p, void *print_to_arg,
- int begin_new_max_period, Uint **hpp, Uint *szp)
+info_status(ErtsMsegAllctr_t *ma, fmtfn_t *print_to_p, void *print_to_arg,
+ int begin_new_max_period, int only_sz, Uint **hpp, Uint *szp)
{
Eterm res = THE_NON_VALUE;
- if (mk->segments.max_ever.no < mk->segments.max.no)
- mk->segments.max_ever.no = mk->segments.max.no;
- if (mk->segments.max_ever.sz < mk->segments.max.sz)
- mk->segments.max_ever.sz = mk->segments.max.sz;
+ if (ma->segments.max_ever.no < ma->segments.max.no)
+ ma->segments.max_ever.no = ma->segments.max.no;
+ if (ma->segments.max_ever.sz < ma->segments.max.sz)
+ ma->segments.max_ever.sz = ma->segments.max.sz;
if (print_to_p) {
- int to = *print_to_p;
+ fmtfn_t to = *print_to_p;
void *arg = print_to_arg;
- erts_print(to, arg, "cached_segments: %beu\n", mk->cache_size);
- erts_print(to, arg, "cache_hits: %beu\n", mk->cache_hits);
- erts_print(to, arg, "segments: %beu %beu %beu\n",
- mk->segments.current.no, mk->segments.max.no, mk->segments.max_ever.no);
- erts_print(to, arg, "segments_size: %beu %beu %beu\n",
- mk->segments.current.sz, mk->segments.max.sz, mk->segments.max_ever.sz);
- erts_print(to, arg, "segments_watermark: %beu\n",
- mk->segments.current.watermark);
+ if (!only_sz) {
+ erts_print(to, arg, "cached_segments: %beu\n", ma->cache_size);
+ erts_print(to, arg, "cache_hits: %beu\n", ma->cache_hits);
+ erts_print(to, arg, "segments: %beu %beu %beu\n",
+ ma->segments.current.no, ma->segments.max.no, ma->segments.max_ever.no);
+ erts_print(to, arg, "segments_watermark: %beu\n",
+ ma->segments.current.watermark);
+ }
+ erts_print(to, arg, "segments_size: %beu %beu %beu\n",
+ ma->segments.current.sz, ma->segments.max.sz, ma->segments.max_ever.sz);
}
if (hpp || szp) {
res = NIL;
- add_2tup(hpp, szp, &res,
- am.segments_watermark,
- bld_unstable_uint(hpp, szp, mk->segments.current.watermark));
- add_4tup(hpp, szp, &res,
- am.segments_size,
- bld_unstable_uint(hpp, szp, mk->segments.current.sz),
- bld_unstable_uint(hpp, szp, mk->segments.max.sz),
- bld_unstable_uint(hpp, szp, mk->segments.max_ever.sz));
- add_4tup(hpp, szp, &res,
- am.segments,
- bld_unstable_uint(hpp, szp, mk->segments.current.no),
- bld_unstable_uint(hpp, szp, mk->segments.max.no),
- bld_unstable_uint(hpp, szp, mk->segments.max_ever.no));
- add_2tup(hpp, szp, &res,
- am.cache_hits,
- bld_unstable_uint(hpp, szp, mk->cache_hits));
- add_2tup(hpp, szp, &res,
- am.cached_segments,
- bld_unstable_uint(hpp, szp, mk->cache_size));
-
+ add_4tup(hpp, szp, &res,
+ am.segments_size,
+ bld_unstable_uint(hpp, szp, ma->segments.current.sz),
+ bld_unstable_uint(hpp, szp, ma->segments.max.sz),
+ bld_unstable_uint(hpp, szp, ma->segments.max_ever.sz));
+ if (!only_sz) {
+ add_2tup(hpp, szp, &res,
+ am.segments_watermark,
+ bld_unstable_uint(hpp, szp, ma->segments.current.watermark));
+ add_4tup(hpp, szp, &res,
+ am.segments,
+ bld_unstable_uint(hpp, szp, ma->segments.current.no),
+ bld_unstable_uint(hpp, szp, ma->segments.max.no),
+ bld_unstable_uint(hpp, szp, ma->segments.max_ever.no));
+ add_2tup(hpp, szp, &res,
+ am.cache_hits,
+ bld_unstable_uint(hpp, szp, ma->cache_hits));
+ add_2tup(hpp, szp, &res,
+ am.cached_segments,
+ bld_unstable_uint(hpp, szp, ma->cache_size));
+ }
}
if (begin_new_max_period) {
- mk->segments.max.no = mk->segments.current.no;
- mk->segments.max.sz = mk->segments.current.sz;
+ ma->segments.max.no = ma->segments.current.no;
+ ma->segments.max.sz = ma->segments.current.sz;
}
return res;
}
-static Eterm info_memkind(ErtsMsegAllctr_t *ma, MemKind* mk, int *print_to_p, void *print_to_arg,
- int begin_max_per, Uint **hpp, Uint *szp)
+static Eterm info_memkind(ErtsMsegAllctr_t *ma, fmtfn_t *print_to_p, void *print_to_arg,
+ int begin_max_per, int only_sz, Uint **hpp, Uint *szp)
{
Eterm res = THE_NON_VALUE;
Eterm atoms[3];
Eterm values[3];
- if (print_to_p) {
- erts_print(*print_to_p, print_to_arg, "memory kind: %s\n", mk->name);
- }
- if (hpp || szp) {
- atoms[0] = am.name;
- atoms[1] = am.status;
- atoms[2] = am.calls;
- values[0] = erts_bld_string(hpp, szp, mk->name);
+ if (!only_sz) {
+ if (print_to_p) {
+ erts_print(*print_to_p, print_to_arg, "memory kind: %s\n", "all memory");
+ }
+ if (hpp || szp) {
+ atoms[0] = am.name;
+ atoms[1] = am.status;
+ atoms[2] = am.calls;
+ values[0] = erts_bld_string(hpp, szp, "all memory");
+ }
}
- values[1] = info_status(ma, mk, print_to_p, print_to_arg, begin_max_per, hpp, szp);
- values[2] = info_calls(ma, print_to_p, print_to_arg, hpp, szp);
+ res = info_status(ma, print_to_p, print_to_arg, begin_max_per, only_sz, hpp, szp);
+ if (!only_sz) {
+ values[1] = res;
+ values[2] = info_calls(ma, print_to_p, print_to_arg, hpp, szp);
- if (hpp || szp)
- res = bld_2tup_list(hpp, szp, 3, atoms, values);
+ if (hpp || szp)
+ res = bld_2tup_list(hpp, szp, 3, atoms, values);
+ }
return res;
}
-
static Eterm
-info_version(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg, Uint **hpp, Uint *szp)
+info_version(ErtsMsegAllctr_t *ma, fmtfn_t *print_to_p, void *print_to_arg, Uint **hpp, Uint *szp)
{
Eterm res = THE_NON_VALUE;
@@ -1285,7 +1225,7 @@ info_version(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg, Uint **h
Eterm
erts_mseg_info_options(int ix,
- int *print_to_p, void *print_to_arg,
+ fmtfn_t *print_to_p, void *print_to_arg,
Uint **hpp, Uint *szp)
{
ErtsMsegAllctr_t *ma = ERTS_MSEG_ALLCTR_IX(ix);
@@ -1298,9 +1238,10 @@ erts_mseg_info_options(int ix,
Eterm
erts_mseg_info(int ix,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
int begin_max_per,
+ int only_sz,
Uint **hpp,
Uint *szp)
{
@@ -1311,29 +1252,29 @@ erts_mseg_info(int ix,
Uint n = 0;
if (hpp || szp) {
-
- if (!atoms_initialized)
- init_atoms(ma);
-
- atoms[0] = am.version;
- atoms[1] = am.options;
- atoms[2] = am.memkind;
- atoms[3] = am.memkind;
+ if (!atoms_initialized)
+ init_atoms(ma);
+ }
+ if (!only_sz) {
+ if (hpp || szp) {
+ atoms[0] = am.version;
+ atoms[1] = am.options;
+ atoms[2] = am.memkind;
+ }
+ values[n++] = info_version(ma, print_to_p, print_to_arg, hpp, szp);
+ values[n++] = info_options(ma, "option ", print_to_p, print_to_arg, hpp, szp);
}
- values[n++] = info_version(ma, print_to_p, print_to_arg, hpp, szp);
- values[n++] = info_options(ma, "option ", print_to_p, print_to_arg, hpp, szp);
ERTS_MSEG_LOCK(ma);
ERTS_DBG_MA_CHK_THR_ACCESS(ma);
-#if HALFWORD_HEAP
- values[n++] = info_memkind(ma, &ma->low_mem, print_to_p, print_to_arg, begin_max_per, hpp, szp);
- values[n++] = info_memkind(ma, &ma->hi_mem, print_to_p, print_to_arg, begin_max_per, hpp, szp);
-#else
- values[n++] = info_memkind(ma, &ma->the_mem, print_to_p, print_to_arg, begin_max_per, hpp, szp);
-#endif
- if (hpp || szp)
- res = bld_2tup_list(hpp, szp, n, atoms, values);
+ res = info_memkind(ma, print_to_p, print_to_arg, begin_max_per, only_sz, hpp, szp);
+
+ if (!only_sz) {
+ values[n++] = res;
+ if (hpp || szp)
+ res = bld_2tup_list(hpp, szp, n, atoms, values);
+ }
ERTS_MSEG_UNLOCK(ma);
@@ -1408,13 +1349,10 @@ Uint
erts_mseg_no(const ErtsMsegOpt_t *opt)
{
ErtsMsegAllctr_t *ma = ERTS_MSEG_ALLCTR_OPT(opt);
- MemKind* mk;
- Uint n = 0;
+ Uint n;
ERTS_MSEG_LOCK(ma);
ERTS_DBG_MA_CHK_THR_ACCESS(ma);
- for (mk=ma->mk_list; mk; mk=mk->next) {
- n += mk->segments.current.no;
- }
+ n = ma->segments.current.no;
ERTS_MSEG_UNLOCK(ma);
return n;
}
@@ -1426,16 +1364,16 @@ erts_mseg_unit_size(void)
}
-static void mem_kind_init(ErtsMsegAllctr_t *ma, MemKind* mk, const char* name)
+static void mem_cache_init(ErtsMsegAllctr_t *ma)
{
int i;
/* Clear all cache headers */
- mseg_cache_clear_node(&(mk->cache_free));
- mseg_cache_clear_node(&(mk->cache_unpowered_node));
+ mseg_cache_clear_node(&(ma->cache_free));
+ mseg_cache_clear_node(&(ma->cache_unpowered_node));
for (i = 0; i < CACHE_AREAS; i++) {
- mseg_cache_clear_node(&(mk->cache_powered_node[i]));
+ mseg_cache_clear_node(&(ma->cache_powered_node[i]));
}
/* Populate cache free list */
@@ -1443,25 +1381,20 @@ static void mem_kind_init(ErtsMsegAllctr_t *ma, MemKind* mk, const char* name)
ASSERT(ma->max_cache_size <= MAX_CACHE_SIZE);
for (i = 0; i < ma->max_cache_size; i++) {
- mseg_cache_clear_node(&(mk->cache[i]));
- erts_circleq_push_head(&(mk->cache_free), &(mk->cache[i]));
+ mseg_cache_clear_node(&(ma->cache[i]));
+ erts_circleq_push_head(&(ma->cache_free), &(ma->cache[i]));
}
- mk->cache_size = 0;
- mk->cache_hits = 0;
-
- mk->segments.current.watermark = 0;
- mk->segments.current.no = 0;
- mk->segments.current.sz = 0;
- mk->segments.max.no = 0;
- mk->segments.max.sz = 0;
- mk->segments.max_ever.no = 0;
- mk->segments.max_ever.sz = 0;
-
- mk->ma = ma;
- mk->name = name;
- mk->next = ma->mk_list;
- ma->mk_list = mk;
+ ma->cache_size = 0;
+ ma->cache_hits = 0;
+
+ ma->segments.current.watermark = 0;
+ ma->segments.current.no = 0;
+ ma->segments.current.sz = 0;
+ ma->segments.max.no = 0;
+ ma->segments.max.sz = 0;
+ ma->segments.max_ever.no = 0;
+ ma->segments.max_ever.sz = 0;
}
void
@@ -1488,16 +1421,16 @@ erts_mseg_init(ErtsMsegInit_t *init)
erts_mtx_init(&init_atoms_mutex, "mseg_init_atoms");
-#if HALFWORD_HEAP
- if (sizeof(void *) != 8)
- erts_exit(1,"Halfword emulator cannot be run in 32bit mode");
-
- init->mmap.virtual_range.start = (char *) sbrk(0);
- init->mmap.virtual_range.end = (char *) 0x100000000UL;
- init->mmap.sco = 0;
+#ifdef ERTS_ALC_A_EXEC
+ /* Initialize erts_exec_mapper *FIRST*, to increase probability
+ * of getting low memory for HiPE AMD64's small code model.
+ */
+ erts_mmap_init(&erts_exec_mmapper, &init->exec_mmap, 1);
+#endif
+ erts_mmap_init(&erts_dflt_mmapper, &init->dflt_mmap, 0);
+#if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
+ erts_mmap_init(&erts_literal_mmapper, &init->literal_mmap, 0);
#endif
-
- erts_mmap_init(&init->mmap);
if (!IS_2POW(GET_PAGE_SIZE))
erts_exit(ERTS_ABORT_EXIT, "erts_mseg: Unexpected page_size %beu\n", GET_PAGE_SIZE);
@@ -1529,14 +1462,7 @@ erts_mseg_init(ErtsMsegInit_t *init)
if (ma->max_cache_size > MAX_CACHE_SIZE)
ma->max_cache_size = MAX_CACHE_SIZE;
- ma->mk_list = NULL;
-
-#if HALFWORD_HEAP
- mem_kind_init(ma, &ma->low_mem, "low memory");
- mem_kind_init(ma, &ma->hi_mem, "high memory");
-#else
- mem_kind_init(ma, &ma->the_mem, "all memory");
-#endif
+ mem_cache_init(ma);
sys_memzero((void *) &ma->calls, sizeof(ErtsMsegCalls));
}
@@ -1545,13 +1471,8 @@ erts_mseg_init(ErtsMsegInit_t *init)
static ERTS_INLINE Uint tot_cache_size(ErtsMsegAllctr_t *ma)
{
- MemKind* mk;
- Uint sz = 0;
ERTS_DBG_MA_CHK_THR_ACCESS(ma);
- for (mk=ma->mk_list; mk; mk=mk->next) {
- sz += mk->cache_size;
- }
- return sz;
+ return ma->cache_size;
}
/*
diff --git a/erts/emulator/sys/common/erl_mseg.h b/erts/emulator/sys/common/erl_mseg.h
index ba04e919fc..bba0dec499 100644
--- a/erts/emulator/sys/common/erl_mseg.h
+++ b/erts/emulator/sys/common/erl_mseg.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2002-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -42,16 +42,6 @@
#if ERTS_HAVE_MSEG_SUPER_ALIGNED
# define MSEG_ALIGN_BITS ERTS_MMAP_SUPERALIGNED_BITS
-#else
-/* If we don't use super aligned multiblock carriers
- * we will mmap with page size alignment (and thus use corresponding
- * align bits).
- *
- * Current implementation needs this to be a constant and
- * only uses this for user dev testing so setting page size
- * to 4096 (12 bits) is fine.
- */
-# define MSEG_ALIGN_BITS (12)
#endif
#if HAVE_ERTS_MSEG
@@ -69,7 +59,9 @@ typedef struct {
Uint rmcbf;
Uint mcs;
Uint nos;
- ErtsMMapInit mmap;
+ ErtsMMapInit dflt_mmap;
+ ErtsMMapInit literal_mmap;
+ ErtsMMapInit exec_mmap;
} ErtsMsegInit_t;
#define ERTS_MSEG_INIT_DEFAULT_INITIALIZER \
@@ -78,7 +70,9 @@ typedef struct {
20, /* rmcbf: Relative max cache bad fit */ \
10, /* mcs: Max cache size */ \
1000, /* cci: Cache check interval */ \
- ERTS_MMAP_INIT_DEFAULT_INITER \
+ ERTS_MMAP_INIT_DEFAULT_INITER, \
+ ERTS_MMAP_INIT_LITERAL_INITER, \
+ ERTS_MMAP_INIT_HIPE_EXEC_INITER \
}
typedef struct {
@@ -87,9 +81,6 @@ typedef struct {
UWord abs_shrink_th;
UWord rel_shrink_th;
int sched_spec;
-#if HALFWORD_HEAP
- int low_mem;
-#endif
} ErtsMsegOpt_t;
extern const ErtsMsegOpt_t erts_mseg_default_opt;
@@ -107,8 +98,8 @@ Uint erts_mseg_unit_size(void);
void erts_mseg_init(ErtsMsegInit_t *init);
void erts_mseg_late_init(void); /* Have to be called after all allocators,
threads and timers have been initialized. */
-Eterm erts_mseg_info_options(int, int *, void*, Uint **, Uint *);
-Eterm erts_mseg_info(int, int *, void*, int, Uint **, Uint *);
+Eterm erts_mseg_info_options(int, fmtfn_t*, void*, Uint **, Uint *);
+Eterm erts_mseg_info(int, fmtfn_t *, void*, int, int, Uint **, Uint *);
#endif /* #if HAVE_ERTS_MSEG */
diff --git a/erts/emulator/sys/common/erl_mtrace_sys_wrap.c b/erts/emulator/sys/common/erl_mtrace_sys_wrap.c
index a8c575835a..fc871f94f1 100644
--- a/erts/emulator/sys/common/erl_mtrace_sys_wrap.c
+++ b/erts/emulator/sys/common/erl_mtrace_sys_wrap.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/sys/common/erl_os_monotonic_time_extender.c b/erts/emulator/sys/common/erl_os_monotonic_time_extender.c
index 367b22d989..d53190fdd5 100644
--- a/erts/emulator/sys/common/erl_os_monotonic_time_extender.c
+++ b/erts/emulator/sys/common/erl_os_monotonic_time_extender.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2015. All Rights Reserved.
+ * Copyright Ericsson AB 2015-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c
index 21fa214364..b8a28bcc18 100644
--- a/erts/emulator/sys/common/erl_poll.c
+++ b/erts/emulator/sys/common/erl_poll.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -74,6 +74,8 @@
#include "erl_thr_progress.h"
#include "erl_driver.h"
#include "erl_alloc.h"
+#include "erl_msacc.h"
+#include "erl_misc_utils.h"
#if !defined(ERTS_POLL_USE_EPOLL) \
&& !defined(ERTS_POLL_USE_DEVPOLL) \
@@ -2131,16 +2133,19 @@ get_timeout(ErtsPollSet ps,
if (timeout > (ErtsMonotonicTime) INT_MAX)
timeout = (ErtsMonotonicTime) INT_MAX;
save_timeout_time += ERTS_MSEC_TO_MONOTONIC(timeout);
+ timeout -= ERTS_PREMATURE_TIMEOUT(timeout, 1000);
break;
case 1000000:
/* Round up to nearest even micro second */
timeout = ERTS_MONOTONIC_TO_USEC(diff_time - 1) + 1;
save_timeout_time += ERTS_USEC_TO_MONOTONIC(timeout);
+ timeout -= ERTS_PREMATURE_TIMEOUT(timeout, 1000*1000);
break;
case 1000000000:
/* Round up to nearest even nano second */
timeout = ERTS_MONOTONIC_TO_NSEC(diff_time - 1) + 1;
save_timeout_time += ERTS_NSEC_TO_MONOTONIC(timeout);
+ timeout -= ERTS_PREMATURE_TIMEOUT(timeout, 1000*1000*1000);
break;
default:
ERTS_INTERNAL_ERROR("Invalid resolution");
@@ -2238,6 +2243,7 @@ static ERTS_INLINE int
check_fd_events(ErtsPollSet ps, ErtsMonotonicTime timeout_time, int max_res)
{
int res;
+ ERTS_MSACC_PUSH_STATE_M();
if (erts_smp_atomic_read_nob(&ps->no_of_user_fds) == 0
&& timeout_time == ERTS_POLL_NO_TIMEOUT) {
/* Nothing to poll and zero timeout; done... */
@@ -2259,6 +2265,7 @@ check_fd_events(ErtsPollSet ps, ErtsMonotonicTime timeout_time, int max_res)
#ifdef ERTS_SMP
erts_thr_progress_prepare_wait(NULL);
#endif
+ ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_SLEEP);
timerfd_set(ps, &its);
res = epoll_wait(ps->kp_fd, ps->res_events, max_res, -1);
res = timerfd_clear(ps, res, max_res);
@@ -2268,10 +2275,12 @@ check_fd_events(ErtsPollSet ps, ErtsMonotonicTime timeout_time, int max_res)
}
#else /* !ERTS_POLL_USE_TIMERFD */
timeout = (int) get_timeout(ps, 1000, timeout_time);
+ if (timeout) {
#ifdef ERTS_SMP
- if (timeout)
erts_thr_progress_prepare_wait(NULL);
#endif
+ ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_SLEEP);
+ }
res = epoll_wait(ps->kp_fd, ps->res_events, max_res, timeout);
#endif /* !ERTS_POLL_USE_TIMERFD */
#elif ERTS_POLL_USE_KQUEUE /* --- kqueue ------------------------------ */
@@ -2279,10 +2288,12 @@ check_fd_events(ErtsPollSet ps, ErtsMonotonicTime timeout_time, int max_res)
if (max_res > ps->res_events_len)
grow_res_events(ps, max_res);
timeout = get_timeout_timespec(ps, &ts, timeout_time);
+ if (timeout) {
#ifdef ERTS_SMP
- if (timeout)
erts_thr_progress_prepare_wait(NULL);
#endif
+ ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_SLEEP);
+ }
res = kevent(ps->kp_fd, NULL, 0, ps->res_events, max_res, &ts);
#endif /* ----------------------------------------- */
}
@@ -2306,26 +2317,33 @@ check_fd_events(ErtsPollSet ps, ErtsMonotonicTime timeout_time, int max_res)
if (poll_res.dp_nfds > ps->res_events_len)
grow_res_events(ps, poll_res.dp_nfds);
poll_res.dp_fds = ps->res_events;
+ if (timeout) {
#ifdef ERTS_SMP
- if (timeout)
erts_thr_progress_prepare_wait(NULL);
#endif
+ ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_SLEEP);
+ }
poll_res.dp_timeout = timeout;
res = ioctl(ps->kp_fd, DP_POLL, &poll_res);
#elif ERTS_POLL_USE_POLL && defined(HAVE_PPOLL) /* --- ppoll ---------------- */
struct timespec ts;
timeout = get_timeout_timespec(ps, &ts, timeout_time);
+ if (timeout) {
#ifdef ERTS_SMP
- if (timeout)
erts_thr_progress_prepare_wait(NULL);
#endif
+ ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_SLEEP);
+ }
res = ppoll(ps->poll_fds, ps->no_poll_fds, &ts, NULL);
#elif ERTS_POLL_USE_POLL /* --- poll --------------------------------- */
timeout = (int) get_timeout(ps, 1000, timeout_time);
+
+ if (timeout) {
#ifdef ERTS_SMP
- if (timeout)
erts_thr_progress_prepare_wait(NULL);
#endif
+ ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_SLEEP);
+ }
res = poll(ps->poll_fds, ps->no_poll_fds, timeout);
#elif ERTS_POLL_USE_SELECT /* --- select ------------------------------ */
SysTimeval to;
@@ -2334,18 +2352,22 @@ check_fd_events(ErtsPollSet ps, ErtsMonotonicTime timeout_time, int max_res)
ERTS_FD_COPY(&ps->input_fds, &ps->res_input_fds);
ERTS_FD_COPY(&ps->output_fds, &ps->res_output_fds);
+ if (timeout) {
#ifdef ERTS_SMP
- if (timeout)
erts_thr_progress_prepare_wait(NULL);
#endif
+ ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_SLEEP);
+ }
res = ERTS_SELECT(ps->max_fd + 1,
&ps->res_input_fds,
&ps->res_output_fds,
NULL,
&to);
#ifdef ERTS_SMP
- if (timeout)
+ if (timeout) {
erts_thr_progress_finalize_wait(NULL);
+ ERTS_MSACC_POP_STATE_M();
+ }
if (res < 0
&& errno == EBADF
&& ERTS_POLLSET_HAVE_UPDATE_REQUESTS(ps)) {
@@ -2380,10 +2402,12 @@ check_fd_events(ErtsPollSet ps, ErtsMonotonicTime timeout_time, int max_res)
return res;
#endif /* ----------------------------------------- */
}
+ if (timeout) {
#ifdef ERTS_SMP
- if (timeout)
erts_thr_progress_finalize_wait(NULL);
#endif
+ ERTS_MSACC_POP_STATE_M();
+ }
return res;
}
}
@@ -2432,7 +2456,15 @@ ERTS_POLL_EXPORT(erts_poll_wait)(ErtsPollSet ps,
}
#endif
- res = check_fd_events(ps, to, no_fds);
+ while (1) {
+ res = check_fd_events(ps, to, no_fds);
+ if (res != 0)
+ break;
+ if (to == ERTS_POLL_NO_TIMEOUT)
+ break;
+ if (erts_get_monotonic_time(NULL) >= timeout_time)
+ break;
+ }
woke_up(ps);
diff --git a/erts/emulator/sys/common/erl_poll.h b/erts/emulator/sys/common/erl_poll.h
index bd3a46ef0f..c16122610d 100644
--- a/erts/emulator/sys/common/erl_poll.h
+++ b/erts/emulator/sys/common/erl_poll.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -93,7 +93,7 @@
# if defined(ERTS_USE_POLL)
# undef ERTS_POLL_USE_POLL
# define ERTS_POLL_USE_POLL 1
-# elif !defined(__WIN32__) && !defined(__OSE__)
+# elif !defined(__WIN32__)
# undef ERTS_POLL_USE_SELECT
# define ERTS_POLL_USE_SELECT 1
# endif
@@ -104,31 +104,13 @@
typedef Uint32 ErtsPollEvents;
#undef ERTS_POLL_EV_E2N
-#if defined(__WIN32__) || defined(__OSE__) /* --- win32 or ose -------- */
+#if defined(__WIN32__) /* --- win32 --------------------------------------- */
#define ERTS_POLL_EV_IN 1
#define ERTS_POLL_EV_OUT 2
#define ERTS_POLL_EV_ERR 4
#define ERTS_POLL_EV_NVAL 8
-#ifdef __OSE__
-
-typedef struct ErtsPollOseMsgList_ {
- struct ErtsPollOseMsgList_ *next;
- union SIGNAL *data;
-} ErtsPollOseMsgList;
-
-struct erts_sys_fd_type {
- SIGSELECT signo;
- ErlDrvOseEventId id;
- ErtsPollOseMsgList *msgs;
- ErlDrvOseEventId (*resolve_signal)(union SIGNAL *sig);
- ethr_mutex mtx;
- void *extra;
-};
-
-#endif
-
#elif ERTS_POLL_USE_EPOLL /* --- epoll ------------------------------- */
#include <sys/epoll.h>
diff --git a/erts/emulator/sys/common/erl_sys_common_misc.c b/erts/emulator/sys/common/erl_sys_common_misc.c
index e292741bfa..79f87eb3a9 100644
--- a/erts/emulator/sys/common/erl_sys_common_misc.c
+++ b/erts/emulator/sys/common/erl_sys_common_misc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/sys/common/erl_util_queue.h b/erts/emulator/sys/common/erl_util_queue.h
index b50e062dd5..73293e0225 100644
--- a/erts/emulator/sys/common/erl_util_queue.h
+++ b/erts/emulator/sys/common/erl_util_queue.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2013. All Rights Reserved.
+ * Copyright Ericsson AB 2013-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/sys/ose/beam.lmconf b/erts/emulator/sys/ose/beam.lmconf
deleted file mode 100644
index 4ad46b01d9..0000000000
--- a/erts/emulator/sys/ose/beam.lmconf
+++ /dev/null
@@ -1,26 +0,0 @@
-OSE_LM_STACK_SIZES=256,512,1024,2048,4096,8192,16384,65536
-OSE_LM_SIGNAL_SIZES=31,63,127,255,1023,4095,16383,65535
-OSE_LM_POOL_SIZE=0x200000
-OSE_LM_MAIN_NAME=main
-OSE_LM_MAIN_STACK_SIZE=0xF000
-OSE_LM_MAIN_PRIORITY=20
-## Has to be of a type that allows MAM
-OSE_LM_PROGRAM_TYPE=APP_RAM
-OSE_LM_DATA_INIT=YES
-OSE_LM_BSS_INIT=YES
-OSE_LM_EXEC_MODEL=SHARED
-HEAP_MAX_SIZE=1000000000
-HEAP_SMALL_BUF_INIT_SIZE=20971520
-HEAP_LARGE_BUF_THRESHOLD=16000000
-HEAP_LOCK_TYPE=2
-
-ERTS_DEFAULT_PRIO=24
-ERTS_SCHEDULER_PRIO=24
-ERTS_ASYNC_PRIO=22
-ERTS_AUX_PRIO=24
-ERTS_SYS_MSG_DISPATCHER_PRIO=21
-
-# Setting the environment variable EFS_RESOLVE_TMO on the block to 0.
-# This will eliminiate delays when trying to open files on not mounted
-# volumes.
-EFS_RESOLVE_TMO=0
diff --git a/erts/emulator/sys/ose/driver_int.h b/erts/emulator/sys/ose/driver_int.h
deleted file mode 100644
index 4a5b7171d1..0000000000
--- a/erts/emulator/sys/ose/driver_int.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1997-2009. 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%
- */
-/*
- * System dependant driver declarations
- */
-
-#ifndef __DRIVER_INT_H__
-#define __DRIVER_INT_H__
-
-#ifdef HAVE_SYS_UIO_H
-#include <sys/types.h>
-#include <sys/uio.h>
-
-typedef struct iovec SysIOVec;
-
-#else
-
-typedef struct {
- char* iov_base;
- int iov_len;
-} SysIOVec;
-
-#endif
-
-#endif
diff --git a/erts/emulator/sys/ose/erl_main.c b/erts/emulator/sys/ose/erl_main.c
deleted file mode 100644
index 877e85f43a..0000000000
--- a/erts/emulator/sys/ose/erl_main.c
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2000-2009. 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 <stdlib.h>
-
-#include "sys.h"
-#include "erl_vm.h"
-#include "global.h"
-#include "ose.h"
-
-int
-main(int argc, char **argv) {
-
- (void)stdin;(void)stdout;(void)stderr;
-
- /* When starting using pm_create -c ARGV="-- -root ..", argv[0] is the first
- part of ARGV and not the name of the executable. So we shuffle some
- pointers here to make erl_start happy. */
- if (argv[0][0] == '-') {
- int i;
- char **tmp_argv = malloc(sizeof(char*)*(argc+1));
- for (i = 0; i < argc; i++)
- tmp_argv[i+1] = argv[i];
- tmp_argv[0] = "beam";
- erl_start(argc+1,tmp_argv);
- free(tmp_argv);
- } else {
- erl_start(argc,argv);
- }
-
- stop(current_process());
-
- return 0;
-}
diff --git a/erts/emulator/sys/ose/erl_ose_sys.h b/erts/emulator/sys/ose/erl_ose_sys.h
deleted file mode 100644
index d0cd3180bf..0000000000
--- a/erts/emulator/sys/ose/erl_ose_sys.h
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1997-2011. 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%
- *
- * This file handles differences between different Unix systems.
- * This should be the only place with conditional compilation
- * depending on the type of OS.
- */
-
-#ifndef _ERL_OSE_SYS_H
-#define _ERL_OSE_SYS_H
-
-#include "ose.h"
-#undef NIL
-#include "ramlog.h"
-#include "erts.sig"
-
-#include "fcntl.h"
-#include "math.h"
-#include "stdio.h"
-#include "stdlib.h"
-#include "string.h"
-#include "sys/param.h"
-#include "sys/time.h"
-#include "time.h"
-#include "dirent.h"
-#include "ethread.h"
-
-/* FIXME: configuration options */
-#define ERTS_SCHED_MIN_SPIN 1
-#define ERTS_SCHED_ONLY_POLL_SCHED_1 1
-#define ERTS_SCHED_FAIR 1
-#define NO_SYSCONF 1
-#define OPEN_MAX FOPEN_MAX
-
-#define MAP_ANON MAP_ANONYMOUS
-
-#ifndef HAVE_MMAP
-# define HAVE_MMAP 0
-#endif
-
-#if HAVE_MMAP
-# include "sys/mman.h"
-#endif
-
-/*
- * Min number of async threads
- */
-#define ERTS_MIN_NO_OF_ASYNC_THREADS 1
-
-/*
- * Our own type of "FD's"
- */
-#define ERTS_SYS_FD_TYPE struct erts_sys_fd_type*
-#define NO_FSTAT_ON_SYS_FD_TYPE 1 /* They are signals, not files */
-
-#include "sys/stat.h"
-
-/* FIXME mremap is not defined in OSE - POSIX issue */
-extern void *mremap (void *__addr, size_t __old_len, size_t __new_len,
- int __flags, ...);
-
-/* FIXME: mremap constants */
-#define MREMAP_MAYMOVE 1
-#define MREMAP_FIXED 2
-
-typedef void *GETENV_STATE;
-
-/*
-** For the erl_timer_sup module.
-*/
-#define HAVE_GETHRTIME
-
-typedef long long SysHrTime;
-extern SysHrTime sys_gethrtime(void);
-
-void sys_init_hrtime(void);
-
-typedef time_t erts_time_t;
-
-typedef struct timeval SysTimeval;
-
-#define sys_gettimeofday(Arg) ((void) gettimeofday((Arg), NULL))
-
-typedef struct {
- clock_t tms_utime;
- clock_t tms_stime;
- clock_t tms_cutime;
- clock_t tms_cstime;
-} SysTimes;
-
-extern int erts_ticks_per_sec;
-
-#define SYS_CLK_TCK (erts_ticks_per_sec)
-
-extern clock_t sys_times(SysTimes *buffer);
-
-/* No use in having other resolutions than 1 Ms. */
-#define SYS_CLOCK_RESOLUTION 1
-
-#define erts_isfinite finite
-
-#ifdef NO_FPE_SIGNALS
-
-#define erts_get_current_fp_exception() NULL
-#ifdef ERTS_SMP
-#define erts_thread_init_fp_exception() do{}while(0)
-#endif
-# define __ERTS_FP_CHECK_INIT(fpexnp) do {} while (0)
-# define __ERTS_FP_ERROR(fpexnp, f, Action) if (!finite(f)) { Action; } else {}
-# define __ERTS_FP_ERROR_THOROUGH(fpexnp, f, Action) __ERTS_FP_ERROR(fpexnp, f, Action)
-# define __ERTS_SAVE_FP_EXCEPTION(fpexnp)
-# define __ERTS_RESTORE_FP_EXCEPTION(fpexnp)
-
-#define erts_sys_block_fpe() 0
-#define erts_sys_unblock_fpe(x) do{}while(0)
-
-#else /* !NO_FPE_SIGNALS */
-
-extern volatile unsigned long *erts_get_current_fp_exception(void);
-#ifdef ERTS_SMP
-extern void erts_thread_init_fp_exception(void);
-#endif
-# if (defined(__i386__) || defined(__x86_64__)) && defined(__GNUC__)
-# define erts_fwait(fpexnp,f) \
- __asm__ __volatile__("fwait" : "=m"(*(fpexnp)) : "m"(f))
-# elif (defined(__powerpc__) || defined(__ppc__)) && defined(__GNUC__)
-# define erts_fwait(fpexnp,f) \
- __asm__ __volatile__("" : "=m"(*(fpexnp)) : "fm"(f))
-# elif defined(__sparc__) && defined(__linux__) && defined(__GNUC__)
-# define erts_fwait(fpexnp,f) \
- __asm__ __volatile__("" : "=m"(*(fpexnp)) : "em"(f))
-# else
-# define erts_fwait(fpexnp,f) \
- __asm__ __volatile__("" : "=m"(*(fpexnp)) : "g"(f))
-# endif
-# if (defined(__i386__) || defined(__x86_64__)) && defined(__GNUC__)
- extern void erts_restore_fpu(void);
-# else
-# define erts_restore_fpu() /*empty*/
-# endif
-# if (!defined(__GNUC__) || \
- (__GNUC__ < 2) || \
- (__GNUC__ == 2 && __GNUC_MINOR < 96)) && \
- !defined(__builtin_expect)
-# define __builtin_expect(x, expected_value) (x)
-# endif
-static __inline__ int erts_check_fpe(volatile unsigned long *fp_exception, double f)
-{
- erts_fwait(fp_exception, f);
- if (__builtin_expect(*fp_exception == 0, 1))
- return 0;
- *fp_exception = 0;
- erts_restore_fpu();
- return 1;
-}
-# undef erts_fwait
-# undef erts_restore_fpu
-extern void erts_fp_check_init_error(volatile unsigned long *fp_exception);
-static __inline__ void __ERTS_FP_CHECK_INIT(volatile unsigned long *fp_exception)
-{
- if (__builtin_expect(*fp_exception == 0, 1))
- return;
- erts_fp_check_init_error(fp_exception);
-}
-# define __ERTS_FP_ERROR(fpexnp, f, Action) do { if (erts_check_fpe((fpexnp),(f))) { Action; } } while (0)
-# define __ERTS_SAVE_FP_EXCEPTION(fpexnp) unsigned long old_erl_fp_exception = *(fpexnp)
-# define __ERTS_RESTORE_FP_EXCEPTION(fpexnp) \
- do { *(fpexnp) = old_erl_fp_exception; } while (0)
- /* This is for library calls where we don't trust the external
- code to always throw floating-point exceptions on errors. */
-static __inline__ int erts_check_fpe_thorough(volatile unsigned long *fp_exception, double f)
-{
- return erts_check_fpe(fp_exception, f) || !finite(f);
-}
-# define __ERTS_FP_ERROR_THOROUGH(fpexnp, f, Action) \
- do { if (erts_check_fpe_thorough((fpexnp),(f))) { Action; } } while (0)
-
-int erts_sys_block_fpe(void);
-void erts_sys_unblock_fpe(int);
-
-#endif /* !NO_FPE_SIGNALS */
-
-#define ERTS_FP_CHECK_INIT(p) __ERTS_FP_CHECK_INIT(&(p)->fp_exception)
-#define ERTS_FP_ERROR(p, f, A) __ERTS_FP_ERROR(&(p)->fp_exception, f, A)
-#define ERTS_FP_ERROR_THOROUGH(p, f, A) __ERTS_FP_ERROR_THOROUGH(&(p)->fp_exception, f, A)
-
-/* FIXME: force HAVE_GETPAGESIZE and stub getpagesize */
-#ifndef HAVE_GETPAGESIZE
-#define HAVE_GETPAGESIZE 1
-#endif
-
-extern int getpagesize(void);
-
-#ifndef HZ
-#define HZ 60
-#endif
-
-/* OSE5 doesn't provide limits.h so a number of macros should be
- * added manually */
-
-#ifndef CHAR_BIT
-#define CHAR_BIT 8
-#endif
-
-/* Minimum and maximum values a `signed int' can hold. */
-#ifndef INT_MAX
-#define INT_MAX 2147483647
-#endif
-
-#ifndef INT_MIN
-#define INT_MIN (-INT_MAX - 1)
-#endif
-
-#ifndef UINT_MAX
-# define UINT_MAX 4294967295U
-#endif
-
-/*
-static void erts_ose_sys_send(union SIGNAL **signal,PROCESS dst,
- char* file,int line) {
- SIGSELECT **ziggy = (SIGSELECT**)signal;
- printf("%s:%d 0x%x Send signal 0x%x(0x%x) to 0x%x\r\n",
- file,line,current_process(),ziggy[0][0],*ziggy,dst);
- send(signal,dst);
-}
-#define send(signal,dst) erts_ose_sys_send(signal,dst,__FILE__,__LINE__)
-
-static void erts_ose_sys_send_w_sender(union SIGNAL **signal,
- PROCESS sender,PROCESS dst,
- char* file,int line) {
- SIGSELECT **ziggy = (SIGSELECT**)signal;
- printf("%s:%d 0x%x Send signal 0x%x(0x%x) to 0x%x as 0x%x\r\n",
- file,line,current_process(),ziggy[0][0],*ziggy,dst,sender);
- send_w_sender(signal,sender,dst);
-}
-#define send_w_sender(signal,sender,dst) \
- erts_ose_sys_send_w_sender(signal,sender,dst,__FILE__,__LINE__)
-
-
-static union SIGNAL *erts_ose_sys_receive(SIGSELECT *sigsel,
- char *file,
- int line) {
- SIGSELECT *sig;
- int i;
-
- printf("%s:%d 0x%x receive({%d,",file,line,current_process(),sigsel[0]);
- for (i = 1; i < sigsel[0]; i++)
- printf("0x%x, ",sigsel[i]);
- if (sigsel[0] != 0)
- printf("0x%x",sigsel[i]);
- printf("})\n");
- sig = (SIGSELECT*)receive(sigsel);
- printf("%s:%d 0x%x got 0x%x from 0x%x\n",file,line,current_process(),
- *sig,sender((union SIGNAL**)(&sig)));
- return (union SIGNAL*)sig;
-}
-#define receive(SIGSEL) erts_ose_sys_receive(SIGSEL,__FILE__,__LINE__)
-
-static union SIGNAL *erts_ose_sys_receive_w_tmo(OSTIME tmo,SIGSELECT *sigsel,
- char *file,int line) {
- SIGSELECT *sig;
- int i;
- if (tmo == 0) {
- sig = (SIGSELECT*)receive_w_tmo(tmo,sigsel);
- if (sig != NULL) {
- printf("%s:%d 0x%x receive_w_tmo(0,{%d,",file,line,current_process(),
- sigsel[0]);
- for (i = 1; i < sigsel[0]; i++)
- printf("0x%x, ",sigsel[i]);
- if (sigsel[0] != 0)
- printf("0x%x",sigsel[i]);
- printf("})\n");
- printf("%s:%d 0x%x got 0x%x from 0x%x\n",file,line,current_process(),
- *sig,sender((union SIGNAL**)(&sig)));
- }
- } else {
- printf("%s:%d 0x%x receive_w_tmo(%u,{%d,",file,line,current_process(),tmo,
- sigsel[0]);
- for (i = 1; i < sigsel[0]; i++)
- printf("0x%x, ",sigsel[i]);
- if (sigsel[0] != 0)
- printf("0x%x",sigsel[i]);
- printf("})\n");
- sig = (SIGSELECT*)receive_w_tmo(tmo,sigsel);
- printf("%s:%d 0x%x got ",file,line,current_process());
- if (sig == NULL)
- printf("TIMEOUT\n");
- else
- printf("0x%x from 0x%x\n",*sig,sender((union SIGNAL**)(&sig)));
- }
-
- return (union SIGNAL*)sig;
-}
-
-#define receive_w_tmo(tmo,sigsel) erts_ose_sys_receive_w_tmo(tmo,sigsel, \
- __FILE__,__LINE__)
-
-static union SIGNAL *erts_ose_sys_receive_fsem(OSTIME tmo,SIGSELECT *sigsel,
- OSFSEMVAL fsem,
- char *file,int line) {
- SIGSELECT *sig;
- int i;
- if (tmo == 0) {
- sig = (SIGSELECT*)receive_fsem(tmo,sigsel,fsem);
- if (sig != NULL && sig != OS_RCV_FSEM) {
- printf("%s:%d 0x%x receive_fsem(0,{%d,",file,line,current_process(),
- sigsel[0]);
- for (i = 1; i < sigsel[0]; i++)
- printf("0x%x, ",sigsel[i]);
- if (sigsel[0] != 0)
- printf("0x%x",sigsel[i]);
- printf("},%d)\n",fsem);
- printf("%s:%d 0x%x got 0x%x from 0x%x\n",file,line,current_process(),
- *sig,sender((union SIGNAL**)(&sig)));
- }
- } else {
- printf("%s:%d 0x%x receive_fsem(%u,{%d,",file,line,current_process(),tmo,
- sigsel[0]);
- for (i = 1; i < sigsel[0]; i++)
- printf("0x%x, ",sigsel[i]);
- if (sigsel[0] != 0)
- printf("0x%x",sigsel[i]);
- printf("},%d)\n",fsem);
- sig = (SIGSELECT*)receive_fsem(tmo,sigsel,fsem);
- printf("%s:%d 0x%x got ",file,line,current_process());
- if (sig == NULL)
- printf("TIMEOUT\n");
- else if (sig == OS_RCV_FSEM)
- printf("FSEM\n");
- else
- printf("0x%x from 0x%x\n",*sig,sender((union SIGNAL**)(&sig)));
- }
-
- return (union SIGNAL*)sig;
-}
-
-#define receive_fsem(tmo,sigsel,fsem) \
- erts_ose_sys_receive_fsem(tmo,sigsel,fsem,__FILE__,__LINE__)
-*/
-#endif /* _ERL_OSE_SYS_H */
diff --git a/erts/emulator/sys/ose/erl_ose_sys_ddll.c b/erts/emulator/sys/ose/erl_ose_sys_ddll.c
deleted file mode 100644
index 5051f7fcc1..0000000000
--- a/erts/emulator/sys/ose/erl_ose_sys_ddll.c
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2006-2013. 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%
- */
-
-/*
- * Interface functions to the dynamic linker using dl* functions.
- * (No support in OSE, we use static linkage instead)
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include "sys.h"
-#include "erl_vm.h"
-#include "global.h"
-
-
-void erl_sys_ddll_init(void) {
-}
-
-/*
- * Open a shared object
- */
-int erts_sys_ddll_open(const char *full_name, void **handle, ErtsSysDdllError* err)
-{
- return ERL_DE_ERROR_NO_DDLL_FUNCTIONALITY;
-}
-
-int erts_sys_ddll_open_noext(char *dlname, void **handle, ErtsSysDdllError* err)
-{
- return ERL_DE_ERROR_NO_DDLL_FUNCTIONALITY;
-}
-
-/*
- * Find a symbol in the shared object
- */
-int erts_sys_ddll_sym2(void *handle, const char *func_name, void **function,
- ErtsSysDdllError* err)
-{
- return ERL_DE_ERROR_NO_DDLL_FUNCTIONALITY;
-}
-
-/* XXX:PaN These two will be changed with new driver interface! */
-
-/*
- * Load the driver init function, might appear under different names depending on object arch...
- */
-
-int erts_sys_ddll_load_driver_init(void *handle, void **function)
-{
- void *fn;
- int res;
- if ((res = erts_sys_ddll_sym2(handle, "driver_init", &fn, NULL)) != ERL_DE_NO_ERROR) {
- res = erts_sys_ddll_sym2(handle, "_driver_init", &fn, NULL);
- }
- if (res == ERL_DE_NO_ERROR) {
- *function = fn;
- }
- return res;
-}
-
-int erts_sys_ddll_load_nif_init(void *handle, void **function, ErtsSysDdllError* err)
-{
- void *fn;
- int res;
- if ((res = erts_sys_ddll_sym2(handle, "nif_init", &fn, err)) != ERL_DE_NO_ERROR) {
- res = erts_sys_ddll_sym2(handle, "_nif_init", &fn, err);
- }
- if (res == ERL_DE_NO_ERROR) {
- *function = fn;
- }
- return res;
-}
-
-/*
- * Call the driver_init function, whatever it's really called, simple on unix...
-*/
-void *erts_sys_ddll_call_init(void *function) {
- void *(*initfn)(void) = function;
- return (*initfn)();
-}
-void *erts_sys_ddll_call_nif_init(void *function) {
- return erts_sys_ddll_call_init(function);
-}
-
-
-
-/*
- * Close a chared object
- */
-int erts_sys_ddll_close2(void *handle, ErtsSysDdllError* err)
-{
- return ERL_DE_ERROR_NO_DDLL_FUNCTIONALITY;
-}
-
-
-/*
- * Return string that describes the (current) error
- */
-char *erts_sys_ddll_error(int code)
-{
- return "Unspecified error";
-}
-
-void erts_sys_ddll_free_error(ErtsSysDdllError* err)
-{
- if (err->str != NULL) {
- erts_free(ERTS_ALC_T_DDLL_TMP_BUF, err->str);
- }
-}
diff --git a/erts/emulator/sys/ose/erl_poll.c b/erts/emulator/sys/ose/erl_poll.c
deleted file mode 100644
index c690f08dea..0000000000
--- a/erts/emulator/sys/ose/erl_poll.c
+++ /dev/null
@@ -1,818 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2006-2012. 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%
- */
-
-/*
- * Description: Poll interface suitable for ERTS on OSE with or without
- * SMP support.
- *
- * The interface is currently implemented using:
- * - receive + receive_fsem
- *
- * Author: Lukas Larsson
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include "erl_thr_progress.h"
-#include "erl_driver.h"
-#include "erl_alloc.h"
-#include "erl_poll.h"
-
-#define NOFILE 4096
-
-/*
- * Some debug macros
- */
-
-/* #define HARDDEBUG
-#define HARDTRACE*/
-#ifdef HARDDEBUG
-#ifdef HARDTRACE
-#define HARDTRACEF(X, ...) { fprintf(stderr, X, __VA_ARGS__); fprintf(stderr,"\r\n"); }
-#else
-#define HARDTRACEF(...)
-#endif
-
-#else
-#define HARDTRACEF(X,...)
-#define HARDDEBUGF(...)
-#endif
-
-#if 0
-#define ERTS_POLL_DEBUG_PRINT
-#endif
-
-#if defined(DEBUG) && 0
-#define HARD_DEBUG
-#endif
-
-# define SEL_ALLOC erts_alloc
-# define SEL_REALLOC realloc_wrap
-# define SEL_FREE erts_free
-
-#ifdef ERTS_SMP
-
-#define ERTS_POLLSET_LOCK(PS) \
- erts_smp_mtx_lock(&(PS)->mtx)
-#define ERTS_POLLSET_UNLOCK(PS) \
- erts_smp_mtx_unlock(&(PS)->mtx)
-
-#else
-
-#define ERTS_POLLSET_LOCK(PS)
-#define ERTS_POLLSET_UNLOCK(PS)
-
-#endif
-
-/*
- * --- Data types ------------------------------------------------------------
- */
-
-union SIGNAL {
- SIGSELECT sig_no;
-};
-
-typedef struct erts_sigsel_item_ ErtsSigSelItem;
-
-struct erts_sigsel_item_ {
- ErtsSigSelItem *next;
- ErtsSysFdType fd;
- ErtsPollEvents events;
-};
-
-typedef struct erts_sigsel_info_ ErtsSigSelInfo;
-
-struct erts_sigsel_info_ {
- ErtsSigSelInfo *next;
- SIGSELECT signo;
- ErlDrvOseEventId (*decode)(union SIGNAL* sig);
- ErtsSigSelItem *fds;
-};
-
-struct ErtsPollSet_ {
- SIGSELECT *sigs;
- ErtsSigSelInfo *info;
- Uint sig_count;
- Uint item_count;
- PROCESS interrupt;
- erts_atomic32_t wakeup_state;
- erts_atomic64_t timeout_time;
-#ifdef ERTS_SMP
- erts_smp_mtx_t mtx;
-#endif
-};
-
-static int max_fds = -1;
-
-static ERTS_INLINE void
-init_timeout_time(ErtsPollSet ps)
-{
- erts_atomic64_init_nob(&ps->timeout_time,
- (erts_aint64_t) ERTS_MONOTONIC_TIME_MAX);
-}
-
-static ERTS_INLINE void
-set_timeout_time(ErtsPollSet ps, ErtsMonotonicTime time)
-{
- erts_atomic64_set_relb(&ps->timeout_time,
- (erts_aint64_t) time);
-}
-
-static ERTS_INLINE ErtsMonotonicTime
-get_timeout_time(ErtsPollSet ps)
-{
- return (ErtsMonotonicTime) erts_atomic64_read_acqb(&ps->timeout_time);
-}
-
-#define ERTS_POLL_NOT_WOKEN ((erts_aint32_t) (1 << 0))
-#define ERTS_POLL_WOKEN_INTR ((erts_aint32_t) (1 << 1))
-#define ERTS_POLL_WOKEN_TIMEDOUT ((erts_aint32_t) (1 << 2))
-#define ERTS_POLL_WOKEN_IO_READY ((erts_aint32_t) (1 << 3))
-#define ERTS_POLL_SLEEPING ((erts_aint32_t) (1 << 4))
-
-/* signal list prototypes */
-static ErtsSigSelInfo *get_sigsel_info(ErtsPollSet ps, SIGSELECT signo);
-static ErtsSigSelItem *get_sigsel_item(ErtsPollSet ps, ErtsSysFdType fd);
-static ErtsSigSelInfo *add_sigsel_info(ErtsPollSet ps, ErtsSysFdType fd,
- ErlDrvOseEventId (*decode)(union SIGNAL* sig));
-static ErtsSigSelItem *add_sigsel_item(ErtsPollSet ps, ErtsSysFdType fd,
- ErlDrvOseEventId (*decode)(union SIGNAL* sig));
-static int del_sigsel_info(ErtsPollSet ps, ErtsSigSelInfo *info);
-static int del_sigsel_item(ErtsPollSet ps, ErtsSigSelItem *item);
-static int update_sigsel(ErtsPollSet ps);
-
-static ErtsSigSelInfo *
-get_sigsel_info(ErtsPollSet ps, SIGSELECT signo) {
- ErtsSigSelInfo *curr = ps->info;
- while (curr != NULL) {
- if (curr->signo == signo)
- return curr;
- curr = curr->next;
- }
- return NULL;
-}
-
-static ErtsSigSelItem *
-get_sigsel_item(ErtsPollSet ps, ErtsSysFdType fd) {
- ErtsSigSelInfo *info = get_sigsel_info(ps,fd->signo);
- ErtsSigSelItem *curr;
-
- if (info == NULL)
- return NULL;
-
- curr = info->fds;
-
- while (curr != NULL) {
- if (curr->fd->id == fd->id) {
- ASSERT(curr->fd->signo == fd->signo);
- return curr;
- }
- curr = curr->next;
- }
- return NULL;
-}
-
-static ErtsSigSelInfo *
-add_sigsel_info(ErtsPollSet ps, ErtsSysFdType fd,
- ErlDrvOseEventId (*decode)(union SIGNAL* sig)) {
- ErtsSigSelInfo *info = SEL_ALLOC(ERTS_ALC_T_POLLSET,
- sizeof(ErtsSigSelInfo));
- info->next = ps->info;
- info->fds = NULL;
- info->signo = fd->signo;
- info->decode = decode;
- ps->info = info;
- ps->sig_count++;
- return info;
-}
-
-static ErtsSigSelItem *
-add_sigsel_item(ErtsPollSet ps, ErtsSysFdType fd,
- ErlDrvOseEventId (*decode)(union SIGNAL* sig)) {
- ErtsSigSelInfo *info = get_sigsel_info(ps,fd->signo);
- ErtsSigSelItem *item = SEL_ALLOC(ERTS_ALC_T_POLLSET,
- sizeof(ErtsSigSelItem));
- if (info == NULL)
- info = add_sigsel_info(ps, fd, decode);
- if (info->decode != decode) {
- erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
- erts_dsprintf(dsbufp, "erts_poll_control() inconsistency: multiple resolve_signal functions for same signal (%d)\n",
- fd->signo);
- erts_send_error_to_logger_nogl(dsbufp);
- }
- ASSERT(info->decode == decode);
- item->next = info->fds;
- item->fd = fd;
- item->events = 0;
- info->fds = item;
- ps->item_count++;
- return item;
-}
-
-static int del_sigsel_info(ErtsPollSet ps, ErtsSigSelInfo *info) {
- ErtsSigSelInfo *curr, *prev;
-
- if (ps->info == info) {
- ps->info = ps->info->next;
- } else {
- curr = ps->info->next;
- prev = ps->info;
-
- while (curr != info) {
- if (curr == NULL)
- return 1;
- prev = curr;
- curr = curr->next;
- }
- prev->next = curr->next;
- }
-
- ps->sig_count--;
- SEL_FREE(ERTS_ALC_T_POLLSET, info);
- return 0;
-}
-
-static int del_sigsel_item(ErtsPollSet ps, ErtsSigSelItem *item) {
- ErtsSigSelInfo *info = get_sigsel_info(ps,item->fd->signo);
- ErtsSigSelItem *curr, *prev;
-
- ps->item_count--;
- ASSERT(ps->item_count >= 0);
-
- if (info->fds == item) {
- info->fds = info->fds->next;
- SEL_FREE(ERTS_ALC_T_POLLSET,item);
- if (info->fds == NULL)
- return del_sigsel_info(ps,info);
- return 0;
- }
-
- curr = info->fds->next;
- prev = info->fds;
-
- while (curr != item) {
- if (curr == NULL) {
- /* We did not find an item to delete so we have to
- * increment item count again.
- */
- ps->item_count++;
- return 1;
- }
- prev = curr;
- curr = curr->next;
- }
- prev->next = curr->next;
- SEL_FREE(ERTS_ALC_T_POLLSET,item);
- return 0;
-}
-
-#ifdef ERTS_SMP
-
-static void update_redir_tables(ErtsPollSet ps) {
- struct OS_redir_entry *redir_table;
- PROCESS sched_1 = ERTS_SCHEDULER_IX(0)->tid.id;
- int i;
- redir_table = SEL_ALLOC(ERTS_ALC_T_POLLSET,
- sizeof(struct OS_redir_entry)*(ps->sig_count+1));
-
- redir_table[0].sig = ps->sig_count+1;
- redir_table[0].pid = 0;
-
- for (i = 1; i < ps->sig_count+1; i++) {
- redir_table[i].sig = ps->sigs[i];
- redir_table[i].pid = sched_1;
- }
-
- for (i = 1; i < erts_no_schedulers; i++) {
- ErtsSchedulerData *esdp = ERTS_SCHEDULER_IX(i);
- set_redirection(esdp->tid.id,redir_table);
- }
-
- SEL_FREE(ERTS_ALC_T_POLLSET,redir_table);
-}
-
-#endif
-
-static int update_sigsel(ErtsPollSet ps) {
- ErtsSigSelInfo *info = ps->info;
-
- int i;
-
- if (ps->sigs != NULL)
- SEL_FREE(ERTS_ALC_T_POLLSET,ps->sigs);
-
- if (ps->sig_count == 0) {
- /* If there are no signals we place a non-valid signal to make sure that
- * we do not trigger on a any unrelated signals which are sent to the
- * process.
- */
- ps->sigs = SEL_ALLOC(ERTS_ALC_T_POLLSET,sizeof(SIGSELECT)*(2));
- ps->sigs[0] = 1;
- ps->sigs[1] = ERTS_SIGNAL_INVALID;
- return 0;
- }
-
- ps->sigs = SEL_ALLOC(ERTS_ALC_T_POLLSET,sizeof(SIGSELECT)*(ps->sig_count+1));
- ps->sigs[0] = ps->sig_count;
-
- for (i = 1; info != NULL; i++, info = info->next)
- ps->sigs[i] = info->signo;
-
-#ifdef ERTS_SMP
- update_redir_tables(ps);
-#endif
-
- return 0;
-}
-
-static ERTS_INLINE void
-wake_poller(ErtsPollSet ps)
-{
- erts_aint32_t wakeup_state;
-
- ERTS_THR_MEMORY_BARRIER;
- wakeup_state = erts_atomic32_read_nob(&ps->wakeup_state);
- while (wakeup_state != ERTS_POLL_WOKEN_IO_READY
- && wakeup_state != ERTS_POLL_WOKEN_INTR) {
- erts_aint32_t act = erts_atomic32_cmpxchg_nob(&ps->wakeup_state,
- ERTS_POLL_WOKEN_INTR,
- wakeup_state);
- if (act == wakeup_state) {
- wakeup_state = act;
- break;
- }
- wakeup_state = act;
- }
- if (wakeup_state == ERTS_POLL_SLEEPING) {
- /*
- * Since we don't know the internals of signal_fsem() we issue
- * a memory barrier as a safety precaution ensuring that
- * the store we just made to wakeup_state wont be reordered
- * with loads in signal_fsem().
- */
- ERTS_THR_MEMORY_BARRIER;
- signal_fsem(ps->interrupt);
- }
-}
-
-static ERTS_INLINE void
-reset_interrupt(ErtsPollSet ps)
-{
- /* We need to keep io-ready if set */
- erts_aint32_t wakeup_state = erts_atomic32_read_nob(&ps->wakeup_state);
- while (wakeup_state != ERTS_POLL_NOT_WOKEN &&
- wakeup_state != ERTS_POLL_SLEEPING) {
- erts_aint32_t act = erts_atomic32_cmpxchg_nob(&ps->wakeup_state,
- ERTS_POLL_NOT_WOKEN,
- wakeup_state);
- if (wakeup_state == act)
- break;
- wakeup_state = act;
- }
- ERTS_THR_MEMORY_BARRIER;
-}
-
-static ERTS_INLINE void
-set_interrupt(ErtsPollSet ps)
-{
- wake_poller(ps);
-}
-
-void erts_poll_interrupt(ErtsPollSet ps,int set) {
- HARDTRACEF("erts_poll_interrupt called!\n");
-
- if (!set)
- reset_interrupt(ps);
- else
- set_interrupt(ps);
-
-}
-
-void erts_poll_interrupt_timed(ErtsPollSet ps,
- int set,
- ErtsTimeoutTime timeout_time) {
- HARDTRACEF("erts_poll_interrupt_timed called!\n");
-
- if (!set)
- reset_interrupt(ps);
- else if (get_timeout_time(ps) > timeout_time)
- set_interrupt(ps);
-}
-
-ErtsPollEvents erts_poll_control(ErtsPollSet ps, ErtsSysFdType fd,
- ErtsPollEvents pe, int on, int* do_wake) {
- ErtsSigSelItem *curr;
- ErtsPollEvents new_events;
- int old_sig_count;
-
- HARDTRACEF(
- "%ux: In erts_poll_control, fd = %d, pe = %d, on = %d, *do_wake = %d, curr = 0x%xu",
- ps, fd, pe, on, do_wake, curr);
-
- ERTS_POLLSET_LOCK(ps);
-
- if (on && (pe & ERTS_POLL_EV_IN) && (pe & ERTS_POLL_EV_OUT)) {
- /* Check to make sure both in and out are not used at the same time */
- new_events = ERTS_POLL_EV_NVAL;
- goto done;
- }
-
- curr = get_sigsel_item(ps, fd);
- old_sig_count = ps->sig_count;
-
- if (curr == NULL && on) {
- curr = add_sigsel_item(ps, fd, fd->resolve_signal);
- } else if (curr == NULL && !on) {
- new_events = ERTS_POLL_EV_NVAL;
- goto done;
- }
-
- new_events = curr->events;
-
- if (pe == 0) {
- *do_wake = 0;
- goto done;
- }
-
- if (on) {
- new_events |= pe;
- curr->events = new_events;
- } else {
- new_events &= ~pe;
- curr->events = new_events;
- if (new_events == 0 && del_sigsel_item(ps, curr)) {
- new_events = ERTS_POLL_EV_NVAL;
- goto done;
- }
- }
-
- if (ps->sig_count != old_sig_count) {
- if (update_sigsel(ps))
- new_events = ERTS_POLL_EV_NVAL;
- }
-done:
- ERTS_POLLSET_UNLOCK(ps);
- HARDTRACEF("%ux: Out erts_poll_control", ps);
- return new_events;
-}
-
-int erts_poll_wait(ErtsPollSet ps,
- ErtsPollResFd pr[],
- int *len,
- ErtsMonotonicTime timeout_time)
-{
- int res = ETIMEDOUT, no_fds, currid = 0;
- OSTIME timeout;
- union SIGNAL *sig;
- ErtsMonotonicTime current_time, diff_time, timeout;
- // HARDTRACEF("%ux: In erts_poll_wait",ps);
- if (ps->interrupt == (PROCESS)0)
- ps->interrupt = current_process();
-
- ASSERT(current_process() == ps->interrupt);
- ASSERT(get_fsem(current_process()) == 0);
- ASSERT(erts_atomic32_read_nob(&ps->wakeup_state) &
- (ERTS_POLL_NOT_WOKEN | ERTS_POLL_WOKEN_INTR));
- /* Max no of spots avable in pr */
- no_fds = *len;
-
- *len = 0;
-
- /* erts_printf("Entering erts_poll_wait(), timeout_time=%bps\n",
- timeout_time); */
-
- if (timeout_time == ERTS_POLL_NO_TIMEOUT) {
- no_timeout:
- timeout = (OSTIME) 0;
- save_timeout_time = ERTS_MONOTONIC_TIME_MIN;
- }
- else {
- ErtsMonotonicTime current_time, diff_time;
- current_time = erts_get_monotonic_time(NULL);
- diff_time = timeout_time - current_time;
- if (diff_time <= 0)
- goto no_timeout;
- diff_time = (ERTS_MONOTONIC_TO_MSEC(diff_time - 1) + 1);
- if (diff_time > INT_MAX)
- diff_time = INT_MAX;
- timeout = (OSTIME) diff_time;
- save_timeout_time = current_time;
- save_timeout_time += ERTS_MSEC_TO_MONOTONIC(diff_time);
- }
-
- set_timeout_time(ps, save_timeout_time);
-
- while (currid < no_fds) {
- if (timeout > 0) {
- erts_aint32_t act = erts_atomic32_cmpxchg_nob(&ps->wakeup_state,
- ERTS_POLL_SLEEPING,
- ERTS_POLL_NOT_WOKEN);
- if (act == ERTS_POLL_NOT_WOKEN) {
-#ifdef ERTS_SMP
- erts_thr_progress_prepare_wait(NULL);
-#endif
- sig = receive_fsem(timeout, ps->sigs, 1);
-#ifdef ERTS_SMP
- erts_thr_progress_finalize_wait(NULL);
-#endif
- } else {
- ASSERT(act == ERTS_POLL_WOKEN_INTR);
- sig = OS_RCV_FSEM;
- }
- } else
- sig = receive_w_tmo(0, ps->sigs);
-
- if (sig == NULL) {
- if (timeout > 0) {
- erts_aint32_t act = erts_atomic32_cmpxchg_nob(&ps->wakeup_state,
- ERTS_POLL_WOKEN_TIMEDOUT,
- ERTS_POLL_SLEEPING);
- if (act == ERTS_POLL_WOKEN_INTR)
- /* Restore fsem as it was signaled but we got a timeout */
- wait_fsem(1);
- } else
- erts_atomic32_cmpxchg_nob(&ps->wakeup_state,
- ERTS_POLL_WOKEN_TIMEDOUT,
- ERTS_POLL_NOT_WOKEN);
- break;
- } else if (sig == OS_RCV_FSEM) {
- ASSERT(erts_atomic32_read_nob(&ps->wakeup_state) == ERTS_POLL_WOKEN_INTR);
- break;
- }
- {
- ErtsSigSelInfo *info = get_sigsel_info(ps, sig->sig_no);
- struct erts_sys_fd_type fd = { sig->sig_no, info->decode(sig) };
- ErtsSigSelItem *item = get_sigsel_item(ps, &fd);
-
- ASSERT(sig);
- if (currid == 0 && timeout > 0) {
- erts_aint32_t act = erts_atomic32_cmpxchg_nob(&ps->wakeup_state,
- ERTS_POLL_WOKEN_IO_READY,
- ERTS_POLL_SLEEPING);
- if (act == ERTS_POLL_WOKEN_INTR) {
- /* Restore fsem as it was signaled but we got a msg */
- wait_fsem(1);
- act = erts_atomic32_cmpxchg_nob(&ps->wakeup_state,
- ERTS_POLL_WOKEN_IO_READY,
- ERTS_POLL_WOKEN_INTR);
- }
- } else if (currid == 0) {
- erts_atomic32_set_nob(&ps->wakeup_state,
- ERTS_POLL_WOKEN_IO_READY);
- }
-
- if (item == NULL) {
- erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
- erts_dsprintf(
- dsbufp,
- "erts_poll_wait() failed: found unkown signal id %d (signo %u) "
- "(curr_proc 0x%x)\n",
- fd.id, fd.signo, current_process());
- erts_send_error_to_logger_nogl(dsbufp);
- timeout = 0;
- /* Under normal circumstances the signal is deallocated by the
- * driver that issued the select operation. But in this case
- * there's no driver waiting for such signal so we have to
- * deallocate it here */
- if (sig)
- free_buf(&sig);
- } else {
- int i;
- struct erts_sys_fd_type *fd = NULL;
- ErtsPollOseMsgList *tl,*new;
-
- /* Check if this fd has already been triggered by a previous signal */
- for (i = 0; i < currid;i++) {
- if (pr[i].fd == item->fd) {
- fd = pr[i].fd;
- pr[i].events |= item->events;
- break;
- }
- }
-
- /* First time this fd is triggered */
- if (fd == NULL) {
- pr[currid].fd = item->fd;
- pr[currid].events = item->events;
- fd = item->fd;
- timeout = 0;
- currid++;
- }
-
- /* Insert new signal in approriate list */
- new = erts_alloc(ERTS_ALC_T_FD_SIG_LIST,sizeof(ErtsPollOseMsgList));
- new->next = NULL;
- new->data = sig;
-
- ethr_mutex_lock(&fd->mtx);
- tl = fd->msgs;
-
- if (tl == NULL) {
- fd->msgs = new;
- } else {
- while (tl->next != NULL)
- tl = tl->next;
- tl->next = new;
- }
- ethr_mutex_unlock(&fd->mtx);
- }
-
- }
- }
-
- {
- erts_aint32_t wakeup_state = erts_atomic32_read_nob(&ps->wakeup_state);
-
- switch (wakeup_state) {
- case ERTS_POLL_WOKEN_IO_READY:
- res = 0;
- break;
- case ERTS_POLL_WOKEN_INTR:
- res = EINTR;
- break;
- case ERTS_POLL_WOKEN_TIMEDOUT:
- res = ETIMEDOUT;
- break;
- case ERTS_POLL_NOT_WOKEN:
- /* This happens when we get an invalid signal only */
- res = EINVAL;
- break;
- default:
- res = 0;
- erts_exit(ERTS_ABORT_EXIT,
- "%s:%d: Internal error: Invalid wakeup_state=%d\n",
- __FILE__, __LINE__, (int) wakeup_state);
- }
- }
-
- erts_atomic32_set_nob(&ps->wakeup_state, ERTS_POLL_NOT_WOKEN);
- set_timeout_time(ps, ERTS_MONOTONIC_TIME_MAX);
-
- *len = currid;
-
- // HARDTRACEF("%ux: Out erts_poll_wait",ps);
- return res;
-}
-
-int erts_poll_max_fds(void)
-{
-
- HARDTRACEF("In/Out erts_poll_max_fds -> %d",max_fds);
- return max_fds;
-}
-
-void erts_poll_info(ErtsPollSet ps,
- ErtsPollInfo *pip)
-{
- Uint size = 0;
- Uint num_events = 0;
-
- size += sizeof(struct ErtsPollSet_);
- size += sizeof(ErtsSigSelInfo)*ps->sig_count;
- size += sizeof(ErtsSigSelItem)*ps->item_count;
- size += sizeof(SIGSELECT)*(ps->sig_count+1);
-
- pip->primary = "receive_fsem";
-
- pip->fallback = NULL;
-
- pip->kernel_poll = NULL;
-
- pip->memory_size = size;
-
- pip->poll_set_size = num_events;
-
- pip->fallback_poll_set_size = 0;
-
- pip->lazy_updates = 0;
-
- pip->pending_updates = 0;
-
- pip->batch_updates = 0;
-
- pip->concurrent_updates = 0;
-
-
- pip->max_fds = erts_poll_max_fds();
- HARDTRACEF("%ux: Out erts_poll_info",ps);
-
-}
-
-ErtsPollSet erts_poll_create_pollset(void)
-{
- ErtsPollSet ps = SEL_ALLOC(ERTS_ALC_T_POLLSET,
- sizeof(struct ErtsPollSet_));
-
- ps->sigs = NULL;
- ps->sig_count = 0;
- ps->item_count = 0;
- ps->info = NULL;
- ps->interrupt = (PROCESS)0;
- erts_atomic32_init_nob(&ps->wakeup_state, ERTS_POLL_NOT_WOKEN);
- init_timeout_time(ps);
-#ifdef ERTS_SMP
- erts_smp_mtx_init(&ps->mtx, "pollset");
-#endif
- update_sigsel(ps);
- HARDTRACEF("%ux: Out erts_poll_create_pollset",ps);
- return ps;
-}
-
-void erts_poll_destroy_pollset(ErtsPollSet ps)
-{
- ErtsSigSelInfo *info;
- for (info = ps->info; ps->info != NULL; info = ps->info, ps->info = ps->info->next) {
- ErtsSigSelItem *item;
- for (item = info->fds; info->fds != NULL; item = info->fds, info->fds = info->fds->next)
- SEL_FREE(ERTS_ALC_T_POLLSET, item);
- SEL_FREE(ERTS_ALC_T_POLLSET, info);
- }
-
- SEL_FREE(ERTS_ALC_T_POLLSET,ps->sigs);
-
-#ifdef ERTS_SMP
- erts_smp_mtx_destroy(&ps->mtx);
-#endif
-
- SEL_FREE(ERTS_ALC_T_POLLSET,ps);
-}
-
-void erts_poll_init(void)
-{
- HARDTRACEF("In %s", __FUNCTION__);
- max_fds = 256;
-
- HARDTRACEF("Out %s", __FUNCTION__);
-}
-
-
-/* OSE driver functions */
-
-union SIGNAL *erl_drv_ose_get_signal(ErlDrvEvent drv_ev) {
- struct erts_sys_fd_type *ev = (struct erts_sys_fd_type *)drv_ev;
- ethr_mutex_lock(&ev->mtx);
- if (ev->msgs == NULL) {
- ethr_mutex_unlock(&ev->mtx);
- return NULL;
- } else {
- ErtsPollOseMsgList *msg = ev->msgs;
- union SIGNAL *sig = (union SIGNAL*)msg->data;
- ASSERT(msg->data);
- ev->msgs = msg->next;
- ethr_mutex_unlock(&ev->mtx);
- erts_free(ERTS_ALC_T_FD_SIG_LIST,msg);
- restore(sig);
- return sig;
- }
-}
-
-ErlDrvEvent
-erl_drv_ose_event_alloc(SIGSELECT signo, ErlDrvOseEventId id,
- ErlDrvOseEventId (*resolve_signal)(union SIGNAL *sig), void *extra) {
- struct erts_sys_fd_type *ev = erts_alloc(ERTS_ALC_T_DRV_EV,
- sizeof(struct erts_sys_fd_type));
- ev->signo = signo;
- ev->extra = extra;
- ev->id = id;
- ev->msgs = NULL;
- ev->resolve_signal = resolve_signal;
- ethr_mutex_init(&ev->mtx);
- return (ErlDrvEvent)ev;
-}
-
-void erl_drv_ose_event_free(ErlDrvEvent drv_ev) {
- struct erts_sys_fd_type *ev = (struct erts_sys_fd_type *)drv_ev;
- ASSERT(ev->msgs == NULL);
- ethr_mutex_destroy(&ev->mtx);
- erts_free(ERTS_ALC_T_DRV_EV,ev);
-}
-
-void erl_drv_ose_event_fetch(ErlDrvEvent drv_ev, SIGSELECT *signo,
- ErlDrvOseEventId *id, void **extra) {
- struct erts_sys_fd_type *ev = (struct erts_sys_fd_type *)drv_ev;
- if (signo)
- *signo = ev->signo;
- if (extra)
- *extra = ev->extra;
- if (id)
- *id = ev->id;
-}
diff --git a/erts/emulator/sys/ose/erts.sig b/erts/emulator/sys/ose/erts.sig
deleted file mode 100644
index 78b883ee6c..0000000000
--- a/erts/emulator/sys/ose/erts.sig
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef ERTS_OSE_SIGNALS
-#define ERTS_OSE_SIGNALS
-
-#ifndef ERTS_OSE_SIGNAL_BASE
-#define ERTS_OSE_SIGNAL_BASE 0x01900280
-#endif
-
-#define ERTS_SIGNAL_INVALID ERTS_OSE_SIGNAL_BASE
-#define ERTS_SIGNAL_FD_DRV_CONFIG ERTS_OSE_SIGNAL_BASE+1
-#define ERTS_SIGNAL_FD_DRV_ASYNC ERTS_OSE_SIGNAL_BASE+2
-#define ERTS_SIGNAL_OSE_DRV_ATTACH ERTS_OSE_SIGNAL_BASE+3
-#define ERTS_SIGNAL_OSE_DRV_HUNT ERTS_OSE_SIGNAL_BASE+4
-
-#define ERTS_SIGNAL_RUN_ERL_SETUP ERTS_OSE_SIGNAL_BASE+100
-#define ERTS_SIGNAL_RUN_ERL_DAEMON ERTS_OSE_SIGNAL_BASE+101
-
-#endif
diff --git a/erts/emulator/sys/ose/gcc_4.4.3_lm_ppc.lcf b/erts/emulator/sys/ose/gcc_4.4.3_lm_ppc.lcf
deleted file mode 100644
index a19d23facf..0000000000
--- a/erts/emulator/sys/ose/gcc_4.4.3_lm_ppc.lcf
+++ /dev/null
@@ -1,182 +0,0 @@
-/*******************************************************************************
- * Copyright (C) 2013-2014 by Enea Software AB,
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * 3. Neither the name of the copyright holder nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- ******************************************************************************/
-
-OUTPUT_FORMAT("elf32-powerpc", "elf32-powerpc", "elf32-powerpc")
-OUTPUT_ARCH("powerpc")
-ENTRY("crt0_lm")
-MEMORY
-{
- rom : ORIGIN = 0x01000000, LENGTH = 0x01000000
- ram : ORIGIN = 0x02000000, LENGTH = 0x01000000
-}
-PHDRS
-{
- ph_conf PT_LOAD ;
- ph_rom PT_LOAD ;
- ph_ram PT_LOAD ;
-}
-SECTIONS
-{
- .text :
- {
- *(.text_first)
- *(.text)
- *(.text.*)
- *(.stub)
- *(oscode)
- *(.init*)
- *(.fini*)
- *(.gnu.warning)
- *(.gnu.linkonce.t.*)
- *(.glue_7t)
- *(.glue_7)
- } > rom :ph_rom = 0
- .ose_sfk_biosentry :
- {
- *(.ose_sfk_biosentry)
- } > rom :ph_rom
- .ctors :
- {
- __CTOR_LIST__ = .;
- *(.ctors)
- *(SORT(.ctors.*))
- __CTOR_END__ = .;
- } > rom :ph_rom
- .dtors :
- {
- __DTOR_LIST__ = .;
- *(.dtors)
- *(SORT(.dtors.*))
- __DTOR_END__ = .;
- } > rom :ph_rom
- OSESYMS :
- {
- *(.osesyms)
- } > rom :ph_rom
- .rodata :
- {
- *(.rodata)
- *(.rodata.*)
- *(.gnu.linkonce.r.*)
- } > rom :ph_rom
- .eh_frame_hdr :
- {
- *(.eh_frame_hdr)
- } > rom :ph_rom
- .eh_frame :
- {
- __EH_FRAME_BEGIN__ = .;
- *(.eh_frame)
- LONG(0)
- __EH_FRAME_END__ = .;
- } > rom :ph_rom
- .gcc_except_table :
- {
- *(.gcc_except_table .gcc_except_table.*)
- } > rom :ph_rom
- .sdata2 :
- {
- PROVIDE (_SDA2_BASE_ = .);
- *(.sdata2)
- *(.sdata2.*)
- *(.gnu.linkonce.s2.*)
- } > rom :ph_rom
- .sbss2 :
- {
- *(.sbss2)
- *(.sbss2.*)
- *(.gnu.linkonce.sb2.*)
- } > rom :ph_rom
- LMCONF :
- {
- obj/?*?/ose_confd.o(.rodata)
- *(LMCONF)
- } > rom :ph_conf
- .data :
- {
- LONG(0xDEADBABE)
- *(.data)
- *(.data.*)
- *(.gnu.linkonce.d.*)
- SORT(CONSTRUCTORS)
- . = ALIGN(0x10);
- } > ram :ph_ram = 0
- .sdata2 :
- {
- _SDA2_BASE_ = .;
- *(.sdata2 .sdata2.* .gnu.linkonce.s2.*)
- }> ram :ph_ram
- .sdata :
- {
- PROVIDE (_SDA_BASE_ = .);
- *(.sdata)
- *(.sdata.*)
- *(.gnu.linkonce.s.*)
- } > ram :ph_ram
- .sbss :
- {
- *(.sbss)
- *(.sbss.*)
- *(.scommon)
- *(.gnu.linkonce.sb.*)
- } > ram :ph_ram
- .bss (NOLOAD) :
- {
- *(.bss)
- *(.bss.*)
- *(COMMON)
- *(.gnu.linkonce.b.*)
- *(.osvars)
- } > ram :ph_ram
- .ignore (NOLOAD) :
- {
- *(.rel.dyn)
- } > ram :ph_ram
- .debug 0 : { *(.debug) }
- .line 0 : { *(.line) }
- .debug_srcinfo 0 : { *(.debug_srcinfo) }
- .debug_sfnames 0 : { *(.debug_sfnames) }
- .debug_aranges 0 : { *(.debug_aranges) }
- .debug_pubnames 0 : { *(.debug_pubnames) }
- .debug_info 0 : { *(.debug_info) *(.gnu.linkonce.wi.*) }
- .debug_abbrev 0 : { *(.debug_abbrev) }
- .debug_line 0 : { *(.debug_line) }
- .debug_frame 0 : { *(.debug_frame) }
- .debug_str 0 : { *(.debug_str) }
- .debug_loc 0 : { *(.debug_loc) }
- .debug_macinfo 0 : { *(.debug_macinfo) }
- .debug_weaknames 0 : { *(.debug_weaknames) }
- .debug_funcnames 0 : { *(.debug_funcnames) }
- .debug_typenames 0 : { *(.debug_typenames) }
- .debug_varnames 0 : { *(.debug_varnames) }
-}
-__OSESYMS_START = ADDR(OSESYMS);
-__OSESYMS_END = ADDR(OSESYMS) + SIZEOF(OSESYMS);
diff --git a/erts/emulator/sys/ose/gcc_4.6.3_lm_ppc.lcf b/erts/emulator/sys/ose/gcc_4.6.3_lm_ppc.lcf
deleted file mode 100644
index 3440c2961b..0000000000
--- a/erts/emulator/sys/ose/gcc_4.6.3_lm_ppc.lcf
+++ /dev/null
@@ -1,242 +0,0 @@
-/*******************************************************************************
- * Copyright (C) 2013-2014 by Enea Software AB,
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * 3. Neither the name of the copyright holder nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- ******************************************************************************/
-
-OUTPUT_FORMAT("elf32-powerpc", "elf32-powerpc", "elf32-powerpc")
-OUTPUT_ARCH("powerpc")
-
-ENTRY("crt0_lm")
-
-/* Note:
- * You may have to increase the length of the "rom" memory region and the
- * origin and length of the "ram" memory region below depending on the size
- * of the code and data in your load module.
- */
-
-MEMORY
-{
- conf : ORIGIN = 0x00100000, LENGTH = 0x00030000
- rom : ORIGIN = 0x01000000, LENGTH = 0x01000000
- ram : ORIGIN = 0x03000000, LENGTH = 0x01000000
-}
-
-PHDRS
-{
- ph_conf PT_LOAD ;
- ph_rom PT_LOAD ;
- ph_ram PT_LOAD ;
-}
-
-SECTIONS
-{
-/*---------------------------------------------------------------------------
- * Load module configuration area
- *-------------------------------------------------------------------------*/
-
- /* Load module configuration section. */
- LMCONF :
- {
- obj/?*?/ose_confd.o(.rodata)
- *(LMCONF)
- } > conf :ph_conf
-
-/*---------------------------------------------------------------------------
- * Read-only area
- *-------------------------------------------------------------------------*/
-
- /* Code section. */
- .text :
- {
- *(.text)
- *(.text.*)
- *(.stub)
- *(oscode)
- *(.init*)
- *(.fini*)
- *(.gnu.warning)
- *(.gnu.linkonce.t.*)
- } > rom :ph_rom = 0
-
- /* OSE symbols section. */
- OSESYMS :
- {
- *(.osesyms)
- } > rom :ph_rom
-
- /* Read-only data section. */
- .rodata :
- {
- *(.rodata)
- *(.rodata.*)
- *(.gnu.linkonce.r.*)
- } > rom :ph_rom
-
- /* C++ exception handling section. */
- .eh_frame :
- {
- __EH_FRAME_BEGIN__ = .;
- *(.eh_frame)
- LONG(0)
- __EH_FRAME_END__ = .;
- } > rom :ph_rom
-
- /* C++ exception handling section. */
- .gcc_except_table :
- {
- *(.gcc_except_table .gcc_except_table.*)
- } > rom :ph_rom
-
- /* PowerPC EABI initialized read-only data section. */
- .sdata2 :
- {
- PROVIDE (_SDA2_BASE_ = .);
- *(.sdata2)
- *(.sdata2.*)
- *(.gnu.linkonce.s2.*)
- } > rom :ph_rom
-
- /* PowerPC EABI uninitialized read-only data section. */
- .sbss2 :
- {
- *(.sbss2)
- *(.sbss2.*)
- *(.gnu.linkonce.sb2.*)
- } > rom :ph_rom
-
-/*---------------------------------------------------------------------------
- * Read-write area
- *-------------------------------------------------------------------------*/
-
- /*-------------------------------------------------------------------
- * Initialized data (copied by PM)
- *-----------------------------------------------------------------*/
-
- /* Data section. */
- .data :
- {
- *(.data)
- *(.data.*)
- *(.gnu.linkonce.d.*)
- SORT(CONSTRUCTORS)
- } > ram :ph_ram
-
- /* C++ constructor section. */
- .ctors :
- {
- __CTOR_LIST__ = .;
- *(.ctors)
- *(SORT(.ctors.*))
- __CTOR_END__ = .;
- } > ram :ph_ram
-
- /* C++ destructor section. */
- .dtors :
- {
- __DTOR_LIST__ = .;
- *(.dtors)
- *(SORT(.dtors.*))
- __DTOR_END__ = .;
- } > ram :ph_ram
-
-
- /* Small data section. */
- .sdata ALIGN(0x10) :
- {
- PROVIDE (_SDA_BASE_ = .);
- *(.sdata)
- *(.sdata.*)
- *(.gnu.linkonce.s.*)
- } > ram :ph_ram
-
- /*-------------------------------------------------------------------
- * Uninitialized data (cleared by PM)
- *-----------------------------------------------------------------*/
-
- /* Small bss section. */
- .sbss :
- {
- *(.sbss)
- *(.sbss.*)
- *(.scommon)
- *(.gnu.linkonce.sb.*)
- } > ram :ph_ram
-
- /* Bss section. */
- .bss :
- {
- *(.bss)
- *(.bss.*)
- *(COMMON)
- *(.gnu.linkonce.b.*)
- } > ram :ph_ram
-
-/*---------------------------------------------------------------------------
- * Debug information
- *-------------------------------------------------------------------------*/
-
- /*
- * Stabs debug sections.
- */
-
- .stab 0 : { *(.stab) }
- .stabstr 0 : { *(.stabstr) }
- .stab.excl 0 : { *(.stab.excl) }
- .stab.exclstr 0 : { *(.stab.exclstr) }
- .stab.index 0 : { *(.stab.index) }
- .stab.indexstr 0 : { *(.stab.indexstr) }
- .comment 0 : { *(.comment) }
-
- /*
- * DWARF debug sections.
- */
-
- /* DWARF 1 */
- .debug 0 : { *(.debug) }
- .line 0 : { *(.line) }
- /* GNU DWARF 1 extensions */
- .debug_srcinfo 0 : { *(.debug_srcinfo) }
- .debug_sfnames 0 : { *(.debug_sfnames) }
- /* DWARF 1.1 and DWARF 2 */
- .debug_aranges 0 : { *(.debug_aranges) }
- .debug_pubnames 0 : { *(.debug_pubnames) }
- /* DWARF 2 */
- .debug_info 0 : { *(.debug_info) *(.gnu.linkonce.wi.*) }
- .debug_abbrev 0 : { *(.debug_abbrev) }
- .debug_line 0 : { *(.debug_line) }
- .debug_frame 0 : { *(.debug_frame) }
- .debug_str 0 : { *(.debug_str) }
- .debug_loc 0 : { *(.debug_loc) }
- .debug_macinfo 0 : { *(.debug_macinfo) }
- /* SGI/MIPS DWARF 2 extensions */
- .debug_weaknames 0 : { *(.debug_weaknames) }
- .debug_funcnames 0 : { *(.debug_funcnames) }
- .debug_typenames 0 : { *(.debug_typenames) }
- .debug_varnames 0 : { *(.debug_varnames) }
-}
diff --git a/erts/emulator/sys/ose/sys.c b/erts/emulator/sys/ose/sys.c
deleted file mode 100644
index e354faf02c..0000000000
--- a/erts/emulator/sys/ose/sys.c
+++ /dev/null
@@ -1,1847 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2013. 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 "sys/time.h"
-#include "time.h"
-#include "sys/uio.h"
-#include "termios.h"
-#include "ctype.h"
-#include "termios.h"
-
-#ifdef HAVE_FCNTL_H
-#include "fcntl.h"
-#endif
-
-#ifdef HAVE_SYS_IOCTL_H
-#include "sys/ioctl.h"
-#endif
-
-#define ERTS_WANT_BREAK_HANDLING
-#define WANT_NONBLOCKING
-#include "sys.h"
-#include "erl_thr_progress.h"
-
-#ifdef USE_THREADS
-#include "erl_threads.h"
-#endif
-
-#include "erl_mseg.h"
-
-#include "unistd.h"
-#include "efs.h"
-#include "erl_printf.h"
-#include "aio.h"
-#include "pm.h"
-#include "fcntl.h"
-
-/* Set the define to 1 to get some logging */
-#if 0
-#include "ramlog.h"
-#define LOG(output) ramlog_printf output
-#else
-#define LOG(output)
-#endif
-
-extern char **environ;
-static erts_smp_rwmtx_t environ_rwmtx;
-static PROCESS sig_proxy_pid = 0;
-
-#define MAX_VSIZE 16 /* Max number of entries allowed in an I/O
- * vector sock_sendv().
- */
-/*
- * Don't need global.h, but bif_table.h (included by bif.h),
- * won't compile otherwise
- */
-#include "global.h"
-#include "bif.h"
-
-#include "erl_sys_driver.h"
-#include "erl_check_io.h"
-#include "erl_cpu_topology.h"
-
-/* The priority for reader/writer processes */
-#define FD_PROC_PRI get_pri(current_process())
-
-typedef struct ErtsSysReportExit_ ErtsSysReportExit;
-struct ErtsSysReportExit_ {
- ErtsSysReportExit *next;
- Eterm port;
- int pid;
- int ifd;
- int ofd;
- ErlDrvEvent attach_event;
- ErlDrvEvent input_event;
- ErlDrvEvent output_event;
-};
-
-/* This data is shared by these drivers - initialized by spawn_init() */
-static struct driver_data {
- ErlDrvPort port_num;
- int ofd;
- int ifd;
- int packet_bytes;
- ErtsSysReportExit *report_exit;
- int pid;
- int alive;
- int status;
- ErlDrvEvent input_event;
- ErlDrvEvent output_event;
- struct aiocb aiocb;
- FmHandle handle;
- char *install_handle;
-} *driver_data; /* indexed by fd */
-
-struct async {
- SIGSELECT signo;
- ErlDrvTermData port;
- ErlDrvTermData proc;
- PROCESS spid;
- PROCESS target;
- Uint32 ref;
-};
-
-static ErtsSysReportExit *report_exit_list;
-static ERTS_INLINE void report_exit_status(ErtsSysReportExit *rep, int status);
-
-extern int driver_interrupt(int, int);
-extern void do_break(void);
-
-extern void erl_sys_args(int*, char**);
-
-/* The following two defs should probably be moved somewhere else */
-
-extern void erts_sys_init_float(void);
-
-extern void erl_crash_dump(char* file, int line, char* fmt, ...);
-
-#define DIR_SEPARATOR_CHAR '/'
-
-#if defined(DEBUG)
-#define ERL_BUILD_TYPE_MARKER ".debug"
-#else /* opt */
-#define ERL_BUILD_TYPE_MARKER
-#endif
-
-#define CHILD_SETUP_PROG_NAME "child_setup" ERL_BUILD_TYPE_MARKER
-
-#ifdef DEBUG
-static int debug_log = 0;
-#endif
-
-#ifdef ERTS_SMP
-static erts_smp_atomic32_t have_prepared_crash_dump;
-#define ERTS_PREPARED_CRASH_DUMP \
- ((int) erts_smp_atomic32_xchg_nob(&have_prepared_crash_dump, 1))
-#else
-static volatile int have_prepared_crash_dump;
-#define ERTS_PREPARED_CRASH_DUMP \
- (have_prepared_crash_dump++)
-#endif
-
-static erts_smp_atomic_t sys_misc_mem_sz;
-
-#if defined(ERTS_SMP)
-erts_mtx_t chld_stat_mtx;
-#endif
-
-#if defined(ERTS_SMP) /* ------------------------------------------------- */
-#define CHLD_STAT_LOCK erts_mtx_lock(&chld_stat_mtx)
-#define CHLD_STAT_UNLOCK erts_mtx_unlock(&chld_stat_mtx)
-
-#else /* ------------------------------------------------------------------- */
-#define CHLD_STAT_LOCK
-#define CHLD_STAT_UNLOCK
-static volatile int children_died;
-#endif
-
-#define SET_AIO(REQ,FD,SIZE,BUFF) \
- memset(&(REQ),0,sizeof(REQ)); \
- (REQ).aio_fildes = FD; \
- (REQ).aio_offset = FM_POSITION_CURRENT; \
- (REQ).aio_nbytes = SIZE; \
- (REQ).aio_buf = BUFF; \
- (REQ).aio_sigevent.sigev_notify = SIGEV_NONE
-
-/* the first sizeof(struct aiocb *) bytes of the write buffer
- * will contain the pointer to the aiocb struct, this needs
- * to be freed between asynchronous writes.
- * A write of 0 bytes is ignored. */
-#define WRITE_AIO(FD,SIZE,BUFF) do { \
- if (SIZE > 0) { \
- struct aiocb *write_req = driver_alloc(sizeof(struct aiocb)); \
- char *write_buff = driver_alloc((sizeof(char)*SIZE)+1+ \
- (sizeof(struct aiocb *))); \
- *(struct aiocb **)write_buff = (struct aiocb *)write_req; \
- write_buff += sizeof(struct aiocb *); \
- memcpy(write_buff,BUFF,SIZE+1); \
- SET_AIO(*write_req,FD,SIZE,write_buff); \
- if (aio_write(write_req)) \
- ramlog_printf("%s:%d: write failed with %d\n", \
- __FILE__,__LINE__,errno); \
- } \
-} while(0)
-
-/* free the write_buffer and write_req
- * created in the WRITE_AIO() request macro */
-#define FREE_AIO(ptr) do { \
- struct aiocb *aiocb_ptr; \
- char *buffer_ptr; \
- aiocb_ptr = *(struct aiocb **)((ptr)-sizeof(struct aiocb *)); \
- buffer_ptr = (((char*)ptr)-sizeof(struct aiocb *)); \
- driver_free(aiocb_ptr); \
- driver_free(buffer_ptr); \
-} while(0)
-
-#define DISPATCH_AIO(sig) do { \
- if (aio_dispatch(sig)) \
- ramlog_printf("%s:%d: dispatch failed with %d\n", \
- __FILE__,__LINE__,errno); \
- } while(0)
-
-#define AIO_PIPE_SIZE 1024
-
-/* debug print macros */
-#define DEBUG_RES 0
-
-#ifdef DEBUG_RES
-#define DEBUG_CHECK_RES(actual, expected) \
- do { \
- if (actual != expected ) { \
- ramlog_printf("Result check failed" \
- " got: 0x%08x expected:0x%08x\nat: %s:%d\n", \
- actual, expected, __FILE__, __LINE__); \
- abort(); /* This might perhaps be too harsh? */ \
- } \
- } while(0)
-#else
-#define DEBUG_CHECK_RES
-#endif
-
-static struct fd_data {
- char pbuf[4]; /* hold partial packet bytes */
- int psz; /* size of pbuf */
- char *buf;
- char *cpos;
- int sz;
- int remain; /* for input on fd */
-} *fd_data; /* indexed by fd */
-
-/********************* General functions ****************************/
-
-/* This is used by both the drivers and general I/O, must be set early */
-static int max_files = -1;
-
-/*
- * a few variables used by the break handler
- */
-#ifdef ERTS_SMP
-erts_smp_atomic32_t erts_break_requested;
-#define ERTS_SET_BREAK_REQUESTED \
- erts_smp_atomic32_set_nob(&erts_break_requested, (erts_aint32_t) 1)
-#define ERTS_UNSET_BREAK_REQUESTED \
- erts_smp_atomic32_set_nob(&erts_break_requested, (erts_aint32_t) 0)
-#else
-volatile int erts_break_requested = 0;
-#define ERTS_SET_BREAK_REQUESTED (erts_break_requested = 1)
-#define ERTS_UNSET_BREAK_REQUESTED (erts_break_requested = 0)
-#endif
-/* set early so the break handler has access to initial mode */
-static struct termios initial_tty_mode;
-static int replace_intr = 0;
-/* assume yes initially, ttsl_init will clear it */
-int using_oldshell = 1;
-static PROCESS get_signal_proxy_pid(void);
-
-static void
-init_check_io(void)
-{
- erts_init_check_io();
- max_files = erts_check_io_max_files();
-}
-
-#ifdef ERTS_POLL_NEED_ASYNC_INTERRUPT_SUPPORT
-#define ERTS_CHK_IO_AS_INTR() erts_check_io_async_sig_interrupt()
-#else
-#define ERTS_CHK_IO_AS_INTR() erts_check_io_interrupt(1)
-#endif
-#define ERTS_CHK_IO_INTR erts_check_io_interrupt
-#define ERTS_CHK_IO_INTR_TMD erts_check_io_interrupt_timed
-#define ERTS_CHK_IO erts_check_io
-#define ERTS_CHK_IO_SZ erts_check_io_size
-
-
-void
-erts_sys_schedule_interrupt(int set)
-{
- ERTS_CHK_IO_INTR(set);
-}
-
-#ifdef ERTS_SMP
-void
-erts_sys_schedule_interrupt_timed(int set, ErtsMonotonicTime timeout_time)
-{
- ERTS_CHK_IO_INTR_TMD(set, timeout_time);
-}
-#endif
-
-Uint
-erts_sys_misc_mem_sz(void)
-{
- Uint res = ERTS_CHK_IO_SZ();
- res += erts_smp_atomic_read_mb(&sys_misc_mem_sz);
- return res;
-}
-
-/*
- * reset the terminal to the original settings on exit
- */
-void sys_tty_reset(int exit_code)
-{
- if (using_oldshell && !replace_intr) {
- SET_BLOCKING(0);
- }
- else if (isatty(0)) {
- tcsetattr(0,TCSANOW,&initial_tty_mode);
- }
-}
-
-#ifdef USE_THREADS
-
-typedef struct {
- int sched_bind_data;
-} erts_thr_create_data_t;
-
-/*
- * thr_create_prepare() is called in parent thread before thread creation.
- * Returned value is passed as argument to thr_create_cleanup().
- */
-static void *
-thr_create_prepare(void)
-{
- erts_thr_create_data_t *tcdp;
-
- tcdp = erts_alloc(ERTS_ALC_T_TMP, sizeof(erts_thr_create_data_t));
-
- tcdp->sched_bind_data = erts_sched_bind_atthrcreate_prepare();
-
- return (void *) tcdp;
-}
-
-
-/* thr_create_cleanup() is called in parent thread after thread creation. */
-static void
-thr_create_cleanup(void *vtcdp)
-{
- erts_thr_create_data_t *tcdp = (erts_thr_create_data_t *) vtcdp;
-
- erts_sched_bind_atthrcreate_parent(tcdp->sched_bind_data);
-
- erts_free(ERTS_ALC_T_TMP, tcdp);
-}
-
-static void
-thr_create_prepare_child(void *vtcdp)
-{
- erts_thr_create_data_t *tcdp = (erts_thr_create_data_t *) vtcdp;
-
-#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_thread_setup();
-#endif
-
- erts_sched_bind_atthrcreate_child(tcdp->sched_bind_data);
-}
-
-#endif /* #ifdef USE_THREADS */
-
-/* The two functions below are stolen from win_con.c
- They have to use malloc/free/realloc directly becasue
- we want to do able to do erts_printf very early on.
- */
-#define VPRINTF_BUF_INC_SIZE 128
-static erts_dsprintf_buf_t *
-grow_vprintf_buf(erts_dsprintf_buf_t *dsbufp, size_t need)
-{
- char *buf;
- size_t size;
-
- ASSERT(dsbufp);
-
- if (!dsbufp->str) {
- size = (((need + VPRINTF_BUF_INC_SIZE - 1)
- / VPRINTF_BUF_INC_SIZE)
- * VPRINTF_BUF_INC_SIZE);
- buf = (char *) malloc(size * sizeof(char));
- }
- else {
- size_t free_size = dsbufp->size - dsbufp->str_len;
-
- if (need <= free_size)
- return dsbufp;
-
- size = need - free_size + VPRINTF_BUF_INC_SIZE;
- size = (((size + VPRINTF_BUF_INC_SIZE - 1)
- / VPRINTF_BUF_INC_SIZE)
- * VPRINTF_BUF_INC_SIZE);
- size += dsbufp->size;
- buf = (char *) realloc((void *) dsbufp->str,
- size * sizeof(char));
- }
- if (!buf)
- return NULL;
- if (buf != dsbufp->str)
- dsbufp->str = buf;
- dsbufp->size = size;
- return dsbufp;
-}
-
-static int erts_sys_ramlog_printf(char *format, va_list arg_list)
-{
- int res,i;
- erts_dsprintf_buf_t dsbuf = ERTS_DSPRINTF_BUF_INITER(grow_vprintf_buf);
- res = erts_vdsprintf(&dsbuf, format, arg_list);
- if (res >= 0) {
- for (i = 0; i < dsbuf.str_len; i+= 50)
- /* We print 50 characters at a time because otherwise
- the ramlog looks broken */
- ramlog_printf("%.*s",dsbuf.str_len-50 < 0?dsbuf.str_len:50,dsbuf.str+i);
- }
- if (dsbuf.str)
- free((void *) dsbuf.str);
- return res;
-}
-
-void
-erts_sys_pre_init(void)
-{
- erts_printf_add_cr_to_stdout = 1;
- erts_printf_add_cr_to_stderr = 1;
-#ifdef USE_THREADS
- {
- erts_thr_init_data_t eid = ERTS_THR_INIT_DATA_DEF_INITER;
-
- eid.thread_create_child_func = thr_create_prepare_child;
- /* Before creation in parent */
- eid.thread_create_prepare_func = thr_create_prepare;
- /* After creation in parent */
- eid.thread_create_parent_func = thr_create_cleanup,
-
- erts_thr_init(&eid);
-
- report_exit_list = NULL;
-
-#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_init();
-#endif
-
-#if defined(ERTS_SMP)
- erts_mtx_init(&chld_stat_mtx, "child_status");
-#endif
- }
-#ifdef ERTS_SMP
- erts_smp_atomic32_init_nob(&erts_break_requested, 0);
- erts_smp_atomic32_init_nob(&have_prepared_crash_dump, 0);
-#else
- erts_break_requested = 0;
- have_prepared_crash_dump = 0;
-#endif
-#if !defined(ERTS_SMP)
- children_died = 0;
-#endif
-#endif /* USE_THREADS */
-
- erts_printf_stdout_func = erts_sys_ramlog_printf;
-
- erts_smp_atomic_init_nob(&sys_misc_mem_sz, 0);
-}
-
-void
-erl_sys_init(void)
-{
-
-#ifdef USE_SETLINEBUF
- setlinebuf(stdout);
-#else
- setvbuf(stdout, (char *)NULL, _IOLBF, BUFSIZ);
-#endif
-
- erts_sys_init_float();
-
- /* we save this so the break handler can set and reset it properly */
- /* also so that we can reset on exit (break handler or not) */
- if (isatty(0)) {
- tcgetattr(0,&initial_tty_mode);
- }
- tzset(); /* Required at least for NetBSD with localtime_r() */
-}
-
-static ERTS_INLINE int
-prepare_crash_dump(int secs)
-{
-#define NUFBUF (3)
- int i, max;
- char env[21]; /* enough to hold any 64-bit integer */
- size_t envsz;
- /*DeclareTmpHeapNoproc(heap,NUFBUF);*/
- /*Eterm *hp = heap;*/
- /*Eterm list = NIL;*/
- int has_heart = 0;
-
- UseTmpHeapNoproc(NUFBUF);
-
- if (ERTS_PREPARED_CRASH_DUMP)
- return 0; /* We have already been called */
-
-
- /* Positive secs means an alarm must be set
- * 0 or negative means no alarm
- *
- * Set alarm before we try to write to a port
- * we don't want to hang on a port write with
- * no alarm.
- *
- */
-
-#if 0 /*ose TBD!!!*/
- if (secs >= 0) {
- alarm((unsigned int)secs);
- }
-#endif
-
- /* Make sure we unregister at epmd (unknown fd) and get at least
- one free filedescriptor (for erl_crash.dump) */
-
- max = max_files;
- if (max < 1024)
- max = 1024;
- for (i = 3; i < max; i++) {
- close(i);
- }
-
- envsz = sizeof(env);
- i = erts_sys_getenv__("ERL_CRASH_DUMP_NICE", env, &envsz);
- if (i >= 0) {
- int nice_val;
- nice_val = i != 0 ? 0 : atoi(env);
- if (nice_val > 39) {
- nice_val = 39;
- }
- set_pri(nice_val);
- }
-
- UnUseTmpHeapNoproc(NUFBUF);
-#undef NUFBUF
- return has_heart;
-}
-
-int erts_sys_prepare_crash_dump(int secs)
-{
- return prepare_crash_dump(secs);
-}
-
-static ERTS_INLINE void
-break_requested(void)
-{
- /*
- * just set a flag - checked for and handled by
- * scheduler threads erts_check_io() (not signal handler).
- */
-#ifdef DEBUG
- fprintf(stderr,"break!\n");
-#endif
- if (ERTS_BREAK_REQUESTED)
- erts_exit(ERTS_INTR_EXIT, "");
-
- ERTS_SET_BREAK_REQUESTED;
- ERTS_CHK_IO_AS_INTR(); /* Make sure we don't sleep in poll */
-}
-
-/* Disable break */
-void erts_set_ignore_break(void) {
-
-}
-
-/* Don't use ctrl-c for break handler but let it be
- used by the shell instead (see user_drv.erl) */
-void erts_replace_intr(void) {
- struct termios mode;
-
- if (isatty(0)) {
- tcgetattr(0, &mode);
-
- /* here's an example of how to replace ctrl-c with ctrl-u */
- /* mode.c_cc[VKILL] = 0;
- mode.c_cc[VINTR] = CKILL; */
-
- mode.c_cc[VINTR] = 0; /* disable ctrl-c */
- tcsetattr(0, TCSANOW, &mode);
- replace_intr = 1;
- }
-}
-
-void init_break_handler(void)
-{
-
-}
-
-int sys_max_files(void)
-{
- return(max_files);
-}
-
-
-/************************** OS info *******************************/
-
-/* Used by erlang:info/1. */
-/* (This code was formerly in drv.XXX/XXX_os_drv.c) */
-
-char os_type[] = "ose";
-
-void
-os_flavor(char* namebuf, /* Where to return the name. */
- unsigned size) /* Size of name buffer. */
-{
-#if 0
- struct utsname uts; /* Information about the system. */
- char* s;
-
- (void) uname(&uts);
- for (s = uts.sysname; *s; s++) {
- if (isupper((int) *s)) {
- *s = tolower((int) *s);
- }
- }
- strcpy(namebuf, uts.sysname);
-#else
- strncpy(namebuf, "release", size);
-#endif
-}
-
-void
-os_version(pMajor, pMinor, pBuild)
-int* pMajor; /* Pointer to major version. */
-int* pMinor; /* Pointer to minor version. */
-int* pBuild; /* Pointer to build number. */
-{
- *pMajor = 5;
- *pMinor = 7;
- *pBuild = 0;
-}
-
-void init_getenv_state(GETENV_STATE *state)
-{
- erts_smp_rwmtx_rlock(&environ_rwmtx);
- *state = NULL;
-}
-
-char **environ; /*ose - needs replacement*/
-
-char *getenv_string(GETENV_STATE *state0)
-{
- char **state = (char **) *state0;
- char *cp;
-
- ERTS_SMP_LC_ASSERT(erts_smp_lc_rwmtx_is_rlocked(&environ_rwmtx));
-
- if (state == NULL)
- state = environ;
-
- cp = *state++;
- *state0 = (GETENV_STATE) state;
-
- return cp;
-}
-
-void fini_getenv_state(GETENV_STATE *state)
-{
- *state = NULL;
- erts_smp_rwmtx_runlock(&environ_rwmtx);
-}
-
-
-/************************** Port I/O *******************************/
-
-/* I. Common stuff */
-
-union SIGNAL {
- SIGSELECT sig_no;
- struct FmReadPtr fm_read_reply;
- struct FmWritePtr fm_write_reply;
- struct async async;
-};
-
-/* II. The spawn/fd drivers */
-
-/*
- * Decreasing the size of it below 16384 is not allowed.
- */
-#define ERTS_SYS_READ_BUF_SZ (64*1024)
-
-/* Driver interfaces */
-static ErlDrvData spawn_start(ErlDrvPort, char*, SysDriverOpts*);
-static ErlDrvData fd_start(ErlDrvPort, char*, SysDriverOpts*);
-static ErlDrvSSizeT fd_control(ErlDrvData, unsigned int, char *, ErlDrvSizeT,
- char **, ErlDrvSizeT);
-static int spawn_init(void);
-static void fd_stop(ErlDrvData);
-static void erl_stop(ErlDrvData);
-static void ready_input(ErlDrvData, ErlDrvEvent);
-static void ready_output(ErlDrvData, ErlDrvEvent);
-static void output(ErlDrvData, char*, ErlDrvSizeT);
-static void stop_select(ErlDrvEvent, void*);
-
-static PROCESS
-get_signal_proxy_pid(void) {
- union SIGNAL *sig;
- SIGSELECT any_sig[] = {1,ERTS_SIGNAL_OSE_DRV_ATTACH};
-
- if (!sig_proxy_pid) {
- sig = alloc(sizeof(union SIGNAL), ERTS_SIGNAL_OSE_DRV_ATTACH);
- hunt("ose_signal_driver_proxy", 0, NULL, &sig);
- sig = receive(any_sig);
- sig_proxy_pid = sender(&sig);
- free_buf(&sig);
- }
- ASSERT(sig_proxy_pid);
- return sig_proxy_pid;
-}
-
-static ErlDrvOseEventId
-resolve_signal(union SIGNAL* sig) {
- switch(sig->sig_no) {
-
- case FM_READ_PTR_REPLY:
- return (ErlDrvOseEventId)sig->fm_read_reply.handle;
-
- case FM_WRITE_PTR_REPLY:
- return (ErlDrvOseEventId)sig->fm_write_reply.handle;
-
- case ERTS_SIGNAL_OSE_DRV_ATTACH:
- return (ErlDrvOseEventId)sig->async.target;
-
- default:
- break;
- }
- return (ErlDrvOseEventId)-1;
-}
-
-struct erl_drv_entry spawn_driver_entry = {
- spawn_init,
- spawn_start,
- NULL, /* erl_stop, */
- output,
- ready_input,
- ready_output,
- "spawn",
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- ERL_DRV_EXTENDED_MARKER,
- ERL_DRV_EXTENDED_MAJOR_VERSION,
- ERL_DRV_EXTENDED_MINOR_VERSION,
- ERL_DRV_FLAG_USE_PORT_LOCKING,
- NULL, NULL,
- stop_select
-};
-struct erl_drv_entry fd_driver_entry = {
- NULL,
- fd_start,
- fd_stop,
- output,
- ready_input,
- ready_output,
- "fd",
- NULL,
- NULL,
- fd_control,
- NULL,
- NULL,
- NULL, /* ready_async */
- NULL, /* flush */
- NULL, /* call */
- NULL, /* event */
- ERL_DRV_EXTENDED_MARKER,
- ERL_DRV_EXTENDED_MAJOR_VERSION,
- ERL_DRV_EXTENDED_MINOR_VERSION,
- 0, /* ERL_DRV_FLAGs */
- NULL, /* handle2 */
- NULL, /* process_exit */
- stop_select
-};
-
-static void
-set_spawn_fd(int local_fd, int remote_fd, PROCESS remote_pid) {
- PROCESS vm_pid;
- FmHandle handle;
- char env_val[55];
- char env_name[10];
- EfsStatus efs_res;
-
- /* get pid of pipevm and handle of chosen fd */
- efs_res = efs_examine_fd(local_fd, FLIB_FD_VMPID, &vm_pid, 0);
- DEBUG_CHECK_RES(efs_res, EFS_SUCCESS);
-
- /* setup the file descriptor to buffer per line */
- efs_res = efs_config_fd(local_fd, FLIB_FD_BUFMODE, FM_BUFF_LINE,
- FLIB_FD_BUFSIZE, 80, 0);
- DEBUG_CHECK_RES(efs_res, EFS_SUCCESS);
-
- /* duplicate handle and set spawn pid owner */
- efs_res = efs_dup_to(local_fd, remote_pid, &handle);
- DEBUG_CHECK_RES(efs_res, EFS_SUCCESS);
-
- sprintf(env_name, "FD%d", remote_fd);
-
- /* Syntax of the environment variable:
- * "FD#" "<pid of pipevm>,<handle>,<buffer mode>,<buff size>,<omode>" */
- sprintf(env_val, "0x%lx,0x%lx,%lu,%lu,0x%x",
- vm_pid, handle,
- FM_BUFF_LINE, 80,
- O_APPEND);
-
- set_env(remote_pid, env_name, env_val);
-}
-
-static ErlDrvData
-set_driver_data(ErlDrvPort port_num,
- int ifd,
- int ofd,
- int packet_bytes,
- int read_write,
- int exit_status,
- PROCESS pid)
-{
- Port *prt;
- ErtsSysReportExit *report_exit;
-
- prt = erts_drvport2port(port_num);
- if (prt != ERTS_INVALID_ERL_DRV_PORT) {
- prt->os_pid = pid;
- }
-
- /* READ */
- if (read_write & DO_READ) {
- EfsStatus res = efs_examine_fd(ifd, FLIB_FD_HANDLE,
- &driver_data[ifd].handle, 0);
- if (res != EFS_SUCCESS)
- ramlog_printf("%s:%d: efs_examine_fd(%d) failed with %d\n",
- __FILE__,__LINE__,ifd,errno);
- driver_data[ifd].ifd = ifd;
- driver_data[ifd].packet_bytes = packet_bytes;
- driver_data[ifd].port_num = port_num;
- driver_data[ifd].pid = pid;
-
- /* async read struct */
- memset(&driver_data[ifd].aiocb, 0, sizeof(struct aiocb));
- driver_data[ifd].aiocb.aio_buf = driver_alloc(AIO_PIPE_SIZE);
- driver_data[ifd].aiocb.aio_fildes = ifd;
- driver_data[ifd].aiocb.aio_nbytes = (packet_bytes?packet_bytes:AIO_PIPE_SIZE);
- driver_data[ifd].alive = 1;
- driver_data[ifd].status = 0;
- driver_data[ifd].input_event =
- erl_drv_ose_event_alloc(FM_READ_PTR_REPLY,
- driver_data[ifd].handle, resolve_signal,
- &driver_data[ifd].ifd);
-
- /* READ & WRITE */
- if (read_write & DO_WRITE) {
- driver_data[ifd].ofd = ofd;
- efs_examine_fd(ofd, FLIB_FD_HANDLE, &driver_data[ofd].handle, 0);
-
- driver_data[ifd].output_event =
- erl_drv_ose_event_alloc(FM_WRITE_PTR_REPLY,
- driver_data[ofd].handle, resolve_signal,
- &driver_data[ofd].ofd);
- driver_data[ofd].pid = pid;
- if (ifd != ofd) {
- driver_data[ofd] = driver_data[ifd];
- driver_data[ofd].aiocb.aio_buf = NULL;
- }
- }
- else { /* READ ONLY */
- driver_data[ifd].ofd = -1;
- }
-
- /* enable input event */
- (void) driver_select(port_num, driver_data[ifd].input_event,
- (ERL_DRV_READ | ERL_DRV_USE), 1);
-
- if (aio_read(&driver_data[ifd].aiocb))
- ramlog_printf("%s:%d: aio_read(%d) failed with %d\n",
- __FILE__,__LINE__,ifd,errno);
- }
- else { /* WRITE ONLY */
- efs_examine_fd(ofd, FLIB_FD_HANDLE, &driver_data[ofd].handle, 0);
- driver_data[ofd].packet_bytes = packet_bytes;
- driver_data[ofd].port_num = port_num;
- driver_data[ofd].ofd = ofd;
- driver_data[ofd].pid = pid;
- driver_data[ofd].alive = 1;
- driver_data[ofd].status = 0;
- driver_data[ofd].output_event =
- erl_drv_ose_event_alloc(FM_WRITE_PTR_REPLY, driver_data[ofd].handle,
- resolve_signal, &driver_data[ofd].ofd);
- driver_data[ofd].input_event = driver_data[ofd].output_event;
- }
-
- /* this is used for spawned load modules, and is needed
- * to properly uninstall them */
- if (exit_status) {
- struct PmProgramInfo *info;
- int install_handle_size;
- union SIGNAL *sig;
- PmStatus pm_status;
- report_exit = erts_alloc(ERTS_ALC_T_PRT_REP_EXIT,
- sizeof(ErtsSysReportExit));
- report_exit->next = report_exit_list;
- report_exit->port = erts_drvport2id(port_num);
- report_exit->pid = pid;
- report_exit->ifd = (read_write & DO_READ) ? ifd : -1;
- report_exit->ofd = (read_write & DO_WRITE) ? ofd : -1;
- report_exit_list = report_exit;
- report_exit->attach_event =
- erl_drv_ose_event_alloc(ERTS_SIGNAL_OSE_DRV_ATTACH, pid,
- resolve_signal, &driver_data[ifd].ifd);
-
- /* setup ifd and ofd report exit */
- driver_data[ifd].report_exit = report_exit;
- driver_data[ofd].report_exit = report_exit;
-
- pm_status = ose_pm_program_info(pid, &info);
- DEBUG_CHECK_RES(pm_status, PM_SUCCESS);
-
- install_handle_size = strlen(info->install_handle)+1;
- driver_data[ifd].install_handle = driver_alloc(install_handle_size);
- strcpy(driver_data[ifd].install_handle,
- info->install_handle);
-
- free_buf((union SIGNAL **)&info);
-
- sig = alloc(sizeof(struct async), ERTS_SIGNAL_OSE_DRV_ATTACH);
- sig->async.target = pid;
- send(&sig, get_signal_proxy_pid());
-
- /* this event will trigger when we receive an attach signal
- * from the recently dead load module */
- (void)driver_select(port_num,report_exit->attach_event, DO_READ, 1);
- }
- else {
- report_exit = NULL;
- }
-
- /* the return value is the pointer to the driver_data struct we created
- * in this function, it will be used in the drivers input
- * and output functions */
- return (ErlDrvData)((!(read_write & DO_READ) && read_write & DO_WRITE)
- ? &driver_data[ofd]
- : &driver_data[ifd]);
-}
-
-static int spawn_init()
-{
- int i;
-
- driver_data = (struct driver_data *)
- erts_alloc(ERTS_ALC_T_DRV_TAB, max_files * sizeof(struct driver_data));
- erts_smp_atomic_add_nob(&sys_misc_mem_sz,
- max_files * sizeof(struct driver_data));
-
- for (i = 0; i < max_files; i++)
- driver_data[i].pid = -1;
-
- return 1;
-}
-
-static void
-init_fd_data(int fd, ErlDrvPort port_num)
-{
- fd_data[fd].buf = NULL;
- fd_data[fd].cpos = NULL;
- fd_data[fd].remain = 0;
- fd_data[fd].sz = 0;
- fd_data[fd].psz = 0;
-}
-
-/* FIXME write a decent text on pipes on ose */
-static ErlDrvData
-spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts)
-{
- int ifd[2];
- int ofd[2];
- static uint32_t ticker = 1;
- PmStatus pm_status;
- OSDOMAIN domain = PM_NEW_DOMAIN;
- PROCESS progpid, mainbid, mainpid;
- char *handle = NULL;
- struct PmProgramInfo *info;
- char *args = NULL;
- char *tmp_handle;
- ErlDrvData res = (ErlDrvData)-1;
- int handle_size;
- char *ptr;
-
-
- args = driver_alloc(strlen(name)+1);
- strcpy(args, name);
- /* We need to handle name in three parts
- * - install handle (must be unique)
- * - install binary (needed for ose_pm_install_load_module())
- * - full path (as argument to the spawned applications env.var
- */
-
- /* full path including arguments */
- args = driver_alloc(strlen(name)+1);
- strcpy(args, name);
-
- /* handle path */
- tmp_handle = strrchr(name, '/');
- if (tmp_handle == NULL) {
- tmp_handle = name;
- }
- else {
- tmp_handle++;
- }
-
- /* handle args */
- ptr = strchr(tmp_handle, ' ');
- if (ptr != NULL) {
- *ptr = '\0';
- handle_size = ptr - tmp_handle;
- }
- else {
- handle_size = strlen(name)+1;
- }
-
- /* make room for ticker */
- handle_size += (ticker<10)?3:((ticker<100)?4:5);
- handle = driver_alloc(handle_size);
-
- do {
- snprintf(handle, handle_size, "%s_%d", tmp_handle, ticker);
- pm_status = ose_pm_install_load_module(0, "ELF", name, handle,
- 0, 0, NULL);
- ticker++;
- } while (pm_status == PM_EINSTALL_HANDLE_ALREADY_INSTALLED);
-
- if (pm_status != PM_SUCCESS) {
- errno = ENOSYS; /* FIXME add comment */
- return ERL_DRV_ERROR_ERRNO;
- }
-
- /* Create Program */
- pm_status = ose_pm_create_program(&domain, handle, 0, 0,
- NULL, &progpid, &mainbid);
- DEBUG_CHECK_RES(pm_status, PM_SUCCESS);
-
- /* Get the mainpid from the newly created program */
- pm_status = ose_pm_program_info(progpid, &info);
- DEBUG_CHECK_RES(pm_status, PM_SUCCESS);
-
- mainpid = info->main_process;
- free_buf ((union SIGNAL **)&info);
-
- /* pipevm needs to be started
- * pipe will return 0 if success, -1 if not,
- * errno will be set */
- if (pipe(ifd) != 0 || pipe(ofd) != 0) {
- DEBUG_CHECK_RES(0, -1);
- ASSERT(0);
- }
-
- /* setup driver data */
- res = set_driver_data(port_num, ofd[0], ifd[1], opts->packet_bytes,
- opts->read_write, 1 /* opts->exit_status */, progpid);
-
- /* init the fd_data array for read/write */
- init_fd_data(ofd[0], port_num);
- init_fd_data(ifd[1], port_num);
-
- /* setup additional configurations
- * for the spawned applications environment */
- if (args != NULL) {
- set_env(progpid, "ARGV", args);
- }
- set_env(mainbid, "EFS_RESOLVE_TMO", 0);
- set_spawn_fd(ifd[0], 0, mainpid);
- set_spawn_fd(ofd[1], 1, mainpid);
- set_spawn_fd(ofd[1], 2, mainpid);
-
- /* start the spawned program */
- pm_status = ose_pm_start_program(mainbid);
- DEBUG_CHECK_RES(pm_status, PM_SUCCESS);
-
- /* close unused fd's */
- close(ifd[0]);
- close(ofd[1]);
-
- if (handle) {
- driver_free(handle);
- }
-
- return (ErlDrvData)res;
-}
-
-#define FD_DEF_HEIGHT 24
-#define FD_DEF_WIDTH 80
-/* Control op */
-#define FD_CTRL_OP_GET_WINSIZE 100
-
-static int fd_get_window_size(int fd, Uint32 *width, Uint32 *height)
-{
-#ifdef TIOCGWINSZ
- struct winsize ws;
- if (ioctl(fd,TIOCGWINSZ,&ws) == 0) {
- *width = (Uint32) ws.ws_col;
- *height = (Uint32) ws.ws_row;
- return 0;
- }
-#endif
- return -1;
-}
-
-static ErlDrvSSizeT fd_control(ErlDrvData drv_data,
- unsigned int command,
- char *buf, ErlDrvSizeT len,
- char **rbuf, ErlDrvSizeT rlen)
-{
- struct driver_data *data = (struct driver_data *)drv_data;
- char resbuff[2*sizeof(Uint32)];
- switch (command) {
- case FD_CTRL_OP_GET_WINSIZE:
- {
- Uint32 w,h;
- if (fd_get_window_size(data->ifd,&w,&h))
- return 0;
- memcpy(resbuff,&w,sizeof(Uint32));
- memcpy(resbuff+sizeof(Uint32),&h,sizeof(Uint32));
- }
- break;
- default:
- return 0;
- }
- if (rlen < 2*sizeof(Uint32)) {
- *rbuf = driver_alloc(2*sizeof(Uint32));
- }
- memcpy(*rbuf,resbuff,2*sizeof(Uint32));
- return 2*sizeof(Uint32);
-}
-
-static ErlDrvData fd_start(ErlDrvPort port_num, char* name,
- SysDriverOpts* opts)
-{
- ErlDrvData res;
-
- CHLD_STAT_LOCK;
- if (opts->read_write & DO_READ) {
- init_fd_data(opts->ifd, port_num);
- }
- if (opts->read_write & DO_WRITE) {
- init_fd_data(opts->ofd, port_num);
- }
- res = set_driver_data(port_num, opts->ifd, opts->ofd,
- opts->packet_bytes,
- opts->read_write, 0, -1);
- CHLD_STAT_UNLOCK;
- return res;
-}
-
-static void clear_fd_data(int fd)
-{
- if (fd_data[fd].sz > 0) {
- erts_free(ERTS_ALC_T_FD_ENTRY_BUF, (void *) fd_data[fd].buf);
- ASSERT(erts_smp_atomic_read_nob(&sys_misc_mem_sz) >= fd_data[fd].sz);
- erts_smp_atomic_add_nob(&sys_misc_mem_sz, -1*fd_data[fd].sz);
- }
- fd_data[fd].buf = NULL;
- fd_data[fd].sz = 0;
- fd_data[fd].remain = 0;
- fd_data[fd].cpos = NULL;
- fd_data[fd].psz = 0;
-}
-
-static void nbio_stop_fd(ErlDrvPort prt, ErlDrvEvent ev)
-{
- int *fd;
- driver_select(prt,ev,DO_READ|DO_WRITE,0);
- erl_drv_ose_event_fetch(ev, NULL, NULL, (void **)&fd);
- clear_fd_data(*fd);
- SET_BLOCKING(*fd);
-}
-
-static void fd_stop(ErlDrvData drv_data) /* Does not close the fds */
-{
- struct driver_data *data = (struct driver_data *)drv_data;
-
- if (data->ofd != -1) {
- if (data->ifd != data->ofd) { /* read and write */
- nbio_stop_fd(data->port_num, data->input_event);
- nbio_stop_fd(data->port_num, data->output_event);
- }
- else { /* write only */
- nbio_stop_fd(data->port_num, data->output_event);
- }
- }
- else { /* read only */
- nbio_stop_fd(data->port_num, data->input_event);
- }
-}
-
-
-static void erl_stop(ErlDrvData drv_data)
-{
- struct driver_data *data = (struct driver_data *)drv_data;
-
- CHLD_STAT_LOCK;
- data->pid = -1;
- CHLD_STAT_UNLOCK;
-
- if (data->ofd != -1) {
- if (data->ifd != data->ofd) { /* read and write */
- nbio_stop_fd(data->port_num, data->input_event);
- nbio_stop_fd(data->port_num, data->output_event);
- }
- else { /* write only */
- nbio_stop_fd(data->port_num, data->output_event);
- }
- }
- else { /* read only */
- nbio_stop_fd(data->port_num, data->input_event);
- }
- close(data->ifd);
- close(data->ofd);
-}
-
-/* The parameter e is a pointer to the driver_data structure
- * related to the fd to be used as output */
-static void output(ErlDrvData drv_data, char* buf, ErlDrvSizeT len)
-{
- ErlDrvSizeT sz;
- char lb[4];
- char* lbp;
- struct driver_data *data = (struct driver_data *)drv_data;
-
- if (((data->packet_bytes == 2) &&
- (len > 0xffff)) || (data->packet_bytes == 1 && len > 0xff)) {
- driver_failure_posix(data->port_num, EINVAL);
- return; /* -1; */
- }
- put_int32(len, lb);
- lbp = lb + (4-(data->packet_bytes));
-
- if ((sz = driver_sizeq(data->port_num)) > 0) {
- if (data->packet_bytes != 0) {
- driver_enq(data->port_num, lbp, data->packet_bytes);
- }
- driver_enq(data->port_num, buf, len);
-
- if (sz + len + data->packet_bytes >= (1 << 13))
- set_busy_port(data->port_num, 1);
- }
- else {
- char *pbbuf;
- if (data->packet_bytes != 0) {
- pbbuf = malloc(len + data->packet_bytes);
- int i;
- for (i = 0; i < data->packet_bytes; i++) {
- *pbbuf++ = *lbp++;
- }
- strncpy(pbbuf, buf, len);
- pbbuf -= data->packet_bytes;
- }
- driver_select(data->port_num, data->output_event,
- ERL_DRV_WRITE|ERL_DRV_USE, 1);
- WRITE_AIO(data->ofd,
- (data->packet_bytes ? len+data->packet_bytes : len),
- (data->packet_bytes ? pbbuf : buf));
- if (data->packet_bytes != 0) free(pbbuf);
- }
- return; /* 0; */
-}
-
-/* This function is being run when we in recieve
- * either a read of 0 bytes, or the attach signal from a dying
- * spawned load module */
-static int port_inp_failure(ErlDrvPort port_num, ErlDrvEvent ready_fd, int res)
- /* Result: 0 (eof) or -1 (error) */
-{
- int *fd;
- SIGSELECT sig_no;
- ASSERT(res <= 0);
-
- erl_drv_ose_event_fetch(ready_fd,&sig_no, NULL, (void **)&fd);
- /* As we need to handle two signals, we do this in two steps */
- if (driver_data[*fd].alive) {
- report_exit_status(driver_data[*fd].report_exit, 0); /* status? */
- }
- else {
- driver_select(port_num,ready_fd,DO_READ|DO_WRITE,0);
- clear_fd_data(*fd);
- driver_report_exit(driver_data[*fd].port_num, driver_data[*fd].status);
- /* As we do not really know if the spawn has crashed or exited nicely
- * we do not check the result status of the following call.. FIXME
- * can we handle this in a better way? */
- ose_pm_uninstall_load_module(driver_data[*fd].install_handle);
- driver_free(driver_data[*fd].install_handle);
- driver_free((void *)driver_data[*fd].aiocb.aio_buf);
-
- close(*fd);
- }
-
- return 0;
-}
-
-/* The parameter e is a pointer to the driver_data structure
- * related to the fd to be used as output.
- * ready_fd is the event that triggered this call to ready_input */
-static void ready_input(ErlDrvData drv_data, ErlDrvEvent ready_fd)
-{
- int res;
- Uint h;
- char *buf;
- union SIGNAL *sig;
- struct driver_data *data = (struct driver_data *)drv_data;
-
- sig = erl_drv_ose_get_signal(ready_fd);
- ASSERT(sig);
-
-
- while (sig) {
- /* If we've recieved an attach signal, we need to handle
- * it in port_inp_failure */
- if (sig->sig_no == ERTS_SIGNAL_OSE_DRV_ATTACH) {
- port_inp_failure(data->port_num, ready_fd, 0);
- }
- else {
- res = sig->fm_read_reply.actual;
- if (res == 0) {
- port_inp_failure(data->port_num, ready_fd, res);
- break;
- }
-
- if (data->packet_bytes == 0) {
- if (res < 0) {
- if ((errno != EINTR) && (errno != ERRNO_BLOCK)) {
- port_inp_failure(data->port_num, ready_fd, res);
- }
- }
- else if (res == 0) {
- /* read of 0 bytes, eof, otherside of pipe is assumed dead */
- port_inp_failure(data->port_num, ready_fd, res);
- break;
- }
- else {
- buf = driver_alloc(res);
- memcpy(buf, (void *)data->aiocb.aio_buf, res);
- driver_select(data->port_num, data->output_event,
- ERL_DRV_WRITE|ERL_DRV_USE, 1);
- driver_output(data->port_num, (char*) buf, res);
- driver_free(buf);
- }
- /* clear the previous read */
- memset(data->aiocb.aio_buf, 0, res);
-
- /* issue a new read */
- DISPATCH_AIO(sig);
- aio_read(&data->aiocb);
- }
- else if (data->packet_bytes && fd_data[data->ifd].remain > 0) {
- /* we've read a partial package, or a header */
-
- if (res == fd_data[data->ifd].remain) { /* we are done! */
- char *buf = data->aiocb.aio_buf;
- int i;
-
- /* do we have anything buffered? */
- if (fd_data[data->ifd].buf != NULL) {
- memcpy(fd_data[data->ifd].buf + fd_data[data->ifd].sz,
- buf, res);
- buf = fd_data[data->ifd].buf;
- }
-
- fd_data[data->ifd].sz += res;
- driver_output(data->port_num, buf, (fd_data[data->ifd].sz>0?fd_data[data->ifd].sz:res));
- clear_fd_data(data->ifd);
-
- /* clear the previous read */
- memset(data->aiocb.aio_buf, 0, res);
-
- /* issue a new read */
- DISPATCH_AIO(sig);
- data->aiocb.aio_nbytes = data->packet_bytes;
-
- if (data->aiocb.aio_buf == NULL) {
- port_inp_failure(data->port_num, ready_fd, -1);
- }
- aio_read(&data->aiocb);
- }
- else if(res < fd_data[data->ifd].remain) { /* received part of a package */
- if (fd_data[data->ifd].sz == 0) {
-
- fd_data[data->ifd].sz += res;
- memcpy(fd_data[data->ifd].buf, data->aiocb.aio_buf, res);
- fd_data[data->ifd].remain -= res;
- }
- else {
- memcpy(fd_data[data->ifd].buf + fd_data[data->ifd].sz,
- data->aiocb.aio_buf, res);
- fd_data[data->ifd].sz += res;
- fd_data[data->ifd].remain -= res;
- }
- /* clear the previous read */
- memset(data->aiocb.aio_buf, 0, res);
-
- /* issue a new read */
- DISPATCH_AIO(sig);
- data->aiocb.aio_nbytes = fd_data[data->ifd].remain;
-
- if (data->aiocb.aio_buf == NULL) {
- port_inp_failure(data->port_num, ready_fd, -1);
- }
- aio_read(&data->aiocb);
- }
- }
- else if (data->packet_bytes && fd_data[data->ifd].remain == 0) { /* we've recieved a header */
-
- /* analyze the header FIXME */
- switch (data->packet_bytes) {
- case 1: h = get_int8(data->aiocb.aio_buf); break;
- case 2: h = get_int16(data->aiocb.aio_buf); break;
- case 4: h = get_int32(data->aiocb.aio_buf); break;
- }
-
- fd_data[data->ifd].buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h + data->packet_bytes);
- fd_data[data->ifd].remain = ((h + data->packet_bytes) - res);
-
- /* clear the previous read */
- memset(data->aiocb.aio_buf, 0, data->packet_bytes);
-
- /* issue a new read */
- DISPATCH_AIO(sig);
- data->aiocb.aio_nbytes = h;
-
- if (data->aiocb.aio_buf == NULL) {
- port_inp_failure(data->port_num, ready_fd, -1);
- }
- aio_read(&data->aiocb);
- }
- }
- sig = erl_drv_ose_get_signal(ready_fd);
- }
-}
-
-
-/* The parameter e is a pointer to the driver_data structure
- * related to the fd to be used as output.
- * ready_fd is the event that triggered this call to ready_input */
-static void ready_output(ErlDrvData drv_data, ErlDrvEvent ready_fd)
-{
- SysIOVec *iov;
- int vlen;
- int res;
- union SIGNAL *sig;
- struct driver_data *data = (struct driver_data *)drv_data;
-
- sig = erl_drv_ose_get_signal(ready_fd);
- ASSERT(sig);
-
- while (sig != NULL) {
- if (sig->fm_write_reply.actual <= 0) {
- int status;
-
- status = efs_status_to_errno(sig->fm_write_reply.status);
- driver_select(data->port_num, ready_fd, ERL_DRV_WRITE, 0);
- DISPATCH_AIO(sig);
- FREE_AIO(sig->fm_write_reply.buffer);
-
- driver_failure_posix(data->port_num, status);
- }
- else { /* written bytes > 0 */
- iov = driver_peekq(data->port_num, &vlen);
- if (vlen > 0) {
- DISPATCH_AIO(sig);
- FREE_AIO(sig->fm_write_reply.buffer);
- res = driver_deq(data->port_num, iov[0].iov_len);
- if (res > 0) {
- iov = driver_peekq(data->port_num, &vlen);
- WRITE_AIO(data->ofd, iov[0].iov_len, iov[0].iov_base);
- }
- }
- else if (vlen == 0) {
- DISPATCH_AIO(sig);
- FREE_AIO(sig->fm_write_reply.buffer);
- }
-
- }
- sig = erl_drv_ose_get_signal(ready_fd);
- }
-}
-
-static void stop_select(ErlDrvEvent ready_fd, void* _)
-{
- int *fd;
- erl_drv_ose_event_fetch(ready_fd, NULL, NULL, (void **)&fd);
- erl_drv_ose_event_free(ready_fd);
- close(*fd);
-}
-
-
-void erts_do_break_handling(void)
-{
- struct termios temp_mode;
- int saved = 0;
-
- /*
- * Most functions that do_break() calls are intentionally not thread safe;
- * therefore, make sure that all threads but this one are blocked before
- * proceeding!
- */
- erts_smp_thr_progress_block();
-
- /* during break we revert to initial settings */
- /* this is done differently for oldshell */
- if (using_oldshell && !replace_intr) {
- SET_BLOCKING(1);
- }
- else if (isatty(0)) {
- tcgetattr(0,&temp_mode);
- tcsetattr(0,TCSANOW,&initial_tty_mode);
- saved = 1;
- }
-
- /* call the break handling function, reset the flag */
- do_break();
-
- fflush(stdout);
-
- /* after break we go back to saved settings */
- if (using_oldshell && !replace_intr) {
- SET_NONBLOCKING(1);
- }
- else if (saved) {
- tcsetattr(0,TCSANOW,&temp_mode);
- }
-
- erts_smp_thr_progress_unblock();
-}
-
-static pid_t
-getpid(void)
-{
- return get_bid(current_process());
-}
-
-int getpagesize(void)
-{
- return 1024;
-}
-
-
-/* Fills in the systems representation of the jam/beam process identifier.
-** The Pid is put in STRING representation in the supplied buffer,
-** no interpretatione of this should be done by the rest of the
-** emulator. The buffer should be at least 21 bytes long.
-*/
-void sys_get_pid(char *buffer, size_t buffer_size){
- pid_t p = getpid();
- /* Assume the pid is scalar and can rest in an unsigned long... */
- erts_snprintf(buffer, buffer_size, "%lu",(unsigned long) p);
-}
-
-int
-erts_sys_putenv_raw(char *key, char *value) {
- return erts_sys_putenv(key, value);
-}
-int
-erts_sys_putenv(char *key, char *value)
-{
- int res;
-
- erts_smp_rwmtx_rwlock(&environ_rwmtx);
- res = set_env(get_bid(current_process()), key,
- value);
- erts_smp_rwmtx_rwunlock(&environ_rwmtx);
- return res;
-}
-
-
-int
-erts_sys_unsetenv(char *key)
-{
- int res;
-
- erts_smp_rwmtx_rwlock(&environ_rwmtx);
- res = set_env(get_bid(current_process()),key,NULL);
- erts_smp_rwmtx_rwunlock(&environ_rwmtx);
-
- return res;
-}
-
-int
-erts_sys_getenv__(char *key, char *value, size_t *size)
-{
- int res;
- char *orig_value = get_env(get_bid(current_process()), key);
- if (!orig_value)
- res = -1;
- else {
- size_t len = sys_strlen(orig_value);
- if (len >= *size) {
- *size = len + 1;
- res = 1;
- }
- else {
- *size = len;
- sys_memcpy((void *) value, (void *) orig_value, len+1);
- res = 0;
- }
- free_buf((union SIGNAL **)&orig_value);
- }
- return res;
-}
-
-int
-erts_sys_getenv_raw(char *key, char *value, size_t *size) {
- return erts_sys_getenv(key, value, size);
-}
-
-/*
- * erts_sys_getenv
- * returns:
- * -1, if environment key is not set with a value
- * 0, if environment key is set and value fits into buffer res
- * 1, if environment key is set but does not fit into buffer res
- * res is set with the needed buffer res value
- */
-
-int
-erts_sys_getenv(char *key, char *value, size_t *size)
-{
- int res;
- erts_smp_rwmtx_rlock(&environ_rwmtx);
- res = erts_sys_getenv__(key, value, size);
- erts_smp_rwmtx_runlock(&environ_rwmtx);
- return res;
-}
-
-void
-sys_init_io(void)
-{
- fd_data = (struct fd_data *)
- erts_alloc(ERTS_ALC_T_FD_TAB, max_files * sizeof(struct fd_data));
- erts_smp_atomic_add_nob(&sys_misc_mem_sz,
- max_files * sizeof(struct fd_data));
-}
-
-extern const char pre_loaded_code[];
-extern Preload pre_loaded[];
-
-void erts_sys_alloc_init(void)
-{
-}
-
-void *erts_sys_alloc(ErtsAlcType_t t, void *x, Uint sz)
-{
- void *res = malloc((size_t) sz);
-#if HAVE_ERTS_MSEG
- if (!res) {
- erts_mseg_clear_cache();
- return malloc((size_t) sz);
- }
-#endif
- return res;
-}
-
-void *erts_sys_realloc(ErtsAlcType_t t, void *x, void *p, Uint sz)
-{
- void *res = realloc(p, (size_t) sz);
-#if HAVE_ERTS_MSEG
- if (!res) {
- erts_mseg_clear_cache();
- return realloc(p, (size_t) sz);
- }
-#endif
- return res;
-}
-
-void erts_sys_free(ErtsAlcType_t t, void *x, void *p)
-{
- free(p);
-}
-
-/* Return a pointer to a vector of names of preloaded modules */
-
-Preload*
-sys_preloaded(void)
-{
- return pre_loaded;
-}
-
-/* Return a pointer to preloaded code for module "module" */
-unsigned char*
-sys_preload_begin(Preload* p)
-{
- return p->code;
-}
-
-/* Clean up if allocated */
-void sys_preload_end(Preload* p)
-{
- /* Nothing */
-}
-
-/* Read a key from console (?) */
-
-int sys_get_key(fd)
-int fd;
-{
- int c;
- unsigned char rbuf[64];
-
- fflush(stdout); /* Flush query ??? */
-
- if ((c = read(fd,rbuf,64)) <= 0) {
- return c;
- }
-
- return rbuf[0];
-}
-
-
-#ifdef DEBUG
-
-extern int erts_initialized;
-void
-erl_assert_error(const char* expr, const char* func,
- const char* file, int line)
-{
- fflush(stdout);
- fprintf(stderr, "%s:%d:%s() Assertion failed: %s\n",
- file, line, func, expr);
- fflush(stderr);
- ramlog_printf("%s:%d:%s() Assertion failed: %s\n",
- file, line, func, expr);
-
- abort();
-}
-
-void
-erl_debug(char* fmt, ...)
-{
- char sbuf[1024]; /* Temporary buffer. */
- va_list va;
-
- if (debug_log) {
- va_start(va, fmt);
- vsprintf(sbuf, fmt, va);
- va_end(va);
- fprintf(stderr, "%s", sbuf);
- }
-}
-
-#endif /* DEBUG */
-
-static ERTS_INLINE void
-report_exit_status(ErtsSysReportExit *rep, int status)
-{
- if (rep->ifd >= 0) {
- driver_data[rep->ifd].alive = 0;
- driver_data[rep->ifd].status = status;
- }
- if (rep->ofd >= 0) {
- driver_data[rep->ofd].alive = 0;
- driver_data[rep->ofd].status = status;
- }
-
- erts_free(ERTS_ALC_T_PRT_REP_EXIT, rep);
-}
-
-#define ERTS_REPORT_EXIT_STATUS report_exit_status
-
-/*
- * Called from schedule() when it runs out of runnable processes,
- * or when Erlang code has performed INPUT_REDUCTIONS reduction
- * steps. runnable == 0 iff there are no runnable Erlang processes.
- */
-void
-erl_sys_schedule(int runnable)
-{
- ASSERT(get_fsem(current_process()) == 0);
-#ifdef ERTS_SMP
- ASSERT(erts_get_scheduler_data()->no == 1);
- ERTS_CHK_IO(!runnable);
-#else
- ERTS_CHK_IO( 1 );
-#endif
- ASSERT(get_fsem(current_process()) == 0);
- ERTS_SMP_LC_ASSERT(!erts_thr_progress_is_blocking());
-}
-
-
-#ifdef ERTS_SMP
-
-void
-erts_sys_main_thread(void)
-{
- erts_thread_disable_fpe();
-
- /* Become signal receiver thread... */
-#ifdef ERTS_ENABLE_LOCK_CHECK
- erts_lc_set_thread_name("signal_receiver");
-#endif
-
- while (1) {
- static const SIGSELECT sigsel[] = {0};
- union SIGNAL *msg = receive(sigsel);
-
- fprintf(stderr,"Main thread got message %d from 0x%x!!\r\n",
- msg->sig_no, sender(&msg));
- free_buf(&msg);
- }
-}
-
-#endif /* ERTS_SMP */
-
-void
-erl_sys_args(int* argc, char** argv)
-{
- int i, j;
-
- erts_smp_rwmtx_init(&environ_rwmtx, "environ");
-
- init_check_io();
-
- /* Handled arguments have been marked with NULL. Slide arguments
- not handled towards the beginning of argv. */
- for (i = 0, j = 0; i < *argc; i++) {
- if (argv[i])
- argv[j++] = argv[i];
- }
- *argc = j;
-
-}
diff --git a/erts/emulator/sys/ose/sys_float.c b/erts/emulator/sys/ose/sys_float.c
deleted file mode 100644
index 51875792ae..0000000000
--- a/erts/emulator/sys/ose/sys_float.c
+++ /dev/null
@@ -1,845 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2001-2013. 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 "sys.h"
-#include "global.h"
-#include "erl_process.h"
-
-
-#ifdef NO_FPE_SIGNALS
-
-void
-erts_sys_init_float(void)
-{
-# ifdef SIGFPE
- sys_sigset(SIGFPE, SIG_IGN); /* Ignore so we can test for NaN and Inf */
-# endif
-}
-
-#else /* !NO_FPE_SIGNALS */
-
-#ifdef ERTS_SMP
-static erts_tsd_key_t fpe_key;
-
-/* once-only initialisation early in the main thread (via erts_sys_init_float()) */
-static void erts_init_fp_exception(void)
-{
- /* XXX: the wrappers prevent using a pthread destructor to
- deallocate the key's value; so when/where do we do that? */
- erts_tsd_key_create(&fpe_key);
-}
-
-void erts_thread_init_fp_exception(void)
-{
- unsigned long *fpe = erts_alloc(ERTS_ALC_T_FP_EXCEPTION, sizeof(*fpe));
- *fpe = 0L;
- erts_tsd_set(fpe_key, fpe);
-}
-
-static ERTS_INLINE volatile unsigned long *erts_thread_get_fp_exception(void)
-{
- return (volatile unsigned long*)erts_tsd_get(fpe_key);
-}
-#else /* !SMP */
-#define erts_init_fp_exception() /*empty*/
-static volatile unsigned long fp_exception;
-#define erts_thread_get_fp_exception() (&fp_exception)
-#endif /* SMP */
-
-volatile unsigned long *erts_get_current_fp_exception(void)
-{
- Process *c_p;
-
- c_p = erts_get_current_process();
- if (c_p)
- return &c_p->fp_exception;
- return erts_thread_get_fp_exception();
-}
-
-static void set_current_fp_exception(unsigned long pc)
-{
- volatile unsigned long *fpexnp = erts_get_current_fp_exception();
- ASSERT(fpexnp != NULL);
- *fpexnp = pc;
-}
-
-void erts_fp_check_init_error(volatile unsigned long *fpexnp)
-{
- char buf[64];
- snprintf(buf, sizeof buf, "ERTS_FP_CHECK_INIT at %p: detected unhandled FPE at %p\r\n",
- __builtin_return_address(0), (void*)*fpexnp);
- if (write(2, buf, strlen(buf)) <= 0)
- erts_exit(ERTS_ABORT_EXIT, "%s", buf);
- *fpexnp = 0;
-#if defined(__i386__) || defined(__x86_64__)
- erts_restore_fpu();
-#endif
-}
-
-/* Is there no standard identifier for Darwin/MacOSX ? */
-#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__)
-#define __DARWIN__ 1
-#endif
-
-#if (defined(__i386__) || defined(__x86_64__)) && defined(__GNUC__)
-
-static void unmask_x87(void)
-{
- unsigned short cw;
-
- __asm__ __volatile__("fstcw %0" : "=m"(cw));
- cw &= ~(0x01|0x04|0x08); /* unmask IM, ZM, OM */
- __asm__ __volatile__("fldcw %0" : : "m"(cw));
-}
-
-/* mask x87 FPE, return true if the previous state was unmasked */
-static int mask_x87(void)
-{
- unsigned short cw;
- int unmasked;
-
- __asm__ __volatile__("fstcw %0" : "=m"(cw));
- unmasked = (cw & (0x01|0x04|0x08)) == 0;
- /* or just set cw = 0x37f */
- cw |= (0x01|0x04|0x08); /* mask IM, ZM, OM */
- __asm__ __volatile__("fldcw %0" : : "m"(cw));
- return unmasked;
-}
-
-static void unmask_sse2(void)
-{
- unsigned int mxcsr;
-
- __asm__ __volatile__("stmxcsr %0" : "=m"(mxcsr));
- mxcsr &= ~(0x003F|0x0680); /* clear exn flags, unmask OM, ZM, IM (not PM, UM, DM) */
- __asm__ __volatile__("ldmxcsr %0" : : "m"(mxcsr));
-}
-
-/* mask SSE2 FPE, return true if the previous state was unmasked */
-static int mask_sse2(void)
-{
- unsigned int mxcsr;
- int unmasked;
-
- __asm__ __volatile__("stmxcsr %0" : "=m"(mxcsr));
- unmasked = (mxcsr & 0x0680) == 0;
- /* or just set mxcsr = 0x1f80 */
- mxcsr &= ~0x003F; /* clear exn flags */
- mxcsr |= 0x0680; /* mask OM, ZM, IM (not PM, UM, DM) */
- __asm__ __volatile__("ldmxcsr %0" : : "m"(mxcsr));
- return unmasked;
-}
-
-#if defined(__x86_64__)
-
-static inline int cpu_has_sse2(void) { return 1; }
-
-#else /* !__x86_64__ */
-
-/*
- * Check if an x86-32 processor has SSE2.
- */
-static unsigned int xor_eflags(unsigned int mask)
-{
- unsigned int eax, edx;
-
- eax = mask; /* eax = mask */
- __asm__("pushfl\n\t"
- "popl %0\n\t" /* edx = original EFLAGS */
- "xorl %0, %1\n\t" /* eax = mask ^ EFLAGS */
- "pushl %1\n\t"
- "popfl\n\t" /* new EFLAGS = mask ^ original EFLAGS */
- "pushfl\n\t"
- "popl %1\n\t" /* eax = new EFLAGS */
- "xorl %0, %1\n\t" /* eax = new EFLAGS ^ old EFLAGS */
- "pushl %0\n\t"
- "popfl" /* restore original EFLAGS */
- : "=d"(edx), "=a"(eax)
- : "1"(eax));
- return eax;
-}
-
-static __inline__ unsigned int cpuid_eax(unsigned int op)
-{
- unsigned int eax, save_ebx;
-
- /* In PIC mode i386 reserves EBX. So we must save
- and restore it ourselves to not upset gcc. */
- __asm__(
- "movl %%ebx, %1\n\t"
- "cpuid\n\t"
- "movl %1, %%ebx"
- : "=a"(eax), "=m"(save_ebx)
- : "0"(op)
- : "cx", "dx");
- return eax;
-}
-
-static __inline__ unsigned int cpuid_edx(unsigned int op)
-{
- unsigned int eax, edx, save_ebx;
-
- /* In PIC mode i386 reserves EBX. So we must save
- and restore it ourselves to not upset gcc. */
- __asm__(
- "movl %%ebx, %2\n\t"
- "cpuid\n\t"
- "movl %2, %%ebx"
- : "=a"(eax), "=d"(edx), "=m"(save_ebx)
- : "0"(op)
- : "cx");
- return edx;
-}
-
-/* The AC bit, bit #18, is a new bit introduced in the EFLAGS
- * register on the Intel486 processor to generate alignment
- * faults. This bit cannot be set on the Intel386 processor.
- */
-static __inline__ int is_386(void)
-{
- return ((xor_eflags(1<<18) >> 18) & 1) == 0;
-}
-
-/* Newer x86 processors have a CPUID instruction, as indicated by
- * the ID bit (#21) in EFLAGS being modifiable.
- */
-static __inline__ int has_CPUID(void)
-{
- return (xor_eflags(1<<21) >> 21) & 1;
-}
-
-static int cpu_has_sse2(void)
-{
- unsigned int maxlev, features;
- static int has_sse2 = -1;
-
- if (has_sse2 >= 0)
- return has_sse2;
- has_sse2 = 0;
-
- if (is_386())
- return 0;
- if (!has_CPUID())
- return 0;
- maxlev = cpuid_eax(0);
- /* Intel A-step Pentium had a preliminary version of CPUID.
- It also didn't have SSE2. */
- if ((maxlev & 0xFFFFFF00) == 0x0500)
- return 0;
- /* If max level is zero then CPUID cannot report any features. */
- if (maxlev == 0)
- return 0;
- features = cpuid_edx(1);
- has_sse2 = (features & (1 << 26)) != 0;
-
- return has_sse2;
-}
-#endif /* !__x86_64__ */
-
-static void unmask_fpe(void)
-{
- __asm__ __volatile__("fnclex");
- unmask_x87();
- if (cpu_has_sse2())
- unmask_sse2();
-}
-
-static void unmask_fpe_conditional(int unmasked)
-{
- if (unmasked)
- unmask_fpe();
-}
-
-/* mask x86 FPE, return true if the previous state was unmasked */
-static int mask_fpe(void)
-{
- int unmasked;
-
- unmasked = mask_x87();
- if (cpu_has_sse2())
- unmasked |= mask_sse2();
- return unmasked;
-}
-
-void erts_restore_fpu(void)
-{
- __asm__ __volatile__("fninit");
- unmask_x87();
- if (cpu_has_sse2())
- unmask_sse2();
-}
-
-#elif defined(__sparc__) && defined(__linux__)
-
-#if defined(__arch64__)
-#define LDX "ldx"
-#define STX "stx"
-#else
-#define LDX "ld"
-#define STX "st"
-#endif
-
-static void unmask_fpe(void)
-{
- unsigned long fsr;
-
- __asm__(STX " %%fsr, %0" : "=m"(fsr));
- fsr &= ~(0x1FUL << 23); /* clear FSR[TEM] field */
- fsr |= (0x1AUL << 23); /* enable NV, OF, DZ exceptions */
- __asm__ __volatile__(LDX " %0, %%fsr" : : "m"(fsr));
-}
-
-static void unmask_fpe_conditional(int unmasked)
-{
- if (unmasked)
- unmask_fpe();
-}
-
-/* mask SPARC FPE, return true if the previous state was unmasked */
-static int mask_fpe(void)
-{
- unsigned long fsr;
- int unmasked;
-
- __asm__(STX " %%fsr, %0" : "=m"(fsr));
- unmasked = ((fsr >> 23) & 0x1A) == 0x1A;
- fsr &= ~(0x1FUL << 23); /* clear FSR[TEM] field */
- __asm__ __volatile__(LDX " %0, %%fsr" : : "m"(fsr));
- return unmasked;
-}
-
-#elif (defined(__powerpc__) && defined(__linux__)) || (defined(__ppc__) && defined(__DARWIN__))
-
-#if defined(__linux__)
-#include <sys/prctl.h>
-
-static void set_fpexc_precise(void)
-{
- if (prctl(PR_SET_FPEXC, PR_FP_EXC_PRECISE) < 0) {
- perror("PR_SET_FPEXC");
- exit(1);
- }
-}
-
-#elif defined(__DARWIN__)
-
-#include <mach/mach.h>
-#include <pthread.h>
-
-/*
- * FE0 FE1 MSR bits
- * 0 0 floating-point exceptions disabled
- * 0 1 floating-point imprecise nonrecoverable
- * 1 0 floating-point imprecise recoverable
- * 1 1 floating-point precise mode
- *
- * Apparently:
- * - Darwin 5.5 (MacOS X <= 10.1) starts with FE0 == FE1 == 0,
- * and resets FE0 and FE1 to 0 after each SIGFPE.
- * - Darwin 6.0 (MacOS X 10.2) starts with FE0 == FE1 == 1,
- * and does not reset FE0 or FE1 after a SIGFPE.
- */
-#define FE0_MASK (1<<11)
-#define FE1_MASK (1<<8)
-
-/* a thread cannot get or set its own MSR bits */
-static void *fpu_fpe_enable(void *arg)
-{
- thread_t t = *(thread_t*)arg;
- struct ppc_thread_state state;
- unsigned int state_size = PPC_THREAD_STATE_COUNT;
-
- if (thread_get_state(t, PPC_THREAD_STATE, (natural_t*)&state, &state_size) != KERN_SUCCESS) {
- perror("thread_get_state");
- exit(1);
- }
- if ((state.srr1 & (FE1_MASK|FE0_MASK)) != (FE1_MASK|FE0_MASK)) {
-#if 1
- /* This would also have to be performed in the SIGFPE handler
- to work around the MSR reset older Darwin releases do. */
- state.srr1 |= (FE1_MASK|FE0_MASK);
- thread_set_state(t, PPC_THREAD_STATE, (natural_t*)&state, state_size);
-#else
- fprintf(stderr, "srr1 == 0x%08x, your Darwin is too old\n", state.srr1);
- exit(1);
-#endif
- }
- return NULL; /* Ok, we appear to be on Darwin 6.0 or later */
-}
-
-static void set_fpexc_precise(void)
-{
- thread_t self = mach_thread_self();
- pthread_t enabler;
-
- if (pthread_create(&enabler, NULL, fpu_fpe_enable, &self)) {
- perror("pthread_create");
- } else if (pthread_join(enabler, NULL)) {
- perror("pthread_join");
- }
-}
-
-#endif
-
-static void set_fpscr(unsigned int fpscr)
-{
- union {
- double d;
- unsigned int fpscr[2];
- } u;
-
- u.fpscr[0] = 0xFFF80000;
- u.fpscr[1] = fpscr;
- __asm__ __volatile__("mtfsf 255,%0" : : "f"(u.d));
-}
-
-static unsigned int get_fpscr(void)
-{
- union {
- double d;
- unsigned int fpscr[2];
- } u;
-
- __asm__("mffs %0" : "=f"(u.d));
- return u.fpscr[1];
-}
-
-static void unmask_fpe(void)
-{
- set_fpexc_precise();
- set_fpscr(0x80|0x40|0x10); /* VE, OE, ZE; not UE or XE */
-}
-
-static void unmask_fpe_conditional(int unmasked)
-{
- if (unmasked)
- unmask_fpe();
-}
-
-/* mask PowerPC FPE, return true if the previous state was unmasked */
-static int mask_fpe(void)
-{
- int unmasked;
-
- unmasked = (get_fpscr() & (0x80|0x40|0x10)) == (0x80|0x40|0x10);
- set_fpscr(0x00);
- return unmasked;
-}
-
-#else
-
-static void unmask_fpe(void)
-{
- fpsetmask(FP_X_INV | FP_X_OFL | FP_X_DZ);
-}
-
-static void unmask_fpe_conditional(int unmasked)
-{
- if (unmasked)
- unmask_fpe();
-}
-
-/* mask IEEE FPE, return true if previous state was unmasked */
-static int mask_fpe(void)
-{
- const fp_except unmasked_mask = FP_X_INV | FP_X_OFL | FP_X_DZ;
- fp_except old_mask;
-
- old_mask = fpsetmask(0);
- return (old_mask & unmasked_mask) == unmasked_mask;
-}
-
-#endif
-
-#if (defined(__linux__) && (defined(__i386__) || defined(__x86_64__) || defined(__sparc__) || defined(__powerpc__))) || (defined(__DARWIN__) && (defined(__i386__) || defined(__x86_64__) || defined(__ppc__))) || (defined(__FreeBSD__) && (defined(__x86_64__) || defined(__i386__))) || ((defined(__NetBSD__) || defined(__OpenBSD__)) && defined(__x86_64__)) || (defined(__sun__) && defined(__x86_64__))
-
-#if defined(__linux__) && defined(__i386__)
-#if !defined(X86_FXSR_MAGIC)
-#define X86_FXSR_MAGIC 0x0000
-#endif
-#elif defined(__FreeBSD__) && defined(__x86_64__)
-#include <sys/types.h>
-#include <machine/fpu.h>
-#elif defined(__FreeBSD__) && defined(__i386__)
-#include <sys/types.h>
-#include <machine/npx.h>
-#elif defined(__DARWIN__)
-#include <machine/signal.h>
-#elif defined(__OpenBSD__) && defined(__x86_64__)
-#include <sys/types.h>
-#include <machine/fpu.h>
-#endif
-#if !(defined(__OpenBSD__) && defined(__x86_64__))
-#include <ucontext.h>
-#endif
-#include <string.h>
-
-#if defined(__linux__) && defined(__x86_64__)
-#define mc_pc(mc) ((mc)->gregs[REG_RIP])
-#elif defined(__linux__) && defined(__i386__)
-#define mc_pc(mc) ((mc)->gregs[REG_EIP])
-#elif defined(__DARWIN__) && defined(__i386__)
-#ifdef DARWIN_MODERN_MCONTEXT
-#define mc_pc(mc) ((mc)->__ss.__eip)
-#else
-#define mc_pc(mc) ((mc)->ss.eip)
-#endif
-#elif defined(__DARWIN__) && defined(__x86_64__)
-#ifdef DARWIN_MODERN_MCONTEXT
-#define mc_pc(mc) ((mc)->__ss.__rip)
-#else
-#define mc_pc(mc) ((mc)->ss.rip)
-#endif
-#elif defined(__FreeBSD__) && defined(__x86_64__)
-#define mc_pc(mc) ((mc)->mc_rip)
-#elif defined(__FreeBSD__) && defined(__i386__)
-#define mc_pc(mc) ((mc)->mc_eip)
-#elif defined(__NetBSD__) && defined(__x86_64__)
-#define mc_pc(mc) ((mc)->__gregs[_REG_RIP])
-#elif defined(__NetBSD__) && defined(__i386__)
-#define mc_pc(mc) ((mc)->__gregs[_REG_EIP])
-#elif defined(__OpenBSD__) && defined(__x86_64__)
-#define mc_pc(mc) ((mc)->sc_rip)
-#elif defined(__sun__) && defined(__x86_64__)
-#define mc_pc(mc) ((mc)->gregs[REG_RIP])
-#endif
-
-static void fpe_sig_action(int sig, siginfo_t *si, void *puc)
-{
- ucontext_t *uc = puc;
- unsigned long pc;
-
-#if defined(__linux__)
-#if defined(__x86_64__)
- mcontext_t *mc = &uc->uc_mcontext;
- fpregset_t fpstate = mc->fpregs;
- pc = mc_pc(mc);
- /* A failed SSE2 instruction will restart. To avoid
- looping we mask SSE2 exceptions now and unmask them
- again later in erts_check_fpe()/erts_restore_fpu().
- On RISCs we update PC to skip the failed instruction,
- but the ever increasing complexity of the x86 instruction
- set encoding makes that a poor solution here. */
- fpstate->mxcsr = 0x1F80;
- fpstate->swd &= ~0xFF;
-#elif defined(__i386__)
- mcontext_t *mc = &uc->uc_mcontext;
- fpregset_t fpstate = mc->fpregs;
- pc = mc_pc(mc);
- if ((fpstate->status >> 16) == X86_FXSR_MAGIC)
- ((struct _fpstate*)fpstate)->mxcsr = 0x1F80;
- fpstate->sw &= ~0xFF;
-#elif defined(__sparc__) && defined(__arch64__)
- /* on SPARC the 3rd parameter points to a sigcontext not a ucontext */
- struct sigcontext *sc = (struct sigcontext*)puc;
- pc = sc->sigc_regs.tpc;
- sc->sigc_regs.tpc = sc->sigc_regs.tnpc;
- sc->sigc_regs.tnpc += 4;
-#elif defined(__sparc__)
- /* on SPARC the 3rd parameter points to a sigcontext not a ucontext */
- struct sigcontext *sc = (struct sigcontext*)puc;
- pc = sc->si_regs.pc;
- sc->si_regs.pc = sc->si_regs.npc;
- sc->si_regs.npc = (unsigned long)sc->si_regs.npc + 4;
-#elif defined(__powerpc__)
-#if defined(__powerpc64__)
- mcontext_t *mc = &uc->uc_mcontext;
- unsigned long *regs = &mc->gp_regs[0];
-#else
- mcontext_t *mc = uc->uc_mcontext.uc_regs;
- unsigned long *regs = &mc->gregs[0];
-#endif
- pc = regs[PT_NIP];
- regs[PT_NIP] += 4;
- regs[PT_FPSCR] = 0x80|0x40|0x10; /* VE, OE, ZE; not UE or XE */
-#endif
-#elif defined(__DARWIN__) && (defined(__i386__) || defined(__x86_64__))
-#ifdef DARWIN_MODERN_MCONTEXT
- mcontext_t mc = uc->uc_mcontext;
- pc = mc_pc(mc);
- mc->__fs.__fpu_mxcsr = 0x1F80;
- *(unsigned short *)&mc->__fs.__fpu_fsw &= ~0xFF;
-#else
- mcontext_t mc = uc->uc_mcontext;
- pc = mc_pc(mc);
- mc->fs.fpu_mxcsr = 0x1F80;
- *(unsigned short *)&mc->fs.fpu_fsw &= ~0xFF;
-#endif /* DARWIN_MODERN_MCONTEXT */
-#elif defined(__DARWIN__) && defined(__ppc__)
- mcontext_t mc = uc->uc_mcontext;
- pc = mc->ss.srr0;
- mc->ss.srr0 += 4;
- mc->fs.fpscr = 0x80|0x40|0x10;
-#elif defined(__FreeBSD__) && defined(__x86_64__)
- mcontext_t *mc = &uc->uc_mcontext;
- struct savefpu *savefpu = (struct savefpu*)&mc->mc_fpstate;
- struct envxmm *envxmm = &savefpu->sv_env;
- pc = mc_pc(mc);
- envxmm->en_mxcsr = 0x1F80;
- envxmm->en_sw &= ~0xFF;
-#elif defined(__FreeBSD__) && defined(__i386__)
- mcontext_t *mc = &uc->uc_mcontext;
- union savefpu *savefpu = (union savefpu*)&mc->mc_fpstate;
- pc = mc_pc(mc);
- if (mc->mc_fpformat == _MC_FPFMT_XMM) {
- struct envxmm *envxmm = &savefpu->sv_xmm.sv_env;
- envxmm->en_mxcsr = 0x1F80;
- envxmm->en_sw &= ~0xFF;
- } else {
- struct env87 *env87 = &savefpu->sv_87.sv_env;
- env87->en_sw &= ~0xFF;
- }
-#elif defined(__NetBSD__) && defined(__x86_64__)
- mcontext_t *mc = &uc->uc_mcontext;
- struct fxsave64 *fxsave = (struct fxsave64 *)&mc->__fpregs;
- pc = mc_pc(mc);
- fxsave->fx_mxcsr = 0x1F80;
- fxsave->fx_fsw &= ~0xFF;
-#elif defined(__NetBSD__) && defined(__i386__)
- mcontext_t *mc = &uc->uc_mcontext;
- pc = mc_pc(mc);
- if (uc->uc_flags & _UC_FXSAVE) {
- struct envxmm *envxmm = (struct envxmm *)&mc->__fpregs;
- envxmm->en_mxcsr = 0x1F80;
- envxmm->en_sw &= ~0xFF;
- } else {
- struct env87 *env87 = (struct env87 *)&mc->__fpregs;
- env87->en_sw &= ~0xFF;
- }
-#elif defined(__OpenBSD__) && defined(__x86_64__)
- struct fxsave64 *fxsave = uc->sc_fpstate;
- pc = mc_pc(uc);
- fxsave->fx_mxcsr = 0x1F80;
- fxsave->fx_fsw &= ~0xFF;
-#elif defined(__sun__) && defined(__x86_64__)
- mcontext_t *mc = &uc->uc_mcontext;
- struct fpchip_state *fpstate = &mc->fpregs.fp_reg_set.fpchip_state;
- pc = mc_pc(mc);
- fpstate->mxcsr = 0x1F80;
- fpstate->sw &= ~0xFF;
-#endif
-#if 0
- {
- char buf[64];
- snprintf(buf, sizeof buf, "%s: FPE at %p\r\n", __FUNCTION__, (void*)pc);
- write(2, buf, strlen(buf));
- }
-#endif
- set_current_fp_exception(pc);
-}
-
-static void erts_thread_catch_fp_exceptions(void)
-{
- struct sigaction act;
- memset(&act, 0, sizeof act);
- act.sa_sigaction = fpe_sig_action;
- act.sa_flags = SA_SIGINFO;
- sigaction(SIGFPE, &act, NULL);
- unmask_fpe();
-}
-
-#else /* !((__linux__ && (__i386__ || __x86_64__ || __powerpc__)) || (__DARWIN__ && (__i386__ || __x86_64__ || __ppc__))) */
-
-static void fpe_sig_handler(int sig)
-{
- set_current_fp_exception(1); /* XXX: convert to sigaction so we can get the trap PC */
-}
-
-static void erts_thread_catch_fp_exceptions(void)
-{
- sys_sigset(SIGFPE, fpe_sig_handler);
- unmask_fpe();
-}
-
-#endif /* (__linux__ && (__i386__ || __x86_64__ || __powerpc__)) || (__DARWIN__ && (__i386__ || __x86_64__ || __ppc__))) */
-
-/* once-only initialisation early in the main thread */
-void erts_sys_init_float(void)
-{
- erts_init_fp_exception();
- erts_thread_catch_fp_exceptions();
- erts_printf_block_fpe = erts_sys_block_fpe;
- erts_printf_unblock_fpe = erts_sys_unblock_fpe;
-}
-
-#endif /* NO_FPE_SIGNALS */
-
-void erts_thread_init_float(void)
-{
-#ifdef ERTS_SMP
- /* This allows Erlang schedulers to leave Erlang-process context
- and still have working FP exceptions. XXX: is this needed? */
- erts_thread_init_fp_exception();
-#endif
-
-#ifndef NO_FPE_SIGNALS
- /* NOTE:
- * erts_thread_disable_fpe() is called in all threads at
- * creation. We at least need to call unmask_fpe()
- */
-#if defined(__DARWIN__) || defined(__FreeBSD__)
- /* Darwin (7.9.0) does not appear to propagate FP exception settings
- to a new thread from its parent. So if we want FP exceptions, we
- must manually re-enable them in each new thread.
- FreeBSD 6.1 appears to suffer from a similar issue. */
- erts_thread_catch_fp_exceptions();
-#else
- unmask_fpe();
-#endif
-
-#endif
-}
-
-void erts_thread_disable_fpe(void)
-{
-#if !defined(NO_FPE_SIGNALS)
- (void)mask_fpe();
-#endif
-}
-
-#if !defined(NO_FPE_SIGNALS)
-int erts_sys_block_fpe(void)
-{
- return mask_fpe();
-}
-
-void erts_sys_unblock_fpe(int unmasked)
-{
- unmask_fpe_conditional(unmasked);
-}
-#endif
-
-/* The following check is incorporated from the Vee machine */
-
-#define ISDIGIT(d) ((d) >= '0' && (d) <= '9')
-
-/*
- ** Convert a double to ascii format 0.dddde[+|-]ddd
- ** return number of characters converted or -1 if error.
- **
- ** These two functions should maybe use localeconv() to pick up
- ** the current radix character, but since it is uncertain how
- ** expensive such a system call is, and since no-one has heard
- ** of other radix characters than '.' and ',' an ad-hoc
- ** low execution time solution is used instead.
- */
-
-int
-sys_double_to_chars_ext(double fp, char *buffer, size_t buffer_size, size_t decimals)
-{
- char *s = buffer;
-
- if (erts_snprintf(buffer, buffer_size, "%.*e", decimals, fp) >= buffer_size)
- return -1;
- /* Search upto decimal point */
- if (*s == '+' || *s == '-') s++;
- while (ISDIGIT(*s)) s++;
- if (*s == ',') *s++ = '.'; /* Replace ',' with '.' */
- /* Scan to end of string */
- while (*s) s++;
- return s-buffer; /* i.e strlen(buffer) */
-}
-
-/* Float conversion */
-
-int
-sys_chars_to_double(char* buf, double* fp)
-{
-#ifndef NO_FPE_SIGNALS
- volatile unsigned long *fpexnp = erts_get_current_fp_exception();
-#endif
- char *s = buf, *t, *dp;
-
- /* Robert says that something like this is what he really wanted:
- * (The [.,] radix test is NOT what Robert wanted - it was added later)
- *
- * 7 == sscanf(Tbuf, "%[+-]%[0-9][.,]%[0-9]%[eE]%[+-]%[0-9]%s", ....);
- * if (*s2 == 0 || *s3 == 0 || *s4 == 0 || *s6 == 0 || *s7)
- * break;
- */
-
- /* Scan string to check syntax. */
- if (*s == '+' || *s == '-') s++;
- if (!ISDIGIT(*s)) /* Leading digits. */
- return -1;
- while (ISDIGIT(*s)) s++;
- if (*s != '.' && *s != ',') /* Decimal part. */
- return -1;
- dp = s++; /* Remember decimal point pos just in case */
- if (!ISDIGIT(*s))
- return -1;
- while (ISDIGIT(*s)) s++;
- if (*s == 'e' || *s == 'E') {
- /* There is an exponent. */
- s++;
- if (*s == '+' || *s == '-') s++;
- if (!ISDIGIT(*s))
- return -1;
- while (ISDIGIT(*s)) s++;
- }
- if (*s) /* That should be it */
- return -1;
-
-#ifdef NO_FPE_SIGNALS
- errno = 0;
-#endif
- __ERTS_FP_CHECK_INIT(fpexnp);
- *fp = strtod(buf, &t);
- __ERTS_FP_ERROR_THOROUGH(fpexnp, *fp, return -1);
- if (t != s) { /* Whole string not scanned */
- /* Try again with other radix char */
- *dp = (*dp == '.') ? ',' : '.';
- errno = 0;
- __ERTS_FP_CHECK_INIT(fpexnp);
- *fp = strtod(buf, &t);
- __ERTS_FP_ERROR_THOROUGH(fpexnp, *fp, return -1);
- }
-
-#ifdef NO_FPE_SIGNALS
- if (errno == ERANGE) {
- if (*fp == HUGE_VAL || *fp == -HUGE_VAL) {
- /* overflow, should give error */
- return -1;
- } else if (t == s && *fp == 0.0) {
- /* This should give 0.0 - OTP-7178 */
- errno = 0;
-
- } else if (*fp == 0.0) {
- return -1;
- }
- }
-#endif
- return 0;
-}
-
-int
-matherr(struct exception *exc)
-{
-#if !defined(NO_FPE_SIGNALS)
- volatile unsigned long *fpexnp = erts_get_current_fp_exception();
- if (fpexnp != NULL)
- *fpexnp = (unsigned long)__builtin_return_address(0);
-#endif
- return 1;
-}
diff --git a/erts/emulator/sys/ose/sys_time.c b/erts/emulator/sys/ose/sys_time.c
deleted file mode 100644
index 5dac75956a..0000000000
--- a/erts/emulator/sys/ose/sys_time.c
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2005-2009. 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 "sys.h"
-#include "global.h"
-
-/******************* Routines for time measurement *********************/
-
-int erts_ticks_per_sec = 0; /* Will be SYS_CLK_TCK in erl_unix_sys.h */
-
-int sys_init_time(void)
-{
- return SYS_CLOCK_RESOLUTION;
-}
-
-clock_t sys_times(SysTimes *now) {
- now->tms_utime = now->tms_stime = now->tms_cutime = now->tms_cstime = 0;
- return 0;
-}
-
-static OSTICK last_tick_count = 0;
-static SysHrTime wrap = 0;
-static OSTICK us_per_tick;
-
-void sys_init_hrtime() {
- us_per_tick = system_tick();
-}
-
-SysHrTime sys_gethrtime() {
- OSTICK ticks = get_ticks();
- if (ticks < (SysHrTime) last_tick_count) {
- wrap += 1ULL << 32;
- }
- last_tick_count = ticks;
- return ((((SysHrTime) ticks) + wrap) * 1000*us_per_tick);
-}
diff --git a/erts/emulator/sys/unix/driver_int.h b/erts/emulator/sys/unix/driver_int.h
index a6b9085245..840b832878 100644
--- a/erts/emulator/sys/unix/driver_int.h
+++ b/erts/emulator/sys/unix/driver_int.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c
index a3c5c20641..69fc6c2879 100644
--- a/erts/emulator/sys/unix/erl_child_setup.c
+++ b/erts/emulator/sys/unix/erl_child_setup.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2002-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,109 +19,284 @@
*/
/*
- * After a vfork() (or fork()) the child exec()s to this program which
- * sets up the child and exec()s to the user program (see spawn_start()
- * in sys.c and ticket OTP-4389).
+ * This program is started at erts startup and all fork's that
+ * have to be done are done in here. This is done for a couple
+ * of reasons:
+ * - Allow usage of fork without a memory explosion.
+ * -- we do not want to use vfork, as it blocks the VM
+ * until the execv is done, and if the program that
+ * is to be executed is on an NFS that is unavailable,
+ * the execv can block for a very long time.
+ * -- we cannot do fork inside the VM as that would temporarily
+ * duplicate the memory usage of the VM per parallel exec.
+ *
+ * Some implementation notes:
+ * - A single Unix Domain Socket is setup in between the VM and
+ * this program. Over that UDS the file descriptors that should
+ * be used to talk to the child program are sent.
+ * The actual command to execute, together with options and the
+ * environment, is sent over the pipe represented by the
+ * file descriptors mentioned above. We don't send the
+ * command over the UDS as that would increase the likely hood
+ * that it's buffer would be full.
+ *
+ * - Since it is this program that execv's, it has to take care of
+ * all the SIGCHLD signals that the child programs generate. The
+ * signals are received and the pid+exit reason is sent as data
+ * on the UDS to the VM. The VM is then able to map the pid to the
+ * port of the child program that just exited and deliver the status
+ * code if requested.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
-#define NEED_CHILD_SETUP_DEFINES
-#include "sys.h"
-#include "erl_misc_utils.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <sys/wait.h>
-#ifdef SIG_SIGSET /* Old SysV */
-void sys_sigrelease(int sig)
+#define WANT_NONBLOCKING
+
+#include "erl_driver.h"
+#include "sys_uds.h"
+#include "hash.h"
+#include "erl_term.h"
+#include "erl_child_setup.h"
+
+#define SET_CLOEXEC(fd) fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC)
+
+#if defined(__ANDROID__)
+#define SHELL "/system/bin/sh"
+#else
+#define SHELL "/bin/sh"
+#endif /* __ANDROID__ */
+
+//#define HARD_DEBUG
+#ifdef HARD_DEBUG
+#define DEBUG_PRINT(fmt, ...) fprintf(stderr, "%d:" fmt "\r\n", getpid(), ##__VA_ARGS__)
+#else
+#define DEBUG_PRINT(fmt, ...)
+#endif
+
+static char abort_reason[200]; /* for core dump inspection */
+
+static void ABORT(const char* fmt, ...)
{
- sigrelse(sig);
+ va_list arglist;
+ va_start(arglist, fmt);
+ vsprintf(abort_reason, fmt, arglist);
+ fprintf(stderr, "erl_child_setup: %s\r\n", abort_reason);
+ va_end(arglist);
+ abort();
}
-#else /* !SIG_SIGSET */
-#ifdef SIG_SIGNAL /* Old BSD */
-sys_sigrelease(int sig)
+
+#ifdef DEBUG
+void
+erl_assert_error(const char* expr, const char* func, const char* file, int line)
{
- sigsetmask(sigblock(0) & ~sigmask(sig));
+ fflush(stdout);
+ fprintf(stderr, "%s:%d:%s() Assertion failed: %s\n",
+ file, line, func, expr);
+ fflush(stderr);
+ abort();
}
-#else /* !SIG_SIGNAL */ /* The True Way - POSIX!:-) */
-void sys_sigrelease(int sig)
+#endif
+
+void sys_sigblock(int sig)
{
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, sig);
+ sigprocmask(SIG_BLOCK, &mask, (sigset_t *)NULL);
+}
+
+void sys_sigrelease(int sig)
+{
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, sig);
sigprocmask(SIG_UNBLOCK, &mask, (sigset_t *)NULL);
}
-#endif /* !SIG_SIGNAL */
-#endif /* !SIG_SIGSET */
-#if defined(__ANDROID__)
-static int system_properties_fd(void);
-#endif /* __ANDROID__ */
-
-#if defined(__ANDROID__)
-#define SHELL "/system/bin/sh"
-#else
-#define SHELL "/bin/sh"
-#endif /* __ANDROID__ */
+static void add_os_pid_to_port_id_mapping(Eterm, pid_t);
+static Eterm get_port_id(pid_t);
+static int forker_hash_init(void);
+static int max_files = -1;
+static int sigchld_pipe[2];
-int
-main(int argc, char *argv[])
+static int
+start_new_child(int pipes[])
{
- int i, from, to;
- int erts_spawn_executable = 0;
+ int errln = -1;
+ int size, res, i, pos = 0;
+ char *buff, *o_buff;
+
+ char *cmd, *cwd, *wd, **new_environ, **args = NULL;
+
+ Sint32 cnt, flags;
- /* OBSERVE!
- * Keep child setup after fork() (implemented in sys.c) up to date
- * if changes are made here.
- */
+ /* only child executes here */
- if (argc != CS_ARGV_NO_OF_ARGS) {
- if (argc < CS_ARGV_NO_OF_ARGS) {
- return 1;
- } else {
- erts_spawn_executable = 1;
- }
+ do {
+ res = read(pipes[0], (char*)&size, sizeof(size));
+ } while(res < 0 && (errno == EINTR || errno == ERRNO_BLOCK));
+
+ if (res <= 0) {
+ errln = __LINE__;
+ goto child_error;
}
- if (strcmp("false", argv[CS_ARGV_UNBIND_IX]) != 0)
- if (erts_unbind_from_cpu_str(argv[CS_ARGV_UNBIND_IX]) != 0)
- return 1;
+ buff = malloc(size);
+
+ DEBUG_PRINT("size = %d", size);
+
+ do {
+ if ((res = read(pipes[0], buff + pos, size - pos)) < 0) {
+ if (errno == ERRNO_BLOCK || errno == EINTR)
+ continue;
+ errln = __LINE__;
+ goto child_error;
+ }
+ if (res == 0) {
+ errno = EPIPE;
+ errln = __LINE__;
+ goto child_error;
+ }
+ pos += res;
+ } while(size - pos != 0);
+
+ o_buff = buff;
+
+ flags = get_int32(buff);
+ buff += sizeof(flags);
- for (i = 0; i < CS_ARGV_NO_OF_DUP2_OPS; i++) {
- if (argv[CS_ARGV_DUP2_OP_IX(i)][0] == '-'
- && argv[CS_ARGV_DUP2_OP_IX(i)][1] == '\0')
- break;
- if (sscanf(argv[CS_ARGV_DUP2_OP_IX(i)], "%d:%d", &from, &to) != 2)
- return 1;
- if (dup2(from, to) < 0)
- return 1;
+ DEBUG_PRINT("flags = %d", flags);
+
+ cmd = buff;
+ buff += strlen(buff) + 1;
+
+ cwd = buff;
+ buff += strlen(buff) + 1;
+
+ if (*buff == '\0') {
+ wd = NULL;
+ } else {
+ wd = buff;
+ buff += strlen(buff) + 1;
}
+ buff++;
- if (sscanf(argv[CS_ARGV_FD_CR_IX], "%d:%d", &from, &to) != 2)
- return 1;
+ DEBUG_PRINT("wd = %s", wd);
-#if defined(HAVE_CLOSEFROM)
- closefrom(from);
-#elif defined(__ANDROID__)
- if (from <= to) {
- int spfd = system_properties_fd();
- for (i = from; i <= to; i++) {
- if (i != spfd) {
- (void) close(i);
- }
- }
+ cnt = get_int32(buff);
+ buff += sizeof(cnt);
+ new_environ = malloc(sizeof(char*)*(cnt + 1));
+
+ DEBUG_PRINT("env_len = %d", cnt);
+ for (i = 0; i < cnt; i++, buff++) {
+ new_environ[i] = buff;
+ while(*buff != '\0') buff++;
+ }
+ new_environ[cnt] = NULL;
+
+ if (o_buff + size != buff) {
+ /* This is a spawn executable call */
+ cnt = get_int32(buff);
+ buff += sizeof(cnt);
+ args = malloc(sizeof(char*)*(cnt + 1));
+ for (i = 0; i < cnt; i++, buff++) {
+ args[i] = buff;
+ while(*buff != '\0') buff++;
+ }
+ args[cnt] = NULL;
+ }
+
+ if (o_buff + size != buff) {
+ errno = EINVAL;
+ errln = __LINE__;
+ fprintf(stderr,"erl_child_setup: failed with protocol "
+ "error %d on line %d", errno, errln);
+ /* we abort here as it is most likely a symptom of an
+ emulator/erl_child_setup bug */
+ abort();
}
-#else /* !__ANDROID__ */
- for (i = from; i <= to; i++) {
- (void) close(i);
+
+ DEBUG_PRINT("read ack");
+ do {
+ ErtsSysForkerProto proto;
+ res = read(pipes[0], &proto, sizeof(proto));
+ if (res > 0) {
+ ASSERT(proto.action == ErtsSysForkerProtoAction_Ack);
+ ASSERT(res == sizeof(proto));
+ }
+ } while(res < 0 && (errno == EINTR || errno == ERRNO_BLOCK));
+
+ if (res < 1) {
+ errno = EPIPE;
+ errln = __LINE__;
+ goto child_error;
}
-#endif /* HAVE_CLOSEFROM */
- if (!(argv[CS_ARGV_WD_IX][0] == '.' && argv[CS_ARGV_WD_IX][1] == '\0')
- && chdir(argv[CS_ARGV_WD_IX]) < 0)
- return 1;
+ DEBUG_PRINT("Set cwd to: '%s'",cwd);
+
+ if (chdir(cwd) < 0) {
+ /* This is not good, it probably means that the cwd of
+ beam is invalid. We ignore it and try anyways as
+ the child might now need a cwd or the chdir below
+ could take us to a valid directory.
+ */
+ }
+
+ DEBUG_PRINT("Set wd to: '%s'",wd);
+
+ if (wd && chdir(wd) < 0) {
+ int err = errno;
+ fprintf(stderr,"spawn: Could not cd to %s\r\n", wd);
+ _exit(err);
+ }
+
+ DEBUG_PRINT("Do that forking business: '%s'",cmd);
+
+ /* When the dup2'ing below is done, only
+ fd's 0, 1, 2 and maybe 3, 4 should survive the
+ exec. All other fds (i.e. the unix domain sockets
+ and stray pipe ends) should have CLOEXEC set on them
+ so they will be closed when the exec happens */
+ if (flags & FORKER_FLAG_USE_STDIO) {
+ /* stdin for process */
+ if (flags & FORKER_FLAG_DO_WRITE &&
+ dup2(pipes[0], 0) < 0) {
+ errln = __LINE__;
+ goto child_error;
+ }
+ /* stdout for process */
+ if (flags & FORKER_FLAG_DO_READ &&
+ dup2(pipes[1], 1) < 0) {
+ errln = __LINE__;
+ goto child_error;
+ }
+ }
+ else { /* XXX will fail if pipes[0] == 4 (unlikely..) */
+ if (flags & FORKER_FLAG_DO_READ && dup2(pipes[1], 4) < 0) {
+ errln = __LINE__;
+ goto child_error;
+ }
+ if (flags & FORKER_FLAG_DO_WRITE && dup2(pipes[0], 3) < 0) {
+ errln = __LINE__;
+ goto child_error;
+ }
+ }
+
+ /* we do the dup2 of stderr last so that errors
+ in child_error will be printed to stderr */
+ if (dup2(pipes[2], 2) < 0) {
+ errln = __LINE__;
+ goto child_error;
+ }
#if defined(USE_SETPGRP_NOARGS) /* SysV */
(void) setpgrp();
@@ -131,34 +306,316 @@ main(int argc, char *argv[])
(void) setsid();
#endif
+ close(pipes[0]);
+ close(pipes[1]);
+ close(pipes[2]);
+
sys_sigrelease(SIGCHLD);
- sys_sigrelease(SIGINT);
- sys_sigrelease(SIGUSR1);
-
- if (erts_spawn_executable) {
- if (argv[CS_ARGV_NO_OF_ARGS + 1] == NULL) {
- execl(argv[CS_ARGV_NO_OF_ARGS],argv[CS_ARGV_NO_OF_ARGS],
- (char *) NULL);
- } else {
- execv(argv[CS_ARGV_NO_OF_ARGS],&(argv[CS_ARGV_NO_OF_ARGS + 1]));
- }
+
+ if (args) {
+ /* spawn_executable */
+ execve(cmd, args, new_environ);
} else {
- execl(SHELL, "sh", "-c", argv[CS_ARGV_CMD_IX], (char *) NULL);
+ execle(SHELL, "sh", "-c", cmd, (char *) NULL, new_environ);
}
- return 1;
+
+ DEBUG_PRINT("exec error: %d",errno);
+ _exit(errno);
+
+child_error:
+ fprintf(stderr,"erl_child_setup: failed with error %d on line %d\r\n",
+ errno, errln);
+ _exit(errno);
+}
+
+
+/*
+ * [OTP-3906]
+ * Solaris signal management gets confused when threads are used and a
+ * lot of child processes dies. The confusion results in that SIGCHLD
+ * signals aren't delivered to the emulator which in turn results in
+ * a lot of defunct processes in the system.
+ *
+ * The problem seems to appear when a signal is frequently
+ * blocked/unblocked at the same time as the signal is frequently
+ * propagated. The child waiter thread is a workaround for this problem.
+ * The SIGCHLD signal is always blocked (in all threads), and the child
+ * waiter thread fetches the signal by a call to sigwait(). See
+ * child_waiter().
+ *
+ * This should be a non-issue since the fork:ing was moved outside of
+ * the emulator into erl_child_setup. I'm leaving the comment here
+ * for posterity. */
+
+static void handle_sigchld(int sig) {
+ int buff[2], res, __preverrno = errno;
+
+ sys_sigblock(SIGCHLD);
+
+ while ((buff[0] = waitpid((pid_t)(-1), buff+1, WNOHANG)) > 0) {
+ do {
+ res = write(sigchld_pipe[1], buff, sizeof(buff));
+ } while (res < 0 && errno == EINTR);
+ if (res <= 0)
+ ABORT("Failed to write to sigchld_pipe (%d): %d (%d)", sigchld_pipe[1], res, errno);
+ DEBUG_PRINT("Reap child %d (%d)", buff[0], buff[1]);
+ }
+
+ sys_sigrelease(SIGCHLD);
+
+ /* We save and restore the original errno as otherwise
+ the thread we are running in may end up with an
+ unexpected errno. An example of when this happened
+ was when the select in main had gotten an EINTR but
+ before the errno was checked the signal handler
+ was called and set errno to ECHILD from waitpid
+ which caused erl_child_setup to abort as it does
+ not expect ECHILD to be set after select */
+ errno = __preverrno;
}
#if defined(__ANDROID__)
static int system_properties_fd(void)
{
- int fd;
+ static int fd = -2;
char *env;
+ if (fd != -2) return fd;
env = getenv("ANDROID_PROPERTY_WORKSPACE");
if (!env) {
+ fd = -1;
return -1;
}
fd = atoi(env);
return fd;
}
#endif /* __ANDROID__ */
+
+int
+main(int argc, char *argv[])
+{
+ /* This fd should be open from beam */
+ int uds_fd = 3, max_fd = 3;
+#ifndef HAVE_CLOSEFROM
+ int i;
+#endif
+ struct sigaction sa;
+
+ if (argc < 1 || sscanf(argv[1],"%d",&max_files) != 1) {
+ ABORT("Invalid arguments to child_setup");
+ }
+
+/* We close all fds except the uds from beam.
+ All other fds from now on will have the
+ CLOEXEC flags set on them. This means that we
+ only have to close a very limited number of fds
+ after we fork before the exec. */
+#if defined(HAVE_CLOSEFROM)
+ closefrom(4);
+#else
+ for (i = 4; i < max_files; i++)
+#if defined(__ANDROID__)
+ if (i != system_properties_fd())
+#endif
+ (void) close(i);
+#endif
+
+ if (pipe(sigchld_pipe) < 0) {
+ ABORT("Failed to setup sigchld pipe (%d)", errno);
+ }
+
+ SET_CLOEXEC(sigchld_pipe[0]);
+ SET_CLOEXEC(sigchld_pipe[1]);
+
+ max_fd = max_fd < sigchld_pipe[0] ? sigchld_pipe[0] : max_fd;
+
+ sa.sa_handler = &handle_sigchld;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
+ if (sigaction(SIGCHLD, &sa, 0) == -1) {
+ perror(NULL);
+ exit(1);
+ }
+
+ forker_hash_init();
+
+ SET_CLOEXEC(uds_fd);
+
+ DEBUG_PRINT("Starting forker %d", max_files);
+
+ while (1) {
+ fd_set read_fds;
+ int res;
+ FD_ZERO(&read_fds);
+ FD_SET(uds_fd, &read_fds);
+ FD_SET(sigchld_pipe[0], &read_fds);
+ DEBUG_PRINT("child_setup selecting on %d, %d (%d)",
+ uds_fd, sigchld_pipe[0], max_fd);
+ res = select(max_fd+1, &read_fds, NULL, NULL, NULL);
+
+ if (res < 0) {
+ if (errno == EINTR) continue;
+ ABORT("Select failed: %d (%d)",res, errno);
+ }
+
+ if (FD_ISSET(uds_fd, &read_fds)) {
+ int pipes[3], res, os_pid;
+ ErtsSysForkerProto proto;
+ errno = 0;
+ if ((res = sys_uds_read(uds_fd, (char*)&proto, sizeof(proto),
+ pipes, 3, MSG_DONTWAIT)) < 0) {
+ if (errno == EINTR)
+ continue;
+ DEBUG_PRINT("erl_child_setup failed to read from uds: %d, %d", res, errno);
+ _exit(0);
+ }
+
+ if (res == 0) {
+ DEBUG_PRINT("uds was closed!");
+ _exit(0);
+ }
+ /* Since we use unix domain sockets and send the entire data in
+ one go we *should* get the entire payload at once. */
+ ASSERT(res == sizeof(proto));
+ ASSERT(proto.action == ErtsSysForkerProtoAction_Start);
+
+ sys_sigblock(SIGCHLD);
+
+ errno = 0;
+
+ os_pid = fork();
+ if (os_pid == 0)
+ start_new_child(pipes);
+
+ add_os_pid_to_port_id_mapping(proto.u.start.port_id, os_pid);
+
+ /* We write an ack here, but expect the reply on
+ the pipes[0] inside the fork */
+ proto.action = ErtsSysForkerProtoAction_Go;
+ proto.u.go.os_pid = os_pid;
+ proto.u.go.error_number = errno;
+ while (write(pipes[1], &proto, sizeof(proto)) < 0 && errno == EINTR)
+ ; /* remove gcc warning */
+
+#ifdef FORKER_PROTO_START_ACK
+ proto.action = ErtsSysForkerProtoAction_StartAck;
+ while (write(uds_fd, &proto, sizeof(proto)) < 0 && errno == EINTR)
+ ; /* remove gcc warning */
+#endif
+
+ sys_sigrelease(SIGCHLD);
+ close(pipes[0]);
+ close(pipes[1]);
+ close(pipes[2]);
+ }
+
+ if (FD_ISSET(sigchld_pipe[0], &read_fds)) {
+ int ibuff[2];
+ ErtsSysForkerProto proto;
+ res = read(sigchld_pipe[0], ibuff, sizeof(ibuff));
+ if (res <= 0) {
+ if (errno == EINTR)
+ continue;
+ ABORT("Failed to read from sigchld pipe: %d (%d)", res, errno);
+ }
+
+ proto.u.sigchld.port_id = get_port_id((pid_t)(ibuff[0]));
+
+ if (proto.u.sigchld.port_id == THE_NON_VALUE)
+ continue; /* exit status report not requested */
+
+ proto.action = ErtsSysForkerProtoAction_SigChld;
+ proto.u.sigchld.error_number = ibuff[1];
+ DEBUG_PRINT("send sigchld to %d (errno = %d)", uds_fd, ibuff[1]);
+ if (write(uds_fd, &proto, sizeof(proto)) < 0) {
+ if (errno == EINTR)
+ continue;
+ /* The uds was close, which most likely means that the VM
+ has exited. This will be detected when we try to read
+ from the uds_fd. */
+ DEBUG_PRINT("Failed to write to uds: %d (%d)", uds_fd, errno);
+ }
+ }
+ }
+ return 1;
+}
+
+typedef struct exit_status {
+ HashBucket hb;
+ pid_t os_pid;
+ Eterm port_id;
+} ErtsSysExitStatus;
+
+static Hash *forker_hash;
+
+static void add_os_pid_to_port_id_mapping(Eterm port_id, pid_t os_pid)
+{
+ if (port_id != THE_NON_VALUE) {
+ /* exit status report requested */
+ ErtsSysExitStatus es;
+ es.os_pid = os_pid;
+ es.port_id = port_id;
+ hash_put(forker_hash, &es);
+ }
+}
+
+static Eterm get_port_id(pid_t os_pid)
+{
+ ErtsSysExitStatus est, *es;
+ Eterm port_id;
+ est.os_pid = os_pid;
+ es = hash_remove(forker_hash, &est);
+ if (!es) return THE_NON_VALUE;
+ port_id = es->port_id;
+ free(es);
+ return port_id;
+}
+
+static int fcmp(void *a, void *b)
+{
+ ErtsSysExitStatus *sa = a;
+ ErtsSysExitStatus *sb = b;
+ return !(sa->os_pid == sb->os_pid);
+}
+
+static HashValue fhash(void *e)
+{
+ ErtsSysExitStatus *se = e;
+ Uint32 val = se->os_pid;
+ val = (val+0x7ed55d16) + (val<<12);
+ val = (val^0xc761c23c) ^ (val>>19);
+ val = (val+0x165667b1) + (val<<5);
+ val = (val+0xd3a2646c) ^ (val<<9);
+ val = (val+0xfd7046c5) + (val<<3);
+ val = (val^0xb55a4f09) ^ (val>>16);
+ return val;
+}
+
+static void *falloc(void *e)
+{
+ ErtsSysExitStatus *se = e;
+ ErtsSysExitStatus *ne = malloc(sizeof(ErtsSysExitStatus));
+ ne->os_pid = se->os_pid;
+ ne->port_id = se->port_id;
+ return ne;
+}
+
+static void *meta_alloc(int type, size_t size) { return malloc(size); }
+static void meta_free(int type, void *p) { free(p); }
+
+static int forker_hash_init(void)
+{
+ HashFunctions forker_hash_functions;
+ forker_hash_functions.hash = fhash;
+ forker_hash_functions.cmp = fcmp;
+ forker_hash_functions.alloc = falloc;
+ forker_hash_functions.free = free;
+ forker_hash_functions.meta_alloc = meta_alloc;
+ forker_hash_functions.meta_free = meta_free;
+ forker_hash_functions.meta_print = NULL;
+
+ forker_hash = hash_new(0, "forker_hash",
+ 16, forker_hash_functions);
+
+ return 1;
+}
diff --git a/erts/emulator/sys/unix/erl_child_setup.h b/erts/emulator/sys/unix/erl_child_setup.h
new file mode 100644
index 0000000000..a28b136bfc
--- /dev/null
+++ b/erts/emulator/sys/unix/erl_child_setup.h
@@ -0,0 +1,77 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2015-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%
+ *
+ * This file defines the interface inbetween erts and child_setup.
+ */
+
+#ifndef _ERL_UNIX_FORKER_H
+#define _ERL_UNIX_FORKER_H
+
+#include "sys.h"
+
+#ifdef __FreeBSD__
+/* The freebsd sendmsg man page explicitly states that
+ you should not close fds before they are known
+ to have reached the other side, so this Ack protects
+ against that. */
+#define FORKER_PROTO_START_ACK 1
+#endif
+
+#define FORKER_ARGV_NO_OF_ARGS 3
+#define FORKER_ARGV_PROGNAME_IX 0 /* Program name */
+#define FORKER_ARGV_MAX_FILES 1 /* max_files */
+
+#define FORKER_FLAG_USE_STDIO (1 << 0) /* dup the pipe to stdin/stderr */
+#define FORKER_FLAG_EXIT_STATUS (1 << 1) /* send the exit status to parent */
+#define FORKER_FLAG_DO_READ (1 << 2) /* dup write fd */
+#define FORKER_FLAG_DO_WRITE (1 << 3) /* dup read fd */
+
+#if SIZEOF_VOID_P == SIZEOF_LONG
+typedef unsigned long ErtsSysPortId;
+#elif SIZEOF_VOID_P == SIZEOF_INT
+typedef unsigned int ErtsSysPortId;
+#elif SIZEOF_VOID_P == SIZEOF_LONG_LONG
+typedef unsigned long long ErtsSysPortId;
+#endif
+
+typedef struct ErtsSysForkerProto_ {
+ enum {
+ ErtsSysForkerProtoAction_Start,
+ ErtsSysForkerProtoAction_StartAck,
+ ErtsSysForkerProtoAction_Go,
+ ErtsSysForkerProtoAction_SigChld,
+ ErtsSysForkerProtoAction_Ack
+ } action;
+ union {
+ struct {
+ ErtsSysPortId port_id;
+ int fds[3];
+ } start;
+ struct {
+ pid_t os_pid;
+ int error_number;
+ } go;
+ struct {
+ ErtsSysPortId port_id;
+ int error_number;
+ } sigchld;
+ } u;
+} ErtsSysForkerProto;
+
+#endif /* #ifndef _ERL_UNIX_FORKER_H */
diff --git a/erts/emulator/sys/unix/erl_main.c b/erts/emulator/sys/unix/erl_main.c
index c417bea622..972b93a505 100644
--- a/erts/emulator/sys/unix/erl_main.c
+++ b/erts/emulator/sys/unix/erl_main.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2000-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h
index 8d4e98bf3a..054a4678c8 100644
--- a/erts/emulator/sys/unix/erl_unix_sys.h
+++ b/erts/emulator/sys/unix/erl_unix_sys.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,9 +30,7 @@
#include <limits.h>
#include <stdlib.h>
#include <string.h>
-#ifndef QNX
#include <memory.h>
-#endif
#if defined(__sun__) && defined(__SVR4) && !defined(__EXTENSIONS__)
# define __EXTENSIONS__
@@ -92,11 +90,6 @@
#include <ieeefp.h>
#endif
-#ifdef QNX
-#include <process.h>
-#include <sys/qnx_glob.h>
-#endif
-
#include <pwd.h>
#ifndef HZ
@@ -108,6 +101,10 @@
#endif
#include <netdb.h>
+#ifdef HAVE_MACH_ABSOLUTE_TIME
+#include <mach/mach_time.h>
+#endif
+
#ifdef HAVE_POSIX_MEMALIGN
# define ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC 1
#endif
@@ -136,13 +133,6 @@
# define ERTS_POLL_NEED_ASYNC_INTERRUPT_SUPPORT
#endif
-#ifndef ENABLE_CHILD_WAITER_THREAD
-# ifdef ERTS_SMP
-# define ERTS_SMP_SCHEDULERS_NEED_TO_CHECK_CHILDREN
-void erts_check_children(void);
-# endif
-#endif
-
typedef void *GETENV_STATE;
/*
@@ -171,8 +161,9 @@ typedef long long ErtsSysHrTime;
#endif
typedef ErtsMonotonicTime ErtsSystemTime;
+typedef ErtsSysHrTime ErtsSysPerfCounter;
-#define ERTS_MONOTONIC_TIME_MIN (((ErtsMonotonicTime) 1) << 63)
+#define ERTS_MONOTONIC_TIME_MIN ((ErtsMonotonicTime) (1ULL << 63))
#define ERTS_MONOTONIC_TIME_MAX (~ERTS_MONOTONIC_TIME_MIN)
/*
@@ -219,6 +210,7 @@ ErtsSystemTime erts_os_system_time(void);
* It may or may not be monotonic.
*/
ErtsSysHrTime erts_sys_hrtime(void);
+#define ERTS_HRTIME_UNIT (1000*1000*1000)
struct erts_sys_time_read_only_data__ {
#ifdef ERTS_OS_MONOTONIC_INLINE_FUNC_PTR_CALL__
@@ -227,6 +219,8 @@ struct erts_sys_time_read_only_data__ {
#ifdef ERTS_OS_TIMES_INLINE_FUNC_PTR_CALL__
void (*os_times)(ErtsMonotonicTime *, ErtsSystemTime *);
#endif
+ ErtsSysPerfCounter (*perf_counter)(void);
+ ErtsSysPerfCounter perf_counter_unit;
int ticks_per_sec;
};
@@ -280,7 +274,24 @@ erts_os_times(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep)
#endif /* ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT */
/*
- *
+ * Functions for getting the performance counter
+ */
+
+ERTS_GLB_INLINE ErtsSysPerfCounter erts_sys_perf_counter(void);
+#define erts_sys_perf_counter_unit() erts_sys_time_data__.r.o.perf_counter_unit
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_FORCE_INLINE ErtsSysPerfCounter
+erts_sys_perf_counter()
+{
+ return (*erts_sys_time_data__.r.o.perf_counter)();
+}
+
+#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
+/*
+ * Functions for measuring CPU time
*/
#if (defined(HAVE_GETHRVTIME) || defined(HAVE_CLOCK_GETTIME_CPU_TIME))
@@ -310,7 +321,8 @@ typedef void (*SIGFUNC)(int);
extern SIGFUNC sys_signal(int, SIGFUNC);
extern void sys_sigrelease(int);
extern void sys_sigblock(int);
-extern void sys_stop_cat(void);
+extern void sys_init_suspend_handler(void);
+extern void erts_sys_unix_later_init(void);
/*
* Handling of floating point exceptions.
@@ -425,19 +437,6 @@ void erts_sys_unblock_fpe(int);
#define ERTS_FP_ERROR_THOROUGH(p, f, A) __ERTS_FP_ERROR_THOROUGH(&(p)->fp_exception, f, A)
-#ifdef NEED_CHILD_SETUP_DEFINES
-/* The child setup argv[] */
-#define CS_ARGV_PROGNAME_IX 0 /* Program name */
-#define CS_ARGV_UNBIND_IX 1 /* Unbind from cpu */
-#define CS_ARGV_WD_IX 2 /* Working directory */
-#define CS_ARGV_CMD_IX 3 /* Command */
-#define CS_ARGV_FD_CR_IX 4 /* Fd close range */
-#define CS_ARGV_DUP2_OP_IX(N) ((N) + 5) /* dup2 operations */
-
-#define CS_ARGV_NO_OF_DUP2_OPS 3 /* Number of dup2 ops */
-#define CS_ARGV_NO_OF_ARGS 8 /* Number of arguments */
-#endif /* #ifdef NEED_CHILD_SETUP_DEFINES */
-
/* Threads */
#ifdef USE_THREADS
extern int init_async(int);
diff --git a/erts/emulator/sys/unix/erl_unix_sys_ddll.c b/erts/emulator/sys/unix/erl_unix_sys_ddll.c
index daed5af1b6..8f1ceac883 100644
--- a/erts/emulator/sys/unix/erl_unix_sys_ddll.c
+++ b/erts/emulator/sys/unix/erl_unix_sys_ddll.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c
index d94b37430e..7f738751ff 100644
--- a/erts/emulator/sys/unix/sys.c
+++ b/erts/emulator/sys/unix/sys.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2014. 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.
@@ -49,7 +49,6 @@
#include <sys/ioctl.h>
#endif
-#define NEED_CHILD_SETUP_DEFINES
#define ERTS_WANT_BREAK_HANDLING
#define ERTS_WANT_GOT_SIGUSR1
#define WANT_NONBLOCKING /* must define this to pull in defs from sys.h */
@@ -67,7 +66,7 @@
#include "erl_mseg.h"
extern char **environ;
-static erts_smp_rwmtx_t environ_rwmtx;
+erts_smp_rwmtx_t environ_rwmtx;
#define MAX_VSIZE 16 /* Max number of entries allowed in an I/O
* vector sock_sendv().
@@ -76,89 +75,12 @@ static erts_smp_rwmtx_t environ_rwmtx;
* Don't need global.h, but bif_table.h (included by bif.h),
* won't compile otherwise
*/
-#include "global.h"
+#include "global.h"
#include "bif.h"
-#include "erl_sys_driver.h"
#include "erl_check_io.h"
#include "erl_cpu_topology.h"
-#ifndef DISABLE_VFORK
-#define DISABLE_VFORK 0
-#endif
-
-#if defined IOV_MAX
-#define MAXIOV IOV_MAX
-#elif defined UIO_MAXIOV
-#define MAXIOV UIO_MAXIOV
-#else
-#define MAXIOV 16
-#endif
-
-#ifdef USE_THREADS
-# ifdef ENABLE_CHILD_WAITER_THREAD
-# define CHLDWTHR ENABLE_CHILD_WAITER_THREAD
-# else
-# define CHLDWTHR 0
-# endif
-# define FDBLOCK 1
-#else
-# define CHLDWTHR 0
-# define FDBLOCK 0
-#endif
-/*
- * [OTP-3906]
- * Solaris signal management gets confused when threads are used and a
- * lot of child processes dies. The confusion results in that SIGCHLD
- * signals aren't delivered to the emulator which in turn results in
- * a lot of defunct processes in the system.
- *
- * The problem seems to appear when a signal is frequently
- * blocked/unblocked at the same time as the signal is frequently
- * propagated. The child waiter thread is a workaround for this problem.
- * The SIGCHLD signal is always blocked (in all threads), and the child
- * waiter thread fetches the signal by a call to sigwait(). See
- * child_waiter().
- */
-
-typedef struct ErtsSysReportExit_ ErtsSysReportExit;
-struct ErtsSysReportExit_ {
- ErtsSysReportExit *next;
- Eterm port;
- int pid;
- int ifd;
- int ofd;
-#if CHLDWTHR && !defined(ERTS_SMP)
- int status;
-#endif
-};
-
-/* Used by the fd driver iff the fd could not be set to non-blocking */
-typedef struct ErtsSysBlocking_ {
- ErlDrvPDL pdl;
- int res;
- int err;
- unsigned int pkey;
-} ErtsSysBlocking;
-
-
-/* This data is shared by these drivers - initialized by spawn_init() */
-static struct driver_data {
- ErlDrvPort port_num;
- int ofd, packet_bytes;
- ErtsSysReportExit *report_exit;
- int pid;
- int alive;
- int status;
- int terminating;
- ErtsSysBlocking *blocking;
-} *driver_data; /* indexed by fd */
-
-static ErtsSysReportExit *report_exit_list;
-#if CHLDWTHR && !defined(ERTS_SMP)
-static ErtsSysReportExit *report_exit_transit_list;
-#endif
-
extern int driver_interrupt(int, int);
extern void do_break(void);
@@ -168,35 +90,6 @@ extern void erl_sys_args(int*, char**);
extern void erts_sys_init_float(void);
-extern void erl_crash_dump(char* file, int line, char* fmt, ...);
-
-#define DIR_SEPARATOR_CHAR '/'
-
-#if defined(__ANDROID__)
-#define SHELL "/system/bin/sh"
-#else
-#define SHELL "/bin/sh"
-#endif /* __ANDROID__ */
-
-
-#if defined(DEBUG)
-#define ERL_BUILD_TYPE_MARKER ".debug"
-#elif defined(PURIFY)
-#define ERL_BUILD_TYPE_MARKER ".purify"
-#elif defined(QUANTIFY)
-#define ERL_BUILD_TYPE_MARKER ".quantify"
-#elif defined(PURECOV)
-#define ERL_BUILD_TYPE_MARKER ".purecov"
-#elif defined(VALGRIND)
-#define ERL_BUILD_TYPE_MARKER ".valgrind"
-#else /* opt */
-#define ERL_BUILD_TYPE_MARKER
-#endif
-
-#define CHILD_SETUP_PROG_NAME "child_setup" ERL_BUILD_TYPE_MARKER
-#if !DISABLE_VFORK
-static char *child_setup_prog;
-#endif
#ifdef DEBUG
static int debug_log = 0;
@@ -220,58 +113,19 @@ static volatile int have_prepared_crash_dump;
(have_prepared_crash_dump++)
#endif
-static erts_smp_atomic_t sys_misc_mem_sz;
+erts_smp_atomic_t sys_misc_mem_sz;
#if defined(ERTS_SMP)
static void smp_sig_notify(char c);
static int sig_notify_fds[2] = {-1, -1};
+#ifdef ERTS_SYS_SUSPEND_SIGNAL
static int sig_suspend_fds[2] = {-1, -1};
-#define ERTS_SYS_SUSPEND_SIGNAL SIGUSR2
-
-#endif
-
-jmp_buf erts_sys_sigsegv_jmp;
-
-#if CHLDWTHR || defined(ERTS_SMP)
-erts_mtx_t chld_stat_mtx;
#endif
-#if CHLDWTHR
-static erts_tid_t child_waiter_tid;
-/* chld_stat_mtx is used to protect against concurrent accesses
- of the driver_data fields pid, alive, and status. */
-erts_cnd_t chld_stat_cnd;
-static long children_alive;
-#define CHLD_STAT_LOCK erts_mtx_lock(&chld_stat_mtx)
-#define CHLD_STAT_UNLOCK erts_mtx_unlock(&chld_stat_mtx)
-#define CHLD_STAT_WAIT erts_cnd_wait(&chld_stat_cnd, &chld_stat_mtx)
-#define CHLD_STAT_SIGNAL erts_cnd_signal(&chld_stat_cnd)
-#elif defined(ERTS_SMP) /* ------------------------------------------------- */
-#define CHLD_STAT_LOCK erts_mtx_lock(&chld_stat_mtx)
-#define CHLD_STAT_UNLOCK erts_mtx_unlock(&chld_stat_mtx)
-#else /* ------------------------------------------------------------------- */
-#define CHLD_STAT_LOCK
-#define CHLD_STAT_UNLOCK
-static volatile int children_died;
#endif
-
-static struct fd_data {
- char pbuf[4]; /* hold partial packet bytes */
- int psz; /* size of pbuf */
- char *buf;
- char *cpos;
- int sz;
- int remain; /* for input on fd */
-} *fd_data; /* indexed by fd */
-
-/* static FUNCTION(int, write_fill, (int, char*, int)); unused? */
-static void note_child_death(int, int);
-
-#if CHLDWTHR
-static void* child_waiter(void *);
-#endif
+jmp_buf erts_sys_sigsegv_jmp;
static int crashdump_companion_cube_fd = -1;
@@ -294,11 +148,18 @@ volatile int erts_break_requested = 0;
#define ERTS_SET_BREAK_REQUESTED (erts_break_requested = 1)
#define ERTS_UNSET_BREAK_REQUESTED (erts_break_requested = 0)
#endif
+
+#ifndef ERTS_SMP
+volatile Uint erts_signal_sigterm = 0;
+#define ERTS_SET_SIGNAL_SIGTERM (erts_signal_sigterm = 1)
+#define ERTS_CLEAR_SIGNAL_SIGTERM (erts_signal_sigterm = 0)
+#endif
+
/* set early so the break handler has access to initial mode */
static struct termios initial_tty_mode;
static int replace_intr = 0;
/* assume yes initially, ttsl_init will clear it */
-int using_oldshell = 1;
+int using_oldshell = 1;
#ifdef ERTS_ENABLE_KERNEL_POLL
@@ -453,9 +314,10 @@ MALLOC_USE_HASH(1);
#ifdef USE_THREADS
#ifdef ERTS_THR_HAVE_SIG_FUNCS
+
/*
* Child thread inherits parents signal mask at creation. In order to
- * guarantee that the main thread will receive all SIGINT, SIGCHLD, and
+ * guarantee that the main thread will receive all SIGINT, and
* SIGUSR1 signals sent to the process, we block these signals in the
* parent thread when creating a new thread.
*/
@@ -551,13 +413,15 @@ erts_sys_pre_init(void)
#ifdef ERTS_THR_HAVE_SIG_FUNCS
sigemptyset(&thr_create_sigmask);
sigaddset(&thr_create_sigmask, SIGINT); /* block interrupt */
- sigaddset(&thr_create_sigmask, SIGCHLD); /* block child signals */
+ sigaddset(&thr_create_sigmask, SIGTERM); /* block terminate signal */
sigaddset(&thr_create_sigmask, SIGUSR1); /* block user defined signal */
#endif
erts_thr_init(&eid);
- report_exit_list = NULL;
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ erts_lc_init();
+#endif
#ifdef ERTS_ENABLE_LOCK_COUNT
erts_lcnt_init();
@@ -569,17 +433,6 @@ erts_sys_pre_init(void)
#ifdef USE_THREADS
-#if CHLDWTHR || defined(ERTS_SMP)
- erts_mtx_init(&chld_stat_mtx, "child_status");
-#endif
-#if CHLDWTHR
-#ifndef ERTS_SMP
- report_exit_transit_list = NULL;
-#endif
- erts_cnd_init(&chld_stat_cnd);
- children_alive = 0;
-#endif
-
#ifdef ERTS_SMP
erts_smp_atomic32_init_nob(&erts_break_requested, 0);
erts_smp_atomic32_init_nob(&erts_got_sigusr1, 0);
@@ -589,9 +442,6 @@ erts_sys_pre_init(void)
erts_got_sigusr1 = 0;
have_prepared_crash_dump = 0;
#endif
-#if !CHLDWTHR && !defined(ERTS_SMP)
- children_died = 0;
-#endif
#endif /* USE_THREADS */
@@ -628,39 +478,6 @@ erts_sys_pre_init(void)
void
erl_sys_init(void)
{
-#if !DISABLE_VFORK
- {
- int res;
- char bindir[MAXPATHLEN];
- size_t bindirsz = sizeof(bindir);
- Uint csp_path_sz;
-
- res = erts_sys_getenv_raw("BINDIR", bindir, &bindirsz);
- if (res != 0) {
- if (res < 0)
- erts_exit(1,
- "Environment variable BINDIR is not set\n");
- if (res > 0)
- erts_exit(1,
- "Value of environment variable BINDIR is too large\n");
- }
- if (bindir[0] != DIR_SEPARATOR_CHAR)
- erts_exit(1,
- "Environment variable BINDIR does not contain an"
- " absolute path\n");
- csp_path_sz = (strlen(bindir)
- + 1 /* DIR_SEPARATOR_CHAR */
- + sizeof(CHILD_SETUP_PROG_NAME)
- + 1);
- child_setup_prog = erts_alloc(ERTS_ALC_T_CS_PROG_PATH, csp_path_sz);
- erts_smp_atomic_add_nob(&sys_misc_mem_sz, csp_path_sz);
- erts_snprintf(child_setup_prog, csp_path_sz,
- "%s%c%s",
- bindir,
- DIR_SEPARATOR_CHAR,
- CHILD_SETUP_PROG_NAME);
- }
-#endif
#ifdef USE_SETLINEBUF
setlinebuf(stdout);
@@ -845,6 +662,40 @@ static RETSIGTYPE request_break(int signum)
#endif
}
+static void stop_requested(void) {
+ Process* p = NULL;
+ Eterm msg, *hp;
+ ErtsProcLocks locks = 0;
+ ErlOffHeap *ohp;
+ Eterm id = erts_whereis_name_to_id(NULL, am_init);
+
+ if ((p = (erts_pid2proc_opt(NULL, 0, id, 0, ERTS_P2P_FLG_INC_REFC))) != NULL) {
+ ErtsMessage *msgp = erts_alloc_message_heap(p, &locks, 3, &hp, &ohp);
+
+ /* init ! {stop,stop} */
+ msg = TUPLE2(hp, am_stop, am_stop);
+ erts_queue_message(p, locks, msgp, msg, am_system);
+
+ if (locks)
+ erts_smp_proc_unlock(p, locks);
+ erts_proc_dec_refc(p);
+ }
+}
+
+#if (defined(SIG_SIGSET) || defined(SIG_SIGNAL))
+static RETSIGTYPE request_stop(void)
+#else
+static RETSIGTYPE request_stop(int signum)
+#endif
+{
+#ifdef ERTS_SMP
+ smp_sig_notify('S');
+#else
+ ERTS_SET_SIGNAL_SIGTERM;
+ ERTS_CHK_IO_AS_INTR();
+#endif
+}
+
static ERTS_INLINE void
sigusr1_exit(void)
{
@@ -864,7 +715,7 @@ sigusr1_exit(void)
}
prepare_crash_dump(secs);
- erts_exit(ERTS_ERROR_EXIT, "Received SIGUSR1\n");
+ erts_exit(ERTS_DUMP_EXIT, "Received SIGUSR1\n");
}
#ifdef ETHR_UNUSABLE_SIGUSRX
@@ -872,7 +723,7 @@ sigusr1_exit(void)
#else
-#ifdef ERTS_SMP
+#ifdef ERTS_SYS_SUSPEND_SIGNAL
void
sys_thr_suspend(erts_tid_t tid) {
erts_thr_kill(tid, ERTS_SYS_SUSPEND_SIGNAL);
@@ -900,20 +751,22 @@ static RETSIGTYPE user_signal1(int signum)
#endif
}
-#ifdef ERTS_SMP
+#ifdef ERTS_SYS_SUSPEND_SIGNAL
#if (defined(SIG_SIGSET) || defined(SIG_SIGNAL))
static RETSIGTYPE suspend_signal(void)
#else
static RETSIGTYPE suspend_signal(int signum)
#endif
{
- int res;
- int buf[1];
- do {
- res = read(sig_suspend_fds[0], buf, sizeof(int));
- } while (res < 0 && errno == EINTR);
+ int res, buf[1], tmp_errno = errno;
+ do {
+ res = read(sig_suspend_fds[0], buf, sizeof(int));
+ } while (res < 0 && errno == EINTR);
+
+ /* restore previous errno in case read changed it */
+ errno = tmp_errno;
}
-#endif /* #ifdef ERTS_SMP */
+#endif /* #ifdef ERTS_SYS_SUSPEND_SIGNAL */
#endif /* #ifndef ETHR_UNUSABLE_SIGUSRX */
@@ -938,9 +791,14 @@ static RETSIGTYPE do_quit(int signum)
/* Disable break */
void erts_set_ignore_break(void) {
- sys_signal(SIGINT, SIG_IGN);
- sys_signal(SIGQUIT, SIG_IGN);
- sys_signal(SIGTSTP, SIG_IGN);
+ /*
+ * Ignore signals that can be sent to the VM by
+ * typing certain key combinations at the
+ * controlling terminal...
+ */
+ sys_signal(SIGINT, SIG_IGN); /* Ctrl-C */
+ sys_signal(SIGQUIT, SIG_IGN); /* Ctrl-\ */
+ sys_signal(SIGTSTP, SIG_IGN); /* Ctrl-Z */
}
/* Don't use ctrl-c for break handler but let it be
@@ -966,53 +824,33 @@ void init_break_handler(void)
sys_signal(SIGINT, request_break);
#ifndef ETHR_UNUSABLE_SIGUSRX
sys_signal(SIGUSR1, user_signal1);
-#ifdef ERTS_SMP
- sys_signal(ERTS_SYS_SUSPEND_SIGNAL, suspend_signal);
-#endif /* #ifdef ERTS_SMP */
#endif /* #ifndef ETHR_UNUSABLE_SIGUSRX */
sys_signal(SIGQUIT, do_quit);
}
-int sys_max_files(void)
+void sys_init_suspend_handler(void)
{
- return(max_files);
+#ifdef ERTS_SYS_SUSPEND_SIGNAL
+ sys_signal(ERTS_SYS_SUSPEND_SIGNAL, suspend_signal);
+#endif
}
-static void block_signals(void)
+void
+erts_sys_unix_later_init(void)
{
-#if !CHLDWTHR
- sys_sigblock(SIGCHLD);
-#endif
-#ifndef ERTS_SMP
- sys_sigblock(SIGINT);
-#ifndef ETHR_UNUSABLE_SIGUSRX
- sys_sigblock(SIGUSR1);
-#endif /* #ifndef ETHR_UNUSABLE_SIGUSRX */
-#endif /* #ifndef ERTS_SMP */
+ char env[5];
+ size_t envsz = sizeof(env);
-#if defined(ERTS_SMP) && !defined(ETHR_UNUSABLE_SIGUSRX)
- sys_sigblock(ERTS_SYS_SUSPEND_SIGNAL);
-#endif
+ if (erts_sys_getenv_raw("ERL_ZZ_SIGTERM_KILL", env, &envsz) == 0)
+ if (envsz == 4 && sys_strncmp("true", env, 4) == 0)
+ return;
+ sys_signal(SIGTERM, request_stop);
}
-static void unblock_signals(void)
+int sys_max_files(void)
{
- /* Update erl_child_setup.c if changed */
-#if !CHLDWTHR
- sys_sigrelease(SIGCHLD);
-#endif
-#ifndef ERTS_SMP
- sys_sigrelease(SIGINT);
-#ifndef ETHR_UNUSABLE_SIGUSRX
- sys_sigrelease(SIGUSR1);
-#endif /* #ifndef ETHR_UNUSABLE_SIGUSRX */
-#endif /* #ifndef ERTS_SMP */
-
-#if defined(ERTS_SMP) && !defined(ETHR_UNUSABLE_SIGUSRX)
- sys_sigrelease(ERTS_SYS_SUSPEND_SIGNAL);
-#endif
-
+ return(max_files);
}
/************************** OS info *******************************/
@@ -1102,1502 +940,6 @@ void fini_getenv_state(GETENV_STATE *state)
erts_smp_rwmtx_runlock(&environ_rwmtx);
}
-
-/************************** Port I/O *******************************/
-
-
-
-/* I. Common stuff */
-
-/*
- * Decreasing the size of it below 16384 is not allowed.
- */
-
-/* II. The spawn/fd/vanilla drivers */
-
-#define ERTS_SYS_READ_BUF_SZ (64*1024)
-
-/* Driver interfaces */
-static ErlDrvData spawn_start(ErlDrvPort, char*, SysDriverOpts*);
-static ErlDrvData fd_start(ErlDrvPort, char*, SysDriverOpts*);
-#if FDBLOCK
-static void fd_async(void *);
-static void fd_ready_async(ErlDrvData drv_data, ErlDrvThreadData thread_data);
-#endif
-static ErlDrvSSizeT fd_control(ErlDrvData, unsigned int, char *, ErlDrvSizeT,
- char **, ErlDrvSizeT);
-static ErlDrvData vanilla_start(ErlDrvPort, char*, SysDriverOpts*);
-static int spawn_init(void);
-static void fd_stop(ErlDrvData);
-static void fd_flush(ErlDrvData);
-static void stop(ErlDrvData);
-static void ready_input(ErlDrvData, ErlDrvEvent);
-static void ready_output(ErlDrvData, ErlDrvEvent);
-static void output(ErlDrvData, char*, ErlDrvSizeT);
-static void outputv(ErlDrvData, ErlIOVec*);
-static void stop_select(ErlDrvEvent, void*);
-
-struct erl_drv_entry spawn_driver_entry = {
- spawn_init,
- spawn_start,
- stop,
- output,
- ready_input,
- ready_output,
- "spawn",
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- ERL_DRV_EXTENDED_MARKER,
- ERL_DRV_EXTENDED_MAJOR_VERSION,
- ERL_DRV_EXTENDED_MINOR_VERSION,
- ERL_DRV_FLAG_USE_PORT_LOCKING,
- NULL, NULL,
- stop_select
-};
-struct erl_drv_entry fd_driver_entry = {
- NULL,
- fd_start,
- fd_stop,
- output,
- ready_input,
- ready_output,
- "fd",
- NULL,
- NULL,
- fd_control,
- NULL,
- outputv,
-#if FDBLOCK
- fd_ready_async, /* ready_async */
-#else
- NULL,
-#endif
- fd_flush, /* flush */
- NULL, /* call */
- NULL, /* event */
- ERL_DRV_EXTENDED_MARKER,
- ERL_DRV_EXTENDED_MAJOR_VERSION,
- ERL_DRV_EXTENDED_MINOR_VERSION,
- 0, /* ERL_DRV_FLAGs */
- NULL, /* handle2 */
- NULL, /* process_exit */
- stop_select
-};
-struct erl_drv_entry vanilla_driver_entry = {
- NULL,
- vanilla_start,
- stop,
- output,
- ready_input,
- ready_output,
- "vanilla",
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL, /* flush */
- NULL, /* call */
- NULL, /* event */
- ERL_DRV_EXTENDED_MARKER,
- ERL_DRV_EXTENDED_MAJOR_VERSION,
- ERL_DRV_EXTENDED_MINOR_VERSION,
- 0, /* ERL_DRV_FLAGs */
- NULL, /* handle2 */
- NULL, /* process_exit */
- stop_select
-};
-
-/* Handle SIGCHLD signals. */
-#if (defined(SIG_SIGSET) || defined(SIG_SIGNAL))
-static RETSIGTYPE onchld(void)
-#else
-static RETSIGTYPE onchld(int signum)
-#endif
-{
-#if CHLDWTHR
- ASSERT(0); /* We should *never* catch a SIGCHLD signal */
-#elif defined(ERTS_SMP)
- smp_sig_notify('C');
-#else
- children_died = 1;
- ERTS_CHK_IO_AS_INTR(); /* Make sure we don't sleep in poll */
-#endif
-}
-
-static int set_blocking_data(struct driver_data *dd) {
-
- dd->blocking = erts_alloc(ERTS_ALC_T_SYS_BLOCKING, sizeof(ErtsSysBlocking));
-
- erts_smp_atomic_add_nob(&sys_misc_mem_sz, sizeof(ErtsSysBlocking));
-
- dd->blocking->pdl = driver_pdl_create(dd->port_num);
- dd->blocking->res = 0;
- dd->blocking->err = 0;
- dd->blocking->pkey = driver_async_port_key(dd->port_num);
-
- return 1;
-}
-
-static int set_driver_data(ErlDrvPort port_num,
- int ifd,
- int ofd,
- int packet_bytes,
- int read_write,
- int exit_status,
- int pid,
- int is_blocking)
-{
- Port *prt;
- ErtsSysReportExit *report_exit;
-
- if (!exit_status)
- report_exit = NULL;
- else {
- report_exit = erts_alloc(ERTS_ALC_T_PRT_REP_EXIT,
- sizeof(ErtsSysReportExit));
- report_exit->next = report_exit_list;
- report_exit->port = erts_drvport2id(port_num);
- report_exit->pid = pid;
- report_exit->ifd = read_write & DO_READ ? ifd : -1;
- report_exit->ofd = read_write & DO_WRITE ? ofd : -1;
-#if CHLDWTHR && !defined(ERTS_SMP)
- report_exit->status = 0;
-#endif
- report_exit_list = report_exit;
- }
-
- prt = erts_drvport2port(port_num);
- if (prt != ERTS_INVALID_ERL_DRV_PORT)
- prt->os_pid = pid;
-
- if (read_write & DO_READ) {
- driver_data[ifd].packet_bytes = packet_bytes;
- driver_data[ifd].port_num = port_num;
- driver_data[ifd].report_exit = report_exit;
- driver_data[ifd].pid = pid;
- driver_data[ifd].alive = 1;
- driver_data[ifd].status = 0;
- driver_data[ifd].terminating = 0;
- driver_data[ifd].blocking = NULL;
- if (read_write & DO_WRITE) {
- driver_data[ifd].ofd = ofd;
- if (is_blocking && FDBLOCK)
- if (!set_blocking_data(driver_data+ifd))
- return -1;
- if (ifd != ofd)
- driver_data[ofd] = driver_data[ifd]; /* structure copy */
- } else { /* DO_READ only */
- driver_data[ifd].ofd = -1;
- }
- (void) driver_select(port_num, ifd, (ERL_DRV_READ|ERL_DRV_USE), 1);
- return(ifd);
- } else { /* DO_WRITE only */
- driver_data[ofd].packet_bytes = packet_bytes;
- driver_data[ofd].port_num = port_num;
- driver_data[ofd].report_exit = report_exit;
- driver_data[ofd].ofd = ofd;
- driver_data[ofd].pid = pid;
- driver_data[ofd].alive = 1;
- driver_data[ofd].status = 0;
- driver_data[ofd].terminating = 0;
- driver_data[ofd].blocking = NULL;
- if (is_blocking && FDBLOCK)
- if (!set_blocking_data(driver_data+ofd))
- return -1;
- return(ofd);
- }
-}
-
-static int spawn_init()
-{
- int i;
-#if CHLDWTHR
- erts_thr_opts_t thr_opts = ERTS_THR_OPTS_DEFAULT_INITER;
-
- thr_opts.detached = 0;
- thr_opts.suggested_stack_size = 0; /* Smallest possible */
- thr_opts.name = "child_waiter";
-#endif
-
- sys_signal(SIGPIPE, SIG_IGN); /* Ignore - we'll handle the write failure */
- driver_data = (struct driver_data *)
- erts_alloc(ERTS_ALC_T_DRV_TAB, max_files * sizeof(struct driver_data));
- erts_smp_atomic_add_nob(&sys_misc_mem_sz,
- max_files * sizeof(struct driver_data));
-
- for (i = 0; i < max_files; i++)
- driver_data[i].pid = -1;
-
-#if CHLDWTHR
- sys_sigblock(SIGCHLD);
-#endif
-
- sys_signal(SIGCHLD, onchld); /* Reap children */
-
-#if CHLDWTHR
- erts_thr_create(&child_waiter_tid, child_waiter, NULL, &thr_opts);
-#endif
-
- return 1;
-}
-
-static void close_pipes(int ifd[2], int ofd[2], int read_write)
-{
- if (read_write & DO_READ) {
- (void) close(ifd[0]);
- (void) close(ifd[1]);
- }
- if (read_write & DO_WRITE) {
- (void) close(ofd[0]);
- (void) close(ofd[1]);
- }
-}
-
-static void init_fd_data(int fd, ErlDrvPort port_num)
-{
- fd_data[fd].buf = NULL;
- fd_data[fd].cpos = NULL;
- fd_data[fd].remain = 0;
- fd_data[fd].sz = 0;
- fd_data[fd].psz = 0;
-}
-
-static char **build_unix_environment(char *block)
-{
- int i;
- int j;
- int len;
- char *cp;
- char **cpp;
- char** old_env;
-
- ERTS_SMP_LC_ASSERT(erts_smp_lc_rwmtx_is_rlocked(&environ_rwmtx));
-
- cp = block;
- len = 0;
- while (*cp != '\0') {
- cp += strlen(cp) + 1;
- len++;
- }
- old_env = environ;
- while (*old_env++ != NULL) {
- len++;
- }
-
- cpp = (char **) erts_alloc_fnf(ERTS_ALC_T_ENVIRONMENT,
- sizeof(char *) * (len+1));
- if (cpp == NULL) {
- return NULL;
- }
-
- cp = block;
- len = 0;
- while (*cp != '\0') {
- cpp[len] = cp;
- cp += strlen(cp) + 1;
- len++;
- }
-
- i = len;
- for (old_env = environ; *old_env; old_env++) {
- char* old = *old_env;
-
- for (j = 0; j < len; j++) {
- char *s, *t;
-
- s = cpp[j];
- t = old;
- while (*s == *t && *s != '=') {
- s++, t++;
- }
- if (*s == '=' && *t == '=') {
- break;
- }
- }
-
- if (j == len) { /* New version not found */
- cpp[len++] = old;
- }
- }
-
- for (j = 0; j < i; ) {
- size_t last = strlen(cpp[j])-1;
- if (cpp[j][last] == '=' && strchr(cpp[j], '=') == cpp[j]+last) {
- cpp[j] = cpp[--len];
- if (len < i) {
- i--;
- } else {
- j++;
- }
- }
- else {
- j++;
- }
- }
-
- cpp[len] = NULL;
- return cpp;
-}
-
-/*
- [arndt] In most Unix systems, including Solaris 2.5, 'fork' allocates memory
- in swap space for the child of a 'fork', whereas 'vfork' does not do this.
- The natural call to use here is therefore 'vfork'. Due to a bug in
- 'vfork' in Solaris 2.5 (apparently fixed in 2.6), using 'vfork'
- can be dangerous in what seems to be these circumstances:
- If the child code under a vfork sets the signal action to SIG_DFL
- (or SIG_IGN)
- for any signal which was previously set to a signal handler, the
- state of the parent is clobbered, so that the later arrival of
- such a signal yields a sigsegv in the parent. If the signal was
- not set to a signal handler, but ignored, all seems to work.
- If you change the forking code below, beware of this.
- */
-
-static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts)
-{
-#define CMD_LINE_PREFIX_STR "exec "
-#define CMD_LINE_PREFIX_STR_SZ (sizeof(CMD_LINE_PREFIX_STR) - 1)
-
- int ifd[2], ofd[2], len, pid, i;
- char **volatile new_environ; /* volatile since a vfork() then cannot
- cause 'new_environ' to be clobbered
- in the parent process. */
- int saved_errno;
- long res;
- char *cmd_line;
-#ifndef QNX
- int unbind;
-#endif
-#if !DISABLE_VFORK
- int no_vfork;
- size_t no_vfork_sz = sizeof(no_vfork);
-
- no_vfork = (erts_sys_getenv_raw("ERL_NO_VFORK",
- (char *) &no_vfork,
- &no_vfork_sz) >= 0);
-#endif
-
- switch (opts->read_write) {
- case DO_READ:
- if (pipe(ifd) < 0)
- return ERL_DRV_ERROR_ERRNO;
- if (ifd[0] >= max_files) {
- close_pipes(ifd, ofd, opts->read_write);
- errno = EMFILE;
- return ERL_DRV_ERROR_ERRNO;
- }
- ofd[1] = -1; /* keep purify happy */
- break;
- case DO_WRITE:
- if (pipe(ofd) < 0) return ERL_DRV_ERROR_ERRNO;
- if (ofd[1] >= max_files) {
- close_pipes(ifd, ofd, opts->read_write);
- errno = EMFILE;
- return ERL_DRV_ERROR_ERRNO;
- }
- ifd[0] = -1; /* keep purify happy */
- break;
- case DO_READ|DO_WRITE:
- if (pipe(ifd) < 0) return ERL_DRV_ERROR_ERRNO;
- errno = EMFILE; /* default for next two conditions */
- if (ifd[0] >= max_files || pipe(ofd) < 0) {
- close_pipes(ifd, ofd, DO_READ);
- return ERL_DRV_ERROR_ERRNO;
- }
- if (ofd[1] >= max_files) {
- close_pipes(ifd, ofd, opts->read_write);
- errno = EMFILE;
- return ERL_DRV_ERROR_ERRNO;
- }
- break;
- default:
- ASSERT(0);
- return ERL_DRV_ERROR_GENERAL;
- }
-
- if (opts->spawn_type == ERTS_SPAWN_EXECUTABLE) {
- /* started with spawn_executable, not with spawn */
- len = strlen(name);
- cmd_line = (char *) erts_alloc_fnf(ERTS_ALC_T_TMP, len + 1);
- if (!cmd_line) {
- close_pipes(ifd, ofd, opts->read_write);
- errno = ENOMEM;
- return ERL_DRV_ERROR_ERRNO;
- }
- memcpy((void *) cmd_line,(void *) name, len);
- cmd_line[len] = '\0';
- if (access(cmd_line,X_OK) != 0) {
- int save_errno = errno;
- erts_free(ERTS_ALC_T_TMP, cmd_line);
- errno = save_errno;
- return ERL_DRV_ERROR_ERRNO;
- }
- } else {
- /* make the string suitable for giving to "sh" */
- len = strlen(name);
- cmd_line = (char *) erts_alloc_fnf(ERTS_ALC_T_TMP,
- CMD_LINE_PREFIX_STR_SZ + len + 1);
- if (!cmd_line) {
- close_pipes(ifd, ofd, opts->read_write);
- errno = ENOMEM;
- return ERL_DRV_ERROR_ERRNO;
- }
- memcpy((void *) cmd_line,
- (void *) CMD_LINE_PREFIX_STR,
- CMD_LINE_PREFIX_STR_SZ);
- memcpy((void *) (cmd_line + CMD_LINE_PREFIX_STR_SZ), (void *) name, len);
- cmd_line[CMD_LINE_PREFIX_STR_SZ + len] = '\0';
- }
-
- erts_smp_rwmtx_rlock(&environ_rwmtx);
-
- if (opts->envir == NULL) {
- new_environ = environ;
- } else if ((new_environ = build_unix_environment(opts->envir)) == NULL) {
- erts_smp_rwmtx_runlock(&environ_rwmtx);
- erts_free(ERTS_ALC_T_TMP, (void *) cmd_line);
- errno = ENOMEM;
- return ERL_DRV_ERROR_ERRNO;
- }
-
-#ifndef QNX
- /* Block child from SIGINT and SIGUSR1. Must be before fork()
- to be safe. */
- block_signals();
-
- CHLD_STAT_LOCK;
-
- unbind = erts_sched_bind_atfork_prepare();
-
-#if !DISABLE_VFORK
- /* See fork/vfork discussion before this function. */
- if (no_vfork) {
-#endif
-
- DEBUGF(("Using fork\n"));
- pid = fork();
-
- if (pid == 0) {
- /* The child! Setup child... */
-
- if (erts_sched_bind_atfork_child(unbind) != 0)
- goto child_error;
-
- /* OBSERVE!
- * Keep child setup after vfork() (implemented below and in
- * erl_child_setup.c) up to date if changes are made here.
- */
-
- if (opts->use_stdio) {
- if (opts->read_write & DO_READ) {
- /* stdout for process */
- if (dup2(ifd[1], 1) < 0)
- goto child_error;
- if(opts->redir_stderr)
- /* stderr for process */
- if (dup2(ifd[1], 2) < 0)
- goto child_error;
- }
- if (opts->read_write & DO_WRITE)
- /* stdin for process */
- if (dup2(ofd[0], 0) < 0)
- goto child_error;
- }
- else { /* XXX will fail if ofd[0] == 4 (unlikely..) */
- if (opts->read_write & DO_READ)
- if (dup2(ifd[1], 4) < 0)
- goto child_error;
- if (opts->read_write & DO_WRITE)
- if (dup2(ofd[0], 3) < 0)
- goto child_error;
- }
-
-#if defined(HAVE_CLOSEFROM)
- closefrom(opts->use_stdio ? 3 : 5);
-#else
- for (i = opts->use_stdio ? 3 : 5; i < max_files; i++)
- (void) close(i);
-#endif
-
- if (opts->wd && chdir(opts->wd) < 0)
- goto child_error;
-
-#if defined(USE_SETPGRP_NOARGS) /* SysV */
- (void) setpgrp();
-#elif defined(USE_SETPGRP) /* BSD */
- (void) setpgrp(0, getpid());
-#else /* POSIX */
- (void) setsid();
-#endif
-
- unblock_signals();
-
- if (opts->spawn_type == ERTS_SPAWN_EXECUTABLE) {
- if (opts->argv == NULL) {
- execle(cmd_line,cmd_line,(char *) NULL, new_environ);
- } else {
- if (opts->argv[0] == erts_default_arg0) {
- opts->argv[0] = cmd_line;
- }
- execve(cmd_line, opts->argv, new_environ);
- if (opts->argv[0] == cmd_line) {
- opts->argv[0] = erts_default_arg0;
- }
- }
- } else {
- execle(SHELL, "sh", "-c", cmd_line, (char *) NULL, new_environ);
- }
- child_error:
- _exit(1);
- }
-#if !DISABLE_VFORK
- }
-#define ENOUGH_BYTES (44)
- else { /* Use vfork() */
- char **cs_argv= erts_alloc(ERTS_ALC_T_TMP,(CS_ARGV_NO_OF_ARGS + 1)*
- sizeof(char *));
- char fd_close_range[ENOUGH_BYTES]; /* 44 bytes are enough to */
- char dup2_op[CS_ARGV_NO_OF_DUP2_OPS][ENOUGH_BYTES]; /* hold any "%d:%d" string */
- /* on a 64-bit machine. */
-
- /* Setup argv[] for the child setup program (implemented in
- erl_child_setup.c) */
- i = 0;
- if (opts->use_stdio) {
- if (opts->read_write & DO_READ){
- /* stdout for process */
- erts_snprintf(&dup2_op[i++][0], ENOUGH_BYTES, "%d:%d", ifd[1], 1);
- if(opts->redir_stderr)
- /* stderr for process */
- erts_snprintf(&dup2_op[i++][0], ENOUGH_BYTES, "%d:%d", ifd[1], 2);
- }
- if (opts->read_write & DO_WRITE)
- /* stdin for process */
- erts_snprintf(&dup2_op[i++][0], ENOUGH_BYTES, "%d:%d", ofd[0], 0);
- } else { /* XXX will fail if ofd[0] == 4 (unlikely..) */
- if (opts->read_write & DO_READ)
- erts_snprintf(&dup2_op[i++][0], ENOUGH_BYTES, "%d:%d", ifd[1], 4);
- if (opts->read_write & DO_WRITE)
- erts_snprintf(&dup2_op[i++][0], ENOUGH_BYTES, "%d:%d", ofd[0], 3);
- }
- for (; i < CS_ARGV_NO_OF_DUP2_OPS; i++)
- strcpy(&dup2_op[i][0], "-");
- erts_snprintf(fd_close_range, ENOUGH_BYTES, "%d:%d", opts->use_stdio ? 3 : 5, max_files-1);
-
- cs_argv[CS_ARGV_PROGNAME_IX] = child_setup_prog;
- cs_argv[CS_ARGV_WD_IX] = opts->wd ? opts->wd : ".";
- cs_argv[CS_ARGV_UNBIND_IX] = erts_sched_bind_atvfork_child(unbind);
- cs_argv[CS_ARGV_FD_CR_IX] = fd_close_range;
- for (i = 0; i < CS_ARGV_NO_OF_DUP2_OPS; i++)
- cs_argv[CS_ARGV_DUP2_OP_IX(i)] = &dup2_op[i][0];
-
- if (opts->spawn_type == ERTS_SPAWN_EXECUTABLE) {
- int num = 0;
- int j = 0;
- if (opts->argv != NULL) {
- for(; opts->argv[num] != NULL; ++num)
- ;
- }
- cs_argv = erts_realloc(ERTS_ALC_T_TMP,cs_argv, (CS_ARGV_NO_OF_ARGS + 1 + num + 1) * sizeof(char *));
- cs_argv[CS_ARGV_CMD_IX] = "-";
- cs_argv[CS_ARGV_NO_OF_ARGS] = cmd_line;
- if (opts->argv != NULL) {
- for (;opts->argv[j] != NULL; ++j) {
- if (opts->argv[j] == erts_default_arg0) {
- cs_argv[CS_ARGV_NO_OF_ARGS + 1 + j] = cmd_line;
- } else {
- cs_argv[CS_ARGV_NO_OF_ARGS + 1 + j] = opts->argv[j];
- }
- }
- }
- cs_argv[CS_ARGV_NO_OF_ARGS + 1 + j] = NULL;
- } else {
- cs_argv[CS_ARGV_CMD_IX] = cmd_line; /* Command */
- cs_argv[CS_ARGV_NO_OF_ARGS] = NULL;
- }
- DEBUGF(("Using vfork\n"));
- pid = vfork();
-
- if (pid == 0) {
- /* The child! */
-
- /* Observe!
- * OTP-4389: The child setup program (implemented in
- * erl_child_setup.c) will perform the necessary setup of the
- * child before it execs to the user program. This because
- * vfork() only allow an *immediate* execve() or _exit() in the
- * child.
- */
- execve(child_setup_prog, cs_argv, new_environ);
- _exit(1);
- }
- erts_free(ERTS_ALC_T_TMP,cs_argv);
- }
-#undef ENOUGH_BYTES
-#endif
-
- erts_sched_bind_atfork_parent(unbind);
-
- if (pid == -1) {
- saved_errno = errno;
- CHLD_STAT_UNLOCK;
- erts_smp_rwmtx_runlock(&environ_rwmtx);
- erts_free(ERTS_ALC_T_TMP, (void *) cmd_line);
- unblock_signals();
- close_pipes(ifd, ofd, opts->read_write);
- errno = saved_errno;
- return ERL_DRV_ERROR_ERRNO;
- }
-#else /* QNX */
- if (opts->use_stdio) {
- if (opts->read_write & DO_READ)
- qnx_spawn_options.iov[1] = ifd[1]; /* stdout for process */
- if (opts->read_write & DO_WRITE)
- qnx_spawn_options.iov[0] = ofd[0]; /* stdin for process */
- }
- else {
- if (opts->read_write & DO_READ)
- qnx_spawn_options.iov[4] = ifd[1];
- if (opts->read_write & DO_WRITE)
- qnx_spawn_options.iov[3] = ofd[0];
- }
- /* Close fds on exec */
- for (i = 3; i < max_files; i++)
- fcntl(i, F_SETFD, 1);
-
- qnx_spawn_options.flags = _SPAWN_SETSID;
- if ((pid = spawnl(P_NOWAIT, SHELL, SHELL, "-c", cmd_line,
- (char *) 0)) < 0) {
- erts_free(ERTS_ALC_T_TMP, (void *) cmd_line);
- reset_qnx_spawn();
- erts_smp_rwmtx_runlock(&environ_rwmtx);
- close_pipes(ifd, ofd, opts->read_write);
- return ERL_DRV_ERROR_GENERAL;
- }
- reset_qnx_spawn();
-#endif /* QNX */
-
- erts_free(ERTS_ALC_T_TMP, (void *) cmd_line);
-
- if (new_environ != environ)
- erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ);
-
- if (opts->read_write & DO_READ)
- (void) close(ifd[1]);
- if (opts->read_write & DO_WRITE)
- (void) close(ofd[0]);
-
- if (opts->read_write & DO_READ) {
- SET_NONBLOCKING(ifd[0]);
- init_fd_data(ifd[0], port_num);
- }
- if (opts->read_write & DO_WRITE) {
- SET_NONBLOCKING(ofd[1]);
- init_fd_data(ofd[1], port_num);
- }
-
- res = set_driver_data(port_num, ifd[0], ofd[1], opts->packet_bytes,
- opts->read_write, opts->exit_status, pid, 0);
- /* Don't unblock SIGCHLD until now, since the call above must
- first complete putting away the info about our new subprocess. */
- unblock_signals();
-
-#if CHLDWTHR
- ASSERT(children_alive >= 0);
-
- if (!(children_alive++))
- CHLD_STAT_SIGNAL; /* Wake up child waiter thread if no children
- was alive before we fork()ed ... */
-#endif
- /* Don't unlock chld_stat_mtx until now of the same reason as above */
- CHLD_STAT_UNLOCK;
-
- erts_smp_rwmtx_runlock(&environ_rwmtx);
-
- return (ErlDrvData)res;
-#undef CMD_LINE_PREFIX_STR
-#undef CMD_LINE_PREFIX_STR_SZ
-}
-
-#ifdef QNX
-static reset_qnx_spawn()
-{
- int i;
-
- /* Reset qnx_spawn_options */
- qnx_spawn_options.flags = 0;
- qnx_spawn_options.iov[0] = 0xff;
- qnx_spawn_options.iov[1] = 0xff;
- qnx_spawn_options.iov[2] = 0xff;
- qnx_spawn_options.iov[3] = 0xff;
-}
-#endif
-
-#define FD_DEF_HEIGHT 24
-#define FD_DEF_WIDTH 80
-/* Control op */
-#define FD_CTRL_OP_GET_WINSIZE 100
-
-static int fd_get_window_size(int fd, Uint32 *width, Uint32 *height)
-{
-#ifdef TIOCGWINSZ
- struct winsize ws;
- if (ioctl(fd,TIOCGWINSZ,&ws) == 0) {
- *width = (Uint32) ws.ws_col;
- *height = (Uint32) ws.ws_row;
- return 0;
- }
-#endif
- return -1;
-}
-
-static ErlDrvSSizeT fd_control(ErlDrvData drv_data,
- unsigned int command,
- char *buf, ErlDrvSizeT len,
- char **rbuf, ErlDrvSizeT rlen)
-{
- int fd = (int)(long)drv_data;
- char resbuff[2*sizeof(Uint32)];
- switch (command) {
- case FD_CTRL_OP_GET_WINSIZE:
- {
- Uint32 w,h;
- if (fd_get_window_size(fd,&w,&h))
- return 0;
- memcpy(resbuff,&w,sizeof(Uint32));
- memcpy(resbuff+sizeof(Uint32),&h,sizeof(Uint32));
- }
- break;
- default:
- return 0;
- }
- if (rlen < 2*sizeof(Uint32)) {
- *rbuf = driver_alloc(2*sizeof(Uint32));
- }
- memcpy(*rbuf,resbuff,2*sizeof(Uint32));
- return 2*sizeof(Uint32);
-}
-
-static ErlDrvData fd_start(ErlDrvPort port_num, char* name,
- SysDriverOpts* opts)
-{
- ErlDrvData res;
- int non_blocking = 0;
-
- if (((opts->read_write & DO_READ) && opts->ifd >= max_files) ||
- ((opts->read_write & DO_WRITE) && opts->ofd >= max_files))
- return ERL_DRV_ERROR_GENERAL;
-
- /*
- * Historical:
- *
- * "Note about nonblocking I/O.
- *
- * At least on Solaris, setting the write end of a TTY to nonblocking,
- * will set the input end to nonblocking as well (and vice-versa).
- * If erl is run in a pipeline like this: cat | erl
- * the input end of the TTY will be the standard input of cat.
- * And cat is not prepared to handle nonblocking I/O."
- *
- * Actually, the reason for this is not that the tty itself gets set
- * in non-blocking mode, but that the "input end" (cat's stdin) and
- * the "output end" (erlang's stdout) are typically the "same" file
- * descriptor, dup()'ed from a single fd by one of this process'
- * ancestors.
- *
- * The workaround for this problem used to be a rather bad kludge,
- * interposing an extra process ("internal cat") between erlang's
- * stdout and the original stdout, allowing erlang to set its stdout
- * in non-blocking mode without affecting the stdin of the preceding
- * process in the pipeline - and being a kludge, it caused all kinds
- * of weird problems.
- *
- * So, this is the current logic:
- *
- * The only reason to set non-blocking mode on the output fd at all is
- * if it's something that can cause a write() to block, of course,
- * i.e. primarily if it points to a tty, socket, pipe, or fifo.
- *
- * If we don't set non-blocking mode when we "should" have, and output
- * becomes blocked, the entire runtime system will be suspended - this
- * is normally bad of course, and can happen fairly "easily" - e.g. user
- * hits ^S on tty - but doesn't necessarily happen.
- *
- * If we do set non-blocking mode when we "shouldn't" have, the runtime
- * system will end up seeing EOF on the input fd (due to the preceding
- * process dying), which typically will cause the entire runtime system
- * to terminate immediately (due to whatever erlang process is seeing
- * the EOF taking it as a signal to halt the system). This is *very* bad.
- *
- * I.e. we should take a conservative approach, and only set non-
- * blocking mode when we a) need to, and b) are reasonably certain
- * that it won't be a problem. And as in the example above, the problem
- * occurs when input fd and output fd point to different "things".
- *
- * However, determining that they are not just the same "type" of
- * "thing", but actually the same instance of that type of thing, is
- * unreasonably complex in many/most cases.
- *
- * Also, with pipes, sockets, and fifos it's far from obvious that the
- * user *wants* non-blocking output: If you're running erlang inside
- * some complex pipeline, you're probably not running a real-time system
- * that must never stop, but rather *want* it to suspend if the output
- * channel is "full".
- *
- * So, the bottom line: We will only set the output fd non-blocking if
- * it points to a tty, and either a) the input fd also points to a tty,
- * or b) we can make sure that setting the output fd non-blocking
- * doesn't interfere with someone else's input, via a somewhat milder
- * kludge than the above.
- *
- * Also keep in mind that while this code is almost exclusively run as
- * a result of an erlang open_port({fd,0,1}, ...), that isn't the only
- * case - it can be called with any old pre-existing file descriptors,
- * the relations between which (if they're even two) we can only guess
- * at - still, we try our best...
- *
- * Added note OTP 18: Some systems seem to use stdout/stderr to log data
- * using unix pipes, so we cannot allow the system to block on a write.
- * Therefore we use an async thread to write the data to fd's that could
- * not be set to non-blocking. When no async threads are available we
- * fall back on the old behaviour.
- *
- * Also the guarantee about what is delivered to the OS has changed.
- * Pre 18 the fd driver did no flushing of data before terminating.
- * Now it does. This is because we want to be able to guarantee that things
- * such as escripts and friends really have outputted all data before
- * terminating. This could potentially block the termination of the system
- * for a very long time, but if the user wants to terminate fast she should
- * use erlang:halt with flush=false.
- */
-
- if (opts->read_write & DO_READ) {
- init_fd_data(opts->ifd, port_num);
- }
- if (opts->read_write & DO_WRITE) {
- init_fd_data(opts->ofd, port_num);
-
- /* If we don't have a read end, all bets are off - no non-blocking. */
- if (opts->read_write & DO_READ) {
-
- if (isatty(opts->ofd)) { /* output fd is a tty:-) */
-
- if (isatty(opts->ifd)) { /* input fd is also a tty */
-
- /* To really do this "right", we should also check that
- input and output fd point to the *same* tty - but
- this seems like overkill; ttyname() isn't for free,
- and this is a very common case - and it's hard to
- imagine a scenario where setting non-blocking mode
- here would cause problems - go ahead and do it. */
-
- non_blocking = 1;
- SET_NONBLOCKING(opts->ofd);
-
- } else { /* output fd is a tty, input fd isn't */
-
- /* This is a "problem case", but also common (see the
- example above) - i.e. it makes sense to try a bit
- harder before giving up on non-blocking mode: Try to
- re-open the tty that the output fd points to, and if
- successful replace the original one with the "new" fd
- obtained this way, and set *that* one in non-blocking
- mode. (Yes, this is a kludge.)
-
- However, re-opening the tty may fail in a couple of
- (unusual) cases:
-
- 1) The name of the tty (or an equivalent one, i.e.
- same major/minor number) can't be found, because
- it actually lives somewhere other than /dev (or
- wherever ttyname() looks for it), and isn't
- equivalent to any of those that do live in the
- "standard" place - this should be *very* unusual.
-
- 2) Permissions on the tty don't allow us to open it -
- it's perfectly possible to have an fd open to an
- object whose permissions wouldn't allow us to open
- it. This is not as unusual as it sounds, one case
- is if the user has su'ed to someone else (not
- root) - we have a read/write fd open to the tty
- (because it has been inherited all the way down
- here), but we have neither read nor write
- permission for the tty.
-
- In these cases, we finally give up, and don't set the
- output fd in non-blocking mode. */
-
- char *tty;
- int nfd;
-
- if ((tty = ttyname(opts->ofd)) != NULL &&
- (nfd = open(tty, O_WRONLY)) != -1) {
- dup2(nfd, opts->ofd);
- close(nfd);
- non_blocking = 1;
- SET_NONBLOCKING(opts->ofd);
- }
- }
- }
- }
- }
- CHLD_STAT_LOCK;
- res = (ErlDrvData)(long)set_driver_data(port_num, opts->ifd, opts->ofd,
- opts->packet_bytes,
- opts->read_write, 0, -1,
- !non_blocking);
- CHLD_STAT_UNLOCK;
- return res;
-}
-
-static void clear_fd_data(int fd)
-{
- if (fd_data[fd].sz > 0) {
- erts_free(ERTS_ALC_T_FD_ENTRY_BUF, (void *) fd_data[fd].buf);
- ASSERT(erts_smp_atomic_read_nob(&sys_misc_mem_sz) >= fd_data[fd].sz);
- erts_smp_atomic_add_nob(&sys_misc_mem_sz, -1*fd_data[fd].sz);
- }
- fd_data[fd].buf = NULL;
- fd_data[fd].sz = 0;
- fd_data[fd].remain = 0;
- fd_data[fd].cpos = NULL;
- fd_data[fd].psz = 0;
-}
-
-static void nbio_stop_fd(ErlDrvPort prt, int fd)
-{
- driver_select(prt,fd,DO_READ|DO_WRITE,0);
- clear_fd_data(fd);
- SET_BLOCKING(fd);
-}
-
-static void fd_stop(ErlDrvData ev) /* Does not close the fds */
-{
- int ofd;
- int fd = (int)(long)ev;
- ErlDrvPort prt = driver_data[fd].port_num;
-
-#if FDBLOCK
- if (driver_data[fd].blocking) {
- erts_free(ERTS_ALC_T_SYS_BLOCKING,driver_data[fd].blocking);
- driver_data[fd].blocking = NULL;
- erts_smp_atomic_add_nob(&sys_misc_mem_sz, -1*sizeof(ErtsSysBlocking));
- }
-#endif
-
- nbio_stop_fd(prt, fd);
- ofd = driver_data[fd].ofd;
- if (ofd != fd && ofd != -1)
- nbio_stop_fd(prt, ofd);
-}
-
-static void fd_flush(ErlDrvData fd)
-{
- if (!driver_data[(int)(long)fd].terminating)
- driver_data[(int)(long)fd].terminating = 1;
-}
-
-static ErlDrvData vanilla_start(ErlDrvPort port_num, char* name,
- SysDriverOpts* opts)
-{
- int flags, fd;
- ErlDrvData res;
-
- flags = (opts->read_write == DO_READ ? O_RDONLY :
- opts->read_write == DO_WRITE ? O_WRONLY|O_CREAT|O_TRUNC :
- O_RDWR|O_CREAT);
- if ((fd = open(name, flags, 0666)) < 0)
- return ERL_DRV_ERROR_GENERAL;
- if (fd >= max_files) {
- close(fd);
- return ERL_DRV_ERROR_GENERAL;
- }
- SET_NONBLOCKING(fd);
- init_fd_data(fd, port_num);
-
- CHLD_STAT_LOCK;
- res = (ErlDrvData)(long)set_driver_data(port_num, fd, fd,
- opts->packet_bytes,
- opts->read_write, 0, -1, 0);
- CHLD_STAT_UNLOCK;
- return res;
-}
-
-/* Note that driver_data[fd].ifd == fd if the port was opened for reading, */
-/* otherwise (i.e. write only) driver_data[fd].ofd = fd. */
-
-static void stop(ErlDrvData fd)
-{
- ErlDrvPort prt;
- int ofd;
-
- prt = driver_data[(int)(long)fd].port_num;
- nbio_stop_fd(prt, (int)(long)fd);
-
- ofd = driver_data[(int)(long)fd].ofd;
- if (ofd != (int)(long)fd && (int)(long)ofd != -1)
- nbio_stop_fd(prt, ofd);
- else
- ofd = -1;
-
- CHLD_STAT_LOCK;
-
- /* Mark as unused. */
- driver_data[(int)(long)fd].pid = -1;
-
- CHLD_STAT_UNLOCK;
-
- /* SMP note: Close has to be last thing done (open file descriptors work
- as locks on driver_data[] entries) */
- driver_select(prt, (int)(long)fd, ERL_DRV_USE, 0); /* close(fd); */
- if (ofd >= 0) {
- driver_select(prt, (int)(long)ofd, ERL_DRV_USE, 0); /* close(ofd); */
- }
-}
-
-/* used by fd_driver */
-static void outputv(ErlDrvData e, ErlIOVec* ev)
-{
- int fd = (int)(long)e;
- ErlDrvPort ix = driver_data[fd].port_num;
- int pb = driver_data[fd].packet_bytes;
- int ofd = driver_data[fd].ofd;
- ssize_t n;
- ErlDrvSizeT sz;
- char lb[4];
- char* lbp;
- ErlDrvSizeT len = ev->size;
-
- /* (len > ((unsigned long)-1 >> (4-pb)*8)) */
- /* if (pb >= 0 && (len & (((ErlDrvSizeT)1 << (pb*8))) - 1) != len) {*/
- if (((pb == 2) && (len > 0xffff)) || (pb == 1 && len > 0xff)) {
- driver_failure_posix(ix, EINVAL);
- return; /* -1; */
- }
- /* Handles 0 <= pb <= 4 only */
- put_int32((Uint32) len, lb);
- lbp = lb + (4-pb);
-
- ev->iov[0].iov_base = lbp;
- ev->iov[0].iov_len = pb;
- ev->size += pb;
-
- if (driver_data[fd].blocking && FDBLOCK)
- driver_pdl_lock(driver_data[fd].blocking->pdl);
-
- if ((sz = driver_sizeq(ix)) > 0) {
- driver_enqv(ix, ev, 0);
-
- if (driver_data[fd].blocking && FDBLOCK)
- driver_pdl_unlock(driver_data[fd].blocking->pdl);
-
- if (sz + ev->size >= (1 << 13))
- set_busy_port(ix, 1);
- }
- else if (!driver_data[fd].blocking || !FDBLOCK) {
- /* We try to write directly if the fd in non-blocking */
- int vsize = ev->vsize > MAX_VSIZE ? MAX_VSIZE : ev->vsize;
-
- n = writev(ofd, (const void *) (ev->iov), vsize);
- if (n == ev->size)
- return; /* 0;*/
- if (n < 0) {
- if ((errno != EINTR) && (errno != ERRNO_BLOCK)) {
- driver_failure_posix(ix, errno);
- return; /* -1;*/
- }
- n = 0;
- }
- driver_enqv(ix, ev, n); /* n is the skip value */
- driver_select(ix, ofd, ERL_DRV_WRITE|ERL_DRV_USE, 1);
- }
-#if FDBLOCK
- else {
- if (ev->size != 0) {
- driver_enqv(ix, ev, 0);
- driver_pdl_unlock(driver_data[fd].blocking->pdl);
- driver_async(ix, &driver_data[fd].blocking->pkey,
- fd_async, driver_data+fd, NULL);
- } else {
- driver_pdl_unlock(driver_data[fd].blocking->pdl);
- }
- }
-#endif
- /* return 0;*/
-}
-
-/* Used by spawn_driver and vanilla driver */
-static void output(ErlDrvData e, char* buf, ErlDrvSizeT len)
-{
- int fd = (int)(long)e;
- ErlDrvPort ix = driver_data[fd].port_num;
- int pb = driver_data[fd].packet_bytes;
- int ofd = driver_data[fd].ofd;
- ssize_t n;
- ErlDrvSizeT sz;
- char lb[4];
- char* lbp;
- struct iovec iv[2];
-
- /* (len > ((unsigned long)-1 >> (4-pb)*8)) */
- if (((pb == 2) && (len > 0xffff)) || (pb == 1 && len > 0xff)) {
- driver_failure_posix(ix, EINVAL);
- return; /* -1; */
- }
- put_int32(len, lb);
- lbp = lb + (4-pb);
-
- if ((sz = driver_sizeq(ix)) > 0) {
- driver_enq(ix, lbp, pb);
- driver_enq(ix, buf, len);
- if (sz + len + pb >= (1 << 13))
- set_busy_port(ix, 1);
- }
- else {
- iv[0].iov_base = lbp;
- iv[0].iov_len = pb; /* should work for pb=0 */
- iv[1].iov_base = buf;
- iv[1].iov_len = len;
- n = writev(ofd, iv, 2);
- if (n == pb+len)
- return; /* 0; */
- if (n < 0) {
- if ((errno != EINTR) && (errno != ERRNO_BLOCK)) {
- driver_failure_posix(ix, errno);
- return; /* -1; */
- }
- n = 0;
- }
- if (n < pb) {
- driver_enq(ix, lbp+n, pb-n);
- driver_enq(ix, buf, len);
- }
- else {
- n -= pb;
- driver_enq(ix, buf+n, len-n);
- }
- driver_select(ix, ofd, ERL_DRV_WRITE|ERL_DRV_USE, 1);
- }
- return; /* 0; */
-}
-
-static int port_inp_failure(ErlDrvPort port_num, int ready_fd, int res)
- /* Result: 0 (eof) or -1 (error) */
-{
- int err = errno;
-
- ASSERT(res <= 0);
- (void) driver_select(port_num, ready_fd, ERL_DRV_READ|ERL_DRV_WRITE, 0);
- clear_fd_data(ready_fd);
-
- if (driver_data[ready_fd].blocking && FDBLOCK) {
- driver_pdl_lock(driver_data[ready_fd].blocking->pdl);
- if (driver_sizeq(driver_data[ready_fd].port_num) > 0) {
- driver_pdl_unlock(driver_data[ready_fd].blocking->pdl);
- /* We have stuff in the output queue, so we just
- set the state to terminating and wait for fd_async_ready
- to terminate the port */
- if (res == 0)
- driver_data[ready_fd].terminating = 2;
- else
- driver_data[ready_fd].terminating = -err;
- return 0;
- }
- driver_pdl_unlock(driver_data[ready_fd].blocking->pdl);
- }
-
- if (res == 0) {
- if (driver_data[ready_fd].report_exit) {
- CHLD_STAT_LOCK;
-
- if (driver_data[ready_fd].alive) {
- /*
- * We have eof and want to report exit status, but the process
- * hasn't exited yet. When it does report_exit_status() will
- * driver_select() this fd which will make sure that we get
- * back here with driver_data[ready_fd].alive == 0 and
- * driver_data[ready_fd].status set.
- */
- CHLD_STAT_UNLOCK;
- return 0;
- }
- else {
- int status = driver_data[ready_fd].status;
- CHLD_STAT_UNLOCK;
-
- /* We need not be prepared for stopped/continued processes. */
- if (WIFSIGNALED(status))
- status = 128 + WTERMSIG(status);
- else
- status = WEXITSTATUS(status);
-
- driver_report_exit(driver_data[ready_fd].port_num, status);
- }
- }
- driver_failure_eof(port_num);
- } else {
- driver_failure_posix(port_num, err);
- }
- return 0;
-}
-
-/* fd is the drv_data that is returned from the */
-/* initial start routine */
-/* ready_fd is the descriptor that is ready to read */
-
-static void ready_input(ErlDrvData e, ErlDrvEvent ready_fd)
-{
- int fd = (int)(long)e;
- ErlDrvPort port_num;
- int packet_bytes;
- int res;
- Uint h;
-
- port_num = driver_data[fd].port_num;
- packet_bytes = driver_data[fd].packet_bytes;
-
-
- if (packet_bytes == 0) {
- byte *read_buf = (byte *) erts_alloc(ERTS_ALC_T_SYS_READ_BUF,
- ERTS_SYS_READ_BUF_SZ);
- res = read(ready_fd, read_buf, ERTS_SYS_READ_BUF_SZ);
- if (res < 0) {
- if ((errno != EINTR) && (errno != ERRNO_BLOCK))
- port_inp_failure(port_num, ready_fd, res);
- }
- else if (res == 0)
- port_inp_failure(port_num, ready_fd, res);
- else
- driver_output(port_num, (char*) read_buf, res);
- erts_free(ERTS_ALC_T_SYS_READ_BUF, (void *) read_buf);
- }
- else if (fd_data[ready_fd].remain > 0) { /* We try to read the remainder */
- /* space is allocated in buf */
- res = read(ready_fd, fd_data[ready_fd].cpos,
- fd_data[ready_fd].remain);
- if (res < 0) {
- if ((errno != EINTR) && (errno != ERRNO_BLOCK))
- port_inp_failure(port_num, ready_fd, res);
- }
- else if (res == 0) {
- port_inp_failure(port_num, ready_fd, res);
- }
- else if (res == fd_data[ready_fd].remain) { /* we're done */
- driver_output(port_num, fd_data[ready_fd].buf,
- fd_data[ready_fd].sz);
- clear_fd_data(ready_fd);
- }
- else { /* if (res < fd_data[ready_fd].remain) */
- fd_data[ready_fd].cpos += res;
- fd_data[ready_fd].remain -= res;
- }
- }
- else if (fd_data[ready_fd].remain == 0) { /* clean fd */
- byte *read_buf = (byte *) erts_alloc(ERTS_ALC_T_SYS_READ_BUF,
- ERTS_SYS_READ_BUF_SZ);
- /* We make one read attempt and see what happens */
- res = read(ready_fd, read_buf, ERTS_SYS_READ_BUF_SZ);
- if (res < 0) {
- if ((errno != EINTR) && (errno != ERRNO_BLOCK))
- port_inp_failure(port_num, ready_fd, res);
- }
- else if (res == 0) { /* eof */
- port_inp_failure(port_num, ready_fd, res);
- }
- else if (res < packet_bytes - fd_data[ready_fd].psz) {
- memcpy(fd_data[ready_fd].pbuf+fd_data[ready_fd].psz,
- read_buf, res);
- fd_data[ready_fd].psz += res;
- }
- else { /* if (res >= packet_bytes) */
- unsigned char* cpos = read_buf;
- int bytes_left = res;
-
- while (1) {
- int psz = fd_data[ready_fd].psz;
- char* pbp = fd_data[ready_fd].pbuf + psz;
-
- while(bytes_left && (psz < packet_bytes)) {
- *pbp++ = *cpos++;
- bytes_left--;
- psz++;
- }
-
- if (psz < packet_bytes) {
- fd_data[ready_fd].psz = psz;
- break;
- }
- fd_data[ready_fd].psz = 0;
-
- switch (packet_bytes) {
- case 1: h = get_int8(fd_data[ready_fd].pbuf); break;
- case 2: h = get_int16(fd_data[ready_fd].pbuf); break;
- case 4: h = get_int32(fd_data[ready_fd].pbuf); break;
- default: ASSERT(0); return; /* -1; */
- }
-
- if (h <= (bytes_left)) {
- driver_output(port_num, (char*) cpos, h);
- cpos += h;
- bytes_left -= h;
- continue;
- }
- else { /* The last message we got was split */
- char *buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h);
- if (!buf) {
- errno = ENOMEM;
- port_inp_failure(port_num, ready_fd, -1);
- }
- else {
- erts_smp_atomic_add_nob(&sys_misc_mem_sz, h);
- sys_memcpy(buf, cpos, bytes_left);
- fd_data[ready_fd].buf = buf;
- fd_data[ready_fd].sz = h;
- fd_data[ready_fd].remain = h - bytes_left;
- fd_data[ready_fd].cpos = buf + bytes_left;
- }
- break;
- }
- }
- }
- erts_free(ERTS_ALC_T_SYS_READ_BUF, (void *) read_buf);
- }
-}
-
-
-/* fd is the drv_data that is returned from the */
-/* initial start routine */
-/* ready_fd is the descriptor that is ready to read */
-
-static void ready_output(ErlDrvData e, ErlDrvEvent ready_fd)
-{
- int fd = (int)(long)e;
- ErlDrvPort ix = driver_data[fd].port_num;
- int n;
- struct iovec* iv;
- int vsize;
-
-
- if ((iv = (struct iovec*) driver_peekq(ix, &vsize)) == NULL) {
- driver_select(ix, ready_fd, ERL_DRV_WRITE, 0);
- if (driver_data[fd].terminating)
- driver_failure_atom(driver_data[fd].port_num,"normal");
- return; /* 0; */
- }
- vsize = vsize > MAX_VSIZE ? MAX_VSIZE : vsize;
- if ((n = writev(ready_fd, iv, vsize)) > 0) {
- if (driver_deq(ix, n) == 0)
- set_busy_port(ix, 0);
- }
- else if (n < 0) {
- if (errno == ERRNO_BLOCK || errno == EINTR)
- return; /* 0; */
- else {
- int res = errno;
- driver_select(ix, ready_fd, ERL_DRV_WRITE, 0);
- driver_failure_posix(ix, res);
- return; /* -1; */
- }
- }
- return; /* 0; */
-}
-
-static void stop_select(ErlDrvEvent fd, void* _)
-{
- close((int)fd);
-}
-
-#if FDBLOCK
-
-static void
-fd_async(void *async_data)
-{
- int res;
- struct driver_data *dd = (struct driver_data*)async_data;
- SysIOVec *iov0;
- SysIOVec *iov;
- int iovlen;
- int err = 0;
- /* much of this code is stolen from efile_drv:invoke_writev */
- driver_pdl_lock(dd->blocking->pdl);
- iov0 = driver_peekq(dd->port_num, &iovlen);
- iovlen = iovlen < MAXIOV ? iovlen : MAXIOV;
- iov = erts_alloc_fnf(ERTS_ALC_T_SYS_WRITE_BUF,
- sizeof(SysIOVec)*iovlen);
- if (!iov) {
- res = -1;
- err = ENOMEM;
- driver_pdl_unlock(dd->blocking->pdl);
- } else {
- memcpy(iov,iov0,iovlen*sizeof(SysIOVec));
- driver_pdl_unlock(dd->blocking->pdl);
-
- do {
- res = writev(dd->ofd, iov, iovlen);
- } while (res < 0 && errno == EINTR);
- if (res < 0)
- err = errno;
-
- erts_free(ERTS_ALC_T_SYS_WRITE_BUF, iov);
- }
- dd->blocking->res = res;
- dd->blocking->err = err;
-}
-
-void fd_ready_async(ErlDrvData drv_data,
- ErlDrvThreadData thread_data) {
- struct driver_data *dd = (struct driver_data *)thread_data;
- ErlDrvPort port_num = dd->port_num;
-
- ASSERT(dd->blocking);
- ASSERT(dd == (driver_data + (int)(long)drv_data));
-
- if (dd->blocking->res > 0) {
- driver_pdl_lock(dd->blocking->pdl);
- if (driver_deq(port_num, dd->blocking->res) == 0) {
- driver_pdl_unlock(dd->blocking->pdl);
- set_busy_port(port_num, 0);
- if (dd->terminating) {
- /* The port is has been ordered to terminate
- from either fd_flush or port_inp_failure */
- if (dd->terminating == 1)
- driver_failure_atom(port_num, "normal");
- else if (dd->terminating == 2)
- driver_failure_eof(port_num);
- else if (dd->terminating < 0)
- driver_failure_posix(port_num, -dd->terminating);
- return; /* -1; */
- }
- } else {
- driver_pdl_unlock(dd->blocking->pdl);
- /* still data left to write in queue */
- driver_async(port_num, &dd->blocking->pkey, fd_async, dd, NULL);
- return /* 0; */;
- }
- } else if (dd->blocking->res < 0) {
- if (dd->blocking->err == ERRNO_BLOCK) {
- set_busy_port(port_num, 1);
- /* still data left to write in queue */
- driver_async(port_num, &dd->blocking->pkey, fd_async, dd, NULL);
- } else
- driver_failure_posix(port_num, dd->blocking->err);
- return; /* -1; */
- }
- return; /* 0; */
-}
-
-#endif
-
void erts_do_break_handling(void)
{
struct termios temp_mode;
@@ -2639,6 +981,13 @@ void erts_do_break_handling(void)
erts_smp_thr_progress_unblock();
}
+#ifdef ERTS_SIGNAL_SIGTERM
+void erts_handle_signal_sigterm(void) {
+ ERTS_CLEAR_SIGNAL_SIGTERM;
+ stop_requested();
+}
+#endif
+
/* Fills in the systems representation of the jam/beam process identifier.
** The Pid is put in STRING representation in the supplied buffer,
** no interpretatione of this should be done by the rest of the
@@ -2738,10 +1087,6 @@ erts_sys_unsetenv(char *key)
void
sys_init_io(void)
{
- fd_data = (struct fd_data *)
- erts_alloc(ERTS_ALC_T_FD_TAB, max_files * sizeof(struct fd_data));
- erts_smp_atomic_add_nob(&sys_misc_mem_sz,
- max_files * sizeof(struct fd_data));
}
#if (0) /* unused? */
@@ -2935,179 +1280,6 @@ erl_debug(char* fmt, ...)
#endif /* DEBUG */
-static ERTS_INLINE void
-report_exit_status(ErtsSysReportExit *rep, int status)
-{
- Port *pp;
-#ifdef ERTS_SMP
- CHLD_STAT_UNLOCK;
- pp = erts_thr_id2port_sflgs(rep->port,
- ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP);
- CHLD_STAT_LOCK;
-#else
- pp = erts_id2port_sflgs(rep->port,
- NULL,
- 0,
- ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP);
-#endif
- if (pp) {
- if (rep->ifd >= 0) {
- driver_data[rep->ifd].alive = 0;
- driver_data[rep->ifd].status = status;
- (void) driver_select(ERTS_Port2ErlDrvPort(pp),
- rep->ifd,
- (ERL_DRV_READ|ERL_DRV_USE),
- 1);
- }
- if (rep->ofd >= 0) {
- driver_data[rep->ofd].alive = 0;
- driver_data[rep->ofd].status = status;
- (void) driver_select(ERTS_Port2ErlDrvPort(pp),
- rep->ofd,
- (ERL_DRV_WRITE|ERL_DRV_USE),
- 1);
- }
-#ifdef ERTS_SMP
- erts_thr_port_release(pp);
-#else
- erts_port_release(pp);
-#endif
- }
- erts_free(ERTS_ALC_T_PRT_REP_EXIT, rep);
-}
-
-#if !CHLDWTHR /* ---------------------------------------------------------- */
-
-#define ERTS_REPORT_EXIT_STATUS report_exit_status
-
-static int check_children(void)
-{
- int res = 0;
- int pid;
- int status;
-
-#ifndef ERTS_SMP
- if (children_died)
-#endif
- {
- sys_sigblock(SIGCHLD);
- CHLD_STAT_LOCK;
- while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
- note_child_death(pid, status);
-#ifndef ERTS_SMP
- children_died = 0;
-#endif
- CHLD_STAT_UNLOCK;
- sys_sigrelease(SIGCHLD);
- res = 1;
- }
- return res;
-}
-
-#ifdef ERTS_SMP
-
-void
-erts_check_children(void)
-{
- (void) check_children();
-}
-
-#endif
-
-#elif CHLDWTHR && defined(ERTS_SMP) /* ------------------------------------- */
-
-#define ERTS_REPORT_EXIT_STATUS report_exit_status
-
-#define check_children() (0)
-
-
-#else /* CHLDWTHR && !defined(ERTS_SMP) ------------------------------------ */
-
-#define ERTS_REPORT_EXIT_STATUS initiate_report_exit_status
-
-static ERTS_INLINE void
-initiate_report_exit_status(ErtsSysReportExit *rep, int status)
-{
- rep->next = report_exit_transit_list;
- rep->status = status;
- report_exit_transit_list = rep;
- erts_sys_schedule_interrupt(1);
-}
-
-static int check_children(void)
-{
- int res;
- ErtsSysReportExit *rep;
- CHLD_STAT_LOCK;
- rep = report_exit_transit_list;
- res = rep != NULL;
- while (rep) {
- ErtsSysReportExit *curr_rep = rep;
- rep = rep->next;
- report_exit_status(curr_rep, curr_rep->status);
- }
- report_exit_transit_list = NULL;
- CHLD_STAT_UNLOCK;
- return res;
-}
-
-#endif /* ------------------------------------------------------------------ */
-
-static void note_child_death(int pid, int status)
-{
- ErtsSysReportExit **repp = &report_exit_list;
- ErtsSysReportExit *rep = report_exit_list;
-
- while (rep) {
- if (pid == rep->pid) {
- *repp = rep->next;
- ERTS_REPORT_EXIT_STATUS(rep, status);
- break;
- }
- repp = &rep->next;
- rep = rep->next;
- }
-}
-
-#if CHLDWTHR
-
-static void *
-child_waiter(void *unused)
-{
- int pid;
- int status;
-
-#ifdef ERTS_ENABLE_LOCK_CHECK
- erts_lc_set_thread_name("child waiter");
-#endif
-
- while(1) {
-#ifdef DEBUG
- int waitpid_errno;
-#endif
- pid = waitpid(-1, &status, 0);
-#ifdef DEBUG
- waitpid_errno = errno;
-#endif
- CHLD_STAT_LOCK;
- if (pid < 0) {
- ASSERT(waitpid_errno == ECHILD);
- }
- else {
- children_alive--;
- ASSERT(children_alive >= 0);
- note_child_death(pid, status);
- }
- while (!children_alive)
- CHLD_STAT_WAIT; /* Wait for children to wait on... :) */
- CHLD_STAT_UNLOCK;
- }
-
- return NULL;
-}
-
-#endif
-
/*
* Called from schedule() when it runs out of runnable processes,
* or when Erlang code has performed INPUT_REDUCTIONS reduction
@@ -3116,13 +1288,8 @@ child_waiter(void *unused)
void
erl_sys_schedule(int runnable)
{
-#ifdef ERTS_SMP
ERTS_CHK_IO(!runnable);
-#else
- ERTS_CHK_IO(runnable ? 0 : !check_children());
-#endif
ERTS_SMP_LC_ASSERT(!erts_thr_progress_is_blocking());
- (void) check_children();
}
@@ -3150,10 +1317,6 @@ smp_sig_notify(char c)
static void *
signal_dispatcher_thread_func(void *unused)
{
-#if !CHLDWTHR
- int initialized = 0;
- int notify_check_children = 0;
-#endif
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_lc_set_thread_name("signal_dispatcher");
#endif
@@ -3191,19 +1354,10 @@ signal_dispatcher_thread_func(void *unused)
*/
switch (buf[i]) {
case 0: /* Emulator initialized */
-#if !CHLDWTHR
- initialized = 1;
- if (!notify_check_children)
-#endif
- break;
-#if !CHLDWTHR
- case 'C': /* SIGCHLD */
- if (initialized)
- erts_smp_notify_check_children_needed();
- else
- notify_check_children = 1;
+ break;
+ case 'S': /* SIGTERM */
+ stop_requested();
break;
-#endif
case 'I': /* SIGINT */
break_requested();
break;
@@ -3248,12 +1402,14 @@ init_smp_sig_notify(void)
static void
init_smp_sig_suspend(void) {
+#ifdef ERTS_SYS_SUSPEND_SIGNAL
if (pipe(sig_suspend_fds) < 0) {
erts_exit(ERTS_ABORT_EXIT,
"Failed to create sig_suspend pipe: %s (%d)\n",
erl_errno_id(errno),
errno);
}
+#endif
}
#ifdef __DARWIN__
diff --git a/erts/emulator/sys/unix/sys_drivers.c b/erts/emulator/sys/unix/sys_drivers.c
new file mode 100644
index 0000000000..93cf64719a
--- /dev/null
+++ b/erts/emulator/sys/unix/sys_drivers.c
@@ -0,0 +1,1878 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef ISC32
+#define _POSIX_SOURCE
+#define _XOPEN_SOURCE
+#endif
+
+#include <sys/times.h> /* ! */
+#include <time.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/uio.h>
+#include <termios.h>
+#include <ctype.h>
+#include <sys/utsname.h>
+#include <sys/select.h>
+#include <arpa/inet.h>
+
+#ifdef ISC32
+#include <sys/bsdtypes.h>
+#endif
+
+#include <termios.h>
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#define WANT_NONBLOCKING /* must define this to pull in defs from sys.h */
+#include "sys.h"
+
+#ifdef USE_THREADS
+#include "erl_threads.h"
+#endif
+
+extern char **environ;
+extern erts_smp_rwmtx_t environ_rwmtx;
+
+extern erts_smp_atomic_t sys_misc_mem_sz;
+
+static Eterm forker_port;
+
+#define MAX_VSIZE 16 /* Max number of entries allowed in an I/O
+ * vector sock_sendv().
+ */
+/*
+ * Don't need global.h, but erl_cpu_topology.h won't compile otherwise
+ */
+#include "global.h"
+#include "erl_cpu_topology.h"
+
+#include "erl_sys_driver.h"
+#include "sys_uds.h"
+
+#include "erl_child_setup.h"
+
+#if defined IOV_MAX
+#define MAXIOV IOV_MAX
+#elif defined UIO_MAXIOV
+#define MAXIOV UIO_MAXIOV
+#else
+#define MAXIOV 16
+#endif
+
+#ifdef USE_THREADS
+# define FDBLOCK 1
+#else
+# define FDBLOCK 0
+#endif
+
+/* Used by the fd driver iff the fd could not be set to non-blocking */
+typedef struct ErtsSysBlocking_ {
+ ErlDrvPDL pdl;
+ int res;
+ int err;
+ unsigned int pkey;
+} ErtsSysBlocking;
+
+typedef struct fd_data {
+ int fd;
+ char pbuf[4]; /* hold partial packet bytes */
+ int psz; /* size of pbuf */
+ char *buf;
+ char *cpos;
+ int sz;
+ int remain; /* for input on fd */
+} ErtsSysFdData;
+
+typedef struct driver_data {
+ ErlDrvPort port_num;
+ ErtsSysFdData *ofd;
+ ErtsSysFdData *ifd;
+ int packet_bytes;
+ int pid;
+ int alive;
+ int status;
+ int terminating;
+ ErtsSysBlocking *blocking;
+} ErtsSysDriverData;
+
+#define DIR_SEPARATOR_CHAR '/'
+
+#if defined(__ANDROID__)
+#define SHELL "/system/bin/sh"
+#else
+#define SHELL "/bin/sh"
+#endif /* __ANDROID__ */
+
+#if defined(DEBUG)
+#define ERL_BUILD_TYPE_MARKER ".debug"
+#elif defined(PURIFY)
+#define ERL_BUILD_TYPE_MARKER ".purify"
+#elif defined(QUANTIFY)
+#define ERL_BUILD_TYPE_MARKER ".quantify"
+#elif defined(PURECOV)
+#define ERL_BUILD_TYPE_MARKER ".purecov"
+#elif defined(VALGRIND)
+#define ERL_BUILD_TYPE_MARKER ".valgrind"
+#else /* opt */
+#define ERL_BUILD_TYPE_MARKER
+#endif
+
+#ifdef DEBUG
+#define close(fd) do { int res = close(fd); ASSERT(res > -1); } while(0)
+#endif
+
+#define CHILD_SETUP_PROG_NAME "erl_child_setup" ERL_BUILD_TYPE_MARKER
+
+// #define HARD_DEBUG
+#ifdef HARD_DEBUG
+#define driver_select(port_num, fd, flags, onoff) \
+ do { \
+ if (((flags) & ERL_DRV_READ) && onoff) \
+ fprintf(stderr,"%010d %p: read select %d\r\n", __LINE__, port_num, (int)fd); \
+ if (((flags) & ERL_DRV_WRITE) && onoff) \
+ fprintf(stderr,"%010d %p: writ select %d\r\n", __LINE__, port_num, (int)fd); \
+ if (((flags) & ERL_DRV_READ) && !onoff) \
+ fprintf(stderr,"%010d %p: read unsele %d\r\n", __LINE__, port_num, (int)fd); \
+ if (((flags) & ERL_DRV_WRITE) && !onoff) \
+ fprintf(stderr,"%010d %p: writ unsele %d\r\n", __LINE__, port_num, (int)fd); \
+ driver_select_nkp(port_num, fd, flags, onoff); \
+ } while(0)
+#endif
+
+/*
+ * Decreasing the size of it below 16384 is not allowed.
+ */
+
+#define ERTS_SYS_READ_BUF_SZ (64*1024)
+
+/* I. Initialization */
+
+void
+erl_sys_late_init(void)
+{
+ SysDriverOpts opts;
+#ifdef ERTS_SMP
+ Port *port;
+#endif
+
+ sys_signal(SIGPIPE, SIG_IGN); /* Ignore - we'll handle the write failure */
+
+ opts.packet_bytes = 0;
+ opts.use_stdio = 1;
+ opts.redir_stderr = 0;
+ opts.read_write = 0;
+ opts.hide_window = 0;
+ opts.wd = NULL;
+ opts.envir = NULL;
+ opts.exit_status = 0;
+ opts.overlapped_io = 0;
+ opts.spawn_type = ERTS_SPAWN_ANY;
+ opts.argv = NULL;
+ opts.parallelism = erts_port_parallelism;
+
+#ifdef ERTS_SMP
+ port =
+#endif
+ erts_open_driver(&forker_driver, make_internal_pid(0), "forker", &opts, NULL, NULL);
+#ifdef ERTS_SMP
+ erts_mtx_unlock(port->lock);
+#endif
+ erts_sys_unix_later_init(); /* Need to be called after forker has been started */
+}
+
+/* II. Prototypes */
+
+/* II.I Spawn prototypes */
+static ErlDrvData spawn_start(ErlDrvPort, char*, SysDriverOpts*);
+static ErlDrvSSizeT spawn_control(ErlDrvData, unsigned int, char *,
+ ErlDrvSizeT, char **, ErlDrvSizeT);
+
+/* II.II Vanilla prototypes */
+static ErlDrvData vanilla_start(ErlDrvPort, char*, SysDriverOpts*);
+
+
+/* II.III FD prototypes */
+static ErlDrvData fd_start(ErlDrvPort, char*, SysDriverOpts*);
+#if FDBLOCK
+static void fd_async(void *);
+static void fd_ready_async(ErlDrvData drv_data, ErlDrvThreadData thread_data);
+#endif
+static ErlDrvSSizeT fd_control(ErlDrvData, unsigned int, char *, ErlDrvSizeT,
+ char **, ErlDrvSizeT);
+static void fd_stop(ErlDrvData);
+static void fd_flush(ErlDrvData);
+
+/* II.IV Common prototypes */
+static void stop(ErlDrvData);
+static void ready_input(ErlDrvData, ErlDrvEvent);
+static void ready_output(ErlDrvData, ErlDrvEvent);
+static void output(ErlDrvData, char*, ErlDrvSizeT);
+static void outputv(ErlDrvData, ErlIOVec*);
+static void stop_select(ErlDrvEvent, void*);
+
+/* II.V Forker prototypes */
+static ErlDrvData forker_start(ErlDrvPort, char*, SysDriverOpts*);
+static void forker_stop(ErlDrvData);
+static void forker_ready_input(ErlDrvData, ErlDrvEvent);
+static void forker_ready_output(ErlDrvData, ErlDrvEvent);
+static ErlDrvSSizeT forker_control(ErlDrvData, unsigned int, char *,
+ ErlDrvSizeT, char **, ErlDrvSizeT);
+
+/* III Driver entries */
+
+/* III.I The spawn driver */
+struct erl_drv_entry spawn_driver_entry = {
+ NULL,
+ spawn_start,
+ stop,
+ output,
+ ready_input,
+ ready_output,
+ "spawn",
+ NULL,
+ NULL,
+ spawn_control,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ ERL_DRV_EXTENDED_MARKER,
+ ERL_DRV_EXTENDED_MAJOR_VERSION,
+ ERL_DRV_EXTENDED_MINOR_VERSION,
+ ERL_DRV_FLAG_USE_PORT_LOCKING | ERL_DRV_FLAG_USE_INIT_ACK,
+ NULL, NULL,
+ stop_select
+};
+
+/* III.II The fd driver */
+struct erl_drv_entry fd_driver_entry = {
+ NULL,
+ fd_start,
+ fd_stop,
+ output,
+ ready_input,
+ ready_output,
+ "fd",
+ NULL,
+ NULL,
+ fd_control,
+ NULL,
+ outputv,
+#if FDBLOCK
+ fd_ready_async, /* ready_async */
+#else
+ NULL,
+#endif
+ fd_flush, /* flush */
+ NULL, /* call */
+ NULL, /* event */
+ ERL_DRV_EXTENDED_MARKER,
+ ERL_DRV_EXTENDED_MAJOR_VERSION,
+ ERL_DRV_EXTENDED_MINOR_VERSION,
+ 0, /* ERL_DRV_FLAGs */
+ NULL, /* handle2 */
+ NULL, /* process_exit */
+ stop_select
+};
+
+/* III.III The vanilla driver */
+struct erl_drv_entry vanilla_driver_entry = {
+ NULL,
+ vanilla_start,
+ stop,
+ output,
+ ready_input,
+ ready_output,
+ "vanilla",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL, /* flush */
+ NULL, /* call */
+ NULL, /* event */
+ ERL_DRV_EXTENDED_MARKER,
+ ERL_DRV_EXTENDED_MAJOR_VERSION,
+ ERL_DRV_EXTENDED_MINOR_VERSION,
+ 0, /* ERL_DRV_FLAGs */
+ NULL, /* handle2 */
+ NULL, /* process_exit */
+ stop_select
+};
+
+/* III.III The forker driver */
+struct erl_drv_entry forker_driver_entry = {
+ NULL,
+ forker_start,
+ forker_stop,
+ NULL,
+ forker_ready_input,
+ forker_ready_output,
+ "spawn_forker",
+ NULL,
+ NULL,
+ forker_control,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ ERL_DRV_EXTENDED_MARKER,
+ ERL_DRV_EXTENDED_MAJOR_VERSION,
+ ERL_DRV_EXTENDED_MINOR_VERSION,
+ 0,
+ NULL, NULL,
+ stop_select
+};
+
+/* Untility functions */
+
+static int set_blocking_data(ErtsSysDriverData *dd) {
+
+ dd->blocking = erts_alloc(ERTS_ALC_T_SYS_BLOCKING, sizeof(ErtsSysBlocking));
+
+ erts_smp_atomic_add_nob(&sys_misc_mem_sz, sizeof(ErtsSysBlocking));
+
+ dd->blocking->pdl = driver_pdl_create(dd->port_num);
+ dd->blocking->res = 0;
+ dd->blocking->err = 0;
+ dd->blocking->pkey = driver_async_port_key(dd->port_num);
+
+ return 1;
+}
+
+static void init_fd_data(ErtsSysFdData *fd_data, int fd)
+{
+ fd_data->fd = fd;
+ fd_data->buf = NULL;
+ fd_data->cpos = NULL;
+ fd_data->remain = 0;
+ fd_data->sz = 0;
+ fd_data->psz = 0;
+}
+
+static ErtsSysDriverData *
+create_driver_data(ErlDrvPort port_num,
+ int ifd,
+ int ofd,
+ int packet_bytes,
+ int read_write,
+ int exit_status,
+ int pid,
+ int is_blocking)
+{
+ Port *prt;
+ ErtsSysDriverData *driver_data;
+ char *data;
+ int size = sizeof(ErtsSysDriverData);
+
+ if (read_write & DO_READ)
+ size += sizeof(ErtsSysFdData);
+
+ if ((read_write & DO_WRITE) &&
+ ((ifd != ofd || ofd == -1) || !(read_write & DO_READ)))
+ size += sizeof(ErtsSysFdData);
+
+ data = erts_alloc(ERTS_ALC_T_DRV_TAB,size);
+ erts_smp_atomic_add_nob(&sys_misc_mem_sz, size);
+
+ driver_data = (ErtsSysDriverData*)data;
+ data += sizeof(*driver_data);
+
+ prt = erts_drvport2port(port_num);
+ if (prt != ERTS_INVALID_ERL_DRV_PORT)
+ prt->os_pid = pid;
+
+ driver_data->packet_bytes = packet_bytes;
+ driver_data->port_num = port_num;
+ driver_data->pid = pid;
+ driver_data->alive = exit_status ? 1 : 0;
+ driver_data->status = 0;
+ driver_data->terminating = 0;
+ driver_data->blocking = NULL;
+
+ if (read_write & DO_READ) {
+ driver_data->ifd = (ErtsSysFdData*)data;
+ data += sizeof(*driver_data->ifd);
+ init_fd_data(driver_data->ifd, ifd);
+ driver_select(port_num, ifd, (ERL_DRV_READ|ERL_DRV_USE), 1);
+ } else {
+ driver_data->ifd = NULL;
+ }
+
+ if (read_write & DO_WRITE) {
+ if (ofd != -1 && ifd == ofd && read_write & DO_READ) {
+ /* This is for when ifd and ofd are the same fd */
+ driver_data->ofd = driver_data->ifd;
+ } else {
+ driver_data->ofd = (ErtsSysFdData*)data;
+ data += sizeof(*driver_data->ofd);
+ init_fd_data(driver_data->ofd, ofd);
+ }
+ if (is_blocking && FDBLOCK)
+ if (!set_blocking_data(driver_data)) {
+ erts_free(ERTS_ALC_T_DRV_TAB, driver_data);
+ return NULL;
+ }
+ } else {
+ driver_data->ofd = NULL;
+ }
+
+ return driver_data;
+}
+
+/* Spawn driver */
+
+static void close_pipes(int ifd[2], int ofd[2])
+{
+ close(ifd[0]);
+ close(ifd[1]);
+ close(ofd[0]);
+ close(ofd[1]);
+}
+
+static char **build_unix_environment(char *block)
+{
+ int i;
+ int j;
+ int len;
+ char *cp;
+ char **cpp;
+ char** old_env;
+
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_rwmtx_is_rlocked(&environ_rwmtx));
+
+ cp = block;
+ len = 0;
+ while (*cp != '\0') {
+ cp += strlen(cp) + 1;
+ len++;
+ }
+ old_env = environ;
+ while (*old_env++ != NULL) {
+ len++;
+ }
+
+ cpp = (char **) erts_alloc_fnf(ERTS_ALC_T_ENVIRONMENT,
+ sizeof(char *) * (len+1));
+ if (cpp == NULL) {
+ return NULL;
+ }
+
+ cp = block;
+ len = 0;
+ while (*cp != '\0') {
+ cpp[len] = cp;
+ cp += strlen(cp) + 1;
+ len++;
+ }
+
+ i = len;
+ for (old_env = environ; *old_env; old_env++) {
+ char* old = *old_env;
+
+ for (j = 0; j < len; j++) {
+ char *s, *t;
+
+ /* check if cpp[j] equals old
+ before the = sign,
+ i.e.
+ "TMPDIR=/tmp/" */
+ s = cpp[j];
+ t = old;
+ while (*s == *t && *s != '=') {
+ s++, t++;
+ }
+ if (*s == '=' && *t == '=') {
+ break;
+ }
+ }
+
+ if (j == len) { /* New version not found */
+ cpp[len++] = old;
+ }
+ }
+
+ for (j = 0; j < i; ) {
+ size_t last = strlen(cpp[j])-1;
+ if (cpp[j][last] == '=' && strchr(cpp[j], '=') == cpp[j]+last) {
+ cpp[j] = cpp[--len];
+ if (len < i) {
+ i--;
+ } else {
+ j++;
+ }
+ }
+ else {
+ j++;
+ }
+ }
+
+ cpp[len] = NULL;
+ return cpp;
+}
+
+static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
+ SysDriverOpts* opts)
+{
+#define CMD_LINE_PREFIX_STR "exec "
+#define CMD_LINE_PREFIX_STR_SZ (sizeof(CMD_LINE_PREFIX_STR) - 1)
+
+ int len;
+ char **new_environ;
+ ErtsSysDriverData *dd;
+ char *cmd_line;
+ char wd_buff[MAXPATHLEN+1];
+ char *wd, *cwd;
+ int ifd[2], ofd[2], stderrfd;
+
+ if (pipe(ifd) < 0) return ERL_DRV_ERROR_ERRNO;
+ errno = EMFILE; /* default for next three conditions */
+ if (ifd[0] >= sys_max_files() || pipe(ofd) < 0) {
+ close(ifd[0]);
+ close(ifd[1]);
+ return ERL_DRV_ERROR_ERRNO;
+ }
+ if (ofd[1] >= sys_max_files()) {
+ close_pipes(ifd, ofd);
+ errno = EMFILE;
+ return ERL_DRV_ERROR_ERRNO;
+ }
+
+ SET_NONBLOCKING(ifd[0]);
+ SET_NONBLOCKING(ofd[1]);
+
+ stderrfd = opts->redir_stderr ? ifd[1] : dup(2);
+
+ if (stderrfd >= sys_max_files() || stderrfd < 0) {
+ close_pipes(ifd, ofd);
+ if (stderrfd > -1)
+ close(stderrfd);
+ return ERL_DRV_ERROR_ERRNO;
+ }
+
+ if (opts->spawn_type == ERTS_SPAWN_EXECUTABLE) {
+ /* started with spawn_executable, not with spawn */
+ len = strlen(name);
+ cmd_line = (char *) erts_alloc_fnf(ERTS_ALC_T_TMP, len + 1);
+ if (!cmd_line) {
+ close_pipes(ifd, ofd);
+ errno = ENOMEM;
+ return ERL_DRV_ERROR_ERRNO;
+ }
+ memcpy((void *) cmd_line,(void *) name, len);
+ cmd_line[len] = '\0';
+ len = len + 1;
+ if (access(cmd_line,X_OK) != 0) {
+ int save_errno = errno;
+ erts_free(ERTS_ALC_T_TMP, cmd_line);
+ close_pipes(ifd, ofd);
+ errno = save_errno;
+ return ERL_DRV_ERROR_ERRNO;
+ }
+ } else {
+ /* make the string suitable for giving to "sh" */
+ len = strlen(name);
+ cmd_line = (char *) erts_alloc_fnf(ERTS_ALC_T_TMP,
+ CMD_LINE_PREFIX_STR_SZ + len + 1);
+ if (!cmd_line) {
+ close_pipes(ifd, ofd);
+ errno = ENOMEM;
+ return ERL_DRV_ERROR_ERRNO;
+ }
+ memcpy((void *) cmd_line,
+ (void *) CMD_LINE_PREFIX_STR,
+ CMD_LINE_PREFIX_STR_SZ);
+ memcpy((void *) (cmd_line + CMD_LINE_PREFIX_STR_SZ), (void *) name, len);
+ cmd_line[CMD_LINE_PREFIX_STR_SZ + len] = '\0';
+ len = CMD_LINE_PREFIX_STR_SZ + len + 1;
+ }
+
+ erts_smp_rwmtx_rlock(&environ_rwmtx);
+
+ if (opts->envir == NULL) {
+ new_environ = environ;
+ } else if ((new_environ = build_unix_environment(opts->envir)) == NULL) {
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+ close_pipes(ifd, ofd);
+ erts_free(ERTS_ALC_T_TMP, (void *) cmd_line);
+ errno = ENOMEM;
+ return ERL_DRV_ERROR_ERRNO;
+ }
+
+ if ((cwd = getcwd(wd_buff, MAXPATHLEN+1)) == NULL) {
+ /* on some OSs this call opens a fd in the
+ background which means that this can
+ return EMFILE */
+ int err = errno;
+ close_pipes(ifd, ofd);
+ erts_free(ERTS_ALC_T_TMP, (void *) cmd_line);
+ if (new_environ != environ)
+ erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ);
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+ errno = err;
+ return ERL_DRV_ERROR_ERRNO;
+ }
+
+ wd = opts->wd;
+
+ {
+ struct iovec *io_vector;
+ int iov_len = 5;
+ char nullbuff[] = "\0";
+ int j, i = 0, res;
+ Sint32 buffsz = 0, env_len = 0, argv_len = 0,
+ flags = (opts->use_stdio ? FORKER_FLAG_USE_STDIO : 0)
+ | (opts->exit_status ? FORKER_FLAG_EXIT_STATUS : 0)
+ | (opts->read_write & DO_READ ? FORKER_FLAG_DO_READ : 0)
+ | (opts->read_write & DO_WRITE ? FORKER_FLAG_DO_WRITE : 0);
+
+ if (wd) iov_len++;
+
+ /* count number of elements in environment */
+ while(new_environ[env_len] != NULL)
+ env_len++;
+ iov_len += 1 + env_len; /* num envs including size int */
+
+ /* count number of element in argument list */
+ if (opts->spawn_type == ERTS_SPAWN_EXECUTABLE) {
+ if (opts->argv != NULL) {
+ while(opts->argv[argv_len] != NULL)
+ argv_len++;
+ } else {
+ argv_len++;
+ }
+ iov_len += 1 + argv_len; /* num argvs including size int */
+ }
+
+ io_vector = erts_alloc_fnf(ERTS_ALC_T_TMP, sizeof(struct iovec) * iov_len);
+
+ if (!io_vector) {
+ close_pipes(ifd, ofd);
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+ erts_free(ERTS_ALC_T_TMP, (void *) cmd_line);
+ if (new_environ != environ)
+ erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ);
+ errno = ENOMEM;
+ return ERL_DRV_ERROR_ERRNO;
+ }
+
+ /*
+ * Whitebox test port_SUITE:pipe_limit_env
+ * assumes this command payload format.
+ */
+ io_vector[i].iov_base = (void*)&buffsz;
+ io_vector[i++].iov_len = sizeof(buffsz);
+
+ io_vector[i].iov_base = (void*)&flags;
+ flags = htonl(flags);
+ io_vector[i++].iov_len = sizeof(flags);
+ buffsz += sizeof(flags);
+
+ io_vector[i].iov_base = cmd_line;
+ io_vector[i++].iov_len = len;
+ buffsz += len;
+
+ io_vector[i].iov_base = cwd;
+ io_vector[i].iov_len = strlen(io_vector[i].iov_base) + 1;
+ buffsz += io_vector[i++].iov_len;
+
+ if (wd) {
+ io_vector[i].iov_base = wd;
+ io_vector[i].iov_len = strlen(io_vector[i].iov_base) + 1;
+ buffsz += io_vector[i++].iov_len;
+ }
+
+ io_vector[i].iov_base = nullbuff;
+ io_vector[i++].iov_len = 1;
+ buffsz += io_vector[i-1].iov_len;
+
+ io_vector[i].iov_base = (void*)&env_len;
+ env_len = htonl(env_len);
+ io_vector[i++].iov_len = sizeof(env_len);
+ buffsz += io_vector[i-1].iov_len;
+
+ for (j = 0; new_environ[j] != NULL; j++) {
+ io_vector[i].iov_base = new_environ[j];
+ io_vector[i++].iov_len = strlen(new_environ[j]) + 1;
+ buffsz += io_vector[i-1].iov_len;
+ }
+
+ /* only append arguments if this was a spawn_executable */
+ if (opts->spawn_type == ERTS_SPAWN_EXECUTABLE) {
+
+ io_vector[i].iov_base = (void*)&argv_len;
+ argv_len = htonl(argv_len);
+ io_vector[i++].iov_len = sizeof(argv_len);
+ buffsz += io_vector[i-1].iov_len;
+
+ if (opts->argv) {
+ /* If there are arguments we copy in the references to
+ them into the iov */
+ for (j = 0; opts->argv[j]; j++) {
+ if (opts->argv[j] == erts_default_arg0)
+ io_vector[i].iov_base = cmd_line;
+ else
+ io_vector[i].iov_base = opts->argv[j];
+ io_vector[i].iov_len = strlen(io_vector[i].iov_base) + 1;
+ buffsz += io_vector[i++].iov_len;
+ }
+ } else {
+ io_vector[i].iov_base = cmd_line;
+ io_vector[i].iov_len = strlen(io_vector[i].iov_base) + 1;
+ buffsz += io_vector[i++].iov_len;
+ }
+ }
+
+ /* we send the request to do the fork */
+ if ((res = writev(ofd[1], io_vector, iov_len > MAXIOV ? MAXIOV : iov_len)) < 0) {
+ if (errno == ERRNO_BLOCK) {
+ res = 0;
+ } else {
+ int err = errno;
+ close_pipes(ifd, ofd);
+ erts_free(ERTS_ALC_T_TMP, io_vector);
+ if (new_environ != environ)
+ erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ);
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+ erts_free(ERTS_ALC_T_TMP, (void *) cmd_line);
+ errno = err;
+ return ERL_DRV_ERROR_ERRNO;
+ }
+ }
+
+ if (res < (buffsz + sizeof(buffsz))) {
+ /* we only wrote part of the command payload. Enqueue the rest. */
+ for (i = 0; i < iov_len; i++) {
+ if (res >= io_vector[i].iov_len)
+ res -= io_vector[i].iov_len;
+ else {
+ driver_enq(port_num, io_vector[i].iov_base + res,
+ io_vector[i].iov_len - res);
+ res = 0;
+ }
+ }
+ driver_select(port_num, ofd[1], ERL_DRV_WRITE|ERL_DRV_USE, 1);
+ }
+
+ erts_free(ERTS_ALC_T_TMP, io_vector);
+ }
+
+ erts_free(ERTS_ALC_T_TMP, (void *) cmd_line);
+
+ if (new_environ != environ)
+ erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ);
+
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+
+ dd = create_driver_data(port_num, ifd[0], ofd[1], opts->packet_bytes,
+ DO_WRITE | DO_READ, opts->exit_status,
+ 0, 0);
+
+ {
+ /* send ofd[0] + ifd[1] + stderrfd to forker port */
+ ErtsSysForkerProto *proto =
+ erts_alloc(ERTS_ALC_T_DRV_CTRL_DATA,
+ sizeof(ErtsSysForkerProto));
+ memset(proto, 0, sizeof(ErtsSysForkerProto));
+ proto->action = ErtsSysForkerProtoAction_Start;
+ proto->u.start.fds[0] = ofd[0];
+ proto->u.start.fds[1] = ifd[1];
+ proto->u.start.fds[2] = stderrfd;
+ proto->u.start.port_id = opts->exit_status ? erts_drvport2id(port_num) : THE_NON_VALUE;
+ if (erl_drv_port_control(forker_port, 'S', (char*)proto, sizeof(*proto))) {
+ /* The forker port has been killed, we close both fd's which will
+ make open_port throw an epipe error */
+ close(ofd[0]);
+ close(ifd[1]);
+ }
+ }
+
+ /* we set these fds to negative to mark if
+ they should be closed after the handshake */
+ if (!(opts->read_write & DO_READ))
+ dd->ifd->fd *= -1;
+
+ if (!(opts->read_write & DO_WRITE))
+ dd->ofd->fd *= -1;
+
+ return (ErlDrvData)dd;
+#undef CMD_LINE_PREFIX_STR
+#undef CMD_LINE_PREFIX_STR_SZ
+}
+
+static ErlDrvSSizeT spawn_control(ErlDrvData e, unsigned int cmd, char *buf,
+ ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen)
+{
+ ErtsSysDriverData *dd = (ErtsSysDriverData*)e;
+ ErtsSysForkerProto *proto = (ErtsSysForkerProto *)buf;
+
+ ASSERT(len == sizeof(*proto));
+ ASSERT(proto->action == ErtsSysForkerProtoAction_SigChld);
+
+ dd->status = proto->u.sigchld.error_number;
+ dd->alive = -1;
+
+ if (dd->ifd)
+ driver_select(dd->port_num, abs(dd->ifd->fd), ERL_DRV_READ | ERL_DRV_USE, 1);
+
+ if (dd->ofd)
+ driver_select(dd->port_num, abs(dd->ofd->fd), ERL_DRV_WRITE | ERL_DRV_USE, 1);
+
+ return 0;
+}
+
+#define FD_DEF_HEIGHT 24
+#define FD_DEF_WIDTH 80
+/* Control op */
+#define FD_CTRL_OP_GET_WINSIZE 100
+
+static int fd_get_window_size(int fd, Uint32 *width, Uint32 *height)
+{
+#ifdef TIOCGWINSZ
+ struct winsize ws;
+ if (ioctl(fd,TIOCGWINSZ,&ws) == 0) {
+ *width = (Uint32) ws.ws_col;
+ *height = (Uint32) ws.ws_row;
+ return 0;
+ }
+#endif
+ return -1;
+}
+
+static ErlDrvSSizeT fd_control(ErlDrvData drv_data,
+ unsigned int command,
+ char *buf, ErlDrvSizeT len,
+ char **rbuf, ErlDrvSizeT rlen)
+{
+ int fd = (int)(long)drv_data;
+ char resbuff[2*sizeof(Uint32)];
+ switch (command) {
+ case FD_CTRL_OP_GET_WINSIZE:
+ {
+ Uint32 w,h;
+ if (fd_get_window_size(fd,&w,&h))
+ return 0;
+ memcpy(resbuff,&w,sizeof(Uint32));
+ memcpy(resbuff+sizeof(Uint32),&h,sizeof(Uint32));
+ }
+ break;
+ default:
+ return 0;
+ }
+ if (rlen < 2*sizeof(Uint32)) {
+ *rbuf = driver_alloc(2*sizeof(Uint32));
+ }
+ memcpy(*rbuf,resbuff,2*sizeof(Uint32));
+ return 2*sizeof(Uint32);
+}
+
+static ErlDrvData fd_start(ErlDrvPort port_num, char* name,
+ SysDriverOpts* opts)
+{
+ int non_blocking = 0;
+
+ if (((opts->read_write & DO_READ) && opts->ifd >= sys_max_files()) ||
+ ((opts->read_write & DO_WRITE) && opts->ofd >= sys_max_files()))
+ return ERL_DRV_ERROR_GENERAL;
+
+ /*
+ * Historical:
+ *
+ * "Note about nonblocking I/O.
+ *
+ * At least on Solaris, setting the write end of a TTY to nonblocking,
+ * will set the input end to nonblocking as well (and vice-versa).
+ * If erl is run in a pipeline like this: cat | erl
+ * the input end of the TTY will be the standard input of cat.
+ * And cat is not prepared to handle nonblocking I/O."
+ *
+ * Actually, the reason for this is not that the tty itself gets set
+ * in non-blocking mode, but that the "input end" (cat's stdin) and
+ * the "output end" (erlang's stdout) are typically the "same" file
+ * descriptor, dup()'ed from a single fd by one of this process'
+ * ancestors.
+ *
+ * The workaround for this problem used to be a rather bad kludge,
+ * interposing an extra process ("internal cat") between erlang's
+ * stdout and the original stdout, allowing erlang to set its stdout
+ * in non-blocking mode without affecting the stdin of the preceding
+ * process in the pipeline - and being a kludge, it caused all kinds
+ * of weird problems.
+ *
+ * So, this is the current logic:
+ *
+ * The only reason to set non-blocking mode on the output fd at all is
+ * if it's something that can cause a write() to block, of course,
+ * i.e. primarily if it points to a tty, socket, pipe, or fifo.
+ *
+ * If we don't set non-blocking mode when we "should" have, and output
+ * becomes blocked, the entire runtime system will be suspended - this
+ * is normally bad of course, and can happen fairly "easily" - e.g. user
+ * hits ^S on tty - but doesn't necessarily happen.
+ *
+ * If we do set non-blocking mode when we "shouldn't" have, the runtime
+ * system will end up seeing EOF on the input fd (due to the preceding
+ * process dying), which typically will cause the entire runtime system
+ * to terminate immediately (due to whatever erlang process is seeing
+ * the EOF taking it as a signal to halt the system). This is *very* bad.
+ *
+ * I.e. we should take a conservative approach, and only set non-
+ * blocking mode when we a) need to, and b) are reasonably certain
+ * that it won't be a problem. And as in the example above, the problem
+ * occurs when input fd and output fd point to different "things".
+ *
+ * However, determining that they are not just the same "type" of
+ * "thing", but actually the same instance of that type of thing, is
+ * unreasonably complex in many/most cases.
+ *
+ * Also, with pipes, sockets, and fifos it's far from obvious that the
+ * user *wants* non-blocking output: If you're running erlang inside
+ * some complex pipeline, you're probably not running a real-time system
+ * that must never stop, but rather *want* it to suspend if the output
+ * channel is "full".
+ *
+ * So, the bottom line: We will only set the output fd non-blocking if
+ * it points to a tty, and either a) the input fd also points to a tty,
+ * or b) we can make sure that setting the output fd non-blocking
+ * doesn't interfere with someone else's input, via a somewhat milder
+ * kludge than the above.
+ *
+ * Also keep in mind that while this code is almost exclusively run as
+ * a result of an erlang open_port({fd,0,1}, ...), that isn't the only
+ * case - it can be called with any old pre-existing file descriptors,
+ * the relations between which (if they're even two) we can only guess
+ * at - still, we try our best...
+ *
+ * Added note OTP 18: Some systems seem to use stdout/stderr to log data
+ * using unix pipes, so we cannot allow the system to block on a write.
+ * Therefore we use an async thread to write the data to fd's that could
+ * not be set to non-blocking. When no async threads are available we
+ * fall back on the old behaviour.
+ *
+ * Also the guarantee about what is delivered to the OS has changed.
+ * Pre 18 the fd driver did no flushing of data before terminating.
+ * Now it does. This is because we want to be able to guarantee that things
+ * such as escripts and friends really have outputted all data before
+ * terminating. This could potentially block the termination of the system
+ * for a very long time, but if the user wants to terminate fast she should
+ * use erlang:halt with flush=false.
+ */
+
+ /* Try to figure out if we can use non-blocking writes */
+ if (opts->read_write & DO_WRITE) {
+
+ /* If we don't have a read end, all bets are off - no non-blocking. */
+ if (opts->read_write & DO_READ) {
+
+ if (isatty(opts->ofd)) { /* output fd is a tty:-) */
+
+ if (isatty(opts->ifd)) { /* input fd is also a tty */
+
+ /* To really do this "right", we should also check that
+ input and output fd point to the *same* tty - but
+ this seems like overkill; ttyname() isn't for free,
+ and this is a very common case - and it's hard to
+ imagine a scenario where setting non-blocking mode
+ here would cause problems - go ahead and do it. */
+
+ non_blocking = 1;
+ SET_NONBLOCKING(opts->ofd);
+
+ } else { /* output fd is a tty, input fd isn't */
+
+ /* This is a "problem case", but also common (see the
+ example above) - i.e. it makes sense to try a bit
+ harder before giving up on non-blocking mode: Try to
+ re-open the tty that the output fd points to, and if
+ successful replace the original one with the "new" fd
+ obtained this way, and set *that* one in non-blocking
+ mode. (Yes, this is a kludge.)
+
+ However, re-opening the tty may fail in a couple of
+ (unusual) cases:
+
+ 1) The name of the tty (or an equivalent one, i.e.
+ same major/minor number) can't be found, because
+ it actually lives somewhere other than /dev (or
+ wherever ttyname() looks for it), and isn't
+ equivalent to any of those that do live in the
+ "standard" place - this should be *very* unusual.
+
+ 2) Permissions on the tty don't allow us to open it -
+ it's perfectly possible to have an fd open to an
+ object whose permissions wouldn't allow us to open
+ it. This is not as unusual as it sounds, one case
+ is if the user has su'ed to someone else (not
+ root) - we have a read/write fd open to the tty
+ (because it has been inherited all the way down
+ here), but we have neither read nor write
+ permission for the tty.
+
+ In these cases, we finally give up, and don't set the
+ output fd in non-blocking mode. */
+
+ char *tty;
+ int nfd;
+
+ if ((tty = ttyname(opts->ofd)) != NULL &&
+ (nfd = open(tty, O_WRONLY)) != -1) {
+ dup2(nfd, opts->ofd);
+ close(nfd);
+ non_blocking = 1;
+ SET_NONBLOCKING(opts->ofd);
+ }
+ }
+ }
+ }
+ }
+ return (ErlDrvData)create_driver_data(port_num, opts->ifd, opts->ofd,
+ opts->packet_bytes,
+ opts->read_write, 0, -1,
+ !non_blocking);
+}
+
+static void clear_fd_data(ErtsSysFdData *fdd)
+{
+ if (fdd->sz > 0) {
+ erts_free(ERTS_ALC_T_FD_ENTRY_BUF, (void *) fdd->buf);
+ ASSERT(erts_smp_atomic_read_nob(&sys_misc_mem_sz) >= fdd->sz);
+ erts_smp_atomic_add_nob(&sys_misc_mem_sz, -1*fdd->sz);
+ }
+ fdd->buf = NULL;
+ fdd->sz = 0;
+ fdd->remain = 0;
+ fdd->cpos = NULL;
+ fdd->psz = 0;
+}
+
+static void nbio_stop_fd(ErlDrvPort prt, ErtsSysFdData *fdd)
+{
+ driver_select(prt, abs(fdd->fd), DO_READ|DO_WRITE, 0);
+ clear_fd_data(fdd);
+ SET_BLOCKING(abs(fdd->fd));
+
+}
+
+static void fd_stop(ErlDrvData ev) /* Does not close the fds */
+{
+ ErtsSysDriverData* dd = (ErtsSysDriverData*)ev;
+ ErlDrvPort prt = dd->port_num;
+ int sz = sizeof(ErtsSysDriverData);
+
+#if FDBLOCK
+ if (dd->blocking) {
+ erts_free(ERTS_ALC_T_SYS_BLOCKING, dd->blocking);
+ dd->blocking = NULL;
+ sz += sizeof(ErtsSysBlocking);
+ }
+#endif
+
+ if (dd->ifd) {
+ sz += sizeof(ErtsSysFdData);
+ nbio_stop_fd(prt, dd->ifd);
+ }
+ if (dd->ofd && dd->ofd != dd->ifd) {
+ sz += sizeof(ErtsSysFdData);
+ nbio_stop_fd(prt, dd->ofd);
+ }
+
+ erts_free(ERTS_ALC_T_DRV_TAB, dd);
+ erts_smp_atomic_add_nob(&sys_misc_mem_sz, -sz);
+}
+
+static void fd_flush(ErlDrvData ev)
+{
+ ErtsSysDriverData* dd = (ErtsSysDriverData*)ev;
+ if (!dd->terminating)
+ dd->terminating = 1;
+}
+
+static ErlDrvData vanilla_start(ErlDrvPort port_num, char* name,
+ SysDriverOpts* opts)
+{
+ int flags, fd;
+ ErlDrvData res;
+
+ flags = (opts->read_write == DO_READ ? O_RDONLY :
+ opts->read_write == DO_WRITE ? O_WRONLY|O_CREAT|O_TRUNC :
+ O_RDWR|O_CREAT);
+ if ((fd = open(name, flags, 0666)) < 0)
+ return ERL_DRV_ERROR_GENERAL;
+ if (fd >= sys_max_files()) {
+ close(fd);
+ return ERL_DRV_ERROR_GENERAL;
+ }
+ SET_NONBLOCKING(fd);
+
+ res = (ErlDrvData)(long)create_driver_data(port_num, fd, fd,
+ opts->packet_bytes,
+ opts->read_write, 0, -1, 0);
+ return res;
+}
+
+/* Note that driver_data[fd].ifd == fd if the port was opened for reading, */
+/* otherwise (i.e. write only) driver_data[fd].ofd = fd. */
+
+static void stop(ErlDrvData ev)
+{
+ ErtsSysDriverData* dd = (ErtsSysDriverData*)ev;
+ ErlDrvPort prt = dd->port_num;
+
+ if (dd->ifd) {
+ nbio_stop_fd(prt, dd->ifd);
+ driver_select(prt, abs(dd->ifd->fd), ERL_DRV_USE, 0); /* close(ifd); */
+ }
+
+ if (dd->ofd && dd->ofd != dd->ifd) {
+ nbio_stop_fd(prt, dd->ofd);
+ driver_select(prt, abs(dd->ofd->fd), ERL_DRV_USE, 0); /* close(ofd); */
+ }
+
+ erts_free(ERTS_ALC_T_DRV_TAB, dd);
+}
+
+/* used by fd_driver */
+static void outputv(ErlDrvData e, ErlIOVec* ev)
+{
+ ErtsSysDriverData *dd = (ErtsSysDriverData*)e;
+ ErlDrvPort ix = dd->port_num;
+ int pb = dd->packet_bytes;
+ int ofd = dd->ofd ? dd->ofd->fd : -1;
+ ssize_t n;
+ ErlDrvSizeT sz;
+ char lb[4];
+ char* lbp;
+ ErlDrvSizeT len = ev->size;
+
+ /* (len > ((unsigned long)-1 >> (4-pb)*8)) */
+ /* if (pb >= 0 && (len & (((ErlDrvSizeT)1 << (pb*8))) - 1) != len) {*/
+ if (((pb == 2) && (len > 0xffff)) || (pb == 1 && len > 0xff)) {
+ driver_failure_posix(ix, EINVAL);
+ return; /* -1; */
+ }
+ /* Handles 0 <= pb <= 4 only */
+ put_int32((Uint32) len, lb);
+ lbp = lb + (4-pb);
+
+ ev->iov[0].iov_base = lbp;
+ ev->iov[0].iov_len = pb;
+ ev->size += pb;
+
+ if (dd->blocking && FDBLOCK)
+ driver_pdl_lock(dd->blocking->pdl);
+
+ if ((sz = driver_sizeq(ix)) > 0) {
+ driver_enqv(ix, ev, 0);
+
+ if (dd->blocking && FDBLOCK)
+ driver_pdl_unlock(dd->blocking->pdl);
+
+ if (sz + ev->size >= (1 << 13))
+ set_busy_port(ix, 1);
+ }
+ else if (!dd->blocking || !FDBLOCK) {
+ /* We try to write directly if the fd in non-blocking */
+ int vsize = ev->vsize > MAX_VSIZE ? MAX_VSIZE : ev->vsize;
+
+ n = writev(ofd, (const void *) (ev->iov), vsize);
+ if (n == ev->size)
+ return; /* 0;*/
+ if (n < 0) {
+ if ((errno != EINTR) && (errno != ERRNO_BLOCK)) {
+ driver_failure_posix(ix, errno);
+ return; /* -1;*/
+ }
+ n = 0;
+ }
+ driver_enqv(ix, ev, n); /* n is the skip value */
+ driver_select(ix, ofd, ERL_DRV_WRITE|ERL_DRV_USE, 1);
+ }
+#if FDBLOCK
+ else {
+ if (ev->size != 0) {
+ driver_enqv(ix, ev, 0);
+ driver_pdl_unlock(dd->blocking->pdl);
+ driver_async(ix, &dd->blocking->pkey,
+ fd_async, dd, NULL);
+ } else {
+ driver_pdl_unlock(dd->blocking->pdl);
+ }
+ }
+#endif
+ /* return 0;*/
+}
+
+/* Used by spawn_driver and vanilla driver */
+static void output(ErlDrvData e, char* buf, ErlDrvSizeT len)
+{
+ ErtsSysDriverData *dd = (ErtsSysDriverData*)e;
+ ErlDrvPort ix = dd->port_num;
+ int pb = dd->packet_bytes;
+ int ofd = dd->ofd ? dd->ofd->fd : -1;
+ ssize_t n;
+ ErlDrvSizeT sz;
+ char lb[4];
+ char* lbp;
+ struct iovec iv[2];
+
+ /* (len > ((unsigned long)-1 >> (4-pb)*8)) */
+ if (((pb == 2) && (len > 0xffff))
+ || (pb == 1 && len > 0xff)
+ || dd->pid == 0 /* Attempt at output before port is ready */) {
+ driver_failure_posix(ix, EINVAL);
+ return; /* -1; */
+ }
+ put_int32(len, lb);
+ lbp = lb + (4-pb);
+
+ if ((sz = driver_sizeq(ix)) > 0) {
+ driver_enq(ix, lbp, pb);
+ driver_enq(ix, buf, len);
+ if (sz + len + pb >= (1 << 13))
+ set_busy_port(ix, 1);
+ }
+ else {
+ iv[0].iov_base = lbp;
+ iv[0].iov_len = pb; /* should work for pb=0 */
+ iv[1].iov_base = buf;
+ iv[1].iov_len = len;
+ n = writev(ofd, iv, 2);
+ if (n == pb+len)
+ return; /* 0; */
+ if (n < 0) {
+ if ((errno != EINTR) && (errno != ERRNO_BLOCK)) {
+ driver_failure_posix(ix, errno);
+ return; /* -1; */
+ }
+ n = 0;
+ }
+ if (n < pb) {
+ driver_enq(ix, lbp+n, pb-n);
+ driver_enq(ix, buf, len);
+ }
+ else {
+ n -= pb;
+ driver_enq(ix, buf+n, len-n);
+ }
+ driver_select(ix, ofd, ERL_DRV_WRITE|ERL_DRV_USE, 1);
+ }
+ return; /* 0; */
+}
+
+static int port_inp_failure(ErtsSysDriverData *dd, int res)
+ /* Result: 0 (eof) or -1 (error) */
+{
+ int err = errno;
+
+ ASSERT(res <= 0);
+ if (dd->ifd) {
+ driver_select(dd->port_num, dd->ifd->fd, ERL_DRV_READ|ERL_DRV_WRITE, 0);
+ clear_fd_data(dd->ifd);
+ }
+
+ if (dd->blocking && FDBLOCK) {
+ driver_pdl_lock(dd->blocking->pdl);
+ if (driver_sizeq(dd->port_num) > 0) {
+ driver_pdl_unlock(dd->blocking->pdl);
+ /* We have stuff in the output queue, so we just
+ set the state to terminating and wait for fd_async_ready
+ to terminate the port */
+ if (res == 0)
+ dd->terminating = 2;
+ else
+ dd->terminating = -err;
+ return 0;
+ }
+ driver_pdl_unlock(dd->blocking->pdl);
+ }
+
+ if (res == 0) {
+ if (dd->alive == 1) {
+ /*
+ * We have eof and want to report exit status, but the process
+ * hasn't exited yet. When it does ready_input will
+ * driver_select() this fd which will make sure that we get
+ * back here with dd->alive == -1 and dd->status set.
+ */
+ return 0;
+ }
+ else if (dd->alive == -1) {
+ int status = dd->status;
+
+ /* We need not be prepared for stopped/continued processes. */
+ if (WIFSIGNALED(status))
+ status = 128 + WTERMSIG(status);
+ else
+ status = WEXITSTATUS(status);
+ driver_report_exit(dd->port_num, status);
+ }
+ driver_failure_eof(dd->port_num);
+ } else if (dd->ifd) {
+ erl_drv_init_ack(dd->port_num, ERL_DRV_ERROR_ERRNO);
+ } else {
+ driver_failure_posix(dd->port_num, err);
+ }
+ return 0;
+}
+
+/* fd is the drv_data that is returned from the */
+/* initial start routine */
+/* ready_fd is the descriptor that is ready to read */
+
+static void ready_input(ErlDrvData e, ErlDrvEvent ready_fd)
+{
+ ErtsSysDriverData *dd = (ErtsSysDriverData*)e;
+ ErlDrvPort port_num;
+ int packet_bytes;
+ int res;
+ Uint h;
+
+ port_num = dd->port_num;
+ packet_bytes = dd->packet_bytes;
+
+ ASSERT(abs(dd->ifd->fd) == ready_fd);
+
+ if (dd->pid == 0) {
+ /* the pid is sent from erl_child_setup. spawn driver only. */
+ ErtsSysForkerProto proto;
+ int res;
+
+ if((res = read(ready_fd, &proto, sizeof(proto))) <= 0) {
+ /* hmm, child setup seems to have closed the pipe too early...
+ we close the port as there is not much else we can do */
+ if (res < 0 && errno == ERRNO_BLOCK)
+ return;
+ driver_select(port_num, ready_fd, ERL_DRV_READ, 0);
+ if (res == 0)
+ errno = EPIPE;
+ port_inp_failure(dd, -1);
+ return;
+ }
+
+ ASSERT(proto.action == ErtsSysForkerProtoAction_Go);
+ dd->pid = proto.u.go.os_pid;
+
+ if (dd->pid == -1) {
+ /* Setup failed! The only reason why this should happen is if
+ the fork fails. */
+ errno = proto.u.go.error_number;
+ port_inp_failure(dd, -1);
+ return;
+ }
+
+ proto.action = ErtsSysForkerProtoAction_Ack;
+
+ if (driver_sizeq(port_num) > 0) {
+ driver_enq(port_num, (char*)&proto, sizeof(proto));
+ } else {
+ if (write(abs(dd->ofd->fd), &proto, sizeof(proto)) < 0)
+ if (errno == ERRNO_BLOCK || errno == EINTR)
+ driver_enq(port_num, (char*)&proto, sizeof(proto));
+ /* do nothing on failure here. If the ofd is broken, then
+ the ifd will probably also be broken and trigger
+ a port_inp_failure */
+ }
+
+ if (dd->ifd->fd < 0) {
+ driver_select(port_num, abs(dd->ifd->fd), ERL_DRV_READ|ERL_DRV_USE, 0);
+ erts_smp_atomic_add_nob(&sys_misc_mem_sz, -sizeof(ErtsSysFdData));
+ dd->ifd = NULL;
+ }
+
+ if (dd->ofd->fd < 0 || driver_sizeq(port_num) > 0)
+ /* we select in order to close fd or write to queue,
+ child setup will close this fd if fd < 0 */
+ driver_select(port_num, abs(dd->ofd->fd), ERL_DRV_WRITE|ERL_DRV_USE, 1);
+
+ erl_drv_set_os_pid(port_num, dd->pid);
+ erl_drv_init_ack(port_num, e);
+ return;
+ }
+
+ if (packet_bytes == 0) {
+ byte *read_buf = (byte *) erts_alloc(ERTS_ALC_T_SYS_READ_BUF,
+ ERTS_SYS_READ_BUF_SZ);
+ res = read(ready_fd, read_buf, ERTS_SYS_READ_BUF_SZ);
+ if (res < 0) {
+ if ((errno != EINTR) && (errno != ERRNO_BLOCK))
+ port_inp_failure(dd, res);
+ }
+ else if (res == 0)
+ port_inp_failure(dd, res);
+ else
+ driver_output(port_num, (char*) read_buf, res);
+ erts_free(ERTS_ALC_T_SYS_READ_BUF, (void *) read_buf);
+ }
+ else if (dd->ifd->remain > 0) { /* We try to read the remainder */
+ /* space is allocated in buf */
+ res = read(ready_fd, dd->ifd->cpos,
+ dd->ifd->remain);
+ if (res < 0) {
+ if ((errno != EINTR) && (errno != ERRNO_BLOCK))
+ port_inp_failure(dd, res);
+ }
+ else if (res == 0) {
+ port_inp_failure(dd, res);
+ }
+ else if (res == dd->ifd->remain) { /* we're done */
+ driver_output(port_num, dd->ifd->buf,
+ dd->ifd->sz);
+ clear_fd_data(dd->ifd);
+ }
+ else { /* if (res < dd->ifd->remain) */
+ dd->ifd->cpos += res;
+ dd->ifd->remain -= res;
+ }
+ }
+ else if (dd->ifd->remain == 0) { /* clean fd */
+ byte *read_buf = (byte *) erts_alloc(ERTS_ALC_T_SYS_READ_BUF,
+ ERTS_SYS_READ_BUF_SZ);
+ /* We make one read attempt and see what happens */
+ res = read(ready_fd, read_buf, ERTS_SYS_READ_BUF_SZ);
+ if (res < 0) {
+ if ((errno != EINTR) && (errno != ERRNO_BLOCK))
+ port_inp_failure(dd, res);
+ }
+ else if (res == 0) { /* eof */
+ port_inp_failure(dd, res);
+ }
+ else if (res < packet_bytes - dd->ifd->psz) {
+ memcpy(dd->ifd->pbuf+dd->ifd->psz,
+ read_buf, res);
+ dd->ifd->psz += res;
+ }
+ else { /* if (res >= packet_bytes) */
+ unsigned char* cpos = read_buf;
+ int bytes_left = res;
+
+ while (1) {
+ int psz = dd->ifd->psz;
+ char* pbp = dd->ifd->pbuf + psz;
+
+ while(bytes_left && (psz < packet_bytes)) {
+ *pbp++ = *cpos++;
+ bytes_left--;
+ psz++;
+ }
+
+ if (psz < packet_bytes) {
+ dd->ifd->psz = psz;
+ break;
+ }
+ dd->ifd->psz = 0;
+
+ switch (packet_bytes) {
+ case 1: h = get_int8(dd->ifd->pbuf); break;
+ case 2: h = get_int16(dd->ifd->pbuf); break;
+ case 4: h = get_int32(dd->ifd->pbuf); break;
+ default: ASSERT(0); return; /* -1; */
+ }
+
+ if (h <= (bytes_left)) {
+ driver_output(port_num, (char*) cpos, h);
+ cpos += h;
+ bytes_left -= h;
+ continue;
+ }
+ else { /* The last message we got was split */
+ char *buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h);
+ if (!buf) {
+ errno = ENOMEM;
+ port_inp_failure(dd, -1);
+ }
+ else {
+ erts_smp_atomic_add_nob(&sys_misc_mem_sz, h);
+ sys_memcpy(buf, cpos, bytes_left);
+ dd->ifd->buf = buf;
+ dd->ifd->sz = h;
+ dd->ifd->remain = h - bytes_left;
+ dd->ifd->cpos = buf + bytes_left;
+ }
+ break;
+ }
+ }
+ }
+ erts_free(ERTS_ALC_T_SYS_READ_BUF, (void *) read_buf);
+ }
+}
+
+
+/* fd is the drv_data that is returned from the */
+/* initial start routine */
+/* ready_fd is the descriptor that is ready to read */
+
+static void ready_output(ErlDrvData e, ErlDrvEvent ready_fd)
+{
+ ErtsSysDriverData *dd = (ErtsSysDriverData*)e;
+ ErlDrvPort ix = dd->port_num;
+ int n;
+ struct iovec* iv;
+ int vsize;
+
+ if ((iv = (struct iovec*) driver_peekq(ix, &vsize)) == NULL) {
+ driver_select(ix, ready_fd, ERL_DRV_WRITE, 0);
+ if (dd->pid > 0 && dd->ofd->fd < 0) {
+ /* The port was opened with 'in' option, which means we
+ should close the output fd as soon as the command has
+ been sent. */
+ driver_select(ix, ready_fd, ERL_DRV_WRITE|ERL_DRV_USE, 0);
+ erts_smp_atomic_add_nob(&sys_misc_mem_sz, -sizeof(ErtsSysFdData));
+ dd->ofd = NULL;
+ }
+ if (dd->terminating)
+ driver_failure_atom(dd->port_num,"normal");
+ return; /* 0; */
+ }
+ vsize = vsize > MAX_VSIZE ? MAX_VSIZE : vsize;
+ if ((n = writev(ready_fd, iv, vsize)) > 0) {
+ if (driver_deq(ix, n) == 0)
+ set_busy_port(ix, 0);
+ }
+ else if (n < 0) {
+ if (errno == ERRNO_BLOCK || errno == EINTR)
+ return; /* 0; */
+ else {
+ int res = errno;
+ driver_select(ix, ready_fd, ERL_DRV_WRITE, 0);
+ driver_failure_posix(ix, res);
+ return; /* -1; */
+ }
+ }
+ return; /* 0; */
+}
+
+static void stop_select(ErlDrvEvent fd, void* _)
+{
+ close((int)fd);
+}
+
+#if FDBLOCK
+
+static void
+fd_async(void *async_data)
+{
+ int res;
+ ErtsSysDriverData *dd = (ErtsSysDriverData *)async_data;
+ SysIOVec *iov0;
+ SysIOVec *iov;
+ int iovlen;
+ int err = 0;
+ /* much of this code is stolen from efile_drv:invoke_writev */
+ driver_pdl_lock(dd->blocking->pdl);
+ iov0 = driver_peekq(dd->port_num, &iovlen);
+ iovlen = iovlen < MAXIOV ? iovlen : MAXIOV;
+ iov = erts_alloc_fnf(ERTS_ALC_T_SYS_WRITE_BUF,
+ sizeof(SysIOVec)*iovlen);
+ if (!iov) {
+ res = -1;
+ err = ENOMEM;
+ driver_pdl_unlock(dd->blocking->pdl);
+ } else {
+ memcpy(iov,iov0,iovlen*sizeof(SysIOVec));
+ driver_pdl_unlock(dd->blocking->pdl);
+
+ do {
+ res = writev(dd->ofd->fd, iov, iovlen);
+ } while (res < 0 && errno == EINTR);
+ if (res < 0)
+ err = errno;
+ err = errno;
+
+ erts_free(ERTS_ALC_T_SYS_WRITE_BUF, iov);
+ }
+ dd->blocking->res = res;
+ dd->blocking->err = err;
+}
+
+void fd_ready_async(ErlDrvData drv_data,
+ ErlDrvThreadData thread_data) {
+ ErtsSysDriverData *dd = (ErtsSysDriverData *)thread_data;
+ ErlDrvPort port_num = dd->port_num;
+
+ ASSERT(dd->blocking);
+
+ if (dd->blocking->res > 0) {
+ driver_pdl_lock(dd->blocking->pdl);
+ if (driver_deq(port_num, dd->blocking->res) == 0) {
+ driver_pdl_unlock(dd->blocking->pdl);
+ set_busy_port(port_num, 0);
+ if (dd->terminating) {
+ /* The port is has been ordered to terminate
+ from either fd_flush or port_inp_failure */
+ if (dd->terminating == 1)
+ driver_failure_atom(port_num, "normal");
+ else if (dd->terminating == 2)
+ driver_failure_eof(port_num);
+ else if (dd->terminating < 0)
+ driver_failure_posix(port_num, -dd->terminating);
+ return; /* -1; */
+ }
+ } else {
+ driver_pdl_unlock(dd->blocking->pdl);
+ /* still data left to write in queue */
+ driver_async(port_num, &dd->blocking->pkey, fd_async, dd, NULL);
+ return /* 0; */;
+ }
+ } else if (dd->blocking->res < 0) {
+ if (dd->blocking->err == ERRNO_BLOCK) {
+ set_busy_port(port_num, 1);
+ /* still data left to write in queue */
+ driver_async(port_num, &dd->blocking->pkey, fd_async, dd, NULL);
+ } else
+ driver_failure_posix(port_num, dd->blocking->err);
+ return; /* -1; */
+ }
+ return; /* 0; */
+}
+
+#endif
+
+/* Forker driver */
+
+static int forker_fd;
+
+static ErlDrvData forker_start(ErlDrvPort port_num, char* name,
+ SysDriverOpts* opts)
+{
+
+ int i;
+ int fds[2];
+ int res, unbind;
+ char bindir[MAXPATHLEN];
+ size_t bindirsz = sizeof(bindir);
+ Uint csp_path_sz;
+ char *child_setup_prog;
+
+ forker_port = erts_drvport2id(port_num);
+
+ res = erts_sys_getenv_raw("BINDIR", bindir, &bindirsz);
+ if (res != 0) {
+ if (res < 0)
+ erts_exit(1,
+ "Environment variable BINDIR is not set\n");
+ if (res > 0)
+ erts_exit(1,
+ "Value of environment variable BINDIR is too large\n");
+ }
+ if (bindir[0] != DIR_SEPARATOR_CHAR)
+ erts_exit(1,
+ "Environment variable BINDIR does not contain an"
+ " absolute path\n");
+ csp_path_sz = (strlen(bindir)
+ + 1 /* DIR_SEPARATOR_CHAR */
+ + sizeof(CHILD_SETUP_PROG_NAME)
+ + 1);
+ child_setup_prog = erts_alloc(ERTS_ALC_T_CS_PROG_PATH, csp_path_sz);
+ erts_snprintf(child_setup_prog, csp_path_sz,
+ "%s%c%s",
+ bindir,
+ DIR_SEPARATOR_CHAR,
+ CHILD_SETUP_PROG_NAME);
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
+ erts_exit(ERTS_ABORT_EXIT,
+ "Could not open unix domain socket in spawn_init: %d\n",
+ errno);
+ }
+
+ forker_fd = fds[0];
+
+ unbind = erts_sched_bind_atfork_prepare();
+
+ i = fork();
+
+ if (i == 0) {
+ /* The child */
+ char *cs_argv[FORKER_ARGV_NO_OF_ARGS] =
+ {CHILD_SETUP_PROG_NAME, NULL, NULL};
+ char buff[128];
+
+ erts_sched_bind_atfork_child(unbind);
+
+ snprintf(buff, 128, "%d", sys_max_files());
+ cs_argv[FORKER_ARGV_MAX_FILES] = buff;
+
+ /* We preallocate fd 3 for the uds fd */
+ if (fds[1] != 3) {
+ dup2(fds[1], 3);
+ }
+
+#if defined(USE_SETPGRP_NOARGS) /* SysV */
+ (void) setpgrp();
+#elif defined(USE_SETPGRP) /* BSD */
+ (void) setpgrp(0, getpid());
+#else /* POSIX */
+ (void) setsid();
+#endif
+
+ execv(child_setup_prog, cs_argv);
+ _exit(1);
+ }
+
+ erts_sched_bind_atfork_parent(unbind);
+
+ erts_free(ERTS_ALC_T_CS_PROG_PATH, child_setup_prog);
+
+ close(fds[1]);
+
+ SET_NONBLOCKING(forker_fd);
+
+ driver_select(port_num, forker_fd, ERL_DRV_READ|ERL_DRV_USE, 1);
+
+ return (ErlDrvData)port_num;
+}
+
+static void forker_stop(ErlDrvData e)
+{
+ /* we probably should do something here,
+ the port has been closed by the user. */
+}
+
+static void forker_ready_input(ErlDrvData e, ErlDrvEvent fd)
+{
+ int res;
+ ErtsSysForkerProto *proto;
+
+ proto = erts_alloc(ERTS_ALC_T_DRV_CTRL_DATA, sizeof(*proto));
+
+ if ((res = read(fd, proto, sizeof(*proto))) < 0) {
+ if (errno == ERRNO_BLOCK)
+ return;
+ erts_exit(ERTS_DUMP_EXIT, "Failed to read from erl_child_setup: %d\n", errno);
+ }
+
+ if (res == 0)
+ erts_exit(ERTS_DUMP_EXIT, "erl_child_setup closed\n");
+
+ ASSERT(res == sizeof(*proto));
+
+#ifdef FORKER_PROTO_START_ACK
+ if (proto->action == ErtsSysForkerProtoAction_StartAck) {
+ /* Ideally we would like to not have to ack each Start
+ command being sent over the uds, but it would seem
+ that some operating systems (only observed on FreeBSD)
+ throw away data on the uds when the socket becomes full,
+ so we have to.
+ */
+ ErlDrvPort port_num = (ErlDrvPort)e;
+ int vlen;
+ SysIOVec *iov = driver_peekq(port_num, &vlen);
+ ErtsSysForkerProto *proto = (ErtsSysForkerProto *)iov[0].iov_base;
+
+ close(proto->u.start.fds[0]);
+ close(proto->u.start.fds[1]);
+ if (proto->u.start.fds[1] != proto->u.start.fds[2])
+ close(proto->u.start.fds[2]);
+
+ driver_deq(port_num, sizeof(*proto));
+
+ if (driver_sizeq(port_num) > 0)
+ driver_select(port_num, forker_fd, ERL_DRV_WRITE|ERL_DRV_USE, 1);
+ } else
+#endif
+ {
+ ASSERT(proto->action == ErtsSysForkerProtoAction_SigChld);
+
+ /* ideally this would be a port_command call, but as command is
+ already used by the spawn_driver, we use control instead.
+ Note that when using erl_drv_port_control it is an asynchronous
+ control. */
+ erl_drv_port_control(proto->u.sigchld.port_id, 'S',
+ (char*)proto, sizeof(*proto));
+ }
+
+}
+
+static void forker_ready_output(ErlDrvData e, ErlDrvEvent fd)
+{
+ ErlDrvPort port_num = (ErlDrvPort)e;
+
+#ifndef FORKER_PROTO_START_ACK
+ while (driver_sizeq(port_num) > 0) {
+#endif
+ int vlen;
+ SysIOVec *iov = driver_peekq(port_num, &vlen);
+ ErtsSysForkerProto *proto = (ErtsSysForkerProto *)iov[0].iov_base;
+ ASSERT(iov[0].iov_len >= (sizeof(*proto)));
+ if (sys_uds_write(forker_fd, (char*)proto, sizeof(*proto),
+ proto->u.start.fds, 3, 0) < 0) {
+ if (errno == ERRNO_BLOCK)
+ return;
+ erts_exit(ERTS_DUMP_EXIT, "Failed to write to erl_child_setup: %d\n", errno);
+ }
+#ifndef FORKER_PROTO_START_ACK
+ close(proto->u.start.fds[0]);
+ close(proto->u.start.fds[1]);
+ if (proto->u.start.fds[1] != proto->u.start.fds[2])
+ close(proto->u.start.fds[2]);
+ driver_deq(port_num, sizeof(*proto));
+ }
+#endif
+
+ driver_select(port_num, forker_fd, ERL_DRV_WRITE, 0);
+}
+
+static ErlDrvSSizeT forker_control(ErlDrvData e, unsigned int cmd, char *buf,
+ ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen)
+{
+ ErtsSysForkerProto *proto = (ErtsSysForkerProto *)buf;
+ ErlDrvPort port_num = (ErlDrvPort)e;
+ int res;
+
+ driver_enq(port_num, buf, len);
+ if (driver_sizeq(port_num) > sizeof(*proto)) {
+ return 0;
+ }
+
+ if ((res = sys_uds_write(forker_fd, (char*)proto, sizeof(*proto),
+ proto->u.start.fds, 3, 0)) < 0) {
+ if (errno == ERRNO_BLOCK) {
+ driver_select(port_num, forker_fd, ERL_DRV_WRITE|ERL_DRV_USE, 1);
+ return 0;
+ }
+ erts_exit(ERTS_DUMP_EXIT, "Failed to write to erl_child_setup: %d\n", errno);
+ }
+
+#ifndef FORKER_PROTO_START_ACK
+ ASSERT(res == sizeof(*proto));
+ close(proto->u.start.fds[0]);
+ close(proto->u.start.fds[1]);
+ if (proto->u.start.fds[1] != proto->u.start.fds[2])
+ close(proto->u.start.fds[2]);
+ driver_deq(port_num, sizeof(*proto));
+#endif
+
+ return 0;
+}
diff --git a/erts/emulator/sys/unix/sys_float.c b/erts/emulator/sys/unix/sys_float.c
index 8fe7e599e5..6435da086f 100644
--- a/erts/emulator/sys/unix/sys_float.c
+++ b/erts/emulator/sys/unix/sys_float.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -53,7 +53,7 @@ static void erts_init_fp_exception(void)
void erts_thread_init_fp_exception(void)
{
unsigned long *fpe = erts_alloc(ERTS_ALC_T_FP_EXCEPTION, sizeof(*fpe));
- *fpe = 0L;
+ *fpe = 0;
erts_tsd_set(fpe_key, fpe);
}
@@ -102,6 +102,17 @@ void erts_fp_check_init_error(volatile unsigned long *fpexnp)
#define __DARWIN__ 1
#endif
+/*
+ * Define two processor and possibly OS-specific primitives:
+ *
+ * static void unmask_fpe(void);
+ * -- unmask invalid, overflow, and divide-by-zero exceptions
+ *
+ * static int mask_fpe(void);
+ * -- mask invalid, overflow, and divide-by-zero exceptions
+ * -- return non-zero if the previous state was unmasked
+ */
+
#if (defined(__i386__) || defined(__x86_64__)) && defined(__GNUC__)
static void unmask_x87(void)
@@ -113,7 +124,6 @@ static void unmask_x87(void)
__asm__ __volatile__("fldcw %0" : : "m"(cw));
}
-/* mask x87 FPE, return true if the previous state was unmasked */
static int mask_x87(void)
{
unsigned short cw;
@@ -136,7 +146,6 @@ static void unmask_sse2(void)
__asm__ __volatile__("ldmxcsr %0" : : "m"(mxcsr));
}
-/* mask SSE2 FPE, return true if the previous state was unmasked */
static int mask_sse2(void)
{
unsigned int mxcsr;
@@ -257,21 +266,19 @@ static int cpu_has_sse2(void)
}
#endif /* !__x86_64__ */
-static void unmask_fpe(void)
+static void unmask_fpe_internal(void)
{
- __asm__ __volatile__("fnclex");
unmask_x87();
if (cpu_has_sse2())
unmask_sse2();
}
-static void unmask_fpe_conditional(int unmasked)
+static void unmask_fpe(void)
{
- if (unmasked)
- unmask_fpe();
+ __asm__ __volatile__("fnclex");
+ unmask_fpe_internal();
}
-/* mask x86 FPE, return true if the previous state was unmasked */
static int mask_fpe(void)
{
int unmasked;
@@ -285,9 +292,7 @@ static int mask_fpe(void)
void erts_restore_fpu(void)
{
__asm__ __volatile__("fninit");
- unmask_x87();
- if (cpu_has_sse2())
- unmask_sse2();
+ unmask_fpe_internal();
}
#elif defined(__sparc__) && defined(__linux__)
@@ -310,13 +315,6 @@ static void unmask_fpe(void)
__asm__ __volatile__(LDX " %0, %%fsr" : : "m"(fsr));
}
-static void unmask_fpe_conditional(int unmasked)
-{
- if (unmasked)
- unmask_fpe();
-}
-
-/* mask SPARC FPE, return true if the previous state was unmasked */
static int mask_fpe(void)
{
unsigned long fsr;
@@ -431,13 +429,6 @@ static void unmask_fpe(void)
set_fpscr(0x80|0x40|0x10); /* VE, OE, ZE; not UE or XE */
}
-static void unmask_fpe_conditional(int unmasked)
-{
- if (unmasked)
- unmask_fpe();
-}
-
-/* mask PowerPC FPE, return true if the previous state was unmasked */
static int mask_fpe(void)
{
int unmasked;
@@ -447,20 +438,13 @@ static int mask_fpe(void)
return unmasked;
}
-#else
+#else /* !(x86 || (sparc && linux) || (powerpc && (linux || darwin))) */
static void unmask_fpe(void)
{
fpsetmask(FP_X_INV | FP_X_OFL | FP_X_DZ);
}
-static void unmask_fpe_conditional(int unmasked)
-{
- if (unmasked)
- unmask_fpe();
-}
-
-/* mask IEEE FPE, return true if previous state was unmasked */
static int mask_fpe(void)
{
const fp_except unmasked_mask = FP_X_INV | FP_X_OFL | FP_X_DZ;
@@ -472,6 +456,16 @@ static int mask_fpe(void)
#endif
+/*
+ * Define a processor and OS-specific SIGFPE handler.
+ *
+ * The effect of receiving a SIGFPE should be:
+ * 1. Update the processor context:
+ * a) on x86: mask FP exceptions, do not skip faulting instruction
+ * b) on SPARC and PowerPC: unmask FP exceptions, skip faulting instruction
+ * 2. call set_current_fp_exception with the PC of the faulting instruction
+ */
+
#if (defined(__linux__) && (defined(__i386__) || defined(__x86_64__) || defined(__sparc__) || defined(__powerpc__))) || (defined(__DARWIN__) && (defined(__i386__) || defined(__x86_64__) || defined(__ppc__))) || (defined(__FreeBSD__) && (defined(__x86_64__) || defined(__i386__))) || ((defined(__NetBSD__) || defined(__OpenBSD__)) && defined(__x86_64__)) || (defined(__sun__) && defined(__x86_64__))
#if defined(__linux__) && defined(__i386__)
@@ -499,18 +493,8 @@ static int mask_fpe(void)
#define mc_pc(mc) ((mc)->gregs[REG_RIP])
#elif defined(__linux__) && defined(__i386__)
#define mc_pc(mc) ((mc)->gregs[REG_EIP])
-#elif defined(__DARWIN__) && defined(__i386__)
-#ifdef DARWIN_MODERN_MCONTEXT
-#define mc_pc(mc) ((mc)->__ss.__eip)
-#else
-#define mc_pc(mc) ((mc)->ss.eip)
-#endif
-#elif defined(__DARWIN__) && defined(__x86_64__)
-#ifdef DARWIN_MODERN_MCONTEXT
-#define mc_pc(mc) ((mc)->__ss.__rip)
-#else
-#define mc_pc(mc) ((mc)->ss.rip)
-#endif
+#elif defined(__DARWIN__)
+# error "Floating-point exceptions not supported on MacOS X"
#elif defined(__FreeBSD__) && defined(__x86_64__)
#define mc_pc(mc) ((mc)->mc_rip)
#elif defined(__FreeBSD__) && defined(__i386__)
@@ -530,8 +514,7 @@ static void fpe_sig_action(int sig, siginfo_t *si, void *puc)
ucontext_t *uc = puc;
unsigned long pc;
-#if defined(__linux__)
-#if defined(__x86_64__)
+#if defined(__linux__) && defined(__x86_64__)
mcontext_t *mc = &uc->uc_mcontext;
fpregset_t fpstate = mc->fpregs;
pc = mc_pc(mc);
@@ -543,26 +526,26 @@ static void fpe_sig_action(int sig, siginfo_t *si, void *puc)
set encoding makes that a poor solution here. */
fpstate->mxcsr = 0x1F80;
fpstate->swd &= ~0xFF;
-#elif defined(__i386__)
+#elif defined(__linux__) && defined(__i386__)
mcontext_t *mc = &uc->uc_mcontext;
fpregset_t fpstate = mc->fpregs;
pc = mc_pc(mc);
if ((fpstate->status >> 16) == X86_FXSR_MAGIC)
((struct _fpstate*)fpstate)->mxcsr = 0x1F80;
fpstate->sw &= ~0xFF;
-#elif defined(__sparc__) && defined(__arch64__)
+#elif defined(__linux__) && defined(__sparc__) && defined(__arch64__)
/* on SPARC the 3rd parameter points to a sigcontext not a ucontext */
struct sigcontext *sc = (struct sigcontext*)puc;
pc = sc->sigc_regs.tpc;
sc->sigc_regs.tpc = sc->sigc_regs.tnpc;
sc->sigc_regs.tnpc += 4;
-#elif defined(__sparc__)
+#elif defined(__linux__) && defined(__sparc__)
/* on SPARC the 3rd parameter points to a sigcontext not a ucontext */
struct sigcontext *sc = (struct sigcontext*)puc;
pc = sc->si_regs.pc;
sc->si_regs.pc = sc->si_regs.npc;
sc->si_regs.npc = (unsigned long)sc->si_regs.npc + 4;
-#elif defined(__powerpc__)
+#elif defined(__linux__) && defined(__powerpc__)
#if defined(__powerpc64__)
mcontext_t *mc = &uc->uc_mcontext;
unsigned long *regs = &mc->gp_regs[0];
@@ -573,19 +556,8 @@ static void fpe_sig_action(int sig, siginfo_t *si, void *puc)
pc = regs[PT_NIP];
regs[PT_NIP] += 4;
regs[PT_FPSCR] = 0x80|0x40|0x10; /* VE, OE, ZE; not UE or XE */
-#endif
#elif defined(__DARWIN__) && (defined(__i386__) || defined(__x86_64__))
-#ifdef DARWIN_MODERN_MCONTEXT
- mcontext_t mc = uc->uc_mcontext;
- pc = mc_pc(mc);
- mc->__fs.__fpu_mxcsr = 0x1F80;
- *(unsigned short *)&mc->__fs.__fpu_fsw &= ~0xFF;
-#else
- mcontext_t mc = uc->uc_mcontext;
- pc = mc_pc(mc);
- mc->fs.fpu_mxcsr = 0x1F80;
- *(unsigned short *)&mc->fs.fpu_fsw &= ~0xFF;
-#endif /* DARWIN_MODERN_MCONTEXT */
+# error "Floating-point exceptions not supported on MacOS X"
#elif defined(__DARWIN__) && defined(__ppc__)
mcontext_t mc = uc->uc_mcontext;
pc = mc->ss.srr0;
@@ -641,7 +613,7 @@ static void fpe_sig_action(int sig, siginfo_t *si, void *puc)
#endif
#if 0
{
- char buf[64];
+ char buf[128];
snprintf(buf, sizeof buf, "%s: FPE at %p\r\n", __FUNCTION__, (void*)pc);
write(2, buf, strlen(buf));
}
@@ -726,7 +698,8 @@ int erts_sys_block_fpe(void)
void erts_sys_unblock_fpe(int unmasked)
{
- unmask_fpe_conditional(unmasked);
+ if (unmasked)
+ unmask_fpe();
}
#endif
@@ -838,11 +811,6 @@ sys_chars_to_double(char* buf, double* fp)
int
matherr(struct exception *exc)
{
-#if !defined(NO_FPE_SIGNALS)
- volatile unsigned long *fpexnp = erts_get_current_fp_exception();
- if (fpexnp != NULL)
- *fpexnp = (unsigned long)__builtin_return_address(0);
-#endif
return 1;
}
diff --git a/erts/emulator/sys/unix/sys_time.c b/erts/emulator/sys/unix/sys_time.c
index 95b3daa4ed..4f26639703 100644
--- a/erts/emulator/sys/unix/sys_time.c
+++ b/erts/emulator/sys/unix/sys_time.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -65,6 +65,8 @@
# include <fcntl.h>
#endif
+static void init_perf_counter(void);
+
/******************* Routines for time measurement *********************/
#undef ERTS_SYS_TIME_INTERNAL_STATE_WRITE_FREQ__
@@ -217,7 +219,10 @@ sys_init_time(ErtsSysInitTimeResult *init_resp)
#endif
init_resp->os_monotonic_time_info.resolution = (Uint64) 1000*1000*1000;
-#if defined(HAVE_CLOCK_GETRES) && defined(MONOTONIC_CLOCK_ID)
+#if defined(ERTS_HAVE_MACH_CLOCK_GETRES) && defined(MONOTONIC_CLOCK_ID)
+ init_resp->os_monotonic_time_info.resolution
+ = mach_clock_getres(&internal_state.r.o.mach.clock.monotonic);
+#elif defined(HAVE_CLOCK_GETRES) && defined(MONOTONIC_CLOCK_ID)
{
struct timespec ts;
if (clock_getres(MONOTONIC_CLOCK_ID, &ts) == 0) {
@@ -227,9 +232,6 @@ sys_init_time(ErtsSysInitTimeResult *init_resp)
init_resp->os_monotonic_time_info.resolution = 1;
}
}
-#elif defined(ERTS_HAVE_MACH_CLOCK_GETRES) && defined(MONOTONIC_CLOCK_ID)
- init_resp->os_monotonic_time_info.resolution
- = mach_clock_getres(&internal_state.r.o.mach.clock.monotonic);
#endif
#ifdef MONOTONIC_CLOCK_ID_STR
@@ -377,7 +379,10 @@ sys_init_time(ErtsSysInitTimeResult *init_resp)
init_resp->os_system_time_info.locked_use = 0;
init_resp->os_system_time_info.resolution = (Uint64) 1000*1000*1000;
-#if defined(HAVE_CLOCK_GETRES) && defined(WALL_CLOCK_ID)
+#if defined(ERTS_HAVE_MACH_CLOCK_GETRES) && defined(WALL_CLOCK_ID)
+ init_resp->os_system_time_info.resolution
+ = mach_clock_getres(&internal_state.r.o.mach.clock.wall);
+#elif defined(HAVE_CLOCK_GETRES) && defined(WALL_CLOCK_ID)
{
struct timespec ts;
if (clock_getres(WALL_CLOCK_ID, &ts) == 0) {
@@ -387,9 +392,6 @@ sys_init_time(ErtsSysInitTimeResult *init_resp)
init_resp->os_system_time_info.resolution = 1;
}
}
-#elif defined(ERTS_HAVE_MACH_CLOCK_GETRES) && defined(WALL_CLOCK_ID)
- init_resp->os_system_time_info.resolution
- = mach_clock_getres(&internal_state.r.o.mach.clock.wall);
#endif
#if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME)
@@ -404,6 +406,8 @@ sys_init_time(ErtsSysInitTimeResult *init_resp)
# error Missing erts_os_system_time() implementation
#endif
+ init_perf_counter();
+
}
void
@@ -908,10 +912,120 @@ erts_os_times(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep)
#endif
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
+ * Performance counter functions *
+\* */
+
+
+/* What resolution to spin to in micro seconds */
+#define RESOLUTION 100
+/* How many iterations to spin */
+#define ITERATIONS 1
+/* How many significant figures to round to */
+#define SIGFIGS 3
+
+static ErtsSysPerfCounter calculate_perf_counter_unit(void) {
+ int i;
+ ErtsSysPerfCounter pre, post;
+ double value = 0;
+ double round_factor;
+#if defined(HAVE_GETHRTIME) && defined(GETHRTIME_WITH_CLOCK_GETTIME)
+ struct timespec basetime,comparetime;
+#define __GETTIME(arg) clock_gettime(CLOCK_MONOTONIC,arg)
+#define __GETUSEC(arg) (arg.tv_nsec / 1000)
+#else
+ SysTimeval basetime,comparetime;
+#define __GETTIME(arg) sys_gettimeofday(arg)
+#define __GETUSEC(arg) arg.tv_usec
+#endif
+
+ for (i = 0; i < ITERATIONS; i++) {
+ /* Make sure usec just flipped over at current resolution */
+ __GETTIME(&basetime);
+ do {
+ __GETTIME(&comparetime);
+ } while ((__GETUSEC(basetime) / RESOLUTION) == (__GETUSEC(comparetime) / RESOLUTION));
+
+ pre = erts_sys_perf_counter();
+
+ __GETTIME(&basetime);
+ do {
+ __GETTIME(&comparetime);
+ } while ((__GETUSEC(basetime) / RESOLUTION) == (__GETUSEC(comparetime) / RESOLUTION));
+
+ post = erts_sys_perf_counter();
+
+ value += post - pre;
+ }
+ /* After this value is ticks per us */
+ value /= (RESOLUTION*ITERATIONS);
+
+ /* We round to 3 significant figures */
+ round_factor = pow(10.0, SIGFIGS - ceil(log10(value)));
+ value = ((ErtsSysPerfCounter)(value * round_factor + 0.5)) / round_factor;
+
+ /* convert to ticks per second */
+ return 1000000 * value;
+}
+
+static int have_rdtscp(void)
+{
+#if defined(ETHR_X86_RUNTIME_CONF__)
+ /* On early x86 cpu's the tsc varies with the current speed of the cpu,
+ which means that the time per tick vary depending on the current
+ load of the cpu. We do not want this as it would give very scewed
+ numbers when the cpu is mostly idle.
+ The linux kernel seems to think that checking for constant and
+ reliable is enough to trust the counter so we do the same.
+
+ If this test is not good enough, I don't know what we'll do.
+ Maybe fallback on erts_sys_hrtime always, but that would be a shame as
+ rdtsc is about 3 times faster than hrtime... */
+ return ETHR_X86_RUNTIME_CONF_HAVE_CONSTANT_TSC__ &&
+ ETHR_X86_RUNTIME_CONF_HAVE_TSC_RELIABLE__;
+#else
+ return 0;
+#endif
+}
+
+static ErtsSysPerfCounter rdtsc(void)
+{
+ /* It may have been a good idea to put the cpuid instruction before
+ the rdtsc, but I decided against it because it is not really
+ needed for msacc, and it slows it down by quite a bit (5-7 times slower).
+ As a result though, this timestamp becomes much less
+ accurate as it might be re-ordered to be executed way before or after this
+ function is called.
+ */
+ ErtsSysPerfCounter ts;
+#if defined(__x86_64__)
+ __asm__ __volatile__ ("rdtsc\n\t"
+ "shl $32, %%rdx\n\t"
+ "or %%rdx, %0" : "=a" (ts) : : "rdx");
+#elif defined(__i386__)
+ __asm__ __volatile__ ("rdtsc\n\t"
+ : "=A" (ts) );
+#endif
+ return ts;
+}
+
+static void init_perf_counter(void)
+{
+ if (have_rdtscp()) {
+ erts_sys_time_data__.r.o.perf_counter = rdtsc;
+ erts_sys_time_data__.r.o.perf_counter_unit = calculate_perf_counter_unit();
+ } else {
+ erts_sys_time_data__.r.o.perf_counter = erts_sys_hrtime;
+ erts_sys_time_data__.r.o.perf_counter_unit = ERTS_HRTIME_UNIT;
+ }
+}
+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifdef HAVE_GETHRVTIME_PROCFS_IOCTL
+/* The code below only has effect on solaris < 10,
+ needed in order for gehhrvtime to work properly */
int sys_start_hrvtime(void)
{
long msacct = PR_MSACCT;
diff --git a/erts/emulator/sys/unix/sys_uds.c b/erts/emulator/sys/unix/sys_uds.c
new file mode 100644
index 0000000000..dd0a3b03ff
--- /dev/null
+++ b/erts/emulator/sys/unix/sys_uds.c
@@ -0,0 +1,155 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2002-2016. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#include "sys_uds.h"
+
+int
+sys_uds_readv(int fd, struct iovec *iov, size_t iov_len,
+ int *fds, int fd_count, int flags) {
+ struct msghdr msg;
+ struct cmsghdr *cmsg = NULL;
+ char ancillary_buff[256] = {0};
+ int res, i = 0;
+
+ /* setup a place to fill in message contents */
+ memset(&msg, 0, sizeof(struct msghdr));
+ msg.msg_iov = iov;
+ msg.msg_iovlen = iov_len;
+
+ /* provide space for the ancillary data */
+ msg.msg_control = ancillary_buff;
+ msg.msg_controllen = sizeof(ancillary_buff);
+
+ if((res = recvmsg(fd, &msg, flags)) < 0) {
+#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__)
+ /* When some OS X versions run out of fd's
+ they give EMSGSIZE instead of EMFILE.
+ We remap this as we want the correct
+ error to appear for the user */
+ if (errno == EMSGSIZE)
+ errno = EMFILE;
+#endif
+ return res;
+ }
+
+ if((msg.msg_flags & MSG_CTRUNC) == MSG_CTRUNC)
+ {
+ /* We assume that we have given enough space for any header
+ that are sent to us. So the only remaining reason to get
+ this flag set is if the caller has run out of file descriptors.
+ */
+ errno = EMFILE;
+ return -1;
+ }
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg) ) {
+ if ((cmsg->cmsg_level == SOL_SOCKET) &&
+ (cmsg->cmsg_type == SCM_RIGHTS)) {
+ int *cmsg_data = (int *)CMSG_DATA(cmsg);
+ while ((char*)cmsg_data < (char*)cmsg + cmsg->cmsg_len) {
+ if (i < fd_count) {
+ fds[i++] = *cmsg_data++;
+ } else {
+ /* for some strange reason, we have received more FD's
+ than we wanted... close them if we are not running
+ debug. */
+ if(i >= fd_count) abort();
+ close(*cmsg_data++);
+ }
+ }
+ }
+ }
+
+ return res;
+}
+
+int
+sys_uds_read(int fd, char *buff, size_t len,
+ int *fds, int fd_count, int flags) {
+ struct iovec iov;
+ iov.iov_base = buff;
+ iov.iov_len = len;
+ return sys_uds_readv(fd, &iov, 1, fds, fd_count, flags);
+}
+
+
+int
+sys_uds_writev(int fd, struct iovec *iov, size_t iov_len,
+ int *fds, int fd_count, int flags) {
+
+ struct msghdr msg;
+ struct cmsghdr *cmsg = NULL;
+ int res, i;
+
+ /* initialize socket message */
+ memset(&msg, 0, sizeof(struct msghdr));
+
+ /* We flatten the iov if it is too long */
+ if (iov_len > MAXIOV) {
+ int size = 0;
+ char *buff;
+ for (i = 0; i < iov_len; i++)
+ size += iov[i].iov_len;
+ buff = malloc(size);
+
+ for (i = 0; i < iov_len; i++) {
+ memcpy(buff, iov[i].iov_base, iov[i].iov_len);
+ buff += iov[i].iov_len;
+ }
+
+ iov[0].iov_base = buff - size;
+ iov[0].iov_len = size;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+ } else {
+ msg.msg_iov = iov;
+ msg.msg_iovlen = iov_len;
+ }
+
+ /* initialize the ancillary data */
+ msg.msg_control = calloc(1, CMSG_SPACE(sizeof(int) * fd_count));
+ msg.msg_controllen = CMSG_SPACE(sizeof(int) * fd_count);
+
+ /* copy the fd array into the ancillary data */
+ cmsg = CMSG_FIRSTHDR(&msg);
+ if(!cmsg) abort();
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int) * fd_count);
+ memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * fd_count);
+
+ res = sendmsg(fd, &msg, flags);
+
+ if (iov_len > MAXIOV)
+ free(iov[0].iov_base);
+
+ free(msg.msg_control);
+
+ return res;
+}
+
+int
+sys_uds_write(int fd, char *buff, size_t len,
+ int *fds, int fd_count, int flags) {
+ struct iovec iov;
+ iov.iov_base = buff;
+ iov.iov_len = len;
+ return sys_uds_writev(fd, &iov, 1, fds, fd_count, flags);
+}
diff --git a/erts/emulator/sys/unix/sys_uds.h b/erts/emulator/sys/unix/sys_uds.h
new file mode 100644
index 0000000000..a598102d5c
--- /dev/null
+++ b/erts/emulator/sys/unix/sys_uds.h
@@ -0,0 +1,57 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2002-2016. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#ifndef _ERL_UNIX_UDS_H
+#define _ERL_UNIX_UDS_H
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#if defined(__sun__) && !defined(_XOPEN_SOURCE)
+#define _XOPEN_SOURCE 500
+#endif
+
+#include <limits.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#if defined IOV_MAX
+#define MAXIOV IOV_MAX
+#elif defined UIO_MAXIOV
+#define MAXIOV UIO_MAXIOV
+#else
+#define MAXIOV 16
+#endif
+
+#include "sys.h"
+
+int sys_uds_readv(int fd, struct iovec *iov, size_t iov_len,
+ int *fds, int fd_count, int flags);
+int sys_uds_read(int fd, char *buff, size_t len,
+ int *fds, int fd_count, int flags);
+int sys_uds_writev(int fd, struct iovec *iov, size_t iov_len,
+ int *fds, int fd_count, int flags);
+int sys_uds_write(int fd, char *buff, size_t len,
+ int *fds, int fd_count, int flags);
+
+#endif /* #ifndef _ERL_UNIX_UDS_H */
diff --git a/erts/emulator/sys/win32/dosmap.c b/erts/emulator/sys/win32/dosmap.c
index a98065c666..95fbba384b 100644
--- a/erts/emulator/sys/win32/dosmap.c
+++ b/erts/emulator/sys/win32/dosmap.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/sys/win32/driver_int.h b/erts/emulator/sys/win32/driver_int.h
index b931da52f5..50097d3fd2 100644
--- a/erts/emulator/sys/win32/driver_int.h
+++ b/erts/emulator/sys/win32/driver_int.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/sys/win32/erl_main.c b/erts/emulator/sys/win32/erl_main.c
index 21d9a58da6..9eee300f48 100644
--- a/erts/emulator/sys/win32/erl_main.c
+++ b/erts/emulator/sys/win32/erl_main.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2000-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/sys/win32/erl_poll.c b/erts/emulator/sys/win32/erl_poll.c
index 547d4b1d21..f23c7ab03d 100644
--- a/erts/emulator/sys/win32/erl_poll.c
+++ b/erts/emulator/sys/win32/erl_poll.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2007-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2007-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@
#include "erl_alloc.h"
#include "erl_poll.h"
#include "erl_time.h"
+#include "erl_msacc.h"
/*
* Some debug macros
@@ -423,7 +424,7 @@ static ERTS_INLINE int
wakeup_cause(ErtsPollSet ps)
{
int res;
- erts_aint32_t wakeup_state = erts_atomic32_read_nob(&ps->wakeup_state);
+ erts_aint32_t wakeup_state = erts_atomic32_read_acqb(&ps->wakeup_state);
switch (wakeup_state) {
case ERTS_POLL_WOKEN_IO_READY:
res = 0;
@@ -486,9 +487,8 @@ wake_poller(ErtsPollSet ps, int io_ready)
{
erts_aint32_t wakeup_state;
if (io_ready) {
- /* We may set the event multiple times. This is, however, harmless. */
- wakeup_state = erts_atomic32_read_nob(&ps->wakeup_state);
- erts_atomic32_set_relb(&ps->wakeup_state, ERTS_POLL_WOKEN_IO_READY);
+ wakeup_state = erts_atomic32_xchg_relb(&ps->wakeup_state,
+ ERTS_POLL_WOKEN_IO_READY);
}
else {
ERTS_THR_MEMORY_BARRIER;
@@ -1188,16 +1188,19 @@ int erts_poll_wait(ErtsPollSet ps,
if (timeout > 0 && !erts_atomic32_read_nob(&break_waiter_state)) {
HANDLE harr[2] = {ps->event_io_ready, break_happened_event};
int num_h = 2;
+ ERTS_MSACC_PUSH_STATE_M();
HARDDEBUGF(("Start waiting %d [%d]",num_h, (int) timeout));
ERTS_POLLSET_UNLOCK(ps);
#ifdef ERTS_SMP
erts_thr_progress_prepare_wait(NULL);
#endif
+ ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_SLEEP);
WaitForMultipleObjects(num_h, harr, FALSE, timeout);
#ifdef ERTS_SMP
erts_thr_progress_finalize_wait(NULL);
#endif
+ ERTS_MSACC_POP_STATE_M();
ERTS_POLLSET_LOCK(ps);
HARDDEBUGF(("Stop waiting %d [%d]",num_h, (int) timeout));
woke_up(ps);
diff --git a/erts/emulator/sys/win32/erl_win32_sys_ddll.c b/erts/emulator/sys/win32/erl_win32_sys_ddll.c
index 7c24a77e31..274133a346 100644
--- a/erts/emulator/sys/win32/erl_win32_sys_ddll.c
+++ b/erts/emulator/sys/win32/erl_win32_sys_ddll.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/sys/win32/erl_win_dyn_driver.h b/erts/emulator/sys/win32/erl_win_dyn_driver.h
index 9c699fdba0..6f28d513c2 100644
--- a/erts/emulator/sys/win32/erl_win_dyn_driver.h
+++ b/erts/emulator/sys/win32/erl_win_dyn_driver.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2003-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h
index 7e8dd8a4ca..78005aada9 100644
--- a/erts/emulator/sys/win32/erl_win_sys.h
+++ b/erts/emulator/sys/win32/erl_win_sys.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2014. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -182,11 +182,14 @@ typedef LONGLONG ErtsMonotonicTime;
typedef LONGLONG ErtsSysHrTime;
#endif
+#define ErtsStrToSint64 _strtoi64
+
typedef ErtsMonotonicTime ErtsSystemTime;
+typedef ErtsMonotonicTime ErtsSysPerfCounter;
ErtsSystemTime erts_os_system_time(void);
-#define ERTS_MONOTONIC_TIME_MIN (((ErtsMonotonicTime) 1) << 63)
+#define ERTS_MONOTONIC_TIME_MIN ((ErtsMonotonicTime) (1ULL << 63))
#define ERTS_MONOTONIC_TIME_MAX (~ERTS_MONOTONIC_TIME_MIN)
#define ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT 1
@@ -213,6 +216,7 @@ ERTS_GLB_INLINE ErtsMonotonicTime erts_os_monotonic_time(void);
ERTS_GLB_INLINE void erts_os_times(ErtsMonotonicTime *,
ErtsSystemTime *);
ERTS_GLB_INLINE ErtsSysHrTime erts_sys_hrtime(void);
+ERTS_GLB_INLINE ErtsSysPerfCounter erts_sys_perf_counter(void);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
@@ -234,6 +238,18 @@ erts_sys_hrtime(void)
return (*erts_sys_time_data__.r.o.sys_hrtime)();
}
+ERTS_GLB_INLINE ErtsSysPerfCounter
+erts_sys_perf_counter(void)
+{
+ return (*erts_sys_time_data__.r.o.sys_hrtime)();
+}
+
+ERTS_GLB_INLINE ErtsSysPerfCounter
+erts_sys_perf_counter_unit(void)
+{
+ return 1000 * 1000 * 1000;
+}
+
#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
extern void sys_gettimeofday(SysTimeval *tv);
diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c
index 37d3e16256..f3881e0736 100644
--- a/erts/emulator/sys/win32/sys.c
+++ b/erts/emulator/sys/win32/sys.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2014. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -40,7 +40,6 @@ void erts_sys_init_float(void);
void erl_start(int, char**);
void erts_exit(int n, char*, ...);
void erl_error(char*, va_list);
-void erl_crash_dump(char*, int, char*, ...);
/*
* Microsoft-specific function to map a WIN32 error code to a Posix errno.
@@ -1334,10 +1333,8 @@ spawn_start(ErlDrvPort port_num, char* utf8_name, SysDriverOpts* opts)
retval = set_driver_data(dp, hFromChild, hToChild, opts->read_write,
opts->exit_status);
if (retval != ERL_DRV_ERROR_GENERAL && retval != ERL_DRV_ERROR_ERRNO) {
- Port *prt = erts_drvport2port(port_num);
- /* We assume that this cannot generate a negative number */
- ASSERT(prt != ERTS_INVALID_ERL_DRV_PORT);
- prt->os_pid = (SWord) pid;
+ /* We assume that this cannot generate a negative number */
+ erl_drv_set_os_pid(port_num, pid);
}
}
@@ -1528,8 +1525,8 @@ create_child_process
* Parse out the program name from the command line (it can be quoted and
* contain spaces).
*/
- newcmdline = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, 2048*sizeof(wchar_t));
cmdlength = parse_command(origcmd);
+ newcmdline = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, (MAX_PATH+wcslen(origcmd)-cmdlength)*sizeof(wchar_t));
thecommand = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, (cmdlength+1)*sizeof(wchar_t));
wcsncpy(thecommand, origcmd, cmdlength);
thecommand[cmdlength] = L'\0';
@@ -3081,6 +3078,8 @@ erl_bin_write(buf, sz, max)
}
}
+#endif /* DEBUG */
+
void
erl_assert_error(const char* expr, const char* func, const char* file, int line)
{
@@ -3096,7 +3095,6 @@ erl_assert_error(const char* expr, const char* func, const char* file, int line)
DebugBreak();
}
-#endif /* DEBUG */
static void
check_supported_os_version(void)
@@ -3188,16 +3186,16 @@ erts_sys_pre_init(void)
eid.thread_create_parent_func = thr_create_cleanup;
erts_thr_init(&eid);
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ erts_lc_init();
#endif
-
- erts_init_sys_time_sup();
-
-#ifdef USE_THREADS
#ifdef ERTS_ENABLE_LOCK_COUNT
erts_lcnt_init();
#endif
#endif
+ erts_init_sys_time_sup();
+
erts_smp_atomic_init_nob(&sys_misc_mem_sz, 0);
}
@@ -3273,6 +3271,12 @@ void erl_sys_init(void)
}
void
+erl_sys_late_init(void)
+{
+ /* do nothing */
+}
+
+void
erts_sys_schedule_interrupt(int set)
{
erts_check_io_interrupt(set);
diff --git a/erts/emulator/sys/win32/sys_env.c b/erts/emulator/sys/win32/sys_env.c
index 67b6e55377..21ef71ad9a 100644
--- a/erts/emulator/sys/win32/sys_env.c
+++ b/erts/emulator/sys/win32/sys_env.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2002-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/sys/win32/sys_float.c b/erts/emulator/sys/win32/sys_float.c
index 86e822da5b..2b2d6ab7d3 100644
--- a/erts/emulator/sys/win32/sys_float.c
+++ b/erts/emulator/sys/win32/sys_float.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -139,8 +139,7 @@ sys_double_to_chars_ext(double fp, char *buffer, size_t buffer_size, size_t deci
int
matherr(struct _exception *exc)
{
- erl_fp_exception = 1;
- DEBUGF(("FP exception (matherr) (0x%x) (%d)\n", exc->type, erl_fp_exception));
+ DEBUGF(("FP exception (matherr) (0x%x)\n", exc->type));
return 1;
}
diff --git a/erts/emulator/sys/win32/sys_interrupt.c b/erts/emulator/sys/win32/sys_interrupt.c
index a89211fa8e..df838960eb 100644
--- a/erts/emulator/sys/win32/sys_interrupt.c
+++ b/erts/emulator/sys/win32/sys_interrupt.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2012. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/sys/win32/sys_time.c b/erts/emulator/sys/win32/sys_time.c
index 3cf7f7cf07..e8c67b3928 100644
--- a/erts/emulator/sys/win32/sys_time.c
+++ b/erts/emulator/sys/win32/sys_time.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -397,6 +397,7 @@ sys_init_time(ErtsSysInitTimeResult *init_resp)
erts_sys_time_data__.r.o.os_monotonic_time = os_mtime_func;
erts_sys_time_data__.r.o.os_times = os_times_func;
+ erts_sys_time_data__.r.o.sys_hrtime = sys_hrtime_func;
init_resp->os_monotonic_time_unit = time_unit;
init_resp->have_os_monotonic_time = 1;
init_resp->have_corrected_os_monotonic_time = 0;
diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile
index 77614d455c..b8e907f769 100644
--- a/erts/emulator/test/Makefile
+++ b/erts/emulator/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2012. 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.
@@ -53,6 +53,7 @@ MODULES= \
crypto_SUITE \
ddll_SUITE \
decode_packet_SUITE \
+ dirty_nif_SUITE \
distribution_SUITE \
driver_SUITE \
efile_SUITE \
@@ -69,19 +70,24 @@ MODULES= \
guard_SUITE \
hash_SUITE \
hibernate_SUITE \
+ hipe_SUITE \
list_bif_SUITE \
+ lttng_SUITE \
map_SUITE \
match_spec_SUITE \
module_info_SUITE \
monitor_SUITE \
+ multi_load_SUITE \
nested_SUITE \
nif_SUITE \
node_container_SUITE \
nofrag_SUITE \
num_bif_SUITE \
+ message_queue_data_SUITE \
op_SUITE \
port_SUITE \
port_bif_SUITE \
+ prim_eval_SUITE \
process_SUITE \
pseudoknot_SUITE \
receive_SUITE \
@@ -107,8 +113,11 @@ MODULES= \
trace_meta_SUITE \
trace_call_count_SUITE \
trace_call_time_SUITE \
+ tracer_SUITE \
+ tracer_test \
scheduler_SUITE \
old_scheduler_SUITE \
+ port_trace_SUITE \
unique_SUITE \
z_SUITE \
old_mod \
@@ -157,7 +166,7 @@ RELSYSDIR = $(RELEASE_PATH)/emulator_test
# FLAGS
# ----------------------------------------------------
ERL_MAKE_FLAGS +=
-ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include
+ERL_COMPILE_FLAGS +=
# ----------------------------------------------------
# Targets
diff --git a/erts/emulator/test/a_SUITE.erl b/erts/emulator/test/a_SUITE.erl
index 16f060fe34..880c5a5821 100644
--- a/erts/emulator/test/a_SUITE.erl
+++ b/erts/emulator/test/a_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -27,68 +27,43 @@
%%%-------------------------------------------------------------------
-module(a_SUITE).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2, long_timers/1, pollset_size/1]).
+-export([all/0, suite/0,
+ long_timers/1, pollset_size/1]).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]}].
all() ->
[long_timers, pollset_size].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-long_timers(doc) ->
- [];
-long_timers(suite) ->
- [];
long_timers(Config) when is_list(Config) ->
- Dir = ?config(data_dir, Config),
- ?line long_timers_test:start(Dir),
- ?line {comment,
- "Testcase started! This test will run in parallel with the "
- "erts testsuite and ends in the z_SUITE:long_timers testcase."}.
+ Dir = proplists:get_value(data_dir, Config),
+ long_timers_test:start(Dir),
+ {comment, "Testcase started! This test will run in parallel with the "
+ "erts testsuite and ends in the z_SUITE:long_timers testcase."}.
-pollset_size(doc) ->
- [];
-pollset_size(suite) ->
- [];
pollset_size(Config) when is_list(Config) ->
%% Ensure inet_gethost_native port program started, in order to
%% allow other suites to use it...
inet_gethost_native:gethostbyname("localhost"),
- ?line Parent = self(),
- ?line Go = make_ref(),
- ?line spawn(fun () ->
- Name = pollset_size_testcase_initial_state_holder,
- true = register(Name, self()),
- ChkIo = get_check_io_info(),
- io:format("Initial: ~p~n", [ChkIo]),
- Parent ! Go,
- receive
- {get_initial_check_io_result, Pid} ->
- Pid ! {initial_check_io_result, ChkIo}
- end
- end),
- ?line receive Go -> ok end,
- ?line {comment,
- "Testcase started! This test will run in parallel with the "
- "erts testsuite and ends in the z_SUITE:pollset_size testcase."}.
+ Parent = self(),
+ Go = make_ref(),
+ spawn(fun () ->
+ Name = pollset_size_testcase_initial_state_holder,
+ true = register(Name, self()),
+ ChkIo = get_check_io_info(),
+ io:format("Initial: ~p~n", [ChkIo]),
+ Parent ! Go,
+ receive
+ {get_initial_check_io_result, Pid} ->
+ Pid ! {initial_check_io_result, ChkIo}
+ end
+ end),
+ receive Go -> ok end,
+ {comment, "Testcase started! This test will run in parallel with the "
+ "erts testsuite and ends in the z_SUITE:pollset_size testcase."}.
%%
%% Internal functions...
@@ -106,5 +81,3 @@ display_check_io(ChkIo) ->
get_check_io_info() ->
z_SUITE:get_check_io_info().
-
-
diff --git a/erts/emulator/test/after_SUITE.erl b/erts/emulator/test/after_SUITE.erl
index 5017a83185..b1f7e06bf5 100644
--- a/erts/emulator/test/after_SUITE.erl
+++ b/erts/emulator/test/after_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -22,92 +22,66 @@
%% Tests receive after.
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
+-export([all/0, suite/0,
t_after/1, receive_after/1, receive_after_big/1,
receive_after_errors/1, receive_var_zero/1, receive_zero/1,
multi_timeout/1, receive_after_32bit/1,
receive_after_blast/1]).
--export([init_per_testcase/2, end_per_testcase/2]).
-
%% Internal exports.
-export([timeout_g/0]).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 4}}].
all() ->
[t_after, receive_after, receive_after_big,
receive_after_errors, receive_var_zero, receive_zero,
multi_timeout, receive_after_32bit, receive_after_blast].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- Dog=?t:timetrap(?t:minutes(3)),
- [{watchdog, Dog}|Config].
-
-end_per_testcase(_Func, Config) ->
- Dog=?config(watchdog, Config),
- ?t:timetrap_cancel(Dog).
-
%% Tests for an old round-off error in 'receive after'."
t_after(Config) when is_list(Config) ->
- ?line spawn(fun frequent_process/0),
- ?line Period = test_server:minutes(1),
- ?line Before = erlang:monotonic_time(),
+ spawn(fun frequent_process/0),
+ Period = test_server:minutes(1),
+ Before = erlang:monotonic_time(),
receive
- after Period ->
- ?line After = erlang:monotonic_time(),
- ?line report(Period, Before, After)
- end.
+ after Period ->
+ After = erlang:monotonic_time(),
+ report(Period, Before, After)
+ end.
report(Period, Before, After) ->
case erlang:convert_time_unit(After - Before, native, 100*1000) / Period of
- Percent when Percent > 100.10 ->
- test_server:fail({too_inaccurate, Percent});
- Percent when Percent < 100.0 ->
- test_server:fail({too_early, Percent});
- Percent ->
- Comment = io_lib:format("Elapsed/expected: ~.2f %", [Percent]),
- {comment, lists:flatten(Comment)}
+ Percent when Percent > 100.10 ->
+ ct:fail({too_inaccurate, Percent});
+ Percent when Percent < 100.0 ->
+ ct:fail({too_early, Percent});
+ Percent ->
+ Comment = io_lib:format("Elapsed/expected: ~.2f %", [Percent]),
+ {comment, lists:flatten(Comment)}
end.
frequent_process() ->
receive
- after 100 ->
- ?line frequent_process()
- end.
+ after 100 ->
+ frequent_process()
+ end.
-receive_after(doc) ->
- "Test that 'receive after' works (doesn't hang). "
- "The test takes 10 seconds to complete.";
+%% Test that 'receive after' works (doesn't hang).
+%% The test takes 10 seconds to complete.
receive_after(Config) when is_list(Config) ->
- ?line receive_after1(5000).
+ receive_after1(5000).
receive_after1(1) ->
- ?line io:format("Testing: receive after ~p~n", [1]),
- ?line receive after 1 -> ok end;
+ io:format("Testing: receive after ~p~n", [1]),
+ receive after 1 -> ok end;
receive_after1(N) ->
- ?line io:format("Testing: receive after ~p~n", [N]),
- ?line receive after N -> receive_after1(N div 2) end.
+ io:format("Testing: receive after ~p~n", [N]),
+ receive after N -> receive_after1(N div 2) end.
receive_after_big(Config) when is_list(Config) ->
%% Test that 'receive after' with a 32 bit number works.
@@ -119,14 +93,14 @@ receive_after_big1(Timeout) ->
erlang:yield(),
spawn(fun() -> Self ! here_is_a_message end),
ok = receive
- here_is_a_message ->
- ok
- after Timeout ->
- %% We test that the timeout can be set,
- %% not that an timeout occurs after the appropriate delay
- %% (48 days, 56 minutes, 48 seconds)!
- timeout
- end.
+ here_is_a_message ->
+ ok
+ after Timeout ->
+ %% We test that the timeout can be set,
+ %% not that an timeout occurs after the appropriate delay
+ %% (48 days, 56 minutes, 48 seconds)!
+ timeout
+ end.
receive_after_big2() ->
Self = self(),
@@ -147,38 +121,38 @@ receive_after_big2() ->
%% Test error cases for 'receive after'.
receive_after_errors(Config) when is_list(Config) ->
- ?line ?TryAfter(-1),
- ?line ?TryAfter(0.0),
- ?line ?TryAfter(3.14),
- ?line ?TryAfter(16#100000000),
- ?line ?TryAfter(392347129847294724972398472984729847129874),
- ?line ?TryAfter(16#3fffffffffffffff),
- ?line ?TryAfter(16#ffffffffffffffff),
- ?line ?TryAfter(-16#100000000),
- ?line ?TryAfter(-3891278094774921784123987129848),
- ?line ?TryAfter(xxx),
+ ?TryAfter(-1),
+ ?TryAfter(0.0),
+ ?TryAfter(3.14),
+ ?TryAfter(16#100000000),
+ ?TryAfter(392347129847294724972398472984729847129874),
+ ?TryAfter(16#3fffffffffffffff),
+ ?TryAfter(16#ffffffffffffffff),
+ ?TryAfter(-16#100000000),
+ ?TryAfter(-3891278094774921784123987129848),
+ ?TryAfter(xxx),
ok.
try_after(Timeout) ->
{'EXIT',{timeout_value,_}} = (catch receive after Timeout -> ok end).
-receive_var_zero(doc) -> "Test 'after Z', when Z == 0.";
+%% Test 'after Z', when Z == 0.
receive_var_zero(Config) when is_list(Config) ->
self() ! x,
self() ! y,
Z = zero(),
timeout = receive
- z -> ok
- after Z -> timeout
- end,
+ z -> ok
+ after Z -> timeout
+ end,
timeout = receive
- after Z -> timeout
- end,
+ after Z -> timeout
+ end,
self() ! w,
receive
x -> ok;
Other ->
- ?line ?t:fail({bad_message,Other})
+ ct:fail({bad_message,Other})
end.
zero() -> 0.
@@ -188,44 +162,43 @@ receive_zero(Config) when is_list(Config) ->
self() ! x,
self() ! y,
timeout = receive
- z -> ok
- after 0 ->
- timeout
- end,
+ z -> ok
+ after 0 ->
+ timeout
+ end,
self() ! w,
timeout = receive
after 0 -> timeout
end,
receive
- x -> ok;
- Other ->
- ?line ?t:fail({bad_message,Other})
+ x -> ok;
+ Other ->
+ ct:fail({bad_message,Other})
end.
-multi_timeout(doc) ->
- "Test for catching invalid assertion in erl_message.c (in queue_message)."
- "This failed (dumped core) with debug-compiled emulator.";
+%% Test for catching invalid assertion in erl_message.c (in queue_message)
+%% This failed (dumped core) with debug-compiled emulator.
multi_timeout(Config) when is_list(Config) ->
- ?line P = spawn(?MODULE, timeout_g, []),
- ?line P ! a,
- ?line P ! b,
- ?line receive
- after 1000 -> ok
- end,
- ?line P ! c,
- ?line receive
- after 1000 -> ok
- end,
- ?line P ! d,
+ P = spawn(?MODULE, timeout_g, []),
+ P ! a,
+ P ! b,
+ receive
+ after 1000 -> ok
+ end,
+ P ! c,
+ receive
+ after 1000 -> ok
+ end,
+ P ! d,
ok.
timeout_g() ->
- ?line receive
- a -> ok
+ receive
+ a -> ok
+ end,
+ receive
+ after 100000 -> ok
end,
- ?line receive
- after 100000 -> ok
- end,
ok.
%% OTP-7493: Timeout for 32 bit numbers (such as 16#ffffFFFF) could
@@ -250,7 +223,7 @@ recv_after_32bit(_, _) ->
blaster() ->
receive
{go, TimeoutTime} ->
- Tmo = TimeoutTime - erlang:monotonic_time(milli_seconds),
+ Tmo = TimeoutTime - erlang:monotonic_time(millisecond),
receive after Tmo -> ok end
end.
@@ -261,11 +234,11 @@ spawn_blasters(N) ->
receive_after_blast(Config) when is_list(Config) ->
PMs = spawn_blasters(10000),
- TimeoutTime = erlang:monotonic_time(milli_seconds) + 5000,
+ TimeoutTime = erlang:monotonic_time(millisecond) + 5000,
lists:foreach(fun ({P, _}) -> P ! {go, TimeoutTime} end, PMs),
lists:foreach(fun ({P, M}) ->
- receive
- {'DOWN', M, process, P, normal} ->
- ok
- end
- end, PMs).
+ receive
+ {'DOWN', M, process, P, normal} ->
+ ok
+ end
+ end, PMs).
diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl
index aa6a1fbcdc..3a721095e2 100644
--- a/erts/emulator/test/alloc_SUITE.erl
+++ b/erts/emulator/test/alloc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -19,8 +19,7 @@
-module(alloc_SUITE).
-author('[email protected]').
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2]).
+-export([all/0, suite/0, init_per_testcase/2, end_per_testcase/2]).
-export([basic/1,
coalesce/1,
@@ -34,42 +33,20 @@
cpool/1,
migration/1]).
--export([init_per_testcase/2, end_per_testcase/2]).
+-include_lib("common_test/include/ct.hrl").
--include_lib("test_server/include/test_server.hrl").
-
--define(DEFAULT_TIMETRAP_SECS, 240).
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 4}}].
all() ->
[basic, coalesce, threads, realloc_copy, bucket_index,
bucket_mask, rbtree, mseg_clear_cache, erts_mmap, cpool, migration].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-
init_per_testcase(Case, Config) when is_list(Config) ->
- Dog = ?t:timetrap(?t:seconds(?DEFAULT_TIMETRAP_SECS)),
- [{watchdog, Dog}, {testcase, Case}, {debug,false} | Config].
+ [{testcase, Case},{debug,false}|Config].
end_per_testcase(_Case, Config) when is_list(Config) ->
- Dog = ?config(watchdog, Config),
- ?t:timetrap_cancel(Dog),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -77,41 +54,15 @@ end_per_testcase(_Case, Config) when is_list(Config) ->
%% Testcases %%
%% %%
-basic(suite) -> [];
-basic(doc) -> [];
-basic(Cfg) -> ?line drv_case(Cfg).
-
-coalesce(suite) -> [];
-coalesce(doc) -> [];
-coalesce(Cfg) -> ?line drv_case(Cfg).
-
-threads(suite) -> [];
-threads(doc) -> [];
-threads(Cfg) -> ?line drv_case(Cfg).
-
-realloc_copy(suite) -> [];
-realloc_copy(doc) -> [];
-realloc_copy(Cfg) -> ?line drv_case(Cfg).
-
-bucket_index(suite) -> [];
-bucket_index(doc) -> [];
-bucket_index(Cfg) -> ?line drv_case(Cfg).
-
-bucket_mask(suite) -> [];
-bucket_mask(doc) -> [];
-bucket_mask(Cfg) -> ?line drv_case(Cfg).
-
-rbtree(suite) -> [];
-rbtree(doc) -> [];
-rbtree(Cfg) -> ?line drv_case(Cfg).
-
-mseg_clear_cache(suite) -> [];
-mseg_clear_cache(doc) -> [];
-mseg_clear_cache(Cfg) -> ?line drv_case(Cfg).
-
-cpool(suite) -> [];
-cpool(doc) -> [];
-cpool(Cfg) -> ?line drv_case(Cfg).
+basic(Cfg) -> drv_case(Cfg).
+coalesce(Cfg) -> drv_case(Cfg).
+threads(Cfg) -> drv_case(Cfg).
+realloc_copy(Cfg) -> drv_case(Cfg).
+bucket_index(Cfg) -> drv_case(Cfg).
+bucket_mask(Cfg) -> drv_case(Cfg).
+rbtree(Cfg) -> drv_case(Cfg).
+mseg_clear_cache(Cfg) -> drv_case(Cfg).
+cpool(Cfg) -> drv_case(Cfg).
migration(Cfg) ->
case erlang:system_info(smp_support) of
@@ -122,19 +73,32 @@ migration(Cfg) ->
end.
erts_mmap(Config) when is_list(Config) ->
- case {?t:os_type(), is_halfword_vm()} of
- {{unix, _}, false} ->
+ case {os:type(), mmsc_flags()} of
+ {{unix,_}, false} ->
[erts_mmap_do(Config, SCO, SCRPM, SCRFSD)
|| SCO <-[true,false], SCRFSD <-[1234,0], SCRPM <- [true,false]];
-
- {_,true} ->
- {skipped, "No supercarrier support on halfword vm"};
- {SkipOs,_} ->
- ?line {skipped,
+ {{unix,_}, Flags} ->
+ {skipped, Flags};
+ {{SkipOs,_},_} ->
+ {skipped,
lists:flatten(["Not run on "
| io_lib:format("~p",[SkipOs])])}
end.
+%% Check if there are ERL_FLAGS set that will mess up this test case
+mmsc_flags() ->
+ case mmsc_flags("ERL_FLAGS") of
+ false -> mmsc_flags("ERL_ZFLAGS");
+ Flags -> Flags
+ end.
+mmsc_flags(Env) ->
+ case os:getenv(Env) of
+ false -> false;
+ V -> case string:str(V, "+MMsc") of
+ 0 -> false;
+ P -> Env ++ "=" ++ string:substr(V, P)
+ end
+ end.
erts_mmap_do(Config, SCO, SCRPM, SCRFSD) ->
%% We use the number of schedulers + 1 * approx main carriers size
@@ -153,25 +117,26 @@ erts_mmap_do(Config, SCO, SCRPM, SCRFSD) ->
{ok, Node} = start_node(Config, Opts),
Self = self(),
Ref = make_ref(),
- F = fun () ->
- SI = erlang:system_info({allocator,mseg_alloc}),
- {erts_mmap,EM} = lists:keyfind(erts_mmap, 1, SI),
- {supercarrier,SC} = lists:keyfind(supercarrier, 1, EM),
- {sizes,Sizes} = lists:keyfind(sizes, 1, SC),
- {free_segs,Segs} = lists:keyfind(free_segs,1,SC),
- {total,Total} = lists:keyfind(total,1,Sizes),
- Total = SCS*1024*1024,
-
- {reserved,Reserved} = lists:keyfind(reserved,1,Segs),
- true = (Reserved >= SCRFSD),
-
- case {SCO,lists:keyfind(os,1,EM)} of
- {true, false} -> ok;
- {false, {os,_}} -> ok
- end,
-
- Self ! {Ref, ok}
- end,
+ F = fun() ->
+ SI = erlang:system_info({allocator,erts_mmap}),
+ {default_mmap,EM} = lists:keyfind(default_mmap, 1, SI),
+ {supercarrier,SC} = lists:keyfind(supercarrier, 1, EM),
+ {sizes,Sizes} = lists:keyfind(sizes, 1, SC),
+ {free_segs,Segs} = lists:keyfind(free_segs,1,SC),
+ {total,Total} = lists:keyfind(total,1,Sizes),
+ io:format("Expecting total ~w, got ~w~n", [SCS*1024*1024,Total]),
+ Total = SCS*1024*1024,
+
+ {reserved,Reserved} = lists:keyfind(reserved,1,Segs),
+ true = (Reserved >= SCRFSD),
+
+ case {SCO,lists:keyfind(os,1,EM)} of
+ {true, false} -> ok;
+ {false, {os,_}} -> ok
+ end,
+
+ Self ! {Ref, ok}
+ end,
spawn_link(Node, F),
Result = receive {Ref, Rslt} -> Rslt end,
@@ -188,28 +153,28 @@ drv_case(Config) ->
drv_case(Config, one_shot, "").
drv_case(Config, Mode, NodeOpts) when is_list(Config) ->
- case ?t:os_type() of
+ case os:type() of
{Family, _} when Family == unix; Family == win32 ->
- ?line {ok, Node} = start_node(Config, NodeOpts),
- ?line Self = self(),
- ?line Ref = make_ref(),
- ?line spawn_link(Node,
+ {ok, Node} = start_node(Config, NodeOpts),
+ Self = self(),
+ Ref = make_ref(),
+ spawn_link(Node,
fun () ->
Res = run_drv_case(Config, Mode),
Self ! {Ref, Res}
end),
- ?line Result = receive {Ref, Rslt} -> Rslt end,
- ?line stop_node(Node),
- ?line Result;
+ Result = receive {Ref, Rslt} -> Rslt end,
+ stop_node(Node),
+ Result;
SkipOs ->
- ?line {skipped,
+ {skipped,
lists:flatten(["Not run on "
| io_lib:format("~p",[SkipOs])])}
end.
run_drv_case(Config, Mode) ->
- DataDir = ?config(data_dir,Config),
- CaseName = ?config(testcase,Config),
+ DataDir = proplists:get_value(data_dir,Config),
+ CaseName = proplists:get_value(testcase,Config),
File = filename:join(DataDir, CaseName),
{ok,CaseName,Bin} = compile:file(File, [binary,return_errors]),
{module,CaseName} = erlang:load_module(CaseName,Bin),
@@ -269,7 +234,6 @@ print_stats(migration) ->
io:format("Number of blocks : ~p\n", [Btot]),
io:format("Number of carriers: ~p\n", [Ctot]);
-
print_stats(_) -> ok.
tuple_add(T1, T2) ->
@@ -341,7 +305,7 @@ repeat_while_loop(Fun, TRef, I) ->
flush_log() ->
receive
{print, Str} ->
- ?t:format("~s", [Str]),
+ io:format("~s", [Str]),
flush_log()
after 0 ->
ok
@@ -351,23 +315,23 @@ handle_result(_State, Result0) ->
flush_log(),
case Result0 of
{'EXIT', Error} ->
- ?line ?t:fail(Error);
+ ct:fail(Error);
{'EXIT', error, Error} ->
- ?line ?t:fail(Error);
+ ct:fail(Error);
{failed, Comment} ->
- ?line ?t:fail(Comment);
+ ct:fail(Comment);
{skipped, Comment} ->
- ?line {skipped, Comment};
+ {skipped, Comment};
{succeeded, ""} ->
- ?line succeeded;
+ succeeded;
{succeeded, Comment} ->
- ?line {comment, Comment};
+ {comment, Comment};
continue ->
continue
end.
start_node(Config, Opts) when is_list(Config), is_list(Opts) ->
- case ?config(debug,Config) of
+ case proplists:get_value(debug,Config) of
true -> {ok, node()};
_ -> start_node_1(Config, Opts)
end.
@@ -376,23 +340,16 @@ start_node_1(Config, Opts) ->
Pa = filename:dirname(code:which(?MODULE)),
Name = list_to_atom(atom_to_list(?MODULE)
++ "-"
- ++ atom_to_list(?config(testcase, Config))
+ ++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))),
- ?t:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]).
+ test_server:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]).
stop_node(Node) when Node =:= node() -> ok;
stop_node(Node) ->
- ?t:stop_node(Node).
-
-is_halfword_vm() ->
- case {erlang:system_info({wordsize, internal}),
- erlang:system_info({wordsize, external})} of
- {4, 8} -> true;
- {WS, WS} -> false
- end.
+ test_server:stop_node(Node).
free_memory() ->
%% Free memory in MB.
@@ -411,5 +368,6 @@ free_memory() ->
TotFree div (1024*1024)
catch
error : undef ->
- ?t:fail({"os_mon not built"})
+ ct:fail({"os_mon not built"})
end.
+
diff --git a/erts/emulator/test/alloc_SUITE_data/cpool.c b/erts/emulator/test/alloc_SUITE_data/cpool.c
index 73026cc758..0c41f4d747 100644
--- a/erts/emulator/test/alloc_SUITE_data/cpool.c
+++ b/erts/emulator/test/alloc_SUITE_data/cpool.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2013. All Rights Reserved.
+ * Copyright Ericsson AB 2013-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/alloc_SUITE_data/migration.c b/erts/emulator/test/alloc_SUITE_data/migration.c
index b006360043..b9a4de03b3 100644
--- a/erts/emulator/test/alloc_SUITE_data/migration.c
+++ b/erts/emulator/test/alloc_SUITE_data/migration.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2014. All Rights Reserved.
+ * Copyright Ericsson AB 2014-2016. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/emulator/test/alloc_SUITE_data/threads.c b/erts/emulator/test/alloc_SUITE_data/threads.c
index 2f5f841e3d..44d982b6c7 100644
--- a/erts/emulator/test/alloc_SUITE_data/threads.c
+++ b/erts/emulator/test/alloc_SUITE_data/threads.c
@@ -96,16 +96,11 @@ static void fail(int t_no, char *frmt, ...)
exit_thread(t_no, 0);
}
-static Allctr_t *alloc_not_ts = NULL;
static Allctr_t *alloc_ts_1 = NULL;
static Allctr_t *alloc_ts_2 = NULL;
static void stop_allocators(void)
{
- if (alloc_not_ts) {
- STOP_ALC(alloc_not_ts);
- alloc_not_ts = NULL;
- }
if (alloc_ts_1) {
STOP_ALC(alloc_ts_1);
alloc_ts_1 = NULL;
@@ -155,7 +150,6 @@ testcase_run(TestCaseState_t *tcs)
if (!IS_THREADS_ENABLED)
testcase_skipped(tcs, "Threads not enabled");
- alloc_not_ts = NULL;
alloc_ts_1 = NULL;
alloc_ts_2 = NULL;
@@ -164,16 +158,12 @@ testcase_run(TestCaseState_t *tcs)
sprintf(sbct_buf, "%d", SBC_THRESHOLD/1024);
memcpy((void *) argv, argv_org, sizeof(argv_org));
- alloc_not_ts = START_ALC("threads_not_ts", 0, argv);
- ASSERT(tcs, alloc_not_ts);
- memcpy((void *) argv, argv_org, sizeof(argv_org));
alloc_ts_1 = START_ALC("threads_ts_1", 1, argv);
ASSERT(tcs, alloc_ts_1);
memcpy((void *) argv, argv_org, sizeof(argv_org));
alloc_ts_2 = START_ALC("threads_ts_2", 1, argv);
ASSERT(tcs, alloc_ts_2);
- ASSERT(tcs, !IS_ALLOC_THREAD_SAFE(alloc_not_ts));
ASSERT(tcs, IS_ALLOC_THREAD_SAFE(alloc_ts_1));
ASSERT(tcs, IS_ALLOC_THREAD_SAFE(alloc_ts_2));
@@ -190,12 +180,7 @@ testcase_run(TestCaseState_t *tcs)
threads[i].arg.no_ops_per_bl = NO_OF_OPS_PER_BL;
- if (i == 1) {
- alc = "threads_not_ts";
- threads[i].arg.no_ops_per_bl *= 2;
- threads[i].arg.a = alloc_not_ts;
- }
- else if (i % 2 == 0) {
+ if (i % 2 == 0) {
alc = "threads_ts_1";
threads[i].arg.a = alloc_ts_1;
}
diff --git a/erts/emulator/test/async_ports_SUITE.erl b/erts/emulator/test/async_ports_SUITE.erl
index c89b3655ff..f0f5fb5687 100644
--- a/erts/emulator/test/async_ports_SUITE.erl
+++ b/erts/emulator/test/async_ports_SUITE.erl
@@ -1,8 +1,10 @@
-module(async_ports_SUITE).
--include_lib("common_test/include/ct.hrl").
+-export([all/0, suite/0]).
+-export([permanent_busy_test/1]).
+-export([run_loop/5]).
--compile(export_all).
+-include_lib("common_test/include/ct.hrl").
-define(PACKET_SIZE, (10 * 1024 * 8)).
-define(CPORT_DELAY, 100).
@@ -11,17 +13,15 @@
-define(TEST_PROCS_COUNT, 2).
-define(TC_TIMETRAP_SECONDS, 10).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {seconds, ?TC_TIMETRAP_SECONDS}}].
all() ->
- [
- permanent_busy_test
- ].
+ [permanent_busy_test].
permanent_busy_test(Config) ->
- ct:timetrap({seconds, ?TC_TIMETRAP_SECONDS}),
- ExePath = filename:join(?config(data_dir, Config), "cport"),
-
+ ExePath = filename:join(proplists:get_value(data_dir, Config), "cport"),
Self = self(),
spawn_link(
fun() ->
@@ -29,17 +29,16 @@ permanent_busy_test(Config) ->
Port = open_port(ExePath),
- Testers =
- lists:map(
- fun(_) ->
- erlang:spawn_link(?MODULE, run_loop,
- [Self,
- Port,
- Block,
- ?TEST_LOOPS_COUNT,
- 0])
- end,
- lists:seq(1, ?TEST_PROCS_COUNT)),
+ Testers = lists:map(
+ fun(_) ->
+ spawn_link(?MODULE, run_loop,
+ [Self,
+ Port,
+ Block,
+ ?TEST_LOOPS_COUNT,
+ 0])
+ end,
+ lists:seq(1, ?TEST_PROCS_COUNT)),
Self ! {test_info, Port, Testers},
endless_flush(Port)
end),
diff --git a/erts/emulator/test/beam_SUITE.erl b/erts/emulator/test/beam_SUITE.erl
index 706a4a1c16..6a54fa87e0 100644
--- a/erts/emulator/test/beam_SUITE.erl
+++ b/erts/emulator/test/beam_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -24,17 +24,19 @@
init_per_group/2,end_per_group/2,
packed_registers/1, apply_last/1, apply_last_bif/1,
buildo_mucho/1, heap_sizes/1, big_lists/1, fconv/1,
- select_val/1]).
+ select_val/1, swap_temp_apply/1]).
--export([applied/2]).
+-export([applied/2,swap_temp_applied/1]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
+-include_lib("syntax_tools/include/merl.hrl").
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[packed_registers, apply_last, apply_last_bif,
- buildo_mucho, heap_sizes, big_lists, select_val].
+ buildo_mucho, heap_sizes, big_lists, select_val,
+ swap_temp_apply].
groups() ->
[].
@@ -61,15 +63,15 @@ apply_last(Config) when is_list(Config) ->
{Pid, finished} ->
stack_size(Pid)
after 30000 ->
- ?t:fail("applied/2 timed out.")
+ ct:fail("applied/2 timed out.")
end,
Pid ! die,
- ?t:format("Size: ~p~n", [Size]),
+ io:format("Size: ~p~n", [Size]),
if
Size < 700 ->
ok;
true ->
- ?t:fail("10000 apply() grew stack too much.")
+ ct:fail("10000 apply() grew stack too much.")
end,
ok.
@@ -92,49 +94,46 @@ applied(Starter, N) ->
apply_last_bif(Config) when is_list(Config) ->
apply(erlang, abs, [1]).
-%% Test three high register numbers in a put_list instruction
-%% (to test whether packing works properly).
+%% Test whether packing works properly.
packed_registers(Config) when is_list(Config) ->
- PrivDir = ?config(priv_dir, Config),
- Mod = packed_regs,
- Name = filename:join(PrivDir, atom_to_list(Mod) ++ ".erl"),
-
- %% Generate a module which generates a list of tuples.
- %% put_list(A) -> [{A, 600}, {A, 999}, ... {A, 0}].
- Code = gen_packed_regs(600, ["-module("++atom_to_list(Mod)++").\n",
- "-export([put_list/1]).\n",
- "put_list(A) ->\n["]),
- ok = file:write_file(Name, Code),
-
- %% Compile the module.
- io:format("Compiling: ~s\n", [Name]),
- CompRc = compile:file(Name, [{outdir, PrivDir}, report]),
- io:format("Result: ~p\n",[CompRc]),
- {ok, Mod} = CompRc,
-
- %% Load it.
- io:format("Loading...\n",[]),
- LoadRc = code:load_abs(filename:join(PrivDir, atom_to_list(Mod))),
- {module,_Module} = LoadRc,
-
- %% Call it and verify result.
- Term = {a, b},
- L = Mod:put_list(Term),
- verify_packed_regs(L, Term, 600),
+ Mod = ?FUNCTION_NAME,
+
+ %% Generate scrambled sequence.
+ Seq0 = [{erlang:phash2(I),I} || I <- lists:seq(0, 260)],
+ Seq = [I || {_,I} <- lists:sort(Seq0)],
+
+ %% Generate a test modules that uses get_list/3 instructions
+ %% with high register numbers.
+ S0 = [begin
+ VarName = list_to_atom("V"++integer_to_list(V)),
+ {merl:var(VarName),V}
+ end || V <- Seq],
+ Vars = [V || {V,_} <- S0],
+ NewVars = [begin
+ VarName = list_to_atom("M"++integer_to_list(V)),
+ merl:var(VarName)
+ end || V <- Seq],
+ S = [?Q("_@Var = id(_@Value@)") || {Var,Value} <- S0],
+ Code = ?Q(["-module('@Mod@').\n"
+ "-export([f/0]).\n"
+ "f() ->\n"
+ "_@S,\n"
+ "_ = id(0),\n"
+ "L = [_@Vars],\n"
+ "_ = id(1),\n"
+ "[_@NewVars] = L,\n" %Test get_list/3.
+ "_ = id(2),\n"
+ "id([_@Vars,_@NewVars]).\n"
+ "id(I) -> I.\n"]),
+ merl:compile_and_load(Code),
+ CombinedSeq = Seq ++ Seq,
+ CombinedSeq = Mod:f(),
+
+ %% Clean up.
+ true = code:delete(Mod),
+ false = code:purge(Mod),
ok.
-gen_packed_regs(0, Acc) ->
- [Acc|"{A,0}].\n"];
-gen_packed_regs(N, Acc) ->
- gen_packed_regs(N-1, [Acc,"{A,",integer_to_list(N)|"},\n"]).
-
-verify_packed_regs([], _, -1) -> ok;
-verify_packed_regs([{Term, N}| T], Term, N) ->
- verify_packed_regs(T, Term, N-1);
-verify_packed_regs(L, Term, N) ->
- ok = io:format("Expected [{~p, ~p}|T]; got\n~p\n", [Term, N, L]),
- test_server:fail().
-
buildo_mucho(Config) when is_list(Config) ->
buildo_mucho_1(),
ok.
@@ -319,7 +318,7 @@ fconv(Config) when is_list(Config) ->
do_fconv(Type) ->
try
do_fconv(Type, 1.0),
- test_server:fail()
+ ct:fail(no_badarith)
catch
error:badarith ->
ok
@@ -347,3 +346,41 @@ do_select_val(X) ->
Int when is_integer(Int) ->
integer
end.
+
+swap_temp_apply(_Config) ->
+ {swap_temp_applied,42} = do_swap_temp_apply(41),
+ not_an_integer = do_swap_temp_apply(not_an_integer),
+ ok.
+
+do_swap_temp_apply(Msg) ->
+ case swap_temp_apply_function(Msg) of
+ undefined -> Msg;
+ Type ->
+ %% The following sequence:
+ %% move {x,0} {x,2}
+ %% move {y,0} {x,0}
+ %% move {x,2} {y,0}
+ %% apply 1
+ %%
+ %% Would be incorrectly transformed to:
+ %% swap {x,0} {y,0}
+ %% apply 1
+ %%
+ %% ({x,1} is the module, {x,2} the function to be applied).
+ %%
+ %% If the instructions are to be transformed, the correct
+ %% transformation is:
+ %%
+ %% swap_temp {x,0} {y,0} {x,2}
+ %% apply 1
+ Fields = ?MODULE:Type(Msg),
+ {Type,Fields}
+ end.
+
+swap_temp_apply_function(Int) when is_integer(Int) ->
+ swap_temp_applied;
+swap_temp_apply_function(_) ->
+ undefined.
+
+swap_temp_applied(Int) ->
+ Int+1.
diff --git a/erts/emulator/test/beam_literals_SUITE.erl b/erts/emulator/test/beam_literals_SUITE.erl
index 9f14ca26e5..09761263e2 100644
--- a/erts/emulator/test/beam_literals_SUITE.erl
+++ b/erts/emulator/test/beam_literals_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2015. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -28,7 +28,7 @@
put_list/1, fconv/1, literal_case_expression/1,
increment/1]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -55,7 +55,7 @@ end_per_group(_GroupName, Config) ->
Config.
-putting(doc) -> "Test creating lists and tuples containing big number literals.";
+%% Test creating lists and tuples containing big number literals.
putting(Config) when is_list(Config) ->
-773973888575883407313908 = chksum(putting1(8987697898797)).
@@ -64,23 +64,22 @@ putting1(X) ->
[X|349873987387373],
[329878349873|-387394729872], -773973937933873929749873}.
-matching_bigs(doc) -> "Test matching of a few big number literals (in Beam,"
- "select_val/3 will NOT be used).";
+%% Test matching of a few big number literals (in Beam select_val/3 will NOT be used).
matching_bigs(Config) when is_list(Config) ->
a = matching1(3972907842873739),
b = matching1(-389789298378939783333333333333333333784),
other = matching1(3141699999999999999999999999999999999),
other = matching1(42).
-matching_smalls(doc) -> "Test matching small numbers (both positive and negative).";
+%% Test matching small numbers (both positive and negative).
matching_smalls(Config) when is_list(Config) ->
- ?line a = m_small(-42),
- ?line b = m_small(0),
- ?line c = m_small(105),
- ?line d = m_small(-13),
- ?line e = m_small(337848),
- ?line other = m_small(324),
- ?line other = m_small(-7),
+ a = m_small(-42),
+ b = m_small(0),
+ c = m_small(105),
+ d = m_small(-13),
+ e = m_small(337848),
+ other = m_small(324),
+ other = m_small(-7),
ok.
m_small(-42) -> a;
@@ -90,17 +89,16 @@ m_small(-13) -> d;
m_small(337848) -> e;
m_small(_) -> other.
-matching_smalls_jt(doc) ->
- "Test matching small numbers (both positive and negative). "
- "Make sure that a jump table is used.";
+%% Test matching small numbers (both positive and negative).
+%% Make sure that a jump table is used.
matching_smalls_jt(Config) when is_list(Config) ->
- ?line a = m_small_jt(-2),
- ?line b = m_small_jt(-1),
- ?line c = m_small_jt(0),
- ?line d = m_small_jt(2),
- ?line e = m_small_jt(3),
- ?line other = m_small(324),
- ?line other = m_small(-7),
+ a = m_small_jt(-2),
+ b = m_small_jt(-1),
+ c = m_small_jt(0),
+ d = m_small_jt(2),
+ e = m_small_jt(3),
+ other = m_small(324),
+ other = m_small(-7),
ok.
m_small_jt(-2) -> a;
@@ -117,8 +115,7 @@ matching1(-389789298378939783333333333333333333784) -> b;
matching1(_) -> other.
-matching_more_bigs(doc) -> "Test matching of a big number literals (in Beam,"
- "a select_val/3 instruction will be used).";
+%% Test matching of a big number literals (in Beam, a select_val/3 instruction will be used)
matching_more_bigs(Config) when is_list(Config) ->
a = matching2(-999766349740978337),
b = matching2(9734097866575478),
@@ -137,8 +134,7 @@ matching2(13987294872948990) -> d;
matching2(777723896192459245) -> e;
matching2(_) -> other.
-matching_bigs_and_smalls(doc) -> "Test matching of a mix of big numbers and literals.";
-matching_bigs_and_smalls(suite) -> [];
+%% Test matching of a mix of big numbers and literals.
matching_bigs_and_smalls(Config) when is_list(Config) ->
a = matching3(38472928723987239873873),
b = matching3(0),
@@ -159,30 +155,30 @@ matching3(42) -> e;
matching3(-4533) -> f;
matching3(_) -> other.
-badmatch(doc) -> "Test literal badmatches with big number and floats.";
+%% Test literal badmatches with big number and floats.
badmatch(Config) when is_list(Config) ->
%% We are satisfied if we can load this module and run it.
Big = id(32984798729847892498297824872982972978239874),
Float = id(3.1415927),
- ?line catch a = Big,
- ?line catch b = Float,
- ?line {'EXIT',{{badmatch,3879373498378993387},_}} =
+ catch a = Big,
+ catch b = Float,
+ {'EXIT',{{badmatch,3879373498378993387},_}} =
(catch c = 3879373498378993387),
- ?line {'EXIT',{{badmatch,7.0},_}} = (catch d = 7.0),
- ?line case Big of
- Big -> ok
- end,
- ?line case Float of
- Float -> ok
- end,
+ {'EXIT',{{badmatch,7.0},_}} = (catch d = 7.0),
+ case Big of
+ Big -> ok
+ end,
+ case Float of
+ Float -> ok
+ end,
ok.
case_clause(Config) when is_list(Config) ->
- ?line {'EXIT',{{case_clause,337.0},_}} = (catch case_clause_float()),
- ?line {'EXIT',{{try_clause,42.0},_}} = (catch try_case_clause_float()),
- ?line {'EXIT',{{case_clause,37932749837839747383847398743789348734987},_}} =
+ {'EXIT',{{case_clause,337.0},_}} = (catch case_clause_float()),
+ {'EXIT',{{try_clause,42.0},_}} = (catch try_case_clause_float()),
+ {'EXIT',{{case_clause,37932749837839747383847398743789348734987},_}} =
(catch case_clause_big()),
- ?line {'EXIT',{{try_clause,977387349872349870423364354398566348},_}} =
+ {'EXIT',{{try_clause,977387349872349870423364354398566348},_}} =
(catch try_case_clause_big()),
ok.
@@ -210,8 +206,7 @@ try_case_clause_big() ->
error
end.
-receiving(doc) -> "Test receive with a big number literal (more than 27 bits, "
- "less than 32 bits).";
+%% Test receive with a big number literal (more than 27 bits, less than 32 bits).
receiving(Config) when is_list(Config) ->
Self = self(),
spawn(fun() -> Self ! here_is_a_message end),
@@ -222,11 +217,11 @@ receiving(Config) when is_list(Config) ->
timeout
end.
-literal_type_tests(doc) -> "Test type tests on literal values.";
+%% Test type tests on literal values.
literal_type_tests(Config) when is_list(Config) ->
%% Generate an Erlang module with all different type of type tests.
- ?line Tests = make_test([{T, L} || T <- type_tests(), L <- literals()]),
- ?line Mod = literal_test,
+ Tests = make_test([{T, L} || T <- type_tests(), L <- literals()]),
+ Mod = literal_test,
Anno = erl_anno:new(0),
Func = {function, Anno, test, 0, [{clause,Anno,[],[],Tests}]},
Form = [{attribute,Anno,module,Mod},
@@ -234,22 +229,22 @@ literal_type_tests(Config) when is_list(Config) ->
Func, {eof,Anno}],
%% Print generated code for inspection.
- ?line lists:foreach(fun (F) -> io:put_chars([erl_pp:form(F),"\n"]) end, Form),
+ lists:foreach(fun (F) -> io:put_chars([erl_pp:form(F),"\n"]) end, Form),
%% Test compile:form/1. This implies full optimization (default).
- ?line {ok,Mod,Code1} = compile:forms(Form),
- ?line {module,Mod} = code:load_binary(Mod, Mod, Code1),
- ?line Mod:test(),
- ?line true = code:delete(Mod),
- ?line code:purge(Mod),
+ {ok,Mod,Code1} = compile:forms(Form),
+ {module,Mod} = code:load_binary(Mod, Mod, Code1),
+ Mod:test(),
+ true = code:delete(Mod),
+ code:purge(Mod),
%% Test compile:form/2. Turn off all optimizations.
- ?line {ok,Mod,Code2} = compile:forms(Form, [binary,report,time,
+ {ok,Mod,Code2} = compile:forms(Form, [binary,report,time,
no_copt,no_postopt]),
- ?line {module,Mod} = code:load_binary(Mod, Mod, Code2),
- ?line Mod:test(),
- ?line true = code:delete(Mod),
- ?line code:purge(Mod),
+ {module,Mod} = code:load_binary(Mod, Mod, Code2),
+ Mod:test(),
+ true = code:delete(Mod),
+ code:purge(Mod),
ok.
make_test([{is_function=T,L}|Ts]) ->
@@ -299,34 +294,34 @@ type_tests() ->
put_list(Config) when is_list(Config) ->
%% put_list x0 Literal Reg
- ?line [Config|8739757395764] = put_list_rqr(Config),
- ?line {[Config|7779757395764],Config} = put_list_rqx(Config),
- ?line [Config|98765432100000] = put_list_rqy(Config),
+ [Config|8739757395764] = put_list_rqr(Config),
+ {[Config|7779757395764],Config} = put_list_rqx(Config),
+ [Config|98765432100000] = put_list_rqy(Config),
%% put_list x Literal Reg
- ?line [Config|16#FFFFF77777137483769] = put_list_xqr(ignore, Config),
- ?line {[Config|16#AAAAAFFFFF77777],{a,b},Config} = put_list_xqx({a,b}, Config),
- ?line [Config|12777765432979879] = put_list_xqy(ignore, Config),
+ [Config|16#FFFFF77777137483769] = put_list_xqr(ignore, Config),
+ {[Config|16#AAAAAFFFFF77777],{a,b},Config} = put_list_xqx({a,b}, Config),
+ [Config|12777765432979879] = put_list_xqy(ignore, Config),
%% put_list y Literal Reg
- ?line [Config|17424134793676869867] = put_list_yqr(Config),
- ?line {[Config|77424134793676869867],Config} = put_list_yqx(Config),
- ?line {Config,[Config|16#BCDEFF4241676869867]} = put_list_yqy(Config),
+ [Config|17424134793676869867] = put_list_yqr(Config),
+ {[Config|77424134793676869867],Config} = put_list_yqx(Config),
+ {Config,[Config|16#BCDEFF4241676869867]} = put_list_yqy(Config),
%% put_list Literal x0 Reg
- ?line [42.0|Config] = put_list_qrr(Config),
- ?line [Config,42.0|Config] = put_list_qrx(Config),
- ?line [100.0|Config] = put_list_qry(Config),
+ [42.0|Config] = put_list_qrr(Config),
+ [Config,42.0|Config] = put_list_qrx(Config),
+ [100.0|Config] = put_list_qry(Config),
%% put_list Literal x1 Reg
- ?line [127.0|Config] = put_list_qxr({ignore,me}, Config),
- ?line [Config,130.0|Config] = put_list_qxx(ignore, Config),
- ?line [99.0|Config] = put_list_qxy(Config),
+ [127.0|Config] = put_list_qxr({ignore,me}, Config),
+ [Config,130.0|Config] = put_list_qxx(ignore, Config),
+ [99.0|Config] = put_list_qxy(Config),
%% put_list Literal y0 Reg
- ?line [200.0|Config] = put_list_qyr(Config),
- ?line [Config,210.0|Config] = put_list_qyx(Config),
- ?line [[300.0|Config]|Config] = put_list_qyy(Config),
+ [200.0|Config] = put_list_qyr(Config),
+ [Config,210.0|Config] = put_list_qyx(Config),
+ [[300.0|Config]|Config] = put_list_qyy(Config),
ok.
@@ -417,8 +412,8 @@ put_list_qyy(Config) ->
[Res|Config].
fconv(Config) when is_list(Config) ->
- ?line 5.0 = fconv_1(-34444444450.0),
- ?line 13.0 = fconv_2(7.0),
+ 5.0 = fconv_1(-34444444450.0),
+ 13.0 = fconv_2(7.0),
ok.
fconv_1(F) when is_float(F) ->
@@ -428,19 +423,18 @@ fconv_2(F) when is_float(F) ->
6.0 + F.
literal_case_expression(Config) when is_list(Config) ->
- ?line DataDir = ?config(data_dir, Config),
- ?line Src = filename:join(DataDir, "literal_case_expression"),
- ?line {ok,literal_case_expression=Mod,Code} =
- compile:file(Src, [from_asm,binary]),
- ?line {module,Mod} = code:load_binary(Mod, Src, Code),
- ?line ok = Mod:x(),
- ?line ok = Mod:y(),
- ?line ok = Mod:zi1(),
- ?line ok = Mod:zi2(),
- ?line ok = Mod:za1(),
- ?line ok = Mod:za2(),
- ?line true = code:delete(Mod),
- ?line code:purge(Mod),
+ DataDir = proplists:get_value(data_dir, Config),
+ Src = filename:join(DataDir, "literal_case_expression"),
+ {ok,literal_case_expression=Mod,Code} = compile:file(Src, [from_asm,binary]),
+ {module,Mod} = code:load_binary(Mod, Src, Code),
+ ok = Mod:x(),
+ ok = Mod:y(),
+ ok = Mod:zi1(),
+ ok = Mod:zi2(),
+ ok = Mod:za1(),
+ ok = Mod:za2(),
+ true = code:delete(Mod),
+ code:purge(Mod),
ok.
%% Test the i_increment instruction.
@@ -452,27 +446,27 @@ increment(Config) when is_list(Config) ->
Neg32 = -(1 bsl 27),
Big32 = id(1 bsl 32),
Result32 = (1 bsl 32) + (1 bsl 27),
- ?line Result32 = Big32 + (1 bsl 27),
- ?line Result32 = Big32 - Neg32,
+ Result32 = Big32 + (1 bsl 27),
+ Result32 = Big32 - Neg32,
%% Same thing, but for the 64-bit emulator.
Neg64 = -(1 bsl 59),
Big64 = id(1 bsl 64),
Result64 = (1 bsl 64) + (1 bsl 59),
- ?line Result64 = Big64 + (1 bsl 59),
- ?line Result64 = Big64 - Neg64,
+ Result64 = Big64 + (1 bsl 59),
+ Result64 = Big64 - Neg64,
%% Test error handling for the i_increment instruction.
Bad = id(bad),
- ?line {'EXIT',{badarith,_}} = (catch Bad + 42),
+ {'EXIT',{badarith,_}} = (catch Bad + 42),
%% Small operands, but a big result.
Res32 = 1 bsl 27,
Small32 = id(Res32-1),
- ?line Res32 = Small32 + 1,
+ Res32 = Small32 + 1,
Res64 = 1 bsl 59,
Small64 = id(Res64-1),
- ?line Res64 = Small64 + 1,
+ Res64 = Small64 + 1,
ok.
%% Help functions.
diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl
index d6a771e7b9..f70fb0e501 100644
--- a/erts/emulator/test/bif_SUITE.erl
+++ b/erts/emulator/test/bif_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,21 +20,24 @@
-module(bif_SUITE).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include_lib("kernel/include/file.hrl").
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2,end_per_testcase/2,
+-export([all/0, suite/0,
display/1, display_huge/0,
erl_bif_types/1,guard_bifs_in_erl_bif_types/1,
shadow_comments/1,
specs/1,improper_bif_stubs/1,auto_imports/1,
t_list_to_existing_atom/1,os_env/1,otp_7526/1,
binary_to_atom/1,binary_to_existing_atom/1,
- atom_to_binary/1,min_max/1, erlang_halt/1]).
+ atom_to_binary/1,min_max/1, erlang_halt/1,
+ erl_crash_dump_bytes/1,
+ is_builtin/1, error_stacktrace/1,
+ error_stacktrace_during_call_trace/1]).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 1}}].
all() ->
[erl_bif_types, guard_bifs_in_erl_bif_types, shadow_comments,
@@ -42,37 +45,10 @@ all() ->
t_list_to_existing_atom, os_env, otp_7526,
display,
atom_to_binary, binary_to_atom, binary_to_existing_atom,
- min_max, erlang_halt].
+ erl_crash_dump_bytes, min_max, erlang_halt, is_builtin,
+ error_stacktrace, error_stacktrace_during_call_trace].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- Dog=?t:timetrap(?t:minutes(1)),
- [{watchdog, Dog}|Config].
-
-end_per_testcase(_Func, Config) ->
- Dog=?config(watchdog, Config),
- ?t:timetrap_cancel(Dog).
-
-
-display(suite) ->
- [];
-display(doc) ->
- ["Uses erlang:display to test that erts_printf does not do deep recursion"];
+%% Uses erlang:display to test that erts_printf does not do deep recursion
display(Config) when is_list(Config) ->
Pa = filename:dirname(code:which(?MODULE)),
{ok, Node} = test_server:start_node(display_huge_term,peer,
@@ -117,7 +93,7 @@ erl_bif_types_2(List) ->
[_|_] ->
io:put_chars("Bifs with bad arity\n"),
io:format("~p\n", [BadArity]),
- ?line ?t:fail({length(BadArity),bad_arity})
+ ct:fail({length(BadArity),bad_arity})
end.
erl_bif_types_3(List) ->
@@ -141,7 +117,7 @@ erl_bif_types_3(List) ->
io:put_chars("Bifs with failing calls to erlang_bif_types:type/3 "
"(or with bogus return values):\n"),
io:format("~p\n", [BadSmokeTest]),
- ?line ?t:fail({length(BadSmokeTest),bad_smoke_test})
+ ct:fail({length(BadSmokeTest),bad_smoke_test})
end.
guard_bifs_in_erl_bif_types(_Config) ->
@@ -162,15 +138,17 @@ guard_bifs_in_erl_bif_types(_Config) ->
"The following guard BIFs have no type information "
"in erl_bif_types:\n\n",
[io_lib:format(" ~p/~p\n", [F,A]) || {F,A} <- Not]]),
- ?t:fail()
+ ct:fail(erl_bif_types)
end.
shadow_comments(_Config) ->
ensure_erl_bif_types_compiled(),
+ ErlangList = [{erlang,F,A} || {F,A} <- erlang:module_info(exports),
+ not is_operator(F,A)],
List0 = erlang:system_info(snifs),
- List1 = [MFA || {M,_,_}=MFA <- List0, M =/= hipe_bifs],
- List = [MFA || MFA <- List1, not is_operator(MFA)],
+ List1 = [MFA || {M,_,_}=MFA <- List0, M =/= hipe_bifs, M =/= erlang],
+ List = List1 ++ ErlangList,
HasTypes = [MFA || {M,F,A}=MFA <- List,
erl_bif_types:is_known(M, F, A)],
Path = get_code_path(),
@@ -202,7 +180,7 @@ shadow_comments(_Config) ->
"obvious.\n\nThe following comments are missing:\n\n",
[io_lib:format("%% Shadowed by erl_bif_types: ~p:~p/~p\n",
[M,F,A]) || {M,F,A} <- NoComments]]),
- ?t:fail()
+ ct:fail(bif_stub)
end,
case NoBifSpecs of
@@ -216,7 +194,7 @@ shadow_comments(_Config) ->
"Therefore, the following comments should be removed:\n\n",
[io_lib:format("%% Shadowed by erl_bif_types: ~p:~p/~p\n",
[M,F,A]) || {M,F,A} <- NoBifSpecs]]),
- ?t:fail()
+ ct:fail(erl_bif_types)
end.
extract_comments(Mod, Path) ->
@@ -238,7 +216,7 @@ ensure_erl_bif_types_compiled() ->
case erlang:function_exported(erl_bif_types, module_info, 0) of
false ->
%% Fail cleanly.
- ?t:fail("erl_bif_types not compiled");
+ ct:fail("erl_bif_types not compiled");
true ->
ok
end.
@@ -276,16 +254,19 @@ specs(_) ->
[_|_] ->
io:put_chars("The following BIFs don't have specs:\n"),
[print_mfa(MFA) || MFA <- NoSpecs],
- ?t:fail()
+ ct:fail(no_spec)
end.
is_operator({erlang,F,A}) ->
+ is_operator(F,A);
+is_operator(_) -> false.
+
+is_operator(F,A) ->
erl_internal:arith_op(F, A) orelse
erl_internal:bool_op(F, A) orelse
erl_internal:comp_op(F, A) orelse
erl_internal:list_op(F, A) orelse
- erl_internal:send_op(F, A);
-is_operator(_) -> false.
+ erl_internal:send_op(F, A).
extract_specs(M, Abstr) ->
[{make_mfa(M, Name),Spec} || {attribute,_,spec,{Name,Spec}} <- Abstr].
@@ -336,7 +317,7 @@ auto_imports([{erlang,F,A}|T], Errors) ->
auto_imports([], 0) ->
ok;
auto_imports([], Errors) ->
- ?t:fail({Errors,inconsistencies}).
+ ct:fail({Errors,inconsistencies}).
extract_functions(M, Abstr) ->
[{{M,F,A},Body} || {function,_,F,A,Body} <- Abstr].
@@ -355,40 +336,36 @@ check_stub({_,F,A}, B) ->
io:put_chars(erl_pp:function(Func)),
io:nl(),
io:put_chars("The body should be: erlang:nif_error(undef)"),
- ?t:fail()
+ ct:fail(invalid_body)
end.
t_list_to_existing_atom(Config) when is_list(Config) ->
- ?line all = list_to_existing_atom("all"),
- ?line ?MODULE = list_to_existing_atom(?MODULE_STRING),
- ?line UnlikelyStr = "dsfj923874390867er869fds9864y97jhg3973qerueoru",
+ all = list_to_existing_atom("all"),
+ ?MODULE = list_to_existing_atom(?MODULE_STRING),
+ UnlikelyStr = "dsfj923874390867er869fds9864y97jhg3973qerueoru",
try
- ?line list_to_existing_atom(UnlikelyStr),
- ?line ?t:fail()
+ list_to_existing_atom(UnlikelyStr),
+ ct:fail(atom_exists)
catch
error:badarg -> ok
end,
%% The compiler has become smarter! We need the call to id/1 in
%% the next line.
- ?line UnlikelyAtom = list_to_atom(id(UnlikelyStr)),
- ?line UnlikelyAtom = list_to_existing_atom(UnlikelyStr),
+ UnlikelyAtom = list_to_atom(id(UnlikelyStr)),
+ UnlikelyAtom = list_to_existing_atom(UnlikelyStr),
ok.
-os_env(doc) ->
- [];
-os_env(suite) ->
- [];
os_env(Config) when is_list(Config) ->
- ?line EnvVar1 = "MjhgvFDrresdCghN mnjkUYg vfrD",
- ?line false = os:getenv(EnvVar1),
- ?line true = os:putenv(EnvVar1, "mors"),
- ?line "mors" = os:getenv(EnvVar1),
- ?line true = os:putenv(EnvVar1, ""),
- ?line case os:getenv(EnvVar1) of
- "" -> ?line ok;
- false -> ?line ok;
- BadVal -> ?line ?t:fail(BadVal)
+ EnvVar1 = "MjhgvFDrresdCghN mnjkUYg vfrD",
+ false = os:getenv(EnvVar1),
+ true = os:putenv(EnvVar1, "mors"),
+ "mors" = os:getenv(EnvVar1),
+ true = os:putenv(EnvVar1, ""),
+ case os:getenv(EnvVar1) of
+ "" -> ok;
+ false -> ok;
+ BadVal -> ct:fail(BadVal)
end,
true = os:putenv(EnvVar1, "mors"),
true = os:unsetenv(EnvVar1),
@@ -396,19 +373,18 @@ os_env(Config) when is_list(Config) ->
true = os:unsetenv(EnvVar1), % unset unset variable
%% os:putenv, os:getenv and os:unsetenv currently use a temp
%% buffer of size 1024 for storing key+value
- ?line os_env_long(1010, 1030, "hej hopp").
+ os_env_long(1010, 1030, "hej hopp").
os_env_long(Min, Max, _Value) when Min > Max ->
- ?line ok;
+ ok;
os_env_long(Min, Max, Value) ->
- ?line EnvVar = lists:duplicate(Min, $X),
- ?line true = os:putenv(EnvVar, Value),
- ?line Value = os:getenv(EnvVar),
+ EnvVar = lists:duplicate(Min, $X),
+ true = os:putenv(EnvVar, Value),
+ Value = os:getenv(EnvVar),
true = os:unsetenv(EnvVar),
- ?line os_env_long(Min+1, Max, Value).
+ os_env_long(Min+1, Max, Value).
-otp_7526(doc) ->
- ["Test that string:to_integer does not Halloc in wrong order."];
+%% Test that string:to_integer does not Halloc in wrong order.
otp_7526(Config) when is_list(Config) ->
ok = test_7526(256).
@@ -423,15 +399,15 @@ do_test_7526(N,M) ->
{Self, Ref} = {self(), make_ref()},
T = erlang:make_tuple(M,0),
spawn_opt(fun()->
- L = iterate_7526(N, []),
- BadList = [X || X <- L, X =/= 9223372036854775808],
- BadLen = length(BadList),
- M = length(tuple_to_list(T)),
- %%io:format("~b bad conversions: ~p~n", [BadLen, BadList]),
- Self ! {done, Ref, BadLen}
- end,
- [link,{fullsweep_after,0}]),
- receive {done, Ref, Len} -> Len end.
+ L = iterate_7526(N, []),
+ BadList = [X || X <- L, X =/= 9223372036854775808],
+ BadLen = length(BadList),
+ M = length(tuple_to_list(T)),
+ %%io:format("~b bad conversions: ~p~n", [BadLen, BadList]),
+ Self ! {done, Ref, BadLen}
+ end,
+ [link,{fullsweep_after,0}]),
+ receive {done, Ref, Len} -> Len end.
test_7526(0) ->
@@ -455,57 +431,53 @@ binary_to_atom(Config) when is_list(Config) ->
LongBin = list_to_binary(Long),
%% latin1
- ?line '' = test_binary_to_atom(<<>>, latin1),
- ?line '\377' = test_binary_to_atom(<<255>>, latin1),
- ?line HalfLongAtom = test_binary_to_atom(HalfLongBin, latin1),
- ?line LongAtom = test_binary_to_atom(LongBin, latin1),
+ '' = test_binary_to_atom(<<>>, latin1),
+ '\377' = test_binary_to_atom(<<255>>, latin1),
+ HalfLongAtom = test_binary_to_atom(HalfLongBin, latin1),
+ LongAtom = test_binary_to_atom(LongBin, latin1),
%% utf8
- ?line '' = test_binary_to_atom(<<>>, utf8),
- ?line HalfLongAtom = test_binary_to_atom(HalfLongBin, utf8),
- ?line HalfLongAtom = test_binary_to_atom(HalfLongBin, unicode),
- ?line [] = [C || C <- lists:seq(128, 255),
+ '' = test_binary_to_atom(<<>>, utf8),
+ HalfLongAtom = test_binary_to_atom(HalfLongBin, utf8),
+ HalfLongAtom = test_binary_to_atom(HalfLongBin, unicode),
+ [] = [C || C <- lists:seq(128, 255),
begin
list_to_atom([C]) =/=
test_binary_to_atom(<<C/utf8>>, utf8)
end],
%% badarg failures.
- ?line fail_binary_to_atom(atom),
- ?line fail_binary_to_atom(42),
- ?line fail_binary_to_atom({a,b,c}),
- ?line fail_binary_to_atom([1,2,3]),
- ?line fail_binary_to_atom([]),
- ?line fail_binary_to_atom(42.0),
- ?line fail_binary_to_atom(self()),
- ?line fail_binary_to_atom(make_ref()),
- ?line fail_binary_to_atom(<<0:7>>),
- ?line fail_binary_to_atom(<<42:13>>),
- ?line ?BADARG(binary_to_atom(id(<<>>), blurf)),
- ?line ?BADARG(binary_to_atom(id(<<>>), [])),
+ fail_binary_to_atom(atom),
+ fail_binary_to_atom(42),
+ fail_binary_to_atom({a,b,c}),
+ fail_binary_to_atom([1,2,3]),
+ fail_binary_to_atom([]),
+ fail_binary_to_atom(42.0),
+ fail_binary_to_atom(self()),
+ fail_binary_to_atom(make_ref()),
+ fail_binary_to_atom(<<0:7>>),
+ fail_binary_to_atom(<<42:13>>),
+ ?BADARG(binary_to_atom(id(<<>>), blurf)),
+ ?BADARG(binary_to_atom(id(<<>>), [])),
%% Bad UTF8 sequences.
- ?line ?BADARG(binary_to_atom(id(<<255>>), utf8)),
- ?line ?BADARG(binary_to_atom(id(<<255,0>>), utf8)),
- ?line ?BADARG(binary_to_atom(id(<<16#C0,16#80>>), utf8)), %Overlong 0.
- ?line [?BADARG(binary_to_atom(<<C/utf8>>, utf8)) ||
- C <- lists:seq(256, 16#D7FF)],
- ?line [?BADARG(binary_to_atom(<<C/utf8>>, utf8)) ||
- C <- lists:seq(16#E000, 16#FFFD)],
- ?line [?BADARG(binary_to_atom(<<C/utf8>>, utf8)) ||
- C <- lists:seq(16#10000, 16#8FFFF)],
- ?line [?BADARG(binary_to_atom(<<C/utf8>>, utf8)) ||
- C <- lists:seq(16#90000, 16#10FFFF)],
+ ?BADARG(binary_to_atom(id(<<255>>), utf8)),
+ ?BADARG(binary_to_atom(id(<<255,0>>), utf8)),
+ ?BADARG(binary_to_atom(id(<<16#C0,16#80>>), utf8)), %Overlong 0.
+ [?BADARG(binary_to_atom(<<C/utf8>>, utf8)) || C <- lists:seq(256, 16#D7FF)],
+ [?BADARG(binary_to_atom(<<C/utf8>>, utf8)) || C <- lists:seq(16#E000, 16#FFFD)],
+ [?BADARG(binary_to_atom(<<C/utf8>>, utf8)) || C <- lists:seq(16#10000, 16#8FFFF)],
+ [?BADARG(binary_to_atom(<<C/utf8>>, utf8)) || C <- lists:seq(16#90000, 16#10FFFF)],
%% system_limit failures.
- ?line ?SYS_LIMIT(binary_to_atom(id(<<0:512/unit:8,255>>), utf8)),
- ?line ?SYS_LIMIT(binary_to_atom(id(<<0:512/unit:8,255,0>>), utf8)),
- ?line ?SYS_LIMIT(binary_to_atom(<<0:256/unit:8>>, latin1)),
- ?line ?SYS_LIMIT(binary_to_atom(<<0:257/unit:8>>, latin1)),
- ?line ?SYS_LIMIT(binary_to_atom(<<0:512/unit:8>>, latin1)),
- ?line ?SYS_LIMIT(binary_to_atom(<<0:256/unit:8>>, utf8)),
- ?line ?SYS_LIMIT(binary_to_atom(<<0:257/unit:8>>, utf8)),
- ?line ?SYS_LIMIT(binary_to_atom(<<0:512/unit:8>>, utf8)),
+ ?SYS_LIMIT(binary_to_atom(id(<<0:512/unit:8,255>>), utf8)),
+ ?SYS_LIMIT(binary_to_atom(id(<<0:512/unit:8,255,0>>), utf8)),
+ ?SYS_LIMIT(binary_to_atom(<<0:256/unit:8>>, latin1)),
+ ?SYS_LIMIT(binary_to_atom(<<0:257/unit:8>>, latin1)),
+ ?SYS_LIMIT(binary_to_atom(<<0:512/unit:8>>, latin1)),
+ ?SYS_LIMIT(binary_to_atom(<<0:256/unit:8>>, utf8)),
+ ?SYS_LIMIT(binary_to_atom(<<0:257/unit:8>>, utf8)),
+ ?SYS_LIMIT(binary_to_atom(<<0:512/unit:8>>, utf8)),
ok.
test_binary_to_atom(Bin0, Encoding) ->
@@ -518,49 +490,49 @@ test_binary_to_atom(Bin0, Encoding) ->
fail_binary_to_atom(Bin) ->
try
- binary_to_atom(Bin, latin1)
+ binary_to_atom(Bin, latin1)
catch
- error:badarg ->
- ok
+ error:badarg ->
+ ok
end,
try
- binary_to_atom(Bin, utf8)
+ binary_to_atom(Bin, utf8)
catch
- error:badarg ->
- ok
+ error:badarg ->
+ ok
end,
try
- binary_to_existing_atom(Bin, latin1)
+ binary_to_existing_atom(Bin, latin1)
catch
- error:badarg ->
- ok
+ error:badarg ->
+ ok
end,
try
- binary_to_existing_atom(Bin, utf8)
+ binary_to_existing_atom(Bin, utf8)
catch
- error:badarg ->
- ok
+ error:badarg ->
+ ok
end.
binary_to_existing_atom(Config) when is_list(Config) ->
- ?line UnlikelyBin = <<"ou0897979655678dsfj923874390867er869fds973qerueoru">>,
+ UnlikelyBin = <<"ou0897979655678dsfj923874390867er869fds973qerueoru">>,
try
- ?line binary_to_existing_atom(UnlikelyBin, latin1),
- ?line ?t:fail()
+ binary_to_existing_atom(UnlikelyBin, latin1),
+ ct:fail(atom_exists)
catch
error:badarg -> ok
end,
try
- ?line binary_to_existing_atom(UnlikelyBin, utf8),
- ?line ?t:fail()
+ binary_to_existing_atom(UnlikelyBin, utf8),
+ ct:fail(atom_exists)
catch
error:badarg -> ok
end,
- ?line UnlikelyAtom = binary_to_atom(id(UnlikelyBin), latin1),
- ?line UnlikelyAtom = binary_to_existing_atom(UnlikelyBin, latin1),
+ UnlikelyAtom = binary_to_atom(id(UnlikelyBin), latin1),
+ UnlikelyAtom = binary_to_existing_atom(UnlikelyBin, latin1),
ok.
@@ -573,32 +545,32 @@ atom_to_binary(Config) when is_list(Config) ->
LongBin = list_to_binary(Long),
%% latin1
- ?line <<>> = atom_to_binary('', latin1),
- ?line <<"abc">> = atom_to_binary(abc, latin1),
- ?line <<127>> = atom_to_binary('\177', latin1),
- ?line HalfLongBin = atom_to_binary(HalfLongAtom, latin1),
- ?line LongBin = atom_to_binary(LongAtom, latin1),
+ <<>> = atom_to_binary('', latin1),
+ <<"abc">> = atom_to_binary(abc, latin1),
+ <<127>> = atom_to_binary('\177', latin1),
+ HalfLongBin = atom_to_binary(HalfLongAtom, latin1),
+ LongBin = atom_to_binary(LongAtom, latin1),
%% utf8.
- ?line <<>> = atom_to_binary('', utf8),
- ?line <<>> = atom_to_binary('', unicode),
- ?line <<127>> = atom_to_binary('\177', utf8),
- ?line <<"abcdef">> = atom_to_binary(abcdef, utf8),
- ?line HalfLongBin = atom_to_binary(HalfLongAtom, utf8),
- ?line LongAtomBin = atom_to_binary(LongAtom, utf8),
- ?line verify_long_atom_bin(LongAtomBin, 0),
+ <<>> = atom_to_binary('', utf8),
+ <<>> = atom_to_binary('', unicode),
+ <<127>> = atom_to_binary('\177', utf8),
+ <<"abcdef">> = atom_to_binary(abcdef, utf8),
+ HalfLongBin = atom_to_binary(HalfLongAtom, utf8),
+ LongAtomBin = atom_to_binary(LongAtom, utf8),
+ verify_long_atom_bin(LongAtomBin, 0),
%% Failing cases.
- ?line fail_atom_to_binary(<<1>>),
- ?line fail_atom_to_binary(42),
- ?line fail_atom_to_binary({a,b,c}),
- ?line fail_atom_to_binary([1,2,3]),
- ?line fail_atom_to_binary([]),
- ?line fail_atom_to_binary(42.0),
- ?line fail_atom_to_binary(self()),
- ?line fail_atom_to_binary(make_ref()),
- ?line ?BADARG(atom_to_binary(id(a), blurf)),
- ?line ?BADARG(atom_to_binary(id(b), [])),
+ fail_atom_to_binary(<<1>>),
+ fail_atom_to_binary(42),
+ fail_atom_to_binary({a,b,c}),
+ fail_atom_to_binary([1,2,3]),
+ fail_atom_to_binary([]),
+ fail_atom_to_binary(42.0),
+ fail_atom_to_binary(self()),
+ fail_atom_to_binary(make_ref()),
+ ?BADARG(atom_to_binary(id(a), blurf)),
+ ?BADARG(atom_to_binary(id(b), [])),
ok.
verify_long_atom_bin(<<I/utf8,T/binary>>, I) ->
@@ -607,74 +579,73 @@ verify_long_atom_bin(<<>>, 255) -> ok.
fail_atom_to_binary(Term) ->
try
- atom_to_binary(Term, latin1)
+ atom_to_binary(Term, latin1)
catch
- error:badarg ->
- ok
+ error:badarg ->
+ ok
end,
try
- atom_to_binary(Term, utf8)
+ atom_to_binary(Term, utf8)
catch
- error:badarg ->
- ok
+ error:badarg ->
+ ok
end.
min_max(Config) when is_list(Config) ->
- ?line a = erlang:min(id(a), a),
- ?line a = erlang:min(id(a), b),
- ?line a = erlang:min(id(b), a),
- ?line b = erlang:min(id(b), b),
- ?line a = erlang:max(id(a), a),
- ?line b = erlang:max(id(a), b),
- ?line b = erlang:max(id(b), a),
- ?line b = erlang:max(id(b), b),
-
- ?line 42.0 = erlang:min(42.0, 42),
- ?line 42.0 = erlang:max(42.0, 42),
+ a = erlang:min(id(a), a),
+ a = erlang:min(id(a), b),
+ a = erlang:min(id(b), a),
+ b = erlang:min(id(b), b),
+ a = erlang:max(id(a), a),
+ b = erlang:max(id(a), b),
+ b = erlang:max(id(b), a),
+ b = erlang:max(id(b), b),
+
+ 42.0 = erlang:min(42.0, 42),
+ 42.0 = erlang:max(42.0, 42),
%% And now (R14) they are also autoimported!
- ?line a = min(id(a), a),
- ?line a = min(id(a), b),
- ?line a = min(id(b), a),
- ?line b = min(id(b), b),
- ?line a = max(id(a), a),
- ?line b = max(id(a), b),
- ?line b = max(id(b), a),
- ?line b = max(id(b), b),
-
- ?line 42.0 = min(42.0, 42),
- ?line 42.0 = max(42.0, 42),
-
+ a = min(id(a), a),
+ a = min(id(a), b),
+ a = min(id(b), a),
+ b = min(id(b), b),
+ a = max(id(a), a),
+ b = max(id(a), b),
+ b = max(id(b), a),
+ b = max(id(b), b),
+
+ 42.0 = min(42.0, 42),
+ 42.0 = max(42.0, 42),
ok.
erlang_halt(Config) when is_list(Config) ->
try erlang:halt(undefined) of
- _-> ?t:fail({erlang,halt,{undefined}})
+ _-> ct:fail({erlang,halt,{undefined}})
catch error:badarg -> ok end,
try halt(undefined) of
- _-> ?t:fail({halt,{undefined}})
+ _-> ct:fail({halt,{undefined}})
catch error:badarg -> ok end,
try erlang:halt(undefined, []) of
- _-> ?t:fail({erlang,halt,{undefined,[]}})
+ _-> ct:fail({erlang,halt,{undefined,[]}})
catch error:badarg -> ok end,
try halt(undefined, []) of
- _-> ?t:fail({halt,{undefined,[]}})
+ _-> ct:fail({halt,{undefined,[]}})
catch error:badarg -> ok end,
try halt(0, undefined) of
- _-> ?t:fail({halt,{0,undefined}})
+ _-> ct:fail({halt,{0,undefined}})
catch error:badarg -> ok end,
try halt(0, [undefined]) of
- _-> ?t:fail({halt,{0,[undefined]}})
+ _-> ct:fail({halt,{0,[undefined]}})
catch error:badarg -> ok end,
try halt(0, [{undefined,true}]) of
- _-> ?t:fail({halt,{0,[{undefined,true}]}})
+ _-> ct:fail({halt,{0,[{undefined,true}]}})
catch error:badarg -> ok end,
try halt(0, [{flush,undefined}]) of
- _-> ?t:fail({halt,{0,[{flush,undefined}]}})
+ _-> ct:fail({halt,{0,[{flush,undefined}]}})
catch error:badarg -> ok end,
try halt(0, [{flush,true,undefined}]) of
- _-> ?t:fail({halt,{0,[{flush,true,undefined}]}})
+ _-> ct:fail({halt,{0,[{flush,true,undefined}]}})
catch error:badarg -> ok end,
H = hostname(),
{ok,N1} = slave:start(H, halt_node1),
@@ -683,6 +654,8 @@ erlang_halt(Config) when is_list(Config) ->
{badrpc,nodedown} = rpc:call(N2, erlang, halt, [0]),
{ok,N3} = slave:start(H, halt_node3),
{badrpc,nodedown} = rpc:call(N3, erlang, halt, [0,[]]),
+ {ok,N4} = slave:start(H, halt_node4),
+ {badrpc,nodedown} = rpc:call(N4, erlang, halt, [lists:duplicate(300,$x)]),
% This test triggers a segfault when dumping a crash dump
% to make sure that we can handle it properly.
@@ -694,7 +667,7 @@ erlang_halt(Config) when is_list(Config) ->
[available_internal_state, true]),
{badrpc,nodedown} = rpc:call(N4, erts_debug, set_internal_state,
[broken_halt, "Validate correct crash dump"]),
- ok = wait_until_stable_size(CrashDump,-1),
+ {ok,_} = wait_until_stable_size(CrashDump,-1),
{ok, Bin} = file:read_file(CrashDump),
case {string:str(binary_to_list(Bin),"\n=end\n"),
string:str(binary_to_list(Bin),"\r\n=end\r\n")} of
@@ -711,11 +684,216 @@ wait_until_stable_size(File,PrevSz) ->
wait_until_stable_size(File,PrevSz-1);
{ok,#file_info{size = PrevSz }} when PrevSz /= -1 ->
io:format("Crashdump file size was: ~p (~s)~n",[PrevSz,File]),
- ok;
+ {ok,PrevSz};
{ok,#file_info{size = NewSz }} ->
wait_until_stable_size(File,NewSz)
end.
+% Test erlang:halt with ERL_CRASH_DUMP_BYTES
+erl_crash_dump_bytes(Config) when is_list(Config) ->
+ Bytes = 1000,
+ CrashDump = do_limited_crash_dump(Config, Bytes),
+ {ok,ActualBytes} = wait_until_stable_size(CrashDump,-1),
+ true = ActualBytes < (Bytes + 100),
+
+ NoDump = do_limited_crash_dump(Config,0),
+ {error,enoent} = wait_until_stable_size(NoDump,-8),
+ ok.
+
+do_limited_crash_dump(Config, Bytes) ->
+ H = hostname(),
+ {ok,N} = slave:start(H, halt_node),
+ BytesStr = integer_to_list(Bytes),
+ CrashDump = filename:join(proplists:get_value(priv_dir,Config),
+ "erl_crash." ++ BytesStr ++ ".dump"),
+ true = rpc:call(N, os, putenv, ["ERL_CRASH_DUMP",CrashDump]),
+ true = rpc:call(N, os, putenv, ["ERL_CRASH_DUMP_BYTES",BytesStr]),
+ {badrpc,nodedown} = rpc:call(N, erlang, halt, ["Testing ERL_CRASH_DUMP_BYTES"]),
+ CrashDump.
+
+
+is_builtin(_Config) ->
+ Exp0 = [{M,F,A} || {M,_} <- code:all_loaded(),
+ {F,A} <- M:module_info(exports)],
+ Exp = ordsets:from_list(Exp0),
+
+ %% erlang:apply/3 is considered to be built-in, but is not
+ %% implemented as other BIFs.
+
+ Builtins0 = [{erlang,apply,3}|erlang:system_info(snifs)],
+ Builtins = ordsets:from_list(Builtins0),
+ NotBuiltin = ordsets:subtract(Exp, Builtins),
+ _ = [true = erlang:is_builtin(M, F, A) || {M,F,A} <- Builtins],
+ _ = [false = erlang:is_builtin(M, F, A) || {M,F,A} <- NotBuiltin],
+
+ ok.
+
+error_stacktrace(Config) when is_list(Config) ->
+ error_stacktrace_test().
+
+error_stacktrace_during_call_trace(Config) when is_list(Config) ->
+ Tracer = spawn_link(fun () ->
+ receive after infinity -> ok end
+ end),
+ Mprog = [{'_',[],[{exception_trace}]}],
+ erlang:trace_pattern({?MODULE,'_','_'}, Mprog, [local]),
+ 1 = erlang:trace_pattern({erlang,error,2}, Mprog, [local]),
+ 1 = erlang:trace_pattern({erlang,error,1}, Mprog, [local]),
+ erlang:trace(all, true, [call,return_to,timestamp,{tracer, Tracer}]),
+ try
+ error_stacktrace_test()
+ after
+ erlang:trace(all, false, [call,return_to,timestamp,{tracer, Tracer}]),
+ erlang:trace_pattern({erlang,error,2}, false, [local]),
+ erlang:trace_pattern({erlang,error,1}, false, [local]),
+ erlang:trace_pattern({?MODULE,'_','_'}, false, [local]),
+ unlink(Tracer),
+ exit(Tracer, kill),
+ Mon = erlang:monitor(process, Tracer),
+ receive
+ {'DOWN', Mon, process, Tracer, _} -> ok
+ end
+ end,
+ ok.
+
+
+error_stacktrace_test() ->
+ Types = [apply_const_last, apply_const, apply_last,
+ apply, double_apply_const_last, double_apply_const,
+ double_apply_last, double_apply, multi_apply_const_last,
+ multi_apply_const, multi_apply_last, multi_apply,
+ call_const_last, call_last, call_const, call],
+ lists:foreach(fun (Type) ->
+ {Pid, Mon} = spawn_monitor(
+ fun () ->
+ stk([a,b,c,d], Type, error_2)
+ end),
+ receive
+ {'DOWN', Mon, process, Pid, Reason} ->
+ {oops, Stack} = Reason,
+%% io:format("Type: ~p Stack: ~p~n",
+%% [Type, Stack]),
+ [{?MODULE, do_error_2, [Type], _},
+ {?MODULE, stk, 3, _},
+ {?MODULE, stk, 3, _}] = Stack
+ end
+ end,
+ Types),
+ lists:foreach(fun (Type) ->
+ {Pid, Mon} = spawn_monitor(
+ fun () ->
+ stk([a,b,c,d], Type, error_1)
+ end),
+ receive
+ {'DOWN', Mon, process, Pid, Reason} ->
+ {oops, Stack} = Reason,
+%% io:format("Type: ~p Stack: ~p~n",
+%% [Type, Stack]),
+ [{?MODULE, do_error_1, 1, _},
+ {?MODULE, stk, 3, _},
+ {?MODULE, stk, 3, _}] = Stack
+ end
+ end,
+ Types),
+ ok.
+
+stk([], Type, Func) ->
+ tail(Type, Func, jump),
+ ok;
+stk([_|L], Type, Func) ->
+ stk(L, Type, Func),
+ ok.
+
+tail(Type, Func, jump) ->
+ tail(Type, Func, do);
+tail(Type, error_1, do) ->
+ do_error_1(Type);
+tail(Type, error_2, do) ->
+ do_error_2(Type).
+
+do_error_2(apply_const_last) ->
+ erlang:apply(erlang, error, [oops, [apply_const_last]]);
+do_error_2(apply_const) ->
+ erlang:apply(erlang, error, [oops, [apply_const]]),
+ ok;
+do_error_2(apply_last) ->
+ erlang:apply(id(erlang), id(error), id([oops, [apply_last]]));
+do_error_2(apply) ->
+ erlang:apply(id(erlang), id(error), id([oops, [apply]])),
+ ok;
+do_error_2(double_apply_const_last) ->
+ erlang:apply(erlang, apply, [erlang, error, [oops, [double_apply_const_last]]]);
+do_error_2(double_apply_const) ->
+ erlang:apply(erlang, apply, [erlang, error, [oops, [double_apply_const]]]),
+ ok;
+do_error_2(double_apply_last) ->
+ erlang:apply(id(erlang), id(apply), [id(erlang), id(error), id([oops, [double_apply_last]])]);
+do_error_2(double_apply) ->
+ erlang:apply(id(erlang), id(apply), [id(erlang), id(error), id([oops, [double_apply]])]),
+ ok;
+do_error_2(multi_apply_const_last) ->
+ erlang:apply(erlang, apply, [erlang, apply, [erlang, apply, [erlang, error, [oops, [multi_apply_const_last]]]]]);
+do_error_2(multi_apply_const) ->
+ erlang:apply(erlang, apply, [erlang, apply, [erlang, apply, [erlang, error, [oops, [multi_apply_const]]]]]),
+ ok;
+do_error_2(multi_apply_last) ->
+ erlang:apply(id(erlang), id(apply), [id(erlang), id(apply), [id(erlang), id(apply), [id(erlang), id(error), id([oops, [multi_apply_last]])]]]);
+do_error_2(multi_apply) ->
+ erlang:apply(id(erlang), id(apply), [id(erlang), id(apply), [id(erlang), id(apply), [id(erlang), id(error), id([oops, [multi_apply]])]]]),
+ ok;
+do_error_2(call_const_last) ->
+ erlang:error(oops, [call_const_last]);
+do_error_2(call_last) ->
+ erlang:error(id(oops), id([call_last]));
+do_error_2(call_const) ->
+ erlang:error(oops, [call_const]),
+ ok;
+do_error_2(call) ->
+ erlang:error(id(oops), id([call])).
+
+
+do_error_1(apply_const_last) ->
+ erlang:apply(erlang, error, [oops]);
+do_error_1(apply_const) ->
+ erlang:apply(erlang, error, [oops]),
+ ok;
+do_error_1(apply_last) ->
+ erlang:apply(id(erlang), id(error), id([oops]));
+do_error_1(apply) ->
+ erlang:apply(id(erlang), id(error), id([oops])),
+ ok;
+do_error_1(double_apply_const_last) ->
+ erlang:apply(erlang, apply, [erlang, error, [oops]]);
+do_error_1(double_apply_const) ->
+ erlang:apply(erlang, apply, [erlang, error, [oops]]),
+ ok;
+do_error_1(double_apply_last) ->
+ erlang:apply(id(erlang), id(apply), [id(erlang), id(error), id([oops])]);
+do_error_1(double_apply) ->
+ erlang:apply(id(erlang), id(apply), [id(erlang), id(error), id([oops])]),
+ ok;
+do_error_1(multi_apply_const_last) ->
+ erlang:apply(erlang, apply, [erlang, apply, [erlang, apply, [erlang, error, [oops]]]]);
+do_error_1(multi_apply_const) ->
+ erlang:apply(erlang, apply, [erlang, apply, [erlang, apply, [erlang, error, [oops]]]]),
+ ok;
+do_error_1(multi_apply_last) ->
+ erlang:apply(id(erlang), id(apply), [id(erlang), id(apply), [id(erlang), id(apply), [id(erlang), id(error), id([oops])]]]);
+do_error_1(multi_apply) ->
+ erlang:apply(id(erlang), id(apply), [id(erlang), id(apply), [id(erlang), id(apply), [id(erlang), id(error), id([oops])]]]),
+ ok;
+do_error_1(call_const_last) ->
+ erlang:error(oops);
+do_error_1(call_last) ->
+ erlang:error(id(oops));
+do_error_1(call_const) ->
+ erlang:error(oops),
+ ok;
+do_error_1(call) ->
+ erlang:error(id(oops)).
+
+
+
%% Helpers
diff --git a/erts/emulator/test/big_SUITE.erl b/erts/emulator/test/big_SUITE.erl
index e8f881f2a4..402751393a 100644
--- a/erts/emulator/test/big_SUITE.erl
+++ b/erts/emulator/test/big_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,8 +20,8 @@
-module(big_SUITE).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2]).
+-export([all/0, suite/0, groups/0]).
+
-export([t_div/1, eq_28/1, eq_32/1, eq_big/1, eq_math/1, big_literals/1,
borders/1, negative/1, big_float_1/1, big_float_2/1,
shift_limit_1/1, powmod/1, system_limit/1, toobig/1, otp_6692/1]).
@@ -32,11 +32,12 @@
-export([fac/1, fib/1, pow/2, gcd/2, lcm/2]).
--export([init_per_testcase/2, end_per_testcase/2]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 3}}].
all() ->
[t_div, eq_28, eq_32, eq_big, eq_math, big_literals,
@@ -46,27 +47,6 @@ all() ->
groups() ->
[{big_float, [], [big_float_1, big_float_2]}].
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- Dog=?t:timetrap(?t:minutes(3)),
- [{watchdog, Dog}|Config].
-
-end_per_testcase(_Func, Config) ->
- Dog=?config(watchdog, Config),
- ?t:timetrap_cancel(Dog).
-
%%
%% Syntax of data files:
%% Expr1 = Expr2.
@@ -95,7 +75,7 @@ eq_math(Config) when is_list(Config) ->
test(TestFile).
-borders(doc) -> "Tests border cases between small/big.";
+%% Tests border cases between small/big.
borders(Config) when is_list(Config) ->
TestFile = test_file(Config, "borders.dat"),
test(TestFile).
@@ -107,7 +87,7 @@ negative(Config) when is_list(Config) ->
%% Find test file
test_file(Config, Name) ->
- DataDir = ?config(data_dir, Config),
+ DataDir = proplists:get_value(data_dir, Config),
filename:join(DataDir, Name).
%%
@@ -119,12 +99,12 @@ test(File) ->
test(File, [node()]).
test(File, Nodes) ->
- ?line {ok,Fd} = file:open(File, [read]),
+ {ok,Fd} = file:open(File, [read]),
Res = test(File, Fd, Nodes),
file:close(Fd),
case Res of
{0,Cases} -> {comment, integer_to_list(Cases) ++ " cases"};
- {_,_} -> test_server:fail()
+ {_,_} -> ct:fail("failed")
end.
test(File, Fd, Ns) ->
@@ -156,7 +136,7 @@ multi_match(Ns, Expr) ->
multi_match(Ns, Expr, []).
multi_match([Node|Ns], Expr, Rs) ->
- ?line X = rpc:call(Node, big_SUITE, eval, [Expr]),
+ X = rpc:call(Node, big_SUITE, eval, [Expr]),
if X == 0 -> multi_match(Ns, Expr, Rs);
true -> multi_match(Ns, Expr, [{Node,X}|Rs])
end;
@@ -248,10 +228,10 @@ lcm(Q, R) ->
%% Test case t_div cut in from R2D test suite.
t_div(Config) when is_list(Config) ->
- ?line 'try'(fun() -> 98765432101234 div 98765432101235 end, 0),
+ 'try'(fun() -> 98765432101234 div 98765432101235 end, 0),
% Big remainder, small quotient.
- ?line 'try'(fun() -> 339254531512 div 68719476736 end, 4),
+ 'try'(fun() -> 339254531512 div 68719476736 end, 4),
ok.
'try'(Fun, Result) ->
@@ -265,65 +245,60 @@ t_div(Config) when is_list(Config) ->
{result, Result} ->
'try'(Iter-1, Fun, Result, [0|Filler]);
{result, Other} ->
- io:format("Expected ~p; got ~p~n", [Result, Other]),
- test_server:fail()
+ ct:fail("Expected ~p; got ~p~n", [Result, Other])
end.
init(ReplyTo, Fun, _Filler) ->
ReplyTo ! {result, Fun()}.
-big_literals(doc) ->
- "Tests that big-number literals work correctly.";
+%% Tests that big-number literals work correctly.
big_literals(Config) when is_list(Config) ->
%% Note: The literal test cannot be compiler on a pre-R4 Beam emulator,
%% so we compile it now.
- ?line DataDir = ?config(data_dir, Config),
- ?line Test = filename:join(DataDir, "literal_test"),
- ?line {ok, Mod, Bin} = compile:file(Test, [binary]),
- ?line {module, Mod} = code:load_binary(Mod, Mod, Bin),
- ?line ok = Mod:t(),
+ DataDir = proplists:get_value(data_dir, Config),
+ Test = filename:join(DataDir, "literal_test"),
+ {ok, Mod, Bin} = compile:file(Test, [binary]),
+ {module, Mod} = code:load_binary(Mod, Mod, Bin),
+ ok = Mod:t(),
ok.
-big_float_1(doc) ->
- ["OTP-2436, part 1"];
+%% OTP-2436, part 1
big_float_1(Config) when is_list(Config) ->
%% F is a number very close to a maximum float.
- ?line F = id(1.7e308),
- ?line I = trunc(F),
- ?line true = (I == F),
- ?line false = (I /= F),
- ?line true = (I > F/2),
- ?line false = (I =< F/2),
- ?line true = (I*2 >= F),
- ?line false = (I*2 < F),
- ?line true = (I*I > F),
- ?line false = (I*I =< F),
-
- ?line true = (F == I),
- ?line false = (F /= I),
- ?line false = (F/2 > I),
- ?line true = (F/2 =< I),
- ?line false = (F >= I*2),
- ?line true = (F < I*2),
- ?line false = (F > I*I),
- ?line true = (F =< I*I),
+ F = id(1.7e308),
+ I = trunc(F),
+ true = (I == F),
+ false = (I /= F),
+ true = (I > F/2),
+ false = (I =< F/2),
+ true = (I*2 >= F),
+ false = (I*2 < F),
+ true = (I*I > F),
+ false = (I*I =< F),
+
+ true = (F == I),
+ false = (F /= I),
+ false = (F/2 > I),
+ true = (F/2 =< I),
+ false = (F >= I*2),
+ true = (F < I*2),
+ false = (F > I*I),
+ true = (F =< I*I),
ok.
-big_float_2(doc) ->
- ["OTP-2436, part 2"];
+%% "OTP-2436, part 2
big_float_2(Config) when is_list(Config) ->
- ?line F = id(1.7e308),
- ?line I = trunc(F),
- ?line {'EXIT', _} = (catch 1/(2*I)),
- ?line _Ignore = 2/I,
- ?line {'EXIT', _} = (catch 4/(2*I)),
+ F = id(1.7e308),
+ I = trunc(F),
+ {'EXIT', _} = (catch 1/(2*I)),
+ _Ignore = 2/I,
+ {'EXIT', _} = (catch 4/(2*I)),
ok.
-shift_limit_1(doc) ->
- ["OTP-3256"];
+%% OTP-3256
shift_limit_1(Config) when is_list(Config) ->
- ?line case catch (id(1) bsl 100000000) of
+ case catch (id(1) bsl 100000000) of
{'EXIT', {system_limit, _}} ->
ok
end,
@@ -352,16 +327,16 @@ powmod(A, B, C) ->
end.
system_limit(Config) when is_list(Config) ->
- ?line Maxbig = maxbig(),
- ?line {'EXIT',{system_limit,_}} = (catch Maxbig+1),
- ?line {'EXIT',{system_limit,_}} = (catch -Maxbig-1),
- ?line {'EXIT',{system_limit,_}} = (catch 2*Maxbig),
- ?line {'EXIT',{system_limit,_}} = (catch bnot Maxbig),
- ?line {'EXIT',{system_limit,_}} = (catch apply(erlang, id('bnot'), [Maxbig])),
- ?line {'EXIT',{system_limit,_}} = (catch Maxbig bsl 2),
- ?line {'EXIT',{system_limit,_}} = (catch apply(erlang, id('bsl'), [Maxbig,2])),
- ?line {'EXIT',{system_limit,_}} = (catch id(1) bsl (1 bsl 45)),
- ?line {'EXIT',{system_limit,_}} = (catch id(1) bsl (1 bsl 69)),
+ Maxbig = maxbig(),
+ {'EXIT',{system_limit,_}} = (catch Maxbig+1),
+ {'EXIT',{system_limit,_}} = (catch -Maxbig-1),
+ {'EXIT',{system_limit,_}} = (catch 2*Maxbig),
+ {'EXIT',{system_limit,_}} = (catch bnot Maxbig),
+ {'EXIT',{system_limit,_}} = (catch apply(erlang, id('bnot'), [Maxbig])),
+ {'EXIT',{system_limit,_}} = (catch Maxbig bsl 2),
+ {'EXIT',{system_limit,_}} = (catch apply(erlang, id('bsl'), [Maxbig,2])),
+ {'EXIT',{system_limit,_}} = (catch id(1) bsl (1 bsl 45)),
+ {'EXIT',{system_limit,_}} = (catch id(1) bsl (1 bsl 69)),
ok.
maxbig() ->
@@ -372,7 +347,7 @@ maxbig() ->
id(I) -> I.
toobig(Config) when is_list(Config) ->
- ?line {'EXIT',{{badmatch,_},_}} = (catch toobig()),
+ {'EXIT',{{badmatch,_},_}} = (catch toobig()),
ok.
toobig() ->
@@ -381,12 +356,9 @@ toobig() ->
<<ANr:ASize>> = A, % should fail
ANr band ANr.
-otp_6692(suite) ->
- [];
-otp_6692(doc) ->
- ["Tests for DIV/REM bug reported in OTP-6692"];
+%% Tests for DIV/REM bug reported in OTP-6692
otp_6692(Config) when is_list(Config)->
- ?line loop1(1,1000).
+ loop1(1,1000).
fact(N) ->
fact(N,1).
diff --git a/erts/emulator/test/big_SUITE_data/literal_test.erl b/erts/emulator/test/big_SUITE_data/literal_test.erl
index 1620693bfa..17c4db467a 100644
--- a/erts/emulator/test/big_SUITE_data/literal_test.erl
+++ b/erts/emulator/test/big_SUITE_data/literal_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl
index 96ba2f64d4..1c7d278bb0 100644
--- a/erts/emulator/test/binary_SUITE.erl
+++ b/erts/emulator/test/binary_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -40,7 +40,7 @@
%% phash2(Binary, N)
%%
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
@@ -106,17 +106,17 @@ end_per_testcase(_Func, _Config) ->
copy_terms(Config) when is_list(Config) ->
Self = self(),
- ?line Pid = spawn_link(fun() -> copy_server(Self) end),
+ Pid = spawn_link(fun() -> copy_server(Self) end),
F = fun(Term) ->
Pid ! Term,
receive
Term -> ok;
Other ->
io:format("Sent: ~P\nGot back:~P", [Term,12,Other,12]),
- ?t:fail(bad_term)
+ ct:fail(bad_term)
end
end,
- ?line test_terms(F),
+ test_terms(F),
ok.
copy_server(Parent) ->
@@ -129,154 +129,152 @@ copy_server(Parent) ->
%% Tests list_to_binary/1, binary_to_list/1 and size/1,
%% using flat lists.
-conversions(suite) -> [];
conversions(Config) when is_list(Config) ->
- ?line test_bin([]),
- ?line test_bin([1]),
- ?line test_bin([1, 2]),
- ?line test_bin([1, 2, 3]),
- ?line test_bin(lists:seq(0, ?heap_binary_size)),
- ?line test_bin(lists:seq(0, ?heap_binary_size+1)),
- ?line test_bin(lists:seq(0, 255)),
- ?line test_bin(lists:duplicate(50000, $@)),
+ test_bin([]),
+ test_bin([1]),
+ test_bin([1, 2]),
+ test_bin([1, 2, 3]),
+ test_bin(lists:seq(0, ?heap_binary_size)),
+ test_bin(lists:seq(0, ?heap_binary_size+1)),
+ test_bin(lists:seq(0, 255)),
+ test_bin(lists:duplicate(50000, $@)),
%% Binary in list.
List = [1,2,3,4,5],
- ?line B1 = make_sub_binary(list_to_binary(List)),
- ?line 5 = size(B1),
- ?line 5 = size(make_unaligned_sub_binary(B1)),
- ?line 40 = bit_size(B1),
- ?line 40 = bit_size(make_unaligned_sub_binary(B1)),
- ?line B2 = list_to_binary([42,B1,19]),
- ?line B2 = list_to_binary([42,make_unaligned_sub_binary(B1),19]),
- ?line B2 = iolist_to_binary(B2),
- ?line B2 = iolist_to_binary(make_unaligned_sub_binary(B2)),
- ?line 7 = size(B2),
- ?line 7 = size(make_sub_binary(B2)),
- ?line 56 = bit_size(B2),
- ?line 56 = bit_size(make_sub_binary(B2)),
- ?line [42,1,2,3,4,5,19] = binary_to_list(B2),
- ?line [42,1,2,3,4,5,19] = binary_to_list(make_sub_binary(B2)),
- ?line [42,1,2,3,4,5,19] = binary_to_list(make_unaligned_sub_binary(B2)),
- ?line [42,1,2,3,4,5,19] = bitstring_to_list(B2),
- ?line [42,1,2,3,4,5,19] = bitstring_to_list(make_sub_binary(B2)),
- ?line [42,1,2,3,4,5,19] = bitstring_to_list(make_unaligned_sub_binary(B2)),
+ B1 = make_sub_binary(list_to_binary(List)),
+ 5 = size(B1),
+ 5 = size(make_unaligned_sub_binary(B1)),
+ 40 = bit_size(B1),
+ 40 = bit_size(make_unaligned_sub_binary(B1)),
+ B2 = list_to_binary([42,B1,19]),
+ B2 = list_to_binary([42,make_unaligned_sub_binary(B1),19]),
+ B2 = iolist_to_binary(B2),
+ B2 = iolist_to_binary(make_unaligned_sub_binary(B2)),
+ 7 = size(B2),
+ 7 = size(make_sub_binary(B2)),
+ 56 = bit_size(B2),
+ 56 = bit_size(make_sub_binary(B2)),
+ [42,1,2,3,4,5,19] = binary_to_list(B2),
+ [42,1,2,3,4,5,19] = binary_to_list(make_sub_binary(B2)),
+ [42,1,2,3,4,5,19] = binary_to_list(make_unaligned_sub_binary(B2)),
+ [42,1,2,3,4,5,19] = bitstring_to_list(B2),
+ [42,1,2,3,4,5,19] = bitstring_to_list(make_sub_binary(B2)),
+ [42,1,2,3,4,5,19] = bitstring_to_list(make_unaligned_sub_binary(B2)),
ok.
test_bin(List) ->
- ?line Size = length(List),
- ?line Bin = list_to_binary(List),
- ?line Bin = iolist_to_binary(List),
- ?line Bin = list_to_bitstring(List),
- ?line Size = iolist_size(List),
- ?line Size = iolist_size(Bin),
- ?line Size = iolist_size(make_unaligned_sub_binary(Bin)),
- ?line Size = size(Bin),
- ?line Size = size(make_sub_binary(Bin)),
- ?line Size = size(make_unaligned_sub_binary(Bin)),
- ?line List = binary_to_list(Bin),
- ?line List = binary_to_list(make_sub_binary(Bin)),
- ?line List = binary_to_list(make_unaligned_sub_binary(Bin)),
- ?line List = bitstring_to_list(Bin),
- ?line List = bitstring_to_list(make_unaligned_sub_binary(Bin)).
+ Size = length(List),
+ Bin = list_to_binary(List),
+ Bin = iolist_to_binary(List),
+ Bin = list_to_bitstring(List),
+ Size = iolist_size(List),
+ Size = iolist_size(Bin),
+ Size = iolist_size(make_unaligned_sub_binary(Bin)),
+ Size = size(Bin),
+ Size = size(make_sub_binary(Bin)),
+ Size = size(make_unaligned_sub_binary(Bin)),
+ List = binary_to_list(Bin),
+ List = binary_to_list(make_sub_binary(Bin)),
+ List = binary_to_list(make_unaligned_sub_binary(Bin)),
+ List = bitstring_to_list(Bin),
+ List = bitstring_to_list(make_unaligned_sub_binary(Bin)).
%% Tests list_to_binary/1, iolist_to_binary/1, list_to_bitstr/1, binary_to_list/1,3,
%% bitstr_to_list/1, and size/1, using deep lists.
deep_lists(Config) when is_list(Config) ->
- ?line test_deep_list(["abc"]),
- ?line test_deep_list([[12,13,[123,15]]]),
- ?line test_deep_list([[12,13,[lists:seq(0, 255), []]]]),
+ test_deep_list(["abc"]),
+ test_deep_list([[12,13,[123,15]]]),
+ test_deep_list([[12,13,[lists:seq(0, 255), []]]]),
ok.
test_deep_list(List) ->
- ?line FlatList = lists:flatten(List),
- ?line Size = length(FlatList),
- ?line Bin = list_to_binary(List),
- ?line Bin = iolist_to_binary(List),
- ?line Bin = iolist_to_binary(Bin),
- ?line Bin = list_to_bitstring(List),
- ?line Size = size(Bin),
- ?line Size = iolist_size(List),
- ?line Size = iolist_size(FlatList),
- ?line Size = iolist_size(Bin),
- ?line Bitsize = bit_size(Bin),
- ?line Bitsize = 8*Size,
- ?line FlatList = binary_to_list(Bin),
- ?line FlatList = bitstring_to_list(Bin),
+ FlatList = lists:flatten(List),
+ Size = length(FlatList),
+ Bin = list_to_binary(List),
+ Bin = iolist_to_binary(List),
+ Bin = iolist_to_binary(Bin),
+ Bin = list_to_bitstring(List),
+ Size = size(Bin),
+ Size = iolist_size(List),
+ Size = iolist_size(FlatList),
+ Size = iolist_size(Bin),
+ Bitsize = bit_size(Bin),
+ Bitsize = 8*Size,
+ FlatList = binary_to_list(Bin),
+ FlatList = bitstring_to_list(Bin),
io:format("testing plain binary..."),
- ?line t_binary_to_list_3(FlatList, Bin, 1, Size),
+ t_binary_to_list_3(FlatList, Bin, 1, Size),
io:format("testing unaligned sub binary..."),
- ?line t_binary_to_list_3(FlatList, make_unaligned_sub_binary(Bin), 1, Size).
+ t_binary_to_list_3(FlatList, make_unaligned_sub_binary(Bin), 1, Size).
t_binary_to_list_3(List, Bin, From, To) ->
- ?line going_up(List, Bin, From, To),
- ?line going_down(List, Bin, From, To),
- ?line going_center(List, Bin, From, To).
+ going_up(List, Bin, From, To),
+ going_down(List, Bin, From, To),
+ going_center(List, Bin, From, To).
going_up(List, Bin, From, To) when From =< To ->
- ?line List = binary_to_list(Bin, From, To),
- ?line going_up(tl(List), Bin, From+1, To);
+ List = binary_to_list(Bin, From, To),
+ going_up(tl(List), Bin, From+1, To);
going_up(_List, _Bin, From, To) when From > To ->
ok.
going_down(List, Bin, From, To) when To > 0->
- ?line compare(List, binary_to_list(Bin, From, To), To-From+1),
- ?line going_down(List, Bin, From, To-1);
+ compare(List, binary_to_list(Bin, From, To), To-From+1),
+ going_down(List, Bin, From, To-1);
going_down(_List, _Bin, _From, _To) ->
ok.
going_center(List, Bin, From, To) when From >= To ->
- ?line compare(List, binary_to_list(Bin, From, To), To-From+1),
- ?line going_center(tl(List), Bin, From+1, To-1);
+ compare(List, binary_to_list(Bin, From, To), To-From+1),
+ going_center(tl(List), Bin, From+1, To-1);
going_center(_List, _Bin, _From, _To) ->
ok.
compare([X|Rest1], [X|Rest2], Left) when Left > 0 ->
- ?line compare(Rest1, Rest2, Left-1);
+ compare(Rest1, Rest2, Left-1);
compare([_X|_], [_Y|_], _Left) ->
- ?line test_server:fail();
+ ct:fail("compare fail");
compare(_List, [], 0) ->
ok.
deep_bitstr_lists(Config) when is_list(Config) ->
- ?line {<<7:3>>,[<<7:3>>]} = test_deep_bitstr([<<7:3>>]),
- ?line {<<42,5:3>>=Bin,[42,<<5:3>>]=List} = test_deep_bitstr([42,<<5:3>>]),
- ?line {Bin,List} = test_deep_bitstr([42|<<5:3>>]),
- ?line {Bin,List} = test_deep_bitstr([<<42,5:3>>]),
- ?line {Bin,List} = test_deep_bitstr([<<1:3>>,<<10:5>>|<<5:3>>]),
- ?line {Bin,List} = test_deep_bitstr([<<1:3>>,<<10:5>>,<<5:3>>]),
- ?line {Bin,List} = test_deep_bitstr([[<<1:3>>,<<10:5>>],[],<<5:3>>]),
- ?line {Bin,List} = test_deep_bitstr([[[<<1:3>>]|<<10:5>>],[],<<5:3>>]),
- ?line {Bin,List} = test_deep_bitstr([[<<0:1>>,<<0:1>>,[],<<1:1>>,<<10:5>>],
+ {<<7:3>>,[<<7:3>>]} = test_deep_bitstr([<<7:3>>]),
+ {<<42,5:3>>=Bin,[42,<<5:3>>]=List} = test_deep_bitstr([42,<<5:3>>]),
+ {Bin,List} = test_deep_bitstr([42|<<5:3>>]),
+ {Bin,List} = test_deep_bitstr([<<42,5:3>>]),
+ {Bin,List} = test_deep_bitstr([<<1:3>>,<<10:5>>|<<5:3>>]),
+ {Bin,List} = test_deep_bitstr([<<1:3>>,<<10:5>>,<<5:3>>]),
+ {Bin,List} = test_deep_bitstr([[<<1:3>>,<<10:5>>],[],<<5:3>>]),
+ {Bin,List} = test_deep_bitstr([[[<<1:3>>]|<<10:5>>],[],<<5:3>>]),
+ {Bin,List} = test_deep_bitstr([[<<0:1>>,<<0:1>>,[],<<1:1>>,<<10:5>>],
<<1:1>>,<<0:1>>,<<1:1>>]),
ok.
test_deep_bitstr(List) ->
- %%?line {'EXIT',{badarg,_}} = list_to_binary(List),
+ %%{'EXIT',{badarg,_}} = list_to_binary(List),
Bin = list_to_bitstring(List),
{Bin,bitstring_to_list(Bin)}.
-bad_list_to_binary(suite) -> [];
bad_list_to_binary(Config) when is_list(Config) ->
- ?line test_bad_bin(atom),
- ?line test_bad_bin(42),
- ?line test_bad_bin([1|2]),
- ?line test_bad_bin([256]),
- ?line test_bad_bin([255, [256]]),
- ?line test_bad_bin([-1]),
- ?line test_bad_bin([atom_in_list]),
- ?line test_bad_bin([[<<8>>]|bad_tail]),
+ test_bad_bin(atom),
+ test_bad_bin(42),
+ test_bad_bin([1|2]),
+ test_bad_bin([256]),
+ test_bad_bin([255, [256]]),
+ test_bad_bin([-1]),
+ test_bad_bin([atom_in_list]),
+ test_bad_bin([[<<8>>]|bad_tail]),
{'EXIT',{badarg,_}} = (catch list_to_binary(id(<<1,2,3>>))),
{'EXIT',{badarg,_}} = (catch list_to_binary(id([<<42:7>>]))),
{'EXIT',{badarg,_}} = (catch list_to_bitstring(id(<<1,2,3>>))),
%% Funs used to be implemented as a type of binary internally.
- ?line test_bad_bin(fun(X, Y) -> X*Y end),
- ?line test_bad_bin([1,fun(X) -> X + 1 end,2|fun() -> 0 end]),
- ?line test_bad_bin([fun(X) -> X + 1 end]),
+ test_bad_bin(fun(X, Y) -> X*Y end),
+ test_bad_bin([1,fun(X) -> X + 1 end,2|fun() -> 0 end]),
+ test_bad_bin([fun(X) -> X + 1 end]),
%% Test iolists that do not fit in the address space.
%% Unfortunately, it would be too slow to test in a 64-bit emulator.
@@ -287,15 +285,15 @@ bad_list_to_binary(Config) when is_list(Config) ->
huge_iolists() ->
FourGigs = 1 bsl 32,
- ?line Sizes = [FourGigs+N || N <- lists:seq(0, 64)] ++
+ Sizes = [FourGigs+N || N <- lists:seq(0, 64)] ++
[1 bsl N || N <- lists:seq(33, 37)],
- ?line Base = <<0:(1 bsl 20)/unit:8>>,
+ Base = <<0:(1 bsl 20)/unit:8>>,
[begin
L = build_iolist(Sz, Base),
- ?line {'EXIT',{system_limit,_}} = (catch list_to_binary([L])),
- ?line {'EXIT',{system_limit,_}} = (catch list_to_bitstring([L])),
- ?line {'EXIT',{system_limit,_}} = (catch binary:list_to_bin([L])),
- ?line {'EXIT',{system_limit,_}} = (catch iolist_to_binary(L))
+ {'EXIT',{system_limit,_}} = (catch list_to_binary([L])),
+ {'EXIT',{system_limit,_}} = (catch list_to_bitstring([L])),
+ {'EXIT',{system_limit,_}} = (catch binary:list_to_bin([L])),
+ {'EXIT',{system_limit,_}} = (catch iolist_to_binary(L))
end || Sz <- Sizes],
ok.
@@ -305,15 +303,15 @@ test_bad_bin(List) ->
{'EXIT',{badarg,_}} = (catch list_to_bitstring(List)),
{'EXIT',{badarg,_}} = (catch iolist_size(List)).
-bad_binary_to_list(doc) -> "Tries binary_to_list/1,3 with bad arguments.";
+%% Tries binary_to_list/1,3 with bad arguments.
bad_binary_to_list(Config) when is_list(Config) ->
- ?line bad_bin_to_list(fun(X) -> X * 42 end),
+ bad_bin_to_list(fun(X) -> X * 42 end),
GoodBin = list_to_binary(lists:seq(1, 10)),
- ?line bad_bin_to_list(fun(X) -> X * 44 end, 1, 2),
- ?line bad_bin_to_list(GoodBin, 0, 1),
- ?line bad_bin_to_list(GoodBin, 2, 1),
- ?line bad_bin_to_list(GoodBin, 11, 11),
+ bad_bin_to_list(fun(X) -> X * 44 end, 1, 2),
+ bad_bin_to_list(GoodBin, 0, 1),
+ bad_bin_to_list(GoodBin, 2, 1),
+ bad_bin_to_list(GoodBin, 11, 11),
{'EXIT',{badarg,_}} = (catch binary_to_list(id(<<42:7>>))),
ok.
@@ -327,63 +325,61 @@ bad_bin_to_list(Bin, First, Last) ->
%% Tries to split a binary at all possible positions.
-t_split_binary(suite) -> [];
t_split_binary(Config) when is_list(Config) ->
- ?line L = lists:seq(0, ?heap_binary_size-5), %Heap binary.
- ?line B = list_to_binary(L),
- ?line split(L, B, size(B)),
+ L = lists:seq(0, ?heap_binary_size-5), %Heap binary.
+ B = list_to_binary(L),
+ split(L, B, size(B)),
%% Sub binary of heap binary.
- ?line split(L, make_sub_binary(B), size(B)),
+ split(L, make_sub_binary(B), size(B)),
{X,_Y} = split_binary(B, size(B) div 2),
- ?line split(binary_to_list(X), X, size(X)),
+ split(binary_to_list(X), X, size(X)),
%% Unaligned sub binary of heap binary.
- ?line split(L, make_unaligned_sub_binary(B), size(B)),
+ split(L, make_unaligned_sub_binary(B), size(B)),
{X,_Y} = split_binary(B, size(B) div 2),
- ?line split(binary_to_list(X), X, size(X)),
+ split(binary_to_list(X), X, size(X)),
%% Reference-counted binary.
- ?line L2 = lists:seq(0, ?heap_binary_size+1),
- ?line B2 = list_to_binary(L2),
- ?line split(L2, B2, size(B2)),
+ L2 = lists:seq(0, ?heap_binary_size+1),
+ B2 = list_to_binary(L2),
+ split(L2, B2, size(B2)),
%% Sub binary of reference-counted binary.
- ?line split(L2, make_sub_binary(B2), size(B2)),
+ split(L2, make_sub_binary(B2), size(B2)),
{X2,_Y2} = split_binary(B2, size(B2) div 2),
- ?line split(binary_to_list(X2), X2, size(X2)),
+ split(binary_to_list(X2), X2, size(X2)),
%% Unaligned sub binary of reference-counted binary.
- ?line split(L2, make_unaligned_sub_binary(B2), size(B2)),
+ split(L2, make_unaligned_sub_binary(B2), size(B2)),
{X2,_Y2} = split_binary(B2, size(B2) div 2),
- ?line split(binary_to_list(X2), X2, size(X2)),
+ split(binary_to_list(X2), X2, size(X2)),
ok.
split(L, B, Pos) when Pos > 0 ->
- ?line {B1, B2} = split_binary(B, Pos),
- ?line B1 = list_to_binary(lists:sublist(L, 1, Pos)),
- ?line B2 = list_to_binary(lists:nthtail(Pos, L)),
- ?line split(L, B, Pos-1);
+ {B1, B2} = split_binary(B, Pos),
+ B1 = list_to_binary(lists:sublist(L, 1, Pos)),
+ B2 = list_to_binary(lists:nthtail(Pos, L)),
+ split(L, B, Pos-1);
split(_L, _B, 0) ->
ok.
-bad_split(doc) -> "Tries split_binary/2 with bad arguments.";
-bad_split(suite) -> [];
+%% Tries split_binary/2 with bad arguments.
bad_split(Config) when is_list(Config) ->
GoodBin = list_to_binary([1,2,3]),
- ?line bad_split(GoodBin, -1),
- ?line bad_split(GoodBin, 4),
- ?line bad_split(GoodBin, a),
+ bad_split(GoodBin, -1),
+ bad_split(GoodBin, 4),
+ bad_split(GoodBin, a),
%% Funs are a kind of binaries.
- ?line bad_split(fun(_X) -> 1 end, 1),
+ bad_split(fun(_X) -> 1 end, 1),
ok.
bad_split(Bin, Pos) ->
{'EXIT',{badarg,_}} = (catch split_binary(Bin, Pos)).
-t_hash(doc) -> "Test hash/2 with different type of binaries.";
+%% Test hash/2 with different type of binaries.
t_hash(Config) when is_list(Config) ->
test_hash([]),
test_hash([253]),
@@ -396,36 +392,34 @@ test_hash(List) ->
Bin = list_to_binary(List),
Sbin = make_sub_binary(List),
Unaligned = make_unaligned_sub_binary(Sbin),
- ?line test_hash_1(Bin, Sbin, Unaligned, fun erlang:hash/2),
- ?line test_hash_1(Bin, Sbin, Unaligned, fun erlang:phash/2),
- ?line test_hash_1(Bin, Sbin, Unaligned, fun erlang:phash2/2).
+ test_hash_1(Bin, Sbin, Unaligned, fun erlang:hash/2),
+ test_hash_1(Bin, Sbin, Unaligned, fun erlang:phash/2),
+ test_hash_1(Bin, Sbin, Unaligned, fun erlang:phash2/2).
test_hash_1(Bin, Sbin, Unaligned, Hash) when is_function(Hash, 2) ->
N = 65535,
case {Hash(Bin, N),Hash(Sbin, N),Hash(Unaligned, N)} of
{H,H,H} -> ok;
{H1,H2,H3} ->
- io:format("Different hash values: ~p, ~p, ~p\n", [H1,H2,H3]),
- ?t:fail()
+ ct:fail("Different hash values: ~p, ~p, ~p\n", [H1,H2,H3])
end.
-bad_size(doc) -> "Try bad arguments to size/1.";
-bad_size(suite) -> [];
+%% Try bad arguments to size/1.
bad_size(Config) when is_list(Config) ->
- ?line {'EXIT',{badarg,_}} = (catch size(fun(X) -> X + 33 end)),
+ {'EXIT',{badarg,_}} = (catch size(fun(X) -> X + 33 end)),
ok.
bad_term_to_binary(Config) when is_list(Config) ->
T = id({a,b,c}),
- ?line {'EXIT',{badarg,_}} = (catch term_to_binary(T, not_a_list)),
- ?line {'EXIT',{badarg,_}} = (catch term_to_binary(T, [blurf])),
- ?line {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed,-1}])),
- ?line {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed,10}])),
- ?line {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed,cucumber}])),
- ?line {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed}])),
- ?line {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{version,1}|bad_tail])),
- ?line {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{minor_version,-1}])),
- ?line {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{minor_version,x}])),
+ {'EXIT',{badarg,_}} = (catch term_to_binary(T, not_a_list)),
+ {'EXIT',{badarg,_}} = (catch term_to_binary(T, [blurf])),
+ {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed,-1}])),
+ {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed,10}])),
+ {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed,cucumber}])),
+ {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed}])),
+ {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{version,1}|bad_tail])),
+ {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{minor_version,-1}])),
+ {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{minor_version,x}])),
ok.
@@ -464,7 +458,7 @@ terms(Config) when is_list(Config) ->
UnalignedC = make_unaligned_sub_binary(BinC),
Term = binary_to_term_stress(UnalignedC)
end,
- ?line test_terms(TestFun),
+ test_terms(TestFun),
ok.
terms_compression_levels(Term, UncompressedSz, Level) when Level < 10 ->
@@ -476,7 +470,7 @@ terms_compression_levels(Term, UncompressedSz, Level) when Level < 10 ->
terms_compression_levels(_, _, _) -> ok.
terms_float(Config) when is_list(Config) ->
- ?line test_floats(fun(Term) ->
+ test_floats(fun(Term) ->
Bin0 = term_to_binary(Term, [{minor_version,0}]),
Term = binary_to_term_stress(Bin0),
Bin1 = term_to_binary(Term),
@@ -492,22 +486,21 @@ terms_float(Config) when is_list(Config) ->
float_middle_endian(Config) when is_list(Config) ->
%% Testing for roundtrip is not enough.
- ?line <<131,70,63,240,0,0,0,0,0,0>> = term_to_binary(1.0, [{minor_version,1}]),
- ?line 1.0 = binary_to_term_stress(<<131,70,63,240,0,0,0,0,0,0>>).
+ <<131,70,63,240,0,0,0,0,0,0>> = term_to_binary(1.0, [{minor_version,1}]),
+ 1.0 = binary_to_term_stress(<<131,70,63,240,0,0,0,0,0,0>>).
external_size(Config) when is_list(Config) ->
%% Build a term whose external size only fits in a big num (on 32-bit CPU).
- ?line external_size_1(16#11111111111111117777777777777777888889999, 0, 16#FFFFFFF),
+ external_size_1(16#11111111111111117777777777777777888889999, 0, 16#FFFFFFF),
%% Test that the same binary aligned and unaligned has the same external size.
- ?line Bin = iolist_to_binary([1,2,3,96]),
- ?line Unaligned = make_unaligned_sub_binary(Bin),
+ Bin = iolist_to_binary([1,2,3,96]),
+ Unaligned = make_unaligned_sub_binary(Bin),
case {erlang:external_size(Bin),erlang:external_size(Unaligned)} of
{X,X} -> ok;
{Sz1,Sz2} ->
- io:format(" Aligned size: ~p\n", [Sz1]),
- io:format("Unaligned size: ~p\n", [Sz2]),
- ?line ?t:fail()
+ ct:fail(" Aligned size: ~p\n"
+ "Unaligned size: ~p\n", [Sz1,Sz2])
end,
true = (erlang:external_size(Bin) =:= erlang:external_size(Bin, [{minor_version, 1}])),
true = (erlang:external_size(Unaligned) =:= erlang:external_size(Unaligned, [{minor_version, 1}])).
@@ -521,30 +514,29 @@ external_size_1(Term, Size0, Limit) when Size0 < Limit ->
external_size_1(_, _, _) -> ok.
t_iolist_size(Config) when is_list(Config) ->
- ?line Seed = {erlang:monotonic_time(),
- erlang:time_offset(),
- erlang:unique_integer([positive])},
- ?line io:format("Seed: ~p", [Seed]),
- ?line random:seed(Seed),
- ?line Base = <<0:(1 bsl 20)/unit:8>>,
- ?line Powers = [1 bsl N || N <- lists:seq(2, 37)],
- ?line Sizes0 = [[N - random:uniform(N div 2),
- lists:seq(N-2, N+2),
- N+N div 2,
- N + random:uniform(N div 2)] ||
- N <- Powers],
+ _ = rand:uniform(), %Seed generator
+ io:format("Seed: ~p", [rand:export_seed()]),
+
+ Base = <<0:(1 bsl 20)/unit:8>>,
+ Powers = [1 bsl N || N <- lists:seq(2, 37)],
+ Sizes0 = [[N - rand:uniform(N div 2),
+ lists:seq(N-2, N+2),
+ N+N div 2,
+ N + rand:uniform(N div 2)] ||
+ N <- Powers],
+
%% Test sizes around 1^32 more thoroughly.
FourGigs = 1 bsl 32,
- ?line Sizes1 = [FourGigs+N || N <- lists:seq(-8, 40)] ++ Sizes0,
- ?line Sizes2 = lists:flatten(Sizes1),
- ?line Sizes = lists:usort(Sizes2),
+ Sizes1 = [FourGigs+N || N <- lists:seq(-8, 40)] ++ Sizes0,
+ Sizes2 = lists:flatten(Sizes1),
+ Sizes = lists:usort(Sizes2),
io:format("~p sizes:", [length(Sizes)]),
io:format("~p\n", [Sizes]),
- ?line [Sz = iolist_size(build_iolist(Sz, Base)) || Sz <- Sizes],
+ _ = [Sz = iolist_size(build_iolist(Sz, Base)) || Sz <- Sizes],
ok.
build_iolist(N, Base) when N < 16 ->
- case random:uniform(3) of
+ case rand:uniform(3) of
1 ->
<<Bin:N/binary,_/binary>> = Base,
Bin;
@@ -552,7 +544,7 @@ build_iolist(N, Base) when N < 16 ->
lists:seq(1, N)
end;
build_iolist(N, Base) when N =< byte_size(Base) ->
- case random:uniform(3) of
+ case rand:uniform(3) of
1 ->
<<Bin:N/binary,_/binary>> = Base,
Bin;
@@ -570,7 +562,7 @@ build_iolist(N, Base) when N =< byte_size(Base) ->
end
end;
build_iolist(N0, Base) ->
- Small = random:uniform(15),
+ Small = rand:uniform(15),
Seq = lists:seq(1, Small),
N = N0 - Small,
case N rem 2 of
@@ -583,33 +575,32 @@ build_iolist(N0, Base) ->
end.
-bad_binary_to_term_2(doc) -> "OTP-4053.";
-bad_binary_to_term_2(suite) -> [];
+%% OTP-4053
bad_binary_to_term_2(Config) when is_list(Config) ->
- ?line {ok, N} = test_server:start_node(plopp, slave, []),
- ?line R = rpc:call(N, erlang, binary_to_term, [<<131,111,255,255,255,0>>]),
- ?line case R of
+ {ok, N} = test_server:start_node(plopp, slave, []),
+ R = rpc:call(N, erlang, binary_to_term, [<<131,111,255,255,255,0>>]),
+ case R of
{badrpc, {'EXIT', _}} ->
ok;
_Other ->
- test_server:fail({rpcresult, R})
+ ct:fail({rpcresult, R})
end,
- ?line test_server:stop_node(N),
+ test_server:stop_node(N),
ok.
-bad_binary_to_term(doc) -> "Try bad input to binary_to_term/1.";
+%% Try bad input to binary_to_term/1.
bad_binary_to_term(Config) when is_list(Config) ->
- ?line bad_bin_to_term(an_atom),
- ?line bad_bin_to_term({an,tuple}),
- ?line bad_bin_to_term({a,list}),
- ?line bad_bin_to_term(fun() -> self() end),
- ?line bad_bin_to_term(fun(X) -> 42*X end),
- ?line bad_bin_to_term(fun(X, Y) -> {X,Y} end),
- ?line bad_bin_to_term(fun(X, Y, Z) -> {X,Y,Z} end),
- ?line bad_bin_to_term(bit_sized_binary(term_to_binary({you,should,'not',see,this,term}))),
+ bad_bin_to_term(an_atom),
+ bad_bin_to_term({an,tuple}),
+ bad_bin_to_term({a,list}),
+ bad_bin_to_term(fun() -> self() end),
+ bad_bin_to_term(fun(X) -> 42*X end),
+ bad_bin_to_term(fun(X, Y) -> {X,Y} end),
+ bad_bin_to_term(fun(X, Y, Z) -> {X,Y,Z} end),
+ bad_bin_to_term(bit_sized_binary(term_to_binary({you,should,'not',see,this,term}))),
%% Bad float.
- ?line bad_bin_to_term(<<131,70,-1:64>>),
+ bad_bin_to_term(<<131,70,-1:64>>),
ok.
bad_bin_to_term(BadBin) ->
@@ -618,25 +609,24 @@ bad_bin_to_term(BadBin) ->
bad_bin_to_term(BadBin,Opts) ->
{'EXIT',{badarg,_}} = (catch binary_to_term_stress(BadBin,Opts)).
-safe_binary_to_term2(doc) -> "Test safety options for binary_to_term/2";
+%% Test safety options for binary_to_term/2
safe_binary_to_term2(Config) when is_list(Config) ->
- ?line bad_bin_to_term(<<131,100,0,14,"undefined_atom">>, [safe]),
- ?line bad_bin_to_term(<<131,100,0,14,"other_bad_atom">>, [safe]),
+ bad_bin_to_term(<<131,100,0,14,"undefined_atom">>, [safe]),
+ bad_bin_to_term(<<131,100,0,14,"other_bad_atom">>, [safe]),
BadHostAtom = <<100,0,14,"badguy@badhost">>,
Empty = <<0,0,0,0>>,
BadRef = <<131,114,0,3,BadHostAtom/binary,0,<<0,0,0,255>>/binary,
Empty/binary,Empty/binary>>,
- ?line bad_bin_to_term(BadRef, [safe]), % good ref, with a bad atom
- ?line fullsweep_after = binary_to_term_stress(<<131,100,0,15,"fullsweep_after">>, [safe]), % should be a good atom
+ bad_bin_to_term(BadRef, [safe]), % good ref, with a bad atom
+ fullsweep_after = binary_to_term_stress(<<131,100,0,15,"fullsweep_after">>, [safe]), % should be a good atom
BadExtFun = <<131,113,100,0,4,98,108,117,101,100,0,4,109,111,111,110,97,3>>,
- ?line bad_bin_to_term(BadExtFun, [safe]),
+ bad_bin_to_term(BadExtFun, [safe]),
ok.
%% Tests bad input to binary_to_term/1.
-bad_terms(suite) -> [];
bad_terms(Config) when is_list(Config) ->
- ?line test_terms(fun corrupter/1),
+ test_terms(fun corrupter/1),
{'EXIT',{badarg,_}} = (catch binary_to_term(<<131,$M,3:32,0,11,22,33>>)),
{'EXIT',{badarg,_}} = (catch binary_to_term(<<131,$M,3:32,9,11,22,33>>)),
{'EXIT',{badarg,_}} = (catch binary_to_term(<<131,$M,0:32,1,11,22,33>>)),
@@ -669,7 +659,7 @@ corrupter(Term) ->
corrupter0(Term).
corrupter0(Term) ->
- ?line try
+ try
S = io_lib:format("About to corrupt: ~P", [Term,12]),
io:put_chars(S)
catch
@@ -677,42 +667,41 @@ corrupter0(Term) ->
io:format("About to corrupt: <<bit-level-binary:~p",
[bit_size(Term)])
end,
- ?line Bin = term_to_binary(Term),
- ?line corrupter(Bin, size(Bin)-1),
- ?line CompressedBin = term_to_binary(Term, [compressed]),
- ?line corrupter(CompressedBin, size(CompressedBin)-1).
+ Bin = term_to_binary(Term),
+ corrupter(Bin, size(Bin)-1),
+ CompressedBin = term_to_binary(Term, [compressed]),
+ corrupter(CompressedBin, size(CompressedBin)-1).
corrupter(Bin, Pos) when Pos >= 0 ->
- ?line {ShorterBin, Rest} = split_binary(Bin, Pos),
- ?line catch binary_to_term_stress(ShorterBin), %% emulator shouldn't crash
- ?line MovedBin = list_to_binary([ShorterBin]),
- ?line catch binary_to_term_stress(MovedBin), %% emulator shouldn't crash
+ {ShorterBin, Rest} = split_binary(Bin, Pos),
+ catch binary_to_term_stress(ShorterBin), %% emulator shouldn't crash
+ MovedBin = list_to_binary([ShorterBin]),
+ catch binary_to_term_stress(MovedBin), %% emulator shouldn't crash
%% Bit faults, shouldn't crash
<<Byte,Tail/binary>> = Rest,
Fun = fun(M) -> FaultyByte = Byte bxor M,
catch binary_to_term_stress(<<ShorterBin/binary,
FaultyByte, Tail/binary>>) end,
- ?line lists:foreach(Fun,[1,2,4,8,16,32,64,128,255]),
- ?line corrupter(Bin, Pos-1);
+ lists:foreach(Fun,[1,2,4,8,16,32,64,128,255]),
+ corrupter(Bin, Pos-1);
corrupter(_Bin, _) ->
ok.
-more_bad_terms(suite) -> [];
more_bad_terms(Config) when is_list(Config) ->
- ?line Data = ?config(data_dir, Config),
- ?line BadFile = filename:join(Data, "bad_binary"),
- ?line ok = io:format("File: ~s\n", [BadFile]),
- ?line case file:read_file(BadFile) of
+ Data = proplists:get_value(data_dir, Config),
+ BadFile = filename:join(Data, "bad_binary"),
+ ok = io:format("File: ~s\n", [BadFile]),
+ case file:read_file(BadFile) of
{ok,Bin} ->
- ?line {'EXIT',{badarg,_}} = (catch binary_to_term_stress(Bin)),
+ {'EXIT',{badarg,_}} = (catch binary_to_term_stress(Bin)),
ok;
Other ->
- ?line ?t:fail(Other)
+ ct:fail(Other)
end.
otp_5484(Config) when is_list(Config) ->
- ?line {'EXIT',_} =
+ {'EXIT',_} =
(catch
binary_to_term_stress(
<<131,
@@ -725,7 +714,7 @@ otp_5484(Config) when is_list(Config) ->
255,
106>>)),
- ?line {'EXIT',_} =
+ {'EXIT',_} =
(catch
binary_to_term_stress(
<<131,
@@ -737,13 +726,13 @@ otp_5484(Config) when is_list(Config) ->
2,
106>>)),
- ?line {'EXIT',_} =
+ {'EXIT',_} =
(catch
binary_to_term_stress(
%% A old-type fun in a list containing a bad creator pid.
<<131,108,0,0,0,1,117,0,0,0,0,103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,115,116,255,255,0,25,255,0,0,0,0,100,0,1,116,97,0,98,6,142,121,72,106>>)),
- ?line {'EXIT',_} =
+ {'EXIT',_} =
(catch
binary_to_term_stress(
%% A new-type fun in a list containing a bad creator pid.
@@ -755,7 +744,7 @@ otp_5484(Config) when is_list(Config) ->
106, %[] instead of an atom.
0,0,0,27,0,0,0,0,0,106>>)),
- ?line {'EXIT',_} =
+ {'EXIT',_} =
(catch
binary_to_term_stress(
%% A new-type fun in a list containing a bad module.
@@ -766,7 +755,7 @@ otp_5484(Config) when is_list(Config) ->
107,0,1,64, %String instead of atom (same length).
97,0,98,6,64,82,230,103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,115,116,0,0,0,48,0,0,0,0,0,97,42,97,7,106>>)),
- ?line {'EXIT',_} =
+ {'EXIT',_} =
(catch
binary_to_term_stress(
%% A new-type fun in a list containing a bad index.
@@ -778,7 +767,7 @@ otp_5484(Config) when is_list(Config) ->
104,0, %Tuple {} instead of integer.
98,6,64,82,230,103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,115,116,0,0,0,48,0,0,0,0,0,97,42,97,7,106>>)),
- ?line {'EXIT',_} =
+ {'EXIT',_} =
(catch
binary_to_term_stress(
%% A new-type fun in a list containing a bad unique value.
@@ -792,46 +781,46 @@ otp_5484(Config) when is_list(Config) ->
103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,115,116,0,0,0,48,0,0,0,0,0,97,42,97,7,106>>)),
%% An absurdly large atom.
- ?line {'EXIT',_} =
+ {'EXIT',_} =
(catch binary_to_term_stress(iolist_to_binary([<<131,100,65000:16>>|
lists:duplicate(65000, 42)]))),
%% Longer than 255 characters.
- ?line {'EXIT',_} =
+ {'EXIT',_} =
(catch binary_to_term_stress(iolist_to_binary([<<131,100,256:16>>|
lists:duplicate(256, 42)]))),
%% OTP-7218. Thanks to Matthew Dempsky. Also make sure that we
%% cover the other error cases for external funs (EXPORT_EXT).
- ?line {'EXIT',_} =
+ {'EXIT',_} =
(catch binary_to_term_stress(
<<131,
113, %EXPORT_EXP
97,13, %Integer: 13
97,13, %Integer: 13
97,13>>)), %Integer: 13
- ?line {'EXIT',_} =
+ {'EXIT',_} =
(catch binary_to_term_stress(
<<131,
113, %EXPORT_EXP
100,0,1,64, %Atom: '@'
97,13, %Integer: 13
97,13>>)), %Integer: 13
- ?line {'EXIT',_} =
+ {'EXIT',_} =
(catch binary_to_term_stress(
<<131,
113, %EXPORT_EXP
100,0,1,64, %Atom: '@'
100,0,1,64, %Atom: '@'
106>>)), %NIL
- ?line {'EXIT',_} =
+ {'EXIT',_} =
(catch binary_to_term_stress(
<<131,
113, %EXPORT_EXP
100,0,1,64, %Atom: '@'
100,0,1,64, %Atom: '@'
98,255,255,255,255>>)), %Integer: -1
- ?line {'EXIT',_} =
+ {'EXIT',_} =
(catch binary_to_term_stress(
<<131,
113, %EXPORT_EXP
@@ -840,7 +829,7 @@ otp_5484(Config) when is_list(Config) ->
113,97,13,97,13,97,13>>)), %fun 13:13/13
%% Bad funs.
- ?line {'EXIT',_} = (catch binary_to_term_stress(fake_fun(0, lists:seq(0, 256)))),
+ {'EXIT',_} = (catch binary_to_term_stress(fake_fun(0, lists:seq(0, 256)))),
ok.
fake_fun(Arity, Env0) ->
@@ -863,9 +852,9 @@ fake_fun(Arity, Env0) ->
%% More bad terms submitted by Matthias Lang.
otp_5933(Config) when is_list(Config) ->
- ?line try_bad_lengths(<<131,$m>>), %binary
- ?line try_bad_lengths(<<131,$n>>), %bignum
- ?line try_bad_lengths(<<131,$o>>), %huge bignum
+ try_bad_lengths(<<131,$m>>), %binary
+ try_bad_lengths(<<131,$n>>), %bignum
+ try_bad_lengths(<<131,$o>>), %huge bignum
ok.
try_bad_lengths(B) ->
@@ -884,7 +873,7 @@ otp_6817(Config) when is_list(Config) ->
%% Floats are only validated when the heap fragment has been allocated.
BadFloat = <<131,99,53,46,48,$X,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,101,45,48,49,0,0,0,0,0>>,
- ?line otp_6817_try_bin(BadFloat),
+ otp_6817_try_bin(BadFloat),
%% {Binary,BadFloat}: When the error in float is discovered, a refc-binary
%% has been allocated and the list of refc-binaries goes through the
@@ -904,7 +893,7 @@ otp_6817(Config) when is_list(Config) ->
230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,
249,250,251,252,253,254,255,99,51,46,49,52,$B,$l,$u,$r,$f,48,48,48,48,48,48,
48,48,49,50,52,51,52,101,43,48,48,0,0,0,0,0>>,
- ?line otp_6817_try_bin(BinAndFloat),
+ otp_6817_try_bin(BinAndFloat),
%% {Fun,BadFloat}
FunAndFloat =
@@ -912,14 +901,14 @@ otp_6817(Config) when is_list(Config) ->
71,8,0,0,0,0,0,0,0,0,100,0,1,116,97,0,98,5,175,169,123,103,100,0,13,110,111,
110,111,100,101,64,110,111,104,111,115,116,0,0,0,41,0,0,0,0,0,99,50,46,55,48,
$Y,57,57,57,57,57,57,57,57,57,57,57,57,57,54,52,52,55,101,43,48,48,0,0,0,0,0>>,
- ?line otp_6817_try_bin(FunAndFloat),
+ otp_6817_try_bin(FunAndFloat),
%% [ExternalPid|BadFloat]
ExtPidAndFloat =
<<131,108,0,0,0,1,103,100,0,13,107,97,108,108,101,64,115,116,114,105,100,101,
114,0,0,0,36,0,0,0,0,2,99,48,46,$@,48,48,48,48,48,48,48,48,48,48,48,48,48,48,
48,48,48,48,48,101,43,48,48,0,0,0,0,0>>,
- ?line otp_6817_try_bin(ExtPidAndFloat),
+ otp_6817_try_bin(ExtPidAndFloat),
ok.
otp_6817_try_bin(Bin) ->
@@ -937,8 +926,7 @@ otp_6817_try_bin(Bin) ->
%% Will crash if the bug is present.
erlang:garbage_collect().
-otp_8117(doc) -> "Some bugs in binary_to_term when 32-bit integers are negative.";
-otp_8117(suite) -> [];
+%% Some bugs in binary_to_term when 32-bit integers are negative.
otp_8117(Config) when is_list(Config) ->
[otp_8117_do(Op,-(1 bsl N)) || Op <- ['fun',named_fun,list,tuple],
N <- lists:seq(0,31)],
@@ -947,23 +935,22 @@ otp_8117(Config) when is_list(Config) ->
otp_8117_do('fun',Neg) ->
% Fun with negative num_free
FunBin = term_to_binary(fun() -> ok end),
- ?line <<B1:27/binary,_NumFree:32,Rest/binary>> = FunBin,
- ?line bad_bin_to_term(<<B1/binary,Neg:32,Rest/binary>>);
+ <<B1:27/binary,_NumFree:32,Rest/binary>> = FunBin,
+ bad_bin_to_term(<<B1/binary,Neg:32,Rest/binary>>);
otp_8117_do(named_fun,Neg) ->
% Named fun with negative num_free
FunBin = term_to_binary(fun F() -> F end),
- ?line <<B1:27/binary,_NumFree:32,Rest/binary>> = FunBin,
- ?line bad_bin_to_term(<<B1/binary,Neg:32,Rest/binary>>);
+ <<B1:27/binary,_NumFree:32,Rest/binary>> = FunBin,
+ bad_bin_to_term(<<B1/binary,Neg:32,Rest/binary>>);
otp_8117_do(list,Neg) ->
%% List with negative length
- ?line bad_bin_to_term(<<131,104,2,108,Neg:32,97,11,104,1,97,12,97,13,106,97,14>>);
+ bad_bin_to_term(<<131,104,2,108,Neg:32,97,11,104,1,97,12,97,13,106,97,14>>);
otp_8117_do(tuple,Neg) ->
%% Tuple with negative arity
- ?line bad_bin_to_term(<<131,104,2,105,Neg:32,97,11,97,12,97,13,97,14>>).
+ bad_bin_to_term(<<131,104,2,105,Neg:32,97,11,97,12,97,13,97,14>>).
-ordering(doc) -> "Tests ordering of binaries.";
-ordering(suite) -> [];
+%% Tests ordering of binaries.
ordering(Config) when is_list(Config) ->
B1 = list_to_binary([7,8,9]),
B2 = make_sub_binary([1,2,3,4]),
@@ -972,54 +959,54 @@ ordering(Config) when is_list(Config) ->
%% From R8 binaries are compared as strings.
- ?line false = B1 == B2,
- ?line false = B1 =:= B2,
- ?line true = B1 /= B2,
- ?line true = B1 =/= B2,
+ false = B1 == B2,
+ false = B1 =:= B2,
+ true = B1 /= B2,
+ true = B1 =/= B2,
- ?line true = B1 > B2,
- ?line true = B2 < B3,
- ?line true = B2 =< B1,
- ?line true = B2 =< B3,
+ true = B1 > B2,
+ true = B2 < B3,
+ true = B2 =< B1,
+ true = B2 =< B3,
- ?line true = B2 =:= Unaligned,
- ?line true = B2 == Unaligned,
- ?line true = Unaligned < B3,
- ?line true = Unaligned =< B3,
+ true = B2 =:= Unaligned,
+ true = B2 == Unaligned,
+ true = Unaligned < B3,
+ true = Unaligned =< B3,
%% Binaries are greater than all other terms.
- ?line true = B1 > 0,
- ?line true = B1 > 39827491247298471289473333333333333333333333333333,
- ?line true = B1 > -3489274937438742190467869234328742398347,
- ?line true = B1 > 3.14,
- ?line true = B1 > [],
- ?line true = B1 > [a],
- ?line true = B1 > {a},
- ?line true = B1 > self(),
- ?line true = B1 > make_ref(),
- ?line true = B1 > xxx,
- ?line true = B1 > fun() -> 1 end,
- ?line true = B1 > fun erlang:send/2,
-
- ?line Path = ?config(priv_dir, Config),
- ?line AFile = filename:join(Path, "vanilla_file"),
- ?line Port = open_port(AFile, [out]),
- ?line true = B1 > Port,
-
- ?line true = B1 >= 0,
- ?line true = B1 >= 39827491247298471289473333333333333333333333333333,
- ?line true = B1 >= -3489274937438742190467869234328742398347,
- ?line true = B1 >= 3.14,
- ?line true = B1 >= [],
- ?line true = B1 >= [a],
- ?line true = B1 >= {a},
- ?line true = B1 >= self(),
- ?line true = B1 >= make_ref(),
- ?line true = B1 >= xxx,
- ?line true = B1 >= fun() -> 1 end,
- ?line true = B1 >= fun erlang:send/2,
- ?line true = B1 >= Port,
+ true = B1 > 0,
+ true = B1 > 39827491247298471289473333333333333333333333333333,
+ true = B1 > -3489274937438742190467869234328742398347,
+ true = B1 > 3.14,
+ true = B1 > [],
+ true = B1 > [a],
+ true = B1 > {a},
+ true = B1 > self(),
+ true = B1 > make_ref(),
+ true = B1 > xxx,
+ true = B1 > fun() -> 1 end,
+ true = B1 > fun erlang:send/2,
+
+ Path = proplists:get_value(priv_dir, Config),
+ AFile = filename:join(Path, "vanilla_file"),
+ Port = open_port(AFile, [out]),
+ true = B1 > Port,
+
+ true = B1 >= 0,
+ true = B1 >= 39827491247298471289473333333333333333333333333333,
+ true = B1 >= -3489274937438742190467869234328742398347,
+ true = B1 >= 3.14,
+ true = B1 >= [],
+ true = B1 >= [a],
+ true = B1 >= {a},
+ true = B1 >= self(),
+ true = B1 >= make_ref(),
+ true = B1 >= xxx,
+ true = B1 >= fun() -> 1 end,
+ true = B1 >= fun erlang:send/2,
+ true = B1 >= Port,
ok.
@@ -1032,153 +1019,153 @@ unaligned_order(Config) when is_list(Config) ->
test_unaligned_order(I, J) ->
Align = {I,J},
io:format("~p ~p", [I,J]),
- ?line true = test_unaligned_order_1('=:=', <<1,2,3,16#AA,16#7C,4,16#5F,5,16#5A>>,
+ true = test_unaligned_order_1('=:=', <<1,2,3,16#AA,16#7C,4,16#5F,5,16#5A>>,
<<1,2,3,16#AA,16#7C,4,16#5F,5,16#5A>>,
Align),
- ?line false = test_unaligned_order_1('=/=', <<1,2,3>>, <<1,2,3>>, Align),
- ?line true = test_unaligned_order_1('==', <<4,5,6>>, <<4,5,6>>, Align),
- ?line false = test_unaligned_order_1('/=', <<1,2,3>>, <<1,2,3>>, Align),
+ false = test_unaligned_order_1('=/=', <<1,2,3>>, <<1,2,3>>, Align),
+ true = test_unaligned_order_1('==', <<4,5,6>>, <<4,5,6>>, Align),
+ false = test_unaligned_order_1('/=', <<1,2,3>>, <<1,2,3>>, Align),
- ?line true = test_unaligned_order_1('<', <<1,2>>, <<1,2,3>>, Align),
- ?line true = test_unaligned_order_1('=<', <<1,2>>, <<1,2,3>>, Align),
- ?line true = test_unaligned_order_1('=<', <<1,2,7,8>>, <<1,2,7,8>>, Align),
+ true = test_unaligned_order_1('<', <<1,2>>, <<1,2,3>>, Align),
+ true = test_unaligned_order_1('=<', <<1,2>>, <<1,2,3>>, Align),
+ true = test_unaligned_order_1('=<', <<1,2,7,8>>, <<1,2,7,8>>, Align),
ok.
test_unaligned_order_1(Op, A, B, {Aa,Ba}) ->
erlang:Op(unaligned_sub_bin(A, Aa), unaligned_sub_bin(B, Ba)).
test_terms(Test_Func) ->
- ?line Test_Func(atom),
- ?line Test_Func(''),
- ?line Test_Func('a'),
- ?line Test_Func('ab'),
- ?line Test_Func('abc'),
- ?line Test_Func('abcd'),
- ?line Test_Func('abcde'),
- ?line Test_Func('abcdef'),
- ?line Test_Func('abcdefg'),
- ?line Test_Func('abcdefgh'),
-
- ?line Test_Func(fun() -> ok end),
+ Test_Func(atom),
+ Test_Func(''),
+ Test_Func('a'),
+ Test_Func('ab'),
+ Test_Func('abc'),
+ Test_Func('abcd'),
+ Test_Func('abcde'),
+ Test_Func('abcdef'),
+ Test_Func('abcdefg'),
+ Test_Func('abcdefgh'),
+
+ Test_Func(fun() -> ok end),
X = id([a,{b,c},c]),
Y = id({x,y,z}),
Z = id(1 bsl 8*257),
- ?line Test_Func(fun() -> X end),
- ?line Test_Func(fun() -> {X,Y} end),
- ?line Test_Func([fun() -> {X,Y,Z} end,
+ Test_Func(fun() -> X end),
+ Test_Func(fun() -> {X,Y} end),
+ Test_Func([fun() -> {X,Y,Z} end,
fun() -> {Z,X,Y} end,
fun() -> {Y,Z,X} end]),
- ?line Test_Func({trace_ts,{even_bigger,{some_data,fun() -> ok end}},{1,2,3}}),
- ?line Test_Func({trace_ts,{even_bigger,{some_data,<<1,2,3,4,5,6,7,8,9,10>>}},
+ Test_Func({trace_ts,{even_bigger,{some_data,fun() -> ok end}},{1,2,3}}),
+ Test_Func({trace_ts,{even_bigger,{some_data,<<1,2,3,4,5,6,7,8,9,10>>}},
{1,2,3}}),
- ?line Test_Func(1),
- ?line Test_Func(42),
- ?line Test_Func(-23),
- ?line Test_Func(256),
- ?line Test_Func(25555),
- ?line Test_Func(-3333),
+ Test_Func(1),
+ Test_Func(42),
+ Test_Func(-23),
+ Test_Func(256),
+ Test_Func(25555),
+ Test_Func(-3333),
- ?line Test_Func(1.0),
+ Test_Func(1.0),
- ?line Test_Func(183749783987483978498378478393874),
- ?line Test_Func(-37894183749783987483978498378478393874),
+ Test_Func(183749783987483978498378478393874),
+ Test_Func(-37894183749783987483978498378478393874),
Very_Big = very_big_num(),
- ?line Test_Func(Very_Big),
- ?line Test_Func(-Very_Big+1),
-
- ?line Test_Func([]),
- ?line Test_Func("abcdef"),
- ?line Test_Func([a, b, 1, 2]),
- ?line Test_Func([a|b]),
-
- ?line Test_Func({}),
- ?line Test_Func({1}),
- ?line Test_Func({a, b}),
- ?line Test_Func({a, b, c}),
- ?line Test_Func(list_to_tuple(lists:seq(0, 255))),
- ?line Test_Func(list_to_tuple(lists:seq(0, 256))),
-
- ?line Test_Func(make_ref()),
- ?line Test_Func([make_ref(), make_ref()]),
-
- ?line Test_Func(make_port()),
-
- ?line Test_Func(make_pid()),
-
- ?line Test_Func(Bin0 = list_to_binary(lists:seq(0, 14))),
- ?line Test_Func(Bin1 = list_to_binary(lists:seq(0, ?heap_binary_size))),
- ?line Test_Func(Bin2 = list_to_binary(lists:seq(0, ?heap_binary_size+1))),
- ?line Test_Func(Bin3 = list_to_binary(lists:seq(0, 255))),
-
- ?line Test_Func(make_unaligned_sub_binary(Bin0)),
- ?line Test_Func(make_unaligned_sub_binary(Bin1)),
- ?line Test_Func(make_unaligned_sub_binary(Bin2)),
- ?line Test_Func(make_unaligned_sub_binary(Bin3)),
-
- ?line Test_Func(make_sub_binary(lists:seq(42, 43))),
- ?line Test_Func(make_sub_binary([42,43,44])),
- ?line Test_Func(make_sub_binary([42,43,44,45])),
- ?line Test_Func(make_sub_binary([42,43,44,45,46])),
- ?line Test_Func(make_sub_binary([42,43,44,45,46,47])),
- ?line Test_Func(make_sub_binary([42,43,44,45,46,47,48])),
- ?line Test_Func(make_sub_binary(lists:seq(42, 49))),
- ?line Test_Func(make_sub_binary(lists:seq(0, 14))),
- ?line Test_Func(make_sub_binary(lists:seq(0, ?heap_binary_size))),
- ?line Test_Func(make_sub_binary(lists:seq(0, ?heap_binary_size+1))),
- ?line Test_Func(make_sub_binary(lists:seq(0, 255))),
-
- ?line Test_Func(make_unaligned_sub_binary(lists:seq(42, 43))),
- ?line Test_Func(make_unaligned_sub_binary([42,43,44])),
- ?line Test_Func(make_unaligned_sub_binary([42,43,44,45])),
- ?line Test_Func(make_unaligned_sub_binary([42,43,44,45,46])),
- ?line Test_Func(make_unaligned_sub_binary([42,43,44,45,46,47])),
- ?line Test_Func(make_unaligned_sub_binary([42,43,44,45,46,47,48])),
- ?line Test_Func(make_unaligned_sub_binary(lists:seq(42, 49))),
- ?line Test_Func(make_unaligned_sub_binary(lists:seq(0, 14))),
- ?line Test_Func(make_unaligned_sub_binary(lists:seq(0, ?heap_binary_size))),
- ?line Test_Func(make_unaligned_sub_binary(lists:seq(0, ?heap_binary_size+1))),
- ?line Test_Func(make_unaligned_sub_binary(lists:seq(0, 255))),
+ Test_Func(Very_Big),
+ Test_Func(-Very_Big+1),
+
+ Test_Func([]),
+ Test_Func("abcdef"),
+ Test_Func([a, b, 1, 2]),
+ Test_Func([a|b]),
+
+ Test_Func({}),
+ Test_Func({1}),
+ Test_Func({a, b}),
+ Test_Func({a, b, c}),
+ Test_Func(list_to_tuple(lists:seq(0, 255))),
+ Test_Func(list_to_tuple(lists:seq(0, 256))),
+
+ Test_Func(make_ref()),
+ Test_Func([make_ref(), make_ref()]),
+
+ Test_Func(make_port()),
+
+ Test_Func(make_pid()),
+
+ Test_Func(Bin0 = list_to_binary(lists:seq(0, 14))),
+ Test_Func(Bin1 = list_to_binary(lists:seq(0, ?heap_binary_size))),
+ Test_Func(Bin2 = list_to_binary(lists:seq(0, ?heap_binary_size+1))),
+ Test_Func(Bin3 = list_to_binary(lists:seq(0, 255))),
+
+ Test_Func(make_unaligned_sub_binary(Bin0)),
+ Test_Func(make_unaligned_sub_binary(Bin1)),
+ Test_Func(make_unaligned_sub_binary(Bin2)),
+ Test_Func(make_unaligned_sub_binary(Bin3)),
+
+ Test_Func(make_sub_binary(lists:seq(42, 43))),
+ Test_Func(make_sub_binary([42,43,44])),
+ Test_Func(make_sub_binary([42,43,44,45])),
+ Test_Func(make_sub_binary([42,43,44,45,46])),
+ Test_Func(make_sub_binary([42,43,44,45,46,47])),
+ Test_Func(make_sub_binary([42,43,44,45,46,47,48])),
+ Test_Func(make_sub_binary(lists:seq(42, 49))),
+ Test_Func(make_sub_binary(lists:seq(0, 14))),
+ Test_Func(make_sub_binary(lists:seq(0, ?heap_binary_size))),
+ Test_Func(make_sub_binary(lists:seq(0, ?heap_binary_size+1))),
+ Test_Func(make_sub_binary(lists:seq(0, 255))),
+
+ Test_Func(make_unaligned_sub_binary(lists:seq(42, 43))),
+ Test_Func(make_unaligned_sub_binary([42,43,44])),
+ Test_Func(make_unaligned_sub_binary([42,43,44,45])),
+ Test_Func(make_unaligned_sub_binary([42,43,44,45,46])),
+ Test_Func(make_unaligned_sub_binary([42,43,44,45,46,47])),
+ Test_Func(make_unaligned_sub_binary([42,43,44,45,46,47,48])),
+ Test_Func(make_unaligned_sub_binary(lists:seq(42, 49))),
+ Test_Func(make_unaligned_sub_binary(lists:seq(0, 14))),
+ Test_Func(make_unaligned_sub_binary(lists:seq(0, ?heap_binary_size))),
+ Test_Func(make_unaligned_sub_binary(lists:seq(0, ?heap_binary_size+1))),
+ Test_Func(make_unaligned_sub_binary(lists:seq(0, 255))),
%% Bit level binaries.
- ?line Test_Func(<<1:1>>),
- ?line Test_Func(<<2:2>>),
- ?line Test_Func(<<42:10>>),
- ?line Test_Func(list_to_bitstring([<<5:6>>|lists:seq(0, 255)])),
+ Test_Func(<<1:1>>),
+ Test_Func(<<2:2>>),
+ Test_Func(<<42:10>>),
+ Test_Func(list_to_bitstring([<<5:6>>|lists:seq(0, 255)])),
- ?line Test_Func(F = fun(A) -> 42*A end),
- ?line Test_Func(lists:duplicate(32, F)),
+ Test_Func(F = fun(A) -> 42*A end),
+ Test_Func(lists:duplicate(32, F)),
- ?line Test_Func(FF = fun binary_SUITE:all/0),
- ?line Test_Func(lists:duplicate(32, FF)),
+ Test_Func(FF = fun binary_SUITE:all/0),
+ Test_Func(lists:duplicate(32, FF)),
ok.
test_floats(Test_Func) ->
- ?line Test_Func(5.5),
- ?line Test_Func(-15.32),
- ?line Test_Func(1.2435e25),
- ?line Test_Func(1.2333e-20),
- ?line Test_Func(199.0e+15),
+ Test_Func(5.5),
+ Test_Func(-15.32),
+ Test_Func(1.2435e25),
+ Test_Func(1.2333e-20),
+ Test_Func(199.0e+15),
ok.
very_big_num() ->
very_big_num(33, 1).
very_big_num(Left, Result) when Left > 0 ->
- ?line very_big_num(Left-1, Result*256);
+ very_big_num(Left-1, Result*256);
very_big_num(0, Result) ->
- ?line Result.
+ Result.
make_port() ->
- ?line open_port({spawn, efile}, [eof]).
+ open_port({spawn, efile}, [eof]).
make_pid() ->
- ?line spawn_link(?MODULE, sleeper, []).
+ spawn_link(?MODULE, sleeper, []).
sleeper() ->
- ?line receive after infinity -> ok end.
+ receive after infinity -> ok end.
%% Test that binaries are garbage collected properly.
@@ -1218,7 +1205,7 @@ gc_test1(Pid) ->
receive
{Pid,done} -> ok
after 10000 ->
- ?line ?t:fail()
+ ct:fail("timeout")
end.
%% Like split binary, but returns REFC binaries. Only useful for gc_test/1.
@@ -1237,7 +1224,7 @@ gc() ->
gc1() -> ok.
bit_sized_binary_sizes(Config) when is_list(Config) ->
- ?line [bsbs_1(A) || A <- lists:seq(1, 8)],
+ [bsbs_1(A) || A <- lists:seq(1, 8)],
ok.
bsbs_1(A) ->
@@ -1279,13 +1266,13 @@ obsolete_funs(Config) when is_list(Config) ->
X = id({1,2,3}),
Y = id([a,b,c,d]),
Z = id({x,y,z}),
- ?line obsolete_fun(fun() -> ok end),
- ?line obsolete_fun(fun() -> X end),
- ?line obsolete_fun(fun(A) -> {A,X} end),
- ?line obsolete_fun(fun() -> {X,Y} end),
- ?line obsolete_fun(fun() -> {X,Y,Z} end),
+ obsolete_fun(fun() -> ok end),
+ obsolete_fun(fun() -> X end),
+ obsolete_fun(fun(A) -> {A,X} end),
+ obsolete_fun(fun() -> {X,Y} end),
+ obsolete_fun(fun() -> {X,Y,Z} end),
- ?line obsolete_fun(fun ?MODULE:all/1),
+ obsolete_fun(fun ?MODULE:all/1),
erts_debug:set_internal_state(available_internal_state, false),
ok.
@@ -1312,41 +1299,41 @@ no_fun_roundtrip(Term) ->
%% but recognized by binary_to_term/1.
robustness(Config) when is_list(Config) ->
- ?line [] = binary_to_term_stress(<<131,107,0,0>>), %Empty string.
- ?line [] = binary_to_term_stress(<<131,108,0,0,0,0,106>>), %Zero-length list.
+ [] = binary_to_term_stress(<<131,107,0,0>>), %Empty string.
+ [] = binary_to_term_stress(<<131,108,0,0,0,0,106>>), %Zero-length list.
%% {[],a} where [] is a zero-length list.
- ?line {[],a} = binary_to_term_stress(<<131,104,2,108,0,0,0,0,106,100,0,1,97>>),
+ {[],a} = binary_to_term_stress(<<131,104,2,108,0,0,0,0,106,100,0,1,97>>),
%% {42,a} where 42 is a zero-length list with 42 in the tail.
- ?line {42,a} = binary_to_term_stress(<<131,104,2,108,0,0,0,0,97,42,100,0,1,97>>),
+ {42,a} = binary_to_term_stress(<<131,104,2,108,0,0,0,0,97,42,100,0,1,97>>),
%% {{x,y},a} where {x,y} is a zero-length list with {x,y} in the tail.
- ?line {{x,y},a} = binary_to_term_stress(<<131,104,2,108,0,0,0,0,
+ {{x,y},a} = binary_to_term_stress(<<131,104,2,108,0,0,0,0,
104,2,100,0,1,120,100,0,1,
121,100,0,1,97>>),
%% Bignums fitting in 32 bits.
- ?line 16#7FFFFFFF = binary_to_term_stress(<<131,98,127,255,255,255>>),
- ?line -1 = binary_to_term_stress(<<131,98,255,255,255,255>>),
+ 16#7FFFFFFF = binary_to_term_stress(<<131,98,127,255,255,255>>),
+ -1 = binary_to_term_stress(<<131,98,255,255,255,255>>),
ok.
%% OTP-8180: Test several terms that have been known to crash the emulator.
%% (Thanks to Scott Lystig Fritchie.)
otp_8180(Config) when is_list(Config) ->
- ?line Data = ?config(data_dir, Config),
- ?line Wc = filename:join(Data, "zzz.*"),
+ Data = proplists:get_value(data_dir, Config),
+ Wc = filename:join(Data, "zzz.*"),
Files = filelib:wildcard(Wc),
[run_otp_8180(F) || F <- Files],
ok.
run_otp_8180(Name) ->
io:format("~s", [Name]),
- ?line {ok,Bins} = file:consult(Name),
+ {ok,Bins} = file:consult(Name),
[begin
io:format("~p\n", [Bin]),
- ?line {'EXIT',{badarg,_}} = (catch binary_to_term_stress(Bin))
+ {'EXIT',{badarg,_}} = (catch binary_to_term_stress(Bin))
end || Bin <- Bins],
ok.
@@ -1518,7 +1505,7 @@ cmp_old_impl(Config) when is_list(Config) ->
false ->
{skipped, "No "++Rel++" available"};
true ->
- {ok, Node} = ?t:start_node(list_to_atom(atom_to_list(?MODULE)++"_"++Rel),
+ {ok, Node} = test_server:start_node(list_to_atom(atom_to_list(?MODULE)++"_"++Rel),
peer,
[{args, " -setcookie "++Cookie},
{erl, [{release, Rel}]}]),
@@ -1560,7 +1547,7 @@ cmp_old_impl(Config) when is_list(Config) ->
cmp_node(Node, {erlang, bitstring_to_list, [list_to_bitstring(list2bitstrlist(mk_list(1000000)))]}),
cmp_node(Node, {erlang, bitstring_to_list, [list_to_bitstring(list2bitstrlist(mk_list(10000000)))]}),
- ?t:stop_node(Node),
+ test_server:stop_node(Node),
ok
end.
@@ -1604,7 +1591,7 @@ bit_sized_binary(Bin0) ->
unaligned_sub_bin(Bin, 0) -> Bin;
unaligned_sub_bin(Bin0, Offs) ->
- F = random:uniform(256),
+ F = rand:uniform(256),
Roffs = 8-Offs,
Bin1 = <<F:Offs,Bin0/binary,F:Roffs>>,
Sz = size(Bin0),
diff --git a/erts/emulator/test/bs_bincomp_SUITE.erl b/erts/emulator/test/bs_bincomp_SUITE.erl
index dcd13c19df..c481e93e41 100644
--- a/erts/emulator/test/bs_bincomp_SUITE.erl
+++ b/erts/emulator/test/bs_bincomp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -131,7 +131,7 @@ tracing(Config) when is_list(Config) ->
random_binary() ->
Seq = [1,2,3,4,5,6,7,8,9,10],
- << <<($a + random:uniform($z - $a)):8>> || _ <- Seq >>.
+ << <<($a + rand:uniform($z - $a)):8>> || _ <- Seq >>.
random_binaries(N) when N > 0 ->
random_binary(),
diff --git a/erts/emulator/test/bs_bit_binaries_SUITE.erl b/erts/emulator/test/bs_bit_binaries_SUITE.erl
index a07fd7609c..d393bc5b91 100644
--- a/erts/emulator/test/bs_bit_binaries_SUITE.erl
+++ b/erts/emulator/test/bs_bit_binaries_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -30,7 +30,7 @@
big_binary_to_and_from_list/1,send_and_receive/1,
send_and_receive_alot/1,append/1]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -57,9 +57,9 @@ end_per_group(_GroupName, Config) ->
misc(Config) when is_list(Config) ->
- ?line <<1:100>> = id(<<1:100>>),
- ?line {ok,ok} = {match(7),match(9)},
- ?line {ok,ok} = {match1(15),match1(31)},
+ <<1:100>> = id(<<1:100>>),
+ {ok,ok} = {match(7),match(9)},
+ {ok,ok} = {match1(15),match1(31)},
ok.
@@ -76,70 +76,70 @@ match1(N) ->
ok.
test_bit_size(Config) when is_list(Config) ->
- ?line 101 = bit_size(<<1:101>>),
- ?line 1001 = bit_size(<<1:1001>>),
- ?line 80 = bit_size(<<1:80>>),
- ?line 800 = bit_size(<<1:800>>),
- ?line Bin = <<0:16#1000000>>,
- ?line BigBin = list_to_bitstring([Bin||_ <- lists:seq(1,16#10)]++[<<1:1>>]),
- ?line 16#10000001 = erlang:bit_size(BigBin),
+ 101 = bit_size(<<1:101>>),
+ 1001 = bit_size(<<1:1001>>),
+ 80 = bit_size(<<1:80>>),
+ 800 = bit_size(<<1:800>>),
+ Bin = <<0:16#1000000>>,
+ BigBin = list_to_bitstring([Bin||_ <- lists:seq(1,16#10)]++[<<1:1>>]),
+ 16#10000001 = erlang:bit_size(BigBin),
%% Only run these on computers with lots of memory
%% HugeBin = list_to_bitstring([BigBin||_ <- lists:seq(1,16#10)]++[<<1:1>>]),
%% 16#100000011 = bit_size(HugeBin),
- ?line 0 = bit_size(<<>>),
+ 0 = bit_size(<<>>),
ok.
horrid_match(Config) when is_list(Config) ->
- ?line <<1:4,B:24/bitstring>> = <<1:4,42:24/little>>,
- ?line <<42:24/little>> = B,
+ <<1:4,B:24/bitstring>> = <<1:4,42:24/little>>,
+ <<42:24/little>> = B,
ok.
test_bitstr(Config) when is_list(Config) ->
- ?line <<1:7,B/bitstring>> = <<1:7,<<1:1,6>>/bitstring>>,
- ?line <<1:1,6>> = B,
- ?line B = <<1:1,6>>,
+ <<1:7,B/bitstring>> = <<1:7,<<1:1,6>>/bitstring>>,
+ <<1:1,6>> = B,
+ B = <<1:1,6>>,
ok.
asymmetric_tests(Config) when is_list(Config) ->
- ?line <<1:12>> = <<0,1:4>>,
- ?line <<0,1:4>> = <<1:12>>,
- ?line <<1:1,X/bitstring>> = <<128,255,0,0:2>>,
- ?line <<1,254,0,0:1>> = X,
- ?line X = <<1,254,0,0:1>>,
- ?line <<1:1,X1:25/bitstring>> = <<128,255,0,0:2>>,
- ?line <<1,254,0,0:1>> = X1,
- ?line X1 = <<1,254,0,0:1>>,
+ <<1:12>> = <<0,1:4>>,
+ <<0,1:4>> = <<1:12>>,
+ <<1:1,X/bitstring>> = <<128,255,0,0:2>>,
+ <<1,254,0,0:1>> = X,
+ X = <<1,254,0,0:1>>,
+ <<1:1,X1:25/bitstring>> = <<128,255,0,0:2>>,
+ <<1,254,0,0:1>> = X1,
+ X1 = <<1,254,0,0:1>>,
ok.
big_asymmetric_tests(Config) when is_list(Config) ->
- ?line <<1:875,1:12>> = <<1:875,0,1:4>>,
- ?line <<1:875,0,1:4>> = <<1:875,1:12>>,
- ?line <<1:1,X/bitstring>> = <<128,255,0,0:2,1:875>>,
- ?line <<1,254,0,0:1,1:875>> = X,
- ?line X = <<1,254,0,0:1,1:875>>,
- ?line <<1:1,X1:900/bitstring>> = <<128,255,0,0:2,1:875>>,
- ?line <<1,254,0,0:1,1:875>> = X1,
- ?line X1 = <<1,254,0,0:1,1:875>>,
+ <<1:875,1:12>> = <<1:875,0,1:4>>,
+ <<1:875,0,1:4>> = <<1:875,1:12>>,
+ <<1:1,X/bitstring>> = <<128,255,0,0:2,1:875>>,
+ <<1,254,0,0:1,1:875>> = X,
+ X = <<1,254,0,0:1,1:875>>,
+ <<1:1,X1:900/bitstring>> = <<128,255,0,0:2,1:875>>,
+ <<1,254,0,0:1,1:875>> = X1,
+ X1 = <<1,254,0,0:1,1:875>>,
ok.
binary_to_and_from_list(Config) when is_list(Config) ->
- ?line <<1,2,3,4,1:1>> = list_to_bitstring(bitstring_to_list(<<1,2,3,4,1:1>>)),
- ?line [1,2,3,4,<<1:1>>] = bitstring_to_list(<<1,2,3,4,1:1>>),
- ?line <<1:1,1,2,3,4>> = list_to_bitstring([<<1:1>>,1,2,3,4]),
- ?line [128,129,1,130,<<0:1>>] = bitstring_to_list(<<1:1,1,2,3,4>>),
+ <<1,2,3,4,1:1>> = list_to_bitstring(bitstring_to_list(<<1,2,3,4,1:1>>)),
+ [1,2,3,4,<<1:1>>] = bitstring_to_list(<<1,2,3,4,1:1>>),
+ <<1:1,1,2,3,4>> = list_to_bitstring([<<1:1>>,1,2,3,4]),
+ [128,129,1,130,<<0:1>>] = bitstring_to_list(<<1:1,1,2,3,4>>),
ok.
big_binary_to_and_from_list(Config) when is_list(Config) ->
- ?line <<1:800,2,3,4,1:1>> = list_to_bitstring(bitstring_to_list(<<1:800,2,3,4,1:1>>)),
- ?line [1,2,3,4|_Rest1] = bitstring_to_list(<<1,2,3,4,1:800,1:1>>),
- ?line <<1:801,1,2,3,4>> = list_to_bitstring([<<1:801>>,1,2,3,4]),
+ <<1:800,2,3,4,1:1>> = list_to_bitstring(bitstring_to_list(<<1:800,2,3,4,1:1>>)),
+ [1,2,3,4|_Rest1] = bitstring_to_list(<<1,2,3,4,1:800,1:1>>),
+ <<1:801,1,2,3,4>> = list_to_bitstring([<<1:801>>,1,2,3,4]),
ok.
send_and_receive(Config) when is_list(Config) ->
- ?line Bin = <<1,2:7>>,
+ Bin = <<1,2:7>>,
Pid = spawn_link(fun() -> receiver(Bin) end),
- ?line Pid ! {self(),<<1:7,8:5,Bin/bitstring>>},
- ?line receive
+ Pid ! {self(),<<1:7,8:5,Bin/bitstring>>},
+ receive
ok ->
ok
end.
@@ -176,8 +176,8 @@ receiver_alot(Bin) ->
append(Config) when is_list(Config) ->
cs_init(),
- ?line <<(-1):256/signed-unit:8>> = cs(do_append(id(<<>>), 256*8)),
- ?line <<(-1):256/signed-unit:8>> = cs(do_append2(id(<<>>), 256*4)),
+ <<(-1):256/signed-unit:8>> = cs(do_append(id(<<>>), 256*8)),
+ <<(-1):256/signed-unit:8>> = cs(do_append2(id(<<>>), 256*4)),
<<(-1):256/signed-unit:8>> = cs(do_append3(id(<<>>), 256*8)),
cs_end().
diff --git a/erts/emulator/test/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl
index 7ed99f5b4e..95042ac802 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-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -22,8 +22,8 @@
-module(bs_construct_SUITE).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
+-export([all/0, suite/0,
+ init_per_suite/1, end_per_suite/1,
test1/1, test2/1, test3/1, test4/1, test5/1, testf/1,
not_used/1, in_guard/1,
mem_leak/1, coerce_to_float/1, bjorn/1,
@@ -31,9 +31,11 @@
copy_writable_binary/1, kostis/1, dynamic/1, bs_add/1,
otp_7422/1, zero_width/1, bad_append/1, bs_add_overflow/1]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {seconds, 10}}].
all() ->
[test1, test2, test3, test4, test5, testf, not_used,
@@ -42,20 +44,11 @@ all() ->
copy_writable_binary, kostis, dynamic, bs_add, otp_7422, zero_width,
bad_append, bs_add_overflow].
-groups() ->
- [].
-
init_per_suite(Config) ->
Config.
end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
+ application:stop(os_mon).
big(1) ->
57285702734876389752897683.
@@ -68,9 +61,9 @@ r(L) ->
-define(T(B, L), {B, ??B, L}).
-define(N(B), {B, ??B, unknown}).
--define(FAIL(Expr), ?line fail_check(catch Expr, ??Expr, [])).
+-define(FAIL(Expr), fail_check(catch Expr, ??Expr, [])).
--define(FAIL_VARS(Expr, Vars), ?line fail_check(catch Expr, ??Expr, Vars)).
+-define(FAIL_VARS(Expr, Vars), fail_check(catch Expr, ??Expr, Vars)).
l(I_13, I_big1) ->
[
@@ -189,7 +182,7 @@ one_test({C_bin, E_bin, Str, Bytes}) when is_list(Bytes) ->
true ->
io:format("ERROR: Compiled: ~p. Expected ~p. Got ~p.~n",
[Str, Bytes, binary_to_list(C_bin)]),
- test_server:fail(comp)
+ ct:fail(comp)
end,
if
E_bin == Bin ->
@@ -197,7 +190,7 @@ one_test({C_bin, E_bin, Str, Bytes}) when is_list(Bytes) ->
true ->
io:format("ERROR: Interpreted: ~p. Expected ~p. Got ~p.~n",
[Str, Bytes, binary_to_list(E_bin)]),
- test_server:fail(comp)
+ ct:fail(comp)
end;
one_test({C_bin, E_bin, Str, Result}) ->
io:format(" ~s ~p~n", [Str, C_bin]),
@@ -218,7 +211,7 @@ one_test({C_bin, E_bin, Str, Result}) ->
io:format("ERROR: Compiled not equal to interpreted:"
"~n ~p, ~p.~n",
[binary_to_list(C_bin), binary_to_list(E_bin)]),
- test_server:fail(comp);
+ ct:fail(comp);
0 ->
ok;
%% For situations where the final bits may not matter, like
@@ -253,23 +246,22 @@ fail_check({'EXIT',{badarg,_}}, Str, Vars) ->
try evaluate(Str, Vars) of
Res ->
io:format("Interpreted result: ~p", [Res]),
- ?t:fail(did_not_fail_in_intepreted_code)
+ ct:fail(did_not_fail_in_intepreted_code)
catch
error:badarg ->
ok
end;
fail_check(Res, _, _) ->
io:format("Compiled result: ~p", [Res]),
- ?t:fail(did_not_fail_in_compiled_code).
+ ct:fail(did_not_fail_in_compiled_code).
%%% Simple working cases
-test1(suite) -> [];
test1(Config) when is_list(Config) ->
- ?line I_13 = i(13),
- ?line I_big1 = big(1),
- ?line Vars = [{'I_13', I_13},
+ I_13 = i(13),
+ I_big1 = big(1),
+ Vars = [{'I_13', I_13},
{'I_big1', I_big1}],
- ?line lists:foreach(fun one_test/1, eval_list(l(I_13, I_big1), Vars)).
+ lists:foreach(fun one_test/1, eval_list(l(I_13, I_big1), Vars)).
%%% Misc
@@ -285,10 +277,9 @@ gen(N, S, A) ->
gen_l(N, S, A) ->
[?T(<<A:S/little, A:(N-S)/little>>, comp(N, A, S))].
-test2(suite) -> [];
test2(Config) when is_list(Config) ->
- ?line test2(0, 8, 2#10101010101010101),
- ?line test2(0, 8, 2#1111111111).
+ test2(0, 8, 2#10101010101010101),
+ test2(0, 8, 2#1111111111).
test2(End, End, _) ->
ok;
@@ -313,10 +304,9 @@ t3() ->
?N(<<>>)
].
-test3(suite) -> [];
test3(Config) when is_list(Config) ->
- ?line Vars = [],
- ?line lists:foreach(fun one_test/1, eval_list(t3(), Vars)).
+ Vars = [],
+ lists:foreach(fun one_test/1, eval_list(t3(), Vars)).
gen_u(N, S, A) ->
[?N(<<A:S, A:(N-S)>>)].
@@ -324,10 +314,9 @@ gen_u(N, S, A) ->
gen_u_l(N, S, A) ->
[?N(<<A:S/little, A:(N-S)/little>>)].
-test4(suite) -> [];
test4(Config) when is_list(Config) ->
- ?line test4(0, 16, 2#10101010101010101),
- ?line test4(0, 16, 2#1111111111).
+ test4(0, 16, 2#10101010101010101),
+ test4(0, 16, 2#1111111111).
test4(End, End, _) ->
ok;
@@ -345,11 +334,10 @@ gen_b(N, S, A) ->
[?T(<<A:S/binary-unit:1, A:(N-S)/binary-unit:1>>,
binary_to_list(<<A:S/binary-unit:1, A:(N-S)/binary-unit:1>>))].
-test5(suite) -> [];
-test5(doc) -> ["OTP-3995"];
+%% OTP-3995
test5(Config) when is_list(Config) ->
- ?line test5(0, 8, <<73>>),
- ?line test5(0, 8, <<68>>).
+ test5(0, 8, <<73>>),
+ test5(0, 8, <<68>>).
test5(End, End, _) ->
ok;
@@ -363,47 +351,46 @@ test5(S, A) ->
lists:foreach(fun one_test/1, eval_list(gen_b(N, S, A), Vars)).
%%% Failure cases
-testf(suite) -> [];
testf(Config) when is_list(Config) ->
- ?line ?FAIL(<<3.14>>),
- ?line ?FAIL(<<<<1,2>>>>),
+ ?FAIL(<<3.14>>),
+ ?FAIL(<<<<1,2>>>>),
- ?line ?FAIL(<<2.71/binary>>),
- ?line ?FAIL(<<24334/binary>>),
- ?line ?FAIL(<<24334344294788947129487129487219847/binary>>),
+ ?FAIL(<<2.71/binary>>),
+ ?FAIL(<<24334/binary>>),
+ ?FAIL(<<24334344294788947129487129487219847/binary>>),
BigInt = id(24334344294788947129487129487219847),
- ?line ?FAIL_VARS(<<BigInt/binary>>, [{'BigInt',BigInt}]),
- ?line ?FAIL_VARS(<<42,BigInt/binary>>, [{'BigInt',BigInt}]),
- ?line ?FAIL_VARS(<<BigInt:2/binary>>, [{'BigInt',BigInt}]),
+ ?FAIL_VARS(<<BigInt/binary>>, [{'BigInt',BigInt}]),
+ ?FAIL_VARS(<<42,BigInt/binary>>, [{'BigInt',BigInt}]),
+ ?FAIL_VARS(<<BigInt:2/binary>>, [{'BigInt',BigInt}]),
%% One negative field size, but the sum of field sizes will be 1 byte.
%% Make sure that we reject that properly.
I_minus_777 = id(-777),
I_minus_2047 = id(-2047),
- ?line ?FAIL_VARS(<<I_minus_777:2048/unit:8,57:I_minus_2047/unit:8>>,
+ ?FAIL_VARS(<<I_minus_777:2048/unit:8,57:I_minus_2047/unit:8>>,
ordsets:from_list([{'I_minus_777',I_minus_777},
{'I_minus_2047',I_minus_2047}])),
- ?line ?FAIL(<<<<1,2,3>>/float>>),
+ ?FAIL(<<<<1,2,3>>/float>>),
%% Negative field widths.
- ?line testf_1(-8, <<1,2,3,4,5>>),
- ?line ?FAIL(<<0:(-(1 bsl 100))>>),
+ testf_1(-8, <<1,2,3,4,5>>),
+ ?FAIL(<<0:(-(1 bsl 100))>>),
- ?line ?FAIL(<<42:(-16)>>),
- ?line ?FAIL(<<3.14:(-8)/float>>),
- ?line ?FAIL(<<<<23,56,0,2>>:(-16)/binary>>),
- ?line ?FAIL(<<<<23,56,0,2>>:(2.5)/binary>>),
- ?line ?FAIL(<<<<23,56,0,2>>:(anka)>>),
- ?line ?FAIL(<<<<23,56,0,2>>:(anka)>>),
+ ?FAIL(<<42:(-16)>>),
+ ?FAIL(<<3.14:(-8)/float>>),
+ ?FAIL(<<<<23,56,0,2>>:(-16)/binary>>),
+ ?FAIL(<<<<23,56,0,2>>:(2.5)/binary>>),
+ ?FAIL(<<<<23,56,0,2>>:(anka)>>),
+ ?FAIL(<<<<23,56,0,2>>:(anka)>>),
%% Unit failures.
- ?line ?FAIL(<<<<1:1>>/binary>>),
+ ?FAIL(<<<<1:1>>/binary>>),
Sz = id(1),
- ?line ?FAIL_VARS(<<<<1:Sz>>/binary>>, [{'Sz',Sz}]),
- ?line {'EXIT',{badarg,_}} = (catch <<<<1:(id(1))>>/binary>>),
- ?line ?FAIL(<<<<7,8,9>>/binary-unit:16>>),
- ?line ?FAIL(<<<<7,8,9,3:7>>/binary-unit:16>>),
- ?line ?FAIL(<<<<7,8,9,3:7>>/binary-unit:17>>),
+ ?FAIL_VARS(<<<<1:Sz>>/binary>>, [{'Sz',Sz}]),
+ {'EXIT',{badarg,_}} = (catch <<<<1:(id(1))>>/binary>>),
+ ?FAIL(<<<<7,8,9>>/binary-unit:16>>),
+ ?FAIL(<<<<7,8,9,3:7>>/binary-unit:16>>),
+ ?FAIL(<<<<7,8,9,3:7>>/binary-unit:17>>),
ok.
@@ -413,14 +400,13 @@ testf_1(W, B) ->
?FAIL_VARS(<<3.14:W/float>>, Vars),
?FAIL_VARS(<<B:W/binary>>, [{'B',B}|Vars]).
-not_used(doc) ->
- "Test that constructed binaries that are not used will still give an exception.";
+%% Test that constructed binaries that are not used will still give an exception.
not_used(Config) when is_list(Config) ->
- ?line ok = not_used1(3, <<"dum">>),
- ?line {'EXIT',{badarg,_}} = (catch not_used1(3, "dum")),
- ?line {'EXIT',{badarg,_}} = (catch not_used2(444, -2)),
- ?line {'EXIT',{badarg,_}} = (catch not_used2(444, anka)),
- ?line {'EXIT',{badarg,_}} = (catch not_used3(444)),
+ ok = not_used1(3, <<"dum">>),
+ {'EXIT',{badarg,_}} = (catch not_used1(3, "dum")),
+ {'EXIT',{badarg,_}} = (catch not_used2(444, -2)),
+ {'EXIT',{badarg,_}} = (catch not_used2(444, anka)),
+ {'EXIT',{badarg,_}} = (catch not_used3(444)),
ok.
not_used1(I, BinString) ->
@@ -436,11 +422,11 @@ not_used3(I) ->
ok.
in_guard(Config) when is_list(Config) ->
- ?line 1 = in_guard(<<16#74ad:16>>, 16#e95, 5),
- ?line 2 = in_guard(<<16#3A,16#F7,"hello">>, 16#3AF7, <<"hello">>),
- ?line 3 = in_guard(<<16#FBCD:14,3.1415/float,3:2>>, 16#FBCD, 3.1415),
- ?line 3 = in_guard(<<16#FBCD:14,3/float,3:2>>, 16#FBCD, 3),
- ?line 3 = in_guard(<<16#FBCD:14,(2 bsl 226)/float,3:2>>, 16#FBCD, 2 bsl 226),
+ 1 = in_guard(<<16#74ad:16>>, 16#e95, 5),
+ 2 = in_guard(<<16#3A,16#F7,"hello">>, 16#3AF7, <<"hello">>),
+ 3 = in_guard(<<16#FBCD:14,3.1415/float,3:2>>, 16#FBCD, 3.1415),
+ 3 = in_guard(<<16#FBCD:14,3/float,3:2>>, 16#FBCD, 3),
+ 3 = in_guard(<<16#FBCD:14,(2 bsl 226)/float,3:2>>, 16#FBCD, 2 bsl 226),
nope = in_guard(<<1>>, 42, b),
nope = in_guard(<<1>>, a, b),
nope = in_guard(<<1,2>>, 1, 1),
@@ -454,16 +440,16 @@ in_guard(Bin, A, B) when <<A:14,B/float,3:2>> == Bin -> 3;
in_guard(Bin, A, B) when {a,b,<<A:14,B/float,3:2>>} == Bin -> cant_happen;
in_guard(_, _, _) -> nope.
-mem_leak(doc) -> "Make sure that construction has no memory leak";
+%% Make sure that construction has no memory leak
mem_leak(Config) when is_list(Config) ->
- ?line B = make_bin(16, <<0>>),
- ?line mem_leak(1024, B),
+ B = make_bin(16, <<0>>),
+ mem_leak(1024, B),
ok.
mem_leak(0, _) -> ok;
mem_leak(N, B) ->
- ?line big_bin(B, <<23>>),
- ?line {'EXIT',{badarg,_}} = (catch big_bin(B, bad)),
+ big_bin(B, <<23>>),
+ {'EXIT',{badarg,_}} = (catch big_bin(B, bad)),
mem_leak(N-1, B).
big_bin(B1, B2) ->
@@ -477,18 +463,18 @@ make_bin(0, Acc) -> Acc;
make_bin(N, Acc) -> make_bin(N-1, <<Acc/binary,Acc/binary>>).
-define(COF(Int0),
- ?line (fun(Int) ->
+ (fun(Int) ->
true = <<Int:32/float>> =:= <<(float(Int)):32/float>>,
true = <<Int:64/float>> =:= <<(float(Int)):64/float>>
end)(nonliteral(Int0)),
- ?line true = <<Int0:32/float>> =:= <<(float(Int0)):32/float>>,
- ?line true = <<Int0:64/float>> =:= <<(float(Int0)):64/float>>).
+ true = <<Int0:32/float>> =:= <<(float(Int0)):32/float>>,
+ true = <<Int0:64/float>> =:= <<(float(Int0)):64/float>>).
-define(COF64(Int0),
- ?line (fun(Int) ->
+ (fun(Int) ->
true = <<Int:64/float>> =:= <<(float(Int)):64/float>>
end)(nonliteral(Int0)),
- ?line true = <<Int0:64/float>> =:= <<(float(Int0)):64/float>>).
+ true = <<Int0:64/float>> =:= <<(float(Int0)):64/float>>).
nonliteral(X) -> X.
@@ -507,7 +493,7 @@ coerce_to_float(Config) when is_list(Config) ->
ok.
bjorn(Config) when is_list(Config) ->
- ?line error = bjorn_1(),
+ error = bjorn_1(),
ok.
bjorn_1() ->
@@ -535,90 +521,74 @@ do_something() ->
throw(blurf).
huge_float_field(Config) when is_list(Config) ->
- ?line {'EXIT',{badarg,_}} = (catch <<0.0:9/float-unit:8>>),
- ?line huge_float_check(catch <<0.0:67108865/float-unit:64>>),
- ?line huge_float_check(catch <<0.0:((1 bsl 26)+1)/float-unit:64>>),
- ?line huge_float_check(catch <<0.0:(id(67108865))/float-unit:64>>),
-%% ?line huge_float_check(catch <<0.0:((1 bsl 60)+1)/float-unit:64>>),
- ?line huge_float_check(catch <<3839739387439387383739387987347983:((1 bsl 26)+1)/float-unit:64>>),
-%% ?line huge_float_check(catch <<3839739387439387383739387987347983:((1 bsl 60)+1)/float-unit:64>>),
+ {'EXIT',{badarg,_}} = (catch <<0.0:9/float-unit:8>>),
+ huge_float_check(catch <<0.0:67108865/float-unit:64>>),
+ huge_float_check(catch <<0.0:((1 bsl 26)+1)/float-unit:64>>),
+ huge_float_check(catch <<0.0:(id(67108865))/float-unit:64>>),
+%% huge_float_check(catch <<0.0:((1 bsl 60)+1)/float-unit:64>>),
+ huge_float_check(catch <<3839739387439387383739387987347983:((1 bsl 26)+1)/float-unit:64>>),
+%% huge_float_check(catch <<3839739387439387383739387987347983:((1 bsl 60)+1)/float-unit:64>>),
ok.
huge_float_check({'EXIT',{system_limit,_}}) -> ok;
huge_float_check({'EXIT',{badarg,_}}) -> ok.
huge_binary(Config) when is_list(Config) ->
- ?line 16777216 = size(<<0:(id(1 bsl 26)),(-1):(id(1 bsl 26))>>),
- ?line garbage_collect(),
+ ct:timetrap({seconds, 60}),
+ 16777216 = size(<<0:(id(1 bsl 26)),(-1):(id(1 bsl 26))>>),
+ garbage_collect(),
{Shift,Return} = case free_mem() of
undefined ->
%% This test has to be inlined inside the case to
%% use a literal Shift
- ?line garbage_collect(),
- ?line id(<<0:((1 bsl 32)-1)>>),
+ garbage_collect(),
+ id(<<0:((1 bsl 32)-1)>>),
{32,ok};
Mb when Mb > 600 ->
- ?line garbage_collect(),
- ?line id(<<0:((1 bsl 32)-1)>>),
+ garbage_collect(),
+ id(<<0:((1 bsl 32)-1)>>),
{32,ok};
Mb when Mb > 300 ->
- ?line garbage_collect(),
- ?line id(<<0:((1 bsl 31)-1)>>),
+ garbage_collect(),
+ id(<<0:((1 bsl 31)-1)>>),
{31,"Limit huge binaries to 256 Mb"};
_ ->
- ?line garbage_collect(),
- ?line id(<<0:((1 bsl 30)-1)>>),
+ garbage_collect(),
+ id(<<0:((1 bsl 30)-1)>>),
{30,"Limit huge binary to 128 Mb"}
end,
- ?line garbage_collect(),
- ?line id(<<0:((1 bsl Shift)-1)>>),
- ?line garbage_collect(),
- ?line id(<<0:(id((1 bsl Shift)-1))>>),
- ?line garbage_collect(),
+ garbage_collect(),
+ id(<<0:((1 bsl Shift)-1)>>),
+ garbage_collect(),
+ id(<<0:(id((1 bsl Shift)-1))>>),
+ garbage_collect(),
case Return of
ok -> ok;
Comment -> {comment, Comment}
end.
free_mem() ->
- Cmd = "uname; free",
- Output = string:tokens(os:cmd(Cmd), "\n"),
- io:format("Output from command ~p\n~p\n",[Cmd,Output]),
- case Output of
- [OS, ColumnNames, Values | _] ->
- case string:str(OS,"Linux") of
- 0 ->
- io:format("Unknown OS\n",[]),
- undefined;
- _ ->
- case {string:tokens(ColumnNames, " \t"),
- string:tokens(Values, " \t")} of
- {[_,_,"free"|_],["Mem:",_,_,FreeKb|_]} ->
- list_to_integer(FreeKb) div 1024;
- _ ->
- io:format("Failed to parse output from 'free':\n",[]),
- undefined
- end
- end;
- _ ->
- io:format("Too few lines in output\n",[]),
- undefined
+ {ok,Apps} = application:ensure_all_started(os_mon),
+ Mem = memsup:get_system_memory_data(),
+ [ok = application:stop(App)||App <- Apps],
+ case proplists:get_value(free_memory,Mem) of
+ undefined -> undefined;
+ Val -> Val div 1024
end.
-
system_limit(Config) when is_list(Config) ->
WordSize = erlang:system_info(wordsize),
BitsPerWord = WordSize * 8,
- ?line {'EXIT',{system_limit,_}} =
+ {'EXIT',{system_limit,_}} =
(catch <<0:(id(0)),42:(id(1 bsl BitsPerWord))>>),
- ?line {'EXIT',{system_limit,_}} =
+ {'EXIT',{system_limit,_}} =
(catch <<42:(id(1 bsl BitsPerWord)),0:(id(0))>>),
- ?line {'EXIT',{system_limit,_}} =
+ {'EXIT',{system_limit,_}} =
(catch <<(id(<<>>))/binary,0:(id(1 bsl 100))>>),
%% Would fail to load.
- ?line {'EXIT',{system_limit,_}} = (catch <<0:(1 bsl 67)>>),
- ?line {'EXIT',{system_limit,_}} = (catch <<0:((1 bsl 64)+1)>>),
+ {'EXIT',{system_limit,_}} = (catch <<0:(1 bsl 67)>>),
+ {'EXIT',{system_limit,_}} = (catch <<0:((1 bsl 64)+1)>>),
case WordSize of
4 ->
@@ -628,60 +598,52 @@ system_limit(Config) when is_list(Config) ->
end.
system_limit_32() ->
- ?line {'EXIT',{badarg,_}} = (catch <<42:(-1)>>),
- ?line {'EXIT',{badarg,_}} = (catch <<42:(id(-1))>>),
- ?line {'EXIT',{badarg,_}} = (catch <<42:(id(-389739873536870912))/unit:8>>),
- ?line {'EXIT',{system_limit,_}} = (catch <<42:536870912/unit:8>>),
- ?line {'EXIT',{system_limit,_}} = (catch <<42:(id(536870912))/unit:8>>),
- ?line {'EXIT',{system_limit,_}} = (catch <<0:(id(8)),42:536870912/unit:8>>),
- ?line {'EXIT',{system_limit,_}} =
- (catch <<0:(id(8)),42:(id(536870912))/unit:8>>),
+ {'EXIT',{badarg,_}} = (catch <<42:(-1)>>),
+ {'EXIT',{badarg,_}} = (catch <<42:(id(-1))>>),
+ {'EXIT',{badarg,_}} = (catch <<42:(id(-389739873536870912))/unit:8>>),
+ {'EXIT',{system_limit,_}} = (catch <<42:536870912/unit:8>>),
+ {'EXIT',{system_limit,_}} = (catch <<42:(id(536870912))/unit:8>>),
+ {'EXIT',{system_limit,_}} = (catch <<0:(id(8)),42:536870912/unit:8>>),
+ {'EXIT',{system_limit,_}} = (catch <<0:(id(8)),42:(id(536870912))/unit:8>>),
%% The size would be silently truncated, resulting in a crash.
- ?line {'EXIT',{system_limit,_}} = (catch <<0:(1 bsl 35)>>),
- ?line {'EXIT',{system_limit,_}} = (catch <<0:((1 bsl 32)+1)>>),
+ {'EXIT',{system_limit,_}} = (catch <<0:(1 bsl 35)>>),
+ {'EXIT',{system_limit,_}} = (catch <<0:((1 bsl 32)+1)>>),
%% Would fail to load.
- ?line {'EXIT',{system_limit,_}} = (catch <<0:(1 bsl 43)>>),
- ?line {'EXIT',{system_limit,_}} = (catch <<0:((1 bsl 40)+1)>>),
+ {'EXIT',{system_limit,_}} = (catch <<0:(1 bsl 43)>>),
+ {'EXIT',{system_limit,_}} = (catch <<0:((1 bsl 40)+1)>>),
ok.
badarg(Config) when is_list(Config) ->
- ?line {'EXIT',{badarg,_}} =
- (catch <<0:(id(1 bsl 100)),0:(id(-1))>>),
- ?line {'EXIT',{badarg,_}} =
- (catch <<0:(id(1 bsl 100)),0:(id(-(1 bsl 70)))>>),
- ?line {'EXIT',{badarg,_}} =
- (catch <<0:(id(-(1 bsl 70))),0:(id(1 bsl 100))>>),
-
- ?line {'EXIT',{badarg,_}} =
- (catch <<(id(<<>>))/binary,0:(id(-(1 bsl 100)))>>),
-
+ {'EXIT',{badarg,_}} = (catch <<0:(id(1 bsl 100)),0:(id(-1))>>),
+ {'EXIT',{badarg,_}} = (catch <<0:(id(1 bsl 100)),0:(id(-(1 bsl 70)))>>),
+ {'EXIT',{badarg,_}} = (catch <<0:(id(-(1 bsl 70))),0:(id(1 bsl 100))>>),
+ {'EXIT',{badarg,_}} = (catch <<(id(<<>>))/binary,0:(id(-(1 bsl 100)))>>),
ok.
copy_writable_binary(Config) when is_list(Config) ->
- ?line [copy_writable_binary_1(I) || I <- lists:seq(0, 256)],
+ [copy_writable_binary_1(I) || I <- lists:seq(0, 256)],
ok.
copy_writable_binary_1(_) ->
- ?line Bin0 = <<(id(<<>>))/binary,0,1,2,3,4,5,6,7>>,
- ?line SubBin = make_sub_bin(Bin0),
- ?line id(<<42,34,55,Bin0/binary>>), %Make reallocation likelier.
- ?line Pid = spawn(fun() ->
+ Bin0 = <<(id(<<>>))/binary,0,1,2,3,4,5,6,7>>,
+ SubBin = make_sub_bin(Bin0),
+ id(<<42,34,55,Bin0/binary>>), %Make reallocation likelier.
+ Pid = spawn(fun() ->
copy_writable_binary_holder(Bin0, SubBin)
end),
- ?line Tab = ets:new(holder, []),
- ?line ets:insert(Tab, {17,Bin0}),
- ?line ets:insert(Tab, {42,SubBin}),
- ?line id(<<Bin0/binary,0:(64*1024*8)>>),
- ?line Pid ! self(),
- ?line [{17,Bin0}] = ets:lookup(Tab, 17),
- ?line [{42,Bin0}] = ets:lookup(Tab, 42),
+ Tab = ets:new(holder, []),
+ ets:insert(Tab, {17,Bin0}),
+ ets:insert(Tab, {42,SubBin}),
+ id(<<Bin0/binary,0:(64*1024*8)>>),
+ Pid ! self(),
+ [{17,Bin0}] = ets:lookup(Tab, 17),
+ [{42,Bin0}] = ets:lookup(Tab, 42),
receive
{Pid,Bin0,Bin0} -> ok;
Other ->
- io:format("Unexpected message: ~p", [Other]),
- ?line ?t:fail()
+ ct:fail("Unexpected message: ~p", [Other])
end,
ok.
@@ -722,8 +684,8 @@ have_250_terabytes_of_ram() -> false.
%% give the same result.
dynamic(Config) when is_list(Config) ->
- ?line dynamic_1(fun dynamic_big/5),
- ?line dynamic_1(fun dynamic_little/5),
+ dynamic_1(fun dynamic_big/5),
+ dynamic_1(fun dynamic_little/5),
ok.
dynamic_1(Dynamic) ->
@@ -802,32 +764,32 @@ bs_add(Config) when is_list(Config) ->
return],
%% Write assembly file and assemble it.
- ?line PrivDir = ?config(priv_dir, Config),
- ?line RootName = filename:join(PrivDir, atom_to_list(Mod)),
- ?line AsmFile = RootName ++ ".S",
- ?line {ok,Fd} = file:open(AsmFile, [write]),
- ?line [io:format(Fd, "~p. \n", [T]) || T <- Code],
- ?line ok = file:close(Fd),
- ?line {ok,Mod} = compile:file(AsmFile, [from_asm,report,{outdir,PrivDir}]),
- ?line LoadRc = code:load_abs(RootName),
- ?line {module,_Module} = LoadRc,
+ PrivDir = proplists:get_value(priv_dir, Config),
+ RootName = filename:join(PrivDir, atom_to_list(Mod)),
+ AsmFile = RootName ++ ".S",
+ {ok,Fd} = file:open(AsmFile, [write]),
+ [io:format(Fd, "~p. \n", [T]) || T <- Code],
+ ok = file:close(Fd),
+ {ok,Mod} = compile:file(AsmFile, [from_asm,report,{outdir,PrivDir}]),
+ LoadRc = code:load_abs(RootName),
+ {module,_Module} = LoadRc,
%% Find smallest positive bignum.
- ?line SmallestBig = smallest_big(),
- ?line io:format("~p\n", [SmallestBig]),
- ?line Expected = SmallestBig + N,
+ SmallestBig = smallest_big(),
+ io:format("~p\n", [SmallestBig]),
+ Expected = SmallestBig + N,
DoTest = fun() ->
exit(Mod:bs_add(SmallestBig, -SmallestBig))
end,
- ?line {Pid,Mref} = spawn_monitor(DoTest),
+ {Pid,Mref} = spawn_monitor(DoTest),
receive
{'DOWN',Mref,process,Pid,Res} -> ok
end,
- ?line Expected = Res,
+ Expected = Res,
%% Clean up.
- ?line ok = file:delete(AsmFile),
- ?line ok = file:delete(code:which(Mod)),
+ ok = file:delete(AsmFile),
+ ok = file:delete(code:which(Mod)),
ok.
@@ -868,17 +830,17 @@ otp_7422_bin(N) when N < 512 ->
otp_7422_bin(_) -> ok.
zero_width(Config) when is_list(Config) ->
- ?line Z = id(0),
+ Z = id(0),
Small = id(42),
Big = id(1 bsl 128),
- ?line <<>> = <<Small:Z>>,
- ?line <<>> = <<Small:0>>,
- ?line <<>> = <<Big:Z>>,
- ?line <<>> = <<Big:0>>,
+ <<>> = <<Small:Z>>,
+ <<>> = <<Small:0>>,
+ <<>> = <<Big:Z>>,
+ <<>> = <<Big:0>>,
- ?line {'EXIT',{badarg,_}} = (catch <<not_a_number:0>>),
- ?line {'EXIT',{badarg,_}} = (catch <<(id(not_a_number)):Z>>),
- ?line {'EXIT',{badarg,_}} = (catch <<(id(not_a_number)):0>>),
+ {'EXIT',{badarg,_}} = (catch <<not_a_number:0>>),
+ {'EXIT',{badarg,_}} = (catch <<(id(not_a_number)):Z>>),
+ {'EXIT',{badarg,_}} = (catch <<(id(not_a_number)):0>>),
ok.
@@ -927,17 +889,26 @@ append_unit_16(Bin) ->
%% Produce a large result of bs_add that, if cast to signed int, would overflow
%% into a negative number that fits a smallnum.
-bs_add_overflow(Config) ->
+bs_add_overflow(_Config) ->
+ Memsize = memsize(),
+ io:format("Memsize = ~w Bytes~n", [Memsize]),
case erlang:system_info(wordsize) of
8 ->
{skip, "64-bit architecture"};
+ _ when Memsize < (2 bsl 30) ->
+ {skip, "Less then 2 GB of memory"};
4 ->
Large = <<0:((1 bsl 30)-1)>>,
{'EXIT',{system_limit,_}} =
(catch <<Large/bits, Large/bits, Large/bits, Large/bits,
- Large/bits, Large/bits, Large/bits, Large/bits,
- Large/bits>>),
+ Large/bits, Large/bits, Large/bits, Large/bits,
+ Large/bits>>),
ok
end.
-
+
id(I) -> I.
+
+memsize() ->
+ application:ensure_all_started(os_mon),
+ {Tot,_Used,_} = memsup:get_memory_data(),
+ Tot.
diff --git a/erts/emulator/test/bs_match_bin_SUITE.erl b/erts/emulator/test/bs_match_bin_SUITE.erl
index ba79643e69..f5c996ae9e 100644
--- a/erts/emulator/test/bs_match_bin_SUITE.erl
+++ b/erts/emulator/test/bs_match_bin_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -24,7 +24,7 @@
init_per_group/2,end_per_group/2,
byte_split_binary/1,bit_split_binary/1,match_huge_bin/1]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -47,33 +47,33 @@ end_per_group(_GroupName, Config) ->
Config.
-byte_split_binary(doc) -> "Tries to split a binary at all byte-aligned positions.";
+%% Tries to split a binary at all byte-aligned positions.
byte_split_binary(Config) when is_list(Config) ->
- ?line L = lists:seq(0, 57),
- ?line B = mkbin(L),
- ?line byte_split(L, B, size(B)),
- ?line Unaligned = make_unaligned_sub_binary(B),
- ?line byte_split(L, Unaligned, size(Unaligned)).
+ L = lists:seq(0, 57),
+ B = mkbin(L),
+ byte_split(L, B, size(B)),
+ Unaligned = make_unaligned_sub_binary(B),
+ byte_split(L, Unaligned, size(Unaligned)).
byte_split(L, B, Pos) when Pos >= 0 ->
- ?line Sz1 = Pos,
- ?line Sz2 = size(B) - Pos,
- ?line <<B1:Sz1/binary,B2:Sz2/binary>> = B,
- ?line B1 = list_to_binary(lists:sublist(L, 1, Pos)),
- ?line B2 = list_to_binary(lists:nthtail(Pos, L)),
- ?line byte_split(L, B, Pos-1);
+ Sz1 = Pos,
+ Sz2 = size(B) - Pos,
+ <<B1:Sz1/binary,B2:Sz2/binary>> = B,
+ B1 = list_to_binary(lists:sublist(L, 1, Pos)),
+ B2 = list_to_binary(lists:nthtail(Pos, L)),
+ byte_split(L, B, Pos-1);
byte_split(_, _, _) -> ok.
-bit_split_binary(doc) -> "Tries to split a binary at all positions.";
+%% Tries to split a binary at all positions.
bit_split_binary(Config) when is_list(Config) ->
Fun = fun(Bin, List, SkipBef, N) ->
- ?line SkipAft = 8*size(Bin) - N - SkipBef,
+ SkipAft = 8*size(Bin) - N - SkipBef,
%%io:format("~p, ~p, ~p", [SkipBef,N,SkipAft]),
- ?line <<_:SkipBef,OutBin:N/binary-unit:1,_:SkipAft>> = Bin,
- ?line OutBin = make_bin_from_list(List, N)
+ <<_:SkipBef,OutBin:N/binary-unit:1,_:SkipAft>> = Bin,
+ OutBin = make_bin_from_list(List, N)
end,
- ?line bit_split_binary1(Fun, erlang:md5(<<1,2,3>>)),
- ?line bit_split_binary1(Fun,
+ bit_split_binary1(Fun, erlang:md5(<<1,2,3>>)),
+ bit_split_binary1(Fun,
make_unaligned_sub_binary(erlang:md5(<<1,2,3>>))),
ok.
@@ -119,19 +119,19 @@ make_unaligned_sub_binary(Bin0) ->
id(I) -> I.
match_huge_bin(Config) when is_list(Config) ->
- ?line Bin = <<0:(1 bsl 27),13:8>>,
- ?line skip_huge_bin_1(1 bsl 27, Bin),
- ?line 16777216 = match_huge_bin_1(1 bsl 27, Bin),
+ Bin = <<0:(1 bsl 27),13:8>>,
+ skip_huge_bin_1(1 bsl 27, Bin),
+ 16777216 = match_huge_bin_1(1 bsl 27, Bin),
%% Test overflowing the size of a binary field.
- ?line nomatch = overflow_huge_bin_skip_32(Bin),
- ?line nomatch = overflow_huge_bin_32(Bin),
- ?line nomatch = overflow_huge_bin_skip_64(Bin),
- ?line nomatch = overflow_huge_bin_64(Bin),
+ nomatch = overflow_huge_bin_skip_32(Bin),
+ nomatch = overflow_huge_bin_32(Bin),
+ nomatch = overflow_huge_bin_skip_64(Bin),
+ nomatch = overflow_huge_bin_64(Bin),
%% Size in variable
- ?line ok = overflow_huge_bin(Bin, lists:seq(25, 32)++lists:seq(50, 64)),
- ?line ok = overflow_huge_bin_unit128(Bin, lists:seq(25, 32)++lists:seq(50, 64)),
+ ok = overflow_huge_bin(Bin, lists:seq(25, 32)++lists:seq(50, 64)),
+ ok = overflow_huge_bin_unit128(Bin, lists:seq(25, 32)++lists:seq(50, 64)),
ok.
diff --git a/erts/emulator/test/bs_match_int_SUITE.erl b/erts/emulator/test/bs_match_int_SUITE.erl
index 368f71978d..a7bd4b8ac3 100644
--- a/erts/emulator/test/bs_match_int_SUITE.erl
+++ b/erts/emulator/test/bs_match_int_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -24,7 +24,7 @@
integer/1,signed_integer/1,dynamic/1,more_dynamic/1,mml/1,
match_huge_int/1,bignum/1,unaligned_32_bit/1]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-import(lists, [seq/2]).
@@ -51,22 +51,22 @@ end_per_group(_GroupName, Config) ->
integer(Config) when is_list(Config) ->
- ?line 0 = get_int(mkbin([])),
- ?line 0 = get_int(mkbin([0])),
- ?line 42 = get_int(mkbin([42])),
- ?line 255 = get_int(mkbin([255])),
- ?line 256 = get_int(mkbin([1,0])),
- ?line 257 = get_int(mkbin([1,1])),
- ?line 258 = get_int(mkbin([1,2])),
- ?line 258 = get_int(mkbin([1,2])),
- ?line 65534 = get_int(mkbin([255,254])),
- ?line 16776455 = get_int(mkbin([255,253,7])),
- ?line 4245492555 = get_int(mkbin([253,13,19,75])),
- ?line 4294967294 = get_int(mkbin([255,255,255,254])),
- ?line 4294967295 = get_int(mkbin([255,255,255,255])),
- ?line Eight = [200,1,19,128,222,42,97,111],
- ?line cmp128(Eight, uint(Eight)),
- ?line fun_clause(catch get_int(mkbin(seq(1,5)))),
+ 0 = get_int(mkbin([])),
+ 0 = get_int(mkbin([0])),
+ 42 = get_int(mkbin([42])),
+ 255 = get_int(mkbin([255])),
+ 256 = get_int(mkbin([1,0])),
+ 257 = get_int(mkbin([1,1])),
+ 258 = get_int(mkbin([1,2])),
+ 258 = get_int(mkbin([1,2])),
+ 65534 = get_int(mkbin([255,254])),
+ 16776455 = get_int(mkbin([255,253,7])),
+ 4245492555 = get_int(mkbin([253,13,19,75])),
+ 4294967294 = get_int(mkbin([255,255,255,254])),
+ 4294967295 = get_int(mkbin([255,255,255,255])),
+ Eight = [200,1,19,128,222,42,97,111],
+ cmp128(Eight, uint(Eight)),
+ fun_clause(catch get_int(mkbin(seq(1,5)))),
ok.
get_int(Bin) ->
@@ -89,13 +89,13 @@ cmp128(<<I:128>>, I) -> equal;
cmp128(_, _) -> not_equal.
signed_integer(Config) when is_list(Config) ->
- ?line {no_match,_} = sint(mkbin([])),
- ?line {no_match,_} = sint(mkbin([1,2,3])),
- ?line 127 = sint(mkbin([127])),
- ?line -1 = sint(mkbin([255])),
- ?line -128 = sint(mkbin([128])),
- ?line 42 = sint(mkbin([42,255])),
- ?line 127 = sint(mkbin([127,255])).
+ {no_match,_} = sint(mkbin([])),
+ {no_match,_} = sint(mkbin([1,2,3])),
+ 127 = sint(mkbin([127])),
+ -1 = sint(mkbin([255])),
+ -128 = sint(mkbin([128])),
+ 42 = sint(mkbin([42,255])),
+ 127 = sint(mkbin([127,255])).
sint(Bin) ->
case Bin of
@@ -130,7 +130,7 @@ dynamic(Bin, S1, S2, A, B) ->
_Other -> erlang:error(badmatch, [Bin,S1,S2,A,B])
end.
-more_dynamic(doc) -> "Extract integers at different alignments and of different sizes.";
+%% Extract integers at different alignments and of different sizes.
more_dynamic(Config) when is_list(Config) ->
% Unsigned big-endian numbers.
@@ -139,7 +139,7 @@ more_dynamic(Config) when is_list(Config) ->
<<_:SkipBef,Int:N,_:SkipAft>> = Bin,
Int = make_int(List, N, 0)
end,
- ?line more_dynamic1(Unsigned, erlang:md5(mkbin([42]))),
+ more_dynamic1(Unsigned, erlang:md5(mkbin([42]))),
%% Signed big-endian numbers.
Signed = fun(Bin, List, SkipBef, N) ->
@@ -151,10 +151,10 @@ more_dynamic(Config) when is_list(Config) ->
io:format("Bin = ~p,", [Bin]),
io:format("SkipBef = ~p, N = ~p", [SkipBef,N]),
io:format("Expected ~p, got ~p", [Int,Other]),
- ?t:fail()
+ ct:fail(signed_big_endian_fail)
end
end,
- ?line more_dynamic1(Signed, erlang:md5(mkbin([43]))),
+ more_dynamic1(Signed, erlang:md5(mkbin([43]))),
%% Unsigned little-endian numbers.
UnsLittle = fun(Bin, List, SkipBef, N) ->
@@ -162,7 +162,7 @@ more_dynamic(Config) when is_list(Config) ->
<<_:SkipBef,Int:N/little,_:SkipAft>> = Bin,
Int = make_int(big_to_little(List, N), N, 0)
end,
- ?line more_dynamic1(UnsLittle, erlang:md5(mkbin([44]))),
+ more_dynamic1(UnsLittle, erlang:md5(mkbin([44]))),
%% Signed little-endian numbers.
SignLittle = fun(Bin, List, SkipBef, N) ->
@@ -171,7 +171,7 @@ more_dynamic(Config) when is_list(Config) ->
Little = big_to_little(List, N),
Int = make_signed_int(Little, N)
end,
- ?line more_dynamic1(SignLittle, erlang:md5(mkbin([45]))),
+ more_dynamic1(SignLittle, erlang:md5(mkbin([45]))),
ok.
@@ -227,23 +227,23 @@ mkbin(L) when is_list(L) -> list_to_binary(L).
mml(Config) when is_list(Config) ->
- ?line single_byte_binary = mml_choose(<<42>>),
- ?line multi_byte_binary = mml_choose(<<42,43>>).
+ single_byte_binary = mml_choose(<<42>>),
+ multi_byte_binary = mml_choose(<<42,43>>).
mml_choose(<<_A:8>>) -> single_byte_binary;
mml_choose(<<_A:8,_T/binary>>) -> multi_byte_binary.
match_huge_int(Config) when is_list(Config) ->
Sz = 1 bsl 27,
- ?line Bin = <<0:Sz,13:8>>,
- ?line skip_huge_int_1(Sz, Bin),
- ?line 0 = match_huge_int_1(Sz, Bin),
+ Bin = <<0:Sz,13:8>>,
+ skip_huge_int_1(Sz, Bin),
+ 0 = match_huge_int_1(Sz, Bin),
%% Test overflowing the size of an integer field.
- ?line nomatch = overflow_huge_int_skip_32(Bin),
+ nomatch = overflow_huge_int_skip_32(Bin),
case erlang:system_info(wordsize) of
4 ->
- ?line nomatch = overflow_huge_int_32(Bin);
+ nomatch = overflow_huge_int_32(Bin);
8 ->
%% An attempt will be made to allocate heap space for
%% the bignum (which will probably fail); only if the
@@ -251,15 +251,15 @@ match_huge_int(Config) when is_list(Config) ->
%% the binary is too small.
ok
end,
- ?line nomatch = overflow_huge_int_skip_64(Bin),
- ?line nomatch = overflow_huge_int_64(Bin),
+ nomatch = overflow_huge_int_skip_64(Bin),
+ nomatch = overflow_huge_int_64(Bin),
%% Test overflowing the size of an integer field using variables as sizes.
- ?line Sizes = case erlang:system_info(wordsize) of
+ Sizes = case erlang:system_info(wordsize) of
4 -> lists:seq(25, 32);
8 -> []
end ++ lists:seq(50, 64),
- ?line ok = overflow_huge_int_unit128(Bin, Sizes),
+ ok = overflow_huge_int_unit128(Bin, Sizes),
ok.
@@ -326,19 +326,19 @@ overflow_huge_int_64(<<Int:9223372036854775808/unit:128,0,_/binary>>) -> {8,Int}
overflow_huge_int_64(_) -> nomatch.
bignum(Config) when is_list(Config) ->
- ?line Bin = id(<<42,0:1024/unit:8,43>>),
- ?line <<42:1025/little-integer-unit:8,_:8>> = Bin,
- ?line <<_:8,43:1025/integer-unit:8>> = Bin,
+ Bin = id(<<42,0:1024/unit:8,43>>),
+ <<42:1025/little-integer-unit:8,_:8>> = Bin,
+ <<_:8,43:1025/integer-unit:8>> = Bin,
- ?line BignumBin = id(<<0:512/unit:8,258254417031933722623:9/unit:8>>),
- ?line <<258254417031933722623:(512+9)/unit:8>> = BignumBin,
+ BignumBin = id(<<0:512/unit:8,258254417031933722623:9/unit:8>>),
+ <<258254417031933722623:(512+9)/unit:8>> = BignumBin,
erlang:garbage_collect(), %Search for holes in debug-build.
ok.
unaligned_32_bit(Config) when is_list(Config) ->
%% There used to be a risk for heap overflow (fixed in R11B-5).
- ?line L = unaligned_32_bit_1(<<-1:(64*1024)>>),
- ?line unaligned_32_bit_verify(L, 1638).
+ L = unaligned_32_bit_1(<<-1:(64*1024)>>),
+ unaligned_32_bit_verify(L, 1638).
unaligned_32_bit_1(<<1:1,U:32,_:7,T/binary>>) ->
[U|unaligned_32_bit_1(T)];
diff --git a/erts/emulator/test/bs_match_misc_SUITE.erl b/erts/emulator/test/bs_match_misc_SUITE.erl
index e875dc859c..17759d78f3 100644
--- a/erts/emulator/test/bs_match_misc_SUITE.erl
+++ b/erts/emulator/test/bs_match_misc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -19,17 +19,18 @@
%%
-module(bs_match_misc_SUITE).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
+-export([all/0, suite/0,
bound_var/1,bound_tail/1,t_float/1,little_float/1,sean/1,
kenneth/1,encode_binary/1,native/1,happi/1,
size_var/1,wiger/1,x0_context/1,huge_float_field/1,
writable_binary_matched/1,otp_7198/1,unordered_bindings/1,
float_middle_endian/1]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {seconds, 10}}].
all() ->
[bound_var, bound_tail, t_float, little_float, sean,
@@ -37,39 +38,24 @@ all() ->
x0_context, huge_float_field, writable_binary_matched,
otp_7198, unordered_bindings, float_middle_endian].
-groups() ->
- [].
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-bound_var(doc) -> "Test matching of bound variables.";
+%% Test matching of bound variables.
bound_var(Config) when is_list(Config) ->
- ?line ok = bound_var(42, 13, <<42,13>>),
- ?line nope = bound_var(42, 13, <<42,255>>),
- ?line nope = bound_var(42, 13, <<154,255>>),
+ ok = bound_var(42, 13, <<42,13>>),
+ nope = bound_var(42, 13, <<42,255>>),
+ nope = bound_var(42, 13, <<154,255>>),
ok.
bound_var(A, B, <<A:8,B:8>>) -> ok;
bound_var(_, _, _) -> nope.
-bound_tail(doc) -> "Test matching of a bound tail.";
+%% Test matching of a bound tail.
bound_tail(Config) when is_list(Config) ->
- ?line ok = bound_tail(<<>>, <<13,14>>),
- ?line ok = bound_tail(<<2,3>>, <<1,1,2,3>>),
- ?line nope = bound_tail(<<2,3>>, <<1,1,2,7>>),
- ?line nope = bound_tail(<<2,3>>, <<1,1,2,3,4>>),
- ?line nope = bound_tail(<<2,3>>, <<>>),
+ ok = bound_tail(<<>>, <<13,14>>),
+ ok = bound_tail(<<2,3>>, <<1,1,2,3>>),
+ nope = bound_tail(<<2,3>>, <<1,1,2,7>>),
+ nope = bound_tail(<<2,3>>, <<1,1,2,3,4>>),
+ nope = bound_tail(<<2,3>>, <<>>),
ok.
bound_tail(T, <<_:16,T/binary>>) -> ok;
@@ -79,26 +65,26 @@ t_float(Config) when is_list(Config) ->
F = f1(),
G = f_one(),
- ?line G = match_float(<<63,128,0,0>>, 32, 0),
- ?line G = match_float(<<63,240,0,0,0,0,0,0>>, 64, 0),
+ G = match_float(<<63,128,0,0>>, 32, 0),
+ G = match_float(<<63,240,0,0,0,0,0,0>>, 64, 0),
- ?line fcmp(F, match_float(<<F:32/float>>, 32, 0)),
- ?line fcmp(F, match_float(<<F:64/float>>, 64, 0)),
- ?line fcmp(F, match_float(<<1:1,F:32/float,127:7>>, 32, 1)),
- ?line fcmp(F, match_float(<<1:1,F:64/float,127:7>>, 64, 1)),
- ?line fcmp(F, match_float(<<1:13,F:32/float,127:3>>, 32, 13)),
- ?line fcmp(F, match_float(<<1:13,F:64/float,127:3>>, 64, 13)),
+ fcmp(F, match_float(<<F:32/float>>, 32, 0)),
+ fcmp(F, match_float(<<F:64/float>>, 64, 0)),
+ fcmp(F, match_float(<<1:1,F:32/float,127:7>>, 32, 1)),
+ fcmp(F, match_float(<<1:1,F:64/float,127:7>>, 64, 1)),
+ fcmp(F, match_float(<<1:13,F:32/float,127:3>>, 32, 13)),
+ fcmp(F, match_float(<<1:13,F:64/float,127:3>>, 64, 13)),
- ?line {'EXIT',{{badmatch,_},_}} = (catch match_float(<<0,0>>, 16, 0)),
- ?line {'EXIT',{{badmatch,_},_}} = (catch match_float(<<0,0>>, 16#7fffffff, 0)),
+ {'EXIT',{{badmatch,_},_}} = (catch match_float(<<0,0>>, 16, 0)),
+ {'EXIT',{{badmatch,_},_}} = (catch match_float(<<0,0>>, 16#7fffffff, 0)),
ok.
float_middle_endian(Config) when is_list(Config) ->
F = 9007199254740990.0, % turns to -NaN when word-swapped
- ?line fcmp(F, match_float(<<F:64/float>>, 64, 0)),
- ?line fcmp(F, match_float(<<1:1,F:64/float,127:7>>, 64, 1)),
- ?line fcmp(F, match_float(<<1:13,F:64/float,127:3>>, 64, 13)),
+ fcmp(F, match_float(<<F:64/float>>, 64, 0)),
+ fcmp(F, match_float(<<1:1,F:64/float,127:7>>, 64, 1)),
+ fcmp(F, match_float(<<1:13,F:64/float,127:3>>, 64, 13)),
ok.
@@ -115,15 +101,15 @@ little_float(Config) when is_list(Config) ->
F = f2(),
G = f_one(),
- ?line G = match_float_little(<<0,0,0,0,0,0,240,63>>, 64, 0),
- ?line G = match_float_little(<<0,0,128,63>>, 32, 0),
+ G = match_float_little(<<0,0,0,0,0,0,240,63>>, 64, 0),
+ G = match_float_little(<<0,0,128,63>>, 32, 0),
- ?line fcmp(F, match_float_little(<<F:32/float-little>>, 32, 0)),
- ?line fcmp(F, match_float_little(<<F:64/float-little>>, 64, 0)),
- ?line fcmp(F, match_float_little(<<1:1,F:32/float-little,127:7>>, 32, 1)),
- ?line fcmp(F, match_float_little(<<1:1,F:64/float-little,127:7>>, 64, 1)),
- ?line fcmp(F, match_float_little(<<1:13,F:32/float-little,127:3>>, 32, 13)),
- ?line fcmp(F, match_float_little(<<1:13,F:64/float-little,127:3>>, 64, 13)),
+ fcmp(F, match_float_little(<<F:32/float-little>>, 32, 0)),
+ fcmp(F, match_float_little(<<F:64/float-little>>, 64, 0)),
+ fcmp(F, match_float_little(<<1:1,F:32/float-little,127:7>>, 32, 1)),
+ fcmp(F, match_float_little(<<1:1,F:64/float-little,127:7>>, 64, 1)),
+ fcmp(F, match_float_little(<<1:13,F:32/float-little,127:3>>, 32, 13)),
+ fcmp(F, match_float_little(<<1:13,F:64/float-little,127:3>>, 64, 13)),
ok.
@@ -151,16 +137,16 @@ f_one() ->
1.0.
sean(Config) when is_list(Config) ->
- ?line small = sean1(<<>>),
- ?line small = sean1(<<1>>),
- ?line small = sean1(<<1,2>>),
- ?line small = sean1(<<1,2,3>>),
- ?line large = sean1(<<1,2,3,4>>),
-
- ?line small = sean1(<<4>>),
- ?line small = sean1(<<4,5>>),
- ?line small = sean1(<<4,5,6>>),
- ?line {'EXIT',{function_clause,_}} = (catch sean1(<<4,5,6,7>>)),
+ small = sean1(<<>>),
+ small = sean1(<<1>>),
+ small = sean1(<<1,2>>),
+ small = sean1(<<1,2,3>>),
+ large = sean1(<<1,2,3,4>>),
+
+ small = sean1(<<4>>),
+ small = sean1(<<4,5>>),
+ small = sean1(<<4,5,6>>),
+ {'EXIT',{function_clause,_}} = (catch sean1(<<4,5,6,7>>)),
ok.
sean1(<<B/binary>>) when byte_size(B) < 4 -> small;
@@ -292,28 +278,28 @@ getBase64Char(_Else) ->
-define(M(F), <<F>> = <<F>>).
native(Config) when is_list(Config) ->
- ?line ?M(3.14:64/native-float),
- ?line ?M(333:16/native),
- ?line ?M(38658345:32/native),
+ ?M(3.14:64/native-float),
+ ?M(333:16/native),
+ ?M(38658345:32/native),
case <<1:16/native>> of
<<0,1>> -> native_big();
<<1,0>> -> native_little()
end.
native_big() ->
- ?line <<37.33:64/native-float>> = <<37.33:64/big-float>>,
- ?line <<3974:16/native-integer>> = <<3974:16/big-integer>>,
+ <<37.33:64/native-float>> = <<37.33:64/big-float>>,
+ <<3974:16/native-integer>> = <<3974:16/big-integer>>,
{comment,"Big endian"}.
native_little() ->
- ?line <<37869.32343:64/native-float>> = <<37869.32343:64/little-float>>,
- ?line <<7974:16/native-integer>> = <<7974:16/little-integer>>,
+ <<37869.32343:64/native-float>> = <<37869.32343:64/little-float>>,
+ <<7974:16/native-integer>> = <<7974:16/little-integer>>,
{comment,"Little endian"}.
happi(Config) when is_list(Config) ->
Bin = <<".123">>,
- ?line <<"123">> = lex_digits1(Bin, 1, []),
- ?line <<"123">> = lex_digits2(Bin, 1, []),
+ <<"123">> = lex_digits1(Bin, 1, []),
+ <<"123">> = lex_digits2(Bin, 1, []),
ok.
lex_digits1(<<$., Rest/binary>>,_Val,_Acc) ->
@@ -334,16 +320,16 @@ dec(A) ->
A-$0.
size_var(Config) when is_list(Config) ->
- ?line {<<45>>,<<>>} = split(<<1:16,45>>),
- ?line {<<45>>,<<46,47>>} = split(<<1:16,45,46,47>>),
- ?line {<<45,46>>,<<47>>} = split(<<2:16,45,46,47>>),
+ {<<45>>,<<>>} = split(<<1:16,45>>),
+ {<<45>>,<<46,47>>} = split(<<1:16,45,46,47>>),
+ {<<45,46>>,<<47>>} = split(<<2:16,45,46,47>>),
- ?line {<<45,46,47>>,<<48>>} = split_2(<<16:8,3:16,45,46,47,48>>),
+ {<<45,46,47>>,<<48>>} = split_2(<<16:8,3:16,45,46,47,48>>),
- ?line {<<45,46>>,<<47>>} = split(2, <<2:16,45,46,47>>),
- ?line {'EXIT',{function_clause,_}} = (catch split(42, <<2:16,45,46,47>>)),
+ {<<45,46>>,<<47>>} = split(2, <<2:16,45,46,47>>),
+ {'EXIT',{function_clause,_}} = (catch split(42, <<2:16,45,46,47>>)),
- ?line <<"cdef">> = skip(<<2:8,"abcdef">>),
+ <<"cdef">> = skip(<<2:8,"abcdef">>),
ok.
@@ -359,11 +345,11 @@ split_2(<<N0:8,N:N0,B:N/binary,T/binary>>) ->
skip(<<N:8,_:N/binary,T/binary>>) -> T.
wiger(Config) when is_list(Config) ->
- ?line ok1 = wcheck(<<3>>),
- ?line ok2 = wcheck(<<1,2,3>>),
- ?line ok3 = wcheck(<<4>>),
- ?line {error,<<1,2,3,4>>} = wcheck(<<1,2,3,4>>),
- ?line {error,<<>>} = wcheck(<<>>),
+ ok1 = wcheck(<<3>>),
+ ok2 = wcheck(<<1,2,3>>),
+ ok3 = wcheck(<<4>>),
+ {error,<<1,2,3,4>>} = wcheck(<<1,2,3,4>>),
+ {error,<<>>} = wcheck(<<>>),
ok.
wcheck(<<A>>) when A==3->
@@ -396,24 +382,24 @@ x0_2(_, Bin) ->
x0_3(_, Bin) ->
case Bin of
- <<_:72,7:8,_/binary>> ->
- ?line ?t:fail();
- <<_:64,0:16,_/binary>> ->
- ?line ?t:fail();
- <<_:64,42:16,123456:32,_/binary>> ->
- ok
+ <<_:72,7:8,_/binary>> ->
+ ct:fail(bs_matched_1);
+ <<_:64,0:16,_/binary>> ->
+ ct:fail(bs_matched_2);
+ <<_:64,42:16,123456:32,_/binary>> ->
+ ok
end.
huge_float_field(Config) when is_list(Config) ->
Sz = 1 bsl 27,
- ?line Bin = <<0:Sz>>,
+ Bin = <<0:Sz>>,
- ?line nomatch = overflow_huge_float_skip_32(Bin),
- ?line nomatch = overflow_huge_float_32(Bin),
+ nomatch = overflow_huge_float_skip_32(Bin),
+ nomatch = overflow_huge_float_32(Bin),
- ?line ok = overflow_huge_float(Bin, lists:seq(25, 32)++lists:seq(50, 64)),
- ?line ok = overflow_huge_float_unit128(Bin, lists:seq(25, 32)++lists:seq(50, 64)),
+ ok = overflow_huge_float(Bin, lists:seq(25, 32)++lists:seq(50, 64)),
+ ok = overflow_huge_float_unit128(Bin, lists:seq(25, 32)++lists:seq(50, 64)),
ok.
overflow_huge_float_skip_32(<<_:4294967296/float,0,_/binary>>) -> 1; % 1 bsl 32
@@ -455,15 +441,15 @@ overflow_huge_float(_, []) -> ok.
overflow_huge_float_unit128(Bin, [Sz0|Sizes]) ->
Sz = id(1 bsl Sz0),
case Bin of
- <<_:Sz/float-unit:128,0,_/binary>> ->
- {error,Sz};
- _ ->
- case Bin of
- <<Var:Sz/float-unit:128,0,_/binary>> ->
- {error,Sz,Var};
- _ ->
- overflow_huge_float_unit128(Bin, Sizes)
- end
+ <<_:Sz/float-unit:128,0,_/binary>> ->
+ {error,Sz};
+ _ ->
+ case Bin of
+ <<Var:Sz/float-unit:128,0,_/binary>> ->
+ {error,Sz,Var};
+ _ ->
+ overflow_huge_float_unit128(Bin, Sizes)
+ end
end;
overflow_huge_float_unit128(_, []) -> ok.
@@ -473,25 +459,24 @@ overflow_huge_float_unit128(_, []) -> ok.
%%
writable_binary_matched(Config) when is_list(Config) ->
- ?line WritableBin = create_writeable_binary(),
- ?line writable_binary_matched(WritableBin, WritableBin, 500).
+ WritableBin = create_writeable_binary(),
+ writable_binary_matched(WritableBin, WritableBin, 500).
writable_binary_matched(<<0>>, _, N) ->
- if
- N =:= 0 -> ok;
- true ->
- put(grow_heap, [N|get(grow_heap)]),
- ?line WritableBin = create_writeable_binary(),
- ?line writable_binary_matched(WritableBin, WritableBin, N-1)
+ if N =:= 0 -> ok;
+ true ->
+ put(grow_heap, [N|get(grow_heap)]),
+ WritableBin = create_writeable_binary(),
+ writable_binary_matched(WritableBin, WritableBin, N-1)
end;
writable_binary_matched(<<B:8,T/binary>>, WritableBin0, N) ->
- ?line WritableBin = writable_binary(WritableBin0, B),
+ WritableBin = writable_binary(WritableBin0, B),
writable_binary_matched(T, WritableBin, N).
writable_binary(WritableBin0, B) when is_binary(WritableBin0) ->
%% Heavy append to force the binary to move.
- ?line WritableBin = <<WritableBin0/binary,0:(size(WritableBin0))/unit:8,B>>,
- ?line id(<<(id(0)):128/unit:8>>),
+ WritableBin = <<WritableBin0/binary,0:(size(WritableBin0))/unit:8,B>>,
+ id(<<(id(0)):128/unit:8>>),
WritableBin.
create_writeable_binary() ->
@@ -502,7 +487,7 @@ otp_7198(Config) when is_list(Config) ->
%% increase the number of saved positions, the thing word was not updated
%% to account for the new size. Therefore, if there was a garbage collection,
%% the new slots would be included in the garbage collection.
- ?line [do_otp_7198(FillerSize) || FillerSize <- lists:seq(0, 256)],
+ [do_otp_7198(FillerSize) || FillerSize <- lists:seq(0, 256)],
ok.
do_otp_7198(FillerSize) ->
@@ -512,8 +497,7 @@ do_otp_7198(FillerSize) ->
{'DOWN',Ref,process,Pid,normal} ->
ok;
{'DOWN',Ref,process,Pid,Reason} ->
- io:format("unexpected: ~p", [Reason]),
- ?line ?t:fail()
+ ct:fail("unexpected: ~p", [Reason])
end.
do_otp_7198_test(_) ->
@@ -540,27 +524,27 @@ otp_7198_scan(<<>>, TokAcc) ->
otp_7198_scan(<<D, Z, Rest/binary>>, TokAcc) when
(D =:= $D orelse D =:= $d) and
((Z =:= $\s) or (Z =:= $() or (Z =:= $))) ->
- otp_7198_scan(<<Z, Rest/binary>>, ['AND' | TokAcc]);
+ otp_7198_scan(<<Z, Rest/binary>>, ['AND' | TokAcc]);
otp_7198_scan(<<D>>, TokAcc) when
(D =:= $D) or (D =:= $d) ->
- otp_7198_scan(<<>>, ['AND' | TokAcc]);
+ otp_7198_scan(<<>>, ['AND' | TokAcc]);
otp_7198_scan(<<N, Z, Rest/binary>>, TokAcc) when
(N =:= $N orelse N =:= $n) and
((Z =:= $\s) or (Z =:= $() or (Z =:= $))) ->
- otp_7198_scan(<<Z, Rest/binary>>, ['NOT' | TokAcc]);
+ otp_7198_scan(<<Z, Rest/binary>>, ['NOT' | TokAcc]);
otp_7198_scan(<<C, Rest/binary>>, TokAcc) when
(C >= $A) and (C =< $Z);
(C >= $a) and (C =< $z);
(C >= $0) and (C =< $9) ->
- case Rest of
- <<$:, R/binary>> ->
- otp_7198_scan(R, [{'FIELD', C} | TokAcc]);
- _ ->
- otp_7198_scan(Rest, [{'KEYWORD', C} | TokAcc])
- end.
+ case Rest of
+ <<$:, R/binary>> ->
+ otp_7198_scan(R, [{'FIELD', C} | TokAcc]);
+ _ ->
+ otp_7198_scan(Rest, [{'KEYWORD', C} | TokAcc])
+ end.
unordered_bindings(Config) when is_list(Config) ->
{<<1,2,3,4>>,<<42,42>>,<<3,3,3>>} =
diff --git a/erts/emulator/test/bs_match_tail_SUITE.erl b/erts/emulator/test/bs_match_tail_SUITE.erl
index 58b0d3fef6..cbebc554c7 100644
--- a/erts/emulator/test/bs_match_tail_SUITE.erl
+++ b/erts/emulator/test/bs_match_tail_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -21,61 +21,46 @@
-module(bs_match_tail_SUITE).
-author('[email protected]').
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,aligned/1,unaligned/1,zero_tail/1]).
+-export([all/0, suite/0,
+ aligned/1,unaligned/1,zero_tail/1]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[aligned, unaligned, zero_tail].
-groups() ->
- [].
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-aligned(doc) -> "Test aligned tails.";
+%% Test aligned tails.
aligned(Config) when is_list(Config) ->
- ?line Tail1 = mkbin([]),
- ?line {258,Tail1} = al_get_tail_used(mkbin([1,2])),
- ?line Tail2 = mkbin(lists:seq(1, 127)),
- ?line {35091,Tail2} = al_get_tail_used(mkbin([137,19|Tail2])),
-
- ?line 64896 = al_get_tail_unused(mkbin([253,128])),
- ?line 64895 = al_get_tail_unused(mkbin([253,127|lists:seq(42, 255)])),
-
- ?line Tail3 = mkbin(lists:seq(0, 19)),
- ?line {0,Tail1} = get_dyn_tail_used(Tail1, 0),
- ?line {0,Tail3} = get_dyn_tail_used(mkbin([Tail3]), 0),
- ?line {73,Tail3} = get_dyn_tail_used(mkbin([73|Tail3]), 8),
-
- ?line 0 = get_dyn_tail_unused(mkbin([]), 0),
- ?line 233 = get_dyn_tail_unused(mkbin([233]), 8),
- ?line 23 = get_dyn_tail_unused(mkbin([23,22,2]), 8),
+ Tail1 = mkbin([]),
+ {258,Tail1} = al_get_tail_used(mkbin([1,2])),
+ Tail2 = mkbin(lists:seq(1, 127)),
+ {35091,Tail2} = al_get_tail_used(mkbin([137,19|Tail2])),
+
+ 64896 = al_get_tail_unused(mkbin([253,128])),
+ 64895 = al_get_tail_unused(mkbin([253,127|lists:seq(42, 255)])),
+
+ Tail3 = mkbin(lists:seq(0, 19)),
+ {0,Tail1} = get_dyn_tail_used(Tail1, 0),
+ {0,Tail3} = get_dyn_tail_used(mkbin([Tail3]), 0),
+ {73,Tail3} = get_dyn_tail_used(mkbin([73|Tail3]), 8),
+
+ 0 = get_dyn_tail_unused(mkbin([]), 0),
+ 233 = get_dyn_tail_unused(mkbin([233]), 8),
+ 23 = get_dyn_tail_unused(mkbin([23,22,2]), 8),
ok.
al_get_tail_used(<<A:16,T/binary>>) -> {A,T}.
al_get_tail_unused(<<A:16,_/binary>>) -> A.
-unaligned(doc) -> "Test that an non-aligned tail cannot be matched out.";
+%% Test that an non-aligned tail cannot be matched out.
unaligned(Config) when is_list(Config) ->
- ?line {'EXIT',{function_clause,_}} = (catch get_tail_used(mkbin([42]))),
- ?line {'EXIT',{{badmatch,_},_}} = (catch get_dyn_tail_used(mkbin([137]), 3)),
- ?line {'EXIT',{function_clause,_}} = (catch get_tail_unused(mkbin([42,33]))),
- ?line {'EXIT',{{badmatch,_},_}} = (catch get_dyn_tail_unused(mkbin([44]), 7)),
+ {'EXIT',{function_clause,_}} = (catch get_tail_used(mkbin([42]))),
+ {'EXIT',{{badmatch,_},_}} = (catch get_dyn_tail_used(mkbin([137]), 3)),
+ {'EXIT',{function_clause,_}} = (catch get_tail_unused(mkbin([42,33]))),
+ {'EXIT',{{badmatch,_},_}} = (catch get_dyn_tail_unused(mkbin([44]), 7)),
ok.
get_tail_used(<<A:1,T/binary>>) -> {A,T}.
@@ -90,11 +75,11 @@ get_dyn_tail_unused(Bin, Sz) ->
<<A:Sz,_/binary>> = Bin,
A.
-zero_tail(doc) -> "Test that zero tails are tested correctly.";
+%% Test that zero tails are tested correctly.
zero_tail(Config) when is_list(Config) ->
- ?line 7 = (catch test_zero_tail(mkbin([7]))),
- ?line {'EXIT',{function_clause,_}} = (catch test_zero_tail(mkbin([1,2]))),
- ?line {'EXIT',{function_clause,_}} = (catch test_zero_tail2(mkbin([1,2,3]))),
+ 7 = (catch test_zero_tail(mkbin([7]))),
+ {'EXIT',{function_clause,_}} = (catch test_zero_tail(mkbin([1,2]))),
+ {'EXIT',{function_clause,_}} = (catch test_zero_tail2(mkbin([1,2,3]))),
ok.
test_zero_tail(<<A:8>>) -> A.
@@ -102,7 +87,3 @@ test_zero_tail(<<A:8>>) -> A.
test_zero_tail2(<<_A:4,_B:4>>) -> ok.
mkbin(L) when is_list(L) -> list_to_binary(L).
-
-
-
-
diff --git a/erts/emulator/test/bs_utf_SUITE.erl b/erts/emulator/test/bs_utf_SUITE.erl
index 0625c22163..a344f5c456 100644
--- a/erts/emulator/test/bs_utf_SUITE.erl
+++ b/erts/emulator/test/bs_utf_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,52 +20,28 @@
-module(bs_utf_SUITE).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2,end_per_testcase/2,
+-export([all/0, suite/0,
utf8_roundtrip/1,utf16_roundtrip/1,utf32_roundtrip/1,
utf8_illegal_sequences/1,utf16_illegal_sequences/1,
utf32_illegal_sequences/1,
bad_construction/1]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
--define(FAIL(Expr), ?line fail_check(catch Expr, ??Expr, [])).
+-define(FAIL(Expr), fail_check(catch Expr, ??Expr, [])).
-init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- Dog = ?t:timetrap(?t:minutes(6)),
- [{watchdog,Dog}|Config].
-
-end_per_testcase(_Func, Config) ->
- Dog = ?config(watchdog, Config),
- ?t:timetrap_cancel(Dog).
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 6}}].
all() ->
[utf8_roundtrip, utf16_roundtrip, utf32_roundtrip,
utf8_illegal_sequences, utf16_illegal_sequences,
utf32_illegal_sequences, bad_construction].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
utf8_roundtrip(Config) when is_list(Config) ->
- ?line utf8_roundtrip(0, 16#D7FF),
- ?line utf8_roundtrip(16#E000, 16#10FFFF),
+ utf8_roundtrip(0, 16#D7FF),
+ utf8_roundtrip(16#E000, 16#10FFFF),
ok.
utf8_roundtrip(First, Last) when First =< Last ->
@@ -83,10 +59,9 @@ utf16_roundtrip(Config) when is_list(Config) ->
Big = fun utf16_big_roundtrip/1,
Little = fun utf16_little_roundtrip/1,
PidRefs = [spawn_monitor(fun() ->
- do_utf16_roundtrip(Fun)
- end) || Fun <- [Big,Little]],
- [receive {'DOWN',Ref,process,Pid,Reason} -> normal=Reason end ||
- {Pid,Ref} <- PidRefs],
+ do_utf16_roundtrip(Fun)
+ end) || Fun <- [Big,Little]],
+ [receive {'DOWN',Ref,process,Pid,Reason} -> normal=Reason end || {Pid,Ref} <- PidRefs],
ok.
do_utf16_roundtrip(Fun) ->
@@ -154,20 +129,20 @@ utf32_little_roundtrip(Char) ->
ok.
utf8_illegal_sequences(Config) when is_list(Config) ->
- ?line fail_range(16#10FFFF+1, 16#10FFFF+512), %Too large.
- ?line fail_range(16#D800, 16#DFFF), %Reserved for UTF-16.
+ fail_range(16#10FFFF+1, 16#10FFFF+512), %Too large.
+ fail_range(16#D800, 16#DFFF), %Reserved for UTF-16.
%% Illegal first character.
- ?line [fail(<<I,16#8F,16#8F,16#8F>>) || I <- lists:seq(16#80, 16#BF)],
+ [fail(<<I,16#8F,16#8F,16#8F>>) || I <- lists:seq(16#80, 16#BF)],
%% Short sequences.
- ?line short_sequences(16#80, 16#10FFFF),
+ short_sequences(16#80, 16#10FFFF),
%% Overlong sequences. (Using more bytes than necessary
%% is not allowed.)
- ?line overlong(0, 127, 2),
- ?line overlong(128, 16#7FF, 3),
- ?line overlong(16#800, 16#FFFF, 4),
+ overlong(0, 127, 2),
+ overlong(128, 16#7FF, 3),
+ overlong(16#800, 16#FFFF, 4),
ok.
fail_range(Char, End) when Char =< End ->
@@ -187,9 +162,9 @@ short_sequences(Char, End) ->
short_sequences_1(Char, Step, End) when Char =< End ->
CharEnd = lists:min([Char+Step-1,End]),
[spawn_monitor(fun() ->
- io:format("~p - ~p\n", [Char,CharEnd]),
- do_short_sequences(Char, CharEnd)
- end)|short_sequences_1(Char+Step, Step, End)];
+ io:format("~p - ~p\n", [Char,CharEnd]),
+ do_short_sequences(Char, CharEnd)
+ end)|short_sequences_1(Char+Step, Step, End)];
short_sequences_1(_, _, _) -> [].
do_short_sequences(Char, End) when Char =< End ->
@@ -228,9 +203,9 @@ overlong(_, _, _) -> ok.
overlong(Char, NumBytes) when NumBytes < 5 ->
case int_to_utf8(Char, NumBytes) of
<<Char/utf8>>=Bin ->
- ?t:fail({illegal_encoding_accepted,Bin,Char});
+ ct:fail({illegal_encoding_accepted,Bin,Char});
<<OtherChar/utf8>>=Bin ->
- ?t:fail({illegal_encoding_accepted,Bin,Char,OtherChar});
+ ct:fail({illegal_encoding_accepted,Bin,Char,OtherChar});
_ -> ok
end,
overlong(Char, NumBytes+1);
@@ -241,16 +216,16 @@ fail(Bin) ->
fail_1(make_unaligned(Bin)).
fail_1(<<Char/utf8>>=Bin) ->
- ?t:fail({illegal_encoding_accepted,Bin,Char});
+ ct:fail({illegal_encoding_accepted,Bin,Char});
fail_1(_) -> ok.
utf16_illegal_sequences(Config) when is_list(Config) ->
- ?line utf16_fail_range(16#10FFFF+1, 16#10FFFF+512), %Too large.
- ?line utf16_fail_range(16#D800, 16#DFFF), %Reserved for UTF-16.
+ utf16_fail_range(16#10FFFF+1, 16#10FFFF+512), %Too large.
+ utf16_fail_range(16#D800, 16#DFFF), %Reserved for UTF-16.
- ?line lonely_hi_surrogate(16#D800, 16#DFFF),
- ?line leading_lo_surrogate(16#DC00, 16#DFFF),
+ lonely_hi_surrogate(16#D800, 16#DFFF),
+ leading_lo_surrogate(16#DC00, 16#DFFF),
ok.
@@ -265,9 +240,9 @@ lonely_hi_surrogate(Char, End) when Char =< End ->
BinLittle = <<Char:16/little>>,
case {BinBig,BinLittle} of
{<<Bad/big-utf16>>,_} ->
- ?t:fail({lonely_hi_surrogate_accepted,Bad});
+ ct:fail({lonely_hi_surrogate_accepted,Bad});
{_,<<Bad/little-utf16>>} ->
- ?t:fail({lonely_hi_surrogate_accepted,Bad});
+ ct:fail({lonely_hi_surrogate_accepted,Bad});
{_,_} ->
ok
end,
@@ -284,9 +259,9 @@ leading_lo_surrogate(HiSurr, LoSurr, End) when LoSurr =< End ->
BinLittle = <<HiSurr:16/little,LoSurr:16/little>>,
case {BinBig,BinLittle} of
{<<Bad/big-utf16,_/bits>>,_} ->
- ?t:fail({leading_lo_surrogate_accepted,Bad});
+ ct:fail({leading_lo_surrogate_accepted,Bad});
{_,<<Bad/little-utf16,_/bits>>} ->
- ?t:fail({leading_lo_surrogate_accepted,Bad});
+ ct:fail({leading_lo_surrogate_accepted,Bad});
{_,_} ->
ok
end,
@@ -294,20 +269,20 @@ leading_lo_surrogate(HiSurr, LoSurr, End) when LoSurr =< End ->
leading_lo_surrogate(_, _, _) -> ok.
utf32_illegal_sequences(Config) when is_list(Config) ->
- ?line utf32_fail_range(16#10FFFF+1, 16#10FFFF+512), %Too large.
- ?line utf32_fail_range(16#D800, 16#DFFF), %Reserved for UTF-16.
- ?line utf32_fail_range(-100, -1),
+ utf32_fail_range(16#10FFFF+1, 16#10FFFF+512), %Too large.
+ utf32_fail_range(16#D800, 16#DFFF), %Reserved for UTF-16.
+ utf32_fail_range(-100, -1),
ok.
utf32_fail_range(Char, End) when Char =< End ->
{'EXIT',_} = (catch <<Char/big-utf32>>),
{'EXIT',_} = (catch <<Char/little-utf32>>),
case {<<Char:32>>,<<Char:32/little>>} of
- {<<Unexpected/utf32>>,_} ->
- ?line ?t:fail(Unexpected);
- {_,<<Unexpected/little-utf32>>} ->
- ?line ?t:fail(Unexpected);
- {_,_} -> ok
+ {<<Unexpected/utf32>>,_} ->
+ ct:fail(Unexpected);
+ {_,<<Unexpected/little-utf32>>} ->
+ ct:fail(Unexpected);
+ {_,_} -> ok
end,
utf32_fail_range(Char+1, End);
utf32_fail_range(_, _) -> ok.
@@ -387,14 +362,14 @@ fail_check({'EXIT',{badarg,_}}, Str, Vars) ->
try evaluate(Str, Vars) of
Res ->
io:format("Interpreted result: ~p", [Res]),
- ?t:fail(did_not_fail_in_intepreted_code)
+ ct:fail(did_not_fail_in_intepreted_code)
catch
error:badarg ->
ok
end;
fail_check(Res, _, _) ->
io:format("Compiled result: ~p", [Res]),
- ?t:fail(did_not_fail_in_compiled_code).
+ ct:fail(did_not_fail_in_compiled_code).
evaluate(Str, Vars) ->
{ok,Tokens,_} =
@@ -406,4 +381,3 @@ evaluate(Str, Vars) ->
end.
id(I) -> I.
-
diff --git a/erts/emulator/test/busy_port_SUITE.erl b/erts/emulator/test/busy_port_SUITE.erl
index 6a2588aadd..7094cee992 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-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,8 +20,7 @@
-module(busy_port_SUITE).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,end_per_testcase/2,
+-export([all/0, suite/0, end_per_testcase/2,
io_to_busy/1, message_order/1, send_3/1,
system_monitor/1, no_trap_exit/1,
no_trap_exit_unlinked/1, trap_exit/1, multiple_writers/1,
@@ -29,12 +28,14 @@
-compile(export_all).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
%% Internal exports.
-export([init/2]).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 4}}].
all() ->
[io_to_busy, message_order, send_3, system_monitor,
@@ -43,21 +44,6 @@ all() ->
scheduling_delay_busy,scheduling_delay_busy_nosuspend,
scheduling_busy_link].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
end_per_testcase(_Case, Config) when is_list(Config) ->
case whereis(busy_drv_server) of
undefined ->
@@ -76,17 +62,14 @@ end_per_testcase(_Case, Config) when is_list(Config) ->
%% Tests I/O operations to a busy port, to make sure a suspended send
%% operation is correctly restarted. This used to crash Beam.
-io_to_busy(suite) -> [];
io_to_busy(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(30)),
+ ct:timetrap({seconds, 30}),
- ?line start_busy_driver(Config),
- ?line process_flag(trap_exit, true),
- ?line Writer = fun_spawn(fun writer/0),
- ?line Generator = fun_spawn(fun() -> generator(100, Writer) end),
- ?line wait_for([Writer, Generator]),
-
- ?line test_server:timetrap_cancel(Dog),
+ start_busy_driver(Config),
+ process_flag(trap_exit, true),
+ Writer = fun_spawn(fun writer/0),
+ Generator = fun_spawn(fun() -> generator(100, Writer) end),
+ wait_for([Writer, Generator]),
ok.
generator(N, Writer) ->
@@ -130,27 +113,24 @@ forget(_) ->
%% Test the interaction of busy ports and message sending.
%% This used to cause the wrong message to be received.
-message_order(suite) -> {req, dynamic_loading};
message_order(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
-
- ?line start_busy_driver(Config),
- ?line Self = self(),
- ?line Busy = fun_spawn(fun () -> send_to_busy_1(Self) end),
- ?line receive after 1000 -> ok end,
- ?line Busy ! first,
- ?line Busy ! second,
- ?line receive after 1 -> ok end,
- ?line unlock_slave(),
- ?line Busy ! third,
- ?line receive
- {Busy, first} ->
- ok;
- Other ->
- test_server:fail({unexpected_message, Other})
- end,
-
- ?line test_server:timetrap_cancel(Dog),
+ ct:timetrap({seconds, 10}),
+
+ start_busy_driver(Config),
+ Self = self(),
+ Busy = fun_spawn(fun () -> send_to_busy_1(Self) end),
+ receive after 1000 -> ok end,
+ Busy ! first,
+ Busy ! second,
+ receive after 1 -> ok end,
+ unlock_slave(),
+ Busy ! third,
+ receive
+ {Busy, first} ->
+ ok;
+ Other ->
+ ct:fail({unexpected_message, Other})
+ end,
ok.
send_to_busy_1(Parent) ->
@@ -164,80 +144,64 @@ send_to_busy_1(Parent) ->
end.
%% Test the bif send/3
-send_3(suite) -> {req,dynamic_loading};
-send_3(doc) -> ["Test the BIF send/3"];
send_3(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
+ ct:timetrap({seconds, 10}),
%%
- ?line start_busy_driver(Config),
- ?line {Owner,Slave} = get_slave(),
- ?line ok = erlang:send(Slave, {Owner,{command,"set busy"}},
- [nosuspend]),
+ start_busy_driver(Config),
+ {Owner,Slave} = get_slave(),
+ ok = erlang:send(Slave, {Owner,{command,"set busy"}}, [nosuspend]),
receive after 100 -> ok end, % ensure command reached port
- ?line nosuspend = erlang:send(Slave, {Owner,{command,"busy"}},
- [nosuspend]),
- ?line unlock_slave(),
- ?line ok = erlang:send(Slave, {Owner,{command,"not busy"}},
- [nosuspend]),
- ?line ok = command(stop),
- %%
- ?line test_server:timetrap_cancel(Dog),
+ nosuspend = erlang:send(Slave, {Owner,{command,"busy"}}, [nosuspend]),
+ unlock_slave(),
+ ok = erlang:send(Slave, {Owner,{command,"not busy"}}, [nosuspend]),
+ ok = command(stop),
ok.
%% Test the erlang:system_monitor(Pid, [busy_port])
-system_monitor(suite) -> {req,dynamic_loading};
-system_monitor(doc) -> ["Test erlang:system_monitor({Pid,[busy_port]})."];
system_monitor(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Self = self(),
+ ct:timetrap({seconds, 10}),
+ Self = self(),
%%
- ?line OldMonitor = erlang:system_monitor(Self, [busy_port]),
- ?line {Self,[busy_port]} = erlang:system_monitor(),
- ?line Void = make_ref(),
- ?line start_busy_driver(Config),
- ?line {Owner,Slave} = get_slave(),
- ?line Master = command(get_master),
- ?line Parent = self(),
- ?line Busy =
- spawn_link(
- fun() ->
- (catch port_command(Slave, "set busy")),
- receive {Parent,alpha} -> ok end,
- (catch port_command(Slave, "busy")),
- (catch port_command(Slave, "free")),
- Parent ! {self(),alpha},
- command(lock),
- receive {Parent,beta} -> ok end,
- command({port_command,"busy"}),
- command({port_command,"free"}),
- Parent ! {self(),beta}
- end),
- ?line Void = rec(Void),
- ?line Busy ! {self(),alpha},
- ?line {monitor,Busy,busy_port,Slave} = rec(Void),
- ?line unlock_slave(),
- ?line {Busy,alpha} = rec(Void),
- ?line Void = rec(Void),
- ?line Busy ! {self(), beta},
- ?line {monitor,Owner,busy_port,Slave} = rec(Void),
- ?line port_command(Master, "u"),
- ?line {Busy,beta} = rec(Void),
- ?line Void = rec(Void),
- ?line _NewMonitor = erlang:system_monitor(OldMonitor),
- ?line OldMonitor = erlang:system_monitor(),
- ?line OldMonitor = erlang:system_monitor(OldMonitor),
- %%
- ?line test_server:timetrap_cancel(Dog),
+ OldMonitor = erlang:system_monitor(Self, [busy_port]),
+ {Self,[busy_port]} = erlang:system_monitor(),
+ Void = make_ref(),
+ start_busy_driver(Config),
+ {Owner,Slave} = get_slave(),
+ Master = command(get_master),
+ Parent = self(),
+ Busy = spawn_link(
+ fun() ->
+ (catch port_command(Slave, "set busy")),
+ receive {Parent,alpha} -> ok end,
+ (catch port_command(Slave, "busy")),
+ (catch port_command(Slave, "free")),
+ Parent ! {self(),alpha},
+ command(lock),
+ receive {Parent,beta} -> ok end,
+ command({port_command,"busy"}),
+ command({port_command,"free"}),
+ Parent ! {self(),beta}
+ end),
+ Void = rec(Void),
+ Busy ! {self(),alpha},
+ {monitor,Busy,busy_port,Slave} = rec(Void),
+ unlock_slave(),
+ {Busy,alpha} = rec(Void),
+ Void = rec(Void),
+ Busy ! {self(), beta},
+ {monitor,Owner,busy_port,Slave} = rec(Void),
+ port_command(Master, "u"),
+ {Busy,beta} = rec(Void),
+ Void = rec(Void),
+ _NewMonitor = erlang:system_monitor(OldMonitor),
+ OldMonitor = erlang:system_monitor(),
+ OldMonitor = erlang:system_monitor(OldMonitor),
ok.
-
-
rec(Tag) ->
receive X -> X after 1000 -> Tag end.
-
-
%% Assuming the following scenario,
%%
%% +---------------+ +-----------+
@@ -248,65 +212,59 @@ rec(Tag) ->
%%
%% tests that the suspended process is killed if the port is killed.
-no_trap_exit(suite) -> [];
no_trap_exit(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line process_flag(trap_exit, true),
- ?line Pid = fun_spawn(fun no_trap_exit_process/3,
- [self(), linked, Config]),
- ?line receive
- {Pid, port_created, Port} ->
- io:format("Process ~w created port ~w", [Pid, Port]),
- ?line exit(Port, die);
- Other1 ->
- test_server:fail({unexpected_message, Other1})
- end,
- ?line receive
- {'EXIT', Pid, die} ->
- ok;
- Other2 ->
- test_server:fail({unexpected_message, Other2})
- end,
-
- ?line test_server:timetrap_cancel(Dog),
+ ct:timetrap({seconds, 10}),
+ process_flag(trap_exit, true),
+ Pid = fun_spawn(fun no_trap_exit_process/3, [self(), linked, Config]),
+ receive
+ {Pid, port_created, Port} ->
+ io:format("Process ~w created port ~w", [Pid, Port]),
+ exit(Port, die);
+ Other1 ->
+ ct:fail({unexpected_message, Other1})
+ end,
+ receive
+ {'EXIT', Pid, die} ->
+ ok;
+ Other2 ->
+ ct:fail({unexpected_message, Other2})
+ end,
ok.
%% The same scenario as above, but the port has been explicitly
%% unlinked from the process.
-no_trap_exit_unlinked(suite) -> [];
no_trap_exit_unlinked(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line process_flag(trap_exit, true),
- ?line Pid = fun_spawn(fun no_trap_exit_process/3,
- [self(), unlink, Config]),
- ?line receive
- {Pid, port_created, Port} ->
- io:format("Process ~w created port ~w", [Pid, Port]),
- ?line exit(Port, die);
- Other1 ->
- test_server:fail({unexpected_message, Other1})
- end,
- ?line receive
- {'EXIT', Pid, normal} ->
- ok;
- Other2 ->
- test_server:fail({unexpected_message, Other2})
- end,
- ?line test_server:timetrap_cancel(Dog),
+ ct:timetrap({seconds, 10}),
+ process_flag(trap_exit, true),
+ Pid = fun_spawn(fun no_trap_exit_process/3,
+ [self(), unlink, Config]),
+ receive
+ {Pid, port_created, Port} ->
+ io:format("Process ~w created port ~w", [Pid, Port]),
+ exit(Port, die);
+ Other1 ->
+ ct:fail({unexpected_message, Other1})
+ end,
+ receive
+ {'EXIT', Pid, normal} ->
+ ok;
+ Other2 ->
+ ct:fail({unexpected_message, Other2})
+ end,
ok.
no_trap_exit_process(ResultTo, Link, Config) ->
- ?line load_busy_driver(Config),
- ?line _Master = open_port({spawn, "busy_drv master"}, [eof]),
- ?line Slave = open_port({spawn, "busy_drv slave"}, [eof]),
- ?line case Link of
- linked -> ok;
- unlink -> unlink(Slave)
- end,
- ?line (catch port_command(Slave, "lock port")),
- ?line ResultTo ! {self(), port_created, Slave},
- ?line (catch port_command(Slave, "suspend me")),
+ load_busy_driver(Config),
+ _Master = open_port({spawn, "busy_drv master"}, [eof]),
+ Slave = open_port({spawn, "busy_drv slave"}, [eof]),
+ case Link of
+ linked -> ok;
+ unlink -> unlink(Slave)
+ end,
+ (catch port_command(Slave, "lock port")),
+ ResultTo ! {self(), port_created, Slave},
+ (catch port_command(Slave, "suspend me")),
ok.
%% Assuming the following scenario,
@@ -320,36 +278,34 @@ no_trap_exit_process(ResultTo, Link, Config) ->
%% tests that the suspended process is scheduled runnable and
%% receives an 'EXIT' message if the port is killed.
-trap_exit(suite) -> [];
trap_exit(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Pid = fun_spawn(fun busy_port_exit_process/2, [self(), Config]),
- ?line receive
+ ct:timetrap({seconds, 10}),
+ Pid = fun_spawn(fun busy_port_exit_process/2, [self(), Config]),
+ receive
{Pid, port_created, Port} ->
io:format("Process ~w created port ~w", [Pid, Port]),
- ?line unlink(Pid),
- ?line {status, suspended} = process_info(Pid, status),
- ?line exit(Port, die);
+ unlink(Pid),
+ {status, suspended} = process_info(Pid, status),
+ exit(Port, die);
Other1 ->
- test_server:fail({unexpected_message, Other1})
+ ct:fail({unexpected_message, Other1})
end,
- ?line receive
+ receive
{Pid, ok} ->
ok;
Other2 ->
- test_server:fail({unexpected_message, Other2})
+ ct:fail({unexpected_message, Other2})
end,
- ?line test_server:timetrap_cancel(Dog),
ok.
busy_port_exit_process(ResultTo, Config) ->
- ?line process_flag(trap_exit, true),
- ?line load_busy_driver(Config),
- ?line _Master = open_port({spawn, "busy_drv master"}, [eof]),
- ?line Slave = open_port({spawn, "busy_drv slave"}, [eof]),
- ?line (catch port_command(Slave, "lock port")),
- ?line ResultTo ! {self(), port_created, Slave},
- ?line (catch port_command(Slave, "suspend me")),
+ process_flag(trap_exit, true),
+ load_busy_driver(Config),
+ _Master = open_port({spawn, "busy_drv master"}, [eof]),
+ Slave = open_port({spawn, "busy_drv slave"}, [eof]),
+ (catch port_command(Slave, "lock port")),
+ ResultTo ! {self(), port_created, Slave},
+ (catch port_command(Slave, "suspend me")),
receive
{'EXIT', Slave, die} ->
ResultTo ! {self(), ok};
@@ -362,19 +318,18 @@ busy_port_exit_process(ResultTo, Config) ->
%% This should work even if some of the processes have terminated
%% in the meantime.
-multiple_writers(suite) -> [];
multiple_writers(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line start_busy_driver(Config),
- ?line process_flag(trap_exit, true),
+ ct:timetrap({seconds, 10}),
+ start_busy_driver(Config),
+ process_flag(trap_exit, true),
%% Start the waiters and make sure they have blocked.
- ?line W1 = fun_spawn(fun quick_writer/0),
- ?line W2 = fun_spawn(fun quick_writer/0),
- ?line W3 = fun_spawn(fun quick_writer/0),
- ?line W4 = fun_spawn(fun quick_writer/0),
- ?line W5 = fun_spawn(fun quick_writer/0),
- ?line test_server:sleep(500), % Make sure writers have blocked.
+ W1 = fun_spawn(fun quick_writer/0),
+ W2 = fun_spawn(fun quick_writer/0),
+ W3 = fun_spawn(fun quick_writer/0),
+ W4 = fun_spawn(fun quick_writer/0),
+ W5 = fun_spawn(fun quick_writer/0),
+ test_server:sleep(500), % Make sure writers have blocked.
%% Kill two of the processes.
exit(W1, kill),
@@ -383,10 +338,8 @@ multiple_writers(Config) when is_list(Config) ->
receive {'EXIT', W3, killed} -> ok end,
%% Unlock the port. The surviving processes should be become runnable.
- ?line unlock_slave(),
- ?line wait_for([W2, W4, W5]),
-
- ?line test_server:timetrap_cancel(Dog),
+ unlock_slave(),
+ wait_for([W2, W4, W5]),
ok.
quick_writer() ->
@@ -402,205 +355,193 @@ soft_busy_driver(Config) when is_list(Config) ->
hs_test(Config, false).
hs_test(Config, HardBusy) when is_list(Config) ->
- ?line DrvName = case HardBusy of
- true -> 'hard_busy_drv';
- false -> 'soft_busy_drv'
- end,
- ?line erl_ddll:start(),
- ?line Path = ?config(data_dir, Config),
+ DrvName = case HardBusy of
+ true -> 'hard_busy_drv';
+ false -> 'soft_busy_drv'
+ end,
+ erl_ddll:start(),
+ Path = proplists:get_value(data_dir, Config),
case erl_ddll:load_driver(Path, DrvName) of
- ok -> ok;
- {error, Error} ->
- io:format("~s\n", [erl_ddll:format_error(Error)]),
- ?line ?t:fail()
+ ok -> ok;
+ {error, Error} ->
+ ct:fail(erl_ddll:format_error(Error))
end,
- ?line Port = open_port({spawn, DrvName}, []),
-
+ Port = open_port({spawn, DrvName}, []),
+
NotSuspended = fun (Proc) ->
- chk_not_value({status,suspended},
- process_info(Proc, status))
- end,
+ chk_not_value({status,suspended},
+ process_info(Proc, status))
+ end,
NotBusyEnd = fun (Proc, Res, Time) ->
- receive
- {Port, caller, Proc} -> ok
- after
- 500 -> exit(missing_caller_message)
- end,
- chk_value({return, true}, Res),
- chk_range(0, Time, 100)
- end,
+ receive
+ {Port, caller, Proc} -> ok
+ after
+ 500 -> exit(missing_caller_message)
+ end,
+ chk_value({return, true}, Res),
+ chk_range(0, Time, 100)
+ end,
ForceEnd = fun (Proc, Res, Time) ->
- case HardBusy of
- false ->
- NotBusyEnd(Proc, Res, Time);
- true ->
- chk_value({error, notsup}, Res),
- chk_range(0, Time, 100),
- receive
- Msg -> exit({unexpected_msg, Msg})
- after
- 500 -> ok
- end
- end
- end,
+ case HardBusy of
+ false ->
+ NotBusyEnd(Proc, Res, Time);
+ true ->
+ chk_value({error, notsup}, Res),
+ chk_range(0, Time, 100),
+ receive
+ Msg -> exit({unexpected_msg, Msg})
+ after
+ 500 -> ok
+ end
+ end
+ end,
BadArg = fun (_Proc, Res, Time) ->
- chk_value({error, badarg}, Res),
- chk_range(0, Time, 100)
- end,
+ chk_value({error, badarg}, Res),
+ chk_range(0, Time, 100)
+ end,
%% Not busy
%% Not busy; nosuspend option
- ?line hs_busy_pcmd(Port, [nosuspend], NotSuspended, NotBusyEnd),
+ hs_busy_pcmd(Port, [nosuspend], NotSuspended, NotBusyEnd),
%% Not busy; force option
- ?line hs_busy_pcmd(Port, [force], NotSuspended, ForceEnd),
+ hs_busy_pcmd(Port, [force], NotSuspended, ForceEnd),
%% Not busy; force and nosuspend option
- ?line hs_busy_pcmd(Port, [force, nosuspend], NotSuspended, ForceEnd),
+ hs_busy_pcmd(Port, [force, nosuspend], NotSuspended, ForceEnd),
%% Not busy; no option
- ?line hs_busy_pcmd(Port, [], NotSuspended, NotBusyEnd),
+ hs_busy_pcmd(Port, [], NotSuspended, NotBusyEnd),
%% Not busy; bad option
- ?line hs_busy_pcmd(Port, [bad_option], NotSuspended, BadArg),
+ hs_busy_pcmd(Port, [bad_option], NotSuspended, BadArg),
%% Make busy
- ?line erlang:port_control(Port, $B, []),
+ erlang:port_control(Port, $B, []),
%% Busy; nosuspend option
- ?line hs_busy_pcmd(Port, [nosuspend], NotSuspended,
- fun (_Proc, Res, Time) ->
- chk_value({return, false}, Res),
- chk_range(0, Time, 100),
- receive
- Msg -> exit({unexpected_msg, Msg})
- after
- 500 -> ok
- end
- end),
+ hs_busy_pcmd(Port, [nosuspend], NotSuspended,
+ fun (_Proc, Res, Time) ->
+ chk_value({return, false}, Res),
+ chk_range(0, Time, 100),
+ receive
+ Msg -> exit({unexpected_msg, Msg})
+ after
+ 500 -> ok
+ end
+ end),
%% Busy; force option
- ?line hs_busy_pcmd(Port, [force], NotSuspended, ForceEnd),
+ hs_busy_pcmd(Port, [force], NotSuspended, ForceEnd),
%% Busy; force and nosuspend option
- ?line hs_busy_pcmd(Port, [force, nosuspend], NotSuspended, ForceEnd),
+ hs_busy_pcmd(Port, [force, nosuspend], NotSuspended, ForceEnd),
%% Busy; bad option
- ?line hs_busy_pcmd(Port, [bad_option], NotSuspended, BadArg),
+ hs_busy_pcmd(Port, [bad_option], NotSuspended, BadArg),
%% no option on busy port
- ?line hs_busy_pcmd(Port, [],
- fun (Proc) ->
- receive after 1000 -> ok end,
- chk_value({status,suspended},
- process_info(Proc, status)),
-
- %% Make not busy
- erlang:port_control(Port, $N, [])
- end,
- fun (_Proc, Res, Time) ->
- chk_value({return, true}, Res),
- chk_range(1000, Time, 2000)
- end),
-
- ?line true = erlang:port_close(Port),
- ?line ok = erl_ddll:unload_driver(DrvName),
- ?line ok = erl_ddll:stop(),
- ?line ok.
+ hs_busy_pcmd(Port, [],
+ fun (Proc) ->
+ receive after 1000 -> ok end,
+ chk_value({status,suspended},
+ process_info(Proc, status)),
+
+ %% Make not busy
+ erlang:port_control(Port, $N, [])
+ end,
+ fun (_Proc, Res, Time) ->
+ chk_value({return, true}, Res),
+ chk_range(1000, Time, 2000)
+ end),
+
+ true = erlang:port_close(Port),
+ ok = erl_ddll:unload_driver(DrvName),
+ ok = erl_ddll:stop(),
+ ok.
hs_busy_pcmd(Prt, Opts, StartFun, EndFun) ->
Tester = self(),
P = spawn_link(fun () ->
- erlang:yield(),
- Tester ! {self(), doing_port_command},
- Start = erlang:monotonic_time(micro_seconds),
- Res = try {return,
- port_command(Prt, [], Opts)}
- catch Exception:Error -> {Exception, Error}
- end,
- End = erlang:monotonic_time(micro_seconds),
- Time = round((End - Start)/1000),
- Tester ! {self(), port_command_result, Res, Time}
- end),
+ erlang:yield(),
+ Tester ! {self(), doing_port_command},
+ Start = erlang:monotonic_time(microsecond),
+ Res = try {return,
+ port_command(Prt, [], Opts)}
+ catch Exception:Error -> {Exception, Error}
+ end,
+ End = erlang:monotonic_time(microsecond),
+ Time = round((End - Start)/1000),
+ Tester ! {self(), port_command_result, Res, Time}
+ end),
receive
- {P, doing_port_command} ->
- ok
+ {P, doing_port_command} ->
+ ok
end,
StartFun(P),
receive
- {P, port_command_result, Res, Time} ->
- EndFun(P, Res, Time)
+ {P, port_command_result, Res, Time} ->
+ EndFun(P, Res, Time)
end.
scheduling_delay_busy(Config) ->
-
- Scenario =
- [{1,{spawn,[{var,drvname},undefined]}},
- {2,{call,[{var,1},open_port]}},
- {3,{spawn,[{var,2},{var,1}]}},
- {0,{ack,[{var,1},{busy,1,250}]}},
- {0,{cast,[{var,3},{command,2}]}},
- [{0,{cast,[{var,3},{command,I}]}}
- || I <- lists:seq(3,50)],
- {0,{cast,[{var,3},take_control]}},
- {0,{cast,[{var,1},{new_owner,{var,3}}]}},
- {0,{cast,[{var,3},close]}},
- {0,{timer,sleep,[300]}},
- {0,{erlang,port_command,[{var,2},<<$N>>,[force]]}},
- [{0,{cast,[{var,1},{command,I}]}}
- || I <- lists:seq(101,127)]
- ,{10,{call,[{var,3},get_data]}}
- ],
+ Scenario = [{1,{spawn,[{var,drvname},undefined]}},
+ {2,{call,[{var,1},open_port]}},
+ {3,{spawn,[{var,2},{var,1}]}},
+ {0,{ack,[{var,1},{busy,1,250}]}},
+ {0,{cast,[{var,3},{command,2}]}},
+ [{0,{cast,[{var,3},{command,I}]}} || I <- lists:seq(3,50)],
+ {0,{cast,[{var,3},take_control]}},
+ {0,{cast,[{var,1},{new_owner,{var,3}}]}},
+ {0,{cast,[{var,3},close]}},
+ {0,{timer,sleep,[300]}},
+ {0,{erlang,port_command,[{var,2},<<$N>>,[force]]}},
+ [{0,{cast,[{var,1},{command,I}]}} || I <- lists:seq(101,127)],
+ {10,{call,[{var,3},get_data]}}],
Validation = [{seq,10,lists:seq(1,50)}],
- port_scheduling(Scenario,Validation,?config(data_dir,Config)).
+ port_scheduling(Scenario,Validation,proplists:get_value(data_dir,Config)).
scheduling_delay_busy_nosuspend(Config) ->
-
- Scenario =
- [{1,{spawn,[{var,drvname},undefined]}},
- {2,{call,[{var,1},open_port]}},
- {0,{cast,[{var,1},{command,1,100}]}},
- {0,{cast,[{var,1},{busy,2}]}},
- {0,{timer,sleep,[200]}}, % ensure reached port
- {10,{call,[{var,1},{command,3,[nosuspend]}]}},
- {0,{timer,sleep,[200]}},
- {0,{erlang,port_command,[{var,2},<<$N>>,[force]]}},
- {0,{cast,[{var,1},close]}},
- {20,{call,[{var,1},get_data]}}
- ],
+ Scenario = [{1,{spawn,[{var,drvname},undefined]}},
+ {2,{call,[{var,1},open_port]}},
+ {0,{cast,[{var,1},{command,1,100}]}},
+ {0,{cast,[{var,1},{busy,2}]}},
+ {0,{timer,sleep,[200]}}, % ensure reached port
+ {10,{call,[{var,1},{command,3,[nosuspend]}]}},
+ {0,{timer,sleep,[200]}},
+ {0,{erlang,port_command,[{var,2},<<$N>>,[force]]}},
+ {0,{cast,[{var,1},close]}},
+ {20,{call,[{var,1},get_data]}}],
Validation = [{eq,10,nosuspend},{seq,20,[1,2]}],
- port_scheduling(Scenario,Validation,?config(data_dir,Config)).
+ port_scheduling(Scenario,Validation,proplists:get_value(data_dir,Config)).
scheduling_busy_link(Config) ->
-
- Scenario =
- [{1,{spawn,[{var,drvname},undefined]}},
- {2,{call,[{var,1},open_port]}},
- {3,{spawn,[{var,2},{var,1}]}},
- {0,{cast,[{var,1},unlink]}},
- {0,{cast,[{var,1},{busy,1}]}},
- {0,{cast,[{var,1},{command,2}]}},
- {0,{cast,[{var,1},link]}},
- {0,{timer,sleep,[1000]}},
- {0,{ack,[{var,3},take_control]}},
- {0,{cast,[{var,1},{new_owner,{var,3}}]}},
- {0,{cast,[{var,3},close]}},
- {10,{call,[{var,3},get_data]}},
- {20,{call,[{var,1},get_exit]}}
- ],
+ Scenario = [{1,{spawn,[{var,drvname},undefined]}},
+ {2,{call,[{var,1},open_port]}},
+ {3,{spawn,[{var,2},{var,1}]}},
+ {0,{cast,[{var,1},unlink]}},
+ {0,{cast,[{var,1},{busy,1}]}},
+ {0,{cast,[{var,1},{command,2}]}},
+ {0,{cast,[{var,1},link]}},
+ {0,{timer,sleep,[1000]}},
+ {0,{ack,[{var,3},take_control]}},
+ {0,{cast,[{var,1},{new_owner,{var,3}}]}},
+ {0,{cast,[{var,3},close]}},
+ {10,{call,[{var,3},get_data]}},
+ {20,{call,[{var,1},get_exit]}}],
Validation = [{seq,10,[1]},
{seq,20,[{'EXIT',noproc}]}],
- port_scheduling(Scenario,Validation,?config(data_dir,Config)).
+ port_scheduling(Scenario,Validation,proplists:get_value(data_dir,Config)).
process_init(DrvName,Owner) ->
process_flag(trap_exit,true),
@@ -699,11 +640,11 @@ handle_msg(close,Port,Owner,_Data) ->
handle_msg(get_data,Port,_Owner,{[],_Exit}) ->
%% Wait for data if it has not arrived yet
receive
- {Port,{data,Data}} ->
- Data
+ {Port,{data,Data}} ->
+ Data
after 2000 ->
- pal("~p",[erlang:process_info(self())]),
- exit(did_not_get_port_data)
+ pal("~p",[erlang:process_info(self())]),
+ exit(did_not_get_port_data)
end;
handle_msg(get_data,_Port,Owner,{Data,Exit}) ->
pal("GetData",[]),
@@ -753,8 +694,7 @@ port_scheduling(Scenario,Validation,Path) ->
case erl_ddll:load_driver(Path, DrvName) of
ok -> ok;
{error, Error} ->
- io:format("~s\n", [erl_ddll:format_error(Error)]),
- ?line ?t:fail()
+ ct:fail(erl_ddll:format_error(Error))
end,
Data = run_scenario(lists:flatten(Scenario),[{drvname,DrvName}]),
@@ -777,7 +717,7 @@ run_command(_M,spawn,{Args,Opts}) ->
run_command(M,spawn,Args) ->
run_command(M,spawn,{Args,[]});
run_command(Mod,Func,Args) ->
- erlang:display({{Mod,Func,Args}, erlang:system_time(micro_seconds)}),
+ erlang:display({{Mod,Func,Args}, erlang:system_time(microsecond)}),
apply(Mod,Func,Args).
validate_scenario(Data,[{print,Var}|T]) ->
@@ -860,7 +800,7 @@ wait_for(Pids) ->
{'EXIT', Pid, normal} ->
wait_for(lists:delete(Pid, Pids));
Other ->
- test_server:fail({bad_exit, Other})
+ ct:fail({bad_exit, Other})
end.
fun_spawn(Fun) ->
@@ -896,39 +836,38 @@ fun_spawn(Fun, Args) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
load_busy_driver(Config) when is_list(Config) ->
- ?line DataDir = ?config(data_dir, Config),
- ?line erl_ddll:start(),
+ DataDir = proplists:get_value(data_dir, Config),
+ erl_ddll:start(),
case erl_ddll:load_driver(DataDir, "busy_drv") of
ok -> ok;
{error, Error} ->
- io:format("~s\n", [erl_ddll:format_error(Error)]),
- ?line ?t:fail()
+ ct:fail(erl_ddll:format_error(Error))
end.
%%% Interface functions.
start_busy_driver(Config) when is_list(Config) ->
- ?line Pid = spawn_link(?MODULE, init, [Config, self()]),
- ?line receive
+ Pid = spawn_link(?MODULE, init, [Config, self()]),
+ receive
{Pid, started} ->
ok;
Other ->
- test_server:fail({unexpected_message, Other})
+ ct:fail({unexpected_message, Other})
end.
unlock_slave() ->
command(unlock).
get_slave() ->
- ?line command(get_slave).
+ command(get_slave).
%% Internal functions.
command(Msg) ->
- ?line whereis(busy_drv_server) ! {self(), Msg},
- ?line receive
- {busy_drv_reply, Reply} ->
- Reply
+ whereis(busy_drv_server) ! {self(), Msg},
+ receive
+ {busy_drv_reply, Reply} ->
+ Reply
end.
%%% Server.
diff --git a/erts/emulator/test/busy_port_SUITE_data/Makefile.src b/erts/emulator/test/busy_port_SUITE_data/Makefile.src
index 0f2842e515..ae6378a6ff 100644
--- a/erts/emulator/test/busy_port_SUITE_data/Makefile.src
+++ b/erts/emulator/test/busy_port_SUITE_data/Makefile.src
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2013. All Rights Reserved.
+# Copyright Ericsson AB 1997-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/busy_port_SUITE_data/hard_busy_drv.c b/erts/emulator/test/busy_port_SUITE_data/hard_busy_drv.c
index f83fa1eeaa..c4e0f13f06 100644
--- a/erts/emulator/test/busy_port_SUITE_data/hard_busy_drv.c
+++ b/erts/emulator/test/busy_port_SUITE_data/hard_busy_drv.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009. All Rights Reserved.
+ * Copyright Ericsson AB 2009-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/busy_port_SUITE_data/hs_busy_drv.c b/erts/emulator/test/busy_port_SUITE_data/hs_busy_drv.c
index be913cf56e..ffaca18e90 100644
--- a/erts/emulator/test/busy_port_SUITE_data/hs_busy_drv.c
+++ b/erts/emulator/test/busy_port_SUITE_data/hs_busy_drv.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2009-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/busy_port_SUITE_data/scheduling_drv.c b/erts/emulator/test/busy_port_SUITE_data/scheduling_drv.c
index 40e42b6ac2..296b3f21de 100644
--- a/erts/emulator/test/busy_port_SUITE_data/scheduling_drv.c
+++ b/erts/emulator/test/busy_port_SUITE_data/scheduling_drv.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2009-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/busy_port_SUITE_data/soft_busy_drv.c b/erts/emulator/test/busy_port_SUITE_data/soft_busy_drv.c
index 3c5bafb451..8a98f050f1 100644
--- a/erts/emulator/test/busy_port_SUITE_data/soft_busy_drv.c
+++ b/erts/emulator/test/busy_port_SUITE_data/soft_busy_drv.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009. All Rights Reserved.
+ * Copyright Ericsson AB 2009-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/call_trace_SUITE.erl b/erts/emulator/test/call_trace_SUITE.erl
index 064404a038..f7ff04430a 100644
--- a/erts/emulator/test/call_trace_SUITE.erl
+++ b/erts/emulator/test/call_trace_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -21,66 +21,46 @@
-module(call_trace_SUITE).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2,end_per_testcase/2,
- hipe/1,process_specs/1,basic/1,flags/1,errors/1,pam/1,change_pam/1,
- return_trace/1,exception_trace/1,on_load/1,deep_exception/1,
- upgrade/1,
- exception_nocatch/1,bit_syntax/1]).
+-export([all/0, suite/0,
+ init_per_testcase/2,end_per_testcase/2,
+ hipe/1,process_specs/1,basic/1,flags/1,errors/1,pam/1,change_pam/1,
+ return_trace/1,exception_trace/1,on_load/1,deep_exception/1,
+ upgrade/1,
+ exception_nocatch/1,bit_syntax/1]).
%% Helper functions.
-export([bar/0,foo/0,foo/1,foo/2,expect/1,worker_foo/1,pam_foo/2,nasty/0,
- id/1,deep/3,deep_1/3,deep_2/2,deep_3/2,deep_4/1,deep_5/1,
- bs_sum_a/2,bs_sum_b/2]).
+ id/1,deep/3,deep_1/3,deep_2/2,deep_3/2,deep_4/1,deep_5/1,
+ bs_sum_a/2,bs_sum_b/2]).
%% Debug
-export([abbr/1,abbr/2]).
-
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-define(P, 20).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {seconds, 30}}].
all() ->
Common = [errors, on_load],
NotHipe = [process_specs, basic, flags, pam, change_pam,
- upgrade,
- return_trace, exception_trace, deep_exception,
- exception_nocatch, bit_syntax],
+ upgrade,
+ return_trace, exception_trace, deep_exception,
+ exception_nocatch, bit_syntax],
Hipe = [hipe],
case test_server:is_native(call_trace_SUITE) of
- true -> Hipe ++ Common;
- false -> NotHipe ++ Common
+ true -> Hipe ++ Common;
+ false -> NotHipe ++ Common
end.
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- Dog = ?t:timetrap(?t:seconds(30)),
- [{watchdog, Dog}|Config].
+ Config.
end_per_testcase(_Func, Config) ->
- Dog = ?config(watchdog, Config),
- ?t:timetrap_cancel(Dog),
-
%% Reloading the module will clear all trace patterns, and
%% in a debug-compiled emulator run assertions of the counters
%% for the number of traced exported functions in this module.
@@ -88,51 +68,63 @@ end_per_testcase(_Func, Config) ->
c:l(?MODULE).
hipe(Config) when is_list(Config) ->
- ?line 0 = erlang:trace_pattern({?MODULE,worker_foo,1}, true),
- ?line 0 = erlang:trace_pattern({?MODULE,worker_foo,1}, true, [local]),
- ?line AllFuncs = erlang:trace_pattern({'_','_','_'}, true),
+ 0 = erlang:trace_pattern({?MODULE,worker_foo,1}, true),
+ 0 = erlang:trace_pattern({?MODULE,worker_foo,1}, true, [local]),
+ AllFuncs = erlang:trace_pattern({'_','_','_'}, true),
%% Make sure that a traced, exported function can still be found.
- ?line true = erlang:function_exported(error_handler, undefined_function, 3),
- ?line AllFuncs = erlang:trace_pattern({'_','_','_'}, false),
+ true = erlang:function_exported(error_handler, undefined_function, 3),
+ AllFuncs = erlang:trace_pattern({'_','_','_'}, false),
ok.
-process_specs(doc) ->
- "Tests 'all', 'new', and 'existing' for specifying processes.";
-process_specs(suite) -> [];
+%% Tests 'all', 'new', and 'existing' for specifying processes.
process_specs(Config) when is_list(Config) ->
- ?line Tracer = start_tracer(),
- ?line {flags,[call]} = trace_info(self(), flags),
- ?line {tracer,Tracer} = trace_info(self(), tracer),
- ?line trace_func({?MODULE,worker_foo,1}, []),
-
- %% Test the 'new' flag.
-
- ?line {Work1A,Work1B} = start_and_trace(new, [1,2,3], A1B={3,2,1}),
- {flags,[]} = trace_info(Work1A, flags),
- {tracer,[]} = trace_info(Work1A, tracer),
- {tracer,Tracer} = trace_info(Work1B, tracer),
- {flags,[call]} = trace_info(Work1B, flags),
- ?line expect({trace,Work1B,call,{?MODULE,worker_foo,[A1B]}}),
- ?line unlink(Work1B),
- ?line Mref = erlang:monitor(process, Work1B),
- ?line exit(Work1B, kill),
- receive
- {'DOWN',Mref,_,_,_} -> ok
- end,
- ?line undefined = trace_info(Work1B, flags),
- ?line {flags,[]} = trace_info(new, flags),
- ?line {tracer,[]} = trace_info(new, tracer),
-
- %% Test the 'existing' flag.
- ?line {Work2A,_Work2B} = start_and_trace(existing, A2A=[5,6,7], [7,6,5]),
- ?line expect({trace,Work2A,call,{?MODULE,worker_foo,[A2A]}}),
-
- %% Test the 'all' flag.
- ?line {Work3A,Work3B} = start_and_trace(all, A3A=[12,13], A3B=[13,12]),
- ?line expect({trace,Work3A,call,{?MODULE,worker_foo,[A3A]}}),
- ?line expect({trace,Work3B,call,{?MODULE,worker_foo,[A3B]}}),
-
+ Tracer = start_tracer(),
+ {flags,[call]} = trace_info(self(), flags),
+ {tracer,Tracer} = trace_info(self(), tracer),
+ trace_func({?MODULE,worker_foo,1}, []),
+
+ %% Test the 'new' and 'new_processes' flags.
+
+ New = fun(Flag) ->
+ {Work1A,Work1B} = start_and_trace(Flag, [1,2,3], A1B={3,2,1}),
+ {flags,[]} = trace_info(Work1A, flags),
+ {tracer,[]} = trace_info(Work1A, tracer),
+ {tracer,Tracer} = trace_info(Work1B, tracer),
+ {flags,[call]} = trace_info(Work1B, flags),
+ expect({trace,Work1B,call,{?MODULE,worker_foo,[A1B]}}),
+ unlink(Work1B),
+ Mref = erlang:monitor(process, Work1B),
+ exit(Work1B, kill),
+ receive
+ {'DOWN',Mref,_,_,_} -> ok
+ end,
+ undefined = trace_info(Work1B, flags),
+ {flags,[]} = trace_info(Flag, flags),
+ {tracer,[]} = trace_info(Flag, tracer)
+ end,
+ New(new),
+ New(new_processes),
+
+ %% Test the 'existing' and 'existing_processes' flags.
+ Existing =
+ fun(Flag) ->
+ {Work2A,_Work2B} = start_and_trace(Flag, A2A=[5,6,7], [7,6,5]),
+ expect({trace,Work2A,call,{?MODULE,worker_foo,[A2A]}})
+ end,
+ Existing(existing),
+ Existing(existing_processes),
+
+ %% Test the 'all' and 'processes' flags.
+ All =
+ fun(Flag) ->
+ {Work3A,Work3B} = start_and_trace(Flag, A3A=[12,13], A3B=[13,12]),
+ expect({trace,Work3A,call,{?MODULE,worker_foo,[A3A]}}),
+ expect({trace,Work3B,call,{?MODULE,worker_foo,[A3B]}})
+ end,
+ All(all),
+ All(processes),
+
ok.
start_and_trace(Flag, A1, A2) ->
@@ -142,33 +134,33 @@ start_and_trace(Flag, A1, A2) ->
call_worker(W1, A1),
call_worker(W2, A2),
case Flag of
- new ->
- {flags,[call]} = trace_info(new, flags),
- {tracer,_} = trace_info(new, tracer);
- _Other ->
- ok
+ new ->
+ {flags,[call]} = trace_info(new, flags),
+ {tracer,_} = trace_info(new, tracer);
+ _Other ->
+ ok
end,
trace_pid(Flag, false, [call]),
{W1,W2}.
start_worker() ->
- ?line spawn(fun worker_loop/0).
+ spawn(fun worker_loop/0).
call_worker(Pid, Arg) ->
Pid ! {self(),{call,Arg}},
receive
- {result,Res} -> Res
+ {result,Res} -> Res
after 5000 ->
- ?line ?t:fail(no_answer_from_worker)
+ ct:fail(no_answer_from_worker)
end.
worker_loop() ->
receive
- {From,{call,Arg}} ->
- From ! {result,?MODULE:worker_foo(Arg)},
- worker_loop();
- Other ->
- exit({unexpected_message,Other})
+ {From,{call,Arg}} ->
+ From ! {result,?MODULE:worker_foo(Arg)},
+ worker_loop();
+ Other ->
+ exit({unexpected_message,Other})
end.
worker_foo(_Arg) ->
@@ -177,98 +169,98 @@ worker_foo(_Arg) ->
%% Basic test of the call tracing (we trace one process).
basic(_Config) ->
case test_server:is_native(lists) of
- true -> {skip,"lists is native"};
- false -> basic()
+ true -> {skip,"lists is native"};
+ false -> basic()
end.
basic() ->
- ?line start_tracer(),
- ?line trace_info(self(), flags),
- ?line trace_info(self(), tracer),
- ?line 0 = trace_func({?MODULE,no_such_function,0}, []),
- ?line {traced,undefined} =
- trace_info({?MODULE,no_such_function,0}, traced),
- ?line {match_spec, undefined} =
- trace_info({?MODULE,no_such_function,0}, match_spec),
+ start_tracer(),
+ trace_info(self(), flags),
+ trace_info(self(), tracer),
+ 0 = trace_func({?MODULE,no_such_function,0}, []),
+ {traced,undefined} =
+ trace_info({?MODULE,no_such_function,0}, traced),
+ {match_spec, undefined} =
+ trace_info({?MODULE,no_such_function,0}, match_spec),
%% Trace some functions...
- ?line trace_func({lists,'_','_'}, []),
+ trace_func({lists,'_','_'}, []),
%% Make sure that tracing the same functions more than once
%% does not cause any problems.
- ?line 3 = trace_func({?MODULE,foo,'_'}, true),
- ?line 3 = trace_func({?MODULE,foo,'_'}, true),
- ?line 1 = trace_func({?MODULE,bar,0}, true),
- ?line 1 = trace_func({?MODULE,bar,0}, true),
- ?line {traced,global} = trace_info({?MODULE,bar,0}, traced),
- ?line 1 = trace_func({erlang,list_to_integer,1}, true),
- ?line {traced,global} = trace_info({erlang,list_to_integer,1}, traced),
+ 3 = trace_func({?MODULE,foo,'_'}, true),
+ 3 = trace_func({?MODULE,foo,'_'}, true),
+ 1 = trace_func({?MODULE,bar,0}, true),
+ 1 = trace_func({?MODULE,bar,0}, true),
+ {traced,global} = trace_info({?MODULE,bar,0}, traced),
+ 1 = trace_func({erlang,list_to_integer,1}, true),
+ {traced,global} = trace_info({erlang,list_to_integer,1}, traced),
%% ... and call them...
- ?line AList = [x,y,z],
- ?line true = lists:member(y, AList),
- ?line foo0 = ?MODULE:foo(),
- ?line 4 = ?MODULE:foo(3),
- ?line 11 = ?MODULE:foo(7, 4),
- ?line ok = ?MODULE:bar(),
- ?line 42 = list_to_integer(non_literal("42")),
+ AList = [x,y,z],
+ true = lists:member(y, AList),
+ foo0 = ?MODULE:foo(),
+ 4 = ?MODULE:foo(3),
+ 11 = ?MODULE:foo(7, 4),
+ ok = ?MODULE:bar(),
+ 42 = list_to_integer(non_literal("42")),
%% ... make sure the we got trace messages (but not for ?MODULE:expect/1).
- ?line Self = self(),
- ?line ?MODULE:expect({trace,Self,call,{lists,member,[y,AList]}}),
- ?line ?MODULE:expect({trace,Self,call,{?MODULE,foo,[]}}),
- ?line ?MODULE:expect({trace,Self,call,{?MODULE,foo,[3]}}),
- ?line ?MODULE:expect({trace,Self,call,{?MODULE,foo,[7,4]}}),
- ?line ?MODULE:expect({trace,Self,call,{?MODULE,bar,[]}}),
- ?line ?MODULE:expect({trace,Self,call,{erlang,list_to_integer,["42"]}}),
+ Self = self(),
+ ?MODULE:expect({trace,Self,call,{lists,member,[y,AList]}}),
+ ?MODULE:expect({trace,Self,call,{?MODULE,foo,[]}}),
+ ?MODULE:expect({trace,Self,call,{?MODULE,foo,[3]}}),
+ ?MODULE:expect({trace,Self,call,{?MODULE,foo,[7,4]}}),
+ ?MODULE:expect({trace,Self,call,{?MODULE,bar,[]}}),
+ ?MODULE:expect({trace,Self,call,{erlang,list_to_integer,["42"]}}),
%% Turn off trace for this module and call functions...
- ?line trace_func({?MODULE,'_','_'}, false),
- ?line {traced,false} = trace_info({?MODULE,bar,0}, traced),
- ?line foo0 = ?MODULE:foo(),
- ?line 4 = ?MODULE:foo(3),
- ?line 11 = ?MODULE:foo(7, 4),
- ?line ok = ?MODULE:bar(),
- ?line [1,2,3,4,5,6,7,8,9,10] = lists:seq(1, 10),
- ?line 777 = list_to_integer(non_literal("777")),
+ trace_func({?MODULE,'_','_'}, false),
+ {traced,false} = trace_info({?MODULE,bar,0}, traced),
+ foo0 = ?MODULE:foo(),
+ 4 = ?MODULE:foo(3),
+ 11 = ?MODULE:foo(7, 4),
+ ok = ?MODULE:bar(),
+ [1,2,3,4,5,6,7,8,9,10] = lists:seq(1, 10),
+ 777 = list_to_integer(non_literal("777")),
%% ... turn on all trace messages...
- ?line trace_func({'_','_','_'}, false),
- ?line [b,a] = lists:reverse([a,b]),
+ trace_func({'_','_','_'}, false),
+ [b,a] = lists:reverse([a,b]),
%% Read out the remaing trace messages.
- ?line ?MODULE:expect({trace,Self,call,{lists,seq,[1,10]}}),
- ?line ?MODULE:expect({trace,Self,call,{erlang,list_to_integer,["777"]}}),
+ ?MODULE:expect({trace,Self,call,{lists,seq,[1,10]}}),
+ ?MODULE:expect({trace,Self,call,{erlang,list_to_integer,["777"]}}),
receive
- Any ->
- ?line ?t:fail({unexpected_message,Any})
+ Any ->
+ ct:fail({unexpected_message,Any})
after 1 ->
- ok
+ ok
end,
%% Turn on and then off tracing on all external functions.
%% This might cause the emulator to crasch later if it doesn't
%% restore all export entries properly.
- ?line AllFuncs = trace_func({'_','_','_'}, true),
+ AllFuncs = trace_func({'_','_','_'}, true),
io:format("AllFuncs = ~p", [AllFuncs]),
%% Make sure that a traced, exported function can still be found.
- ?line true = erlang:function_exported(error_handler, undefined_function, 3),
- ?line AllFuncs = trace_func({'_','_','_'}, false),
- ?line erlang:trace_delivered(all),
+ true = erlang:function_exported(error_handler, undefined_function, 3),
+ AllFuncs = trace_func({'_','_','_'}, false),
+ erlang:trace_delivered(all),
receive
- {trace_delivered,_,_} -> ok
+ {trace_delivered,_,_} -> ok
end,
c:flush(), % Print the traces messages.
c:flush(), % Print the traces messages.
- ?line {traced,false} = trace_info({erlang,list_to_integer,1}, traced),
+ {traced,false} = trace_info({erlang,list_to_integer,1}, traced),
ok.
@@ -287,8 +279,8 @@ foo(X, Y) -> X+Y.
%% This test case was written to verify that we do not change
%% any behaviour with the introduction of "block-free" upgrade in R16.
%% In short: Do not refer to this test case as an authority of how it must work.
-upgrade(doc) ->
- "Test tracing on module being upgraded";
+
+%% Test tracing on module being upgraded
upgrade(Config) when is_list(Config) ->
V1 = compile_version(my_upgrade_test, 1, Config),
V2 = compile_version(my_upgrade_test, 2, Config),
@@ -304,8 +296,8 @@ upgrade_do(V1, V2, TraceLocalVersion) ->
trace_func({my_upgrade_test,'_','_'}, [], [global]),
case TraceLocalVersion of
- true -> trace_func({my_upgrade_test,local_version,0}, [], [local]);
- _ -> ok
+ true -> trace_func({my_upgrade_test,local_version,0}, [], [local]);
+ _ -> ok
end,
1 = my_upgrade_test:version(),
1 = my_upgrade_test:do_local(),
@@ -320,15 +312,15 @@ upgrade_do(V1, V2, TraceLocalVersion) ->
expect({trace,Self,call,{my_upgrade_test,do_local,[]}}),
expect({trace,Self,call,{my_upgrade_test,do_real_local,[]}}),
case TraceLocalVersion of
- true -> expect({trace,Self,call,{my_upgrade_test,local_version,[]}});
- _ -> ok
+ true -> expect({trace,Self,call,{my_upgrade_test,local_version,[]}});
+ _ -> ok
end,
expect({trace,Self,call,{my_upgrade_test,make_fun_exp,[]}}),
expect({trace,Self,call,{my_upgrade_test,make_fun_local,[]}}),
expect({trace,Self,call,{my_upgrade_test,version,[]}}), % F1_exp
case TraceLocalVersion of
- true -> expect({trace,Self,call,{my_upgrade_test,local_version,[]}}); % F1_loc
- _ -> ok
+ true -> expect({trace,Self,call,{my_upgrade_test,local_version,[]}}); % F1_loc
+ _ -> ok
end,
{module,my_upgrade_test} = erlang:load_module(my_upgrade_test, V2),
@@ -350,8 +342,8 @@ upgrade_do(V1, V2, TraceLocalVersion) ->
trace_func({my_upgrade_test,'_','_'}, [], [global]),
case TraceLocalVersion of
- true -> trace_func({my_upgrade_test,local_version,0}, [], [local]);
- _ -> ok
+ true -> trace_func({my_upgrade_test,local_version,0}, [], [local]);
+ _ -> ok
end,
2 = my_upgrade_test:version(),
2 = my_upgrade_test:do_local(),
@@ -363,13 +355,13 @@ upgrade_do(V1, V2, TraceLocalVersion) ->
expect({trace,Self,call,{my_upgrade_test,do_local,[]}}),
expect({trace,Self,call,{my_upgrade_test,do_real_local,[]}}),
case TraceLocalVersion of
- true -> expect({trace,Self,call,{my_upgrade_test,local_version,[]}});
- _ -> ok
+ true -> expect({trace,Self,call,{my_upgrade_test,local_version,[]}});
+ _ -> ok
end,
expect({trace,Self,call,{my_upgrade_test,version,[]}}), % F2_exp
case TraceLocalVersion of
- true -> expect({trace,Self,call,{my_upgrade_test,local_version,[]}}); % F2_loc
- _ -> ok
+ true -> expect({trace,Self,call,{my_upgrade_test,local_version,[]}}); % F2_loc
+ _ -> ok
end,
true = erlang:delete_module(my_upgrade_test),
@@ -385,10 +377,10 @@ upgrade_do(V1, V2, TraceLocalVersion) ->
ok.
compile_version(Module, Version, Config) ->
- Data = ?config(data_dir, Config),
+ Data = proplists:get_value(data_dir, Config),
File = filename:join(Data, atom_to_list(Module)),
{ok,Module,Bin} = compile:file(File, [{d,'VERSION',Version},
- binary,report]),
+ binary,report]),
Bin.
@@ -397,162 +389,157 @@ compile_version(Module, Version, Config) ->
%% Also, test the '{tracer,Pid}' option.
flags(_Config) ->
case test_server:is_native(filename) of
- true -> {skip,"filename is native"};
- false -> flags()
+ true -> {skip,"filename is native"};
+ false -> flags()
end.
flags() ->
- ?line Tracer = start_tracer_loop(),
- ?line trace_pid(self(), true, [call,{tracer,Tracer}]),
+ Tracer = start_tracer_loop(),
+ trace_pid(self(), true, [call,{tracer,Tracer}]),
%% Trace some functions...
- ?line trace_func({filename,'_','_'}, true),
+ trace_func({filename,'_','_'}, true),
%% ... and call them...
- ?line Self = self(),
- ?line filename:absname("nisse"),
- ?line ?MODULE:expect({trace,Self,call,{filename,absname,["nisse"]}}),
- ?line trace_pid(Self, true, [call,arity]),
- ?line filename:absname("kalle"),
- ?line filename:absname("kalle", "/root"),
- ?line ?MODULE:expect({trace,Self,call,{filename,absname,1}}),
- ?line ?MODULE:expect({trace,Self,call,{filename,absname,2}}),
- ?line trace_info(Self, flags),
+ Self = self(),
+ filename:absname("nisse"),
+ ?MODULE:expect({trace,Self,call,{filename,absname,["nisse"]}}),
+ trace_pid(Self, true, [call,arity]),
+ filename:absname("kalle"),
+ filename:absname("kalle", "/root"),
+ ?MODULE:expect({trace,Self,call,{filename,absname,1}}),
+ ?MODULE:expect({trace,Self,call,{filename,absname,2}}),
+ trace_info(Self, flags),
%% Timestamp + arity.
flag_test(fun() ->
- ?line trace_pid(Self, true, [timestamp]),
- ?line "dum" = filename:basename("/abcd/dum"),
- ?line Ts = expect({trace_ts,Self,call,{filename,basename,1},ts}),
- ?line trace_info(Self, flags),
- Ts
- end),
+ trace_pid(Self, true, [timestamp]),
+ "dum" = filename:basename("/abcd/dum"),
+ Ts = expect({trace_ts,Self,call,{filename,basename,1},ts}),
+ trace_info(Self, flags),
+ Ts
+ end),
%% Timestamp.
- ?line AnArg = "/abcd/hejsan",
+ AnArg = "/abcd/hejsan",
flag_test(fun() ->
- ?line trace_pid(Self, false, [arity]),
- ?line "hejsan" = filename:basename(AnArg),
- ?line Ts = expect({trace_ts,Self,call,
- {filename,basename,[AnArg]},ts}),
- ?line trace_info(Self, flags),
- Ts
- end),
+ trace_pid(Self, false, [arity]),
+ "hejsan" = filename:basename(AnArg),
+ Ts = expect({trace_ts,Self,call,
+ {filename,basename,[AnArg]},ts}),
+ trace_info(Self, flags),
+ Ts
+ end),
%% All flags turned off.
- ?line trace_pid(Self, false, [timestamp]),
- ?line AnotherArg = filename:join(AnArg, "hoppsan"),
- ?line "hoppsan" = filename:basename(AnotherArg),
- ?line expect({trace,Self,call,{filename,join,[AnArg,"hoppsan"]}}),
- ?line expect({trace,Self,call,{filename,basename,[AnotherArg]}}),
- ?line trace_info(Self, flags),
-
+ trace_pid(Self, false, [timestamp]),
+ AnotherArg = filename:join(AnArg, "hoppsan"),
+ "hoppsan" = filename:basename(AnotherArg),
+ expect({trace,Self,call,{filename,join,[AnArg,"hoppsan"]}}),
+ expect({trace,Self,call,{filename,basename,[AnotherArg]}}),
+ trace_info(Self, flags),
+
ok.
flag_test(Test) ->
Now = now(),
Ts = Test(),
case timer:now_diff(Ts, Now) of
- Time when Time < 5*1000000 ->
- %% Reasonable short time.
- ok;
- _Diff ->
- %% Too large difference.
- io:format("Now = ~p\n", [Now]),
- io:format("Ts = ~p\n", [Ts]),
- ?line ?t:fail()
+ Time when Time < 5*1000000 ->
+ %% Reasonable short time.
+ ok;
+ _Diff ->
+ %% Too large difference.
+ ct:fail("Now = ~p, Ts = ~p", [Now, Ts])
end,
flag_test_cpu_timestamp(Test).
flag_test_cpu_timestamp(Test) ->
try erlang:trace(all, true, [cpu_timestamp]) of
- _ ->
- io:format("CPU timestamps"),
- Ts = Test(),
- erlang:trace(all, false, [cpu_timestamp]),
- Origin = {0,0,0},
- Hour = 3600*1000000,
- case timer:now_diff(Ts, Origin) of
- Diff when Diff < 4*Hour ->
- %% In the worst case, CPU timestamps count from when this
- %% Erlang emulator was started. The above test is a conservative
- %% test that all CPU timestamps should pass.
- ok;
- _Time ->
- io:format("Strange CPU timestamp: ~p", [Ts]),
- ?line ?t:fail()
- end,
- io:format("Turned off CPU timestamps")
+ _ ->
+ io:format("CPU timestamps"),
+ Ts = Test(),
+ erlang:trace(all, false, [cpu_timestamp]),
+ Origin = {0,0,0},
+ Hour = 3600*1000000,
+ case timer:now_diff(Ts, Origin) of
+ Diff when Diff < 4*Hour ->
+ %% In the worst case, CPU timestamps count from when this
+ %% Erlang emulator was started. The above test is a conservative
+ %% test that all CPU timestamps should pass.
+ ok;
+ _Time ->
+ ct:fail("Strange CPU timestamp: ~p", [Ts])
+ end,
+ io:format("Turned off CPU timestamps")
catch
- error:badarg -> ok
+ error:badarg -> ok
end.
-errors(doc) -> "Test bad arguments for trace/3 and trace_pattern/3.";
-errors(suite) -> [];
+%% Test bad arguments for trace/3 and trace_pattern/3.
errors(Config) when is_list(Config) ->
- ?line expect_badarg_pid(aaa, true, []),
- ?line expect_badarg_pid({pid,dum}, false, []),
- ?line expect_badarg_func({'_','_',1}, []),
- ?line expect_badarg_func({'_',gosh,1}, []),
- ?line expect_badarg_func({xxx,'_',2}, []),
- ?line expect_badarg_func({xxx,yyy,b}, glurp),
+ expect_badarg_pid(aaa, true, []),
+ expect_badarg_pid({pid,dum}, false, []),
+ expect_badarg_func({'_','_',1}, []),
+ expect_badarg_func({'_',gosh,1}, []),
+ expect_badarg_func({xxx,'_',2}, []),
+ expect_badarg_func({xxx,yyy,b}, glurp),
ok.
expect_badarg_pid(What, How, Flags) ->
case catch erlang:trace(What, How, Flags) of
- {'EXIT',{badarg,Where}} ->
- io:format("trace(~p, ~p, ~p) ->\n {'EXIT',{badarg,~p}}",
- [What,How,Flags,Where]),
- ok;
- Other ->
- io:format("trace(~p, ~p, ~p) -> ~p",
- [What,How,Flags,Other]),
- ?t:fail({unexpected,Other})
+ {'EXIT',{badarg,Where}} ->
+ io:format("trace(~p, ~p, ~p) ->\n {'EXIT',{badarg,~p}}",
+ [What,How,Flags,Where]),
+ ok;
+ Other ->
+ io:format("trace(~p, ~p, ~p) -> ~p",
+ [What,How,Flags,Other]),
+ ct:fail({unexpected,Other})
end.
expect_badarg_func(MFA, Pattern) ->
case catch erlang:trace_pattern(MFA, Pattern) of
- {'EXIT',{badarg,Where}} ->
- io:format("trace_pattern(~p, ~p) ->\n {'EXIT',{badarg,~p}}",
- [MFA,Pattern,Where]),
- ok;
- Other ->
- io:format("trace_pattern(~p, ~p) -> ~p",
- [MFA, Pattern, Other]),
- ?t:fail({unexpected,Other})
+ {'EXIT',{badarg,Where}} ->
+ io:format("trace_pattern(~p, ~p) ->\n {'EXIT',{badarg,~p}}",
+ [MFA,Pattern,Where]),
+ ok;
+ Other ->
+ io:format("trace_pattern(~p, ~p) -> ~p",
+ [MFA, Pattern, Other]),
+ ct:fail({unexpected,Other})
end.
-pam(doc) -> "Basic test of PAM.";
-pam(suite) -> [];
+%% Basic test of PAM.
pam(Config) when is_list(Config) ->
- ?line start_tracer(),
- ?line Self = self(),
+ start_tracer(),
+ Self = self(),
%% Build the match program.
- ?line Prog1 = {[{a,tuple},'$1'],[],[]},
- ?line Prog2 = {[{a,bigger,tuple},'$1'],[],[{message,'$1'}]},
- ?line MatchProg = [Prog1,Prog2],
- ?line pam_trace(MatchProg),
+ Prog1 = {[{a,tuple},'$1'],[],[]},
+ Prog2 = {[{a,bigger,tuple},'$1'],[],[{message,'$1'}]},
+ MatchProg = [Prog1,Prog2],
+ pam_trace(MatchProg),
%% Do some calls.
- ?line ?MODULE:pam_foo(not_a_tuple, [a,b]),
- ?line ?MODULE:pam_foo({a,tuple}, [a,list]),
- ?line ?MODULE:pam_foo([this,one,will,'not',match], dummy_arg),
- ?line LongList = lists:seq(1,10),
- ?line ?MODULE:pam_foo({a,bigger,tuple}, LongList),
+ ?MODULE:pam_foo(not_a_tuple, [a,b]),
+ ?MODULE:pam_foo({a,tuple}, [a,list]),
+ ?MODULE:pam_foo([this,one,will,'not',match], dummy_arg),
+ LongList = lists:seq(1,10),
+ ?MODULE:pam_foo({a,bigger,tuple}, LongList),
%% Check that we get the correct trace messages.
- ?line expect({trace,Self,call,{?MODULE,pam_foo,[{a,tuple},[a,list]]}}),
- ?line expect({trace,Self,call,
- {?MODULE,pam_foo,[{a,bigger,tuple},LongList]},
- LongList}),
+ expect({trace,Self,call,{?MODULE,pam_foo,[{a,tuple},[a,list]]}}),
+ expect({trace,Self,call,
+ {?MODULE,pam_foo,[{a,bigger,tuple},LongList]},
+ LongList}),
- ?line trace_func({?MODULE,pam_foo,'_'}, false),
+ trace_func({?MODULE,pam_foo,'_'}, false),
ok.
pam_trace(Prog) ->
@@ -567,38 +554,38 @@ pam_foo(A, B) ->
%% Test changing PAM programs for a function.
change_pam(_Config) ->
case test_server:is_native(lists) of
- true -> {skip,"lists is native"};
- false -> change_pam()
+ true -> {skip,"lists is native"};
+ false -> change_pam()
end.
change_pam() ->
- ?line start_tracer(),
- ?line Self = self(),
+ start_tracer(),
+ Self = self(),
%% Install the first match program.
%% Test using timestamp at the same time.
- ?line trace_pid(Self, true, [call,arity,timestamp]),
- ?line Prog1 = [{['$1','$2'],[],[{message,'$1'}]}],
- ?line change_pam_trace(Prog1),
- ?line [x,y] = lists:append(id([x]), id([y])),
- ?line {heap_size,_} = erlang:process_info(Self, heap_size),
- ?line expect({trace_ts,Self,call,{lists,append,2},[x],ts}),
- ?line expect({trace_ts,Self,call,{erlang,process_info,2},Self,ts}),
+ trace_pid(Self, true, [call,arity,timestamp]),
+ Prog1 = [{['$1','$2'],[],[{message,'$1'}]}],
+ change_pam_trace(Prog1),
+ [x,y] = lists:append(id([x]), id([y])),
+ {heap_size,_} = erlang:process_info(Self, heap_size),
+ expect({trace_ts,Self,call,{lists,append,2},[x],ts}),
+ expect({trace_ts,Self,call,{erlang,process_info,2},Self,ts}),
%% Install a new PAM program.
- ?line Prog2 = [{['$1','$2'],[],[{message,'$2'}]}],
- ?line change_pam_trace(Prog2),
- ?line [xx,yy] = lists:append(id([xx]), id([yy])),
- ?line {current_function,_} = erlang:process_info(Self, current_function),
- ?line expect({trace_ts,Self,call,{lists,append,2},[yy],ts}),
- ?line expect({trace_ts,Self,call,{erlang,process_info,2},current_function,ts}),
+ Prog2 = [{['$1','$2'],[],[{message,'$2'}]}],
+ change_pam_trace(Prog2),
+ [xx,yy] = lists:append(id([xx]), id([yy])),
+ {current_function,_} = erlang:process_info(Self, current_function),
+ expect({trace_ts,Self,call,{lists,append,2},[yy],ts}),
+ expect({trace_ts,Self,call,{erlang,process_info,2},current_function,ts}),
- ?line 1 = trace_func({lists,append,2}, false),
- ?line 1 = trace_func({erlang,process_info,2}, false),
- ?line {match_spec,false} = trace_info({lists,append,2}, match_spec),
- ?line {match_spec,false} = trace_info({erlang,process_info,2}, match_spec),
+ 1 = trace_func({lists,append,2}, false),
+ 1 = trace_func({erlang,process_info,2}, false),
+ {match_spec,false} = trace_info({lists,append,2}, match_spec),
+ {match_spec,false} = trace_info({erlang,process_info,2}, match_spec),
ok.
@@ -611,71 +598,71 @@ change_pam_trace(Prog) ->
return_trace(_Config) ->
case test_server:is_native(lists) of
- true -> {skip,"lists is native"};
- false -> return_trace()
+ true -> {skip,"lists is native"};
+ false -> return_trace()
end.
return_trace() ->
X = {save,me},
- ?line start_tracer(),
- ?line Self = self(),
+ start_tracer(),
+ Self = self(),
%% Test call and return trace and timestamp.
- ?line trace_pid(Self, true, [call,timestamp]),
+ trace_pid(Self, true, [call,timestamp]),
Stupid = {pointless,tuple},
- ?line Prog1 = [{['$1','$2'],[],[{return_trace},{message,{Stupid}}]}],
- ?line 1 = trace_func({lists,append,2}, Prog1),
- ?line 1 = trace_func({erlang,process_info,2}, Prog1),
- ?line {match_spec,Prog1} = trace_info({lists,append,2}, match_spec),
- ?line {match_spec,Prog1} = trace_info({erlang,process_info,2}, match_spec),
+ Prog1 = [{['$1','$2'],[],[{return_trace},{message,{Stupid}}]}],
+ 1 = trace_func({lists,append,2}, Prog1),
+ 1 = trace_func({erlang,process_info,2}, Prog1),
+ {match_spec,Prog1} = trace_info({lists,append,2}, match_spec),
+ {match_spec,Prog1} = trace_info({erlang,process_info,2}, match_spec),
- ?line [x,y] = lists:append(id([x]), id([y])),
+ [x,y] = lists:append(id([x]), id([y])),
Current = {current_function,{?MODULE,return_trace,0}},
- ?line Current = erlang:process_info(Self, current_function),
- ?line expect({trace_ts,Self,call,{lists,append,[[x],[y]]},Stupid,ts}),
- ?line expect({trace_ts,Self,return_from,{lists,append,2},[x,y],ts}),
- ?line expect({trace_ts,Self,call,{erlang,process_info,[Self,current_function]},
- Stupid,ts}),
- ?line expect({trace_ts,Self,return_from,{erlang,process_info,2},Current,ts}),
+ Current = erlang:process_info(Self, current_function),
+ expect({trace_ts,Self,call,{lists,append,[[x],[y]]},Stupid,ts}),
+ expect({trace_ts,Self,return_from,{lists,append,2},[x,y],ts}),
+ expect({trace_ts,Self,call,{erlang,process_info,[Self,current_function]},
+ Stupid,ts}),
+ expect({trace_ts,Self,return_from,{erlang,process_info,2},Current,ts}),
%% Try catch/exit.
- ?line 1 = trace_func({?MODULE,nasty,0}, [{[],[],[{return_trace},{message,false}]}]),
- ?line {'EXIT',good_bye} = (catch ?MODULE:nasty()),
- ?line 1 = trace_func({?MODULE,nasty,0}, false),
+ 1 = trace_func({?MODULE,nasty,0}, [{[],[],[{return_trace},{message,false}]}]),
+ {'EXIT',good_bye} = (catch ?MODULE:nasty()),
+ 1 = trace_func({?MODULE,nasty,0}, false),
%% Turn off trace.
- ?line 1 = trace_func({lists,append,2}, false),
- ?line 1 = trace_func({erlang,process_info,2}, false),
- ?line {match_spec,false} = trace_info({lists,append,2}, match_spec),
- ?line {match_spec,false} = trace_info({erlang,process_info,2}, match_spec),
+ 1 = trace_func({lists,append,2}, false),
+ 1 = trace_func({erlang,process_info,2}, false),
+ {match_spec,false} = trace_info({lists,append,2}, match_spec),
+ {match_spec,false} = trace_info({erlang,process_info,2}, match_spec),
%% No timestamp, no trace message for call.
- ?line trace_pid(Self, false, [timestamp]),
- ?line Prog2 = [{['$1','$2'],[],[{return_trace},{message,false}]},
- {['$1'],[],[{return_trace},{message,false}]}],
- ?line 1 = trace_func({lists,seq,2}, Prog2),
- ?line 1 = trace_func({erlang,atom_to_list,1}, Prog2),
- ?line {match_spec,Prog2} = trace_info({lists,seq,2}, match_spec),
- ?line {match_spec,Prog2} = trace_info({erlang,atom_to_list,1}, match_spec),
+ trace_pid(Self, false, [timestamp]),
+ Prog2 = [{['$1','$2'],[],[{return_trace},{message,false}]},
+ {['$1'],[],[{return_trace},{message,false}]}],
+ 1 = trace_func({lists,seq,2}, Prog2),
+ 1 = trace_func({erlang,atom_to_list,1}, Prog2),
+ {match_spec,Prog2} = trace_info({lists,seq,2}, match_spec),
+ {match_spec,Prog2} = trace_info({erlang,atom_to_list,1}, match_spec),
- ?line lists:seq(2, 7),
- ?line _ = atom_to_list(non_literal(nisse)),
- ?line expect({trace,Self,return_from,{lists,seq,2},[2,3,4,5,6,7]}),
- ?line expect({trace,Self,return_from,{erlang,atom_to_list,1},"nisse"}),
+ lists:seq(2, 7),
+ _ = atom_to_list(non_literal(nisse)),
+ expect({trace,Self,return_from,{lists,seq,2},[2,3,4,5,6,7]}),
+ expect({trace,Self,return_from,{erlang,atom_to_list,1},"nisse"}),
%% Turn off trace.
- ?line 1 = trace_func({lists,seq,2}, false),
- ?line 1 = trace_func({erlang,atom_to_list,1}, false),
- ?line {match_spec,false} = trace_info({lists,seq,2}, match_spec),
- ?line {match_spec,false} = trace_info({erlang,atom_to_list,1}, match_spec),
+ 1 = trace_func({lists,seq,2}, false),
+ 1 = trace_func({erlang,atom_to_list,1}, false),
+ {match_spec,false} = trace_info({lists,seq,2}, match_spec),
+ {match_spec,false} = trace_info({erlang,atom_to_list,1}, match_spec),
+
+ {save,me} = X,
- ?line {save,me} = X,
-
ok.
nasty() ->
@@ -683,396 +670,393 @@ nasty() ->
exception_trace(_Config) ->
case test_server:is_native(lists) of
- true -> {skip,"lists is native"};
- false -> exception_trace()
+ true -> {skip,"lists is native"};
+ false -> exception_trace()
end.
exception_trace() ->
X = {save,me},
- ?line start_tracer(),
- ?line Self = self(),
+ start_tracer(),
+ Self = self(),
%% Test call and return trace and timestamp.
- ?line trace_pid(Self, true, [call,timestamp]),
+ trace_pid(Self, true, [call,timestamp]),
Stupid = {pointless,tuple},
- ?line Prog1 = [{['$1','$2'],[],[{exception_trace},{message,{Stupid}}]}],
- ?line 1 = trace_func({lists,append,2}, Prog1),
- ?line 1 = trace_func({erlang,process_info,2}, Prog1),
- ?line {match_spec,Prog1} = trace_info({lists,append,2}, match_spec),
- ?line {match_spec,Prog1} =
- trace_info({erlang,process_info,2}, match_spec),
-
- ?line [x,y] = lists:append(id([x]), id([y])),
+ Prog1 = [{['$1','$2'],[],[{exception_trace},{message,{Stupid}}]}],
+ 1 = trace_func({lists,append,2}, Prog1),
+ 1 = trace_func({erlang,process_info,2}, Prog1),
+ {match_spec,Prog1} = trace_info({lists,append,2}, match_spec),
+ {match_spec,Prog1} =
+ trace_info({erlang,process_info,2}, match_spec),
+
+ [x,y] = lists:append(id([x]), id([y])),
Current = {current_function,{?MODULE,exception_trace,0}},
- ?line Current = erlang:process_info(Self, current_function),
- ?line expect({trace_ts,Self,call,{lists,append,[[x],[y]]},Stupid,ts}),
- ?line expect({trace_ts,Self,return_from,{lists,append,2},[x,y],ts}),
- ?line expect({trace_ts,Self,call,{erlang,process_info,
- [Self,current_function]},
- Stupid,ts}),
- ?line expect({trace_ts,Self,return_from,
- {erlang,process_info,2},Current,ts}),
+ Current = erlang:process_info(Self, current_function),
+ expect({trace_ts,Self,call,{lists,append,[[x],[y]]},Stupid,ts}),
+ expect({trace_ts,Self,return_from,{lists,append,2},[x,y],ts}),
+ expect({trace_ts,Self,call,{erlang,process_info,
+ [Self,current_function]},
+ Stupid,ts}),
+ expect({trace_ts,Self,return_from,
+ {erlang,process_info,2},Current,ts}),
%% Try catch/exit.
- ?line 1 = trace_func({?MODULE,nasty,0},
- [{[],[],[{exception_trace},{message,false}]}]),
- ?line {'EXIT',good_bye} = (catch ?MODULE:nasty()),
- ?line expect({trace_ts,Self,exception_from,
- {?MODULE,nasty,0},{exit,good_bye},ts}),
- ?line 1 = trace_func({?MODULE,nasty,0}, false),
+ 1 = trace_func({?MODULE,nasty,0},
+ [{[],[],[{exception_trace},{message,false}]}]),
+ {'EXIT',good_bye} = (catch ?MODULE:nasty()),
+ expect({trace_ts,Self,exception_from,
+ {?MODULE,nasty,0},{exit,good_bye},ts}),
+ 1 = trace_func({?MODULE,nasty,0}, false),
%% Turn off trace.
- ?line 1 = trace_func({lists,append,2}, false),
- ?line 1 = trace_func({erlang,process_info,2}, false),
- ?line {match_spec,false} = trace_info({lists,append,2}, match_spec),
- ?line {match_spec,false} =
- trace_info({erlang,process_info,2}, match_spec),
+ 1 = trace_func({lists,append,2}, false),
+ 1 = trace_func({erlang,process_info,2}, false),
+ {match_spec,false} = trace_info({lists,append,2}, match_spec),
+ {match_spec,false} =
+ trace_info({erlang,process_info,2}, match_spec),
%% No timestamp, no trace message for call.
- ?line trace_pid(Self, false, [timestamp]),
- ?line Prog2 = [{['$1','$2'],[],[{exception_trace},{message,false}]},
- {['$1'],[],[{exception_trace},{message,false}]}],
- ?line 1 = trace_func({lists,seq,2}, Prog2),
- ?line 1 = trace_func({erlang,atom_to_list,1}, Prog2),
- ?line {match_spec,Prog2} = trace_info({lists,seq,2}, match_spec),
- ?line {match_spec,Prog2} =
- trace_info({erlang,atom_to_list,1}, match_spec),
+ trace_pid(Self, false, [timestamp]),
+ Prog2 = [{['$1','$2'],[],[{exception_trace},{message,false}]},
+ {['$1'],[],[{exception_trace},{message,false}]}],
+ 1 = trace_func({lists,seq,2}, Prog2),
+ 1 = trace_func({erlang,atom_to_list,1}, Prog2),
+ {match_spec,Prog2} = trace_info({lists,seq,2}, match_spec),
+ {match_spec,Prog2} =
+ trace_info({erlang,atom_to_list,1}, match_spec),
- ?line lists:seq(2, 7),
- ?line _ = atom_to_list(non_literal(nisse)),
- ?line expect({trace,Self,return_from,{lists,seq,2},[2,3,4,5,6,7]}),
- ?line expect({trace,Self,return_from,{erlang,atom_to_list,1},"nisse"}),
+ lists:seq(2, 7),
+ _ = atom_to_list(non_literal(nisse)),
+ expect({trace,Self,return_from,{lists,seq,2},[2,3,4,5,6,7]}),
+ expect({trace,Self,return_from,{erlang,atom_to_list,1},"nisse"}),
%% Turn off trace.
- ?line 1 = trace_func({lists,seq,2}, false),
- ?line 1 = trace_func({erlang,atom_to_list,1}, false),
- ?line {match_spec,false} = trace_info({lists,seq,2}, match_spec),
- ?line {match_spec,false} =
- trace_info({erlang,atom_to_list,1}, match_spec),
+ 1 = trace_func({lists,seq,2}, false),
+ 1 = trace_func({erlang,atom_to_list,1}, false),
+ {match_spec,false} = trace_info({lists,seq,2}, match_spec),
+ {match_spec,false} =
+ trace_info({erlang,atom_to_list,1}, match_spec),
- ?line expect(),
- ?line {save,me} = X,
+ expect(),
+ {save,me} = X,
ok.
-on_load(doc) -> "Test the on_load argument for trace_pattern/3.";
-on_load(suite) -> [];
+%% Test the on_load argument for trace_pattern/3.
on_load(Config) when is_list(Config) ->
- ?line 0 = erlang:trace_pattern(on_load, []),
- ?line {traced,global} = erlang:trace_info(on_load, traced),
- ?line {match_spec,[]} = erlang:trace_info(on_load, match_spec),
+ 0 = erlang:trace_pattern(on_load, []),
+ {traced,global} = erlang:trace_info(on_load, traced),
+ {match_spec,[]} = erlang:trace_info(on_load, match_spec),
- ?line 0 = erlang:trace_pattern(on_load, true, [local]),
- ?line {traced,local} = erlang:trace_info(on_load, traced),
- ?line {match_spec,[]} = erlang:trace_info(on_load, match_spec),
+ 0 = erlang:trace_pattern(on_load, true, [local]),
+ {traced,local} = erlang:trace_info(on_load, traced),
+ {match_spec,[]} = erlang:trace_info(on_load, match_spec),
- ?line 0 = erlang:trace_pattern(on_load, false, [local]),
- ?line {traced,false} = erlang:trace_info(on_load, traced),
- ?line {match_spec,false} = erlang:trace_info(on_load, match_spec),
+ 0 = erlang:trace_pattern(on_load, false, [local]),
+ {traced,false} = erlang:trace_info(on_load, traced),
+ {match_spec,false} = erlang:trace_info(on_load, match_spec),
- ?line Pam1 = [{[],[],[{message,false}]}],
- ?line 0 = erlang:trace_pattern(on_load, Pam1),
- ?line {traced,global} = erlang:trace_info(on_load, traced),
- ?line {match_spec,Pam1} = erlang:trace_info(on_load, match_spec),
+ Pam1 = [{[],[],[{message,false}]}],
+ 0 = erlang:trace_pattern(on_load, Pam1),
+ {traced,global} = erlang:trace_info(on_load, traced),
+ {match_spec,Pam1} = erlang:trace_info(on_load, match_spec),
- ?line 0 = erlang:trace_pattern(on_load, true, [local]),
- ?line 0 = erlang:trace_pattern(on_load, false, [local]),
+ 0 = erlang:trace_pattern(on_load, true, [local]),
+ 0 = erlang:trace_pattern(on_load, false, [local]),
ok.
-deep_exception(doc) -> "Test the new exception trace.";
-deep_exception(suite) -> [];
+%% Test the new exception trace.
deep_exception(Config) when is_list(Config) ->
deep_exception().
deep_exception() ->
- ?line start_tracer(),
- ?line Self = self(),
- ?line N = 200000,
- ?line LongImproperList = seq(1, N-1, N),
-
+ start_tracer(),
+ Self = self(),
+ N = 200000,
+ LongImproperList = seq(1, N-1, N),
+
Prog = [{'_',[],[{exception_trace}]}],
-%% ?line 1 = trace_pid(Self, true, [call]),
- ?line 1 = trace_func({?MODULE,deep,'_'}, Prog),
- ?line 1 = trace_func({?MODULE,deep_1,'_'}, Prog),
- ?line 1 = trace_func({?MODULE,deep_2,'_'}, Prog),
- ?line 1 = trace_func({?MODULE,deep_3,'_'}, Prog),
- ?line 1 = trace_func({?MODULE,deep_4,'_'}, Prog),
- ?line 1 = trace_func({?MODULE,deep_5,'_'}, Prog),
- ?line 1 = trace_func({?MODULE,id,'_'}, Prog),
- ?line 1 = trace_func({erlang,'++','_'}, Prog),
- ?line 1 = trace_func({erlang,exit,1}, Prog),
- ?line 1 = trace_func({erlang,throw,1}, Prog),
- ?line 2 = trace_func({erlang,error,'_'}, Prog),
- ?line 1 = trace_func({lists,reverse,2}, Prog),
-
- ?line deep_exception(?LINE, exit, [paprika], 1,
- [{trace,Self,call,{erlang,exit,[paprika]}},
- {trace,Self,exception_from,{erlang,exit,1},
- {exit,paprika}}],
- exception_from, {exit,paprika}),
- ?line deep_exception(?LINE, throw, [3.14], 2,
- [{trace,Self,call,{erlang,throw,[3.14]}},
- {trace,Self,exception_from,{erlang,throw,1},
- {throw,3.14}}],
- exception_from, {throw,3.14}),
- ?line deep_exception(?LINE, error, [{paprika}], 3,
- [{trace,Self,call,{erlang,error,[{paprika}]}},
- {trace,Self,exception_from,{erlang,error,1},
- {error,{paprika}}}],
- exception_from, {error,{paprika}}),
- ?line deep_exception(?LINE, error, ["{paprika}",[]], 3,
- [{trace,Self,call,{erlang,error,["{paprika}",[]]}},
- {trace,Self,exception_from,{erlang,error,2},
- {error,"{paprika}"}}],
- exception_from, {error,"{paprika}"}),
- ?line deep_exception(?LINE, id, [broccoli], 4, [],
- return_from, broccoli),
- ?line deep_exception(
- ?LINE, append, [1,2], 5,
- [{trace,Self,call,{erlang,'++',[1,2]}},
- {trace,Self,exception_from,{erlang,'++',2},{error,badarg}}],
- exception_from, {error,badarg}),
- ?line deep_exception(?LINE, '=', [1,2], 6, [],
- exception_from, {error,{badmatch,2}}),
+ %% 1 = trace_pid(Self, true, [call]),
+ 1 = trace_func({?MODULE,deep,'_'}, Prog),
+ 1 = trace_func({?MODULE,deep_1,'_'}, Prog),
+ 1 = trace_func({?MODULE,deep_2,'_'}, Prog),
+ 1 = trace_func({?MODULE,deep_3,'_'}, Prog),
+ 1 = trace_func({?MODULE,deep_4,'_'}, Prog),
+ 1 = trace_func({?MODULE,deep_5,'_'}, Prog),
+ 1 = trace_func({?MODULE,id,'_'}, Prog),
+ 1 = trace_func({erlang,'++','_'}, Prog),
+ 1 = trace_func({erlang,exit,1}, Prog),
+ 1 = trace_func({erlang,throw,1}, Prog),
+ 2 = trace_func({erlang,error,'_'}, Prog),
+ 1 = trace_func({lists,reverse,2}, Prog),
+
+ deep_exception(?LINE, exit, [paprika], 1,
+ [{trace,Self,call,{erlang,exit,[paprika]}},
+ {trace,Self,exception_from,{erlang,exit,1},
+ {exit,paprika}}],
+ exception_from, {exit,paprika}),
+ deep_exception(?LINE, throw, [3.14], 2,
+ [{trace,Self,call,{erlang,throw,[3.14]}},
+ {trace,Self,exception_from,{erlang,throw,1},
+ {throw,3.14}}],
+ exception_from, {throw,3.14}),
+ deep_exception(?LINE, error, [{paprika}], 3,
+ [{trace,Self,call,{erlang,error,[{paprika}]}},
+ {trace,Self,exception_from,{erlang,error,1},
+ {error,{paprika}}}],
+ exception_from, {error,{paprika}}),
+ deep_exception(?LINE, error, ["{paprika}",[]], 3,
+ [{trace,Self,call,{erlang,error,["{paprika}",[]]}},
+ {trace,Self,exception_from,{erlang,error,2},
+ {error,"{paprika}"}}],
+ exception_from, {error,"{paprika}"}),
+ deep_exception(?LINE, id, [broccoli], 4, [],
+ return_from, broccoli),
+ deep_exception(
+ ?LINE, append, [1,2], 5,
+ [{trace,Self,call,{erlang,'++',[1,2]}},
+ {trace,Self,exception_from,{erlang,'++',2},{error,badarg}}],
+ exception_from, {error,badarg}),
+ deep_exception(?LINE, '=', [1,2], 6, [],
+ exception_from, {error,{badmatch,2}}),
%%
- ?line io:format("== Subtest: ~w", [?LINE]),
- ?line try lists:reverse(LongImproperList, []) of
- R1 -> test_server:fail({returned,abbr(R1)})
- catch error:badarg -> ok
- end,
- ?line expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}})
- when is_list(L1), is_list(L2), S == Self ->
- next;
- ({trace,S,exception_from,
- {lists,reverse,2},{error,badarg}})
- when S == Self ->
- expected;
- ('_') ->
- {trace,Self,exception_from,
- {lists,reverse,2},{error,badarg}};
- (_) ->
- {unexpected,
- {trace,Self,exception_from,
- {lists,reverse,2},{error,badarg}}}
- end),
- ?line deep_exception(?LINE, deep_5, [1,2], 7,
- [{trace,Self,call,{erlang,error,[undef]}},
- {trace,Self,exception_from,{erlang,error,1},
- {error,undef}}],
- exception_from, {error,undef}),
- ?line deep_exception(?LINE, deep_5, [undef], 8,
- [{trace,Self,call,{?MODULE,deep_5,[undef]}},
- {trace,Self,exception_from,{?MODULE,deep_5,1},
- {error,function_clause}}],
- exception_from, {error,function_clause}),
-
+ io:format("== Subtest: ~w", [?LINE]),
+ try lists:reverse(LongImproperList, []) of
+ R1 -> ct:fail({returned,abbr(R1)})
+ catch error:badarg -> ok
+ end,
+ expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}})
+ when is_list(L1), is_list(L2), S == Self ->
+ next;
+ ({trace,S,exception_from,
+ {lists,reverse,2},{error,badarg}})
+ when S == Self ->
+ expected;
+ ('_') ->
+ {trace,Self,exception_from,
+ {lists,reverse,2},{error,badarg}};
+ (_) ->
+ {unexpected,
+ {trace,Self,exception_from,
+ {lists,reverse,2},{error,badarg}}}
+ end),
+ deep_exception(?LINE, deep_5, [1,2], 7,
+ [{trace,Self,call,{erlang,error,[undef]}},
+ {trace,Self,exception_from,{erlang,error,1},
+ {error,undef}}],
+ exception_from, {error,undef}),
+ deep_exception(?LINE, deep_5, [undef], 8,
+ [{trace,Self,call,{?MODULE,deep_5,[undef]}},
+ {trace,Self,exception_from,{?MODULE,deep_5,1},
+ {error,function_clause}}],
+ exception_from, {error,function_clause}),
+
%% Apply
%%
- ?line deep_exception(?LINE, apply, [erlang,error,[[mo|rot]]], 1,
- [{trace,Self,call,{erlang,error,[[mo|rot]]}},
- {trace,Self,exception_from,{erlang,error,1},
- {error,[mo|rot]}}],
- exception_from, {error,[mo|rot]}),
- ?line deep_exception(?LINE, apply, [erlang,error,[[mo|"rot"],[]]], 1,
- [{trace,Self,call,{erlang,error,[[mo|"rot"],[]]}},
- {trace,Self,exception_from,{erlang,error,2},
- {error,[mo|"rot"]}}],
- exception_from, {error,[mo|"rot"]}),
- ?line Morot = make_ref(),
- ?line deep_exception(?LINE, apply, [erlang,throw,[Morot]], 3,
- [{trace,Self,call,{erlang,throw,[Morot]}},
- {trace,Self,exception_from,{erlang,throw,1},
- {throw,Morot}}],
- exception_from, {throw,Morot}),
- ?line deep_exception(?LINE, apply, [erlang,exit,[["morot"|Morot]]], 2,
- [{trace,Self,call,{erlang,exit,[["morot"|Morot]]}},
- {trace,Self,exception_from,{erlang,exit,1},
- {exit,["morot"|Morot]}}],
- exception_from, {exit,["morot"|Morot]}),
- ?line deep_exception(
- ?LINE, apply, [?MODULE,id,[spenat]], 4,
- [{trace,Self,call,{?MODULE,id,[spenat]}},
- {trace,Self,return_from,{?MODULE,id,1},spenat}],
- return_from, spenat),
- ?line deep_exception(
- ?LINE, apply, [erlang,'++',[1,2]], 5,
- [{trace,Self,call,{erlang,'++',[1,2]}},
- {trace,Self,exception_from,{erlang,'++',2},{error,badarg}}],
- exception_from, {error,badarg}),
- ?line io:format("== Subtest: ~w", [?LINE]),
- ?line try apply(lists, reverse, [LongImproperList, []]) of
- R2 -> test_server:fail({returned,abbr(R2)})
- catch error:badarg -> ok
- end,
- ?line expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}})
- when is_list(L1), is_list(L2), S == Self ->
- next;
- ({trace,S,exception_from,
- {lists,reverse,2},{error,badarg}})
- when S == Self ->
- expected;
- ('_') ->
- {trace,Self,exception_from,
- {lists,reverse,2},{error,badarg}};
- (_) ->
- {unexpected,
- {trace,Self,exception_from,
- {lists,reverse,2},{error,badarg}}}
- end),
- ?line deep_exception(?LINE, apply, [?MODULE,deep_5,[1,2]], 7,
- [{trace,Self,call,{erlang,error,[undef]}},
- {trace,Self,exception_from,{erlang,error,1},
- {error,undef}}],
- exception_from, {error,undef}),
- ?line deep_exception(?LINE, apply, [?MODULE,deep_5,[undef]], 8,
- [{trace,Self,call,{?MODULE,deep_5,[undef]}},
- {trace,Self,exception_from,{?MODULE,deep_5,1},
- {error,function_clause}}],
- exception_from, {error,function_clause}),
+ deep_exception(?LINE, apply, [erlang,error,[[mo|rot]]], 1,
+ [{trace,Self,call,{erlang,error,[[mo|rot]]}},
+ {trace,Self,exception_from,{erlang,error,1},
+ {error,[mo|rot]}}],
+ exception_from, {error,[mo|rot]}),
+ deep_exception(?LINE, apply, [erlang,error,[[mo|"rot"],[]]], 1,
+ [{trace,Self,call,{erlang,error,[[mo|"rot"],[]]}},
+ {trace,Self,exception_from,{erlang,error,2},
+ {error,[mo|"rot"]}}],
+ exception_from, {error,[mo|"rot"]}),
+ Morot = make_ref(),
+ deep_exception(?LINE, apply, [erlang,throw,[Morot]], 3,
+ [{trace,Self,call,{erlang,throw,[Morot]}},
+ {trace,Self,exception_from,{erlang,throw,1},
+ {throw,Morot}}],
+ exception_from, {throw,Morot}),
+ deep_exception(?LINE, apply, [erlang,exit,[["morot"|Morot]]], 2,
+ [{trace,Self,call,{erlang,exit,[["morot"|Morot]]}},
+ {trace,Self,exception_from,{erlang,exit,1},
+ {exit,["morot"|Morot]}}],
+ exception_from, {exit,["morot"|Morot]}),
+ deep_exception(
+ ?LINE, apply, [?MODULE,id,[spenat]], 4,
+ [{trace,Self,call,{?MODULE,id,[spenat]}},
+ {trace,Self,return_from,{?MODULE,id,1},spenat}],
+ return_from, spenat),
+ deep_exception(
+ ?LINE, apply, [erlang,'++',[1,2]], 5,
+ [{trace,Self,call,{erlang,'++',[1,2]}},
+ {trace,Self,exception_from,{erlang,'++',2},{error,badarg}}],
+ exception_from, {error,badarg}),
+ io:format("== Subtest: ~w", [?LINE]),
+ try apply(lists, reverse, [LongImproperList, []]) of
+ R2 -> ct:fail({returned,abbr(R2)})
+ catch error:badarg -> ok
+ end,
+ expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}})
+ when is_list(L1), is_list(L2), S == Self ->
+ next;
+ ({trace,S,exception_from,
+ {lists,reverse,2},{error,badarg}})
+ when S == Self ->
+ expected;
+ ('_') ->
+ {trace,Self,exception_from,
+ {lists,reverse,2},{error,badarg}};
+ (_) ->
+ {unexpected,
+ {trace,Self,exception_from,
+ {lists,reverse,2},{error,badarg}}}
+ end),
+ deep_exception(?LINE, apply, [?MODULE,deep_5,[1,2]], 7,
+ [{trace,Self,call,{erlang,error,[undef]}},
+ {trace,Self,exception_from,{erlang,error,1},
+ {error,undef}}],
+ exception_from, {error,undef}),
+ deep_exception(?LINE, apply, [?MODULE,deep_5,[undef]], 8,
+ [{trace,Self,call,{?MODULE,deep_5,[undef]}},
+ {trace,Self,exception_from,{?MODULE,deep_5,1},
+ {error,function_clause}}],
+ exception_from, {error,function_clause}),
%% Apply of fun
%%
- ?line deep_exception(?LINE, apply,
- [fun () ->
- erlang:error([{"palsternacka",3.14},17])
- end, []], 1,
- [{trace,Self,call,
- {erlang,error,[[{"palsternacka",3.14},17]]}},
- {trace,Self,exception_from,{erlang,error,1},
- {error,[{"palsternacka",3.14},17]}}],
- exception_from, {error,[{"palsternacka",3.14},17]}),
- ?line deep_exception(?LINE, apply,
- [fun () ->
- erlang:error(["palsternacka",17], [])
- end, []], 1,
- [{trace,Self,call,
- {erlang,error,[["palsternacka",17],[]]}},
- {trace,Self,exception_from,{erlang,error,2},
- {error,["palsternacka",17]}}],
- exception_from, {error,["palsternacka",17]}),
- ?line deep_exception(?LINE, apply,
- [fun () -> erlang:throw(Self) end, []], 2,
- [{trace,Self,call,{erlang,throw,[Self]}},
- {trace,Self,exception_from,{erlang,throw,1},
- {throw,Self}}],
- exception_from, {throw,Self}),
- ?line deep_exception(?LINE, apply,
- [fun () ->
- erlang:exit({1,2,3,4,[5,palsternacka]})
- end, []], 3,
- [{trace,Self,call,
- {erlang,exit,[{1,2,3,4,[5,palsternacka]}]}},
- {trace,Self,exception_from,{erlang,exit,1},
- {exit,{1,2,3,4,[5,palsternacka]}}}],
- exception_from, {exit,{1,2,3,4,[5,palsternacka]}}),
- ?line deep_exception(?LINE, apply,
- [fun () -> ?MODULE:id(bladsallad) end, []], 4,
- [{trace,Self,call,{?MODULE,id,[bladsallad]}},
- {trace,Self,return_from,{?MODULE,id,1},bladsallad}],
- return_from, bladsallad),
- ?line deep_exception(?LINE, apply,
- [fun (A, B) -> A ++ B end, [1,2]], 5,
- [{trace,Self,call,{erlang,'++',[1,2]}},
- {trace,Self,exception_from,
- {erlang,'++',2},{error,badarg}}],
- exception_from, {error,badarg}),
- ?line deep_exception(?LINE, apply, [fun (A, B) -> A = B end, [1,2]], 6,
- [],
- exception_from, {error,{badmatch,2}}),
- ?line io:format("== Subtest: ~w", [?LINE]),
- ?line try apply(fun() -> lists:reverse(LongImproperList, []) end, []) of
- R3 -> test_server:fail({returned,abbr(R3)})
- catch error:badarg -> ok
- end,
- ?line expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}})
- when is_list(L1), is_list(L2), S == Self ->
- next;
- ({trace,S,exception_from,
- {lists,reverse,2},{error,badarg}})
- when S == Self ->
- expected;
- ('_') ->
- {trace,Self,exception_from,
- {lists,reverse,2},{error,badarg}};
- (_) ->
- {unexpected,
- {trace,Self,exception_from,
- {lists,reverse,2},{error,badarg}}}
- end),
- ?line deep_exception(?LINE, apply,
- [fun () -> ?MODULE:deep_5(1,2) end, []], 7,
- [{trace,Self,call,{erlang,error,[undef]}},
- {trace,Self,exception_from,{erlang,error,1},
- {error,undef}}],
- exception_from, {error,undef}),
- ?line deep_exception(?LINE, apply,
- [fun () -> ?MODULE:deep_5(undef) end, []], 8,
- [{trace,Self,call,{?MODULE,deep_5,[undef]}},
- {trace,Self,exception_from,{?MODULE,deep_5,1},
- {error,function_clause}}],
- exception_from, {error,function_clause}),
-
- ?line trace_func({?MODULE,'_','_'}, false),
- ?line trace_func({erlang,'_','_'}, false),
- ?line trace_func({lists,'_','_'}, false),
- ?line expect(),
- ?line ok.
+ deep_exception(?LINE, apply,
+ [fun () ->
+ erlang:error([{"palsternacka",3.14},17])
+ end, []], 1,
+ [{trace,Self,call,
+ {erlang,error,[[{"palsternacka",3.14},17]]}},
+ {trace,Self,exception_from,{erlang,error,1},
+ {error,[{"palsternacka",3.14},17]}}],
+ exception_from, {error,[{"palsternacka",3.14},17]}),
+ deep_exception(?LINE, apply,
+ [fun () ->
+ erlang:error(["palsternacka",17], [])
+ end, []], 1,
+ [{trace,Self,call,
+ {erlang,error,[["palsternacka",17],[]]}},
+ {trace,Self,exception_from,{erlang,error,2},
+ {error,["palsternacka",17]}}],
+ exception_from, {error,["palsternacka",17]}),
+ deep_exception(?LINE, apply,
+ [fun () -> erlang:throw(Self) end, []], 2,
+ [{trace,Self,call,{erlang,throw,[Self]}},
+ {trace,Self,exception_from,{erlang,throw,1},
+ {throw,Self}}],
+ exception_from, {throw,Self}),
+ deep_exception(?LINE, apply,
+ [fun () ->
+ erlang:exit({1,2,3,4,[5,palsternacka]})
+ end, []], 3,
+ [{trace,Self,call,
+ {erlang,exit,[{1,2,3,4,[5,palsternacka]}]}},
+ {trace,Self,exception_from,{erlang,exit,1},
+ {exit,{1,2,3,4,[5,palsternacka]}}}],
+ exception_from, {exit,{1,2,3,4,[5,palsternacka]}}),
+ deep_exception(?LINE, apply,
+ [fun () -> ?MODULE:id(bladsallad) end, []], 4,
+ [{trace,Self,call,{?MODULE,id,[bladsallad]}},
+ {trace,Self,return_from,{?MODULE,id,1},bladsallad}],
+ return_from, bladsallad),
+ deep_exception(?LINE, apply,
+ [fun (A, B) -> A ++ B end, [1,2]], 5,
+ [{trace,Self,call,{erlang,'++',[1,2]}},
+ {trace,Self,exception_from,
+ {erlang,'++',2},{error,badarg}}],
+ exception_from, {error,badarg}),
+ deep_exception(?LINE, apply, [fun (A, B) -> A = B end, [1,2]], 6,
+ [],
+ exception_from, {error,{badmatch,2}}),
+ io:format("== Subtest: ~w", [?LINE]),
+ try apply(fun() -> lists:reverse(LongImproperList, []) end, []) of
+ R3 -> ct:fail({returned,abbr(R3)})
+ catch error:badarg -> ok
+ end,
+ expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}})
+ when is_list(L1), is_list(L2), S == Self ->
+ next;
+ ({trace,S,exception_from,
+ {lists,reverse,2},{error,badarg}})
+ when S == Self ->
+ expected;
+ ('_') ->
+ {trace,Self,exception_from,
+ {lists,reverse,2},{error,badarg}};
+ (_) ->
+ {unexpected,
+ {trace,Self,exception_from,
+ {lists,reverse,2},{error,badarg}}}
+ end),
+ deep_exception(?LINE, apply,
+ [fun () -> ?MODULE:deep_5(1,2) end, []], 7,
+ [{trace,Self,call,{erlang,error,[undef]}},
+ {trace,Self,exception_from,{erlang,error,1},
+ {error,undef}}],
+ exception_from, {error,undef}),
+ deep_exception(?LINE, apply,
+ [fun () -> ?MODULE:deep_5(undef) end, []], 8,
+ [{trace,Self,call,{?MODULE,deep_5,[undef]}},
+ {trace,Self,exception_from,{?MODULE,deep_5,1},
+ {error,function_clause}}],
+ exception_from, {error,function_clause}),
+
+ trace_func({?MODULE,'_','_'}, false),
+ trace_func({erlang,'_','_'}, false),
+ trace_func({lists,'_','_'}, false),
+ expect(),
+ ok.
deep_exception(Line, B, Q, N, Extra, Tag, R) ->
- ?line Self = self(),
- ?line io:format("== Subtest: ~w", [Line]),
- ?line Result = ?MODULE:deep(N, B, Q),
- ?line Result = deep_expect(Self, B, Q, N, Extra, Tag, R).
+ Self = self(),
+ io:format("== Subtest: ~w", [Line]),
+ Result = ?MODULE:deep(N, B, Q),
+ Result = deep_expect(Self, B, Q, N, Extra, Tag, R).
deep_expect(Self, B, Q, N, Extra, Tag, R) ->
- ?line expect({trace,Self,call,{?MODULE,deep,[N,B,Q]}}),
- ?line Result = deep_expect_N(Self, B, Q, N, Extra, Tag, R),
- ?line expect({trace,Self,return_from,{?MODULE,deep,3},Result}),
- ?line Result.
+ expect({trace,Self,call,{?MODULE,deep,[N,B,Q]}}),
+ Result = deep_expect_N(Self, B, Q, N, Extra, Tag, R),
+ expect({trace,Self,return_from,{?MODULE,deep,3},Result}),
+ Result.
deep_expect_N(Self, B, Q, N, Extra, Tag, R) ->
deep_expect_N(Self, B, Q, N, Extra, Tag, R, N).
deep_expect_N(Self, B, Q, N, Extra, Tag, R, J) when J > 0 ->
- ?line expect({trace,Self,call,{?MODULE,deep_1,[J,B,Q]}}),
- ?line deep_expect_N(Self, B, Q, N, Extra, Tag, R, J-1);
+ expect({trace,Self,call,{?MODULE,deep_1,[J,B,Q]}}),
+ deep_expect_N(Self, B, Q, N, Extra, Tag, R, J-1);
deep_expect_N(Self, B, Q, N, Extra, Tag, R, 0) ->
- ?line expect({trace,Self,call,{?MODULE,deep_2,[B,Q]}}),
- ?line expect({trace,Self,call,{?MODULE,deep_3,[B,Q]}}),
- ?line expect({trace,Self,return_from,{?MODULE,deep_3,2},{B,Q}}),
- ?line expect({trace,Self,call,{?MODULE,deep_4,[{B,Q}]}}),
- ?line expect({trace,Self,call,{?MODULE,id,[{B,Q}]}}),
- ?line expect({trace,Self,return_from,{?MODULE,id,1},{B,Q}}),
- ?line deep_expect_Extra(Self, N, Extra, Tag, R),
- ?line expect({trace,Self,Tag,{?MODULE,deep_4,1},R}),
- ?line expect({trace,Self,Tag,{?MODULE,deep_2,2},R}),
- ?line deep_expect_N(Self, N, Tag, R).
+ expect({trace,Self,call,{?MODULE,deep_2,[B,Q]}}),
+ expect({trace,Self,call,{?MODULE,deep_3,[B,Q]}}),
+ expect({trace,Self,return_from,{?MODULE,deep_3,2},{B,Q}}),
+ expect({trace,Self,call,{?MODULE,deep_4,[{B,Q}]}}),
+ expect({trace,Self,call,{?MODULE,id,[{B,Q}]}}),
+ expect({trace,Self,return_from,{?MODULE,id,1},{B,Q}}),
+ deep_expect_Extra(Self, N, Extra, Tag, R),
+ expect({trace,Self,Tag,{?MODULE,deep_4,1},R}),
+ expect({trace,Self,Tag,{?MODULE,deep_2,2},R}),
+ deep_expect_N(Self, N, Tag, R).
deep_expect_Extra(Self, N, [E|Es], Tag, R) ->
- ?line expect(E),
- ?line deep_expect_Extra(Self, N, Es, Tag, R);
+ expect(E),
+ deep_expect_Extra(Self, N, Es, Tag, R);
deep_expect_Extra(_Self, _N, [], _Tag, _R) ->
- ?line ok.
+ ok.
deep_expect_N(Self, N, Tag, R) when N > 0 ->
- ?line expect({trace,Self,Tag,{?MODULE,deep_1,3},R}),
- ?line deep_expect_N(Self, N-1, Tag, R);
+ expect({trace,Self,Tag,{?MODULE,deep_1,3},R}),
+ deep_expect_N(Self, N-1, Tag, R);
deep_expect_N(_Self, 0, return_from, R) ->
- ?line {value,R};
+ {value,R};
deep_expect_N(_Self, 0, exception_from, R) ->
- ?line R.
+ R.
-exception_nocatch(doc) -> "Test the new exception trace.";
-exception_nocatch(suite) -> [];
+%% Test the new exception trace.
exception_nocatch(Config) when is_list(Config) ->
exception_nocatch().
@@ -1082,78 +1066,76 @@ exception_nocatch() ->
Deep4LocBadmatch = get_deep_4_loc({'=',[a,b]}),
Prog = [{'_',[],[{exception_trace}]}],
- ?line 1 = erlang:trace_pattern({?MODULE,deep_1,'_'}, Prog),
- ?line 1 = erlang:trace_pattern({?MODULE,deep_2,'_'}, Prog),
- ?line 1 = erlang:trace_pattern({?MODULE,deep_3,'_'}, Prog),
- ?line 1 = erlang:trace_pattern({?MODULE,deep_4,'_'}, Prog),
- ?line 1 = erlang:trace_pattern({?MODULE,deep_5,'_'}, Prog),
- ?line 1 = erlang:trace_pattern({?MODULE,id,'_'}, Prog),
- ?line 1 = erlang:trace_pattern({erlang,exit,1}, Prog),
- ?line 1 = erlang:trace_pattern({erlang,throw,1}, Prog),
- ?line 2 = erlang:trace_pattern({erlang,error,'_'}, Prog),
- ?line Q1 = {make_ref(),Prog},
- ?line T1 =
- exception_nocatch(?LINE, exit, [Q1], 3,
- [{trace,t1,call,{erlang,exit,[Q1]}},
- {trace,t1,exception_from,{erlang,exit,1},
- {exit,Q1}}],
- exception_from, {exit,Q1}),
- ?line expect({trace,T1,exit,Q1}),
- ?line Q2 = {cake,14.125},
- ?line T2 =
- exception_nocatch(?LINE, throw, [Q2], 2,
- [{trace,t2,call,{erlang,throw,[Q2]}},
- {trace,t2,exception_from,{erlang,throw,1},
- {error,{nocatch,Q2}}}],
- exception_from, {error,{nocatch,Q2}}),
- ?line expect({trace,T2,exit,{{nocatch,Q2},[{erlang,throw,[Q2],[]},
- {?MODULE,deep_4,1,
- Deep4LocThrow}]}}),
- ?line Q3 = {dump,[dump,{dump}]},
- ?line T3 =
- exception_nocatch(?LINE, error, [Q3], 4,
- [{trace,t3,call,{erlang,error,[Q3]}},
- {trace,t3,exception_from,{erlang,error,1},
- {error,Q3}}],
- exception_from, {error,Q3}),
- ?line expect({trace,T3,exit,{Q3,[{erlang,error,[Q3],[]},
- {?MODULE,deep_4,1,Deep4LocError}]}}),
- ?line T4 =
- exception_nocatch(?LINE, '=', [17,4711], 5, [],
- exception_from, {error,{badmatch,4711}}),
- ?line expect({trace,T4,exit,{{badmatch,4711},
- [{?MODULE,deep_4,1,Deep4LocBadmatch}]}}),
+ 1 = erlang:trace_pattern({?MODULE,deep_1,'_'}, Prog),
+ 1 = erlang:trace_pattern({?MODULE,deep_2,'_'}, Prog),
+ 1 = erlang:trace_pattern({?MODULE,deep_3,'_'}, Prog),
+ 1 = erlang:trace_pattern({?MODULE,deep_4,'_'}, Prog),
+ 1 = erlang:trace_pattern({?MODULE,deep_5,'_'}, Prog),
+ 1 = erlang:trace_pattern({?MODULE,id,'_'}, Prog),
+ 1 = erlang:trace_pattern({erlang,exit,1}, Prog),
+ 1 = erlang:trace_pattern({erlang,throw,1}, Prog),
+ 2 = erlang:trace_pattern({erlang,error,'_'}, Prog),
+ Q1 = {make_ref(),Prog},
+ T1 =
+ exception_nocatch(?LINE, exit, [Q1], 3,
+ [{trace,t1,call,{erlang,exit,[Q1]}},
+ {trace,t1,exception_from,{erlang,exit,1},
+ {exit,Q1}}],
+ exception_from, {exit,Q1}),
+ expect({trace,T1,exit,Q1}),
+ Q2 = {cake,14.125},
+ T2 =
+ exception_nocatch(?LINE, throw, [Q2], 2,
+ [{trace,t2,call,{erlang,throw,[Q2]}},
+ {trace,t2,exception_from,{erlang,throw,1},
+ {error,{nocatch,Q2}}}],
+ exception_from, {error,{nocatch,Q2}}),
+ expect({trace,T2,exit,{{nocatch,Q2},[{?MODULE,deep_4,1,
+ Deep4LocThrow}]}}),
+ Q3 = {dump,[dump,{dump}]},
+ T3 =
+ exception_nocatch(?LINE, error, [Q3], 4,
+ [{trace,t3,call,{erlang,error,[Q3]}},
+ {trace,t3,exception_from,{erlang,error,1},
+ {error,Q3}}],
+ exception_from, {error,Q3}),
+ expect({trace,T3,exit,{Q3,[{?MODULE,deep_4,1,Deep4LocError}]}}),
+ T4 =
+ exception_nocatch(?LINE, '=', [17,4711], 5, [],
+ exception_from, {error,{badmatch,4711}}),
+ expect({trace,T4,exit,{{badmatch,4711},
+ [{?MODULE,deep_4,1,Deep4LocBadmatch}]}}),
%%
- ?line erlang:trace_pattern({?MODULE,'_','_'}, false),
- ?line erlang:trace_pattern({erlang,'_','_'}, false),
- ?line expect(),
- ?line ok.
+ erlang:trace_pattern({?MODULE,'_','_'}, false),
+ erlang:trace_pattern({erlang,'_','_'}, false),
+ expect(),
+ ok.
get_deep_4_loc(Arg) ->
try
- deep_4(Arg),
- ?t:fail(should_not_return_to_here)
+ deep_4(Arg),
+ ct:fail(should_not_return_to_here)
catch
- _:_ ->
- [{?MODULE,deep_4,1,Loc0}|_] = erlang:get_stacktrace(),
- Loc0
+ _:_ ->
+ [{?MODULE,deep_4,1,Loc0}|_] = erlang:get_stacktrace(),
+ Loc0
end.
exception_nocatch(Line, B, Q, N, Extra, Tag, R) ->
- ?line io:format("== Subtest: ~w", [Line]),
- ?line Go = make_ref(),
- ?line Tracee =
- spawn(fun () ->
- receive
- Go ->
- deep_1(N, B, Q)
- end
- end),
- ?line 1 = erlang:trace(Tracee, true, [call,return_to,procs]),
- ?line Tracee ! Go,
- ?line deep_expect_N(Tracee, B, Q, N-1,
- [setelement(2, T, Tracee) || T <- Extra], Tag, R),
- ?line Tracee.
+ io:format("== Subtest: ~w", [Line]),
+ Go = make_ref(),
+ Tracee =
+ spawn(fun () ->
+ receive
+ Go ->
+ deep_1(N, B, Q)
+ end
+ end),
+ 1 = erlang:trace(Tracee, true, [call,return_to,procs]),
+ Tracee ! Go,
+ deep_expect_N(Tracee, B, Q, N-1,
+ [setelement(2, T, Tracee) || T <- Extra], Tag, R),
+ Tracee.
%% Make sure that code that uses the optimized bit syntax matching
%% can be traced without crashing the emulator. (Actually, it seems
@@ -1161,22 +1143,22 @@ exception_nocatch(Line, B, Q, N, Extra, Tag, R) ->
%% will keep the test case anyway.)
bit_syntax(Config) when is_list(Config) ->
- ?line start_tracer(),
- ?line 1 = trace_func({?MODULE,bs_sum_a,'_'}, []),
- ?line 1 = trace_func({?MODULE,bs_sum_b,'_'}, []),
+ start_tracer(),
+ 1 = trace_func({?MODULE,bs_sum_a,'_'}, []),
+ 1 = trace_func({?MODULE,bs_sum_b,'_'}, []),
- ?line 6 = call_bs_sum_a(<<1,2,3>>),
- ?line 10 = call_bs_sum_b(<<1,2,3,4>>),
+ 6 = call_bs_sum_a(<<1,2,3>>),
+ 10 = call_bs_sum_b(<<1,2,3,4>>),
- ?line trace_func({?MODULE,'_','_'}, false),
- ?line erlang:trace_delivered(all),
+ trace_func({?MODULE,'_','_'}, false),
+ erlang:trace_delivered(all),
receive
- {trace_delivered,_,_} -> ok
+ {trace_delivered,_,_} -> ok
end,
-
+
Self = self(),
- ?line expect({trace,Self,call,{?MODULE,bs_sum_a,[<<2,3>>,1]}}),
- ?line expect({trace,Self,call,{?MODULE,bs_sum_b,[1,<<2,3,4>>]}}),
+ expect({trace,Self,call,{?MODULE,bs_sum_a,[<<2,3>>,1]}}),
+ expect({trace,Self,call,{?MODULE,bs_sum_b,[1,<<2,3,4>>]}}),
ok.
@@ -1191,7 +1173,7 @@ bs_sum_a(<<>>, Acc) -> Acc.
bs_sum_b(Acc, <<H,T/binary>>) -> bs_sum_b(H+Acc, T);
bs_sum_b(Acc, <<>>) -> Acc.
-
+
@@ -1199,98 +1181,98 @@ bs_sum_b(Acc, <<>>) -> Acc.
expect() ->
case flush() of
- [] -> ok;
- Msgs ->
- test_server:fail({unexpected,abbr(Msgs)})
+ [] -> ok;
+ Msgs ->
+ ct:fail({unexpected,abbr(Msgs)})
end.
expect({trace_ts,Pid,Type,MFA,Term,ts}=Message) ->
receive
- M ->
- case M of
- {trace_ts,Pid,Type,MFA,Term,Ts}=MessageTs ->
- ok = io:format("Expected and got ~p", [abbr(MessageTs)]),
- Ts;
- _ ->
- io:format("Expected ~p; got ~p", [abbr(Message),abbr(M)]),
- test_server:fail({unexpected,abbr([M|flush()])})
- end
+ M ->
+ case M of
+ {trace_ts,Pid,Type,MFA,Term,Ts}=MessageTs ->
+ ok = io:format("Expected and got ~p", [abbr(MessageTs)]),
+ Ts;
+ _ ->
+ io:format("Expected ~p; got ~p", [abbr(Message),abbr(M)]),
+ ct:fail({unexpected,abbr([M|flush()])})
+ end
after 5000 ->
- io:format("Expected ~p; got nothing", [abbr(Message)]),
- test_server:fail(no_trace_message)
+ io:format("Expected ~p; got nothing", [abbr(Message)]),
+ ct:fail(no_trace_message)
end;
expect({trace_ts,Pid,Type,MFA,ts}=Message) ->
receive
- M ->
- case M of
- {trace_ts,Pid,Type,MFA,Ts} ->
- ok = io:format("Expected and got ~p", [abbr(M)]),
- Ts;
- _ ->
- io:format("Expected ~p; got ~p", [abbr(Message),abbr(M)]),
- test_server:fail({unexpected,abbr([M|flush()])})
- end
+ M ->
+ case M of
+ {trace_ts,Pid,Type,MFA,Ts} ->
+ ok = io:format("Expected and got ~p", [abbr(M)]),
+ Ts;
+ _ ->
+ io:format("Expected ~p; got ~p", [abbr(Message),abbr(M)]),
+ ct:fail({unexpected,abbr([M|flush()])})
+ end
after 5000 ->
- io:format("Expected ~p; got nothing", [abbr(Message)]),
- test_server:fail(no_trace_message)
+ io:format("Expected ~p; got nothing", [abbr(Message)]),
+ ct:fail(no_trace_message)
end;
expect(Validator) when is_function(Validator) ->
receive
- M ->
- case Validator(M) of
- expected ->
- ok = io:format("Expected and got ~p", [abbr(M)]);
- next ->
- ok = io:format("Expected and got ~p", [abbr(M)]),
- expect(Validator);
- {unexpected,Message} ->
- io:format("Expected ~p; got ~p", [abbr(Message),abbr(M)]),
- test_server:fail({unexpected,abbr([M|flush()])})
- end
+ M ->
+ case Validator(M) of
+ expected ->
+ ok = io:format("Expected and got ~p", [abbr(M)]);
+ next ->
+ ok = io:format("Expected and got ~p", [abbr(M)]),
+ expect(Validator);
+ {unexpected,Message} ->
+ io:format("Expected ~p; got ~p", [abbr(Message),abbr(M)]),
+ ct:fail({unexpected,abbr([M|flush()])})
+ end
after 5000 ->
- io:format("Expected ~p; got nothing", [abbr(Validator('_'))]),
- test_server:fail(no_trace_message)
+ io:format("Expected ~p; got nothing", [abbr(Validator('_'))]),
+ ct:fail(no_trace_message)
end;
expect(Message) ->
receive
- M ->
- case M of
- Message ->
- ok = io:format("Expected and got ~p", [abbr(Message)]);
- Other ->
- io:format("Expected ~p; got ~p",
- [abbr(Message),abbr(Other)]),
- test_server:fail({unexpected,abbr([Other|flush()])})
- end
+ M ->
+ case M of
+ Message ->
+ ok = io:format("Expected and got ~p", [abbr(Message)]);
+ Other ->
+ io:format("Expected ~p; got ~p",
+ [abbr(Message),abbr(Other)]),
+ ct:fail({unexpected,abbr([Other|flush()])})
+ end
after 5000 ->
- io:format("Expected ~p; got nothing", [abbr(Message)]),
- test_server:fail(no_trace_message)
+ io:format("Expected ~p; got nothing", [abbr(Message)]),
+ ct:fail(no_trace_message)
end.
trace_info(What, Key) ->
get(tracer) ! {apply,self(),{erlang,trace_info,[What,Key]}},
Res = receive
- {apply_result,Result} -> Result
- end,
+ {apply_result,Result} -> Result
+ end,
ok = io:format("erlang:trace_info(~p, ~p) -> ~p",
- [What,Key,Res]),
+ [What,Key,Res]),
Res.
-
+
trace_func(MFA, MatchSpec) ->
trace_func(MFA, MatchSpec, []).
trace_func(MFA, MatchSpec, Flags) ->
get(tracer) ! {apply,self(),{erlang,trace_pattern,[MFA, MatchSpec, Flags]}},
Res = receive
- {apply_result,Result} -> Result
- end,
+ {apply_result,Result} -> Result
+ end,
ok = io:format("trace_pattern(~p, ~p, ~p) -> ~p", [MFA,MatchSpec,Flags,Res]),
Res.
trace_pid(Pid, On, Flags) ->
get(tracer) ! {apply,self(),{erlang,trace,[Pid,On,Flags]}},
Res = receive
- {apply_result,Result} -> Result
- end,
+ {apply_result,Result} -> Result
+ end,
ok = io:format("trace(~p, ~p, ~p) -> ~p", [Pid,On,Flags,Res]),
Res.
@@ -1310,19 +1292,19 @@ tracer(RelayTo) ->
tracer_loop(RelayTo) ->
receive
- {apply,From,{M,F,A}} ->
- From ! {apply_result,apply(M, F, A)},
- tracer_loop(RelayTo);
- Msg ->
- RelayTo ! Msg,
- tracer_loop(RelayTo)
+ {apply,From,{M,F,A}} ->
+ From ! {apply_result,apply(M, F, A)},
+ tracer_loop(RelayTo);
+ Msg ->
+ RelayTo ! Msg,
+ tracer_loop(RelayTo)
end.
id(I) -> I.
deep(N, Class, Reason) ->
try ?MODULE:deep_1(N, Class, Reason) of
- Value -> {value,Value}
+ Value -> {value,Value}
catch C:R -> {C,R}
end.
@@ -1339,30 +1321,30 @@ deep_3(Class, Reason) ->
deep_4(CR) ->
case ?MODULE:id(CR) of
- {exit,[Reason]} ->
- erlang:exit(Reason);
- {throw,[Reason]} ->
- erlang:throw(Reason);
- {error,[Reason,Arglist]} ->
- erlang:error(Reason, Arglist);
- {error,[Reason]} ->
- erlang:error(Reason);
- {id,[Reason]} ->
- Reason;
- {reverse,[A,B]} ->
- lists:reverse(A, B);
- {append,[A,B]} ->
- A ++ B;
- {apply,[Fun,Args]} ->
- erlang:apply(Fun, Args);
- {apply,[M,F,Args]} ->
- erlang:apply(M, F, Args);
- {deep_5,[A,B]} ->
- ?MODULE:deep_5(A, B);
- {deep_5,[A]} ->
- ?MODULE:deep_5(A);
- {'=',[A,B]} ->
- A = B
+ {exit,[Reason]} ->
+ erlang:exit(Reason);
+ {throw,[Reason]} ->
+ erlang:throw(Reason);
+ {error,[Reason,Arglist]} ->
+ erlang:error(Reason, Arglist);
+ {error,[Reason]} ->
+ erlang:error(Reason);
+ {id,[Reason]} ->
+ Reason;
+ {reverse,[A,B]} ->
+ lists:reverse(A, B);
+ {append,[A,B]} ->
+ A ++ B;
+ {apply,[Fun,Args]} ->
+ erlang:apply(Fun, Args);
+ {apply,[M,F,Args]} ->
+ erlang:apply(M, F, Args);
+ {deep_5,[A,B]} ->
+ ?MODULE:deep_5(A, B);
+ {deep_5,[A]} ->
+ ?MODULE:deep_5(A);
+ {'=',[A,B]} ->
+ A = B
end.
deep_5(A) when is_integer(A) ->
@@ -1370,9 +1352,9 @@ deep_5(A) when is_integer(A) ->
flush() ->
receive X ->
- [X|flush()]
+ [X|flush()]
after 1000 ->
- []
+ []
end.
%% Abbreviate large complex terms
@@ -1395,10 +1377,10 @@ abbr_tuple(_, _, _, R) ->
%%
abbr_list(_, 0, R) ->
case io_lib:printable_list(R) of
- true ->
- reverse(R, "...");
- false ->
- reverse(R, '...')
+ true ->
+ reverse(R, "...");
+ false ->
+ reverse(R, '...')
end;
abbr_list([H|T], N, R) ->
M = N-1,
diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl
index 9f318a38be..1065b56e19 100644
--- a/erts/emulator/test/code_SUITE.erl
+++ b/erts/emulator/test/code_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -19,29 +19,28 @@
%%
-module(code_SUITE).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- versions/1,new_binary_types/1,
- t_check_process_code/1,t_check_old_code/1,
- t_check_process_code_ets/1,
- external_fun/1,get_chunk/1,module_md5/1,make_stub/1,
- make_stub_many_funs/1,constant_pools/1,constant_refc_binaries/1,
- false_dependency/1,coverage/1,fun_confusion/1]).
+-export([all/0, suite/0, init_per_suite/1, end_per_suite/1,
+ versions/1,new_binary_types/1, call_purged_fun_code_gone/1,
+ call_purged_fun_code_reload/1, call_purged_fun_code_there/1,
+ t_check_process_code/1,t_check_old_code/1,
+ t_check_process_code_ets/1,
+ external_fun/1,get_chunk/1,module_md5/1,make_stub/1,
+ make_stub_many_funs/1,constant_pools/1,constant_refc_binaries/1,
+ false_dependency/1,coverage/1,fun_confusion/1,
+ t_copy_literals/1, t_copy_literals_frags/1]).
-define(line_trace, 1).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [versions, new_binary_types, t_check_process_code,
+ [versions, new_binary_types, call_purged_fun_code_gone,
+ call_purged_fun_code_reload, call_purged_fun_code_there, t_check_process_code,
t_check_process_code_ets, t_check_old_code, external_fun, get_chunk,
module_md5, make_stub, make_stub_many_funs,
constant_pools, constant_refc_binaries, false_dependency,
- coverage, fun_confusion].
-
-groups() ->
- [].
+ coverage, fun_confusion, t_copy_literals, t_copy_literals_frags].
init_per_suite(Config) ->
erts_debug:set_internal_state(available_internal_state, true),
@@ -51,12 +50,6 @@ end_per_suite(_Config) ->
catch erts_debug:set_internal_state(available_internal_state, false),
ok.
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
%% Make sure that only two versions of a module can be loaded.
versions(Config) when is_list(Config) ->
V1 = compile_version(1, Config),
@@ -73,14 +66,14 @@ versions(Config) when is_list(Config) ->
2 = versions:version(),
%% Kill processes, unload code.
- P1 ! P2 ! done,
_ = monitor(process, P1),
_ = monitor(process, P2),
+ P1 ! P2 ! done,
receive
- {'DOWN',_,process,P1,normal} -> ok
+ {'DOWN',_,process,P1,normal} -> ok
end,
receive
- {'DOWN',_,process,P2,normal} -> ok
+ {'DOWN',_,process,P2,normal} -> ok
end,
true = erlang:purge_module(versions),
true = erlang:delete_module(versions),
@@ -88,84 +81,241 @@ versions(Config) when is_list(Config) ->
ok.
compile_version(Version, Config) ->
- Data = ?config(data_dir, Config),
+ Data = proplists:get_value(data_dir, Config),
File = filename:join(Data, "versions"),
{ok,versions,Bin} = compile:file(File, [{d,'VERSION',Version},
- binary,report]),
+ binary,report]),
Bin.
load_version(Code, Ver) ->
case erlang:load_module(versions, Code) of
- {module,versions} ->
- Pid = spawn_link(versions, loop, []),
- Ver = versions:version(),
- Ver = check_version(Pid),
- {ok,Pid,Ver};
- Error ->
- Error
+ {module,versions} ->
+ Pid = spawn_link(versions, loop, []),
+ Ver = versions:version(),
+ Ver = check_version(Pid),
+ {ok,Pid,Ver};
+ Error ->
+ Error
end.
check_version(Pid) ->
Pid ! {self(),version},
receive
- {Pid,version,Version} ->
- Version
+ {Pid,version,Version} ->
+ Version
end.
new_binary_types(Config) when is_list(Config) ->
- ?line Data = ?config(data_dir, Config),
- ?line File = filename:join(Data, "my_code_test"),
- ?line {ok,my_code_test,Bin} = compile:file(File, [binary]),
- ?line {module,my_code_test} = erlang:load_module(my_code_test,
- make_sub_binary(Bin)),
- ?line true = erlang:delete_module(my_code_test),
- ?line true = erlang:purge_module(my_code_test),
-
- ?line {module,my_code_test} = erlang:load_module(my_code_test,
- make_unaligned_sub_binary(Bin)),
- ?line true = erlang:delete_module(my_code_test),
- ?line true = erlang:purge_module(my_code_test),
+ Data = proplists:get_value(data_dir, Config),
+ File = filename:join(Data, "my_code_test"),
+ {ok,my_code_test,Bin} = compile:file(File, [binary]),
+ {module,my_code_test} = erlang:load_module(my_code_test,
+ make_sub_binary(Bin)),
+ true = erlang:delete_module(my_code_test),
+ true = erlang:purge_module(my_code_test),
+
+ {module,my_code_test} = erlang:load_module(my_code_test,
+ make_unaligned_sub_binary(Bin)),
+ true = erlang:delete_module(my_code_test),
+ true = erlang:purge_module(my_code_test),
%% Try heap binaries and bad binaries.
- ?line {error,badfile} = erlang:load_module(my_code_test, <<1,2>>),
- ?line {error,badfile} = erlang:load_module(my_code_test,
- make_sub_binary(<<1,2>>)),
- ?line {error,badfile} = erlang:load_module(my_code_test,
- make_unaligned_sub_binary(<<1,2>>)),
- ?line {'EXIT',{badarg,_}} = (catch erlang:load_module(my_code_test,
- bit_sized_binary(Bin))),
+ {error,badfile} = erlang:load_module(my_code_test, <<1,2>>),
+ {error,badfile} = erlang:load_module(my_code_test,
+ make_sub_binary(<<1,2>>)),
+ {error,badfile} = erlang:load_module(my_code_test,
+ make_unaligned_sub_binary(<<1,2>>)),
+ {'EXIT',{badarg,_}} = (catch erlang:load_module(my_code_test,
+ bit_sized_binary(Bin))),
+ ok.
+
+call_purged_fun_code_gone(Config) when is_list(Config) ->
+ Priv = proplists:get_value(priv_dir, Config),
+ Data = proplists:get_value(data_dir, Config),
+ call_purged_fun_test(Priv, Data, code_gone),
+ ok.
+
+call_purged_fun_code_reload(Config) when is_list(Config) ->
+ Priv = proplists:get_value(priv_dir, Config),
+ Data = proplists:get_value(data_dir, Config),
+ Path = code:get_path(),
+ true = code:add_path(Priv),
+ try
+ call_purged_fun_test(Priv, Data, code_reload)
+ after
+ code:set_path(Path)
+ end,
+ ok.
+
+call_purged_fun_code_there(Config) when is_list(Config) ->
+ Priv = proplists:get_value(priv_dir, Config),
+ Data = proplists:get_value(data_dir, Config),
+ call_purged_fun_test(Priv, Data, code_there),
+ ok.
+
+call_purged_fun_test(Priv, Data, Type) ->
+ File = filename:join(Data, "my_code_test2"),
+ Code = filename:join(Priv, "my_code_test2"),
+
+ catch erlang:purge_module(my_code_test2),
+ catch erlang:delete_module(my_code_test2),
+ catch erlang:purge_module(my_code_test2),
+
+ {ok,my_code_test2} = c:c(File, [{outdir,Priv}]),
+
+ T = ets:new(my_code_test2_fun_table, []),
+ ets:insert(T, {my_fun,my_code_test2:make_fun(4711)}),
+ ets:insert(T, {my_fun2,my_code_test2:make_fun2()}),
+
+ spawn(fun () ->
+ [{my_fun2,F2}] = ets:lookup(T, my_fun2),
+ F2(fun () ->
+ receive after infinity -> ok end
+ end,
+ fun () -> ok end),
+ exit(completed)
+ end),
+
+ PurgeType = case Type of
+ code_gone ->
+ ok = file:delete(Code++".beam"),
+ true;
+ code_reload ->
+ true;
+ code_there ->
+ false
+ end,
+
+ true = erlang:delete_module(my_code_test2),
+
+ Purge = start_purge(my_code_test2, PurgeType),
+
+ {P0, M0} = spawn_monitor(fun () ->
+ [{my_fun,F}] = ets:lookup(T, my_fun),
+ 4712 = F(1),
+ exit(completed)
+ end),
+
+ wait_until(fun () ->
+ {status, suspended}
+ == process_info(P0, status)
+ end),
+
+ ok = continue_purge(Purge),
+
+ {P1, M1} = spawn_monitor(fun () ->
+ [{my_fun,F}] = ets:lookup(T, my_fun),
+ 4713 = F(2),
+ exit(completed)
+ end),
+ {P2, M2} = spawn_monitor(fun () ->
+ [{my_fun,F}] = ets:lookup(T, my_fun),
+ 4714 = F(3),
+ exit(completed)
+ end),
+
+ wait_until(fun () ->
+ {status, suspended}
+ == process_info(P1, status)
+ end),
+ wait_until(fun () ->
+ {status, suspended}
+ == process_info(P2, status)
+ end),
+
+ {current_function,
+ {erts_code_purger,
+ pending_purge_lambda,
+ 3}} = process_info(P0, current_function),
+ {current_function,
+ {erts_code_purger,
+ pending_purge_lambda,
+ 3}} = process_info(P1, current_function),
+ {current_function,
+ {erts_code_purger,
+ pending_purge_lambda,
+ 3}} = process_info(P2, current_function),
+
+ case Type of
+ code_there ->
+ false = complete_purge(Purge);
+ _ ->
+ {true, true} = complete_purge(Purge)
+ end,
+
+ case Type of
+ code_gone ->
+ receive
+ {'DOWN', M0, process, P0, Reason0} ->
+ {undef, _} = Reason0
+ end,
+ receive
+ {'DOWN', M1, process, P1, Reason1} ->
+ {undef, _} = Reason1
+ end,
+ receive
+ {'DOWN', M2, process, P2, Reason2} ->
+ {undef, _} = Reason2
+ end;
+ _ ->
+ receive
+ {'DOWN', M0, process, P0, Reason0} ->
+ completed = Reason0
+ end,
+ receive
+ {'DOWN', M1, process, P1, Reason1} ->
+ completed = Reason1
+ end,
+ receive
+ {'DOWN', M2, process, P2, Reason2} ->
+ completed = Reason2
+ end,
+ catch erlang:purge_module(my_code_test2),
+ catch erlang:delete_module(my_code_test2),
+ catch erlang:purge_module(my_code_test2)
+ end,
ok.
t_check_process_code(Config) when is_list(Config) ->
- ?line Priv = ?config(priv_dir, Config),
- ?line Data = ?config(data_dir, Config),
- ?line File = filename:join(Data, "my_code_test"),
- ?line Code = filename:join(Priv, "my_code_test"),
+ case check_process_code_handle(indirect_references) of
+ false -> {skipped, "check_process_code() ignores funs"};
+ true -> t_check_process_code_test(Config)
+ end.
- ?line {ok,my_code_test} = c:c(File, [{outdir,Priv}]),
+t_check_process_code_test(Config) ->
+ Priv = proplists:get_value(priv_dir, Config),
+ Data = proplists:get_value(data_dir, Config),
+ File = filename:join(Data, "my_code_test"),
+ Code = filename:join(Priv, "my_code_test"),
- ?line MyFun = fun(X, Y) -> X + Y end, %Confuse things.
- ?line F = my_code_test:make_fun(42),
- ?line 2 = fun_refc(F),
- ?line MyFun2 = fun(X, Y) -> X * Y end, %Confuse things.
- ?line 44 = F(2),
+ catch erlang:purge_module(my_code_test),
+ catch erlang:delete_module(my_code_test),
+ catch erlang:purge_module(my_code_test),
+
+ {ok,my_code_test} = c:c(File, [{outdir,Priv}]),
+
+ MyFun = fun(X, Y) -> X + Y end, %Confuse things.
+ F = my_code_test:make_fun(42),
+ 2 = fun_refc(F),
+ MyFun2 = fun(X, Y) -> X * Y end, %Confuse things.
+ 44 = F(2),
%% Delete the module and call the fun again.
- ?line true = erlang:delete_module(my_code_test),
- ?line 2 = fun_refc(F),
- ?line 45 = F(3),
- ?line {'EXIT',{undef,_}} = (catch my_code_test:make_fun(33)),
+ true = erlang:delete_module(my_code_test),
+ 2 = fun_refc(F),
+ 45 = F(3),
+ {'EXIT',{undef,_}} = (catch my_code_test:make_fun(33)),
%% The fun should still be there, preventing purge.
- ?line true = erlang:check_process_code(self(), my_code_test),
+ true = erlang:check_process_code(self(), my_code_test),
gc(),
gc(), %Place funs on the old heap.
- ?line true = erlang:check_process_code(self(), my_code_test),
+ true = erlang:check_process_code(self(), my_code_test),
%% Using the funs here guarantees that they will not be prematurely garbed.
- ?line 48 = F(6),
- ?line 3 = MyFun(1, 2),
- ?line 12 = MyFun2(3, 4),
+ 48 = F(6),
+ 3 = MyFun(1, 2),
+ 12 = MyFun2(3, 4),
%% Kill all funs.
t_check_process_code1(Code, []).
@@ -173,64 +323,64 @@ t_check_process_code(Config) when is_list(Config) ->
%% The real fun was killed, but we have some fakes which look similar.
t_check_process_code1(Code, Fakes) ->
- ?line MyFun = fun(X, Y) -> X + Y + 1 end, %Confuse things.
- ?line false = erlang:check_process_code(self(), my_code_test),
- ?line 4 = MyFun(1, 2),
+ MyFun = fun(X, Y) -> X + Y + 1 end, %Confuse things.
+ false = erlang:check_process_code(self(), my_code_test),
+ 4 = MyFun(1, 2),
t_check_process_code2(Code, Fakes).
t_check_process_code2(Code, _) ->
- ?line false = erlang:check_process_code(self(), my_code_test),
- ?line true = erlang:purge_module(my_code_test),
+ false = erlang:check_process_code(self(), my_code_test),
+ true = erlang:purge_module(my_code_test),
%% In the next test we will load the same module twice.
- ?line {module,my_code_test} = code:load_abs(Code),
- ?line F = my_code_test:make_fun(37),
- ?line 2 = fun_refc(F),
- ?line false = erlang:check_process_code(self(), my_code_test),
- ?line {module,my_code_test} = code:load_abs(Code),
- ?line 2 = fun_refc(F),
+ {module,my_code_test} = code:load_abs(Code),
+ F = my_code_test:make_fun(37),
+ 2 = fun_refc(F),
+ false = erlang:check_process_code(self(), my_code_test),
+ {module,my_code_test} = code:load_abs(Code),
+ 2 = fun_refc(F),
%% Still false because the fun with the same identify is found
%% in the current code.
- ?line false = erlang:check_process_code(self(), my_code_test),
-
+ false = erlang:check_process_code(self(), my_code_test),
+
%% Some fake funs in the same module should not do any difference.
- ?line false = erlang:check_process_code(self(), my_code_test),
+ false = erlang:check_process_code(self(), my_code_test),
38 = F(1),
t_check_process_code3(Code, F, []).
t_check_process_code3(Code, F, Fakes) ->
Pid = spawn_link(fun() -> body(F, Fakes) end),
- ?line true = erlang:purge_module(my_code_test),
- ?line false = erlang:check_process_code(self(), my_code_test),
- ?line false = erlang:check_process_code(Pid, my_code_test),
+ true = erlang:purge_module(my_code_test),
+ false = erlang:check_process_code(self(), my_code_test),
+ false = erlang:check_process_code(Pid, my_code_test),
- ?line true = erlang:delete_module(my_code_test),
- ?line true = erlang:check_process_code(self(), my_code_test),
- ?line true = erlang:check_process_code(Pid, my_code_test),
+ true = erlang:delete_module(my_code_test),
+ true = erlang:check_process_code(self(), my_code_test),
+ true = erlang:check_process_code(Pid, my_code_test),
39 = F(2),
t_check_process_code4(Code, Pid).
t_check_process_code4(_Code, Pid) ->
Pid ! drop_funs,
receive after 1 -> ok end,
- ?line false = erlang:check_process_code(Pid, my_code_test),
+ false = erlang:check_process_code(Pid, my_code_test),
ok.
body(F, Fakes) ->
receive
- jog ->
- 40 = F(3),
- erlang:garbage_collect(),
- body(F, Fakes);
- drop_funs ->
- dropped_body()
+ jog ->
+ 40 = F(3),
+ erlang:garbage_collect(),
+ body(F, Fakes);
+ drop_funs ->
+ dropped_body()
end.
dropped_body() ->
receive
- X -> exit(X)
+ X -> exit(X)
end.
gc() ->
@@ -238,61 +388,66 @@ gc() ->
gc1().
gc1() -> ok.
-t_check_process_code_ets(doc) ->
- "Test check_process_code/2 in combination with a fun obtained from an ets table.";
+%% Test check_process_code/2 in combination with a fun obtained from an ets table.
t_check_process_code_ets(Config) when is_list(Config) ->
- case test_server:is_native(?MODULE) of
- true ->
- {skip,"Native code"};
+ case check_process_code_handle(indirect_references) of
false ->
- do_check_process_code_ets(Config)
+ {skipped, "check_process_code() ignores funs"};
+ true ->
+ case test_server:is_native(?MODULE) of
+ true ->
+ {skip,"Native code"};
+ false ->
+ do_check_process_code_ets(Config)
+ end
end.
do_check_process_code_ets(Config) ->
- ?line Priv = ?config(priv_dir, Config),
- ?line Data = ?config(data_dir, Config),
- ?line File = filename:join(Data, "my_code_test"),
-
- ?line erlang:purge_module(my_code_test),
- ?line erlang:delete_module(my_code_test),
- ?line {ok,my_code_test} = c:c(File, [{outdir,Priv}]),
-
- ?line T = ets:new(my_code_test, []),
- ?line ets:insert(T, {7,my_code_test:make_fun(107)}),
- ?line ets:insert(T, {8,my_code_test:make_fun(108)}),
- ?line erlang:delete_module(my_code_test),
- ?line false = erlang:check_process_code(self(), my_code_test),
+ Priv = proplists:get_value(priv_dir, Config),
+ Data = proplists:get_value(data_dir, Config),
+ File = filename:join(Data, "my_code_test"),
+
+ catch erlang:purge_module(my_code_test),
+ catch erlang:delete_module(my_code_test),
+ catch erlang:purge_module(my_code_test),
+ {ok,my_code_test} = c:c(File, [{outdir,Priv}]),
+
+ T = ets:new(my_code_test, []),
+ ets:insert(T, {7,my_code_test:make_fun(107)}),
+ ets:insert(T, {8,my_code_test:make_fun(108)}),
+ erlang:delete_module(my_code_test),
+ false = erlang:check_process_code(self(), my_code_test),
Body = fun() ->
- [{7,F1}] = ets:lookup(T, 7),
- [{8,F2}] = ets:lookup(T, 8),
- IdleLoop = fun() -> receive _X -> ok end end,
- RecLoop = fun(Again) ->
- receive
- call -> 110 = F1(3),
- 100 = F2(-8),
- Again(Again);
- {drop_funs,To} ->
- To ! funs_dropped,
- IdleLoop()
- end
- end,
- true = erlang:check_process_code(self(), my_code_test),
- RecLoop(RecLoop)
- end,
- ?line Pid = spawn_link(Body),
+ [{7,F1}] = ets:lookup(T, 7),
+ [{8,F2}] = ets:lookup(T, 8),
+ IdleLoop = fun() -> receive _X -> ok end end,
+ RecLoop = fun(Again) ->
+ receive
+ call -> 110 = F1(3),
+ 100 = F2(-8),
+ Again(Again);
+ {drop_funs,To} ->
+ To ! funs_dropped,
+ IdleLoop()
+ end
+ end,
+ true = erlang:check_process_code(self(), my_code_test),
+ RecLoop(RecLoop)
+ end,
+ Pid = spawn_link(Body),
receive after 1 -> ok end,
- ?line true = erlang:check_process_code(Pid, my_code_test),
+ true = erlang:check_process_code(Pid, my_code_test),
Pid ! call,
Pid ! {drop_funs,self()},
receive
- funs_dropped -> ok;
- Other -> ?t:fail({unexpected,Other})
+ funs_dropped -> ok;
+ Other -> ct:fail({unexpected,Other})
after 10000 ->
- ?line ?t:fail(no_funs_dropped_answer)
+ ct:fail(no_funs_dropped_answer)
end,
- ?line false = erlang:check_process_code(Pid, my_code_test),
+ false = erlang:check_process_code(Pid, my_code_test),
ok.
fun_refc(F) ->
@@ -302,89 +457,89 @@ fun_refc(F) ->
%% Test the erlang:check_old_code/1 BIF.
t_check_old_code(Config) when is_list(Config) ->
- ?line Data = ?config(data_dir, Config),
- ?line File = filename:join(Data, "my_code_test"),
+ Data = proplists:get_value(data_dir, Config),
+ File = filename:join(Data, "my_code_test"),
+
+ catch erlang:purge_module(my_code_test),
+ catch erlang:delete_module(my_code_test),
+ catch erlang:purge_module(my_code_test),
- ?line erlang:purge_module(my_code_test),
- ?line erlang:delete_module(my_code_test),
- ?line catch erlang:purge_module(my_code_test),
+ false = erlang:check_old_code(my_code_test),
- ?line false = erlang:check_old_code(my_code_test),
+ {ok,my_code_test,Code} = compile:file(File, [binary]),
+ {module,my_code_test} = code:load_binary(my_code_test, File, Code),
- ?line {ok,my_code_test,Code} = compile:file(File, [binary]),
- ?line {module,my_code_test} = code:load_binary(my_code_test, File, Code),
-
- ?line false = erlang:check_old_code(my_code_test),
- ?line {module,my_code_test} = code:load_binary(my_code_test, File, Code),
- ?line true = erlang:check_old_code(my_code_test),
+ false = erlang:check_old_code(my_code_test),
+ {module,my_code_test} = code:load_binary(my_code_test, File, Code),
+ true = erlang:check_old_code(my_code_test),
- ?line true = erlang:purge_module(my_code_test),
- ?line true = erlang:delete_module(my_code_test),
- ?line true = erlang:purge_module(my_code_test),
+ true = erlang:purge_module(my_code_test),
+ true = erlang:delete_module(my_code_test),
+ true = erlang:purge_module(my_code_test),
+
+ {'EXIT',_} = (catch erlang:check_old_code([])),
- ?line {'EXIT',_} = (catch erlang:check_old_code([])),
-
ok.
external_fun(Config) when is_list(Config) ->
- ?line false = erlang:function_exported(another_code_test, x, 1),
+ false = erlang:function_exported(another_code_test, x, 1),
AnotherCodeTest = id(another_code_test),
ExtFun = fun AnotherCodeTest:x/1,
- ?line {'EXIT',{undef,_}} = (catch ExtFun(answer)),
- ?line false = erlang:function_exported(another_code_test, x, 1),
- ?line false = lists:member(another_code_test, erlang:loaded()),
- ?line Data = ?config(data_dir, Config),
- ?line File = filename:join(Data, "another_code_test"),
- ?line {ok,another_code_test,Code} = compile:file(File, [binary,report]),
- ?line {module,another_code_test} = erlang:load_module(another_code_test, Code),
- ?line 42 = ExtFun(answer),
+ {'EXIT',{undef,_}} = (catch ExtFun(answer)),
+ false = erlang:function_exported(another_code_test, x, 1),
+ false = lists:member(another_code_test, erlang:loaded()),
+ Data = proplists:get_value(data_dir, Config),
+ File = filename:join(Data, "another_code_test"),
+ {ok,another_code_test,Code} = compile:file(File, [binary,report]),
+ {module,another_code_test} = erlang:load_module(another_code_test, Code),
+ 42 = ExtFun(answer),
ok.
get_chunk(Config) when is_list(Config) ->
- ?line Data = ?config(data_dir, Config),
- ?line File = filename:join(Data, "my_code_test"),
- ?line {ok,my_code_test,Code} = compile:file(File, [binary]),
+ Data = proplists:get_value(data_dir, Config),
+ File = filename:join(Data, "my_code_test"),
+ {ok,my_code_test,Code} = compile:file(File, [binary]),
%% Should work.
- ?line Chunk = get_chunk_ok("Atom", Code),
- ?line Chunk = get_chunk_ok("Atom", make_sub_binary(Code)),
- ?line Chunk = get_chunk_ok("Atom", make_unaligned_sub_binary(Code)),
+ Chunk = get_chunk_ok("Atom", Code),
+ Chunk = get_chunk_ok("Atom", make_sub_binary(Code)),
+ Chunk = get_chunk_ok("Atom", make_unaligned_sub_binary(Code)),
%% Should fail.
- ?line {'EXIT',{badarg,_}} = (catch code:get_chunk(bit_sized_binary(Code), "Atom")),
- ?line {'EXIT',{badarg,_}} = (catch code:get_chunk(Code, "bad chunk id")),
+ {'EXIT',{badarg,_}} = (catch code:get_chunk(bit_sized_binary(Code), "Atom")),
+ {'EXIT',{badarg,_}} = (catch code:get_chunk(Code, "bad chunk id")),
%% Invalid beam code or missing chunk should return 'undefined'.
- ?line undefined = code:get_chunk(<<"not a beam module">>, "Atom"),
- ?line undefined = code:get_chunk(Code, "XXXX"),
+ undefined = code:get_chunk(<<"not a beam module">>, "Atom"),
+ undefined = code:get_chunk(Code, "XXXX"),
ok.
get_chunk_ok(Chunk, Code) ->
case code:get_chunk(Code, Chunk) of
- Bin when is_binary(Bin) -> Bin
+ Bin when is_binary(Bin) -> Bin
end.
module_md5(Config) when is_list(Config) ->
- ?line Data = ?config(data_dir, Config),
- ?line File = filename:join(Data, "my_code_test"),
- ?line {ok,my_code_test,Code} = compile:file(File, [binary]),
+ Data = proplists:get_value(data_dir, Config),
+ File = filename:join(Data, "my_code_test"),
+ {ok,my_code_test,Code} = compile:file(File, [binary]),
%% Should work.
- ?line Chunk = module_md5_ok(Code),
- ?line Chunk = module_md5_ok(make_sub_binary(Code)),
- ?line Chunk = module_md5_ok(make_unaligned_sub_binary(Code)),
+ Chunk = module_md5_ok(Code),
+ Chunk = module_md5_ok(make_sub_binary(Code)),
+ Chunk = module_md5_ok(make_unaligned_sub_binary(Code)),
%% Should fail.
- ?line {'EXIT',{badarg,_}} = (catch code:module_md5(bit_sized_binary(Code))),
+ {'EXIT',{badarg,_}} = (catch code:module_md5(bit_sized_binary(Code))),
%% Invalid beam code should return 'undefined'.
- ?line undefined = code:module_md5(<<"not a beam module">>),
+ undefined = code:module_md5(<<"not a beam module">>),
ok.
-
+
module_md5_ok(Code) ->
case code:module_md5(Code) of
- Bin when is_binary(Bin), size(Bin) =:= 16 -> Bin
+ Bin when is_binary(Bin), size(Bin) =:= 16 -> Bin
end.
@@ -392,107 +547,138 @@ make_stub(Config) when is_list(Config) ->
catch erlang:purge_module(my_code_test),
MD5 = erlang:md5(<<>>),
- ?line Data = ?config(data_dir, Config),
- ?line File = filename:join(Data, "my_code_test"),
- ?line {ok,my_code_test,Code} = compile:file(File, [binary]),
+ Data = proplists:get_value(data_dir, Config),
+ File = filename:join(Data, "my_code_test"),
+ {ok,my_code_test,Code} = compile:file(File, [binary]),
- ?line my_code_test = code:make_stub_module(my_code_test, Code, {[],[],MD5}),
- ?line true = erlang:delete_module(my_code_test),
- ?line true = erlang:purge_module(my_code_test),
+ my_code_test = code:make_stub_module(my_code_test, Code, {[],[],MD5}),
+ true = erlang:delete_module(my_code_test),
+ true = erlang:purge_module(my_code_test),
- ?line my_code_test = code:make_stub_module(my_code_test,
- make_unaligned_sub_binary(Code),
- {[],[],MD5}),
- ?line true = erlang:delete_module(my_code_test),
- ?line true = erlang:purge_module(my_code_test),
+ my_code_test = code:make_stub_module(my_code_test,
+ make_unaligned_sub_binary(Code),
+ {[],[],MD5}),
+ true = erlang:delete_module(my_code_test),
+ true = erlang:purge_module(my_code_test),
- ?line my_code_test = code:make_stub_module(my_code_test, zlib:gzip(Code),
- {[],[],MD5}),
- ?line true = erlang:delete_module(my_code_test),
- ?line true = erlang:purge_module(my_code_test),
+ my_code_test = code:make_stub_module(my_code_test, zlib:gzip(Code),
+ {[],[],MD5}),
+ true = erlang:delete_module(my_code_test),
+ true = erlang:purge_module(my_code_test),
%% Should fail.
- ?line {'EXIT',{badarg,_}} =
- (catch code:make_stub_module(my_code_test, <<"bad">>, {[],[],MD5})),
- ?line {'EXIT',{badarg,_}} =
- (catch code:make_stub_module(my_code_test,
- bit_sized_binary(Code),
- {[],[],MD5})),
- ?line {'EXIT',{badarg,_}} =
- (catch code:make_stub_module(my_code_test_with_wrong_name,
- Code, {[],[],MD5})),
+ {'EXIT',{badarg,_}} =
+ (catch code:make_stub_module(my_code_test, <<"bad">>, {[],[],MD5})),
+ {'EXIT',{badarg,_}} =
+ (catch code:make_stub_module(my_code_test,
+ bit_sized_binary(Code),
+ {[],[],MD5})),
+ {'EXIT',{badarg,_}} =
+ (catch code:make_stub_module(my_code_test_with_wrong_name,
+ Code, {[],[],MD5})),
ok.
make_stub_many_funs(Config) when is_list(Config) ->
catch erlang:purge_module(many_funs),
MD5 = erlang:md5(<<>>),
- ?line Data = ?config(data_dir, Config),
- ?line File = filename:join(Data, "many_funs"),
- ?line {ok,many_funs,Code} = compile:file(File, [binary]),
+ Data = proplists:get_value(data_dir, Config),
+ File = filename:join(Data, "many_funs"),
+ {ok,many_funs,Code} = compile:file(File, [binary]),
- ?line many_funs = code:make_stub_module(many_funs, Code, {[],[],MD5}),
- ?line true = erlang:delete_module(many_funs),
- ?line true = erlang:purge_module(many_funs),
- ?line many_funs = code:make_stub_module(many_funs,
- make_unaligned_sub_binary(Code),
- {[],[],MD5}),
- ?line true = erlang:delete_module(many_funs),
- ?line true = erlang:purge_module(many_funs),
+ many_funs = code:make_stub_module(many_funs, Code, {[],[],MD5}),
+ true = erlang:delete_module(many_funs),
+ true = erlang:purge_module(many_funs),
+ many_funs = code:make_stub_module(many_funs,
+ make_unaligned_sub_binary(Code),
+ {[],[],MD5}),
+ true = erlang:delete_module(many_funs),
+ true = erlang:purge_module(many_funs),
%% Should fail.
- ?line {'EXIT',{badarg,_}} =
- (catch code:make_stub_module(many_funs, <<"bad">>, {[],[],MD5})),
- ?line {'EXIT',{badarg,_}} =
- (catch code:make_stub_module(many_funs,
- bit_sized_binary(Code),
- {[],[],MD5})),
+ {'EXIT',{badarg,_}} =
+ (catch code:make_stub_module(many_funs, <<"bad">>, {[],[],MD5})),
+ {'EXIT',{badarg,_}} =
+ (catch code:make_stub_module(many_funs,
+ bit_sized_binary(Code),
+ {[],[],MD5})),
ok.
constant_pools(Config) when is_list(Config) ->
- ?line Data = ?config(data_dir, Config),
- ?line File = filename:join(Data, "literals"),
- ?line {ok,literals,Code} = compile:file(File, [report,binary]),
- ?line {module,literals} = erlang:load_module(literals,
- make_sub_binary(Code)),
+ Data = proplists:get_value(data_dir, Config),
+ File = filename:join(Data, "literals"),
+ {ok,literals,Code} = compile:file(File, [report,binary]),
+ {module,literals} = erlang:load_module(literals,
+ make_sub_binary(Code)),
%% Initialize.
- ?line A = literals:a(),
- ?line B = literals:b(),
- ?line C = literals:huge_bignum(),
- ?line process_flag(trap_exit, true),
+ A = literals:a(),
+ B = literals:b(),
+ C = literals:huge_bignum(),
+ process_flag(trap_exit, true),
Self = self(),
%% Have a process WITHOUT old heap that references the literals
%% in the 'literals' module.
- ?line NoOldHeap = spawn_link(fun() -> no_old_heap(Self) end),
+ NoOldHeap = spawn_link(fun() -> no_old_heap(Self) end),
receive go -> ok end,
- ?line true = erlang:delete_module(literals),
- ?line false = erlang:check_process_code(NoOldHeap, literals),
- ?line erlang:check_process_code(self(), literals),
- ?line true = erlang:purge_module(literals),
- ?line NoOldHeap ! done,
- ?line receive
- {'EXIT',NoOldHeap,{A,B,C}} ->
- ok;
- Other ->
- ?line ?t:fail({unexpected,Other})
- end,
- ?line {module,literals} = erlang:load_module(literals, Code),
+ true = erlang:delete_module(literals),
+ false = erlang:check_process_code(NoOldHeap, literals),
+ erlang:check_process_code(self(), literals),
+ true = erlang:purge_module(literals),
+ NoOldHeap ! done,
+ receive
+ {'EXIT',NoOldHeap,{A,B,C}} ->
+ ok;
+ Other ->
+ ct:fail({unexpected,Other})
+ end,
+ {module,literals} = erlang:load_module(literals, Code),
%% Have a process WITH an old heap that references the literals
%% in the 'literals' module.
- ?line OldHeap = spawn_link(fun() -> old_heap(Self) end),
+ OldHeap = spawn_link(fun() -> old_heap(Self) end),
receive go -> ok end,
- ?line true = erlang:delete_module(literals),
- ?line false = erlang:check_process_code(OldHeap, literals),
- ?line erlang:check_process_code(self(), literals),
- ?line erlang:purge_module(literals),
- ?line OldHeap ! done,
+ true = erlang:delete_module(literals),
+ false = erlang:check_process_code(OldHeap, literals),
+ erlang:check_process_code(self(), literals),
+ erlang:purge_module(literals),
+ OldHeap ! done,
receive
{'EXIT',OldHeap,{A,B,C,[1,2,3|_]=Seq}} when length(Seq) =:= 16 ->
ok
- end.
+ end,
+
+ {module,literals} = erlang:load_module(literals, Code),
+ %% Have a hibernated process that references the literals
+ %% in the 'literals' module.
+ {Hib, Mon} = spawn_monitor(fun() -> hibernated(Self) end),
+ receive go -> ok end,
+ [{heap_size,OldHeapSz},
+ {total_heap_size,OldTotHeapSz}] = process_info(Hib, [heap_size,
+ total_heap_size]),
+ OldHeapSz = OldTotHeapSz,
+ io:format("OldHeapSz=~p OldTotHeapSz=~p~n", [OldHeapSz, OldTotHeapSz]),
+ true = erlang:delete_module(literals),
+ false = erlang:check_process_code(Hib, literals),
+ erlang:check_process_code(self(), literals),
+ erlang:purge_module(literals),
+ receive after 1000 -> ok end,
+ [{heap_size,HeapSz},
+ {total_heap_size,TotHeapSz}] = process_info(Hib, [heap_size,
+ total_heap_size]),
+ io:format("HeapSz=~p TotHeapSz=~p~n", [HeapSz, TotHeapSz]),
+ Hib ! hej,
+ receive
+ {'DOWN', Mon, process, Hib, Reason} ->
+ {undef, [{no_module,
+ no_function,
+ [{A,B,C,[1,2,3|_]=Seq}], _}]} = Reason,
+ 16 = length(Seq)
+ end,
+ HeapSz = TotHeapSz, %% Ensure restored to hibernated state...
+ true = HeapSz > OldHeapSz,
+ ok.
no_old_heap(Parent) ->
A = literals:a(),
@@ -500,8 +686,8 @@ no_old_heap(Parent) ->
Res = {A,B,literals:huge_bignum()},
Parent ! go,
receive
- done ->
- exit(Res)
+ done ->
+ exit(Res)
end.
old_heap(Parent) ->
@@ -511,16 +697,23 @@ old_heap(Parent) ->
create_old_heap(),
Parent ! go,
receive
- done ->
- exit(Res)
+ done ->
+ exit(Res)
end.
+hibernated(Parent) ->
+ A = literals:a(),
+ B = literals:b(),
+ Res = {A,B,literals:huge_bignum(),lists:seq(1, 16)},
+ Parent ! go,
+ erlang:hibernate(no_module, no_function, [Res]).
+
create_old_heap() ->
case process_info(self(), [heap_size,total_heap_size]) of
- [{heap_size,Sz},{total_heap_size,Total}] when Sz < Total ->
- ok;
- _ ->
- create_old_heap()
+ [{heap_size,Sz},{total_heap_size,Total}] when Sz < Total ->
+ ok;
+ _ ->
+ create_old_heap()
end.
constant_refc_binaries(Config) when is_list(Config) ->
@@ -529,7 +722,7 @@ constant_refc_binaries(Config) when is_list(Config) ->
io:format("Binary data (bytes) before test: ~p\n", [Bef]),
%% Compile the the literals module.
- Data = ?config(data_dir, Config),
+ Data = proplists:get_value(data_dir, Config),
File = filename:join(Data, "literals"),
{ok,literals,Code} = compile:file(File, [report,binary]),
@@ -554,29 +747,29 @@ constant_refc_binaries(Config) when is_list(Config) ->
io:format("Binary data (bytes) after test: ~p", [Aft]),
Diff = Aft - Bef,
if
- Diff < 0 ->
- io:format("~p less bytes", [abs(Diff)]);
- Diff > 0 ->
- io:format("~p more bytes", [Diff]);
- true ->
- ok
+ Diff < 0 ->
+ io:format("~p less bytes", [abs(Diff)]);
+ Diff > 0 ->
+ io:format("~p more bytes", [Diff]);
+ true ->
+ ok
end,
%% Test for leaks. We must accept some natural variations in
%% the size of allocated binaries.
if
- Diff > 64*1024 ->
- ?t:fail(binary_leak);
- true ->
- ok
+ Diff > 64*1024 ->
+ ct:fail(binary_leak);
+ true ->
+ ok
end.
memory_binary() ->
try
- erlang:memory(binary)
+ erlang:memory(binary)
catch
- error:notsup ->
- 0
+ error:notsup ->
+ 0
end.
provoke_mem_leak(0, _, _) -> ok;
@@ -586,19 +779,19 @@ provoke_mem_leak(N, Code, Check) ->
%% Create several processes with references to the literal binary.
Self = self(),
Pids = [spawn_link(fun() ->
- create_binaries(Self, NumRefs, Check)
- end) || NumRefs <- lists:seq(1, 10)],
+ create_binaries(Self, NumRefs, Check)
+ end) || NumRefs <- lists:seq(1, 10)],
[receive {started,Pid} -> ok end || Pid <- Pids],
%% Make the code old and remove references to the constant pool
%% in all processes.
true = erlang:delete_module(literals),
Ms = [spawn_monitor(fun() ->
- false = erlang:check_process_code(Pid, literals)
- end) || Pid <- Pids],
+ false = erlang:check_process_code(Pid, literals)
+ end) || Pid <- Pids],
[receive
- {'DOWN',R,process,P,normal} ->
- ok
+ {'DOWN',R,process,P,normal} ->
+ ok
end || {P,R} <- Ms],
%% Purge the code.
@@ -606,14 +799,14 @@ provoke_mem_leak(N, Code, Check) ->
%% Tell the processes that the code has been purged.
[begin
- monitor(process, Pid),
- Pid ! purged
+ monitor(process, Pid),
+ Pid ! purged
end || Pid <- Pids],
%% Wait for all processes to terminate.
[receive
- {'DOWN',_,process,Pid,normal} ->
- ok
+ {'DOWN',_,process,Pid,normal} ->
+ ok
end || Pid <- Pids],
%% We now expect that the binary has been deallocated.
@@ -625,112 +818,112 @@ create_binaries(Parent, NumRefs, Check) ->
{bits,Bits} = literals:bits(),
Parent ! {started,self()},
receive
- purged ->
- %% The code has been purged. Now make sure that
- %% the binaries haven't been corrupted.
- Check = erlang:md5(Bin),
- [Bin = B || B <- Bins],
- <<42:13,Bin/binary>> = Bits,
-
- %% Remove all references to the binaries
- %% Doing it explicitly like this ensures that
- %% the binaries are gone when the parent process
- %% receives the 'DOWN' message.
- erlang:garbage_collect()
+ purged ->
+ %% The code has been purged. Now make sure that
+ %% the binaries haven't been corrupted.
+ Check = erlang:md5(Bin),
+ [Bin = B || B <- Bins],
+ <<42:13,Bin/binary>> = Bits,
+
+ %% Remove all references to the binaries
+ %% Doing it explicitly like this ensures that
+ %% the binaries are gone when the parent process
+ %% receives the 'DOWN' message.
+ erlang:garbage_collect()
end.
wait_for_memory_deallocations() ->
try
- erts_debug:set_internal_state(wait, deallocations)
+ erts_debug:set_internal_state(wait, deallocations)
catch
- error:undef ->
- erts_debug:set_internal_state(available_internal_state, true),
- wait_for_memory_deallocations()
+ error:undef ->
+ erts_debug:set_internal_state(available_internal_state, true),
+ wait_for_memory_deallocations()
end.
%% OTP-7559: c_p->cp could contain garbage and create a false dependency
%% to a module in a process. (Thanks to Richard Carlsson.)
false_dependency(Config) when is_list(Config) ->
- ?line Data = ?config(data_dir, Config),
- ?line File = filename:join(Data, "cpbugx"),
- ?line {ok,cpbugx,Code} = compile:file(File, [binary,report]),
+ Data = proplists:get_value(data_dir, Config),
+ File = filename:join(Data, "cpbugx"),
+ {ok,cpbugx,Code} = compile:file(File, [binary,report]),
do_false_dependency(fun cpbugx:before/0, Code),
do_false_dependency(fun cpbugx:before2/0, Code),
do_false_dependency(fun cpbugx:before3/0, Code),
-%% %% Spawn process. Make sure it has called cpbugx:before/0 and returned.
-%% Parent = self(),
-%% ?line Pid = spawn_link(fun() -> false_dependency_loop(Parent) end),
-%% ?line receive initialized -> ok end,
+ %% %% Spawn process. Make sure it has called cpbugx:before/0 and returned.
+ %% Parent = self(),
+ %% Pid = spawn_link(fun() -> false_dependency_loop(Parent) end),
+ %% receive initialized -> ok end,
-%% %% Reload the module. Make sure the process is still alive.
-%% ?line {module,cpbugx} = erlang:load_module(cpbugx, Bin),
-%% ?line io:put_chars(binary_to_list(element(2, process_info(Pid, backtrace)))),
-%% ?line true = is_process_alive(Pid),
+ %% %% Reload the module. Make sure the process is still alive.
+ %% {module,cpbugx} = erlang:load_module(cpbugx, Bin),
+ %% io:put_chars(binary_to_list(element(2, process_info(Pid, backtrace)))),
+ %% true = is_process_alive(Pid),
-%% %% There should not be any dependency to cpbugx.
-%% ?line false = erlang:check_process_code(Pid, cpbugx),
-
+ %% %% There should not be any dependency to cpbugx.
+ %% false = erlang:check_process_code(Pid, cpbugx),
-%% %% Kill the process.
-%% ?line unlink(Pid), exit(Pid, kill),
+
+ %% %% Kill the process.
+ %% unlink(Pid), exit(Pid, kill),
ok.
do_false_dependency(Init, Code) ->
- ?line {module,cpbugx} = erlang:load_module(cpbugx, Code),
+ {module,cpbugx} = erlang:load_module(cpbugx, Code),
%% Spawn process. Make sure it has the appropriate init function
%% and returned. CP should not contain garbage after the return.
Parent = self(),
- ?line Pid = spawn_link(fun() -> false_dependency_loop(Parent, Init, true) end),
- ?line receive initialized -> ok end,
+ Pid = spawn_link(fun() -> false_dependency_loop(Parent, Init, true) end),
+ receive initialized -> ok end,
%% Reload the module. Make sure the process is still alive.
- ?line {module,cpbugx} = erlang:load_module(cpbugx, Code),
- ?line io:put_chars(binary_to_list(element(2, process_info(Pid, backtrace)))),
- ?line true = is_process_alive(Pid),
+ {module,cpbugx} = erlang:load_module(cpbugx, Code),
+ io:put_chars(binary_to_list(element(2, process_info(Pid, backtrace)))),
+ true = is_process_alive(Pid),
%% There should not be any dependency to cpbugx.
- ?line false = erlang:check_process_code(Pid, cpbugx),
+ false = erlang:check_process_code(Pid, cpbugx),
%% Kill the process and completely unload the code.
- ?line unlink(Pid), exit(Pid, kill),
- ?line true = erlang:purge_module(cpbugx),
- ?line true = erlang:delete_module(cpbugx),
- ?line code:is_module_native(cpbugx), % test is_module_native on deleted code
- ?line true = erlang:purge_module(cpbugx),
- ?line code:is_module_native(cpbugx), % test is_module_native on purged code
+ unlink(Pid), exit(Pid, kill),
+ true = erlang:purge_module(cpbugx),
+ true = erlang:delete_module(cpbugx),
+ code:is_module_native(cpbugx), % test is_module_native on deleted code
+ true = erlang:purge_module(cpbugx),
+ code:is_module_native(cpbugx), % test is_module_native on purged code
ok.
-
+
false_dependency_loop(Parent, Init, SendInitAck) ->
Init(),
case SendInitAck of
- true -> Parent ! initialized;
- false -> void
- %% Just send one init-ack. I guess the point of this test
- %% wasn't to fill parents msg-queue (?). Seen to cause
- %% out-of-mem (on halfword-vm for some reason) by
- %% 91 million msg in queue. /sverker
+ true -> Parent ! initialized;
+ false -> void
+ %% Just send one init-ack. I guess the point of this test
+ %% wasn't to fill parents msg-queue (?). Seen to cause
+ %% out-of-mem (on halfword-vm for some reason) by
+ %% 91 million msg in queue. /sverker
end,
receive
- _ -> false_dependency_loop(Parent, Init, false)
+ _ -> false_dependency_loop(Parent, Init, false)
end.
coverage(Config) when is_list(Config) ->
- ?line code:is_module_native(?MODULE),
- ?line {'EXIT',{badarg,_}} = (catch erlang:purge_module({a,b,c})),
- ?line {'EXIT',{badarg,_}} = (catch code:is_module_native({a,b,c})),
- ?line {'EXIT',{badarg,_}} = (catch erlang:check_process_code(not_a_pid, ?MODULE)),
- ?line {'EXIT',{badarg,_}} = (catch erlang:check_process_code(self(), [not_a_module])),
- ?line {'EXIT',{badarg,_}} = (catch erlang:delete_module([a,b,c])),
- ?line {'EXIT',{badarg,_}} = (catch erlang:module_loaded(42)),
+ code:is_module_native(?MODULE),
+ {'EXIT',{badarg,_}} = (catch erlang:purge_module({a,b,c})),
+ {'EXIT',{badarg,_}} = (catch code:is_module_native({a,b,c})),
+ {'EXIT',{badarg,_}} = (catch erlang:check_process_code(not_a_pid, ?MODULE)),
+ {'EXIT',{badarg,_}} = (catch erlang:check_process_code(self(), [not_a_module])),
+ {'EXIT',{badarg,_}} = (catch erlang:delete_module([a,b,c])),
+ {'EXIT',{badarg,_}} = (catch erlang:module_loaded(42)),
ok.
fun_confusion(Config) when is_list(Config) ->
- Data = ?config(data_dir, Config),
+ Data = proplists:get_value(data_dir, Config),
Src = filename:join(Data, "fun_confusion"),
Mod = fun_confusion,
@@ -753,6 +946,208 @@ compile_load(Mod, Src, Ver) ->
{module,Mod} = code:load_binary(Mod, "fun_confusion.beam", Code1),
ok.
+
+t_copy_literals(Config) when is_list(Config) ->
+ %% Compile the the literals module.
+ Data = proplists:get_value(data_dir, Config),
+ File = filename:join(Data, "literals"),
+ {ok,literals,Code} = compile:file(File, [report,binary]),
+ {module,literals} = erlang:load_module(literals, Code),
+
+ N = 30,
+ Me = self(),
+ %% reload literals code every 567 ms
+ Rel = spawn_link(fun() -> reloader(literals,Code,567) end),
+ %% add new literal msgs to the loop every 789 ms
+ Sat = spawn_link(fun() -> saturate(Me,789) end),
+ %% run for 10s
+ _ = spawn_link(fun() -> receive after 10000 -> Me ! done end end),
+ ok = chase_msg(N, Me),
+ %% cleanup
+ Rel ! done,
+ Sat ! done,
+ ok = flush(),
+ ok.
+
+-define(mod, t_copy_literals_frags).
+t_copy_literals_frags(Config) when is_list(Config) ->
+ Bin = gen_lit(?mod,[{a,{1,2,3,4,5,6,7}},
+ {b,"hello world"},
+ {c, <<"hello world">>},
+ {d, {"hello world", {1.0, 2.0, <<"some">>, "string"}}},
+ {e, <<"off heap", 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9,10,11,12,13,14,15,
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9,10,11,12,13,14,15,
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9,10,11,12,13,14,15,
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9,10,11,12,13,14,15>>}]),
+
+ {module, ?mod} = erlang:load_module(?mod, Bin),
+ N = 6000,
+ Recv = spawn_opt(fun() -> receive
+ read ->
+ io:format("reading"),
+ literal_receiver()
+ end
+ end, [link,{min_heap_size, 10000}]),
+ Switcher = spawn_link(fun() -> literal_switcher() end),
+ Pids = [spawn_opt(fun() -> receive
+ {Pid, go, Recv, N} ->
+ io:format("sender batch (~w) start ~w~n",[N,self()]),
+ literal_sender(N,Recv),
+ Pid ! {self(), ok}
+ end
+ end, [link,{min_heap_size,800}]) || _ <- lists:seq(1,100)],
+ _ = [Pid ! {self(), go, Recv, N} || Pid <- Pids],
+ %% don't read immediately
+ timer:sleep(5),
+ Recv ! read,
+ Switcher ! {switch,?mod,Bin,[Recv|Pids],200},
+ _ = [receive {Pid, ok} -> ok end || Pid <- Pids],
+ Switcher ! {self(), done},
+ receive {Switcher, ok} -> ok end,
+ Recv ! {self(), done},
+ receive {Recv, ok} -> ok end,
+ ok.
+
+literal_receiver() ->
+ receive
+ {Pid, done} ->
+ io:format("reader_done~n"),
+ Pid ! {self(), ok};
+ {_Pid, msg, [A,B,C,D,E]} ->
+ A = ?mod:a(),
+ B = ?mod:b(),
+ C = ?mod:c(),
+ D = ?mod:d(),
+ E = ?mod:e(),
+ literal_receiver();
+ {Pid, sender_confirm} ->
+ io:format("sender confirm ~w~n", [Pid]),
+ Pid ! {self(), ok},
+ literal_receiver()
+ end.
+
+literal_sender(0, Recv) ->
+ Recv ! {self(), sender_confirm},
+ receive {Recv, ok} -> ok end;
+literal_sender(N, Recv) ->
+ Recv ! {self(), msg, [?mod:a(),
+ ?mod:b(),
+ ?mod:c(),
+ ?mod:d(),
+ ?mod:e()]},
+ literal_sender(N - 1, Recv).
+
+literal_switcher() ->
+ receive
+ {switch,Mod,Bin,Pids,Tmo} ->
+ literal_switcher(Mod,Bin,Pids,Tmo)
+ end.
+literal_switcher(Mod,Bin,Pids,Tmo) ->
+ receive
+ {Pid,done} ->
+ Pid ! {self(),ok}
+ after Tmo ->
+ io:format("load module ~w~n", [Mod]),
+ {module, Mod} = erlang:load_module(Mod,Bin),
+ ok = check_and_purge(Pids,Mod),
+ io:format("purge complete ~w~n", [Mod]),
+ literal_switcher(Mod,Bin,Pids,Tmo+Tmo)
+ end.
+
+check_and_purge([],Mod) ->
+ erlang:purge_module(Mod),
+ ok;
+check_and_purge(Pids,Mod) ->
+ io:format("purge ~w~n", [Mod]),
+ Tag = make_ref(),
+ _ = [begin
+ erlang:check_process_code(Pid,Mod,[{async,{Tag,Pid}}])
+ end || Pid <- Pids],
+ Retry = check_and_purge_receive(Pids,Tag,[]),
+ check_and_purge(Retry,Mod).
+
+check_and_purge_receive([Pid|Pids],Tag,Retry) ->
+ receive
+ {check_process_code, {Tag, Pid}, false} ->
+ check_and_purge_receive(Pids,Tag,Retry);
+ {check_process_code, {Tag, Pid}, true} ->
+ check_and_purge_receive(Pids,Tag,[Pid|Retry])
+ end;
+check_and_purge_receive([],_,Retry) ->
+ Retry.
+
+
+gen_lit(Module,Terms) ->
+ FunStrings = [lists:flatten(io_lib:format("~w() -> ~w.~n", [F,Term]))||{F,Term}<-Terms],
+ FunForms = function_forms(FunStrings),
+ Forms = [{attribute,erl_anno:new(1),module,Module},
+ {attribute,erl_anno:new(2),export,[FA || {FA,_} <- FunForms]}] ++
+ [Function || {_, Function} <- FunForms],
+ {ok, Module, Bin} = compile:forms(Forms),
+ Bin.
+
+function_forms([]) -> [];
+function_forms([S|Ss]) ->
+ {ok, Ts,_} = erl_scan:string(S),
+ {ok, Form} = erl_parse:parse_form(Ts),
+ Fun = element(3, Form),
+ Arity = element(4, Form),
+ [{{Fun,Arity}, Form}|function_forms(Ss)].
+
+chase_msg(0, Pid) ->
+ chase_loop(Pid);
+chase_msg(N, Master) ->
+ Pid = spawn_link(fun() -> chase_msg(N - 1,Master) end),
+ chase_loop(Pid).
+
+chase_loop(Pid) ->
+ receive
+ done ->
+ Pid ! done,
+ ok;
+ {_From,Msg} ->
+ Pid ! {self(), Msg},
+ ok = traverse(Msg),
+ chase_loop(Pid)
+ end.
+
+saturate(Pid,Time) ->
+ Es = [msg1,msg2,msg3,msg4,msg5],
+ Msg = [literals:E()||E <- Es],
+ Pid ! {self(), Msg},
+ receive
+ done -> ok
+ after Time ->
+ saturate(Pid,Time)
+ end.
+
+traverse([]) -> ok;
+traverse([H|T]) ->
+ ok = traverse(H),
+ traverse(T);
+traverse(T) when is_tuple(T) -> ok;
+traverse(B) when is_binary(B) -> ok;
+traverse(I) when is_integer(I) -> ok;
+traverse(#{ 1 := V1, b := V2 }) ->
+ ok = traverse(V1),
+ ok = traverse(V2),
+ ok.
+
+
+reloader(Mod,Code,Time) ->
+ receive
+ done -> ok
+ after Time ->
+ code:purge(Mod),
+ {module,Mod} = erlang:load_module(Mod, Code),
+ reloader(Mod,Code,Time)
+ end.
+
+
%% Utilities.
make_sub_binary(Bin) when is_binary(Bin) ->
@@ -775,4 +1170,43 @@ bit_sized_binary(Bin0) ->
BitSize = 8*size(Bin) + 1,
Bin.
+flush() ->
+ receive _ -> flush() after 0 -> ok end.
+
id(I) -> I.
+
+check_process_code_handle(What) ->
+ lists:member(What, erlang:system_info(check_process_code)).
+
+wait_until(Fun) ->
+ case Fun() of
+ true ->
+ ok;
+ false ->
+ receive after 100 -> ok end,
+ wait_until(Fun)
+ end.
+
+start_purge(Mod, Type) when is_atom(Mod)
+ andalso ((Type == true)
+ orelse (Type == false)) ->
+ Ref = make_ref(),
+ erts_code_purger ! {test_purge, Mod, self(), Type, Ref},
+ receive
+ {started, Ref} ->
+ Ref
+ end.
+
+continue_purge(Ref) when is_reference(Ref) ->
+ erts_code_purger ! {continue, Ref},
+ receive
+ {continued, Ref} ->
+ ok
+ end.
+
+complete_purge(Ref) when is_reference(Ref) ->
+ erts_code_purger ! {complete, Ref},
+ receive
+ {test_purge, Res, Ref} ->
+ Res
+ end.
diff --git a/erts/emulator/test/code_SUITE_data/another_code_test.erl b/erts/emulator/test/code_SUITE_data/another_code_test.erl
index f6f9e32996..5708ec682c 100644
--- a/erts/emulator/test/code_SUITE_data/another_code_test.erl
+++ b/erts/emulator/test/code_SUITE_data/another_code_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/code_SUITE_data/cpbugx.erl b/erts/emulator/test/code_SUITE_data/cpbugx.erl
index ea01ce411b..ae2075c867 100644
--- a/erts/emulator/test/code_SUITE_data/cpbugx.erl
+++ b/erts/emulator/test/code_SUITE_data/cpbugx.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/code_SUITE_data/fun_confusion.erl b/erts/emulator/test/code_SUITE_data/fun_confusion.erl
index 8d42937d3c..35279f241d 100644
--- a/erts/emulator/test/code_SUITE_data/fun_confusion.erl
+++ b/erts/emulator/test/code_SUITE_data/fun_confusion.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/code_SUITE_data/literals.erl b/erts/emulator/test/code_SUITE_data/literals.erl
index 9802d9d3f9..7c3b0ebe73 100644
--- a/erts/emulator/test/code_SUITE_data/literals.erl
+++ b/erts/emulator/test/code_SUITE_data/literals.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@
-module(literals).
-export([a/0,b/0,huge_bignum/0,binary/0,unused_binaries/0,bits/0]).
+-export([msg1/0,msg2/0,msg3/0,msg4/0,msg5/0]).
a() ->
{a,42.0,[7,38877938333399637266518333334747]}.
@@ -101,3 +102,9 @@ unused_binaries() ->
bits() ->
{bits,<<42:13,?MB_1>>}.
+
+msg1() -> "halloj".
+msg2() -> {"hello","world"}.
+msg3() -> <<"halloj">>.
+msg4() -> #{ 1=> "hello", b => "world"}.
+msg5() -> {1,2,3,4,5,6}.
diff --git a/erts/emulator/test/code_SUITE_data/many_funs.erl b/erts/emulator/test/code_SUITE_data/many_funs.erl
index e832f271d0..ada570feee 100644
--- a/erts/emulator/test/code_SUITE_data/many_funs.erl
+++ b/erts/emulator/test/code_SUITE_data/many_funs.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/code_SUITE_data/my_code_test.erl b/erts/emulator/test/code_SUITE_data/my_code_test.erl
index 57d867a5ac..9d12aa9897 100644
--- a/erts/emulator/test/code_SUITE_data/my_code_test.erl
+++ b/erts/emulator/test/code_SUITE_data/my_code_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -24,5 +24,3 @@
make_fun(A) ->
fun(X) -> A + X end.
-
-
diff --git a/erts/emulator/test/code_SUITE_data/my_code_test2.erl b/erts/emulator/test/code_SUITE_data/my_code_test2.erl
new file mode 100644
index 0000000000..57973535d4
--- /dev/null
+++ b/erts/emulator/test/code_SUITE_data/my_code_test2.erl
@@ -0,0 +1,32 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(my_code_test2).
+
+-export([make_fun/1, make_fun2/0]).
+
+make_fun(A) ->
+ fun(X) -> A + X end.
+
+make_fun2() ->
+ fun (F1,F2) ->
+ F1(),
+ F2()
+ end.
diff --git a/erts/emulator/test/code_SUITE_data/versions.erl b/erts/emulator/test/code_SUITE_data/versions.erl
index 0e2d92c8f1..56407e877a 100644
--- a/erts/emulator/test/code_SUITE_data/versions.erl
+++ b/erts/emulator/test/code_SUITE_data/versions.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/code_parallel_load_SUITE.erl b/erts/emulator/test/code_parallel_load_SUITE.erl
index b7ac0420cd..827add71e5 100644
--- a/erts/emulator/test/code_parallel_load_SUITE.erl
+++ b/erts/emulator/test/code_parallel_load_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2012-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2012-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -19,49 +19,34 @@
%%
-module(code_parallel_load_SUITE).
--export([
- all/0,
- suite/0,
- init_per_suite/1,
- end_per_suite/1,
- init_per_testcase/2,
- end_per_testcase/2
- ]).
-
--export([
- multiple_load_check_purge_repeat/1,
- many_load_distributed_only_once/1
- ]).
+-export([all/0,
+ suite/0,
+ init_per_testcase/2,
+ end_per_testcase/2]).
+
+-export([multiple_load_check_purge_repeat/1,
+ many_load_distributed_only_once/1]).
-define(model, code_parallel_load_SUITE_model).
-define(interval, 50).
-define(number_of_processes, 160).
-define(passes, 4).
+-include_lib("common_test/include/ct.hrl").
--include_lib("test_server/include/test_server.hrl").
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 4}}].
all() ->
- [
- multiple_load_check_purge_repeat,
- many_load_distributed_only_once
- ].
-
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
+ [ multiple_load_check_purge_repeat,
+ many_load_distributed_only_once ].
init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- Dog=?t:timetrap(?t:minutes(3)),
- [{watchdog, Dog}|Config].
+ Config.
end_per_testcase(_Func, Config) ->
- SConf = ?config(save_config, Config),
+ SConf = proplists:get_value(save_config, Config),
Pids = proplists:get_value(purge_pids, SConf),
case check_old_code(?model) of
@@ -72,9 +57,7 @@ end_per_testcase(_Func, Config) ->
true -> check_and_purge_processes_code(Pids, ?model);
_ -> ok
end,
- Dog=?config(watchdog, Config),
- ?t:timetrap_cancel(Dog).
-
+ ok.
multiple_load_check_purge_repeat(_Conf) ->
Ts = [v1,v2,v3,v4,v5,v6],
diff --git a/erts/emulator/test/crypto_SUITE.erl b/erts/emulator/test/crypto_SUITE.erl
index 3622592586..afb1be7332 100644
--- a/erts/emulator/test/crypto_SUITE.erl
+++ b/erts/emulator/test/crypto_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,65 +20,44 @@
-module(crypto_SUITE).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- t_md5/1,t_md5_update/1,error/1,unaligned_context/1,random_lists/1,
- misc_errors/1]).
+-export([all/0, suite/0,
+ t_md5/1,t_md5_update/1,error/1,unaligned_context/1,random_lists/1,
+ misc_errors/1]).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]}].
all() ->
[t_md5, t_md5_update, error, unaligned_context,
random_lists, misc_errors].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-
-misc_errors(doc) ->
- ["Test crc32, adler32 and md5 error cases not covered by other tests"];
-misc_errors(suite) ->
- [];
+%% Test crc32, adler32 and md5 error cases not covered by other tests"
misc_errors(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:minutes(2)),
- ?line 1 = erlang:adler32([]),
- ?line L = lists:duplicate(600,3),
- ?line 1135871753 = erlang:adler32(L),
- ?line L2 = lists:duplicate(22000,3),
- ?line 1100939744 = erlang:adler32(L2),
- ?line {'EXIT', {badarg,_}} = (catch erlang:adler32(L++[a])),
- ?line {'EXIT', {badarg,_}} = (catch erlang:crc32(L++[a])),
- ?line {'EXIT', {badarg,_}} = (catch erlang:crc32([1,2,3|<<25:7>>])),
- ?line {'EXIT', {badarg,_}} = (catch erlang:crc32([1,2,3|4])),
- ?line Big = 111111111111111111111111111111,
- ?line {'EXIT', {badarg,_}} = (catch erlang:crc32(Big,<<"hej">>)),
- ?line {'EXIT', {badarg,_}} = (catch erlang:crc32(25,[1,2,3|4])),
- ?line {'EXIT', {badarg,_}} = (catch erlang:crc32_combine(Big,3,3)),
- ?line {'EXIT', {badarg,_}} = (catch erlang:crc32_combine(3,Big,3)),
- ?line {'EXIT', {badarg,_}} = (catch erlang:crc32_combine(3,3,Big)),
- ?line {'EXIT', {badarg,_}} = (catch erlang:adler32(Big,<<"hej">>)),
- ?line {'EXIT', {badarg,_}} = (catch erlang:adler32(25,[1,2,3|4])),
- ?line {'EXIT', {badarg,_}} = (catch erlang:adler32_combine(Big,3,3)),
- ?line {'EXIT', {badarg,_}} = (catch erlang:adler32_combine(3,Big,3)),
- ?line {'EXIT', {badarg,_}} = (catch erlang:adler32_combine(3,3,Big)),
- ?line {'EXIT', {badarg,_}} = (catch erlang:md5_update(<<"hej">>,<<"hej">>)),
- ?line {'EXIT', {badarg,_}} = (catch erlang:md5_final(<<"hej">>)),
- ?line test_server:timetrap_cancel(Dog),
+ ct:timetrap({minutes, 2}),
+ 1 = erlang:adler32([]),
+ L = lists:duplicate(600,3),
+ 1135871753 = erlang:adler32(L),
+ L2 = lists:duplicate(22000,3),
+ 1100939744 = erlang:adler32(L2),
+ {'EXIT', {badarg,_}} = (catch erlang:adler32(L++[a])),
+ {'EXIT', {badarg,_}} = (catch erlang:crc32(L++[a])),
+ {'EXIT', {badarg,_}} = (catch erlang:crc32([1,2,3|<<25:7>>])),
+ {'EXIT', {badarg,_}} = (catch erlang:crc32([1,2,3|4])),
+ Big = 111111111111111111111111111111,
+ {'EXIT', {badarg,_}} = (catch erlang:crc32(Big,<<"hej">>)),
+ {'EXIT', {badarg,_}} = (catch erlang:crc32(25,[1,2,3|4])),
+ {'EXIT', {badarg,_}} = (catch erlang:crc32_combine(Big,3,3)),
+ {'EXIT', {badarg,_}} = (catch erlang:crc32_combine(3,Big,3)),
+ {'EXIT', {badarg,_}} = (catch erlang:crc32_combine(3,3,Big)),
+ {'EXIT', {badarg,_}} = (catch erlang:adler32(Big,<<"hej">>)),
+ {'EXIT', {badarg,_}} = (catch erlang:adler32(25,[1,2,3|4])),
+ {'EXIT', {badarg,_}} = (catch erlang:adler32_combine(Big,3,3)),
+ {'EXIT', {badarg,_}} = (catch erlang:adler32_combine(3,Big,3)),
+ {'EXIT', {badarg,_}} = (catch erlang:adler32_combine(3,3,Big)),
+ {'EXIT', {badarg,_}} = (catch erlang:md5_update(<<"hej">>,<<"hej">>)),
+ {'EXIT', {badarg,_}} = (catch erlang:md5_final(<<"hej">>)),
ok.
@@ -93,7 +72,7 @@ nicesplit(N,L) ->
nicesplit(0,Tail,Acc) ->
{lists:reverse(Acc),Tail};
nicesplit(_,[],Acc) ->
- {lists:reverse(Acc),[]};
+ {lists:reverse(Acc),[]};
nicesplit(N,[H|Tail],Acc) ->
nicesplit(N-1,Tail,[H|Acc]).
@@ -102,17 +81,17 @@ run_in_para([],_) ->
run_in_para(FunList,Schedulers) ->
{ThisTime,NextTime} = nicesplit(Schedulers,FunList),
case length(ThisTime) of
- 1 ->
- [{L,Fun}] = ThisTime,
- try
- Fun()
+ 1 ->
+ [{L,Fun}] = ThisTime,
+ try
+ Fun()
catch
- _:Reason ->
- exit({error_at_line,L,Reason})
- end;
+ _:Reason ->
+ exit({error_at_line,L,Reason})
+ end;
_ ->
- These = [ {L,erlang:spawn_monitor(F)} || {L,F} <- ThisTime ],
- collect_workers(These)
+ These = [ {L,erlang:spawn_monitor(F)} || {L,F} <- ThisTime ],
+ collect_workers(These)
end,
run_in_para(NextTime,Schedulers).
@@ -120,159 +99,147 @@ collect_workers([]) ->
ok;
collect_workers([{L,{Pid,Ref}}|T]) ->
receive
- {'DOWN',Ref,process,Pid,normal} ->
- collect_workers(T);
- {'DOWN',Ref,process,Pid,Other} ->
- exit({error_at_line,L,Other})
+ {'DOWN',Ref,process,Pid,normal} ->
+ collect_workers(T);
+ {'DOWN',Ref,process,Pid,Other} ->
+ exit({error_at_line,L,Other})
end.
-random_lists(doc) ->
- ["Test crc32, adler32 and md5 on a number of pseudo-randomly generated "
- "lists."];
-random_lists(suite) ->
- [];
+%% Test crc32, adler32 and md5 on a number of pseudo-randomly generated lists.
random_lists(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:minutes(5)),
- ?line Num = erlang:system_info(schedulers_online),
- ?line B = list_to_binary(
- lists:duplicate(
- (erlang:system_info(context_reductions)*10) - 50,$!)),
- ?line CRC32_1 = fun(L) -> erlang:crc32(L) end,
- ?line CRC32_2 = fun(L) -> ?REF:crc32(L) end,
- ?line ADLER32_1 = fun(L) -> erlang:adler32(L) end,
- ?line ADLER32_2 = fun(L) -> ?REF:adler32(L) end,
- ?line MD5_1 = fun(L) -> erlang:md5(L) end,
- ?line MD5_2 = fun(L) -> ?REF:md5_final(
- ?REF:md5_update(?REF:md5_init(),L)) end,
- ?line MD5_3 = fun(L) -> erlang:md5_final(
- erlang:md5_update(erlang:md5_init(),L)) end,
- ?line CRC32_1_L = fun(L) -> erlang:crc32([B|L]) end,
- ?line CRC32_2_L = fun(L) -> ?REF:crc32([B|L]) end,
- ?line ADLER32_1_L = fun(L) -> erlang:adler32([B|L]) end,
- ?line ADLER32_2_L = fun(L) -> ?REF:adler32([B|L]) end,
- ?line MD5_1_L = fun(L) -> erlang:md5([B|L]) end,
- ?line MD5_2_L = fun(L) -> ?REF:md5_final(
- ?REF:md5_update(?REF:md5_init(),[B|L])) end,
- ?line MD5_3_L = fun(L) -> erlang:md5_final(
- erlang:md5_update(
- erlang:md5_init(),[B|L])) end,
- ?line Wlist0 =
- [{?LINE, fun() -> random_iolist:run(150, CRC32_1, CRC32_2) end},
- {?LINE, fun() -> random_iolist:run(150, ADLER32_1, ADLER32_2) end},
- {?LINE, fun() -> random_iolist:run(150,MD5_1,MD5_2) end},
- {?LINE, fun() -> random_iolist:run(150,MD5_1,MD5_3) end},
- {?LINE, fun() -> random_iolist:run(150, CRC32_1_L, CRC32_2_L) end},
- {?LINE,
- fun() -> random_iolist:run(150, ADLER32_1_L, ADLER32_2_L) end},
- {?LINE, fun() -> random_iolist:run(150,MD5_1_L,MD5_2_L) end},
- {?LINE, fun() -> random_iolist:run(150,MD5_1_L,MD5_3_L) end}],
- ?line run_in_para(Wlist0,Num),
- ?line CRC32_1_2 = fun(L1,L2) -> erlang:crc32([L1,L2]) end,
- ?line CRC32_2_2 = fun(L1,L2) -> erlang:crc32(erlang:crc32(L1),L2) end,
- ?line CRC32_3_2 = fun(L1,L2) -> erlang:crc32_combine(
- erlang:crc32(L1),
- erlang:crc32(L2),
- erlang:iolist_size(L2))
- end,
- ?line ADLER32_1_2 = fun(L1,L2) -> erlang:adler32([L1,L2]) end,
- ?line ADLER32_2_2 = fun(L1,L2) -> erlang:adler32(
- erlang:adler32(L1),L2) end,
- ?line ADLER32_3_2 = fun(L1,L2) -> erlang:adler32_combine(
- erlang:adler32(L1),
- erlang:adler32(L2),
- erlang:iolist_size(L2))
- end,
- ?line MD5_1_2 = fun(L1,L2) -> erlang:md5([L1,L2]) end,
- ?line MD5_2_2 = fun(L1,L2) ->
- erlang:md5_final(
- erlang:md5_update(
- erlang:md5_update(
- erlang:md5_init(),
- L1),
- L2))
- end,
- ?line CRC32_1_L_2 = fun(L1,L2) -> erlang:crc32([[B|L1],[B|L2]]) end,
- ?line CRC32_2_L_2 = fun(L1,L2) -> erlang:crc32(
- erlang:crc32([B|L1]),[B|L2]) end,
- ?line CRC32_3_L_2 = fun(L1,L2) -> erlang:crc32_combine(
- erlang:crc32([B|L1]),
- erlang:crc32([B|L2]),
- erlang:iolist_size([B|L2]))
- end,
- ?line ADLER32_1_L_2 = fun(L1,L2) -> erlang:adler32([[B|L1],[B|L2]]) end,
- ?line ADLER32_2_L_2 = fun(L1,L2) -> erlang:adler32(
- erlang:adler32([B|L1]),
- [B|L2])
- end,
- ?line ADLER32_3_L_2 = fun(L1,L2) -> erlang:adler32_combine(
- erlang:adler32([B|L1]),
- erlang:adler32([B|L2]),
- erlang:iolist_size([B|L2]))
- end,
- ?line MD5_1_L_2 = fun(L1,L2) -> erlang:md5([[B|L1],[B|L2]]) end,
- ?line MD5_2_L_2 = fun(L1,L2) ->
- erlang:md5_final(
- erlang:md5_update(
- erlang:md5_update(
- erlang:md5_init(),
- [B|L1]),
- [B|L2]))
- end,
- ?line Wlist1 =
- [{?LINE, fun() -> random_iolist:run2(150,CRC32_1_2,CRC32_2_2) end},
- {?LINE, fun() -> random_iolist:run2(150,CRC32_1_2,CRC32_3_2) end},
- {?LINE, fun() -> random_iolist:run2(150,ADLER32_1_2,ADLER32_2_2) end},
- {?LINE, fun() -> random_iolist:run2(150,ADLER32_1_2,ADLER32_3_2) end},
- {?LINE, fun() -> random_iolist:run2(150,MD5_1_2,MD5_2_2) end},
- {?LINE, fun() -> random_iolist:run2(150,CRC32_1_L_2,CRC32_2_L_2) end},
- {?LINE, fun() -> random_iolist:run2(150,CRC32_1_L_2,CRC32_3_L_2) end},
- {?LINE,
- fun() -> random_iolist:run2(150,ADLER32_1_L_2,ADLER32_2_L_2) end},
- {?LINE,
- fun() -> random_iolist:run2(150,ADLER32_1_L_2,ADLER32_3_L_2) end},
- {?LINE, fun() -> random_iolist:run2(150,MD5_1_L_2,MD5_2_L_2) end}],
- ?line run_in_para(Wlist1,Num),
- ?line test_server:timetrap_cancel(Dog),
+ ct:timetrap({minutes, 5}),
+ Num = erlang:system_info(schedulers_online),
+ B = list_to_binary(
+ lists:duplicate(
+ (erlang:system_info(context_reductions)*10) - 50,$!)),
+ CRC32_1 = fun(L) -> erlang:crc32(L) end,
+ CRC32_2 = fun(L) -> ?REF:crc32(L) end,
+ ADLER32_1 = fun(L) -> erlang:adler32(L) end,
+ ADLER32_2 = fun(L) -> ?REF:adler32(L) end,
+ MD5_1 = fun(L) -> erlang:md5(L) end,
+ MD5_2 = fun(L) -> ?REF:md5_final(
+ ?REF:md5_update(?REF:md5_init(),L)) end,
+ MD5_3 = fun(L) -> erlang:md5_final(
+ erlang:md5_update(erlang:md5_init(),L)) end,
+ CRC32_1_L = fun(L) -> erlang:crc32([B|L]) end,
+ CRC32_2_L = fun(L) -> ?REF:crc32([B|L]) end,
+ ADLER32_1_L = fun(L) -> erlang:adler32([B|L]) end,
+ ADLER32_2_L = fun(L) -> ?REF:adler32([B|L]) end,
+ MD5_1_L = fun(L) -> erlang:md5([B|L]) end,
+ MD5_2_L = fun(L) -> ?REF:md5_final(
+ ?REF:md5_update(?REF:md5_init(),[B|L])) end,
+ MD5_3_L = fun(L) -> erlang:md5_final(
+ erlang:md5_update(
+ erlang:md5_init(),[B|L])) end,
+ Wlist0 =
+ [{?LINE, fun() -> random_iolist:run(150, CRC32_1, CRC32_2) end},
+ {?LINE, fun() -> random_iolist:run(150, ADLER32_1, ADLER32_2) end},
+ {?LINE, fun() -> random_iolist:run(150,MD5_1,MD5_2) end},
+ {?LINE, fun() -> random_iolist:run(150,MD5_1,MD5_3) end},
+ {?LINE, fun() -> random_iolist:run(150, CRC32_1_L, CRC32_2_L) end},
+ {?LINE,
+ fun() -> random_iolist:run(150, ADLER32_1_L, ADLER32_2_L) end},
+ {?LINE, fun() -> random_iolist:run(150,MD5_1_L,MD5_2_L) end},
+ {?LINE, fun() -> random_iolist:run(150,MD5_1_L,MD5_3_L) end}],
+ run_in_para(Wlist0,Num),
+ CRC32_1_2 = fun(L1,L2) -> erlang:crc32([L1,L2]) end,
+ CRC32_2_2 = fun(L1,L2) -> erlang:crc32(erlang:crc32(L1),L2) end,
+ CRC32_3_2 = fun(L1,L2) -> erlang:crc32_combine(
+ erlang:crc32(L1),
+ erlang:crc32(L2),
+ erlang:iolist_size(L2))
+ end,
+ ADLER32_1_2 = fun(L1,L2) -> erlang:adler32([L1,L2]) end,
+ ADLER32_2_2 = fun(L1,L2) -> erlang:adler32(
+ erlang:adler32(L1),L2) end,
+ ADLER32_3_2 = fun(L1,L2) -> erlang:adler32_combine(
+ erlang:adler32(L1),
+ erlang:adler32(L2),
+ erlang:iolist_size(L2))
+ end,
+ MD5_1_2 = fun(L1,L2) -> erlang:md5([L1,L2]) end,
+ MD5_2_2 = fun(L1,L2) ->
+ erlang:md5_final(
+ erlang:md5_update(
+ erlang:md5_update(
+ erlang:md5_init(),
+ L1),
+ L2))
+ end,
+ CRC32_1_L_2 = fun(L1,L2) -> erlang:crc32([[B|L1],[B|L2]]) end,
+ CRC32_2_L_2 = fun(L1,L2) -> erlang:crc32(
+ erlang:crc32([B|L1]),[B|L2]) end,
+ CRC32_3_L_2 = fun(L1,L2) -> erlang:crc32_combine(
+ erlang:crc32([B|L1]),
+ erlang:crc32([B|L2]),
+ erlang:iolist_size([B|L2]))
+ end,
+ ADLER32_1_L_2 = fun(L1,L2) -> erlang:adler32([[B|L1],[B|L2]]) end,
+ ADLER32_2_L_2 = fun(L1,L2) -> erlang:adler32(
+ erlang:adler32([B|L1]),
+ [B|L2])
+ end,
+ ADLER32_3_L_2 = fun(L1,L2) -> erlang:adler32_combine(
+ erlang:adler32([B|L1]),
+ erlang:adler32([B|L2]),
+ erlang:iolist_size([B|L2]))
+ end,
+ MD5_1_L_2 = fun(L1,L2) -> erlang:md5([[B|L1],[B|L2]]) end,
+ MD5_2_L_2 = fun(L1,L2) ->
+ erlang:md5_final(
+ erlang:md5_update(
+ erlang:md5_update(
+ erlang:md5_init(),
+ [B|L1]),
+ [B|L2]))
+ end,
+ Wlist1 =
+ [{?LINE, fun() -> random_iolist:run2(150,CRC32_1_2,CRC32_2_2) end},
+ {?LINE, fun() -> random_iolist:run2(150,CRC32_1_2,CRC32_3_2) end},
+ {?LINE, fun() -> random_iolist:run2(150,ADLER32_1_2,ADLER32_2_2) end},
+ {?LINE, fun() -> random_iolist:run2(150,ADLER32_1_2,ADLER32_3_2) end},
+ {?LINE, fun() -> random_iolist:run2(150,MD5_1_2,MD5_2_2) end},
+ {?LINE, fun() -> random_iolist:run2(150,CRC32_1_L_2,CRC32_2_L_2) end},
+ {?LINE, fun() -> random_iolist:run2(150,CRC32_1_L_2,CRC32_3_L_2) end},
+ {?LINE,
+ fun() -> random_iolist:run2(150,ADLER32_1_L_2,ADLER32_2_L_2) end},
+ {?LINE,
+ fun() -> random_iolist:run2(150,ADLER32_1_L_2,ADLER32_3_L_2) end},
+ {?LINE, fun() -> random_iolist:run2(150,MD5_1_L_2,MD5_2_L_2) end}],
+ run_in_para(Wlist1,Num),
ok.
-%%
-%%
-t_md5(doc) ->
- ["Generate MD5 message digests and check the result. Examples are "
- "from RFC-1321."];
+%% Generate MD5 message digests and check the result. Examples are from RFC-1321.
t_md5(Config) when is_list(Config) ->
- ?line t_md5_test("", "d41d8cd98f00b204e9800998ecf8427e"),
- ?line t_md5_test("a", "0cc175b9c0f1b6a831c399e269772661"),
- ?line t_md5_test("abc", "900150983cd24fb0d6963f7d28e17f72"),
- ?line t_md5_test(["message ","digest"], "f96b697d7cb7938d525a2f31aaf161d0"),
- ?line t_md5_test(["message ",unaligned_sub_bin(<<"digest">>)],
- "f96b697d7cb7938d525a2f31aaf161d0"),
- ?line t_md5_test("abcdefghijklmnopqrstuvwxyz",
- "c3fcd3d76192e4007dfb496cca67e13b"),
- ?line t_md5_test("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
- "0123456789",
- "d174ab98d277d9f5a5611c2c9f419d9f"),
- ?line t_md5_test("12345678901234567890123456789012345678901234567890"
- "123456789012345678901234567890",
- "57edf4a22be3c955ac49da2e2107b67a"),
+ t_md5_test("", "d41d8cd98f00b204e9800998ecf8427e"),
+ t_md5_test("a", "0cc175b9c0f1b6a831c399e269772661"),
+ t_md5_test("abc", "900150983cd24fb0d6963f7d28e17f72"),
+ t_md5_test(["message ","digest"], "f96b697d7cb7938d525a2f31aaf161d0"),
+ t_md5_test(["message ",unaligned_sub_bin(<<"digest">>)],
+ "f96b697d7cb7938d525a2f31aaf161d0"),
+ t_md5_test("abcdefghijklmnopqrstuvwxyz",
+ "c3fcd3d76192e4007dfb496cca67e13b"),
+ t_md5_test("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+ "0123456789",
+ "d174ab98d277d9f5a5611c2c9f419d9f"),
+ t_md5_test("12345678901234567890123456789012345678901234567890"
+ "123456789012345678901234567890",
+ "57edf4a22be3c955ac49da2e2107b67a"),
ok.
-%%
-%%
-t_md5_update(doc) ->
- ["Generate MD5 message using md5_init, md5_update, and md5_final, and"
- "check the result. Examples are from RFC-1321."];
+%% Generate MD5 message using md5_init, md5_update, and md5_final, and
+%% check the result. Examples are from RFC-1321.
t_md5_update(Config) when is_list(Config) ->
- ?line t_md5_update_1(fun(Str) -> Str end),
- ?line t_md5_update_1(fun(Str) -> list_to_binary(Str) end),
- ?line t_md5_update_1(fun(Str) -> unaligned_sub_bin(list_to_binary(Str)) end),
+ t_md5_update_1(fun(Str) -> Str end),
+ t_md5_update_1(fun(Str) -> list_to_binary(Str) end),
+ t_md5_update_1(fun(Str) -> unaligned_sub_bin(list_to_binary(Str)) end),
ok.
t_md5_update_1(Tr) when is_function(Tr, 1) ->
Ctx = erlang:md5_init(),
Ctx1 = erlang:md5_update(Ctx, Tr("ABCDEFGHIJKLMNOPQRSTUVWXYZ")),
Ctx2 = erlang:md5_update(Ctx1, Tr("abcdefghijklmnopqrstuvwxyz"
- "0123456789")),
+ "0123456789")),
m(erlang:md5_final(Ctx2),
hexstr2bin("d174ab98d277d9f5a5611c2c9f419d9f")),
ok.
@@ -280,28 +247,28 @@ t_md5_update_1(Tr) when is_function(Tr, 1) ->
%%
%%
error(Config) when is_list(Config) ->
- ?line {'EXIT',{badarg,_}} = (catch erlang:md5(bit_sized_binary(<<"abc">>))),
- ?line Ctx0 = erlang:md5_init(),
- ?line {'EXIT',{badarg,_}} =
- (catch erlang:md5_update(Ctx0, bit_sized_binary(<<"abcfjldjd">>))),
- ?line {'EXIT',{badarg,_}} =
- (catch erlang:md5_update(Ctx0, ["something",bit_sized_binary(<<"abcfjldjd">>)])),
- ?line {'EXIT',{badarg,_}} =
- (catch erlang:md5_update(bit_sized_binary(Ctx0), "something")),
- ?line {'EXIT',{badarg,_}} = (catch erlang:md5_final(bit_sized_binary(Ctx0))),
- ?line m(erlang:md5_final(Ctx0), hexstr2bin("d41d8cd98f00b204e9800998ecf8427e")),
+ {'EXIT',{badarg,_}} = (catch erlang:md5(bit_sized_binary(<<"abc">>))),
+ Ctx0 = erlang:md5_init(),
+ {'EXIT',{badarg,_}} =
+ (catch erlang:md5_update(Ctx0, bit_sized_binary(<<"abcfjldjd">>))),
+ {'EXIT',{badarg,_}} =
+ (catch erlang:md5_update(Ctx0, ["something",bit_sized_binary(<<"abcfjldjd">>)])),
+ {'EXIT',{badarg,_}} =
+ (catch erlang:md5_update(bit_sized_binary(Ctx0), "something")),
+ {'EXIT',{badarg,_}} = (catch erlang:md5_final(bit_sized_binary(Ctx0))),
+ m(erlang:md5_final(Ctx0), hexstr2bin("d41d8cd98f00b204e9800998ecf8427e")),
ok.
%%
%%
unaligned_context(Config) when is_list(Config) ->
- ?line Ctx0 = erlang:md5_init(),
- ?line Ctx1 = erlang:md5_update(unaligned_sub_bin(Ctx0), "ABCDEFGHIJKLMNOPQRSTUVWXYZ"),
- ?line Ctx = erlang:md5_update(unaligned_sub_bin(Ctx1),
- "abcdefghijklmnopqrstuvwxyz0123456789"),
- ?line m(erlang:md5_final(unaligned_sub_bin(Ctx)),
- hexstr2bin("d174ab98d277d9f5a5611c2c9f419d9f")),
+ Ctx0 = erlang:md5_init(),
+ Ctx1 = erlang:md5_update(unaligned_sub_bin(Ctx0), "ABCDEFGHIJKLMNOPQRSTUVWXYZ"),
+ Ctx = erlang:md5_update(unaligned_sub_bin(Ctx1),
+ "abcdefghijklmnopqrstuvwxyz0123456789"),
+ m(erlang:md5_final(unaligned_sub_bin(Ctx)),
+ hexstr2bin("d174ab98d277d9f5a5611c2c9f419d9f")),
ok.
%%
@@ -347,5 +314,3 @@ bit_sized_binary(Bin0) ->
Bin.
id(I) -> I.
-
-
diff --git a/erts/emulator/test/crypto_reference.erl b/erts/emulator/test/crypto_reference.erl
index 7797eacd75..950b0c1560 100644
--- a/erts/emulator/test/crypto_reference.erl
+++ b/erts/emulator/test/crypto_reference.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/ddll_SUITE.erl b/erts/emulator/test/ddll_SUITE.erl
index cabd6472d4..93b6f2d956 100644
--- a/erts/emulator/test/ddll_SUITE.erl
+++ b/erts/emulator/test/ddll_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -31,30 +31,31 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2, ddll_test/1, errors/1,
- reference_count/1,
- kill_port/1, dont_kill_port/1]).
+-export([all/0, suite/0,
+ ddll_test/1, errors/1, reference_count/1,
+ kill_port/1, dont_kill_port/1]).
-export([unload_on_process_exit/1, delayed_unload_with_ports/1,
- unload_due_to_process_exit/1,
- no_unload_due_to_process_exit/1, no_unload_due_to_process_exit_2/1,
- unload_reload_thingie/1, unload_reload_thingie_2/1,
- unload_reload_thingie_3/1, reload_pending/1, reload_pending_kill/1,
- load_fail_init/1,
- reload_pending_fail_init/1,
- more_error_codes/1, forced_port_killing/1,
- no_trap_exit_and_kill_ports/1,
- monitor_demonitor/1, monitor_demonitor_load/1, new_interface/1,
- lock_driver/1]).
+ unload_due_to_process_exit/1,
+ no_unload_due_to_process_exit/1, no_unload_due_to_process_exit_2/1,
+ unload_reload_thingie/1, unload_reload_thingie_2/1,
+ unload_reload_thingie_3/1, reload_pending/1, reload_pending_kill/1,
+ load_fail_init/1,
+ reload_pending_fail_init/1,
+ more_error_codes/1, forced_port_killing/1,
+ no_trap_exit_and_kill_ports/1,
+ monitor_demonitor/1, monitor_demonitor_load/1, new_interface/1,
+ lock_driver/1]).
% Private exports
-export([echo_loader/2, nice_echo_loader/2 ,properties/1, load_and_unload/1]).
-import(ordsets, [subtract/2]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {seconds, 10}}].
all() ->
[ddll_test, errors, reference_count, kill_port,
@@ -70,1057 +71,931 @@ all() ->
no_trap_exit_and_kill_ports, monitor_demonitor,
monitor_demonitor_load, new_interface, lock_driver].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-unload_on_process_exit(suite) ->
- [];
-unload_on_process_exit(doc) ->
- ["Check that the driver is unloaded on process exit"];
+%% Check that the driver is unloaded on process exit
unload_on_process_exit(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Path = ?config(data_dir, Config),
- ?line false = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())),
+ Path = proplists:get_value(data_dir, Config),
+ false = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())),
Parent = self(),
- ?line Pid = spawn(fun() ->
- receive go -> ok end,
- erl_ddll:try_load(Path, echo_drv, []),
- Parent ! gone,
- receive go -> ok end,
- erl_ddll:loaded_drivers(),
- exit(banan)
- end),
- ?line Ref = erlang:monitor(process,Pid),
- ?line false = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())),
+ Pid = spawn(fun() ->
+ receive go -> ok end,
+ erl_ddll:try_load(Path, echo_drv, []),
+ Parent ! gone,
+ receive go -> ok end,
+ erl_ddll:loaded_drivers(),
+ exit(banan)
+ end),
+ Ref = erlang:monitor(process,Pid),
+ false = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())),
Pid ! go,
- ?line receive
- gone -> ok
+ receive
+ gone -> ok
end,
- ?line true = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())),
+ true = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())),
Pid ! go,
- ?line receive
- {'DOWN', Ref, process, Pid, banan} ->
- ok
+ receive
+ {'DOWN', Ref, process, Pid, banan} ->
+ ok
end,
receive after 500 -> ok end,
- ?line false = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())),
- ?line test_server:timetrap_cancel(Dog),
+ false = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())),
ok.
-delayed_unload_with_ports(suite) ->
- [];
-delayed_unload_with_ports(doc) ->
- ["Check that the driver is unloaded when the last port is closed"];
+%% Check that the driver is unloaded when the last port is closed
delayed_unload_with_ports(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Path = ?config(data_dir, Config),
- ?line erl_ddll:try_load(Path, echo_drv, []),
- ?line erl_ddll:try_load(Path, echo_drv, []),
- ?line Port = open_port({spawn, echo_drv}, [eof]),
- ?line 1 = erl_ddll:info(echo_drv, port_count),
- ?line Port2 = open_port({spawn, echo_drv}, [eof]),
- ?line 2 = erl_ddll:info(echo_drv, port_count),
- ?line {ok,pending_process} = erl_ddll:try_unload(echo_drv,[{monitor, pending_driver}]),
- ?line {ok,pending_driver,Ref} = erl_ddll:try_unload(echo_drv,[{monitor, pending_driver}]),
- ?line ok = receive _ -> false after 0 -> ok end,
- ?line Port ! {self(), close},
- ?line ok = receive {Port,closed} -> ok after 1000 -> false end,
- ?line 1 = erl_ddll:info(echo_drv, port_count),
- ?line Port2 ! {self(), close},
- ?line ok = receive {Port2,closed} -> ok after 1000 -> false end,
- ?line ok = receive {'DOWN', Ref, driver, echo_drv, unloaded} -> ok after 1000 -> false end,
- ?line test_server:timetrap_cancel(Dog),
+ Path = proplists:get_value(data_dir, Config),
+ erl_ddll:try_load(Path, echo_drv, []),
+ erl_ddll:try_load(Path, echo_drv, []),
+ Port = open_port({spawn, echo_drv}, [eof]),
+ 1 = erl_ddll:info(echo_drv, port_count),
+ Port2 = open_port({spawn, echo_drv}, [eof]),
+ 2 = erl_ddll:info(echo_drv, port_count),
+ {ok,pending_process} = erl_ddll:try_unload(echo_drv,[{monitor, pending_driver}]),
+ {ok,pending_driver,Ref} = erl_ddll:try_unload(echo_drv,[{monitor, pending_driver}]),
+ ok = receive _ -> false after 0 -> ok end,
+ Port ! {self(), close},
+ ok = receive {Port,closed} -> ok after 1000 -> false end,
+ 1 = erl_ddll:info(echo_drv, port_count),
+ Port2 ! {self(), close},
+ ok = receive {Port2,closed} -> ok after 1000 -> false end,
+ ok = receive {'DOWN', Ref, driver, echo_drv, unloaded} -> ok after 1000 -> false end,
ok.
-unload_due_to_process_exit(suite) ->
- [];
-unload_due_to_process_exit(doc) ->
- ["Check that the driver with ports is unloaded on process exit"];
+%% Check that the driver with ports is unloaded on process exit
unload_due_to_process_exit(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Path = ?config(data_dir, Config),
- ?line Parent = self(),
- ?line F3 = fun() ->
- Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}),
- receive X -> Parent ! {got,X} end
- end,
- ?line Pid = spawn(fun() ->
- receive go -> ok end,
- {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
- spawn(F3),
- receive go -> ok end,
- _Port = open_port({spawn, echo_drv}, [eof]),
- _Port2 = open_port({spawn, echo_drv}, [eof]),
- exit(banan)
- end),
- ?line Ref = erlang:monitor(process,Pid),
+ Path = proplists:get_value(data_dir, Config),
+ Parent = self(),
+ F3 = fun() ->
+ Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}),
+ receive X -> Parent ! {got,X} end
+ end,
+ Pid = spawn(fun() ->
+ receive go -> ok end,
+ {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
+ spawn(F3),
+ receive go -> ok end,
+ _Port = open_port({spawn, echo_drv}, [eof]),
+ _Port2 = open_port({spawn, echo_drv}, [eof]),
+ exit(banan)
+ end),
+ Ref = erlang:monitor(process,Pid),
Pid ! go,
- ?line {ok,Ref2} = receive
- R when is_reference(R) -> {ok,R};
- Other -> {error, Other}
- after 500 -> {error, timeout}
- end,
+ {ok,Ref2} = receive
+ R when is_reference(R) -> {ok,R};
+ Other -> {error, Other}
+ after 500 -> {error, timeout}
+ end,
Pid ! go,
- ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
- ?line ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end,
- ?line test_server:timetrap_cancel(Dog),
+ ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
+ ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end,
ok.
-no_unload_due_to_process_exit(suite) ->
- [];
-no_unload_due_to_process_exit(doc) ->
- ["Check that a driver with driver loaded in another process is not unloaded on process exit"];
+%% Check that a driver with driver loaded in another process is not unloaded on process exit
no_unload_due_to_process_exit(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Path = ?config(data_dir, Config),
- ?line Parent = self(),
- ?line F3 = fun() ->
- Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}),
- receive X -> Parent ! {got,X} end
- end,
- ?line Pid = spawn(fun() ->
- receive go -> ok end,
- {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
- spawn(F3),
- receive go -> ok end,
- _Port = open_port({spawn, echo_drv}, [eof]),
- _Port2 = open_port({spawn, echo_drv}, [eof]),
- exit(banan)
- end),
- ?line Ref = erlang:monitor(process,Pid),
+ Path = proplists:get_value(data_dir, Config),
+ Parent = self(),
+ F3 = fun() ->
+ Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}),
+ receive X -> Parent ! {got,X} end
+ end,
+ Pid = spawn(fun() ->
+ receive go -> ok end,
+ {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
+ spawn(F3),
+ receive go -> ok end,
+ _Port = open_port({spawn, echo_drv}, [eof]),
+ _Port2 = open_port({spawn, echo_drv}, [eof]),
+ exit(banan)
+ end),
+ Ref = erlang:monitor(process,Pid),
Pid ! go,
- ?line {ok,Ref2} = receive
- R when is_reference(R) -> {ok,R};
- Other -> {error, Other}
- after 500 -> {error, timeout}
- end,
- ?line {ok, already_loaded} = erl_ddll:try_load(Path, echo_drv, []),
+ {ok,Ref2} = receive
+ R when is_reference(R) -> {ok,R};
+ Other -> {error, Other}
+ after 500 -> {error, timeout}
+ end,
+ {ok, already_loaded} = erl_ddll:try_load(Path, echo_drv, []),
Pid ! go,
- ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
- ?line ok = receive X -> {error, X} after 300 -> ok end,
- ?line ok = unload_expect_fast(echo_drv,[]),
- ?line ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end,
- ?line test_server:timetrap_cancel(Dog),
+ ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
+ ok = receive X -> {error, X} after 300 -> ok end,
+ ok = unload_expect_fast(echo_drv,[]),
+ ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end,
ok.
-no_unload_due_to_process_exit_2(suite) ->
- [];
-no_unload_due_to_process_exit_2(doc) ->
- ["Check that a driver with open ports in another process is not unloaded on process exit"];
+%% Check that a driver with open ports in another process is not unloaded on process exit
no_unload_due_to_process_exit_2(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Path = ?config(data_dir, Config),
- ?line Parent = self(),
- ?line F3 = fun() ->
- Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}),
- receive X -> Parent ! {got,X} end
- end,
- ?line Pid = spawn(fun() ->
- receive go -> ok end,
- {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
- spawn(F3),
- receive go -> ok end,
- _Port = open_port({spawn, echo_drv}, [eof]),
- _Port2 = open_port({spawn, echo_drv}, [eof]),
- exit(banan)
- end),
- ?line Ref = erlang:monitor(process,Pid),
+ Path = proplists:get_value(data_dir, Config),
+ Parent = self(),
+ F3 = fun() ->
+ Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}),
+ receive X -> Parent ! {got,X} end
+ end,
+ Pid = spawn(fun() ->
+ receive go -> ok end,
+ {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
+ spawn(F3),
+ receive go -> ok end,
+ _Port = open_port({spawn, echo_drv}, [eof]),
+ _Port2 = open_port({spawn, echo_drv}, [eof]),
+ exit(banan)
+ end),
+ Ref = erlang:monitor(process,Pid),
Pid ! go,
- ?line {ok,Ref2} = receive
- R when is_reference(R) -> {ok,R};
- Other -> {error, Other}
- after 500 -> {error, timeout}
- end,
- ?line Port = open_port({spawn, echo_drv}, [eof]),
+ {ok,Ref2} = receive
+ R when is_reference(R) -> {ok,R};
+ Other -> {error, Other}
+ after 500 -> {error, timeout}
+ end,
+ Port = open_port({spawn, echo_drv}, [eof]),
Pid ! go,
- ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
- ?line ok = receive X -> {error, X} after 300 -> ok end,
- ?line erlang:port_close(Port),
- ?line ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end,
- ?line test_server:timetrap_cancel(Dog),
+ ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
+ ok = receive X -> {error, X} after 300 -> ok end,
+ erlang:port_close(Port),
+ ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end,
ok.
-unload_reload_thingie(suite) ->
- [];
-unload_reload_thingie(doc) ->
- ["Check delayed unload and reload"];
+%% Check delayed unload and reload
unload_reload_thingie(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Path = ?config(data_dir, Config),
- ?line Parent = self(),
- ?line {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
- ?line F3 = fun() ->
- Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded_only}),
- receive X -> Parent ! {got,X} end
- end,
- ?line Pid = spawn(fun() ->
- receive go -> ok end,
- _Port = open_port({spawn, echo_drv}, [eof]),
- spawn(F3),
- receive go -> ok end,
- exit(banan)
- end),
- ?line Ref = erlang:monitor(process,Pid),
+ Path = proplists:get_value(data_dir, Config),
+ Parent = self(),
+ {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
+ F3 = fun() ->
+ Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded_only}),
+ receive X -> Parent ! {got,X} end
+ end,
+ Pid = spawn(fun() ->
+ receive go -> ok end,
+ _Port = open_port({spawn, echo_drv}, [eof]),
+ spawn(F3),
+ receive go -> ok end,
+ exit(banan)
+ end),
+ Ref = erlang:monitor(process,Pid),
+ Pid ! go,
+ {ok,Ref2} = receive
+ R when is_reference(R) -> {ok,R};
+ Other -> {error, Other}
+ after 500 -> {error, timeout}
+ end,
+ {ok,pending_driver,Ref3} = erl_ddll:try_unload(echo_drv,[{monitor,pending}]),
+ Ref4 = erl_ddll:monitor(driver,{echo_drv,loaded}),
+ ok = receive {'DOWN',Ref4, driver,echo_drv,load_cancelled} -> ok after 1000 -> false end,
+ {ok,already_loaded} = erl_ddll:try_load(Path, echo_drv, []),
+ ok = receive {'UP',Ref3, driver,echo_drv,unload_cancelled} -> ok after 1000 -> false end,
Pid ! go,
- ?line {ok,Ref2} = receive
- R when is_reference(R) -> {ok,R};
- Other -> {error, Other}
- after 500 -> {error, timeout}
- end,
- ?line {ok,pending_driver,Ref3} = erl_ddll:try_unload(echo_drv,[{monitor,pending}]),
- ?line Ref4 = erl_ddll:monitor(driver,{echo_drv,loaded}),
- ?line ok = receive {'DOWN',Ref4, driver,echo_drv,load_cancelled} -> ok after 1000 -> false end,
- ?line {ok,already_loaded} = erl_ddll:try_load(Path, echo_drv, []),
- ?line ok = receive {'UP',Ref3, driver,echo_drv,unload_cancelled} -> ok after 1000 -> false end,
- ?line Pid ! go,
- ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
- ?line [{Parent,1}] = erl_ddll:info(echo_drv, processes),
- ?line 0 = erl_ddll:info(echo_drv, port_count),
- ?line ok = unload_expect_fast(echo_drv,[{monitor,pending}]),
- ?line ok = receive
- {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok
- after 300 -> error
- end,
- ?line ok = receive X -> {error, X} after 300 -> ok end,
- ?line test_server:timetrap_cancel(Dog),
+ ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
+ [{Parent,1}] = erl_ddll:info(echo_drv, processes),
+ 0 = erl_ddll:info(echo_drv, port_count),
+ ok = unload_expect_fast(echo_drv,[{monitor,pending}]),
+ ok = receive
+ {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok
+ after 300 -> error
+ end,
+ ok = receive X -> {error, X} after 300 -> ok end,
ok.
-unload_reload_thingie_2(suite) ->
- [];
-unload_reload_thingie_2(doc) ->
- ["Check delayed unload and reload"];
+%% Check delayed unload and reload
unload_reload_thingie_2(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Path = ?config(data_dir, Config),
- ?line Parent = self(),
- ?line {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
- ?line F3 = fun() ->
- Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded_only}),
- receive X -> Parent ! {got,X} end
- end,
- ?line Pid = spawn(fun() ->
- receive go -> ok end,
- _Port = open_port({spawn, echo_drv}, [eof]),
- spawn(F3),
- receive go -> ok end,
- exit(banan)
- end),
- ?line Ref = erlang:monitor(process,Pid),
+ Path = proplists:get_value(data_dir, Config),
+ Parent = self(),
+ {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
+ F3 = fun() ->
+ Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded_only}),
+ receive X -> Parent ! {got,X} end
+ end,
+ Pid = spawn(fun() ->
+ receive go -> ok end,
+ _Port = open_port({spawn, echo_drv}, [eof]),
+ spawn(F3),
+ receive go -> ok end,
+ exit(banan)
+ end),
+ Ref = erlang:monitor(process,Pid),
+ Pid ! go,
+ {ok,Ref2} = receive
+ R when is_reference(R) -> {ok,R};
+ Other -> {error, Other}
+ after 500 -> {error, timeout}
+ end,
+ {ok,pending_driver,Ref3} = erl_ddll:try_load(Path, echo_drv,
+ [{monitor,pending_driver},{reload,pending_driver}]),
+ Ref4 = erl_ddll:monitor(driver,{echo_drv,unloaded}),
Pid ! go,
- ?line {ok,Ref2} = receive
- R when is_reference(R) -> {ok,R};
- Other -> {error, Other}
- after 500 -> {error, timeout}
- end,
- ?line {ok,pending_driver,Ref3} = erl_ddll:try_load(Path,echo_drv,[{monitor,pending_driver},{reload,pending_driver}]),
- ?line Ref4 = erl_ddll:monitor(driver,{echo_drv,unloaded}),
- ?line Pid ! go,
- ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
- ?line ok = receive {'DOWN',Ref4, driver,echo_drv,unloaded} -> ok after 1000 -> false end,
- ?line ok = receive {'UP',Ref3, driver,echo_drv,loaded} -> ok after 1000 -> false end,
- ?line [{Parent,1}] = erl_ddll:info(echo_drv, processes),
- ?line 0 = erl_ddll:info(echo_drv, port_count),
- ?line ok = receive
- {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok
- after 300 -> error
- end,
- ?line ok = unload_expect_fast(echo_drv,[{monitor,pending}]),
- ?line ok = receive X -> {error, X} after 300 -> ok end,
- ?line test_server:timetrap_cancel(Dog),
+ ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
+ ok = receive {'DOWN',Ref4, driver,echo_drv,unloaded} -> ok after 1000 -> false end,
+ ok = receive {'UP',Ref3, driver,echo_drv,loaded} -> ok after 1000 -> false end,
+ [{Parent,1}] = erl_ddll:info(echo_drv, processes),
+ 0 = erl_ddll:info(echo_drv, port_count),
+ ok = receive
+ {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok
+ after 300 -> error
+ end,
+ ok = unload_expect_fast(echo_drv,[{monitor,pending}]),
+ ok = receive X -> {error, X} after 300 -> ok end,
ok.
-unload_reload_thingie_3(suite) ->
- [];
-unload_reload_thingie_3(doc) ->
- ["Check delayed unload and reload failure"];
+%% Check delayed unload and reload failure
unload_reload_thingie_3(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Path = ?config(data_dir, Config),
- ?line Parent = self(),
- ?line {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
- ?line F3 = fun() ->
- Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}),
- receive X -> Parent ! {got,X} end
- end,
- ?line Pid = spawn(fun() ->
- receive go -> ok end,
- _Port = open_port({spawn, echo_drv}, [eof]),
- spawn(F3),
- receive go -> ok end,
- exit(banan)
- end),
- ?line Ref = erlang:monitor(process,Pid),
+ Path = proplists:get_value(data_dir, Config),
+ Parent = self(),
+ {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
+ F3 = fun() ->
+ Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}),
+ receive X -> Parent ! {got,X} end
+ end,
+ Pid = spawn(fun() ->
+ receive go -> ok end,
+ _Port = open_port({spawn, echo_drv}, [eof]),
+ spawn(F3),
+ receive go -> ok end,
+ exit(banan)
+ end),
+ Ref = erlang:monitor(process,Pid),
+ Pid ! go,
+ {ok,Ref2} = receive
+ R when is_reference(R) -> {ok,R};
+ Other -> {error, Other}
+ after 500 -> {error, timeout}
+ end,
+ {ok,pending_driver,Ref3} = erl_ddll:try_load(filename:join([Path,"skrumpf"]), echo_drv,
+ [{monitor,pending_driver},{reload,pending_driver}]),
+ Ref4 = erl_ddll:monitor(driver,{echo_drv,unloaded}),
Pid ! go,
- ?line {ok,Ref2} = receive
- R when is_reference(R) -> {ok,R};
- Other -> {error, Other}
- after 500 -> {error, timeout}
- end,
- ?line {ok,pending_driver,Ref3} = erl_ddll:try_load(filename:join([Path,"skrumpf"]),echo_drv,[{monitor,pending_driver},{reload,pending_driver}]),
- ?line Ref4 = erl_ddll:monitor(driver,{echo_drv,unloaded}),
- ?line Pid ! go,
- ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
- ?line ok = receive
- {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok
- after 300 -> error
- end,
- ?line ok = receive {'DOWN',Ref4,driver,echo_drv,unloaded} -> ok after 300 -> false end,
- ?line ok = receive
- {'DOWN',Ref3, driver,echo_drv,{load_failure,_}} -> ok
- after 1000 -> false
- end,
- ?line {'EXIT',_} = (catch erl_ddll:info(echo_drv, port_count)),
- ?line {error, not_loaded} = erl_ddll:try_unload(echo_drv,[{monitor,pending}]),
- ?line ok = receive X -> {error, X} after 300 -> ok end,
- ?line test_server:timetrap_cancel(Dog),
+ ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
+ ok = receive
+ {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok
+ after 300 -> error
+ end,
+ ok = receive {'DOWN',Ref4,driver,echo_drv,unloaded} -> ok after 300 -> false end,
+ ok = receive
+ {'DOWN',Ref3, driver,echo_drv,{load_failure,_}} -> ok
+ after 1000 -> false
+ end,
+ {'EXIT',_} = (catch erl_ddll:info(echo_drv, port_count)),
+ {error, not_loaded} = erl_ddll:try_unload(echo_drv,[{monitor,pending}]),
+ ok = receive X -> {error, X} after 300 -> ok end,
ok.
-reload_pending(suite) -> [];
-reload_pending(doc) -> ["Reload a driver that is pending on a user"];
+%% Reload a driver that is pending on a user
reload_pending(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Path = ?config(data_dir, Config),
- ?line Parent = self(),
- ?line F3 = fun() ->
- Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}),
- receive X -> Parent ! {got,X} end
- end,
- ?line Pid = spawn(fun() ->
- receive go -> ok end,
- {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
- spawn(F3),
- receive go -> ok end,
- _Port = open_port({spawn, echo_drv}, [eof]),
- _Port2 = open_port({spawn, echo_drv}, [eof]),
- Parent ! opened,
- receive go -> ok end,
- exit(banan)
- end),
- ?line Ref = erlang:monitor(process,Pid),
+ Path = proplists:get_value(data_dir, Config),
+ Parent = self(),
+ F3 = fun() ->
+ Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}),
+ receive X -> Parent ! {got,X} end
+ end,
+ Pid = spawn(fun() ->
+ receive go -> ok end,
+ {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
+ spawn(F3),
+ receive go -> ok end,
+ _Port = open_port({spawn, echo_drv}, [eof]),
+ _Port2 = open_port({spawn, echo_drv}, [eof]),
+ Parent ! opened,
+ receive go -> ok end,
+ exit(banan)
+ end),
+ Ref = erlang:monitor(process,Pid),
Pid ! go,
- ?line {ok,Ref2} = receive
- R when is_reference(R) -> {ok,R};
- Other -> {error, Other}
- after 500 -> {error, timeout}
- end,
- ?line {ok, already_loaded} = erl_ddll:try_load(Path, echo_drv, []),
- ?line Port = open_port({spawn, echo_drv}, [eof]),
+ {ok,Ref2} = receive
+ R when is_reference(R) -> {ok,R};
+ Other -> {error, Other}
+ after 500 -> {error, timeout}
+ end,
+ {ok, already_loaded} = erl_ddll:try_load(Path, echo_drv, []),
+ Port = open_port({spawn, echo_drv}, [eof]),
Pid ! go,
- ?line receive opened -> ok end,
- ?line {error, pending_process} =
- erl_ddll:try_load(Path, echo_drv,
- [{reload,pending_driver},
- {monitor,pending_driver}]),
- ?line {ok, pending_process, Ref3} =
- erl_ddll:try_load(Path, echo_drv,
- [{reload,pending},
- {monitor,pending}]),
- ?line ok = receive X -> {error, X} after 300 -> ok end,
+ receive opened -> ok end,
+ {error, pending_process} =
+ erl_ddll:try_load(Path, echo_drv,
+ [{reload,pending_driver},
+ {monitor,pending_driver}]),
+ {ok, pending_process, Ref3} =
+ erl_ddll:try_load(Path, echo_drv,
+ [{reload,pending},
+ {monitor,pending}]),
+ ok = receive X -> {error, X} after 300 -> ok end,
Pid ! go,
- ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
- ?line ok = receive Y -> {error, Y} after 300 -> ok end,
- ?line erlang:port_close(Port),
- ?line ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end,
- ?line ok = receive {'UP', Ref3, driver, echo_drv, loaded} -> ok after 300 -> error end,
+ ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
+ ok = receive Y -> {error, Y} after 300 -> ok end,
+ erlang:port_close(Port),
+ ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end,
+ ok = receive {'UP', Ref3, driver, echo_drv, loaded} -> ok after 300 -> error end,
[{Parent,1}] = erl_ddll:info(echo_drv,processes),
- ?line ok = receive Z -> {error, Z} after 300 -> ok end,
- ?line test_server:timetrap_cancel(Dog),
+ ok = receive Z -> {error, Z} after 300 -> ok end,
ok.
-load_fail_init(suite) -> [];
-load_fail_init(doc) -> ["Tests failure in the init in driver struct."];
+%% Tests failure in the init in driver struct.
load_fail_init(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Path = ?config(data_dir, Config),
- ?line PathFailing = ?config(priv_dir, Config),
- ?line [_|_] = AllFailInits = filelib:wildcard("echo_drv_fail_init.*",Path),
- ?line lists:foreach(fun(Name) ->
- Src = filename:join([Path,Name]),
- Ext = filename:extension(Name),
- Dst =filename:join([PathFailing,"echo_drv"++Ext]),
- file:delete(Dst),
- {ok,_} = file:copy(Src,Dst)
- end,
- AllFailInits),
- ?line [_|_] = filelib:wildcard("echo_drv.*",PathFailing),
- ?line {error, driver_init_failed} = erl_ddll:try_load(PathFailing,
- echo_drv,
- [{monitor,pending}]),
- ?line ok = receive XX ->
- {unexpected,XX}
- after 300 ->
- ok
- end,
- ?line test_server:timetrap_cancel(Dog),
+ Path = proplists:get_value(data_dir, Config),
+ PathFailing = proplists:get_value(priv_dir, Config),
+ [_|_] = AllFailInits = filelib:wildcard("echo_drv_fail_init.*",Path),
+ lists:foreach(fun(Name) ->
+ Src = filename:join([Path,Name]),
+ Ext = filename:extension(Name),
+ Dst =filename:join([PathFailing,"echo_drv"++Ext]),
+ file:delete(Dst),
+ {ok,_} = file:copy(Src,Dst)
+ end,
+ AllFailInits),
+ [_|_] = filelib:wildcard("echo_drv.*",PathFailing),
+ {error, driver_init_failed} = erl_ddll:try_load(PathFailing,
+ echo_drv,
+ [{monitor,pending}]),
+ ok = receive XX ->
+ {unexpected,XX}
+ after 300 ->
+ ok
+ end,
ok.
-reload_pending_fail_init(suite) -> [];
-reload_pending_fail_init(doc) -> ["Reload a driver that is pending but init fails"];
+%% Reload a driver that is pending but init fails
reload_pending_fail_init(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Path = ?config(data_dir, Config),
- ?line PathFailing = ?config(priv_dir, Config),
- ?line [_|_] = AllFailInits = filelib:wildcard("echo_drv_fail_init.*",Path),
- ?line lists:foreach(fun(Name) ->
- Src = filename:join([Path,Name]),
- Ext = filename:extension(Name),
- Dst =filename:join([PathFailing,"echo_drv"++Ext]),
- file:delete(Dst),
- {ok,_} = file:copy(Src,Dst)
- end,
- AllFailInits),
- ?line [_|_] = filelib:wildcard("echo_drv.*",PathFailing),
- ?line Parent = self(),
- ?line F3 = fun() ->
- Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}),
- receive X -> Parent ! {got,X} end
- end,
- ?line Pid = spawn(fun() ->
- receive go -> ok end,
- {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
- spawn(F3),
- receive go -> ok end,
- _Port = open_port({spawn, echo_drv}, [eof]),
- _Port2 = open_port({spawn, echo_drv}, [eof]),
- Parent ! opened,
- receive go -> ok end,
- exit(banan)
- end),
- ?line Ref = erlang:monitor(process,Pid),
+ Path = proplists:get_value(data_dir, Config),
+ PathFailing = proplists:get_value(priv_dir, Config),
+ [_|_] = AllFailInits = filelib:wildcard("echo_drv_fail_init.*",Path),
+ lists:foreach(fun(Name) ->
+ Src = filename:join([Path,Name]),
+ Ext = filename:extension(Name),
+ Dst =filename:join([PathFailing,"echo_drv"++Ext]),
+ file:delete(Dst),
+ {ok,_} = file:copy(Src,Dst)
+ end,
+ AllFailInits),
+ [_|_] = filelib:wildcard("echo_drv.*",PathFailing),
+ Parent = self(),
+ F3 = fun() ->
+ Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}),
+ receive X -> Parent ! {got,X} end
+ end,
+ Pid = spawn(fun() ->
+ receive go -> ok end,
+ {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
+ spawn(F3),
+ receive go -> ok end,
+ _Port = open_port({spawn, echo_drv}, [eof]),
+ _Port2 = open_port({spawn, echo_drv}, [eof]),
+ Parent ! opened,
+ receive go -> ok end,
+ exit(banan)
+ end),
+ Ref = erlang:monitor(process,Pid),
Pid ! go,
- ?line {ok,Ref2} = receive
- R when is_reference(R) -> {ok,R};
- Other -> {error, Other}
- after 500 -> {error, timeout}
- end,
- ?line {ok, already_loaded} = erl_ddll:try_load(Path, echo_drv, []),
- ?line Port = open_port({spawn, echo_drv}, [eof]),
+ {ok,Ref2} = receive
+ R when is_reference(R) -> {ok,R};
+ Other -> {error, Other}
+ after 500 -> {error, timeout}
+ end,
+ {ok, already_loaded} = erl_ddll:try_load(Path, echo_drv, []),
+ Port = open_port({spawn, echo_drv}, [eof]),
Pid ! go,
- ?line receive opened -> ok end,
- ?line {ok, pending_process, Ref3} =
- erl_ddll:try_load(PathFailing, echo_drv,
- [{reload,pending},
- {monitor,pending}]),
- ?line ok = receive X -> {error, X} after 300 -> ok end,
+ receive opened -> ok end,
+ {ok, pending_process, Ref3} =
+ erl_ddll:try_load(PathFailing, echo_drv,
+ [{reload,pending},
+ {monitor,pending}]),
+ ok = receive X -> {error, X} after 300 -> ok end,
Pid ! go,
- ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
- ?line ok = receive Y -> {error, Y} after 300 -> ok end,
- ?line erlang:port_close(Port),
- ?line ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end,
- ?line ok = receive {'DOWN', Ref3, driver, echo_drv, {load_failure,driver_init_failed}} -> ok after 300 -> error end,
- ?line {'EXIT',{badarg,_}} = (catch erl_ddll:info(echo_drv,processes)),
-
- ?line ok = receive Z -> {error, Z} after 300 -> ok end,
- ?line test_server:timetrap_cancel(Dog),
+ ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
+ ok = receive Y -> {error, Y} after 300 -> ok end,
+ erlang:port_close(Port),
+ ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end,
+ ok = receive {'DOWN', Ref3, driver, echo_drv, {load_failure,driver_init_failed}} -> ok after 300 -> error end,
+ {'EXIT',{badarg,_}} = (catch erl_ddll:info(echo_drv,processes)),
+
+ ok = receive Z -> {error, Z} after 300 -> ok end,
ok.
-reload_pending_kill(suite) -> [];
-reload_pending_kill(doc) -> ["Reload a driver with kill_ports option "
- "that is pending on a user"];
+%% Reload a driver with kill_ports option that is pending on a user
reload_pending_kill(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line OldFlag = process_flag(trap_exit,true),
- ?line Path = ?config(data_dir, Config),
- ?line Parent = self(),
- ?line F3 = fun() ->
- Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}),
- receive X -> Parent ! {got,X} end
- end,
- ?line Pid = spawn(fun() ->
- process_flag(trap_exit,true),
- receive go -> ok end,
- {ok, loaded} = erl_ddll:try_load(Path, echo_drv, [{driver_options,[kill_ports]}]),
- spawn(F3),
- receive go -> ok end,
- Port = open_port({spawn, echo_drv}, [eof]),
- Port2 = open_port({spawn, echo_drv}, [eof]),
- Parent ! opened,
- receive go -> ok end,
- receive
- {'EXIT', Port2, driver_unloaded} ->
- Parent ! first_exit
- end,
- receive
- {'EXIT', Port, driver_unloaded} ->
- Parent ! second_exit
- end,
- receive go -> ok end,
- exit(banan)
- end),
- ?line Ref = erlang:monitor(process,Pid),
+ OldFlag = process_flag(trap_exit,true),
+ Path = proplists:get_value(data_dir, Config),
+ Parent = self(),
+ F3 = fun() ->
+ Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}),
+ receive X -> Parent ! {got,X} end
+ end,
+ Pid = spawn(fun() ->
+ process_flag(trap_exit,true),
+ receive go -> ok end,
+ {ok, loaded} = erl_ddll:try_load(Path, echo_drv, [{driver_options,[kill_ports]}]),
+ spawn(F3),
+ receive go -> ok end,
+ Port = open_port({spawn, echo_drv}, [eof]),
+ Port2 = open_port({spawn, echo_drv}, [eof]),
+ Parent ! opened,
+ receive go -> ok end,
+ receive
+ {'EXIT', Port2, driver_unloaded} ->
+ Parent ! first_exit
+ end,
+ receive
+ {'EXIT', Port, driver_unloaded} ->
+ Parent ! second_exit
+ end,
+ receive go -> ok end,
+ exit(banan)
+ end),
+ Ref = erlang:monitor(process,Pid),
Pid ! go,
- ?line {ok,Ref2} = receive
- R when is_reference(R) -> {ok,R};
- Other -> {error, Other}
- after 500 -> {error, timeout}
- end,
- ?line {ok, already_loaded} = erl_ddll:try_load(Path, echo_drv, [{driver_options,[kill_ports]}]),
- ?line {error,inconsistent} = erl_ddll:try_load(Path, echo_drv, []),
- ?line Port = open_port({spawn, echo_drv}, [eof]),
+ {ok,Ref2} = receive
+ R when is_reference(R) -> {ok,R};
+ Other -> {error, Other}
+ after 500 -> {error, timeout}
+ end,
+ {ok, already_loaded} = erl_ddll:try_load(Path, echo_drv, [{driver_options,[kill_ports]}]),
+ {error,inconsistent} = erl_ddll:try_load(Path, echo_drv, []),
+ Port = open_port({spawn, echo_drv}, [eof]),
Pid ! go,
- ?line receive opened -> ok end,
- ?line {error, pending_process} =
- erl_ddll:try_load(Path, echo_drv,
- [{driver_options,[kill_ports]},
- {reload,pending_driver},
- {monitor,pending_driver}]),
- ?line {ok, pending_process, Ref3} =
- erl_ddll:try_load(Path, echo_drv,
- [{driver_options,[kill_ports]},
- {reload,pending},
- {monitor,pending}]),
- ?line ok = receive
- {'EXIT', Port, driver_unloaded} ->
- ok
- after 300 -> error
- end,
+ receive opened -> ok end,
+ {error, pending_process} =
+ erl_ddll:try_load(Path, echo_drv,
+ [{driver_options,[kill_ports]},
+ {reload,pending_driver},
+ {monitor,pending_driver}]),
+ {ok, pending_process, Ref3} =
+ erl_ddll:try_load(Path, echo_drv,
+ [{driver_options,[kill_ports]},
+ {reload,pending},
+ {monitor,pending}]),
+ ok = receive
+ {'EXIT', Port, driver_unloaded} ->
+ ok
+ after 300 -> error
+ end,
Pid ! go,
- ?line ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end,
- ?line ok = receive {'UP', Ref3, driver, echo_drv, loaded} -> ok after 300 -> error end,
- ?line [_,_] = erl_ddll:info(echo_drv,processes),
- ?line ok = receive first_exit -> ok after 300 -> error end,
- ?line ok = receive second_exit -> ok after 300 -> error end,
- ?line 0 = erl_ddll:info(echo_drv,port_count),
- ?line ok = receive X -> {error, X} after 300 -> ok end,
+ ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end,
+ ok = receive {'UP', Ref3, driver, echo_drv, loaded} -> ok after 300 -> error end,
+ [_,_] = erl_ddll:info(echo_drv,processes),
+ ok = receive first_exit -> ok after 300 -> error end,
+ ok = receive second_exit -> ok after 300 -> error end,
+ 0 = erl_ddll:info(echo_drv,port_count),
+ ok = receive X -> {error, X} after 300 -> ok end,
Pid ! go,
- ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
- ?line ok = receive Y -> {error, Y} after 300 -> ok end,
- ?line Port2 = open_port({spawn, echo_drv}, [eof]),
- ?line true = is_port(Port2),
+ ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
+ ok = receive Y -> {error, Y} after 300 -> ok end,
+ Port2 = open_port({spawn, echo_drv}, [eof]),
+ true = is_port(Port2),
[{Parent,1}] = erl_ddll:info(echo_drv,processes),
- ?line 1 = erl_ddll:info(echo_drv,port_count),
- ?line erlang:port_close(Port2),
- ?line ok = receive {'EXIT', Port2, normal} -> ok after 300 -> error end,
- ?line 0 = erl_ddll:info(echo_drv,port_count),
- ?line [{Parent,1}] = erl_ddll:info(echo_drv,processes),
- ?line Port3 = open_port({spawn, echo_drv}, [eof]),
- ?line {ok, pending_driver, Ref4} =
- erl_ddll:try_unload(echo_drv,[{monitor,pending_driver}]),
- ?line ok = receive
- {'EXIT', Port3, driver_unloaded} ->
- ok
- after 300 -> error
- end,
- ?line ok = receive {'DOWN', Ref4, driver, echo_drv, unloaded} -> ok after 300 -> error end,
+ 1 = erl_ddll:info(echo_drv,port_count),
+ erlang:port_close(Port2),
+ ok = receive {'EXIT', Port2, normal} -> ok after 300 -> error end,
+ 0 = erl_ddll:info(echo_drv,port_count),
+ [{Parent,1}] = erl_ddll:info(echo_drv,processes),
+ Port3 = open_port({spawn, echo_drv}, [eof]),
+ {ok, pending_driver, Ref4} =
+ erl_ddll:try_unload(echo_drv,[{monitor,pending_driver}]),
+ ok = receive
+ {'EXIT', Port3, driver_unloaded} ->
+ ok
+ after 300 -> error
+ end,
+ ok = receive {'DOWN', Ref4, driver, echo_drv, unloaded} -> ok after 300 -> error end,
io:format("Port = ~w, Port2 = ~w, Port3 = ~w~n",[Port,Port2,Port3]),
- ?line ok = receive Z -> {error, Z} after 300 -> ok end,
- ?line process_flag(trap_exit,OldFlag),
- ?line test_server:timetrap_cancel(Dog),
+ ok = receive Z -> {error, Z} after 300 -> ok end,
+ process_flag(trap_exit,OldFlag),
ok.
-more_error_codes(suite) ->
- [];
-more_error_codes(doc) ->
- ["Some more error code checking"];
+%% Some more error code checking
more_error_codes(Config) when is_list(Config) ->
- ?line {error,Err} = erl_ddll:try_load("./echo_dr",echo_dr,[]),
- ?line true = is_list(erl_ddll:format_error(Err)),
- ?line true = is_list(erl_ddll:format_error(not_loaded)),
+ {error,Err} = erl_ddll:try_load("./echo_dr",echo_dr,[]),
+ true = is_list(erl_ddll:format_error(Err)),
+ true = is_list(erl_ddll:format_error(not_loaded)),
ok.
-forced_port_killing(suite) ->
- [];
-forced_port_killing(doc) ->
- ["Check kill_ports option to try_unload "];
+%% Check kill_ports option to try_unload
forced_port_killing(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Path = ?config(data_dir, Config),
- ?line OldFlag=process_flag(trap_exit,true),
- ?line Parent = self(),
- ?line F3 = fun() ->
- Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}),
- receive X -> Parent ! {got,X} end
- end,
- ?line {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
- ?line spawn(F3),
- ?line {ok,Ref2} = receive
- R when is_reference(R) -> {ok,R};
- Other -> {error, Other}
- after 500 -> {error, timeout}
- end,
- ?line Port = open_port({spawn, echo_drv}, [eof]),
- ?line Port2 = open_port({spawn, echo_drv}, [eof]),
- ?line {ok, pending_driver, Ref1} =
- erl_ddll:try_unload(echo_drv,[{monitor,pending_driver},kill_ports]),
- ?line ok = receive
- {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok
- after 300 -> error
- end,
- ?line ok = receive {'EXIT',Port,driver_unloaded} -> ok after 300 -> false end,
- ?line ok = receive {'EXIT',Port2,driver_unloaded} -> ok after 300 -> false end,
- ?line ok = receive {'DOWN',Ref1, driver, echo_drv, unloaded} -> ok after 300 -> false end,
- ?line process_flag(trap_exit,OldFlag),
- ?line ok = receive X -> {error, X} after 300 -> ok end,
- ?line test_server:timetrap_cancel(Dog),
+ Path = proplists:get_value(data_dir, Config),
+ OldFlag=process_flag(trap_exit,true),
+ Parent = self(),
+ F3 = fun() ->
+ Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}),
+ receive X -> Parent ! {got,X} end
+ end,
+ {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
+ spawn(F3),
+ {ok,Ref2} = receive
+ R when is_reference(R) -> {ok,R};
+ Other -> {error, Other}
+ after 500 -> {error, timeout}
+ end,
+ Port = open_port({spawn, echo_drv}, [eof]),
+ Port2 = open_port({spawn, echo_drv}, [eof]),
+ {ok, pending_driver, Ref1} =
+ erl_ddll:try_unload(echo_drv,[{monitor,pending_driver},kill_ports]),
+ ok = receive
+ {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok
+ after 300 -> error
+ end,
+ ok = receive {'EXIT',Port,driver_unloaded} -> ok after 300 -> false end,
+ ok = receive {'EXIT',Port2,driver_unloaded} -> ok after 300 -> false end,
+ ok = receive {'DOWN',Ref1, driver, echo_drv, unloaded} -> ok after 300 -> false end,
+ process_flag(trap_exit,OldFlag),
+ ok = receive X -> {error, X} after 300 -> ok end,
ok.
-no_trap_exit_and_kill_ports(suite) ->
- [];
-no_trap_exit_and_kill_ports(doc) ->
- ["Check delayed unload and reload with no trap_exit"];
+%% Check delayed unload and reload with no trap_exit
no_trap_exit_and_kill_ports(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Path = ?config(data_dir, Config),
- ?line Parent = self(),
- ?line OldFlag=process_flag(trap_exit,true),
- ?line F3 = fun() ->
- Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}),
- receive X -> Parent ! {got,X} end
- end,
- ?line Pid = spawn(fun() ->
- process_flag(trap_exit,false),
- receive go -> ok end,
- {ok, loaded} = erl_ddll:try_load(Path, echo_drv,
- [{driver_options,[kill_ports]}]),
- spawn(F3),
- receive go -> ok end,
- _Port = open_port({spawn, echo_drv}, [eof]),
- _Port2 = open_port({spawn, echo_drv}, [eof]),
- exit(banan)
- end),
- ?line Ref = erlang:monitor(process,Pid),
+ Path = proplists:get_value(data_dir, Config),
+ Parent = self(),
+ OldFlag=process_flag(trap_exit,true),
+ F3 = fun() ->
+ Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}),
+ receive X -> Parent ! {got,X} end
+ end,
+ Pid = spawn(fun() ->
+ process_flag(trap_exit,false),
+ receive go -> ok end,
+ {ok, loaded} = erl_ddll:try_load(Path, echo_drv,
+ [{driver_options,[kill_ports]}]),
+ spawn(F3),
+ receive go -> ok end,
+ _Port = open_port({spawn, echo_drv}, [eof]),
+ _Port2 = open_port({spawn, echo_drv}, [eof]),
+ exit(banan)
+ end),
+ Ref = erlang:monitor(process,Pid),
Pid ! go,
- ?line {ok,Ref2} = receive
- R when is_reference(R) -> {ok,R};
- Other -> {error, Other}
- after 500 -> {error, timeout}
- end,
- ?line {error, inconsistent} = erl_ddll:try_load(Path, echo_drv, []),
- ?line MyPort = open_port({spawn, echo_drv}, [eof]),
+ {ok,Ref2} = receive
+ R when is_reference(R) -> {ok,R};
+ Other -> {error, Other}
+ after 500 -> {error, timeout}
+ end,
+ {error, inconsistent} = erl_ddll:try_load(Path, echo_drv, []),
+ MyPort = open_port({spawn, echo_drv}, [eof]),
Pid ! go,
- ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
- ?line ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end,
- ?line ok = receive {'EXIT',MyPort,driver_unloaded} -> ok after 300 -> error end,
- ?line process_flag(trap_exit,OldFlag),
- ?line test_server:timetrap_cancel(Dog),
+ ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
+ ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end,
+ ok = receive {'EXIT',MyPort,driver_unloaded} -> ok after 300 -> error end,
+ process_flag(trap_exit,OldFlag),
ok.
-monitor_demonitor(suite) ->
- [];
-monitor_demonitor(doc) ->
- ["Check monitor and demonitor of drivers"];
+%% Check monitor and demonitor of drivers
monitor_demonitor(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Path = ?config(data_dir, Config),
- ?line erl_ddll:try_load(Path, echo_drv, []),
- ?line Ref = erl_ddll:monitor(driver,{echo_drv,unloaded}),
- ?line Self = self(),
- ?line [{Self,1}] = erl_ddll:info(echo_drv,awaiting_unload),
- ?line true = erl_ddll:demonitor(Ref),
- ?line [] = erl_ddll:info(echo_drv,awaiting_unload),
- ?line erl_ddll:try_unload(echo_drv,[]),
- ?line ok = receive _ -> error after 300 -> ok end,
- ?line test_server:timetrap_cancel(Dog),
+ Path = proplists:get_value(data_dir, Config),
+ erl_ddll:try_load(Path, echo_drv, []),
+ Ref = erl_ddll:monitor(driver,{echo_drv,unloaded}),
+ Self = self(),
+ [{Self,1}] = erl_ddll:info(echo_drv,awaiting_unload),
+ true = erl_ddll:demonitor(Ref),
+ [] = erl_ddll:info(echo_drv,awaiting_unload),
+ erl_ddll:try_unload(echo_drv,[]),
+ ok = receive _ -> error after 300 -> ok end,
ok.
-monitor_demonitor_load(suite) ->
- [];
-monitor_demonitor_load(doc) ->
- ["Check monitor/demonitor of driver loading"];
+%% Check monitor/demonitor of driver loading
monitor_demonitor_load(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Path = ?config(data_dir, Config),
- ?line {ok,loaded} = erl_ddll:try_load(Path, echo_drv, []),
- ?line Port = open_port({spawn, echo_drv}, [eof]),
- ?line Ref = erl_ddll:monitor(driver,{echo_drv,loaded}),
- ?line ok = receive {'UP',Ref,driver,echo_drv,loaded} -> ok after 500 -> error end,
- ?line {ok, pending_driver} = erl_ddll:try_unload(echo_drv,[]),
- ?line Ref2 = erl_ddll:monitor(driver,{echo_drv,loaded}),
- ?line ok = receive {'DOWN',Ref2,driver,echo_drv,load_cancelled} -> ok after 0 -> error end,
- ?line {ok,already_loaded} = erl_ddll:try_load(Path, echo_drv, []),
- ?line {ok, pending_driver} =
- erl_ddll:try_load(Path, echo_drv, [{reload,pending_driver}]),
- ?line Ref3 = erl_ddll:monitor(driver,{echo_drv,loaded}),
- ?line Ref4 = erl_ddll:monitor(driver,{echo_drv,unloaded}),
- ?line ok = receive _ -> error after 300 -> ok end,
- ?line Self = self(),
- ?line [{Self,1}] = erl_ddll:info(echo_drv,awaiting_load),
- ?line true = erl_ddll:demonitor(Ref3),
- ?line [] = erl_ddll:info(echo_drv,awaiting_load),
- ?line erlang:port_close(Port),
- ?line ok = receive {'DOWN',Ref4,driver,echo_drv,unloaded} -> ok after 300 -> error end,
- ?line ok = receive _ -> error after 300 -> ok end,
- ?line ok = unload_expect_fast(echo_drv,[]),
- ?line test_server:timetrap_cancel(Dog),
+ Path = proplists:get_value(data_dir, Config),
+ {ok,loaded} = erl_ddll:try_load(Path, echo_drv, []),
+ Port = open_port({spawn, echo_drv}, [eof]),
+ Ref = erl_ddll:monitor(driver,{echo_drv,loaded}),
+ ok = receive {'UP',Ref,driver,echo_drv,loaded} -> ok after 500 -> error end,
+ {ok, pending_driver} = erl_ddll:try_unload(echo_drv,[]),
+ Ref2 = erl_ddll:monitor(driver,{echo_drv,loaded}),
+ ok = receive {'DOWN',Ref2,driver,echo_drv,load_cancelled} -> ok after 0 -> error end,
+ {ok,already_loaded} = erl_ddll:try_load(Path, echo_drv, []),
+ {ok, pending_driver} =
+ erl_ddll:try_load(Path, echo_drv, [{reload,pending_driver}]),
+ Ref3 = erl_ddll:monitor(driver,{echo_drv,loaded}),
+ Ref4 = erl_ddll:monitor(driver,{echo_drv,unloaded}),
+ ok = receive _ -> error after 300 -> ok end,
+ Self = self(),
+ [{Self,1}] = erl_ddll:info(echo_drv,awaiting_load),
+ true = erl_ddll:demonitor(Ref3),
+ [] = erl_ddll:info(echo_drv,awaiting_load),
+ erlang:port_close(Port),
+ ok = receive {'DOWN',Ref4,driver,echo_drv,unloaded} -> ok after 300 -> error end,
+ ok = receive _ -> error after 300 -> ok end,
+ ok = unload_expect_fast(echo_drv,[]),
ok.
-new_interface(suite) ->
- [];
-new_interface(doc) ->
- ["Test the new load/unload/reload interface"];
+%% Test the new load/unload/reload interface
new_interface(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Path = ?config(data_dir, Config),
+ Path = proplists:get_value(data_dir, Config),
% Typical scenario
- ?line ok = erl_ddll:load(Path, echo_drv),
- ?line Port = open_port({spawn, echo_drv}, [eof]),
- ?line ok = erl_ddll:unload(echo_drv),
- ?line Port ! {self(), {command, "text"}},
- ?line ok = receive
- {Port, {data, "text"}} -> ok;
- _ -> error
- after
- 1000 -> error
- end,
- ?line Ref = erl_ddll:monitor(driver,{echo_drv,unloaded}),
- ?line ok = receive X -> {error, X} after 300 -> ok end,
- ?line erlang:port_close(Port),
- ?line ok = receive {'DOWN', Ref, driver, echo_drv, unloaded} -> ok after 300 -> error end,
+ ok = erl_ddll:load(Path, echo_drv),
+ Port = open_port({spawn, echo_drv}, [eof]),
+ ok = erl_ddll:unload(echo_drv),
+ Port ! {self(), {command, "text"}},
+ ok = receive
+ {Port, {data, "text"}} -> ok;
+ _ -> error
+ after
+ 1000 -> error
+ end,
+ Ref = erl_ddll:monitor(driver,{echo_drv,unloaded}),
+ ok = receive X -> {error, X} after 300 -> ok end,
+ erlang:port_close(Port),
+ ok = receive {'DOWN', Ref, driver, echo_drv, unloaded} -> ok after 300 -> error end,
% More than one user
- ?line ok = erl_ddll:load(Path, echo_drv),
- ?line Ref2 = erl_ddll:monitor(driver,{echo_drv,unloaded}),
- ?line ok = erl_ddll:load(Path, echo_drv),
- ?line ok = erl_ddll:load(Path, echo_drv),
- ?line Port2 = open_port({spawn, echo_drv}, [eof]),
- ?line ok = erl_ddll:unload(echo_drv),
- ?line Port2 ! {self(), {command, "text"}},
- ?line ok = receive
- {Port2, {data, "text"}} -> ok;
- _ -> error
- after
- 1000 -> error
- end,
- ?line ok = erl_ddll:unload(echo_drv),
- ?line Port2 ! {self(), {command, "text"}},
- ?line ok = receive
- {Port2, {data, "text"}} -> ok;
- _ -> error
- after
- 1000 -> error
- end,
- ?line ok = erl_ddll:unload(echo_drv),
- ?line Port2 ! {self(), {command, "text"}},
- ?line ok = receive
- {Port2, {data, "text"}} -> ok;
- _ -> error
- after
- 1000 -> error
- end,
- ?line ok = receive X2 -> {error, X2} after 300 -> ok end,
- ?line ok = erl_ddll:load(Path, echo_drv),
- ?line ok = receive {'UP', Ref2, driver, echo_drv, unload_cancelled} -> ok after 300 -> error end,
- ?line Ref3 = erl_ddll:monitor(driver,{echo_drv,unloaded_only}),
- ?line erlang:port_close(Port2),
- ?line ok = receive X3 -> {error, X3} after 300 -> ok end,
- ?line ok = erl_ddll:unload(echo_drv),
- ?line ok = receive {'DOWN', Ref3, driver, echo_drv, unloaded} -> ok after 300 -> error end,
- ?line test_server:timetrap_cancel(Dog),
+ ok = erl_ddll:load(Path, echo_drv),
+ Ref2 = erl_ddll:monitor(driver,{echo_drv,unloaded}),
+ ok = erl_ddll:load(Path, echo_drv),
+ ok = erl_ddll:load(Path, echo_drv),
+ Port2 = open_port({spawn, echo_drv}, [eof]),
+ ok = erl_ddll:unload(echo_drv),
+ Port2 ! {self(), {command, "text"}},
+ ok = receive
+ {Port2, {data, "text"}} -> ok;
+ _ -> error
+ after
+ 1000 -> error
+ end,
+ ok = erl_ddll:unload(echo_drv),
+ Port2 ! {self(), {command, "text"}},
+ ok = receive
+ {Port2, {data, "text"}} -> ok;
+ _ -> error
+ after
+ 1000 -> error
+ end,
+ ok = erl_ddll:unload(echo_drv),
+ Port2 ! {self(), {command, "text"}},
+ ok = receive
+ {Port2, {data, "text"}} -> ok;
+ _ -> error
+ after
+ 1000 -> error
+ end,
+ ok = receive X2 -> {error, X2} after 300 -> ok end,
+ ok = erl_ddll:load(Path, echo_drv),
+ ok = receive {'UP', Ref2, driver, echo_drv, unload_cancelled} -> ok after 300 -> error end,
+ Ref3 = erl_ddll:monitor(driver,{echo_drv,unloaded_only}),
+ erlang:port_close(Port2),
+ ok = receive X3 -> {error, X3} after 300 -> ok end,
+ ok = erl_ddll:unload(echo_drv),
+ ok = receive {'DOWN', Ref3, driver, echo_drv, unloaded} -> ok after 300 -> error end,
ok.
-
-
+
+
ddll_test(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Path = ?config(data_dir, Config),
+ Path = proplists:get_value(data_dir, Config),
- %?line {error,{already_started,ErlDdllPid}} = erl_ddll:start(),
- %?line ErlDdllPid = whereis(ddll_server),
+ %{error,{already_started,ErlDdllPid}} = erl_ddll:start(),
+ %ErlDdllPid = whereis(ddll_server),
%% Load the echo driver and verify that it was loaded.
{ok,L1,L2}=load_echo_driver(Path),
%% Verify that the driver works.
- ?line Port = open_port({spawn, echo_drv}, [eof]),
- ?line {hej, "hopp",4711,123445567436543653} =
- erlang:port_call(Port,{hej, "hopp",4711,123445567436543653}),
- ?line {hej, "hopp",4711,123445567436543653} =
- erlang:port_call(Port,47,{hej, "hopp",4711,123445567436543653}),
- ?line Port ! {self(), {command, "text"}},
- ?line 1 = receive
- {Port, {data, "text"}} -> 1;
- _Other -> 2
- after
- 1000 -> 2
- end,
- ?line Port ! {self(), close},
- ?line receive {Port, closed} -> ok end,
-
-%% %% Unload the driver and verify that it was unloaded.
- ok = unload_echo_driver(L1,L2),
-
-%% %?line {error, {already_started, _}} = erl_ddll:start(),
-
- ?line test_server:timetrap_cancel(Dog),
+ Port = open_port({spawn, echo_drv}, [eof]),
+ {hej, "hopp",4711,123445567436543653} =
+ erlang:port_call(Port,{hej, "hopp",4711,123445567436543653}),
+ {hej, "hopp",4711,123445567436543653} =
+ erlang:port_call(Port,47,{hej, "hopp",4711,123445567436543653}),
+ Port ! {self(), {command, "text"}},
+ 1 = receive
+ {Port, {data, "text"}} -> 1;
+ _Other -> 2
+ after
+ 1000 -> 2
+ end,
+ Port ! {self(), close},
+ receive {Port, closed} -> ok end,
+
+ %% %% Unload the driver and verify that it was unloaded.
+ ok = unload_echo_driver(L1,L2),
+
+ %% %{error, {already_started, _}} = erl_ddll:start(),
ok.
%% Tests errors having to do with bad drivers.
errors(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Path = ?config(data_dir, Config),
+ Path = proplists:get_value(data_dir, Config),
- ?line {ok, L1} = erl_ddll:loaded_drivers(),
+ {ok, L1} = erl_ddll:loaded_drivers(),
- ?line {error, {open_error, _}} = erl_ddll:load_driver(Path, bad_name),
- ?line {error, driver_init_failed} = erl_ddll:load_driver(Path, initfail_drv),
- ?line {error, bad_driver_name} = erl_ddll:load_driver(Path, wrongname_drv),
+ {error, {open_error, _}} = erl_ddll:load_driver(Path, bad_name),
+ {error, driver_init_failed} = erl_ddll:load_driver(Path, initfail_drv),
+ {error, bad_driver_name} = erl_ddll:load_driver(Path, wrongname_drv),
%% We assume that there is a statically linked driver named "ddll":
- ?line {error, linked_in_driver} = erl_ddll:unload_driver(efile),
- ?line {error, not_loaded} = erl_ddll:unload_driver("__pucko_driver__"),
-
+ {error, linked_in_driver} = erl_ddll:unload_driver(efile),
+ {error, not_loaded} = erl_ddll:unload_driver("__pucko_driver__"),
+
case os:type() of
- {unix, _} ->
- ?line {error, no_driver_init} =
- erl_ddll:load_driver(Path, noinit_drv);
- _ ->
- ok
+ {unix, _} ->
+ {error, no_driver_init} =
+ erl_ddll:load_driver(Path, noinit_drv);
+ _ ->
+ ok
end,
- ?line {ok, L1} = erl_ddll:loaded_drivers(),
-
- ?line test_server:timetrap_cancel(Dog),
+ {ok, L1} = erl_ddll:loaded_drivers(),
ok.
-reference_count(doc) ->
- ["Check that drivers are unloaded when their reference count ",
- "reaches zero, and that they cannot be unloaded while ",
- "they are still referenced."];
+%% Check that drivers are unloaded when their reference count
+%% reaches zero, and that they cannot be unloaded while
+%% they are still referenced.
reference_count(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Path = ?config(data_dir, Config),
+ Path = proplists:get_value(data_dir, Config),
%% Spawn a process that loads the driver (and holds a reference
%% to it).
Pid1=spawn_link(?MODULE, echo_loader, [Path, self()]),
receive
- {Pid1, echo_loaded} -> ok
- after 2000 -> test_server:fail("echo_loader failed to start.")
+ {Pid1, echo_loaded} -> ok
+ after 2000 -> ct:fail("echo_loader failed to start.")
end,
Pid1 ! {self(), die},
- ?line test_server:sleep(200), % Give time to unload.
+ test_server:sleep(200), % Give time to unload.
% Verify that the driver was automaticly unloaded when the
% process died.
- ?line {error, not_loaded}=erl_ddll:unload_driver(echo_drv),
-
- ?line test_server:timetrap_cancel(Dog),
+ {error, not_loaded}=erl_ddll:unload_driver(echo_drv),
ok.
% Loads the echo driver, send msg to started, sits and waits to
% get a signal to die, then unloads the driver and terminates.
echo_loader(Path, Starter) ->
- ?line {ok, L1, L2}=load_echo_driver(Path),
- ?line Starter ! {self(), echo_loaded},
+ {ok, L1, L2}=load_echo_driver(Path),
+ Starter ! {self(), echo_loaded},
receive
- {Starter, die} ->
- ?line unload_echo_driver(L1,L2)
+ {Starter, die} ->
+ unload_echo_driver(L1,L2)
end.
% Loads the echo driver, send msg to started, sits and waits to
% get a signal to die, then unloads the driver and terminates.
nice_echo_loader(Path, Starter) ->
- ?line {ok, L1, L2}=load_nice_echo_driver(Path),
- ?line Starter ! {self(), echo_loaded},
+ {ok, L1, L2}=load_nice_echo_driver(Path),
+ Starter ! {self(), echo_loaded},
receive
- {Starter, die} ->
- ?line unload_echo_driver(L1,L2)
+ {Starter, die} ->
+ unload_echo_driver(L1,L2)
end.
-kill_port(doc) ->
- ["Test that a port that uses a driver is killed when the ",
- "process that loaded the driver dies."];
+%% Test that a port that uses a driver is killed when the
+%% process that loaded the driver dies.
kill_port(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Path = ?config(data_dir, Config),
+ Path = proplists:get_value(data_dir, Config),
%% Spawn a process that loads the driver (and holds a reference
%% to it).
- ?line Pid1=spawn(?MODULE, echo_loader, [Path, self()]),
- ?line receive
- {Pid1, echo_loaded} ->
- ok
- after 3000 ->
- ?line exit(Pid1, kill),
- ?line test_server:fail("echo_loader failed to start.")
- end,
+ Pid1=spawn(?MODULE, echo_loader, [Path, self()]),
+ receive
+ {Pid1, echo_loaded} ->
+ ok
+ after 3000 ->
+ exit(Pid1, kill),
+ ct:fail("echo_loader failed to start.")
+ end,
% Spawn off a port that uses the driver.
- ?line Port = open_port({spawn, echo_drv}, [eof]),
+ Port = open_port({spawn, echo_drv}, [eof]),
% Kill the process / unload the driver.
- ?line process_flag(trap_exit, true),
- ?line exit(Pid1, kill),
- ?line test_server:sleep(200), % Give some time to unload.
- ?line {error, not_loaded} = erl_ddll:unload_driver(echo_drv),
+ process_flag(trap_exit, true),
+ exit(Pid1, kill),
+ test_server:sleep(200), % Give some time to unload.
+ {error, not_loaded} = erl_ddll:unload_driver(echo_drv),
% See if the port is killed.
receive
- {'EXIT', Port, Reason} ->
- io:format("Port exited with reason ~w", [Reason])
+ {'EXIT', Port, Reason} ->
+ io:format("Port exited with reason ~w", [Reason])
after 5000 ->
- ?line test_server:fail("Echo port did not terminate.")
+ ct:fail("Echo port did not terminate.")
end,
-
- %% Cleanup and exit.
- ?line test_server:timetrap_cancel(Dog),
ok.
-dont_kill_port(doc) ->
- ["Test that a port that uses a driver is not killed when the ",
- "process that loaded the driver dies and it's nicely opened."];
+%% Test that a port that uses a driver is not killed when the
+%% process that loaded the driver dies and it's nicely opened.
dont_kill_port(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Path = ?config(data_dir, Config),
+ Path = proplists:get_value(data_dir, Config),
%% Spawn a process that loads the driver (and holds a reference
%% to it).
- ?line Pid1=spawn(?MODULE, nice_echo_loader, [Path, self()]),
- ?line receive
- {Pid1, echo_loaded} ->
- ok
- after 3000 ->
- ?line exit(Pid1, kill),
- ?line test_server:fail("echo_loader failed to start.")
- end,
+ Pid1=spawn(?MODULE, nice_echo_loader, [Path, self()]),
+ receive
+ {Pid1, echo_loaded} ->
+ ok
+ after 3000 ->
+ exit(Pid1, kill),
+ ct:fail("echo_loader failed to start.")
+ end,
% Spawn off a port that uses the driver.
- ?line Port = open_port({spawn, echo_drv}, [eof]),
+ Port = open_port({spawn, echo_drv}, [eof]),
% Kill the process / unload the driver.
- ?line process_flag(trap_exit, true),
- ?line exit(Pid1, kill),
- ?line test_server:sleep(200), % Give some time to unload.
- ?line {hej, "hopp",4711,123445567436543653} =
- erlang:port_call(Port,{hej, "hopp",4711,123445567436543653}),
- ?line [] = erl_ddll:info(echo_drv,processes),
+ process_flag(trap_exit, true),
+ exit(Pid1, kill),
+ test_server:sleep(200), % Give some time to unload.
+ {hej, "hopp",4711,123445567436543653} =
+ erlang:port_call(Port,{hej, "hopp",4711,123445567436543653}),
+ [] = erl_ddll:info(echo_drv,processes),
%% unload should work with no owner
- ?line ok = erl_ddll:unload_driver(echo_drv), %Kill ports while at it
+ ok = erl_ddll:unload_driver(echo_drv), %Kill ports while at it
% See if the port is killed.
receive
- {'EXIT', Port, Reason} ->
- io:format("Port exited with reason ~w", [Reason])
+ {'EXIT', Port, Reason} ->
+ io:format("Port exited with reason ~w", [Reason])
after 5000 ->
- ?line test_server:fail("Echo port did not terminate.")
+ ct:fail("Echo port did not terminate.")
end,
-
- %% Cleanup and exit.
- ?line test_server:timetrap_cancel(Dog),
ok.
-properties(doc) -> ["Test that a process that loaded a driver ",
- "is the only process that can unload it."];
+%% Test that a process that loaded a driver
+%% is the only process that can unload it.
properties(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Path = ?config(data_dir, Config),
+ Path = proplists:get_value(data_dir, Config),
% Let another process load the echo driver.
Pid=spawn_link(?MODULE, echo_loader, [Path, self()]),
receive
- {Pid, echo_loaded} -> ok
- after 2000 -> test_server:fail("echo_loader failed to start.")
+ {Pid, echo_loaded} -> ok
+ after 2000 -> ct:fail("echo_loader failed to start.")
end,
% Try to unload the driver from this process (the wrong one).
- ?line {error, _} = erl_ddll:unload_driver(echo_drv),
- ?line {ok, Drivers} = erl_ddll:loaded_drivers(),
- ?line case lists:member("echo_drv", Drivers) of
- true ->
- ok;
- false ->
- test_server:fail("Unload from wrong process "
- "succeeded.")
- end,
+ {error, _} = erl_ddll:unload_driver(echo_drv),
+ {ok, Drivers} = erl_ddll:loaded_drivers(),
+ case lists:member("echo_drv", Drivers) of
+ true ->
+ ok;
+ false ->
+ ct:fail("Unload from wrong process succeeded.")
+ end,
% Unload the driver and terminate dummy process.
- ?line Pid ! {self(), die},
- ?line test_server:sleep(200), % Give time to unload.
- ?line test_server:timetrap_cancel(Dog),
+ Pid ! {self(), die},
+ test_server:sleep(200), % Give time to unload.
ok.
-load_and_unload(doc) -> ["Load two drivers and unload them in load order."];
+%% Load two drivers and unload them in load order.
load_and_unload(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(60)),
- ?line Path = ?config(data_dir, Config),
- ?line {ok, Loaded_drivers1} = erl_ddll:loaded_drivers(),
- ?line ok = erl_ddll:load_driver(Path, echo_drv),
- ?line ok = erl_ddll:load_driver(Path, dummy_drv),
- ?line ok = erl_ddll:unload_driver(echo_drv),
- ?line ok = erl_ddll:unload_driver(dummy_drv),
- ?line {ok, Loaded_drivers2} = erl_ddll:loaded_drivers(),
- ?line Set1 = ordsets:from_list(Loaded_drivers1),
- ?line Set2 = ordsets:from_list(Loaded_drivers2),
- ?line io:format("~p == ~p\n", [Loaded_drivers1, Loaded_drivers2]),
- ?line [] = ordsets:to_list(ordsets:subtract(Set2, Set1)),
-
- ?line test_server:timetrap_cancel(Dog),
+ Path = proplists:get_value(data_dir, Config),
+ {ok, Loaded_drivers1} = erl_ddll:loaded_drivers(),
+ ok = erl_ddll:load_driver(Path, echo_drv),
+ ok = erl_ddll:load_driver(Path, dummy_drv),
+ ok = erl_ddll:unload_driver(echo_drv),
+ ok = erl_ddll:unload_driver(dummy_drv),
+ {ok, Loaded_drivers2} = erl_ddll:loaded_drivers(),
+ Set1 = ordsets:from_list(Loaded_drivers1),
+ Set2 = ordsets:from_list(Loaded_drivers2),
+ io:format("~p == ~p\n", [Loaded_drivers1, Loaded_drivers2]),
+ [] = ordsets:to_list(ordsets:subtract(Set2, Set1)),
ok.
-lock_driver(suite) ->
- [];
-lock_driver(doc) ->
- ["Check multiple calls to driver_lock_driver"];
+%% Check multiple calls to driver_lock_driver
lock_driver(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Path = ?config(data_dir, Config),
- ?line {ok, _} = erl_ddll:try_load(Path, lock_drv, []),
- ?line Port1 = open_port({spawn, lock_drv}, [eof]),
- ?line Port2 = open_port({spawn, lock_drv}, [eof]),
- ?line true = erl_ddll:info(lock_drv,permanent),
- ?line erlang:port_close(Port1),
- ?line erlang:port_close(Port2),
- ?line test_server:timetrap_cancel(Dog),
+ Path = proplists:get_value(data_dir, Config),
+ {ok, _} = erl_ddll:try_load(Path, lock_drv, []),
+ Port1 = open_port({spawn, lock_drv}, [eof]),
+ Port2 = open_port({spawn, lock_drv}, [eof]),
+ true = erl_ddll:info(lock_drv,permanent),
+ erlang:port_close(Port1),
+ erlang:port_close(Port2),
ok.
-
+
% Load and unload the echo_drv driver.
% Make sure that the driver doesn't exist before we load it,
% and that it exists before we unload it.
load_echo_driver(Path) ->
- ?line {ok, L1} = erl_ddll:loaded_drivers(),
- ?line ok = erl_ddll:load_driver(Path, echo_drv),
- ?line {ok, L2} = erl_ddll:loaded_drivers(),
- ?line ["echo_drv"] = ordsets:to_list(subtract(ordsets:from_list(L2),
- ordsets:from_list(L1))),
+ {ok, L1} = erl_ddll:loaded_drivers(),
+ ok = erl_ddll:load_driver(Path, echo_drv),
+ {ok, L2} = erl_ddll:loaded_drivers(),
+ ["echo_drv"] = ordsets:to_list(subtract(ordsets:from_list(L2),
+ ordsets:from_list(L1))),
{ok,L1,L2}.
load_nice_echo_driver(Path) ->
- ?line {ok, L1} = erl_ddll:loaded_drivers(),
- ?line ok = erl_ddll:load(Path, echo_drv),
- ?line {ok, L2} = erl_ddll:loaded_drivers(),
- ?line ["echo_drv"] = ordsets:to_list(subtract(ordsets:from_list(L2),
- ordsets:from_list(L1))),
+ {ok, L1} = erl_ddll:loaded_drivers(),
+ ok = erl_ddll:load(Path, echo_drv),
+ {ok, L2} = erl_ddll:loaded_drivers(),
+ ["echo_drv"] = ordsets:to_list(subtract(ordsets:from_list(L2),
+ ordsets:from_list(L1))),
{ok,L1,L2}.
unload_echo_driver(L1,L2) ->
- ?line {ok, L2} = erl_ddll:loaded_drivers(),
- ?line ok = erl_ddll:unload_driver(echo_drv),
- ?line {ok, L3} = erl_ddll:loaded_drivers(),
- ?line [] = ordsets:to_list(subtract(ordsets:from_list(L3),
- ordsets:from_list(L1))),
+ {ok, L2} = erl_ddll:loaded_drivers(),
+ ok = erl_ddll:unload_driver(echo_drv),
+ {ok, L3} = erl_ddll:loaded_drivers(),
+ [] = ordsets:to_list(subtract(ordsets:from_list(L3),
+ ordsets:from_list(L1))),
ok.
unload_expect_fast(Driver,XFlags) ->
{ok, pending_driver, Ref} =
- erl_ddll:try_unload(Driver,
- [{monitor,pending_driver}]++XFlags),
+ erl_ddll:try_unload(Driver,
+ [{monitor,pending_driver}]++XFlags),
receive
- {'DOWN', Ref, driver, Driver, unloaded} ->
- case lists:member(atom_to_list(Driver),element(2,erl_ddll:loaded_drivers())) of
- true ->
- {error, {still_there, Driver}};
- false ->
- ok
- end
+ {'DOWN', Ref, driver, Driver, unloaded} ->
+ case lists:member(atom_to_list(Driver),element(2,erl_ddll:loaded_drivers())) of
+ true ->
+ {error, {still_there, Driver}};
+ false ->
+ ok
+ end
after 1000 ->
- {error,{unable_to_unload, Driver}}
+ {error,{unable_to_unload, Driver}}
end.
diff --git a/erts/emulator/test/decode_packet_SUITE.erl b/erts/emulator/test/decode_packet_SUITE.erl
index 6a5ca20ac3..54ee4d5567 100644
--- a/erts/emulator/test/decode_packet_SUITE.erl
+++ b/erts/emulator/test/decode_packet_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -22,15 +22,16 @@
-module(decode_packet_SUITE).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2,end_per_testcase/2,
- basic/1, packet_size/1, neg/1, http/1, line/1, ssl/1, otp_8536/1,
+-export([all/0, suite/0,groups/0,
+ init_per_testcase/2,end_per_testcase/2,
+ basic/1, packet_size/1, neg/1, http/1, line/1, ssl/1, otp_8536/1,
otp_9389/1, otp_9389_line/1]).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 1}}].
all() ->
[basic, packet_size, neg, http, line, ssl, otp_8536,
@@ -39,69 +40,49 @@ all() ->
groups() ->
[].
-init_per_suite(Config) ->
+init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
+ rand:seed(exsplus),
+ io:format("*** SEED: ~p ***\n", [rand:export_seed()]),
Config.
-end_per_suite(_Config) ->
+end_per_testcase(_Func, _Config) ->
ok.
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- Seed = {S1,S2,S3} = {erlang:monotonic_time(),
- erlang:time_offset(),
- erlang:unique_integer()},
- random:seed(S1,S2,S3),
- io:format("*** SEED: ~p ***\n", [Seed]),
- Dog=?t:timetrap(?t:minutes(1)),
- [{watchdog, Dog}|Config].
-
-end_per_testcase(_Func, Config) ->
- Dog=?config(watchdog, Config),
- ?t:timetrap_cancel(Dog).
-
-basic(doc) -> [];
-basic(suite) -> [];
basic(Config) when is_list(Config) ->
- ?line Packet = <<101,22,203,54,175>>,
- ?line Rest = <<123,34,0,250>>,
- ?line Bin = <<Packet/binary,Rest/binary>>,
- ?line {ok, Bin, <<>>} = decode_pkt(raw,Bin),
+ Packet = <<101,22,203,54,175>>,
+ Rest = <<123,34,0,250>>,
+ Bin = <<Packet/binary,Rest/binary>>,
+ {ok, Bin, <<>>} = decode_pkt(raw,Bin),
- ?line {more, 5+1} = decode_pkt(1,<<5,1,2,3,4>>),
- ?line {more, 5+2} = decode_pkt(2,<<0,5,1,2,3,4>>),
- ?line {more, 5+4} = decode_pkt(4,<<0,0,0,5,1,2,3,4>>),
+ {more, 5+1} = decode_pkt(1,<<5,1,2,3,4>>),
+ {more, 5+2} = decode_pkt(2,<<0,5,1,2,3,4>>),
+ {more, 5+4} = decode_pkt(4,<<0,0,0,5,1,2,3,4>>),
- ?line {more, undefined} = decode_pkt(1,<<>>),
- ?line {more, undefined} = decode_pkt(2,<<0>>),
- ?line {more, undefined} = decode_pkt(4,<<0,0,0>>),
+ {more, undefined} = decode_pkt(1,<<>>),
+ {more, undefined} = decode_pkt(2,<<0>>),
+ {more, undefined} = decode_pkt(4,<<0,0,0>>),
Types = [1,2,4,asn1,sunrm,cdr,fcgi,tpkt,ssl_tls],
%% Run tests for different header types and bit offsets.
lists:foreach(fun({Type,Bits})->basic_pack(Type,Packet,Rest,Bits),
- more_length(Type,Packet,Bits) end,
- [{T,B} || T<-Types, B<-lists:seq(0,32)]),
+ more_length(Type,Packet,Bits) end,
+ [{T,B} || T<-Types, B<-lists:seq(0,32)]),
ok.
basic_pack(Type,Body,Rest,BitOffs) ->
- ?line {Bin,Unpacked,_} = pack(Type,Body,Rest,BitOffs),
- ?line {ok, Unpacked, Rest} = decode_pkt(Type,Bin),
+ {Bin,Unpacked,_} = pack(Type,Body,Rest,BitOffs),
+ {ok, Unpacked, Rest} = decode_pkt(Type,Bin),
case Rest of
- <<>> -> ok;
- _ ->
- ?line <<_:1,NRest/bits>> = Rest,
- basic_pack(Type,Body,NRest,BitOffs)
+ <<>> -> ok;
+ _ ->
+ <<_:1,NRest/bits>> = Rest,
+ basic_pack(Type,Body,NRest,BitOffs)
end.
more_length(Type,Body,BitOffs) ->
- ?line {Bin,_,_} = pack(Type,Body,<<>>,BitOffs),
+ {Bin,_,_} = pack(Type,Body,<<>>,BitOffs),
HdrSize = byte_size(Bin) - byte_size(Body),
more_length_do(Type,HdrSize,Bin,byte_size(Bin)).
@@ -110,17 +91,17 @@ more_length_do(_,_,_,0) ->
more_length_do(Type,HdrSize,Bin,Size) ->
TrySize = (Size*3) div 4,
NSize = if TrySize < HdrSize -> Size - 1;
- true -> TrySize
- end,
+ true -> TrySize
+ end,
{B1,_} = split_binary(Bin,NSize),
- ?line {more, Length} = decode_pkt(Type,B1),
+ {more, Length} = decode_pkt(Type,B1),
case Length of
- L when L=:=byte_size(Bin) -> ok;
- undefined when NSize<HdrSize -> ok
+ L when L=:=byte_size(Bin) -> ok;
+ undefined when NSize<HdrSize -> ok
end,
more_length_do(Type,HdrSize,Bin,NSize).
-
+
pack(Type,Packet,Rest) ->
{Bin,Unpacked} = pack(Type,Packet),
@@ -136,7 +117,7 @@ pack(Type,Body,Rest,BitOffs) ->
{Packet,Unpacked} = pack(Type,Body),
%% Make Bin a sub-bin with an arbitrary bitoffset within Orig
- Prefix = random:uniform(1 bsl BitOffs) - 1,
+ Prefix = rand:uniform(1 bsl BitOffs) - 1,
Orig = <<Prefix:BitOffs,Packet/binary,Rest/bits>>,
<<_:BitOffs,Bin/bits>> = Orig,
{Bin,Unpacked,Orig}.
@@ -151,212 +132,206 @@ pack(4,Bin) ->
Psz = byte_size(Bin),
{<<Psz:32,Bin/binary>>, Bin};
pack(asn1,Bin) ->
- Ident = case random:uniform(3) of
- 1 -> <<17>>;
- 2 -> <<16#1f,16#81,17>>;
- 3 -> <<16#1f,16#81,16#80,16#80,17>>
- end,
+ Ident = case rand:uniform(3) of
+ 1 -> <<17>>;
+ 2 -> <<16#1f,16#81,17>>;
+ 3 -> <<16#1f,16#81,16#80,16#80,17>>
+ end,
Psz = byte_size(Bin),
- Length = case random:uniform(4) of
- 1 when Psz < 128 ->
- <<Psz:8>>;
- R when R=<2 andalso Psz < 16#10000 ->
- <<16#82,Psz:16>>;
- R when R=<3 andalso Psz < 16#1000000 ->
- <<16#83,Psz:24>>;
- _ when Psz < 16#100000000 ->
- <<16#84,Psz:32>>
- end,
+ Length = case rand:uniform(4) of
+ 1 when Psz < 128 ->
+ <<Psz:8>>;
+ R when R=<2 andalso Psz < 16#10000 ->
+ <<16#82,Psz:16>>;
+ R when R=<3 andalso Psz < 16#1000000 ->
+ <<16#83,Psz:24>>;
+ _ when Psz < 16#100000000 ->
+ <<16#84,Psz:32>>
+ end,
Res = <<Ident/binary,Length/binary,Bin/binary>>,
{Res,Res};
pack(sunrm,Bin) ->
Psz = byte_size(Bin),
Res = if Psz < 16#80000000 ->
- <<Psz:32,Bin/binary>>
- end,
+ <<Psz:32,Bin/binary>>
+ end,
{Res,Res};
pack(cdr,Bin) ->
GIOP = <<"GIOP">>,
- Major = random:uniform(256) - 1,
- Minor = random:uniform(256) - 1,
- MType = random:uniform(256) - 1,
+ Major = rand:uniform(256) - 1,
+ Minor = rand:uniform(256) - 1,
+ MType = rand:uniform(256) - 1,
Psz = byte_size(Bin),
- Res = case random:uniform(2) of
- 1 -> <<GIOP/binary,Major:8,Minor:8,0:8,MType:8,Psz:32/big,Bin/binary>>;
- 2 -> <<GIOP/binary,Major:8,Minor:8,1:8,MType:8,Psz:32/little,Bin/binary>>
- end,
+ Res = case rand:uniform(2) of
+ 1 -> <<GIOP/binary,Major:8,Minor:8,0:8,MType:8,Psz:32/big,Bin/binary>>;
+ 2 -> <<GIOP/binary,Major:8,Minor:8,1:8,MType:8,Psz:32/little,Bin/binary>>
+ end,
{Res,Res};
pack(fcgi,Bin) ->
Ver = 1,
- Type = random:uniform(256) - 1,
- Id = random:uniform(65536) - 1,
- PaddSz = random:uniform(16) - 1,
+ Type = rand:uniform(256) - 1,
+ Id = rand:uniform(65536) - 1,
+ PaddSz = rand:uniform(16) - 1,
Psz = byte_size(Bin),
- Reserv = random:uniform(256) - 1,
+ Reserv = rand:uniform(256) - 1,
Padd = case PaddSz of
- 0 -> <<>>;
- _ -> list_to_binary([random:uniform(256)-1
- || _<- lists:seq(1,PaddSz)])
- end,
+ 0 -> <<>>;
+ _ -> list_to_binary([rand:uniform(256)-1
+ || _<- lists:seq(1,PaddSz)])
+ end,
Res = <<Ver:8,Type:8,Id:16,Psz:16/big,PaddSz:8,Reserv:8,Bin/binary>>,
{<<Res/binary,Padd/binary>>, Res};
pack(tpkt,Bin) ->
Ver = 3,
- Reserv = random:uniform(256) - 1,
+ Reserv = rand:uniform(256) - 1,
Size = byte_size(Bin) + 4,
Res = <<Ver:8,Reserv:8,Size:16,Bin/binary>>,
{Res, Res};
pack(ssl_tls,Bin) ->
- Content = case (random:uniform(256) - 1) of
- C when C<128 -> C;
- _ -> v2hello
- end,
- Major = random:uniform(256) - 1,
- Minor = random:uniform(256) - 1,
+ Content = case (rand:uniform(256) - 1) of
+ C when C<128 -> C;
+ _ -> v2hello
+ end,
+ Major = rand:uniform(256) - 1,
+ Minor = rand:uniform(256) - 1,
pack_ssl(Content,Major,Minor,Bin).
pack_ssl(Content, Major, Minor, Body) ->
case Content of
- v2hello ->
- Size = byte_size(Body),
- Res = <<1:1,(Size+3):15, 1:8, Major:8, Minor:8, Body/binary>>,
- C = 22,
- Data = <<1:8, (Size+2):24, Major:8, Minor:8, Body/binary>>;
- C when is_integer(C) ->
- Size = byte_size(Body),
- Res = <<Content:8, Major:8, Minor:8, Size:16, Body/binary>>,
- Data = Body
+ v2hello ->
+ Size = byte_size(Body),
+ Res = <<1:1,(Size+3):15, 1:8, Major:8, Minor:8, Body/binary>>,
+ C = 22,
+ Data = <<1:8, (Size+2):24, Major:8, Minor:8, Body/binary>>;
+ C when is_integer(C) ->
+ Size = byte_size(Body),
+ Res = <<Content:8, Major:8, Minor:8, Size:16, Body/binary>>,
+ Data = Body
end,
{Res, {ssl_tls,[],C,{Major,Minor}, Data}}.
-packet_size(doc) -> [];
-packet_size(suite) -> [];
packet_size(Config) when is_list(Config) ->
- ?line Packet = <<101,22,203,54,175>>,
- ?line Rest = <<123,34,0,250>>,
+ Packet = <<101,22,203,54,175>>,
+ Rest = <<123,34,0,250>>,
F = fun({Type,Max})->
- ?line {Bin,Unpacked} = pack(Type,Packet,Rest),
- ?line case decode_pkt(Type,Bin,[{packet_size,Max}]) of
- {ok,Unpacked,Rest} when Max=:=0; Max>=byte_size(Packet) ->
- ok;
- {error,_} when Max<byte_size(Packet), Max=/=0 ->
- ok;
- {error,_} when Type=:=fcgi, Max=/=0 ->
- %% packet includes random amount of padding
- ok
- end
- end,
- ?line lists:foreach(F, [{T,D} || T<-[1,2,4,asn1,sunrm,cdr,fcgi,tpkt,ssl_tls],
- D<-lists:seq(0, byte_size(Packet)*2)]),
+ {Bin,Unpacked} = pack(Type,Packet,Rest),
+ case decode_pkt(Type,Bin,[{packet_size,Max}]) of
+ {ok,Unpacked,Rest} when Max=:=0; Max>=byte_size(Packet) ->
+ ok;
+ {error,_} when Max<byte_size(Packet), Max=/=0 ->
+ ok;
+ {error,_} when Type=:=fcgi, Max=/=0 ->
+ %% packet includes random amount of padding
+ ok
+ end
+ end,
+ lists:foreach(F, [{T,D} || T<-[1,2,4,asn1,sunrm,cdr,fcgi,tpkt,ssl_tls],
+ D<-lists:seq(0, byte_size(Packet)*2)]),
%% Test OTP-8102, "negative" 4-byte sizes.
lists:foreach(fun(Size) ->
- ?line {error,_} = decode_pkt(4,<<Size:32,Packet/binary>>)
- end,
- lists:seq(-10,-1)),
+ {error,_} = decode_pkt(4,<<Size:32,Packet/binary>>)
+ end,
+ lists:seq(-10,-1)),
%% Test OTP-9389, long HTTP header lines.
Opts = [{packet_size, 128}],
Pkt = list_to_binary(["GET / HTTP/1.1\r\nHost: localhost\r\nLink: /",
string:chars($Y, 64), "\r\n\r\n"]),
<<Pkt1:50/binary, Pkt2/binary>> = Pkt,
- ?line {ok, {http_request,'GET',{abs_path,"/"},{1,1}}, Rest1} =
- erlang:decode_packet(http, Pkt1, Opts),
- ?line {ok, {http_header,_,'Host',_,"localhost"}, Rest2} =
- erlang:decode_packet(httph, Rest1, Opts),
- ?line {more, undefined} = erlang:decode_packet(httph, Rest2, Opts),
- ?line {ok, {http_header,_,"Link",_,_}, _} =
- erlang:decode_packet(httph, list_to_binary([Rest2, Pkt2]), Opts),
+ {ok, {http_request,'GET',{abs_path,"/"},{1,1}}, Rest1} =
+ erlang:decode_packet(http, Pkt1, Opts),
+ {ok, {http_header,_,'Host',_,"localhost"}, Rest2} =
+ erlang:decode_packet(httph, Rest1, Opts),
+ {more, undefined} = erlang:decode_packet(httph, Rest2, Opts),
+ {ok, {http_header,_,"Link",_,_}, _} =
+ erlang:decode_packet(httph, list_to_binary([Rest2, Pkt2]), Opts),
Pkt3 = list_to_binary(["GET / HTTP/1.1\r\nHost: localhost\r\nLink: /",
string:chars($Y, 129), "\r\n\r\n"]),
- ?line {ok, {http_request,'GET',{abs_path,"/"},{1,1}}, Rest3} =
- erlang:decode_packet(http, Pkt3, Opts),
- ?line {ok, {http_header,_,'Host',_,"localhost"}, Rest4} =
- erlang:decode_packet(httph, Rest3, Opts),
- ?line {error, invalid} = erlang:decode_packet(httph, Rest4, Opts),
+ {ok, {http_request,'GET',{abs_path,"/"},{1,1}}, Rest3} =
+ erlang:decode_packet(http, Pkt3, Opts),
+ {ok, {http_header,_,'Host',_,"localhost"}, Rest4} =
+ erlang:decode_packet(httph, Rest3, Opts),
+ {error, invalid} = erlang:decode_packet(httph, Rest4, Opts),
ok.
-neg(doc) -> [];
-neg(suite) -> [];
neg(Config) when is_list(Config) ->
- ?line Bin = <<"dummy">>,
+ Bin = <<"dummy">>,
Fun = fun()->dummy end,
-
+
BadargF = fun(T,B,Opts)-> {'EXIT',{badarg,_}} = (catch decode_pkt(T,B,Opts)) end,
%% Invalid Type args
lists:foreach(fun(T)-> BadargF(T,Bin,[]) end,
- [3,-1,5,2.0,{2},unknown,[],"line",Bin,Fun,self()]),
+ [3,-1,5,2.0,{2},unknown,[],"line",Bin,Fun,self()]),
%% Invalid Bin args
lists:foreach(fun(B)-> BadargF(0,B,[]) end,
- [3,2.0,unknown,[],"Bin",[Bin],{Bin},Fun,self()]),
+ [3,2.0,unknown,[],"Bin",[Bin],{Bin},Fun,self()]),
%% Invalid options
InvOpts = [2,false,self(),Bin,"Options",Fun,
- packet_size,{packet_size},{packet_size,0,false},
- {packet_size,-1},{packet_size,100.0},{packet_size,false},
- {line_length,-1},{line_length,100.0},{line_length,false}],
+ packet_size,{packet_size},{packet_size,0,false},
+ {packet_size,-1},{packet_size,100.0},{packet_size,false},
+ {line_length,-1},{line_length,100.0},{line_length,false}],
lists:foreach(fun(Opt)-> BadargF(0,Bin,Opt),
- BadargF(0,Bin,[Opt]),
- BadargF(0,Bin,[Opt,{packet_size,1000}]),
- BadargF(0,Bin,[{packet_size,1000},Opt]) end,
- InvOpts),
+ BadargF(0,Bin,[Opt]),
+ BadargF(0,Bin,[Opt,{packet_size,1000}]),
+ BadargF(0,Bin,[{packet_size,1000},Opt]) end,
+ InvOpts),
ok.
-http(doc) -> [];
-http(suite) -> [];
http(Config) when is_list(Config) ->
- ?line <<"foo">> = http_do(http_request("foo")),
- ?line <<" bar">> = http_do(http_request(" bar")),
- ?line <<"Hello!">> = http_do(http_response("Hello!")),
+ <<"foo">> = http_do(http_request("foo")),
+ <<" bar">> = http_do(http_request(" bar")),
+ <<"Hello!">> = http_do(http_response("Hello!")),
%% Test all known header atoms
Val = "dummy value",
ValB = list_to_binary(Val),
Rest = <<"Rest">>,
HdrF = fun(Str,N) ->
- ?line StrA = list_to_atom(Str),
- ?line StrB = list_to_binary(Str),
- ?line Bin = <<StrB/binary,": ",ValB/binary,"\r\n",Rest/binary>>,
- ?line {ok, {http_header,N,StrA,undefined,Val}, Rest} = decode_pkt(httph,Bin),
- ?line {ok, {http_header,N,StrA,undefined,ValB}, Rest} = decode_pkt(httph_bin,Bin),
- ?line N + 1
- end,
- ?line lists:foldl(HdrF, 1, http_hdr_strings()),
+ StrA = list_to_atom(Str),
+ StrB = list_to_binary(Str),
+ Bin = <<StrB/binary,": ",ValB/binary,"\r\n",Rest/binary>>,
+ {ok, {http_header,N,StrA,undefined,Val}, Rest} = decode_pkt(httph,Bin),
+ {ok, {http_header,N,StrA,undefined,ValB}, Rest} = decode_pkt(httph_bin,Bin),
+ N + 1
+ end,
+ lists:foldl(HdrF, 1, http_hdr_strings()),
%% Test all known method atoms
MethF = fun(Meth) ->
- ?line MethA = list_to_atom(Meth),
- ?line MethB = list_to_binary(Meth),
- ?line Bin = <<MethB/binary," /invalid/url HTTP/1.0\r\n",Rest/binary>>,
- ?line {ok, {http_request,MethA,{abs_path,"/invalid/url"},{1,0}},
- Rest} = decode_pkt(http,Bin),
- ?line {ok, {http_request,MethA,{abs_path,<<"/invalid/url">>},{1,0}},
- Rest} = decode_pkt(http_bin,Bin)
- end,
- ?line lists:foreach(MethF, http_meth_strings()),
+ MethA = list_to_atom(Meth),
+ MethB = list_to_binary(Meth),
+ Bin = <<MethB/binary," /invalid/url HTTP/1.0\r\n",Rest/binary>>,
+ {ok, {http_request,MethA,{abs_path,"/invalid/url"},{1,0}},
+ Rest} = decode_pkt(http,Bin),
+ {ok, {http_request,MethA,{abs_path,<<"/invalid/url">>},{1,0}},
+ Rest} = decode_pkt(http_bin,Bin)
+ end,
+ lists:foreach(MethF, http_meth_strings()),
%% Test all uri variants
UriF = fun({Str,ResL,ResB}) ->
- Bin = <<"GET ",(list_to_binary(Str))/binary," HTTP/1.1\r\n",Rest/binary>>,
- {ok, {http_request, 'GET', ResL, {1,1}}, Rest} = decode_pkt(http,Bin),
- {ok, {http_request, 'GET', ResB, {1,1}}, Rest} = decode_pkt(http_bin,Bin)
- end,
+ Bin = <<"GET ",(list_to_binary(Str))/binary," HTTP/1.1\r\n",Rest/binary>>,
+ {ok, {http_request, 'GET', ResL, {1,1}}, Rest} = decode_pkt(http,Bin),
+ {ok, {http_request, 'GET', ResB, {1,1}}, Rest} = decode_pkt(http_bin,Bin)
+ end,
lists:foreach(UriF, http_uri_variants()),
%% Response with empty phrase
- ?line {ok,{http_response,{1,1},200,[]},<<>>} = decode_pkt(http, <<"HTTP/1.1 200\r\n">>, []),
- ?line {ok,{http_response,{1,1},200,<<>>},<<>>} = decode_pkt(http_bin, <<"HTTP/1.1 200\r\n">>, []),
+ {ok,{http_response,{1,1},200,[]},<<>>} = decode_pkt(http, <<"HTTP/1.1 200\r\n">>, []),
+ {ok,{http_response,{1,1},200,<<>>},<<>>} = decode_pkt(http_bin, <<"HTTP/1.1 200\r\n">>, []),
ok.
-
+
http_with_bin(http) ->
http_bin;
http_with_bin(httph) ->
@@ -367,88 +342,88 @@ http_do(Tup) ->
http_do({Bin, []}, _) ->
Bin;
http_do({Bin,[{_Line,PL,PB}|Tail]}, Type) ->
- ?line {ok, PL, Rest} = decode_pkt(Type,Bin),
- ?line {ok, PB, Rest} = decode_pkt(http_with_bin(Type),Bin),
+ {ok, PL, Rest} = decode_pkt(Type,Bin),
+ {ok, PB, Rest} = decode_pkt(http_with_bin(Type),Bin),
%% Same tests again but as SubBin
- PreLen = random:uniform(64),
- Prefix = random:uniform(1 bsl PreLen) - 1,
- SufLen = random:uniform(64),
- Suffix = random:uniform(1 bsl SufLen) - 1,
+ PreLen = rand:uniform(64),
+ Prefix = rand:uniform(1 bsl PreLen) - 1,
+ SufLen = rand:uniform(64),
+ Suffix = rand:uniform(1 bsl SufLen) - 1,
Orig = <<Prefix:PreLen, Bin/bits, Suffix:SufLen>>,
BinLen = bit_size(Bin),
<<_:PreLen, SubBin:BinLen/bits, _/bits>> = Orig, % Make SubBin
- ?line SubBin = Bin, % just to make sure
+ SubBin = Bin, % just to make sure
- ?line {ok, PL, Rest} = decode_pkt(Type,SubBin),
- ?line {ok, PB, Rest} = decode_pkt(http_with_bin(Type),SubBin),
+ {ok, PL, Rest} = decode_pkt(Type,SubBin),
+ {ok, PB, Rest} = decode_pkt(http_with_bin(Type),SubBin),
http_do({Rest, Tail}, httph).
http_request(Msg) ->
QnA = [{"POST /invalid/url HTTP/1.1\r\n",
- {http_request, 'POST', {abs_path, "/invalid/url" }, {1,1}},
- {http_request, 'POST', {abs_path,<<"/invalid/url">>}, {1,1}}},
- {"Connection: close\r\n",
- {http_header,2,'Connection',undefined, "close"},
- {http_header,2,'Connection',undefined,<<"close">>}},
- {"Host\t : localhost:8000\r\n", % white space before :
- {http_header,14,'Host',undefined, "localhost:8000"},
- {http_header,14,'Host',undefined,<<"localhost:8000">>}},
- {"User-Agent: perl post\r\n",
- {http_header,24,'User-Agent',undefined, "perl post"},
- {http_header,24,'User-Agent',undefined,<<"perl post">>}},
- {"Content-Length: 4\r\n",
- {http_header,38,'Content-Length',undefined, "4"},
- {http_header,38,'Content-Length',undefined,<<"4">>}},
- {"Content-Type: text/xml; charset=utf-8\r\n",
- {http_header,42,'Content-Type',undefined, "text/xml; charset=utf-8"},
- {http_header,42,'Content-Type',undefined,<<"text/xml; charset=utf-8">>}},
- {"Other-Field: with some text\r\n",
- {http_header,0, "Other-Field" ,undefined, "with some text"},
- {http_header,0,<<"Other-Field">>,undefined,<<"with some text">>}},
- {"Make-sure-a-LONG-HEaDer-fIeLd-is-fORMATTED-NicelY: with some text\r\n",
- {http_header,0, "Make-Sure-A-Long-Header-Field-Is-Formatted-Nicely" ,undefined, "with some text"},
- {http_header,0,<<"Make-Sure-A-Long-Header-Field-Is-Formatted-Nicely">>,undefined,<<"with some text">>}},
- {"Multi-Line: Once upon a time in a land far far away,\r\n"
- " there lived a princess imprisoned in the highest tower\r\n"
- " of the most haunted castle.\r\n",
- {http_header,0, "Multi-Line" ,undefined, "Once upon a time in a land far far away,\r\n there lived a princess imprisoned in the highest tower\r\n of the most haunted castle."},
- {http_header,0,<<"Multi-Line">>,undefined,<<"Once upon a time in a land far far away,\r\n there lived a princess imprisoned in the highest tower\r\n of the most haunted castle.">>}},
- {"\r\n",
- http_eoh,
- http_eoh}],
+ {http_request, 'POST', {abs_path, "/invalid/url" }, {1,1}},
+ {http_request, 'POST', {abs_path,<<"/invalid/url">>}, {1,1}}},
+ {"Connection: close\r\n",
+ {http_header,2,'Connection',undefined, "close"},
+ {http_header,2,'Connection',undefined,<<"close">>}},
+ {"Host\t : localhost:8000\r\n", % white space before :
+ {http_header,14,'Host',undefined, "localhost:8000"},
+ {http_header,14,'Host',undefined,<<"localhost:8000">>}},
+ {"User-Agent: perl post\r\n",
+ {http_header,24,'User-Agent',undefined, "perl post"},
+ {http_header,24,'User-Agent',undefined,<<"perl post">>}},
+ {"Content-Length: 4\r\n",
+ {http_header,38,'Content-Length',undefined, "4"},
+ {http_header,38,'Content-Length',undefined,<<"4">>}},
+ {"Content-Type: text/xml; charset=utf-8\r\n",
+ {http_header,42,'Content-Type',undefined, "text/xml; charset=utf-8"},
+ {http_header,42,'Content-Type',undefined,<<"text/xml; charset=utf-8">>}},
+ {"Other-Field: with some text\r\n",
+ {http_header,0, "Other-Field" ,undefined, "with some text"},
+ {http_header,0,<<"Other-Field">>,undefined,<<"with some text">>}},
+ {"Make-sure-a-LONG-HEaDer-fIeLd-is-fORMATTED-NicelY: with some text\r\n",
+ {http_header,0, "Make-Sure-A-Long-Header-Field-Is-Formatted-Nicely" ,undefined, "with some text"},
+ {http_header,0,<<"Make-Sure-A-Long-Header-Field-Is-Formatted-Nicely">>,undefined,<<"with some text">>}},
+ {"Multi-Line: Once upon a time in a land far far away,\r\n"
+ " there lived a princess imprisoned in the highest tower\r\n"
+ " of the most haunted castle.\r\n",
+ {http_header,0, "Multi-Line" ,undefined, "Once upon a time in a land far far away,\r\n there lived a princess imprisoned in the highest tower\r\n of the most haunted castle."},
+ {http_header,0,<<"Multi-Line">>,undefined,<<"Once upon a time in a land far far away,\r\n there lived a princess imprisoned in the highest tower\r\n of the most haunted castle.">>}},
+ {"\r\n",
+ http_eoh,
+ http_eoh}],
Bin = lists:foldl(fun({Line,_,_},Acc) -> LineBin = list_to_binary(Line),
- <<Acc/binary,LineBin/binary>> end,
- <<"">>, QnA),
+ <<Acc/binary,LineBin/binary>> end,
+ <<"">>, QnA),
MsgBin = list_to_binary(Msg),
{<<Bin/binary,MsgBin/binary>>, QnA}.
http_response(Msg) ->
QnA = [{"HTTP/1.0 404 Object Not Found\r\n",
- {http_response, {1,0}, 404, "Object Not Found"},
- {http_response, {1,0}, 404, <<"Object Not Found">>}},
- {"Server: inets/4.7.16\r\n",
- {http_header, 30, 'Server', undefined, "inets/4.7.16"},
- {http_header, 30, 'Server', undefined, <<"inets/4.7.16">>}},
- {"Date: Fri, 04 Jul 2008 17:16:22 GMT\r\n",
- {http_header, 3, 'Date', undefined, "Fri, 04 Jul 2008 17:16:22 GMT"},
- {http_header, 3, 'Date', undefined, <<"Fri, 04 Jul 2008 17:16:22 GMT">>}},
- {"Content-Type: text/html\r\n",
- {http_header, 42, 'Content-Type', undefined, "text/html"},
- {http_header, 42, 'Content-Type', undefined, <<"text/html">>}},
- {"Content-Length: 207\r\n",
- {http_header, 38, 'Content-Length', undefined, "207"},
- {http_header, 38, 'Content-Length', undefined, <<"207">>}},
- {"\r\n",
- http_eoh,
- http_eoh}],
+ {http_response, {1,0}, 404, "Object Not Found"},
+ {http_response, {1,0}, 404, <<"Object Not Found">>}},
+ {"Server: inets/4.7.16\r\n",
+ {http_header, 30, 'Server', undefined, "inets/4.7.16"},
+ {http_header, 30, 'Server', undefined, <<"inets/4.7.16">>}},
+ {"Date: Fri, 04 Jul 2008 17:16:22 GMT\r\n",
+ {http_header, 3, 'Date', undefined, "Fri, 04 Jul 2008 17:16:22 GMT"},
+ {http_header, 3, 'Date', undefined, <<"Fri, 04 Jul 2008 17:16:22 GMT">>}},
+ {"Content-Type: text/html\r\n",
+ {http_header, 42, 'Content-Type', undefined, "text/html"},
+ {http_header, 42, 'Content-Type', undefined, <<"text/html">>}},
+ {"Content-Length: 207\r\n",
+ {http_header, 38, 'Content-Length', undefined, "207"},
+ {http_header, 38, 'Content-Length', undefined, <<"207">>}},
+ {"\r\n",
+ http_eoh,
+ http_eoh}],
Bin = lists:foldl(fun({Line,_,_},Acc) -> LineBin = list_to_binary(Line),
- <<Acc/binary,LineBin/binary>> end,
- <<"">>, QnA),
+ <<Acc/binary,LineBin/binary>> end,
+ <<"">>, QnA),
MsgBin = list_to_binary(Msg),
{<<Bin/binary,MsgBin/binary>>, QnA}.
@@ -489,60 +464,56 @@ http_uri_variants() ->
{"something_else", "something_else", <<"something_else">>}].
-line(doc) -> [];
-line(suite) -> [];
line(Config) when is_list(Config) ->
Text = <<"POST /invalid/url HTTP/1.1\r\n"
- "Connection: close\r\n"
- "Host\t : localhost:8000\r\n"
- "User-Agent: perl post\r\n"
- "Content-Length: 4\r\n"
- "Content-Type: text/xml; charset=utf-8\r\n"
- "Other-Field: with some text\r\n"
- "Multi-Line: Once upon a time in a land far far away,\r\n"
- " there lived a princess imprisoned in the highest tower\r\n"
- " of the most haunted castle.\r\n"
- "\r\nThe residue">>,
+ "Connection: close\r\n"
+ "Host\t : localhost:8000\r\n"
+ "User-Agent: perl post\r\n"
+ "Content-Length: 4\r\n"
+ "Content-Type: text/xml; charset=utf-8\r\n"
+ "Other-Field: with some text\r\n"
+ "Multi-Line: Once upon a time in a land far far away,\r\n"
+ " there lived a princess imprisoned in the highest tower\r\n"
+ " of the most haunted castle.\r\n"
+ "\r\nThe residue">>,
lists:foreach(fun(MaxLen) -> line_do(Text,MaxLen) end,
- [0,7,19,29,37]),
+ [0,7,19,29,37]),
ok.
line_do(Bin,MaxLen) ->
Res = decode_pkt(line,Bin,[{line_length,MaxLen}]),
MyRes = decode_line(Bin,MaxLen),
- ?line MyRes = Res,
+ MyRes = Res,
case Res of
- {ok,_,Rest} ->
- line_do(Rest,MaxLen);
- {more,undefined} ->
- ok
+ {ok,_,Rest} ->
+ line_do(Rest,MaxLen);
+ {more,undefined} ->
+ ok
end.
-
+
% Emulates decode_packet(line,Bin,[{line_length,MaxLen}])
decode_line(Bin,MaxLen) ->
- ?line case find_in_binary($\n,Bin) of
- notfound when MaxLen>0 andalso byte_size(Bin) >= MaxLen ->
- {LineB,Rest} = split_binary(Bin,MaxLen),
- {ok,LineB,Rest};
- notfound ->
- {more,undefined};
- Pos when MaxLen>0 andalso Pos > MaxLen ->
- {LineB,Rest} = split_binary(Bin,MaxLen),
- {ok,LineB,Rest};
- Pos ->
- {LineB,Rest} = split_binary(Bin,Pos),
- {ok,LineB,Rest}
+ case find_in_binary($\n,Bin) of
+ notfound when MaxLen>0 andalso byte_size(Bin) >= MaxLen ->
+ {LineB,Rest} = split_binary(Bin,MaxLen),
+ {ok,LineB,Rest};
+ notfound ->
+ {more,undefined};
+ Pos when MaxLen>0 andalso Pos > MaxLen ->
+ {LineB,Rest} = split_binary(Bin,MaxLen),
+ {ok,LineB,Rest};
+ Pos ->
+ {LineB,Rest} = split_binary(Bin,Pos),
+ {ok,LineB,Rest}
end.
find_in_binary(Byte, Bin) ->
case string:chr(binary_to_list(Bin),Byte) of
- 0 -> notfound;
- P -> P
+ 0 -> notfound;
+ P -> P
end.
-ssl(doc) -> [];
-ssl(suite) -> [];
ssl(Config) when is_list(Config) ->
Major = 34,
Minor = 17,
@@ -550,15 +521,15 @@ ssl(Config) when is_list(Config) ->
Rest = <<23,123,203,12,234>>,
F = fun(Content) ->
- {Packet,Unpacked} = pack_ssl(Content, Major, Minor, Body),
- Bin = <<Packet/binary,Rest/binary>>,
- ?line {ok, Unpacked, Rest} = decode_pkt(ssl_tls, Bin)
- end,
+ {Packet,Unpacked} = pack_ssl(Content, Major, Minor, Body),
+ Bin = <<Packet/binary,Rest/binary>>,
+ {ok, Unpacked, Rest} = decode_pkt(ssl_tls, Bin)
+ end,
F(25),
F(v2hello),
ok.
-otp_8536(doc) -> ["Corrupt sub-binary-strings from httph_bin"];
+%% Corrupt sub-binary-strings from httph_bin
otp_8536(Config) when is_list(Config) ->
lists:foreach(fun otp_8536_do/1, lists:seq(1,50)),
ok.
@@ -571,7 +542,7 @@ otp_8536_do(N) ->
Bin = <<Hdr/binary, ": ", Data/binary, "\r\n\r\n">>,
io:format("Bin='~p'\n",[Bin]),
- ?line {ok,{http_header,0,Hdr2,undefined,Data2},<<"\r\n">>} = decode_pkt(httph_bin, Bin, []),
+ {ok,{http_header,0,Hdr2,undefined,Data2},<<"\r\n">>} = decode_pkt(httph_bin, Bin, []),
%% Do something to trash the C-stack, how about another decode_packet:
decode_pkt(httph_bin,<<Letters/binary, ": ", Data/binary, "\r\n\r\n">>, []),
@@ -587,8 +558,7 @@ decode_pkt(Type,Bin,Opts) ->
%%io:format(" -> ~p\n",[Res]),
Res.
-otp_9389(doc) -> ["Verify line_length works correctly for HTTP headers"];
-otp_9389(suite) -> [];
+%% Verify line_length works correctly for HTTP headers
otp_9389(Config) when is_list(Config) ->
Opts = [{packet_size, 16384}, {line_length, 3000}],
Pkt = list_to_binary(["GET / HTTP/1.1\r\nHost: localhost\r\nLink: /",
@@ -596,26 +566,25 @@ otp_9389(Config) when is_list(Config) ->
"\r\nContent-Length: 0\r\n\r\n"]),
<<Pkt1:5000/binary, Pkt2/binary>> = Pkt,
{ok, {http_request,'GET',{abs_path,"/"},{1,1}}, Rest1} =
- erlang:decode_packet(http, Pkt1, Opts),
+ erlang:decode_packet(http, Pkt1, Opts),
{ok, {http_header,_,'Host',_,"localhost"}, Rest2} =
- erlang:decode_packet(httph, Rest1, Opts),
+ erlang:decode_packet(httph, Rest1, Opts),
{more, undefined} = erlang:decode_packet(httph, Rest2, Opts),
{ok, {http_header,_,"Link",_,Link}, Rest3} =
- erlang:decode_packet(httph, list_to_binary([Rest2, Pkt2]), Opts),
+ erlang:decode_packet(httph, list_to_binary([Rest2, Pkt2]), Opts),
true = (length(Link) > 8000),
{ok, {http_header,_,'Content-Length',_,"0"}, <<"\r\n">>} =
- erlang:decode_packet(httph, Rest3, Opts),
+ erlang:decode_packet(httph, Rest3, Opts),
ok.
-otp_9389_line(doc) -> ["Verify packet_size works correctly for line mode"];
-otp_9389_line(suite) -> [];
+%% Verify packet_size works correctly for line mode
otp_9389_line(Config) when is_list(Config) ->
Opts = [{packet_size, 20}],
Line1 = <<"0123456789012345678\n">>,
Line2 = <<"0123456789\n">>,
Line3 = <<"01234567890123456789\n">>,
Pkt = list_to_binary([Line1, Line2, Line3]),
- ?line {ok, Line1, Rest1} = erlang:decode_packet(line, Pkt, Opts),
- ?line {ok, Line2, Rest2} = erlang:decode_packet(line, Rest1, Opts),
- ?line {error, invalid} = erlang:decode_packet(line, Rest2, Opts),
+ {ok, Line1, Rest1} = erlang:decode_packet(line, Pkt, Opts),
+ {ok, Line2, Rest2} = erlang:decode_packet(line, Rest1, Opts),
+ {error, invalid} = erlang:decode_packet(line, Rest2, Opts),
ok.
diff --git a/erts/emulator/test/dgawd_handler.erl b/erts/emulator/test/dgawd_handler.erl
index bba69ef87e..52cdd26427 100644
--- a/erts/emulator/test/dgawd_handler.erl
+++ b/erts/emulator/test/dgawd_handler.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/dirty_nif_SUITE.erl b/erts/emulator/test/dirty_nif_SUITE.erl
new file mode 100644
index 0000000000..a61fd92a18
--- /dev/null
+++ b/erts/emulator/test/dirty_nif_SUITE.erl
@@ -0,0 +1,547 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(dirty_nif_SUITE).
+
+%%-define(line_trace,true).
+-define(CHECK(Exp,Got), check(Exp,Got,?LINE)).
+%%-define(CHECK(Exp,Got), Exp = Got).
+
+-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,
+ dirty_nif/1, dirty_nif_send/1,
+ dirty_nif_exception/1, call_dirty_nif_exception/1,
+ dirty_scheduler_exit/1, dirty_call_while_terminated/1,
+ dirty_heap_access/1, dirty_process_info/1,
+ dirty_process_register/1, dirty_process_trace/1,
+ code_purge/1, dirty_nif_send_traced/1]).
+
+-define(nif_stub,nif_stub_error(?LINE)).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [dirty_nif,
+ dirty_nif_send,
+ dirty_nif_exception,
+ dirty_scheduler_exit,
+ dirty_call_while_terminated,
+ dirty_heap_access,
+ dirty_process_info,
+ dirty_process_register,
+ dirty_process_trace,
+ code_purge,
+ dirty_nif_send_traced].
+
+init_per_suite(Config) ->
+ try erlang:system_info(dirty_cpu_schedulers) of
+ N when is_integer(N), N > 0 ->
+ case lib_loaded() of
+ false ->
+ ok = erlang:load_nif(
+ filename:join(?config(data_dir, Config),
+ "dirty_nif_SUITE"), []);
+ true ->
+ ok
+ end,
+ Config
+ catch _:_ ->
+ {skipped, "No dirty scheduler support"}
+ end.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(Case, Config) ->
+ [{testcase, Case} | Config].
+
+end_per_testcase(_Case, _Config) ->
+ ok.
+
+dirty_nif(Config) when is_list(Config) ->
+ Val1 = 42,
+ Val2 = "Erlang",
+ Val3 = list_to_binary([Val2, 0]),
+ {Val1, Val2, Val3} = call_dirty_nif(Val1, Val2, Val3),
+ LargeArray = lists:duplicate(1000, ok),
+ LargeArray = call_dirty_nif_zero_args(),
+ ok.
+
+dirty_nif_send(Config) when is_list(Config) ->
+ Parent = self(),
+ Pid = spawn_link(fun() ->
+ Self = self(),
+ {ok, Self} = receive_any(),
+ Parent ! {ok, Self}
+ end),
+ {ok, Pid} = send_from_dirty_nif(Pid),
+ {ok, Pid} = receive_any(),
+ ok.
+
+dirty_nif_exception(Config) when is_list(Config) ->
+ try
+ %% this checks that the expected exception occurs when the
+ %% dirty NIF returns the result of enif_make_badarg
+ %% directly
+ call_dirty_nif_exception(1),
+ ct:fail(expected_badarg)
+ catch
+ error:badarg ->
+ [{?MODULE,call_dirty_nif_exception,[1],_}|_] =
+ erlang:get_stacktrace(),
+ ok
+ end,
+ try
+ %% this checks that the expected exception occurs when the
+ %% dirty NIF calls enif_make_badarg at some point but then
+ %% returns a value that isn't an exception
+ call_dirty_nif_exception(0),
+ ct:fail(expected_badarg)
+ catch
+ error:badarg ->
+ [{?MODULE,call_dirty_nif_exception,[0],_}|_] =
+ erlang:get_stacktrace(),
+ ok
+ end,
+ %% this checks that a dirty NIF can raise various terms as
+ %% exceptions
+ ok = nif_raise_exceptions(call_dirty_nif_exception).
+
+nif_raise_exceptions(NifFunc) ->
+ ExcTerms = [{error, test}, "a string", <<"a binary">>,
+ 42, [1,2,3,4,5], [{p,1},{p,2},{p,3}]],
+ lists:foldl(fun(Term, ok) ->
+ try
+ erlang:apply(?MODULE,NifFunc,[Term]),
+ ct:fail({expected,Term})
+ catch
+ error:Term ->
+ [{?MODULE,NifFunc,[Term],_}|_] = erlang:get_stacktrace(),
+ ok
+ end
+ end, ok, ExcTerms).
+
+dirty_scheduler_exit(Config) when is_list(Config) ->
+ {ok, Node} = start_node(Config, "+SDio 1"),
+ Path = proplists:get_value(data_dir, Config),
+ NifLib = filename:join(Path, atom_to_list(?MODULE)),
+ [ok] = mcall(Node,
+ [fun() ->
+ ok = erlang:load_nif(NifLib, []),
+ Start = erlang:monotonic_time(millisecond),
+ ok = test_dirty_scheduler_exit(),
+ End = erlang:monotonic_time(millisecond),
+ io:format("Time=~p ms~n", [End-Start]),
+ ok
+ end]),
+ stop_node(Node),
+ ok.
+
+test_dirty_scheduler_exit() ->
+ process_flag(trap_exit,true),
+ test_dse(10,[]).
+test_dse(0,Pids) ->
+ timer:sleep(100),
+ kill_dse(Pids,[]);
+test_dse(N,Pids) ->
+ Pid = spawn_link(fun dirty_sleeper/0),
+ test_dse(N-1,[Pid|Pids]).
+
+kill_dse([],Killed) ->
+ wait_dse(Killed);
+kill_dse([Pid|Pids],AlreadyKilled) ->
+ exit(Pid,kill),
+ kill_dse(Pids,[Pid|AlreadyKilled]).
+
+wait_dse([]) ->
+ ok;
+wait_dse([Pid|Pids]) ->
+ receive
+ {'EXIT',Pid,Reason} ->
+ killed = Reason
+ end,
+ wait_dse(Pids).
+
+dirty_call_while_terminated(Config) when is_list(Config) ->
+ Me = self(),
+ Bin = list_to_binary(lists:duplicate(4711, $r)),
+ {value, {BinAddr, 4711, 1}} = lists:keysearch(4711, 2,
+ element(2,
+ process_info(self(),
+ binary))),
+ {Dirty, DM} = spawn_opt(fun () ->
+ dirty_call_while_terminated_nif(Me),
+ blipp:blupp(Bin)
+ end,
+ [monitor,link]),
+ receive {dirty_alive, _Pid} -> ok end,
+ {value, {BinAddr, 4711, 2}} = lists:keysearch(4711, 2,
+ element(2,
+ process_info(self(),
+ binary))),
+ Reason = die_dirty_process,
+ OT = process_flag(trap_exit, true),
+ exit(Dirty, Reason),
+ receive
+ {'DOWN', DM, process, Dirty, R0} ->
+ R0 = Reason
+ end,
+ receive
+ {'EXIT', Dirty, R1} ->
+ R1 = Reason
+ end,
+ undefined = process_info(Dirty),
+ undefined = process_info(Dirty, status),
+ false = erlang:is_process_alive(Dirty),
+ false = lists:member(Dirty, processes()),
+ %% Binary still refered by Dirty process not yet cleaned up
+ %% since the dirty nif has not yet returned...
+ {value, {BinAddr, 4711, 2}} = lists:keysearch(4711, 2,
+ element(2,
+ process_info(self(),
+ binary))),
+ receive after 2000 -> ok end,
+ receive
+ Msg ->
+ ct:fail({unexpected_message, Msg})
+ after
+ 0 ->
+ ok
+ end,
+ {value, {BinAddr, 4711, 1}} = lists:keysearch(4711, 2,
+ element(2,
+ process_info(self(),
+ binary))),
+ process_flag(trap_exit, OT),
+ try
+ blipp:blupp(Bin)
+ catch
+ _ : _ -> ok
+ end.
+
+dirty_heap_access(Config) when is_list(Config) ->
+ {ok, Node} = start_node(Config),
+ Me = self(),
+ RGL = rpc:call(Node,erlang,whereis,[init]),
+ Ref = rpc:call(Node,erlang,make_ref,[]),
+ Dirty = spawn_link(fun () ->
+ Res = dirty_heap_access_nif(Ref),
+ garbage_collect(),
+ Me ! {self(), Res},
+ receive after infinity -> ok end
+ end),
+ {N, R} = access_dirty_heap(Dirty, RGL, 0, 0),
+ receive
+ {_Pid, Res} ->
+ 1000 = length(Res),
+ lists:foreach(fun (X) -> Ref = X end, Res)
+ end,
+ unlink(Dirty),
+ exit(Dirty, kill),
+ stop_node(Node),
+ {comment, integer_to_list(N) ++ " GL change loops; "
+ ++ integer_to_list(R) ++ " while running dirty"}.
+
+access_dirty_heap(Dirty, RGL, N, R) ->
+ case process_info(Dirty, status) of
+ {status, waiting} ->
+ {N, R};
+ {status, Status} ->
+ {group_leader, GL} = process_info(Dirty, group_leader),
+ true = group_leader(RGL, Dirty),
+ {group_leader, RGL} = process_info(Dirty, group_leader),
+ true = group_leader(GL, Dirty),
+ {group_leader, GL} = process_info(Dirty, group_leader),
+ access_dirty_heap(Dirty, RGL, N+1, case Status of
+ running ->
+ R+1;
+ _ ->
+ R
+ end)
+ end.
+
+%% These tests verify that processes that access a process executing a
+%% dirty NIF where the main lock is needed for that access do not get
+%% blocked. Each test passes its pid to dirty_sleeper, which sends a
+%% 'ready' message when it's running on a dirty scheduler and just before
+%% it starts a 6 second sleep. When it receives the message, it verifies
+%% that access to the dirty process is as it expects. After the dirty
+%% process finishes its 6 second sleep but before it returns from the dirty
+%% scheduler, it sends a 'done' message. If the tester already received
+%% that message, the test fails because it means attempting to access the
+%% dirty process waited for that process to return to a regular scheduler,
+%% so verify that we haven't received that message, and also verify that
+%% the dirty process is still alive immediately after accessing it.
+dirty_process_info(Config) when is_list(Config) ->
+ access_dirty_process(
+ Config,
+ fun() -> ok end,
+ fun(NifPid) ->
+ PI = process_info(NifPid),
+ {current_function,{?MODULE,dirty_sleeper,1}} =
+ lists:keyfind(current_function, 1, PI),
+ ok
+ end,
+ fun(_) -> ok end).
+
+dirty_process_register(Config) when is_list(Config) ->
+ access_dirty_process(
+ Config,
+ fun() -> ok end,
+ fun(NifPid) ->
+ register(test_dirty_process_register, NifPid),
+ NifPid = whereis(test_dirty_process_register),
+ unregister(test_dirty_process_register),
+ false = lists:member(test_dirty_process_register,
+ registered()),
+ ok
+ end,
+ fun(_) -> ok end).
+
+dirty_process_trace(Config) when is_list(Config) ->
+ access_dirty_process(
+ Config,
+ fun() ->
+ erlang:trace_pattern({?MODULE,dirty_sleeper,1},
+ [{'_',[],[{return_trace}]}],
+ [local,meta]),
+ ok
+ end,
+ fun(NifPid) ->
+ erlang:trace(NifPid, true, [call,timestamp]),
+ ok
+ end,
+ fun(NifPid) ->
+ receive
+ done ->
+ receive
+ {trace_ts,NifPid,call,{?MODULE,dirty_sleeper,_},_} ->
+ ok
+ after
+ 0 ->
+ error(missing_trace_call_message)
+ end,
+ receive
+ {trace_ts,NifPid,return_from,{?MODULE,dirty_sleeper,1},
+ ok,_} ->
+ ok
+ after
+ 100 ->
+ error(missing_trace_return_message)
+ end
+ after
+ 6500 ->
+ error(missing_done_message)
+ end,
+ ok
+ end).
+
+dirty_code_test_code() ->
+ "
+-module(dirty_code_test).
+
+-export([func/1]).
+
+func(Fun) ->
+ Fun(),
+ blipp:blapp().
+
+".
+
+code_purge(Config) when is_list(Config) ->
+ Path = ?config(data_dir, Config),
+ File = filename:join(Path, "dirty_code_test.erl"),
+ ok = file:write_file(File, dirty_code_test_code()),
+ {ok, dirty_code_test, Bin} = compile:file(File, [binary]),
+ {module, dirty_code_test} = erlang:load_module(dirty_code_test, Bin),
+ Start = erlang:monotonic_time(),
+ {Pid1, Mon1} = spawn_monitor(fun () ->
+ dirty_code_test:func(fun () ->
+ %% Sleep for 6 seconds
+ %% in dirty nif...
+ dirty_sleeper()
+ end)
+ end),
+ {module, dirty_code_test} = erlang:load_module(dirty_code_test, Bin),
+ {Pid2, Mon2} = spawn_monitor(fun () ->
+ dirty_code_test:func(fun () ->
+ %% Sleep for 6 seconds
+ %% in dirty nif...
+ dirty_sleeper()
+ end)
+ end),
+ receive
+ {'DOWN', Mon1, process, Pid1, _} ->
+ ct:fail(premature_death)
+ after 100 ->
+ ok
+ end,
+ true = erlang:purge_module(dirty_code_test),
+ receive
+ {'DOWN', Mon1, process, Pid1, Reason1} ->
+ killed = Reason1
+ end,
+ receive
+ {'DOWN', Mon2, process, Pid2, _} ->
+ ct:fail(premature_death)
+ after 100 ->
+ ok
+ end,
+ true = erlang:delete_module(dirty_code_test),
+ receive
+ {'DOWN', Mon2, process, Pid2, _} ->
+ ct:fail(premature_death)
+ after 100 ->
+ ok
+ end,
+ true = erlang:purge_module(dirty_code_test),
+ receive
+ {'DOWN', Mon2, process, Pid2, Reason2} ->
+ killed = Reason2
+ end,
+ End = erlang:monotonic_time(),
+ Time = erlang:convert_time_unit(End-Start, native, milli_seconds),
+ io:format("Time=~p~n", [Time]),
+ true = Time =< 1000,
+ ok.
+
+dirty_nif_send_traced(Config) when is_list(Config) ->
+ Parent = self(),
+ Rcvr = spawn_link(fun() ->
+ Self = self(),
+ receive {ok, Self} -> ok end,
+ Parent ! {Self, received}
+ end),
+ Sndr = spawn_link(fun () ->
+ receive {Parent, go} -> ok end,
+ {ok, Rcvr} = send_wait_from_dirty_nif(Rcvr),
+ Parent ! {self(), sent}
+ end),
+ 1 = erlang:trace(Sndr, true, [send]),
+ Start = erlang:monotonic_time(),
+ Sndr ! {self(), go},
+ receive {trace, Sndr, send, {ok, Rcvr}, Rcvr} -> ok end,
+ receive {Rcvr, received} -> ok end,
+ End1 = erlang:monotonic_time(),
+ Time1 = erlang:convert_time_unit(End1-Start, native, 1000),
+ io:format("Time1: ~p milliseconds~n", [Time1]),
+ true = Time1 < 500,
+ receive {Sndr, sent} -> ok end,
+ End2 = erlang:monotonic_time(),
+ Time2 = erlang:convert_time_unit(End2-Start, native, 1000),
+ io:format("Time2: ~p milliseconds~n", [Time2]),
+ true = Time2 >= 1900,
+ ok.
+
+%%
+%% Internal...
+%%
+
+access_dirty_process(Config, Start, Test, Finish) ->
+ {ok, Node} = start_node(Config, ""),
+ [ok] = mcall(Node,
+ [fun() ->
+ Path = ?config(data_dir, Config),
+ Lib = atom_to_list(?MODULE),
+ ok = erlang:load_nif(filename:join(Path,Lib), []),
+ ok = test_dirty_process_access(Start, Test, Finish)
+ end]),
+ stop_node(Node),
+ ok.
+
+test_dirty_process_access(Start, Test, Finish) ->
+ ok = Start(),
+ Self = self(),
+ NifPid = spawn_link(fun() ->
+ ok = dirty_sleeper(Self)
+ end),
+ ok = receive
+ ready ->
+ ok = Test(NifPid),
+ receive
+ done ->
+ error(dirty_process_info_blocked)
+ after
+ 0 ->
+ true = erlang:is_process_alive(NifPid),
+ ok
+ end
+ after
+ 3000 ->
+ error(timeout)
+ end,
+ ok = Finish(NifPid).
+
+receive_any() ->
+ receive M -> M end.
+
+start_node(Config) ->
+ start_node(Config, "").
+
+start_node(Config, Args) when is_list(Config) ->
+ Pa = filename:dirname(code:which(?MODULE)),
+ Name = list_to_atom(atom_to_list(?MODULE)
+ ++ "-"
+ ++ atom_to_list(proplists:get_value(testcase, Config))
+ ++ "-"
+ ++ integer_to_list(erlang:system_time(second))
+ ++ "-"
+ ++ integer_to_list(erlang:unique_integer([positive]))),
+ test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]).
+
+stop_node(Node) ->
+ test_server:stop_node(Node).
+
+mcall(Node, Funs) ->
+ Parent = self(),
+ Refs = lists:map(fun (Fun) ->
+ Ref = make_ref(),
+ spawn_link(Node,
+ fun () ->
+ Res = Fun(),
+ unlink(Parent),
+ Parent ! {Ref, Res}
+ end),
+ Ref
+ end, Funs),
+ lists:map(fun (Ref) ->
+ receive
+ {Ref, Res} ->
+ Res
+ end
+ end, Refs).
+
+%% The NIFs:
+lib_loaded() -> false.
+call_dirty_nif(_,_,_) -> ?nif_stub.
+send_from_dirty_nif(_) -> ?nif_stub.
+send_wait_from_dirty_nif(_) -> ?nif_stub.
+call_dirty_nif_exception(_) -> ?nif_stub.
+call_dirty_nif_zero_args() -> ?nif_stub.
+dirty_call_while_terminated_nif(_) -> ?nif_stub.
+dirty_sleeper() -> ?nif_stub.
+dirty_sleeper(_) -> ?nif_stub.
+dirty_heap_access_nif(_) -> ?nif_stub.
+
+nif_stub_error(Line) ->
+ exit({nif_not_loaded,module,?MODULE,line,Line}).
diff --git a/erts/emulator/test/dirty_nif_SUITE_data/Makefile.src b/erts/emulator/test/dirty_nif_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..e9301753b0
--- /dev/null
+++ b/erts/emulator/test/dirty_nif_SUITE_data/Makefile.src
@@ -0,0 +1,6 @@
+
+NIF_LIBS = dirty_nif_SUITE@dll@
+
+all: $(NIF_LIBS)
+
+@SHLIB_RULES@
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
new file mode 100644
index 0000000000..08efa23c81
--- /dev/null
+++ b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c
@@ -0,0 +1,275 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2009-2016. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+#include "erl_nif.h"
+#include <assert.h>
+#ifdef __WIN32__
+#include <windows.h>
+#else
+#include <unistd.h>
+#endif
+
+static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
+{
+ return 0;
+}
+
+static ERL_NIF_TERM lib_loaded(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ return enif_make_atom(env, "true");
+}
+
+static int have_dirty_schedulers(void)
+{
+ ErlNifSysInfo si;
+ enif_system_info(&si, sizeof(si));
+ return si.dirty_scheduler_support;
+}
+
+static ERL_NIF_TERM dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ int n;
+ char s[10];
+ ErlNifBinary b;
+ if (have_dirty_schedulers()) {
+ assert(ERL_NIF_THR_DIRTY_CPU_SCHEDULER == enif_thread_type()
+ || ERL_NIF_THR_DIRTY_IO_SCHEDULER == enif_thread_type());
+ }
+ assert(argc == 3);
+ enif_get_int(env, argv[0], &n);
+ enif_get_string(env, argv[1], s, sizeof s, ERL_NIF_LATIN1);
+ enif_inspect_binary(env, argv[2], &b);
+ return enif_make_tuple3(env,
+ enif_make_int(env, n),
+ enif_make_string(env, s, ERL_NIF_LATIN1),
+ enif_make_binary(env, &b));
+}
+
+static ERL_NIF_TERM call_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ int n;
+ char s[10];
+ ErlNifBinary b;
+ assert(ERL_NIF_THR_NORMAL_SCHEDULER == enif_thread_type());
+ if (argc != 3)
+ return enif_make_badarg(env);
+ if (have_dirty_schedulers()) {
+ if (enif_get_int(env, argv[0], &n) &&
+ enif_get_string(env, argv[1], s, sizeof s, ERL_NIF_LATIN1) &&
+ enif_inspect_binary(env, argv[2], &b))
+ return enif_schedule_nif(env, "call_dirty_nif", ERL_NIF_DIRTY_JOB_CPU_BOUND, dirty_nif, argc, argv);
+ else
+ return enif_make_badarg(env);
+ } else {
+ return dirty_nif(env, argc, argv);
+ }
+}
+
+static ERL_NIF_TERM send_from_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ERL_NIF_TERM result;
+ ErlNifPid pid;
+ ErlNifEnv* menv;
+ int res;
+
+ if (!enif_get_local_pid(env, argv[0], &pid))
+ return enif_make_badarg(env);
+ result = enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_pid(env, &pid));
+ menv = enif_alloc_env();
+ res = enif_send(env, &pid, menv, result);
+ enif_free_env(menv);
+ if (!res)
+ return enif_make_badarg(env);
+ else
+ return result;
+}
+
+static ERL_NIF_TERM send_wait_from_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ERL_NIF_TERM result;
+ ErlNifPid pid;
+ ErlNifEnv* menv;
+ int res;
+
+ if (!enif_get_local_pid(env, argv[0], &pid))
+ return enif_make_badarg(env);
+ result = enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_pid(env, &pid));
+ menv = enif_alloc_env();
+ res = enif_send(env, &pid, menv, result);
+ enif_free_env(menv);
+
+#ifdef __WIN32__
+ Sleep(2000);
+#else
+ sleep(2);
+#endif
+
+ if (!res)
+ return enif_make_badarg(env);
+ else
+ return result;
+}
+
+static ERL_NIF_TERM call_dirty_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ switch (argc) {
+ case 1: {
+ int arg;
+ if (enif_get_int(env, argv[0], &arg) && arg < 2) {
+ ERL_NIF_TERM args[255];
+ int i;
+ args[0] = argv[0];
+ for (i = 1; i < 255; i++)
+ args[i] = enif_make_int(env, i);
+ return enif_schedule_nif(env, "call_dirty_nif_exception", ERL_NIF_DIRTY_JOB_CPU_BOUND,
+ call_dirty_nif_exception, 255, args);
+ } else {
+ return enif_raise_exception(env, argv[0]);
+ }
+ }
+ case 2: {
+ int return_badarg_directly;
+ enif_get_int(env, argv[0], &return_badarg_directly);
+ assert(return_badarg_directly == 1 || return_badarg_directly == 0);
+ if (return_badarg_directly)
+ return enif_make_badarg(env);
+ else {
+ /* ignore return value */ enif_make_badarg(env);
+ return enif_make_atom(env, "ok");
+ }
+ }
+ default:
+ return enif_schedule_nif(env, "call_dirty_nif_exception", ERL_NIF_DIRTY_JOB_CPU_BOUND,
+ call_dirty_nif_exception, argc-1, argv);
+ }
+}
+
+static ERL_NIF_TERM call_dirty_nif_zero_args(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ int i;
+ ERL_NIF_TERM result[1000];
+ ERL_NIF_TERM ok = enif_make_atom(env, "ok");
+ assert(argc == 0);
+ for (i = 0; i < sizeof(result)/sizeof(*result); i++) {
+ result[i] = ok;
+ }
+ return enif_make_list_from_array(env, result, i);
+}
+
+static ERL_NIF_TERM
+dirty_sleeper(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifPid pid;
+ ErlNifEnv* msg_env = NULL;
+
+ assert(ERL_NIF_THR_DIRTY_CPU_SCHEDULER == enif_thread_type()
+ || ERL_NIF_THR_DIRTY_IO_SCHEDULER == enif_thread_type());
+
+ /* If we get a pid argument, it indicates a process involved in the
+ test wants a message from us. Prior to the sleep we send a 'ready'
+ message, and then after the sleep, send a 'done' message. */
+ if (argc == 1 && enif_get_local_pid(env, argv[0], &pid)) {
+ msg_env = enif_alloc_env();
+ enif_send(env, &pid, msg_env, enif_make_atom(msg_env, "ready"));
+ }
+
+#ifdef __WIN32__
+ Sleep(6000);
+#else
+ sleep(6);
+#endif
+
+ if (argc == 1) {
+ assert(msg_env != NULL);
+ enif_send(env, &pid, msg_env, enif_make_atom(msg_env, "done"));
+ enif_free_env(msg_env);
+ }
+
+ return enif_make_atom(env, "ok");
+}
+
+static ERL_NIF_TERM dirty_call_while_terminated_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifPid self;
+ ERL_NIF_TERM result, self_term;
+ ErlNifPid to;
+ ErlNifEnv* menv;
+ int res;
+
+ if (!enif_get_local_pid(env, argv[0], &to))
+ return enif_make_badarg(env);
+
+ if (!enif_self(env, &self))
+ return enif_make_badarg(env);
+
+ self_term = enif_make_pid(env, &self);
+
+ result = enif_make_tuple2(env, enif_make_atom(env, "dirty_alive"), self_term);
+ menv = enif_alloc_env();
+ res = enif_send(env, &to, menv, result);
+ enif_free_env(menv);
+ if (!res)
+ return enif_make_badarg(env);
+
+ /* Wait until we have been killed */
+ while (enif_is_process_alive(env, &self))
+ ;
+
+ result = enif_make_tuple2(env, enif_make_atom(env, "dirty_dead"), self_term);
+ menv = enif_alloc_env();
+ res = enif_send(env, &to, menv, result);
+ enif_free_env(menv);
+
+#ifdef __WIN32__
+ Sleep(1000);
+#else
+ sleep(1);
+#endif
+
+ return enif_make_atom(env, "ok");
+}
+
+static ERL_NIF_TERM dirty_heap_access_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ERL_NIF_TERM res = enif_make_list(env, 0);
+ int i;
+ assert(ERL_NIF_THR_DIRTY_CPU_SCHEDULER == enif_thread_type()
+ || ERL_NIF_THR_DIRTY_IO_SCHEDULER == enif_thread_type());
+ for (i = 0; i < 1000; i++)
+ res = enif_make_list_cell(env, enif_make_copy(env, argv[0]), res);
+
+ return res;
+}
+
+
+static ErlNifFunc nif_funcs[] =
+{
+ {"lib_loaded", 0, lib_loaded},
+ {"call_dirty_nif", 3, call_dirty_nif},
+ {"send_from_dirty_nif", 1, send_from_dirty_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND},
+ {"send_wait_from_dirty_nif", 1, send_wait_from_dirty_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"call_dirty_nif_exception", 1, call_dirty_nif_exception, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"call_dirty_nif_zero_args", 0, call_dirty_nif_zero_args, ERL_NIF_DIRTY_JOB_CPU_BOUND},
+ {"dirty_sleeper", 0, dirty_sleeper, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"dirty_sleeper", 1, dirty_sleeper, ERL_NIF_DIRTY_JOB_CPU_BOUND},
+ {"dirty_call_while_terminated_nif", 1, dirty_call_while_terminated_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND},
+ {"dirty_heap_access_nif", 1, dirty_heap_access_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND}
+};
+
+ERL_NIF_INIT(dirty_nif_SUITE,nif_funcs,load,NULL,NULL,NULL)
diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl
index 151fab0a0e..6994bfef83 100644
--- a/erts/emulator/test/distribution_SUITE.erl
+++ b/erts/emulator/test/distribution_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -33,43 +33,46 @@
%% Tests distribution and the tcp driver.
--include_lib("test_server/include/test_server.hrl").
-
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- ping/1, bulk_send_small/1,
- bulk_send_big/1, bulk_send_bigbig/1,
- local_send_small/1, local_send_big/1,
- local_send_legal/1, link_to_busy/1, exit_to_busy/1,
- lost_exit/1, link_to_dead/1, link_to_dead_new_node/1,
- applied_monitor_node/1, ref_port_roundtrip/1, nil_roundtrip/1,
- trap_bif_1/1, trap_bif_2/1, trap_bif_3/1,
- stop_dist/1,
- dist_auto_connect_never/1, dist_auto_connect_once/1,
- dist_parallel_send/1,
- atom_roundtrip/1,
- unicode_atom_roundtrip/1,
- atom_roundtrip_r15b/1,
- contended_atom_cache_entry/1,
- contended_unicode_atom_cache_entry/1,
- bad_dist_structure/1,
- bad_dist_ext_receive/1,
- bad_dist_ext_process_info/1,
- bad_dist_ext_control/1,
- bad_dist_ext_connection_id/1]).
-
--export([init_per_testcase/2, end_per_testcase/2]).
+-include_lib("common_test/include/ct.hrl").
+
+-export([all/0, suite/0, groups/0,
+ ping/1, bulk_send_small/1,
+ bulk_send_big/1, bulk_send_bigbig/1,
+ local_send_small/1, local_send_big/1,
+ local_send_legal/1, link_to_busy/1, exit_to_busy/1,
+ lost_exit/1, link_to_dead/1, link_to_dead_new_node/1,
+ applied_monitor_node/1, ref_port_roundtrip/1, nil_roundtrip/1,
+ trap_bif_1/1, trap_bif_2/1, trap_bif_3/1,
+ stop_dist/1,
+ dist_auto_connect_never/1, dist_auto_connect_once/1,
+ dist_parallel_send/1,
+ atom_roundtrip/1,
+ unicode_atom_roundtrip/1,
+ atom_roundtrip_r15b/1,
+ contended_atom_cache_entry/1,
+ contended_unicode_atom_cache_entry/1,
+ bad_dist_structure/1,
+ bad_dist_ext_receive/1,
+ bad_dist_ext_process_info/1,
+ bad_dist_ext_control/1,
+ bad_dist_ext_connection_id/1,
+ start_epmd_false/1, epmd_module/1]).
%% Internal exports.
-export([sender/3, receiver2/2, dummy_waiter/0, dead_process/0,
- roundtrip/1, bounce/1, do_dist_auto_connect/1, inet_rpc_server/1,
- dist_parallel_sender/3, dist_parallel_receiver/0,
- dist_evil_parallel_receiver/0,
+ roundtrip/1, bounce/1, do_dist_auto_connect/1, inet_rpc_server/1,
+ dist_parallel_sender/3, dist_parallel_receiver/0,
+ dist_evil_parallel_receiver/0,
sendersender/4, sendersender2/4]).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+%% epmd_module exports
+-export([start_link/0, register_node/2, register_node/3, port_please/2]).
-all() ->
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 4}}].
+
+all() ->
[ping, {group, bulk_send}, {group, local_send},
link_to_busy, exit_to_busy, lost_exit, link_to_dead,
link_to_dead_new_node, applied_monitor_node,
@@ -77,9 +80,10 @@ all() ->
{group, trap_bif}, {group, dist_auto_connect},
dist_parallel_send, atom_roundtrip, unicode_atom_roundtrip, atom_roundtrip_r15b,
contended_atom_cache_entry, contended_unicode_atom_cache_entry,
- bad_dist_structure, {group, bad_dist_ext}].
+ bad_dist_structure, {group, bad_dist_ext},
+ start_epmd_false, epmd_module].
-groups() ->
+groups() ->
[{bulk_send, [], [bulk_send_small, bulk_send_big, bulk_send_bigbig]},
{local_send, [],
[local_send_small, local_send_big, local_send_legal]},
@@ -90,80 +94,54 @@ groups() ->
[bad_dist_ext_receive, bad_dist_ext_process_info,
bad_dist_ext_control, bad_dist_ext_connection_id]}].
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
--define(DEFAULT_TIMETRAP, 4*60*1000).
-
-init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- Dog=?t:timetrap(?DEFAULT_TIMETRAP),
- [{watchdog, Dog},{testcase, Func}|Config].
-
-end_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- Dog=?config(watchdog, Config),
- ?t:timetrap_cancel(Dog).
-
-ping(doc) ->
- ["Tests pinging a node in different ways."];
+%% Tests pinging a node in different ways.
ping(Config) when is_list(Config) ->
Times = 1024,
%% Ping a non-existing node many times. This used to crash the emulator
%% on Windows.
- ?line Host = hostname(),
- ?line BadName = list_to_atom("__pucko__@" ++ Host),
- ?line io:format("Pinging ~s (assumed to not exist)", [BadName]),
- ?line test_server:do_times(Times, fun() -> pang = net_adm:ping(BadName)
- end),
+ Host = hostname(),
+ BadName = list_to_atom("__pucko__@" ++ Host),
+ io:format("Pinging ~s (assumed to not exist)", [BadName]),
+ test_server:do_times(Times, fun() -> pang = net_adm:ping(BadName)
+ end),
%% Pings another node.
- ?line {ok, OtherNode} = start_node(distribution_SUITE_other),
- ?line io:format("Pinging ~s (assumed to exist)", [OtherNode]),
- ?line test_server:do_times(Times, fun() -> pong = net_adm:ping(OtherNode) end),
- ?line stop_node(OtherNode),
+ {ok, OtherNode} = start_node(distribution_SUITE_other),
+ io:format("Pinging ~s (assumed to exist)", [OtherNode]),
+ test_server:do_times(Times, fun() -> pong = net_adm:ping(OtherNode) end),
+ stop_node(OtherNode),
%% Pings our own node many times.
- ?line Node = node(),
- ?line io:format("Pinging ~s (the same node)", [Node]),
- ?line test_server:do_times(Times, fun() -> pong = net_adm:ping(Node) end),
+ Node = node(),
+ io:format("Pinging ~s (the same node)", [Node]),
+ test_server:do_times(Times, fun() -> pong = net_adm:ping(Node) end),
ok.
bulk_send_small(Config) when is_list(Config) ->
- ?line bulk_send(64, 32).
+ bulk_send(64, 32).
bulk_send_big(Config) when is_list(Config) ->
- ?line bulk_send(32, 64).
+ bulk_send(32, 64).
bulk_send_bigbig(Config) when is_list(Config) ->
- ?line bulk_sendsend(32*5, 4).
+ bulk_sendsend(32*5, 4).
bulk_send(Terms, BinSize) ->
- ?line Dog = test_server:timetrap(test_server:seconds(30)),
-
- ?line io:format("Sending ~w binaries, each of size ~w K",
- [Terms, BinSize]),
- ?line {ok, Node} = start_node(bulk_receiver),
- ?line Recv = spawn(Node, erlang, apply, [fun receiver/2, [0, 0]]),
- ?line Bin = list_to_binary(lists:duplicate(BinSize*1024, 253)),
- ?line Size = Terms*size(Bin),
- ?line {Elapsed, {Terms, Size}} = test_server:timecall(?MODULE, sender,
- [Recv, Bin, Terms]),
- ?line stop_node(Node),
-
- ?line test_server:timetrap_cancel(Dog),
+ ct:timetrap({seconds, 30}),
+
+ io:format("Sending ~w binaries, each of size ~w K", [Terms, BinSize]),
+ {ok, Node} = start_node(bulk_receiver),
+ Recv = spawn(Node, erlang, apply, [fun receiver/2, [0, 0]]),
+ Bin = list_to_binary(lists:duplicate(BinSize*1024, 253)),
+ Size = Terms*size(Bin),
+ {Elapsed, {Terms, Size}} = test_server:timecall(?MODULE, sender,
+ [Recv, Bin, Terms]),
+ stop_node(Node),
{comment, integer_to_list(trunc(Size/1024/max(1,Elapsed)+0.5)) ++ " K/s"}.
bulk_sendsend(Terms, BinSize) ->
@@ -173,29 +151,29 @@ bulk_sendsend(Terms, BinSize) ->
true -> MonitorCount1 / MonitorCount2
end,
Comment = integer_to_list(Rate1) ++ " K/s, " ++
- integer_to_list(Rate2) ++ " K/s, " ++
- integer_to_list(MonitorCount1) ++ " monitor msgs, " ++
- integer_to_list(MonitorCount2) ++ " monitor msgs, " ++
- float_to_list(Ratio) ++ " monitor ratio",
+ integer_to_list(Rate2) ++ " K/s, " ++
+ integer_to_list(MonitorCount1) ++ " monitor msgs, " ++
+ integer_to_list(MonitorCount2) ++ " monitor msgs, " ++
+ float_to_list(Ratio) ++ " monitor ratio",
if
- %% A somewhat arbitrary ratio, but hopefully one that will
- %% accommodate a wide range of CPU speeds.
- Ratio > 8.0 ->
- {comment,Comment};
- true ->
- io:put_chars(Comment),
- ?line ?t:fail(ratio_too_low)
+ %% A somewhat arbitrary ratio, but hopefully one that will
+ %% accommodate a wide range of CPU speeds.
+ Ratio > 8.0 ->
+ {comment,Comment};
+ true ->
+ io:put_chars(Comment),
+ ct:fail(ratio_too_low)
end.
bulk_sendsend2(Terms, BinSize, BusyBufSize) ->
- ?line Dog = test_server:timetrap(test_server:seconds(30)),
+ ct:timetrap({seconds, 30}),
- ?line io:format("Sending ~w binaries, each of size ~w K",
- [Terms, BinSize]),
- ?line {ok, NodeRecv} = start_node(bulk_receiver),
- ?line Recv = spawn(NodeRecv, erlang, apply, [fun receiver/2, [0, 0]]),
- ?line Bin = list_to_binary(lists:duplicate(BinSize*1024, 253)),
- %%?line Size = Terms*size(Bin),
+ io:format("Sending ~w binaries, each of size ~w K",
+ [Terms, BinSize]),
+ {ok, NodeRecv} = start_node(bulk_receiver),
+ Recv = spawn(NodeRecv, erlang, apply, [fun receiver/2, [0, 0]]),
+ Bin = list_to_binary(lists:duplicate(BinSize*1024, 253)),
+ %%Size = Terms*size(Bin),
%% SLF LEFT OFF HERE.
%% When the caller uses small hunks, like 4k via
@@ -206,23 +184,26 @@ bulk_sendsend2(Terms, BinSize, BusyBufSize) ->
%% default busy size and "+zdbbl 5", and if the 5 case gets
%% "many many more" monitor messages, then we know we're working.
- ?line {ok, NodeSend} = start_node(bulk_sender, "+zdbbl " ++ integer_to_list(BusyBufSize)),
- ?line _Send = spawn(NodeSend, erlang, apply, [fun sendersender/4, [self(), Recv, Bin, Terms]]),
- ?line {Elapsed, {_TermsN, SizeN}, MonitorCount} =
- receive {sendersender, BigRes} ->
- BigRes
- end,
- ?line stop_node(NodeRecv),
- ?line stop_node(NodeSend),
-
- ?line test_server:timetrap_cancel(Dog),
+ {ok, NodeSend} = start_node(bulk_sender, "+zdbbl " ++ integer_to_list(BusyBufSize)),
+ _Send = spawn(NodeSend, erlang, apply, [fun sendersender/4, [self(), Recv, Bin, Terms]]),
+ {Elapsed, {_TermsN, SizeN}, MonitorCount} =
+ receive
+ %% On some platforms (windows), the time taken is 0 so we
+ %% simulate that some little time has passed.
+ {sendersender, {0.0,T,MC}} ->
+ {0.0015, T, MC};
+ {sendersender, BigRes} ->
+ BigRes
+ end,
+ stop_node(NodeRecv),
+ stop_node(NodeSend),
{trunc(SizeN/1024/Elapsed+0.5), MonitorCount}.
sender(To, _Bin, 0) ->
To ! {done, self()},
receive
- Any ->
- Any
+ Any ->
+ Any
end;
sender(To, Bin, Left) ->
To ! {term, Bin},
@@ -233,9 +214,9 @@ sender(To, Bin, Left) ->
sendersender(Parent, To, Bin, Left) ->
erlang:system_monitor(self(), [busy_dist_port]),
[spawn(fun() -> sendersender2(To, Bin, Left, false) end) ||
- _ <- lists:seq(1,1)],
+ _ <- lists:seq(1,1)],
{USec, {Res, MonitorCount}} =
- timer:tc(?MODULE, sendersender2, [To, Bin, Left, true]),
+ timer:tc(?MODULE, sendersender2, [To, Bin, Left, true]),
Parent ! {sendersender, {USec/1000000, Res, MonitorCount}}.
sendersender2(To, Bin, Left, SendDone) ->
@@ -243,22 +224,22 @@ sendersender2(To, Bin, Left, SendDone) ->
sendersender3(To, _Bin, 0, SendDone, MonitorCount) ->
if SendDone ->
- To ! {done, self()};
+ To ! {done, self()};
true ->
- ok
+ ok
end,
receive
{monitor, _Pid, _Type, _Info} ->
sendersender3(To, _Bin, 0, SendDone, MonitorCount + 1)
after 0 ->
- if SendDone ->
- receive
- Any when is_tuple(Any), size(Any) == 2 ->
- {Any, MonitorCount}
- end;
- true ->
- exit(normal)
- end
+ if SendDone ->
+ receive
+ Any when is_tuple(Any), size(Any) == 2 ->
+ {Any, MonitorCount}
+ end;
+ true ->
+ exit(normal)
+ end
end;
sendersender3(To, Bin, Left, SendDone, MonitorCount) ->
To ! {term, Bin},
@@ -269,80 +250,74 @@ sendersender3(To, Bin, Left, SendDone, MonitorCount) ->
receiver(Terms, Size) ->
receive
- {term, Bin} ->
- receiver(Terms+1, Size+size(Bin));
- {done, ReplyTo} ->
- ReplyTo ! {Terms, Size}
+ {term, Bin} ->
+ receiver(Terms+1, Size+size(Bin));
+ {done, ReplyTo} ->
+ ReplyTo ! {Terms, Size}
end.
-local_send_big(doc) ->
- ["Sends several big message to an non-registered process on ",
- "the local node."];
+%% Sends several big message to an non-registered process on the local node.
local_send_big(Config) when is_list(Config) ->
- Data0=local_send_big(doc)++
- ["Tests sending small and big messages to a non-existing ",
- "local registered process."],
+ Data0= ["Tests sending small and big messages to a non-existing ",
+ "local registered process."],
Data1=[Data0,[Data0, Data0, [Data0], Data0],Data0],
Data2=Data0++lists:flatten(Data1)++
- list_to_binary(lists:flatten(Data1)),
+ list_to_binary(lists:flatten(Data1)),
Func=fun() -> Data2= {arbitrary_name, node()} ! Data2 end,
- ?line test_server:do_times(4096, Func),
+ test_server:do_times(4096, Func),
ok.
-local_send_small(doc) ->
- ["Sends a small message to an non-registered process on the ",
- "local node."];
+%% Sends a small message to an non-registered process on the local node.
local_send_small(Config) when is_list(Config) ->
Data={some_stupid, "arbitrary", 'Data'},
Func=fun() -> Data= {unregistered_name, node()} ! Data end,
- ?line test_server:do_times(4096, Func),
+ test_server:do_times(4096, Func),
ok.
-local_send_legal(doc) ->
- ["Sends data to a registered process on the local node, ",
- "as if it was on another node."];
+%% Sends data to a registered process on the local node, as if it was on another node.
local_send_legal(Config) when is_list(Config) ->
Times=16384,
- Data={local_send_legal(doc), local_send_legal(doc)},
+ Txt = "Some Not so random Data",
+ Data={[Txt,Txt,Txt], [Txt,Txt,Txt]},
Pid=spawn(?MODULE,receiver2, [0, 0]) ,
- ?line true=register(registered_process, Pid),
+ true=register(registered_process, Pid),
Func=fun() -> Data={registered_process, node()} ! Data end,
TotalSize=size(Data)*Times,
- ?line test_server:do_times(Times, Func),
+ test_server:do_times(Times, Func),
% Check that all msgs really came through.
Me=self(),
- ?line {done, Me}=
- {registered_process, node()} ! {done, Me},
+ {done, Me}=
+ {registered_process, node()} ! {done, Me},
receive
- {Times, TotalSize} ->
- ok;
- _ ->
- test_server:fail("Wrong number of msgs received.")
+ {Times, TotalSize} ->
+ ok;
+ _ ->
+ ct:fail("Wrong number of msgs received.")
end,
ok.
receiver2(Num, TotSize) ->
receive
- {done, ReplyTo} ->
- ReplyTo ! {Num, TotSize};
- Stuff ->
- receiver2(Num+1, TotSize+size(Stuff))
+ {done, ReplyTo} ->
+ ReplyTo ! {Num, TotSize};
+ Stuff ->
+ receiver2(Num+1, TotSize+size(Stuff))
end.
-link_to_busy(doc) -> "Test that link/1 to a busy distribution port works.";
+%% Test that link/1 to a busy distribution port works.
link_to_busy(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(60)),
- ?line {ok, Node} = start_node(link_to_busy),
- ?line Recv = spawn(Node, erlang, apply, [fun sink/1, [link_to_busy_sink]]),
+ ct:timetrap({seconds, 60}),
+ {ok, Node} = start_node(link_to_busy),
+ Recv = spawn(Node, erlang, apply, [fun sink/1, [link_to_busy_sink]]),
Tracer = case os:getenv("TRACE_BUSY_DIST_PORT") of
- "true" -> start_busy_dist_port_tracer();
- _ -> false
- end,
+ "true" -> start_busy_dist_port_tracer();
+ _ -> false
+ end,
%% We will spawn off a process which will try to link to the other
%% node. The linker process will not actually run until this
@@ -351,20 +326,19 @@ link_to_busy(Config) when is_list(Config) ->
%% process will block, too, because of the because busy port,
%% and will later be restarted.
- ?line do_busy_test(Node, fun () -> linker(Recv) end),
+ do_busy_test(Node, fun () -> linker(Recv) end),
%% Same thing, but we apply link/1 instead of calling it directly.
- ?line do_busy_test(Node, fun () -> applied_linker(Recv) end),
+ do_busy_test(Node, fun () -> applied_linker(Recv) end),
%% Same thing again, but we apply link/1 in the tail of a function.
- ?line do_busy_test(Node, fun () -> tail_applied_linker(Recv) end),
+ do_busy_test(Node, fun () -> tail_applied_linker(Recv) end),
%% Done.
- ?line stop_node(Node),
- ?line stop_busy_dist_port_tracer(Tracer),
- ?line test_server:timetrap_cancel(Dog),
+ stop_node(Node),
+ stop_busy_dist_port_tracer(Tracer),
ok.
linker(Pid) ->
@@ -379,16 +353,16 @@ applied_linker(Pid) ->
tail_applied_linker(Pid) ->
apply(erlang, link, [Pid]).
-
-exit_to_busy(doc) -> "Test that exit/2 to a busy distribution port works.";
+
+%% Test that exit/2 to a busy distribution port works.
exit_to_busy(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(60)),
- ?line {ok, Node} = start_node(exit_to_busy),
+ ct:timetrap({seconds, 60}),
+ {ok, Node} = start_node(exit_to_busy),
Tracer = case os:getenv("TRACE_BUSY_DIST_PORT") of
- "true" -> start_busy_dist_port_tracer();
- _ -> false
- end,
+ "true" -> start_busy_dist_port_tracer();
+ _ -> false
+ end,
%% We will spawn off a process which will try to exit a process on
%% the other node. That process will not actually run until this
@@ -397,59 +371,58 @@ exit_to_busy(Config) when is_list(Config) ->
%% too, because of the busy distribution port, and will be allowed
%% to continue when the port becomes non-busy.
- ?line Recv1 = spawn(Node, fun () -> sink(exit_to_busy_sink) end),
- ?line M1 = erlang:monitor(process, Recv1),
- ?line do_busy_test(Node, fun () -> joey_killer(Recv1) end),
- ?line receive
- {'DOWN', M1, process, Recv1, R1} ->
- ?line joey_said_die = R1
- end,
+ Recv1 = spawn(Node, fun () -> sink(exit_to_busy_sink) end),
+ M1 = erlang:monitor(process, Recv1),
+ do_busy_test(Node, fun () -> joey_killer(Recv1) end),
+ receive
+ {'DOWN', M1, process, Recv1, R1} ->
+ joey_said_die = R1
+ end,
%% Same thing, but tail call to exit/2.
- ?line Recv2 = spawn(Node, fun () -> sink(exit_to_busy_sink) end),
- ?line M2 = erlang:monitor(process, Recv2),
- ?line do_busy_test(Node, fun () -> tail_joey_killer(Recv2) end),
- ?line receive
- {'DOWN', M2, process, Recv2, R2} ->
- ?line joey_said_die = R2
- end,
+ Recv2 = spawn(Node, fun () -> sink(exit_to_busy_sink) end),
+ M2 = erlang:monitor(process, Recv2),
+ do_busy_test(Node, fun () -> tail_joey_killer(Recv2) end),
+ receive
+ {'DOWN', M2, process, Recv2, R2} ->
+ joey_said_die = R2
+ end,
%% Same thing, but we apply exit/2 instead of calling it directly.
- ?line Recv3 = spawn(Node, fun () -> sink(exit_to_busy_sink) end),
- ?line M3 = erlang:monitor(process, Recv3),
- ?line do_busy_test(Node, fun () -> applied_joey_killer(Recv3) end),
- ?line receive
- {'DOWN', M3, process, Recv3, R3} ->
- ?line joey_said_die = R3
- end,
+ Recv3 = spawn(Node, fun () -> sink(exit_to_busy_sink) end),
+ M3 = erlang:monitor(process, Recv3),
+ do_busy_test(Node, fun () -> applied_joey_killer(Recv3) end),
+ receive
+ {'DOWN', M3, process, Recv3, R3} ->
+ joey_said_die = R3
+ end,
%% Same thing again, but we apply exit/2 in the tail of a function.
- ?line Recv4 = spawn(Node, fun () -> sink(exit_to_busy_sink) end),
- ?line M4 = erlang:monitor(process, Recv4),
- ?line do_busy_test(Node, fun () -> tail_applied_joey_killer(Recv4) end),
- ?line receive
- {'DOWN', M4, process, Recv4, R4} ->
- ?line joey_said_die = R4
- end,
-
+ Recv4 = spawn(Node, fun () -> sink(exit_to_busy_sink) end),
+ M4 = erlang:monitor(process, Recv4),
+ do_busy_test(Node, fun () -> tail_applied_joey_killer(Recv4) end),
+ receive
+ {'DOWN', M4, process, Recv4, R4} ->
+ joey_said_die = R4
+ end,
+
%% Done.
- ?line stop_node(Node),
- ?line stop_busy_dist_port_tracer(Tracer),
- ?line test_server:timetrap_cancel(Dog),
+ stop_node(Node),
+ stop_busy_dist_port_tracer(Tracer),
ok.
make_busy_data() ->
Size = 1024*1024,
Key = '__busy__port__data__',
case get(Key) of
- undefined ->
- Data = list_to_binary(lists:duplicate(Size, 253)),
- put(Key, Data),
- Data;
- Data ->
- true = is_binary(Data),
- true = size(Data) == Size,
- Data
+ undefined ->
+ Data = list_to_binary(lists:duplicate(Size, 253)),
+ put(Key, Data),
+ Data;
+ Data ->
+ true = is_binary(Data),
+ true = size(Data) == Size,
+ Data
end.
make_busy(Node, Time) when is_integer(Time) ->
@@ -458,27 +431,27 @@ make_busy(Node, Time) when is_integer(Time) ->
Data = make_busy_data(),
%% first make port busy
Pid = spawn_link(fun () ->
- forever(fun () ->
- dport_reg_send(Node,
- '__noone__',
- Data)
- end)
- end),
+ forever(fun () ->
+ dport_reg_send(Node,
+ '__noone__',
+ Data)
+ end)
+ end),
receive after Own -> ok end,
until(fun () ->
- case process_info(Pid, status) of
- {status, suspended} -> true;
- _ -> false
- end
- end),
+ case process_info(Pid, status) of
+ {status, suspended} -> true;
+ _ -> false
+ end
+ end),
%% then dist entry
make_busy(Node, [nosuspend], Data),
Pid.
make_busy(Node, Opts, Data) ->
case erlang:send({'__noone__', Node}, Data, Opts) of
- nosuspend -> nosuspend;
- _ -> make_busy(Node, Opts, Data)
+ nosuspend -> nosuspend;
+ _ -> make_busy(Node, Opts, Data)
end.
unmake_busy(Pid) ->
@@ -491,29 +464,29 @@ do_busy_test(Node, Fun) ->
receive after 100 -> ok end,
Pinfo = process_info(P, [status, current_function]),
unmake_busy(Busy),
- ?t:format("~p : ~p~n", [P, Pinfo]),
+ io:format("~p : ~p~n", [P, Pinfo]),
case Pinfo of
- undefined ->
- receive
- {'DOWN', M, process, P, Reason} ->
- ?t:format("~p died with exit reason ~p~n", [P, Reason])
- end,
- ?t:fail(premature_death);
- _ ->
- %% Don't match arity; it is different in debug and
- %% optimized emulator
- [{status, suspended},
- {current_function, {erlang, bif_return_trap, _}}] = Pinfo,
- receive
- {'DOWN', M, process, P, Reason} ->
- ?t:format("~p died with exit reason ~p~n", [P, Reason]),
- normal = Reason
- end
+ undefined ->
+ receive
+ {'DOWN', M, process, P, Reason} ->
+ io:format("~p died with exit reason ~p~n", [P, Reason])
+ end,
+ ct:fail(premature_death);
+ _ ->
+ %% Don't match arity; it is different in debug and
+ %% optimized emulator
+ [{status, suspended},
+ {current_function, {erlang, bif_return_trap, _}}] = Pinfo,
+ receive
+ {'DOWN', M, process, P, Reason} ->
+ io:format("~p died with exit reason ~p~n", [P, Reason]),
+ normal = Reason
+ end
end.
remote_is_process_alive(Pid) ->
rpc:call(node(Pid), erlang, is_process_alive,
- [Pid]).
+ [Pid]).
joey_killer(Pid) ->
exit(Pid, joey_said_die),
@@ -535,234 +508,227 @@ sink(Name) ->
sink1() ->
receive
- _Any -> sink1()
+ _Any -> sink1()
end.
-lost_exit(doc) ->
- "Test that EXIT and DOWN messages send to another node are not lost if "
- "the distribution port is busy.";
+%% Test that EXIT and DOWN messages send to another node are not lost if
+%% the distribution port is busy.
lost_exit(Config) when is_list(Config) ->
- ?line {ok, Node} = start_node(lost_exit),
+ {ok, Node} = start_node(lost_exit),
Tracer = case os:getenv("TRACE_BUSY_DIST_PORT") of
- "true" -> start_busy_dist_port_tracer();
- _ -> false
- end,
+ "true" -> start_busy_dist_port_tracer();
+ _ -> false
+ end,
Self = self(),
Die = make_ref(),
- ?line R1 = spawn(fun () -> receive after infinity -> ok end end),
- ?line MR1 = erlang:monitor(process, R1),
-
- ?line {L1, ML1} = spawn_monitor(fun() ->
- link(R1),
- Self ! {self(), linked},
- receive
- Die ->
- exit(controlled_suicide)
- end
- end),
-
- ?line R2 = spawn(fun () ->
- M = erlang:monitor(process, L1),
- receive
- {'DOWN', M, process, L1, R} ->
- Self ! {self(), got_down_message, L1, R}
- end
- end),
-
- ?line receive {L1, linked} -> ok end,
-
+ R1 = spawn(fun () -> receive after infinity -> ok end end),
+ MR1 = erlang:monitor(process, R1),
+
+ {L1, ML1} = spawn_monitor(fun() ->
+ link(R1),
+ Self ! {self(), linked},
+ receive
+ Die ->
+ exit(controlled_suicide)
+ end
+ end),
+
+ R2 = spawn(fun () ->
+ M = erlang:monitor(process, L1),
+ receive
+ {'DOWN', M, process, L1, R} ->
+ Self ! {self(), got_down_message, L1, R}
+ end
+ end),
+
+ receive {L1, linked} -> ok end,
+
Busy = make_busy(Node, 2000),
receive after 100 -> ok end,
L1 ! Die,
- ?line receive
- {'DOWN', ML1, process, L1, RL1} ->
- ?line controlled_suicide = RL1
- end,
+ receive
+ {'DOWN', ML1, process, L1, RL1} ->
+ controlled_suicide = RL1
+ end,
receive after 500 -> ok end,
unmake_busy(Busy),
- ?line receive
- {'DOWN', MR1, process, R1, RR1} ->
- ?line controlled_suicide = RR1
- end,
-
- ?line receive
- {R2, got_down_message, L1, RR2} ->
- ?line controlled_suicide = RR2
- end,
+ receive
+ {'DOWN', MR1, process, R1, RR1} ->
+ controlled_suicide = RR1
+ end,
+
+ receive
+ {R2, got_down_message, L1, RR2} ->
+ controlled_suicide = RR2
+ end,
%% Done.
- ?line stop_busy_dist_port_tracer(Tracer),
- ?line stop_node(Node),
+ stop_busy_dist_port_tracer(Tracer),
+ stop_node(Node),
ok.
dummy_waiter() ->
receive
after infinity ->
- ok
+ ok
end.
-link_to_dead(doc) ->
- ["Test that linking to a dead remote process gives an EXIT message ",
- "AND that the link is teared down."];
+%% Test that linking to a dead remote process gives an EXIT message
+%% AND that the link is teared down.
link_to_dead(Config) when is_list(Config) ->
- ?line process_flag(trap_exit, true),
- ?line {ok, Node} = start_node(link_to_dead),
-% ?line monitor_node(Node, true),
- ?line net_adm:ping(Node), %% Ts_cross_server workaround.
- ?line Pid = spawn(Node, ?MODULE, dead_process, []),
+ process_flag(trap_exit, true),
+ {ok, Node} = start_node(link_to_dead),
+ % monitor_node(Node, true),
+ net_adm:ping(Node), %% Ts_cross_server workaround.
+ Pid = spawn(Node, ?MODULE, dead_process, []),
receive
after 5000 -> ok
end,
- ?line link(Pid),
- ?line receive
- {'EXIT', Pid, noproc} ->
- ok;
- Other ->
- ?line test_server:fail({unexpected_message, Other})
- after 5000 ->
- ?line test_server:fail(nothing_received)
- end,
- ?line {links, Links} = process_info(self(), links),
- ?line io:format("Pid=~p, links=~p", [Pid, Links]),
- ?line false = lists:member(Pid, Links),
- ?line stop_node(Node),
- ?line receive
- Message ->
- ?line test_server:fail({unexpected_message, Message})
- after 3000 ->
- ok
- end,
+ link(Pid),
+ receive
+ {'EXIT', Pid, noproc} ->
+ ok;
+ Other ->
+ ct:fail({unexpected_message, Other})
+ after 5000 ->
+ ct:fail(nothing_received)
+ end,
+ {links, Links} = process_info(self(), links),
+ io:format("Pid=~p, links=~p", [Pid, Links]),
+ false = lists:member(Pid, Links),
+ stop_node(Node),
+ receive
+ Message ->
+ ct:fail({unexpected_message, Message})
+ after 3000 ->
+ ok
+ end,
ok.
-
+
dead_process() ->
erlang:error(die).
-link_to_dead_new_node(doc) ->
- ["Test that linking to a pid on node that has gone and restarted gives ",
- "the correct EXIT message (OTP-2304)."];
+%% Test that linking to a pid on node that has gone and restarted gives
+%% the correct EXIT message (OTP-2304).
link_to_dead_new_node(Config) when is_list(Config) ->
- ?line process_flag(trap_exit, true),
+ process_flag(trap_exit, true),
%% Start the node, get a Pid and stop the node again.
- ?line {ok, Node} = start_node(link_to_dead_new_node),
- ?line Pid = spawn(Node, ?MODULE, dead_process, []),
- ?line stop_node(Node),
+ {ok, Node} = start_node(link_to_dead_new_node),
+ Pid = spawn(Node, ?MODULE, dead_process, []),
+ stop_node(Node),
%% Start a new node with the same name.
- ?line {ok, Node} = start_node(link_to_dead_new_node),
- ?line link(Pid),
- ?line receive
- {'EXIT', Pid, noproc} ->
- ok;
- Other ->
- ?line test_server:fail({unexpected_message, Other})
- after 5000 ->
- ?line test_server:fail(nothing_received)
- end,
+ {ok, Node} = start_node(link_to_dead_new_node),
+ link(Pid),
+ receive
+ {'EXIT', Pid, noproc} ->
+ ok;
+ Other ->
+ ct:fail({unexpected_message, Other})
+ after 5000 ->
+ ct:fail(nothing_received)
+ end,
%% Make sure that the link wasn't created.
- ?line {links, Links} = process_info(self(), links),
- ?line io:format("Pid=~p, links=~p", [Pid, Links]),
- ?line false = lists:member(Pid, Links),
- ?line stop_node(Node),
- ?line receive
- Message ->
- ?line test_server:fail({unexpected_message, Message})
- after 3000 ->
- ok
- end,
+ {links, Links} = process_info(self(), links),
+ io:format("Pid=~p, links=~p", [Pid, Links]),
+ false = lists:member(Pid, Links),
+ stop_node(Node),
+ receive
+ Message ->
+ ct:fail({unexpected_message, Message})
+ after 3000 ->
+ ok
+ end,
ok.
-applied_monitor_node(doc) ->
- "Test that monitor_node/2 works when applied.";
+%% Test that monitor_node/2 works when applied.
applied_monitor_node(Config) when is_list(Config) ->
- ?line NonExisting = list_to_atom("__non_existing__@" ++ hostname()),
+ NonExisting = list_to_atom("__non_existing__@" ++ hostname()),
%% Tail-recursive call to apply (since the node is non-existing,
%% there will be a trap).
- ?line true = tail_apply(erlang, monitor_node, [NonExisting, true]),
- ?line [{nodedown, NonExisting}] = test_server:messages_get(),
+ true = tail_apply(erlang, monitor_node, [NonExisting, true]),
+ [{nodedown, NonExisting}] = test_server:messages_get(),
%% Ordinary call (with trap).
- ?line true = apply(erlang, monitor_node, [NonExisting, true]),
- ?line [{nodedown, NonExisting}] = test_server:messages_get(),
-
+ true = apply(erlang, monitor_node, [NonExisting, true]),
+ [{nodedown, NonExisting}] = test_server:messages_get(),
+
ok.
tail_apply(M, F, A) ->
apply(M, F, A).
-ref_port_roundtrip(doc) ->
- "Test that sending a port or reference to another node and back again "
- "doesn't correct them in any way.";
+%% Test that sending a port or reference to another node and back again
+%% doesn't correct them in any way.
ref_port_roundtrip(Config) when is_list(Config) ->
- ?line process_flag(trap_exit, true),
- ?line Port = open_port({spawn, efile}, []),
- ?line Ref = make_ref(),
- ?line {ok, Node} = start_node(ref_port_roundtrip),
- ?line net_adm:ping(Node),
- ?line Term = {Port, Ref},
- ?line io:format("Term before: ~p", [show_term(Term)]),
- ?line Pid = spawn_link(Node, ?MODULE, roundtrip, [Term]),
- ?line receive after 5000 -> ok end,
- ?line stop_node(Node),
- ?line receive
- {'EXIT', Pid, {Port, Ref}} ->
- ?line io:format("Term after: ~p", [show_term(Term)]),
- ok;
- Other ->
- ?line io:format("Term after: ~p", [show_term(Term)]),
- ?line test_server:fail({unexpected, Other})
- after 10000 ->
- ?line test_server:fail(timeout)
- end,
+ process_flag(trap_exit, true),
+ Port = open_port({spawn, efile}, []),
+ Ref = make_ref(),
+ {ok, Node} = start_node(ref_port_roundtrip),
+ net_adm:ping(Node),
+ Term = {Port, Ref},
+ io:format("Term before: ~p", [show_term(Term)]),
+ Pid = spawn_link(Node, ?MODULE, roundtrip, [Term]),
+ receive after 5000 -> ok end,
+ stop_node(Node),
+ receive
+ {'EXIT', Pid, {Port, Ref}} ->
+ io:format("Term after: ~p", [show_term(Term)]),
+ ok;
+ Other ->
+ io:format("Term after: ~p", [show_term(Term)]),
+ ct:fail({unexpected, Other})
+ after 10000 ->
+ ct:fail(timeout)
+ end,
ok.
roundtrip(Term) ->
exit(Term).
-nil_roundtrip(doc) ->
- "Test that the smallest external term [] aka NIL can be sent to "
- "another node node and back again.";
+%% Test that the smallest external term [] aka NIL can be sent to
+%% another node node and back again.
nil_roundtrip(Config) when is_list(Config) ->
- ?line process_flag(trap_exit, true),
- ?line {ok, Node} = start_node(nil_roundtrip),
- ?line net_adm:ping(Node),
- ?line Pid = spawn_link(Node, ?MODULE, bounce, [self()]),
- ?line Pid ! [],
- ?line receive
- [] ->
- ?line receive
- {'EXIT', Pid, []} ->
- ?line stop_node(Node),
- ok
- end
- end.
+ process_flag(trap_exit, true),
+ {ok, Node} = start_node(nil_roundtrip),
+ net_adm:ping(Node),
+ Pid = spawn_link(Node, ?MODULE, bounce, [self()]),
+ Pid ! [],
+ receive
+ [] ->
+ receive
+ {'EXIT', Pid, []} ->
+ stop_node(Node),
+ ok
+ end
+ end.
bounce(Dest) ->
receive Msg ->
- Dest ! Msg,
- exit(Msg)
+ Dest ! Msg,
+ exit(Msg)
end.
show_term(Term) ->
binary_to_list(term_to_binary(Term)).
-stop_dist(doc) ->
- ["Tests behaviour after net_kernel:stop (OTP-2586)."];
+%% Tests behaviour after net_kernel:stop (OTP-2586).
stop_dist(Config) when is_list(Config) ->
- ?line Str = os:cmd(atom_to_list(lib:progname())
- ++ " -noshell -pa "
- ++ ?config(data_dir, Config)
- ++ " -s run"),
+ Str = os:cmd(atom_to_list(lib:progname())
+ ++ " -noshell -pa "
+ ++ proplists:get_value(data_dir, Config)
+ ++ " -s run"),
%% The "true" may be followed by an error report, so ignore anything that
%% follows it.
- ?line "true\n"++_ = Str,
+ "true\n"++_ = Str,
%% "May fail on FreeBSD due to differently configured name lookup - ask Arndt",
%% if you can find him.
@@ -770,37 +736,31 @@ stop_dist(Config) when is_list(Config) ->
ok.
-trap_bif_1(doc) ->
- [""];
trap_bif_1(Config) when is_list(Config) ->
- ?line {true} = tr1(),
+ {true} = tr1(),
ok.
-trap_bif_2(doc) ->
- [""];
trap_bif_2(Config) when is_list(Config) ->
- ?line {true} = tr2(),
+ {true} = tr2(),
ok.
-trap_bif_3(doc) ->
- [""];
trap_bif_3(Config) when is_list(Config) ->
- ?line {hoo} = tr3(),
+ {hoo} = tr3(),
ok.
tr1() ->
- ?line NonExisting = 'abc@boromir',
- ?line X = erlang:monitor_node(NonExisting, true),
+ NonExisting = 'abc@boromir',
+ X = erlang:monitor_node(NonExisting, true),
{X}.
tr2() ->
- ?line NonExisting = 'abc@boromir',
- ?line X = apply(erlang, monitor_node, [NonExisting, true]),
+ NonExisting = 'abc@boromir',
+ X = apply(erlang, monitor_node, [NonExisting, true]),
{X}.
tr3() ->
- ?line NonExisting = 'abc@boromir',
- ?line X = {NonExisting, glirp} ! hoo,
+ NonExisting = 'abc@boromir',
+ X = {NonExisting, glirp} ! hoo,
{X}.
@@ -821,60 +781,61 @@ tr3() ->
% * n2 gets pang when pinging n1
% * n2 forces connection by using net_kernel:connect_node (ovverrides)
% * n2 gets pong when pinging n1.
-dist_auto_connect_once(doc) -> "Test the dist_auto_connect once kernel parameter";
+
+%% Test the dist_auto_connect once kernel parameter
dist_auto_connect_once(Config) when is_list(Config) ->
- ?line Sock = start_relay_node(dist_auto_connect_relay_node,[]),
- ?line NN = inet_rpc_nodename(Sock),
- ?line Sock2 = start_relay_node(dist_auto_connect_once_node,
- "-kernel dist_auto_connect once"),
- ?line NN2 = inet_rpc_nodename(Sock2),
- ?line {ok,[]} = do_inet_rpc(Sock,erlang,nodes,[]),
- ?line {ok, pong} = do_inet_rpc(Sock2,net_adm,ping,[NN]),
- ?line {ok,[NN2]} = do_inet_rpc(Sock,erlang,nodes,[]),
- ?line {ok,[NN]} = do_inet_rpc(Sock2,erlang,nodes,[]),
- ?line [_,HostPartPeer] = string:tokens(atom_to_list(NN),"@"),
- ?line [_,MyHostPart] = string:tokens(atom_to_list(node()),"@"),
+ Sock = start_relay_node(dist_auto_connect_relay_node,[]),
+ NN = inet_rpc_nodename(Sock),
+ Sock2 = start_relay_node(dist_auto_connect_once_node,
+ "-kernel dist_auto_connect once"),
+ NN2 = inet_rpc_nodename(Sock2),
+ {ok,[]} = do_inet_rpc(Sock,erlang,nodes,[]),
+ {ok, pong} = do_inet_rpc(Sock2,net_adm,ping,[NN]),
+ {ok,[NN2]} = do_inet_rpc(Sock,erlang,nodes,[]),
+ {ok,[NN]} = do_inet_rpc(Sock2,erlang,nodes,[]),
+ [_,HostPartPeer] = string:tokens(atom_to_list(NN),"@"),
+ [_,MyHostPart] = string:tokens(atom_to_list(node()),"@"),
% Give net_kernel a chance to change the state of the node to up to.
- ?line receive after 1000 -> ok end,
+ receive after 1000 -> ok end,
case HostPartPeer of
- MyHostPart ->
- ?line ok = stop_relay_node(Sock),
- ?line {ok,pang} = do_inet_rpc(Sock2,net_adm,ping,[NN]);
- _ ->
- ?line {ok, true} = do_inet_rpc(Sock,net_kernel,disconnect,[NN2]),
- receive
- after 500 -> ok
- end
+ MyHostPart ->
+ ok = stop_relay_node(Sock),
+ {ok,pang} = do_inet_rpc(Sock2,net_adm,ping,[NN]);
+ _ ->
+ {ok, true} = do_inet_rpc(Sock,net_kernel,disconnect,[NN2]),
+ receive
+ after 500 -> ok
+ end
end,
- ?line {ok, []} = do_inet_rpc(Sock2,erlang,nodes,[]),
+ {ok, []} = do_inet_rpc(Sock2,erlang,nodes,[]),
Sock3 = case HostPartPeer of
- MyHostPart ->
- ?line start_relay_node(dist_auto_connect_relay_node,[]);
- _ ->
- Sock
- end,
- ?line TS1 = timestamp(),
- ?line {ok, pang} = do_inet_rpc(Sock2,net_adm,ping,[NN]),
- ?line TS2 = timestamp(),
+ MyHostPart ->
+ start_relay_node(dist_auto_connect_relay_node,[]);
+ _ ->
+ Sock
+ end,
+ TS1 = timestamp(),
+ {ok, pang} = do_inet_rpc(Sock2,net_adm,ping,[NN]),
+ TS2 = timestamp(),
RefT = net_kernel:connecttime() - 1000,
- ?line true = ((TS2 - TS1) < RefT),
- ?line TS3 = timestamp(),
- ?line {ok, true} = do_inet_rpc(Sock2,erlang,monitor_node,
- [NN,true,[allow_passive_connect]]),
- ?line TS4 = timestamp(),
- ?line true = ((TS4 - TS3) > RefT),
- ?line {ok, pong} = do_inet_rpc(Sock3,net_adm,ping,[NN2]),
- ?line {ok, pong} = do_inet_rpc(Sock2,net_adm,ping,[NN]),
- ?line {ok, true} = do_inet_rpc(Sock3,net_kernel,disconnect,[NN2]),
+ true = ((TS2 - TS1) < RefT),
+ TS3 = timestamp(),
+ {ok, true} = do_inet_rpc(Sock2,erlang,monitor_node,
+ [NN,true,[allow_passive_connect]]),
+ TS4 = timestamp(),
+ true = ((TS4 - TS3) > RefT),
+ {ok, pong} = do_inet_rpc(Sock3,net_adm,ping,[NN2]),
+ {ok, pong} = do_inet_rpc(Sock2,net_adm,ping,[NN]),
+ {ok, true} = do_inet_rpc(Sock3,net_kernel,disconnect,[NN2]),
receive
after 500 -> ok
end,
- ?line {ok, pang} = do_inet_rpc(Sock2,net_adm,ping,[NN]),
- ?line {ok, true} = do_inet_rpc(Sock2,net_kernel,connect_node,[NN]),
- ?line {ok, pong} = do_inet_rpc(Sock2,net_adm,ping,[NN]),
- ?line stop_relay_node(Sock3),
- ?line stop_relay_node(Sock2).
-
+ {ok, pang} = do_inet_rpc(Sock2,net_adm,ping,[NN]),
+ {ok, true} = do_inet_rpc(Sock2,net_kernel,connect_node,[NN]),
+ {ok, pong} = do_inet_rpc(Sock2,net_adm,ping,[NN]),
+ stop_relay_node(Sock3),
+ stop_relay_node(Sock2).
+
%% Start a relay node and a lonely (dist_auto_connect never) node.
@@ -883,62 +844,53 @@ dist_auto_connect_once(Config) when is_list(Config) ->
%% Result is sent here through relay node.
dist_auto_connect_never(Config) when is_list(Config) ->
Self = self(),
- ?line {ok, RelayNode} =
- start_node(dist_auto_connect_relay),
- ?line spawn(RelayNode,
- fun() ->
- register(dist_auto_connect_relay, self()),
- dist_auto_connect_relay(Self)
- end),
- ?line {ok, Handle} = dist_auto_connect_start(dist_auto_connect, never),
- ?line Result =
- receive
- {do_dist_auto_connect, ok} ->
- ok;
- {do_dist_auto_connect, Error} ->
- {error, Error};
- Other ->
- {error, Other}
- after 32000 ->
- timeout
- end,
- ?line stop_node(RelayNode),
- ?line Stopped = dist_auto_connect_stop(Handle),
- ?line Junk =
- receive
- {do_dist_auto_connect, _} = J ->
- J
- after 0 ->
- ok
- end,
- ?line {ok, ok, ok} = {Result, Stopped, Junk},
+ {ok, RelayNode} = start_node(dist_auto_connect_relay),
+ spawn(RelayNode,
+ fun() ->
+ register(dist_auto_connect_relay, self()),
+ dist_auto_connect_relay(Self)
+ end),
+ {ok, Handle} = dist_auto_connect_start(dist_auto_connect, never),
+ Result = receive
+ {do_dist_auto_connect, ok} ->
+ ok;
+ {do_dist_auto_connect, Error} ->
+ {error, Error};
+ Other ->
+ {error, Other}
+ after 32000 ->
+ timeout
+ end,
+ stop_node(RelayNode),
+ Stopped = dist_auto_connect_stop(Handle),
+ Junk = receive
+ {do_dist_auto_connect, _} = J -> J
+ after 0 -> ok
+ end,
+ {ok, ok, ok} = {Result, Stopped, Junk},
ok.
do_dist_auto_connect([never]) ->
Node = list_to_atom("dist_auto_connect_relay@" ++ hostname()),
- io:format("~p:do_dist_auto_connect([false]) Node=~p~n",
- [?MODULE, Node]),
+ io:format("~p:do_dist_auto_connect([false]) Node=~p~n", [?MODULE, Node]),
Ping = net_adm:ping(Node),
- io:format("~p:do_dist_auto_connect([false]) Ping=~p~n",
- [?MODULE, Ping]),
+ io:format("~p:do_dist_auto_connect([false]) Ping=~p~n", [?MODULE, Ping]),
Result = case Ping of
- pang -> ok;
- _ -> {error, Ping}
- end,
- io:format("~p:do_dist_auto_connect([false]) Result=~p~n",
- [?MODULE, Result]),
+ pang -> ok;
+ _ -> {error, Ping}
+ end,
+ io:format("~p:do_dist_auto_connect([false]) Result=~p~n", [?MODULE, Result]),
net_kernel:connect_node(Node),
catch {dist_auto_connect_relay, Node} ! {do_dist_auto_connect, Result};
% receive after 1000 -> ok end,
% halt();
do_dist_auto_connect(Arg) ->
- io:format("~p:do_dist_auto_connect(~p)~n",
- [?MODULE, Arg]),
+ io:format("~p:do_dist_auto_connect(~p)~n", [?MODULE, Arg]),
receive after 10000 -> ok end,
halt().
-
+
dist_auto_connect_start(Name, Value) when is_atom(Name) ->
dist_auto_connect_start(atom_to_list(Name), Value);
@@ -948,16 +900,16 @@ dist_auto_connect_start(Name, Value) when is_list(Name), is_atom(Value) ->
ValueStr = atom_to_list(Value),
Cookie = atom_to_list(erlang:get_cookie()),
Cmd = lists:concat(
- [%"xterm -e ",
- atom_to_list(lib:progname()),
-% " -noinput ",
- " -detached ",
- long_or_short(), " ", Name,
- " -setcookie ", Cookie,
- " -pa ", ModuleDir,
- " -s ", atom_to_list(?MODULE),
- " do_dist_auto_connect ", ValueStr,
- " -kernel dist_auto_connect ", ValueStr]),
+ [%"xterm -e ",
+ atom_to_list(lib:progname()),
+ % " -noinput ",
+ " -detached ",
+ long_or_short(), " ", Name,
+ " -setcookie ", Cookie,
+ " -pa ", ModuleDir,
+ " -s ", atom_to_list(?MODULE),
+ " do_dist_auto_connect ", ValueStr,
+ " -kernel dist_auto_connect ", ValueStr]),
io:format("~p:dist_auto_connect_start() cmd: ~p~n", [?MODULE, Cmd]),
Port = open_port({spawn, Cmd}, [stream]),
{ok, {Port, Node}}.
@@ -975,102 +927,83 @@ dist_auto_connect_stop(Port, _Node, Pid, N) when is_integer(N), N =< 0 ->
Result;
dist_auto_connect_stop(Port, Node, Pid, N) when is_integer(N) ->
case net_adm:ping(Node) of
- pong ->
- receive after 100 -> ok end,
- dist_auto_connect_stop(Port, Node, Pid, N-100);
- pang ->
- exit(Pid, normal),
- catch erlang:port_close(Port),
- io:format("~p:dist_auto_connect_stop() ok~n", [?MODULE]),
- ok
+ pong ->
+ receive after 100 -> ok end,
+ dist_auto_connect_stop(Port, Node, Pid, N-100);
+ pang ->
+ exit(Pid, normal),
+ catch erlang:port_close(Port),
+ io:format("~p:dist_auto_connect_stop() ok~n", [?MODULE]),
+ ok
end.
-dist_auto_connect_relay(Parent) ->
+dist_auto_connect_relay(Parent) ->
receive X ->
- catch Parent ! X
+ catch Parent ! X
end,
dist_auto_connect_relay(Parent).
-dist_parallel_send(doc) ->
- [];
-dist_parallel_send(suite) ->
- [];
dist_parallel_send(Config) when is_list(Config) ->
- ?line {ok, RNode} = start_node(dist_parallel_receiver),
- ?line {ok, SNode} = start_node(dist_parallel_sender),
- ?line WatchDog = spawn_link(
- fun () ->
- TRef = erlang:start_timer((?DEFAULT_TIMETRAP
- div 2),
- self(),
- oops),
- receive
- {timeout, TRef, _ } ->
- spawn(SNode,
- fun () ->
- abort(timeout)
- end),
- spawn(RNode,
- fun () ->
- abort(timeout)
- end)
-%% rpc:cast(SNode, erlang, halt,
-%% ["Timetrap (sender)"]),
-%% rpc:cast(RNode, erlang, halt,
-%% ["Timetrap (receiver)"])
- end
- end),
- ?line MkSndrs = fun (Receiver) ->
- lists:map(fun (_) ->
- spawn_link(SNode,
- ?MODULE,
- dist_parallel_sender,
- [self(),
- Receiver,
- 1000])
- end,
- lists:seq(1, 64))
- end,
- ?line SndrsStart = fun (Sndrs) ->
- Parent = self(),
- spawn_link(
- SNode,
- fun () ->
- lists:foreach(fun (P) ->
- P ! {go, Parent}
- end,
- Sndrs)
- end)
- end,
- ?line SndrsWait = fun (Sndrs) ->
- lists:foreach(fun (P) ->
- receive {P, done} -> ok end
- end,
- Sndrs)
- end,
- ?line DPR = spawn_link(RNode, ?MODULE, dist_parallel_receiver, []),
- ?line Sndrs1 = MkSndrs(DPR),
- ?line SndrsStart(Sndrs1),
- ?line SndrsWait(Sndrs1),
- ?line unlink(DPR),
- ?line exit(DPR, bang),
-
- ?line DEPR = spawn_link(RNode, ?MODULE, dist_evil_parallel_receiver, []),
- ?line Sndrs2 = MkSndrs(DEPR),
- ?line SndrsStart(Sndrs2),
- ?line SndrsWait(Sndrs2),
- ?line unlink(DEPR),
- ?line exit(DEPR, bang),
-
- ?line unlink(WatchDog),
- ?line exit(WatchDog, bang),
-
- ?line stop_node(RNode),
- ?line stop_node(SNode),
-
- ?line ok.
+ {ok, RNode} = start_node(dist_parallel_receiver),
+ {ok, SNode} = start_node(dist_parallel_sender),
+ WatchDog = spawn_link(
+ fun () ->
+ TRef = erlang:start_timer((2*60*1000), self(), oops),
+ receive
+ {timeout, TRef, _ } ->
+ spawn(SNode, fun () -> abort(timeout) end),
+ spawn(RNode, fun () -> abort(timeout) end)
+ %% rpc:cast(SNode, erlang, halt,
+ %% ["Timetrap (sender)"]),
+ %% rpc:cast(RNode, erlang, halt,
+ %% ["Timetrap (receiver)"])
+ end
+ end),
+ MkSndrs = fun (Receiver) ->
+ lists:map(fun (_) ->
+ spawn_link(SNode,
+ ?MODULE,
+ dist_parallel_sender,
+ [self(), Receiver, 1000])
+ end, lists:seq(1, 64))
+ end,
+ SndrsStart = fun (Sndrs) ->
+ Parent = self(),
+ spawn_link(SNode,
+ fun () ->
+ lists:foreach(fun (P) ->
+ P ! {go, Parent}
+ end, Sndrs)
+ end)
+ end,
+ SndrsWait = fun (Sndrs) ->
+ lists:foreach(fun (P) ->
+ receive {P, done} -> ok end
+ end, Sndrs)
+ end,
+ DPR = spawn_link(RNode, ?MODULE, dist_parallel_receiver, []),
+ Sndrs1 = MkSndrs(DPR),
+ SndrsStart(Sndrs1),
+ SndrsWait(Sndrs1),
+ unlink(DPR),
+ exit(DPR, bang),
+
+ DEPR = spawn_link(RNode, ?MODULE, dist_evil_parallel_receiver, []),
+ Sndrs2 = MkSndrs(DEPR),
+ SndrsStart(Sndrs2),
+ SndrsWait(Sndrs2),
+ unlink(DEPR),
+ exit(DEPR, bang),
+
+ unlink(WatchDog),
+ exit(WatchDog, bang),
+
+ stop_node(RNode),
+ stop_node(SNode),
+
+ ok.
do_dist_parallel_sender(Parent, _Receiver, 0) ->
Parent ! {self(), done};
@@ -1092,71 +1025,75 @@ dist_evil_parallel_receiver() ->
dist_evil_parallel_receiver().
atom_roundtrip(Config) when is_list(Config) ->
- ?line AtomData = atom_data(),
- ?line verify_atom_data(AtomData),
- ?line {ok, Node} = start_node(Config),
- ?line do_atom_roundtrip(Node, AtomData),
- ?line stop_node(Node),
- ?line ok.
+ AtomData = atom_data(),
+ verify_atom_data(AtomData),
+ {ok, Node} = start_node(Config),
+ do_atom_roundtrip(Node, AtomData),
+ stop_node(Node),
+ ok.
atom_roundtrip_r15b(Config) when is_list(Config) ->
- case ?t:is_release_available("r15b") of
- true ->
- ?line AtomData = atom_data(),
- ?line verify_atom_data(AtomData),
- ?line {ok, Node} = start_node(Config, [], "r15b"),
- ?line do_atom_roundtrip(Node, AtomData),
- ?line stop_node(Node),
- ?line ok;
- false ->
- ?line {skip,"No OTP R15B available"}
+ case test_server:is_release_available("r15b") of
+ true ->
+ ct:timetrap({minutes, 6}),
+ AtomData = atom_data(),
+ verify_atom_data(AtomData),
+ case start_node(Config, [], "r15b") of
+ {ok, Node} ->
+ do_atom_roundtrip(Node, AtomData),
+ stop_node(Node);
+ {error, timeout} ->
+ {skip,"Unable to start OTP R15B release"}
+ end;
+ false ->
+ {skip,"No OTP R15B available"}
end.
unicode_atom_roundtrip(Config) when is_list(Config) ->
- ?line AtomData = unicode_atom_data(),
- ?line verify_atom_data(AtomData),
- ?line {ok, Node} = start_node(Config),
- ?line do_atom_roundtrip(Node, AtomData),
- ?line stop_node(Node),
- ?line ok.
+ AtomData = unicode_atom_data(),
+ verify_atom_data(AtomData),
+ {ok, Node} = start_node(Config),
+ do_atom_roundtrip(Node, AtomData),
+ stop_node(Node),
+ ok.
do_atom_roundtrip(Node, AtomData) ->
- ?line Parent = self(),
- ?line Proc = spawn_link(Node, fun () -> verify_atom_data_loop(Parent) end),
- ?line Proc ! {self(), AtomData},
- ?line receive {Proc, AD1} -> AtomData = AD1 end,
- ?line Proc ! {self(), AtomData},
- ?line receive {Proc, AD2} -> AtomData = AD2 end,
- ?line RevAtomData = lists:reverse(AtomData),
- ?line Proc ! {self(), RevAtomData},
- ?line receive {Proc, RAD1} -> RevAtomData = RAD1 end,
- ?line unlink(Proc),
- ?line exit(Proc, bang),
- ?line ok.
+ Parent = self(),
+ Proc = spawn_link(Node, fun () -> verify_atom_data_loop(Parent) end),
+ Proc ! {self(), AtomData},
+ receive {Proc, AD1} -> AtomData = AD1 end,
+ Proc ! {self(), AtomData},
+ receive {Proc, AD2} -> AtomData = AD2 end,
+ RevAtomData = lists:reverse(AtomData),
+ Proc ! {self(), RevAtomData},
+ receive {Proc, RAD1} -> RevAtomData = RAD1 end,
+ unlink(Proc),
+ exit(Proc, bang),
+ ok.
verify_atom_data_loop(From) ->
receive
- {From, AtomData} ->
- verify_atom_data(AtomData),
- From ! {self(), AtomData},
- verify_atom_data_loop(From)
+ {From, AtomData} ->
+ verify_atom_data(AtomData),
+ From ! {self(), AtomData},
+ verify_atom_data_loop(From)
end.
atom_data() ->
lists:map(fun (N) ->
- ATxt = "a"++integer_to_list(N),
- {list_to_atom(ATxt), ATxt}
- end,
- lists:seq(1, 2000)).
+ ATxt = "a"++integer_to_list(N),
+ {list_to_atom(ATxt), ATxt}
+ end,
+ lists:seq(1, 2000)).
verify_atom_data(AtomData) ->
lists:foreach(fun ({Atom, AtomTxt}) when is_atom(Atom) ->
- AtomTxt = atom_to_list(Atom);
- ({PPR, AtomTxt}) ->
- % Pid, Port, or Ref
- AtomTxt = atom_to_list(node(PPR))
- end,
- AtomData).
+ AtomTxt = atom_to_list(Atom);
+ ({PPR, AtomTxt}) ->
+ % Pid, Port, or Ref
+ AtomTxt = atom_to_list(node(PPR))
+ end,
+ AtomData).
uc_atom_tup(ATxt) ->
Atom = string_to_atom(ATxt),
@@ -1209,9 +1146,8 @@ unicode_atom_data() ->
uc_atom_tup(lists:seq(65500, 65754)),
uc_atom_tup(lists:seq(65500, 65563))
| lists:map(fun (N) ->
- uc_atom_tup(lists:seq(64000+N, 64254+N))
- end,
- lists:seq(1, 2000))].
+ uc_atom_tup(lists:seq(64000+N, 64254+N))
+ end, lists:seq(1, 2000))].
contended_atom_cache_entry(Config) when is_list(Config) ->
contended_atom_cache_entry_test(Config, latin1).
@@ -1220,79 +1156,77 @@ contended_unicode_atom_cache_entry(Config) when is_list(Config) ->
contended_atom_cache_entry_test(Config, unicode).
contended_atom_cache_entry_test(Config, Type) ->
- ?line TestServer = self(),
- ?line ProcessPairs = 10,
- ?line Msgs = 100000,
- ?line {ok, SNode} = start_node(Config),
- ?line {ok, RNode} = start_node(Config),
- ?line Success = make_ref(),
- ?line spawn_link(
- SNode,
- fun () ->
- erts_debug:set_internal_state(available_internal_state,
- true),
- Master = self(),
- CIX = get_cix(),
- TestAtoms = case Type of
- latin1 ->
- get_conflicting_atoms(CIX,
- ProcessPairs);
- unicode ->
- get_conflicting_unicode_atoms(CIX,
- ProcessPairs)
- end,
- io:format("Testing with the following atoms all using "
- "cache index ~p:~n ~w~n",
- [CIX, TestAtoms]),
- Ps = lists:map(
- fun (A) ->
- Ref = make_ref(),
- R = spawn_link(
- RNode,
- fun () ->
- Atom = receive
- {Ref, txt, ATxt} ->
- case Type of
- latin1 ->
- list_to_atom(ATxt);
- unicode ->
- string_to_atom(ATxt)
- end
- end,
- receive_ref_atom(Ref,
- Atom,
- Msgs),
- Master ! {self(), success}
- end),
- S = spawn_link(
- SNode,
- fun () ->
- receive go -> ok end,
- R ! {Ref,
- txt,
- atom_to_list(A)},
- send_ref_atom(R, Ref, A, Msgs)
- end),
- {S, R}
- end,
- TestAtoms),
- lists:foreach(fun ({S, _}) ->
- S ! go
- end,
- Ps),
- lists:foreach(fun ({_, R}) ->
- receive {R, success} -> ok end
- end,
- Ps),
- TestServer ! Success
- end),
- ?line receive
- Success ->
- ok
- end,
- ?line stop_node(SNode),
- ?line stop_node(RNode),
- ?line ok.
+ TestServer = self(),
+ ProcessPairs = 10,
+ Msgs = 100000,
+ {ok, SNode} = start_node(Config),
+ {ok, RNode} = start_node(Config),
+ Success = make_ref(),
+ spawn_link(
+ SNode,
+ fun () ->
+ erts_debug:set_internal_state(available_internal_state,
+ true),
+ Master = self(),
+ CIX = get_cix(),
+ TestAtoms = case Type of
+ latin1 ->
+ get_conflicting_atoms(CIX,
+ ProcessPairs);
+ unicode ->
+ get_conflicting_unicode_atoms(CIX,
+ ProcessPairs)
+ end,
+ io:format("Testing with the following atoms all using "
+ "cache index ~p:~n ~w~n",
+ [CIX, TestAtoms]),
+ Ps = lists:map(
+ fun (A) ->
+ Ref = make_ref(),
+ R = spawn_link(RNode,
+ fun () ->
+ Atom = receive
+ {Ref, txt, ATxt} ->
+ case Type of
+ latin1 ->
+ list_to_atom(ATxt);
+ unicode ->
+ string_to_atom(ATxt)
+ end
+ end,
+ receive_ref_atom(Ref,
+ Atom,
+ Msgs),
+ Master ! {self(), success}
+ end),
+ S = spawn_link(SNode,
+ fun () ->
+ receive go -> ok end,
+ R ! {Ref,
+ txt,
+ atom_to_list(A)},
+ send_ref_atom(R, Ref, A, Msgs)
+ end),
+ {S, R}
+ end,
+ TestAtoms),
+ lists:foreach(fun ({S, _}) ->
+ S ! go
+ end,
+ Ps),
+ lists:foreach(fun ({_, R}) ->
+ receive {R, success} -> ok end
+ end,
+ Ps),
+ TestServer ! Success
+ end),
+ receive
+ Success ->
+ ok
+ end,
+ stop_node(SNode),
+ stop_node(RNode),
+ ok.
send_ref_atom(_To, _Ref, _Atom, 0) ->
ok;
@@ -1304,11 +1238,11 @@ receive_ref_atom(_Ref, _Atom, 0) ->
ok;
receive_ref_atom(Ref, Atom, N) ->
receive
- {Ref, Value} ->
- Atom = Value
+ {Ref, Value} ->
+ Atom = Value
end,
receive_ref_atom(Ref, Atom, N-1).
-
+
get_cix() ->
get_cix(1000).
@@ -1316,34 +1250,34 @@ get_cix(CIX) when is_integer(CIX), CIX < 0 ->
get_cix(0);
get_cix(CIX) when is_integer(CIX) ->
get_cix(CIX,
- unwanted_cixs(),
- erts_debug:get_internal_state(max_atom_out_cache_index)).
+ unwanted_cixs(),
+ erts_debug:get_internal_state(max_atom_out_cache_index)).
get_cix(CIX, Unwanted, MaxCIX) when CIX > MaxCIX ->
get_cix(0, Unwanted, MaxCIX);
get_cix(CIX, Unwanted, MaxCIX) ->
case lists:member(CIX, Unwanted) of
- true -> get_cix(CIX+1, Unwanted, MaxCIX);
- false -> CIX
+ true -> get_cix(CIX+1, Unwanted, MaxCIX);
+ false -> CIX
end.
unwanted_cixs() ->
lists:map(fun (Node) ->
- erts_debug:get_internal_state({atom_out_cache_index,
- Node})
- end,
- nodes()).
-
-
+ erts_debug:get_internal_state({atom_out_cache_index,
+ Node})
+ end,
+ nodes()).
+
+
get_conflicting_atoms(_CIX, 0) ->
[];
get_conflicting_atoms(CIX, N) ->
Atom = list_to_atom("atom" ++ integer_to_list(erlang:unique_integer([positive]))),
case erts_debug:get_internal_state({atom_out_cache_index, Atom}) of
- CIX ->
- [Atom|get_conflicting_atoms(CIX, N-1)];
- _ ->
- get_conflicting_atoms(CIX, N)
+ CIX ->
+ [Atom|get_conflicting_atoms(CIX, N-1)];
+ _ ->
+ get_conflicting_atoms(CIX, N)
end.
get_conflicting_unicode_atoms(_CIX, 0) ->
@@ -1351,10 +1285,10 @@ get_conflicting_unicode_atoms(_CIX, 0) ->
get_conflicting_unicode_atoms(CIX, N) ->
Atom = string_to_atom([16#1f608] ++ "atom" ++ integer_to_list(erlang:unique_integer([positive]))),
case erts_debug:get_internal_state({atom_out_cache_index, Atom}) of
- CIX ->
- [Atom|get_conflicting_unicode_atoms(CIX, N-1)];
- _ ->
- get_conflicting_unicode_atoms(CIX, N)
+ CIX ->
+ [Atom|get_conflicting_unicode_atoms(CIX, N-1)];
+ _ ->
+ get_conflicting_unicode_atoms(CIX, N)
end.
-define(COOKIE, '').
@@ -1376,482 +1310,474 @@ get_conflicting_unicode_atoms(CIX, N) ->
-define(DOP_MONITOR_P_EXIT, 21).
start_monitor(Offender,P) ->
- ?line Parent = self(),
- ?line Q = spawn(Offender,
- fun () ->
- Ref = erlang:monitor(process,P),
- Parent ! {self(),ref,Ref},
- receive
- just_stay_alive -> ok
- end
- end),
- ?line Ref = receive
- {Q,ref,R} ->
- R
- after 5000 ->
- error
- end,
+ Parent = self(),
+ Q = spawn(Offender,
+ fun () ->
+ Ref = erlang:monitor(process,P),
+ Parent ! {self(),ref,Ref},
+ receive
+ just_stay_alive -> ok
+ end
+ end),
+ Ref = receive
+ {Q,ref,R} ->
+ R
+ after 5000 ->
+ error
+ end,
io:format("Ref is ~p~n",[Ref]),
ok.
start_link(Offender,P) ->
- ?line Parent = self(),
- ?line Q = spawn(Offender,
- fun () ->
- process_flag(trap_exit,true),
- link(P),
- Parent ! {self(),ref,P},
- receive
- just_stay_alive -> ok
- end
- end),
- ?line Ref = receive
- {Q,ref,R} ->
- R
- after 5000 ->
- error
- end,
+ Parent = self(),
+ Q = spawn(Offender,
+ fun () ->
+ process_flag(trap_exit,true),
+ link(P),
+ Parent ! {self(),ref,P},
+ receive
+ just_stay_alive -> ok
+ end
+ end),
+ Ref = receive
+ {Q,ref,R} ->
+ R
+ after 5000 ->
+ error
+ end,
io:format("Ref is ~p~n",[Ref]),
ok.
-bad_dist_structure(suite) ->
- [];
-bad_dist_structure(doc) ->
- ["Test dist messages with valid structure (binary to term ok) but malformed"
- "control content"];
+%% Test dist messages with valid structure (binary to term ok) but malformed control content
bad_dist_structure(Config) when is_list(Config) ->
- %process_flag(trap_exit,true),
- ODog = ?config(watchdog, Config),
- ?t:timetrap_cancel(ODog),
- Dog = ?t:timetrap(?t:seconds(15)),
-
- ?line {ok, Offender} = start_node(bad_dist_structure_offender),
- ?line {ok, Victim} = start_node(bad_dist_structure_victim),
- ?line start_node_monitors([Offender,Victim]),
- ?line Parent = self(),
- ?line P = spawn(Victim,
- fun () ->
- process_flag(trap_exit,true),
- Parent ! {self(), started},
- receive check_msgs -> ok end,
- bad_dist_struct_check_msgs([one,
- two]),
- Parent ! {self(), messages_checked},
- receive done -> ok end
- end),
- ?line receive {P, started} -> ok end,
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line verify_up(Offender, Victim),
- ?line true = lists:member(Offender, rpc:call(Victim, erlang, nodes, [])),
- ?line start_monitor(Offender,P),
- ?line P ! one,
- ?line send_bad_structure(Offender, P,{?DOP_MONITOR_P_EXIT,'replace',P,normal},2),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line start_monitor(Offender,P),
- ?line send_bad_structure(Offender, P,{?DOP_MONITOR_P_EXIT,'replace',P,normal,normal},2),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line start_link(Offender,P),
- ?line send_bad_structure(Offender, P,{?DOP_LINK},0),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line start_link(Offender,P),
- ?line send_bad_structure(Offender, P,{?DOP_UNLINK,'replace'},2),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line start_link(Offender,P),
- ?line send_bad_structure(Offender, P,{?DOP_UNLINK,'replace',make_ref()},2),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line start_link(Offender,P),
- ?line send_bad_structure(Offender, P,{?DOP_UNLINK,make_ref(),P},0),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line start_link(Offender,P),
- ?line send_bad_structure(Offender, P,{?DOP_UNLINK,normal,normal},0),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line start_monitor(Offender,P),
- ?line send_bad_structure(Offender, P,{?DOP_MONITOR_P,'replace',P},2),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line start_monitor(Offender,P),
- ?line send_bad_structure(Offender, P,{?DOP_MONITOR_P,'replace',P,normal},2),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line start_monitor(Offender,P),
- ?line send_bad_structure(Offender, P,{?DOP_DEMONITOR_P,'replace',P},2),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line start_monitor(Offender,P),
- ?line send_bad_structure(Offender, P,{?DOP_DEMONITOR_P,'replace',P,normal},2),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line send_bad_structure(Offender, P,{?DOP_EXIT,'replace',P},2),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line send_bad_structure(Offender, P,{?DOP_EXIT,make_ref(),normal,normal},0),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line send_bad_structure(Offender, P,{?DOP_EXIT_TT,'replace',token,P},2),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line send_bad_structure(Offender, P,{?DOP_EXIT_TT,make_ref(),token,normal,normal},0),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line send_bad_structure(Offender, P,{?DOP_EXIT2,'replace',P},2),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line send_bad_structure(Offender, P,{?DOP_EXIT2,make_ref(),normal,normal},0),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line send_bad_structure(Offender, P,{?DOP_EXIT2_TT,'replace',token,P},2),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line send_bad_structure(Offender, P,{?DOP_EXIT2_TT,make_ref(),token,normal,normal},0),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line send_bad_structure(Offender, P,{?DOP_GROUP_LEADER,'replace'},2),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line send_bad_structure(Offender, P,{?DOP_GROUP_LEADER,'replace','atomic'},2),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line send_bad_structure(Offender, P,{?DOP_GROUP_LEADER,'replace',P},0),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line send_bad_structure(Offender, P,{?DOP_REG_SEND_TT,'replace','',name},2,{message}),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line send_bad_structure(Offender, P,{?DOP_REG_SEND_TT,'replace','',name,token},0,{message}),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace',''},2,{message}),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace','',P},0,{message}),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace','',name},0,{message}),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace','',name,{token}},2,{message}),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line send_bad_structure(Offender, P,{?DOP_SEND_TT,'',P},0,{message}),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line send_bad_structure(Offender, P,{?DOP_SEND_TT,'',name,token},0,{message}),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line send_bad_structure(Offender, P,{?DOP_SEND,''},0,{message}),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line send_bad_structure(Offender, P,{?DOP_SEND,'',name},0,{message}),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line send_bad_structure(Offender, P,{?DOP_SEND,'',P,{token}},0,{message}),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line P ! two,
- ?line P ! check_msgs,
- ?line receive
- {P, messages_checked} -> ok
- after 5000 ->
- exit(victim_is_dead)
- end,
-
- ?line {message_queue_len, 0}
- = rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
-
- ?line unlink(P),
- ?line P ! done,
- ?line stop_node(Offender),
- ?line stop_node(Victim),
- ?t:timetrap_cancel(Dog),
+ ct:timetrap({seconds, 15}),
+
+ {ok, Offender} = start_node(bad_dist_structure_offender),
+ {ok, Victim} = start_node(bad_dist_structure_victim),
+ start_node_monitors([Offender,Victim]),
+ Parent = self(),
+ P = spawn(Victim,
+ fun () ->
+ process_flag(trap_exit,true),
+ Parent ! {self(), started},
+ receive check_msgs -> ok end,
+ bad_dist_struct_check_msgs([one,
+ two]),
+ Parent ! {self(), messages_checked},
+ receive done -> ok end
+ end),
+ receive {P, started} -> ok end,
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ verify_up(Offender, Victim),
+ true = lists:member(Offender, rpc:call(Victim, erlang, nodes, [])),
+ start_monitor(Offender,P),
+ P ! one,
+ send_bad_structure(Offender, P,{?DOP_MONITOR_P_EXIT,'replace',P,normal},2),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ start_monitor(Offender,P),
+ send_bad_structure(Offender, P,{?DOP_MONITOR_P_EXIT,'replace',P,normal,normal},2),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ start_link(Offender,P),
+ send_bad_structure(Offender, P,{?DOP_LINK},0),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ start_link(Offender,P),
+ send_bad_structure(Offender, P,{?DOP_UNLINK,'replace'},2),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ start_link(Offender,P),
+ send_bad_structure(Offender, P,{?DOP_UNLINK,'replace',make_ref()},2),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ start_link(Offender,P),
+ send_bad_structure(Offender, P,{?DOP_UNLINK,make_ref(),P},0),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ start_link(Offender,P),
+ send_bad_structure(Offender, P,{?DOP_UNLINK,normal,normal},0),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ start_monitor(Offender,P),
+ send_bad_structure(Offender, P,{?DOP_MONITOR_P,'replace',P},2),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ start_monitor(Offender,P),
+ send_bad_structure(Offender, P,{?DOP_MONITOR_P,'replace',P,normal},2),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ start_monitor(Offender,P),
+ send_bad_structure(Offender, P,{?DOP_DEMONITOR_P,'replace',P},2),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ start_monitor(Offender,P),
+ send_bad_structure(Offender, P,{?DOP_DEMONITOR_P,'replace',P,normal},2),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ send_bad_structure(Offender, P,{?DOP_EXIT,'replace',P},2),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ send_bad_structure(Offender, P,{?DOP_EXIT,make_ref(),normal,normal},0),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ send_bad_structure(Offender, P,{?DOP_EXIT_TT,'replace',token,P},2),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ send_bad_structure(Offender, P,{?DOP_EXIT_TT,make_ref(),token,normal,normal},0),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ send_bad_structure(Offender, P,{?DOP_EXIT2,'replace',P},2),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ send_bad_structure(Offender, P,{?DOP_EXIT2,make_ref(),normal,normal},0),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ send_bad_structure(Offender, P,{?DOP_EXIT2_TT,'replace',token,P},2),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ send_bad_structure(Offender, P,{?DOP_EXIT2_TT,make_ref(),token,normal,normal},0),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ send_bad_structure(Offender, P,{?DOP_GROUP_LEADER,'replace'},2),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ send_bad_structure(Offender, P,{?DOP_GROUP_LEADER,'replace','atomic'},2),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ send_bad_structure(Offender, P,{?DOP_GROUP_LEADER,'replace',P},0),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ send_bad_structure(Offender, P,{?DOP_REG_SEND_TT,'replace','',name},2,{message}),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ send_bad_structure(Offender, P,{?DOP_REG_SEND_TT,'replace','',name,token},0,{message}),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace',''},2,{message}),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace','',P},0,{message}),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace','',name},0,{message}),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace','',name,{token}},2,{message}),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ send_bad_structure(Offender, P,{?DOP_SEND_TT,'',P},0,{message}),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ send_bad_structure(Offender, P,{?DOP_SEND_TT,'',name,token},0,{message}),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ send_bad_structure(Offender, P,{?DOP_SEND,''},0,{message}),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ send_bad_structure(Offender, P,{?DOP_SEND,'',name},0,{message}),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ send_bad_structure(Offender, P,{?DOP_SEND,'',P,{token}},0,{message}),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ P ! two,
+ P ! check_msgs,
+ receive
+ {P, messages_checked} -> ok
+ after 5000 ->
+ exit(victim_is_dead)
+ end,
+
+ {message_queue_len, 0}
+ = rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
+
+ unlink(P),
+ P ! done,
+ stop_node(Offender),
+ stop_node(Victim),
ok.
bad_dist_ext_receive(Config) when is_list(Config) ->
- ?line {ok, Offender} = start_node(bad_dist_ext_receive_offender),
- ?line {ok, Victim} = start_node(bad_dist_ext_receive_victim),
- ?line start_node_monitors([Offender,Victim]),
-
- ?line Parent = self(),
-
- ?line P = spawn_link(Victim,
- fun () ->
- Parent ! {self(), started},
- receive check_msgs -> ok end,
- bad_dist_ext_check_msgs([one,
- two,
- three]),
- Parent ! {self(), messages_checked},
- receive done -> ok end
- end),
-
- ?line receive {P, started} -> ok end,
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line verify_up(Offender, Victim),
- ?line true = lists:member(Offender, rpc:call(Victim, erlang, nodes, [])),
- ?line P ! one,
- ?line send_bad_msg(Offender, P),
- ?line P ! two,
- ?line verify_down(Offender, connection_closed, Victim, killed),
- ?line {message_queue_len, 2}
- = rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
-
- ?line Suspended = make_ref(),
- ?line S = spawn(Victim,
- fun () ->
- erlang:suspend_process(P),
- Parent ! Suspended,
- receive after infinity -> ok end
- end),
- ?line MS = erlang:monitor(process, S),
- ?line receive Suspended -> ok end,
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line verify_up(Offender, Victim),
- ?line true = lists:member(Offender, rpc:call(Victim, erlang, nodes, [])),
- ?line send_bad_msgs(Offender, P, 5),
- ?line true = lists:member(Offender, rpc:call(Victim, erlang, nodes, [])),
- ?line P ! three,
- ?line send_bad_msgs(Offender, P, 5),
+ {ok, Offender} = start_node(bad_dist_ext_receive_offender),
+ {ok, Victim} = start_node(bad_dist_ext_receive_victim),
+ start_node_monitors([Offender,Victim]),
+
+ Parent = self(),
+
+ P = spawn_link(Victim,
+ fun () ->
+ Parent ! {self(), started},
+ receive check_msgs -> ok end,
+ bad_dist_ext_check_msgs([one,
+ two,
+ three]),
+ Parent ! {self(), messages_checked},
+ receive done -> ok end
+ end),
+
+ receive {P, started} -> ok end,
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ verify_up(Offender, Victim),
+ true = lists:member(Offender, rpc:call(Victim, erlang, nodes, [])),
+ P ! one,
+ send_bad_msg(Offender, P),
+ P ! two,
+ verify_down(Offender, connection_closed, Victim, killed),
+ {message_queue_len, 2}
+ = rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
+
+ Suspended = make_ref(),
+ S = spawn(Victim,
+ fun () ->
+ erlang:suspend_process(P),
+ Parent ! Suspended,
+ receive after infinity -> ok end
+ end),
+ MS = erlang:monitor(process, S),
+ receive Suspended -> ok end,
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ verify_up(Offender, Victim),
+ true = lists:member(Offender, rpc:call(Victim, erlang, nodes, [])),
+ send_bad_msgs(Offender, P, 5),
+ true = lists:member(Offender, rpc:call(Victim, erlang, nodes, [])),
+ P ! three,
+ send_bad_msgs(Offender, P, 5),
%% Make sure bad msgs has reached Victim
- ?line rpc:call(Offender, rpc, call, [Victim, erlang, node, []]),
-
- ?line verify_still_up(Offender, Victim),
- ?line {message_queue_len, 13}
- = rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
-
- ?line exit(S, bang),
- ?line receive {'DOWN', MS, process, S, bang} -> ok end,
- ?line verify_down(Offender, connection_closed, Victim, killed),
- ?line {message_queue_len, 3}
- = rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
-
- ?line P ! check_msgs,
- ?line receive {P, messages_checked} -> ok end,
-
- ?line {message_queue_len, 0}
- = rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
-
- ?line P ! done,
- ?line unlink(P),
- ?line verify_no_down(Offender, Victim),
- ?line stop_node(Offender),
- ?line stop_node(Victim).
+ rpc:call(Offender, rpc, call, [Victim, erlang, node, []]),
+
+ verify_still_up(Offender, Victim),
+ {message_queue_len, 13}
+ = rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
+
+ exit(S, bang),
+ receive {'DOWN', MS, process, S, bang} -> ok end,
+ verify_down(Offender, connection_closed, Victim, killed),
+ {message_queue_len, 3}
+ = rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
+
+ P ! check_msgs,
+ receive {P, messages_checked} -> ok end,
+
+ {message_queue_len, 0}
+ = rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
+
+ P ! done,
+ unlink(P),
+ verify_no_down(Offender, Victim),
+ stop_node(Offender),
+ stop_node(Victim).
bad_dist_ext_process_info(Config) when is_list(Config) ->
- ?line {ok, Offender} = start_node(bad_dist_ext_process_info_offender),
- ?line {ok, Victim} = start_node(bad_dist_ext_process_info_victim),
- ?line start_node_monitors([Offender,Victim]),
-
- ?line Parent = self(),
- ?line P = spawn_link(Victim,
- fun () ->
- Parent ! {self(), started},
- receive check_msgs -> ok end,
- bad_dist_ext_check_msgs([one, two]),
- Parent ! {self(), messages_checked},
- receive done -> ok end
- end),
-
- ?line receive {P, started} -> ok end,
- ?line P ! one,
-
- ?line Suspended = make_ref(),
- ?line S = spawn(Victim,
- fun () ->
- erlang:suspend_process(P),
- Parent ! Suspended,
- receive after infinity -> ok end
- end),
-
- ?line receive Suspended -> ok end,
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line verify_up(Offender, Victim),
- ?line send_bad_msgs(Offender, P, 5),
-
- ?line P ! two,
- ?line send_bad_msgs(Offender, P, 5),
+ {ok, Offender} = start_node(bad_dist_ext_process_info_offender),
+ {ok, Victim} = start_node(bad_dist_ext_process_info_victim),
+ start_node_monitors([Offender,Victim]),
+
+ Parent = self(),
+ P = spawn_link(Victim,
+ fun () ->
+ Parent ! {self(), started},
+ receive check_msgs -> ok end,
+ bad_dist_ext_check_msgs([one, two]),
+ Parent ! {self(), messages_checked},
+ receive done -> ok end
+ end),
+
+ receive {P, started} -> ok end,
+ P ! one,
+
+ Suspended = make_ref(),
+ S = spawn(Victim,
+ fun () ->
+ erlang:suspend_process(P),
+ Parent ! Suspended,
+ receive after infinity -> ok end
+ end),
+
+ receive Suspended -> ok end,
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ verify_up(Offender, Victim),
+ send_bad_msgs(Offender, P, 5),
+
+ P ! two,
+ send_bad_msgs(Offender, P, 5),
%% Make sure bad msgs has reached Victim
- ?line rpc:call(Offender, rpc, call, [Victim, erlang, node, []]),
-
- ?line verify_still_up(Offender, Victim),
- ?line {message_queue_len, 12}
- = rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
- ?line verify_still_up(Offender, Victim),
- ?line [{message_queue_len, 2},
- {messages, [one, two]}]
- = rpc:call(Victim, erlang, process_info, [P, [message_queue_len,
- messages]]),
- ?line verify_down(Offender, connection_closed, Victim, killed),
-
- ?line P ! check_msgs,
- ?line exit(S, bang),
- ?line receive {P, messages_checked} -> ok end,
-
- ?line {message_queue_len, 0}
- = rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
-
- ?line P ! done,
- ?line unlink(P),
- ?line verify_no_down(Offender, Victim),
- ?line stop_node(Offender),
- ?line stop_node(Victim).
+ rpc:call(Offender, rpc, call, [Victim, erlang, node, []]),
+
+ verify_still_up(Offender, Victim),
+ {message_queue_len, 12}
+ = rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
+ verify_still_up(Offender, Victim),
+ [{message_queue_len, 2},
+ {messages, [one, two]}]
+ = rpc:call(Victim, erlang, process_info, [P, [message_queue_len,
+ messages]]),
+ verify_down(Offender, connection_closed, Victim, killed),
+
+ P ! check_msgs,
+ exit(S, bang),
+ receive {P, messages_checked} -> ok end,
+
+ {message_queue_len, 0}
+ = rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
+
+ P ! done,
+ unlink(P),
+ verify_no_down(Offender, Victim),
+ stop_node(Offender),
+ stop_node(Victim).
bad_dist_ext_control(Config) when is_list(Config) ->
- ?line {ok, Offender} = start_node(bad_dist_ext_control_offender),
- ?line {ok, Victim} = start_node(bad_dist_ext_control_victim),
- ?line start_node_monitors([Offender,Victim]),
+ {ok, Offender} = start_node(bad_dist_ext_control_offender),
+ {ok, Victim} = start_node(bad_dist_ext_control_victim),
+ start_node_monitors([Offender,Victim]),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line verify_up(Offender, Victim),
- ?line send_bad_dhdr(Offender, Victim),
- ?line verify_down(Offender, connection_closed, Victim, killed),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ verify_up(Offender, Victim),
+ send_bad_dhdr(Offender, Victim),
+ verify_down(Offender, connection_closed, Victim, killed),
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line verify_up(Offender, Victim),
- ?line send_bad_ctl(Offender, Victim),
- ?line verify_down(Offender, connection_closed, Victim, killed),
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ verify_up(Offender, Victim),
+ send_bad_ctl(Offender, Victim),
+ verify_down(Offender, connection_closed, Victim, killed),
- ?line verify_no_down(Offender, Victim),
- ?line stop_node(Offender),
- ?line stop_node(Victim).
+ verify_no_down(Offender, Victim),
+ stop_node(Offender),
+ stop_node(Victim).
bad_dist_ext_connection_id(Config) when is_list(Config) ->
- ?line {ok, Offender} = start_node(bad_dist_ext_connection_id_offender),
- ?line {ok, Victim} = start_node(bad_dist_ext_connection_id_victim),
- ?line start_node_monitors([Offender,Victim]),
-
- ?line Parent = self(),
- ?line P = spawn_link(Victim,
- fun () ->
- Parent ! {self(), started},
- receive check_msgs -> ok end,
- bad_dist_ext_check_msgs([]),
- Parent ! {self(), messages_checked},
- receive done -> ok end
- end),
-
- ?line receive {P, started} -> ok end,
- ?line Suspended = make_ref(),
- ?line S = spawn(Victim,
- fun () ->
- erlang:suspend_process(P),
- Parent ! Suspended,
- receive after infinity -> ok end
- end),
- ?line MS = erlang:monitor(process, S),
- ?line receive Suspended -> ok end,
- ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
- ?line verify_up(Offender, Victim),
- ?line send_bad_msg(Offender, P),
+ {ok, Offender} = start_node(bad_dist_ext_connection_id_offender),
+ {ok, Victim} = start_node(bad_dist_ext_connection_id_victim),
+ start_node_monitors([Offender,Victim]),
+
+ Parent = self(),
+ P = spawn_link(Victim,
+ fun () ->
+ Parent ! {self(), started},
+ receive check_msgs -> ok end,
+ bad_dist_ext_check_msgs([]),
+ Parent ! {self(), messages_checked},
+ receive done -> ok end
+ end),
+
+ receive {P, started} -> ok end,
+ Suspended = make_ref(),
+ S = spawn(Victim,
+ fun () ->
+ erlang:suspend_process(P),
+ Parent ! Suspended,
+ receive after infinity -> ok end
+ end),
+ MS = erlang:monitor(process, S),
+ receive Suspended -> ok end,
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ verify_up(Offender, Victim),
+ send_bad_msg(Offender, P),
%% Make sure bad msg has reached Victim
- ?line rpc:call(Offender, rpc, call, [Victim, erlang, node, []]),
+ rpc:call(Offender, rpc, call, [Victim, erlang, node, []]),
- ?line {message_queue_len, 1}
- = rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
+ {message_queue_len, 1}
+ = rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
- ?line true = rpc:call(Offender, net_kernel, disconnect, [Victim]),
- ?line verify_down(Offender, disconnect, Victim, connection_closed),
- ?line pong = rpc:call(Offender, net_adm, ping, [Victim]),
+ true = rpc:call(Offender, net_kernel, disconnect, [Victim]),
+ verify_down(Offender, disconnect, Victim, connection_closed),
+ pong = rpc:call(Offender, net_adm, ping, [Victim]),
- ?line verify_up(Offender, Victim),
+ verify_up(Offender, Victim),
%% We have a new connection between Offender and Victim, bad message
%% should not bring it down.
- ?line {message_queue_len, 1}
- = rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
+ {message_queue_len, 1}
+ = rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
- ?line exit(S, bang),
- ?line receive {'DOWN', MS, process, S, bang} -> ok end,
+ exit(S, bang),
+ receive {'DOWN', MS, process, S, bang} -> ok end,
%% Wait for a while (if the connection is taken down it might take a
%% while).
- ?line receive after 2000 -> ok end,
- ?line verify_still_up(Offender, Victim),
+ receive after 2000 -> ok end,
+ verify_still_up(Offender, Victim),
+
+ P ! check_msgs,
+ receive {P, messages_checked} -> ok end,
- ?line P ! check_msgs,
- ?line receive {P, messages_checked} -> ok end,
+ {message_queue_len, 0}
+ = rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
- ?line {message_queue_len, 0}
- = rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
-
- ?line verify_still_up(Offender, Victim),
- ?line P ! done,
- ?line unlink(P),
- ?line verify_no_down(Offender, Victim),
- ?line stop_node(Offender),
- ?line stop_node(Victim).
+ verify_still_up(Offender, Victim),
+ P ! done,
+ unlink(P),
+ verify_no_down(Offender, Victim),
+ stop_node(Offender),
+ stop_node(Victim).
bad_dist_struct_check_msgs([]) ->
receive
- Msg ->
- exit({unexpected_message, Msg})
+ Msg ->
+ exit({unexpected_message, Msg})
after 0 ->
- ok
+ ok
end;
bad_dist_struct_check_msgs([M|Ms]) ->
receive
- {'EXIT',_,_} = EM ->
- io:format("Ignoring exit message: ~p~n",[EM]),
- bad_dist_struct_check_msgs([M|Ms]);
- Msg ->
- M = Msg,
- bad_dist_struct_check_msgs(Ms)
+ {'EXIT',_,_} = EM ->
+ io:format("Ignoring exit message: ~p~n",[EM]),
+ bad_dist_struct_check_msgs([M|Ms]);
+ Msg ->
+ M = Msg,
+ bad_dist_struct_check_msgs(Ms)
end.
bad_dist_ext_check_msgs([]) ->
receive
- Msg ->
- exit({unexpected_message, Msg})
+ Msg ->
+ exit({unexpected_message, Msg})
after 0 ->
- ok
+ ok
end;
bad_dist_ext_check_msgs([M|Ms]) ->
receive
- Msg ->
- M = Msg,
- bad_dist_ext_check_msgs(Ms)
+ Msg ->
+ M = Msg,
+ bad_dist_ext_check_msgs(Ms)
end.
-
+
dport_reg_send(Node, Name, Msg) ->
DPrt = case dport(Node) of
- undefined ->
- pong = net_adm:ping(Node),
- dport(Node);
- Prt ->
- Prt
- end,
+ undefined ->
+ pong = net_adm:ping(Node),
+ dport(Node);
+ Prt ->
+ Prt
+ end,
port_command(DPrt, [dmsg_hdr(),
- dmsg_ext({?DOP_REG_SEND,
- self(),
- ?COOKIE,
- Name}),
- dmsg_ext(Msg)]).
+ dmsg_ext({?DOP_REG_SEND,
+ self(),
+ ?COOKIE,
+ Name}),
+ dmsg_ext(Msg)]).
dport_send(To, Msg) ->
Node = node(To),
DPrt = case dport(Node) of
- undefined ->
- pong = net_adm:ping(Node),
- dport(Node);
- Prt ->
- Prt
- end,
+ undefined ->
+ pong = net_adm:ping(Node),
+ dport(Node);
+ Prt ->
+ Prt
+ end,
port_command(DPrt, [dmsg_hdr(),
- dmsg_ext({?DOP_SEND,
- ?COOKIE,
- To}),
- dmsg_ext(Msg)]).
+ dmsg_ext({?DOP_SEND,
+ ?COOKIE,
+ To}),
+ dmsg_ext(Msg)]).
send_bad_structure(Offender,Victim,Bad,WhereToPutSelf) ->
send_bad_structure(Offender,Victim,Bad,WhereToPutSelf,[]).
send_bad_structure(Offender,Victim,Bad,WhereToPutSelf,PayLoad) ->
Parent = self(),
Done = make_ref(),
spawn(Offender,
- fun () ->
- Node = node(Victim),
- pong = net_adm:ping(Node),
- DPrt = dport(Node),
- Bad1 = case WhereToPutSelf of
- 0 ->
- Bad;
- N when N > 0 ->
- setelement(N,Bad,self())
- end,
- DData = [dmsg_hdr(),
- dmsg_ext(Bad1)] ++
- case PayLoad of
- [] -> [];
- _Other -> [dmsg_ext(PayLoad)]
- end,
- port_command(DPrt, DData),
- Parent ! {DData,Done}
- end),
- receive
- {WhatSent,Done} ->
- io:format("Offender sent ~p~n",[WhatSent]),
- ok
+ fun () ->
+ Node = node(Victim),
+ pong = net_adm:ping(Node),
+ DPrt = dport(Node),
+ Bad1 = case WhereToPutSelf of
+ 0 ->
+ Bad;
+ N when N > 0 ->
+ setelement(N,Bad,self())
+ end,
+ DData = [dmsg_hdr(),
+ dmsg_ext(Bad1)] ++
+ case PayLoad of
+ [] -> [];
+ _Other -> [dmsg_ext(PayLoad)]
+ end,
+ port_command(DPrt, DData),
+ Parent ! {DData,Done}
+ end),
+ receive
+ {WhatSent,Done} ->
+ io:format("Offender sent ~p~n",[WhatSent]),
+ ok
after 5000 ->
- exit(unable_to_send)
+ exit(unable_to_send)
end.
-
+
%% send_bad_msgs():
%% Send a valid distribution header and control message
@@ -1861,21 +1787,21 @@ send_bad_msg(BadNode, To) ->
send_bad_msgs(BadNode, To, 1).
send_bad_msgs(BadNode, To, Repeat) when is_atom(BadNode),
- is_pid(To),
- is_integer(Repeat) ->
+ is_pid(To),
+ is_integer(Repeat) ->
Parent = self(),
Done = make_ref(),
spawn_link(BadNode,
- fun () ->
- Node = node(To),
- pong = net_adm:ping(Node),
- DPrt = dport(Node),
- DData = [dmsg_hdr(),
- dmsg_ext({?DOP_SEND, ?COOKIE, To}),
- dmsg_bad_atom_cache_ref()],
- repeat(fun () -> port_command(DPrt, DData) end, Repeat),
- Parent ! Done
- end),
+ fun () ->
+ Node = node(To),
+ pong = net_adm:ping(Node),
+ DPrt = dport(Node),
+ DData = [dmsg_hdr(),
+ dmsg_ext({?DOP_SEND, ?COOKIE, To}),
+ dmsg_bad_atom_cache_ref()],
+ repeat(fun () -> port_command(DPrt, DData) end, Repeat),
+ Parent ! Done
+ end),
receive Done -> ok end.
%% send_bad_ctl():
@@ -1884,24 +1810,24 @@ send_bad_ctl(BadNode, ToNode) when is_atom(BadNode), is_atom(ToNode) ->
Parent = self(),
Done = make_ref(),
spawn_link(BadNode,
- fun () ->
- pong = net_adm:ping(ToNode),
- %% We creat a valid ctl msg and replace an
- %% atom with an invalid atom cache reference
- <<131,Replace/binary>> = term_to_binary(replace),
- Ctl = dmsg_ext({?DOP_REG_SEND,
- self(),
- ?COOKIE,
- replace}),
- CtlBeginSize = size(Ctl) - size(Replace),
- <<CtlBegin:CtlBeginSize/binary, Replace/binary>> = Ctl,
- port_command(dport(ToNode),
- [dmsg_fake_hdr2(),
- CtlBegin,
- dmsg_bad_atom_cache_ref(),
- dmsg_ext({a, message})]),
- Parent ! Done
- end),
+ fun () ->
+ pong = net_adm:ping(ToNode),
+ %% We creat a valid ctl msg and replace an
+ %% atom with an invalid atom cache reference
+ <<131,Replace/binary>> = term_to_binary(replace),
+ Ctl = dmsg_ext({?DOP_REG_SEND,
+ self(),
+ ?COOKIE,
+ replace}),
+ CtlBeginSize = size(Ctl) - size(Replace),
+ <<CtlBegin:CtlBeginSize/binary, Replace/binary>> = Ctl,
+ port_command(dport(ToNode),
+ [dmsg_fake_hdr2(),
+ CtlBegin,
+ dmsg_bad_atom_cache_ref(),
+ dmsg_ext({a, message})]),
+ Parent ! Done
+ end),
receive Done -> ok end.
%% send_bad_dhr():
@@ -1910,17 +1836,17 @@ send_bad_dhdr(BadNode, ToNode) when is_atom(BadNode), is_atom(ToNode) ->
Parent = self(),
Done = make_ref(),
spawn_link(BadNode,
- fun () ->
- pong = net_adm:ping(ToNode),
- port_command(dport(ToNode), dmsg_bad_hdr()),
- Parent ! Done
- end),
+ fun () ->
+ pong = net_adm:ping(ToNode),
+ port_command(dport(ToNode), dmsg_bad_hdr()),
+ Parent ! Done
+ end),
receive Done -> ok end.
dport(Node) when is_atom(Node) ->
case catch erts_debug:get_internal_state(available_internal_state) of
- true -> true;
- _ -> erts_debug:set_internal_state(available_internal_state, true)
+ true -> true;
+ _ -> erts_debug:set_internal_state(available_internal_state, true)
end,
erts_debug:get_internal_state({dist_port, Node}).
@@ -1933,7 +1859,7 @@ dmsg_bad_hdr() ->
[131, % Version Magic
$D, % Dist header
255]. % 255 atom references
-
+
%% dmsg_fake_hdr1() ->
%% A = <<"fake header atom 1">>,
@@ -1952,17 +1878,79 @@ dmsg_fake_hdr2() ->
1, size(A2), A2,
2, size(A3), A3].
-dmsg_ext(Term) ->
+dmsg_ext(Term) ->
<<131, Res/binary>> = term_to_binary(Term),
Res.
dmsg_bad_atom_cache_ref() ->
[$R, 137].
+start_epmd_false(Config) when is_list(Config) ->
+ %% Start a node with the option -start_epmd false.
+ {ok, OtherNode} = start_node(start_epmd_false, "-start_epmd false"),
+ %% We should be able to ping it, as epmd was started by us:
+ pong = net_adm:ping(OtherNode),
+ stop_node(OtherNode),
+
+ ok.
+
+epmd_module(Config) when is_list(Config) ->
+ %% We need a relay node to test this, since the test node uses the
+ %% standard epmd module.
+ Sock1 = start_relay_node(epmd_module_node1, "-epmd_module " ++ ?MODULE_STRING),
+ Node1 = inet_rpc_nodename(Sock1),
+ %% Ask what port it's listening on - it won't have registered with
+ %% epmd.
+ {ok, {ok, Port1}} = do_inet_rpc(Sock1, application, get_env, [kernel, dist_listen_port]),
+
+ %% Start a second node, passing the port number as a secret
+ %% argument.
+ Sock2 = start_relay_node(epmd_module_node2, "-epmd_module " ++ ?MODULE_STRING
+ ++ " -other_node_port " ++ integer_to_list(Port1)),
+ Node2 = inet_rpc_nodename(Sock2),
+ %% Node 1 can't ping node 2
+ {ok, pang} = do_inet_rpc(Sock1, net_adm, ping, [Node2]),
+ {ok, []} = do_inet_rpc(Sock1, erlang, nodes, []),
+ {ok, []} = do_inet_rpc(Sock2, erlang, nodes, []),
+ %% But node 2 can ping node 1
+ {ok, pong} = do_inet_rpc(Sock2, net_adm, ping, [Node1]),
+ {ok, [Node2]} = do_inet_rpc(Sock1, erlang, nodes, []),
+ {ok, [Node1]} = do_inet_rpc(Sock2, erlang, nodes, []),
+
+ stop_relay_node(Sock2),
+ stop_relay_node(Sock1).
+
+%% epmd_module functions:
+
+start_link() ->
+ ignore.
+
+register_node(Name, Port) ->
+ register_node(Name, Port, inet_tcp).
+register_node(_Name, Port, _Driver) ->
+ %% Save the port number we're listening on.
+ application:set_env(kernel, dist_listen_port, Port),
+ Creation = rand:uniform(3),
+ {ok, Creation}.
+
+port_please(_Name, _Ip) ->
+ case init:get_argument(other_node_port) of
+ error ->
+ %% None specified. Default to 42.
+ Port = 42,
+ Version = 5,
+ {port, Port, Version};
+ {ok, [[PortS]]} ->
+ %% Port number given on command line.
+ Port = list_to_integer(PortS),
+ Version = 5,
+ {port, Port, Version}
+ end.
+
%%% Utilities
timestamp() ->
- erlang:monotonic_time(milli_seconds).
+ erlang:monotonic_time(millisecond).
start_node(X) ->
start_node(X, [], []).
@@ -1974,21 +1962,21 @@ start_node(Name, Args, Rel) when is_atom(Name), is_list(Rel) ->
Pa = filename:dirname(code:which(?MODULE)),
Cookie = atom_to_list(erlang:get_cookie()),
RelArg = case Rel of
- [] -> [];
- _ -> [{erl,[{release,Rel}]}]
- end,
- test_server:start_node(Name, slave,
- [{args,
- Args++" -setcookie "++Cookie++" -pa \""++Pa++"\""}
- | RelArg]);
+ [] -> [];
+ _ -> [{erl,[{release,Rel}]}]
+ end,
+ test_server:start_node(Name, slave,
+ [{args,
+ Args++" -setcookie "++Cookie++" -pa \""++Pa++"\""}
+ | RelArg]);
start_node(Config, Args, Rel) when is_list(Config), is_list(Rel) ->
Name = list_to_atom((atom_to_list(?MODULE)
- ++ "-"
- ++ atom_to_list(?config(testcase, Config))
- ++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
- ++ "-"
- ++ integer_to_list(erlang:unique_integer([positive])))),
+ ++ "-"
+ ++ atom_to_list(proplists:get_value(testcase, Config))
+ ++ "-"
+ ++ integer_to_list(erlang:system_time(second))
+ ++ "-"
+ ++ integer_to_list(erlang:unique_integer([positive])))),
start_node(Name, Args, Rel).
stop_node(Node) ->
@@ -1999,13 +1987,13 @@ freeze_node(Node, MS) ->
DoingIt = make_ref(),
Freezer = self(),
spawn_link(Node,
- fun () ->
- erts_debug:set_internal_state(available_internal_state,
- true),
- dport_send(Freezer, DoingIt),
- receive after Own -> ok end,
- erts_debug:set_internal_state(block, MS+Own)
- end),
+ fun () ->
+ erts_debug:set_internal_state(available_internal_state,
+ true),
+ dport_send(Freezer, DoingIt),
+ receive after Own -> ok end,
+ erts_debug:set_internal_state(block, MS+Own)
+ end),
receive DoingIt -> ok end,
receive after Own -> ok end.
@@ -2016,46 +2004,44 @@ do_inet_rpc({_,_,Sock},M,F,A) ->
Bin = term_to_binary({M,F,A}),
gen_tcp:send(Sock,Bin),
case gen_tcp:recv(Sock,0) of
- {ok, Bin2} ->
- T = binary_to_term(Bin2),
- {ok,T};
- Else ->
- {error, Else}
+ {ok, Bin2} ->
+ T = binary_to_term(Bin2),
+ {ok,T};
+ Else ->
+ {error, Else}
end.
inet_rpc_server([Host, PortList]) ->
Port = list_to_integer(PortList),
{ok, Sock} = gen_tcp:connect(Host, Port,[binary, {packet, 4},
- {active, false}]),
+ {active, false}]),
inet_rpc_server_loop(Sock).
inet_rpc_server_loop(Sock) ->
case gen_tcp:recv(Sock,0) of
- {ok, Bin} ->
- {M,F,A} = binary_to_term(Bin),
- Res = (catch apply(M,F,A)),
- RB = term_to_binary(Res),
- gen_tcp:send(Sock,RB),
- inet_rpc_server_loop(Sock);
- _ ->
- erlang:halt()
+ {ok, Bin} ->
+ {M,F,A} = binary_to_term(Bin),
+ Res = (catch apply(M,F,A)),
+ RB = term_to_binary(Res),
+ gen_tcp:send(Sock,RB),
+ inet_rpc_server_loop(Sock);
+ _ ->
+ erlang:halt()
end.
-
+
start_relay_node(Node, Args) ->
Pa = filename:dirname(code:which(?MODULE)),
Cookie = "NOT"++atom_to_list(erlang:get_cookie()),
- {ok, LSock} = gen_tcp:listen(0, [binary, {packet, 4},
- {active, false}]),
+ {ok, LSock} = gen_tcp:listen(0, [binary, {packet, 4}, {active, false}]),
{ok, Port} = inet:port(LSock),
{ok, Host} = inet:gethostname(),
RunArg = "-run " ++ atom_to_list(?MODULE) ++ " inet_rpc_server " ++
- Host ++ " " ++ integer_to_list(Port),
- {ok, NN} =
- test_server:start_node(Node, peer,
- [{args, Args ++
- " -setcookie "++Cookie++" -pa "++Pa++" "++
- RunArg}]),
+ Host ++ " " ++ integer_to_list(Port),
+ {ok, NN} = test_server:start_node(Node, peer,
+ [{args, Args ++
+ " -setcookie "++Cookie++" -pa "++Pa++" "++
+ RunArg}]),
[N,H] = string:tokens(atom_to_list(NN),"@"),
{ok, Sock} = gen_tcp:accept(LSock),
pang = net_adm:ping(NN),
@@ -2070,28 +2056,28 @@ wait_dead(N,H,0) ->
{error,{not_dead,N,H}};
wait_dead(N,H,X) ->
case erl_epmd:port_please(N,H) of
- {port,_,_} ->
- receive
- after 1000 ->
- ok
- end,
- wait_dead(N,H,X-1);
- noport ->
- ok;
- Else ->
- {error, {unexpected, Else}}
+ {port,_,_} ->
+ receive
+ after 1000 ->
+ ok
+ end,
+ wait_dead(N,H,X-1);
+ noport ->
+ ok;
+ Else ->
+ {error, {unexpected, Else}}
end.
-
+
start_node_monitors(Nodes) ->
Master = self(),
lists:foreach(fun (Node) ->
- spawn(Node,
- fun () ->
- node_monitor(Master)
- end)
- end,
- Nodes),
+ spawn(Node,
+ fun () ->
+ node_monitor(Master)
+ end)
+ end,
+ Nodes),
ok.
node_monitor(Master) ->
@@ -2100,42 +2086,42 @@ node_monitor(Master) ->
net_kernel:monitor_nodes(true, Opts),
Nodes1 = nodes(connected),
case lists:sort(Nodes0) == lists:sort(Nodes1) of
- true ->
- lists:foreach(fun (Node) ->
- Master ! {nodeup, node(), Node}
- end,
- Nodes0),
- ?t:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Nodes0]),
- node_monitor_loop(Master);
- false ->
- net_kernel:monitor_nodes(false, Opts),
- flush_node_changes(),
- node_monitor(Master)
+ true ->
+ lists:foreach(fun (Node) ->
+ Master ! {nodeup, node(), Node}
+ end,
+ Nodes0),
+ io:format("~p ~p: ~p~n", [node(), erlang:system_time(microsecond), Nodes0]),
+ node_monitor_loop(Master);
+ false ->
+ net_kernel:monitor_nodes(false, Opts),
+ flush_node_changes(),
+ node_monitor(Master)
end.
flush_node_changes() ->
receive
- {NodeChange, _Node, _InfoList} when NodeChange == nodeup;
- NodeChange == nodedown ->
- flush_node_changes()
+ {NodeChange, _Node, _InfoList} when NodeChange == nodeup;
+ NodeChange == nodedown ->
+ flush_node_changes()
after 0 ->
- ok
+ ok
end.
node_monitor_loop(Master) ->
receive
- {nodeup, Node, _InfoList} = Msg ->
- Master ! {nodeup, node(), Node},
- ?t:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Msg]),
- node_monitor_loop(Master);
- {nodedown, Node, InfoList} = Msg ->
- Reason = case lists:keysearch(nodedown_reason, 1, InfoList) of
- {value, {nodedown_reason, R}} -> R;
- _ -> undefined
- end,
- Master ! {nodedown, node(), Node, Reason},
- ?t:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Msg]),
- node_monitor_loop(Master)
+ {nodeup, Node, _InfoList} = Msg ->
+ Master ! {nodeup, node(), Node},
+ io:format("~p ~p: ~p~n", [node(), erlang:system_time(microsecond), Msg]),
+ node_monitor_loop(Master);
+ {nodedown, Node, InfoList} = Msg ->
+ Reason = case lists:keysearch(nodedown_reason, 1, InfoList) of
+ {value, {nodedown_reason, R}} -> R;
+ _ -> undefined
+ end,
+ Master ! {nodedown, node(), Node, Reason},
+ io:format("~p ~p: ~p~n", [node(), erlang:system_time(microsecond), Msg]),
+ node_monitor_loop(Master)
end.
verify_up(A, B) ->
@@ -2149,16 +2135,16 @@ verify_still_up(A, B) ->
verify_no_down(A, B) ->
receive
- {nodedown, A, B, _} = Msg0 ->
- ?t:fail(Msg0)
+ {nodedown, A, B, _} = Msg0 ->
+ ct:fail(Msg0)
after 0 ->
- ok
+ ok
end,
receive
- {nodedown, B, A, _} = Msg1 ->
- ?t:fail(Msg1)
+ {nodedown, B, A, _} = Msg1 ->
+ ct:fail(Msg1)
after 0 ->
- ok
+ ok
end.
%% verify_down(A, B) ->
@@ -2167,12 +2153,12 @@ verify_no_down(A, B) ->
verify_down(A, ReasonA, B, ReasonB) ->
receive
- {nodedown, A, B, _} = Msg0 ->
- {nodedown, A, B, ReasonA} = Msg0
+ {nodedown, A, B, _} = Msg0 ->
+ {nodedown, A, B, ReasonA} = Msg0
end,
receive
- {nodedown, B, A, _} = Msg1 ->
- {nodedown, B, A, ReasonB} = Msg1
+ {nodedown, B, A, _} = Msg1 ->
+ {nodedown, B, A, ReasonB} = Msg1
end,
ok.
@@ -2192,17 +2178,17 @@ from(_, []) -> [].
long_or_short() ->
case net_kernel:longnames() of
- true -> " -name ";
- false -> " -sname "
+ true -> " -name ";
+ false -> " -sname "
end.
until(Fun) ->
case Fun() of
- true ->
- ok;
- false ->
- receive after 10 -> ok end,
- until(Fun)
+ true ->
+ ok;
+ false ->
+ receive after 10 -> ok end,
+ until(Fun)
end.
forever(Fun) ->
@@ -2227,9 +2213,9 @@ stop_busy_dist_port_tracer(_) ->
busy_dist_port_tracer() ->
receive
- {monitor, _SuspendedProcess, busy_dist_port, _Port} = M ->
- erlang:display(M),
- busy_dist_port_tracer()
+ {monitor, _SuspendedProcess, busy_dist_port, _Port} = M ->
+ erlang:display(M),
+ busy_dist_port_tracer()
end.
repeat(_Fun, 0) ->
@@ -2242,38 +2228,38 @@ string_to_atom_ext(String) ->
Utf8List = string_to_utf8_list(String),
Len = length(Utf8List),
case Len < 256 of
- true ->
- [?SMALL_ATOM_UTF8_EXT, Len | Utf8List];
- false ->
- [?ATOM_UTF8_EXT, Len bsr 8, Len band 16#ff | Utf8List]
+ true ->
+ [?SMALL_ATOM_UTF8_EXT, Len | Utf8List];
+ false ->
+ [?ATOM_UTF8_EXT, Len bsr 8, Len band 16#ff | Utf8List]
end.
string_to_atom(String) ->
binary_to_term(list_to_binary([?VERSION_MAGIC
- | string_to_atom_ext(String)])).
+ | string_to_atom_ext(String)])).
string_to_utf8_list([]) ->
[];
string_to_utf8_list([CP|CPs]) when is_integer(CP),
- 0 =< CP,
- CP =< 16#7F ->
+ 0 =< CP,
+ CP =< 16#7F ->
[CP | string_to_utf8_list(CPs)];
string_to_utf8_list([CP|CPs]) when is_integer(CP),
- 16#80 =< CP,
- CP =< 16#7FF ->
+ 16#80 =< CP,
+ CP =< 16#7FF ->
[16#C0 bor (CP bsr 6),
16#80 bor (16#3F band CP)
| string_to_utf8_list(CPs)];
string_to_utf8_list([CP|CPs]) when is_integer(CP),
- 16#800 =< CP,
- CP =< 16#FFFF ->
+ 16#800 =< CP,
+ CP =< 16#FFFF ->
[16#E0 bor (CP bsr 12),
16#80 bor (16#3F band (CP bsr 6)),
16#80 bor (16#3F band CP)
| string_to_utf8_list(CPs)];
string_to_utf8_list([CP|CPs]) when is_integer(CP),
- 16#10000 =< CP,
- CP =< 16#10FFFF ->
+ 16#10000 =< CP,
+ CP =< 16#10FFFF ->
[16#F0 bor (CP bsr 18),
16#80 bor (16#3F band (CP bsr 12)),
16#80 bor (16#3F band (CP bsr 6)),
@@ -2283,47 +2269,47 @@ string_to_utf8_list([CP|CPs]) when is_integer(CP),
utf8_list_to_string([]) ->
[];
utf8_list_to_string([B|Bs]) when is_integer(B),
- 0 =< B,
- B =< 16#7F ->
+ 0 =< B,
+ B =< 16#7F ->
[B | utf8_list_to_string(Bs)];
utf8_list_to_string([B0, B1 | Bs]) when is_integer(B0),
- 16#C0 =< B0,
- B0 =< 16#DF,
- is_integer(B1),
- 16#80 =< B1,
- B1 =< 16#BF ->
+ 16#C0 =< B0,
+ B0 =< 16#DF,
+ is_integer(B1),
+ 16#80 =< B1,
+ B1 =< 16#BF ->
[(((B0 band 16#1F) bsl 6)
- bor (B1 band 16#3F))
+ bor (B1 band 16#3F))
| utf8_list_to_string(Bs)];
utf8_list_to_string([B0, B1, B2 | Bs]) when is_integer(B0),
- 16#E0 =< B0,
- B0 =< 16#EF,
- is_integer(B1),
- 16#80 =< B1,
- B1 =< 16#BF,
- is_integer(B2),
- 16#80 =< B2,
- B2 =< 16#BF ->
+ 16#E0 =< B0,
+ B0 =< 16#EF,
+ is_integer(B1),
+ 16#80 =< B1,
+ B1 =< 16#BF,
+ is_integer(B2),
+ 16#80 =< B2,
+ B2 =< 16#BF ->
[(((B0 band 16#F) bsl 12)
- bor ((B1 band 16#3F) bsl 6)
- bor (B2 band 16#3F))
+ bor ((B1 band 16#3F) bsl 6)
+ bor (B2 band 16#3F))
| utf8_list_to_string(Bs)];
utf8_list_to_string([B0, B1, B2, B3 | Bs]) when is_integer(B0),
- 16#F0 =< B0,
- B0 =< 16#F7,
- is_integer(B1),
- 16#80 =< B1,
- B1 =< 16#BF,
- is_integer(B2),
- 16#80 =< B2,
- B2 =< 16#BF,
- is_integer(B3),
- 16#80 =< B3,
- B3 =< 16#BF ->
+ 16#F0 =< B0,
+ B0 =< 16#F7,
+ is_integer(B1),
+ 16#80 =< B1,
+ B1 =< 16#BF,
+ is_integer(B2),
+ 16#80 =< B2,
+ B2 =< 16#BF,
+ is_integer(B3),
+ 16#80 =< B3,
+ B3 =< 16#BF ->
[(((B0 band 16#7) bsl 18)
- bor ((B1 band 16#3F) bsl 12)
- bor ((B2 band 16#3F) bsl 6)
- bor (B3 band 16#3F))
+ bor ((B1 band 16#3F) bsl 12)
+ bor ((B2 band 16#3F) bsl 6)
+ bor (B3 band 16#3F))
| utf8_list_to_string(Bs)].
mk_pid({NodeName, Creation}, Number, Serial) when is_atom(NodeName) ->
@@ -2331,17 +2317,17 @@ mk_pid({NodeName, Creation}, Number, Serial) when is_atom(NodeName) ->
mk_pid({NodeNameExt, Creation}, Number, Serial);
mk_pid({NodeNameExt, Creation}, Number, Serial) ->
case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
- ?PID_EXT,
- NodeNameExt,
- uint32_be(Number),
- uint32_be(Serial),
- uint8(Creation)])) of
- Pid when is_pid(Pid) ->
- Pid;
- {'EXIT', {badarg, _}} ->
- exit({badarg, mk_pid, [{NodeNameExt, Creation}, Number, Serial]});
- Other ->
- exit({unexpected_binary_to_term_result, Other})
+ ?PID_EXT,
+ NodeNameExt,
+ uint32_be(Number),
+ uint32_be(Serial),
+ uint8(Creation)])) of
+ Pid when is_pid(Pid) ->
+ Pid;
+ {'EXIT', {badarg, _}} ->
+ exit({badarg, mk_pid, [{NodeNameExt, Creation}, Number, Serial]});
+ Other ->
+ exit({unexpected_binary_to_term_result, Other})
end.
mk_port({NodeName, Creation}, Number) when is_atom(NodeName) ->
@@ -2349,59 +2335,59 @@ mk_port({NodeName, Creation}, Number) when is_atom(NodeName) ->
mk_port({NodeNameExt, Creation}, Number);
mk_port({NodeNameExt, Creation}, Number) ->
case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
- ?PORT_EXT,
- NodeNameExt,
- uint32_be(Number),
- uint8(Creation)])) of
- Port when is_port(Port) ->
- Port;
- {'EXIT', {badarg, _}} ->
- exit({badarg, mk_port, [{NodeNameExt, Creation}, Number]});
- Other ->
- exit({unexpected_binary_to_term_result, Other})
+ ?PORT_EXT,
+ NodeNameExt,
+ uint32_be(Number),
+ uint8(Creation)])) of
+ Port when is_port(Port) ->
+ Port;
+ {'EXIT', {badarg, _}} ->
+ exit({badarg, mk_port, [{NodeNameExt, Creation}, Number]});
+ Other ->
+ exit({unexpected_binary_to_term_result, Other})
end.
mk_ref({NodeName, Creation}, [Number] = NL) when is_atom(NodeName),
- is_integer(Creation),
- is_integer(Number) ->
+ is_integer(Creation),
+ is_integer(Number) ->
<<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName),
mk_ref({NodeNameExt, Creation}, NL);
mk_ref({NodeNameExt, Creation}, [Number]) when is_integer(Creation),
- is_integer(Number) ->
+ is_integer(Number) ->
case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
- ?REFERENCE_EXT,
- NodeNameExt,
- uint32_be(Number),
- uint8(Creation)])) of
- Ref when is_reference(Ref) ->
- Ref;
- {'EXIT', {badarg, _}} ->
- exit({badarg, mk_ref, [{NodeNameExt, Creation}, [Number]]});
- Other ->
- exit({unexpected_binary_to_term_result, Other})
+ ?REFERENCE_EXT,
+ NodeNameExt,
+ uint32_be(Number),
+ uint8(Creation)])) of
+ Ref when is_reference(Ref) ->
+ Ref;
+ {'EXIT', {badarg, _}} ->
+ exit({badarg, mk_ref, [{NodeNameExt, Creation}, [Number]]});
+ Other ->
+ exit({unexpected_binary_to_term_result, Other})
end;
mk_ref({NodeName, Creation}, Numbers) when is_atom(NodeName),
- is_integer(Creation),
- is_list(Numbers) ->
+ is_integer(Creation),
+ is_list(Numbers) ->
<<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName),
mk_ref({NodeNameExt, Creation}, Numbers);
mk_ref({NodeNameExt, Creation}, Numbers) when is_integer(Creation),
- is_list(Numbers) ->
+ is_list(Numbers) ->
case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
- ?NEW_REFERENCE_EXT,
- uint16_be(length(Numbers)),
- NodeNameExt,
- uint8(Creation),
- lists:map(fun (N) ->
- uint32_be(N)
- end,
- Numbers)])) of
- Ref when is_reference(Ref) ->
- Ref;
- {'EXIT', {badarg, _}} ->
- exit({badarg, mk_ref, [{NodeNameExt, Creation}, Numbers]});
- Other ->
- exit({unexpected_binary_to_term_result, Other})
+ ?NEW_REFERENCE_EXT,
+ uint16_be(length(Numbers)),
+ NodeNameExt,
+ uint8(Creation),
+ lists:map(fun (N) ->
+ uint32_be(N)
+ end,
+ Numbers)])) of
+ Ref when is_reference(Ref) ->
+ Ref;
+ {'EXIT', {badarg, _}} ->
+ exit({badarg, mk_ref, [{NodeNameExt, Creation}, Numbers]});
+ Other ->
+ exit({unexpected_binary_to_term_result, Other})
end.
diff --git a/erts/emulator/test/distribution_SUITE_data/run.erl b/erts/emulator/test/distribution_SUITE_data/run.erl
index d5ed139369..f574b2c02c 100644
--- a/erts/emulator/test/distribution_SUITE_data/run.erl
+++ b/erts/emulator/test/distribution_SUITE_data/run.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl
index 4211c49848..2fbf6eae61 100644
--- a/erts/emulator/test/driver_SUITE.erl
+++ b/erts/emulator/test/driver_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -29,64 +29,64 @@
-module(driver_SUITE).
-export([all/0, suite/0,groups/0,init_per_suite/1,
- end_per_suite/1, init_per_group/2,end_per_group/2,
- init_per_testcase/2,
- end_per_testcase/2,
-
- a_test/1,
- outputv_echo/1,
- timer_measure/1,
- timer_cancel/1,
- timer_change/1,
- timer_delay/1,
- queue_echo/1,
- outputv_errors/1,
- driver_unloaded/1,
- io_ready_exit/1,
- use_fallback_pollset/1,
- bad_fd_in_pollset/1,
- driver_event/1,
- fd_change/1,
- steal_control/1,
- otp_6602/1,
- driver_system_info_base_ver/1,
- driver_system_info_prev_ver/1,
- driver_system_info_current_ver/1,
- driver_monitor/1,
-
- ioq_exit_ready_input/1,
- ioq_exit_ready_output/1,
- ioq_exit_timeout/1,
- ioq_exit_ready_async/1,
- ioq_exit_event/1,
- ioq_exit_ready_input_async/1,
- ioq_exit_ready_output_async/1,
- ioq_exit_timeout_async/1,
- ioq_exit_event_async/1,
- zero_extended_marker_garb_drv/1,
- invalid_extended_marker_drv/1,
- larger_major_vsn_drv/1,
- larger_minor_vsn_drv/1,
- smaller_major_vsn_drv/1,
- smaller_minor_vsn_drv/1,
- peek_non_existing_queue/1,
- otp_6879/1,
- caller/1,
- many_events/1,
- missing_callbacks/1,
- smp_select/1,
- driver_select_use/1,
- thread_mseg_alloc_cache_clean/1,
- otp_9302/1,
- thr_free_drv/1,
- async_blast/1,
- thr_msg_blast/1,
- consume_timeslice/1,
- z_test/1]).
+ end_per_suite/1, init_per_group/2,end_per_group/2,
+ init_per_testcase/2,
+ end_per_testcase/2,
+
+ a_test/1,
+ outputv_echo/1,
+ timer_measure/1,
+ timer_cancel/1,
+ timer_change/1,
+ timer_delay/1,
+ queue_echo/1,
+ outputv_errors/1,
+ driver_unloaded/1,
+ io_ready_exit/1,
+ use_fallback_pollset/1,
+ bad_fd_in_pollset/1,
+ driver_event/1,
+ fd_change/1,
+ steal_control/1,
+ otp_6602/1,
+ driver_system_info_base_ver/1,
+ driver_system_info_prev_ver/1,
+ driver_system_info_current_ver/1,
+ driver_monitor/1,
+
+ ioq_exit_ready_input/1,
+ ioq_exit_ready_output/1,
+ ioq_exit_timeout/1,
+ ioq_exit_ready_async/1,
+ ioq_exit_event/1,
+ ioq_exit_ready_input_async/1,
+ ioq_exit_ready_output_async/1,
+ ioq_exit_timeout_async/1,
+ ioq_exit_event_async/1,
+ zero_extended_marker_garb_drv/1,
+ invalid_extended_marker_drv/1,
+ larger_major_vsn_drv/1,
+ larger_minor_vsn_drv/1,
+ smaller_major_vsn_drv/1,
+ smaller_minor_vsn_drv/1,
+ peek_non_existing_queue/1,
+ otp_6879/1,
+ caller/1,
+ many_events/1,
+ missing_callbacks/1,
+ smp_select/1,
+ driver_select_use/1,
+ thread_mseg_alloc_cache_clean/1,
+ otp_9302/1,
+ thr_free_drv/1,
+ async_blast/1,
+ thr_msg_blast/1,
+ consume_timeslice/1,
+ z_test/1]).
-export([bin_prefix/2]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
% First byte in communication with the timer driver
@@ -114,27 +114,27 @@
-define(MAX_DATA_SIZE, 16384).
% This is the allowed delay when testing the driver timer functionality
--define(delay, 100).
+-define(delay, 400).
-define(heap_binary_size, 64).
init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
- Dog=?t:timetrap(?t:minutes(2)),
case catch erts_debug:get_internal_state(available_internal_state) of
- true -> ok;
- _ -> erts_debug:set_internal_state(available_internal_state, true)
+ true -> ok;
+ _ -> erts_debug:set_internal_state(available_internal_state, true)
end,
erlang:display({init_per_testcase, Case}),
- ?line 0 = element(1, erts_debug:get_internal_state(check_io_debug)),
- [{watchdog, Dog},{testcase, Case}|Config].
+ 0 = element(1, erts_debug:get_internal_state(check_io_debug)),
+ [{testcase, Case}|Config].
end_per_testcase(Case, Config) ->
- Dog = ?config(watchdog, Config),
erlang:display({end_per_testcase, Case}),
- ?line 0 = element(1, erts_debug:get_internal_state(check_io_debug)),
- ?t:timetrap_cancel(Dog).
+ 0 = element(1, erts_debug:get_internal_state(check_io_debug)),
+ ok.
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 1}}].
all() -> %% Keep a_test first and z_test last...
[a_test, outputv_errors, outputv_echo, queue_echo, {group, timer},
@@ -179,42 +179,42 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
-outputv_errors(doc) -> "Test sending bad types to port with an outputv-capable driver.";
+%% Test sending bad types to port with an outputv-capable driver.
outputv_errors(Config) when is_list(Config) ->
- ?line Path = ?config(data_dir, Config),
- ?line erl_ddll:start(),
- ?line ok = load_driver(Path, outputv_drv),
+ Path = proplists:get_value(data_dir, Config),
+ erl_ddll:start(),
+ ok = load_driver(Path, outputv_drv),
outputv_bad_types(fun(T) ->
- ?line outputv_errors_1(T),
- ?line outputv_errors_1([1|T]),
- ?line L = [1,2,3],
- ?line outputv_errors_1([L,T]),
- ?line outputv_errors_1([L|T])
- end),
+ outputv_errors_1(T),
+ outputv_errors_1([1|T]),
+ L = [1,2,3],
+ outputv_errors_1([L,T]),
+ outputv_errors_1([L|T])
+ end),
outputv_errors_1(42),
%% Test iolists that do not fit in the address space.
%% Unfortunately, it would be too slow to test in a 64-bit emulator.
case erlang:system_info(wordsize) of
- 4 -> outputv_huge_iolists();
- _ -> ok
+ 4 -> outputv_huge_iolists();
+ _ -> ok
end.
outputv_bad_types(Test) ->
Types = [-1,256,atom,42.0,{a,b,c},make_ref(),fun() -> 42 end,
- [1|2],<<1:1>>,<<1:9>>,<<1:15>>],
+ [1|2],<<1:1>>,<<1:9>>,<<1:15>>],
_ = [Test(Type) || Type <- Types],
ok.
outputv_huge_iolists() ->
FourGigs = 1 bsl 32,
- ?line Sizes = [FourGigs+N || N <- lists:seq(0, 64)] ++
- [1 bsl N || N <- lists:seq(33, 37)],
- ?line Base = <<0:(1 bsl 20)/unit:8>>,
+ Sizes = [FourGigs+N || N <- lists:seq(0, 64)] ++
+ [1 bsl N || N <- lists:seq(33, 37)],
+ Base = <<0:(1 bsl 20)/unit:8>>,
[begin
- ?line L = build_iolist(Sz, Base),
- ?line outputv_errors_1(L)
+ L = build_iolist(Sz, Base),
+ outputv_errors_1(L)
end || Sz <- Sizes],
ok.
@@ -224,95 +224,94 @@ outputv_errors_1(Term) ->
port_close(Port).
build_iolist(N, Base) when N < 16 ->
- case random:uniform(3) of
- 1 ->
- <<Bin:N/binary,_/binary>> = Base,
- Bin;
- _ ->
- lists:seq(1, N)
+ case rand:uniform(3) of
+ 1 ->
+ <<Bin:N/binary,_/binary>> = Base,
+ Bin;
+ _ ->
+ lists:seq(1, N)
end;
build_iolist(N, Base) when N =< byte_size(Base) ->
- case random:uniform(3) of
- 1 ->
- <<Bin:N/binary,_/binary>> = Base,
- Bin;
- 2 ->
- <<Bin:N/binary,_/binary>> = Base,
- [Bin];
- 3 ->
- case N rem 2 of
- 0 ->
- L = build_iolist(N div 2, Base),
- [L,L];
- 1 ->
- L = build_iolist(N div 2, Base),
- [L,L,45]
- end
+ case rand:uniform(3) of
+ 1 ->
+ <<Bin:N/binary,_/binary>> = Base,
+ Bin;
+ 2 ->
+ <<Bin:N/binary,_/binary>> = Base,
+ [Bin];
+ 3 ->
+ case N rem 2 of
+ 0 ->
+ L = build_iolist(N div 2, Base),
+ [L,L];
+ 1 ->
+ L = build_iolist(N div 2, Base),
+ [L,L,45]
+ end
end;
build_iolist(N0, Base) ->
- Small = random:uniform(15),
+ Small = rand:uniform(15),
Seq = lists:seq(1, Small),
N = N0 - Small,
case N rem 2 of
- 0 ->
- L = build_iolist(N div 2, Base),
- [L,L|Seq];
- 1 ->
- L = build_iolist(N div 2, Base),
- [47,L,L|Seq]
+ 0 ->
+ L = build_iolist(N div 2, Base),
+ [L,L|Seq];
+ 1 ->
+ L = build_iolist(N div 2, Base),
+ [47,L,L|Seq]
end.
-outputv_echo(doc) -> ["Test echoing data with a driver that supports outputv."];
+%% Test echoing data with a driver that supports outputv.
outputv_echo(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:minutes(10)),
+ ct:timetrap({minutes, 10}),
Name = 'outputv_drv',
P = start_driver(Config, Name, true),
- ?line ov_test(P, {bin,0}),
- ?line ov_test(P, {bin,1}),
- ?line ov_test(P, {bin,2}),
- ?line ov_test(P, {bin,3}),
- ?line ov_test(P, {bin,4}),
- ?line ov_test(P, {bin,5}),
- ?line ov_test(P, {bin,6}),
- ?line ov_test(P, {bin,7}),
- ?line ov_test(P, {bin,8}),
- ?line ov_test(P, {bin,15}),
- ?line ov_test(P, {bin,16}),
- ?line ov_test(P, {bin,17}),
-
- ?line ov_test(P, {list,0}),
- ?line ov_test(P, {list,1}),
- ?line ov_test(P, {list,2}),
- ?line ov_test(P, [int,int,{list,0},int]),
- ?line ov_test(P, [int,int,{list,1},int]),
- ?line ov_test(P, [int,int,{list,2}]),
- ?line ov_test(P, [{list,3},int,int,{list,2}]),
- ?line ov_test(P, {list,33}),
-
- ?line ov_test(P, [{bin,0}]),
- ?line ov_test(P, [{bin,1}]),
- ?line ov_test(P, [{bin,2}]),
- ?line ov_test(P, [{bin,3}]),
- ?line ov_test(P, [{bin,4}]),
- ?line ov_test(P, [{bin,5}]),
- ?line ov_test(P, [{bin,6},int]),
- ?line ov_test(P, [int,{bin,3}]),
- ?line ov_test(P, [int|{bin,4}]),
- ?line ov_test(P, [{bin,17},int,{bin,13}|{bin,3}]),
-
- ?line ov_test(P, [int,{bin,17},int,{bin,?heap_binary_size+1}|{bin,3}]),
+ ov_test(P, {bin,0}),
+ ov_test(P, {bin,1}),
+ ov_test(P, {bin,2}),
+ ov_test(P, {bin,3}),
+ ov_test(P, {bin,4}),
+ ov_test(P, {bin,5}),
+ ov_test(P, {bin,6}),
+ ov_test(P, {bin,7}),
+ ov_test(P, {bin,8}),
+ ov_test(P, {bin,15}),
+ ov_test(P, {bin,16}),
+ ov_test(P, {bin,17}),
+
+ ov_test(P, {list,0}),
+ ov_test(P, {list,1}),
+ ov_test(P, {list,2}),
+ ov_test(P, [int,int,{list,0},int]),
+ ov_test(P, [int,int,{list,1},int]),
+ ov_test(P, [int,int,{list,2}]),
+ ov_test(P, [{list,3},int,int,{list,2}]),
+ ov_test(P, {list,33}),
+
+ ov_test(P, [{bin,0}]),
+ ov_test(P, [{bin,1}]),
+ ov_test(P, [{bin,2}]),
+ ov_test(P, [{bin,3}]),
+ ov_test(P, [{bin,4}]),
+ ov_test(P, [{bin,5}]),
+ ov_test(P, [{bin,6},int]),
+ ov_test(P, [int,{bin,3}]),
+ ov_test(P, [int|{bin,4}]),
+ ov_test(P, [{bin,17},int,{bin,13}|{bin,3}]),
+
+ ov_test(P, [int,{bin,17},int,{bin,?heap_binary_size+1}|{bin,3}]),
stop_driver(P, Name),
- ?line test_server:timetrap_cancel(Dog),
ok.
ov_test(Port, Template) ->
Self = self(),
spawn_opt(erlang, apply, [fun () -> ov_test(Self, Port, Template) end,[]],
- [link,{fullsweep_after,0}]),
+ [link,{fullsweep_after,0}]),
receive
- done -> ok
+ done -> ok
end.
ov_test(Parent, Port, Template) ->
@@ -354,21 +353,20 @@ ov_send_and_test(Port, Data, ExpectedResult) ->
io:format("~p ! ~P", [Port,Data,12]),
Port ! {self(),{command,Data}},
receive
- {Port,{data,ReturnData}} ->
- io:format("~p returned ~P", [Port,ReturnData,12]),
- compare(ReturnData, ExpectedResult);
- {Port,{data,OtherData}} ->
- io:format("~p returned WRONG data ~p", [Port,OtherData]),
- ?line test_server:fail();
- Wrong ->
- ?line test_server:fail({unexpected_port_or_data,Wrong})
+ {Port,{data,ReturnData}} ->
+ io:format("~p returned ~P", [Port,ReturnData,12]),
+ compare(ReturnData, ExpectedResult);
+ {Port,{data,OtherData}} ->
+ ct:fail("~p returned WRONG data ~p", [Port,OtherData]);
+ Wrong ->
+ ct:fail({unexpected_port_or_data,Wrong})
end.
compare(Got, Expected) ->
case {list_to_binary([Got]),list_to_binary([Expected])} of
- {B,B} -> ok;
- {_Gb,_Eb} ->
- ?t:fail(got_bad_data)
+ {B,B} -> ok;
+ {_Gb,_Eb} ->
+ ct:fail(got_bad_data)
end.
@@ -377,146 +375,134 @@ compare(Got, Expected) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-timer_measure(doc) -> ["Check that timers time out in good time."];
+%% Check that timers time out in good time.
timer_measure(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:minutes(1)),
Name = 'timer_drv',
- ?line Port = start_driver(Config, Name, false),
+ Port = start_driver(Config, Name, false),
- ?line try_timeouts(Port, 8997),
+ try_timeouts(Port, 8997),
- ?line stop_driver(Port, Name),
- ?line test_server:timetrap_cancel(Dog),
+ stop_driver(Port, Name),
ok.
try_timeouts(_, 0) -> ok;
try_timeouts(Port, Timeout) ->
- ?line TimeBefore = erlang:monotonic_time(),
- ?line erlang:port_command(Port, <<?START_TIMER,Timeout:32>>),
+ TimeBefore = erlang:monotonic_time(),
+ erlang:port_command(Port, <<?START_TIMER,Timeout:32>>),
receive
- {Port,{data,[?TIMER]}} ->
- ?line Elapsed = erl_millisecs() - erl_millisecs(TimeBefore),
- io:format("Elapsed: ~p Timeout: ~p\n", [Elapsed, Timeout]),
- if
- Elapsed < Timeout ->
- ?line ?t:fail(too_short);
- Elapsed > Timeout + ?delay ->
- ?line ?t:fail(too_long);
- true ->
- try_timeouts(Port, Timeout div 2)
- end
- after Timeout + ?delay ->
- ?line test_server:fail("driver failed to timeout")
+ {Port,{data,[?TIMER]}} ->
+ Elapsed = erl_millisecs() - erl_millisecs(TimeBefore),
+ io:format("Elapsed: ~p Timeout: ~p\n", [Elapsed, Timeout]),
+ if
+ Elapsed < Timeout ->
+ ct:fail(too_short);
+ Elapsed > Timeout + ?delay ->
+ ct:fail(too_long);
+ true ->
+ try_timeouts(Port, Timeout div 2)
+ end
+ after Timeout + 100*?delay ->
+ ct:fail("driver failed to timeout")
end.
-timer_cancel(doc) -> ["Try cancelling timers set in a driver."];
+%% Try cancelling timers set in a driver.
timer_cancel(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:minutes(1)),
Name = 'timer_drv',
- ?line Port = start_driver(Config, Name, false),
+ Port = start_driver(Config, Name, false),
- ?line try_cancel(Port, 10000),
+ try_cancel(Port, 10000),
- ?line stop_driver(Port, Name),
- ?line test_server:timetrap_cancel(Dog),
+ stop_driver(Port, Name),
ok.
-
+
try_cancel(Port, Timeout) ->
- ?line T_before = erl_millisecs(),
+ T_before = erl_millisecs(),
Port ! {self(),{command,<<?START_TIMER,(Timeout + ?delay):32>>}},
receive
- {Port, {data, [?TIMER]}} ->
- ?line test_server:fail("driver timed out before cancelling it")
+ {Port, {data, [?TIMER]}} ->
+ ct:fail("driver timed out before cancelling it")
after Timeout ->
- Port ! {self(), {command, [?CANCEL_TIMER]}},
- receive
- {Port, {data, [?TIMER]}} ->
- ?line test_server:fail("driver timed out after cancelling it");
- {Port, {data, [?CANCELLED]}} ->
- ?line Time_milli_secs = erl_millisecs() - T_before,
-
- io:format("Time_milli_secs: ~p Timeout: ~p\n",
- [Time_milli_secs, Timeout]),
- if
- Time_milli_secs > (Timeout + ?delay) ->
- ?line test_server:fail("too long real time");
- Timeout == 0 -> ok;
- true -> try_cancel(Port, Timeout div 2)
- end
- after ?delay ->
- test_server:fail("No message from driver")
- end
+ Port ! {self(), {command, [?CANCEL_TIMER]}},
+ receive
+ {Port, {data, [?TIMER]}} ->
+ ct:fail("driver timed out after cancelling it");
+ {Port, {data, [?CANCELLED]}} ->
+ Time_milli_secs = erl_millisecs() - T_before,
+
+ io:format("Time_milli_secs: ~p Timeout: ~p\n",
+ [Time_milli_secs, Timeout]),
+ if
+ Time_milli_secs > (Timeout + ?delay) ->
+ ct:fail("too long real time");
+ Timeout == 0 -> ok;
+ true -> try_cancel(Port, Timeout div 2)
+ end
+ after 100*?delay ->
+ ct:fail("No message from driver")
+ end
end.
%% Test that timers don't time out too early if we do a sleep
%% before setting a timer.
timer_delay(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:minutes(1)),
Name = 'timer_drv',
- ?line Port = start_driver(Config, Name, false),
+ Port = start_driver(Config, Name, false),
- ?line TimeBefore = erlang:monotonic_time(),
+ TimeBefore = erlang:monotonic_time(),
Timeout0 = 350,
- ?line erlang:port_command(Port, <<?DELAY_START_TIMER,Timeout0:32>>),
- Timeout = Timeout0 +
- case os:type() of
- {win32,_} -> 0; %Driver doesn't sleep on Windows.
- _ -> 1000
- end,
+ erlang:port_command(Port, <<?DELAY_START_TIMER,Timeout0:32>>),
+ Timeout = Timeout0 + 1000,
receive
- {Port,{data,[?TIMER]}} ->
- ?line Elapsed = erl_millisecs() - erl_millisecs(TimeBefore),
- io:format("Elapsed time: ~p Timeout: ~p\n",
- [Elapsed,Timeout]),
- if
- Elapsed < Timeout ->
- ?line ?t:fail(too_short);
- Elapsed > Timeout + ?delay ->
- ?line ?t:fail(too_long);
- true ->
- ok
- end
+ {Port,{data,[?TIMER]}} ->
+ Elapsed = erl_millisecs() - erl_millisecs(TimeBefore),
+ io:format("Elapsed time: ~p Timeout: ~p\n",
+ [Elapsed,Timeout]),
+ if
+ Elapsed < Timeout ->
+ ct:fail(too_short);
+ Elapsed > Timeout + ?delay ->
+ ct:fail(too_long);
+ true ->
+ ok
+ end
end,
- ?line stop_driver(Port, Name),
- ?line test_server:timetrap_cancel(Dog),
+ stop_driver(Port, Name),
ok.
%% Test that driver_set_timer with new timout really changes
%% the timer (ticket OTP-5942), it didn't work before
timer_change(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:minutes(1)),
Name = 'timer_drv',
- ?line Port = start_driver(Config, Name, false),
+ Port = start_driver(Config, Name, false),
- ?line try_change_timer(Port, 10000),
+ try_change_timer(Port, 10000),
- ?line stop_driver(Port, Name),
- ?line test_server:timetrap_cancel(Dog),
+ stop_driver(Port, Name),
ok.
-
+
try_change_timer(_Port, 0) -> ok;
try_change_timer(Port, Timeout) ->
- ?line Timeout_3 = Timeout*3,
- ?line TimeBefore = erlang:monotonic_time(),
- ?line erlang:port_command(Port, <<?START_TIMER,Timeout_3:32>>),
- ?line erlang:port_command(Port, <<?START_TIMER,Timeout:32>>),
+ Timeout_3 = Timeout*3,
+ TimeBefore = erlang:monotonic_time(),
+ erlang:port_command(Port, <<?START_TIMER,Timeout_3:32>>),
+ erlang:port_command(Port, <<?START_TIMER,Timeout:32>>),
receive
- {Port,{data,[?TIMER]}} ->
- ?line Elapsed = erl_millisecs() - erl_millisecs(TimeBefore),
- io:format("Elapsed: ~p Timeout: ~p\n", [Elapsed,Timeout]),
- if
- Elapsed < Timeout ->
- ?line ?t:fail(too_short);
- Elapsed > Timeout + ?delay ->
- ?line ?t:fail(too_long);
- true ->
- try_timeouts(Port, Timeout div 2)
- end
- after Timeout + ?delay ->
- ?line test_server:fail("driver failed to timeout")
+ {Port,{data,[?TIMER]}} ->
+ Elapsed = erl_millisecs() - erl_millisecs(TimeBefore),
+ io:format("Elapsed: ~p Timeout: ~p\n", [Elapsed,Timeout]),
+ if
+ Elapsed < Timeout ->
+ ct:fail(too_short);
+ Elapsed > Timeout + ?delay ->
+ ct:fail(too_long);
+ true ->
+ try_timeouts(Port, Timeout div 2)
+ end
+ after Timeout + 100*?delay ->
+ ct:fail("driver failed to timeout")
end.
@@ -524,49 +510,47 @@ try_change_timer(Port, Timeout) ->
%% Queue test suites
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-queue_echo(doc) ->
- ["1) Queue up data in a driver that uses the full driver_queue API to do this."
- "2) Get the data back, a random amount at a time."];
+%% 1) Queue up data in a driver that uses the full driver_queue API to do this.
+%% 2) Get the data back, a random amount at a time.
queue_echo(Config) when is_list(Config) ->
- case ?t:is_native(?MODULE) of
- true -> exit(crashes_native_code);
- false -> queue_echo_1(Config)
+ case test_server:is_native(?MODULE) of
+ true -> exit(crashes_native_code);
+ false -> queue_echo_1(Config)
end.
queue_echo_1(Config) ->
- ?line Dog = test_server:timetrap(test_server:minutes(10)),
+ ct:timetrap({minutes, 10}),
Name = 'queue_drv',
- ?line P = start_driver(Config, Name, true),
-
- ?line q_echo(P, [{?ENQ, {list,1}},
- {?ENQ, {list,0}},
- {?ENQ, {bin,0}},
- {?ENQ, {bin,1}},
- {?ENQ, {bin,2}},
- {?ENQ, {bin,3}},
- {?ENQ, {bin,4}},
- {?ENQ, {bin,5}},
- {?ENQ, {bin,600}},
- {?PUSHQ, {list,0}},
- {?PUSHQ, {list,1}},
- {?PUSHQ, {bin,0}},
- {?PUSHQ, {bin,1}},
- {?PUSHQ, {bin,888}},
- {?ENQ_BIN, {bin,0}},
- {?ENQ_BIN, {bin,1}},
- {?ENQ_BIN, {bin,2}},
- {?ENQ_BIN, {bin,3}},
- {?ENQ_BIN, {bin,4}},
- {?ENQ_BIN, {bin,777}},
- {?PUSHQ_BIN, {bin,0}},
- {?PUSHQ_BIN, {bin,1}},
- {?PUSHQ_BIN, {bin,334}},
- {?ENQV, [{bin,0},{list,1},{bin,1},{bin,555}]},
- {?ENQV, [{bin,0},{list,1},{bin,1}]},
- {?PUSHQV, [{bin,0},{list,1},{bin,1},{bin,319}]}]),
-
- ?line stop_driver(P, Name),
- ?line test_server:timetrap_cancel(Dog),
+ P = start_driver(Config, Name, true),
+
+ q_echo(P, [{?ENQ, {list,1}},
+ {?ENQ, {list,0}},
+ {?ENQ, {bin,0}},
+ {?ENQ, {bin,1}},
+ {?ENQ, {bin,2}},
+ {?ENQ, {bin,3}},
+ {?ENQ, {bin,4}},
+ {?ENQ, {bin,5}},
+ {?ENQ, {bin,600}},
+ {?PUSHQ, {list,0}},
+ {?PUSHQ, {list,1}},
+ {?PUSHQ, {bin,0}},
+ {?PUSHQ, {bin,1}},
+ {?PUSHQ, {bin,888}},
+ {?ENQ_BIN, {bin,0}},
+ {?ENQ_BIN, {bin,1}},
+ {?ENQ_BIN, {bin,2}},
+ {?ENQ_BIN, {bin,3}},
+ {?ENQ_BIN, {bin,4}},
+ {?ENQ_BIN, {bin,777}},
+ {?PUSHQ_BIN, {bin,0}},
+ {?PUSHQ_BIN, {bin,1}},
+ {?PUSHQ_BIN, {bin,334}},
+ {?ENQV, [{bin,0},{list,1},{bin,1},{bin,555}]},
+ {?ENQV, [{bin,0},{list,1},{bin,1}]},
+ {?PUSHQV, [{bin,0},{list,1},{bin,1},{bin,319}]}]),
+
+ stop_driver(P, Name),
ok.
q_echo(Port, SpecList) ->
@@ -606,7 +590,7 @@ q_echo(Port, SpecList) ->
feed_and_dequeue(Port, HeapData, 2),
feed_and_dequeue(Port, HeapData, 3),
feed_and_dequeue(Port, HeapData, 4),
-
+
io:format("\n").
feed_and_dequeue(Port, Data, DeqSize) ->
@@ -626,9 +610,9 @@ feed_driver(Port, [], ExpectedInPort, Qb) ->
{ExpectedInPort,Qb};
feed_driver(Port, [{Method0,Data}|T], Expected_return, Qb_before) ->
Method = case Method0 of
- ?RANDOM -> uniform(6)-1;
- Other -> Other
- end,
+ ?RANDOM -> uniform(6)-1;
+ Other -> Other
+ end,
Size = size(list_to_binary([Data])),
%% ***********************************************************************
@@ -643,22 +627,21 @@ feed_driver(Port, [{Method0,Data}|T], Expected_return, Qb_before) ->
Qb_in_driver = bytes_queued(Port),
case Qb_before + Size of
- Qb_in_driver -> ok;
- Sum ->
- io:format("Qb_before: ~p\n"
- "Qb_before+Size: ~p\n"
- "Qb_in_driver: ~p",
- [Qb_before,Sum,Qb_in_driver]),
- ?t:fail()
+ Qb_in_driver -> ok;
+ Sum ->
+ ct:fail("Qb_before: ~p\n"
+ "Qb_before+Size: ~p\n"
+ "Qb_in_driver: ~p",
+ [Qb_before,Sum,Qb_in_driver])
end,
X_return = case Method of
- ?ENQ -> list_to_binary([Expected_return,Data]);
- ?PUSHQ -> list_to_binary([Data,Expected_return]);
- ?PUSHQ_BIN -> list_to_binary([Data,Expected_return]);
- ?ENQ_BIN -> list_to_binary([Expected_return,Data]);
- ?PUSHQV -> list_to_binary([Data,Expected_return]);
- ?ENQV -> list_to_binary([Expected_return,Data])
- end,
+ ?ENQ -> list_to_binary([Expected_return,Data]);
+ ?PUSHQ -> list_to_binary([Data,Expected_return]);
+ ?PUSHQ_BIN -> list_to_binary([Data,Expected_return]);
+ ?ENQ_BIN -> list_to_binary([Expected_return,Data]);
+ ?PUSHQV -> list_to_binary([Data,Expected_return]);
+ ?ENQV -> list_to_binary([Expected_return,Data])
+ end,
feed_driver(Port, T, X_return, Qb_before + Size).
%% method_name(0) -> pushq;
@@ -676,26 +659,22 @@ compare_return(Port, _Data_list, 0, _Back_len) ->
0 = bytes_queued(Port);
compare_return(Port, QueuedInPort0, Len_to_get, DeqSize) ->
case bytes_queued(Port) of
- Len_to_get -> ok;
- BytesInQueue ->
- io:format("Len_to_get: ~p", [Len_to_get]),
- io:format("Bytes in queue: ~p", [BytesInQueue]),
- ?line test_server:fail()
+ Len_to_get -> ok;
+ BytesInQueue ->
+ ct:fail("Len_to_get: ~p\nBytes in queue: ~p", [Len_to_get,BytesInQueue])
end,
BytesToDequeue = if (DeqSize > Len_to_get) -> Len_to_get;
- true -> DeqSize
- end,
+ true -> DeqSize
+ end,
Dequeued = read_head(Port, BytesToDequeue),
case bin_prefix(Dequeued, QueuedInPort0) of
- true ->
- deq(Port, BytesToDequeue),
- <<_:BytesToDequeue/binary,QueuedInPort/binary>> = QueuedInPort0,
- compare_return(Port, QueuedInPort, Len_to_get - BytesToDequeue, DeqSize);
- false ->
- io:format("Bytes to dequeue: ~p", [BytesToDequeue]),
- io:format("Dequeued: ~p", [Dequeued]),
- io:format("Queued in port: ~P", [QueuedInPort0,12]),
- ?t:fail()
+ true ->
+ deq(Port, BytesToDequeue),
+ <<_:BytesToDequeue/binary,QueuedInPort/binary>> = QueuedInPort0,
+ compare_return(Port, QueuedInPort, Len_to_get - BytesToDequeue, DeqSize);
+ false ->
+ ct:fail("Bytes to dequeue: ~p\nDequeued: ~p\nQueued in port: ~P",
+ [BytesToDequeue, Dequeued, QueuedInPort0,12])
end.
%% bin_prefix(PrefixBinary, Binary)
@@ -713,8 +692,8 @@ queue_op(Port, Method, Data) ->
bytes_queued(Port) ->
case erlang:port_control(Port, ?BYTES_QUEUED, []) of
- <<I:32>> -> I;
- Bad -> ?t:fail({bad_result,Bad})
+ <<I:32>> -> I;
+ Bad -> ct:fail({bad_result,Bad})
end.
deq(Port, Size) ->
@@ -724,83 +703,77 @@ read_head(Port, Size) ->
erlang:port_control(Port, ?READ_HEAD, <<Size:32>>).
-driver_unloaded(doc) ->
- [];
-driver_unloaded(suite) ->
- [];
driver_unloaded(Config) when is_list(Config) ->
- ?line process_flag(trap_exit, true),
- ?line Drv = timer_drv,
- ?line User = self(),
- ?line Loaded = make_ref(),
- ?line Die = make_ref(),
- ?line Loader = spawn(fun () ->
- erl_ddll:start(),
- ok = load_driver(?config(data_dir,
- Config),
- Drv),
- User ! Loaded,
- receive Die -> exit(bye) end
- end),
- ?line receive Loaded -> ok end,
- ?line Port = open_port({spawn, Drv}, []),
- ?line Loader ! Die,
- ?line receive
- {'EXIT', Port, Reason} ->
- ?line driver_unloaded = Reason
- %% Reason used to be -1
- end.
-
-
-io_ready_exit(doc) -> [];
-io_ready_exit(suite) -> [];
+ process_flag(trap_exit, true),
+ Drv = timer_drv,
+ User = self(),
+ Loaded = make_ref(),
+ Die = make_ref(),
+ Loader = spawn(fun () ->
+ erl_ddll:start(),
+ ok = load_driver(proplists:get_value(data_dir,
+ Config),
+ Drv),
+ User ! Loaded,
+ receive Die -> exit(bye) end
+ end),
+ receive Loaded -> ok end,
+ Port = open_port({spawn, Drv}, []),
+ Loader ! Die,
+ receive
+ {'EXIT', Port, Reason} ->
+ driver_unloaded = Reason
+ %% Reason used to be -1
+ end.
+
+
io_ready_exit(Config) when is_list(Config) ->
- ?line OTE = process_flag(trap_exit, true),
- ?line Test = self(),
- ?line Dgawd = spawn(fun () ->
- ok = dgawd_handler:install(),
- Mon = erlang:monitor(process, Test),
- Test ! dgawd_handler_started,
- receive
- {'DOWN', Mon, _, _, _} -> ok;
- stop_dgawd_handler -> ok
- end,
- dgawd_handler:restore(),
- Test ! dgawd_handler_stopped
- end),
- ?line receive dgawd_handler_started -> ok end,
- ?line Drv = io_ready_exit_drv,
- ?line erl_ddll:start(),
- ?line ok = load_driver(?config(data_dir, Config), Drv),
- ?line Port = open_port({spawn, Drv}, []),
- ?line case erlang:port_control(Port, 0, "") of
- "ok" ->
- receive
- {'EXIT', Port, Reason} ->
- ?line case Reason of
- ready_output_driver_failure ->
- ?t:format("Exited in output_ready()~n"),
- ?line ok;
- ready_input_driver_failure ->
- ?t:format("Exited in input_ready()~n"),
- ?line ok;
- Error -> ?line ?t:fail(Error)
- end
- end,
- receive after 2000 -> ok end,
- ?line false = dgawd_handler:got_dgawd_report(),
- ?line Dgawd ! stop_dgawd_handler,
- ?line receive dgawd_handler_stopped -> ok end,
- ?line process_flag(trap_exit, OTE),
- ?line ok;
- "nyiftos" ->
- ?line process_flag(trap_exit, OTE),
- ?line {skipped, "Not yet implemented for this OS"};
- Error ->
- ?line process_flag(trap_exit, OTE),
- ?line ?t:fail({unexpected_control_result, Error})
- end.
-
+ OTE = process_flag(trap_exit, true),
+ Test = self(),
+ Dgawd = spawn(fun () ->
+ ok = dgawd_handler:install(),
+ Mon = erlang:monitor(process, Test),
+ Test ! dgawd_handler_started,
+ receive
+ {'DOWN', Mon, _, _, _} -> ok;
+ stop_dgawd_handler -> ok
+ end,
+ dgawd_handler:restore(),
+ Test ! dgawd_handler_stopped
+ end),
+ receive dgawd_handler_started -> ok end,
+ Drv = io_ready_exit_drv,
+ erl_ddll:start(),
+ ok = load_driver(proplists:get_value(data_dir, Config), Drv),
+ Port = open_port({spawn, Drv}, []),
+ case erlang:port_control(Port, 0, "") of
+ "ok" ->
+ receive
+ {'EXIT', Port, Reason} ->
+ case Reason of
+ ready_output_driver_failure ->
+ io:format("Exited in output_ready()~n"),
+ ok;
+ ready_input_driver_failure ->
+ io:format("Exited in input_ready()~n"),
+ ok;
+ Error -> ct:fail(Error)
+ end
+ end,
+ receive after 2000 -> ok end,
+ false = dgawd_handler:got_dgawd_report(),
+ Dgawd ! stop_dgawd_handler,
+ receive dgawd_handler_stopped -> ok end,
+ process_flag(trap_exit, OTE),
+ ok;
+ "nyiftos" ->
+ process_flag(trap_exit, OTE),
+ {skipped, "Not yet implemented for this OS"};
+ Error ->
+ process_flag(trap_exit, OTE),
+ ct:fail({unexpected_control_result, Error})
+ end.
+
-define(CHKIO_STOP, 0).
-define(CHKIO_USE_FALLBACK_POLLSET, 1).
@@ -812,138 +785,128 @@ io_ready_exit(Config) when is_list(Config) ->
-define(CHKIO_SMP_SELECT, 7).
-define(CHKIO_DRV_USE, 8).
-use_fallback_pollset(doc) -> [];
-use_fallback_pollset(suite) -> [];
use_fallback_pollset(Config) when is_list(Config) ->
FlbkFun = fun () ->
- ChkIoDuring = erlang:system_info(check_io),
- case lists:keysearch(fallback_poll_set_size,
- 1,
- ChkIoDuring) of
- {value,
- {fallback_poll_set_size, N}} when N > 0 ->
- ?line ok;
- Error ->
- ?line ?t:fail({failed_to_use_fallback, Error})
- end
- end,
- ?line {BckupTest, Handel, OkRes}
- = case chkio_test_init(Config) of
- {erts_poll_info, ChkIo} = Hndl ->
- case lists:keysearch(fallback, 1, ChkIo) of
- {value, {fallback, B}} when B =/= false ->
- ?line {FlbkFun, Hndl, ok};
- _ ->
- ?line {fun () -> ok end,
- Hndl,
- {comment,
- "This implementation does not use "
- "a fallback pollset"}}
- end;
- Skip ->
- {fun () -> ok end, Skip, ok}
- end,
- ?line case chkio_test_fini(chkio_test(Handel,
- ?CHKIO_USE_FALLBACK_POLLSET,
- fun () ->
- ?line sleep(1000),
- ?line BckupTest()
- end)) of
- {skipped, _} = Res -> ?line Res;
- _ -> ?line OkRes
- end.
-
-bad_fd_in_pollset(doc) -> [];
-bad_fd_in_pollset(suite) -> [];
+ ChkIoDuring = erlang:system_info(check_io),
+ case lists:keysearch(fallback_poll_set_size,
+ 1,
+ ChkIoDuring) of
+ {value,
+ {fallback_poll_set_size, N}} when N > 0 ->
+ ok;
+ Error ->
+ ct:fail({failed_to_use_fallback, Error})
+ end
+ end,
+ {BckupTest, Handel, OkRes}
+ = case chkio_test_init(Config) of
+ {erts_poll_info, ChkIo} = Hndl ->
+ case lists:keysearch(fallback, 1, ChkIo) of
+ {value, {fallback, B}} when B =/= false ->
+ {FlbkFun, Hndl, ok};
+ _ ->
+ {fun () -> ok end,
+ Hndl,
+ {comment,
+ "This implementation does not use "
+ "a fallback pollset"}}
+ end;
+ Skip ->
+ {fun () -> ok end, Skip, ok}
+ end,
+ case chkio_test_fini(chkio_test(Handel,
+ ?CHKIO_USE_FALLBACK_POLLSET,
+ fun () ->
+ sleep(1000),
+ BckupTest()
+ end)) of
+ {skipped, _} = Res -> Res;
+ _ -> OkRes
+ end.
+
bad_fd_in_pollset(Config) when is_list(Config) ->
- ?line chkio_test_fini(chkio_test(chkio_test_init(Config),
- ?CHKIO_BAD_FD_IN_POLLSET,
- fun () -> ?line sleep(1000) end)).
+ chkio_test_fini(chkio_test(chkio_test_init(Config),
+ ?CHKIO_BAD_FD_IN_POLLSET,
+ fun () -> sleep(1000) end)).
-driver_event(doc) -> [];
-driver_event(suite) -> [];
driver_event(Config) when is_list(Config) ->
- ?line chkio_test_fini(chkio_test(chkio_test_init(Config),
- ?CHKIO_DRIVER_EVENT,
- fun () -> ?line sleep(1000) end)).
+ chkio_test_fini(chkio_test(chkio_test_init(Config),
+ ?CHKIO_DRIVER_EVENT,
+ fun () -> sleep(1000) end)).
-fd_change(doc) -> [];
-fd_change(suite) -> [];
fd_change(Config) when is_list(Config) ->
- ?line chkio_test_fini(chkio_test(chkio_test_init(Config),
- ?CHKIO_FD_CHANGE,
- fun () -> ?line sleep(1000) end)).
+ chkio_test_fini(chkio_test(chkio_test_init(Config),
+ ?CHKIO_FD_CHANGE,
+ fun () -> sleep(1000) end)).
-steal_control(doc) -> [];
-steal_control(suite) -> [];
steal_control(Config) when is_list(Config) ->
- ?line chkio_test_fini(case chkio_test_init(Config) of
- {erts_poll_info, _} = Hndl ->
- ?line steal_control_test(Hndl);
- Skip ->
- ?line Skip
- end).
+ chkio_test_fini(case chkio_test_init(Config) of
+ {erts_poll_info, _} = Hndl ->
+ steal_control_test(Hndl);
+ Skip ->
+ Skip
+ end).
steal_control_test(Hndl = {erts_poll_info, Before}) ->
- ?line Port = open_chkio_port(),
- ?line case erlang:port_control(Port, ?CHKIO_STEAL_AUX, "") of
- [$f,$d,$s,$:| _] = FdList ->
- ?line chk_chkio_port(Port),
- sleep(500),
- ?line chk_chkio_port(Port),
- ?line Res = chkio_test(Hndl,
- ?CHKIO_STEAL,
- FdList,
- fun () ->
- ?line chk_chkio_port(Port),
- ?line sleep(500),
- ?line chk_chkio_port(Port)
- end),
- ?line case erlang:port_control(Port, ?CHKIO_STOP, "") of
- "ok" ->
- ?line chk_chkio_port(Port),
- ?line ok;
- StopErr ->
- ?line chk_chkio_port(Port),
- ?line ?t:fail({stop_error, StopErr})
- end,
- ?line close_chkio_port(Port),
- ?line Res;
- [$s,$k,$i,$p,$:,$\ |Skip] ->
- ?line chk_chkio_port(Port),
- ?line close_chkio_port(Port),
- {chkio_test_result,
- {skipped, Skip},
- Before};
- StartErr ->
- ?line chk_chkio_port(Port),
- ?line ?t:fail({start_error, StartErr})
- end.
+ Port = open_chkio_port(),
+ case erlang:port_control(Port, ?CHKIO_STEAL_AUX, "") of
+ [$f,$d,$s,$:| _] = FdList ->
+ chk_chkio_port(Port),
+ sleep(500),
+ chk_chkio_port(Port),
+ Res = chkio_test(Hndl,
+ ?CHKIO_STEAL,
+ FdList,
+ fun () ->
+ chk_chkio_port(Port),
+ sleep(500),
+ chk_chkio_port(Port)
+ end),
+ case erlang:port_control(Port, ?CHKIO_STOP, "") of
+ "ok" ->
+ chk_chkio_port(Port),
+ ok;
+ StopErr ->
+ chk_chkio_port(Port),
+ ct:fail({stop_error, StopErr})
+ end,
+ close_chkio_port(Port),
+ Res;
+ [$s,$k,$i,$p,$:,$\ |Skip] ->
+ chk_chkio_port(Port),
+ close_chkio_port(Port),
+ {chkio_test_result,
+ {skipped, Skip},
+ Before};
+ StartErr ->
+ chk_chkio_port(Port),
+ ct:fail({start_error, StartErr})
+ end.
chkio_test_init(Config) when is_list(Config) ->
- ?line ChkIo = get_stable_check_io_info(),
- ?line case catch lists:keysearch(name, 1, ChkIo) of
- {value, {name, erts_poll}} ->
- ?line ?t:format("Before test: ~p~n", [ChkIo]),
- ?line Path = ?config(data_dir, Config),
- ?line erl_ddll:start(),
- ?line ok = load_driver(Path, 'chkio_drv'),
- ?line process_flag(trap_exit, true),
- ?line {erts_poll_info, ChkIo};
- _ ->
- ?line {skipped, "Test written to test erts_poll() which isn't used"}
- end.
-
+ ChkIo = get_stable_check_io_info(),
+ case catch lists:keysearch(name, 1, ChkIo) of
+ {value, {name, erts_poll}} ->
+ io:format("Before test: ~p~n", [ChkIo]),
+ Path = proplists:get_value(data_dir, Config),
+ erl_ddll:start(),
+ ok = load_driver(Path, 'chkio_drv'),
+ process_flag(trap_exit, true),
+ {erts_poll_info, ChkIo};
+ _ ->
+ {skipped, "Test written to test erts_poll() which isn't used"}
+ end.
+
chkio_test_fini({skipped, _} = Res) ->
Res;
chkio_test_fini({chkio_test_result, Res, Before}) ->
- ?line ok = erl_ddll:unload_driver('chkio_drv'),
- ?line ok = erl_ddll:stop(),
- ?line After = get_stable_check_io_info(),
- ?line ?t:format("After test: ~p~n", [After]),
- ?line verify_chkio_state(Before, After),
- ?line Res.
+ ok = erl_ddll:unload_driver('chkio_drv'),
+ ok = erl_ddll:stop(),
+ After = get_stable_check_io_info(),
+ io:format("After test: ~p~n", [After]),
+ verify_chkio_state(Before, After),
+ Res.
open_chkio_port() ->
open_port({spawn, 'chkio_drv'}, []).
@@ -951,269 +914,255 @@ open_chkio_port() ->
close_chkio_port(Port) when is_port(Port) ->
true = erlang:port_close(Port),
receive
- {'EXIT', Port, normal} ->
- ok;
- {'EXIT', Port, Reason} ->
- ?t:fail({abnormal_port_exit, Port, Reason});
- {Port, Message} ->
- ?t:fail({strange_message_from_port, Message})
+ {'EXIT', Port, normal} ->
+ ok;
+ {'EXIT', Port, Reason} ->
+ ct:fail({abnormal_port_exit, Port, Reason});
+ {Port, Message} ->
+ ct:fail({strange_message_from_port, Message})
end.
chk_chkio_port(Port) ->
receive
- {'EXIT', Port, Reason} when Reason /= normal ->
- ?t:fail({port_exited, Port, Reason})
+ {'EXIT', Port, Reason} when Reason /= normal ->
+ ct:fail({port_exited, Port, Reason})
after 0 ->
- ok
+ ok
end.
-
+
chkio_test({skipped, _} = Res, _Test, _Fun) ->
- ?line Res;
+ Res;
chkio_test({erts_poll_info, _Before} = EPI, Test, Fun) when is_integer(Test) ->
chkio_test(EPI, Test, "", Fun).
chkio_test({skipped, _} = Res, _Test, _TestArgs, _Fun) ->
- ?line Res;
+ Res;
chkio_test({erts_poll_info, Before},
- Test,
- TestArgs,
- Fun) when is_integer(Test),
- is_list(TestArgs) ->
- ?line Port = open_chkio_port(),
- ?line case erlang:port_control(Port, Test, TestArgs) of
- "ok" ->
- ?line chk_chkio_port(Port),
- ?line Fun(),
- ?line During = erlang:system_info(check_io),
- ?line erlang:display(During),
- ?line 0 = element(1, erts_debug:get_internal_state(check_io_debug)),
- ?line ?t:format("During test: ~p~n", [During]),
- ?line chk_chkio_port(Port),
- ?line case erlang:port_control(Port, ?CHKIO_STOP, "") of
- Res when is_list(Res) ->
- ?line chk_chkio_port(Port),
- ?line ?t:format("~s", [Res]),
- ?line close_chkio_port(Port),
- ?line Res,
- ?line case Res of
- [$c,$o,$m,$m,$e,$n,$t,$:,$\ |Cmnt] ->
- ?line {chkio_test_result,
- {comment, Cmnt},
- Before};
- _ ->
- ?line {chkio_test_result,
- Res,
- Before}
- end;
- StopErr ->
- ?line chk_chkio_port(Port),
- ?line ?t:fail({stop_error, StopErr})
- end;
- [$s,$k,$i,$p,$:,$\ |Skip] ->
- ?line chk_chkio_port(Port),
- ?line close_chkio_port(Port),
- {chkio_test_result,
- {skipped, Skip},
- Before};
- StartErr ->
- ?line chk_chkio_port(Port),
- ?line ?t:fail({start_error, StartErr})
- end.
+ Test,
+ TestArgs,
+ Fun) when is_integer(Test),
+ is_list(TestArgs) ->
+ Port = open_chkio_port(),
+ case erlang:port_control(Port, Test, TestArgs) of
+ "ok" ->
+ chk_chkio_port(Port),
+ Fun(),
+ During = erlang:system_info(check_io),
+ erlang:display(During),
+ 0 = element(1, erts_debug:get_internal_state(check_io_debug)),
+ io:format("During test: ~p~n", [During]),
+ chk_chkio_port(Port),
+ case erlang:port_control(Port, ?CHKIO_STOP, "") of
+ Res when is_list(Res) ->
+ chk_chkio_port(Port),
+ io:format("~s", [Res]),
+ close_chkio_port(Port),
+ Res,
+ case Res of
+ [$c,$o,$m,$m,$e,$n,$t,$:,$\ |Cmnt] ->
+ {chkio_test_result,
+ {comment, Cmnt},
+ Before};
+ _ ->
+ {chkio_test_result,
+ Res,
+ Before}
+ end;
+ StopErr ->
+ chk_chkio_port(Port),
+ ct:fail({stop_error, StopErr})
+ end;
+ [$s,$k,$i,$p,$:,$\ |Skip] ->
+ chk_chkio_port(Port),
+ close_chkio_port(Port),
+ {chkio_test_result,
+ {skipped, Skip},
+ Before};
+ StartErr ->
+ chk_chkio_port(Port),
+ ct:fail({start_error, StartErr})
+ end.
verify_chkio_state(Before, After) ->
- ?line TotSetSize = lists:keysearch(total_poll_set_size, 1, Before),
- ?line TotSetSize = lists:keysearch(total_poll_set_size, 1, After),
- ?line case lists:keysearch(fallback, 1, Before) of
- {value,{fallback,false}} ->
- ?line ok;
- _ ->
- ?line BckupSetSize = lists:keysearch(fallback_poll_set_size,
- 1,
- Before),
- ?line BckupSetSize = lists:keysearch(fallback_poll_set_size,
- 1,
- After)
- end,
- ?line ok.
+ TotSetSize = lists:keysearch(total_poll_set_size, 1, Before),
+ TotSetSize = lists:keysearch(total_poll_set_size, 1, After),
+ case lists:keysearch(fallback, 1, Before) of
+ {value,{fallback,false}} ->
+ ok;
+ _ ->
+ BckupSetSize = lists:keysearch(fallback_poll_set_size,
+ 1,
+ Before),
+ BckupSetSize = lists:keysearch(fallback_poll_set_size,
+ 1,
+ After)
+ end,
+ ok.
get_stable_check_io_info() ->
ChkIo = erlang:system_info(check_io),
PendUpdNo = case lists:keysearch(pending_updates, 1, ChkIo) of
- {value, {pending_updates, PendNo}} ->
- PendNo;
- false ->
- 0
- end,
+ {value, {pending_updates, PendNo}} ->
+ PendNo;
+ false ->
+ 0
+ end,
{value, {active_fds, ActFds}} = lists:keysearch(active_fds, 1, ChkIo),
case {PendUpdNo, ActFds} of
- {0, 0} ->
- ChkIo;
- _ ->
- receive after 10 -> ok end,
- get_stable_check_io_info()
+ {0, 0} ->
+ ChkIo;
+ _ ->
+ receive after 10 -> ok end,
+ get_stable_check_io_info()
end.
-otp_6602(doc) -> ["Missed port lock when stealing control of fd from a "
- "driver that didn't use the same lock. The lock checker "
- "used to trigger on this and dump core."];
-otp_6602(suite) ->
- [];
+%% Missed port lock when stealing control of fd from a
+%% driver that didn't use the same lock. The lock checker
+%% used to trigger on this and dump core.
otp_6602(Config) when is_list(Config) ->
- ?line {ok, Node} = start_node(Config),
- ?line Done = make_ref(),
- ?line Parent = self(),
- ?line Tester = spawn_link(Node,
- fun () ->
- %% Inet driver use port locking...
- {ok, S} = gen_udp:open(0),
- {ok, Fd} = inet:getfd(S),
- %% Steal fd (lock checker used to
- %% trigger here).
- {ok, _S2} = gen_udp:open(0,[{fd,Fd}]),
- Parent ! Done
- end),
- ?line receive Done -> ok end,
- ?line unlink(Tester),
- ?line stop_node(Node),
- ?line ok.
+ {ok, Node} = start_node(Config),
+ Done = make_ref(),
+ Parent = self(),
+ Tester = spawn_link(Node,
+ fun () ->
+ %% Inet driver use port locking...
+ {ok, S} = gen_udp:open(0),
+ {ok, Fd} = inet:getfd(S),
+ %% Steal fd (lock checker used to
+ %% trigger here).
+ {ok, _S2} = gen_udp:open(0,[{fd,Fd}]),
+ Parent ! Done
+ end),
+ receive Done -> ok end,
+ unlink(Tester),
+ stop_node(Node),
+ ok.
-define(EXPECTED_SYSTEM_INFO_NAMES1,
- ["drv_drv_vsn",
- "emu_drv_vsn",
- "erts_vsn",
- "otp_vsn",
- "thread",
- "smp"]).
+ ["drv_drv_vsn",
+ "emu_drv_vsn",
+ "erts_vsn",
+ "otp_vsn",
+ "thread",
+ "smp"]).
-define(EXPECTED_SYSTEM_INFO_NAMES2,
- (?EXPECTED_SYSTEM_INFO_NAMES1 ++
- ["async_thrs",
- "sched_thrs"])).
+ (?EXPECTED_SYSTEM_INFO_NAMES1 ++
+ ["async_thrs",
+ "sched_thrs"])).
-define(EXPECTED_SYSTEM_INFO_NAMES3,
- (?EXPECTED_SYSTEM_INFO_NAMES2 ++
- ["emu_nif_vsn"])).
+ (?EXPECTED_SYSTEM_INFO_NAMES2 ++
+ ["emu_nif_vsn"])).
-define(EXPECTED_SYSTEM_INFO_NAMES4,
- (?EXPECTED_SYSTEM_INFO_NAMES3 ++
- ["dirty_sched"])).
+ (?EXPECTED_SYSTEM_INFO_NAMES3 ++
+ ["dirty_sched"])).
-define(EXPECTED_SYSTEM_INFO_NAMES, ?EXPECTED_SYSTEM_INFO_NAMES4).
-'driver_system_info_base_ver'(doc) ->
- [];
-'driver_system_info_base_ver'(suite) ->
- [];
'driver_system_info_base_ver'(Config) when is_list(Config) ->
- ?line driver_system_info_test(Config, sys_info_base_drv).
+ driver_system_info_test(Config, sys_info_base_drv).
-'driver_system_info_prev_ver'(doc) ->
- [];
-'driver_system_info_prev_ver'(suite) ->
- [];
'driver_system_info_prev_ver'(Config) when is_list(Config) ->
- ?line driver_system_info_test(Config, sys_info_prev_drv).
+ driver_system_info_test(Config, sys_info_prev_drv).
-driver_system_info_current_ver(doc) ->
- [];
-driver_system_info_current_ver(suite) ->
- [];
driver_system_info_current_ver(Config) when is_list(Config) ->
- ?line driver_system_info_test(Config, sys_info_curr_drv).
+ driver_system_info_test(Config, sys_info_curr_drv).
driver_system_info_test(Config, Name) ->
- ?line Port = start_driver(Config, Name, false),
- ?line case erlang:port_control(Port, 0, []) of
- [$o,$k,$:,_ | Result] ->
- ?line check_driver_system_info_result(Result);
- [$e,$r,$r,$o,$r,$:,_ | Error] ->
- ?line ?t:fail(Error);
- Unexpected ->
- ?line ?t:fail({unexpected_result, Unexpected})
- end,
- ?line stop_driver(Port, Name),
- ?line ok.
+ Port = start_driver(Config, Name, false),
+ case erlang:port_control(Port, 0, []) of
+ [$o,$k,$:,_ | Result] ->
+ check_driver_system_info_result(Result);
+ [$e,$r,$r,$o,$r,$:,_ | Error] ->
+ ct:fail(Error);
+ Unexpected ->
+ ct:fail({unexpected_result, Unexpected})
+ end,
+ stop_driver(Port, Name),
+ ok.
check_driver_system_info_result(Result) ->
- ?line ?t:format("All names: ~p~n", [?EXPECTED_SYSTEM_INFO_NAMES]),
- ?line ?t:format("Result: ~p~n", [Result]),
- ?line {[], Ns, DDVSN} = chk_sis(lists:map(fun (Str) ->
- string:tokens(Str, "=")
- end,
- string:tokens(Result, " ")),
- ?EXPECTED_SYSTEM_INFO_NAMES),
- ?line case {DDVSN,
- drv_vsn_str2tup(erlang:system_info(driver_version))} of
- {DDVSN, DDVSN} ->
- ?line [] = Ns;
- %% {{1, 0}, _} ->
- %% ?line ExpNs = lists:sort(?EXPECTED_SYSTEM_INFO_NAMES
- %% -- ?EXPECTED_SYSTEM_INFO_NAMES1),
- %% ?line ExpNs = lists:sort(Ns);
- %% {{1, 1}, _} ->
- %% ?line ExpNs = lists:sort(?EXPECTED_SYSTEM_INFO_NAMES
- %% -- ?EXPECTED_SYSTEM_INFO_NAMES2),
- %% ?line ExpNs = lists:sort(Ns);
- {{3, 0}, _} ->
- ?line ExpNs = lists:sort(?EXPECTED_SYSTEM_INFO_NAMES
- -- ?EXPECTED_SYSTEM_INFO_NAMES3),
- ?line ExpNs = lists:sort(Ns)
- end.
+ io:format("All names: ~p~n", [?EXPECTED_SYSTEM_INFO_NAMES]),
+ io:format("Result: ~p~n", [Result]),
+ {[], Ns, DDVSN} = chk_sis(lists:map(fun (Str) ->
+ string:tokens(Str, "=")
+ end,
+ string:tokens(Result, " ")),
+ ?EXPECTED_SYSTEM_INFO_NAMES),
+ case {DDVSN,
+ drv_vsn_str2tup(erlang:system_info(driver_version))} of
+ {DDVSN, DDVSN} ->
+ [] = Ns;
+ %% {{1, 0}, _} ->
+ %% ExpNs = lists:sort(?EXPECTED_SYSTEM_INFO_NAMES
+ %% -- ?EXPECTED_SYSTEM_INFO_NAMES1),
+ %% ExpNs = lists:sort(Ns);
+ %% {{1, 1}, _} ->
+ %% ExpNs = lists:sort(?EXPECTED_SYSTEM_INFO_NAMES
+ %% -- ?EXPECTED_SYSTEM_INFO_NAMES2),
+ %% ExpNs = lists:sort(Ns);
+ {{3, 0}, _} ->
+ ExpNs = lists:sort(?EXPECTED_SYSTEM_INFO_NAMES
+ -- ?EXPECTED_SYSTEM_INFO_NAMES3),
+ ExpNs = lists:sort(Ns)
+ end.
chk_sis(SIs, Ns) ->
chk_sis(SIs, Ns, unknown).
chk_sis(SIs, [], DDVSN) ->
- ?line {SIs, [], DDVSN};
+ {SIs, [], DDVSN};
chk_sis([], Ns, DDVSN) ->
- ?line {[], Ns, DDVSN};
+ {[], Ns, DDVSN};
chk_sis([[N, _] = SI| SIs], Ns, DDVSN) ->
- ?line true = lists:member(N, Ns),
- ?line case check_si_res(SI) of
- {driver_version, NewDDVSN} ->
- ?line chk_sis(SIs, lists:delete(N, Ns), NewDDVSN);
- _ ->
- ?line chk_sis(SIs, lists:delete(N, Ns), DDVSN)
- end.
+ true = lists:member(N, Ns),
+ case check_si_res(SI) of
+ {driver_version, NewDDVSN} ->
+ chk_sis(SIs, lists:delete(N, Ns), NewDDVSN);
+ _ ->
+ chk_sis(SIs, lists:delete(N, Ns), DDVSN)
+ end.
%% Data in first version of driver_system_info() (driver version 1.0)
check_si_res(["drv_drv_vsn", Value]) ->
- ?line DDVSN = drv_vsn_str2tup(Value),
- ?line {Major, DMinor} = DDVSN,
- ?line {Major, EMinor} = drv_vsn_str2tup(erlang:system_info(driver_version)),
- ?line true = DMinor =< EMinor,
- ?line {driver_version, DDVSN};
+ DDVSN = drv_vsn_str2tup(Value),
+ {Major, DMinor} = DDVSN,
+ {Major, EMinor} = drv_vsn_str2tup(erlang:system_info(driver_version)),
+ true = DMinor =< EMinor,
+ {driver_version, DDVSN};
check_si_res(["emu_drv_vsn", Value]) ->
- ?line Value = erlang:system_info(driver_version);
+ Value = erlang:system_info(driver_version);
check_si_res(["erts_vsn", Value]) ->
- ?line Value = erlang:system_info(version);
+ Value = erlang:system_info(version);
check_si_res(["otp_vsn", Value]) ->
- ?line Value = erlang:system_info(otp_release);
+ Value = erlang:system_info(otp_release);
check_si_res(["thread", "true"]) ->
- ?line true = erlang:system_info(threads);
+ true = erlang:system_info(threads);
check_si_res(["thread", "false"]) ->
- ?line false = erlang:system_info(threads);
+ false = erlang:system_info(threads);
check_si_res(["smp", "true"]) ->
- ?line true = erlang:system_info(smp_support);
+ true = erlang:system_info(smp_support);
check_si_res(["smp", "false"]) ->
- ?line false = erlang:system_info(smp_support);
+ false = erlang:system_info(smp_support);
%% Data added in second version of driver_system_info() (driver version 1.1)
check_si_res(["async_thrs", Value]) ->
- ?line Value = integer_to_list(erlang:system_info(thread_pool_size));
+ Value = integer_to_list(erlang:system_info(thread_pool_size));
check_si_res(["sched_thrs", Value]) ->
- ?line Value = integer_to_list(erlang:system_info(schedulers));
+ Value = integer_to_list(erlang:system_info(schedulers));
%% Data added in 3rd version of driver_system_info() (driver version 1.5)
check_si_res(["emu_nif_vsn", Value]) ->
- ?line Value = erlang:system_info(nif_version);
+ Value = erlang:system_info(nif_version);
%% Data added in 4th version of driver_system_info() (driver version 3.1)
check_si_res(["dirty_sched", _Value]) ->
true;
check_si_res(Unexpected) ->
- ?line ?t:fail({unexpected_result, Unexpected}).
+ ct:fail({unexpected_result, Unexpected}).
-define(MON_OP_I_AM_IPID,1).
-define(MON_OP_MONITOR_ME,2).
@@ -1221,171 +1170,168 @@ check_si_res(Unexpected) ->
-define(MON_OP_MONITOR_ME_LATER,4).
-define(MON_OP_DO_DELAYED_MONITOR,5).
-driver_monitor(suite) ->
- [];
-driver_monitor(doc) ->
- ["Test monitoring of processes from drivers"];
+%% Test monitoring of processes from drivers
driver_monitor(Config) when is_list(Config) ->
- ?line Name = monitor_drv,
- ?line Port = start_driver(Config, Name, false),
- ?line "ok" = port_control(Port,?MON_OP_I_AM_IPID,[]),
- ?line "ok" = port_control(Port,?MON_OP_MONITOR_ME,[]),
- ?line "ok" = port_control(Port,?MON_OP_DEMONITOR_ME,[]),
- ?line {monitors, []} = erlang:port_info(Port,monitors),
-
- ?line "ok:"++Id1 = port_control(Port,?MON_OP_MONITOR_ME_LATER,[]),
- ?line {monitored_by, []} = process_info(self(),monitored_by),
- ?line "ok" = port_control(Port,?MON_OP_DO_DELAYED_MONITOR,Id1),
- ?line {monitored_by, [Port]} = process_info(self(),monitored_by),
- ?line "ok" = port_control(Port,?MON_OP_DEMONITOR_ME,[]),
- ?line {monitored_by, []} = process_info(self(),monitored_by),
-
- ?line "ok" = port_control(Port,?MON_OP_MONITOR_ME,[]),
- ?line Me = self(),
- ?line {Pid1,Ref1} =
- spawn_monitor(fun() ->
- Me ! port_control(Port,?MON_OP_MONITOR_ME,[]),
- Me ! process_info(self(),monitored_by),
- Me ! erlang:port_info(Port,monitors)
- end),
- ?line ok = receive
- "ok" ->
- ok
- after 1000 ->
- timeout
- end,
- ?line ok = receive
- {monitored_by, L} ->
- L2 = lists:sort(L),
- L3 = lists:sort([Me,Port]),
- case L2 of
- L3 ->
- ok;
- _ ->
- mismatch
- end
- after 1000 ->
- timeout
- end,
- ?line ok = receive
- {monitors, LL} ->
- LL2 = lists:sort(LL),
- LL3 = lists:sort([{process,Me},{process,Pid1}]),
- case LL2 of
- LL3 ->
- ok;
- _ ->
- mismatch
- end
- after 1000 ->
- timeout
- end,
- ?line ok = receive
- {'DOWN', Ref1, process, Pid1, _} ->
- ok
- after 1000 ->
- timeout
- end,
- ?line ok = receive
- {monitor_fired,Port,Pid1} ->
- ok
- after 1000 ->
- timeout
- end,
- ?line "ok" = port_control(Port,?MON_OP_DEMONITOR_ME,[]),
- ?line {monitors,[]} = erlang:port_info(Port,monitors),
- ?line {monitored_by, []} = process_info(self(),monitored_by),
-
- ?line "ok" = port_control(Port,?MON_OP_MONITOR_ME,[]),
- ?line {Pid2,Ref2} =
- spawn_monitor(fun() ->
- receive go -> ok end,
- Me ! port_control(Port,?MON_OP_MONITOR_ME_LATER,[]),
- Me ! process_info(self(),monitored_by),
- Me ! erlang:port_info(Port,monitors)
- end),
- ?line Pid2 ! go,
- ?line {ok,Id2} = receive
- "ok:"++II ->
- {ok,II}
- after 1000 ->
- timeout
- end,
- ?line ok = receive
- {monitored_by, [Me]} ->
- ok
- after 1000 ->
- timeout
- end,
- ?line ok = receive
- {monitors, [{process,Me}]} ->
- ok
- after 1000 ->
- timeout
- end,
- ?line ok = receive
- {'DOWN', Ref2, process, Pid2, _} ->
- ok
- after 1000 ->
- timeout
- end,
- ?line "noproc" = port_control(Port,?MON_OP_DO_DELAYED_MONITOR,Id2),
- ?line {monitors,[{process,Me}]} = erlang:port_info(Port,monitors),
- ?line "ok" = port_control(Port,?MON_OP_DEMONITOR_ME,[]),
- ?line "not_monitored" = port_control(Port,?MON_OP_DEMONITOR_ME,[]),
- ?line {monitors,[]} = erlang:port_info(Port,monitors),
- ?line {monitored_by, []} = process_info(self(),monitored_by),
-
-
- ?line "ok" = port_control(Port,?MON_OP_MONITOR_ME,[]),
- ?line {Pid3,Ref3} =
- spawn_monitor(fun() ->
- receive go -> ok end,
- Me ! port_control(Port,?MON_OP_MONITOR_ME_LATER,[]),
- Me ! process_info(self(),monitored_by),
- Me ! erlang:port_info(Port,monitors) ,
- receive die -> ok end
- end),
- ?line Pid3 ! go,
- ?line {ok,Id3} = receive
- "ok:"++III ->
- {ok,III}
- after 1000 ->
- timeout
- end,
- ?line ok = receive
- {monitored_by, [Me]} ->
- ok
- after 1000 ->
- timeout
- end,
- ?line ok = receive
- {monitors, [{process,Me}]} ->
- ok
- after 1000 ->
- timeout
- end,
- ?line "ok" = port_control(Port,?MON_OP_DO_DELAYED_MONITOR,Id3),
- ?line LLL1 = lists:sort([{process,Me},{process,Pid3}]),
- ?line {monitors,LLL2} = erlang:port_info(Port,monitors),
- ?line LLL1 = lists:sort(LLL2),
- ?line "ok" = port_control(Port,?MON_OP_DEMONITOR_ME,[]),
- ?line {monitors,[{process,Pid3}]} = erlang:port_info(Port,monitors),
- ?line Pid3 ! die,
- ?line ok = receive
- {'DOWN', Ref3, process, Pid3, _} ->
- ok
- after 1000 ->
- timeout
- end,
- ?line "not_found" = port_control(Port,?MON_OP_DO_DELAYED_MONITOR,Id2),
- ?line {monitors,[]} = erlang:port_info(Port,monitors),
- ?line "not_monitored" = port_control(Port,?MON_OP_DEMONITOR_ME,[]),
- ?line {monitors,[]} = erlang:port_info(Port,monitors),
- ?line {monitored_by, []} = process_info(self(),monitored_by),
-
- ?line stop_driver(Port, Name),
- ?line ok.
+ Name = monitor_drv,
+ Port = start_driver(Config, Name, false),
+ "ok" = port_control(Port,?MON_OP_I_AM_IPID,[]),
+ "ok" = port_control(Port,?MON_OP_MONITOR_ME,[]),
+ "ok" = port_control(Port,?MON_OP_DEMONITOR_ME,[]),
+ {monitors, []} = erlang:port_info(Port,monitors),
+
+ "ok:"++Id1 = port_control(Port,?MON_OP_MONITOR_ME_LATER,[]),
+ {monitored_by, []} = process_info(self(),monitored_by),
+ "ok" = port_control(Port,?MON_OP_DO_DELAYED_MONITOR,Id1),
+ {monitored_by, [Port]} = process_info(self(),monitored_by),
+ "ok" = port_control(Port,?MON_OP_DEMONITOR_ME,[]),
+ {monitored_by, []} = process_info(self(),monitored_by),
+
+ "ok" = port_control(Port,?MON_OP_MONITOR_ME,[]),
+ Me = self(),
+ {Pid1,Ref1} =
+ spawn_monitor(fun() ->
+ Me ! port_control(Port,?MON_OP_MONITOR_ME,[]),
+ Me ! process_info(self(),monitored_by),
+ Me ! erlang:port_info(Port,monitors)
+ end),
+ ok = receive
+ "ok" ->
+ ok
+ after 1000 ->
+ timeout
+ end,
+ ok = receive
+ {monitored_by, L} ->
+ L2 = lists:sort(L),
+ L3 = lists:sort([Me,Port]),
+ case L2 of
+ L3 ->
+ ok;
+ _ ->
+ mismatch
+ end
+ after 1000 ->
+ timeout
+ end,
+ ok = receive
+ {monitors, LL} ->
+ LL2 = lists:sort(LL),
+ LL3 = lists:sort([{process,Me},{process,Pid1}]),
+ case LL2 of
+ LL3 ->
+ ok;
+ _ ->
+ mismatch
+ end
+ after 1000 ->
+ timeout
+ end,
+ ok = receive
+ {'DOWN', Ref1, process, Pid1, _} ->
+ ok
+ after 1000 ->
+ timeout
+ end,
+ ok = receive
+ {monitor_fired,Port,Pid1} ->
+ ok
+ after 1000 ->
+ timeout
+ end,
+ "ok" = port_control(Port,?MON_OP_DEMONITOR_ME,[]),
+ {monitors,[]} = erlang:port_info(Port,monitors),
+ {monitored_by, []} = process_info(self(),monitored_by),
+
+ "ok" = port_control(Port,?MON_OP_MONITOR_ME,[]),
+ {Pid2,Ref2} =
+ spawn_monitor(fun() ->
+ receive go -> ok end,
+ Me ! port_control(Port,?MON_OP_MONITOR_ME_LATER,[]),
+ Me ! process_info(self(),monitored_by),
+ Me ! erlang:port_info(Port,monitors)
+ end),
+ Pid2 ! go,
+ {ok,Id2} = receive
+ "ok:"++II ->
+ {ok,II}
+ after 1000 ->
+ timeout
+ end,
+ ok = receive
+ {monitored_by, [Me]} ->
+ ok
+ after 1000 ->
+ timeout
+ end,
+ ok = receive
+ {monitors, [{process,Me}]} ->
+ ok
+ after 1000 ->
+ timeout
+ end,
+ ok = receive
+ {'DOWN', Ref2, process, Pid2, _} ->
+ ok
+ after 1000 ->
+ timeout
+ end,
+ "noproc" = port_control(Port,?MON_OP_DO_DELAYED_MONITOR,Id2),
+ {monitors,[{process,Me}]} = erlang:port_info(Port,monitors),
+ "ok" = port_control(Port,?MON_OP_DEMONITOR_ME,[]),
+ "not_monitored" = port_control(Port,?MON_OP_DEMONITOR_ME,[]),
+ {monitors,[]} = erlang:port_info(Port,monitors),
+ {monitored_by, []} = process_info(self(),monitored_by),
+
+
+ "ok" = port_control(Port,?MON_OP_MONITOR_ME,[]),
+ {Pid3,Ref3} =
+ spawn_monitor(fun() ->
+ receive go -> ok end,
+ Me ! port_control(Port,?MON_OP_MONITOR_ME_LATER,[]),
+ Me ! process_info(self(),monitored_by),
+ Me ! erlang:port_info(Port,monitors) ,
+ receive die -> ok end
+ end),
+ Pid3 ! go,
+ {ok,Id3} = receive
+ "ok:"++III ->
+ {ok,III}
+ after 1000 ->
+ timeout
+ end,
+ ok = receive
+ {monitored_by, [Me]} ->
+ ok
+ after 1000 ->
+ timeout
+ end,
+ ok = receive
+ {monitors, [{process,Me}]} ->
+ ok
+ after 1000 ->
+ timeout
+ end,
+ "ok" = port_control(Port,?MON_OP_DO_DELAYED_MONITOR,Id3),
+ LLL1 = lists:sort([{process,Me},{process,Pid3}]),
+ {monitors,LLL2} = erlang:port_info(Port,monitors),
+ LLL1 = lists:sort(LLL2),
+ "ok" = port_control(Port,?MON_OP_DEMONITOR_ME,[]),
+ {monitors,[{process,Pid3}]} = erlang:port_info(Port,monitors),
+ Pid3 ! die,
+ ok = receive
+ {'DOWN', Ref3, process, Pid3, _} ->
+ ok
+ after 1000 ->
+ timeout
+ end,
+ "not_found" = port_control(Port,?MON_OP_DO_DELAYED_MONITOR,Id2),
+ {monitors,[]} = erlang:port_info(Port,monitors),
+ "not_monitored" = port_control(Port,?MON_OP_DEMONITOR_ME,[]),
+ {monitors,[]} = erlang:port_info(Port,monitors),
+ {monitored_by, []} = process_info(self(),monitored_by),
+
+ stop_driver(Port, Name),
+ ok.
-define(IOQ_EXIT_READY_INPUT, 1).
@@ -1399,726 +1345,663 @@ driver_monitor(Config) when is_list(Config) ->
-define(IOQ_EXIT_EVENT_ASYNC, 9).
ioq_exit_test(Config, TestNo) ->
- ?line Drv = ioq_exit_drv,
- ?line try
- begin
- ?line case load_driver(?config(data_dir, Config),
- Drv) of
- ok -> ?line ok;
- {error, permanent} -> ?line ok;
- LoadError -> ?line ?t:fail({load_error, LoadError})
- end,
- case open_port({spawn, Drv}, []) of
- Port when is_port(Port) ->
- try port_control(Port, TestNo, "") of
- "ok" ->
- ?line ok;
- "nyiftos" ->
- ?line throw({skipped,
- "Not yet implemented for "
- "this OS"});
- [$s,$k,$i,$p,$:,$ | Comment] ->
- ?line throw({skipped, Comment});
- [$e,$r,$r,$o,$r,$:,$ | Error] ->
- ?line ?t:fail(Error)
- after
- Port ! {self(), close},
- receive {Port, closed} -> ok end,
- false = lists:member(Port, erlang:ports()),
- ok
- end;
- Error ->
- ?line ?t:fail({open_port_failed, Error})
- end
- end
- catch
- throw:Term -> ?line Term
- after
- erl_ddll:unload_driver(Drv)
- end.
-
-ioq_exit_ready_input(doc) -> [];
-ioq_exit_ready_input(suite) -> [];
+ Drv = ioq_exit_drv,
+ try
+ begin
+ case load_driver(proplists:get_value(data_dir, Config),
+ Drv) of
+ ok -> ok;
+ {error, permanent} -> ok;
+ LoadError -> ct:fail({load_error, LoadError})
+ end,
+ case open_port({spawn, Drv}, []) of
+ Port when is_port(Port) ->
+ try port_control(Port, TestNo, "") of
+ "ok" ->
+ ok;
+ "nyiftos" ->
+ throw({skipped,
+ "Not yet implemented for "
+ "this OS"});
+ [$s,$k,$i,$p,$:,$ | Comment] ->
+ throw({skipped, Comment});
+ [$e,$r,$r,$o,$r,$:,$ | Error] ->
+ ct:fail(Error)
+ after
+ Port ! {self(), close},
+ receive {Port, closed} -> ok end,
+ false = lists:member(Port, erlang:ports()),
+ ok
+ end;
+ Error ->
+ ct:fail({open_port_failed, Error})
+ end
+ end
+ catch
+ throw:Term -> Term
+ after
+ erl_ddll:unload_driver(Drv)
+ end.
+
ioq_exit_ready_input(Config) when is_list(Config) ->
ioq_exit_test(Config, ?IOQ_EXIT_READY_INPUT).
-ioq_exit_ready_output(doc) -> [];
-ioq_exit_ready_output(suite) -> [];
ioq_exit_ready_output(Config) when is_list(Config) ->
ioq_exit_test(Config, ?IOQ_EXIT_READY_OUTPUT).
-ioq_exit_timeout(doc) -> [];
-ioq_exit_timeout(suite) -> [];
ioq_exit_timeout(Config) when is_list(Config) ->
ioq_exit_test(Config, ?IOQ_EXIT_TIMEOUT).
-ioq_exit_ready_async(doc) -> [];
-ioq_exit_ready_async(suite) -> [];
ioq_exit_ready_async(Config) when is_list(Config) ->
ioq_exit_test(Config, ?IOQ_EXIT_READY_ASYNC).
-ioq_exit_event(doc) -> [];
-ioq_exit_event(suite) -> [];
ioq_exit_event(Config) when is_list(Config) ->
ioq_exit_test(Config, ?IOQ_EXIT_EVENT).
-ioq_exit_ready_input_async(doc) -> [];
-ioq_exit_ready_input_async(suite) -> [];
ioq_exit_ready_input_async(Config) when is_list(Config) ->
ioq_exit_test(Config, ?IOQ_EXIT_READY_INPUT_ASYNC).
-ioq_exit_ready_output_async(doc) -> [];
-ioq_exit_ready_output_async(suite) -> [];
ioq_exit_ready_output_async(Config) when is_list(Config) ->
ioq_exit_test(Config, ?IOQ_EXIT_READY_OUTPUT_ASYNC).
-ioq_exit_timeout_async(doc) -> [];
-ioq_exit_timeout_async(suite) -> [];
ioq_exit_timeout_async(Config) when is_list(Config) ->
ioq_exit_test(Config, ?IOQ_EXIT_TIMEOUT_ASYNC).
-ioq_exit_event_async(doc) -> [];
-ioq_exit_event_async(suite) -> [];
ioq_exit_event_async(Config) when is_list(Config) ->
ioq_exit_test(Config, ?IOQ_EXIT_EVENT_ASYNC).
vsn_mismatch_test(Config, LoadResult) ->
- ?line Path = ?config(data_dir, Config),
- ?line DrvName = ?config(testcase, Config),
- ?line LoadResult = load_driver(Path, DrvName),
- ?line case LoadResult of
- ok ->
- ?line Port = open_port({spawn, DrvName}, []),
- ?line true = is_port(Port),
- ?line true = port_close(Port),
- ?line ok = erl_ddll:unload_driver(DrvName);
- _ ->
- ?line ok
- end.
-
-zero_extended_marker_garb_drv(doc) -> [];
-zero_extended_marker_garb_drv(suite) -> [];
+ Path = proplists:get_value(data_dir, Config),
+ DrvName = proplists:get_value(testcase, Config),
+ LoadResult = load_driver(Path, DrvName),
+ case LoadResult of
+ ok ->
+ Port = open_port({spawn, DrvName}, []),
+ true = is_port(Port),
+ true = port_close(Port),
+ ok = erl_ddll:unload_driver(DrvName);
+ _ ->
+ ok
+ end.
+
zero_extended_marker_garb_drv(Config) when is_list(Config) ->
vsn_mismatch_test(Config, {error, driver_incorrect_version}).
-invalid_extended_marker_drv(doc) -> [];
-invalid_extended_marker_drv(suite) -> [];
invalid_extended_marker_drv(Config) when is_list(Config) ->
vsn_mismatch_test(Config, {error, driver_incorrect_version}).
-larger_major_vsn_drv(doc) -> [];
-larger_major_vsn_drv(suite) -> [];
larger_major_vsn_drv(Config) when is_list(Config) ->
vsn_mismatch_test(Config, {error, driver_incorrect_version}).
-larger_minor_vsn_drv(doc) -> [];
-larger_minor_vsn_drv(suite) -> [];
larger_minor_vsn_drv(Config) when is_list(Config) ->
vsn_mismatch_test(Config, {error, driver_incorrect_version}).
-smaller_major_vsn_drv(doc) -> [];
-smaller_major_vsn_drv(suite) -> [];
smaller_major_vsn_drv(Config) when is_list(Config) ->
vsn_mismatch_test(Config, {error, driver_incorrect_version}).
-smaller_minor_vsn_drv(doc) -> [];
-smaller_minor_vsn_drv(suite) -> [];
smaller_minor_vsn_drv(Config) when is_list(Config) ->
DrvVsnStr = erlang:system_info(driver_version),
case drv_vsn_str2tup(DrvVsnStr) of
- {_, 0} ->
- {skipped,
- "Cannot perform test when minor driver version is 0. "
- "Current driver version is " ++ DrvVsnStr ++ "."};
- _ ->
- vsn_mismatch_test(Config, ok)
+ {_, 0} ->
+ {skipped,
+ "Cannot perform test when minor driver version is 0. "
+ "Current driver version is " ++ DrvVsnStr ++ "."};
+ _ ->
+ vsn_mismatch_test(Config, ok)
end.
-define(PEEK_NONXQ_TEST, 0).
-define(PEEK_NONXQ_WAIT, 1).
-peek_non_existing_queue(doc) -> [];
-peek_non_existing_queue(suite) -> [];
peek_non_existing_queue(Config) when is_list(Config) ->
- ?line OTE = process_flag(trap_exit, true),
- ?line Drv = peek_non_existing_queue_drv,
- ?line try
- begin
- ?line case load_driver(?config(data_dir, Config),
- Drv) of
- ok -> ?line ok;
- {error, permanent} -> ?line ok;
- LoadError -> ?line ?t:fail({load_error, LoadError})
- end,
- case open_port({spawn, Drv}, []) of
- Port1 when is_port(Port1) ->
- try port_control(Port1, ?PEEK_NONXQ_TEST, "") of
- "ok" ->
- ?line ok;
- [$s,$k,$i,$p,$p,$e,$d,$:,$ | SkipReason] ->
- ?line throw({skipped, SkipReason});
- [$e,$r,$r,$o,$r,$:,$ | Error1] ->
- ?line ?t:fail(Error1)
- after
- exit(Port1, kill),
- receive {'EXIT', Port1, _} -> ok end
- end;
- Error1 ->
- ?line ?t:fail({open_port1_failed, Error1})
- end,
- case open_port({spawn, Drv}, []) of
- Port2 when is_port(Port2) ->
- try port_control(Port2, ?PEEK_NONXQ_WAIT, "") of
- "ok" ->
- ?line ok;
- [$e,$r,$r,$o,$r,$:,$ | Error2] ->
- ?line ?t:fail(Error2)
- after
- receive {Port2, test_successful} -> ok end,
- Port2 ! {self(), close},
- receive {Port2, closed} -> ok end
- end;
- Error2 ->
- ?line ?t:fail({open_port2_failed, Error2})
- end
- end
- catch
- throw:Term -> ?line Term
- after
- process_flag(trap_exit, OTE),
- erl_ddll:unload_driver(Drv)
- end.
-
-otp_6879(doc) ->
- [];
-otp_6879(suite) ->
- [];
+ OTE = process_flag(trap_exit, true),
+ Drv = peek_non_existing_queue_drv,
+ try
+ begin
+ case load_driver(proplists:get_value(data_dir, Config),
+ Drv) of
+ ok -> ok;
+ {error, permanent} -> ok;
+ LoadError -> ct:fail({load_error, LoadError})
+ end,
+ case open_port({spawn, Drv}, []) of
+ Port1 when is_port(Port1) ->
+ try port_control(Port1, ?PEEK_NONXQ_TEST, "") of
+ "ok" ->
+ ok;
+ [$s,$k,$i,$p,$p,$e,$d,$:,$ | SkipReason] ->
+ throw({skipped, SkipReason});
+ [$e,$r,$r,$o,$r,$:,$ | Error1] ->
+ ct:fail(Error1)
+ after
+ exit(Port1, kill),
+ receive {'EXIT', Port1, _} -> ok end
+ end;
+ Error1 ->
+ ct:fail({open_port1_failed, Error1})
+ end,
+ case open_port({spawn, Drv}, []) of
+ Port2 when is_port(Port2) ->
+ try port_control(Port2, ?PEEK_NONXQ_WAIT, "") of
+ "ok" ->
+ ok;
+ [$e,$r,$r,$o,$r,$:,$ | Error2] ->
+ ct:fail(Error2)
+ after
+ receive {Port2, test_successful} -> ok end,
+ Port2 ! {self(), close},
+ receive {Port2, closed} -> ok end
+ end;
+ Error2 ->
+ ct:fail({open_port2_failed, Error2})
+ end
+ end
+ catch
+ throw:Term -> Term
+ after
+ process_flag(trap_exit, OTE),
+ erl_ddll:unload_driver(Drv)
+ end.
+
otp_6879(Config) when is_list(Config) ->
- ?line Drv = 'otp_6879_drv',
- ?line Parent = self(),
- ?line ok = load_driver(?config(data_dir, Config), Drv),
- ?line Procs = lists:map(
- fun (No) ->
- spawn_link(
- fun () ->
- case open_port({spawn, Drv}, []) of
- Port when is_port(Port) ->
- Res = otp_6879_call(Port, No, 10000),
- erlang:port_close(Port),
- Parent ! {self(), Res};
- _ ->
- Parent ! {self(),
- open_port_failed}
- end
- end)
- end,
- lists:seq(1,10)),
- ?line lists:foreach(fun (P) ->
- ?line receive
- {P, ok} ->
- ?line ok;
- {P, Error} ->
- ?line ?t:fail({P, Error})
- end
- end,
- Procs),
+ Drv = 'otp_6879_drv',
+ Parent = self(),
+ ok = load_driver(proplists:get_value(data_dir, Config), Drv),
+ Procs = lists:map(
+ fun (No) ->
+ spawn_link(
+ fun () ->
+ case open_port({spawn, Drv}, []) of
+ Port when is_port(Port) ->
+ Res = otp_6879_call(Port, No, 10000),
+ erlang:port_close(Port),
+ Parent ! {self(), Res};
+ _ ->
+ Parent ! {self(),
+ open_port_failed}
+ end
+ end)
+ end,
+ lists:seq(1,10)),
+ lists:foreach(fun (P) ->
+ receive
+ {P, ok} ->
+ ok;
+ {P, Error} ->
+ ct:fail({P, Error})
+ end
+ end,
+ Procs),
%% Also try it when input exceeds default buffer (256 bytes)
- ?line Data = lists:seq(1, 1000),
- ?line case open_port({spawn, Drv}, []) of
- Port when is_port(Port) ->
- ?line ok = otp_6879_call(Port, Data, 10),
- ?line erlang:port_close(Port);
- _ ->
- ?line ?t:fail(open_port_failed)
- end,
- ?line erl_ddll:unload_driver(Drv),
- ?line ok.
+ Data = lists:seq(1, 1000),
+ case open_port({spawn, Drv}, []) of
+ Port when is_port(Port) ->
+ ok = otp_6879_call(Port, Data, 10),
+ erlang:port_close(Port);
+ _ ->
+ ct:fail(open_port_failed)
+ end,
+ erl_ddll:unload_driver(Drv),
+ ok.
otp_6879_call(_Port, _Data, 0) ->
ok;
otp_6879_call(Port, Data, N) ->
case catch erlang:port_call(Port, 0, Data) of
- Data -> otp_6879_call(Port, Data, N-1);
- BadData -> {mismatch, Data, BadData}
+ Data -> otp_6879_call(Port, Data, N-1);
+ BadData -> {mismatch, Data, BadData}
end.
-caller(doc) ->
- [];
-caller(suite) ->
- [];
caller(Config) when is_list(Config) ->
- ?line run_caller_test(Config, false),
- ?line run_caller_test(Config, true).
-
+ run_caller_test(Config, false),
+ run_caller_test(Config, true).
+
run_caller_test(Config, Outputv) ->
- ?line Drv = 'caller_drv',
- ?line Cmd = case Outputv of
- true ->
- ?line os:putenv("CALLER_DRV_USE_OUTPUTV",
- "true"),
- outputv;
- false ->
- ?line os:putenv("CALLER_DRV_USE_OUTPUTV",
- "false"),
- output
- end,
- ?line ok = load_driver(?config(data_dir, Config), Drv),
- ?line Port = open_port({spawn, Drv}, []),
- ?line true = is_port(Port),
- ?line chk_caller(Port, start, self()),
- ?line chk_caller(Port,
- Cmd,
- spawn_link(
- fun () ->
- port_command(Port, "")
- end)),
- ?line Port ! {self(), {command, ""}},
- ?line chk_caller(Port, Cmd, self()),
- ?line chk_caller(Port,
- control,
- spawn_link(
- fun () ->
- port_control(Port, 0, "")
- end)),
- ?line chk_caller(Port,
- call,
- spawn_link(
- fun () ->
- erlang:port_call(Port, 0, "")
- end)),
- ?line true = port_close(Port),
- ?line erl_ddll:unload_driver(Drv),
- ?line ok.
+ Drv = 'caller_drv',
+ Cmd = case Outputv of
+ true ->
+ os:putenv("CALLER_DRV_USE_OUTPUTV",
+ "true"),
+ outputv;
+ false ->
+ os:putenv("CALLER_DRV_USE_OUTPUTV",
+ "false"),
+ output
+ end,
+ ok = load_driver(proplists:get_value(data_dir, Config), Drv),
+ Port = open_port({spawn, Drv}, []),
+ true = is_port(Port),
+ chk_caller(Port, start, self()),
+ chk_caller(Port,
+ Cmd,
+ spawn_link(
+ fun () ->
+ port_command(Port, "")
+ end)),
+ Port ! {self(), {command, ""}},
+ chk_caller(Port, Cmd, self()),
+ chk_caller(Port,
+ control,
+ spawn_link(
+ fun () ->
+ port_control(Port, 0, "")
+ end)),
+ chk_caller(Port,
+ call,
+ spawn_link(
+ fun () ->
+ erlang:port_call(Port, 0, "")
+ end)),
+ true = port_close(Port),
+ erl_ddll:unload_driver(Drv),
+ ok.
chk_caller(Port, Callback, ExpectedCaller) ->
receive
- {caller, Port, Callback, Caller} ->
- ExpectedCaller = Caller
+ {caller, Port, Callback, Caller} ->
+ ExpectedCaller = Caller
end.
-many_events(suite) ->
- [];
-many_events(doc) ->
- ["Check that many simultaneously signalled events work (win32)"];
+%% Check that many simultaneously signalled events work (win32)
many_events(Config) when is_list(Config) ->
- ?line Name = 'many_events_drv',
- ?line Port = start_driver(Config, Name, false),
+ Name = 'many_events_drv',
+ Port = start_driver(Config, Name, false),
Number = "1000",
Port ! {self(), {command, Number}},
receive
- {Port, {data,Number}} ->
- ?line receive %% Just to make sure the emulator does not crash
- %% after this case is run (if faulty)
- after 2000 ->
- ok
- end
+ {Port, {data,Number}} ->
+ receive %% Just to make sure the emulator does not crash
+ %% after this case is run (if faulty)
+ after 2000 ->
+ ok
+ end
after 1000 ->
- ?line exit(the_driver_does_not_respond)
+ exit(the_driver_does_not_respond)
end,
- ?line stop_driver(Port, Name),
- ?line ok.
-
-
-missing_callbacks(doc) ->
- [];
-missing_callbacks(suite) ->
- [];
+ stop_driver(Port, Name),
+ ok.
+
+
missing_callbacks(Config) when is_list(Config) ->
- ?line Name = 'missing_callback_drv',
- ?line Port = start_driver(Config, Name, false),
+ Name = 'missing_callback_drv',
+ Port = start_driver(Config, Name, false),
- ?line Port ! {self(), {command, "tjenix"}},
- ?line true = erlang:port_command(Port, "halloj"),
- ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(Port, 4711, "mors")),
- ?line {'EXIT', {badarg, _}} = (catch erlang:port_call(Port, 17, "hej")),
+ Port ! {self(), {command, "tjenix"}},
+ true = erlang:port_command(Port, "halloj"),
+ {'EXIT', {badarg, _}} = (catch erlang:port_control(Port, 4711, "mors")),
+ {'EXIT', {badarg, _}} = (catch erlang:port_call(Port, 17, "hej")),
- ?line %% Give the (non-existing) ready_output(), ready_input(), event(),
- ?line %% and timeout() some time to be called.
- ?line receive after 1000 -> ok end,
+ %% Give the (non-existing) ready_output(), ready_input(), event(),
+ %% and timeout() some time to be called.
+ receive after 1000 -> ok end,
- ?line stop_driver(Port, Name),
- ?line ok.
+ stop_driver(Port, Name),
+ ok.
-smp_select(doc) ->
- ["Test concurrent calls to driver_select."];
-smp_select(suite) ->
- [];
+%% Test concurrent calls to driver_select.
smp_select(Config) when is_list(Config) ->
case os:type() of
- {win32,_} -> {skipped, "Test not implemented for this OS"};
- _ -> smp_select0(Config)
+ {win32,_} -> {skipped, "Test not implemented for this OS"};
+ _ -> smp_select0(Config)
end.
-
+
smp_select0(Config) ->
- ?line DrvName = 'chkio_drv',
- Path = ?config(data_dir, Config),
+ DrvName = 'chkio_drv',
+ Path = proplists:get_value(data_dir, Config),
erl_ddll:start(),
- ?line ok = load_driver(Path, DrvName),
+ ok = load_driver(Path, DrvName),
Master = self(),
ProcFun = fun()-> io:format("Worker ~p starting\n",[self()]),
- ?line Port = open_port({spawn, DrvName}, []),
- smp_select_loop(Port, 100000),
- sleep(1000), % wait for driver to handle pending events
- ?line true = erlang:port_close(Port),
- Master ! {ok,self()},
- io:format("Worker ~p finished\n",[self()])
- end,
- ?line Pids = lists:map(fun(_) -> spawn_link(ProcFun) end,
- lists:seq(1,4)),
+ Port = open_port({spawn, DrvName}, []),
+ smp_select_loop(Port, 100000),
+ sleep(1000), % wait for driver to handle pending events
+ true = erlang:port_close(Port),
+ Master ! {ok,self()},
+ io:format("Worker ~p finished\n",[self()])
+ end,
+ Pids = lists:map(fun(_) -> spawn_link(ProcFun) end,
+ lists:seq(1,4)),
TimeoutMsg = make_ref(),
{ok,TRef} = timer:send_after(5*1000, TimeoutMsg), % Limit test duration on slow machines
smp_select_wait(Pids, TimeoutMsg),
timer:cancel(TRef),
- ?line ok = erl_ddll:unload_driver(DrvName),
- ?line ok = erl_ddll:stop(),
+ ok = erl_ddll:unload_driver(DrvName),
+ ok = erl_ddll:stop(),
ok.
smp_select_loop(_, 0) ->
ok;
smp_select_loop(Port, N) ->
- ?line "ok" = erlang:port_control(Port, ?CHKIO_SMP_SELECT, []),
+ "ok" = erlang:port_control(Port, ?CHKIO_SMP_SELECT, []),
receive
- stop ->
- io:format("Worker ~p stopped with ~p laps left\n",[self(), N]),
- ok
+ stop ->
+ io:format("Worker ~p stopped with ~p laps left\n",[self(), N]),
+ ok
after 0 ->
- smp_select_loop(Port, N-1)
+ smp_select_loop(Port, N-1)
end.
smp_select_wait([], _) ->
ok;
smp_select_wait(Pids, TimeoutMsg) ->
receive
- {ok,Pid} when is_pid(Pid) ->
- smp_select_wait(lists:delete(Pid,Pids), TimeoutMsg);
- TimeoutMsg ->
- lists:foreach(fun(Pid)-> Pid ! stop end,
- Pids),
- smp_select_wait(Pids, TimeoutMsg)
+ {ok,Pid} when is_pid(Pid) ->
+ smp_select_wait(lists:delete(Pid,Pids), TimeoutMsg);
+ TimeoutMsg ->
+ lists:foreach(fun(Pid)-> Pid ! stop end,
+ Pids),
+ smp_select_wait(Pids, TimeoutMsg)
end.
-driver_select_use(doc) ->
- ["Test driver_select() with new ERL_DRV_USE flag."];
-driver_select_use(suite) ->
- [];
+%% Test driver_select() with new ERL_DRV_USE flag.
driver_select_use(Config) when is_list(Config) ->
case os:type() of
- {win32,_} -> {skipped, "Test not implemented for this OS"};
- _ -> driver_select_use0(Config)
+ {win32,_} -> {skipped, "Test not implemented for this OS"};
+ _ -> driver_select_use0(Config)
end.
-
+
driver_select_use0(Config) ->
- ?line DrvName = 'chkio_drv',
- Path = ?config(data_dir, Config),
+ DrvName = 'chkio_drv',
+ Path = proplists:get_value(data_dir, Config),
erl_ddll:start(),
- ?line ok = load_driver(Path, DrvName),
- ?line Port = open_port({spawn, DrvName}, []),
- ?line "ok" = erlang:port_control(Port, ?CHKIO_DRV_USE, []),
- ?line {Port,{data,"TheEnd"}} = receive Msg -> Msg
- after 10000 -> timeout end,
- ?line true = erlang:port_close(Port),
- ?line ok = erl_ddll:unload_driver(DrvName),
- ?line ok = erl_ddll:stop(),
+ ok = load_driver(Path, DrvName),
+ Port = open_port({spawn, DrvName}, []),
+ "ok" = erlang:port_control(Port, ?CHKIO_DRV_USE, []),
+ {Port,{data,"TheEnd"}} = receive Msg -> Msg
+ after 10000 -> timeout end,
+ true = erlang:port_close(Port),
+ ok = erl_ddll:unload_driver(DrvName),
+ ok = erl_ddll:stop(),
ok.
thread_mseg_alloc_cache_clean(Config) when is_list(Config) ->
case {erlang:system_info(threads),
- erlang:system_info({allocator,mseg_alloc}),
- driver_alloc_sbct()} of
- {_, false, _} ->
- ?line {skipped, "No mseg_alloc"};
- {false, _, _} ->
- ?line {skipped, "No threads"};
- {_, _, false} ->
- ?line {skipped, "driver_alloc() not using the alloc_util framework"};
- {_, _, SBCT} when is_integer(SBCT), SBCT > 10*1024*1024 ->
- ?line {skipped, "driver_alloc() using too large single block threshold"};
- {_, _, 0} ->
- ?line {skipped, "driver_alloc() using too low single block threshold"};
- {true, _MsegAllocInfo, SBCT} ->
- ?line DrvName = 'thr_alloc_drv',
- ?line Path = ?config(data_dir, Config),
- ?line erl_ddll:start(),
- ?line ok = load_driver(Path, DrvName),
- ?line Port = open_port({spawn, DrvName}, []),
- ?line CCI = 1000,
- ?line ?t:format("CCI = ~p~n", [CCI]),
- ?line CCC = mseg_alloc_ccc(),
- ?line ?t:format("CCC = ~p~n", [CCC]),
- ?line thread_mseg_alloc_cache_clean_test(Port,
- 10,
- CCI,
- SBCT+100),
- ?line true = erlang:port_close(Port),
- ?line ok = erl_ddll:unload_driver(DrvName),
- ?line ok = erl_ddll:stop(),
- ?line ok
+ erlang:system_info({allocator,mseg_alloc}),
+ driver_alloc_sbct()} of
+ {_, false, _} ->
+ {skipped, "No mseg_alloc"};
+ {false, _, _} ->
+ {skipped, "No threads"};
+ {_, _, false} ->
+ {skipped, "driver_alloc() not using the alloc_util framework"};
+ {_, _, SBCT} when is_integer(SBCT), SBCT > 10*1024*1024 ->
+ {skipped, "driver_alloc() using too large single block threshold"};
+ {_, _, 0} ->
+ {skipped, "driver_alloc() using too low single block threshold"};
+ {true, _MsegAllocInfo, SBCT} ->
+ DrvName = 'thr_alloc_drv',
+ Path = proplists:get_value(data_dir, Config),
+ erl_ddll:start(),
+ ok = load_driver(Path, DrvName),
+ Port = open_port({spawn, DrvName}, []),
+ CCI = 1000,
+ io:format("CCI = ~p~n", [CCI]),
+ CCC = mseg_alloc_ccc(),
+ io:format("CCC = ~p~n", [CCC]),
+ thread_mseg_alloc_cache_clean_test(Port,
+ 10,
+ CCI,
+ SBCT+100),
+ true = erlang:port_close(Port),
+ ok = erl_ddll:unload_driver(DrvName),
+ ok = erl_ddll:stop(),
+ ok
end.
mseg_alloc_cci(MsegAllocInfo) ->
- ?line {value,{options, OL}}
- = lists:keysearch(options, 1, MsegAllocInfo),
- ?line {value,{cci,CCI}} = lists:keysearch(cci,1,OL),
- ?line CCI.
+ {value,{options, OL}}
+ = lists:keysearch(options, 1, MsegAllocInfo),
+ {value,{cci,CCI}} = lists:keysearch(cci,1,OL),
+ CCI.
mseg_alloc_ccc() ->
mseg_alloc_ccc(mseg_inst_info(0)).
mseg_alloc_ccc(MsegAllocInfo) ->
- ?line {value,{memkind, MKL}} = lists:keysearch(memkind,1,MsegAllocInfo),
- ?line {value,{calls, CL}} = lists:keysearch(calls, 1, MKL),
- ?line {value,{mseg_check_cache, GigaCCC, CCC}}
- = lists:keysearch(mseg_check_cache, 1, CL),
- ?line GigaCCC*1000000000 + CCC.
+ {value,{memkind, MKL}} = lists:keysearch(memkind,1,MsegAllocInfo),
+ {value,{calls, CL}} = lists:keysearch(calls, 1, MKL),
+ {value,{mseg_check_cache, GigaCCC, CCC}}
+ = lists:keysearch(mseg_check_cache, 1, CL),
+ GigaCCC*1000000000 + CCC.
mseg_alloc_cached_segments() ->
mseg_alloc_cached_segments(mseg_inst_info(0)).
mseg_alloc_cached_segments(MsegAllocInfo) ->
- MemName = case is_halfword_vm() of
- true -> "high memory";
- false -> "all memory"
- end,
- ?line [{memkind,DrvMem}]
- = lists:filter(fun(E) -> case E of
- {memkind, [{name, MemName} | _]} -> true;
- _ -> false
- end end, MsegAllocInfo),
- ?line {value,{status, SL}}
- = lists:keysearch(status, 1, DrvMem),
- ?line {value,{cached_segments, CS}}
- = lists:keysearch(cached_segments, 1, SL),
- ?line CS.
+ MemName = "all memory",
+ [{memkind,DrvMem}]
+ = lists:filter(fun(E) -> case E of
+ {memkind, [{name, MemName} | _]} -> true;
+ _ -> false
+ end end, MsegAllocInfo),
+ {value,{status, SL}}
+ = lists:keysearch(status, 1, DrvMem),
+ {value,{cached_segments, CS}}
+ = lists:keysearch(cached_segments, 1, SL),
+ CS.
mseg_inst_info(I) ->
{value, {instance, I, Value}}
- = lists:keysearch(I,
- 2,
- erlang:system_info({allocator,mseg_alloc})),
+ = lists:keysearch(I,
+ 2,
+ erlang:system_info({allocator,mseg_alloc})),
Value.
-is_halfword_vm() ->
- case {erlang:system_info({wordsize, internal}),
- erlang:system_info({wordsize, external})} of
- {4, 8} -> true;
- {WS, WS} -> false
- end.
-
driver_alloc_sbct() ->
{_, _, _, As} = erlang:system_info(allocator),
case lists:keysearch(driver_alloc, 1, As) of
- {value,{driver_alloc,DAOPTs}} ->
- case lists:keysearch(sbct, 1, DAOPTs) of
- {value,{sbct,SBCT}} ->
- SBCT;
- _ ->
- false
- end;
- _ ->
- false
+ {value,{driver_alloc,DAOPTs}} ->
+ case lists:keysearch(sbct, 1, DAOPTs) of
+ {value,{sbct,SBCT}} ->
+ SBCT;
+ _ ->
+ false
+ end;
+ _ ->
+ false
end.
thread_mseg_alloc_cache_clean_test(_Port, 0, _CCI, _Size) ->
- ?line ok;
+ ok;
thread_mseg_alloc_cache_clean_test(Port, N, CCI, Size) ->
- ?line wait_until(fun () -> 0 == mseg_alloc_cached_segments() end),
- ?line receive after CCI+500 -> ok end,
- ?line OCCC = mseg_alloc_ccc(),
- ?line "ok" = erlang:port_control(Port, 0, integer_to_list(Size)),
- ?line receive after CCI+500 -> ok end,
- ?line CCC = mseg_alloc_ccc(),
- ?line ?t:format("CCC = ~p~n", [CCC]),
- ?line true = CCC > OCCC,
- ?line thread_mseg_alloc_cache_clean_test(Port, N-1, CCI, Size).
+ wait_until(fun () -> 0 == mseg_alloc_cached_segments() end),
+ receive after CCI+500 -> ok end,
+ OCCC = mseg_alloc_ccc(),
+ "ok" = erlang:port_control(Port, 0, integer_to_list(Size)),
+ receive after CCI+500 -> ok end,
+ CCC = mseg_alloc_ccc(),
+ io:format("CCC = ~p~n", [CCC]),
+ true = CCC > OCCC,
+ thread_mseg_alloc_cache_clean_test(Port, N-1, CCI, Size).
otp_9302(Config) when is_list(Config) ->
- ?line Path = ?config(data_dir, Config),
- ?line erl_ddll:start(),
- ?line ok = load_driver(Path, otp_9302_drv),
- ?line Port = open_port({spawn, otp_9302_drv}, []),
- ?line true = is_port(Port),
- ?line port_command(Port, ""),
- ?line {msg, block} = get_port_msg(Port, infinity),
- ?line {msg, job} = get_port_msg(Port, infinity),
- ?line C = case erlang:system_info(thread_pool_size) of
- 0 ->
- ?line {msg, cancel} = get_port_msg(Port, infinity),
- ?line {msg, job} = get_port_msg(Port, infinity),
- ?line false;
- _ ->
- case get_port_msg(Port, infinity) of
- {msg, cancel} -> %% Cancel always fail in Rel >= 15
- ?line {msg, job} = get_port_msg(Port, infinity),
- ?line false;
- {msg, job} ->
- ?line ok,
- ?line true
- end
- end,
- ?line {msg, end_of_jobs} = get_port_msg(Port, infinity),
- ?line no_msg = get_port_msg(Port, 2000),
- ?line port_close(Port),
- ?line case C of
- true ->
- ?line {comment, "Async job cancelled"};
- false ->
- ?line {comment, "Async job not cancelled"}
- end.
+ Path = proplists:get_value(data_dir, Config),
+ erl_ddll:start(),
+ ok = load_driver(Path, otp_9302_drv),
+ Port = open_port({spawn, otp_9302_drv}, []),
+ true = is_port(Port),
+ port_command(Port, ""),
+ {msg, block} = get_port_msg(Port, infinity),
+ {msg, job} = get_port_msg(Port, infinity),
+ C = case erlang:system_info(thread_pool_size) of
+ 0 ->
+ {msg, cancel} = get_port_msg(Port, infinity),
+ {msg, job} = get_port_msg(Port, infinity),
+ false;
+ _ ->
+ case get_port_msg(Port, infinity) of
+ {msg, cancel} -> %% Cancel always fail in Rel >= 15
+ {msg, job} = get_port_msg(Port, infinity),
+ false;
+ {msg, job} ->
+ ok,
+ true
+ end
+ end,
+ {msg, end_of_jobs} = get_port_msg(Port, infinity),
+ no_msg = get_port_msg(Port, 2000),
+ port_close(Port),
+ case C of
+ true ->
+ {comment, "Async job cancelled"};
+ false ->
+ {comment, "Async job not cancelled"}
+ end.
thr_free_drv(Config) when is_list(Config) ->
case erlang:system_info(threads) of
- false ->
- {skipped, "No thread support"};
- true ->
- thr_free_drv_do(Config)
+ false ->
+ {skipped, "No thread support"};
+ true ->
+ thr_free_drv_do(Config)
end.
thr_free_drv_do(Config) ->
- ?line Path = ?config(data_dir, Config),
- ?line erl_ddll:start(),
- ?line ok = load_driver(Path, thr_free_drv),
- ?line MemBefore = driver_alloc_size(),
-% io:format("SID=~p", [erlang:system_info(scheduler_id)]),
- ?line Port = open_port({spawn, thr_free_drv}, []),
- ?line MemPeek = driver_alloc_size(),
- ?line true = is_port(Port),
- ?line ok = thr_free_drv_control(Port, 0),
- ?line port_close(Port),
- ?line MemAfter = driver_alloc_size(),
- ?line io:format("MemPeek=~p~n", [MemPeek]),
- ?line io:format("MemBefore=~p, MemAfter=~p~n", [MemBefore, MemAfter]),
- ?line MemBefore = MemAfter,
- ?line case MemPeek of
- undefined -> ok;
- _ ->
- ?line true = MemPeek > MemBefore
- end,
- ?line ok.
+ Path = proplists:get_value(data_dir, Config),
+ erl_ddll:start(),
+ ok = load_driver(Path, thr_free_drv),
+ MemBefore = driver_alloc_size(),
+ % io:format("SID=~p", [erlang:system_info(scheduler_id)]),
+ Port = open_port({spawn, thr_free_drv}, []),
+ MemPeek = driver_alloc_size(),
+ true = is_port(Port),
+ ok = thr_free_drv_control(Port, 0),
+ port_close(Port),
+ MemAfter = driver_alloc_size(),
+ io:format("MemPeek=~p~n", [MemPeek]),
+ io:format("MemBefore=~p, MemAfter=~p~n", [MemBefore, MemAfter]),
+ MemBefore = MemAfter,
+ case MemPeek of
+ undefined -> ok;
+ _ ->
+ true = MemPeek > MemBefore
+ end,
+ ok.
thr_free_drv_control(Port, N) ->
case erlang:port_control(Port, 0, "") of
- "done" ->
- ok;
- "more" ->
- erlang:yield(),
-% io:format("N=~p, SID=~p", [N, erlang:system_info(scheduler_id)]),
- thr_free_drv_control(Port, N+1)
+ "done" ->
+ ok;
+ "more" ->
+ erlang:yield(),
+ % io:format("N=~p, SID=~p", [N, erlang:system_info(scheduler_id)]),
+ thr_free_drv_control(Port, N+1)
end.
-
+
async_blast(Config) when is_list(Config) ->
- ?line Path = ?config(data_dir, Config),
- ?line erl_ddll:start(),
- ?line ok = load_driver(Path, async_blast_drv),
- ?line SchedOnln = erlang:system_info(schedulers_online),
- ?line MemBefore = driver_alloc_size(),
- ?line Start = os:timestamp(),
- ?line Blast = fun () ->
- Port = open_port({spawn, async_blast_drv}, []),
- true = is_port(Port),
- port_command(Port, ""),
- receive
- {Port, done} ->
- ok
- end,
- port_close(Port)
- end,
- ?line Ps = lists:map(fun (N) ->
- spawn_opt(Blast,
- [{scheduler,
- (N rem SchedOnln)+ 1},
- monitor])
- end,
- lists:seq(1, 100)),
- ?line MemMid = driver_alloc_size(),
- ?line lists:foreach(fun ({Pid, Mon}) ->
- receive
- {'DOWN',Mon,process,Pid,_} -> ok
- end
- end, Ps),
- ?line End = os:timestamp(),
- ?line MemAfter = driver_alloc_size(),
- ?line io:format("MemBefore=~p, MemMid=~p, MemAfter=~p~n",
- [MemBefore, MemMid, MemAfter]),
- ?line AsyncBlastTime = timer:now_diff(End,Start)/1000000,
- ?line io:format("AsyncBlastTime=~p~n", [AsyncBlastTime]),
- ?line MemBefore = MemAfter,
- ?line erlang:display({async_blast_time, AsyncBlastTime}),
- ?line ok.
+ Path = proplists:get_value(data_dir, Config),
+ erl_ddll:start(),
+ ok = load_driver(Path, async_blast_drv),
+ SchedOnln = erlang:system_info(schedulers_online),
+ MemBefore = driver_alloc_size(),
+ Start = os:timestamp(),
+ Blast = fun () ->
+ Port = open_port({spawn, async_blast_drv}, []),
+ true = is_port(Port),
+ port_command(Port, ""),
+ receive
+ {Port, done} ->
+ ok
+ end,
+ port_close(Port)
+ end,
+ Ps = lists:map(fun (N) ->
+ spawn_opt(Blast,
+ [{scheduler,
+ (N rem SchedOnln)+ 1},
+ monitor])
+ end,
+ lists:seq(1, 100)),
+ MemMid = driver_alloc_size(),
+ lists:foreach(fun ({Pid, Mon}) ->
+ receive
+ {'DOWN',Mon,process,Pid,_} -> ok
+ end
+ end, Ps),
+ End = os:timestamp(),
+ MemAfter = driver_alloc_size(),
+ io:format("MemBefore=~p, MemMid=~p, MemAfter=~p~n",
+ [MemBefore, MemMid, MemAfter]),
+ AsyncBlastTime = timer:now_diff(End,Start)/1000000,
+ io:format("AsyncBlastTime=~p~n", [AsyncBlastTime]),
+ MemBefore = MemAfter,
+ erlang:display({async_blast_time, AsyncBlastTime}),
+ ok.
thr_msg_blast_receiver(_Port, N, N) ->
ok;
thr_msg_blast_receiver(Port, N, Max) ->
receive
- {Port, hi} ->
- thr_msg_blast_receiver(Port, N+1, Max)
+ {Port, hi} ->
+ thr_msg_blast_receiver(Port, N+1, Max)
end.
thr_msg_blast_receiver_proc(Port, Max, Parent, Done) ->
case port_control(Port, 0, "") of
- "receiver" ->
- spawn(fun () ->
- thr_msg_blast_receiver_proc(Port, Max+1, Parent, Done)
- end),
- thr_msg_blast_receiver(Port, 0, Max);
- "done" ->
- Parent ! Done
+ "receiver" ->
+ spawn(fun () ->
+ thr_msg_blast_receiver_proc(Port, Max+1, Parent, Done)
+ end),
+ thr_msg_blast_receiver(Port, 0, Max);
+ "done" ->
+ Parent ! Done
end.
thr_msg_blast(Config) when is_list(Config) ->
case erlang:system_info(smp_support) of
- false ->
- {skipped, "Non-SMP emulator; nothing to test..."};
- true ->
- Path = ?config(data_dir, Config),
- erl_ddll:start(),
- ok = load_driver(Path, thr_msg_blast_drv),
- MemBefore = driver_alloc_size(),
- Start = os:timestamp(),
- Port = open_port({spawn, thr_msg_blast_drv}, []),
- true = is_port(Port),
- Done = make_ref(),
- Me = self(),
- spawn(fun () ->
- thr_msg_blast_receiver_proc(Port, 1, Me, Done)
- end),
- receive
- Done -> ok
- end,
- ok = thr_msg_blast_receiver(Port, 0, 32*10000),
- port_close(Port),
- End = os:timestamp(),
- receive
- Garbage ->
- ?t:fail({received_garbage, Port, Garbage})
- after 2000 ->
- ok
- end,
- MemAfter = driver_alloc_size(),
- io:format("MemBefore=~p, MemAfter=~p~n",
- [MemBefore, MemAfter]),
- ThrMsgBlastTime = timer:now_diff(End,Start)/1000000,
- io:format("ThrMsgBlastTime=~p~n", [ThrMsgBlastTime]),
- MemBefore = MemAfter,
- Res = {thr_msg_blast_time, ThrMsgBlastTime},
- erlang:display(Res),
- Res
+ false ->
+ {skipped, "Non-SMP emulator; nothing to test..."};
+ true ->
+ Path = proplists:get_value(data_dir, Config),
+ erl_ddll:start(),
+ ok = load_driver(Path, thr_msg_blast_drv),
+ MemBefore = driver_alloc_size(),
+ Start = os:timestamp(),
+ Port = open_port({spawn, thr_msg_blast_drv}, []),
+ true = is_port(Port),
+ Done = make_ref(),
+ Me = self(),
+ spawn(fun () ->
+ thr_msg_blast_receiver_proc(Port, 1, Me, Done)
+ end),
+ receive
+ Done -> ok
+ end,
+ ok = thr_msg_blast_receiver(Port, 0, 32*10000),
+ port_close(Port),
+ End = os:timestamp(),
+ receive
+ Garbage ->
+ ct:fail({received_garbage, Port, Garbage})
+ after 2000 ->
+ ok
+ end,
+ MemAfter = driver_alloc_size(),
+ io:format("MemBefore=~p, MemAfter=~p~n",
+ [MemBefore, MemAfter]),
+ ThrMsgBlastTime = timer:now_diff(End,Start)/1000000,
+ io:format("ThrMsgBlastTime=~p~n", [ThrMsgBlastTime]),
+ MemBefore = MemAfter,
+ Res = {thr_msg_blast_time, ThrMsgBlastTime},
+ erlang:display(Res),
+ Res
end.
-define(IN_RANGE(LoW_, VaLuE_, HiGh_),
- case in_range(LoW_, VaLuE_, HiGh_) of
- true -> ok;
- false ->
- case erlang:system_info(lock_checking) of
- true ->
- ?t:format("~p:~p: Ignore bad sched count due to "
- "lock checking~n",
- [?MODULE,?LINE]);
- false ->
- ?t:fail({unexpected_sched_counts, VaLuE_})
- end
- end).
+ case in_range(LoW_, VaLuE_, HiGh_) of
+ true -> ok;
+ false ->
+ case erlang:system_info(lock_checking) of
+ true ->
+ io:format("~p:~p: Ignore bad sched count due to "
+ "lock checking~n",
+ [?MODULE,?LINE]);
+ false ->
+ ct:fail({unexpected_sched_counts, VaLuE_})
+ end
+ end).
consume_timeslice(Config) when is_list(Config) ->
@@ -2150,7 +2033,7 @@ consume_timeslice(Config) when is_list(Config) ->
%% the port instead.
%%
- Path = ?config(data_dir, Config),
+ Path = proplists:get_value(data_dir, Config),
erl_ddll:start(),
ok = load_driver(Path, consume_timeslice_drv),
Port = open_port({spawn, consume_timeslice_drv}, [{parallelism, false}]),
@@ -2160,18 +2043,18 @@ consume_timeslice(Config) when is_list(Config) ->
"enabled" = port_control(Port, $E, ""),
Proc1 = spawn_link(fun () ->
- receive Go -> ok end,
- Port ! {Parent, {command, ""}},
- Port ! {Parent, {command, ""}},
- Port ! {Parent, {command, ""}},
- Port ! {Parent, {command, ""}},
- Port ! {Parent, {command, ""}},
- Port ! {Parent, {command, ""}},
- Port ! {Parent, {command, ""}},
- Port ! {Parent, {command, ""}},
- Port ! {Parent, {command, ""}},
- Port ! {Parent, {command, ""}}
- end),
+ receive Go -> ok end,
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}}
+ end),
receive after 100 -> ok end,
count_pp_sched_start(),
Proc1 ! Go,
@@ -2182,18 +2065,18 @@ consume_timeslice(Config) when is_list(Config) ->
"disabled" = port_control(Port, $D, ""),
Proc2 = spawn_link(fun () ->
- receive Go -> ok end,
- Port ! {Parent, {command, ""}},
- Port ! {Parent, {command, ""}},
- Port ! {Parent, {command, ""}},
- Port ! {Parent, {command, ""}},
- Port ! {Parent, {command, ""}},
- Port ! {Parent, {command, ""}},
- Port ! {Parent, {command, ""}},
- Port ! {Parent, {command, ""}},
- Port ! {Parent, {command, ""}},
- Port ! {Parent, {command, ""}}
- end),
+ receive Go -> ok end,
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}}
+ end),
receive after 100 -> ok end,
count_pp_sched_start(),
Proc2 ! Go,
@@ -2204,18 +2087,18 @@ consume_timeslice(Config) when is_list(Config) ->
"enabled" = port_control(Port, $E, ""),
Proc3 = spawn_link(fun () ->
- receive Go -> ok end,
- port_command(Port, ""),
- port_command(Port, ""),
- port_command(Port, ""),
- port_command(Port, ""),
- port_command(Port, ""),
- port_command(Port, ""),
- port_command(Port, ""),
- port_command(Port, ""),
- port_command(Port, ""),
- port_command(Port, "")
- end),
+ receive Go -> ok end,
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, "")
+ end),
count_pp_sched_start(),
Proc3 ! Go,
wait_command_msgs(Port, 10),
@@ -2225,18 +2108,18 @@ consume_timeslice(Config) when is_list(Config) ->
"disabled" = port_control(Port, $D, ""),
Proc4 = spawn_link(fun () ->
- receive Go -> ok end,
- port_command(Port, ""),
- port_command(Port, ""),
- port_command(Port, ""),
- port_command(Port, ""),
- port_command(Port, ""),
- port_command(Port, ""),
- port_command(Port, ""),
- port_command(Port, ""),
- port_command(Port, ""),
- port_command(Port, "")
- end),
+ receive Go -> ok end,
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, "")
+ end),
count_pp_sched_start(),
Proc4 ! Go,
wait_command_msgs(Port, 10),
@@ -2248,43 +2131,43 @@ consume_timeslice(Config) when is_list(Config) ->
%% If only one scheduler use port with parallelism set to true,
%% in order to trigger scheduling of command signals
Port2 = case SOnl of
- 1 ->
- Port ! {self(), close},
- receive {Port, closed} -> ok end,
- open_port({spawn, consume_timeslice_drv},
- [{parallelism, true}]);
- _ ->
- process_flag(scheduler, 1),
- 1 = erlang:system_info(scheduler_id),
- Port
- end,
+ 1 ->
+ Port ! {self(), close},
+ receive {Port, closed} -> ok end,
+ open_port({spawn, consume_timeslice_drv},
+ [{parallelism, true}]);
+ _ ->
+ process_flag(scheduler, 1),
+ 1 = erlang:system_info(scheduler_id),
+ Port
+ end,
count_pp_sched_start(),
"enabled" = port_control(Port2, $E, ""),
W5 = case SOnl of
- 1 ->
- false;
- _ ->
- W1= spawn_opt(fun () ->
- 2 = erlang:system_info(scheduler_id),
- "sleeped" = port_control(Port2, $S, "")
- end, [link,{scheduler,2}]),
- receive after 100 -> ok end,
- W1
- end,
+ 1 ->
+ false;
+ _ ->
+ W1= spawn_opt(fun () ->
+ 2 = erlang:system_info(scheduler_id),
+ "sleeped" = port_control(Port2, $S, "")
+ end, [link,{scheduler,2}]),
+ receive after 100 -> ok end,
+ W1
+ end,
Proc5 = spawn_opt(fun () ->
- receive Go -> ok end,
- 1 = erlang:system_info(scheduler_id),
- Port2 ! {Parent, {command, ""}},
- Port2 ! {Parent, {command, ""}},
- Port2 ! {Parent, {command, ""}},
- Port2 ! {Parent, {command, ""}},
- Port2 ! {Parent, {command, ""}},
- Port2 ! {Parent, {command, ""}},
- Port2 ! {Parent, {command, ""}},
- Port2 ! {Parent, {command, ""}},
- Port2 ! {Parent, {command, ""}},
- Port2 ! {Parent, {command, ""}}
- end, [link,{scheduler,1}]),
+ receive Go -> ok end,
+ 1 = erlang:system_info(scheduler_id),
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}}
+ end, [link,{scheduler,1}]),
receive after 100 -> ok end,
Proc5 ! Go,
wait_procs_exit([W5, Proc5]),
@@ -2292,34 +2175,34 @@ consume_timeslice(Config) when is_list(Config) ->
[{Port2, Sprt5}, {Proc5, Sproc5}] = count_pp_sched_stop([Port2, Proc5]),
?IN_RANGE(2, Sproc5, 3),
?IN_RANGE(6, Sprt5, 20),
-
+
count_pp_sched_start(),
"disabled" = port_control(Port2, $D, ""),
W6 = case SOnl of
- 1 ->
- false;
- _ ->
- W2= spawn_opt(fun () ->
- 2 = erlang:system_info(scheduler_id),
- "sleeped" = port_control(Port2, $S, "")
- end, [link,{scheduler,2}]),
- receive after 100 -> ok end,
- W2
- end,
+ 1 ->
+ false;
+ _ ->
+ W2= spawn_opt(fun () ->
+ 2 = erlang:system_info(scheduler_id),
+ "sleeped" = port_control(Port2, $S, "")
+ end, [link,{scheduler,2}]),
+ receive after 100 -> ok end,
+ W2
+ end,
Proc6 = spawn_opt(fun () ->
- receive Go -> ok end,
- 1 = erlang:system_info(scheduler_id),
- Port2 ! {Parent, {command, ""}},
- Port2 ! {Parent, {command, ""}},
- Port2 ! {Parent, {command, ""}},
- Port2 ! {Parent, {command, ""}},
- Port2 ! {Parent, {command, ""}},
- Port2 ! {Parent, {command, ""}},
- Port2 ! {Parent, {command, ""}},
- Port2 ! {Parent, {command, ""}},
- Port2 ! {Parent, {command, ""}},
- Port2 ! {Parent, {command, ""}}
- end, [link,{scheduler,1}]),
+ receive Go -> ok end,
+ 1 = erlang:system_info(scheduler_id),
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}}
+ end, [link,{scheduler,1}]),
receive after 100 -> ok end,
Proc6 ! Go,
wait_procs_exit([W6, Proc6]),
@@ -2339,19 +2222,19 @@ wait_command_msgs(_, 0) ->
ok;
wait_command_msgs(Port, N) ->
receive
- {Port, command} ->
- wait_command_msgs(Port, N-1)
+ {Port, command} ->
+ wait_command_msgs(Port, N-1)
end.
in_range(Low, Val, High) when is_integer(Low),
- is_integer(Val),
- is_integer(High),
- Low =< Val,
- Val =< High ->
+ is_integer(Val),
+ is_integer(High),
+ Low =< Val,
+ Val =< High ->
true;
in_range(Low, Val, High) when is_integer(Low),
- is_integer(Val),
- is_integer(High) ->
+ is_integer(Val),
+ is_integer(High) ->
false.
count_pp_sched_start() ->
@@ -2364,7 +2247,7 @@ count_pp_sched_stop(Ps) ->
PNs = lists:map(fun (P) -> {P, 0} end, Ps),
receive {trace_delivered, all, Td} -> ok end,
Res = count_proc_sched(Ps, PNs),
- ?t:format("Scheduling counts: ~p~n", [Res]),
+ io:format("Scheduling counts: ~p~n", [Res]),
erlang:display({scheduling_counts, Res}),
Res.
@@ -2377,22 +2260,22 @@ do_inc_pn(P, [PN|PNs]) ->
inc_pn(P, PNs) ->
try
- do_inc_pn(P, PNs)
+ do_inc_pn(P, PNs)
catch
- throw:undefined -> PNs
+ throw:undefined -> PNs
end.
count_proc_sched(Ps, PNs) ->
receive
- TT when element(1, TT) == trace, element(3, TT) == in ->
-% erlang:display(TT),
- count_proc_sched(Ps, inc_pn(element(2, TT), PNs));
- TT when element(1, TT) == trace, element(3, TT) == out ->
- count_proc_sched(Ps, PNs)
+ TT when element(1, TT) == trace, element(3, TT) == in ->
+ % erlang:display(TT),
+ count_proc_sched(Ps, inc_pn(element(2, TT), PNs));
+ TT when element(1, TT) == trace, element(3, TT) == out ->
+ count_proc_sched(Ps, PNs)
after 0 ->
- PNs
+ PNs
end.
-
+
a_test(Config) when is_list(Config) ->
check_io_debug().
@@ -2405,13 +2288,35 @@ z_test(Config) when is_list(Config) ->
check_io_debug() ->
get_stable_check_io_info(),
- {NoErrorFds, NoUsedFds, NoDrvSelStructs, NoDrvEvStructs}
- = erts_debug:get_internal_state(check_io_debug),
+ {NoErrorFds, NoUsedFds, NoDrvSelStructs, NoDrvEvStructs} = CheckIoDebug
+ = erts_debug:get_internal_state(check_io_debug),
+ HasGetHost = has_gethost(),
+ ct:log("check_io_debug: ~p~n"
+ "HasGetHost: ~p",[CheckIoDebug, HasGetHost]),
0 = NoErrorFds,
- NoUsedFds = NoDrvSelStructs,
+ if
+ NoUsedFds == NoDrvSelStructs ->
+ ok;
+ HasGetHost andalso (NoUsedFds == (NoDrvSelStructs - 1)) ->
+ %% If the inet_gethost port is alive, we may have
+ %% one extra used fd that is not selected on
+ ok
+ end,
0 = NoDrvEvStructs,
ok.
+has_gethost() ->
+ has_gethost(erlang:ports()).
+has_gethost([P|T]) ->
+ case erlang:port_info(P, name) of
+ {name,"inet_gethost"++_} ->
+ true;
+ _ ->
+ has_gethost(T)
+ end;
+has_gethost([]) ->
+ false.
+
%flush_msgs() ->
% receive
% M ->
@@ -2426,26 +2331,26 @@ wait_procs_exit([]) ->
wait_procs_exit([P|Ps]) when is_pid(P) ->
Mon = erlang:monitor(process, P),
receive
- {'DOWN', Mon, process, P, _} ->
- wait_procs_exit(Ps)
+ {'DOWN', Mon, process, P, _} ->
+ wait_procs_exit(Ps)
end;
wait_procs_exit([_|Ps]) ->
wait_procs_exit(Ps).
get_port_msg(Port, Timeout) ->
receive
- {Port, What} ->
- {msg, What}
+ {Port, What} ->
+ {msg, What}
after Timeout ->
- no_msg
+ no_msg
end.
wait_until(Fun) ->
case Fun() of
- true -> ok;
- false ->
- receive after 100 -> ok end,
- wait_until(Fun)
+ true -> ok;
+ false ->
+ receive after 100 -> ok end,
+ wait_until(Fun)
end.
drv_vsn_str2tup(Str) ->
@@ -2476,11 +2381,11 @@ transform_bins(_Transform, Other) -> Other.
make_sub_binaries(Term) ->
MakeSub = fun(Bin0) ->
- Bin1 = <<243:8,0:3,Bin0/binary,31:5,19:8>>,
- Sz = size(Bin0),
- <<243:8,0:3,Bin:Sz/binary,31:5,19:8>> = id(Bin1),
- Bin
- end,
+ Bin1 = <<243:8,0:3,Bin0/binary,31:5,19:8>>,
+ Sz = size(Bin0),
+ <<243:8,0:3,Bin:Sz/binary,31:5,19:8>> = id(Bin1),
+ Bin
+ end,
transform_bins(MakeSub, Term).
id(I) -> I.
@@ -2512,24 +2417,17 @@ random_char() ->
uniform(256) - 1.
uniform(N) ->
- case get(random_seed) of
- undefined ->
- {X, Y, Z} = time(),
- random:seed(X, Y, Z);
- _ ->
- ok
- end,
- random:uniform(N).
+ rand:uniform(N).
erl_millisecs() ->
erl_millisecs(erlang:monotonic_time()).
erl_millisecs(MonotonicTime) ->
- (1000*MonotonicTime)/erlang:convert_time_unit(1,seconds,native).
+ (1000*MonotonicTime)/erlang:convert_time_unit(1,second,native).
%% Start/stop drivers.
start_driver(Config, Name, Binary) ->
- Path = ?config(data_dir, Config),
+ Path = proplists:get_value(data_dir, Config),
erl_ddll:start(),
%% Load the driver
@@ -2537,31 +2435,31 @@ start_driver(Config, Name, Binary) ->
%% open port.
case Binary of
- true ->
- open_port({spawn, Name}, [binary]);
- false ->
- open_port({spawn, Name}, [])
+ true ->
+ open_port({spawn, Name}, [binary]);
+ false ->
+ open_port({spawn, Name}, [])
end.
stop_driver(Port, Name) ->
- ?line true = erlang:port_close(Port),
+ true = erlang:port_close(Port),
receive
- {Port,Message} ->
- ?t:fail({strange_message_from_port,Message})
+ {Port,Message} ->
+ ct:fail({strange_message_from_port,Message})
after 0 ->
- ok
+ ok
end,
%% Unload the driver.
ok = erl_ddll:unload_driver(Name),
- ?line ok = erl_ddll:stop().
+ ok = erl_ddll:stop().
load_driver(Dir, Driver) ->
case erl_ddll:load_driver(Dir, Driver) of
- ok -> ok;
- {error, Error} = Res ->
- io:format("~s\n", [erl_ddll:format_error(Error)]),
- Res
+ ok -> ok;
+ {error, Error} = Res ->
+ io:format("~s\n", [erl_ddll:format_error(Error)]),
+ Res
end.
sleep() ->
@@ -2576,50 +2474,50 @@ sleep(Ms) when is_integer(Ms), Ms >= 0 ->
start_node(Config) when is_list(Config) ->
Pa = filename:dirname(code:which(?MODULE)),
Name = list_to_atom(atom_to_list(?MODULE)
- ++ "-"
- ++ atom_to_list(?config(testcase, Config))
- ++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
- ++ "-"
- ++ integer_to_list(erlang:unique_integer([positive]))),
- ?t:start_node(Name, slave, [{args, "-pa "++Pa}]).
+ ++ "-"
+ ++ atom_to_list(proplists:get_value(testcase, Config))
+ ++ "-"
+ ++ integer_to_list(erlang:system_time(second))
+ ++ "-"
+ ++ integer_to_list(erlang:unique_integer([positive]))),
+ test_server:start_node(Name, slave, [{args, "-pa "++Pa}]).
stop_node(Node) ->
- ?t:stop_node(Node).
+ test_server:stop_node(Node).
wait_deallocations() ->
try
- erts_debug:set_internal_state(wait, deallocations)
+ erts_debug:set_internal_state(wait, deallocations)
catch error:undef ->
- erts_debug:set_internal_state(available_internal_state, true),
- wait_deallocations()
+ erts_debug:set_internal_state(available_internal_state, true),
+ wait_deallocations()
end.
driver_alloc_size() ->
case erlang:system_info(smp_support) of
- true ->
- ok;
- false ->
- %% driver_alloc also used by elements in lock-free queues,
- %% give these some time to be deallocated...
- receive after 100 -> ok end
+ true ->
+ ok;
+ false ->
+ %% driver_alloc also used by elements in lock-free queues,
+ %% give these some time to be deallocated...
+ receive after 100 -> ok end
end,
wait_deallocations(),
case erlang:system_info({allocator_sizes, driver_alloc}) of
- false ->
- undefined;
- MemInfo ->
- CS = lists:foldl(
- fun ({instance, _, L}, Acc) ->
- {value,{_,MBCS}} = lists:keysearch(mbcs, 1, L),
- {value,{_,SBCS}} = lists:keysearch(sbcs, 1, L),
- [MBCS,SBCS | Acc]
- end,
- [],
- MemInfo),
- lists:foldl(
- fun(L, Sz0) ->
- {value,{_,Sz,_,_}} = lists:keysearch(blocks_size, 1, L),
- Sz0+Sz
- end, 0, CS)
+ false ->
+ undefined;
+ MemInfo ->
+ CS = lists:foldl(
+ fun ({instance, _, L}, Acc) ->
+ {value,{_,MBCS}} = lists:keysearch(mbcs, 1, L),
+ {value,{_,SBCS}} = lists:keysearch(sbcs, 1, L),
+ [MBCS,SBCS | Acc]
+ end,
+ [],
+ MemInfo),
+ lists:foldl(
+ fun(L, Sz0) ->
+ {value,{_,Sz,_,_}} = lists:keysearch(blocks_size, 1, L),
+ Sz0+Sz
+ end, 0, CS)
end.
diff --git a/erts/emulator/test/driver_SUITE_data/async_blast_drv.c b/erts/emulator/test/driver_SUITE_data/async_blast_drv.c
index a1008afcae..1432bc42c1 100644
--- a/erts/emulator/test/driver_SUITE_data/async_blast_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/async_blast_drv.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2011-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2011-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/driver_SUITE_data/consume_timeslice_drv.c b/erts/emulator/test/driver_SUITE_data/consume_timeslice_drv.c
index 192ac02d3e..142ae46247 100644
--- a/erts/emulator/test/driver_SUITE_data/consume_timeslice_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/consume_timeslice_drv.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2012-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2012-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c b/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c
index e2221b9e17..d87c2bec93 100644
--- a/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2007-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2007-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c b/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c
index fdf8e4c0ad..37cb93fb3a 100644
--- a/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2011-2014. All Rights Reserved.
+ * Copyright Ericsson AB 2011-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/driver_SUITE_data/thr_free_drv.c b/erts/emulator/test/driver_SUITE_data/thr_free_drv.c
index 54205f190e..48fe5fa435 100644
--- a/erts/emulator/test/driver_SUITE_data/thr_free_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/thr_free_drv.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2011. All Rights Reserved.
+ * Copyright Ericsson AB 2011-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/driver_SUITE_data/thr_msg_blast_drv.c b/erts/emulator/test/driver_SUITE_data/thr_msg_blast_drv.c
index f7a7cc2b8e..56183c9484 100644
--- a/erts/emulator/test/driver_SUITE_data/thr_msg_blast_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/thr_msg_blast_drv.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2012. All Rights Reserved.
+ * Copyright Ericsson AB 2012-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/driver_SUITE_data/timer_drv.c b/erts/emulator/test/driver_SUITE_data/timer_drv.c
index 57538e0d57..c3ce3b6e49 100644
--- a/erts/emulator/test/driver_SUITE_data/timer_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/timer_drv.c
@@ -1,5 +1,13 @@
#include <stdio.h>
#include "erl_driver.h"
+#ifdef __WIN32__
+# include <windows.h>
+#else
+# include <sys/time.h>
+# include <sys/types.h>
+# include <sys/select.h>
+# include <unistd.h>
+#endif
#define get_int32(s) ((((unsigned char*) (s))[0] << 24) | \
(((unsigned char*) (s))[1] << 16) | \
@@ -17,6 +25,7 @@ static ErlDrvData timer_start(ErlDrvPort, char*);
static void timer_stop(ErlDrvData);
static void timer_read(ErlDrvData, char*, ErlDrvSizeT);
static void timer(ErlDrvData);
+static void ms_sleep(int ms);
static ErlDrvEntry timer_driver_entry =
{
@@ -75,9 +84,7 @@ static void timer_read(ErlDrvData p, char *buf, ErlDrvSizeT len)
reply[0] = CANCELLED;
driver_output(port, reply, 1);
} else if (buf[0] == DELAY_START_TIMER) {
-#ifndef __WIN32__
- sleep(1);
-#endif
+ ms_sleep(1000);
driver_set_timer(port, get_int32(buf + 1));
}
}
@@ -95,3 +102,34 @@ static void timer(ErlDrvData port)
reply[0] = TIMER;
driver_output((ErlDrvPort)port, reply, 1);
}
+
+static void
+ms_sleep(int ms)
+{
+ /* Important that we do not return too early... */
+ ErlDrvTime time, timeout_time;
+
+ time = erl_drv_monotonic_time(ERL_DRV_USEC);
+
+ timeout_time = time + ((ErlDrvTime) ms)*1000;
+
+ while (time < timeout_time) {
+ ErlDrvTime timeout = timeout_time - time;
+
+#ifdef __WIN32__
+ Sleep((DWORD) (timeout / 1000));
+#else
+ {
+ struct timeval tv;
+
+ tv.tv_sec = (long) timeout / (1000*1000);
+ tv.tv_usec = (long) timeout % (1000*1000);
+
+ select(0, NULL, NULL, NULL, &tv);
+ }
+#endif
+
+ time = erl_drv_monotonic_time(ERL_DRV_USEC);
+ }
+
+}
diff --git a/erts/emulator/test/efile_SUITE.erl b/erts/emulator/test/efile_SUITE.erl
index 4d8d89db9b..6bb8487c4e 100644
--- a/erts/emulator/test/efile_SUITE.erl
+++ b/erts/emulator/test/efile_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -18,34 +18,18 @@
%% %CopyrightEnd%
-module(efile_SUITE).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2]).
+-export([all/0, suite/0]).
-export([iter_max_files/1, async_dist/1]).
-export([do_iter_max_files/2, do_async_dist/1]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[iter_max_files, async_dist].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
do_async_dist(Dir) ->
X = 100,
AT = erlang:system_info(thread_pool_size),
@@ -70,59 +54,58 @@ file_keys(Dir,Num,FdList,FnList) ->
Name = "dummy"++integer_to_list(Num),
FN = filename:join([Dir,Name]),
case file:open(FN,[write,raw]) of
- {ok,FD} ->
- {file_descriptor,prim_file,{Port,_}} = FD,
- <<X:32/integer-big>> =
- iolist_to_binary(erlang:port_control(Port,$K,[])),
- [X | file_keys(Dir,Num-1,[FD|FdList],[FN|FnList])];
- {error,_} ->
- % Try freeing up FD's if there are any
- case FdList of
- [] ->
- exit({cannot_open_file,FN});
- _ ->
- [ file:close(FD) || FD <- FdList ],
- [ file:delete(F) || F <- FnList ],
- file_keys(Dir,Num,[],[])
- end
+ {ok,FD} ->
+ {file_descriptor,prim_file,{Port,_}} = FD,
+ <<X:32/integer-big>> =
+ iolist_to_binary(erlang:port_control(Port,$K,[])),
+ [X | file_keys(Dir,Num-1,[FD|FdList],[FN|FnList])];
+ {error,_} ->
+ % Try freeing up FD's if there are any
+ case FdList of
+ [] ->
+ exit({cannot_open_file,FN});
+ _ ->
+ [ file:close(FD) || FD <- FdList ],
+ [ file:delete(F) || F <- FnList ],
+ file_keys(Dir,Num,[],[])
+ end
end.
-async_dist(doc) ->
- "Check that the distribution of files over async threads is fair";
+%% Check that the distribution of files over async threads is fair
async_dist(Config) when is_list(Config) ->
- DataDir = ?config(data_dir,Config),
+ DataDir = proplists:get_value(data_dir,Config),
TestFile = filename:join(DataDir, "existing_file"),
Dir = filename:dirname(code:which(?MODULE)),
AsyncSizes = [7,10,100,255,256,64,63,65],
Max = 0.5,
lists:foreach(fun(Size) ->
- {ok,Node} =
- test_server:start_node
- (test_iter_max_files,slave,
- [{args,
- "+A "++integer_to_list(Size)++
- " -pa " ++ Dir}]),
- {Distr,SD} = rpc:call(Node,?MODULE,do_async_dist,
- [DataDir]),
- test_server:stop_node(Node),
- if
- SD > Max ->
- io:format("Bad async queue distribution for "
- "~p async threads:~n"
- " Standard deviation is ~p~n"
- " Key distribution:~n ~lp~n",
- [Size,SD,Distr]),
- exit({bad_async_dist,Size,SD,Distr});
- true ->
- io:format("OK async queue distribution for "
- "~p async threads:~n"
- " Standard deviation is ~p~n"
- " Key distribution:~n ~lp~n",
- [Size,SD,Distr]),
- ok
- end
- end, AsyncSizes),
+ {ok,Node} =
+ test_server:start_node
+ (test_iter_max_files,slave,
+ [{args,
+ "+A "++integer_to_list(Size)++
+ " -pa " ++ Dir}]),
+ {Distr,SD} = rpc:call(Node,?MODULE,do_async_dist,
+ [DataDir]),
+ test_server:stop_node(Node),
+ if
+ SD > Max ->
+ io:format("Bad async queue distribution for "
+ "~p async threads:~n"
+ " Standard deviation is ~p~n"
+ " Key distribution:~n ~lp~n",
+ [Size,SD,Distr]),
+ exit({bad_async_dist,Size,SD,Distr});
+ true ->
+ io:format("OK async queue distribution for "
+ "~p async threads:~n"
+ " Standard deviation is ~p~n"
+ " Key distribution:~n ~lp~n",
+ [Size,SD,Distr]),
+ ok
+ end
+ end, AsyncSizes),
ok.
%%
@@ -130,54 +113,53 @@ async_dist(Config) when is_list(Config) ->
%% that we get the same number of files every time.
%%
-iter_max_files(suite) -> [];
iter_max_files(Config) when is_list(Config) ->
- DataDir = ?config(data_dir,Config),
+ DataDir = proplists:get_value(data_dir,Config),
TestFile = filename:join(DataDir, "existing_file"),
N = 10,
%% Run on a different node in order to set the max ports
Dir = filename:dirname(code:which(?MODULE)),
{ok,Node} = test_server:start_node(test_iter_max_files,slave,
- [{args,"+Q 1524 -pa " ++ Dir}]),
+ [{args,"+Q 1524 -pa " ++ Dir}]),
L = rpc:call(Node,?MODULE,do_iter_max_files,[N, TestFile]),
test_server:stop_node(Node),
io:format("Number of files opened in each test:~n~w\n", [L]),
all_equal(L),
Head = hd(L),
if Head >= 2 -> ok;
- true -> ?line test_server:fail(too_few_files)
+ true -> ct:fail(too_few_files)
end,
{comment, "Max files: " ++ integer_to_list(hd(L))}.
do_iter_max_files(N, Name) when N > 0 ->
- ?line [max_files(Name)| do_iter_max_files(N-1, Name)];
+ [max_files(Name)| do_iter_max_files(N-1, Name)];
do_iter_max_files(_, _) ->
[].
all_equal([E, E| T]) ->
- ?line all_equal([E| T]);
+ all_equal([E| T]);
all_equal([_]) ->
ok;
all_equal([]) ->
ok.
-
+
max_files(Name) ->
- ?line Fds = open_files(Name),
- ?line N = length(Fds),
- ?line close_files(Fds),
+ Fds = open_files(Name),
+ N = length(Fds),
+ close_files(Fds),
N.
close_files([Fd| Fds]) ->
- ?line file:close(Fd),
- ?line close_files(Fds);
+ file:close(Fd),
+ close_files(Fds);
close_files([]) ->
ok.
open_files(Name) ->
- ?line case file:open(Name, [read,raw]) of
- {ok, Fd} ->
- [Fd| open_files(Name)];
- {error, _Reason} ->
-% io:format("Error reason: ~p", [_Reason]),
- []
- end.
+ case file:open(Name, [read,raw]) of
+ {ok, Fd} ->
+ [Fd| open_files(Name)];
+ {error, _Reason} ->
+ % io:format("Error reason: ~p", [_Reason]),
+ []
+ end.
diff --git a/erts/emulator/test/emulator.spec.ose b/erts/emulator/test/emulator.spec.ose
deleted file mode 100644
index 9f494609d9..0000000000
--- a/erts/emulator/test/emulator.spec.ose
+++ /dev/null
@@ -1,2 +0,0 @@
-{topcase, {dir, "../emulator_test"}}.
-{skip, {obsolete_SUITE, "Not on ose"}}.
diff --git a/erts/emulator/test/emulator_smoke.spec b/erts/emulator/test/emulator_smoke.spec
index 3219aeb823..b2d0de8835 100644
--- a/erts/emulator/test/emulator_smoke.spec
+++ b/erts/emulator/test/emulator_smoke.spec
@@ -1,3 +1,9 @@
-{suites,"../emulator_test",[smoke_test_SUITE,time_SUITE]}.
-{cases,"../emulator_test",crypto_SUITE,[t_md5]}.
-{cases,"../emulator_test",float_SUITE,[fpe,cmp_integer]}. \ No newline at end of file
+{define,'Dir',"../emulator_test"}.
+{suites,'Dir',[smoke_test_SUITE]}.
+{suites,'Dir',[time_SUITE]}.
+{skip_cases,'Dir',time_SUITE,
+ [univ_to_local,local_to_univ],"Depends on CET timezone"}.
+{skip_cases,'Dir',time_SUITE,
+ [consistency],"Not reliable in October and March"}.
+{cases,'Dir',crypto_SUITE,[t_md5]}.
+{cases,'Dir',float_SUITE,[fpe,cmp_integer]}.
diff --git a/erts/emulator/test/erl_drv_thread_SUITE.erl b/erts/emulator/test/erl_drv_thread_SUITE.erl
index 2cd569ce4f..f99c151936 100644
--- a/erts/emulator/test/erl_drv_thread_SUITE.erl
+++ b/erts/emulator/test/erl_drv_thread_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,12 +20,11 @@
-module(erl_drv_thread_SUITE).
-author('[email protected]').
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2]).
+-export([all/0, suite/0]).
-export([basic/1, rwlock/1, tsd/1]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-define(DEFAULT_TIMETRAP_SECS, 240).
@@ -34,38 +33,17 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[basic, rwlock, tsd].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%
%% Testcases %%
%% %%
-basic(suite) -> [];
-basic(doc) -> [];
-basic(Cfg) -> ?line drv_case(Cfg, basic).
+basic(Cfg) -> drv_case(Cfg, basic).
-rwlock(suite) -> [];
-rwlock(doc) -> [];
-rwlock(Cfg) -> ?line drv_case(Cfg, rwlock).
+rwlock(Cfg) -> drv_case(Cfg, rwlock).
-tsd(suite) -> [];
-tsd(doc) -> [];
-tsd(Cfg) -> ?line drv_case(Cfg, tsd).
+tsd(Cfg) -> drv_case(Cfg, tsd).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%
@@ -81,58 +59,54 @@ drv_case(Config, CaseName, Command) when is_list(Command) ->
drv_case(Config, CaseName, Command, ?DEFAULT_TIMETRAP_SECS).
drv_case(Config, CaseName, TimeTrap, Command) when is_list(Command),
- is_integer(TimeTrap) ->
+ is_integer(TimeTrap) ->
drv_case(Config, CaseName, Command, TimeTrap);
drv_case(Config, CaseName, Command, TimeTrap) when is_list(Config),
- is_atom(CaseName),
- is_list(Command),
- is_integer(TimeTrap) ->
- case ?t:os_type() of
- {Family, _} when Family == unix; Family == win32 ->
- ?line run_drv_case(Config, CaseName, Command, TimeTrap);
- SkipOs ->
- ?line {skipped,
- lists:flatten(["Not run on "
- | io_lib:format("~p",[SkipOs])])}
+ is_atom(CaseName),
+ is_list(Command),
+ is_integer(TimeTrap) ->
+ case os:type() of
+ {Family, _} when Family == unix; Family == win32 ->
+ run_drv_case(Config, CaseName, Command, TimeTrap);
+ SkipOs ->
+ {skipped, lists:flatten(["Not run on " | io_lib:format("~p",[SkipOs])])}
end.
run_drv_case(Config, CaseName, Command, TimeTrap) ->
- ?line Dog = test_server:timetrap(test_server:seconds(TimeTrap)),
- ?line DataDir = ?config(data_dir,Config),
+ ct:timetrap({seconds, TimeTrap}),
+ DataDir = proplists:get_value(data_dir,Config),
case erl_ddll:load_driver(DataDir, CaseName) of
- ok -> ok;
- {error, Error} ->
- io:format("~s\n", [erl_ddll:format_error(Error)]),
- ?line ?t:fail()
+ ok -> ok;
+ {error, Error} ->
+ ct:fail(erl_ddll:format_error(Error))
+ end,
+ Port = open_port({spawn, atom_to_list(CaseName)}, []),
+ true = is_port(Port),
+ Port ! {self(), {command, Command}},
+ Result = receive_drv_result(Port, CaseName),
+ Port ! {self(), close},
+ receive
+ {Port, closed} ->
+ ok
end,
- ?line Port = open_port({spawn, atom_to_list(CaseName)}, []),
- ?line true = is_port(Port),
- ?line Port ! {self(), {command, Command}},
- ?line Result = receive_drv_result(Port, CaseName),
- ?line Port ! {self(), close},
- ?line receive
- {Port, closed} ->
- ok
- end,
- ?line ok = erl_ddll:unload_driver(CaseName),
- ?line test_server:timetrap_cancel(Dog),
- ?line Result.
+ ok = erl_ddll:unload_driver(CaseName),
+ Result.
receive_drv_result(Port, CaseName) ->
- ?line receive
- {print, Port, CaseName, Str} ->
- ?line ?t:format("~s", [Str]),
- ?line receive_drv_result(Port, CaseName);
- {'EXIT', Port, Error} ->
- ?line ?t:fail(Error);
- {'EXIT', error, Error} ->
- ?line ?t:fail(Error);
- {failed, Port, CaseName, Comment} ->
- ?line ?t:fail(Comment);
- {skipped, Port, CaseName, Comment} ->
- ?line {skipped, Comment};
- {succeeded, Port, CaseName, ""} ->
- ?line succeeded;
- {succeeded, Port, CaseName, Comment} ->
- ?line {comment, Comment}
- end.
+ receive
+ {print, Port, CaseName, Str} ->
+ io:format("~s", [Str]),
+ receive_drv_result(Port, CaseName);
+ {'EXIT', Port, Error} ->
+ ct:fail(Error);
+ {'EXIT', error, Error} ->
+ ct:fail(Error);
+ {failed, Port, CaseName, Comment} ->
+ ct:fail(Comment);
+ {skipped, Port, CaseName, Comment} ->
+ {skipped, Comment};
+ {succeeded, Port, CaseName, ""} ->
+ succeeded;
+ {succeeded, Port, CaseName, Comment} ->
+ {comment, Comment}
+ end.
diff --git a/erts/emulator/test/erl_link_SUITE.erl b/erts/emulator/test/erl_link_SUITE.erl
index a7a45046ca..89e1aefb50 100644
--- a/erts/emulator/test/erl_link_SUITE.erl
+++ b/erts/emulator/test/erl_link_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -29,24 +29,23 @@
-author('[email protected]').
%-define(line_trace, 1).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2]).
+-export([all/0, suite/0, init_per_suite/1, end_per_suite/1]).
% Test cases
-export([links/1,
- dist_links/1,
- monitor_nodes/1,
- process_monitors/1,
- dist_process_monitors/1,
- busy_dist_port_monitor/1,
- busy_dist_port_link/1,
- otp_5772_link/1,
- otp_5772_dist_link/1,
- otp_5772_monitor/1,
- otp_5772_dist_monitor/1,
- otp_7946/1]).
+ dist_links/1,
+ monitor_nodes/1,
+ process_monitors/1,
+ dist_process_monitors/1,
+ busy_dist_port_monitor/1,
+ busy_dist_port_link/1,
+ otp_5772_link/1,
+ otp_5772_dist_link/1,
+ otp_5772_monitor/1,
+ otp_5772_dist_monitor/1,
+ otp_7946/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
@@ -65,21 +64,20 @@
-record(erl_link, {type = ?LINK_UNDEF,
- pid = [],
- targets = []}).
+ pid = [],
+ targets = []}).
% This is to be kept in sync with erl_bif_info.c (make_monitor_list)
--record(erl_monitor, {
- type, % MON_ORIGIN or MON_TARGET (1 or 3)
- ref,
- pid, % Process or nodename
- name = [] % registered name or []
- }).
+-record(erl_monitor, {type, % MON_ORIGIN or MON_TARGET (1 or 3)
+ ref,
+ pid, % Process or nodename
+ name = []}). % registered name or []
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 1}}].
all() ->
[links, dist_links, monitor_nodes, process_monitors,
@@ -87,8 +85,15 @@ all() ->
busy_dist_port_link, otp_5772_link, otp_5772_dist_link,
otp_5772_monitor, otp_5772_dist_monitor, otp_7946].
-groups() ->
- [].
+init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
+ case catch erts_debug:get_internal_state(available_internal_state) of
+ true -> ok;
+ _ -> erts_debug:set_internal_state(available_internal_state, true)
+ end,
+ Config.
+
+end_per_testcase(_Func, _Config) ->
+ ok.
init_per_suite(Config) ->
Config.
@@ -96,419 +101,397 @@ init_per_suite(Config) ->
end_per_suite(_Config) ->
catch erts_debug:set_internal_state(available_internal_state, false).
-init_per_group(_GroupName, Config) ->
- Config.
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-links(doc) -> ["Tests node local links"];
-links(suite) -> [];
+%% Tests node local links
links(Config) when is_list(Config) ->
- ?line common_link_test(node(), node()),
- ?line true = link(self()),
- ?line [] = find_erl_link(self(), ?LINK_PID, self()),
- ?line true = unlink(self()),
- ?line ok.
-
-dist_links(doc) -> ["Tests distributed links"];
-dist_links(suite) -> [];
+ common_link_test(node(), node()),
+ true = link(self()),
+ [] = find_erl_link(self(), ?LINK_PID, self()),
+ true = unlink(self()),
+ ok.
+
+%% Tests distributed links
dist_links(Config) when is_list(Config) ->
- ?line [NodeName] = get_names(1, dist_link),
- ?line {ok, Node} = start_node(NodeName),
- ?line common_link_test(node(), Node),
- ?line TP4 = spawn(?MODULE, test_proc, []),
- ?line TP5 = spawn(?MODULE, test_proc, []),
- ?line TP6 = spawn(Node, ?MODULE, test_proc, []),
- ?line true = tp_call(TP6, fun() -> link(TP4) end),
- ?line check_link(TP4, TP6),
- ?line true = tp_call(TP5,
- fun() ->
- process_flag(trap_exit,true),
- link(TP6)
- end),
- ?line check_link(TP5, TP6),
- ?line rpc:cast(Node, erlang, halt, []),
- ?line wait_until(fun () -> ?line is_proc_dead(TP4) end),
- ?line check_unlink(TP4, TP6),
- ?line true = tp_call(TP5,
- fun() ->
- receive
- {'EXIT', TP6, noconnection} ->
- true
- end
- end),
- ?line check_unlink(TP5, TP6),
- ?line tp_cast(TP5, fun() -> exit(normal) end),
- ?line ok.
+ [NodeName] = get_names(1, dist_link),
+ {ok, Node} = start_node(NodeName),
+ common_link_test(node(), Node),
+ TP4 = spawn(?MODULE, test_proc, []),
+ TP5 = spawn(?MODULE, test_proc, []),
+ TP6 = spawn(Node, ?MODULE, test_proc, []),
+ true = tp_call(TP6, fun() -> link(TP4) end),
+ check_link(TP4, TP6),
+ true = tp_call(TP5,
+ fun() ->
+ process_flag(trap_exit,true),
+ link(TP6)
+ end),
+ check_link(TP5, TP6),
+ rpc:cast(Node, erlang, halt, []),
+ wait_until(fun () -> is_proc_dead(TP4) end),
+ check_unlink(TP4, TP6),
+ true = tp_call(TP5,
+ fun() ->
+ receive
+ {'EXIT', TP6, noconnection} ->
+ true
+ end
+ end),
+ check_unlink(TP5, TP6),
+ tp_cast(TP5, fun() -> exit(normal) end),
+ ok.
common_link_test(NodeA, NodeB) ->
- ?line TP1 = spawn(NodeA, ?MODULE, test_proc, []),
- ?line check_unlink(TP1, self()),
- ?line TP2 = tp_call(TP1,
- fun () ->
- spawn_link(NodeB, ?MODULE, test_proc, [])
- end),
- ?line check_link(TP1, TP2),
- ?line true = tp_call(TP2, fun() -> unlink(TP1) end),
- ?line check_unlink(TP1, TP2),
- ?line true = tp_call(TP2, fun() -> link(TP1) end),
- ?line check_link(TP1, TP2),
- ?line false = tp_call(TP2, fun() -> process_flag(trap_exit, true) end),
- ?line tp_cast(TP1, fun () -> exit(died) end),
- ?line true = tp_call(TP2, fun() ->
- receive
- {'EXIT', TP1, died} ->
- true
- end
- end),
- ?line check_unlink(TP1, TP2),
- ?line TP3 = tp_call(TP2,
- fun () ->
- spawn_link(NodeA, ?MODULE, test_proc, [])
- end),
- ?line check_link(TP3, TP2),
- ?line tp_cast(TP2, fun() -> exit(died) end),
- ?line wait_until(fun () -> ?line is_proc_dead(TP3) end),
- ?line check_unlink(TP3, TP2),
- ?line ok.
-
-monitor_nodes(doc) -> ["Tests monitor of nodes"];
-monitor_nodes(suite) -> [];
+ TP1 = spawn(NodeA, ?MODULE, test_proc, []),
+ check_unlink(TP1, self()),
+ TP2 = tp_call(TP1,
+ fun () ->
+ spawn_link(NodeB, ?MODULE, test_proc, [])
+ end),
+ check_link(TP1, TP2),
+ true = tp_call(TP2, fun() -> unlink(TP1) end),
+ check_unlink(TP1, TP2),
+ true = tp_call(TP2, fun() -> link(TP1) end),
+ check_link(TP1, TP2),
+ false = tp_call(TP2, fun() -> process_flag(trap_exit, true) end),
+ tp_cast(TP1, fun () -> exit(died) end),
+ true = tp_call(TP2, fun() ->
+ receive
+ {'EXIT', TP1, died} ->
+ true
+ end
+ end),
+ check_unlink(TP1, TP2),
+ TP3 = tp_call(TP2,
+ fun () ->
+ spawn_link(NodeA, ?MODULE, test_proc, [])
+ end),
+ check_link(TP3, TP2),
+ tp_cast(TP2, fun() -> exit(died) end),
+ wait_until(fun () -> is_proc_dead(TP3) end),
+ check_unlink(TP3, TP2),
+ ok.
+
+%% Tests monitor of nodes
monitor_nodes(Config) when is_list(Config) ->
- ?line [An, Bn, Cn, Dn] = get_names(4, dist_link),
- ?line {ok, A} = start_node(An),
- ?line {ok, B} = start_node(Bn),
- ?line C = list_to_atom(lists:concat([Cn, "@", hostname()])),
- ?line D = list_to_atom(lists:concat([Dn, "@", hostname()])),
- ?line 0 = no_of_monitor_node(self(), A),
- ?line 0 = no_of_monitor_node(self(), B),
- ?line monitor_node(A, true),
- ?line monitor_node(B, true),
- ?line monitor_node(D, true),
- ?line monitor_node(D, true),
+ [An, Bn, Cn, Dn] = get_names(4, dist_link),
+ {ok, A} = start_node(An),
+ {ok, B} = start_node(Bn),
+ C = list_to_atom(lists:concat([Cn, "@", hostname()])),
+ D = list_to_atom(lists:concat([Dn, "@", hostname()])),
+ 0 = no_of_monitor_node(self(), A),
+ 0 = no_of_monitor_node(self(), B),
+ monitor_node(A, true),
+ monitor_node(B, true),
+ monitor_node(D, true),
+ monitor_node(D, true),
%% Has been known to crash the emulator.
- ?line {memory,_} = process_info(self(), memory),
-
- ?line monitor_node(A, false),
- ?line monitor_node(B, true),
- ?line monitor_node(C, true),
- ?line monitor_node(C, false),
- ?line monitor_node(C, true),
- ?line monitor_node(B, true),
- ?line monitor_node(A, false),
- ?line monitor_node(B, true),
- ?line monitor_node(B, false),
- ?line monitor_node(A, true),
- ?line check_monitor_node(self(), A, 1),
- ?line check_monitor_node(self(), B, 3),
- ?line check_monitor_node(self(), C, 0),
- ?line check_monitor_node(self(), D, 0),
- ?line receive {nodedown, C} -> ok end,
- ?line receive {nodedown, C} -> ok end,
- ?line receive {nodedown, C} -> ok end,
- ?line receive {nodedown, D} -> ok end,
- ?line receive {nodedown, D} -> ok end,
- ?line stop_node(A),
- ?line receive {nodedown, A} -> ok end,
- ?line check_monitor_node(self(), A, 0),
- ?line check_monitor_node(self(), B, 3),
- ?line stop_node(B),
- ?line receive {nodedown, B} -> ok end,
- ?line receive {nodedown, B} -> ok end,
- ?line receive {nodedown, B} -> ok end,
- ?line check_monitor_node(self(), B, 0),
- ?line receive
- {nodedown, X} ->
- ?line ?t:fail({unexpected_nodedown, X})
- after 0 ->
- ?line ok
- end,
- ?line ok.
-
-
-process_monitors(doc) -> ["Tests node local process monitors"];
-process_monitors(suite) -> [];
+ {memory,_} = process_info(self(), memory),
+
+ monitor_node(A, false),
+ monitor_node(B, true),
+ monitor_node(C, true),
+ monitor_node(C, false),
+ monitor_node(C, true),
+ monitor_node(B, true),
+ monitor_node(A, false),
+ monitor_node(B, true),
+ monitor_node(B, false),
+ monitor_node(A, true),
+ check_monitor_node(self(), A, 1),
+ check_monitor_node(self(), B, 3),
+ check_monitor_node(self(), C, 0),
+ check_monitor_node(self(), D, 0),
+ receive {nodedown, C} -> ok end,
+ receive {nodedown, C} -> ok end,
+ receive {nodedown, C} -> ok end,
+ receive {nodedown, D} -> ok end,
+ receive {nodedown, D} -> ok end,
+ stop_node(A),
+ receive {nodedown, A} -> ok end,
+ check_monitor_node(self(), A, 0),
+ check_monitor_node(self(), B, 3),
+ stop_node(B),
+ receive {nodedown, B} -> ok end,
+ receive {nodedown, B} -> ok end,
+ receive {nodedown, B} -> ok end,
+ check_monitor_node(self(), B, 0),
+ receive
+ {nodedown, X} ->
+ ct:fail({unexpected_nodedown, X})
+ after 0 ->
+ ok
+ end,
+ ok.
+
+
+%% Tests node local process monitors
process_monitors(Config) when is_list(Config) ->
- ?line common_process_monitors(node(), node()),
- ?line Mon1 = erlang:monitor(process,self()),
- ?line [] = find_erl_monitor(self(), Mon1),
- ?line [Name] = get_names(1, process_monitors),
- ?line true = register(Name, self()),
- ?line Mon2 = erlang:monitor(process, Name),
- ?line [] = find_erl_monitor(self(), Mon2),
- ?line receive
- {'DOWN', Mon1, _, _, _} = Msg ->
- ?line ?t:fail({unexpected_down_msg, Msg});
- {'DOWN', Mon2, _, _, _} = Msg ->
- ?line ?t:fail({unexpected_down_msg, Msg})
- after 500 ->
- ?line true = erlang:demonitor(Mon1),
- ?line true = erlang:demonitor(Mon2),
- ?line ok
- end.
-
-dist_process_monitors(doc) -> ["Tests distributed process monitors"];
-dist_process_monitors(suite) -> [];
+ common_process_monitors(node(), node()),
+ Mon1 = erlang:monitor(process,self()),
+ [] = find_erl_monitor(self(), Mon1),
+ [Name] = get_names(1, process_monitors),
+ true = register(Name, self()),
+ Mon2 = erlang:monitor(process, Name),
+ [] = find_erl_monitor(self(), Mon2),
+ receive
+ {'DOWN', Mon1, _, _, _} = Msg ->
+ ct:fail({unexpected_down_msg, Msg});
+ {'DOWN', Mon2, _, _, _} = Msg ->
+ ct:fail({unexpected_down_msg, Msg})
+ after 500 ->
+ true = erlang:demonitor(Mon1),
+ true = erlang:demonitor(Mon2),
+ ok
+ end.
+
+%% Tests distributed process monitors
dist_process_monitors(Config) when is_list(Config) ->
- ?line [Name] = get_names(1,dist_process_monitors),
- ?line {ok, Node} = start_node(Name),
- ?line common_process_monitors(node(), Node),
- ?line TP1 = spawn(Node, ?MODULE, test_proc, []),
- ?line R1 = erlang:monitor(process, TP1),
- ?line TP1O = get_down_object(TP1, self()),
- ?line check_process_monitor(self(), TP1, R1),
- ?line tp_cast(TP1, fun () -> halt() end),
- ?line receive
- {'DOWN',R1,process,TP1O,noconnection} ->
- ?line ok
- end,
- ?line check_process_demonitor(self(), TP1, R1),
- ?line R2 = erlang:monitor(process, TP1),
- ?line receive
- {'DOWN',R2,process,TP1O,noconnection} ->
- ?line ok
- end,
- ?line check_process_demonitor(self(), TP1, R2),
- ?line ok.
+ [Name] = get_names(1,dist_process_monitors),
+ {ok, Node} = start_node(Name),
+ common_process_monitors(node(), Node),
+ TP1 = spawn(Node, ?MODULE, test_proc, []),
+ R1 = erlang:monitor(process, TP1),
+ TP1O = get_down_object(TP1, self()),
+ check_process_monitor(self(), TP1, R1),
+ tp_cast(TP1, fun () -> halt() end),
+ receive
+ {'DOWN',R1,process,TP1O,noconnection} ->
+ ok
+ end,
+ check_process_demonitor(self(), TP1, R1),
+ R2 = erlang:monitor(process, TP1),
+ receive
+ {'DOWN',R2,process,TP1O,noconnection} ->
+ ok
+ end,
+ check_process_demonitor(self(), TP1, R2),
+ ok.
common_process_monitors(NodeA, NodeB) ->
- ?line TP1 = spawn(NodeA, ?MODULE, test_proc, []),
- ?line TP2 = spawn(NodeB, ?MODULE, test_proc, []),
- ?line run_common_process_monitors(TP1, TP2),
- ?line TP3 = spawn(NodeA, ?MODULE, test_proc, []),
- ?line TP4 = spawn(NodeB, ?MODULE, test_proc, []),
- ?line [TP4N] = get_names(1, common_process_monitors),
- ?line true = tp_call(TP4, fun () -> register(TP4N,self()) end),
- ?line run_common_process_monitors(TP3,
- case node() == node(TP4) of
- true -> TP4N;
- false -> {TP4N, node(TP4)}
- end),
- ?line ok.
+ TP1 = spawn(NodeA, ?MODULE, test_proc, []),
+ TP2 = spawn(NodeB, ?MODULE, test_proc, []),
+ run_common_process_monitors(TP1, TP2),
+ TP3 = spawn(NodeA, ?MODULE, test_proc, []),
+ TP4 = spawn(NodeB, ?MODULE, test_proc, []),
+ [TP4N] = get_names(1, common_process_monitors),
+ true = tp_call(TP4, fun () -> register(TP4N,self()) end),
+ run_common_process_monitors(TP3,
+ case node() == node(TP4) of
+ true -> TP4N;
+ false -> {TP4N, node(TP4)}
+ end),
+ ok.
run_common_process_monitors(TP1, TP2) ->
- ?line R1 = tp_call(TP1, fun () -> erlang:monitor(process, TP2) end),
- ?line check_process_monitor(TP1, TP2, R1),
-
- ?line tp_call(TP2, fun () -> catch erlang:demonitor(R1) end),
- ?line check_process_monitor(TP1, TP2, R1),
-
- ?line true = tp_call(TP1, fun () -> erlang:demonitor(R1) end),
- ?line check_process_demonitor(TP1, TP2, R1),
-
- ?line R2 = tp_call(TP1, fun () -> erlang:monitor(process, TP2) end),
- ?line TP2O = get_down_object(TP2, TP1),
- ?line check_process_monitor(TP1, TP2, R2),
- ?line tp_cast(TP2, fun () -> exit(bye) end),
- ?line wait_until(fun () -> ?line is_proc_dead(TP2) end),
- ?line ok = tp_call(TP1, fun () ->
- ?line receive
- {'DOWN',R2,process,TP2O,bye} ->
- ?line ok
- end
- end),
- ?line check_process_demonitor(TP1, TP2, R2),
-
- ?line R3 = tp_call(TP1, fun () -> erlang:monitor(process, TP2) end),
- ?line ok = tp_call(TP1, fun () ->
- ?line receive
- {'DOWN',R3,process,TP2O,noproc} ->
- ?line ok
- end
- end),
- ?line check_process_demonitor(TP1, TP2, R3),
-
- ?line tp_cast(TP1, fun () -> exit(normal) end),
- ?line wait_until(fun () -> ?line is_proc_dead(TP1) end),
- ?line ok.
-
-
-busy_dist_port_monitor(doc) -> ["Tests distributed monitor/2, demonitor/1, "
- "and 'DOWN' message over busy distribution "
- "port"];
-busy_dist_port_monitor(suite) -> [];
+ R1 = tp_call(TP1, fun () -> erlang:monitor(process, TP2) end),
+ check_process_monitor(TP1, TP2, R1),
+
+ tp_call(TP2, fun () -> catch erlang:demonitor(R1) end),
+ check_process_monitor(TP1, TP2, R1),
+
+ true = tp_call(TP1, fun () -> erlang:demonitor(R1) end),
+ check_process_demonitor(TP1, TP2, R1),
+
+ R2 = tp_call(TP1, fun () -> erlang:monitor(process, TP2) end),
+ TP2O = get_down_object(TP2, TP1),
+ check_process_monitor(TP1, TP2, R2),
+ tp_cast(TP2, fun () -> exit(bye) end),
+ wait_until(fun () -> is_proc_dead(TP2) end),
+ ok = tp_call(TP1, fun () ->
+ receive
+ {'DOWN',R2,process,TP2O,bye} ->
+ ok
+ end
+ end),
+ check_process_demonitor(TP1, TP2, R2),
+
+ R3 = tp_call(TP1, fun () -> erlang:monitor(process, TP2) end),
+ ok = tp_call(TP1, fun () ->
+ receive
+ {'DOWN',R3,process,TP2O,noproc} ->
+ ok
+ end
+ end),
+ check_process_demonitor(TP1, TP2, R3),
+
+ tp_cast(TP1, fun () -> exit(normal) end),
+ wait_until(fun () -> is_proc_dead(TP1) end),
+ ok.
+
+
+%% Tests distributed monitor/2, demonitor/1, and 'DOWN' message
+%% over busy distribution port
busy_dist_port_monitor(Config) when is_list(Config) ->
- ?line Tracer = case os:getenv("TRACE_BUSY_DIST_PORT") of
- "true" -> start_busy_dist_port_tracer();
- _ -> false
- end,
+ Tracer = case os:getenv("TRACE_BUSY_DIST_PORT") of
+ "true" -> start_busy_dist_port_tracer();
+ _ -> false
+ end,
- ?line [An] = get_names(1, busy_dist_port_monitor),
- ?line {ok, A} = start_node(An),
- ?line TP1 = spawn(A, ?MODULE, test_proc, []),
+ [An] = get_names(1, busy_dist_port_monitor),
+ {ok, A} = start_node(An),
+ TP1 = spawn(A, ?MODULE, test_proc, []),
%% Check monitor over busy port
- ?line M1 = suspend_on_busy_test(A,
- "erlang:monitor(process, TP1)",
- fun () -> erlang:monitor(process, TP1) end),
- ?line check_process_monitor(self(), TP1, M1),
+ M1 = suspend_on_busy_test(A,
+ "erlang:monitor(process, TP1)",
+ fun () -> erlang:monitor(process, TP1) end),
+ check_process_monitor(self(), TP1, M1),
%% Check demonitor over busy port
- ?line suspend_on_busy_test(A,
- "erlang:demonitor(M1)",
- fun () -> erlang:demonitor(M1) end),
- ?line check_process_demonitor(self(), TP1, M1),
+ suspend_on_busy_test(A,
+ "erlang:demonitor(M1)",
+ fun () -> erlang:demonitor(M1) end),
+ check_process_demonitor(self(), TP1, M1),
%% Check down message over busy port
- ?line TP2 = spawn(?MODULE, test_proc, []),
- ?line M2 = tp_call(TP1, fun () -> erlang:monitor(process, TP2) end),
- ?line check_process_monitor(TP1, TP2, M2),
- ?line Ref = make_ref(),
- ?line Busy = make_busy(A, 1000),
- ?line receive after 100 -> ok end,
- ?line tp_cast(TP2, fun () -> exit(Ref) end),
- ?line receive after 100 -> ok end,
- ?line unmake_busy(Busy),
- ?line Ref = tp_call(TP1, fun () ->
- receive
- {'DOWN', M2, process, TP2, Ref} ->
- Ref
- end
- end),
- ?line tp_cast(TP1, fun () -> exit(normal) end),
- ?line stop_node(A),
- ?line stop_busy_dist_port_tracer(Tracer),
- ?line ok.
-
-busy_dist_port_link(doc) -> ["Tests distributed link/1, unlink/1, and 'EXIT'",
- " message over busy distribution port"];
-busy_dist_port_link(suite) -> [];
+ TP2 = spawn(?MODULE, test_proc, []),
+ M2 = tp_call(TP1, fun () -> erlang:monitor(process, TP2) end),
+ check_process_monitor(TP1, TP2, M2),
+ Ref = make_ref(),
+ Busy = make_busy(A, 1000),
+ receive after 100 -> ok end,
+ tp_cast(TP2, fun () -> exit(Ref) end),
+ receive after 100 -> ok end,
+ unmake_busy(Busy),
+ Ref = tp_call(TP1, fun () ->
+ receive
+ {'DOWN', M2, process, TP2, Ref} ->
+ Ref
+ end
+ end),
+ tp_cast(TP1, fun () -> exit(normal) end),
+ stop_node(A),
+ stop_busy_dist_port_tracer(Tracer),
+ ok.
+
+%% Tests distributed link/1, unlink/1, and 'EXIT'
+%% message over busy distribution port
busy_dist_port_link(Config) when is_list(Config) ->
- ?line Tracer = case os:getenv("TRACE_BUSY_DIST_PORT") of
- "true" -> start_busy_dist_port_tracer();
- _ -> false
- end,
-
- ?line [An] = get_names(1, busy_dist_port_link),
- ?line {ok, A} = start_node(An),
- ?line TP1 = spawn(A, ?MODULE, test_proc, []),
+ Tracer = case os:getenv("TRACE_BUSY_DIST_PORT") of
+ "true" -> start_busy_dist_port_tracer();
+ _ -> false
+ end,
+
+ [An] = get_names(1, busy_dist_port_link),
+ {ok, A} = start_node(An),
+ TP1 = spawn(A, ?MODULE, test_proc, []),
%% Check link over busy port
- ?line suspend_on_busy_test(A,
- "link(TP1)",
- fun () -> link(TP1) end),
- ?line check_link(self(), TP1),
+ suspend_on_busy_test(A,
+ "link(TP1)",
+ fun () -> link(TP1) end),
+ check_link(self(), TP1),
%% Check unlink over busy port
- ?line suspend_on_busy_test(A,
- "unlink(TP1)",
- fun () -> unlink(TP1) end),
- ?line check_unlink(self(), TP1),
+ suspend_on_busy_test(A,
+ "unlink(TP1)",
+ fun () -> unlink(TP1) end),
+ check_unlink(self(), TP1),
%% Check trap exit message over busy port
- ?line TP2 = spawn(?MODULE, test_proc, []),
- ?line ok = tp_call(TP1, fun () ->
- process_flag(trap_exit, true),
- link(TP2),
- ok
- end),
- ?line check_link(TP1, TP2),
- ?line Ref = make_ref(),
- ?line Busy = make_busy(A, 1000),
- ?line receive after 100 -> ok end,
- ?line tp_cast(TP2, fun () -> exit(Ref) end),
- ?line receive after 100 -> ok end,
- ?line unmake_busy(Busy),
- ?line Ref = tp_call(TP1, fun () ->
- receive
- {'EXIT', TP2, Ref} ->
- Ref
- end
- end),
- ?line tp_cast(TP1, fun () -> exit(normal) end),
- ?line stop_node(A),
- ?line stop_busy_dist_port_tracer(Tracer),
- ?line ok.
-
-
-otp_5772_link(doc) -> [];
-otp_5772_link(suite) -> [];
+ TP2 = spawn(?MODULE, test_proc, []),
+ ok = tp_call(TP1, fun () ->
+ process_flag(trap_exit, true),
+ link(TP2),
+ ok
+ end),
+ check_link(TP1, TP2),
+ Ref = make_ref(),
+ Busy = make_busy(A, 1000),
+ receive after 100 -> ok end,
+ tp_cast(TP2, fun () -> exit(Ref) end),
+ receive after 100 -> ok end,
+ unmake_busy(Busy),
+ Ref = tp_call(TP1, fun () ->
+ receive
+ {'EXIT', TP2, Ref} ->
+ Ref
+ end
+ end),
+ tp_cast(TP1, fun () -> exit(normal) end),
+ stop_node(A),
+ stop_busy_dist_port_tracer(Tracer),
+ ok.
+
+
otp_5772_link(Config) when is_list(Config) ->
- ?line otp_5772_link_test(node()).
+ otp_5772_link_test(node()).
-otp_5772_dist_link(doc) -> [];
-otp_5772_dist_link(suite) -> [];
otp_5772_dist_link(Config) when is_list(Config) ->
- ?line [An] = get_names(1, otp_5772_dist_link),
- ?line {ok, A} = start_node(An),
- ?line otp_5772_link_test(A),
- ?line stop_node(A).
+ [An] = get_names(1, otp_5772_dist_link),
+ {ok, A} = start_node(An),
+ otp_5772_link_test(A),
+ stop_node(A).
otp_5772_link_test(Node) ->
- ?line Prio = process_flag(priority, high),
- ?line TE = process_flag(trap_exit, true),
- ?line TP1 = spawn_opt(Node, ?MODULE, test_proc, [],
- [link, {priority, low}]),
+ Prio = process_flag(priority, high),
+ TE = process_flag(trap_exit, true),
+ TP1 = spawn_opt(Node, ?MODULE, test_proc, [],
+ [link, {priority, low}]),
exit(TP1, bang),
unlink(TP1),
- ?line receive
- {'EXIT', TP1, _} ->
- ?line ok
- after 0 ->
- ?line ok
- end,
- ?line receive
- {'EXIT', TP1, _} = Exit ->
- ?line ?t:fail({got_late_exit_message, Exit})
- after 1000 ->
- ?line ok
- end,
- ?line process_flag(trap_exit, TE),
- ?line process_flag(priority, Prio),
- ?line ok.
-
-otp_5772_monitor(doc) -> [];
-otp_5772_monitor(suite) -> [];
+ receive
+ {'EXIT', TP1, _} ->
+ ok
+ after 0 ->
+ ok
+ end,
+ receive
+ {'EXIT', TP1, _} = Exit ->
+ ct:fail({got_late_exit_message, Exit})
+ after 1000 ->
+ ok
+ end,
+ process_flag(trap_exit, TE),
+ process_flag(priority, Prio),
+ ok.
+
otp_5772_monitor(Config) when is_list(Config) ->
- ?line otp_5772_monitor_test(node()).
+ otp_5772_monitor_test(node()).
-otp_5772_dist_monitor(doc) -> [];
-otp_5772_dist_monitor(suite) -> [];
otp_5772_dist_monitor(Config) when is_list(Config) ->
- ?line [An] = get_names(1, otp_5772_dist_monitor),
- ?line {ok, A} = start_node(An),
- ?line otp_5772_monitor_test(A),
- ?line stop_node(A),
- ?line ok.
+ [An] = get_names(1, otp_5772_dist_monitor),
+ {ok, A} = start_node(An),
+ otp_5772_monitor_test(A),
+ stop_node(A),
+ ok.
otp_5772_monitor_test(Node) ->
- ?line Prio = process_flag(priority, high),
- ?line TP1 = spawn_opt(Node, ?MODULE, test_proc, [], [{priority, low}]),
- ?line M1 = erlang:monitor(process, TP1),
- ?line exit(TP1, bang),
- ?line erlang:demonitor(M1),
- ?line receive
- {'DOWN', M1, _, _, _} ->
- ?line ok
- after 0 ->
- ?line ok
- end,
- ?line receive
- {'DOWN', M1, _, _, _} = Down ->
- ?line ?t:fail({got_late_down_message, Down})
- after 1000 ->
- ?line ok
- end,
- ?line process_flag(priority, Prio),
- ?line ok.
+ Prio = process_flag(priority, high),
+ TP1 = spawn_opt(Node, ?MODULE, test_proc, [], [{priority, low}]),
+ M1 = erlang:monitor(process, TP1),
+ exit(TP1, bang),
+ erlang:demonitor(M1),
+ receive
+ {'DOWN', M1, _, _, _} ->
+ ok
+ after 0 ->
+ ok
+ end,
+ receive
+ {'DOWN', M1, _, _, _} = Down ->
+ ct:fail({got_late_down_message, Down})
+ after 1000 ->
+ ok
+ end,
+ process_flag(priority, Prio),
+ ok.
otp_7946(Config) when is_list(Config) ->
- ?line [NodeName] = get_names(1, otp_7946),
- ?line {ok, Node} = start_node(NodeName),
- ?line Proc = rpc:call(Node, erlang, whereis, [net_kernel]),
- ?line Mon = erlang:monitor(process, Proc),
- ?line rpc:cast(Node, erlang, halt, []),
- ?line receive {'DOWN', Mon, process, Proc , _} -> ok end,
- ?line {Linker, LMon} = spawn_monitor(fun () ->
- link(Proc),
- receive
- after infinity -> ok
- end
- end),
- ?line receive
- {'DOWN', LMon, process, Linker, Reason} ->
- ?line ?t:format("Reason=~p~n", [Reason]),
- ?line Reason = noconnection
- end.
+ [NodeName] = get_names(1, otp_7946),
+ {ok, Node} = start_node(NodeName),
+ Proc = rpc:call(Node, erlang, whereis, [net_kernel]),
+ Mon = erlang:monitor(process, Proc),
+ rpc:cast(Node, erlang, halt, []),
+ receive {'DOWN', Mon, process, Proc , _} -> ok end,
+ {Linker, LMon} = spawn_monitor(fun () ->
+ link(Proc),
+ receive
+ after infinity -> ok
+ end
+ end),
+ receive
+ {'DOWN', LMon, process, Linker, Reason} ->
+ io:format("Reason=~p~n", [Reason]),
+ Reason = noconnection
+ end.
%%
%% -- Internal utils --------------------------------------------------------
@@ -519,27 +502,27 @@ otp_7946(Config) when is_list(Config) ->
busy_data() ->
case get(?BUSY_DATA_KEY) of
- undefined ->
- set_busy_data([]);
- Data ->
- true = is_binary(Data),
- true = size(Data) == ?BUSY_DATA_SIZE,
- Data
+ undefined ->
+ set_busy_data([]);
+ Data ->
+ true = is_binary(Data),
+ true = size(Data) == ?BUSY_DATA_SIZE,
+ Data
end.
set_busy_data(SetData) ->
case get(?BUSY_DATA_KEY) of
- undefined ->
- Data = case SetData of
- D when is_binary(D), size(D) == ?BUSY_DATA_SIZE ->
- SetData;
- _ ->
- list_to_binary(lists:duplicate(?BUSY_DATA_SIZE, 253))
- end,
- put(?BUSY_DATA_KEY, Data),
- Data;
- OldData ->
- OldData
+ undefined ->
+ Data = case SetData of
+ D when is_binary(D), size(D) == ?BUSY_DATA_SIZE ->
+ SetData;
+ _ ->
+ list_to_binary(lists:duplicate(?BUSY_DATA_SIZE, 253))
+ end,
+ put(?BUSY_DATA_KEY, Data),
+ Data;
+ OldData ->
+ OldData
end.
freeze_node(Node, MS) ->
@@ -547,13 +530,13 @@ freeze_node(Node, MS) ->
DoingIt = make_ref(),
Freezer = self(),
spawn_link(Node,
- fun () ->
- erts_debug:set_internal_state(available_internal_state,
- true),
- dport_send(Freezer, DoingIt),
- receive after Own -> ok end,
- erts_debug:set_internal_state(block, MS+Own)
- end),
+ fun () ->
+ erts_debug:set_internal_state(available_internal_state,
+ true),
+ dport_send(Freezer, DoingIt),
+ receive after Own -> ok end,
+ erts_debug:set_internal_state(block, MS+Own)
+ end),
receive DoingIt -> ok end,
receive after Own -> ok end.
@@ -563,27 +546,27 @@ make_busy(Node, Time) when is_integer(Time) ->
Data = busy_data(),
%% first make port busy
Pid = spawn_link(fun () ->
- forever(fun () ->
- dport_reg_send(Node,
- '__noone__',
- Data)
- end)
- end),
+ forever(fun () ->
+ dport_reg_send(Node,
+ '__noone__',
+ Data)
+ end)
+ end),
receive after Own -> ok end,
wait_until(fun () ->
- case process_info(Pid, status) of
- {status, suspended} -> true;
- _ -> false
- end
- end),
+ case process_info(Pid, status) of
+ {status, suspended} -> true;
+ _ -> false
+ end
+ end),
%% then dist entry
make_busy(Node, [nosuspend], Data),
Pid.
make_busy(Node, Opts, Data) ->
case erlang:send({'__noone__', Node}, Data, Opts) of
- nosuspend -> nosuspend;
- _ -> make_busy(Node, Opts, Data)
+ nosuspend -> nosuspend;
+ _ -> make_busy(Node, Opts, Data)
end.
unmake_busy(Pid) ->
@@ -596,33 +579,33 @@ suspend_on_busy_test(Node, Doing, Fun) ->
Done = make_ref(),
Data = busy_data(),
spawn_link(fun () ->
- set_busy_data(Data),
- Busy = make_busy(Node, 1000),
- Tester ! DoIt,
- receive after 100 -> ok end,
- Info = process_info(Tester, [status, current_function]),
- unmake_busy(Busy),
- ?t:format("~p doing ~s: ~p~n", [Tester, Doing, Info]),
- Tester ! {Done, Info}
- end),
+ set_busy_data(Data),
+ Busy = make_busy(Node, 1000),
+ Tester ! DoIt,
+ receive after 100 -> ok end,
+ Info = process_info(Tester, [status, current_function]),
+ unmake_busy(Busy),
+ io:format("~p doing ~s: ~p~n", [Tester, Doing, Info]),
+ Tester ! {Done, Info}
+ end),
receive DoIt -> ok end,
Res = Fun(),
receive
- {Done, MyInfo} ->
- %% Don't match arity; it is different in
- %% debug and optimized emulator
- [{status, suspended},
- {current_function, {erlang, bif_return_trap, _}}] = MyInfo,
- ok
+ {Done, MyInfo} ->
+ %% Don't match arity; it is different in
+ %% debug and optimized emulator
+ [{status, suspended},
+ {current_function, {erlang, bif_return_trap, _}}] = MyInfo,
+ ok
end,
Res.
% get_node(Name) when is_atom(Name) ->
-% ?line node();
+% node();
% get_node({Name, Node}) when is_atom(Name) ->
-% ?line Node;
+% Node;
% get_node(NC) when is_pid(NC); is_port(NC); is_reference(NC) ->
-% ?line node(NC).
+% node(NC).
get_down_object(Item, _) when is_pid(Item) ->
Item;
@@ -637,90 +620,78 @@ get_down_object(Item, Watcher) when is_atom(Item), is_atom(Watcher) ->
is_proc_dead(P) ->
case is_proc_alive(P) of
- true -> false;
- false -> true
+ true -> false;
+ false -> true
end.
is_proc_alive(Pid) when is_pid(Pid), node(Pid) == node() ->
- ?line is_process_alive(Pid);
+ is_process_alive(Pid);
is_proc_alive(Name) when is_atom(Name) ->
- ?line case catch whereis(Name) of
- Pid when is_pid(Pid) ->
- ?line is_proc_alive(Pid);
- _ ->
- ?line false
- end;
+ case catch whereis(Name) of
+ Pid when is_pid(Pid) ->
+ is_proc_alive(Pid);
+ _ ->
+ false
+ end;
is_proc_alive({Name, Node}) when is_atom(Name), Node == node() ->
- ?line is_proc_alive(Name);
+ is_proc_alive(Name);
is_proc_alive(Proc) ->
- ?line is_remote_proc_alive(Proc).
+ is_remote_proc_alive(Proc).
is_remote_proc_alive({Name, Node}) when is_atom(Name), is_atom(Node) ->
- ?line is_remote_proc_alive(Name, Node);
+ is_remote_proc_alive(Name, Node);
is_remote_proc_alive(Pid) when is_pid(Pid) ->
- ?line is_remote_proc_alive(Pid, node(Pid));
+ is_remote_proc_alive(Pid, node(Pid));
is_remote_proc_alive(_) ->
- ?line false.
+ false.
is_remote_proc_alive(PN, Node) ->
- ?line S = self(),
- ?line R = make_ref(),
- ?line monitor_node(Node, true),
- ?line _P = spawn(Node, fun () -> S ! {R, is_proc_alive(PN)} end),
- ?line receive
- {R, Bool} ->
- ?line monitor_node(Node, false),
- ?line Bool;
- {nodedown, Node} ->
- ?line false
- end.
+ S = self(),
+ R = make_ref(),
+ monitor_node(Node, true),
+ _P = spawn(Node, fun () -> S ! {R, is_proc_alive(PN)} end),
+ receive
+ {R, Bool} ->
+ monitor_node(Node, false),
+ Bool;
+ {nodedown, Node} ->
+ false
+ end.
wait_until(Fun) ->
- ?line case Fun() of
- true ->
- ?line ok;
- _ ->
- ?line receive
- after 100 ->
- ?line wait_until(Fun)
- end
- end.
+ case Fun() of
+ true ->
+ ok;
+ _ ->
+ receive
+ after 100 ->
+ wait_until(Fun)
+ end
+ end.
forever(Fun) ->
Fun(),
forever(Fun).
-init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- ?line Dog = ?t:timetrap(?t:minutes(1)),
- case catch erts_debug:get_internal_state(available_internal_state) of
- true -> ok;
- _ -> erts_debug:set_internal_state(available_internal_state, true)
- end,
- ?line [{watchdog, Dog}|Config].
-
-end_per_testcase(_Func, Config) ->
- ?line Dog = ?config(watchdog, Config),
- ?line ?t:timetrap_cancel(Dog).
-
tp_call(Tp, Fun) ->
- ?line R = make_ref(),
- ?line Tp ! {call, self(), R, Fun},
- ?line receive
- {R, Res} ->
- ?line Res
- end.
+ R = make_ref(),
+ Tp ! {call, self(), R, Fun},
+ receive
+ {R, Res} ->
+ Res
+ end.
tp_cast(Tp, Fun) ->
- ?line Tp ! {cast, Fun}.
+ Tp ! {cast, Fun}.
test_proc() ->
- ?line receive
- {call, From, Ref, Fun} ->
- ?line From ! {Ref, Fun()};
- {cast, Fun} ->
- ?line Fun()
- end,
- ?line test_proc().
+ receive
+ {call, From, Ref, Fun} ->
+ From ! {Ref, Fun()};
+ {cast, Fun} ->
+ Fun()
+ end,
+ test_proc().
expand_link_list([#erl_link{type = ?LINK_NODE, targets = N} = Rec | T]) ->
lists:duplicate(N,Rec#erl_link{targets = []}) ++ expand_link_list(T);
@@ -728,7 +699,7 @@ expand_link_list([#erl_link{targets = [#erl_link{pid = Pid}]} = Rec | T]) ->
[Rec#erl_link{targets = [Pid]} | expand_link_list(T)];
expand_link_list([#erl_link{targets = [#erl_link{pid = Pid}|TT]} = Rec | T]) ->
[ Rec#erl_link{targets = [Pid]} | expand_link_list(
- [Rec#erl_link{targets = TT} | T])];
+ [Rec#erl_link{targets = TT} | T])];
expand_link_list([#erl_link{targets = []} = Rec | T]) ->
[Rec | expand_link_list(T)];
expand_link_list([]) ->
@@ -736,19 +707,19 @@ expand_link_list([]) ->
get_local_link_list(Obj) ->
case catch erts_debug:get_internal_state({link_list, Obj}) of
- LL when is_list(LL) ->
- expand_link_list(LL);
- _ ->
- []
+ LL when is_list(LL) ->
+ expand_link_list(LL);
+ _ ->
+ []
end.
get_remote_link_list(Node, Obj) ->
case catch rpc:call(Node, erts_debug, get_internal_state,
- [{link_list, Obj}]) of
- LL when is_list(LL) ->
- expand_link_list(LL);
- _ ->
- []
+ [{link_list, Obj}]) of
+ LL when is_list(LL) ->
+ expand_link_list(LL);
+ _ ->
+ []
end.
@@ -758,30 +729,30 @@ get_link_list({Node, DistEntry}) when is_atom(Node), is_atom(DistEntry) ->
get_remote_link_list(Node, DistEntry);
get_link_list(P) when is_pid(P); is_port(P) ->
case node(P) of
- Node when Node == node() ->
- get_local_link_list(P);
- Node ->
- get_remote_link_list(Node, P)
- end;
+ Node when Node == node() ->
+ get_local_link_list(P);
+ Node ->
+ get_remote_link_list(Node, P)
+ end;
get_link_list(undefined) ->
[].
get_local_monitor_list(Obj) ->
case catch erts_debug:get_internal_state({monitor_list, Obj}) of
- LL when is_list(LL) ->
- LL;
- _ ->
- []
- end.
+ LL when is_list(LL) ->
+ LL;
+ _ ->
+ []
+ end.
get_remote_monitor_list(Node, Obj) ->
case catch rpc:call(Node, erts_debug, get_internal_state,
- [{monitor_list, Obj}]) of
- LL when is_list(LL) ->
- LL;
- _ ->
- []
- end.
+ [{monitor_list, Obj}]) of
+ LL when is_list(LL) ->
+ LL;
+ _ ->
+ []
+ end.
get_monitor_list({Node, DistEntry}) when Node == node(), is_atom(DistEntry) ->
@@ -790,242 +761,242 @@ get_monitor_list({Node, DistEntry}) when is_atom(Node), is_atom(DistEntry) ->
get_remote_monitor_list(Node, DistEntry);
get_monitor_list(P) when is_pid(P) ->
case node(P) of
- Node when Node == node() ->
- get_local_monitor_list(P);
- Node ->
- get_remote_monitor_list(Node, P)
- end;
+ Node when Node == node() ->
+ get_local_monitor_list(P);
+ Node ->
+ get_remote_monitor_list(Node, P)
+ end;
get_monitor_list(undefined) ->
[].
find_erl_monitor(Pid, Ref) when is_reference(Ref) ->
lists:foldl(fun (#erl_monitor{ref = R} = EL, Acc) when R == Ref ->
- [EL|Acc];
- (_, Acc) ->
- Acc
- end,
- [],
- get_monitor_list(Pid)).
+ [EL|Acc];
+ (_, Acc) ->
+ Acc
+ end,
+ [],
+ get_monitor_list(Pid)).
% find_erl_link(Obj, Ref) when is_reference(Ref) ->
-% ?line lists:foldl(fun (#erl_link{ref = R} = EL, Acc) when R == Ref ->
-% ?line [EL|Acc];
+% lists:foldl(fun (#erl_link{ref = R} = EL, Acc) when R == Ref ->
+% [EL|Acc];
% (_, Acc) ->
-% ?line Acc
+% Acc
% end,
% [],
% get_link_list(Obj)).
find_erl_link(Obj, Type, [Item, Data]) when is_pid(Item);
- is_port(Item);
- is_atom(Item) ->
+ is_port(Item);
+ is_atom(Item) ->
lists:foldl(fun (#erl_link{type = T, pid = I, targets = D} = EL,
- Acc) when T == Type, I == Item ->
- case Data of
- D ->
- [EL|Acc];
- [] ->
- [EL|Acc];
- _ ->
- Acc
- end;
- (_, Acc) ->
- Acc
- end,
- [],
- get_link_list(Obj));
+ Acc) when T == Type, I == Item ->
+ case Data of
+ D ->
+ [EL|Acc];
+ [] ->
+ [EL|Acc];
+ _ ->
+ Acc
+ end;
+ (_, Acc) ->
+ Acc
+ end,
+ [],
+ get_link_list(Obj));
find_erl_link(Obj, Type, Item) when is_pid(Item); is_port(Item); is_atom(Item) ->
find_erl_link(Obj, Type, [Item, []]).
-
+
check_link(A, B) ->
- ?line [#erl_link{type = ?LINK_PID,
- pid = B,
- targets = []}] = find_erl_link(A, ?LINK_PID, B),
- ?line [#erl_link{type = ?LINK_PID,
- pid = A,
- targets = []}] = find_erl_link(B, ?LINK_PID, A),
- ?line case node(A) == node(B) of
- false ->
- ?line [#erl_link{type = ?LINK_PID,
- pid = A,
- targets = [B]}] = find_erl_link({node(A),
- node(B)},
- ?LINK_PID,
- [A, [B]]),
- ?line [#erl_link{type = ?LINK_PID,
- pid = B,
- targets = [A]}] = find_erl_link({node(B),
- node(A)},
- ?LINK_PID,
- [B, [A]]);
- true ->
- ?line [] = find_erl_link({node(A), node(B)},
- ?LINK_PID,
- [A, [B]]),
- ?line [] = find_erl_link({node(B), node(A)},
- ?LINK_PID,
- [B, [A]])
- end,
- ?line ok.
+ [#erl_link{type = ?LINK_PID,
+ pid = B,
+ targets = []}] = find_erl_link(A, ?LINK_PID, B),
+ [#erl_link{type = ?LINK_PID,
+ pid = A,
+ targets = []}] = find_erl_link(B, ?LINK_PID, A),
+ case node(A) == node(B) of
+ false ->
+ [#erl_link{type = ?LINK_PID,
+ pid = A,
+ targets = [B]}] = find_erl_link({node(A),
+ node(B)},
+ ?LINK_PID,
+ [A, [B]]),
+ [#erl_link{type = ?LINK_PID,
+ pid = B,
+ targets = [A]}] = find_erl_link({node(B),
+ node(A)},
+ ?LINK_PID,
+ [B, [A]]);
+ true ->
+ [] = find_erl_link({node(A), node(B)},
+ ?LINK_PID,
+ [A, [B]]),
+ [] = find_erl_link({node(B), node(A)},
+ ?LINK_PID,
+ [B, [A]])
+ end,
+ ok.
check_unlink(A, B) ->
- ?line [] = find_erl_link(A, ?LINK_PID, B),
- ?line [] = find_erl_link(B, ?LINK_PID, A),
- ?line [] = find_erl_link({node(A), node(B)}, ?LINK_PID, [A, [B]]),
- ?line [] = find_erl_link({node(B), node(A)}, ?LINK_PID, [B, [A]]),
- ?line ok.
+ [] = find_erl_link(A, ?LINK_PID, B),
+ [] = find_erl_link(B, ?LINK_PID, A),
+ [] = find_erl_link({node(A), node(B)}, ?LINK_PID, [A, [B]]),
+ [] = find_erl_link({node(B), node(A)}, ?LINK_PID, [B, [A]]),
+ ok.
check_process_monitor(From, {Name, Node}, Ref) when is_pid(From),
- is_atom(Name),
- Node == node(From),
- is_reference(Ref) ->
- ?line check_process_monitor(From, Name, Ref);
+ is_atom(Name),
+ Node == node(From),
+ is_reference(Ref) ->
+ check_process_monitor(From, Name, Ref);
check_process_monitor(From, {Name, Node}, Ref) when is_pid(From),
- is_atom(Name),
- is_atom(Node),
- is_reference(Ref) ->
- ?line MonitoredPid = rpc:call(Node, erlang, whereis, [Name]),
- ?line [#erl_monitor{type = ?MON_ORIGIN,
- ref = Ref,
- pid = Node,
- name = Name}] = find_erl_monitor(From, Ref),
- ?line [#erl_monitor{type = ?MON_TARGET,
- ref = Ref,
- pid = From,
- name = Name}] = find_erl_monitor({node(From), Node}, Ref),
- ?line [#erl_monitor{type = ?MON_ORIGIN,
- ref = Ref,
- pid = MonitoredPid,
- name = Name}] = find_erl_monitor({Node, node(From)}, Ref),
- ?line [#erl_monitor{type = ?MON_TARGET,
- ref = Ref,
- pid = From,
- name = Name}] = find_erl_monitor(MonitoredPid, Ref),
- ?line ok;
+ is_atom(Name),
+ is_atom(Node),
+ is_reference(Ref) ->
+ MonitoredPid = rpc:call(Node, erlang, whereis, [Name]),
+ [#erl_monitor{type = ?MON_ORIGIN,
+ ref = Ref,
+ pid = Node,
+ name = Name}] = find_erl_monitor(From, Ref),
+ [#erl_monitor{type = ?MON_TARGET,
+ ref = Ref,
+ pid = From,
+ name = Name}] = find_erl_monitor({node(From), Node}, Ref),
+ [#erl_monitor{type = ?MON_ORIGIN,
+ ref = Ref,
+ pid = MonitoredPid,
+ name = Name}] = find_erl_monitor({Node, node(From)}, Ref),
+ [#erl_monitor{type = ?MON_TARGET,
+ ref = Ref,
+ pid = From,
+ name = Name}] = find_erl_monitor(MonitoredPid, Ref),
+ ok;
check_process_monitor(From, Name, Ref) when is_pid(From),
- is_atom(Name),
- undefined /= Name,
- is_reference(Ref) ->
- ?line MonitoredPid = rpc:call(node(From), erlang, whereis, [Name]),
-
- ?line [#erl_monitor{type = ?MON_ORIGIN,
- ref = Ref,
- pid = MonitoredPid,
- name = Name}] = find_erl_monitor(From, Ref),
-
-
- ?line [#erl_monitor{type = ?MON_TARGET,
- ref = Ref,
- pid = From,
- name = Name}] = find_erl_monitor(MonitoredPid,Ref),
+ is_atom(Name),
+ undefined /= Name,
+ is_reference(Ref) ->
+ MonitoredPid = rpc:call(node(From), erlang, whereis, [Name]),
+
+ [#erl_monitor{type = ?MON_ORIGIN,
+ ref = Ref,
+ pid = MonitoredPid,
+ name = Name}] = find_erl_monitor(From, Ref),
+
+
+ [#erl_monitor{type = ?MON_TARGET,
+ ref = Ref,
+ pid = From,
+ name = Name}] = find_erl_monitor(MonitoredPid,Ref),
ok;
check_process_monitor(From, To, Ref) when is_pid(From),
- is_pid(To),
- is_reference(Ref) ->
- ?line OriMon = [#erl_monitor{type = ?MON_ORIGIN,
- ref = Ref,
- pid = To}],
-
- ?line OriMon = find_erl_monitor(From, Ref),
-
- ?line TargMon = [#erl_monitor{type = ?MON_TARGET,
- ref = Ref,
- pid = From}],
- ?line TargMon = find_erl_monitor(To, Ref),
-
-
- ?line case node(From) == node(To) of
- false ->
- ?line TargMon = find_erl_monitor({node(From), node(To)}, Ref),
- ?line OriMon = find_erl_monitor({node(To), node(From)}, Ref);
- true ->
- ?line [] = find_erl_monitor({node(From), node(From)}, Ref)
- end,
- ?line ok.
+ is_pid(To),
+ is_reference(Ref) ->
+ OriMon = [#erl_monitor{type = ?MON_ORIGIN,
+ ref = Ref,
+ pid = To}],
+
+ OriMon = find_erl_monitor(From, Ref),
+
+ TargMon = [#erl_monitor{type = ?MON_TARGET,
+ ref = Ref,
+ pid = From}],
+ TargMon = find_erl_monitor(To, Ref),
+
+
+ case node(From) == node(To) of
+ false ->
+ TargMon = find_erl_monitor({node(From), node(To)}, Ref),
+ OriMon = find_erl_monitor({node(To), node(From)}, Ref);
+ true ->
+ [] = find_erl_monitor({node(From), node(From)}, Ref)
+ end,
+ ok.
check_process_demonitor(From, {undefined, Node}, Ref) when is_pid(From),
- is_reference(Ref) ->
- ?line [] = find_erl_monitor(From, Ref),
- ?line case node(From) == Node of
- false ->
- ?line [] = find_erl_monitor({node(From), Node}, Ref),
- ?line [] = find_erl_monitor({Node, node(From)}, Ref);
- true ->
- ?line [] = find_erl_monitor({Node, Node}, Ref)
- end,
- ?line ok;
+ is_reference(Ref) ->
+ [] = find_erl_monitor(From, Ref),
+ case node(From) == Node of
+ false ->
+ [] = find_erl_monitor({node(From), Node}, Ref),
+ [] = find_erl_monitor({Node, node(From)}, Ref);
+ true ->
+ [] = find_erl_monitor({Node, Node}, Ref)
+ end,
+ ok;
check_process_demonitor(From, {Name, Node}, Ref) when is_pid(From),
- is_atom(Name),
- Node == node(From),
- is_reference(Ref) ->
- ?line MonitoredPid = rpc:call(Node, erlang, whereis, [Name]),
- ?line case rpc:call(Node, erlang, whereis, [Name]) of
- undefined ->
- ?line check_process_demonitor(From, {undefined, Node}, Ref);
- MonitoredPid ->
- ?line check_process_demonitor(From, MonitoredPid, Ref)
- end;
+ is_atom(Name),
+ Node == node(From),
+ is_reference(Ref) ->
+ MonitoredPid = rpc:call(Node, erlang, whereis, [Name]),
+ case rpc:call(Node, erlang, whereis, [Name]) of
+ undefined ->
+ check_process_demonitor(From, {undefined, Node}, Ref);
+ MonitoredPid ->
+ check_process_demonitor(From, MonitoredPid, Ref)
+ end;
check_process_demonitor(From, {Name, Node}, Ref) when is_pid(From),
- is_atom(Name),
- is_atom(Node),
- is_reference(Ref) ->
- ?line MonitoredPid = rpc:call(Node, erlang, whereis, [Name]),
- ?line [] = find_erl_monitor(From, Ref),
- ?line [] = find_erl_monitor({node(From), Node}, Ref),
- ?line [] = find_erl_monitor({Node, node(From)}, Ref),
- ?line [] = find_erl_monitor(MonitoredPid, Ref),
- ?line ok;
+ is_atom(Name),
+ is_atom(Node),
+ is_reference(Ref) ->
+ MonitoredPid = rpc:call(Node, erlang, whereis, [Name]),
+ [] = find_erl_monitor(From, Ref),
+ [] = find_erl_monitor({node(From), Node}, Ref),
+ [] = find_erl_monitor({Node, node(From)}, Ref),
+ [] = find_erl_monitor(MonitoredPid, Ref),
+ ok;
check_process_demonitor(From, undefined, Ref) when is_pid(From),
- is_reference(Ref) ->
- ?line [] = find_erl_monitor(From, Ref),
- ?line case node(From) == node() of
- false ->
- ?line [] = find_erl_monitor({node(From), node()}, Ref),
- ?line [] = find_erl_monitor({node(), node(From)}, Ref);
- true ->
- ?line [] = find_erl_monitor({node(), node()}, Ref)
- end,
- ?line ok;
+ is_reference(Ref) ->
+ [] = find_erl_monitor(From, Ref),
+ case node(From) == node() of
+ false ->
+ [] = find_erl_monitor({node(From), node()}, Ref),
+ [] = find_erl_monitor({node(), node(From)}, Ref);
+ true ->
+ [] = find_erl_monitor({node(), node()}, Ref)
+ end,
+ ok;
check_process_demonitor(From, Name, Ref) when is_pid(From),
- is_atom(Name),
- undefined /= Name,
- is_reference(Ref) ->
- ?line check_process_demonitor(From, {Name, node()}, Ref);
+ is_atom(Name),
+ undefined /= Name,
+ is_reference(Ref) ->
+ check_process_demonitor(From, {Name, node()}, Ref);
check_process_demonitor(From, To, Ref) when is_pid(From),
- is_pid(To),
- is_reference(Ref) ->
- ?line [] = find_erl_monitor(From, Ref),
- ?line [] = find_erl_monitor(To, Ref),
- ?line case node(From) == node(To) of
- false ->
- ?line [] = find_erl_monitor({node(From), node(To)}, Ref),
- ?line [] = find_erl_monitor({node(To), node(From)}, Ref);
- true ->
- ?line [] = find_erl_monitor({node(From), node(From)}, Ref)
- end,
- ?line ok.
+ is_pid(To),
+ is_reference(Ref) ->
+ [] = find_erl_monitor(From, Ref),
+ [] = find_erl_monitor(To, Ref),
+ case node(From) == node(To) of
+ false ->
+ [] = find_erl_monitor({node(From), node(To)}, Ref),
+ [] = find_erl_monitor({node(To), node(From)}, Ref);
+ true ->
+ [] = find_erl_monitor({node(From), node(From)}, Ref)
+ end,
+ ok.
no_of_monitor_node(From, Node) when is_pid(From), is_atom(Node) ->
- ?line length(find_erl_link(From, ?LINK_NODE, Node)).
+ length(find_erl_link(From, ?LINK_NODE, Node)).
check_monitor_node(From, Node, No) when is_pid(From),
- is_atom(Node),
- is_integer(No),
- No >= 0 ->
- ?line LL = lists:duplicate(No, #erl_link{type = ?LINK_NODE, pid = Node}),
- ?line DLL = lists:duplicate(No, #erl_link{type = ?LINK_NODE, pid = From}),
- ?line LL = find_erl_link(From, ?LINK_NODE, Node),
- ?line DLL = find_erl_link({node(From), Node}, ?LINK_NODE, From),
- ?line ok.
+ is_atom(Node),
+ is_integer(No),
+ No >= 0 ->
+ LL = lists:duplicate(No, #erl_link{type = ?LINK_NODE, pid = Node}),
+ DLL = lists:duplicate(No, #erl_link{type = ?LINK_NODE, pid = From}),
+ LL = find_erl_link(From, ?LINK_NODE, Node),
+ DLL = find_erl_link({node(From), Node}, ?LINK_NODE, From),
+ ok.
hostname() ->
- ?line from($@, atom_to_list(node())).
+ from($@, atom_to_list(node())).
from(H, [H | T]) -> T;
from(H, [_ | T]) -> from(H, T);
@@ -1037,27 +1008,27 @@ get_names(0, _, Acc) ->
Acc;
get_names(N, T, Acc) ->
get_names(N-1, T, [list_to_atom(atom_to_list(?MODULE)
- ++ "-"
- ++ atom_to_list(T)
- ++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
- ++ "-"
- ++ integer_to_list(erlang:unique_integer([positive]))) | Acc]).
+ ++ "-"
+ ++ atom_to_list(T)
+ ++ "-"
+ ++ integer_to_list(erlang:system_time(second))
+ ++ "-"
+ ++ integer_to_list(erlang:unique_integer([positive]))) | Acc]).
start_node(Name) ->
- ?line start_node(Name, "").
+ start_node(Name, "").
start_node(Name, Args) ->
- ?line Pa = filename:dirname(code:which(?MODULE)),
- ?line Res = ?t:start_node(Name, slave, [{args, Args ++ " -pa " ++ Pa}]),
- ?line {ok, Node} = Res,
- ?line rpc:call(Node, erts_debug, set_internal_state,
- [available_internal_state, true]),
- ?line Res.
-
+ Pa = filename:dirname(code:which(?MODULE)),
+ Res = test_server:start_node(Name, slave, [{args, Args ++ " -pa " ++ Pa}]),
+ {ok, Node} = Res,
+ rpc:call(Node, erts_debug, set_internal_state,
+ [available_internal_state, true]),
+ Res.
+
stop_node(Node) ->
- ?line ?t:stop_node(Node).
+ test_server:stop_node(Node).
-define(COOKIE, '').
-define(DOP_LINK, 1).
@@ -1080,37 +1051,37 @@ stop_node(Node) ->
dport_send(To, Msg) ->
Node = node(To),
DPrt = case dport(Node) of
- undefined ->
- pong = net_adm:ping(Node),
- dport(Node);
- Prt ->
- Prt
- end,
+ undefined ->
+ pong = net_adm:ping(Node),
+ dport(Node);
+ Prt ->
+ Prt
+ end,
port_command(DPrt, [dmsg_hdr(),
- dmsg_ext({?DOP_SEND,
- ?COOKIE,
- To}),
- dmsg_ext(Msg)]).
+ dmsg_ext({?DOP_SEND,
+ ?COOKIE,
+ To}),
+ dmsg_ext(Msg)]).
dport_reg_send(Node, Name, Msg) ->
DPrt = case dport(Node) of
- undefined ->
- pong = net_adm:ping(Node),
- dport(Node);
- Prt ->
- Prt
- end,
+ undefined ->
+ pong = net_adm:ping(Node),
+ dport(Node);
+ Prt ->
+ Prt
+ end,
port_command(DPrt, [dmsg_hdr(),
- dmsg_ext({?DOP_REG_SEND,
- self(),
- ?COOKIE,
- Name}),
- dmsg_ext(Msg)]).
+ dmsg_ext({?DOP_REG_SEND,
+ self(),
+ ?COOKIE,
+ Name}),
+ dmsg_ext(Msg)]).
dport(Node) when is_atom(Node) ->
case catch erts_debug:get_internal_state(available_internal_state) of
- true -> true;
- _ -> erts_debug:set_internal_state(available_internal_state, true)
+ true -> true;
+ _ -> erts_debug:set_internal_state(available_internal_state, true)
end,
erts_debug:get_internal_state({dist_port, Node}).
@@ -1136,11 +1107,7 @@ stop_busy_dist_port_tracer(_) ->
busy_dist_port_tracer() ->
receive
- {monitor, _SuspendedProcess, busy_dist_port, _Port} = M ->
- erlang:display(M),
- busy_dist_port_tracer()
+ {monitor, _SuspendedProcess, busy_dist_port, _Port} = M ->
+ erlang:display(M),
+ busy_dist_port_tracer()
end.
-
-
-
-
diff --git a/erts/emulator/test/erts_debug_SUITE.erl b/erts/emulator/test/erts_debug_SUITE.erl
index 35677f9953..23871585f7 100644
--- a/erts/emulator/test/erts_debug_SUITE.erl
+++ b/erts/emulator/test/erts_debug_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -19,42 +19,18 @@
%%
-module(erts_debug_SUITE).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2,end_per_testcase/2,
- test_size/1,flat_size_big/1,df/1,
+-export([all/0, suite/0,
+ test_size/1,flat_size_big/1,df/1,term_type/1,
instructions/1]).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 2}}].
all() ->
- [test_size, flat_size_big, df, instructions].
-
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- Dog=?t:timetrap(?t:minutes(2)),
- [{watchdog, Dog}|Config].
-
-end_per_testcase(_Func, Config) ->
- Dog=?config(watchdog, Config),
- ?t:timetrap_cancel(Dog).
+ [test_size, flat_size_big, df, instructions, term_type].
test_size(Config) when is_list(Config) ->
ConsCell1 = id([a|b]),
@@ -138,9 +114,50 @@ flat_size_big_1(Term, Size0, Limit) when Size0 < Limit ->
end;
flat_size_big_1(_, _, _) -> ok.
+
+term_type(Config) when is_list(Config) ->
+ Ts = [{fixnum, 1},
+ {fixnum, -1},
+ {bignum, 1 bsl 300},
+ {bignum, -(1 bsl 300)},
+ {hfloat, 0.0},
+ {hfloat, 0.0/-1},
+ {hfloat, 1.0/(1 bsl 302)},
+ {hfloat, 1.0*(1 bsl 302)},
+ {hfloat, -1.0/(1 bsl 302)},
+ {hfloat, -1.0*(1 bsl 302)},
+ {hfloat, 3.1416},
+ {hfloat, 1.0e18},
+ {hfloat, -3.1416},
+ {hfloat, -1.0e18},
+
+ {heap_binary, <<1,2,3>>},
+ {refc_binary, <<0:(8*80)>>},
+ {sub_binary, <<5:7>>},
+
+ {flatmap, #{ a => 1}},
+ {hashmap, maps:from_list([{I,I}||I <- lists:seq(1,76)])},
+
+ {list, [1,2,3]},
+ {nil, []},
+ {tuple, {1,2,3}},
+ {tuple, {}},
+
+ {export, fun lists:sort/1},
+ {'fun', fun() -> ok end},
+ {pid, self()},
+ {atom, atom}],
+ lists:foreach(fun({E,Val}) ->
+ R = erts_internal:term_type(Val),
+ io:format("expecting term type ~w, got ~w (~p)~n", [E,R,Val]),
+ E = R
+ end, Ts),
+ ok.
+
+
df(Config) when is_list(Config) ->
P0 = pps(),
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
ok = file:set_cwd(PrivDir),
AllLoaded = [M || {M,_} <- code:all_loaded()],
diff --git a/erts/emulator/test/estone_SUITE.erl b/erts/emulator/test/estone_SUITE.erl
index d49ab3de4d..3ce849b88e 100644
--- a/erts/emulator/test/estone_SUITE.erl
+++ b/erts/emulator/test/estone_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -19,9 +19,8 @@
-module(estone_SUITE).
%% Test functions
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,estone/1,estone_bench/1]).
--export([init_per_testcase/2, end_per_testcase/2]).
+-export([all/0, suite/0, groups/0,
+ estone/1, estone_bench/1]).
%% Internal exports for EStone tests
-export([lists/1,
@@ -46,12 +45,9 @@
run_micro/3,p1/1,ppp/3,macro/2,micros/0]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include_lib("common_test/include/ct_event.hrl").
-%% Test suite defines
--define(default_timeout, ?t:minutes(10)).
-
%% EStone defines
-define(TOTAL, (3000 * 1000 * 100)). %% 300 secs
-define(BIGPROCS, 2).
@@ -66,17 +62,9 @@
str}). %% Header string
-
-
-init_per_testcase(_Case, Config) ->
- ?line Dog=test_server:timetrap(?default_timeout),
- [{watchdog, Dog}|Config].
-end_per_testcase(_Case, Config) ->
- Dog=?config(watchdog, Config),
- ?t:timetrap_cancel(Dog),
- ok.
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 4}}].
all() ->
[estone].
@@ -84,34 +72,18 @@ all() ->
groups() ->
[{estone_bench, [{repeat,50}],[estone_bench]}].
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-estone(suite) ->
- [];
-estone(doc) ->
- ["EStone Test"];
+%% EStone Test
estone(Config) when is_list(Config) ->
- ?line DataDir = ?config(data_dir,Config),
- ?line Mhz=get_cpu_speed(os:type(),DataDir),
- ?line L = ?MODULE:macro(?MODULE:micros(),DataDir),
- ?line {Total, Stones} = sum_micros(L, 0, 0),
- ?line pp(Mhz,Total,Stones,L),
- ?line {comment,Mhz ++ " MHz, " ++
- integer_to_list(Stones) ++ " ESTONES"}.
+ DataDir = proplists:get_value(data_dir,Config),
+ Mhz=get_cpu_speed(os:type(),DataDir),
+ L = ?MODULE:macro(?MODULE:micros(),DataDir),
+ {Total, Stones} = sum_micros(L, 0, 0),
+ pp(Mhz,Total,Stones,L),
+ {comment,Mhz ++ " MHz, " ++ integer_to_list(Stones) ++ " ESTONES"}.
estone_bench(Config) ->
- DataDir = ?config(data_dir,Config),
+ DataDir = proplists:get_value(data_dir,Config),
L = ?MODULE:macro(?MODULE:micros(),DataDir),
[ct_event:notify(
#event{name = benchmark_data,
@@ -392,7 +364,7 @@ monotonic_time() ->
try erlang:monotonic_time() catch error:undef -> erlang:now() end.
subtr(Before, After) when is_integer(Before), is_integer(After) ->
- erlang:convert_time_unit(After-Before, native, micro_seconds);
+ erlang:convert_time_unit(After-Before, native, microsecond);
subtr({_,_,_}=Before, {_,_,_}=After) ->
timer:now_diff(After, Before).
@@ -1136,4 +1108,3 @@ wait_for_pids([P|Tail]) ->
send_procs([P|Tail], Msg) -> P ! Msg, send_procs(Tail, Msg);
send_procs([], _) -> ok.
-
diff --git a/erts/emulator/test/evil_SUITE.erl b/erts/emulator/test/evil_SUITE.erl
index 484d2a8bf5..9416ac7a02 100644
--- a/erts/emulator/test/evil_SUITE.erl
+++ b/erts/emulator/test/evil_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -19,23 +19,22 @@
-module(evil_SUITE).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2,end_per_testcase/2,
- heap_frag/1,
- encode_decode_ext/1,
- decode_integer_ext/1,
- decode_small_big_ext/1,
- decode_large_big_ext/1,
- decode_small_big_ext_neg/1,
- decode_large_big_ext_neg/1,
- decode_too_small/1,
- decode_pos_neg_zero/1
- ]).
+-export([all/0, suite/0,
+ heap_frag/1,
+ encode_decode_ext/1,
+ decode_integer_ext/1,
+ decode_small_big_ext/1,
+ decode_large_big_ext/1,
+ decode_small_big_ext_neg/1,
+ decode_large_big_ext_neg/1,
+ decode_too_small/1,
+ decode_pos_neg_zero/1]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {seconds, 30}}].
all() ->
[heap_frag, encode_decode_ext, decode_integer_ext,
@@ -43,41 +42,16 @@ all() ->
decode_small_big_ext_neg, decode_large_big_ext_neg,
decode_too_small, decode_pos_neg_zero].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-init_per_testcase(_Case, Config) ->
- ?line Dog = test_server:timetrap(?t:minutes(0.5)),
- [{watchdog, Dog}|Config].
-
-end_per_testcase(_Case, Config) ->
- Dog=?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
- ok.
-
heap_frag(Config) when is_list(Config) ->
N = 512,
Self = self(),
- ?line Pid = spawn_link(fun() -> appender(Self, N) end),
+ Pid = spawn_link(fun() -> appender(Self, N) end),
receive
- {Pid,Res} ->
- ?line Res = my_appender(N);
- Garbage ->
- io:format("Garbage: ~p\n", [Garbage]),
- ?line ?t:fail(got_garbage)
+ {Pid,Res} ->
+ Res = my_appender(N);
+ Garbage ->
+ io:format("Garbage: ~p\n", [Garbage]),
+ ct:fail(got_garbage)
end.
@@ -87,29 +61,28 @@ heap_frag(Config) when is_list(Config) ->
%% These test cases are not "evil" but the next test case is....
encode_decode_ext(Config) when is_list(Config) ->
- ?line enc_dec( 2, 0), % SMALL_INTEGER_EXT smallest
- ?line enc_dec( 2, 255), % SMALL_INTEGER_EXT largest
- ?line enc_dec( 5, 256), % INTEGER_EXT smallest pos (*)
- ?line enc_dec( 5, -1), % INTEGER_EXT largest neg
-
- ?line enc_dec( 5, 16#07ffffff), % INTEGER_EXT largest (28 bits)
- ?line enc_dec( 5,-16#08000000), % INTEGER_EXT smallest
- ?line enc_dec( 7, 16#08000000), % SMALL_BIG_EXT smallest pos(*)
- ?line enc_dec( 7,-16#08000001), % SMALL_BIG_EXT largest neg (*)
-
- ?line enc_dec( 7, 16#7fffffff), % SMALL_BIG_EXT largest i32
- ?line enc_dec( 7,-16#80000000), % SMALL_BIG_EXT smallest i32
-
- ?line enc_dec( 7, 16#80000000), % SMALL_BIG_EXT u32
- ?line enc_dec( 7, 16#ffffffff), % SMALL_BIG_EXT largest u32
-
- ?line enc_dec( 9, 16#7fffffffffff), % largest i48
- ?line enc_dec( 9,-16#800000000000), % smallest i48
- ?line enc_dec( 9, 16#ffffffffffff), % largest u48
- ?line enc_dec(11, 16#7fffffffffffffff), % largest i64
- ?line enc_dec(11,-16#8000000000000000), % smallest i64
- ?line enc_dec(11, 16#ffffffffffffffff), % largest u64
-
+ enc_dec( 2, 0), % SMALL_INTEGER_EXT smallest
+ enc_dec( 2, 255), % SMALL_INTEGER_EXT largest
+ enc_dec( 5, 256), % INTEGER_EXT smallest pos (*)
+ enc_dec( 5, -1), % INTEGER_EXT largest neg
+
+ enc_dec( 5, 16#07ffffff), % INTEGER_EXT largest (28 bits)
+ enc_dec( 5,-16#08000000), % INTEGER_EXT smallest
+ enc_dec( 7, 16#08000000), % SMALL_BIG_EXT smallest pos(*)
+ enc_dec( 7,-16#08000001), % SMALL_BIG_EXT largest neg (*)
+
+ enc_dec( 7, 16#7fffffff), % SMALL_BIG_EXT largest i32
+ enc_dec( 7,-16#80000000), % SMALL_BIG_EXT smallest i32
+
+ enc_dec( 7, 16#80000000), % SMALL_BIG_EXT u32
+ enc_dec( 7, 16#ffffffff), % SMALL_BIG_EXT largest u32
+
+ enc_dec( 9, 16#7fffffffffff), % largest i48
+ enc_dec( 9,-16#800000000000), % smallest i48
+ enc_dec( 9, 16#ffffffffffff), % largest u48
+ enc_dec(11, 16#7fffffffffffffff), % largest i64
+ enc_dec(11,-16#8000000000000000), % smallest i64
+ enc_dec(11, 16#ffffffffffffffff), % largest u64
ok.
@@ -125,213 +98,213 @@ encode_decode_ext(Config) when is_list(Config) ->
%% erl_interface, i.e. not how it is encoded in the test case below.
decode_integer_ext(Config) when is_list(Config) ->
- ?line decode( 0, <<131,98, 0:32>>), % SMALL_INTEGER_EXT
- ?line decode( 42, <<131,98, 42:32>>), % SMALL_INTEGER_EXT
- ?line decode(255, <<131,98,255:32>>), % SMALL_INTEGER_EXT
- ?line decode( 16#08000000, <<131,98, 16#08000000:32>>), % SMALL_BIG_EXT
- ?line decode(-16#08000001, <<131,98,-16#08000001:32>>), % SMALL_BIG_EXT
- ?line decode( 16#7fffffff, <<131,98, 16#7fffffff:32>>), % SMALL_BIG_EXT
- ?line decode(-16#80000000, <<131,98,-16#80000000:32>>), % SMALL_BIG_EXT
+ decode( 0, <<131,98, 0:32>>), % SMALL_INTEGER_EXT
+ decode( 42, <<131,98, 42:32>>), % SMALL_INTEGER_EXT
+ decode(255, <<131,98,255:32>>), % SMALL_INTEGER_EXT
+ decode( 16#08000000, <<131,98, 16#08000000:32>>), % SMALL_BIG_EXT
+ decode(-16#08000001, <<131,98,-16#08000001:32>>), % SMALL_BIG_EXT
+ decode( 16#7fffffff, <<131,98, 16#7fffffff:32>>), % SMALL_BIG_EXT
+ decode(-16#80000000, <<131,98,-16#80000000:32>>), % SMALL_BIG_EXT
ok.
decode_small_big_ext(Config) when is_list(Config) ->
- ?line decode(256,<<131,110,2,0,0,1>>), % INTEGER_EXT
- ?line decode(16#07ffffff,<<131,110,4,0,255,255,255,7>>), % INTEGER_EXT
- ?line decode(16#7fffffff,<<131,110,4,0,255,255,255,127>>), % SMALL_BIG_EXT
-
- ?line decode(42,<<131,110,1,0,42>>), % SMALL_INTEGER_EXT
- ?line decode(42,<<131,110,2,0,42,0>>), % Redundant zeros from now on
- ?line decode(42,<<131,110,3,0,42,0,0>>),
- ?line decode(42,<<131,110,4,0,42,0,0,0>>),
- ?line decode(42,<<131,110,5,0,42,0,0,0,0>>),
- ?line decode(42,<<131,110,6,0,42,0,0,0,0,0>>),
- ?line decode(42,<<131,110,7,0,42,0,0,0,0,0,0>>),
- ?line decode(42,<<131,110,8,0,42,0,0,0,0,0,0,0>>),
+ decode(256,<<131,110,2,0,0,1>>), % INTEGER_EXT
+ decode(16#07ffffff,<<131,110,4,0,255,255,255,7>>), % INTEGER_EXT
+ decode(16#7fffffff,<<131,110,4,0,255,255,255,127>>), % SMALL_BIG_EXT
+
+ decode(42,<<131,110,1,0,42>>), % SMALL_INTEGER_EXT
+ decode(42,<<131,110,2,0,42,0>>), % Redundant zeros from now on
+ decode(42,<<131,110,3,0,42,0,0>>),
+ decode(42,<<131,110,4,0,42,0,0,0>>),
+ decode(42,<<131,110,5,0,42,0,0,0,0>>),
+ decode(42,<<131,110,6,0,42,0,0,0,0,0>>),
+ decode(42,<<131,110,7,0,42,0,0,0,0,0,0>>),
+ decode(42,<<131,110,8,0,42,0,0,0,0,0,0,0>>),
ok.
decode_large_big_ext(Config) when is_list(Config) ->
- ?line decode(256,<<131,111,2:32,0,0,1>>), % INTEGER_EXT
- ?line decode(16#07ffffff,<<131,111,4:32,0,255,255,255,7>>), % INTEG_EXT
- ?line decode(16#7fffffff,<<131,111,4:32,0,255,255,255,127>>), % SMA_BIG
- ?line decode(16#ffffffff,<<131,111,4:32,0,255,255,255,255>>), % SMA_BIG
+ decode(256,<<131,111,2:32,0,0,1>>), % INTEGER_EXT
+ decode(16#07ffffff,<<131,111,4:32,0,255,255,255,7>>), % INTEG_EXT
+ decode(16#7fffffff,<<131,111,4:32,0,255,255,255,127>>), % SMA_BIG
+ decode(16#ffffffff,<<131,111,4:32,0,255,255,255,255>>), % SMA_BIG
N = largest_small_big(),
- ?line decode(N,<<131,111,255:32,0,N:2040/little>>), % SMALL_BIG_EXT
-
- ?line decode(42,<<131,111,1:32,0,42>>),
- ?line decode(42,<<131,111,2:32,0,42,0>>), % Redundant zeros from now on
- ?line decode(42,<<131,111,3:32,0,42,0,0>>),
- ?line decode(42,<<131,111,4:32,0,42,0,0,0>>),
- ?line decode(42,<<131,111,5:32,0,42,0,0,0,0>>),
- ?line decode(42,<<131,111,6:32,0,42,0,0,0,0,0>>),
- ?line decode(42,<<131,111,7:32,0,42,0,0,0,0,0,0>>),
- ?line decode(42,<<131,111,8:32,0,42,0,0,0,0,0,0,0>>),
+ decode(N,<<131,111,255:32,0,N:2040/little>>), % SMALL_BIG_EXT
+
+ decode(42,<<131,111,1:32,0,42>>),
+ decode(42,<<131,111,2:32,0,42,0>>), % Redundant zeros from now on
+ decode(42,<<131,111,3:32,0,42,0,0>>),
+ decode(42,<<131,111,4:32,0,42,0,0,0>>),
+ decode(42,<<131,111,5:32,0,42,0,0,0,0>>),
+ decode(42,<<131,111,6:32,0,42,0,0,0,0,0>>),
+ decode(42,<<131,111,7:32,0,42,0,0,0,0,0,0>>),
+ decode(42,<<131,111,8:32,0,42,0,0,0,0,0,0,0>>),
ok.
decode_small_big_ext_neg(Config) when is_list(Config) ->
- ?line decode(-1,<<131,110,1,1,1>>), % INTEGER_EXT
- ?line decode(-16#08000000,<<131,110,4,1,0,0,0,8>>), % INTEGER_EXT
- ?line decode(-16#80000000,<<131,110,4,1,0,0,0,128>>), % SMALL_BIG_EXT
- ?line decode(-16#ffffffff,<<131,110,4,1,255,255,255,255>>), % SMALL_BIG_EXT
+ decode(-1,<<131,110,1,1,1>>), % INTEGER_EXT
+ decode(-16#08000000,<<131,110,4,1,0,0,0,8>>), % INTEGER_EXT
+ decode(-16#80000000,<<131,110,4,1,0,0,0,128>>), % SMALL_BIG_EXT
+ decode(-16#ffffffff,<<131,110,4,1,255,255,255,255>>), % SMALL_BIG_EXT
N = largest_small_big(),
- ?line decode(-N,<<131,111,255:32,1,N:2040/little>>), % SMALL_BIG_EXT
-
- ?line decode(-42,<<131,110,1,1,42>>),
- ?line decode(-42,<<131,110,2,1,42,0>>), % Redundant zeros from now on
- ?line decode(-42,<<131,110,3,1,42,0,0>>),
- ?line decode(-42,<<131,110,4,1,42,0,0,0>>),
- ?line decode(-42,<<131,110,5,1,42,0,0,0,0>>),
- ?line decode(-42,<<131,110,6,1,42,0,0,0,0,0>>),
- ?line decode(-42,<<131,110,7,1,42,0,0,0,0,0,0>>),
- ?line decode(-42,<<131,110,8,1,42,0,0,0,0,0,0,0>>),
+ decode(-N,<<131,111,255:32,1,N:2040/little>>), % SMALL_BIG_EXT
+
+ decode(-42,<<131,110,1,1,42>>),
+ decode(-42,<<131,110,2,1,42,0>>), % Redundant zeros from now on
+ decode(-42,<<131,110,3,1,42,0,0>>),
+ decode(-42,<<131,110,4,1,42,0,0,0>>),
+ decode(-42,<<131,110,5,1,42,0,0,0,0>>),
+ decode(-42,<<131,110,6,1,42,0,0,0,0,0>>),
+ decode(-42,<<131,110,7,1,42,0,0,0,0,0,0>>),
+ decode(-42,<<131,110,8,1,42,0,0,0,0,0,0,0>>),
ok.
decode_large_big_ext_neg(Config) when is_list(Config) ->
- ?line decode(-1,<<131,111,1:32,1,1>>), % INTEGER_EXT
- ?line decode(-16#08000000,<<131,111,4:32,1,0,0,0,8>>), % INTEGER_EXT
- ?line decode(-16#80000000,<<131,111,4:32,1,0,0,0,128>>), % SMALL_BIG_EXT
-
- ?line decode(-42,<<131,111,1:32,1,42>>),
- ?line decode(-42,<<131,111,2:32,1,42,0>>), % Redundant zeros from now on
- ?line decode(-42,<<131,111,3:32,1,42,0,0>>),
- ?line decode(-42,<<131,111,4:32,1,42,0,0,0>>),
- ?line decode(-42,<<131,111,5:32,1,42,0,0,0,0>>),
- ?line decode(-42,<<131,111,6:32,1,42,0,0,0,0,0>>),
- ?line decode(-42,<<131,111,7:32,1,42,0,0,0,0,0,0>>),
- ?line decode(-42,<<131,111,8:32,1,42,0,0,0,0,0,0,0>>),
+ decode(-1,<<131,111,1:32,1,1>>), % INTEGER_EXT
+ decode(-16#08000000,<<131,111,4:32,1,0,0,0,8>>), % INTEGER_EXT
+ decode(-16#80000000,<<131,111,4:32,1,0,0,0,128>>), % SMALL_BIG_EXT
+
+ decode(-42,<<131,111,1:32,1,42>>),
+ decode(-42,<<131,111,2:32,1,42,0>>), % Redundant zeros from now on
+ decode(-42,<<131,111,3:32,1,42,0,0>>),
+ decode(-42,<<131,111,4:32,1,42,0,0,0>>),
+ decode(-42,<<131,111,5:32,1,42,0,0,0,0>>),
+ decode(-42,<<131,111,6:32,1,42,0,0,0,0,0>>),
+ decode(-42,<<131,111,7:32,1,42,0,0,0,0,0,0>>),
+ decode(-42,<<131,111,8:32,1,42,0,0,0,0,0,0,0>>),
ok.
decode_pos_neg_zero(Config) when is_list(Config) ->
- ?line decode( 0, <<131,110,0,0>>), % SMALL_BIG_EXT (positive zero)
- ?line decode( 0, <<131,110,1,0,0>>), % SMALL_BIG_EXT (positive zero)
- ?line decode( 0, <<131,110,0,1>>), % SMALL_BIG_EXT (negative zero)
- ?line decode( 0, <<131,110,1,1,0>>), % SMALL_BIG_EXT (negative zero)
+ decode( 0, <<131,110,0,0>>), % SMALL_BIG_EXT (positive zero)
+ decode( 0, <<131,110,1,0,0>>), % SMALL_BIG_EXT (positive zero)
+ decode( 0, <<131,110,0,1>>), % SMALL_BIG_EXT (negative zero)
+ decode( 0, <<131,110,1,1,0>>), % SMALL_BIG_EXT (negative zero)
- ?line decode( 0, <<131,111,0:32,0>>), % SMALL_BIG_EXT (positive zero)
- ?line decode( 0, <<131,111,1:32,0,0>>), % SMALL_BIG_EXT (positive zero)
- ?line decode( 0, <<131,111,0:32,1>>), % SMALL_BIG_EXT (negative zero)
- ?line decode( 0, <<131,111,1:32,1,0>>), % SMALL_BIG_EXT (negative zero)
+ decode( 0, <<131,111,0:32,0>>), % SMALL_BIG_EXT (positive zero)
+ decode( 0, <<131,111,1:32,0,0>>), % SMALL_BIG_EXT (positive zero)
+ decode( 0, <<131,111,0:32,1>>), % SMALL_BIG_EXT (negative zero)
+ decode( 0, <<131,111,1:32,1,0>>), % SMALL_BIG_EXT (negative zero)
N = largest_small_big(),
- ?line decode( N,<<131,110,255,0,N:2040/little>>), % largest SMALL_BIG_EXT
- ?line decode(-N,<<131,110,255,1,N:2040/little>>), % largest SMALL_BIG_EXT
+ decode( N,<<131,110,255,0,N:2040/little>>), % largest SMALL_BIG_EXT
+ decode(-N,<<131,110,255,1,N:2040/little>>), % largest SMALL_BIG_EXT
ok.
%% Test to decode uncompleted encodings for all in "erl_ext_dist.txt"
decode_too_small(Config) when is_list(Config) ->
- ?line decode_badarg(<<131, 97>>),
- ?line decode_badarg(<<131, 98>>),
- ?line decode_badarg(<<131, 98, 0>>),
- ?line decode_badarg(<<131, 98, 0, 0>>),
- ?line decode_badarg(<<131, 98, 0, 0, 0>>),
- ?line decode_badarg(<<131, 99>>),
- ?line decode_badarg(<<131, 99, 0>>),
- ?line decode_badarg(<<131, 99, 0:240>>),
-
- ?line decode_badarg(<<131,100>>),
- ?line decode_badarg(<<131,100, 1:16/big>>),
- ?line decode_badarg(<<131,100, 2:16/big>>),
- ?line decode_badarg(<<131,100, 2:16/big, "A">>),
+ decode_badarg(<<131, 97>>),
+ decode_badarg(<<131, 98>>),
+ decode_badarg(<<131, 98, 0>>),
+ decode_badarg(<<131, 98, 0, 0>>),
+ decode_badarg(<<131, 98, 0, 0, 0>>),
+ decode_badarg(<<131, 99>>),
+ decode_badarg(<<131, 99, 0>>),
+ decode_badarg(<<131, 99, 0:240>>),
+
+ decode_badarg(<<131,100>>),
+ decode_badarg(<<131,100, 1:16/big>>),
+ decode_badarg(<<131,100, 2:16/big>>),
+ decode_badarg(<<131,100, 2:16/big, "A">>),
% FIXME node name "A" seem ok, should it be?
-% ?line decode_badarg(<<131,101,100,1:16/big,"A",42:32/big,0>>),
-
- ?line decode_badarg(<<131,101>>),
- ?line decode_badarg(<<131,101,106>>),
- ?line decode_badarg(<<131,101,255>>),
- ?line decode_badarg(<<131,101,106,42:8/big>>),
- ?line decode_badarg(<<131,101,106,42:16/big>>),
- ?line decode_badarg(<<131,101,255,42:24/big>>),
- ?line decode_badarg(<<131,101,255,42:32/big,0>>),
- ?line decode_badarg(<<131,101,100,1:16/big,"A">>),
- ?line decode_badarg(<<131,101,100,1:16/big,"A",42:32/big>>),
-
- ?line decode_badarg(<<131,102>>),
- ?line decode_badarg(<<131,102,106,42:32/big,0>>),
- ?line decode_badarg(<<131,102,255,42:32/big,0>>),
- ?line decode_badarg(<<131,102,100,1:16/big,"A">>),
- ?line decode_badarg(<<131,102,100,1:16/big,"A",42:32/big>>),
-
- ?line decode_badarg(<<131,103>>),
- ?line decode_badarg(<<131,103,106,42:32/big,0>>),
- ?line decode_badarg(<<131,103,255,42:32/big,0>>),
- ?line decode_badarg(<<131,103,100,1:16/big,"A">>),
- ?line decode_badarg(<<131,103,100,1:16/big,"A",42:32/big>>),
- ?line decode_badarg(<<131,103,100,1:16/big,"A",4:32/big,2:32/big>>),
-
- ?line decode_badarg(<<131,104>>),
- ?line decode_badarg(<<131,104, 1>>),
- ?line decode_badarg(<<131,104, 2, 106>>),
- ?line decode_badarg(<<131,105, 1:32/big>>),
- ?line decode_badarg(<<131,105, 2:32/big, 106>>),
-
- ?line decode_badarg(<<131,107>>),
- ?line decode_badarg(<<131,107, 1:16/big>>),
- ?line decode_badarg(<<131,107, 2:16/big>>),
- ?line decode_badarg(<<131,107, 2:16/big, "A">>),
-
- ?line decode_badarg(<<131,108>>),
- ?line decode_badarg(<<131,108, 1:32/big>>),
- ?line decode_badarg(<<131,108, 2:32/big>>),
- ?line decode_badarg(<<131,108, 2:32/big, 106>>), % FIXME don't use NIL
-
- ?line decode_badarg(<<131,109>>),
- ?line decode_badarg(<<131,109, 1:32/big>>),
- ?line decode_badarg(<<131,109, 2:32/big>>),
- ?line decode_badarg(<<131,109, 2:32/big, 42>>),
+ % decode_badarg(<<131,101,100,1:16/big,"A",42:32/big,0>>),
+
+ decode_badarg(<<131,101>>),
+ decode_badarg(<<131,101,106>>),
+ decode_badarg(<<131,101,255>>),
+ decode_badarg(<<131,101,106,42:8/big>>),
+ decode_badarg(<<131,101,106,42:16/big>>),
+ decode_badarg(<<131,101,255,42:24/big>>),
+ decode_badarg(<<131,101,255,42:32/big,0>>),
+ decode_badarg(<<131,101,100,1:16/big,"A">>),
+ decode_badarg(<<131,101,100,1:16/big,"A",42:32/big>>),
+
+ decode_badarg(<<131,102>>),
+ decode_badarg(<<131,102,106,42:32/big,0>>),
+ decode_badarg(<<131,102,255,42:32/big,0>>),
+ decode_badarg(<<131,102,100,1:16/big,"A">>),
+ decode_badarg(<<131,102,100,1:16/big,"A",42:32/big>>),
+
+ decode_badarg(<<131,103>>),
+ decode_badarg(<<131,103,106,42:32/big,0>>),
+ decode_badarg(<<131,103,255,42:32/big,0>>),
+ decode_badarg(<<131,103,100,1:16/big,"A">>),
+ decode_badarg(<<131,103,100,1:16/big,"A",42:32/big>>),
+ decode_badarg(<<131,103,100,1:16/big,"A",4:32/big,2:32/big>>),
+
+ decode_badarg(<<131,104>>),
+ decode_badarg(<<131,104, 1>>),
+ decode_badarg(<<131,104, 2, 106>>),
+ decode_badarg(<<131,105, 1:32/big>>),
+ decode_badarg(<<131,105, 2:32/big, 106>>),
+
+ decode_badarg(<<131,107>>),
+ decode_badarg(<<131,107, 1:16/big>>),
+ decode_badarg(<<131,107, 2:16/big>>),
+ decode_badarg(<<131,107, 2:16/big, "A">>),
+
+ decode_badarg(<<131,108>>),
+ decode_badarg(<<131,108, 1:32/big>>),
+ decode_badarg(<<131,108, 2:32/big>>),
+ decode_badarg(<<131,108, 2:32/big, 106>>), % FIXME don't use NIL
+
+ decode_badarg(<<131,109>>),
+ decode_badarg(<<131,109, 1:32/big>>),
+ decode_badarg(<<131,109, 2:32/big>>),
+ decode_badarg(<<131,109, 2:32/big, 42>>),
N = largest_small_big(),
- ?line decode_badarg(<<131,110>>),
- ?line decode_badarg(<<131,110,1>>),
- ?line decode_badarg(<<131,110,1,0>>),
- ?line decode_badarg(<<131,110,1,1>>),
- ?line decode_badarg(<<131,110,2,0,42>>),
- ?line decode_badarg(<<131,110,2,1,42>>),
- ?line decode_badarg(<<131,110,255,0,N:2032/little>>),
- ?line decode_badarg(<<131,110,255,1,N:2032/little>>),
-
- ?line decode_badarg(<<131,111>>),
- ?line decode_badarg(<<131,111, 1:32/big>>),
- ?line decode_badarg(<<131,111, 1:32/big,0>>),
- ?line decode_badarg(<<131,111, 1:32/big,1>>),
- ?line decode_badarg(<<131,111, 2:32/big,0,42>>),
- ?line decode_badarg(<<131,111, 2:32/big,1,42>>),
- ?line decode_badarg(<<131,111,256:32/big,0,N:2032/little>>),
- ?line decode_badarg(<<131,111,256:32/big,1,N:2032/little>>),
- ?line decode_badarg(<<131,111,256:32/big,0,N:2040/little>>),
- ?line decode_badarg(<<131,111,256:32/big,1,N:2040/little>>),
- ?line decode_badarg(<<131,111,257:32/big,0,N:2048/little>>),
- ?line decode_badarg(<<131,111,257:32/big,1,N:2048/little>>),
+ decode_badarg(<<131,110>>),
+ decode_badarg(<<131,110,1>>),
+ decode_badarg(<<131,110,1,0>>),
+ decode_badarg(<<131,110,1,1>>),
+ decode_badarg(<<131,110,2,0,42>>),
+ decode_badarg(<<131,110,2,1,42>>),
+ decode_badarg(<<131,110,255,0,N:2032/little>>),
+ decode_badarg(<<131,110,255,1,N:2032/little>>),
+
+ decode_badarg(<<131,111>>),
+ decode_badarg(<<131,111, 1:32/big>>),
+ decode_badarg(<<131,111, 1:32/big,0>>),
+ decode_badarg(<<131,111, 1:32/big,1>>),
+ decode_badarg(<<131,111, 2:32/big,0,42>>),
+ decode_badarg(<<131,111, 2:32/big,1,42>>),
+ decode_badarg(<<131,111,256:32/big,0,N:2032/little>>),
+ decode_badarg(<<131,111,256:32/big,1,N:2032/little>>),
+ decode_badarg(<<131,111,256:32/big,0,N:2040/little>>),
+ decode_badarg(<<131,111,256:32/big,1,N:2040/little>>),
+ decode_badarg(<<131,111,257:32/big,0,N:2048/little>>),
+ decode_badarg(<<131,111,257:32/big,1,N:2048/little>>),
% Emulator dies if trying to create large bignum....
-% ?line decode_badarg(<<131,111,16#ffffffff:32/big,0>>),
-% ?line decode_badarg(<<131,111,16#ffffffff:32/big,1>>),
-
- ?line decode_badarg(<<131, 78>>),
- ?line decode_badarg(<<131, 78, 42>>),
- ?line decode_badarg(<<131, 78, 42, 1>>),
- ?line decode_badarg(<<131, 78, 42, 1:16/big>>),
- ?line decode_badarg(<<131, 78, 42, 2:16/big>>),
- ?line decode_badarg(<<131, 78, 42, 2:16/big, "A">>),
-
- ?line decode_badarg(<<131, 67>>),
-
- ?line decode_badarg(<<131,114>>),
- ?line decode_badarg(<<131,114,0>>),
- ?line decode_badarg(<<131,114,1:16/big>>),
- ?line decode_badarg(<<131,114,1:16/big,100>>),
- ?line decode_badarg(<<131,114,1:16/big,100,1:16/big>>),
- ?line decode_badarg(<<131,114,1:16/big,100,1:16/big,"A">>),
- ?line decode_badarg(<<131,114,1:16/big,100,1:16/big,"A",0>>),
- ?line decode_badarg(<<131,114,1:16/big,100,1:16/big,"A",0,42:8>>),
- ?line decode_badarg(<<131,114,1:16/big,100,1:16/big,"A",0,42:16>>),
- ?line decode_badarg(<<131,114,1:16/big,100,1:16/big,"A",0,42:24>>),
-
- ?line decode_badarg(<<131,117>>), % FIXME needs more tests
+ % decode_badarg(<<131,111,16#ffffffff:32/big,0>>),
+ % decode_badarg(<<131,111,16#ffffffff:32/big,1>>),
+
+ decode_badarg(<<131, 78>>),
+ decode_badarg(<<131, 78, 42>>),
+ decode_badarg(<<131, 78, 42, 1>>),
+ decode_badarg(<<131, 78, 42, 1:16/big>>),
+ decode_badarg(<<131, 78, 42, 2:16/big>>),
+ decode_badarg(<<131, 78, 42, 2:16/big, "A">>),
+
+ decode_badarg(<<131, 67>>),
+
+ decode_badarg(<<131,114>>),
+ decode_badarg(<<131,114,0>>),
+ decode_badarg(<<131,114,1:16/big>>),
+ decode_badarg(<<131,114,1:16/big,100>>),
+ decode_badarg(<<131,114,1:16/big,100,1:16/big>>),
+ decode_badarg(<<131,114,1:16/big,100,1:16/big,"A">>),
+ decode_badarg(<<131,114,1:16/big,100,1:16/big,"A",0>>),
+ decode_badarg(<<131,114,1:16/big,100,1:16/big,"A",0,42:8>>),
+ decode_badarg(<<131,114,1:16/big,100,1:16/big,"A",0,42:16>>),
+ decode_badarg(<<131,114,1:16/big,100,1:16/big,"A",0,42:24>>),
+
+ decode_badarg(<<131,117>>), % FIXME needs more tests
ok.
@@ -380,12 +353,11 @@ my_appender_1(N, T0) ->
U = rnd_term(),
T = [U|T0],
my_appender_1(N-1, T).
-
+
seed() ->
- random:seed(3172, 9815, 20129).
+ rand:seed(exsplus, {3172,9815,20129}).
rnd_term() ->
- U0 = random:uniform(),
+ U0 = rand:uniform(),
B = <<U0/float>>,
{U0,U0 * 2.5 + 3.14,[U0*2.3,B]}.
-
diff --git a/erts/emulator/test/exception_SUITE.erl b/erts/emulator/test/exception_SUITE.erl
index 11caea3698..76e3556bc4 100644
--- a/erts/emulator/test/exception_SUITE.erl
+++ b/erts/emulator/test/exception_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,67 +20,52 @@
-module(exception_SUITE).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- badmatch/1, pending_errors/1, nil_arith/1,
+-export([all/0, suite/0,
+ badmatch/1, pending_errors/1, nil_arith/1,
stacktrace/1, nested_stacktrace/1, raise/1, gunilla/1, per/1,
- exception_with_heap_frag/1, line_numbers/1]).
+ exception_with_heap_frag/1, line_numbers/1]).
-export([bad_guy/2]).
-export([crash/1]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-import(lists, [foreach/2]).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {seconds, 10}}].
all() ->
[badmatch, pending_errors, nil_arith, stacktrace,
nested_stacktrace, raise, gunilla, per,
exception_with_heap_frag, line_numbers].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-define(try_match(E),
- catch ?MODULE:bar(),
- {'EXIT', {{badmatch, nomatch}, _}} = (catch E = id(nomatch))).
+ catch ?MODULE:bar(),
+ {'EXIT', {{badmatch, nomatch}, _}} = (catch E = id(nomatch))).
%% Test that deliberately bad matches are reported correctly.
badmatch(Config) when is_list(Config) ->
- ?line ?try_match(a),
- ?line ?try_match(42),
- ?line ?try_match({a, b, c}),
- ?line ?try_match([]),
- ?line ?try_match(1.0),
+ ?try_match(a),
+ ?try_match(42),
+ ?try_match({a, b, c}),
+ ?try_match([]),
+ ?try_match(1.0),
ok.
%% Test various exceptions, in the presence of a previous error suppressed
%% in a guard.
pending_errors(Config) when is_list(Config) ->
- ?line pending(e_badmatch, {badmatch, b}),
- ?line pending(x, function_clause),
- ?line pending(e_case, {case_clause, xxx}),
- ?line pending(e_if, if_clause),
- ?line pending(e_badarith, badarith),
- ?line pending(e_undef, undef),
- ?line pending(e_timeoutval, timeout_value),
- ?line pending(e_badarg, badarg),
- ?line pending(e_badarg_spawn, badarg),
+ pending(e_badmatch, {badmatch, b}),
+ pending(x, function_clause),
+ pending(e_case, {case_clause, xxx}),
+ pending(e_if, if_clause),
+ pending(e_badarith, badarith),
+ pending(e_undef, undef),
+ pending(e_timeoutval, timeout_value),
+ pending(e_badarg, badarg),
+ pending(e_badarg_spawn, badarg),
ok.
bad_guy(pe_badarith, Other) when Other+1 == 0 -> % badarith (suppressed)
@@ -89,11 +74,11 @@ bad_guy(pe_badarg, Other) when length(Other) > 0 -> % badarg (suppressed)
ok;
bad_guy(_, e_case) ->
case id(xxx) of
- ok -> ok
+ ok -> ok
end; % case_clause
bad_guy(_, e_if) ->
if
- a == b -> ok
+ a == b -> ok
end; % if_clause
bad_guy(_, e_badarith) ->
1+b; % badarith
@@ -101,9 +86,9 @@ bad_guy(_, e_undef) ->
non_existing_module:foo(); % undef
bad_guy(_, e_timeoutval) ->
receive
- after arne -> % timeout_value
- ok
- end;
+ after arne -> % timeout_value
+ ok
+ end;
bad_guy(_, e_badarg) ->
node(xxx); % badarg
bad_guy(_, e_badarg_spawn) ->
@@ -122,30 +107,30 @@ pending(First, Second, Expected) ->
pending_catched(First, Second, Expected) ->
ok = io:format("Catching bad_guy(~p, ~p)", [First, Second]),
case catch bad_guy(First, Second) of
- {'EXIT', Reason} ->
- pending(Reason, bad_guy, [First, Second], Expected);
- Other ->
- test_server:fail({not_exit, Other})
+ {'EXIT', Reason} ->
+ pending(Reason, bad_guy, [First, Second], Expected);
+ Other ->
+ ct:fail({not_exit, Other})
end.
pending_exit_message(Args, Expected) ->
ok = io:format("Trapping EXITs from spawn_link(~p, ~p, ~p)",
- [?MODULE, bad_guy, Args]),
+ [?MODULE, bad_guy, Args]),
process_flag(trap_exit, true),
Pid = spawn_link(?MODULE, bad_guy, Args),
receive
- {'EXIT', Pid, Reason} ->
- pending(Reason, bad_guy, Args, Expected);
- Other ->
- test_server:fail({unexpected_message, Other})
+ {'EXIT', Pid, Reason} ->
+ pending(Reason, bad_guy, Args, Expected);
+ Other ->
+ ct:fail({unexpected_message, Other})
after 10000 ->
- test_server:fail(timeout)
+ ct:fail(timeout)
end,
process_flag(trap_exit, false).
pending({badarg,[{erlang,Bif,BifArgs,Loc1},
- {?MODULE,Func,Arity,Loc2}|_]},
- Func, Args, _Code)
+ {?MODULE,Func,Arity,Loc2}|_]},
+ Func, Args, _Code)
when is_atom(Bif), is_list(BifArgs), length(Args) =:= Arity,
is_list(Loc1), is_list(Loc2) ->
ok;
@@ -159,67 +144,67 @@ pending({Code,[{?MODULE,Func,Arity,Loc}|_]}, Func, Args, Code)
when length(Args) =:= Arity, is_list(Loc) ->
ok;
pending(Reason, _Function, _Args, _Code) ->
- test_server:fail({bad_exit_reason,Reason}).
+ ct:fail({bad_exit_reason,Reason}).
%% Test that doing arithmetics on [] gives a badarith EXIT and not a crash.
nil_arith(Config) when is_list(Config) ->
- ?line ba_plus_minus_times([], []),
-
- ?line ba_plus_minus_times([], 0),
- ?line ba_plus_minus_times([], 42),
- ?line ba_plus_minus_times([], 38724978123478923784),
- ?line ba_plus_minus_times([], 38.72),
-
- ?line ba_plus_minus_times(0, []),
- ?line ba_plus_minus_times(334, []),
- ?line ba_plus_minus_times(387249797813478923784, []),
- ?line ba_plus_minus_times(344.22, []),
-
- ?line ba_div_rem([], []),
-
- ?line ba_div_rem([], 0),
- ?line ba_div_rem([], 1),
- ?line ba_div_rem([], 42),
- ?line ba_div_rem([], 38724978123478923784),
- ?line ba_div_rem(344.22, []),
-
- ?line ba_div_rem(0, []),
- ?line ba_div_rem(1, []),
- ?line ba_div_rem(334, []),
- ?line ba_div_rem(387249797813478923784, []),
- ?line ba_div_rem(344.22, []),
-
- ?line ba_div_rem(344.22, 0.0),
- ?line ba_div_rem(1, 0.0),
- ?line ba_div_rem(392873498733971, 0.0),
-
- ?line ba_bop([], []),
- ?line ba_bop(0, []),
- ?line ba_bop(42, []),
- ?line ba_bop(-42342742987343, []),
- ?line ba_bop(238.342, []),
- ?line ba_bop([], 0),
- ?line ba_bop([], -243),
- ?line ba_bop([], 243),
- ?line ba_bop([], 2438724982478933),
- ?line ba_bop([], 3987.37),
-
- ?line ba_bnot([]),
- ?line ba_bnot(23.33),
-
- ?line ba_shift([], []),
- ?line ba_shift([], 0),
- ?line ba_shift([], 4),
- ?line ba_shift([], -4),
- ?line ba_shift([], 2343333333333),
- ?line ba_shift([], -333333333),
- ?line ba_shift([], 234.00),
- ?line ba_shift(23, []),
- ?line ba_shift(0, []),
- ?line ba_shift(-3433443433433323, []),
- ?line ba_shift(433443433433323, []),
- ?line ba_shift(343.93, []),
+ ba_plus_minus_times([], []),
+
+ ba_plus_minus_times([], 0),
+ ba_plus_minus_times([], 42),
+ ba_plus_minus_times([], 38724978123478923784),
+ ba_plus_minus_times([], 38.72),
+
+ ba_plus_minus_times(0, []),
+ ba_plus_minus_times(334, []),
+ ba_plus_minus_times(387249797813478923784, []),
+ ba_plus_minus_times(344.22, []),
+
+ ba_div_rem([], []),
+
+ ba_div_rem([], 0),
+ ba_div_rem([], 1),
+ ba_div_rem([], 42),
+ ba_div_rem([], 38724978123478923784),
+ ba_div_rem(344.22, []),
+
+ ba_div_rem(0, []),
+ ba_div_rem(1, []),
+ ba_div_rem(334, []),
+ ba_div_rem(387249797813478923784, []),
+ ba_div_rem(344.22, []),
+
+ ba_div_rem(344.22, 0.0),
+ ba_div_rem(1, 0.0),
+ ba_div_rem(392873498733971, 0.0),
+
+ ba_bop([], []),
+ ba_bop(0, []),
+ ba_bop(42, []),
+ ba_bop(-42342742987343, []),
+ ba_bop(238.342, []),
+ ba_bop([], 0),
+ ba_bop([], -243),
+ ba_bop([], 243),
+ ba_bop([], 2438724982478933),
+ ba_bop([], 3987.37),
+
+ ba_bnot([]),
+ ba_bnot(23.33),
+
+ ba_shift([], []),
+ ba_shift([], 0),
+ ba_shift([], 4),
+ ba_shift([], -4),
+ ba_shift([], 2343333333333),
+ ba_shift([], -333333333),
+ ba_shift([], 234.00),
+ ba_shift(23, []),
+ ba_shift(0, []),
+ ba_shift(-3433443433433323, []),
+ ba_shift(433443433433323, []),
+ ba_shift(343.93, []),
ok.
ba_plus_minus_times(A, B) ->
@@ -251,7 +236,7 @@ ba_shift(A, B) ->
{'EXIT', {badarith, _}} = (catch A bsl B),
io:format("~p bsr ~p", [A, B]),
{'EXIT', {badarith, _}} = (catch A bsr B).
-
+
ba_bnot(A) ->
io:format("bnot ~p", [A]),
{'EXIT', {badarith, _}} = (catch bnot A).
@@ -260,38 +245,38 @@ ba_bnot(A) ->
stacktrace(Conf) when is_list(Conf) ->
Tag = make_ref(),
- ?line {_,Mref} = spawn_monitor(fun() -> exit({Tag,erlang:get_stacktrace()}) end),
- ?line {Tag,[]} = receive {'DOWN',Mref,_,_,Info} -> Info end,
+ {_,Mref} = spawn_monitor(fun() -> exit({Tag,erlang:get_stacktrace()}) end),
+ {Tag,[]} = receive {'DOWN',Mref,_,_,Info} -> Info end,
V = [make_ref()|self()],
- ?line {value2,{caught1,badarg,[{erlang,abs,[V],_}|_]=St1}} =
- stacktrace_1({'abs',V}, error, {value,V}),
- ?line St1 = erase(stacktrace1),
- ?line St1 = erase(stacktrace2),
- ?line St1 = erlang:get_stacktrace(),
- ?line {caught2,{error,badarith},[{?MODULE,my_add,2,_}|_]=St2} =
- stacktrace_1({'div',{1,0}}, error, {'add',{0,a}}),
- ?line [{?MODULE,my_div,2,_}|_] = erase(stacktrace1),
- ?line St2 = erase(stacktrace2),
- ?line St2 = erlang:get_stacktrace(),
- ?line {caught2,{error,{try_clause,V}},[{?MODULE,stacktrace_1,3,_}|_]=St3} =
- stacktrace_1({value,V}, error, {value,V}),
- ?line St3 = erase(stacktrace1),
- ?line St3 = erase(stacktrace2),
- ?line St3 = erlang:get_stacktrace(),
- ?line {caught2,{throw,V},[{?MODULE,foo,1,_}|_]=St4} =
- stacktrace_1({value,V}, error, {throw,V}),
- ?line [{?MODULE,stacktrace_1,3,_}|_] = erase(stacktrace1),
- ?line St4 = erase(stacktrace2),
- ?line St4 = erlang:get_stacktrace(),
+ {value2,{caught1,badarg,[{erlang,abs,[V],_}|_]=St1}} =
+ stacktrace_1({'abs',V}, error, {value,V}),
+ St1 = erase(stacktrace1),
+ St1 = erase(stacktrace2),
+ St1 = erlang:get_stacktrace(),
+ {caught2,{error,badarith},[{?MODULE,my_add,2,_}|_]=St2} =
+ stacktrace_1({'div',{1,0}}, error, {'add',{0,a}}),
+ [{?MODULE,my_div,2,_}|_] = erase(stacktrace1),
+ St2 = erase(stacktrace2),
+ St2 = erlang:get_stacktrace(),
+ {caught2,{error,{try_clause,V}},[{?MODULE,stacktrace_1,3,_}|_]=St3} =
+ stacktrace_1({value,V}, error, {value,V}),
+ St3 = erase(stacktrace1),
+ St3 = erase(stacktrace2),
+ St3 = erlang:get_stacktrace(),
+ {caught2,{throw,V},[{?MODULE,foo,1,_}|_]=St4} =
+ stacktrace_1({value,V}, error, {throw,V}),
+ [{?MODULE,stacktrace_1,3,_}|_] = erase(stacktrace1),
+ St4 = erase(stacktrace2),
+ St4 = erlang:get_stacktrace(),
try
- ?line stacktrace_2()
+ stacktrace_2()
catch
- error:{badmatch,_} ->
- [{?MODULE,stacktrace_2,0,_},
- {?MODULE,stacktrace,1,_}|_] =
- erlang:get_stacktrace(),
- ok
+ error:{badmatch,_} ->
+ [{?MODULE,stacktrace_2,0,_},
+ {?MODULE,stacktrace,1,_}|_] =
+ erlang:get_stacktrace(),
+ ok
end.
stacktrace_1(X, C1, Y) ->
@@ -303,7 +288,7 @@ stacktrace_1(X, C1, Y) ->
C1:D1 -> {caught1,D1,erlang:get_stacktrace()}
after
put(stacktrace1, erlang:get_stacktrace()),
- foo(Y)
+ foo(Y)
end of
V2 -> {value2,V2}
catch
@@ -319,21 +304,21 @@ stacktrace_2() ->
nested_stacktrace(Conf) when is_list(Conf) ->
V = [{make_ref()}|[self()]],
- ?line value1 =
- nested_stacktrace_1({{value,{V,x1}},void,{V,x1}},
- {void,void,void}),
- ?line {caught1,
- [{?MODULE,my_add,2,_}|_],
- value2,
- [{?MODULE,my_add,2,_}|_]} =
- nested_stacktrace_1({{'add',{V,x1}},error,badarith},
- {{value,{V,x2}},void,{V,x2}}),
- ?line {caught1,
- [{?MODULE,my_add,2,_}|_],
- {caught2,[{erlang,abs,[V],_}|_]},
- [{erlang,abs,[V],_}|_]} =
- nested_stacktrace_1({{'add',{V,x1}},error,badarith},
- {{'abs',V},error,badarg}),
+ value1 =
+ nested_stacktrace_1({{value,{V,x1}},void,{V,x1}},
+ {void,void,void}),
+ {caught1,
+ [{?MODULE,my_add,2,_}|_],
+ value2,
+ [{?MODULE,my_add,2,_}|_]} =
+ nested_stacktrace_1({{'add',{V,x1}},error,badarith},
+ {{value,{V,x2}},void,{V,x2}}),
+ {caught1,
+ [{?MODULE,my_add,2,_}|_],
+ {caught2,[{erlang,abs,[V],_}|_]},
+ [{erlang,abs,[V],_}|_]} =
+ nested_stacktrace_1({{'add',{V,x1}},error,badarith},
+ {{'abs',V},error,badarg}),
ok.
nested_stacktrace_1({X1,C1,V1}, {X2,C2,V2}) ->
@@ -341,64 +326,64 @@ nested_stacktrace_1({X1,C1,V1}, {X2,C2,V2}) ->
V1 -> value1
catch
C1:V1 ->
- S1 = erlang:get_stacktrace(),
+ S1 = erlang:get_stacktrace(),
T2 =
- try foo(X2) of
- V2 -> value2
- catch
- C2:V2 -> {caught2,erlang:get_stacktrace()}
- end,
+ try foo(X2) of
+ V2 -> value2
+ catch
+ C2:V2 -> {caught2,erlang:get_stacktrace()}
+ end,
{caught1,S1,T2,erlang:get_stacktrace()}
end.
raise(Conf) when is_list(Conf) ->
- ?line erase(raise),
- ?line A =
- try
- ?line try foo({'div',{1,0}})
- catch
- error:badarith ->
- put(raise, A0 = erlang:get_stacktrace()),
- ?line erlang:raise(error, badarith, A0)
- end
- catch
- error:badarith ->
- ?line A1 = erlang:get_stacktrace(),
- ?line A1 = get(raise)
- end,
- ?line A = erlang:get_stacktrace(),
- ?line A = get(raise),
- ?line [{?MODULE,my_div,2,_}|_] = A,
+ erase(raise),
+ A =
+ try
+ try foo({'div',{1,0}})
+ catch
+ error:badarith ->
+ put(raise, A0 = erlang:get_stacktrace()),
+ erlang:raise(error, badarith, A0)
+ end
+ catch
+ error:badarith ->
+ A1 = erlang:get_stacktrace(),
+ A1 = get(raise)
+ end,
+ A = erlang:get_stacktrace(),
+ A = get(raise),
+ [{?MODULE,my_div,2,_}|_] = A,
%%
N = 8, % Must be even
- ?line N = erlang:system_flag(backtrace_depth, N),
- ?line B = odd_even(N, []),
- ?line try even(N)
- catch error:function_clause -> ok
- end,
- ?line B = erlang:get_stacktrace(),
+ N = erlang:system_flag(backtrace_depth, N),
+ B = odd_even(N, []),
+ try even(N)
+ catch error:function_clause -> ok
+ end,
+ B = erlang:get_stacktrace(),
%%
- ?line C0 = odd_even(N+1, []),
- ?line C = lists:sublist(C0, N),
- ?line try odd(N+1)
- catch error:function_clause -> ok
- end,
- ?line C = erlang:get_stacktrace(),
- ?line try erlang:raise(error, function_clause, C0)
- catch error:function_clause -> ok
- end,
- ?line C = erlang:get_stacktrace(),
+ C0 = odd_even(N+1, []),
+ C = lists:sublist(C0, N),
+ try odd(N+1)
+ catch error:function_clause -> ok
+ end,
+ C = erlang:get_stacktrace(),
+ try erlang:raise(error, function_clause, C0)
+ catch error:function_clause -> ok
+ end,
+ C = erlang:get_stacktrace(),
ok.
odd_even(N, R) when is_integer(N), N > 1 ->
odd_even(N-1,
- [if (N rem 2) == 0 ->
- {?MODULE,even,1,[{file,"odd_even.erl"},{line,3}]};
- true ->
- {?MODULE,odd,1,[{file,"odd_even.erl"},{line,6}]}
- end|R]);
+ [if (N rem 2) == 0 ->
+ {?MODULE,even,1,[{file,"odd_even.erl"},{line,3}]};
+ true ->
+ {?MODULE,odd,1,[{file,"odd_even.erl"},{line,6}]}
+ end|R]);
odd_even(1, R) ->
[{?MODULE,odd,[1],[{file,"odd_even.erl"},{line,5}]}|R].
@@ -428,18 +413,18 @@ my_add(A, B) ->
my_abs(X) -> abs(X).
gunilla(Config) when is_list(Config) ->
- ?line {throw,kalle} = gunilla_1(),
- ?line [] = erlang:get_stacktrace(),
+ {throw,kalle} = gunilla_1(),
+ [] = erlang:get_stacktrace(),
ok.
gunilla_1() ->
try try arne()
- after
- pelle
- end
+ after
+ pelle
+ end
catch
- C:R ->
- {C,R}
+ C:R ->
+ {C,R}
end.
arne() ->
@@ -448,18 +433,18 @@ arne() ->
per(Config) when is_list(Config) ->
try
- t1(0,pad,0),
- t2(0,pad,0)
+ t1(0,pad,0),
+ t2(0,pad,0)
catch
- error:badarith ->
- ok
+ error:badarith ->
+ ok
end.
t1(_,X,_) ->
- (1 bsl X) + 1.
+ (1 bsl X) + 1.
t2(_,X,_) ->
- (X bsl 1) + 1.
+ (X bsl 1) + 1.
%%
%% Make sure that even if a BIF builds an heap fragment, then causes an exception,
@@ -471,155 +456,155 @@ exception_with_heap_frag(Config) when is_list(Config) ->
%% Floats are only validated when the heap fragment has been allocated.
BadFloat = <<131,99,53,46,48,$X,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,101,45,48,49,0,0,0,0,0>>,
- ?line do_exception_with_heap_frag(BadFloat, Sizes),
+ do_exception_with_heap_frag(BadFloat, Sizes),
%% {Binary,BadFloat}: When the error in float is discovered, a refc-binary
%% has been allocated and the list of refc-binaries goes through the
%% heap fragment.
BinAndFloat =
- <<131,104,2,109,0,0,1,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,
- 21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,
- 46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,
- 71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
- 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,
- 116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,
- 135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,
- 154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,
- 173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
- 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,
- 211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,
- 230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,
- 249,250,251,252,253,254,255,99,51,46,49,52,$B,$l,$u,$r,$f,48,48,48,48,48,48,
- 48,48,49,50,52,51,52,101,43,48,48,0,0,0,0,0>>,
- ?line do_exception_with_heap_frag(BinAndFloat, Sizes),
+ <<131,104,2,109,0,0,1,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,
+ 21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,
+ 46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,
+ 71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
+ 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,
+ 116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,
+ 135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,
+ 154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,
+ 173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
+ 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,
+ 211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,
+ 230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,
+ 249,250,251,252,253,254,255,99,51,46,49,52,$B,$l,$u,$r,$f,48,48,48,48,48,48,
+ 48,48,49,50,52,51,52,101,43,48,48,0,0,0,0,0>>,
+ do_exception_with_heap_frag(BinAndFloat, Sizes),
%% {Fun,BadFloat}
FunAndFloat =
- <<131,104,2,112,0,0,0,66,0,238,239,135,138,137,216,89,57,22,111,52,126,16,84,
- 71,8,0,0,0,0,0,0,0,0,100,0,1,116,97,0,98,5,175,169,123,103,100,0,13,110,111,
- 110,111,100,101,64,110,111,104,111,115,116,0,0,0,41,0,0,0,0,0,99,50,46,55,48,
- $Y,57,57,57,57,57,57,57,57,57,57,57,57,57,54,52,52,55,101,43,48,48,0,0,0,0,0>>,
- ?line do_exception_with_heap_frag(FunAndFloat, Sizes),
+ <<131,104,2,112,0,0,0,66,0,238,239,135,138,137,216,89,57,22,111,52,126,16,84,
+ 71,8,0,0,0,0,0,0,0,0,100,0,1,116,97,0,98,5,175,169,123,103,100,0,13,110,111,
+ 110,111,100,101,64,110,111,104,111,115,116,0,0,0,41,0,0,0,0,0,99,50,46,55,48,
+ $Y,57,57,57,57,57,57,57,57,57,57,57,57,57,54,52,52,55,101,43,48,48,0,0,0,0,0>>,
+ do_exception_with_heap_frag(FunAndFloat, Sizes),
%% [ExternalPid|BadFloat]
ExtPidAndFloat =
- <<131,108,0,0,0,1,103,100,0,13,107,97,108,108,101,64,115,116,114,105,100,101,
- 114,0,0,0,36,0,0,0,0,2,99,48,46,$@,48,48,48,48,48,48,48,48,48,48,48,48,48,48,
- 48,48,48,48,48,101,43,48,48,0,0,0,0,0>>,
- ?line do_exception_with_heap_frag(ExtPidAndFloat, Sizes),
-
+ <<131,108,0,0,0,1,103,100,0,13,107,97,108,108,101,64,115,116,114,105,100,101,
+ 114,0,0,0,36,0,0,0,0,2,99,48,46,$@,48,48,48,48,48,48,48,48,48,48,48,48,48,48,
+ 48,48,48,48,48,101,43,48,48,0,0,0,0,0>>,
+ do_exception_with_heap_frag(ExtPidAndFloat, Sizes),
+
ok.
do_exception_with_heap_frag(Bin, [Sz|Sizes]) ->
Filler = erlang:make_tuple(Sz, a),
spawn(fun() ->
- try
- binary_to_term(Bin)
- catch
- _:_ ->
- %% term_to_binary/1 is an easy way to traverse the
- %% entire stacktrace term to make sure that every part
- %% of it is OK.
- term_to_binary(erlang:get_stacktrace())
- end,
- id(Filler)
- end),
+ try
+ binary_to_term(Bin)
+ catch
+ _:_ ->
+ %% term_to_binary/1 is an easy way to traverse the
+ %% entire stacktrace term to make sure that every part
+ %% of it is OK.
+ term_to_binary(erlang:get_stacktrace())
+ end,
+ id(Filler)
+ end),
do_exception_with_heap_frag(Bin, Sizes);
do_exception_with_heap_frag(_, []) -> ok.
line_numbers(Config) when is_list(Config) ->
{'EXIT',{{case_clause,bad_tag},
- [{?MODULE,line1,2,
- [{file,"fake_file.erl"},{line,3}]},
- {?MODULE,line_numbers,1,_}|_]}} =
- (catch line1(bad_tag, 0)),
+ [{?MODULE,line1,2,
+ [{file,"fake_file.erl"},{line,3}]},
+ {?MODULE,line_numbers,1,_}|_]}} =
+ (catch line1(bad_tag, 0)),
{'EXIT',{badarith,
- [{?MODULE,line1,2,
- [{file,"fake_file.erl"},{line,5}]},
- {?MODULE,line_numbers,1,_}|_]}} =
- (catch line1(a, not_an_integer)),
+ [{?MODULE,line1,2,
+ [{file,"fake_file.erl"},{line,5}]},
+ {?MODULE,line_numbers,1,_}|_]}} =
+ (catch line1(a, not_an_integer)),
{'EXIT',{{badmatch,{ok,1}},
- [{?MODULE,line1,2,
- [{file,"fake_file.erl"},{line,7}]},
- {?MODULE,line_numbers,1,_}|_]}} =
- (catch line1(a, 0)),
+ [{?MODULE,line1,2,
+ [{file,"fake_file.erl"},{line,7}]},
+ {?MODULE,line_numbers,1,_}|_]}} =
+ (catch line1(a, 0)),
{'EXIT',{crash,
- [{?MODULE,crash,1,
- [{file,"fake_file.erl"},{line,14}]},
- {?MODULE,line_numbers,1,_}|_]}} =
- (catch line1(a, 41)),
+ [{?MODULE,crash,1,
+ [{file,"fake_file.erl"},{line,14}]},
+ {?MODULE,line_numbers,1,_}|_]}} =
+ (catch line1(a, 41)),
ModFile = ?MODULE_STRING++".erl",
[{?MODULE,maybe_crash,1,[{file,"call.erl"},{line,28}]},
{?MODULE,call1,0,[{file,"call.erl"},{line,14}]},
{?MODULE,close_calls,1,[{file,"call.erl"},{line,5}]},
{?MODULE,line_numbers,1,[{file,ModFile},{line,_}]}|_] =
- close_calls(call1),
+ close_calls(call1),
[{?MODULE,maybe_crash,1,[{file,"call.erl"},{line,28}]},
{?MODULE,call2,0,[{file,"call.erl"},{line,18}]},
{?MODULE,close_calls,1,[{file,"call.erl"},{line,6}]},
{?MODULE,line_numbers,1,[{file,ModFile},{line,_}]}|_] =
- close_calls(call2),
+ close_calls(call2),
[{?MODULE,maybe_crash,1,[{file,"call.erl"},{line,28}]},
{?MODULE,call3,0,[{file,"call.erl"},{line,22}]},
{?MODULE,close_calls,1,[{file,"call.erl"},{line,7}]},
{?MODULE,line_numbers,1,[{file,ModFile},{line,_}]}|_] =
- close_calls(call3),
+ close_calls(call3),
no_crash = close_calls(other),
<<0,0>> = build_binary1(16),
{'EXIT',{badarg,
- [{?MODULE,build_binary1,1,
- [{file,"bit_syntax.erl"},{line,72503}]},
- {?MODULE,line_numbers,1,
- [{file,ModFile},{line,_}]}|_]}} =
- (catch build_binary1(bad_size)),
+ [{?MODULE,build_binary1,1,
+ [{file,"bit_syntax.erl"},{line,72503}]},
+ {?MODULE,line_numbers,1,
+ [{file,ModFile},{line,_}]}|_]}} =
+ (catch build_binary1(bad_size)),
<<7,1,2,3>> = build_binary2(8, <<1,2,3>>),
{'EXIT',{badarg,
- [{?MODULE,build_binary2,2,
- [{file,"bit_syntax.erl"},{line,72507}]},
- {?MODULE,line_numbers,1,
- [{file,ModFile},{line,_}]}|_]}} =
- (catch build_binary2(bad_size, <<>>)),
+ [{?MODULE,build_binary2,2,
+ [{file,"bit_syntax.erl"},{line,72507}]},
+ {?MODULE,line_numbers,1,
+ [{file,ModFile},{line,_}]}|_]}} =
+ (catch build_binary2(bad_size, <<>>)),
{'EXIT',{badarg,
- [{erlang,bit_size,[bad_binary],[]},
- {?MODULE,build_binary2,2,
- [{file,"bit_syntax.erl"},{line,72507}]},
- {?MODULE,line_numbers,1,
- [{file,ModFile},{line,_}]}|_]}} =
- (catch build_binary2(8, bad_binary)),
+ [{erlang,bit_size,[bad_binary],[]},
+ {?MODULE,build_binary2,2,
+ [{file,"bit_syntax.erl"},{line,72507}]},
+ {?MODULE,line_numbers,1,
+ [{file,ModFile},{line,_}]}|_]}} =
+ (catch build_binary2(8, bad_binary)),
<<"abc",357:16>> = build_binary3(<<"abc">>),
{'EXIT',{badarg,[{?MODULE,build_binary3,1,
- [{file,"bit_syntax.erl"},{line,72511}]},
- {?MODULE,line_numbers,1,
- [{file,ModFile},{line,_}]}|_]}} =
- (catch build_binary3(no_binary)),
+ [{file,"bit_syntax.erl"},{line,72511}]},
+ {?MODULE,line_numbers,1,
+ [{file,ModFile},{line,_}]}|_]}} =
+ (catch build_binary3(no_binary)),
{'EXIT',{function_clause,
- [{?MODULE,do_call_abs,[y,y],
- [{file,"gc_bif.erl"},{line,18}]},
- {?MODULE,line_numbers,1,_}|_]}} =
- (catch do_call_abs(y, y)),
+ [{?MODULE,do_call_abs,[y,y],
+ [{file,"gc_bif.erl"},{line,18}]},
+ {?MODULE,line_numbers,1,_}|_]}} =
+ (catch do_call_abs(y, y)),
{'EXIT',{badarg,
- [{erlang,abs,[[]],[]},
- {?MODULE,do_call_abs,2,
- [{file,"gc_bif.erl"},{line,19}]},
- {?MODULE,line_numbers,1,_}|_]}} =
- (catch do_call_abs(x, [])),
+ [{erlang,abs,[[]],[]},
+ {?MODULE,do_call_abs,2,
+ [{file,"gc_bif.erl"},{line,19}]},
+ {?MODULE,line_numbers,1,_}|_]}} =
+ (catch do_call_abs(x, [])),
{'EXIT',{{badmatch,"42"},
- [{MODULE,applied_bif_1,1,[{file,"applied_bif.erl"},{line,5}]},
- {?MODULE,line_numbers,1,_}|_]}} =
- (catch applied_bif_1(42)),
+ [{MODULE,applied_bif_1,1,[{file,"applied_bif.erl"},{line,5}]},
+ {?MODULE,line_numbers,1,_}|_]}} =
+ (catch applied_bif_1(42)),
{'EXIT',{{badmatch,{current_location,
- {?MODULE,applied_bif_2,0,
- [{file,"applied_bif.erl"},{line,9}]}}},
- [{MODULE,applied_bif_2,0,[{file,"applied_bif.erl"},{line,10}]},
- {?MODULE,line_numbers,1,_}|_]}} =
- (catch applied_bif_2()),
+ {?MODULE,applied_bif_2,0,
+ [{file,"applied_bif.erl"},{line,9}]}}},
+ [{MODULE,applied_bif_2,0,[{file,"applied_bif.erl"},{line,10}]},
+ {?MODULE,line_numbers,1,_}|_]}} =
+ (catch applied_bif_2()),
ok.
@@ -646,13 +631,13 @@ odd(N) when is_integer(N), N > 1, (N rem 2) == 1 ->
-file("fake_file.erl", 1). %Line 1
line1(Tag, X) -> %Line 2
case Tag of %Line 3
- a ->
- Y = X + 1, %Line 5
- Res = id({ok,Y}), %Line 6
- ?MODULE:crash({ok,42} = Res); %Line 7
- b ->
- x = id(x), %Line 9
- ok %Line 10
+ a ->
+ Y = X + 1, %Line 5
+ Res = id({ok,Y}), %Line 6
+ ?MODULE:crash({ok,42} = Res); %Line 7
+ b ->
+ x = id(x), %Line 9
+ ok %Line 10
end. %Line 11
crash(_) -> %Line 13
@@ -662,12 +647,12 @@ crash(_) -> %Line 13
close_calls(Where) -> %Line 2
put(where_to_crash, Where), %Line 3
try
- call1(), %Line 5
- call2(), %Line 6
- call3(), %Line 7
- no_crash %Line 8
+ call1(), %Line 5
+ call2(), %Line 6
+ call3(), %Line 7
+ no_crash %Line 8
catch error:crash ->
- erlang:get_stacktrace() %Line 10
+ erlang:get_stacktrace() %Line 10
end. %Line 11
call1() -> %Line 13
@@ -684,10 +669,10 @@ call3() -> %Line 21
maybe_crash(Name) -> %Line 25
case get(where_to_crash) of %Line 26
- Name ->
- erlang:error(crash); %Line 28
- _ ->
- ok %Line 30
+ Name ->
+ erlang:error(crash); %Line 28
+ _ ->
+ ok %Line 30
end.
-file("bit_syntax.erl", 72500). %Line 72500
diff --git a/erts/emulator/test/float_SUITE.erl b/erts/emulator/test/float_SUITE.erl
index c1a76b8af4..36b1f9179f 100644
--- a/erts/emulator/test/float_SUITE.erl
+++ b/erts/emulator/test/float_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,63 +20,40 @@
-module(float_SUITE).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2,end_per_testcase/2,
- fpe/1,fp_drv/1,fp_drv_thread/1,denormalized/1,match/1,
- bad_float_unpack/1, write/1, cmp_zero/1, cmp_integer/1, cmp_bignum/1]).
+-export([all/0, suite/0, groups/0,
+ fpe/1,fp_drv/1,fp_drv_thread/1,denormalized/1,match/1,
+ t_mul_add_ops/1,
+ bad_float_unpack/1, write/1, cmp_zero/1, cmp_integer/1, cmp_bignum/1]).
-export([otp_7178/1]).
-export([hidden_inf/1]).
+-export([arith/1]).
-
-init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- Dog = ?t:timetrap(?t:minutes(3)),
- [{watchdog, Dog},{testcase,Func}|Config].
-
-end_per_testcase(_Func, Config) ->
- Dog = ?config(watchdog, Config),
- ?t:timetrap_cancel(Dog).
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 3}}].
all() ->
[fpe, fp_drv, fp_drv_thread, otp_7178, denormalized,
match, bad_float_unpack, write, {group, comparison}
,hidden_inf
- ].
+ ,arith, t_mul_add_ops].
groups() ->
[{comparison, [parallel], [cmp_zero, cmp_integer, cmp_bignum]}].
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
%%
%% OTP-7178, list_to_float on very small numbers should give 0.0
%% instead of exception, i.e. ignore underflow.
%%
-otp_7178(suite) ->
- [];
-otp_7178(doc) ->
- ["test that list_to_float on very small numbers give 0.0"];
+%% test that list_to_float on very small numbers give 0.0
otp_7178(Config) when is_list(Config) ->
- ?line X = list_to_float("1.0e-325"),
- ?line true = (X < 0.00000001) and (X > -0.00000001),
- ?line Y = list_to_float("1.0e-325325325"),
- ?line true = (Y < 0.00000001) and (Y > -0.00000001),
- ?line {'EXIT', {badarg,_}} = (catch list_to_float("1.0e83291083210")),
+ X = list_to_float("1.0e-325"),
+ true = (X < 0.00000001) and (X > -0.00000001),
+ Y = list_to_float("1.0e-325325325"),
+ true = (Y < 0.00000001) and (Y > -0.00000001),
+ {'EXIT', {badarg,_}} = (catch list_to_float("1.0e83291083210")),
ok.
%% Forces floating point exceptions and tests that subsequent, legal,
@@ -84,15 +61,15 @@ otp_7178(Config) when is_list(Config) ->
%% Strollo.
fpe(Config) when is_list(Config) ->
- ?line 0.0 = math:log(1.0),
- ?line {'EXIT', {badarith, _}} = (catch math:log(-1.0)),
- ?line 0.0 = math:log(1.0),
- ?line {'EXIT', {badarith, _}} = (catch math:log(0.0)),
- ?line 0.0 = math:log(1.0),
- ?line {'EXIT',{badarith,_}} = (catch 3.23e133 * id(3.57e257)),
- ?line 0.0 = math:log(1.0),
- ?line {'EXIT',{badarith,_}} = (catch 5.0/id(0.0)),
- ?line 0.0 = math:log(1.0),
+ 0.0 = math:log(1.0),
+ {'EXIT', {badarith, _}} = (catch math:log(-1.0)),
+ 0.0 = math:log(1.0),
+ {'EXIT', {badarith, _}} = (catch math:log(0.0)),
+ 0.0 = math:log(1.0),
+ {'EXIT',{badarith,_}} = (catch 3.23e133 * id(3.57e257)),
+ 0.0 = math:log(1.0),
+ {'EXIT',{badarith,_}} = (catch 5.0/id(0.0)),
+ 0.0 = math:log(1.0),
ok.
@@ -100,70 +77,70 @@ fpe(Config) when is_list(Config) ->
-define(ERTS_FP_THREAD_TEST, 1).
fp_drv(Config) when is_list(Config) ->
- fp_drv_test(?ERTS_FP_CONTROL_TEST, ?config(data_dir, Config)).
+ fp_drv_test(?ERTS_FP_CONTROL_TEST, proplists:get_value(data_dir, Config)).
fp_drv_thread(Config) when is_list(Config) ->
%% Run in a separate node since it used to crash the emulator...
- ?line Parent = self(),
- ?line DrvDir = ?config(data_dir, Config),
- ?line {ok,Node} = start_node(Config),
- ?line Tester = spawn_link(Node,
- fun () ->
- Parent !
- {self(),
- fp_drv_test(?ERTS_FP_THREAD_TEST,
- DrvDir)}
- end),
- ?line Result = receive {Tester, Res} -> Res end,
- ?line stop_node(Node),
- ?line Result.
+ Parent = self(),
+ DrvDir = proplists:get_value(data_dir, Config),
+ {ok,Node} = start_node(Config),
+ Tester = spawn_link(Node,
+ fun () ->
+ Parent !
+ {self(),
+ fp_drv_test(?ERTS_FP_THREAD_TEST,
+ DrvDir)}
+ end),
+ Result = receive {Tester, Res} -> Res end,
+ stop_node(Node),
+ Result.
fp_drv_test(Test, DrvDir) ->
- ?line Drv = fp_drv,
- ?line try
- begin
- ?line case erl_ddll:load_driver(DrvDir, Drv) of
- ok ->
- ok;
- {error, permanent} ->
- ok;
- {error, LoadError} ->
- exit({load_error,
- erl_ddll:format_error(LoadError)});
- LoadError ->
- exit({load_error, LoadError})
- end,
- case open_port({spawn, Drv}, []) of
- Port when is_port(Port) ->
- try port_control(Port, Test, "") of
- "ok" ->
- 0.0 = math:log(1.0),
- ok;
- [$s,$k,$i,$p,$:,$ | Reason] ->
- {skipped, Reason};
- Error ->
- exit(Error)
- after
- Port ! {self(), close},
- receive {Port, closed} -> ok end,
- false = lists:member(Port, erlang:ports()),
- ok
- end;
- Error ->
- exit({open_port_failed, Error})
- end
- end
- catch
- throw:Term -> ?line Term
- after
- erl_ddll:unload_driver(Drv)
- end.
+ Drv = fp_drv,
+ try
+ begin
+ case erl_ddll:load_driver(DrvDir, Drv) of
+ ok ->
+ ok;
+ {error, permanent} ->
+ ok;
+ {error, LoadError} ->
+ exit({load_error,
+ erl_ddll:format_error(LoadError)});
+ LoadError ->
+ exit({load_error, LoadError})
+ end,
+ case open_port({spawn, Drv}, []) of
+ Port when is_port(Port) ->
+ try port_control(Port, Test, "") of
+ "ok" ->
+ 0.0 = math:log(1.0),
+ ok;
+ [$s,$k,$i,$p,$:,$ | Reason] ->
+ {skipped, Reason};
+ Error ->
+ exit(Error)
+ after
+ Port ! {self(), close},
+ receive {Port, closed} -> ok end,
+ false = lists:member(Port, erlang:ports()),
+ ok
+ end;
+ Error ->
+ exit({open_port_failed, Error})
+ end
+ end
+ catch
+ throw:Term -> Term
+ after
+ erl_ddll:unload_driver(Drv)
+ end.
denormalized(Config) when is_list(Config) ->
- ?line Denormalized = 1.0e-307 / 1000,
- ?line roundtrip(Denormalized),
- ?line NegDenormalized = -1.0e-307 / 1000,
- ?line roundtrip(NegDenormalized),
+ Denormalized = 1.0e-307 / 1000,
+ roundtrip(Denormalized),
+ NegDenormalized = -1.0e-307 / 1000,
+ roundtrip(NegDenormalized),
ok.
roundtrip(N) ->
@@ -171,12 +148,12 @@ roundtrip(N) ->
N = binary_to_term(term_to_binary(N, [{minor_version,1}])).
match(Config) when is_list(Config) ->
- ?line one = match_1(1.0),
- ?line two = match_1(2.0),
- ?line a_lot = match_1(1000.0),
- ?line {'EXIT',_} = (catch match_1(0.5)),
+ one = match_1(1.0),
+ two = match_1(2.0),
+ a_lot = match_1(1000.0),
+ {'EXIT',_} = (catch match_1(0.5)),
ok.
-
+
match_1(1.0) -> one;
match_1(2.0) -> two;
match_1(1000.0) -> a_lot.
@@ -184,8 +161,8 @@ match_1(1000.0) -> a_lot.
%% Thanks to Per Gustafsson.
bad_float_unpack(Config) when is_list(Config) ->
- ?line Bin = <<-1:64>>,
- ?line -1 = bad_float_unpack_match(Bin),
+ Bin = <<-1:64>>,
+ -1 = bad_float_unpack_match(Bin),
ok.
bad_float_unpack_match(<<F:64/float>>) -> F;
@@ -237,75 +214,75 @@ span_cmp(Axis, Incr, Length) ->
%% Diff: How much the float and int should differ when comparing
span_cmp(Axis, Incr, Length, Diff) ->
[begin
- cmp(round(Axis*-1.0)+Diff+I*Incr,Axis*-1.0+I*Incr),
- cmp(Axis*-1.0+I*Incr,round(Axis*-1.0)-Diff+I*Incr)
+ cmp(round(Axis*-1.0)+Diff+I*Incr,Axis*-1.0+I*Incr),
+ cmp(Axis*-1.0+I*Incr,round(Axis*-1.0)-Diff+I*Incr)
end || I <- lists:seq((Length div 2)*-1,(Length div 2))],
[begin
- cmp(round(Axis)+Diff+I*Incr,Axis+I*Incr),
- cmp(Axis+I*Incr,round(Axis)-Diff+I*Incr)
+ cmp(round(Axis)+Diff+I*Incr,Axis+I*Incr),
+ cmp(Axis+I*Incr,round(Axis)-Diff+I*Incr)
end || I <- lists:seq((Length div 2)*-1,(Length div 2))].
cmp(Big,Small) when is_float(Big) ->
BigGtSmall = lists:flatten(
- io_lib:format("~f > ~p",[Big,Small])),
+ io_lib:format("~f > ~p",[Big,Small])),
BigLtSmall = lists:flatten(
- io_lib:format("~f < ~p",[Big,Small])),
+ io_lib:format("~f < ~p",[Big,Small])),
BigEqSmall = lists:flatten(
- io_lib:format("~f == ~p",[Big,Small])),
+ io_lib:format("~f == ~p",[Big,Small])),
SmallGtBig = lists:flatten(
- io_lib:format("~p > ~f",[Small,Big])),
+ io_lib:format("~p > ~f",[Small,Big])),
SmallLtBig = lists:flatten(
- io_lib:format("~p < ~f",[Small,Big])),
+ io_lib:format("~p < ~f",[Small,Big])),
SmallEqBig = lists:flatten(
- io_lib:format("~p == ~f",[Small,Big])),
+ io_lib:format("~p == ~f",[Small,Big])),
cmp(Big,Small,BigGtSmall,BigLtSmall,SmallGtBig,SmallLtBig,
- SmallEqBig,BigEqSmall);
+ SmallEqBig,BigEqSmall);
cmp(Big,Small) when is_float(Small) ->
BigGtSmall = lists:flatten(
- io_lib:format("~p > ~f",[Big,Small])),
+ io_lib:format("~p > ~f",[Big,Small])),
BigLtSmall = lists:flatten(
- io_lib:format("~p < ~f",[Big,Small])),
+ io_lib:format("~p < ~f",[Big,Small])),
BigEqSmall = lists:flatten(
- io_lib:format("~p == ~f",[Big,Small])),
+ io_lib:format("~p == ~f",[Big,Small])),
SmallGtBig = lists:flatten(
- io_lib:format("~f > ~p",[Small,Big])),
+ io_lib:format("~f > ~p",[Small,Big])),
SmallLtBig = lists:flatten(
- io_lib:format("~f < ~p",[Small,Big])),
+ io_lib:format("~f < ~p",[Small,Big])),
SmallEqBig = lists:flatten(
- io_lib:format("~f == ~p",[Small,Big])),
+ io_lib:format("~f == ~p",[Small,Big])),
cmp(Big,Small,BigGtSmall,BigLtSmall,SmallGtBig,SmallLtBig,
- SmallEqBig,BigEqSmall).
+ SmallEqBig,BigEqSmall).
cmp(Big,Small,BigGtSmall,BigLtSmall,SmallGtBig,SmallLtBig,
SmallEqBig,BigEqSmall) ->
{_,_,_,true} = {Big,Small,BigGtSmall,
- Big > Small},
+ Big > Small},
{_,_,_,false} = {Big,Small,BigLtSmall,
- Big < Small},
+ Big < Small},
{_,_,_,false} = {Big,Small,SmallGtBig,
- Small > Big},
+ Small > Big},
{_,_,_,true} = {Big,Small,SmallLtBig,
- Small < Big},
+ Small < Big},
{_,_,_,false} = {Big,Small,SmallEqBig,
- Small == Big},
+ Small == Big},
{_,_,_,false} = {Big,Small,BigEqSmall,
- Big == Small}.
+ Big == Small}.
id(I) -> I.
-
+
start_node(Config) when is_list(Config) ->
- ?line Pa = filename:dirname(code:which(?MODULE)),
- ?line Name = list_to_atom(atom_to_list(?MODULE)
- ++ "-"
- ++ atom_to_list(?config(testcase, Config))
- ++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
- ++ "-"
- ++ integer_to_list(erlang:unique_integer([positive]))),
- ?line ?t:start_node(Name, slave, [{args, "-pa "++Pa}]).
+ Pa = filename:dirname(code:which(?MODULE)),
+ Name = list_to_atom(atom_to_list(?MODULE)
+ ++ "-"
+ ++ atom_to_list(proplists:get_value(testcase, Config))
+ ++ "-"
+ ++ integer_to_list(erlang:system_time(second))
+ ++ "-"
+ ++ integer_to_list(erlang:unique_integer([positive]))),
+ test_server:start_node(Name, slave, [{args, "-pa "++Pa}]).
stop_node(Node) ->
- ?t:stop_node(Node).
+ test_server:stop_node(Node).
%% Test that operations that might hide infinite intermediate results
@@ -315,8 +292,8 @@ hidden_inf(Config) when is_list(Config) ->
ZeroN = id(ZeroP) * (-1),
[hidden_inf_1(A, B, Z, 9.23e307)
|| A <- [1.0, -1.0, 3.1415, -0.00001000131, 3.57e257, ZeroP, ZeroN],
- B <- [1.0, -1.0, 3.1415, -0.00001000131, 3.57e257, ZeroP, ZeroN],
- Z <- [ZeroP, ZeroN]],
+ B <- [1.0, -1.0, 3.1415, -0.00001000131, 3.57e257, ZeroP, ZeroN],
+ Z <- [ZeroP, ZeroN]],
ok.
hidden_inf_1(A, B, Zero, Huge) ->
@@ -328,3 +305,122 @@ hidden_inf_1(A, B, Zero, Huge) ->
{'EXIT',{badarith,_}} = (catch (B * (Huge + Huge))),
{'EXIT',{badarith,_}} = (catch (B / (-Huge - Huge))),
{'EXIT',{badarith,_}} = (catch (B * (-Huge - Huge))).
+
+%% Improve code coverage in our different arithmetic functions
+%% and make sure they yield consistent results.
+arith(_Config) ->
+ _TAG_IMMED1_SIZE = 4,
+
+ <<FLOAT_MAX/float>> = <<0:1, 16#7fe:11, -1:52>>,
+ <<FLOAT_MIN/float>> = <<0:1, 0:11, 1:52>>,
+ <<FloatNegZero/float>> = <<1:1, 0:11, 0:52>>,
+
+ WORD_BITS = erlang:system_info(wordsize) * 8,
+ SMALL_BITS = (WORD_BITS - _TAG_IMMED1_SIZE),
+ SMALL_MAX = (1 bsl (SMALL_BITS-1)) - 1,
+ SMALL_MIN = -(1 bsl (SMALL_BITS-1)),
+ BIG1_MAX = (1 bsl WORD_BITS) - 1,
+ BIG2_MAX = (1 bsl (WORD_BITS*2)) - 1,
+
+ fixnum = erts_internal:term_type(SMALL_MAX),
+ fixnum = erts_internal:term_type(SMALL_MIN),
+ bignum = erts_internal:term_type(SMALL_MAX + 1),
+ bignum = erts_internal:term_type(SMALL_MIN - 1),
+
+ L = [0, 0.0, FloatNegZero, 1, 1.0, 17, 17.0, 0.17,
+ FLOAT_MIN, FLOAT_MAX,
+ SMALL_MAX, SMALL_MAX+1,
+ SMALL_MIN, SMALL_MIN-1,
+ BIG1_MAX, BIG1_MAX+1,
+ BIG2_MAX, BIG2_MAX+1,
+ trunc(FLOAT_MAX), trunc(FLOAT_MAX)+1, trunc(FLOAT_MAX)*2,
+ immed_badarg,
+ "list badarg",
+ {"boxed badarg"}],
+
+ foreach_pair(fun(A,B) -> do_bin_ops(A,B) end, L).
+
+foreach_pair(F, L) ->
+ lists:foreach(
+ fun(A) -> lists:foreach(fun(B) -> F(A,B) end, L) end,
+ L).
+
+do_bin_ops(A, B) ->
+ Fun = fun(Op) ->
+ Op(A,B),
+ is_number(A) andalso Op(-A,B),
+ is_number(B) andalso Op(A,-B),
+ is_number(A) andalso is_number(B) andalso Op(-A,-B)
+ end,
+ lists:foreach(Fun,
+ [fun op_add/2, fun op_sub/2, fun op_mul/2, fun op_div/2]).
+
+op_add(A, B) ->
+ Info = [A,B],
+ R = unify(catch A + B, Info),
+ R = unify(my_apply(erlang,'+',[A,B]), Info),
+ case R of
+ _ when A + B =:= element(1,R) -> ok;
+ {{'EXIT',badarith}, Info} -> ok
+ end.
+
+op_sub(A, B) ->
+ Info = [A,B],
+ R = unify(catch A - B, Info),
+ R = unify(my_apply(erlang,'-',[A,B]), Info),
+ case R of
+ _ when A - B =:= element(1,R) -> ok;
+ {{'EXIT',badarith}, Info} -> ok
+ end.
+
+op_mul(A, B) ->
+ Info = [A,B],
+ R = unify(catch A * B, Info),
+ R = unify(my_apply(erlang,'*',[A,B]), Info),
+ case R of
+ _ when A * B =:= element(1,R) -> ok;
+ {{'EXIT',badarith}, Info} -> ok
+ end.
+
+op_div(A, B) ->
+ Info = [A,B],
+ R = unify(catch A / B, Info),
+ R = unify(my_apply(erlang,'/',[A,B]), Info),
+ case R of
+ _ when A / B =:= element(1,R) -> ok;
+ {{'EXIT',badarith}, Info} -> ok
+ end.
+
+my_apply(M, F, A) ->
+ catch apply(id(M), id(F), A).
+
+% Unify exceptions be removing stack traces.
+% and add argument info to make it easer to debug failed matches.
+unify({'EXIT',{Reason,_Stack}}, Info) ->
+ {{'EXIT', Reason}, Info};
+unify(Other, Info) ->
+ {Other, Info}.
+
+
+-define(epsilon, 1.0e-20).
+check_epsilon(R,Val) ->
+ if erlang:abs(R-Val) < ?epsilon -> ok;
+ true -> ct:fail({R,Val})
+ end.
+
+t_mul_add_ops(Config) when is_list(Config) ->
+ check_epsilon(op_mul_add(1, 2.0, 1.0, 0.0), 1.0),
+ check_epsilon(op_mul_add(2, 2.0, 1.0, 0.0), 3.0),
+ check_epsilon(op_mul_add(3, 2.0, 1.0, 0.0), 7.0),
+ check_epsilon(op_mul_add(4, 2.0, 1.0, 0.0), 15.0),
+ check_epsilon(op_mul_add(5, 2.0, 1.0, 0.0), 31.0),
+ check_epsilon(op_mul_add(6, 2.0, 1.0, 0.0), 63.0),
+ check_epsilon(op_mul_add(6, 2.0, 1.3, 0.0), 81.9),
+ check_epsilon(op_mul_add(6, 2.03, 1.3, 0.0), 87.06260151458997),
+ ok.
+
+
+op_mul_add(0, _, _, R) -> R;
+op_mul_add(N, A, B, R) when is_float(A), is_float(B), is_float(R) ->
+ op_mul_add(N - 1, A, B, R * A + B).
+
diff --git a/erts/emulator/test/float_SUITE_data/fp_drv.c b/erts/emulator/test/float_SUITE_data/fp_drv.c
index 5919dd8e2f..a91d622040 100644
--- a/erts/emulator/test/float_SUITE_data/fp_drv.c
+++ b/erts/emulator/test/float_SUITE_data/fp_drv.c
@@ -18,6 +18,7 @@
*/
#if defined(DEBUG) || 0
+# include <stdio.h>
# define PRINTF(X) printf X
#else
# define PRINTF(X)
diff --git a/erts/emulator/test/float_SUITE_data/has_fpe_bug.erl b/erts/emulator/test/float_SUITE_data/has_fpe_bug.erl
index 79ab74dfff..26837de274 100644
--- a/erts/emulator/test/float_SUITE_data/has_fpe_bug.erl
+++ b/erts/emulator/test/float_SUITE_data/has_fpe_bug.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/fun_SUITE.erl b/erts/emulator/test/fun_SUITE.erl
index b18f9f5c6b..26fa955e3c 100644
--- a/erts/emulator/test/fun_SUITE.erl
+++ b/erts/emulator/test/fun_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -21,11 +21,7 @@
-module(fun_SUITE).
-compile({nowarn_deprecated_function, {erlang,hash,2}}).
--define(default_timeout, ?t:minutes(1)).
-
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2,end_per_testcase/2,
+-export([all/0, suite/0,
bad_apply/1,bad_fun_call/1,badarity/1,ext_badarity/1,
equality/1,ordering/1,
fun_to_port/1,t_hash/1,t_phash/1,t_phash2/1,md5/1,
@@ -35,9 +31,12 @@
-export([nothing/0]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 1}}].
-suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[bad_apply, bad_fun_call, badarity, ext_badarity,
@@ -46,45 +45,18 @@ all() ->
const_propagation, t_arity, t_is_function2, t_fun_info,
t_fun_info_mfa].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-init_per_testcase(_Case, Config) ->
- ?line Dog = test_server:timetrap(?default_timeout),
- [{watchdog, Dog}|Config].
-
-end_per_testcase(_Case, Config) ->
- Dog=?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
- ok.
-
-bad_apply(doc) ->
- "Test that the correct EXIT code is returned for all types of bad funs.";
-bad_apply(suite) -> [];
+%% Test that the correct EXIT code is returned for all types of bad funs.
bad_apply(Config) when is_list(Config) ->
- ?line bad_apply_fc(42, [0]),
- ?line bad_apply_fc(xx, [1]),
- ?line bad_apply_fc({}, [2]),
- ?line bad_apply_fc({1}, [3]),
- ?line bad_apply_fc({1,2,3}, [4]),
- ?line bad_apply_fc({1,2,3}, [5]),
- ?line bad_apply_fc({1,2,3,4}, [6]),
- ?line bad_apply_fc({1,2,3,4,5,6}, [7]),
- ?line bad_apply_fc({1,2,3,4,5}, [8]),
- ?line bad_apply_badarg({1,2}, [9]),
+ bad_apply_fc(42, [0]),
+ bad_apply_fc(xx, [1]),
+ bad_apply_fc({}, [2]),
+ bad_apply_fc({1}, [3]),
+ bad_apply_fc({1,2,3}, [4]),
+ bad_apply_fc({1,2,3}, [5]),
+ bad_apply_fc({1,2,3,4}, [6]),
+ bad_apply_fc({1,2,3,4,5,6}, [7]),
+ bad_apply_fc({1,2,3,4,5}, [8]),
+ bad_apply_badarg({1,2}, [9]),
ok.
bad_apply_fc(Fun, Args) ->
@@ -96,7 +68,7 @@ bad_apply_fc(Fun, Args) ->
ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res]);
Other ->
ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res]),
- ?t:fail({bad_result,Other})
+ ct:fail({bad_result,Other})
end.
bad_apply_badarg(Fun, Args) ->
@@ -108,23 +80,21 @@ bad_apply_badarg(Fun, Args) ->
ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res]);
Other ->
ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res]),
- ?t:fail({bad_result, Other})
+ ct:fail({bad_result, Other})
end.
-bad_fun_call(doc) ->
- "Try directly calling bad funs.";
-bad_fun_call(suite) -> [];
+%% Try directly calling bad funs.
bad_fun_call(Config) when is_list(Config) ->
- ?line bad_call_fc(42),
- ?line bad_call_fc(xx),
- ?line bad_call_fc({}),
- ?line bad_call_fc({1}),
- ?line bad_call_fc({1,2,3}),
- ?line bad_call_fc({1,2,3}),
- ?line bad_call_fc({1,2,3,4}),
- ?line bad_call_fc({1,2,3,4,5,6}),
- ?line bad_call_fc({1,2,3,4,5}),
- ?line bad_call_fc({1,2}),
+ bad_call_fc(42),
+ bad_call_fc(xx),
+ bad_call_fc({}),
+ bad_call_fc({1}),
+ bad_call_fc({1,2,3}),
+ bad_call_fc({1,2,3}),
+ bad_call_fc({1,2,3,4}),
+ bad_call_fc({1,2,3,4,5,6}),
+ bad_call_fc({1,2,3,4,5}),
+ bad_call_fc({1,2}),
ok.
bad_call_fc(Fun) ->
@@ -135,74 +105,74 @@ bad_call_fc(Fun) ->
ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]);
Other ->
ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]),
- ?t:fail({bad_result,Other})
+ ct:fail({bad_result,Other})
end.
%% Call and apply valid funs with wrong number of arguments.
badarity(Config) when is_list(Config) ->
- ?line Fun = fun() -> ok end,
- ?line Stupid = {stupid,arguments},
- ?line Args = [some,{stupid,arguments},here],
+ Fun = fun() -> ok end,
+ Stupid = {stupid,arguments},
+ Args = [some,{stupid,arguments},here],
%% Simple call.
- ?line Res = (catch Fun(some, Stupid, here)),
+ Res = (catch Fun(some, Stupid, here)),
erlang:garbage_collect(),
erlang:yield(),
case Res of
{'EXIT',{{badarity,{Fun,Args}},_}} ->
- ?line ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]);
+ ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]);
_ ->
- ?line ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]),
- ?line ?t:fail({bad_result,Res})
+ ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]),
+ ct:fail({bad_result,Res})
end,
%% Apply.
- ?line Res2 = (catch apply(Fun, Args)),
+ Res2 = (catch apply(Fun, Args)),
erlang:garbage_collect(),
erlang:yield(),
case Res2 of
{'EXIT',{{badarity,{Fun,Args}},_}} ->
- ?line ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]);
+ ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]);
_ ->
- ?line ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]),
- ?line ?t:fail({bad_result,Res2})
+ ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]),
+ ct:fail({bad_result,Res2})
end,
ok.
%% Call and apply valid external funs with wrong number of arguments.
ext_badarity(Config) when is_list(Config) ->
- ?line Fun = fun ?MODULE:nothing/0,
- ?line Stupid = {stupid,arguments},
- ?line Args = [some,{stupid,arguments},here],
+ Fun = fun ?MODULE:nothing/0,
+ Stupid = {stupid,arguments},
+ Args = [some,{stupid,arguments},here],
%% Simple call.
- ?line Res = (catch Fun(some, Stupid, here)),
+ Res = (catch Fun(some, Stupid, here)),
erlang:garbage_collect(),
erlang:yield(),
case Res of
{'EXIT',{{badarity,{Fun,Args}},_}} ->
- ?line ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]);
+ ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]);
_ ->
- ?line ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]),
- ?line ?t:fail({bad_result,Res})
+ ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]),
+ ct:fail({bad_result,Res})
end,
%% Apply.
- ?line Res2 = (catch apply(Fun, Args)),
+ Res2 = (catch apply(Fun, Args)),
erlang:garbage_collect(),
erlang:yield(),
case Res2 of
{'EXIT',{{badarity,{Fun,Args}},_}} ->
- ?line ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]);
+ ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]);
_ ->
- ?line ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]),
- ?line ?t:fail({bad_result,Res2})
+ ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]),
+ ct:fail({bad_result,Res2})
end,
ok.
@@ -214,29 +184,29 @@ nothing() ->
equality(Config) when is_list(Config) ->
F0 = fun() -> 1 end,
F0_copy = copy_term(F0),
- ?line true = eq(F0, F0),
- ?line true = eq(F0, F0_copy),
+ true = eq(F0, F0),
+ true = eq(F0, F0_copy),
%% Compare different arities.
F1 = fun(X) -> X + 1 end,
- ?line true = eq(F1, F1),
- ?line false = eq(F0, F1),
- ?line false = eq(F0_copy, F1),
+ true = eq(F1, F1),
+ false = eq(F0, F1),
+ false = eq(F0_copy, F1),
%% Compare different environments.
G1 = make_fun(1),
G2 = make_fun(2),
- ?line true = eq(G1, G1),
- ?line true = eq(G2, G2),
- ?line false = eq(G1, G2),
- ?line false = eq(G2, G1),
+ true = eq(G1, G1),
+ true = eq(G2, G2),
+ false = eq(G1, G2),
+ false = eq(G2, G1),
G1_copy = copy_term(G1),
- ?line true = eq(G1, G1_copy),
+ true = eq(G1, G1_copy),
%% Compare fun with binaries.
B = list_to_binary([7,8,9]),
- ?line false = eq(B, G1),
- ?line false = eq(G1, B),
+ false = eq(B, G1),
+ false = eq(G1, B),
%% Compare external funs.
FF0 = fun aa:blurf/0,
@@ -246,23 +216,23 @@ equality(Config) when is_list(Config) ->
FF3 = fun erlang:exit/2,
FF4 = fun z:ff/0,
- ?line true = eq(FF0, FF0),
- ?line true = eq(FF0, FF0_copy),
- ?line true = eq(FF1, FF1),
- ?line true = eq(FF2, FF2),
- ?line true = eq(FF3, FF3),
- ?line true = eq(FF4, FF4),
- ?line false = eq(FF0, FF1),
- ?line false = eq(FF0, FF2),
- ?line false = eq(FF0, FF3),
- ?line false = eq(FF0, FF4),
- ?line false = eq(FF1, FF0),
- ?line false = eq(FF1, FF2),
- ?line false = eq(FF1, FF3),
- ?line false = eq(FF1, FF4),
- ?line false = eq(FF2, FF3),
- ?line false = eq(FF2, FF4),
- ?line false = eq(FF3, FF4),
+ true = eq(FF0, FF0),
+ true = eq(FF0, FF0_copy),
+ true = eq(FF1, FF1),
+ true = eq(FF2, FF2),
+ true = eq(FF3, FF3),
+ true = eq(FF4, FF4),
+ false = eq(FF0, FF1),
+ false = eq(FF0, FF2),
+ false = eq(FF0, FF3),
+ false = eq(FF0, FF4),
+ false = eq(FF1, FF0),
+ false = eq(FF1, FF2),
+ false = eq(FF1, FF3),
+ false = eq(FF1, FF4),
+ false = eq(FF2, FF3),
+ false = eq(FF2, FF4),
+ false = eq(FF3, FF4),
%% EEP37
H1 = fun Fact(N) when N > 0 -> N * Fact(N - 1); Fact(0) -> 1 end,
@@ -285,7 +255,7 @@ copy_term(Term) ->
make_fun(X) ->
fun() -> X end.
-ordering(doc) -> "Tests ordering of funs.";
+%% Tests ordering of funs.
ordering(Config) when is_list(Config) ->
F1 = make_fun(1, 2),
F1_copy = copy_term(F1),
@@ -298,140 +268,139 @@ ordering(Config) when is_list(Config) ->
FF3 = fun erlang:exit/2,
FF4 = fun z:ff/0,
- ?line true = FF0 < FF1,
- ?line true = FF1 < FF2,
- ?line true = FF2 < FF3,
- ?line true = FF3 < FF4,
+ true = FF0 < FF1,
+ true = FF1 < FF2,
+ true = FF2 < FF3,
+ true = FF3 < FF4,
- ?line true = FF0 > F1,
- ?line true = FF0 > F2,
- ?line true = FF0 > F3,
- ?line true = FF4 > F1,
- ?line true = FF4 > F2,
- ?line true = FF4 > F3,
+ true = FF0 > F1,
+ true = FF0 > F2,
+ true = FF0 > F3,
+ true = FF4 > F1,
+ true = FF4 > F2,
+ true = FF4 > F3,
- ?line true = F1 == F1,
- ?line true = F1 == F1_copy,
- ?line true = F1 /= F2,
+ true = F1 == F1,
+ true = F1 == F1_copy,
+ true = F1 /= F2,
- ?line true = F1 < F2,
- ?line true = F2 > F1,
- ?line true = F2 < F3,
- ?line true = F3 > F2,
+ true = F1 < F2,
+ true = F2 > F1,
+ true = F2 < F3,
+ true = F3 > F2,
- ?line false = F1 > F2,
- ?line false = F2 > F3,
+ false = F1 > F2,
+ false = F2 > F3,
%% Compare with binaries.
B = list_to_binary([7,8,9,10]),
- ?line false = B == F1,
- ?line false = F1 == B,
+ false = B == F1,
+ false = F1 == B,
- ?line true = F1 < B,
- ?line true = B > F2,
+ true = F1 < B,
+ true = B > F2,
- ?line false = F1 > B,
- ?line false = B < F2,
+ false = F1 > B,
+ false = B < F2,
- ?line false = F1 >= B,
- ?line false = B =< F2,
+ false = F1 >= B,
+ false = B =< F2,
%% Compare module funs with binaries.
- ?line false = B == FF1,
- ?line false = FF1 == B,
+ false = B == FF1,
+ false = FF1 == B,
- ?line true = FF1 < B,
- ?line true = B > FF2,
+ true = FF1 < B,
+ true = B > FF2,
- ?line false = FF1 > B,
- ?line false = B < FF2,
+ false = FF1 > B,
+ false = B < FF2,
- ?line false = FF1 >= B,
- ?line false = B =< FF2,
+ false = FF1 >= B,
+ false = B =< FF2,
%% Create a port and ref.
- ?line Path = ?config(priv_dir, Config),
- ?line AFile = filename:join(Path, "vanilla_file"),
- ?line P = open_port(AFile, [out]),
- ?line R = make_ref(),
+ Path = proplists:get_value(priv_dir, Config),
+ AFile = filename:join(Path, "vanilla_file"),
+ P = open_port(AFile, [out]),
+ R = make_ref(),
%% Compare funs with ports and refs.
- ?line true = R < F3,
- ?line true = F3 > R,
- ?line true = F3 < P,
- ?line true = P > F3,
+ true = R < F3,
+ true = F3 > R,
+ true = F3 < P,
+ true = P > F3,
- ?line true = R =< F3,
- ?line true = F3 >= R,
- ?line true = F3 =< P,
- ?line true = P >= F3,
+ true = R =< F3,
+ true = F3 >= R,
+ true = F3 =< P,
+ true = P >= F3,
- ?line false = R > F3,
- ?line false = F3 < R,
- ?line false = F3 > P,
- ?line false = P < F3,
+ false = R > F3,
+ false = F3 < R,
+ false = F3 > P,
+ false = P < F3,
%% Compare funs with conses and nils.
- ?line true = F1 < [a],
- ?line true = F1 < [],
- ?line true = [a,b] > F1,
- ?line true = [] > F1,
+ true = F1 < [a],
+ true = F1 < [],
+ true = [a,b] > F1,
+ true = [] > F1,
- ?line false = [1] < F1,
- ?line false = [] < F1,
- ?line false = F1 > [2],
- ?line false = F1 > [],
+ false = [1] < F1,
+ false = [] < F1,
+ false = F1 > [2],
+ false = F1 > [],
- ?line false = [1] =< F1,
- ?line false = [] =< F1,
- ?line false = F1 >= [2],
- ?line false = F1 >= [],
+ false = [1] =< F1,
+ false = [] =< F1,
+ false = F1 >= [2],
+ false = F1 >= [],
%% Compare module funs with conses and nils.
- ?line true = FF1 < [a],
- ?line true = FF1 < [],
- ?line true = [a,b] > FF1,
- ?line true = [] > FF1,
+ true = FF1 < [a],
+ true = FF1 < [],
+ true = [a,b] > FF1,
+ true = [] > FF1,
- ?line false = [1] < FF1,
- ?line false = [] < FF1,
- ?line false = FF1 > [2],
- ?line false = FF1 > [],
+ false = [1] < FF1,
+ false = [] < FF1,
+ false = FF1 > [2],
+ false = FF1 > [],
- ?line false = [1] =< FF1,
- ?line false = [] =< FF1,
- ?line false = FF1 >= [2],
- ?line false = FF1 >= [],
+ false = [1] =< FF1,
+ false = [] =< FF1,
+ false = FF1 >= [2],
+ false = FF1 >= [],
ok.
make_fun(X, Y) ->
fun(A) -> A*X+Y end.
-fun_to_port(doc) -> "Try sending funs to ports (should fail).";
-fun_to_port(suite) -> [];
+%% Try sending funs to ports (should fail).
fun_to_port(Config) when is_list(Config) ->
- ?line fun_to_port(Config, xxx),
- ?line fun_to_port(Config, fun() -> 42 end),
- ?line fun_to_port(Config, [fun() -> 43 end]),
- ?line fun_to_port(Config, [1,fun() -> 44 end]),
- ?line fun_to_port(Config, [0,1|fun() -> 45 end]),
+ fun_to_port(Config, xxx),
+ fun_to_port(Config, fun() -> 42 end),
+ fun_to_port(Config, [fun() -> 43 end]),
+ fun_to_port(Config, [1,fun() -> 44 end]),
+ fun_to_port(Config, [0,1|fun() -> 45 end]),
B64K = build_io_list(65536),
- ?line fun_to_port(Config, [B64K,fun() -> 45 end]),
- ?line fun_to_port(Config, [B64K|fun() -> 45 end]),
+ fun_to_port(Config, [B64K,fun() -> 45 end]),
+ fun_to_port(Config, [B64K|fun() -> 45 end]),
ok.
fun_to_port(Config, IoList) ->
- Path = ?config(priv_dir, Config),
+ Path = proplists:get_value(priv_dir, Config),
AFile = filename:join(Path, "vanilla_file"),
Port = open_port(AFile, [out]),
case catch port_command(Port, IoList) of
{'EXIT',{badarg,_}} -> ok;
- Other -> ?t:fail({unexpected_retval,Other})
+ Other -> ct:fail({unexpected_retval,Other})
end.
build_io_list(0) -> [];
@@ -443,86 +412,83 @@ build_io_list(N) ->
1 -> [7,L|L]
end.
-t_hash(doc) -> "Test the hash/2 BIF on funs.";
-t_hash(suite) -> [];
+%% Test the hash/2 BIF on funs.
t_hash(Config) when is_list(Config) ->
F1 = fun(_X) -> 1 end,
F2 = fun(_X) -> 2 end,
- ?line true = hash(F1) /= hash(F2),
+ true = hash(F1) /= hash(F2),
G1 = make_fun(1, 2, 3),
G2 = make_fun(1, 2, 3),
G3 = make_fun(1, 2, 4),
- ?line true = hash(G1) == hash(G2),
- ?line true = hash(G2) /= hash(G3),
+ true = hash(G1) == hash(G2),
+ true = hash(G2) /= hash(G3),
FF0 = fun erlang:abs/1,
FF1 = fun erlang:exit/1,
FF2 = fun erlang:exit/2,
FF3 = fun blurf:exit/2,
- ?line true = hash(FF0) =/= hash(FF1),
- ?line true = hash(FF0) =/= hash(FF2),
- ?line true = hash(FF0) =/= hash(FF3),
- ?line true = hash(FF1) =/= hash(FF2),
- ?line true = hash(FF1) =/= hash(FF3),
- ?line true = hash(FF2) =/= hash(FF3),
+ true = hash(FF0) =/= hash(FF1),
+ true = hash(FF0) =/= hash(FF2),
+ true = hash(FF0) =/= hash(FF3),
+ true = hash(FF1) =/= hash(FF2),
+ true = hash(FF1) =/= hash(FF3),
+ true = hash(FF2) =/= hash(FF3),
ok.
hash(Term) ->
erlang:hash(Term, 16#7ffffff).
-t_phash(doc) -> "Test the phash/2 BIF on funs.";
-t_phash(suite) -> [];
+%% Test the phash/2 BIF on funs.
t_phash(Config) when is_list(Config) ->
F1 = fun(_X) -> 1 end,
F2 = fun(_X) -> 2 end,
- ?line true = phash(F1) /= phash(F2),
+ true = phash(F1) /= phash(F2),
G1 = make_fun(1, 2, 3),
G2 = make_fun(1, 2, 3),
G3 = make_fun(1, 2, 4),
- ?line true = phash(G1) == phash(G2),
- ?line true = phash(G2) /= phash(G3),
+ true = phash(G1) == phash(G2),
+ true = phash(G2) /= phash(G3),
FF0 = fun erlang:abs/1,
FF1 = fun erlang:exit/1,
FF2 = fun erlang:exit/2,
FF3 = fun blurf:exit/2,
- ?line true = phash(FF0) =/= phash(FF1),
- ?line true = phash(FF0) =/= phash(FF2),
- ?line true = phash(FF0) =/= phash(FF3),
- ?line true = phash(FF1) =/= phash(FF2),
- ?line true = phash(FF1) =/= phash(FF3),
- ?line true = phash(FF2) =/= phash(FF3),
+ true = phash(FF0) =/= phash(FF1),
+ true = phash(FF0) =/= phash(FF2),
+ true = phash(FF0) =/= phash(FF3),
+ true = phash(FF1) =/= phash(FF2),
+ true = phash(FF1) =/= phash(FF3),
+ true = phash(FF2) =/= phash(FF3),
ok.
phash(Term) ->
erlang:phash(Term, 16#7ffffff).
-t_phash2(doc) -> "Test the phash2/2 BIF on funs.";
-t_phash2(suite) -> [];
+%% Test the phash2/2 BIF on funs.
t_phash2(Config) when is_list(Config) ->
F1 = fun(_X) -> 1 end,
F2 = fun(_X) -> 2 end,
- ?line true = phash2(F1) /= phash2(F2),
+ true = phash2(F1) /= phash2(F2),
G1 = make_fun(1, 2, 3),
G2 = make_fun(1, 2, 3),
G3 = make_fun(1, 2, 4),
- ?line true = phash2(G1) == phash2(G2),
- ?line true = phash2(G2) /= phash2(G3),
+ true = phash2(G1) == phash2(G2),
+ true = phash2(G2) /= phash2(G3),
FF0 = fun erlang:abs/1,
FF1 = fun erlang:exit/1,
FF2 = fun erlang:exit/2,
FF3 = fun blurf:exit/2,
- ?line true = phash2(FF0) =/= phash2(FF1),
- ?line true = phash2(FF0) =/= phash2(FF2),
- ?line true = phash2(FF0) =/= phash2(FF3),
- ?line true = phash2(FF1) =/= phash2(FF2),
- ?line true = phash2(FF1) =/= phash2(FF3),
- ?line true = phash2(FF2) =/= phash2(FF3),
+ true = phash2(FF0) =/= phash2(FF1),
+ true = phash2(FF0) =/= phash2(FF2),
+ true = phash2(FF0) =/= phash2(FF3),
+ true = phash2(FF1) =/= phash2(FF2),
+ true = phash2(FF1) =/= phash2(FF3),
+ true = phash2(FF2) =/= phash2(FF3),
ok.
@@ -532,52 +498,51 @@ phash2(Term) ->
make_fun(X, Y, Z) ->
fun() -> {X,Y,Z} end.
-md5(doc) -> "Test that MD5 bifs reject funs properly.";
-md5(suite) -> [];
+%% Test that MD5 bifs reject funs properly.
md5(Config) when is_list(Config) ->
_ = size(erlang:md5_init()),
%% Try funs in the i/o list.
- ?line bad_md5(fun(_X) -> 42 end),
- ?line bad_md5([fun(_X) -> 43 end]),
- ?line bad_md5([1,fun(_X) -> 44 end]),
- ?line bad_md5([1|fun(_X) -> 45 end]),
- ?line B64K = build_io_list(65536),
- ?line bad_md5([B64K,fun(_X) -> 46 end]),
- ?line bad_md5([B64K|fun(_X) -> 46 end]),
+ bad_md5(fun(_X) -> 42 end),
+ bad_md5([fun(_X) -> 43 end]),
+ bad_md5([1,fun(_X) -> 44 end]),
+ bad_md5([1|fun(_X) -> 45 end]),
+ B64K = build_io_list(65536),
+ bad_md5([B64K,fun(_X) -> 46 end]),
+ bad_md5([B64K|fun(_X) -> 46 end]),
ok.
bad_md5(Bad) ->
{'EXIT',{badarg,_}} = (catch erlang:md5(Bad)).
refc(Config) when is_list(Config) ->
- ?line F1 = fun_factory(2),
- ?line {refc,2} = erlang:fun_info(F1, refc),
- ?line F2 = fun_factory(42),
- ?line {refc,3} = erlang:fun_info(F1, refc),
+ F1 = fun_factory(2),
+ {refc,2} = erlang:fun_info(F1, refc),
+ F2 = fun_factory(42),
+ {refc,3} = erlang:fun_info(F1, refc),
- ?line process_flag(trap_exit, true),
- ?line Pid = spawn_link(fun() -> {refc,4} = erlang:fun_info(F1, refc) end),
+ process_flag(trap_exit, true),
+ Pid = spawn_link(fun() -> {refc,4} = erlang:fun_info(F1, refc) end),
receive
{'EXIT',Pid,normal} -> ok;
- Other -> ?line ?t:fail({unexpected,Other})
+ Other -> ct:fail({unexpected,Other})
end,
- ?line process_flag(trap_exit, false),
- ?line {refc,3} = erlang:fun_info(F1, refc),
+ process_flag(trap_exit, false),
+ {refc,3} = erlang:fun_info(F1, refc),
%% Garbage collect. Only the F2 fun will be left.
- ?line 7 = F1(5),
- ?line true = erlang:garbage_collect(),
- ?line 40 = F2(-2),
- ?line {refc,2} = erlang:fun_info(F2, refc),
+ 7 = F1(5),
+ true = erlang:garbage_collect(),
+ 40 = F2(-2),
+ {refc,2} = erlang:fun_info(F2, refc),
ok.
fun_factory(Const) ->
fun(X) -> X + Const end.
refc_ets(Config) when is_list(Config) ->
- ?line F = fun(X) -> X + 33 end,
- ?line {refc,2} = erlang:fun_info(F, refc),
+ F = fun(X) -> X + 33 end,
+ {refc,2} = erlang:fun_info(F, refc),
refc_ets_set(F, [set]),
refc_ets_set(F, [ordered_set]),
@@ -586,115 +551,112 @@ refc_ets(Config) when is_list(Config) ->
ok.
refc_ets_set(F1, Options) ->
- ?line io:format("~p", [Options]),
- ?line Tab = ets:new(kalle, Options),
- ?line true = ets:insert(Tab, {a_key,F1}),
- ?line 3 = fun_refc(F1),
- ?line [{a_key,F3}] = ets:lookup(Tab, a_key),
- ?line 4 = fun_refc(F1),
- ?line true = ets:insert(Tab, {a_key,not_a_fun}),
- ?line 3 = fun_refc(F1),
- ?line true = ets:insert(Tab, {another_key,F1}),
- ?line 4 = fun_refc(F1),
- ?line true = ets:delete(Tab),
- ?line 3 = fun_refc(F1),
- ?line 10 = F3(-23),
- ?line true = erlang:garbage_collect(),
- ?line 2 = fun_refc(F1),
+ io:format("~p", [Options]),
+ Tab = ets:new(kalle, Options),
+ true = ets:insert(Tab, {a_key,F1}),
+ 3 = fun_refc(F1),
+ [{a_key,F3}] = ets:lookup(Tab, a_key),
+ 4 = fun_refc(F1),
+ true = ets:insert(Tab, {a_key,not_a_fun}),
+ 3 = fun_refc(F1),
+ true = ets:insert(Tab, {another_key,F1}),
+ 4 = fun_refc(F1),
+ true = ets:delete(Tab),
+ 3 = fun_refc(F1),
+ 10 = F3(-23),
+ true = erlang:garbage_collect(),
+ 2 = fun_refc(F1),
ok.
refc_ets_bag(F1, Options) ->
- ?line io:format("~p", [Options]),
- ?line Tab = ets:new(kalle, Options),
- ?line true = ets:insert(Tab, {a_key,F1}),
- ?line 3 = fun_refc(F1),
- ?line [{a_key,F3}] = ets:lookup(Tab, a_key),
- ?line 4 = fun_refc(F1),
- ?line true = ets:insert(Tab, {a_key,not_a_fun}),
- ?line 4 = fun_refc(F1),
- ?line true = ets:insert(Tab, {another_key,F1}),
- ?line 5 = fun_refc(F1),
- ?line true = ets:delete(Tab),
- ?line 3 = fun_refc(F1),
- ?line 10 = F3(-23),
- ?line true = erlang:garbage_collect(),
- ?line 2 = fun_refc(F1),
+ io:format("~p", [Options]),
+ Tab = ets:new(kalle, Options),
+ true = ets:insert(Tab, {a_key,F1}),
+ 3 = fun_refc(F1),
+ [{a_key,F3}] = ets:lookup(Tab, a_key),
+ 4 = fun_refc(F1),
+ true = ets:insert(Tab, {a_key,not_a_fun}),
+ 4 = fun_refc(F1),
+ true = ets:insert(Tab, {another_key,F1}),
+ 5 = fun_refc(F1),
+ true = ets:delete(Tab),
+ 3 = fun_refc(F1),
+ 10 = F3(-23),
+ true = erlang:garbage_collect(),
+ 2 = fun_refc(F1),
ok.
refc_dist(Config) when is_list(Config) ->
- ?line {ok,Node} = start_node(fun_SUITE_refc_dist),
- ?line process_flag(trap_exit, true),
- ?line Pid = spawn_link(Node,
- fun() -> receive
- Fun when is_function(Fun) ->
- 2 = fun_refc(Fun),
- exit({normal,Fun}) end
- end),
- ?line F = fun() -> 42 end,
- ?line 2 = fun_refc(F),
- ?line Pid ! F,
+ {ok,Node} = start_node(fun_SUITE_refc_dist),
+ process_flag(trap_exit, true),
+ Pid = spawn_link(Node, fun() -> receive
+ Fun when is_function(Fun) ->
+ 2 = fun_refc(Fun),
+ exit({normal,Fun}) end
+ end),
+ F = fun() -> 42 end,
+ 2 = fun_refc(F),
+ Pid ! F,
F2 = receive
{'EXIT',Pid,{normal,Fun}} -> Fun;
- Other -> ?line ?t:fail({unexpected,Other})
+ Other -> ct:fail({unexpected,Other})
end,
%% dist.c:net_mess2 have a reference to Fun for a while since
%% Fun is passed in an exit signal. Wait until it is gone.
- ?line wait_until(fun () -> 4 =/= fun_refc(F2) end),
- ?line 3 = fun_refc(F2),
- ?line true = erlang:garbage_collect(),
- ?line 2 = fun_refc(F),
+ wait_until(fun () -> 4 =/= fun_refc(F2) end),
+ 3 = fun_refc(F2),
+ true = erlang:garbage_collect(),
+ 2 = fun_refc(F),
refc_dist_send(Node, F).
refc_dist_send(Node, F) ->
- ?line Pid = spawn_link(Node,
- fun() -> receive
- {To,Fun} when is_function(Fun) ->
- wait_until(fun () ->
- 2 =:= fun_refc(Fun)
- end),
- To ! Fun
- end
- end),
- ?line 2 = fun_refc(F),
+ Pid = spawn_link(Node, fun() -> receive
+ {To,Fun} when is_function(Fun) ->
+ wait_until(fun () ->
+ 2 =:= fun_refc(Fun)
+ end),
+ To ! Fun
+ end
+ end),
+ 2 = fun_refc(F),
Pid ! {self(),F},
F2 = receive
Fun when is_function(Fun) -> Fun;
- Other -> ?line ?t:fail({unexpected,Other})
+ Other -> ct:fail({unexpected,Other})
end,
receive {'EXIT',Pid,normal} -> ok end,
%% No reference from dist.c:net_mess2 since Fun is passed
%% in an ordinary message.
- ?line 3 = fun_refc(F),
- ?line 3 = fun_refc(F2),
+ 3 = fun_refc(F),
+ 3 = fun_refc(F2),
refc_dist_reg_send(Node, F).
refc_dist_reg_send(Node, F) ->
- ?line true = erlang:garbage_collect(),
- ?line 2 = fun_refc(F),
- ?line Ref = make_ref(),
- ?line Me = self(),
- ?line Pid = spawn_link(Node,
- fun() ->
- true = register(my_fun_tester, self()),
- Me ! Ref,
- receive
- {Me,Fun} when is_function(Fun) ->
- 2 = fun_refc(Fun),
- Me ! Fun
- end
- end),
+ true = erlang:garbage_collect(),
+ 2 = fun_refc(F),
+ Ref = make_ref(),
+ Me = self(),
+ Pid = spawn_link(Node, fun() ->
+ true = register(my_fun_tester, self()),
+ Me ! Ref,
+ receive
+ {Me,Fun} when is_function(Fun) ->
+ 2 = fun_refc(Fun),
+ Me ! Fun
+ end
+ end),
erlang:yield(),
- ?line 2 = fun_refc(F),
+ 2 = fun_refc(F),
receive Ref -> ok end,
{my_fun_tester,Node} ! {self(),F},
F2 = receive
Fun when is_function(Fun) -> Fun;
- Other -> ?line ?t:fail({unexpected,Other})
+ Other -> ct:fail({unexpected,Other})
end,
receive {'EXIT',Pid,normal} -> ok end,
- ?line 3 = fun_refc(F),
- ?line 3 = fun_refc(F2),
+ 3 = fun_refc(F),
+ 3 = fun_refc(F2),
ok.
fun_refc(F) ->
@@ -702,67 +664,67 @@ fun_refc(F) ->
Count.
const_propagation(Config) when is_list(Config) ->
- ?line Fun1 = fun start_node/1,
- ?line 2 = fun_refc(Fun1),
- ?line Fun2 = Fun1,
- ?line my_cmp({Fun1,Fun2}),
-
- ?line Fun3 = fun() -> ok end,
- ?line 2 = fun_refc(Fun3),
- ?line Fun4 = Fun3,
- ?line my_cmp({Fun3,Fun4}),
+ Fun1 = fun start_node/1,
+ 2 = fun_refc(Fun1),
+ Fun2 = Fun1,
+ my_cmp({Fun1,Fun2}),
+
+ Fun3 = fun() -> ok end,
+ 2 = fun_refc(Fun3),
+ Fun4 = Fun3,
+ my_cmp({Fun3,Fun4}),
ok.
my_cmp({Fun,Fun}) -> ok;
my_cmp({Fun1,Fun2}) ->
io:format("Fun1: ~p", [erlang:fun_info(Fun1)]),
io:format("Fun2: ~p", [erlang:fun_info(Fun2)]),
- ?t:fail().
+ ct:fail(no_match).
t_arity(Config) when is_list(Config) ->
- ?line 0 = fun_arity(fun() -> ok end),
- ?line 0 = fun_arity(fun() -> Config end),
- ?line 1 = fun_arity(fun(X) -> X+1 end),
- ?line 1 = fun_arity(fun(X) -> Config =:= X end),
+ 0 = fun_arity(fun() -> ok end),
+ 0 = fun_arity(fun() -> Config end),
+ 1 = fun_arity(fun(X) -> X+1 end),
+ 1 = fun_arity(fun(X) -> Config =:= X end),
A = id(42),
%% Test that the arity is transferred properly.
- ?line process_flag(trap_exit, true),
- ?line {ok,Node} = start_node(fun_test_arity),
- ?line hello_world = spawn_call(Node, fun() -> hello_world end),
- ?line 0 = spawn_call(Node, fun(X) -> X end),
- ?line 42 = spawn_call(Node, fun(_X) -> A end),
- ?line 43 = spawn_call(Node, fun(X, Y) -> A+X+Y end),
- ?line 1 = spawn_call(Node, fun(X, Y) -> X+Y end),
- ?line 45 = spawn_call(Node, fun(X, Y, Z) -> A+X+Y+Z end),
+ process_flag(trap_exit, true),
+ {ok,Node} = start_node(fun_test_arity),
+ hello_world = spawn_call(Node, fun() -> hello_world end),
+ 0 = spawn_call(Node, fun(X) -> X end),
+ 42 = spawn_call(Node, fun(_X) -> A end),
+ 43 = spawn_call(Node, fun(X, Y) -> A+X+Y end),
+ 1 = spawn_call(Node, fun(X, Y) -> X+Y end),
+ 45 = spawn_call(Node, fun(X, Y, Z) -> A+X+Y+Z end),
ok.
t_is_function2(Config) when is_list(Config) ->
false = is_function(id({a,b}), 0),
false = is_function(id({a,b}), 234343434333433433),
- ?line true = is_function(fun() -> ok end, 0),
- ?line true = is_function(fun(_) -> ok end, 1),
- ?line false = is_function(fun(_) -> ok end, 0),
+ true = is_function(fun() -> ok end, 0),
+ true = is_function(fun(_) -> ok end, 1),
+ false = is_function(fun(_) -> ok end, 0),
- ?line true = is_function(fun erlang:abs/1, 1),
- ?line true = is_function(fun erlang:abs/99, 99),
- ?line false = is_function(fun erlang:abs/1, 0),
- ?line false = is_function(fun erlang:abs/99, 0),
+ true = is_function(fun erlang:abs/1, 1),
+ true = is_function(fun erlang:abs/99, 99),
+ false = is_function(fun erlang:abs/1, 0),
+ false = is_function(fun erlang:abs/99, 0),
- ?line false = is_function(id(self()), 0),
- ?line false = is_function(id({a,b,c}), 0),
- ?line false = is_function(id({a}), 0),
- ?line false = is_function(id([a,b,c]), 0),
+ false = is_function(id(self()), 0),
+ false = is_function(id({a,b,c}), 0),
+ false = is_function(id({a}), 0),
+ false = is_function(id([a,b,c]), 0),
%% Bad arity argument.
- ?line bad_arity(a),
- ?line bad_arity(-1),
- ?line bad_arity(-9738974938734938793873498378),
- ?line bad_arity([]),
- ?line bad_arity(fun() -> ok end),
- ?line bad_arity({}),
- ?line bad_arity({a,b}),
- ?line bad_arity(self()),
+ bad_arity(a),
+ bad_arity(-1),
+ bad_arity(-9738974938734938793873498378),
+ bad_arity([]),
+ bad_arity(fun() -> ok end),
+ bad_arity({}),
+ bad_arity({a,b}),
+ bad_arity(self()),
ok.
bad_arity(A) ->
@@ -771,59 +733,57 @@ bad_arity(A) ->
ok.
t_fun_info(Config) when is_list(Config) ->
- ?line F = fun t_fun_info/1,
- ?line try F(blurf) of
+ F = fun t_fun_info/1,
+ try F(blurf) of
FAny ->
- io:format("should fail; returned ~p\n", [FAny]),
- ?line ?t:fail()
+ ct:fail("should fail; returned ~p\n", [FAny])
catch
error:function_clause -> ok
end,
- ?line {module,?MODULE} = erlang:fun_info(F, module),
- ?line case erlang:fun_info(F, name) of
+ {module,?MODULE} = erlang:fun_info(F, module),
+ case erlang:fun_info(F, name) of
undefined ->
- ?line ?t:fail();
+ ct:fail(no_fun_info);
_ -> ok
end,
- ?line {arity,1} = erlang:fun_info(F, arity),
- ?line {env,[]} = erlang:fun_info(F, env),
- ?line verify_not_undef(F, index),
- ?line verify_not_undef(F, uniq),
- ?line verify_not_undef(F, new_index),
- ?line verify_not_undef(F, new_uniq),
- ?line verify_not_undef(F, refc),
- ?line {'EXIT',_} = (catch erlang:fun_info(F, blurf)),
+ {arity,1} = erlang:fun_info(F, arity),
+ {env,[]} = erlang:fun_info(F, env),
+ verify_not_undef(F, index),
+ verify_not_undef(F, uniq),
+ verify_not_undef(F, new_index),
+ verify_not_undef(F, new_uniq),
+ verify_not_undef(F, refc),
+ {'EXIT',_} = (catch erlang:fun_info(F, blurf)),
%% Module fun.
- ?line FF = fun ?MODULE:t_fun_info/1,
- ?line try FF(blurf) of
+ FF = fun ?MODULE:t_fun_info/1,
+ try FF(blurf) of
FFAny ->
- io:format("should fail; returned ~p\n", [FFAny]),
- ?line ?t:fail()
+ ct:fail("should fail; returned ~p\n", [FFAny])
catch
error:function_clause -> ok
end,
- ?line {module,?MODULE} = erlang:fun_info(FF, module),
- ?line {name,t_fun_info} = erlang:fun_info(FF, name),
- ?line {arity,1} = erlang:fun_info(FF, arity),
- ?line {env,[]} = erlang:fun_info(FF, env),
- ?line verify_undef(FF, index),
- ?line verify_undef(FF, uniq),
- ?line verify_undef(FF, new_index),
- ?line verify_undef(FF, new_uniq),
- ?line verify_undef(FF, refc),
- ?line {'EXIT',_} = (catch erlang:fun_info(FF, blurf)),
+ {module,?MODULE} = erlang:fun_info(FF, module),
+ {name,t_fun_info} = erlang:fun_info(FF, name),
+ {arity,1} = erlang:fun_info(FF, arity),
+ {env,[]} = erlang:fun_info(FF, env),
+ verify_undef(FF, index),
+ verify_undef(FF, uniq),
+ verify_undef(FF, new_index),
+ verify_undef(FF, new_uniq),
+ verify_undef(FF, refc),
+ {'EXIT',_} = (catch erlang:fun_info(FF, blurf)),
%% Not fun.
- ?line bad_info(abc),
- ?line bad_info(42),
- ?line bad_info({fun erlang:list_to_integer/1}),
- ?line bad_info([42]),
- ?line bad_info([]),
- ?line bad_info(self()),
- ?line bad_info(<<>>),
- ?line bad_info(<<1,2>>),
+ bad_info(abc),
+ bad_info(42),
+ bad_info({fun erlang:list_to_integer/1}),
+ bad_info([42]),
+ bad_info([]),
+ bad_info(self()),
+ bad_info(<<>>),
+ bad_info(<<1,2>>),
ok.
t_fun_info_mfa(Config) when is_list(Config) ->
@@ -847,8 +807,7 @@ t_fun_info_mfa(Config) when is_list(Config) ->
bad_info(Term) ->
try erlang:fun_info(Term, module) of
Any ->
- io:format("should fail; returned ~p\n", [Any]),
- ?t:fail()
+ ict:fail("should fail; returned ~p\n", [Any])
catch
error:badarg -> ok
end.
@@ -859,7 +818,7 @@ verify_undef(Fun, Tag) ->
verify_not_undef(Fun, Tag) ->
case erlang:fun_info(Fun, Tag) of
{Tag,undefined} ->
- ?t:fail();
+ ct:fail("tag ~w not defined in fun_info", [Tag]);
{Tag,_} -> ok
end.
@@ -884,15 +843,15 @@ spawn_call(Node, AFun) ->
Pid ! {AFun,AFun,AFun},
Res = receive
{result,R} -> R;
- Other -> ?t:fail({bad_message,Other})
+ Other -> ct:fail({bad_message,Other})
after 10000 ->
- ?t:fail(timeout_waiting_for_result)
+ ct:fail(timeout_waiting_for_result)
end,
receive
{'EXIT',Pid,normal} -> ok;
- Other2 -> ?t:fail({bad_message_waiting_for_exit,Other2})
+ Other2 -> ct:fail({bad_message_waiting_for_exit,Other2})
after 10000 ->
- ?t:fail(timeout_waiting_for_exit)
+ ct:fail(timeout_waiting_for_exit)
end,
Res.
@@ -911,6 +870,3 @@ wait_until(Fun) ->
true -> ok;
_ -> receive after 100 -> wait_until(Fun) end
end.
-
-% stop_node(Node) ->
-% test_server:stop_node(Node).
diff --git a/erts/emulator/test/fun_r13_SUITE.erl b/erts/emulator/test/fun_r13_SUITE.erl
index 7ab5e65cb3..a45ed08b9d 100644
--- a/erts/emulator/test/fun_r13_SUITE.erl
+++ b/erts/emulator/test/fun_r13_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -21,77 +21,53 @@
-module(fun_r13_SUITE).
-compile(r13).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2,end_per_testcase/2,dist_old_release/1]).
+-export([all/0, suite/0,
+ dist_old_release/1]).
--define(default_timeout, ?t:minutes(1)).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 1}}].
all() ->
[dist_old_release].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-init_per_testcase(_Case, Config) ->
- ?line Dog = test_server:timetrap(?default_timeout),
- [{watchdog, Dog}|Config].
-
-end_per_testcase(_Case, Config) ->
- Dog=?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
- ok.
-
dist_old_release(Config) when is_list(Config) ->
- case ?t:is_release_available("r12b") of
- true -> do_dist_old(Config);
- false -> {skip,"No R12B found"}
+ case test_server:is_release_available("r12b") of
+ true -> do_dist_old(Config);
+ false -> {skip,"No R12B found"}
end.
do_dist_old(Config) when is_list(Config) ->
- ?line Pa = filename:dirname(code:which(?MODULE)),
+ Pa = filename:dirname(code:which(?MODULE)),
Name = fun_dist_r12,
- ?line {ok,Node} = ?t:start_node(Name, peer,
- [{args,"-pa "++Pa},
- {erl,[{release,"r12b"}]}]),
-
- ?line Pid = spawn_link(Node,
- fun() ->
- receive
- Fun when is_function(Fun) ->
- R12BFun = fun(H) -> cons(H, [b,c]) end,
- Fun(Fun, R12BFun)
- end
- end),
+ {ok,Node} = test_server:start_node(Name, peer,
+ [{args,"-pa "++Pa},
+ {erl,[{release,"r12b"}]}]),
+
+ Pid = spawn_link(Node,
+ fun() ->
+ receive
+ Fun when is_function(Fun) ->
+ R12BFun = fun(H) -> cons(H, [b,c]) end,
+ Fun(Fun, R12BFun)
+ end
+ end),
Self = self(),
Fun = fun(F, R12BFun) ->
- {pid,Self} = erlang:fun_info(F, pid),
- {module,?MODULE} = erlang:fun_info(F, module),
- Self ! {ok,F,R12BFun}
- end,
- ?line Pid ! Fun,
- ?line receive
- {ok,Fun,R12BFun} ->
- ?line [a,b,c] = R12BFun(a);
- Other ->
- ?line ?t:fail({bad_message,Other})
- end,
+ {pid,Self} = erlang:fun_info(F, pid),
+ {module,?MODULE} = erlang:fun_info(F, module),
+ Self ! {ok,F,R12BFun}
+ end,
+ Pid ! Fun,
+ receive
+ {ok,Fun,R12BFun} ->
+ [a,b,c] = R12BFun(a);
+ Other ->
+ ct:fail({bad_message,Other})
+ end,
+ true = test_server:stop_node(Node),
ok.
cons(H, T) ->
diff --git a/erts/emulator/test/gc_SUITE.erl b/erts/emulator/test/gc_SUITE.erl
index 1e155e7b09..8a600b7d9f 100644
--- a/erts/emulator/test/gc_SUITE.erl
+++ b/erts/emulator/test/gc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -22,45 +22,27 @@
-module(gc_SUITE).
--include_lib("test_server/include/test_server.hrl").
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2]).
+-include_lib("common_test/include/ct.hrl").
+-export([all/0, suite/0]).
--define(default_timeout, ?t:minutes(10)).
+-export([grow_heap/1, grow_stack/1, grow_stack_heap/1, max_heap_size/1]).
--export([grow_heap/1, grow_stack/1, grow_stack_heap/1]).
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]}].
all() ->
- [grow_heap, grow_stack, grow_stack_heap].
-
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
+ [grow_heap, grow_stack, grow_stack_heap, max_heap_size].
-grow_heap(doc) -> ["Produce a growing list of elements, ",
- "for X calls, then drop one item per call",
- "until the list is empty."];
+%% Produce a growing list of elements,
+%% for X calls, then drop one item per call
+%% until the list is empty.
grow_heap(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:minutes(40)),
+ ct:timetrap({minutes, 40}),
ok = grow_heap1(256),
ok = grow_heap1(512),
ok = grow_heap1(1024),
ok = grow_heap1(2048),
- test_server:timetrap_cancel(Dog),
ok.
grow_heap1(Len) ->
@@ -86,14 +68,13 @@ grow_heap1([_|List], MaxLen, CurLen, down) ->
-grow_stack(doc) -> ["Increase and decrease stack size, and ",
- "drop off some garbage from time to time."];
+%% Increase and decrease stack size, and
+%% drop off some garbage from time to time.
grow_stack(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:minutes(80)),
+ ct:timetrap({minutes, 80}),
show_heap("before:"),
grow_stack1(200, 0),
show_heap("after:"),
- test_server:timetrap_cancel(Dog),
ok.
grow_stack1(0, _) ->
@@ -110,14 +91,12 @@ grow_stack1(Recs, CurRecs) ->
%% Let's see how BEAM handles this one...
-grow_stack_heap(doc) -> ["While growing the heap, bounces the size ",
- "of the stack, and while reducing the heap",
- "bounces the stack usage."];
+%% While growing the heap, bounces the size of the
+%% stack, and while reducing the heap, bounces the stack usage.
grow_stack_heap(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:minutes(40)),
+ ct:timetrap({minutes, 40}),
grow_stack_heap1(16),
grow_stack_heap1(32),
- test_server:timetrap_cancel(Dog),
ok.
grow_stack_heap1(MaxLen) ->
@@ -184,3 +163,30 @@ show_heap(String) ->
{stack_size, SSize}=process_info(self(), stack_size),
io:format("Heap/Stack "++String++"~p/~p", [HSize, SSize]).
+%% Test that doing a remote GC that triggers the max heap size
+%% kills the process.
+max_heap_size(_Config) ->
+
+ Pid = spawn_opt(fun long_receive/0,[{max_heap_size, 1024},
+ {message_queue_data, on_heap}]),
+ [Pid ! lists:duplicate(I,I) || I <- lists:seq(1,100)],
+ Ref = erlang:monitor(process, Pid),
+
+ %% Force messages to be viewed as part of heap
+ erlang:process_info(Pid, messages),
+
+ %% Do the GC that triggers max heap
+ erlang:garbage_collect(Pid),
+
+ %% Verify that max heap was triggered
+ receive
+ {'DOWN', Ref, process, Pid, killed} -> ok
+ after 5000 ->
+ ct:fail({process_did_not_die, Pid, erlang:process_info(Pid)})
+ end.
+
+long_receive() ->
+ receive
+ after 10000 ->
+ ok
+ end.
diff --git a/erts/emulator/test/guard_SUITE.erl b/erts/emulator/test/guard_SUITE.erl
index b3a85c6423..1a93a9f5c2 100644
--- a/erts/emulator/test/guard_SUITE.erl
+++ b/erts/emulator/test/guard_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. 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.
@@ -20,12 +20,12 @@
-module(guard_SUITE).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2, bad_arith/1, bad_tuple/1,
+-export([all/0, suite/0,
+ bad_arith/1, bad_tuple/1,
test_heap_guards/1, guard_bifs/1,
type_tests/1,guard_bif_binary_part/1]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-export([init/3]).
-import(lists, [member/2]).
@@ -36,27 +36,12 @@ all() ->
[bad_arith, bad_tuple, test_heap_guards, guard_bifs,
type_tests, guard_bif_binary_part].
-groups() ->
- [].
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-bad_arith(doc) -> "Test that a bad arithmetic operation in a guard works correctly.";
+%% Test that a bad arithmetic operation in a guard works correctly.
bad_arith(Config) when is_list(Config) ->
- ?line 5 = bad_arith1(2, 3),
- ?line 10 = bad_arith1(1, infinity),
- ?line 10 = bad_arith1(infinity, 1),
+ 5 = bad_arith1(2, 3),
+ 10 = bad_arith1(1, infinity),
+ 10 = bad_arith1(infinity, 1),
ok.
bad_arith1(T1, T2) when T1+T2 < 10 ->
@@ -64,12 +49,12 @@ bad_arith1(T1, T2) when T1+T2 < 10 ->
bad_arith1(_, _) ->
10.
-bad_tuple(doc) -> "Test that bad arguments to element/2 are handled correctly.";
+%% Test that bad arguments to element/2 are handled correctly.
bad_tuple(Config) when is_list(Config) ->
- ?line error = bad_tuple1(a),
- ?line error = bad_tuple1({a, b}),
- ?line x = bad_tuple1({x, b}),
- ?line y = bad_tuple1({a, b, y}),
+ error = bad_tuple1(a),
+ error = bad_tuple1({a, b}),
+ x = bad_tuple1({x, b}),
+ y = bad_tuple1({a, b, y}),
ok.
bad_tuple1(T) when element(1, T) == x ->
@@ -79,26 +64,25 @@ bad_tuple1(T) when element(3, T) == y ->
bad_tuple1(_) ->
error.
-test_heap_guards(doc) -> "";
test_heap_guards(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:minutes(2)),
+ ct:timetrap({minutes, 2}),
- ?line process_flag(trap_exit, true),
- ?line Tuple = {a, tuple, is, built, here, xxx},
- ?line List = [a, list, is, built, here],
+ process_flag(trap_exit, true),
+ Tuple = {a, tuple, is, built, here, xxx},
+ List = [a, list, is, built, here],
- ?line 'try'(fun a_case/1, [Tuple], [Tuple]),
- ?line 'try'(fun a_case/1, [List], [List, List]),
- ?line 'try'(fun a_case/1, [a], [a]),
+ 'try'(fun a_case/1, [Tuple], [Tuple]),
+ 'try'(fun a_case/1, [List], [List, List]),
+ 'try'(fun a_case/1, [a], [a]),
- ?line 'try'(fun an_if/1, [Tuple], [Tuple]),
- ?line 'try'(fun an_if/1, [List], [List, List]),
- ?line 'try'(fun an_if/1, [a], [a]),
+ 'try'(fun an_if/1, [Tuple], [Tuple]),
+ 'try'(fun an_if/1, [List], [List, List]),
+ 'try'(fun an_if/1, [a], [a]),
- ?line 'try'(fun receive_test/1, [Tuple], [Tuple]),
- ?line 'try'(fun receive_test/1, [List], [List, List]),
- ?line 'try'(fun receive_test/1, [a], [a]),
- ?line test_server:timetrap_cancel(Dog).
+ 'try'(fun receive_test/1, [Tuple], [Tuple]),
+ 'try'(fun receive_test/1, [List], [List, List]),
+ 'try'(fun receive_test/1, [a], [a]),
+ ok.
a_case(V) ->
case V of
@@ -143,12 +127,11 @@ a_receive() ->
Pid = spawn_link(?MODULE, init, [Fun,Args,list_to_tuple(Filler)]),
receive
{'EXIT', Pid, {result, Result}} ->
- ?line 'try'(Iter-1, Fun, Args, Result, [0|Filler]);
+ 'try'(Iter-1, Fun, Args, Result, [0|Filler]);
{result, Other} ->
- ?line io:format("Expected ~p; got ~p~n", [Result, Other]),
- ?line test_server:fail();
+ ct:fail("Expected ~p; got ~p~n", [Result, Other]);
Other ->
- ?line test_server:fail({unexpected_message, Other})
+ ct:fail({unexpected_message, Other})
end.
init(Fun, Args, Filler) ->
@@ -165,15 +148,14 @@ mask_error({'EXIT',{Err,_}}) ->
mask_error(Else) ->
Else.
-guard_bif_binary_part(doc) ->
- ["Test the binary_part/2,3 guard BIF's extensively"];
+%% Test the binary_part/2,3 guard BIF's extensively
guard_bif_binary_part(Config) when is_list(Config) ->
%% Overflow tests that need to be unoptimized
- ?line badarg =
+ badarg =
?MASK_ERROR(
binary_part(<<1,2,3>>,{16#FFFFFFFFFFFFFFFF,
-16#7FFFFFFFFFFFFFFF-1})),
- ?line badarg =
+ badarg =
?MASK_ERROR(
binary_part(<<1,2,3>>,{16#FFFFFFFFFFFFFFFF,
16#7FFFFFFFFFFFFFFF})),
@@ -198,66 +180,66 @@ guard_bif_binary_part(Config) when is_list(Config) ->
do_binary_part_guard() ->
- ?line 1 = bptest(<<1,2,3>>),
- ?line 2 = bptest(<<2,1,3>>),
- ?line error = bptest(<<1>>),
- ?line error = bptest(<<>>),
- ?line error = bptest(apa),
- ?line 3 = bptest(<<2,3,3>>),
+ 1 = bptest(<<1,2,3>>),
+ 2 = bptest(<<2,1,3>>),
+ error = bptest(<<1>>),
+ error = bptest(<<>>),
+ error = bptest(apa),
+ 3 = bptest(<<2,3,3>>),
% With one variable (pos)
- ?line 1 = bptest(<<1,2,3>>,1),
- ?line 2 = bptest(<<2,1,3>>,1),
- ?line error = bptest(<<1>>,1),
- ?line error = bptest(<<>>,1),
- ?line error = bptest(apa,1),
- ?line 3 = bptest(<<2,3,3>>,1),
+ 1 = bptest(<<1,2,3>>,1),
+ 2 = bptest(<<2,1,3>>,1),
+ error = bptest(<<1>>,1),
+ error = bptest(<<>>,1),
+ error = bptest(apa,1),
+ 3 = bptest(<<2,3,3>>,1),
% With one variable (length)
- ?line 1 = bptesty(<<1,2,3>>,1),
- ?line 2 = bptesty(<<2,1,3>>,1),
- ?line error = bptesty(<<1>>,1),
- ?line error = bptesty(<<>>,1),
- ?line error = bptesty(apa,1),
- ?line 3 = bptesty(<<2,3,3>>,2),
+ 1 = bptesty(<<1,2,3>>,1),
+ 2 = bptesty(<<2,1,3>>,1),
+ error = bptesty(<<1>>,1),
+ error = bptesty(<<>>,1),
+ error = bptesty(apa,1),
+ 3 = bptesty(<<2,3,3>>,2),
% With one variable (whole tuple)
- ?line 1 = bptestx(<<1,2,3>>,{1,1}),
- ?line 2 = bptestx(<<2,1,3>>,{1,1}),
- ?line error = bptestx(<<1>>,{1,1}),
- ?line error = bptestx(<<>>,{1,1}),
- ?line error = bptestx(apa,{1,1}),
- ?line 3 = bptestx(<<2,3,3>>,{1,2}),
+ 1 = bptestx(<<1,2,3>>,{1,1}),
+ 2 = bptestx(<<2,1,3>>,{1,1}),
+ error = bptestx(<<1>>,{1,1}),
+ error = bptestx(<<>>,{1,1}),
+ error = bptestx(apa,{1,1}),
+ 3 = bptestx(<<2,3,3>>,{1,2}),
% With two variables
- ?line 1 = bptest(<<1,2,3>>,1,1),
- ?line 2 = bptest(<<2,1,3>>,1,1),
- ?line error = bptest(<<1>>,1,1),
- ?line error = bptest(<<>>,1,1),
- ?line error = bptest(apa,1,1),
- ?line 3 = bptest(<<2,3,3>>,1,2),
+ 1 = bptest(<<1,2,3>>,1,1),
+ 2 = bptest(<<2,1,3>>,1,1),
+ error = bptest(<<1>>,1,1),
+ error = bptest(<<>>,1,1),
+ error = bptest(apa,1,1),
+ 3 = bptest(<<2,3,3>>,1,2),
% Direct (autoimported) call, these will be evaluated by the compiler...
- ?line <<2>> = binary_part(<<1,2,3>>,1,1),
- ?line <<1>> = binary_part(<<2,1,3>>,1,1),
+ <<2>> = binary_part(<<1,2,3>>,1,1),
+ <<1>> = binary_part(<<2,1,3>>,1,1),
% Compiler warnings due to constant evaluation expected (3)
- ?line badarg = ?MASK_ERROR(binary_part(<<1>>,1,1)),
- ?line badarg = ?MASK_ERROR(binary_part(<<>>,1,1)),
- ?line badarg = ?MASK_ERROR(binary_part(apa,1,1)),
- ?line <<3,3>> = binary_part(<<2,3,3>>,1,2),
+ badarg = ?MASK_ERROR(binary_part(<<1>>,1,1)),
+ badarg = ?MASK_ERROR(binary_part(<<>>,1,1)),
+ badarg = ?MASK_ERROR(binary_part(apa,1,1)),
+ <<3,3>> = binary_part(<<2,3,3>>,1,2),
% Direct call through apply
- ?line <<2>> = apply(erlang,binary_part,[<<1,2,3>>,1,1]),
- ?line <<1>> = apply(erlang,binary_part,[<<2,1,3>>,1,1]),
+ <<2>> = apply(erlang,binary_part,[<<1,2,3>>,1,1]),
+ <<1>> = apply(erlang,binary_part,[<<2,1,3>>,1,1]),
% Compiler warnings due to constant evaluation expected (3)
- ?line badarg = ?MASK_ERROR(apply(erlang,binary_part,[<<1>>,1,1])),
- ?line badarg = ?MASK_ERROR(apply(erlang,binary_part,[<<>>,1,1])),
- ?line badarg = ?MASK_ERROR(apply(erlang,binary_part,[apa,1,1])),
- ?line <<3,3>> = apply(erlang,binary_part,[<<2,3,3>>,1,2]),
+ badarg = ?MASK_ERROR(apply(erlang,binary_part,[<<1>>,1,1])),
+ badarg = ?MASK_ERROR(apply(erlang,binary_part,[<<>>,1,1])),
+ badarg = ?MASK_ERROR(apply(erlang,binary_part,[apa,1,1])),
+ <<3,3>> = apply(erlang,binary_part,[<<2,3,3>>,1,2]),
% Constant propagation
- ?line Bin = <<1,2,3>>,
- ?line ok = if
+ Bin = <<1,2,3>>,
+ ok = if
binary_part(Bin,1,1) =:= <<2>> ->
ok;
%% Compiler warning, clause cannot match (expected)
true ->
error
end,
- ?line ok = if
+ ok = if
binary_part(Bin,{1,1}) =:= <<2>> ->
ok;
%% Compiler warning, clause cannot match (expected)
@@ -323,91 +305,92 @@ bptest(_,_,_) ->
error.
-guard_bifs(doc) -> "Test all guard bifs with nasty (but legal arguments).";
+%% Test all guard bifs with nasty (but legal arguments).
guard_bifs(Config) when is_list(Config) ->
- ?line Big = -237849247829874297658726487367328971246284736473821617265433,
- ?line Float = 387924.874,
+ Big = -237849247829874297658726487367328971246284736473821617265433,
+ Float = 387924.874,
%% Succeding use of guard bifs.
- ?line try_gbif('abs/1', Big, -Big),
- ?line try_gbif('float/1', Big, float(Big)),
- ?line try_gbif('float/1', Big, float(id(Big))),
- ?line try_gbif('trunc/1', Float, 387924.0),
- ?line try_gbif('round/1', Float, 387925.0),
- ?line try_gbif('length/1', [], 0),
+ try_gbif('abs/1', Big, -Big),
+ try_gbif('float/1', Big, float(Big)),
+ try_gbif('float/1', Big, float(id(Big))),
+ try_gbif('trunc/1', Float, 387924.0),
+ try_gbif('round/1', Float, 387925.0),
+ try_gbif('round/1', 6209607916799025.0, 6209607916799025),
+ try_gbif('length/1', [], 0),
- ?line try_gbif('length/1', [a], 1),
- ?line try_gbif('length/1', [a, b], 2),
- ?line try_gbif('length/1', lists:seq(0, 31), 32),
+ try_gbif('length/1', [a], 1),
+ try_gbif('length/1', [a, b], 2),
+ try_gbif('length/1', lists:seq(0, 31), 32),
- ?line try_gbif('hd/1', [a], a),
- ?line try_gbif('hd/1', [a, b], a),
+ try_gbif('hd/1', [a], a),
+ try_gbif('hd/1', [a, b], a),
- ?line try_gbif('tl/1', [a], []),
- ?line try_gbif('tl/1', [a, b], [b]),
- ?line try_gbif('tl/1', [a, b, c], [b, c]),
+ try_gbif('tl/1', [a], []),
+ try_gbif('tl/1', [a, b], [b]),
+ try_gbif('tl/1', [a, b, c], [b, c]),
- ?line try_gbif('size/1', {}, 0),
- ?line try_gbif('size/1', {a}, 1),
- ?line try_gbif('size/1', {a, b}, 2),
- ?line try_gbif('size/1', {a, b, c}, 3),
- ?line try_gbif('size/1', list_to_binary([]), 0),
- ?line try_gbif('size/1', list_to_binary([1]), 1),
- ?line try_gbif('size/1', list_to_binary([1, 2]), 2),
- ?line try_gbif('size/1', list_to_binary([1, 2, 3]), 3),
+ try_gbif('size/1', {}, 0),
+ try_gbif('size/1', {a}, 1),
+ try_gbif('size/1', {a, b}, 2),
+ try_gbif('size/1', {a, b, c}, 3),
+ try_gbif('size/1', list_to_binary([]), 0),
+ try_gbif('size/1', list_to_binary([1]), 1),
+ try_gbif('size/1', list_to_binary([1, 2]), 2),
+ try_gbif('size/1', list_to_binary([1, 2, 3]), 3),
- ?line try_gbif('bit_size/1', <<0:7>>, 7),
+ try_gbif('bit_size/1', <<0:7>>, 7),
- ?line try_gbif('element/2', {x}, {1, x}),
- ?line try_gbif('element/2', {x, y}, {1, x}),
- ?line try_gbif('element/2', {x, y}, {2, y}),
+ try_gbif('element/2', {x}, {1, x}),
+ try_gbif('element/2', {x, y}, {1, x}),
+ try_gbif('element/2', {x, y}, {2, y}),
- ?line try_gbif('self/0', 0, self()),
- ?line try_gbif('node/0', 0, node()),
- ?line try_gbif('node/1', self(), node()),
+ try_gbif('self/0', 0, self()),
+ try_gbif('node/0', 0, node()),
+ try_gbif('node/1', self(), node()),
%% Failing use of guard bifs.
- ?line try_fail_gbif('abs/1', Big, 1),
- ?line try_fail_gbif('abs/1', [], 1),
+ try_fail_gbif('abs/1', Big, 1),
+ try_fail_gbif('abs/1', [], 1),
- ?line try_fail_gbif('float/1', Big, 42),
- ?line try_fail_gbif('float/1', [], 42),
+ try_fail_gbif('float/1', Big, 42),
+ try_fail_gbif('float/1', [], 42),
- ?line try_fail_gbif('trunc/1', Float, 0.0),
- ?line try_fail_gbif('trunc/1', [], 0.0),
+ try_fail_gbif('trunc/1', Float, 0.0),
+ try_fail_gbif('trunc/1', [], 0.0),
- ?line try_fail_gbif('round/1', Float, 1.0),
- ?line try_fail_gbif('round/1', [], a),
+ try_fail_gbif('round/1', Float, 1.0),
+ try_fail_gbif('round/1', [], a),
- ?line try_fail_gbif('length/1', [], 1),
- ?line try_fail_gbif('length/1', [a], 0),
- ?line try_fail_gbif('length/1', a, 0),
- ?line try_fail_gbif('length/1', {a}, 0),
+ try_fail_gbif('length/1', [], 1),
+ try_fail_gbif('length/1', [a], 0),
+ try_fail_gbif('length/1', a, 0),
+ try_fail_gbif('length/1', {a}, 0),
- ?line try_fail_gbif('hd/1', [], 0),
- ?line try_fail_gbif('hd/1', [a], x),
- ?line try_fail_gbif('hd/1', x, x),
+ try_fail_gbif('hd/1', [], 0),
+ try_fail_gbif('hd/1', [a], x),
+ try_fail_gbif('hd/1', x, x),
- ?line try_fail_gbif('tl/1', [], 0),
- ?line try_fail_gbif('tl/1', [a], x),
- ?line try_fail_gbif('tl/1', x, x),
+ try_fail_gbif('tl/1', [], 0),
+ try_fail_gbif('tl/1', [a], x),
+ try_fail_gbif('tl/1', x, x),
- ?line try_fail_gbif('size/1', {}, 1),
- ?line try_fail_gbif('size/1', [], 0),
- ?line try_fail_gbif('size/1', [a], 1),
- ?line try_fail_gbif('size/1', fun() -> 1 end, 0),
- ?line try_fail_gbif('size/1', fun() -> 1 end, 1),
+ try_fail_gbif('size/1', {}, 1),
+ try_fail_gbif('size/1', [], 0),
+ try_fail_gbif('size/1', [a], 1),
+ try_fail_gbif('size/1', fun() -> 1 end, 0),
+ try_fail_gbif('size/1', fun() -> 1 end, 1),
- ?line try_fail_gbif('element/2', {}, {1, x}),
- ?line try_fail_gbif('element/2', {x}, {1, y}),
- ?line try_fail_gbif('element/2', [], {1, z}),
+ try_fail_gbif('element/2', {}, {1, x}),
+ try_fail_gbif('element/2', {x}, {1, y}),
+ try_fail_gbif('element/2', [], {1, z}),
- ?line try_fail_gbif('self/0', 0, list_to_pid("<0.0.0>")),
- ?line try_fail_gbif('node/0', 0, xxxx),
- ?line try_fail_gbif('node/1', self(), xxx),
- ?line try_fail_gbif('node/1', yyy, xxx),
+ try_fail_gbif('self/0', 0, list_to_pid("<0.0.0>")),
+ try_fail_gbif('node/0', 0, xxxx),
+ try_fail_gbif('node/1', self(), xxx),
+ try_fail_gbif('node/1', yyy, xxx),
ok.
try_gbif(Id, X, Y) ->
@@ -415,9 +398,7 @@ try_gbif(Id, X, Y) ->
{Id, X, Y} ->
io:format("guard_bif(~p, ~p, ~p) -- ok", [Id, X, Y]);
Other ->
- ?line ok = io:format("guard_bif(~p, ~p, ~p) -- bad result: ~p\n",
- [Id, X, Y, Other]),
- ?line test_server:fail()
+ ct:fail("guard_bif(~p, ~p, ~p) -- bad result: ~p\n", [Id, X, Y, Other])
end.
try_fail_gbif(Id, X, Y) ->
@@ -425,9 +406,7 @@ try_fail_gbif(Id, X, Y) ->
{'EXIT',{function_clause,[{?MODULE,guard_bif,[Id,X,Y],_}|_]}} ->
io:format("guard_bif(~p, ~p, ~p) -- ok", [Id,X,Y]);
Other ->
- ?line ok = io:format("guard_bif(~p, ~p, ~p) -- bad result: ~p\n",
- [Id, X, Y, Other]),
- ?line test_server:fail()
+ ct:fail("guard_bif(~p, ~p, ~p) -- bad result: ~p\n", [Id, X, Y, Other])
end.
guard_bif('abs/1', X, Y) when abs(X) == Y ->
@@ -457,22 +436,20 @@ guard_bif('node/0', X, Y) when node() == Y ->
guard_bif('node/1', X, Y) when node(X) == Y ->
{'node/1', X, Y}.
-type_tests(doc) -> "Test the type tests.";
+%% Test the type tests.
type_tests(Config) when is_list(Config) ->
- ?line Types = all_types(),
- ?line Tests = type_test_desc(),
- ?line put(errors, 0),
- ?line put(violations, 0),
- ?line type_tests(Tests, Types),
- ?line case {get(errors), get(violations)} of
+ Types = all_types(),
+ Tests = type_test_desc(),
+ put(errors, 0),
+ put(violations, 0),
+ type_tests(Tests, Types),
+ case {get(errors), get(violations)} of
{0, 0} ->
ok;
{0, N} ->
{comment, integer_to_list(N) ++ " standard violation(s)"};
{Errors, Violations} ->
- io:format("~p sub test(s) failed, ~p violation(s)",
- [Errors, Violations]),
- ?line test_server:fail()
+ ct:fail("~p sub test(s) failed, ~p violation(s)", [Errors, Violations])
end.
type_tests([{Test, AllowedTypes}| T], AllTypes) ->
@@ -499,7 +476,7 @@ type_tests(Test, [Type|T], Allowed) ->
when is_list(Loc) ->
ok;
{'EXIT',Other} ->
- ?line test_server:fail({unexpected_error_reason,Other});
+ ct:fail({unexpected_error_reason,Other});
tuple when is_function(Value) ->
io:format("Standard violation: Test ~p(~p) should fail",
[Test, Value]),
diff --git a/erts/emulator/test/hash_SUITE.erl b/erts/emulator/test/hash_SUITE.erl
index 2ea49467b8..a39d101b0d 100644
--- a/erts/emulator/test/hash_SUITE.erl
+++ b/erts/emulator/test/hash_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -50,7 +50,7 @@
-define(config(A,B),config(A,B)).
-export([config/2]).
-else.
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-endif.
-ifdef(debug).
@@ -70,86 +70,43 @@ config(priv_dir,_) ->
".".
-else.
%% When run in test server.
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
+-export([all/0, suite/0,
test_basic/1,test_cmp/1,test_range/1,test_spread/1,
test_phash2/1,otp_5292/1,bit_level_binaries/1,otp_7127/1,
- test_hash_zero/1,
- end_per_testcase/2,init_per_testcase/2]).
-init_per_testcase(_Case, Config) ->
- Dog=test_server:timetrap(test_server:minutes(10)),
- [{watchdog, Dog}|Config].
-
-end_per_testcase(_Case, Config) ->
- Dog=?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
- ok.
-suite() -> [{ct_hooks,[ts_install_cth]}].
+ test_hash_zero/1]).
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 10}}].
all() ->
[test_basic, test_cmp, test_range, test_spread,
test_phash2, otp_5292, bit_level_binaries, otp_7127,
- test_hash_zero
- ].
-
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
+ test_hash_zero].
-test_basic(suite) ->
- [];
-test_basic(doc) ->
- ["Tests basic functionality of erlang:phash and that the "
- "hashes has not changed (neither hash nor phash)"];
+%% Tests basic functionality of erlang:phash and that the
+%% hashes has not changed (neither hash nor phash)
test_basic(Config) when is_list(Config) ->
basic_test().
-test_cmp(suite) ->
- [];
-test_cmp(doc) ->
- ["Compares integer hashes made by erlang:phash with those of a reference "
- "implementation"];
+%% Compares integer hashes made by erlang:phash with those of a reference implementation
test_cmp(Config) when is_list(Config) ->
cmp_test(10000).
-test_range(suite) ->
- [];
-test_range(doc) ->
- ["Tests ranges on erlang:phash from 1 to 2^32"];
+%% Tests ranges on erlang:phash from 1 to 2^32
test_range(Config) when is_list(Config) ->
range_test().
-test_spread(suite) ->
- [];
-test_spread(doc) ->
- ["Tests that the hashes are spread ok"];
+%% Tests that the hashes are spread ok
test_spread(Config) when is_list(Config) ->
spread_test(10).
-test_phash2(suite) ->
- [];
-test_phash2(doc) ->
- ["Tests phash2"];
+%% Tests phash2
test_phash2(Config) when is_list(Config) ->
phash2_test().
-otp_5292(suite) ->
- [];
-otp_5292(doc) ->
- ["Tests hash, phash and phash2 regarding integers."];
+%% Tests hash, phash and phash2 regarding integers.
otp_5292(Config) when is_list(Config) ->
otp_5292_test().
@@ -157,10 +114,7 @@ otp_5292(Config) when is_list(Config) ->
bit_level_binaries(Config) when is_list(Config) ->
bit_level_binaries_do().
-otp_7127(suite) ->
- [];
-otp_7127(doc) ->
- ["Tests phash2/1."];
+%% Tests phash2/1.
otp_7127(Config) when is_list(Config) ->
otp_7127_test().
@@ -223,11 +177,10 @@ basic_test() ->
range_test() ->
- random:seed(),
F = fun(From,From,_FF) ->
ok;
(From,To,FF) ->
- R = random:uniform(16#FFFFFFFFFFFFFFFF),
+ R = rand:uniform(16#FFFFFFFFFFFFFFFF),
X = erlang:phash(R, From),
Y = erlang:phash(R, 16#100000000) - 1,
Z = (Y rem From) + 1,
@@ -265,14 +218,13 @@ spread_test(N) ->
cmp_test(N) ->
- % No need to save seed, the error indicates what number caused it.
- random:seed(),
do_cmp_hashes(N,8).
+
do_cmp_hashes(0,_) ->
ok;
do_cmp_hashes(N,Steps) ->
- R0 = random:uniform(1 bsl Steps - 1) + random:uniform(16#FFFFFFFF),
- R = case random:uniform(2) of
+ R0 = rand:uniform(1 bsl Steps - 1) + rand:uniform(16#FFFFFFFF),
+ R = case rand:uniform(2) of
1 ->
R0;
_ ->
diff --git a/erts/emulator/test/hibernate_SUITE.erl b/erts/emulator/test/hibernate_SUITE.erl
index 4ac8c272db..6f8ce02266 100644
--- a/erts/emulator/test/hibernate_SUITE.erl
+++ b/erts/emulator/test/hibernate_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,47 +20,25 @@
-module(hibernate_SUITE).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2,end_per_testcase/2,
+-export([all/0, suite/0,
basic/1,dynamic_call/1,min_heap_size/1,bad_args/1,
- messages_in_queue/1,undefined_mfa/1,no_heap/1,wake_up_and_bif_trap/1]).
+ messages_in_queue/1,undefined_mfa/1,no_heap/1,
+ wake_up_and_bif_trap/1]).
%% Used by test cases.
--export([basic_hibernator/1,dynamic_call_hibernator/2,messages_in_queue_restart/2, no_heap_loop/0,characters_to_list_trap/1]).
+-export([basic_hibernator/1,dynamic_call_hibernator/2,messages_in_queue_restart/2,
+ no_heap_loop/0,characters_to_list_trap/1]).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 3}}].
all() ->
[basic, dynamic_call, min_heap_size, bad_args, messages_in_queue,
undefined_mfa, no_heap, wake_up_and_bif_trap].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- Dog = ?t:timetrap(?t:minutes(3)),
- [{watchdog,Dog}|Config].
-
-end_per_testcase(_Func, Config) ->
- Dog=?config(watchdog, Config),
- ?t:timetrap_cancel(Dog).
-
%%%
%%% Testing the basic functionality of erlang:hibernate/3.
%%%
@@ -69,9 +47,9 @@ basic(Config) when is_list(Config) ->
Ref = make_ref(),
Info = {self(),Ref},
ExpectedHeapSz = erts_debug:size([Info]),
- ?line Child = spawn_link(fun() -> basic_hibernator(Info) end),
- ?line hibernate_wake_up(100, ExpectedHeapSz, Child),
- ?line Child ! please_quit_now,
+ Child = spawn_link(fun() -> basic_hibernator(Info) end),
+ hibernate_wake_up(100, ExpectedHeapSz, Child),
+ Child ! please_quit_now,
ok.
hibernate_wake_up(0, _, _) -> ok;
@@ -85,35 +63,35 @@ hibernate_wake_up(N, ExpectedHeapSz, Child) ->
end;
1 -> ok
end,
- ?line Child ! {hibernate,self()},
- ?line wait_until(fun () ->
+ Child ! {hibernate,self()},
+ wait_until(fun () ->
{current_function,{erlang,hibernate,3}} ==
process_info(Child, current_function)
end),
- ?line {message_queue_len,0} = process_info(Child, message_queue_len),
- ?line {status,waiting} = process_info(Child, status),
- ?line {heap_size,ExpectedHeapSz} = process_info(Child, heap_size),
+ {message_queue_len,0} = process_info(Child, message_queue_len),
+ {status,waiting} = process_info(Child, status),
+ {heap_size,ExpectedHeapSz} = process_info(Child, heap_size),
io:format("Before hibernation: ~p After hibernation: ~p\n",
[Before,ExpectedHeapSz]),
- ?line Child ! {whats_up,self()},
- ?line receive
- {all_fine,X,Child,_Ref} ->
- if
- N =:= 1 -> io:format("~p\n", [X]);
- true -> ok
- end,
- {backtrace,Bin} = process_info(Child, backtrace),
- if
- size(Bin) > 1000 ->
- io:format("~s\n", [binary_to_list(Bin)]),
- ?line ?t:fail(stack_is_growing);
- true ->
- hibernate_wake_up(N-1, ExpectedHeapSz, Child)
- end;
- Other ->
- ?line io:format("~p\n", [Other]),
- ?line ?t:fail(unexpected_message)
- end.
+ Child ! {whats_up,self()},
+ receive
+ {all_fine,X,Child,_Ref} ->
+ if
+ N =:= 1 -> io:format("~p\n", [X]);
+ true -> ok
+ end,
+ {backtrace,Bin} = process_info(Child, backtrace),
+ if
+ size(Bin) > 1000 ->
+ io:format("~s\n", [binary_to_list(Bin)]),
+ ct:fail(stack_is_growing);
+ true ->
+ hibernate_wake_up(N-1, ExpectedHeapSz, Child)
+ end;
+ Other ->
+ io:format("~p\n", [Other]),
+ ct:fail(unexpected_message)
+ end.
basic_hibernator(Info) ->
{catchlevel,0} = process_info(self(), catchlevel),
@@ -165,9 +143,9 @@ dynamic_call(Config) when is_list(Config) ->
Ref = make_ref(),
Info = {self(),Ref},
ExpectedHeapSz = erts_debug:size([Info]),
- ?line Child = spawn_link(fun() -> ?MODULE:dynamic_call_hibernator(Info, hibernate) end),
- ?line hibernate_wake_up(100, ExpectedHeapSz, Child),
- ?line Child ! please_quit_now,
+ Child = spawn_link(fun() -> ?MODULE:dynamic_call_hibernator(Info, hibernate) end),
+ hibernate_wake_up(100, ExpectedHeapSz, Child),
+ Child ! please_quit_now,
ok.
dynamic_call_hibernator(Info, Function) ->
@@ -195,34 +173,32 @@ min_heap_size(Config) when is_list(Config) ->
end.
min_heap_size_1(Config) when is_list(Config) ->
- ?line erlang:trace(new, true, [call]),
+ erlang:trace(new, true, [call]),
MFA = {?MODULE,min_hibernator,1},
- ?line 1 = erlang:trace_pattern(MFA, true, [local]),
+ 1 = erlang:trace_pattern(MFA, true, [local]),
Ref = make_ref(),
Info = {self(),Ref},
- ?line Child = spawn_opt(fun() -> min_hibernator(Info) end,
+ Child = spawn_opt(fun() -> min_hibernator(Info) end,
[{min_heap_size,15000},link]),
receive
- {trace,Child,call,{?MODULE,min_hibernator,_}} ->
- ?line 1 = erlang:trace_pattern(MFA, false, [local]),
- ?line erlang:trace(new, false, [call])
+ {trace,Child,call,{?MODULE,min_hibernator,_}} ->
+ 1 = erlang:trace_pattern(MFA, false, [local]),
+ erlang:trace(new, false, [call])
end,
{heap_size,HeapSz} = process_info(Child, heap_size),
io:format("Heap size: ~p\n", [HeapSz]),
- ?line if
- HeapSz < 20 -> ok
- end,
- ?line Child ! wake_up,
+ if
+ HeapSz < 20 -> ok
+ end,
+ Child ! wake_up,
receive
{heap_size,AfterSize} ->
io:format("Heap size after wakeup: ~p\n", [AfterSize]),
- ?line
- if
- AfterSize >= 15000 -> ok
- end;
+ if
+ AfterSize >= 15000 -> ok
+ end;
Other ->
- io:format("Unexpected: ~p\n", [Other]),
- ?line ?t:fail()
+ ct:fail("Unexpected: ~p\n", [Other])
end.
min_hibernator({Parent,_Ref}) ->
@@ -239,23 +215,23 @@ min_hibernator_recv(Parent) ->
%%%
bad_args(Config) when is_list(Config) ->
- ?line bad_args(?MODULE, {name,glurf}, [0]),
- ?line {'EXIT',{system_limit,_}} =
+ bad_args(?MODULE, {name,glurf}, [0]),
+ {'EXIT',{system_limit,_}} =
(catch erlang:hibernate(x, y, lists:duplicate(5122, xxx))),
- ?line bad_args(42, name, [0]),
- ?line bad_args(xx, 42, [1]),
- ?line bad_args(xx, 42, glurf),
- ?line bad_args(xx, 42, {}),
- ?line bad_args({}, name, [2]),
- ?line bad_args({1}, name, [3]),
- ?line bad_args({1,2,3}, name, [4]),
- ?line bad_args({1,2,3}, name, [5]),
- ?line bad_args({1,2,3,4}, name, [6]),
- ?line bad_args({1,2,3,4,5,6}, name,[7]),
- ?line bad_args({1,2,3,4,5}, name, [8]),
- ?line bad_args({1,2}, name, [9]),
- ?line bad_args([1,2], name, [9]),
- ?line bad_args(55.0, name, [9]),
+ bad_args(42, name, [0]),
+ bad_args(xx, 42, [1]),
+ bad_args(xx, 42, glurf),
+ bad_args(xx, 42, {}),
+ bad_args({}, name, [2]),
+ bad_args({1}, name, [3]),
+ bad_args({1,2,3}, name, [4]),
+ bad_args({1,2,3}, name, [5]),
+ bad_args({1,2,3,4}, name, [6]),
+ bad_args({1,2,3,4,5,6}, name,[7]),
+ bad_args({1,2,3,4,5}, name, [8]),
+ bad_args({1,2}, name, [9]),
+ bad_args([1,2], name, [9]),
+ bad_args(55.0, name, [9]),
ok.
bad_args(Mod, Name, Args) ->
@@ -266,7 +242,7 @@ bad_args(Mod, Name, Args) ->
io:format("erlang:hibernate(~p, ~p, ~p) -> ~p\n", [Mod,Name,Args,Res]);
Other ->
io:format("erlang:hibernate(~p, ~p, ~p) -> ~p\n", [Mod,Name,Args,Res]),
- ?t:fail({bad_result,Other})
+ ct:fail({bad_result,Other})
end.
@@ -283,8 +259,8 @@ messages_in_queue(Config) when is_list(Config) ->
receive
done -> ok;
Other ->
- ?line io:format("~p\n", [Other]),
- ?line ?t:fail(unexpected_message)
+ io:format("~p\n", [Other]),
+ ct:fail(unexpected_message)
end.
messages_in_queue_1(Parent, ExpectedMsg) ->
@@ -296,13 +272,13 @@ messages_in_queue_1(Parent, ExpectedMsg) ->
[Parent,ExpectedMsg]).
messages_in_queue_restart(Parent, ExpectedMessage) ->
- ?line receive
- ExpectedMessage ->
- Parent ! done;
- Other ->
- io:format("~p\n", [Other]),
- ?t:fail(unexpected_message)
- end,
+ receive
+ ExpectedMessage ->
+ Parent ! done;
+ Other ->
+ io:format("~p\n", [Other]),
+ ct:fail(unexpected_message)
+ end,
ok.
@@ -312,36 +288,36 @@ messages_in_queue_restart(Parent, ExpectedMessage) ->
%%%
undefined_mfa(Config) when is_list(Config) ->
- ?line process_flag(trap_exit, true),
- ?line Pid = spawn_link(fun() ->
+ process_flag(trap_exit, true),
+ Pid = spawn_link(fun() ->
%% Will be a call_only instruction.
erlang:hibernate(?MODULE, blarf, []) end),
- ?line Pid ! {a,message},
- ?line receive
- {'EXIT',Pid,{undef,Undef}} ->
- io:format("~p\n", [Undef]),
- ok;
- Other ->
- ?line io:format("~p\n", [Other]),
- ?line ?t:fail(unexpected_message)
- end,
+ Pid ! {a,message},
+ receive
+ {'EXIT',Pid,{undef,Undef}} ->
+ io:format("~p\n", [Undef]),
+ ok;
+ Other ->
+ io:format("~p\n", [Other]),
+ ct:fail(unexpected_message)
+ end,
undefined_mfa_1().
undefined_mfa_1() ->
- ?line Pid = spawn_link(fun() ->
+ Pid = spawn_link(fun() ->
%% Force a call_last instruction by calling bar()
%% (if that is not obvious).
bar(),
erlang:hibernate(?MODULE, blarf, [])
end),
- ?line Pid ! {another,message},
- ?line receive
+ Pid ! {another,message},
+ receive
{'EXIT',Pid,{undef,Undef}} ->
io:format("~p\n", [Undef]),
ok;
Other ->
- ?line io:format("~p\n", [Other]),
- ?line ?t:fail(unexpected_message)
+ io:format("~p\n", [Other]),
+ ct:fail(unexpected_message)
end,
ok.
@@ -352,23 +328,17 @@ bar() ->
%% No heap
%%
-no_heap(doc) -> [];
-no_heap(suite) -> [];
no_heap(Config) when is_list(Config) ->
- ?line H = spawn_link(fun () -> clean_dict(), no_heap_loop() end),
- ?line lists:foreach(fun (_) ->
- wait_until(fun () -> is_hibernated(H) end),
- ?line [{heap_size,1},
- {total_heap_size,1}]
- = process_info(H,
- [heap_size,
- total_heap_size]),
- receive after 10 -> ok end,
- H ! again
- end,
- lists:seq(1, 100)),
- ?line unlink(H),
- ?line exit(H, bye).
+ H = spawn_link(fun () -> clean_dict(), no_heap_loop() end),
+ lists:foreach(fun (_) ->
+ wait_until(fun () -> is_hibernated(H) end),
+ [{heap_size,1}, {total_heap_size,1}]
+ = process_info(H, [heap_size, total_heap_size]),
+ receive after 10 -> ok end,
+ H ! again
+ end, lists:seq(1, 100)),
+ unlink(H),
+ exit(H, bye).
no_heap_loop() ->
flush(),
@@ -382,19 +352,17 @@ clean_dict() ->
%% Wake up and then immediatly bif trap with a lengthy computation.
%%
-wake_up_and_bif_trap(doc) -> [];
-wake_up_and_bif_trap(suite) -> [];
wake_up_and_bif_trap(Config) when is_list(Config) ->
- ?line Self = self(),
- ?line Pid = spawn_link(fun() -> erlang:hibernate(?MODULE, characters_to_list_trap, [Self]) end),
- ?line Pid ! wakeup,
- ?line receive
+ Self = self(),
+ Pid = spawn_link(fun() -> erlang:hibernate(?MODULE, characters_to_list_trap, [Self]) end),
+ Pid ! wakeup,
+ receive
{ok, Pid0} when Pid0 =:= Pid -> ok
after 5000 ->
- ?line ?t:fail(process_blocked)
+ ct:fail(process_blocked)
end,
- ?line unlink(Pid),
- ?line exit(Pid, bye).
+ unlink(Pid),
+ exit(Pid, bye).
%% Lengthy computation that traps (in characters_to_list_trap_3).
characters_to_list_trap(Parent) ->
diff --git a/erts/emulator/test/hipe_SUITE.erl b/erts/emulator/test/hipe_SUITE.erl
new file mode 100644
index 0000000000..a556b4ddc0
--- /dev/null
+++ b/erts/emulator/test/hipe_SUITE.erl
@@ -0,0 +1,67 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(hipe_SUITE).
+-export([all/0, t_copy_literals/1]).
+
+all() ->
+ case erlang:system_info(hipe_architecture) of
+ undefined -> {skip, "HiPE is disabled"};
+ _ -> [t_copy_literals]
+ end.
+
+t_copy_literals(doc) ->
+ "Check that BEAM literals referenced from HiPE stack are copied by"
+ " check_process_code";
+t_copy_literals(Config) when is_list(Config) ->
+ %% Compile the the ref_cell and literals modules.
+ Data = proplists:get_value(data_dir, Config),
+ Priv = proplists:get_value(priv_dir, Config),
+ RefFile = filename:join(Data, "ref_cell"),
+ {ok,ref_cell} = c:c(RefFile, [{outdir,Priv},native]),
+ true = code:is_module_native(ref_cell),
+ LitFile = filename:join(Data, "literals"),
+ {ok,literals} = c:c(LitFile, [{outdir,Priv}]),
+
+ %% store references to literals on HiPE stacks
+ PA = ref_cell:start_link(),
+ ref_cell:call(PA, {put_res_of, fun literals:a/0}),
+ PB = ref_cell:start_link_deep(),
+ ref_cell:call(PB, {put_res_of, fun literals:b/0}),
+
+ %% purge the literals
+ _ = (catch erlang:purge_module(literals)),
+ true = erlang:delete_module(literals),
+ true = erlang:purge_module(literals),
+
+ %% Give the literal collector some time to work...
+ receive after 2000 -> ok end,
+
+ %% check that the ex-literals are ok
+ [a,b,c] = ref_cell:call(PA, get),
+ {a,b,c} = ref_cell:call(PB, get),
+
+ %% cleanup
+ ref_cell:call(PA, done),
+ ref_cell:call(PB, done),
+ _ = (catch erlang:purge_module(ref_cell)),
+ true = erlang:delete_module(ref_cell),
+ true = erlang:purge_module(ref_cell),
+ ok.
diff --git a/erts/emulator/test/hipe_SUITE_data/literals.erl b/erts/emulator/test/hipe_SUITE_data/literals.erl
new file mode 100644
index 0000000000..31e443970f
--- /dev/null
+++ b/erts/emulator/test/hipe_SUITE_data/literals.erl
@@ -0,0 +1,26 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(literals).
+
+-export([a/0, b/0]).
+
+a() -> [a,b,c].
+b() -> {a,b,c}.
diff --git a/erts/emulator/test/hipe_SUITE_data/ref_cell.erl b/erts/emulator/test/hipe_SUITE_data/ref_cell.erl
new file mode 100644
index 0000000000..2654e4077b
--- /dev/null
+++ b/erts/emulator/test/hipe_SUITE_data/ref_cell.erl
@@ -0,0 +1,64 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ref_cell).
+
+-export([start_link/0, start_link_deep/0, call/2]).
+
+-compile(native).
+
+-define(DEPTH, 100).
+-define(ALLOCS, 500).
+
+start_link() ->
+ spawn_link(fun() -> loop(undefined) end).
+
+start_link_deep() ->
+ spawn_link(fun() -> go_deep(?DEPTH) end).
+
+%% Create a stack large enough to get a graylimit trap placed next time there's
+%% a minor gc.
+go_deep(0) ->
+ alloc_some(?ALLOCS),
+ loop(undefined),
+ 0;
+go_deep(Depth) ->
+ go_deep(Depth-1)+1.
+
+%% Do some allocation to trigger a minor gc
+alloc_some(Amount) ->
+ Check = (Amount * (Amount + 1)) div 2,
+ Check = lists:sum(lists:seq(1, Amount)).
+
+call(Pid, Call) ->
+ Pid ! {Call, self()},
+ receive {Pid, Res} -> Res end.
+
+loop(Thing) ->
+ receive
+ {done, Pid} -> Pid ! {self(), done};
+ {{put_res_of, Fun}, Pid} ->
+ NewThing = Fun(),
+ Pid ! {self(), put},
+ loop(NewThing);
+ {get, Pid} ->
+ Pid ! {self(), Thing},
+ loop(Thing)
+ end.
diff --git a/erts/emulator/test/ignore_cores.erl b/erts/emulator/test/ignore_cores.erl
index 13f34cd10f..25dce346b9 100644
--- a/erts/emulator/test/ignore_cores.erl
+++ b/erts/emulator/test/ignore_cores.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -28,7 +28,7 @@
-module(ignore_cores).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-export([init/1, fini/1, setup/3, setup/4, restore/1, dir/1]).
@@ -53,7 +53,7 @@ init(Config) ->
fini(Config) ->
#ignore_cores{org_cwd = OrgCWD,
org_path = OrgPath,
- org_pwd_env = OrgPWD} = ?config(ignore_cores, Config),
+ org_pwd_env = OrgPWD} = proplists:get_value(ignore_cores, Config),
ok = file:set_cwd(OrgCWD),
true = code:set_path(OrgPath),
case OrgPWD of
@@ -70,10 +70,10 @@ setup(Suite, Testcase, Config, SetCwd) when is_atom(Suite),
is_list(Config) ->
#ignore_cores{org_cwd = OrgCWD,
org_path = OrgPath,
- org_pwd_env = OrgPWD} = ?config(ignore_cores, Config),
+ org_pwd_env = OrgPWD} = proplists:get_value(ignore_cores, Config),
Path = lists:map(fun (".") -> OrgCWD; (Dir) -> Dir end, OrgPath),
true = code:set_path(Path),
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
IgnDir = filename:join([PrivDir,
atom_to_list(Suite)
++ "_"
@@ -94,7 +94,7 @@ setup(Suite, Testcase, Config, SetCwd) when is_atom(Suite),
end,
ok = file:write_file(filename:join([IgnDir, "ignore_core_files"]), <<>>),
%% cores are dumped in /cores on MacOS X
- CoresDir = case {?t:os_type(), filelib:is_dir("/cores")} of
+ CoresDir = case {os:type(), filelib:is_dir("/cores")} of
{{unix,darwin}, true} ->
filelib:fold_files("/cores",
"^core.*$",
@@ -119,7 +119,7 @@ restore(Config) ->
org_path = OrgPath,
org_pwd_env = OrgPWD,
ign_dir = IgnDir,
- cores_dir = CoresDir} = ?config(ignore_cores, Config),
+ cores_dir = CoresDir} = proplists:get_value(ignore_cores, Config),
try
case CoresDir of
false ->
@@ -155,5 +155,5 @@ restore(Config) ->
dir(Config) ->
- #ignore_cores{ign_dir = Dir} = ?config(ignore_cores, Config),
+ #ignore_cores{ign_dir = Dir} = proplists:get_value(ignore_cores, Config),
Dir.
diff --git a/erts/emulator/test/list_bif_SUITE.erl b/erts/emulator/test/list_bif_SUITE.erl
index 9e930822cf..514dd2f412 100644
--- a/erts/emulator/test/list_bif_SUITE.erl
+++ b/erts/emulator/test/list_bif_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -19,149 +19,108 @@
%%
-module(list_bif_SUITE).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2,end_per_testcase/2]).
+-export([all/0, suite/0]).
-export([hd_test/1,tl_test/1,t_length/1,t_list_to_pid/1,
t_list_to_float/1,t_list_to_integer/1]).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 1}}].
+
all() ->
[hd_test, tl_test, t_length, t_list_to_pid,
t_list_to_float, t_list_to_integer].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-init_per_testcase(_Case, Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(60)),
- [{watchdog,Dog}|Config].
-
-end_per_testcase(_Case, Config) ->
- Dog = ?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
- ok.
-
-t_list_to_integer(suite) ->
- [];
-t_list_to_integer(doc) ->
- ["tests list_to_integer and string:to_integer"];
+%% Tests list_to_integer and string:to_integer
t_list_to_integer(Config) when is_list(Config) ->
- ?line {'EXIT',{badarg,_}} = (catch list_to_integer("12373281903728109372810937209817320981321ABC")),
- ?line 12373281903728109372810937209817320981321 = (catch list_to_integer("12373281903728109372810937209817320981321")),
- ?line 12373 = (catch list_to_integer("12373")),
- ?line -12373 = (catch list_to_integer("-12373")),
- ?line 12373 = (catch list_to_integer("+12373")),
- ?line {'EXIT',{badarg,_}} = ( catch list_to_integer(abc)),
- ?line {'EXIT',{badarg,_}} = (catch list_to_integer("")),
- ?line {12373281903728109372810937209817320981321,"ABC"} = string:to_integer("12373281903728109372810937209817320981321ABC"),
- ?line {-12373281903728109372810937209817320981321,"ABC"} = string:to_integer("-12373281903728109372810937209817320981321ABC"),
- ?line {12,[345]} = string:to_integer([$1,$2,345]),
- ?line {12,[a]} = string:to_integer([$1,$2,a]),
- ?line {error,no_integer} = string:to_integer([$A]),
- ?line {error,not_a_list} = string:to_integer($A),
+ {'EXIT',{badarg,_}} = (catch list_to_integer("12373281903728109372810937209817320981321ABC")),
+ 12373281903728109372810937209817320981321 = (catch list_to_integer("12373281903728109372810937209817320981321")),
+ 12373 = (catch list_to_integer("12373")),
+ -12373 = (catch list_to_integer("-12373")),
+ 12373 = (catch list_to_integer("+12373")),
+ {'EXIT',{badarg,_}} = ( catch list_to_integer(abc)),
+ {'EXIT',{badarg,_}} = (catch list_to_integer("")),
+ {12373281903728109372810937209817320981321,"ABC"} = string:to_integer("12373281903728109372810937209817320981321ABC"),
+ {-12373281903728109372810937209817320981321,"ABC"} = string:to_integer("-12373281903728109372810937209817320981321ABC"),
+ {12,[345]} = string:to_integer([$1,$2,345]),
+ {12,[a]} = string:to_integer([$1,$2,a]),
+ {error,no_integer} = string:to_integer([$A]),
+ {error,not_a_list} = string:to_integer($A),
ok.
%% Test hd/1 with correct and incorrect arguments.
hd_test(Config) when is_list(Config) ->
- ?line $h = hd(id("hejsan")),
- ?line case catch hd(id($h)) of
- {'EXIT', {badarg, _}} -> ok;
- Res ->
- Str=io_lib:format("hd/1 with incorrect args "++
- "succeeded.~nResult: ~p", [Res]),
- test_server:fail(Str)
- end,
+ $h = hd(id("hejsan")),
+ case catch hd(id($h)) of
+ {'EXIT', {badarg, _}} -> ok;
+ Res ->
+ ct:fail("hd/1 with incorrect args succeeded.~nResult: ~p", [Res])
+ end,
ok.
%% Test tl/1 with correct and incorrect arguments.
tl_test(Config) when is_list(Config) ->
- ?line "ejsan" = tl(id("hejsan")),
- ?line case catch tl(id(104)) of
- {'EXIT', {badarg, _}} ->
- ok;
- Res ->
- Str=io_lib:format("tl/1 with incorrect args "++
- "succeeded.~nResult: ~p", [Res]),
- test_server:fail(Str)
- end,
+ "ejsan" = tl(id("hejsan")),
+ case catch tl(id(104)) of
+ {'EXIT', {badarg, _}} ->
+ ok;
+ Res ->
+ ct:fail("tl/1 with incorrect args succeeded.~nResult: ~p", [Res])
+ end,
ok.
%% Test length/1 with correct and incorrect arguments.
t_length(Config) when is_list(Config) ->
- ?line 0 = length(""),
- ?line 0 = length([]),
- ?line 1 = length([1]),
- ?line 2 = length([1,a]),
- ?line 2 = length("ab"),
- ?line 3 = length("abc"),
- ?line 4 = length(id([x|"abc"])),
- ?line 6 = length("hejsan"),
- ?line {'EXIT',{badarg,_}} = (catch length(id([a,b|c]))),
- ?line case catch length({tuple}) of
- {'EXIT', {badarg, _}} ->
- ok;
- Res ->
- Str = io_lib:format("length/1 with incorrect args "++
- "succeeded.~nResult: ~p", [Res]),
- ?line test_server:fail(Str)
- end,
+ 0 = length(""),
+ 0 = length([]),
+ 1 = length([1]),
+ 2 = length([1,a]),
+ 2 = length("ab"),
+ 3 = length("abc"),
+ 4 = length(id([x|"abc"])),
+ 6 = length("hejsan"),
+ {'EXIT',{badarg,_}} = (catch length(id([a,b|c]))),
+ case catch length({tuple}) of
+ {'EXIT', {badarg, _}} ->
+ ok;
+ Res ->
+ ct:fail("length/1 with incorrect args succeeded.~nResult: ~p", [Res])
+ end,
ok.
%% Test list_to_pid/1 with correct and incorrect arguments.
t_list_to_pid(Config) when is_list(Config) ->
- ?line Me = self(),
- ?line MyListedPid = pid_to_list(Me),
- ?line Me = list_to_pid(MyListedPid),
- ?line case catch list_to_pid(id("Incorrect list")) of
- {'EXIT', {badarg, _}} ->
- ok;
- Res ->
- Str=io_lib:format("list_to_pid/1 with incorrect "++
- "arg succeeded.~nResult: ~p",
- [Res]),
- test_server:fail(Str)
- end,
+ Me = self(),
+ MyListedPid = pid_to_list(Me),
+ Me = list_to_pid(MyListedPid),
+ case catch list_to_pid(id("Incorrect list")) of
+ {'EXIT', {badarg, _}} ->
+ ok;
+ Res ->
+ ct:fail("list_to_pid/1 with incorrect arg succeeded.~nResult: ~p", [Res])
+ end,
ok.
%% Test list_to_float/1 with correct and incorrect arguments.
t_list_to_float(Config) when is_list(Config) ->
- ?line 5.89000 = list_to_float(id("5.89")),
- ?line 5.89898 = list_to_float(id("5.89898")),
- ?line case catch list_to_float(id("58")) of
- {'EXIT', {badarg, _}} -> ok;
- Res ->
- Str=io_lib:format("list_to_float with incorrect "++
- "arg succeeded.~nResult: ~p",
- [Res]),
- test_server:fail(Str)
- end,
+ 5.89000 = list_to_float(id("5.89")),
+ 5.89898 = list_to_float(id("5.89898")),
+ case catch list_to_float(id("58")) of
+ {'EXIT', {badarg, _}} -> ok;
+ Res ->
+ ct:fail("list_to_float with incorrect arg succeeded.~nResult: ~p", [Res])
+ end,
ok.
id(I) -> I.
-
-
diff --git a/erts/emulator/test/long_timers_test.erl b/erts/emulator/test/long_timers_test.erl
index 9415e1cced..c9a380a229 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-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -27,11 +27,16 @@
%%% Created : 21 Aug 2006 by Rickard Green <[email protected]>
%%%-------------------------------------------------------------------
+-define(HIGH_CPU_INFO, "Ignored due to high CPU utilization.").
+-define(MISSING_CPU_INFO, "Ignored due to missing CPU utilization information.").
-define(MAX_TIMEOUT, 60). % Minutes
--define(MAX_LATE_MS, 15*1000). % Milliseconds
+-define(MAX_LATE_MS, 1000). % Milliseconds
-define(REG_NAME, '___LONG___TIMERS___TEST___SERVER___').
+-define(HIGH_UTIL, 96.0).
+-define(UTIL_INTERVAL, 10000).
+
-define(DRV_NAME, timer_driver).
% First byte in communication with the timer driver
@@ -72,52 +77,149 @@ check_result() ->
receive
{'DOWN', Mon, process, _, Reason} ->
{?REG_NAME, 'DOWN', Reason};
- {result, ?REG_NAME, TORs, Start, End} ->
+ {result, ?REG_NAME, TORs, Start, End, UtilData} ->
erlang:demonitor(Mon),
receive {'DOWN', Mon, _, _, _} -> ok after 0 -> ok end,
stop_node(Node),
- check(TORs, ms((End - Start) - max_late()), ok)
+ Res = check(TORs, Start, End, UtilData, ms((End - Start) - max_late()), ok),
+ io:format("Start = ~p~n End = ~p~n UtilData = ~p~n", [Start, End, UtilData]),
+ Res
end.
+res(New, Old) when New == failed; Old == failed ->
+ failed;
+res(New, Old) when New == missing_cpu_info; Old == missing_cpu_info ->
+ missing_cpu_info;
+res(New, Old) when New == high_cpu; Old == high_cpu ->
+ high_cpu;
+res(New, _Old) ->
+ New.
+
check([#timeout_rec{timeout = Timeout,
type = Type,
timeout_diff = undefined} | TORs],
+ Start,
+ End,
+ UtilData,
NeedRes,
- _Ok) when Timeout < NeedRes ->
- io:format("~p timeout = ~p ms failed! No timeout.~n",
- [Type, Timeout]),
- check(TORs, NeedRes, failed);
+ Ok) when Timeout < NeedRes ->
+ {NewOk, HCPU} = case had_high_cpu_util(Start,
+ Timeout,
+ End - Timeout*1000,
+ UtilData) of
+ yes -> {res(high_cpu, Ok), ?HIGH_CPU_INFO};
+ no -> {res(failed, Ok), ""};
+ missing -> {res(missing_cpu_info, Ok), "FAILED", ?MISSING_CPU_INFO}
+ end,
+ io:format("~p timeout = ~p ms FAILED! No timeout. ~s~n",
+ [Type, Timeout, HCPU]),
+ check(TORs, Start, End, UtilData, NeedRes, NewOk);
check([#timeout_rec{timeout_diff = undefined} | TORs],
+ Start,
+ End,
+ UtilData,
NeedRes,
Ok) ->
- check(TORs, NeedRes, Ok);
+ check(TORs, Start, End, UtilData, NeedRes, Ok);
check([#timeout_rec{timeout = Timeout,
type = Type,
timeout_diff = {error, Reason}} | TORs],
+ Start,
+ End,
+ UtilData,
NeedRes,
_Ok) ->
- io:format("~p timeout = ~p ms failed! exit reason ~p~n",
+ io:format("~p timeout = ~p ms FAILED! exit reason ~p~n",
[Type, Timeout, Reason]),
- check(TORs, NeedRes, failed);
+ check(TORs, Start, End, UtilData, NeedRes, failed);
check([#timeout_rec{timeout = Timeout,
type = Type,
timeout_diff = TimeoutDiff} | TORs],
+ Start,
+ End,
+ UtilData,
NeedRes,
Ok) ->
- {NewOk, SuccessStr} = case ((0 =< TimeoutDiff)
- andalso (TimeoutDiff =< max_late())) of
- true -> {Ok, "succeeded"};
- false -> {failed, "FAILED"}
+ {NewOk, SuccessStr, HCPU} = case {(0 =< TimeoutDiff),
+ (TimeoutDiff =< max_late())} of
+ {true, true} ->
+ {res(ok, Ok), "succeeded", ""};
+ {false, _} ->
+ {res(failed, Ok), "FAILED", ""};
+ _ ->
+ case had_high_cpu_util(Start,
+ Timeout,
+ TimeoutDiff,
+ UtilData) of
+ yes -> {res(high_cpu, Ok), "FAILED", ?HIGH_CPU_INFO};
+ no -> {res(failed, Ok), "FAILED", ""};
+ missing -> {res(missing_cpu_info, Ok), "FAILED", ?MISSING_CPU_INFO}
+ end
end,
- io:format("~s timeout = ~s ms ~s! timeout diff = ~s.~n",
+ io:format("~s timeout = ~s ms ~s! timeout diff = ~s. ~s~n",
[type_str(Type),
time_str(Timeout),
SuccessStr,
- time_str(TimeoutDiff, erlang:convert_time_unit(1, seconds, native))]),
- check(TORs, NeedRes, NewOk);
-check([], _NeedRes, Ok) ->
+ time_str(TimeoutDiff, 1000000),
+ HCPU]),
+ check(TORs, Start, End, UtilData, NeedRes, NewOk);
+check([],_Start,_End,_UtilData,_NeedRes, Ok) ->
Ok.
+% TargetTimeout in ms, other in us.
+had_high_cpu_util(StartTime,
+ TargetTimeout,
+ TimeoutDiff,
+ UtilData) ->
+ TargetTo = StartTime + TargetTimeout*1000,
+ ActTo = TargetTo + TimeoutDiff,
+ hcpu(ActTo, TargetTo, UtilData).
+
+hcpu(_ActTo, _TargetTo, [{UT, 0} | _] = UD) ->
+ missing; %% Util is the integer zero when not supported...
+%% UT2 =:= UT1
+hcpu(ActTo, TargetTo, [{UT, _}, {UT, _} | _] = UD) ->
+ hcpu(ActTo, TargetTo, tl(UD));
+%% UT2 > UT1 > ActTo > TargetTo
+hcpu(ActTo, TargetTo, [{_UT2, _}, {UT1, _} | _] = UD) when UT1 > ActTo ->
+ hcpu(ActTo, TargetTo, tl(UD));
+%% UT2 >= ActTo > TargetTo >= UT1
+hcpu(ActTo, TargetTo,
+ [{UT2, U}, {UT1, _} | _]) when UT2 >= ActTo,
+ TargetTo >= UT1 ->
+ case U >= (((ActTo - TargetTo) / (UT2 - UT1))
+ * (?HIGH_UTIL/100.0)) of
+ true -> yes;
+ false -> no
+ end;
+%% UT2 >= ActTo >= UT1 > TargetTo
+hcpu(ActTo, TargetTo,
+ [{UT2, U}, {UT1, _} | _] = UD) when UT2 >= ActTo,
+ ActTo >= UT1,
+ UT1 > TargetTo ->
+ case U >= (((ActTo - UT1) / (UT2 - UT1))
+ * (?HIGH_UTIL/100.0)) of
+ true -> hcpu(ActTo, TargetTo, tl(UD));
+ false -> no
+ end;
+%% ActTo > UT2 >= TargetTo >= UT1
+hcpu(ActTo, TargetTo,
+ [{UT2, U}, {UT1, _} | _]) when ActTo > UT2,
+ TargetTo >= UT1 ->
+ case U >= (((UT2 - TargetTo) / (UT2 - UT1))
+ * (?HIGH_UTIL/100.0)) of
+ true -> yes;
+ false -> no
+ end;
+%% ActTo > UT2 > UT1 > TargetTo
+hcpu(ActTo, TargetTo,
+ [{UT2, U}, {UT1, _} | _] = UD) when ActTo > UT2,
+ UT1 > TargetTo ->
+ case U >= ?HIGH_UTIL of
+ true -> hcpu(ActTo, TargetTo, tl(UD));
+ false -> no
+ end.
+
type_str(receive_after) -> "receive ... after";
type_str(bif_timer) -> "BIF timer";
type_str(driver) -> "driver".
@@ -142,24 +244,24 @@ unit_str(Res) -> Res.
to_diff(Timeout, Start, Stop) ->
%% 'Timeout' in milli seconds
- %% 'Start', 'Stop', and result in native unit
- (Stop - Start) - erlang:convert_time_unit(Timeout, milli_seconds, native).
+ %% 'Start', 'Stop', and result in micro seconds
+ (Stop - Start) - Timeout*1000.
ms(Time) ->
- erlang:convert_time_unit(Time, native, milli_seconds).
+ erlang:convert_time_unit(Time, microsecond, millisecond).
max_late() ->
- erlang:convert_time_unit(?MAX_LATE_MS, milli_seconds, native).
+ erlang:convert_time_unit(?MAX_LATE_MS, millisecond, microsecond).
receive_after(Timeout) ->
- Start = erlang:monotonic_time(),
+ Start = erlang:monotonic_time(microsecond),
receive
{get_result, ?REG_NAME} ->
?REG_NAME ! #timeout_rec{pid = self(),
type = receive_after,
timeout = Timeout}
after Timeout ->
- Stop = erlang:monotonic_time(),
+ Stop = erlang:monotonic_time(microsecond),
receive
{get_result, ?REG_NAME} ->
?REG_NAME ! #timeout_rec{pid = self(),
@@ -174,7 +276,7 @@ receive_after(Timeout) ->
driver(Timeout) ->
Port = open_port({spawn, ?DRV_NAME},[]),
link(Port),
- Start = erlang:monotonic_time(),
+ Start = erlang:monotonic_time(microsecond),
erlang:port_command(Port, <<?START_TIMER, Timeout:32>>),
receive
{get_result, ?REG_NAME} ->
@@ -182,7 +284,7 @@ driver(Timeout) ->
type = driver,
timeout = Timeout};
{Port,{data,[?TIMER]}} ->
- Stop = erlang:monotonic_time(),
+ Stop = erlang:monotonic_time(microsecond),
unlink(Port),
true = erlang:port_close(Port),
receive
@@ -197,7 +299,7 @@ driver(Timeout) ->
end.
bif_timer(Timeout) ->
- Start = erlang:monotonic_time(),
+ Start = erlang:monotonic_time(microsecond),
Tmr = erlang:start_timer(Timeout, self(), ok),
receive
{get_result, ?REG_NAME} ->
@@ -205,7 +307,7 @@ bif_timer(Timeout) ->
type = bif_timer,
timeout = Timeout};
{timeout, Tmr, ok} ->
- Stop = erlang:monotonic_time(),
+ Stop = erlang:monotonic_time(microsecond),
receive
{get_result, ?REG_NAME} ->
?REG_NAME ! #timeout_rec{pid = self(),
@@ -218,13 +320,22 @@ bif_timer(Timeout) ->
end.
test(Starter, DrvDir, StartDone) ->
+ process_flag(priority, high),
erl_ddll:start(),
ok = load_driver(DrvDir, ?DRV_NAME),
process_flag(trap_exit, true),
register(?REG_NAME, self()),
{group_leader, GL} = process_info(whereis(net_kernel),group_leader),
group_leader(GL, self()),
- Start = erlang:monotonic_time(),
+ try
+ application:start(sasl),
+ application:start(os_mon)
+ catch
+ _ : _ ->
+ ok
+ end,
+ UtilData = new_util(),
+ Start = erlang:monotonic_time(microsecond),
TORs = lists:map(fun (Min) ->
TO = Min*60*1000,
[#timeout_rec{pid = spawn_opt(
@@ -252,16 +363,27 @@ test(Starter, DrvDir, StartDone) ->
lists:seq(1, ?MAX_TIMEOUT)),
FlatTORs = lists:flatten(TORs),
Starter ! StartDone,
- test_loop(FlatTORs, Start).
+ test_loop(FlatTORs, Start, UtilData).
+
+new_util() ->
+ new_util([]).
+
+new_util(UtilData) ->
+ Util = cpu_sup:util(),
+ Time = erlang:monotonic_time(microsecond),
+ [{Time, Util} | UtilData].
-test_loop(TORs, Start) ->
+test_loop(TORs, Start, UtilData) ->
receive
{get_result, ?REG_NAME, Pid} ->
- End = erlang:monotonic_time(),
- Pid ! {result, ?REG_NAME, get_test_results(TORs), Start, End},
+ End = erlang:monotonic_time(microsecond),
+ EndUtilData = new_util(UtilData),
+ Pid ! {result, ?REG_NAME, get_test_results(TORs), Start, End, EndUtilData},
erl_ddll:unload_driver(?DRV_NAME),
erl_ddll:stop(),
exit(bye)
+ after ?UTIL_INTERVAL ->
+ test_loop(TORs, Start, new_util(UtilData))
end.
get_test_results(TORs) ->
diff --git a/erts/emulator/test/lttng_SUITE.erl b/erts/emulator/test/lttng_SUITE.erl
new file mode 100644
index 0000000000..c12f63706a
--- /dev/null
+++ b/erts/emulator/test/lttng_SUITE.erl
@@ -0,0 +1,502 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(lttng_SUITE).
+
+-export([all/0, suite/0]).
+-export([init_per_suite/1, end_per_suite/1]).
+-export([init_per_testcase/2, end_per_testcase/2]).
+
+-export([t_lttng_list/1,
+ t_carrier_pool/1,
+ t_memory_carrier/1,
+ t_async_io_pool/1,
+ t_driver_control_ready_async/1,
+ t_driver_start_stop/1,
+ t_driver_ready_input_output/1,
+ t_driver_timeout/1,
+ t_driver_caller/1,
+ t_driver_flush/1,
+ t_scheduler_poll/1]).
+
+-include_lib("common_test/include/ct.hrl").
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {seconds, 10}}].
+
+all() ->
+ [t_lttng_list,
+ t_memory_carrier,
+ t_carrier_pool,
+ t_async_io_pool,
+ t_driver_start_stop,
+ t_driver_ready_input_output,
+ t_driver_control_ready_async,
+ t_driver_timeout,
+ t_driver_caller,
+ t_driver_flush,
+ t_scheduler_poll].
+
+
+init_per_suite(Config) ->
+ case erlang:system_info(dynamic_trace) of
+ lttng ->
+ ensure_lttng_stopped("--all"),
+ Config;
+ _ ->
+ {skip, "No LTTng configured on system."}
+ end.
+
+end_per_suite(_Config) ->
+ ensure_lttng_stopped("--all"),
+ ok.
+
+init_per_testcase(Case, Config) ->
+ Name = atom_to_list(Case),
+ ok = ensure_lttng_started(Name, Config),
+ [{session, Name}|Config].
+
+end_per_testcase(Case, _Config) ->
+ Name = atom_to_list(Case),
+ ok = ensure_lttng_stopped(Name),
+ ok.
+
+%% Not tested yet
+%% org_erlang_otp:driver_process_exit
+%% org_erlang_otp:driver_event
+
+%% tracepoints
+%%
+%% org_erlang_otp:carrier_pool_get
+%% org_erlang_otp:carrier_pool_put
+%% org_erlang_otp:carrier_destroy
+%% org_erlang_otp:carrier_create
+%% org_erlang_otp:aio_pool_put
+%% org_erlang_otp:aio_pool_get
+%% org_erlang_otp:driver_control
+%% org_erlang_otp:driver_call
+%% org_erlang_otp:driver_finish
+%% org_erlang_otp:driver_ready_async
+%% org_erlang_otp:driver_process_exit
+%% org_erlang_otp:driver_stop
+%% org_erlang_otp:driver_flush
+%% org_erlang_otp:driver_stop_select
+%% org_erlang_otp:driver_timeout
+%% org_erlang_otp:driver_event
+%% org_erlang_otp:driver_ready_output
+%% org_erlang_otp:driver_ready_input
+%% org_erlang_otp:driver_output
+%% org_erlang_otp:driver_outputv
+%% org_erlang_otp:driver_init
+%% org_erlang_otp:driver_start
+%% org_erlang_otp:scheduler_poll
+
+%%
+%% Testcases
+%%
+
+t_lttng_list(_Config) ->
+ {ok, _} = cmd("lttng list -u"),
+ ok.
+
+%% org_erlang_otp:carrier_pool_get
+%% org_erlang_otp:carrier_pool_put
+t_carrier_pool(Config) ->
+ case have_carriers(ets_alloc) of
+ false ->
+ {skip, "No Memory Carriers configured on system."};
+ true ->
+ ok = lttng_start_event("org_erlang_otp:carrier_pool*", Config),
+
+ ok = ets_load(),
+
+ Res = lttng_stop_and_view(Config),
+ ok = check_tracepoint("org_erlang_otp:carrier_pool_get", Res),
+ ok = check_tracepoint("org_erlang_otp:carrier_pool_put", Res),
+ ok
+ end.
+
+%% org_erlang_otp:carrier_destroy
+%% org_erlang_otp:carrier_create
+t_memory_carrier(Config) ->
+ case have_carriers(ets_alloc) of
+ false ->
+ {skip, "No Memory Carriers configured on system."};
+ true ->
+ ok = lttng_start_event("org_erlang_otp:carrier_*", Config),
+
+ ok = ets_load(),
+
+ Res = lttng_stop_and_view(Config),
+ ok = check_tracepoint("org_erlang_otp:carrier_destroy", Res),
+ ok = check_tracepoint("org_erlang_otp:carrier_create", Res),
+ ok
+ end.
+
+%% org_erlang_otp:aio_pool_put
+%% org_erlang_otp:aio_pool_get
+t_async_io_pool(Config) ->
+ case have_async_threads() of
+ false ->
+ {skip, "No Async Threads configured on system."};
+ true ->
+ ok = lttng_start_event("org_erlang_otp:aio_pool_*", Config),
+
+ Path1 = proplists:get_value(priv_dir, Config),
+ {ok, [[Path2]]} = init:get_argument(home),
+ {ok, _} = file:list_dir(Path1),
+ {ok, _} = file:list_dir(Path2),
+ {ok, _} = file:list_dir(Path1),
+ {ok, _} = file:list_dir(Path2),
+
+ Res = lttng_stop_and_view(Config),
+ ok = check_tracepoint("org_erlang_otp:aio_pool_put", Res),
+ ok = check_tracepoint("org_erlang_otp:aio_pool_get", Res),
+ ok
+ end.
+
+
+%% org_erlang_otp:driver_start
+%% org_erlang_otp:driver_stop
+t_driver_start_stop(Config) ->
+ ok = lttng_start_event("org_erlang_otp:driver_*", Config),
+ timer:sleep(500),
+ Path = proplists:get_value(priv_dir, Config),
+ Name = filename:join(Path, "sometext.txt"),
+ Bin = txt(),
+ ok = file:write_file(Name, Bin),
+ {ok, Bin} = file:read_file(Name),
+ timer:sleep(500),
+ Res = lttng_stop_and_view(Config),
+ ok = check_tracepoint("org_erlang_otp:driver_start", Res),
+ ok = check_tracepoint("org_erlang_otp:driver_stop", Res),
+ ok = check_tracepoint("org_erlang_otp:driver_control", Res),
+ ok = check_tracepoint("org_erlang_otp:driver_outputv", Res),
+ ok = check_tracepoint("org_erlang_otp:driver_ready_async", Res),
+ ok.
+
+%% org_erlang_otp:driver_control
+%% org_erlang_otp:driver_outputv
+%% org_erlang_otp:driver_ready_async
+t_driver_control_ready_async(Config) ->
+ ok = lttng_start_event("org_erlang_otp:driver_control", Config),
+ ok = lttng_start_event("org_erlang_otp:driver_outputv", Config),
+ ok = lttng_start_event("org_erlang_otp:driver_ready_async", Config),
+ Path = proplists:get_value(priv_dir, Config),
+ Name = filename:join(Path, "sometext.txt"),
+ Bin = txt(),
+ ok = file:write_file(Name, Bin),
+ {ok, Bin} = file:read_file(Name),
+ Res = lttng_stop_and_view(Config),
+ ok = check_tracepoint("org_erlang_otp:driver_control", Res),
+ ok = check_tracepoint("org_erlang_otp:driver_outputv", Res),
+ ok = check_tracepoint("org_erlang_otp:driver_ready_async", Res),
+ ok.
+
+%% org_erlang_otp:driver_ready_input
+%% org_erlang_otp:driver_ready_output
+t_driver_ready_input_output(Config) ->
+ ok = lttng_start_event("org_erlang_otp:driver_ready_*", Config),
+ timer:sleep(500),
+ Me = self(),
+ Pid = spawn_link(fun() -> tcp_server(Me, active) end),
+ receive {Pid, accept} -> ok end,
+ Bin = txt(),
+ Sz = byte_size(Bin),
+
+ {ok, Sock} = gen_tcp:connect("localhost", 5679, [binary, {packet, 2}]),
+ ok = gen_tcp:send(Sock, <<Sz:16, Bin/binary>>),
+ ok = gen_tcp:send(Sock, <<Sz:16, Bin/binary>>),
+ ok = gen_tcp:close(Sock),
+ receive {Pid, done} -> ok end,
+
+ timer:sleep(500),
+ Res = lttng_stop_and_view(Config),
+ ok = check_tracepoint("org_erlang_otp:driver_ready_input", Res),
+ ok = check_tracepoint("org_erlang_otp:driver_ready_output", Res),
+ ok.
+
+
+%% org_erlang_otp:driver_stop_select
+%% org_erlang_otp:driver_timeout
+t_driver_timeout(Config) ->
+ ok = lttng_start_event("org_erlang_otp:driver_*", Config),
+ Me = self(),
+ Pid = spawn_link(fun() -> tcp_server(Me, timeout) end),
+ receive {Pid, accept} -> ok end,
+ {ok, Sock} = gen_tcp:connect("localhost", 5679, [binary]),
+ ok = gen_tcp:send(Sock, <<"hej">>),
+ receive {Pid, done} -> ok end,
+ ok = gen_tcp:close(Sock),
+ Res = lttng_stop_and_view(Config),
+ ok = check_tracepoint("org_erlang_otp:driver_timeout", Res),
+ ok = check_tracepoint("org_erlang_otp:driver_stop_select", Res),
+ ok.
+
+%% org_erlang_otp:driver_call
+%% org_erlang_otp:driver_output
+%% org_erlang_otp:driver_init
+%% org_erlang_otp:driver_finish
+t_driver_caller(Config) ->
+ ok = lttng_start_event("org_erlang_otp:driver_*", Config),
+
+ Drv = 'caller_drv',
+ os:putenv("CALLER_DRV_USE_OUTPUTV", "false"),
+
+ ok = load_driver(proplists:get_value(data_dir, Config), Drv),
+ Port = open_port({spawn, Drv}, []),
+ true = is_port(Port),
+
+ chk_caller(Port, start, self()),
+ chk_caller(Port, output, spawn_link(fun() ->
+ port_command(Port, "")
+ end)),
+ Port ! {self(), {command, ""}},
+ chk_caller(Port, output, self()),
+ chk_caller(Port, control, spawn_link(fun () ->
+ port_control(Port, 0, "")
+ end)),
+ chk_caller(Port, call, spawn_link(fun() ->
+ erlang:port_call(Port, 0, "")
+ end)),
+
+ true = port_close(Port),
+ erl_ddll:unload_driver(Drv),
+
+ Res = lttng_stop_and_view(Config),
+ ok = check_tracepoint("org_erlang_otp:driver_call", Res),
+ ok = check_tracepoint("org_erlang_otp:driver_output", Res),
+ ok = check_tracepoint("org_erlang_otp:driver_init", Res),
+ ok = check_tracepoint("org_erlang_otp:driver_finish", Res),
+ ok.
+
+%% org_erlang_otp:scheduler_poll
+t_scheduler_poll(Config) ->
+ ok = lttng_start_event("org_erlang_otp:scheduler_poll", Config),
+
+ ok = memory_load(),
+
+ Res = lttng_stop_and_view(Config),
+ ok = check_tracepoint("org_erlang_otp:scheduler_poll", Res),
+ ok.
+
+%% org_erlang_otp:driver_flush
+t_driver_flush(Config) ->
+ ok = lttng_start_event("org_erlang_otp:driver_flush", Config),
+
+ Me = self(),
+ Pid = spawn_link(fun() -> tcp_server(Me, passive_no_read) end),
+ receive {Pid, accept} -> ok end,
+ Bin = iolist_to_binary([txt() || _ <- lists:seq(1,100)]),
+ Sz = byte_size(Bin),
+
+ %% We want to create a scenario where sendings stalls and we
+ %% queue packets in the driver.
+ %% When we close the socket it has to flush the queue.
+ {ok, Sock} = gen_tcp:connect("localhost", 5679, [binary, {packet, 2},
+ {send_timeout, 10},
+ {sndbuf, 10000000}]),
+ Pids = [spawn_link(fun() ->
+ gen_tcp:send(Sock, <<Sz:16, Bin/binary>>),
+ Me ! {self(), ok}
+ end) || _ <- lists:seq(1,100)],
+ [receive {P, ok} -> ok end || P <- Pids],
+ ok = gen_tcp:close(Sock),
+ Pid ! die,
+ receive {Pid, done} -> ok end,
+
+ Res = lttng_stop_and_view(Config),
+ ok = check_tracepoint("org_erlang_otp:driver_flush", Res),
+ ok.
+
+%%
+%% AUX
+%%
+
+chk_caller(Port, Callback, ExpectedCaller) ->
+ receive
+ {caller, Port, Callback, Caller} ->
+ ExpectedCaller = Caller
+ end.
+
+
+ets_load() ->
+ Tid = ets:new(ets_load, [public,set]),
+ N = erlang:system_info(schedulers_online),
+ Pids = [spawn_link(fun() -> ets_shuffle(Tid) end) || _ <- lists:seq(1,N)],
+ ok = ets_kill(Pids, 500),
+ ok.
+
+
+ets_kill([], _) -> ok;
+ets_kill([Pid|Pids], Time) ->
+ timer:sleep(Time),
+ Pid ! done,
+ ets_kill(Pids, Time).
+
+ets_shuffle(Tid) ->
+ Payload = lists:duplicate(100, $x),
+ ets_shuffle(Tid, 100, Payload).
+ets_shuffle(Tid, I, Data) ->
+ ets_shuffle(Tid, I, I, Data, Data).
+
+ets_shuffle(Tid, 0, N, _, Data) ->
+ ets_shuffle(Tid, N, N, Data, Data);
+ets_shuffle(Tid, I, N, Data, Data0) ->
+ receive
+ done -> ok
+ after 0 ->
+ Key = rand:uniform(1000),
+ Data1 = [I|Data],
+ ets:insert(Tid, {Key, Data1}),
+ ets_shuffle(Tid, I - 1, N, Data1, Data0)
+ end.
+
+
+
+
+memory_load() ->
+ Me = self(),
+ Pids0 = [spawn_link(fun() -> memory_loop(Me, 20, <<42>>) end) || _ <- lists:seq(1,30)],
+ timer:sleep(50),
+ Pids1 = [spawn_link(fun() -> memory_loop(Me, 20, <<42>>) end) || _ <- lists:seq(1,30)],
+ [receive {Pid, done} -> ok end || Pid <- Pids0 ++ Pids1],
+ timer:sleep(500),
+ ok.
+
+memory_loop(Parent, N, Bin) ->
+ memory_loop(Parent, N, Bin, []).
+
+memory_loop(Parent, 0, _Bin, _) ->
+ Parent ! {self(), done};
+memory_loop(Parent, N, Bin0, Ls) ->
+ Bin = binary:copy(<<Bin0/binary, Bin0/binary>>),
+ memory_loop(Parent, N - 1, Bin, [a,b,c|Ls]).
+
+tcp_server(Pid, Type) ->
+ {ok, LSock} = gen_tcp:listen(5679, [binary,
+ {reuseaddr, true},
+ {active, false}]),
+ Pid ! {self(), accept},
+ {ok, Sock} = gen_tcp:accept(LSock),
+ case Type of
+ passive_no_read ->
+ receive die -> ok end;
+ active ->
+ inet:setopts(Sock, [{active, once}, {packet,2}]),
+ receive Msg1 -> io:format("msg1: ~p~n", [Msg1]) end,
+ inet:setopts(Sock, [{active, once}, {packet,2}]),
+ receive Msg2 -> io:format("msg2: ~p~n", [Msg2]) end,
+ ok = gen_tcp:close(Sock);
+ timeout ->
+ Res = gen_tcp:recv(Sock, 2000, 1000),
+ io:format("res ~p~n", [Res])
+ end,
+ Pid ! {self(), done},
+ ok.
+
+txt() ->
+ <<"%% tracepoints\n"
+ "%%\n"
+ "%% org_erlang_otp:carrier_pool_get\n"
+ "%% org_erlang_otp:carrier_pool_put\n"
+ "%% org_erlang_otp:carrier_destroy\n"
+ "%% org_erlang_otp:carrier_create\n"
+ "%% org_erlang_otp:aio_pool_put\n"
+ "%% org_erlang_otp:aio_pool_get\n"
+ "%% org_erlang_otp:driver_control\n"
+ "%% org_erlang_otp:driver_call\n"
+ "%% org_erlang_otp:driver_finish\n"
+ "%% org_erlang_otp:driver_ready_async\n"
+ "%% org_erlang_otp:driver_process_exit\n"
+ "%% org_erlang_otp:driver_stop\n"
+ "%% org_erlang_otp:driver_flush\n"
+ "%% org_erlang_otp:driver_stop_select\n"
+ "%% org_erlang_otp:driver_timeout\n"
+ "%% org_erlang_otp:driver_event\n"
+ "%% org_erlang_otp:driver_ready_output\n"
+ "%% org_erlang_otp:driver_ready_input\n"
+ "%% org_erlang_otp:driver_output\n"
+ "%% org_erlang_otp:driver_outputv\n"
+ "%% org_erlang_otp:driver_init\n"
+ "%% org_erlang_otp:driver_start\n"
+ "%% org_erlang_otp:scheduler_poll">>.
+
+load_driver(Dir, Driver) ->
+ case erl_ddll:load_driver(Dir, Driver) of
+ ok -> ok;
+ {error, Error} = Res ->
+ io:format("~s\n", [erl_ddll:format_error(Error)]),
+ Res
+ end.
+
+%% check
+
+have_carriers(Alloc) ->
+ case erlang:system_info({allocator,Alloc}) of
+ false -> false;
+ _ -> true
+ end.
+
+have_async_threads() ->
+ Tps = erlang:system_info(thread_pool_size),
+ if Tps =:= 0 -> false;
+ true -> true
+ end.
+
+%% lttng
+lttng_stop_and_view(Config) ->
+ Path = proplists:get_value(priv_dir, Config),
+ Name = proplists:get_value(session, Config),
+ {ok,_} = cmd("lttng stop " ++ Name),
+ {ok,Res} = cmd("lttng view " ++ Name ++ " --trace-path=" ++ Path),
+ Res.
+
+check_tracepoint(TP, Data) ->
+ case re:run(Data, TP, [global]) of
+ {match, _} -> ok;
+ _ -> notfound
+ end.
+
+lttng_start_event(Event, Config) ->
+ Name = proplists:get_value(session, Config),
+ {ok, _} = cmd("lttng enable-event -u " ++ Event ++ " --session=" ++ Name),
+ {ok, _} = cmd("lttng start " ++ Name),
+ ok.
+
+ensure_lttng_started(Name, Config) ->
+ Out = case proplists:get_value(priv_dir, Config) of
+ undefined -> [];
+ Path -> "--output="++Path++" "
+ end,
+ {ok,_} = cmd("lttng create " ++ Out ++ Name),
+ ok.
+
+ensure_lttng_stopped(Name) ->
+ {ok,_} = cmd("lttng stop"),
+ {ok,_} = cmd("lttng destroy " ++ Name),
+ ok.
+
+cmd(Cmd) ->
+ io:format("<< ~ts~n", [Cmd]),
+ Res = os:cmd(Cmd),
+ io:format(">> ~ts~n", [Res]),
+ {ok,Res}.
diff --git a/erts/emulator/test/lttng_SUITE_data/Makefile.src b/erts/emulator/test/lttng_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..fe7a1b6ef3
--- /dev/null
+++ b/erts/emulator/test/lttng_SUITE_data/Makefile.src
@@ -0,0 +1,7 @@
+
+MISC_DRVS = caller_drv@dll@
+
+
+all: $(MISC_DRVS)
+
+@SHLIB_RULES@
diff --git a/erts/emulator/test/lttng_SUITE_data/caller_drv.c b/erts/emulator/test/lttng_SUITE_data/caller_drv.c
new file mode 100644
index 0000000000..86fd0a2995
--- /dev/null
+++ b/erts/emulator/test/lttng_SUITE_data/caller_drv.c
@@ -0,0 +1,159 @@
+/* ``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.
+ *
+ * The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+ * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+ * AB. All Rights Reserved.''
+ *
+ * $Id$
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "erl_driver.h"
+
+static int init();
+static void stop(ErlDrvData drv_data);
+static void finish();
+static void flush(ErlDrvData drv_data);
+static ErlDrvData start(ErlDrvPort port, char *command);
+static void output(ErlDrvData drv_data, char *buf, ErlDrvSizeT len);
+static void outputv(ErlDrvData drv_data, ErlIOVec *ev);
+static ErlDrvSSizeT control(ErlDrvData drv_data,
+ unsigned int command, char *buf,
+ ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen);
+static ErlDrvSSizeT call(ErlDrvData drv_data,
+ unsigned int command,
+ char *buf, ErlDrvSizeT len,
+ char **rbuf, ErlDrvSizeT rlen,
+ unsigned int *flags);
+
+static ErlDrvEntry caller_drv_entry = {
+ init,
+ start,
+ stop,
+ output,
+ NULL /* ready_input */,
+ NULL /* ready_output */,
+ "caller_drv",
+ finish,
+ NULL /* handle */,
+ control,
+ NULL /* timeout */,
+ outputv,
+ NULL /* ready_async */,
+ flush,
+ call,
+ NULL /* event */,
+ ERL_DRV_EXTENDED_MARKER,
+ ERL_DRV_EXTENDED_MAJOR_VERSION,
+ ERL_DRV_EXTENDED_MINOR_VERSION,
+ ERL_DRV_FLAG_USE_PORT_LOCKING,
+ NULL /* handle2 */,
+ NULL /* handle_monitor */
+};
+
+DRIVER_INIT(caller_drv)
+{
+ char buf[10];
+ size_t bufsz = sizeof(buf);
+ char *use_outputv;
+ use_outputv = (erl_drv_getenv("CALLER_DRV_USE_OUTPUTV", buf, &bufsz) == 0
+ ? buf
+ : "false");
+ if (strcmp(use_outputv, "true") != 0)
+ caller_drv_entry.outputv = NULL;
+ return &caller_drv_entry;
+}
+
+void
+send_caller(ErlDrvData drv_data, char *func)
+{
+ int res;
+ ErlDrvPort port = (ErlDrvPort) drv_data;
+ ErlDrvTermData msg[] = {
+ ERL_DRV_ATOM, driver_mk_atom("caller"),
+ ERL_DRV_PORT, driver_mk_port(port),
+ ERL_DRV_ATOM, driver_mk_atom(func),
+ ERL_DRV_PID, driver_caller(port),
+ ERL_DRV_TUPLE, (ErlDrvTermData) 4
+ };
+ res = erl_drv_output_term(driver_mk_port(port), msg, sizeof(msg)/sizeof(ErlDrvTermData));
+ if (res <= 0)
+ driver_failure_atom(port, "erl_drv_output_term failed");
+}
+
+static int
+init() {
+ return 0;
+}
+
+static void
+stop(ErlDrvData drv_data)
+{
+
+}
+
+static void
+flush(ErlDrvData drv_data)
+{
+
+}
+
+static void
+finish()
+{
+
+}
+
+static ErlDrvData
+start(ErlDrvPort port, char *command)
+{
+ send_caller((ErlDrvData) port, "start");
+ return (ErlDrvData) port;
+}
+
+static void
+output(ErlDrvData drv_data, char *buf, ErlDrvSizeT len)
+{
+ send_caller(drv_data, "output");
+}
+
+static void
+outputv(ErlDrvData drv_data, ErlIOVec *ev)
+{
+ send_caller(drv_data, "outputv");
+}
+
+static ErlDrvSSizeT
+control(ErlDrvData drv_data,
+ unsigned int command, char *buf,
+ ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen)
+{
+ send_caller(drv_data, "control");
+ return 0;
+}
+
+static ErlDrvSSizeT
+call(ErlDrvData drv_data,
+ unsigned int command,
+ char *buf, ErlDrvSizeT len,
+ char **rbuf, ErlDrvSizeT rlen,
+ unsigned int *flags)
+{
+ /* echo call */
+ if (len > rlen)
+ *rbuf = driver_alloc(len);
+ memcpy((void *) *rbuf, (void *) buf, len);
+ send_caller(drv_data, "call");
+ return len;
+}
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index a256cf4195..5af676c409 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -17,73 +17,72 @@
%% %CopyrightEnd%
%%
-module(map_SUITE).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2
- ]).
-
--export([
- t_build_and_match_literals/1, t_build_and_match_literals_large/1,
- t_update_literals/1, t_update_literals_large/1,
- t_match_and_update_literals/1, t_match_and_update_literals_large/1,
- t_update_map_expressions/1,
- t_update_assoc/1, t_update_assoc_large/1,
- t_update_exact/1, t_update_exact_large/1,
- t_guard_bifs/1,
- t_guard_sequence/1, t_guard_sequence_large/1,
- t_guard_update/1, t_guard_update_large/1,
- t_guard_receive/1, t_guard_receive_large/1,
- t_guard_fun/1,
- t_update_deep/1,
- t_list_comprehension/1,
- t_map_sort_literals/1,
- t_map_equal/1,
- t_map_compare/1,
- t_map_size/1,
- t_is_map/1,
-
- %% Specific Map BIFs
- t_bif_map_get/1,
- t_bif_map_find/1,
- t_bif_map_is_key/1,
- t_bif_map_keys/1,
- t_bif_map_merge/1,
- t_bif_map_new/1,
- t_bif_map_put/1,
- t_bif_map_remove/1,
- t_bif_map_update/1,
- t_bif_map_values/1,
- t_bif_map_to_list/1,
- t_bif_map_from_list/1,
-
- %% erlang
- t_erlang_hash/1,
- t_map_encode_decode/1,
- t_gc_rare_map_overflow/1,
-
- %% non specific BIF related
- t_bif_build_and_check/1,
- t_bif_merge_and_check/1,
-
- %% maps module not bifs
- t_maps_fold/1,
- t_maps_map/1,
- t_maps_size/1,
- t_maps_without/1,
-
- %% misc
- t_hashmap_balance/1,
- t_erts_internal_order/1,
- t_erts_internal_hash/1,
- t_pdict/1,
- t_ets/1,
- t_dets/1,
- t_tracing/1,
-
- %% instruction-level tests
- t_has_map_fields/1,
- y_regs/1,
- badmap_17/1
- ]).
+-export([all/0, suite/0]).
+-compile({nowarn_deprecated_function, {erlang,hash,2}}).
+
+-export([t_build_and_match_literals/1, t_build_and_match_literals_large/1,
+ t_update_literals/1, t_update_literals_large/1,
+ t_match_and_update_literals/1, t_match_and_update_literals_large/1,
+ t_update_map_expressions/1,
+ t_update_assoc/1, t_update_assoc_large/1,
+ t_update_exact/1, t_update_exact_large/1,
+ t_guard_bifs/1,
+ t_guard_sequence/1, t_guard_sequence_large/1,
+ t_guard_update/1, t_guard_update_large/1,
+ t_guard_receive/1, t_guard_receive_large/1,
+ t_guard_fun/1,
+ t_update_deep/1,
+ t_list_comprehension/1,
+ t_map_sort_literals/1,
+ t_map_equal/1,
+ t_map_compare/1,
+ t_map_size/1,
+ t_is_map/1,
+
+ %% Specific Map BIFs
+ t_bif_map_get/1,
+ t_bif_map_find/1,
+ t_bif_map_is_key/1,
+ t_bif_map_keys/1,
+ t_bif_map_merge/1,
+ t_bif_map_new/1,
+ t_bif_map_put/1,
+ t_bif_map_remove/1,
+ t_bif_map_take/1, t_bif_map_take_large/1,
+ t_bif_map_update/1,
+ t_bif_map_values/1,
+ t_bif_map_to_list/1,
+ t_bif_map_from_list/1,
+
+ %% erlang
+ t_erlang_hash/1,
+ t_map_encode_decode/1,
+ t_gc_rare_map_overflow/1,
+
+ %% non specific BIF related
+ t_bif_build_and_check/1,
+ t_bif_merge_and_check/1,
+
+ %% maps module not bifs
+ t_maps_fold/1,
+ t_maps_map/1,
+ t_maps_size/1,
+ t_maps_without/1,
+
+ %% misc
+ t_hashmap_balance/1,
+ t_erts_internal_order/1,
+ t_erts_internal_hash/1,
+ t_pdict/1,
+ t_ets/1,
+ t_dets/1,
+ t_tracing/1,
+ t_hash_entropy/1,
+
+ %% instruction-level tests
+ t_has_map_fields/1,
+ y_regs/1,
+ badmap_17/1]).
-include_lib("stdlib/include/ms_transform.hrl").
@@ -96,65 +95,58 @@
suite() -> [].
-all() -> [
- t_build_and_match_literals, t_build_and_match_literals_large,
- t_update_literals, t_update_literals_large,
- t_match_and_update_literals, t_match_and_update_literals_large,
- t_update_map_expressions,
- t_update_assoc, t_update_assoc_large,
- t_update_exact, t_update_exact_large,
- t_guard_bifs,
- t_guard_sequence, t_guard_sequence_large,
- t_guard_update, t_guard_update_large,
- t_guard_receive, t_guard_receive_large,
- t_guard_fun, t_list_comprehension,
- t_update_deep,
- t_map_equal, t_map_compare,
- t_map_sort_literals,
-
- %% Specific Map BIFs
- t_bif_map_get,t_bif_map_find,t_bif_map_is_key,
- t_bif_map_keys, t_bif_map_merge, t_bif_map_new,
- t_bif_map_put,
- t_bif_map_remove, t_bif_map_update,
- t_bif_map_values,
- t_bif_map_to_list, t_bif_map_from_list,
-
- %% erlang
- t_erlang_hash, t_map_encode_decode,
- t_gc_rare_map_overflow,
- t_map_size, t_is_map,
-
- %% non specific BIF related
- t_bif_build_and_check,
- t_bif_merge_and_check,
-
- %% maps module
- t_maps_fold, t_maps_map,
- t_maps_size, t_maps_without,
-
-
- %% Other functions
- t_hashmap_balance,
- t_erts_internal_order,
- t_erts_internal_hash,
- t_pdict,
- t_ets,
- t_tracing,
-
- %% instruction-level tests
- t_has_map_fields,
- y_regs,
- badmap_17
- ].
-
-groups() -> [].
-
-init_per_suite(Config) -> Config.
-end_per_suite(_Config) -> ok.
-
-init_per_group(_GroupName, Config) -> Config.
-end_per_group(_GroupName, Config) -> Config.
+all() -> [t_build_and_match_literals, t_build_and_match_literals_large,
+ t_update_literals, t_update_literals_large,
+ t_match_and_update_literals, t_match_and_update_literals_large,
+ t_update_map_expressions,
+ t_update_assoc, t_update_assoc_large,
+ t_update_exact, t_update_exact_large,
+ t_guard_bifs,
+ t_guard_sequence, t_guard_sequence_large,
+ t_guard_update, t_guard_update_large,
+ t_guard_receive, t_guard_receive_large,
+ t_guard_fun, t_list_comprehension,
+ t_update_deep,
+ t_map_equal, t_map_compare,
+ t_map_sort_literals,
+
+ %% Specific Map BIFs
+ t_bif_map_get,t_bif_map_find,t_bif_map_is_key,
+ t_bif_map_keys, t_bif_map_merge, t_bif_map_new,
+ t_bif_map_put,
+ t_bif_map_remove,
+ t_bif_map_take, t_bif_map_take_large,
+ t_bif_map_update,
+ t_bif_map_values,
+ t_bif_map_to_list, t_bif_map_from_list,
+
+ %% erlang
+ t_erlang_hash, t_map_encode_decode,
+ t_gc_rare_map_overflow,
+ t_map_size, t_is_map,
+
+ %% non specific BIF related
+ t_bif_build_and_check,
+ t_bif_merge_and_check,
+
+ %% maps module
+ t_maps_fold, t_maps_map,
+ t_maps_size, t_maps_without,
+
+
+ %% Other functions
+ t_hashmap_balance,
+ t_erts_internal_order,
+ t_erts_internal_hash,
+ t_pdict,
+ t_ets,
+ t_tracing,
+ t_hash_entropy,
+
+ %% instruction-level tests
+ t_has_map_fields,
+ y_regs,
+ badmap_17].
%% tests
@@ -1511,11 +1503,8 @@ t_map_equal(Config) when is_list(Config) ->
t_map_compare(Config) when is_list(Config) ->
- Seed = {erlang:monotonic_time(),
- erlang:time_offset(),
- erlang:unique_integer()},
- io:format("seed = ~p\n", [Seed]),
- random:seed(Seed),
+ rand:seed(exsplus),
+ io:format("seed = ~p\n", [rand:export_seed()]),
repeat(100, fun(_) -> float_int_compare() end, []),
repeat(100, fun(_) -> recursive_compare() end, []),
ok.
@@ -1533,7 +1522,7 @@ float_int_compare() ->
numeric_keys(N) ->
lists:foldl(fun(_,Acc) ->
- Int = random:uniform(N*4) - N*2,
+ Int = rand:uniform(N*4) - N*2,
Float = float(Int),
[Int, Float, Float * 0.99, Float * 1.01 | Acc]
end,
@@ -1564,7 +1553,7 @@ do_compare([Gen1, Gen2]) ->
%% Change one key from int to float (or vice versa) and check compare
ML1 = maps:to_list(M1),
- {K1,V1} = lists:nth(random:uniform(length(ML1)), ML1),
+ {K1,V1} = lists:nth(rand:uniform(length(ML1)), ML1),
case K1 of
I when is_integer(I) ->
case maps:find(float(I),M1) of
@@ -1655,9 +1644,9 @@ cmp_others(T1, T2, _) ->
map_gen(Pairs, Size) ->
{_,L} = lists:foldl(fun(_, {Keys, Acc}) ->
- KI = random:uniform(size(Keys)),
+ KI = rand:uniform(size(Keys)),
K = element(KI,Keys),
- KV = element(random:uniform(size(K)), K),
+ KV = element(rand:uniform(size(K)), K),
{erlang:delete_element(KI,Keys), [KV | Acc]}
end,
{Pairs, []},
@@ -1697,20 +1686,19 @@ term_gen_recursive(Leafs, Flags, Depth) ->
MaxDepth = 10,
Rnd = case {Flags, Depth} of
{_, MaxDepth} -> % Only leafs
- random:uniform(size(Leafs)) + 3;
+ rand:uniform(size(Leafs)) + 3;
{0, 0} -> % Only containers
- random:uniform(3);
+ rand:uniform(3);
{0,_} -> % Anything
- random:uniform(size(Leafs)+3)
+ rand:uniform(size(Leafs)+3)
end,
case Rnd of
1 -> % Make map
- Size = random:uniform(size(Leafs)),
+ Size = rand:uniform(size(Leafs)),
lists:foldl(fun(_, {Acc1,Acc2}) ->
{K1,K2} = term_gen_recursive(Leafs, Flags,
Depth+1),
{V1,V2} = term_gen_recursive(Leafs, Flags, Depth+1),
- %%ok = check_keys(K1,K2, 0),
{maps:put(K1,V1, Acc1), maps:put(K2,V2, Acc2)}
end,
{maps:new(), maps:new()},
@@ -1720,7 +1708,7 @@ term_gen_recursive(Leafs, Flags, Depth) ->
{Cdr1,Cdr2} = term_gen_recursive(Leafs, Flags, Depth+1),
{[Car1 | Cdr1], [Car2 | Cdr2]};
3 -> % Make tuple
- Size = random:uniform(size(Leafs)),
+ Size = rand:uniform(size(Leafs)),
L = lists:map(fun(_) -> term_gen_recursive(Leafs, Flags, Depth+1) end,
lists:seq(1,Size)),
{L1, L2} = lists:unzip(L),
@@ -1729,7 +1717,7 @@ term_gen_recursive(Leafs, Flags, Depth) ->
N -> % Make leaf
case element(N-3, Leafs) of
I when is_integer(I) ->
- case random:uniform(4) of
+ case rand:uniform(4) of
1 -> {I, float(I)};
2 -> {float(I), I};
_ -> {I,I}
@@ -1738,26 +1726,6 @@ term_gen_recursive(Leafs, Flags, Depth) ->
end
end.
-check_keys(K1, K2, _) when K1 =:= K2 ->
- case erlang:phash3(K1) =:= erlang:phash3(K2) of
- true -> ok;
- false ->
- io:format("Same keys with different hash values !!!\nK1 = ~p\nK2 = ~p\n", [K1,K2]),
- error
- end;
-check_keys(K1, K2, 0) ->
- case {erlang:phash3(K1), erlang:phash3(K2)} of
- {H,H} -> check_keys(K1, K2, 1);
- {_,_} -> ok
- end;
-check_keys(K1, K2, L) when L < 10 ->
- case {erlang:phash3([L|K1]), erlang:phash3([L|K2])} of
- {H,H} -> check_keys(K1, K2, L+1);
- {_,_} -> ok
- end;
-check_keys(K1, K2, L) ->
- io:format("Same hash value at level ~p !!!\nK1 = ~p\nK2 = ~p\n", [L,K1,K2]),
- error.
%% BIFs
t_bif_map_get(Config) when is_list(Config) ->
@@ -2007,7 +1975,7 @@ t_bif_map_remove(Config) when is_list(Config) ->
0 = erlang:map_size(maps:remove(some_key, #{})),
M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
- 4 => number, 18446744073709551629 => wat},
+ 4 => number, 18446744073709551629 => wat},
M1 = maps:remove("hi", M0),
true = is_members([4,18446744073709551629,int,<<"key">>],maps:keys(M1)),
@@ -2036,10 +2004,71 @@ t_bif_map_remove(Config) when is_list(Config) ->
%% error case
do_badmap(fun(T) ->
- {'EXIT',{{badmap,T},[{maps,remove,_,_}|_]}} =
- (catch maps:remove(a, T))
+ {'EXIT',{{badmap,T},[{maps,remove,_,_}|_]}} = (catch maps:remove(a, T))
end),
- ok.
+ ok.
+
+t_bif_map_take(Config) when is_list(Config) ->
+ error = maps:take(some_key, #{}),
+
+ M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat},
+
+ 5 = maps:size(M0),
+ {"hello", M1} = maps:take("hi", M0),
+ true = is_members([4,18446744073709551629,int,<<"key">>],maps:keys(M1)),
+ true = is_members([number,wat,3,<<"value">>],maps:values(M1)),
+ error = maps:take("hi", M1),
+ 4 = maps:size(M1),
+
+ {3, M2} = maps:take(int, M1),
+ true = is_members([4,18446744073709551629,<<"key">>],maps:keys(M2)),
+ true = is_members([number,wat,<<"value">>],maps:values(M2)),
+ error = maps:take(int, M2),
+ 3 = maps:size(M2),
+
+ {<<"value">>,M3} = maps:take(<<"key">>, M2),
+ true = is_members([4,18446744073709551629],maps:keys(M3)),
+ true = is_members([number,wat],maps:values(M3)),
+ error = maps:take(<<"key">>, M3),
+ 2 = maps:size(M3),
+
+ {wat,M4} = maps:take(18446744073709551629, M3),
+ true = is_members([4],maps:keys(M4)),
+ true = is_members([number],maps:values(M4)),
+ error = maps:take(18446744073709551629, M4),
+ 1 = maps:size(M4),
+
+ {number,M5} = maps:take(4, M4),
+ [] = maps:keys(M5),
+ [] = maps:values(M5),
+ error = maps:take(4, M5),
+ 0 = maps:size(M5),
+
+ {wat,#{ "hi" := "hello", int := 3, 4 := number, <<"key">> := <<"value">>}} = maps:take(18446744073709551629,M0),
+
+ %% error case
+ do_badmap(fun(T) ->
+ {'EXIT',{{badmap,T},[{maps,take,_,_}|_]}} = (catch maps:take(a, T))
+ end),
+ ok.
+
+t_bif_map_take_large(Config) when is_list(Config) ->
+ KVs = [{{erlang:md5(<<I:64>>),I}, I}|| I <- lists:seq(1,500)],
+ M0 = maps:from_list(KVs),
+ ok = bif_map_take_all(KVs, M0),
+ ok.
+
+bif_map_take_all([], M0) ->
+ 0 = maps:size(M0),
+ ok;
+bif_map_take_all([{K,V}|KVs],M0) ->
+ {ok,V} = maps:find(K,M0),
+ {V,M1} = maps:take(K,M0),
+ error = maps:find(K,M1),
+ error = maps:take(K,M1),
+ bif_map_take_all(KVs,M1).
+
t_bif_map_update(Config) when is_list(Config) ->
M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
@@ -2598,7 +2627,7 @@ hashmap_balance(KeyFun) ->
F = fun(I, {M0,Max0}) ->
Key = KeyFun(I),
M1 = M0#{Key => Key},
- Max1 = case erts_internal:map_type(M1) of
+ Max1 = case erts_internal:term_type(M1) of
hashmap ->
Nodes = hashmap_nodes(M1),
Avg = maps:size(M1) * 0.4,
@@ -2993,10 +3022,43 @@ do_badmap_17(Config) ->
id(I) -> I.
+%% OTP-13763
+t_hash_entropy(Config) when is_list(Config) ->
+ %% entropy bug in 18.3, 19.0
+ M1 = maps:from_list([{#{"id" => I}, ok}||I <- lists:seq(1,50000)]),
+
+ #{ #{"id" => 100} := ok,
+ #{"id" => 200} := ok,
+ #{"id" => 300} := ok,
+ #{"id" => 400} := ok,
+ #{"id" => 500} := ok,
+ #{"id" => 600} := ok,
+ #{"id" => 700} := ok,
+ #{"id" => 800} := ok,
+ #{"id" => 900} := ok,
+ #{"id" => 25061} := ok,
+ #{"id" => 39766} := ok } = M1,
+
+ M0 = maps:from_list([{I,ok}||I <- lists:seq(1,33)]),
+ M2 = maps:from_list([{M0#{"id" => I}, ok}||I <- lists:seq(1,50000)]),
+
+ ok = maps:get(M0#{"id" => 100}, M2),
+ ok = maps:get(M0#{"id" => 200}, M2),
+ ok = maps:get(M0#{"id" => 300}, M2),
+ ok = maps:get(M0#{"id" => 400}, M2),
+ ok = maps:get(M0#{"id" => 500}, M2),
+ ok = maps:get(M0#{"id" => 600}, M2),
+ ok = maps:get(M0#{"id" => 700}, M2),
+ ok = maps:get(M0#{"id" => 800}, M2),
+ ok = maps:get(M0#{"id" => 900}, M2),
+ ok = maps:get(M0#{"id" => 25061}, M2),
+ ok = maps:get(M0#{"id" => 39766}, M2),
+ ok.
+
%% OTP-13146
%% Provoke major GC with a lot of "fat" maps on external format in msg queue
%% causing heap fragments to be allocated.
-t_gc_rare_map_overflow(Config) ->
+t_gc_rare_map_overflow(Config) when is_list(Config) ->
Pa = filename:dirname(code:which(?MODULE)),
{ok, Node} = test_server:start_node(gc_rare_map_overflow, slave, [{args, "-pa \""++Pa++"\""}]),
erts_debug:set_internal_state(available_internal_state, true),
@@ -3006,7 +3068,7 @@ t_gc_rare_map_overflow(Config) ->
Loop()
end),
FatMap = fatmap(34),
- false = (flatmap =:= erts_internal:map_type(FatMap)),
+ false = (flatmap =:= erts_internal:term_type(FatMap)),
t_gc_rare_map_overflow_do(Echo, FatMap, fun() -> erlang:garbage_collect() end),
@@ -3016,7 +3078,7 @@ t_gc_rare_map_overflow(Config) ->
unlink(Echo),
%% Test fatmap in exit signal
- Exiter = spawn_link(Node, fun Loop() -> receive {From,Msg} ->
+ Exiter = spawn_link(Node, fun Loop() -> receive {_From,Msg} ->
"not_a_map" = Msg % badmatch!
end,
Loop()
@@ -3034,7 +3096,7 @@ t_gc_rare_map_overflow(Config) ->
t_gc_rare_map_overflow_do(Echo, FatMap, GcFun) ->
Master = self(),
- true = receive M -> false after 0 -> true end, % assert empty msg queue
+ true = receive _M -> false after 0 -> true end, % assert empty msg queue
Echo ! {Master, token},
repeat(1000, fun(_) -> Echo ! {Master, FatMap} end, void),
diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl
index 3f986ca2ed..6733237b20 100644
--- a/erts/emulator/test/match_spec_SUITE.erl
+++ b/erts/emulator/test/match_spec_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,11 +20,10 @@
-module(match_spec_SUITE).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2, not_run/1]).
+-export([all/0, suite/0, not_run/1]).
-export([test_1/1, test_2/1, test_3/1, bad_match_spec_bin/1,
trace_control_word/1, silent/1, silent_no_ms/1, silent_test/1,
- ms_trace2/1, ms_trace3/1, boxed_and_small/1,
+ ms_trace2/1, ms_trace3/1, ms_trace_dead/1, boxed_and_small/1,
destructive_in_test_bif/1, guard_exceptions/1,
empty_list/1,
unary_plus/1, unary_minus/1, moving_labels/1]).
@@ -39,27 +38,18 @@
% This test suite assumes that tracing in general works. What we test is
% the match spec functionality.
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
--export([init_per_testcase/2, end_per_testcase/2]).
-
-init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- Dog=?t:timetrap(?t:seconds(30)),
- [{watchdog, Dog}|Config].
-
-end_per_testcase(_Func, Config) ->
- Dog=?config(watchdog, Config),
- ?t:timetrap_cancel(Dog).
-
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {seconds, 30}}].
all() ->
case test_server:is_native(match_spec_SUITE) of
false ->
[test_1, test_2, test_3, bad_match_spec_bin,
trace_control_word, silent, silent_no_ms, silent_test, ms_trace2,
- ms_trace3, boxed_and_small, destructive_in_test_bif,
+ ms_trace3, ms_trace_dead, boxed_and_small, destructive_in_test_bif,
guard_exceptions, unary_plus, unary_minus, fpe,
moving_labels,
faulty_seq_trace,
@@ -69,106 +59,87 @@ all() ->
true -> [not_run]
end.
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
not_run(Config) when is_list(Config) ->
{skipped, "Native Code"}.
-test_1(doc) ->
- [""];
-test_1(suite) -> [];
test_1(Config) when is_list(Config) ->
- ?line tr(fun() -> ?MODULE:f1(a) end,
- {?MODULE, f1, 1},
- [],
- [{call, {?MODULE, f1, [a]}}]),
-
- ?line tr(fun() -> ?MODULE:f2(a, a) end,
- {?MODULE, f2, 2},
- [{['$1','$1'],[{is_atom, '$1'}],[]}],
- [{call, {?MODULE, f2, [a, a]}}]),
-
- ?line tr(fun() -> ?MODULE:f2(a, a) end,
- {?MODULE, f2, 2},
- [{['$1','$1'],[{is_atom, '$1'}],[{message, false}]}],
- []),
-
- ?line tr(fun() -> ?MODULE:f2(a, a) end,
- {?MODULE, f2, 2},
- [{['$1','$1'],[{is_atom, '$1'}],[{message, 4711}]}],
- [{call, {?MODULE, f2, [a, a]}, 4711}]),
+ tr(fun() -> ?MODULE:f1(a) end,
+ {?MODULE, f1, 1},
+ [],
+ [{call, {?MODULE, f1, [a]}}]),
+
+ tr(fun() -> ?MODULE:f2(a, a) end,
+ {?MODULE, f2, 2},
+ [{['$1','$1'],[{is_atom, '$1'}],[]}],
+ [{call, {?MODULE, f2, [a, a]}}]),
+
+ tr(fun() -> ?MODULE:f2(a, a) end,
+ {?MODULE, f2, 2},
+ [{['$1','$1'],[{is_atom, '$1'}],[{message, false}]}],
+ []),
+
+ tr(fun() -> ?MODULE:f2(a, a) end,
+ {?MODULE, f2, 2},
+ [{['$1','$1'],[{is_atom, '$1'}],[{message, 4711}]}],
+ [{call, {?MODULE, f2, [a, a]}, 4711}]),
Ref = make_ref(),
- ?line tr(fun() -> ?MODULE:f2(Ref, Ref) end,
- {?MODULE, f2, 2},
- [{[Ref,'$1'],[{is_reference, '$1'}],[{message, 4711}]}],
- [{call, {?MODULE, f2, [Ref, Ref]}, 4711}]),
- ?line tr(fun() -> ?MODULE:f2(Ref, Ref) end,
- {?MODULE, f2, 2},
- [{['$1',Ref],[{is_reference, '$1'}],[{message, 4711}]}],
- [{call, {?MODULE, f2, [Ref, Ref]}, 4711}]),
-
- ?line tr(fun() -> ?MODULE:f2(a, a) end,
- {?MODULE, f2, 2},
- [{['$0','$0'],[{is_atom, '$0'}],[{message, 4711}]}],
- [{call, {?MODULE, f2, [a, a]}, 4711}]),
-
- ?line tr(fun() -> ?MODULE:f2(a, b) end,
- {?MODULE, f2, 2},
- [{['_','_'],[],[]}],
- [{call, {?MODULE, f2, [a, b]}}]),
-
- ?line tr(fun() -> ?MODULE:f2(a, b) end,
- {?MODULE, f2, 2},
- [{['_','_'],[],[{message, '$_'}]}],
- [{call, {?MODULE, f2, [a, b]}, [a, b]}]),
-
- ?line tr(fun() -> ?MODULE:f2(a, '$_') end,
- {?MODULE, f2, 2},
- [{['$1','$_'],[{is_atom, '$1'}],[]}],
- [{call, {?MODULE, f2, [a, '$_']}}]),
-
- ?line tr(fun() -> ?MODULE:f1({a}) end,
- {?MODULE, f1, 1},
- [{['$1'],[{'==', '$1', {const, {a}}}],[]}],
- [{call, {?MODULE, f1, [{a}]}}]),
-
- ?line tr(fun() -> ?MODULE:f1({a}) end,
- {?MODULE, f1, 1},
- [{['$1'],[{'==', '$1', {{a}}}],[]}],
- [{call, {?MODULE, f1, [{a}]}}]),
-
-%% Undocumented, currently.
- ?line tr(fun() -> ?MODULE:f2(a, a) end,
- {?MODULE, f2, 2},
- [{['$1','$1'],[{is_atom, '$1'}],[{message, 4711},
- {message, true}]}],
- [{call, {?MODULE, f2, [a, a]}}]),
-
- ?line tr(fun() -> ?MODULE:f2(a, a) end,
- {?MODULE, f2, 2},
- [{['$1','$1'],[{is_atom, '$1'}],[{message, 4711},
- {message, false}]}],
- []),
-
- ?line tr(fun() -> ?MODULE:f2(a, a) end,
- {?MODULE, f2, 2},
- [{['$1','$1'],[{is_atom, '$1'}],[kakalorum]}],
- [{call, {?MODULE, f2, [a, a]}}]),
+ tr(fun() -> ?MODULE:f2(Ref, Ref) end,
+ {?MODULE, f2, 2},
+ [{[Ref,'$1'],[{is_reference, '$1'}],[{message, 4711}]}],
+ [{call, {?MODULE, f2, [Ref, Ref]}, 4711}]),
+ tr(fun() -> ?MODULE:f2(Ref, Ref) end,
+ {?MODULE, f2, 2},
+ [{['$1',Ref],[{is_reference, '$1'}],[{message, 4711}]}],
+ [{call, {?MODULE, f2, [Ref, Ref]}, 4711}]),
+
+ tr(fun() -> ?MODULE:f2(a, a) end,
+ {?MODULE, f2, 2},
+ [{['$0','$0'],[{is_atom, '$0'}],[{message, 4711}]}],
+ [{call, {?MODULE, f2, [a, a]}, 4711}]),
+
+ tr(fun() -> ?MODULE:f2(a, b) end,
+ {?MODULE, f2, 2},
+ [{['_','_'],[],[]}],
+ [{call, {?MODULE, f2, [a, b]}}]),
+
+ tr(fun() -> ?MODULE:f2(a, b) end,
+ {?MODULE, f2, 2},
+ [{['_','_'],[],[{message, '$_'}]}],
+ [{call, {?MODULE, f2, [a, b]}, [a, b]}]),
+
+ tr(fun() -> ?MODULE:f2(a, '$_') end,
+ {?MODULE, f2, 2},
+ [{['$1','$_'],[{is_atom, '$1'}],[]}],
+ [{call, {?MODULE, f2, [a, '$_']}}]),
+
+ tr(fun() -> ?MODULE:f1({a}) end,
+ {?MODULE, f1, 1},
+ [{['$1'],[{'==', '$1', {const, {a}}}],[]}],
+ [{call, {?MODULE, f1, [{a}]}}]),
+
+ tr(fun() -> ?MODULE:f1({a}) end,
+ {?MODULE, f1, 1},
+ [{['$1'],[{'==', '$1', {{a}}}],[]}],
+ [{call, {?MODULE, f1, [{a}]}}]),
+
+ %% Undocumented, currently.
+ tr(fun() -> ?MODULE:f2(a, a) end,
+ {?MODULE, f2, 2},
+ [{['$1','$1'],[{is_atom, '$1'}],[{message, 4711},
+ {message, true}]}],
+ [{call, {?MODULE, f2, [a, a]}}]),
+
+ tr(fun() -> ?MODULE:f2(a, a) end,
+ {?MODULE, f2, 2},
+ [{['$1','$1'],[{is_atom, '$1'}],[{message, 4711},
+ {message, false}]}],
+ []),
+
+ tr(fun() -> ?MODULE:f2(a, a) end,
+ {?MODULE, f2, 2},
+ [{['$1','$1'],[{is_atom, '$1'}],[kakalorum]}],
+ [{call, {?MODULE, f2, [a, a]}}]),
%% Verify that 'process_dump' can handle a matchstate on the stack.
tr(fun() -> fbinmatch(<<0>>, 0) end,
@@ -176,56 +147,49 @@ test_1(Config) when is_list(Config) ->
[{['_'],[],[{message, {process_dump}}]}],
[fun({trace, _, call, {?MODULE, f1, [0]}, _Bin}) -> true end]),
-% Error cases
- ?line errchk([{['$1','$1'],[{is_atom, '$1'}],[{banka, kanin}]}]),
-
+ % Error cases
+ errchk([{['$1','$1'],[{is_atom, '$1'}],[{banka, kanin}]}]),
ok.
-test_2(doc) ->
- [""];
-test_2(suite) -> [];
test_2(Config) when is_list(Config) ->
- ?line tr(fun() -> ?MODULE:f2(a, a) end,
- {?MODULE, f2, 2},
- [{['$1','$1'],[{is_atom, '$1'}],[{return_trace}]}],
- [{call, {?MODULE, f2, [a, a]}},
- {return_from, {?MODULE, f2, 2}, {a, a}}]),
+ tr(fun() -> ?MODULE:f2(a, a) end,
+ {?MODULE, f2, 2},
+ [{['$1','$1'],[{is_atom, '$1'}],[{return_trace}]}],
+ [{call, {?MODULE, f2, [a, a]}},
+ {return_from, {?MODULE, f2, 2}, {a, a}}]),
ok.
-test_3(doc) ->
- ["Test the enable_trace/2 and caller/0 PAM instructions"];
-test_3(suite) -> [];
+%% Test the enable_trace/2 and caller/0 PAM instructions
test_3(Config) when is_list(Config) ->
- ?line Fun1 = fun() ->
+ Fun1 = fun() ->
register(fnoppelklopfer,self()),
?MODULE:f2(a, b),
?MODULE:f2(a, b)
end,
- ?line P1 = spawn(?MODULE, runner, [self(), Fun1]),
- ?line Pat = [{['$1','$1'],[],[{message,
- [{enable_trace, P1, call},{caller}]}]},
- {['_','_'],[],[{message,
- [{disable_trace, fnoppelklopfer, call}]}]}],
- ?line Fun2 = fun() -> ?MODULE:f3(a, a) end,
- ?line P2 = spawn(?MODULE, runner, [self(), Fun2]),
- ?line erlang:trace(P2, true, [call]),
- ?line erlang:trace_pattern({?MODULE, f2, 2}, Pat),
- ?line collect(P2, [{trace, P2, call, {?MODULE, f2, [a, a]}, [true,
+ P1 = spawn(?MODULE, runner, [self(), Fun1]),
+ Pat = [{['$1','$1'],[],[{message,
+ [{enable_trace, P1, call},{caller}]}]},
+ {['_','_'],[],[{message,
+ [{disable_trace, fnoppelklopfer, call}]}]}],
+ Fun2 = fun() -> ?MODULE:f3(a, a) end,
+ P2 = spawn(?MODULE, runner, [self(), Fun2]),
+ erlang:trace(P2, true, [call]),
+ erlang:trace_pattern({?MODULE, f2, 2}, Pat),
+ collect(P2, [{trace, P2, call, {?MODULE, f2, [a, a]}, [true,
{?MODULE,f3,2}]}]),
- ?line collect(P1, [{trace, P1, call, {?MODULE, f2, [a, b]}, [true]}]),
- ?line ok.
+ collect(P1, [{trace, P1, call, {?MODULE, f2, [a, b]}, [true]}]),
+ ok.
-otp_9422(doc) -> [];
otp_9422(Config) when is_list(Config) ->
Laps = 10000,
- ?line Fun1 = fun() -> otp_9422_tracee() end,
- ?line P1 = spawn_link(?MODULE, loop_runner, [self(), Fun1, Laps]),
+ Fun1 = fun() -> otp_9422_tracee() end,
+ P1 = spawn_link(?MODULE, loop_runner, [self(), Fun1, Laps]),
io:format("spawned ~p as tracee\n", [P1]),
- ?line erlang:trace(P1, true, [call, silent]),
+ erlang:trace(P1, true, [call, silent]),
- ?line Fun2 = fun() -> otp_9422_trace_changer() end,
- ?line P2 = spawn_link(?MODULE, loop_runner, [self(), Fun2, Laps]),
+ Fun2 = fun() -> otp_9422_trace_changer() end,
+ P2 = spawn_link(?MODULE, loop_runner, [self(), Fun2, Laps]),
io:format("spawned ~p as trace_changer\n", [P2]),
start_collect(P1),
@@ -244,9 +208,9 @@ otp_9422_tracee() ->
otp_9422_trace_changer() ->
Pat1 = [{[a], [], [{enable_trace, arity}]}],
- ?line erlang:trace_pattern({?MODULE, f1, 1}, Pat1),
+ erlang:trace_pattern({?MODULE, f1, 1}, Pat1),
Pat2 = [{[b], [], [{disable_trace, arity}]}],
- ?line erlang:trace_pattern({?MODULE, f1, 1}, Pat2).
+ erlang:trace_pattern({?MODULE, f1, 1}, Pat2).
@@ -261,261 +225,227 @@ bad_match_spec_bin(Config) when is_list(Config) ->
-trace_control_word(doc) ->
- ["Test the erlang:system_info(trace_control_word) and ",
- "erlang:system_flag(trace_control_word, Value) BIFs, ",
- "as well as the get_tcw/0 and set_tcw/1 PAM instructions"];
-trace_control_word(suite) -> [];
+%% Test the erlang:system_info(trace_control_word) and
+%% erlang:system_flag(trace_control_word, Value) BIFs,
+%% as well as the get_tcw/0 and set_tcw/1 PAM instructions
trace_control_word(Config) when is_list(Config) ->
- ?line 32 = Bits = tcw_bits(),
- ?line High = 1 bsl (Bits - 1),
- ?line erlang:system_flag(trace_control_word, 17),
- ?line tr(fun() -> ?MODULE:f1(a) end,
- {?MODULE, f1, 1},
- [{'_',[{'=:=', {get_tcw}, 17}],[]}],
- [{call, {?MODULE, f1, [a]}}]),
- ?line tr(fun() -> ?MODULE:f1(a) end,
- {?MODULE, f1, 1},
- [{'_',[{'=:=', {get_tcw}, 18}],[]}],
- []),
- ?line erlang:system_flag(trace_control_word, High),
- ?line tr(fun() -> ?MODULE:f1(a) end,
- {?MODULE, f1, 1},
- [{'_',[{'=:=', {get_tcw}, High}],[]}],
- [{call, {?MODULE, f1, [a]}}]),
- ?line erlang:system_flag(trace_control_word, 0),
- ?line tr(fun() ->
- ?MODULE:f1(a),
- ?MODULE:f1(start),
- ?MODULE:f1(b),
- ?MODULE:f1(c),
- ?MODULE:f1(high),
- ?MODULE:f1(d),
- ?MODULE:f1(stop),
- ?MODULE:f1(e)
- end,
- {?MODULE, f1, 1},
- [{[start],
- [],
- [{message, {set_tcw, 17}}]},
- {[stop],
- [],
- [{message, {set_tcw, 0}}]},
- {[high],
- [],
- [{message, {set_tcw, High}}]},
- {['_'],
- [{'>', {get_tcw}, 0}],
- [{set_tcw, {'+', 1, {get_tcw}}}, {message, {get_tcw}}] }],
- [{call, {?MODULE, f1, [start]}, 0},
- {call, {?MODULE, f1, [b]}, 18},
- {call, {?MODULE, f1, [c]}, 19},
- {call, {?MODULE, f1, [high]}, 19},
- {call, {?MODULE, f1, [d]}, High + 1},
- {call, {?MODULE, f1, [stop]}, High + 1}]),
- ?line 0 = erlang:system_info(trace_control_word),
+ 32 = Bits = tcw_bits(),
+ High = 1 bsl (Bits - 1),
+ erlang:system_flag(trace_control_word, 17),
+ tr(fun() -> ?MODULE:f1(a) end,
+ {?MODULE, f1, 1},
+ [{'_',[{'=:=', {get_tcw}, 17}],[]}],
+ [{call, {?MODULE, f1, [a]}}]),
+ tr(fun() -> ?MODULE:f1(a) end,
+ {?MODULE, f1, 1},
+ [{'_',[{'=:=', {get_tcw}, 18}],[]}],
+ []),
+ erlang:system_flag(trace_control_word, High),
+ tr(fun() -> ?MODULE:f1(a) end,
+ {?MODULE, f1, 1},
+ [{'_',[{'=:=', {get_tcw}, High}],[]}],
+ [{call, {?MODULE, f1, [a]}}]),
+ erlang:system_flag(trace_control_word, 0),
+ tr(fun() ->
+ ?MODULE:f1(a),
+ ?MODULE:f1(start),
+ ?MODULE:f1(b),
+ ?MODULE:f1(c),
+ ?MODULE:f1(high),
+ ?MODULE:f1(d),
+ ?MODULE:f1(stop),
+ ?MODULE:f1(e)
+ end,
+ {?MODULE, f1, 1},
+ [{[start],
+ [],
+ [{message, {set_tcw, 17}}]},
+ {[stop],
+ [],
+ [{message, {set_tcw, 0}}]},
+ {[high],
+ [],
+ [{message, {set_tcw, High}}]},
+ {['_'],
+ [{'>', {get_tcw}, 0}],
+ [{set_tcw, {'+', 1, {get_tcw}}}, {message, {get_tcw}}] }],
+ [{call, {?MODULE, f1, [start]}, 0},
+ {call, {?MODULE, f1, [b]}, 18},
+ {call, {?MODULE, f1, [c]}, 19},
+ {call, {?MODULE, f1, [high]}, 19},
+ {call, {?MODULE, f1, [d]}, High + 1},
+ {call, {?MODULE, f1, [stop]}, High + 1}]),
+ 0 = erlang:system_info(trace_control_word),
ok.
tcw_bits() ->
- ?line tcw_bits(erlang:system_flag(trace_control_word, 0), 0, 0).
+ tcw_bits(erlang:system_flag(trace_control_word, 0), 0, 0).
tcw_bits(Save, Prev, Bits) ->
- ?line Curr = 1 bsl Bits,
- ?line case catch erlang:system_flag(trace_control_word, Curr) of
- {'EXIT' , {badarg, _}} ->
- ?line Prev = erlang:system_flag(trace_control_word, Save),
- Bits;
- Prev ->
- ?line Curr = erlang:system_info(trace_control_word),
- tcw_bits(Save, Curr, Bits+1)
- end.
-
-
-
-silent(doc) ->
- ["Test the erlang:trace(_, _, [silent]) flag ",
- "as well as the silent/0 PAM instruction"];
-silent(suite) -> [];
+ Curr = 1 bsl Bits,
+ case catch erlang:system_flag(trace_control_word, Curr) of
+ {'EXIT' , {badarg, _}} ->
+ Prev = erlang:system_flag(trace_control_word, Save),
+ Bits;
+ Prev ->
+ Curr = erlang:system_info(trace_control_word),
+ tcw_bits(Save, Curr, Bits+1)
+ end.
+
+
+%% Test the erlang:trace(_, _, [silent]) flag
+%% as well as the silent/0 PAM instruction
silent(Config) when is_list(Config) ->
%% Global call trace
- ?line tr(fun() ->
- ?MODULE:f1(a), % No trace - not active
- ?MODULE:f1(miss), % No trace - no activation
- ?MODULE:f1(b), % No trace - still not active
- ?MODULE:f1(start), % Trace - activation
- ?MODULE:f1(c), % Trace - active
- f1(d), % No trace - local call
- ?MODULE:f1(miss), % Trace - no inactivation
- ?MODULE:f1(e), % Trace - still active
- ?MODULE:f1(stop), % No trace - inactivation
- ?MODULE:f1(f) % No trace - not active
- end,
- {?MODULE, f1, 1},
- [call, silent],
- [{[start],
- [],
- [{silent, false}, {message, start}]},
- {[stop],
- [],
- [{silent, true}, {message, stop}]},
- {[miss],
- [],
- [{silent, neither_true_nor_false}, {message, miss}]},
- {['$1'],
- [],
- [{message, '$1'}] }],
- [global],
- [{call, {?MODULE, f1, [start]}, start},
- {call, {?MODULE, f1, [c]}, c},
- {call, {?MODULE, f1, [miss]}, miss},
- {call, {?MODULE, f1, [e]}, e} ]),
+ tr(fun() ->
+ ?MODULE:f1(a), % No trace - not active
+ ?MODULE:f1(miss), % No trace - no activation
+ ?MODULE:f1(b), % No trace - still not active
+ ?MODULE:f1(start), % Trace - activation
+ ?MODULE:f1(c), % Trace - active
+ f1(d), % No trace - local call
+ ?MODULE:f1(miss), % Trace - no inactivation
+ ?MODULE:f1(e), % Trace - still active
+ ?MODULE:f1(stop), % No trace - inactivation
+ ?MODULE:f1(f) % No trace - not active
+ end,
+ {?MODULE, f1, 1},
+ [call, silent],
+ [{[start],
+ [],
+ [{silent, false}, {message, start}]},
+ {[stop],
+ [],
+ [{silent, true}, {message, stop}]},
+ {[miss],
+ [],
+ [{silent, neither_true_nor_false}, {message, miss}]},
+ {['$1'],
+ [],
+ [{message, '$1'}] }],
+ [global],
+ [{call, {?MODULE, f1, [start]}, start},
+ {call, {?MODULE, f1, [c]}, c},
+ {call, {?MODULE, f1, [miss]}, miss},
+ {call, {?MODULE, f1, [e]}, e} ]),
%% Local call trace
- ?line tr(fun() ->
- ?MODULE:f1(a), % No trace - not active
- f1(b), % No trace - not active
- ?MODULE:f1(start), % Trace - activation
- ?MODULE:f1(c), % Trace - active
- f1(d), % Trace - active
- f1(stop), % No trace - inactivation
- ?MODULE:f1(e), % No trace - not active
- f1(f) % No trace - not active
- end,
- {?MODULE, f1, 1},
- [call, silent],
- [{[start],
- [],
- [{silent, false}, {message, start}]},
- {[stop],
- [],
- [{silent, true}, {message, stop}]},
- {['$1'],
- [],
- [{message, '$1'}] }],
- [local],
- [{call, {?MODULE, f1, [start]}, start},
- {call, {?MODULE, f1, [c]}, c},
- {call, {?MODULE, f1, [d]}, d} ]),
+ tr(fun() ->
+ ?MODULE:f1(a), % No trace - not active
+ f1(b), % No trace - not active
+ ?MODULE:f1(start), % Trace - activation
+ ?MODULE:f1(c), % Trace - active
+ f1(d), % Trace - active
+ f1(stop), % No trace - inactivation
+ ?MODULE:f1(e), % No trace - not active
+ f1(f) % No trace - not active
+ end,
+ {?MODULE, f1, 1},
+ [call, silent],
+ [{[start],
+ [],
+ [{silent, false}, {message, start}]},
+ {[stop],
+ [],
+ [{silent, true}, {message, stop}]},
+ {['$1'],
+ [],
+ [{message, '$1'}] }],
+ [local],
+ [{call, {?MODULE, f1, [start]}, start},
+ {call, {?MODULE, f1, [c]}, c},
+ {call, {?MODULE, f1, [d]}, d} ]),
ok.
-silent_no_ms(doc) ->
- ["Test the erlang:trace(_, _, [silent]) flag without match specs"];
-silent_no_ms(suite) -> [];
+%% Test the erlang:trace(_, _, [silent]) flag without match specs
silent_no_ms(Config) when is_list(Config) ->
%% Global call trace
%%
%% Trace f2/2 and erlang:integer_to_list/1 without match spec
%% and use match spec on f1/1 to control silent flag.
- ?line tr(
- fun () ->
- ?MODULE:f1(a),
- ?MODULE:f2(b, c),
- _ = erlang:integer_to_list(id(1)),
- ?MODULE:f3(d, e),
- ?MODULE:f1(start),
- ?MODULE:f2(f, g),
- _ = erlang:integer_to_list(id(2)),
- ?MODULE:f3(h, i),
- ?MODULE:f1(stop),
- ?MODULE:f2(j, k),
- _ = erlang:integer_to_list(id(3)),
- ?MODULE:f3(l, m)
- end,
- fun (Tracee) ->
- ?line 1 =
- erlang:trace(Tracee, true,
- [call,silent,return_to]),
- ?line 1 =
- erlang:trace_pattern(
- {?MODULE,f2,2},
- [],
- [global]),
- ?line 1 =
- erlang:trace_pattern(
- {erlang,integer_to_list,1},
- [],
- [global]),
- ?line 1 =
- erlang:trace_pattern(
- {?MODULE,f1,1},
- [{[start],[],[{silent,false}]},
- {[stop],[],[{silent,true}]}],
- [global]),
- %%
- %% Expected: (no return_to for global call trace)
- %%
- ?line
- [{trace,Tracee,call,{?MODULE,f1,[start]}},
- {trace,Tracee,call,{?MODULE,f2,[f,g]}},
- {trace,Tracee,call,{erlang,integer_to_list,[2]}},
- {trace,Tracee,call,{?MODULE,f2,[h,i]}}]
- end),
+ tr(
+ fun () ->
+ ?MODULE:f1(a),
+ ?MODULE:f2(b, c),
+ _ = erlang:integer_to_list(id(1)),
+ ?MODULE:f3(d, e),
+ ?MODULE:f1(start),
+ ?MODULE:f2(f, g),
+ _ = erlang:integer_to_list(id(2)),
+ ?MODULE:f3(h, i),
+ ?MODULE:f1(stop),
+ ?MODULE:f2(j, k),
+ _ = erlang:integer_to_list(id(3)),
+ ?MODULE:f3(l, m)
+ end,
+ fun (Tracee) ->
+ 1 = erlang:trace(Tracee, true, [call,silent,return_to]),
+ 1 = erlang:trace_pattern( {?MODULE,f2,2}, [], [global]),
+ 1 = erlang:trace_pattern( {erlang,integer_to_list,1}, [], [global]),
+ 1 = erlang:trace_pattern(
+ {?MODULE,f1,1},
+ [{[start],[],[{silent,false}]},
+ {[stop],[],[{silent,true}]}],
+ [global]),
+ %%
+ %% Expected: (no return_to for global call trace)
+ %%
+ [{trace,Tracee,call,{?MODULE,f1,[start]}},
+ {trace,Tracee,call,{?MODULE,f2,[f,g]}},
+ {trace,Tracee,call,{erlang,integer_to_list,[2]}},
+ {trace,Tracee,call,{?MODULE,f2,[h,i]}}]
+ end),
%% Local call trace
%%
%% Trace f2/2 and erlang:integer_to_list/1 without match spec
%% and use match spec on f1/1 to control silent flag.
- ?line tr(
- fun () ->
- ?MODULE:f1(a),
- ?MODULE:f2(b, c),
- _ = erlang:integer_to_list(id(1)),
- ?MODULE:f3(d, e),
- ?MODULE:f1(start),
- ?MODULE:f2(f, g),
- _ = erlang:integer_to_list(id(2)),
- ?MODULE:f3(h, i),
- ?MODULE:f1(stop),
- ?MODULE:f2(j, k),
- _ = erlang:integer_to_list(id(3)),
- ?MODULE:f3(l, m)
- end,
- fun (Tracee) ->
- ?line 1 =
- erlang:trace(Tracee, true,
- [call,silent,return_to]),
- ?line 1 =
- erlang:trace_pattern(
- {?MODULE,f2,2},
- [],
- [local]),
- ?line 1 =
- erlang:trace_pattern(
- {erlang,integer_to_list,1},
- [],
- [local]),
- ?line 1 =
- erlang:trace_pattern(
- {?MODULE,f1,1},
- [{[start],[],[{silent,false}]},
- {[stop],[],[{silent,true}]}],
- [local]),
- %%
- %% Expected:
- %%
- ?line
- [{trace,Tracee,call,{?MODULE,f1,[start]}},
- {trace,Tracee,return_to,
- {?MODULE,'-silent_no_ms/1-fun-2-',0}},
- {trace,Tracee,call,{?MODULE,f2,[f,g]}},
- {trace,Tracee,return_to,
- {?MODULE,'-silent_no_ms/1-fun-2-',0}},
- {trace,Tracee,call,{erlang,integer_to_list,[2]}},
- {trace,Tracee,return_to,
- {?MODULE,'-silent_no_ms/1-fun-2-',0}},
- {trace,Tracee,call,{?MODULE,f2,[h,i]}},
- {trace,Tracee,return_to,{?MODULE,f3,2}}]
- end).
-
-silent_test(doc) ->
- ["Test that match_spec_test does not activate silent"];
+ tr(
+ fun () ->
+ ?MODULE:f1(a),
+ ?MODULE:f2(b, c),
+ _ = erlang:integer_to_list(id(1)),
+ ?MODULE:f3(d, e),
+ ?MODULE:f1(start),
+ ?MODULE:f2(f, g),
+ _ = erlang:integer_to_list(id(2)),
+ ?MODULE:f3(h, i),
+ ?MODULE:f1(stop),
+ ?MODULE:f2(j, k),
+ _ = erlang:integer_to_list(id(3)),
+ ?MODULE:f3(l, m)
+ end,
+ fun (Tracee) ->
+ 1 = erlang:trace(Tracee, true, [call,silent,return_to]),
+ 1 = erlang:trace_pattern( {?MODULE,f2,2}, [], [local]),
+ 1 = erlang:trace_pattern( {erlang,integer_to_list,1}, [], [local]),
+ 1 = erlang:trace_pattern(
+ {?MODULE,f1,1},
+ [{[start],[],[{silent,false}]},
+ {[stop],[],[{silent,true}]}],
+ [local]),
+ %%
+ %% Expected:
+ %%
+ [{trace,Tracee,call,{?MODULE,f1,[start]}},
+ {trace,Tracee,return_to,
+ {?MODULE,'-silent_no_ms/1-fun-2-',0}},
+ {trace,Tracee,call,{?MODULE,f2,[f,g]}},
+ {trace,Tracee,return_to,
+ {?MODULE,'-silent_no_ms/1-fun-2-',0}},
+ {trace,Tracee,call,{erlang,integer_to_list,[2]}},
+ {trace,Tracee,return_to,
+ {?MODULE,'-silent_no_ms/1-fun-2-',0}},
+ {trace,Tracee,call,{?MODULE,f2,[h,i]}},
+ {trace,Tracee,return_to,{?MODULE,f3,2}}]
+ end).
+
+%% Test that match_spec_test does not activate silent
silent_test(_Config) ->
{flags,[]} = erlang:trace_info(self(),flags),
erlang:match_spec_test([],[{'_',[],[{silent,true}]}],trace),
{flags,[]} = erlang:trace_info(self(),flags).
-ms_trace2(doc) ->
- ["Test the match spec functions {trace/2}"];
-ms_trace2(suite) -> [];
+%% Test the match spec functions {trace/2}
ms_trace2(Config) when is_list(Config) ->
Tracer = self(),
%% Meta trace init
@@ -523,75 +453,60 @@ ms_trace2(Config) when is_list(Config) ->
%% Trace global f1/1, local f2/2, global erlang:integer_to_list/1
%% without match spec. Use match spec functions
%% {trace/2} to control trace through fn/2,3.
- ?line tr(
- fun () ->
- ?MODULE:f1(a),
- ?MODULE:f2(b, c),
- _ = erlang:integer_to_list(id(1)),
- ?MODULE:f3(d, e),
- fn([all], [call,return_to,{tracer,Tracer}]),
- ?MODULE:f1(f),
- f2(g, h),
- f1(i),
- _ = erlang:integer_to_list(id(2)),
- ?MODULE:f3(j, k),
- fn([call,return_to], []),
- ?MODULE:f1(l),
- ?MODULE:f2(m, n),
- _ = erlang:integer_to_list(id(3)),
- ?MODULE:f3(o, p)
- end,
- fun (Tracee) ->
- ?line 1 =
- erlang:trace(Tracee, false, [all]),
- ?line 1 =
- erlang:trace_pattern(
- {?MODULE,f1,1},
- [],
- [global]),
- ?line 1 =
- erlang:trace_pattern(
- {?MODULE,f2,2},
- [],
- [local]),
- ?line 1 =
- erlang:trace_pattern(
- {erlang,integer_to_list,1},
- [],
- [global]),
- ?line 3 =
- erlang:trace_pattern(
- {?MODULE,fn,'_'},
- [{['$1','$2'],[],
- [{trace,'$1','$2'},{message,ms_trace2}]}],
- [meta]),
- %%
- %% Expected: (no return_to for global call trace)
- %%
- ?line Origin = {match_spec_SUITE,'-ms_trace2/1-fun-0-',1},
- ?line
- [{trace_ts,Tracee,call,
- {?MODULE,fn,
- [[all],[call,return_to,{tracer,Tracer}]]},
- ms_trace2},
- {trace,Tracee,call,{?MODULE,f1,[f]}},
- {trace,Tracee,call,{?MODULE,f2,[g,h]}},
- {trace,Tracee,return_to,Origin},
- {trace,Tracee,call,{erlang,integer_to_list,[2]}},
- {trace,Tracee,call,{?MODULE,f2,[j,k]}},
- {trace,Tracee,return_to,{?MODULE,f3,2}},
- {trace_ts,Tracee,call,
- {?MODULE,fn,
- [[call,return_to],[]]},
- ms_trace2}]
- end),
+ tr(
+ fun () ->
+ ?MODULE:f1(a),
+ ?MODULE:f2(b, c),
+ _ = erlang:integer_to_list(id(1)),
+ ?MODULE:f3(d, e),
+ fn([all], [call,return_to,{tracer,Tracer}]),
+ ?MODULE:f1(f),
+ f2(g, h),
+ f1(i),
+ _ = erlang:integer_to_list(id(2)),
+ ?MODULE:f3(j, k),
+ fn([call,return_to], []),
+ ?MODULE:f1(l),
+ ?MODULE:f2(m, n),
+ _ = erlang:integer_to_list(id(3)),
+ ?MODULE:f3(o, p)
+ end,
+ fun (Tracee) ->
+ 1 = erlang:trace(Tracee, false, [all]),
+ 1 = erlang:trace_pattern( {?MODULE,f1,1}, [], [global]),
+ 1 = erlang:trace_pattern( {?MODULE,f2,2}, [], [local]),
+ 1 = erlang:trace_pattern( {erlang,integer_to_list,1}, [], [global]),
+ 3 = erlang:trace_pattern(
+ {?MODULE,fn,'_'},
+ [{['$1','$2'],[],
+ [{trace,'$1','$2'},{message,ms_trace2}]}],
+ [meta]),
+ %%
+ %% Expected: (no return_to for global call trace)
+ %%
+ Origin = {match_spec_SUITE,'-ms_trace2/1-fun-0-',1},
+ [{trace_ts,Tracee,call,
+ {?MODULE,fn,
+ [[all],[call,return_to,{tracer,Tracer}]]},
+ ms_trace2},
+ {trace,Tracee,call,{?MODULE,f1,[f]}},
+ {trace,Tracee,call,{?MODULE,f2,[g,h]}},
+ {trace,Tracee,return_to,Origin},
+ {trace,Tracee,call,{erlang,integer_to_list,[2]}},
+ {trace,Tracee,call,{?MODULE,f2,[j,k]}},
+ {trace,Tracee,return_to,{?MODULE,f3,2}},
+ {trace_ts,Tracee,call,
+ {?MODULE,fn,
+ [[call,return_to],[]]},
+ ms_trace2}]
+ end),
+ %% Silence valgrind
+ erlang:trace_pattern({?MODULE,fn,'_'},[],[]),
ok.
-ms_trace3(doc) ->
- ["Test the match spec functions {trace/3}"];
-ms_trace3(suite) -> [];
+%% Test the match spec functions {trace/3}
ms_trace3(Config) when is_list(Config) ->
TraceeName = 'match_spec_SUITE:ms_trace3',
Tracer = self(),
@@ -602,134 +517,140 @@ ms_trace3(Config) when is_list(Config) ->
%% {trace/2} to control trace through fn/2,3.
Tag = make_ref(),
Controller =
- spawn_link(
- fun () ->
- receive
- {Tracee,Tag,start} ->
- fn(TraceeName, [all],
- [call,return_to,send,'receive',
- {tracer,Tracer}]),
- Tracee ! {self(),Tag,started},
- receive {Tracee,Tag,stop_1} -> ok end,
- fn(Tracee, [call,return_to], []),
- Tracee ! {self(),Tag,stopped_1},
- receive {Tracee,Tag,stop_2} -> ok end,
- fn(Tracee, [all], []),
- Tracee ! {self(),Tag,stopped_2}
- end
- end),
- ?line tr(
- fun () -> %% Action
- register(TraceeName, self()),
- ?MODULE:f1(a),
- ?MODULE:f2(b, c),
- _ = erlang:integer_to_list(id(1)),
- ?MODULE:f3(d, e),
- Controller ! {self(),Tag,start},
- receive {Controller,Tag,started} -> ok end,
- ?MODULE:f1(f),
- f2(g, h),
- f1(i),
- _ = erlang:integer_to_list(id(2)),
- ?MODULE:f3(j, k),
- Controller ! {self(),Tag,stop_1},
- receive {Controller,Tag,stopped_1} -> ok end,
- ?MODULE:f1(l),
- ?MODULE:f2(m, n),
- _ = erlang:integer_to_list(id(3)),
- ?MODULE:f3(o, p),
- Controller ! {self(),Tag,stop_2},
- receive {Controller,Tag,stopped_2} -> ok end,
- ?MODULE:f1(q),
- ?MODULE:f2(r, s),
- _ = erlang:integer_to_list(id(4)),
- ?MODULE:f3(t, u)
- end,
-
- fun (Tracee) -> %% Startup
- ?line 1 =
- erlang:trace(Tracee, false, [all]),
- ?line 1 =
- erlang:trace_pattern(
- {?MODULE,f1,1},
- [],
- [global]),
- ?line 1 =
- erlang:trace_pattern(
- {?MODULE,f2,2},
- [],
- [local]),
- ?line 1 =
- erlang:trace_pattern(
- {erlang,integer_to_list,1},
- [],
- [global]),
- ?line 3 =
- erlang:trace_pattern(
- {?MODULE,fn,'_'},
- [{['$1','$2','$3'],[],
- [{trace,'$1','$2','$3'},{message,Tag}]}],
- [meta]),
- %%
- %% Expected: (no return_to for global call trace)
- %%
- ?line Origin = {match_spec_SUITE,'-ms_trace3/1-fun-1-',2},
- ?line
- [{trace_ts,Controller,call,
- {?MODULE,fn,[TraceeName,[all],
- [call,return_to,send,'receive',
- {tracer,Tracer}]]},
- Tag},
- {trace,Tracee,'receive',{Controller,Tag,started}},
- {trace,Tracee,call,{?MODULE,f1,[f]}},
- {trace,Tracee,call,{?MODULE,f2,[g,h]}},
- {trace,Tracee,return_to,Origin},
- {trace,Tracee,call,{erlang,integer_to_list,[2]}},
- {trace,Tracee,call,{?MODULE,f2,[j,k]}},
- {trace,Tracee,return_to,{?MODULE,f3,2}},
- {trace,Tracee,send,{Tracee,Tag,stop_1},Controller},
- {trace_ts,Controller,call,
- {?MODULE,fn,[Tracee,[call,return_to],[]]},
- Tag},
- {trace_ts,Controller,call,
- {?MODULE,fn,[Tracee,[all],[]]},
- Tag}]
- end),
+ spawn_link(
+ fun () ->
+ receive
+ {Tracee,Tag,start} ->
+ fn(TraceeName, [all],
+ [call,return_to,send,'receive',
+ {tracer,Tracer}]),
+ Tracee ! {self(),Tag,started},
+ receive {Tracee,Tag,stop_1} -> ok end,
+ fn(Tracee, [call,return_to], []),
+ Tracee ! {self(),Tag,stopped_1},
+ receive {Tracee,Tag,stop_2} -> ok end,
+ fn(Tracee, [all], []),
+ Tracee ! {self(),Tag,stopped_2}
+ end
+ end),
+ tr(
+ fun () -> %% Action
+ register(TraceeName, self()),
+ ?MODULE:f1(a),
+ ?MODULE:f2(b, c),
+ _ = erlang:integer_to_list(id(1)),
+ ?MODULE:f3(d, e),
+ Controller ! {self(),Tag,start},
+ receive {Controller,Tag,started} -> ok end,
+ ?MODULE:f1(f),
+ f2(g, h),
+ f1(i),
+ _ = erlang:integer_to_list(id(2)),
+ ?MODULE:f3(j, k),
+ Controller ! {self(),Tag,stop_1},
+ receive {Controller,Tag,stopped_1} -> ok end,
+ ?MODULE:f1(l),
+ ?MODULE:f2(m, n),
+ _ = erlang:integer_to_list(id(3)),
+ ?MODULE:f3(o, p),
+ Controller ! {self(),Tag,stop_2},
+ receive {Controller,Tag,stopped_2} -> ok end,
+ ?MODULE:f1(q),
+ ?MODULE:f2(r, s),
+ _ = erlang:integer_to_list(id(4)),
+ ?MODULE:f3(t, u)
+ end,
+
+ fun (Tracee) -> %% Startup
+ 1 = erlang:trace(Tracee, false, [all]),
+ 1 = erlang:trace_pattern( {?MODULE,f1,1}, [], [global]),
+ 1 = erlang:trace_pattern( {?MODULE,f2,2}, [], [local]),
+ 1 = erlang:trace_pattern( {erlang,integer_to_list,1}, [], [global]),
+ 3 = erlang:trace_pattern(
+ {?MODULE,fn,'_'},
+ [{['$1','$2','$3'],[],
+ [{trace,'$1','$2','$3'},{message,Tag}]}],
+ [meta]),
+ %%
+ %% Expected: (no return_to for global call trace)
+ %%
+ Origin = {match_spec_SUITE,'-ms_trace3/1-fun-1-',2},
+ [{trace_ts,Controller,call,
+ {?MODULE,fn,[TraceeName,[all],
+ [call,return_to,send,'receive',
+ {tracer,Tracer}]]},
+ Tag},
+ {trace,Tracee,'receive',{Controller,Tag,started}},
+ {trace,Tracee,call,{?MODULE,f1,[f]}},
+ {trace,Tracee,call,{?MODULE,f2,[g,h]}},
+ {trace,Tracee,return_to,Origin},
+ {trace,Tracee,call,{erlang,integer_to_list,[2]}},
+ {trace,Tracee,call,{?MODULE,f2,[j,k]}},
+ {trace,Tracee,return_to,{?MODULE,f3,2}},
+ {trace,Tracee,send,{Tracee,Tag,stop_1},Controller},
+ {trace_ts,Controller,call,
+ {?MODULE,fn,[Tracee,[call,return_to],[]]},
+ Tag},
+ {trace_ts,Controller,call,
+ {?MODULE,fn,[Tracee,[all],[]]},
+ Tag}]
+ end),
ok.
-
-
-destructive_in_test_bif(doc) ->
- ["Test that destructive operations in test bif does not really happen"];
-destructive_in_test_bif(suite) -> [];
+%% Test that a dead tracer is removed using ms
+ms_trace_dead(_Config) ->
+ Self = self(),
+ TFun = fun F() -> receive M -> Self ! M, F() end end,
+ {Tracer, MRef} = spawn_monitor(TFun),
+ MetaTracer = spawn_link(TFun),
+ erlang:trace_pattern({?MODULE, f1, '_'},
+ [{'_',[],[{message, false},
+ {trace,[],
+ [call,{const,{tracer,Tracer}}]}]}],
+ [{meta, MetaTracer}]),
+ erlang:trace_pattern({?MODULE, f2, '_'}, []),
+ ?MODULE:f2(1,2),
+ ?MODULE:f1(1),
+ {tracer,Tracer} = erlang:trace_info(self(), tracer),
+ {flags,[call]} = erlang:trace_info(self(), flags),
+ ?MODULE:f2(2,3),
+ receive {trace, Self, call, {?MODULE, f2, _}} -> ok end,
+ exit(Tracer, stop),
+ receive {'DOWN',MRef,_,_,_} -> ok end,
+ ?MODULE:f1(2),
+ {tracer,[]} = erlang:trace_info(self(), tracer),
+ ?MODULE:f2(3,4),
+ TRef = erlang:trace_delivered(all),
+ receive {trace_delivered, _, TRef} -> ok end,
+ receive M -> ct:fail({unexpected, M}) after 10 -> ok end.
+
+%% Test that destructive operations in test bif does not really happen
destructive_in_test_bif(Config) when is_list(Config) ->
- ?line {ok,OldToken,_,_} = erlang:match_spec_test
+ {ok,OldToken,_,_} = erlang:match_spec_test
([],
[{'_',[],[{message,{get_seq_token}}]}],trace),
- ?line {ok,_,_,_} = erlang:match_spec_test
+ {ok,_,_,_} = erlang:match_spec_test
([],
[{'_',[],[{message,{set_seq_token, label, 1}}]}],
trace),
- ?line {ok,OldToken,_,_} = erlang:match_spec_test
+ {ok,OldToken,_,_} = erlang:match_spec_test
([],
[{'_',[],[{message,{get_seq_token}}]}],trace),
- ?line {ok, OldTCW,_,_} = erlang:match_spec_test
+ {ok, OldTCW,_,_} = erlang:match_spec_test
([],[{'_',[],[{message,{get_tcw}}]}],trace),
- ?line {ok,OldTCW,_,_} = erlang:match_spec_test
+ {ok,OldTCW,_,_} = erlang:match_spec_test
([],
[{'_',[],[{message,{set_tcw, OldTCW+1}}]}],
trace),
- ?line {ok, OldTCW,_,_} = erlang:match_spec_test
+ {ok, OldTCW,_,_} = erlang:match_spec_test
([],[{'_',[],[{message,{get_tcw}}]}],trace),
ok.
-boxed_and_small(doc) ->
- ["Test that the comparision between boxed and small does not crash emulator"];
-boxed_and_small(suite) -> [];
+%% Test that the comparision between boxed and small does not crash emulator
boxed_and_small(Config) when is_list(Config) ->
- ?line {ok, Node} = start_node(match_spec_suite_other),
- ?line ok = rpc:call(Node,?MODULE,do_boxed_and_small,[]),
- ?line stop_node(Node),
+ {ok, Node} = start_node(match_spec_suite_other),
+ ok = rpc:call(Node,?MODULE,do_boxed_and_small,[]),
+ stop_node(Node),
ok.
do_boxed_and_small() ->
@@ -739,13 +660,11 @@ do_boxed_and_small() ->
{ok, false, _, _} = erlang:match_spec_test({0,3},[{{make_ref(),'_'},[],['$_']}],table),
ok.
-faulty_seq_trace(doc) ->
- ["Test that faulty seq_trace_call does not crash emulator"];
-faulty_seq_trace(suite) -> [];
+%% Test that faulty seq_trace_call does not crash emulator
faulty_seq_trace(Config) when is_list(Config) ->
- ?line {ok, Node} = start_node(match_spec_suite_other),
- ?line ok = rpc:call(Node,?MODULE,do_faulty_seq_trace,[]),
- ?line stop_node(Node),
+ {ok, Node} = start_node(match_spec_suite_other),
+ ok = rpc:call(Node,?MODULE,do_faulty_seq_trace,[]),
+ stop_node(Node),
ok.
do_faulty_seq_trace() ->
@@ -757,63 +676,58 @@ errchk(Pat) ->
{'EXIT', {badarg, _}} ->
ok;
Other ->
- test_server:fail({noerror, Other})
+ ct:fail({noerror, Other})
end.
-unary_minus(suite) ->
- [];
-unary_minus(doc) ->
- ["Checks that unary minus works"];
+%% Checks that unary minus works
unary_minus(Config) when is_list(Config) ->
- ?line {ok,true,[],[]} = erlang:match_spec_test
+ {ok,true,[],[]} = erlang:match_spec_test
(5,
[{'$1',
[{'<',{'-','$1'},-4}],
[true]}],
table),
- ?line {ok,false,[],[]} = erlang:match_spec_test
+ {ok,false,[],[]} = erlang:match_spec_test
(5,
[{'$1',
[{'<',{'-','$1'},-6}],
[true]}],
table),
- ?line {ok,true,[],[]} = erlang:match_spec_test
+ {ok,true,[],[]} = erlang:match_spec_test
(5,
[{'$1',
[{'=:=',{'-','$1',2},3}],
[true]}],
table),
- ?line {ok,false,[],[]} = erlang:match_spec_test
+ {ok,false,[],[]} = erlang:match_spec_test
(hej,
[{'$1',
[{'=/=',{'-','$1'},0}],
[true]}],
table),
ok.
-unary_plus(suite) ->
- [];
-unary_plus(doc) ->
- ["Checks that unary plus works"];
+
+%% Checks that unary plus works
unary_plus(Config) when is_list(Config) ->
- ?line {ok,true,[],[]} = erlang:match_spec_test
+ {ok,true,[],[]} = erlang:match_spec_test
(5,
[{'$1',
[{'<',{'+','$1'},6}],
[true]}],
table),
- ?line {ok,false,[],[]} = erlang:match_spec_test
+ {ok,false,[],[]} = erlang:match_spec_test
(5,
[{'$1',
[{'<',{'+','$1'},4}],
[true]}],
table),
- ?line {ok,true,[],[]} = erlang:match_spec_test
+ {ok,true,[],[]} = erlang:match_spec_test
(5,
[{'$1',
[{'=:=',{'+','$1',2},7}],
[true]}],
table),
- ?line {ok,false,[],[]} = erlang:match_spec_test
+ {ok,false,[],[]} = erlang:match_spec_test
(hej,
[{'$1',
[{'=/=',{'+','$1'},0}],
@@ -824,53 +738,50 @@ unary_plus(Config) when is_list(Config) ->
-guard_exceptions(suite) ->
- [];
-guard_exceptions(doc) ->
- ["Checks that exceptions in guards are handled correctly"];
+%% Checks that exceptions in guards are handled correctly
guard_exceptions(Config) when is_list(Config) ->
- ?line {ok,false,[],[]} = erlang:match_spec_test
+ {ok,false,[],[]} = erlang:match_spec_test
(5,
[{'$1',
[{'or',{is_integer,'$1'},{'or','$1','$1'}}],
[true]}],
table),
- ?line {ok,true,[],[]} = erlang:match_spec_test
+ {ok,true,[],[]} = erlang:match_spec_test
(5,
[{'$1',
[{'orelse',{is_integer,'$1'},
{'or','$1','$1'}}],
[true]}],
table),
- ?line {ok,false,[],[]} = erlang:match_spec_test
+ {ok,false,[],[]} = erlang:match_spec_test
(5,
[{'$1',
[{'orelse',{'or','$1',true},
{is_integer,'$1'}}],
[true]}],
table),
- ?line {ok,false,[],[]} = erlang:match_spec_test
+ {ok,false,[],[]} = erlang:match_spec_test
(5,
[{'$1',
[{'or',{is_integer,'$1'},
{'orelse','$1',true}}],
[true]}],
table),
- ?line {ok,true,[],[]} = erlang:match_spec_test
+ {ok,true,[],[]} = erlang:match_spec_test
(5,
[{'$1',
[{'or',{is_integer,'$1'},
{'orelse',true,'$1'}}],
[true]}],
table),
- ?line {ok,true,[],[]} = erlang:match_spec_test
+ {ok,true,[],[]} = erlang:match_spec_test
(5,
[{'$1',
[{'or',{is_integer,'$1'},
{'andalso',false,'$1'}}],
[true]}],
table),
- ?line {ok,false,[],[]} = erlang:match_spec_test
+ {ok,false,[],[]} = erlang:match_spec_test
(5,
[{'$1',
[{'or',{is_integer,'$1'},
@@ -878,7 +789,7 @@ guard_exceptions(Config) when is_list(Config) ->
[true]}],
table),
- ?line {ok,false,[],[]} = erlang:match_spec_test
+ {ok,false,[],[]} = erlang:match_spec_test
(5,
[{'$1',
[{'or',{is_integer,'$1'},
@@ -888,19 +799,16 @@ guard_exceptions(Config) when is_list(Config) ->
ok.
-fpe(suite) ->
- [];
-fpe(doc) ->
- ["Checks floating point exceptions in match-specs"];
+%% Checks floating point exceptions in match-specs
fpe(Config) when is_list(Config) ->
MS = [{{'$1'},[],[{'/','$1',0}]}],
case catch (['EXIT','EXIT'] =
ets:match_spec_run([{1},{2}],ets:match_spec_compile(MS))) of
- {'EXIT',_} -> test_server:fail({error,
- "Floating point exceptions faulty"});
+ {'EXIT',_} -> ct:fail({error, "Floating point exceptions faulty"});
_ -> ok
end.
+%% Test maps in match-specs
maps(Config) when is_list(Config) ->
{ok,#{},[],[]} = erlang:match_spec_test(#{}, [{'_',[],['$_']}], table),
{ok,#{},[],[]} = erlang:match_spec_test(#{}, [{#{},[],['$_']}], table),
@@ -980,11 +888,11 @@ moving_labels(Config) when is_list(Config) ->
%% point at their correct target.
%%
Ms = [{{'$1','$2'},[],[{{ok,{'andalso','$1','$2'},[1,2,3]}}]}],
- ?line {ok,{ok,false,[1,2,3]},[],[]} =
+ {ok,{ok,false,[1,2,3]},[],[]} =
erlang:match_spec_test({true,false}, Ms, table),
Ms2 = [{{'$1','$2'},[],[{{ok,{'orelse','$1','$2'},[1,2,3]}}]}],
- ?line {ok,{ok,true,[1,2,3]},[],[]} =
+ {ok,{ok,true,[1,2,3]},[],[]} =
erlang:match_spec_test({true,false}, Ms2, table),
ok.
@@ -995,13 +903,13 @@ tr(Fun, MFA, Pat, Expected) ->
tr(Fun, MFA, TraceFlags, Pat, PatFlags, Expected0) ->
tr(Fun,
fun(P) ->
- erlang:trace(P, true, TraceFlags),
- erlang:trace_pattern(MFA, Pat, PatFlags),
- lists:map(
- fun(X) when is_function(X,1) -> X;
- (X) -> list_to_tuple([trace, P | tuple_to_list(X)])
- end,
- Expected0)
+ erlang:trace(P, true, TraceFlags),
+ erlang:trace_pattern(MFA, Pat, PatFlags),
+ lists:map(
+ fun(X) when is_function(X,1) -> X;
+ (X) -> list_to_tuple([trace, P | tuple_to_list(X)])
+ end,
+ Expected0)
end).
tr(RunFun, ControlFun) ->
@@ -1016,15 +924,19 @@ collect(P, TMs) ->
collect([]) ->
receive
M ->
- ?t:format("Got unexpected: ~p~n", [M]),
+ io:format("Got unexpected: ~p~n", [M]),
flush({got_unexpected,M})
after 17 ->
ok
end;
collect([TM | TMs]) ->
- ?t:format( "Expecting: ~p~n", [TM]),
+ io:format( "Expecting: ~p~n", [TM]),
receive
- M0 ->
+ %% We only look at trace messages with the same tracee
+ %% as the message we are looking for. This because
+ %% the order of trace messages is only guaranteed from
+ %% within a single process.
+ M0 when element(2, M0) =:= element(2, TM); is_function(TM, 1) ->
M = case element(1, M0) of
trace_ts ->
list_to_tuple(lists:reverse(
@@ -1035,32 +947,34 @@ collect([TM | TMs]) ->
true ->
case (catch TM(M)) of
true ->
- ?t:format("Got: ~p~n", [M]),
+ io:format("Got: ~p~n", [M]),
collect(TMs);
_ ->
- ?t:format("Got unexpected: ~p~n", [M]),
+ io:format("Got unexpected: ~p~n", [M]),
flush({got_unexpected,M})
end;
false ->
case M of
TM ->
- ?t:format("Got: ~p~n", [M]),
+ io:format("Got: ~p~n", [M]),
collect(TMs);
_ ->
- ?t:format("Got unexpected: ~p~n", [M]),
+ io:format("Got unexpected: ~p~n", [M]),
flush({got_unexpected,M})
end
end
+ after 15000 ->
+ flush(timeout)
end.
flush(Reason) ->
receive
- M ->
- ?t:format("In queue: ~p~n", [M]),
- flush(Reason)
+ M ->
+ io:format("In queue: ~p~n", [M]),
+ flush(Reason)
after 17 ->
- ?t:fail(Reason)
+ ct:fail(Reason)
end.
start_collect(P) ->
@@ -1071,33 +985,33 @@ stop_collect(P) ->
stop_collect(P, Order) ->
P ! {Order, self()},
receive
- {gone, P} ->
- ok
+ {gone, P} ->
+ ok
end.
runner(Collector, Fun) ->
receive
- {go, Collector} ->
- go
+ {go, Collector} ->
+ go
end,
Fun(),
receive
- {done, Collector} ->
- Collector ! {gone, self()}
+ {done, Collector} ->
+ Collector ! {gone, self()}
end.
loop_runner(Collector, Fun, Laps) ->
receive
- {go, Collector} ->
- go
+ {go, Collector} ->
+ go
end,
loop_runner_cont(Collector, Fun, 0, Laps).
loop_runner_cont(Collector, _Fun, Laps, Laps) ->
receive
- {done, Collector} -> ok;
- {abort, Collector} -> ok
+ {done, Collector} -> ok;
+ {abort, Collector} -> ok
end,
io:format("loop_runner ~p exit after ~p laps\n", [self(), Laps]),
Collector ! {gone, self()};
@@ -1105,11 +1019,11 @@ loop_runner_cont(Collector, _Fun, Laps, Laps) ->
loop_runner_cont(Collector, Fun, N, Laps) ->
Fun(),
receive
- {abort, Collector} ->
- io:format("loop_runner ~p aborted after ~p of ~p laps\n", [self(), N+1, Laps]),
- Collector ! {gone, self()}
+ {abort, Collector} ->
+ io:format("loop_runner ~p aborted after ~p of ~p laps\n", [self(), N+1, Laps]),
+ Collector ! {gone, self()}
after 0 ->
- loop_runner_cont(Collector, Fun, N+1, Laps)
+ loop_runner_cont(Collector, Fun, N+1, Laps)
end.
@@ -1141,7 +1055,7 @@ start_node(Name) ->
Pa = filename:dirname(code:which(?MODULE)),
Cookie = atom_to_list(erlang:get_cookie()),
test_server:start_node(Name, slave,
- [{args, "-setcookie " ++ Cookie ++" -pa " ++ Pa}]).
+ [{args, "-setcookie " ++ Cookie ++" -pa " ++ Pa}]).
stop_node(Node) ->
test_server:stop_node(Node).
diff --git a/erts/emulator/test/message_queue_data_SUITE.erl b/erts/emulator/test/message_queue_data_SUITE.erl
new file mode 100644
index 0000000000..e084b9482d
--- /dev/null
+++ b/erts/emulator/test/message_queue_data_SUITE.erl
@@ -0,0 +1,209 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2014-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(message_queue_data_SUITE).
+
+-export([all/0, suite/0]).
+-export([basic/1, process_info_messages/1, total_heap_size/1]).
+
+-export([basic_test/1]).
+
+-include_lib("common_test/include/ct.hrl").
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 2}}].
+
+all() ->
+ [basic, process_info_messages, total_heap_size].
+
+%%
+%%
+%% Test cases
+%%
+%%
+
+basic(Config) when is_list(Config) ->
+
+ basic_test(erlang:system_info(message_queue_data)),
+
+ {ok, Node1} = start_node(Config, "+hmqd off_heap"),
+ ok = rpc:call(Node1, ?MODULE, basic_test, [off_heap]),
+ stop_node(Node1),
+
+ {ok, Node2} = start_node(Config, "+hmqd on_heap"),
+ ok = rpc:call(Node2, ?MODULE, basic_test, [on_heap]),
+ stop_node(Node2),
+
+ ok.
+
+is_valid_mqd_value(off_heap) ->
+ true;
+is_valid_mqd_value(on_heap) ->
+ true;
+is_valid_mqd_value(_) ->
+ false.
+
+
+basic_test(Default) ->
+
+ Default = erlang:system_info(message_queue_data),
+ true = is_valid_mqd_value(Default),
+
+ {message_queue_data, Default} = process_info(self(), message_queue_data),
+ Default = process_flag(message_queue_data, off_heap),
+ {message_queue_data, off_heap} = process_info(self(), message_queue_data),
+ off_heap = process_flag(message_queue_data, on_heap),
+ {message_queue_data, on_heap} = process_info(self(), message_queue_data),
+ {'EXIT', _} = (catch process_flag(message_queue_data, blupp)),
+
+ P1 = spawn_opt(fun () -> receive after infinity -> ok end end,
+ [link]),
+ {message_queue_data, Default} = process_info(P1, message_queue_data),
+ unlink(P1),
+ exit(P1, bye),
+
+ P2 = spawn_opt(fun () -> receive after infinity -> ok end end,
+ [link, {message_queue_data, off_heap}]),
+ {message_queue_data, off_heap} = process_info(P2, message_queue_data),
+ unlink(P2),
+ exit(P2, bye),
+
+ P3 = spawn_opt(fun () -> receive after infinity -> ok end end,
+ [link, {message_queue_data, on_heap}]),
+ {message_queue_data, on_heap} = process_info(P3, message_queue_data),
+ unlink(P3),
+ exit(P3, bye),
+
+ {'EXIT', _} = (catch spawn_opt(fun () -> receive after infinity -> ok end end,
+ [link, {message_queue_data, blapp}])),
+
+ ok.
+
+process_info_messages(Config) when is_list(Config) ->
+ Tester = self(),
+ P1 = spawn_opt(fun () ->
+ receive after 500 -> ok end,
+ on_heap = process_flag(message_queue_data, off_heap),
+ Tester ! first,
+ receive after 500 -> ok end,
+ off_heap = process_flag(message_queue_data, on_heap),
+ Tester ! second,
+ receive after 500 -> ok end,
+ on_heap = process_flag(message_queue_data, off_heap),
+ Tester ! third,
+
+ receive after infinity -> ok end
+ end,
+ [link, {message_queue_data, on_heap}]),
+
+ P1 ! "A",
+ receive first -> ok end,
+ P1 ! "B",
+ receive second -> ok end,
+ P1 ! "C",
+ receive third -> ok end,
+ P1 ! "D",
+
+ {messages, ["A", "B", "C", "D"]} = process_info(P1, messages),
+
+ P2 = spawn_opt(fun () ->
+ receive after 500 -> ok end,
+ on_heap = process_flag(message_queue_data, off_heap),
+ Tester ! first,
+ receive after 500 -> ok end,
+ off_heap = process_flag(message_queue_data, on_heap),
+ Tester ! second,
+ receive after 500 -> ok end,
+ on_heap = process_flag(message_queue_data, off_heap),
+ Tester ! third,
+ receive after 500 -> ok end,
+
+ Tester ! process_info(self(), messages),
+
+ receive M1 -> M1 = "A" end,
+ receive M2 -> M2 = "B" end,
+ receive M3 -> M3 = "C" end,
+ receive M4 -> M4 = "D" end,
+
+ Tester ! self()
+ end,
+ [link, {message_queue_data, on_heap}]),
+
+ P2 ! "A",
+ receive first -> ok end,
+ P2 ! "B",
+ receive second -> ok end,
+ P2 ! "C",
+ receive third -> ok end,
+ P2 ! "D",
+
+ receive
+ Msg ->
+ {messages, ["A", "B", "C", "D"]} = Msg
+ end,
+
+ receive P2 -> ok end,
+
+ ok.
+
+total_heap_size(_Config) ->
+
+ Fun = fun F() -> receive Pid when is_pid(Pid) -> Pid ! ok,F() end end,
+
+ %% Test that on_heap messages grow the heap even if they are not received
+ OnPid = spawn_opt(Fun, [{message_queue_data, on_heap}]),
+ {total_heap_size, OnSize} = erlang:process_info(OnPid, total_heap_size),
+ [OnPid ! lists:duplicate(N,N) || N <- lists:seq(1,100)],
+ OnPid ! self(), receive ok -> ok end,
+ {total_heap_size, OnSizeAfter} = erlang:process_info(OnPid, total_heap_size),
+ ct:log("OnSize = ~p, OnSizeAfter = ~p",[OnSize, OnSizeAfter]),
+ true = OnSize < OnSizeAfter,
+
+ %% Test that off_heap messages do not grow the heap if they are not received
+ OffPid = spawn_opt(Fun, [{message_queue_data, off_heap}]),
+ {total_heap_size, OffSize} = erlang:process_info(OffPid, total_heap_size),
+ [OffPid ! lists:duplicate(N,N) || N <- lists:seq(1,100)],
+ OffPid ! self(), receive ok -> ok end,
+ {total_heap_size, OffSizeAfter} = erlang:process_info(OffPid, total_heap_size),
+ ct:log("OffSize = ~p, OffSizeAfter = ~p",[OffSize, OffSizeAfter]),
+ true = OffSize == OffSizeAfter.
+
+%%
+%%
+%% helpers
+%%
+%%
+
+start_node(Config) ->
+ start_node(Config, []).
+start_node(Config, Opts) when is_list(Config), is_list(Opts) ->
+ Pa = filename:dirname(code:which(?MODULE)),
+ Name = list_to_atom(atom_to_list(?MODULE)
+ ++ "-"
+ ++ atom_to_list(proplists:get_value(testcase, Config))
+ ++ "-"
+ ++ integer_to_list(erlang:system_time(second))
+ ++ "-"
+ ++ integer_to_list(erlang:unique_integer([positive]))),
+ test_server:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]).
+
+stop_node(Node) ->
+ test_server:stop_node(Node).
diff --git a/erts/emulator/test/module_info_SUITE.erl b/erts/emulator/test/module_info_SUITE.erl
index 7c2101ca05..ba9b564fdc 100644
--- a/erts/emulator/test/module_info_SUITE.erl
+++ b/erts/emulator/test/module_info_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,11 +20,9 @@
-module(module_info_SUITE).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2,end_per_testcase/2,
+-export([all/0, suite/0,
exports/1,functions/1,deleted/1,native/1,info/1]).
%%-compile(native).
@@ -32,46 +30,23 @@
%% Helper.
-export([native_proj/1,native_filter/1]).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 3}}].
all() ->
modules().
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
modules() ->
[exports, functions, deleted, native, info].
-init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- Dog = ?t:timetrap(?t:minutes(3)),
- [{watchdog,Dog}|Config].
-
-end_per_testcase(_Func, Config) ->
- Dog = ?config(watchdog, Config),
- ?t:timetrap_cancel(Dog).
-
%% Should return all functions exported from this module. (local)
all_exported() ->
All = add_arity(modules()),
- lists:sort([{all,0},{suite,0},{groups,0},
- {init_per_suite,1},{end_per_suite,1},
- {init_per_group,2},{end_per_group,2},
- {init_per_testcase,2},{end_per_testcase,2},
- {module_info,0},{module_info,1},{native_proj,1},
- {native_filter,1}|All]).
+ lists:sort([{all,0},{suite,0},
+ {module_info,0},{module_info,1},
+ {native_proj,1},
+ {native_filter,1}|All]).
%% Should return all functions in this module. (local)
all_functions() ->
@@ -95,7 +70,7 @@ functions(Config) when is_list(Config) ->
%% Test that deleted modules cause badarg
deleted(Config) when is_list(Config) ->
- Data = ?config(data_dir, Config),
+ Data = proplists:get_value(data_dir, Config),
File = filename:join(Data, "module_info_test"),
{ok,module_info_test,Code} = compile:file(File, [binary]),
{module,module_info_test} = erlang:load_module(module_info_test, Code),
diff --git a/erts/emulator/test/monitor_SUITE.erl b/erts/emulator/test/monitor_SUITE.erl
index 4db17969c0..827ed817cc 100644
--- a/erts/emulator/test/monitor_SUITE.erl
+++ b/erts/emulator/test/monitor_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,21 +20,21 @@
-module(monitor_SUITE).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
+-include_lib("eunit/include/eunit.hrl").
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- case_1/1, case_1a/1, case_2/1, case_2a/1, mon_e_1/1, demon_e_1/1, demon_1/1,
- demon_2/1, demon_3/1, demonitor_flush/1,
- local_remove_monitor/1, remote_remove_monitor/1, mon_1/1, mon_2/1,
- large_exit/1, list_cleanup/1, mixer/1, named_down/1, otp_5827/1,
- monitor_time_offset/1]).
-
--export([init_per_testcase/2, end_per_testcase/2]).
+-export([all/0, suite/0, groups/0,
+ case_1/1, case_1a/1, case_2/1, case_2a/1, mon_e_1/1, demon_e_1/1, demon_1/1,
+ demon_2/1, demon_3/1, demonitor_flush/1,
+ local_remove_monitor/1, remote_remove_monitor/1, mon_1/1, mon_2/1,
+ large_exit/1, list_cleanup/1, mixer/1, named_down/1, otp_5827/1,
+ monitor_time_offset/1]).
-export([y2/1, g/1, g0/0, g1/0, large_exit_sub/1]).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 15}}].
all() ->
[case_1, case_1a, case_2, case_2a, mon_e_1, demon_e_1,
@@ -47,132 +47,103 @@ groups() ->
[{remove_monitor, [],
[local_remove_monitor, remote_remove_monitor]}].
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- Dog=?t:timetrap(?t:minutes(15)),
- [{watchdog, Dog},{testcase, Func}|Config].
-
-end_per_testcase(_Func, Config) ->
- Dog=?config(watchdog, Config),
- ?t:timetrap_cancel(Dog).
-
-case_1(doc) ->
- "A monitors B, B kills A and then exits (yielded core dump)";
-case_1(suite) -> [];
+%% A monitors B, B kills A and then exits (yielded core dump)
case_1(Config) when is_list(Config) ->
- ?line process_flag(trap_exit, true),
- ?line spawn_link(?MODULE, g0, []),
- ?line receive _ -> ok end,
+ process_flag(trap_exit, true),
+ spawn_link(?MODULE, g0, []),
+ receive _ -> ok end,
ok.
-case_1a(doc) ->
- "A monitors B, B kills A and then exits (yielded core dump)";
+%% A monitors B, B kills A and then exits (yielded core dump)
case_1a(Config) when is_list(Config) ->
- ?line process_flag(trap_exit, true),
- ?line spawn_link(?MODULE, g1, []),
- ?line receive _ -> ok end,
+ process_flag(trap_exit, true),
+ spawn_link(?MODULE, g1, []),
+ receive _ -> ok end,
ok.
g0() ->
- ?line B = spawn(?MODULE, g, [self()]),
- ?line erlang:monitor(process, B),
- ?line B ! ok,
- ?line receive ok -> ok end,
+ B = spawn(?MODULE, g, [self()]),
+ erlang:monitor(process, B),
+ B ! ok,
+ receive ok -> ok end,
ok.
g1() ->
- ?line {B,_} = spawn_monitor(?MODULE, g, [self()]),
- ?line B ! ok,
- ?line receive ok -> ok end,
+ {B,_} = spawn_monitor(?MODULE, g, [self()]),
+ B ! ok,
+ receive ok -> ok end,
ok.
g(Parent) ->
- ?line receive ok -> ok end,
- ?line exit(Parent, foo),
- ?line ok.
+ receive ok -> ok end,
+ exit(Parent, foo),
+ ok.
-case_2(doc) ->
- "A monitors B, B demonitors A (yielded core dump)";
+%% A monitors B, B demonitors A (yielded core dump)
case_2(Config) when is_list(Config) ->
- ?line B = spawn(?MODULE, y2, [self()]),
- ?line R = erlang:monitor(process, B),
- ?line B ! R,
- ?line receive
- {'EXIT', _} -> ok;
- Other ->
- test_server:fail({rec, Other})
- end,
- ?line expect_down(R, B, normal),
+ B = spawn(?MODULE, y2, [self()]),
+ R = erlang:monitor(process, B),
+ B ! R,
+ receive
+ {'EXIT', _} -> ok;
+ Other ->
+ ct:fail({rec, Other})
+ end,
+ expect_down(R, B, normal),
ok.
-case_2a(doc) ->
- "A monitors B, B demonitors A (yielded core dump)";
+%% A monitors B, B demonitors A (yielded core dump)
case_2a(Config) when is_list(Config) ->
- ?line {B,R} = spawn_monitor(?MODULE, y2, [self()]),
- ?line B ! R,
- ?line receive
- {'EXIT', _} -> ok;
- Other ->
- test_server:fail({rec, Other})
- end,
- ?line expect_down(R, B, normal),
+ {B,R} = spawn_monitor(?MODULE, y2, [self()]),
+ B ! R,
+ receive
+ {'EXIT', _} -> ok;
+ Other ->
+ ct:fail({rec, Other})
+ end,
+ expect_down(R, B, normal),
ok.
y2(Parent) ->
- ?line R = receive T -> T end,
- ?line Parent ! (catch erlang:demonitor(R)),
+ R = receive T -> T end,
+ Parent ! (catch erlang:demonitor(R)),
ok.
expect_down(Ref, P) ->
receive
- {'DOWN', Ref, process, P, Reason} ->
- Reason;
- Other ->
- test_server:fail({rec, Other})
+ {'DOWN', Ref, process, P, Reason} ->
+ Reason;
+ Other ->
+ ct:fail({rec, Other})
end.
expect_down(Ref, P, Reason) ->
receive
- {'DOWN', Ref, process, P, Reason} ->
- ok;
- Other ->
- test_server:fail({rec, Other})
+ {'DOWN', Ref, process, P, Reason} ->
+ ok;
+ Other ->
+ ct:fail({rec, Other})
end.
expect_no_msg() ->
receive
- Msg ->
- test_server:fail({msg, Msg})
+ Msg ->
+ ct:fail({msg, Msg})
after 0 ->
- ok
+ ok
end.
%%% Error cases for monitor/2
-mon_e_1(doc) ->
- "Error cases for monitor/2";
-mon_e_1(suite) -> [];
mon_e_1(Config) when is_list(Config) ->
- ?line {ok, N} = test_server:start_node(hej, slave, []),
- ?line mon_error(plutt, self()),
- ?line mon_error(process, [bingo]),
- ?line mon_error(process, {rex, N, junk}),
- ?line mon_error(process, 1),
+ {ok, N} = test_server:start_node(hej, slave, []),
+ mon_error(plutt, self()),
+ mon_error(process, [bingo]),
+ mon_error(process, {rex, N, junk}),
+ mon_error(process, 1),
- ?line true = test_server:stop_node(N),
+ true = test_server:stop_node(N),
ok.
%%% We would also like to have a test case that tries to monitor something
@@ -185,155 +156,142 @@ mon_e_1(Config) when is_list(Config) ->
mon_error(Type, Item) ->
case catch erlang:monitor(Type, Item) of
- {'EXIT', _} ->
- ok;
- Other ->
- test_server:fail({err, Other})
+ {'EXIT', _} ->
+ ok;
+ Other ->
+ ct:fail({err, Other})
end.
%%% Error cases for demonitor/1
-demon_e_1(doc) ->
- "Error cases for demonitor/1";
-demon_e_1(suite) -> [];
demon_e_1(Config) when is_list(Config) ->
- ?line {ok, N} = test_server:start_node(hej, slave, []),
- ?line demon_error(plutt, badarg),
- ?line demon_error(1, badarg),
+ {ok, N} = test_server:start_node(hej, slave, []),
+ demon_error(plutt, badarg),
+ demon_error(1, badarg),
%% Demonitor with ref created at other node
- ?line R1 = rpc:call(N, erlang, make_ref, []),
- ?line demon_error(R1, badarg),
+ R1 = rpc:call(N, erlang, make_ref, []),
+ demon_error(R1, badarg),
%% Demonitor with ref created at wrong monitor link end
- ?line P0 = self(),
- ?line P2 = spawn(
- fun() ->
- P0 ! {self(), ref, erlang:monitor(process,P0)},
- receive {P0, stop} -> ok end
- end ),
- ?line receive
- {P2, ref, R2} ->
- ?line demon_error(R2, badarg),
- ?line P2 ! {self(), stop};
- Other2 ->
- test_server:fail({rec, Other2})
- end,
-
- ?line true = test_server:stop_node(N),
+ P0 = self(),
+ P2 = spawn(
+ fun() ->
+ P0 ! {self(), ref, erlang:monitor(process,P0)},
+ receive {P0, stop} -> ok end
+ end ),
+ receive
+ {P2, ref, R2} ->
+ demon_error(R2, badarg),
+ P2 ! {self(), stop};
+ Other2 ->
+ ct:fail({rec, Other2})
+ end,
+
+ true = test_server:stop_node(N),
ok.
demon_error(Ref, Reason) ->
case catch erlang:demonitor(Ref) of
- {'EXIT', {Reason, _}} ->
- ok;
- Other ->
- test_server:fail({err, Other})
+ {'EXIT', {Reason, _}} ->
+ ok;
+ Other ->
+ ct:fail({err, Other})
end.
%%% No-op cases for demonitor/1
-demon_1(doc) ->
- "demonitor/1";
-demon_1(suite) -> [];
demon_1(Config) when is_list(Config) ->
- ?line true = erlang:demonitor(make_ref()),
+ true = erlang:demonitor(make_ref()),
ok.
%%% Cases for demonitor/1
-demon_2(doc) ->
- "Cases for demonitor/1";
-demon_2(suite) -> [];
demon_2(Config) when is_list(Config) ->
- ?line R1 = erlang:monitor(process, self()),
- ?line true = erlang:demonitor(R1),
+ R1 = erlang:monitor(process, self()),
+ true = erlang:demonitor(R1),
%% Extra demonitor
- ?line true = erlang:demonitor(R1),
- ?line expect_no_msg(),
+ true = erlang:demonitor(R1),
+ expect_no_msg(),
%% Normal 'DOWN'
- ?line P2 = spawn(timer, sleep, [1]),
- ?line R2 = erlang:monitor(process, P2),
- ?line case expect_down(R2, P2) of
- normal -> ?line ok;
- noproc -> ?line ok;
- BadReason -> ?line ?t:fail({bad_reason, BadReason})
- end,
-
-%% OTP-5772
-% %% 'DOWN' before demonitor
-% ?line P3 = spawn(timer, sleep, [100000]),
-% ?line R3 = erlang:monitor(process, P3),
-% ?line exit(P3, frop),
-% ?line erlang:demonitor(R3),
-% ?line expect_down(R3, P3, frop),
+ P2 = spawn(timer, sleep, [1]),
+ R2 = erlang:monitor(process, P2),
+ case expect_down(R2, P2) of
+ normal -> ok;
+ noproc -> ok;
+ BadReason -> ct:fail({bad_reason, BadReason})
+ end,
+
+ %% OTP-5772
+ % %% 'DOWN' before demonitor
+ % P3 = spawn(timer, sleep, [100000]),
+ % R3 = erlang:monitor(process, P3),
+ % exit(P3, frop),
+ % erlang:demonitor(R3),
+ % expect_down(R3, P3, frop),
%% Demonitor before 'DOWN'
- ?line P4 = spawn(timer, sleep, [100000]),
- ?line R4 = erlang:monitor(process, P4),
- ?line erlang:demonitor(R4),
- ?line exit(P4, frop),
- ?line expect_no_msg(),
+ P4 = spawn(timer, sleep, [100000]),
+ R4 = erlang:monitor(process, P4),
+ erlang:demonitor(R4),
+ exit(P4, frop),
+ expect_no_msg(),
ok.
-demon_3(doc) ->
- "Distributed case for demonitor/1 (OTP-3499)";
-demon_3(suite) -> [];
+%% Distributed case for demonitor/1 (OTP-3499)
demon_3(Config) when is_list(Config) ->
- ?line {ok, N} = test_server:start_node(hej, slave, []),
+ {ok, N} = test_server:start_node(hej, slave, []),
%% 'DOWN' before demonitor
- ?line P2 = spawn(N, timer, sleep, [100000]),
- ?line R2 = erlang:monitor(process, P2),
- ?line true = test_server:stop_node(N),
- ?line true = erlang:demonitor(R2),
- ?line expect_down(R2, P2, noconnection),
+ P2 = spawn(N, timer, sleep, [100000]),
+ R2 = erlang:monitor(process, P2),
+ true = test_server:stop_node(N),
+ true = erlang:demonitor(R2),
+ expect_down(R2, P2, noconnection),
- ?line {ok, N2} = test_server:start_node(hej, slave, []),
+ {ok, N2} = test_server:start_node(hej, slave, []),
%% Demonitor before 'DOWN'
- ?line P3 = spawn(N2, timer, sleep, [100000]),
- ?line R3 = erlang:monitor(process, P3),
- ?line true = erlang:demonitor(R3),
- ?line true = test_server:stop_node(N2),
- ?line expect_no_msg(),
+ P3 = spawn(N2, timer, sleep, [100000]),
+ R3 = erlang:monitor(process, P3),
+ true = erlang:demonitor(R3),
+ true = test_server:stop_node(N2),
+ expect_no_msg(),
ok.
-demonitor_flush(suite) -> [];
-demonitor_flush(doc) -> [];
demonitor_flush(Config) when is_list(Config) ->
- ?line {'EXIT', {badarg, _}} = (catch erlang:demonitor(make_ref(), flush)),
- ?line {'EXIT', {badarg, _}} = (catch erlang:demonitor(make_ref(), [flus])),
- ?line {'EXIT', {badarg, _}} = (catch erlang:demonitor(x, [flush])),
- ?line {ok, N} = test_server:start_node(demonitor_flush, slave, []),
- ?line ok = demonitor_flush_test(N),
- ?line true = test_server:stop_node(N),
- ?line ok = demonitor_flush_test(node()).
-
+ {'EXIT', {badarg, _}} = (catch erlang:demonitor(make_ref(), flush)),
+ {'EXIT', {badarg, _}} = (catch erlang:demonitor(make_ref(), [flus])),
+ {'EXIT', {badarg, _}} = (catch erlang:demonitor(x, [flush])),
+ {ok, N} = test_server:start_node(demonitor_flush, slave, []),
+ ok = demonitor_flush_test(N),
+ true = test_server:stop_node(N),
+ ok = demonitor_flush_test(node()).
+
demonitor_flush_test(Node) ->
- ?line P = spawn(Node, timer, sleep, [100000]),
- ?line M1 = erlang:monitor(process, P),
- ?line M2 = erlang:monitor(process, P),
- ?line M3 = erlang:monitor(process, P),
- ?line M4 = erlang:monitor(process, P),
- ?line true = erlang:demonitor(M1, [flush, flush]),
- ?line exit(P, bang),
- ?line receive {'DOWN', M2, process, P, bang} -> ok end,
- ?line receive after 100 -> ok end,
- ?line true = erlang:demonitor(M3, [flush]),
- ?line true = erlang:demonitor(M4, []),
- ?line receive {'DOWN', M4, process, P, bang} -> ok end,
- ?line receive
- {'DOWN', M, _, _, _} =DM when M == M1,
- M == M3 ->
- ?line ?t:fail({unexpected_down_message, DM})
- after 100 ->
- ?line ok
- end.
+ P = spawn(Node, timer, sleep, [100000]),
+ M1 = erlang:monitor(process, P),
+ M2 = erlang:monitor(process, P),
+ M3 = erlang:monitor(process, P),
+ M4 = erlang:monitor(process, P),
+ true = erlang:demonitor(M1, [flush, flush]),
+ exit(P, bang),
+ receive {'DOWN', M2, process, P, bang} -> ok end,
+ receive after 100 -> ok end,
+ true = erlang:demonitor(M3, [flush]),
+ true = erlang:demonitor(M4, []),
+ receive {'DOWN', M4, process, P, bang} -> ok end,
+ receive
+ {'DOWN', M, _, _, _} =DM when M == M1,
+ M == M3 ->
+ ct:fail({unexpected_down_message, DM})
+ after 100 ->
+ ok
+ end.
-define(RM_MON_GROUPS, 100).
-define(RM_MON_GPROCS, 100).
@@ -341,33 +299,33 @@ demonitor_flush_test(Node) ->
local_remove_monitor(Config) when is_list(Config) ->
Gs = generate(fun () -> start_remove_monitor_group(node()) end,
- ?RM_MON_GROUPS),
+ ?RM_MON_GROUPS),
{True, False} = lists:foldl(fun (G, {T, F}) ->
- receive
- {rm_mon_res, G, {GT, GF}} ->
- {T+GT, F+GF}
- end
- end,
- {0, 0},
- Gs),
+ receive
+ {rm_mon_res, G, {GT, GF}} ->
+ {T+GT, F+GF}
+ end
+ end,
+ {0, 0},
+ Gs),
erlang:display({local_remove_monitor, True, False}),
{comment,
"True = "++integer_to_list(True)++"; False = "++integer_to_list(False)}.
-
+
remote_remove_monitor(Config) when is_list(Config) ->
- ?line {ok, N} = test_server:start_node(demonitor_flush, slave, []),
+ {ok, N} = test_server:start_node(demonitor_flush, slave, []),
Gs = generate(fun () -> start_remove_monitor_group(node()) end,
- ?RM_MON_GROUPS),
+ ?RM_MON_GROUPS),
{True, False} = lists:foldl(fun (G, {T, F}) ->
- receive
- {rm_mon_res, G, {GT, GF}} ->
- {T+GT, F+GF}
- end
- end,
- {0, 0},
- Gs),
+ receive
+ {rm_mon_res, G, {GT, GF}} ->
+ {T+GT, F+GF}
+ end
+ end,
+ {0, 0},
+ Gs),
erlang:display({remote_remove_monitor, True, False}),
- ?line true = test_server:stop_node(N),
+ true = test_server:stop_node(N),
{comment,
"True = "++integer_to_list(True)++"; False = "++integer_to_list(False)}.
@@ -375,161 +333,153 @@ start_remove_monitor_group(Node) ->
Master = self(),
spawn_link(
fun () ->
- Ms = generate(fun () ->
- P = spawn(Node, fun () -> ok end),
- erlang:monitor(process, P)
- end, ?RM_MON_GPROCS),
- Res = lists:foldl(fun (M, {T, F}) ->
- case erlang:demonitor(M, [info]) of
- true ->
- receive
- {'DOWN', M, _, _, _} ->
- exit(down_msg_found)
- after 0 ->
- ok
- end,
- {T+1, F};
- false ->
- receive
- {'DOWN', M, _, _, _} ->
- ok
- after 0 ->
- exit(no_down_msg_found)
- end,
- {T, F+1}
- end
- end,
- {0,0},
- Ms),
- Master ! {rm_mon_res, self(), Res}
+ Ms = generate(fun () ->
+ P = spawn(Node, fun () -> ok end),
+ erlang:monitor(process, P)
+ end, ?RM_MON_GPROCS),
+ Res = lists:foldl(fun (M, {T, F}) ->
+ case erlang:demonitor(M, [info]) of
+ true ->
+ receive
+ {'DOWN', M, _, _, _} ->
+ exit(down_msg_found)
+ after 0 ->
+ ok
+ end,
+ {T+1, F};
+ false ->
+ receive
+ {'DOWN', M, _, _, _} ->
+ ok
+ after 0 ->
+ exit(no_down_msg_found)
+ end,
+ {T, F+1}
+ end
+ end,
+ {0,0},
+ Ms),
+ Master ! {rm_mon_res, self(), Res}
end).
-
-
+
+
%%% Cases for monitor/2
-mon_1(doc) ->
- "Cases for monitor/2";
-mon_1(suite) -> [];
mon_1(Config) when is_list(Config) ->
%% Normal case
- ?line P2 = spawn(timer, sleep, [1]),
- ?line R2 = erlang:monitor(process, P2),
- ?line case expect_down(R2, P2) of
- normal -> ?line ok;
- noproc -> ?line ok;
- BadReason -> ?line ?t:fail({bad_reason, BadReason})
- end,
- ?line {P2A,R2A} = spawn_monitor(timer, sleep, [1]),
- ?line expect_down(R2A, P2A, normal),
+ P2 = spawn(timer, sleep, [1]),
+ R2 = erlang:monitor(process, P2),
+ case expect_down(R2, P2) of
+ normal -> ok;
+ noproc -> ok;
+ BadReason -> ct:fail({bad_reason, BadReason})
+ end,
+ {P2A,R2A} = spawn_monitor(timer, sleep, [1]),
+ expect_down(R2A, P2A, normal),
%% 'DOWN' with other reason
- ?line P3 = spawn(timer, sleep, [100000]),
- ?line R3 = erlang:monitor(process, P3),
- ?line exit(P3, frop),
- ?line expect_down(R3, P3, frop),
- ?line {P3A,R3A} = spawn_monitor(timer, sleep, [100000]),
- ?line exit(P3A, frop),
- ?line expect_down(R3A, P3A, frop),
+ P3 = spawn(timer, sleep, [100000]),
+ R3 = erlang:monitor(process, P3),
+ exit(P3, frop),
+ expect_down(R3, P3, frop),
+ {P3A,R3A} = spawn_monitor(timer, sleep, [100000]),
+ exit(P3A, frop),
+ expect_down(R3A, P3A, frop),
%% Monitor fails because process is dead
- ?line R4 = erlang:monitor(process, P3),
- ?line expect_down(R4, P3, noproc),
+ R4 = erlang:monitor(process, P3),
+ expect_down(R4, P3, noproc),
%% Normal case (named process)
- ?line P5 = start_jeeves(jeeves),
- ?line R5 = erlang:monitor(process, jeeves),
- ?line tell_jeeves(P5, stop),
- ?line expect_down(R5, {jeeves, node()}, normal),
+ P5 = start_jeeves(jeeves),
+ R5 = erlang:monitor(process, jeeves),
+ tell_jeeves(P5, stop),
+ expect_down(R5, {jeeves, node()}, normal),
%% 'DOWN' with other reason and node explicit activation
- ?line P6 = start_jeeves(jeeves),
- ?line R6 = erlang:monitor(process, {jeeves, node()}),
- ?line tell_jeeves(P6, {exit, frop}),
- ?line expect_down(R6, {jeeves, node()}, frop),
+ P6 = start_jeeves(jeeves),
+ R6 = erlang:monitor(process, {jeeves, node()}),
+ tell_jeeves(P6, {exit, frop}),
+ expect_down(R6, {jeeves, node()}, frop),
%% Monitor (named process) fails because process is dead
- ?line R7 = erlang:monitor(process, {jeeves, node()}),
- ?line expect_down(R7, {jeeves, node()}, noproc),
+ R7 = erlang:monitor(process, {jeeves, node()}),
+ expect_down(R7, {jeeves, node()}, noproc),
ok.
-mon_2(doc) ->
- "Distributed cases for monitor/2";
-mon_2(suite) -> [];
+%% Distributed cases for monitor/2
mon_2(Config) when is_list(Config) ->
- ?line {ok, N1} = test_server:start_node(hej1, slave, []),
+ {ok, N1} = test_server:start_node(hej1, slave, []),
%% Normal case
- ?line P2 = spawn(N1, timer, sleep, [4000]),
- ?line R2 = erlang:monitor(process, P2),
- ?line expect_down(R2, P2, normal),
+ P2 = spawn(N1, timer, sleep, [4000]),
+ R2 = erlang:monitor(process, P2),
+ expect_down(R2, P2, normal),
%% 'DOWN' with other reason
- ?line P3 = spawn(N1, timer, sleep, [100000]),
- ?line R3 = erlang:monitor(process, P3),
- ?line exit(P3, frop),
- ?line expect_down(R3, P3, frop),
+ P3 = spawn(N1, timer, sleep, [100000]),
+ R3 = erlang:monitor(process, P3),
+ exit(P3, frop),
+ expect_down(R3, P3, frop),
%% Monitor fails because process is dead
- ?line R4 = erlang:monitor(process, P3),
- ?line expect_down(R4, P3, noproc),
+ R4 = erlang:monitor(process, P3),
+ expect_down(R4, P3, noproc),
%% Other node goes down
- ?line P5 = spawn(N1, timer, sleep, [100000]),
- ?line R5 = erlang:monitor(process, P5),
+ P5 = spawn(N1, timer, sleep, [100000]),
+ R5 = erlang:monitor(process, P5),
- ?line true = test_server:stop_node(N1),
+ true = test_server:stop_node(N1),
- ?line expect_down(R5, P5, noconnection),
+ expect_down(R5, P5, noconnection),
%% Monitor fails because other node is dead
- ?line P6 = spawn(N1, timer, sleep, [100000]),
- ?line R6 = erlang:monitor(process, P6),
- ?line R6_Reason = expect_down(R6, P6),
- ?line true = (R6_Reason == noconnection) orelse (R6_Reason == noproc),
+ P6 = spawn(N1, timer, sleep, [100000]),
+ R6 = erlang:monitor(process, P6),
+ R6_Reason = expect_down(R6, P6),
+ true = (R6_Reason == noconnection) orelse (R6_Reason == noproc),
%% Start a new node that can load code in this module
- ?line PA = filename:dirname(code:which(?MODULE)),
- ?line {ok, N2} = test_server:start_node
- (hej2, slave, [{args, "-pa " ++ PA}]),
+ PA = filename:dirname(code:which(?MODULE)),
+ {ok, N2} = test_server:start_node
+ (hej2, slave, [{args, "-pa " ++ PA}]),
%% Normal case (named process)
- ?line P7 = start_jeeves({jeeves, N2}),
- ?line R7 = erlang:monitor(process, {jeeves, N2}),
- ?line tell_jeeves(P7, stop),
- ?line expect_down(R7, {jeeves, N2}, normal),
+ P7 = start_jeeves({jeeves, N2}),
+ R7 = erlang:monitor(process, {jeeves, N2}),
+ tell_jeeves(P7, stop),
+ expect_down(R7, {jeeves, N2}, normal),
%% 'DOWN' with other reason (named process)
- ?line P8 = start_jeeves({jeeves, N2}),
- ?line R8 = erlang:monitor(process, {jeeves, N2}),
- ?line tell_jeeves(P8, {exit, frop}),
- ?line expect_down(R8, {jeeves, N2}, frop),
+ P8 = start_jeeves({jeeves, N2}),
+ R8 = erlang:monitor(process, {jeeves, N2}),
+ tell_jeeves(P8, {exit, frop}),
+ expect_down(R8, {jeeves, N2}, frop),
%% Monitor (named process) fails because process is dead
- ?line R9 = erlang:monitor(process, {jeeves, N2}),
- ?line expect_down(R9, {jeeves, N2}, noproc),
+ R9 = erlang:monitor(process, {jeeves, N2}),
+ expect_down(R9, {jeeves, N2}, noproc),
%% Other node goes down (named process)
- ?line _P10 = start_jeeves({jeeves, N2}),
- ?line R10 = erlang:monitor(process, {jeeves, N2}),
+ _P10 = start_jeeves({jeeves, N2}),
+ R10 = erlang:monitor(process, {jeeves, N2}),
- ?line true = test_server:stop_node(N2),
+ true = test_server:stop_node(N2),
- ?line expect_down(R10, {jeeves, N2}, noconnection),
+ expect_down(R10, {jeeves, N2}, noconnection),
%% Monitor (named process) fails because other node is dead
- ?line R11 = erlang:monitor(process, {jeeves, N2}),
- ?line expect_down(R11, {jeeves, N2}, noconnection),
+ R11 = erlang:monitor(process, {jeeves, N2}),
+ expect_down(R11, {jeeves, N2}, noconnection),
ok.
%%% Large exit reason. Crashed first attempt to release R5B.
-large_exit(doc) ->
- "Large exit reason";
-large_exit(suite) -> [];
large_exit(Config) when is_list(Config) ->
- ?line f(100),
+ f(100),
ok.
f(0) ->
@@ -539,23 +489,23 @@ f(N) ->
f(N-1).
f() ->
- ?line S0 = {big, tuple, with, [list, 4563784278]},
- ?line S = {S0, term_to_binary(S0)},
- ?line P = spawn(?MODULE, large_exit_sub, [S]),
- ?line R = erlang:monitor(process, P),
- ?line P ! hej,
+ S0 = {big, tuple, with, [list, 4563784278]},
+ S = {S0, term_to_binary(S0)},
+ P = spawn(?MODULE, large_exit_sub, [S]),
+ R = erlang:monitor(process, P),
+ P ! hej,
receive
- {'DOWN', R, process, P, X} ->
- ?line io:format(" -> ~p~n", [X]),
- if
- X == S ->
- ok;
- true ->
- test_server:fail({X, S})
- end;
- Other ->
- ?line io:format(" -> ~p~n", [Other]),
- exit({answer, Other})
+ {'DOWN', R, process, P, X} ->
+ io:format(" -> ~p~n", [X]),
+ if
+ X == S ->
+ ok;
+ true ->
+ ct:fail({X, S})
+ end;
+ Other ->
+ io:format(" -> ~p~n", [Other]),
+ exit({answer, Other})
end.
large_exit_sub(S) ->
@@ -566,105 +516,99 @@ large_exit_sub(S) ->
%%% by using erlang:process_info(self(), monitors)
%%% and erlang:process_info(self(), monitored_by)
-list_cleanup(doc) ->
- "Testing of monitor link list cleanup by using " ++
- "erlang:process_info/2";
-list_cleanup(suite) -> [];
list_cleanup(Config) when is_list(Config) ->
- ?line P0 = self(),
- ?line M = node(),
- ?line PA = filename:dirname(code:which(?MODULE)),
- ?line true = register(master_bertie, self()),
+ P0 = self(),
+ M = node(),
+ PA = filename:dirname(code:which(?MODULE)),
+ true = register(master_bertie, self()),
%% Normal local case, monitor and demonitor
- ?line P1 = start_jeeves(jeeves),
- ?line {[], []} = monitors(),
- ?line expect_jeeves(P1, monitors, {monitors, {[], []}}),
- ?line R1a = erlang:monitor(process, P1),
- ?line {[{process, P1}], []} = monitors(),
- ?line expect_jeeves(P1, monitors, {monitors, {[], [P0]}}),
- ?line true = erlang:demonitor(R1a),
- ?line expect_no_msg(),
- ?line {[], []} = monitors(),
- ?line expect_jeeves(P1, monitors, {monitors, {[], []}}),
+ P1 = start_jeeves(jeeves),
+ {[], []} = monitors(),
+ expect_jeeves(P1, monitors, {monitors, {[], []}}),
+ R1a = erlang:monitor(process, P1),
+ {[{process, P1}], []} = monitors(),
+ expect_jeeves(P1, monitors, {monitors, {[], [P0]}}),
+ true = erlang:demonitor(R1a),
+ expect_no_msg(),
+ {[], []} = monitors(),
+ expect_jeeves(P1, monitors, {monitors, {[], []}}),
%% Remonitor named and try again, now exiting the monitored process
- ?line R1b = erlang:monitor(process, jeeves),
- ?line {[{process, {jeeves, M}}], []} = monitors(),
- ?line expect_jeeves(P1, monitors, {monitors, {[], [P0]}}),
- ?line tell_jeeves(P1, stop),
- ?line expect_down(R1b, {jeeves, node()}, normal),
- ?line {[], []} = monitors(),
+ R1b = erlang:monitor(process, jeeves),
+ {[{process, {jeeves, M}}], []} = monitors(),
+ expect_jeeves(P1, monitors, {monitors, {[], [P0]}}),
+ tell_jeeves(P1, stop),
+ expect_down(R1b, {jeeves, node()}, normal),
+ {[], []} = monitors(),
%% Slightly weird local case - the monitoring process crashes
- ?line P2 = start_jeeves(jeeves),
- ?line {[], []} = monitors(),
- ?line expect_jeeves(P2, monitors, {monitors, {[], []}}),
- ?line {monitor_process, _R2} =
- ask_jeeves(P2, {monitor_process, master_bertie}),
- ?line {[], [P2]} = monitors(),
- ?line expect_jeeves(P2, monitors,
- {monitors, {[{process, {master_bertie, node()}}], []}}),
- ?line tell_jeeves(P2, {exit, frop}),
+ P2 = start_jeeves(jeeves),
+ {[], []} = monitors(),
+ expect_jeeves(P2, monitors, {monitors, {[], []}}),
+ {monitor_process, _R2} =
+ ask_jeeves(P2, {monitor_process, master_bertie}),
+ {[], [P2]} = monitors(),
+ expect_jeeves(P2, monitors,
+ {monitors, {[{process, {master_bertie, node()}}], []}}),
+ tell_jeeves(P2, {exit, frop}),
timer:sleep(2000),
- ?line {[], []} = monitors(),
+ {[], []} = monitors(),
%% Start a new node that can load code in this module
- ?line {ok, J} = test_server:start_node
- (jeeves, slave, [{args, "-pa " ++ PA}]),
+ {ok, J} = test_server:start_node
+ (jeeves, slave, [{args, "-pa " ++ PA}]),
%% Normal remote case, monitor and demonitor
- ?line P3 = start_jeeves({jeeves, J}),
- ?line {[], []} = monitors(),
- ?line expect_jeeves(P3, monitors, {monitors, {[], []}}),
- ?line R3a = erlang:monitor(process, P3),
- ?line {[{process, P3}], []} = monitors(),
- ?line expect_jeeves(P3, monitors, {monitors, {[], [P0]}}),
- ?line true = erlang:demonitor(R3a),
- ?line expect_no_msg(),
- ?line {[], []} = monitors(),
- ?line expect_jeeves(P3, monitors, {monitors, {[], []}}),
+ P3 = start_jeeves({jeeves, J}),
+ {[], []} = monitors(),
+ expect_jeeves(P3, monitors, {monitors, {[], []}}),
+ R3a = erlang:monitor(process, P3),
+ {[{process, P3}], []} = monitors(),
+ expect_jeeves(P3, monitors, {monitors, {[], [P0]}}),
+ true = erlang:demonitor(R3a),
+ expect_no_msg(),
+ {[], []} = monitors(),
+ expect_jeeves(P3, monitors, {monitors, {[], []}}),
%% Remonitor named and try again, now exiting the monitored process
- ?line R3b = erlang:monitor(process, {jeeves, J}),
- ?line {[{process, {jeeves, J}}], []} = monitors(),
- ?line expect_jeeves(P3, monitors, {monitors, {[], [P0]}}),
- ?line tell_jeeves(P3, stop),
- ?line expect_down(R3b, {jeeves, J}, normal),
- ?line {[], []} = monitors(),
+ R3b = erlang:monitor(process, {jeeves, J}),
+ {[{process, {jeeves, J}}], []} = monitors(),
+ expect_jeeves(P3, monitors, {monitors, {[], [P0]}}),
+ tell_jeeves(P3, stop),
+ expect_down(R3b, {jeeves, J}, normal),
+ {[], []} = monitors(),
%% Slightly weird remote case - the monitoring process crashes
- ?line P4 = start_jeeves({jeeves, J}),
- ?line {[], []} = monitors(),
- ?line expect_jeeves(P4, monitors, {monitors, {[], []}}),
- ?line {monitor_process, _R4} =
- ask_jeeves(P4, {monitor_process, {master_bertie, M}}),
- ?line {[], [P4]} = monitors(),
- ?line expect_jeeves(P4, monitors,
- {monitors, {[{process, {master_bertie, M}}], []}} ),
- ?line tell_jeeves(P4, {exit, frop}),
+ P4 = start_jeeves({jeeves, J}),
+ {[], []} = monitors(),
+ expect_jeeves(P4, monitors, {monitors, {[], []}}),
+ {monitor_process, _R4} =
+ ask_jeeves(P4, {monitor_process, {master_bertie, M}}),
+ {[], [P4]} = monitors(),
+ expect_jeeves(P4, monitors,
+ {monitors, {[{process, {master_bertie, M}}], []}} ),
+ tell_jeeves(P4, {exit, frop}),
timer:sleep(2000),
- ?line {[], []} = monitors(),
-
+ {[], []} = monitors(),
+
%% Now, the monitoring remote node crashes
- ?line P5 = start_jeeves({jeeves, J}),
- ?line {[], []} = monitors(),
- ?line expect_jeeves(P5, monitors, {monitors, {[], []}}),
- ?line {monitor_process, _R5} =
- ask_jeeves(P5, {monitor_process, P0}),
- ?line {[], [P5]} = monitors(),
- ?line expect_jeeves(P5, monitors,
- {monitors, {[{process, P0}], []}} ),
- ?line test_server:stop_node(J),
+ P5 = start_jeeves({jeeves, J}),
+ {[], []} = monitors(),
+ expect_jeeves(P5, monitors, {monitors, {[], []}}),
+ {monitor_process, _R5} =
+ ask_jeeves(P5, {monitor_process, P0}),
+ {[], [P5]} = monitors(),
+ expect_jeeves(P5, monitors,
+ {monitors, {[{process, P0}], []}} ),
+ test_server:stop_node(J),
timer:sleep(4000),
- ?line {[], []} = monitors(),
-
- ?line true = unregister(master_bertie),
+ {[], []} = monitors(),
+
+ true = unregister(master_bertie),
ok.
-
+
%%% Mixed internal and external monitors
-mixer(doc) ->
- "Test mixing of internal and external monitors.";
mixer(Config) when is_list(Config) ->
PA = filename:dirname(code:which(?MODULE)),
NN = [j0,j1,j2],
@@ -748,115 +692,113 @@ mixer(Config) when is_list(Config) ->
[test_server:stop_node(K) || K <- NL0],
ok.
-named_down(doc) -> ["Test that DOWN message for a named monitor isn't"
- " delivered until name has been unregistered"];
-named_down(suite) -> [];
+%% Test that DOWN message for a named monitor isn't
+%% delivered until name has been unregistered
named_down(Config) when is_list(Config) ->
- ?line Name = list_to_atom(atom_to_list(?MODULE)
- ++ "-named_down-"
- ++ integer_to_list(erlang:system_time(seconds))
- ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))),
- ?line Prio = process_flag(priority,high),
+ Name = list_to_atom(atom_to_list(?MODULE)
+ ++ "-named_down-"
+ ++ integer_to_list(erlang:system_time(second))
+ ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))),
+ Prio = process_flag(priority,high),
%% Spawn a bunch of high prio cpu bound processes to prevent
%% normal prio processes from terminating during the next
%% 500 ms...
- ?line Self = self(),
- ?line spawn_opt(fun () ->
- WFun = fun
- (F, hej) -> F(F, hopp);
- (F, hopp) -> F(F, hej)
- end,
- NoSchedulers = erlang:system_info(schedulers_online),
- lists:foreach(fun (_) ->
- spawn_opt(fun () ->
- WFun(WFun,
- hej)
- end,
- [{priority,high},
- link])
- end,
- lists:seq(1, NoSchedulers)),
- receive after 500 -> ok end,
- unlink(Self),
- exit(bang)
- end,
- [{priority,high}, link]),
- ?line NamedProc = spawn_link(fun () ->
- receive after infinity -> ok end
- end),
- ?line true = register(Name, NamedProc),
- ?line unlink(NamedProc),
- ?line exit(NamedProc, bang),
- ?line Mon = erlang:monitor(process, Name),
- ?line receive {'DOWN',Mon, _, _, _} -> ok end,
- ?line true = register(Name, self()),
- ?line true = unregister(Name),
- ?line process_flag(priority,Prio),
+ Self = self(),
+ spawn_opt(fun () ->
+ WFun = fun
+ (F, hej) -> F(F, hopp);
+ (F, hopp) -> F(F, hej)
+ end,
+ NoSchedulers = erlang:system_info(schedulers_online),
+ lists:foreach(fun (_) ->
+ spawn_opt(fun () ->
+ WFun(WFun,
+ hej)
+ end,
+ [{priority,high},
+ link])
+ end,
+ lists:seq(1, NoSchedulers)),
+ receive after 500 -> ok end,
+ unlink(Self),
+ exit(bang)
+ end,
+ [{priority,high}, link]),
+ NamedProc = spawn_link(fun () ->
+ receive after infinity -> ok end
+ end),
+ ?assertEqual(true, register(Name, NamedProc)),
+ unlink(NamedProc),
+ exit(NamedProc, bang),
+ Mon = erlang:monitor(process, Name),
+ receive {'DOWN',Mon, _, _, bang} -> ok
+ after 3000 -> ?assert(false) end,
+ ?assertEqual(true, register(Name, self())),
+ ?assertEqual(true, unregister(Name)),
+ process_flag(priority,Prio),
ok.
-otp_5827(doc) -> [];
-otp_5827(suite) -> [];
otp_5827(Config) when is_list(Config) ->
%% Make a pid with the same nodename but with another creation
- ?line [CreEnd | RPTail]
- = lists:reverse(binary_to_list(term_to_binary(self()))),
- ?line NewCreEnd = case CreEnd of
- 0 -> 1;
- 1 -> 2;
- _ -> CreEnd - 1
- end,
- ?line OtherCreationPid
- = binary_to_term(list_to_binary(lists:reverse([NewCreEnd | RPTail]))),
+ [CreEnd | RPTail]
+ = lists:reverse(binary_to_list(term_to_binary(self()))),
+ NewCreEnd = case CreEnd of
+ 0 -> 1;
+ 1 -> 2;
+ _ -> CreEnd - 1
+ end,
+ OtherCreationPid
+ = binary_to_term(list_to_binary(lists:reverse([NewCreEnd | RPTail]))),
%% If the bug is present erlang:monitor(process, OtherCreationPid)
%% will hang...
- ?line Parent = self(),
- ?line Ok = make_ref(),
- ?line spawn(fun () ->
- Mon = erlang:monitor(process, OtherCreationPid),
- % Should get the DOWN message right away
- receive
- {'DOWN', Mon, process, OtherCreationPid, noproc} ->
- Parent ! Ok
- end
- end),
- ?line receive
- Ok ->
- ?line ok
- after 1000 ->
- ?line ?t:fail("erlang:monitor/2 hangs")
- end.
+ Parent = self(),
+ Ok = make_ref(),
+ spawn(fun () ->
+ Mon = erlang:monitor(process, OtherCreationPid),
+ % Should get the DOWN message right away
+ receive
+ {'DOWN', Mon, process, OtherCreationPid, noproc} ->
+ Parent ! Ok
+ end
+ end),
+ receive
+ Ok ->
+ ok
+ after 1000 ->
+ ct:fail("erlang:monitor/2 hangs")
+ end.
monitor_time_offset(Config) when is_list(Config) ->
{ok, Node} = start_node(Config, "+C single_time_warp"),
Me = self(),
PMs = lists:map(fun (_) ->
- Pid = spawn(Node,
- fun () ->
- check_monitor_time_offset(Me)
- end),
- {Pid, erlang:monitor(process, Pid)}
- end,
- lists:seq(1, 100)),
+ Pid = spawn(Node,
+ fun () ->
+ check_monitor_time_offset(Me)
+ end),
+ {Pid, erlang:monitor(process, Pid)}
+ end,
+ lists:seq(1, 100)),
lists:foreach(fun ({P, _M}) ->
- P ! check_no_change_message
- end, PMs),
+ P ! check_no_change_message
+ end, PMs),
lists:foreach(fun ({P, M}) ->
- receive
- {no_change_message_received, P} ->
- ok;
- {'DOWN', M, process, P, Reason} ->
- ?t:fail(Reason)
- end
- end, PMs),
+ receive
+ {no_change_message_received, P} ->
+ ok;
+ {'DOWN', M, process, P, Reason} ->
+ ct:fail(Reason)
+ end
+ end, PMs),
preliminary = rpc:call(Node, erlang, system_flag, [time_offset, finalize]),
lists:foreach(fun ({P, M}) ->
- receive
- {change_messages_received, P} ->
- erlang:demonitor(M, [flush]);
- {'DOWN', M, process, P, Reason} ->
- ?t:fail(Reason)
- end
- end, PMs),
+ receive
+ {change_messages_received, P} ->
+ erlang:demonitor(M, [flush]);
+ {'DOWN', M, process, P, Reason} ->
+ ct:fail(Reason)
+ end
+ end, PMs),
stop_node(Node),
ok.
@@ -867,42 +809,42 @@ check_monitor_time_offset(Leader) ->
Mon4 = erlang:monitor(time_offset, clock_service),
erlang:demonitor(Mon2, [flush]),
-
+
Mon5 = erlang:monitor(time_offset, clock_service),
Mon6 = erlang:monitor(time_offset, clock_service),
Mon7 = erlang:monitor(time_offset, clock_service),
receive check_no_change_message -> ok end,
receive
- {'CHANGE', _, time_offset, clock_service, _} ->
- exit(unexpected_change_message_received)
+ {'CHANGE', _, time_offset, clock_service, _} ->
+ exit(unexpected_change_message_received)
after 0 ->
- Leader ! {no_change_message_received, self()}
+ Leader ! {no_change_message_received, self()}
end,
receive after 100 -> ok end,
erlang:demonitor(Mon4, [flush]),
receive
- {'CHANGE', Mon3, time_offset, clock_service, _} ->
- ok
+ {'CHANGE', Mon3, time_offset, clock_service, _} ->
+ ok
end,
receive
- {'CHANGE', Mon6, time_offset, clock_service, _} ->
- ok
+ {'CHANGE', Mon6, time_offset, clock_service, _} ->
+ ok
end,
erlang:demonitor(Mon5, [flush]),
receive
- {'CHANGE', Mon7, time_offset, clock_service, _} ->
- ok
+ {'CHANGE', Mon7, time_offset, clock_service, _} ->
+ ok
end,
receive
- {'CHANGE', Mon1, time_offset, clock_service, _} ->
- ok
+ {'CHANGE', Mon1, time_offset, clock_service, _} ->
+ ok
end,
receive
- {'CHANGE', _, time_offset, clock_service, _} ->
- exit(unexpected_change_message_received)
+ {'CHANGE', _, time_offset, clock_service, _} ->
+ exit(unexpected_change_message_received)
after 1000 ->
- ok
+ ok
end,
Leader ! {change_messages_received, self()}.
@@ -916,17 +858,17 @@ wait_for_m(Monitors, MonitoredBy, N) ->
{monitors,M0} = process_info(self(),monitors),
{monitored_by,MB0} = process_info(self(),monitored_by),
case lists:sort(M0) of
- Monitors ->
- case lists:sort(MB0) of
- MonitoredBy ->
- ok;
- _ ->
- receive after 100 -> ok end,
- wait_for_m(Monitors,MonitoredBy,N-1)
- end;
- _ ->
- receive after 100 -> ok end,
- wait_for_m(Monitors,MonitoredBy,N-1)
+ Monitors ->
+ case lists:sort(MB0) of
+ MonitoredBy ->
+ ok;
+ _ ->
+ receive after 100 -> ok end,
+ wait_for_m(Monitors,MonitoredBy,N-1)
+ end;
+ _ ->
+ receive after 100 -> ok end,
+ wait_for_m(Monitors,MonitoredBy,N-1)
end.
% All permutations of a list...
@@ -950,32 +892,32 @@ jeeves(Parent, Name, Ref)
when is_pid(Parent), (is_atom(Name) or (Name =:= [])), is_reference(Ref) ->
%%io:format("monitor_SUITE:jeeves(~p, ~p)~n", [Parent, Name]),
case Name of
- Atom when is_atom(Atom) ->
- register(Name, self());
- [] ->
- ok
+ Atom when is_atom(Atom) ->
+ register(Name, self());
+ [] ->
+ ok
end,
Parent ! {self(), Ref},
jeeves_loop(Parent).
jeeves_loop(Parent) ->
receive
- {Parent, monitors} ->
- Parent ! {self(), {monitors, monitors()}},
- jeeves_loop(Parent);
- {Parent, {monitor_process, P}} ->
- Parent ! {self(), {monitor_process,
- catch erlang:monitor(process, P) }},
- jeeves_loop(Parent);
- {Parent, {demonitor, Ref}} ->
- Parent ! {self(), {demonitor, catch erlang:demonitor(Ref)}},
- jeeves_loop(Parent);
- {Parent, stop} ->
- ok;
- {Parent, {exit, Reason}} ->
- exit(Reason);
- Other ->
- io:format("~p:jeeves_loop received ~p~n", [?MODULE, Other])
+ {Parent, monitors} ->
+ Parent ! {self(), {monitors, monitors()}},
+ jeeves_loop(Parent);
+ {Parent, {monitor_process, P}} ->
+ Parent ! {self(), {monitor_process,
+ catch erlang:monitor(process, P) }},
+ jeeves_loop(Parent);
+ {Parent, {demonitor, Ref}} ->
+ Parent ! {self(), {demonitor, catch erlang:demonitor(Ref)}},
+ jeeves_loop(Parent);
+ {Parent, stop} ->
+ ok;
+ {Parent, {exit, Reason}} ->
+ exit(Reason);
+ Other ->
+ io:format("~p:jeeves_loop received ~p~n", [?MODULE, Other])
end.
@@ -985,10 +927,10 @@ start_jeeves({Name, Node})
Ref = make_ref(),
Pid = spawn(Node, fun() -> jeeves(Parent, Name, Ref) end),
receive
- {Pid, Ref} ->
- ok;
- Other ->
- test_server:fail({rec, Other})
+ {Pid, Ref} ->
+ ok;
+ Other ->
+ ct:fail({rec, Other})
end,
Pid;
start_jeeves(Name) when is_atom(Name) ->
@@ -1002,20 +944,20 @@ tell_jeeves(Pid, What) when is_pid(Pid) ->
ask_jeeves(Pid, Request) when is_pid(Pid) ->
Pid ! {self(), Request},
receive
- {Pid, Response} ->
- Response;
- Other ->
- test_server:fail({rec, Other})
+ {Pid, Response} ->
+ Response;
+ Other ->
+ ct:fail({rec, Other})
end.
expect_jeeves(Pid, Request, Response) when is_pid(Pid) ->
Pid ! {self(), Request},
receive
- {Pid, Response} ->
- ok;
- Other ->
- test_server:fail({rec, Other})
+ {Pid, Response} ->
+ ok;
+ Other ->
+ ct:fail({rec, Other})
end.
@@ -1036,20 +978,20 @@ start_node(Config) ->
start_node(Config, "").
start_node(Config, Args) ->
- TestCase = ?config(testcase, Config),
+ TestCase = proplists:get_value(testcase, Config),
PA = filename:dirname(code:which(?MODULE)),
ESTime = erlang:monotonic_time(1) + erlang:time_offset(1),
Unique = erlang:unique_integer([positive]),
Name = list_to_atom(atom_to_list(?MODULE)
- ++ "-"
- ++ atom_to_list(TestCase)
- ++ "-"
- ++ integer_to_list(ESTime)
- ++ "-"
- ++ integer_to_list(Unique)),
+ ++ "-"
+ ++ atom_to_list(TestCase)
+ ++ "-"
+ ++ integer_to_list(ESTime)
+ ++ "-"
+ ++ integer_to_list(Unique)),
test_server:start_node(Name,
- slave,
- [{args, "-pa " ++ PA ++ " " ++ Args}]).
+ slave,
+ [{args, "-pa " ++ PA ++ " " ++ Args}]).
stop_node(Node) ->
test_server:stop_node(Node).
diff --git a/erts/emulator/test/mtx_SUITE.erl b/erts/emulator/test/mtx_SUITE.erl
index 87dace4721..12928ed6d8 100644
--- a/erts/emulator/test/mtx_SUITE.erl
+++ b/erts/emulator/test/mtx_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -29,9 +29,8 @@
-include_lib("common_test/include/ct.hrl").
--export([all/0,suite/0,groups/0,
- init_per_group/2,end_per_group/2, init_per_suite/1,
- end_per_suite/1, init_per_testcase/2, end_per_testcase/2]).
+-export([all/0,suite/0, init_per_suite/1, end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2]).
-export([long_rwlock/1,
hammer_ets_rwlock/1,
@@ -56,8 +55,30 @@
hammer_sched_freqread_tryrwlock/1,
hammer_sched_freqread_tryrwlock_check/1]).
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 15}}].
+
+all() ->
+ [long_rwlock, hammer_rwlock_check, hammer_rwlock,
+ hammer_tryrwlock_check, hammer_tryrwlock,
+ hammer_ets_rwlock, hammer_sched_long_rwlock_check,
+ hammer_sched_long_rwlock,
+ hammer_sched_long_freqread_rwlock_check,
+ hammer_sched_long_freqread_rwlock,
+ hammer_sched_long_tryrwlock_check,
+ hammer_sched_long_tryrwlock,
+ hammer_sched_long_freqread_tryrwlock_check,
+ hammer_sched_long_freqread_tryrwlock,
+ hammer_sched_rwlock_check, hammer_sched_rwlock,
+ hammer_sched_freqread_rwlock_check,
+ hammer_sched_freqread_rwlock,
+ hammer_sched_tryrwlock_check, hammer_sched_tryrwlock,
+ hammer_sched_freqread_tryrwlock_check,
+ hammer_sched_freqread_tryrwlock].
+
init_per_suite(Config) when is_list(Config) ->
- DataDir = ?config(data_dir, Config),
+ DataDir = proplists:get_value(data_dir, Config),
Lib = filename:join([DataDir, atom_to_list(?MODULE)]),
case {erlang:load_nif(Lib, none),erlang:system_info(threads)} of
{{error,_},false} ->
@@ -71,15 +92,13 @@ end_per_suite(Config) when is_list(Config) ->
Config.
init_per_testcase(_Case, Config) ->
- Dog = ?t:timetrap(?t:minutes(15)),
%% Wait for deallocations to complete since we measure
%% runtime in test cases.
wait_deallocations(),
- [{watchdog, Dog}|Config].
+ Config.
end_per_testcase(_Func, Config) ->
- Dog = ?config(watchdog, Config),
- ?t:timetrap_cancel(Dog).
+ ok.
wait_deallocations() ->
try
@@ -90,45 +109,15 @@ wait_deallocations() ->
wait_deallocations()
end.
-suite() -> [{ct_hooks,[ts_install_cth]}].
-
-all() ->
- [long_rwlock, hammer_rwlock_check, hammer_rwlock,
- hammer_tryrwlock_check, hammer_tryrwlock,
- hammer_ets_rwlock, hammer_sched_long_rwlock_check,
- hammer_sched_long_rwlock,
- hammer_sched_long_freqread_rwlock_check,
- hammer_sched_long_freqread_rwlock,
- hammer_sched_long_tryrwlock_check,
- hammer_sched_long_tryrwlock,
- hammer_sched_long_freqread_tryrwlock_check,
- hammer_sched_long_freqread_tryrwlock,
- hammer_sched_rwlock_check, hammer_sched_rwlock,
- hammer_sched_freqread_rwlock_check,
- hammer_sched_freqread_rwlock,
- hammer_sched_tryrwlock_check, hammer_sched_tryrwlock,
- hammer_sched_freqread_tryrwlock_check,
- hammer_sched_freqread_tryrwlock].
-
-groups() ->
- [].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
long_rwlock(Config) when is_list(Config) ->
statistics(runtime),
LLRes = long_rw_test(),
{_, RunTime} = statistics(runtime),
%% A very short run time is expected, since
%% threads in the test mostly wait
- ?t:format("RunTime=~p~n", [RunTime]),
- ?line true = RunTime < 400,
- ?line RunTimeStr = "Run-time during test was "++integer_to_list(RunTime)++" ms.",
+ io:format("RunTime=~p~n", [RunTime]),
+ true = RunTime < 400,
+ RunTimeStr = "Run-time during test was "++integer_to_list(RunTime)++" ms.",
case LLRes of
ok ->
{comment, RunTimeStr};
@@ -198,100 +187,100 @@ hammer_sched_long_freqread_tryrwlock_check(Config) when is_list(Config) ->
hammer_sched_rwlock_test(FreqRead, LockCheck, Blocking, WaitLocked, WaitUnlocked) ->
case create_rwlock(FreqRead, LockCheck) of
- enotsup ->
- {skipped, "Not supported."};
- RWLock ->
- Onln = erlang:system_info(schedulers_online),
- NWPs = case Onln div 3 of
- 1 -> case Onln < 4 of
- true -> 1;
- false -> 2
- end;
- X -> X
- end,
- NRPs = Onln - NWPs,
- NoLockOps = ((((50000000 div Onln)
- div case {Blocking, WaitLocked} of
- {false, 0} -> 1;
- _ -> 10
- end)
- div (case WaitLocked == 0 of
- true -> 1;
- false -> WaitLocked*250
- end))
- div handicap()),
- ?t:format("NoLockOps=~p~n", [NoLockOps]),
- Sleep = case Blocking of
- true -> NoLockOps;
- false -> NoLockOps div 10
- end,
- WPs = lists:map(
- fun (Sched) ->
- spawn_opt(
- fun () ->
- io:format("Writer on scheduler ~p.~n",
- [Sched]),
- Sched = erlang:system_info(scheduler_id),
- receive go -> gone end,
- hammer_sched_rwlock_proc(RWLock,
- Blocking,
- true,
- WaitLocked,
- WaitUnlocked,
- NoLockOps,
- Sleep),
- Sched = erlang:system_info(scheduler_id)
- end,
- [link, {scheduler, Sched}])
- end,
- lists:seq(1, NWPs)),
- RPs = lists:map(
- fun (Sched) ->
- spawn_opt(
- fun () ->
- io:format("Reader on scheduler ~p.~n",
- [Sched]),
- Sched = erlang:system_info(scheduler_id),
- receive go -> gone end,
- hammer_sched_rwlock_proc(RWLock,
- Blocking,
- false,
- WaitLocked,
- WaitUnlocked,
- NoLockOps,
- Sleep),
- Sched = erlang:system_info(scheduler_id)
- end,
- [link, {scheduler, Sched}])
- end,
- lists:seq(NWPs + 1, NWPs + NRPs)),
- Procs = WPs ++ RPs,
- case {Blocking, WaitLocked} of
- {_, 0} -> ok;
- {false, _} -> ok;
- _ -> statistics(runtime)
- end,
- lists:foreach(fun (P) -> P ! go end, Procs),
- lists:foreach(fun (P) ->
- M = erlang:monitor(process, P),
- receive
- {'DOWN', M, process, P, _} ->
- ok
- end
- end,
- Procs),
- case {Blocking, WaitLocked} of
- {_, 0} -> ok;
- {false, _} -> ok;
- _ ->
- {_, RunTime} = statistics(runtime),
- ?t:format("RunTime=~p~n", [RunTime]),
- ?line true = RunTime < 700,
- {comment,
- "Run-time during test was "
- ++ integer_to_list(RunTime)
- ++ " ms."}
- end
+ enotsup ->
+ {skipped, "Not supported."};
+ RWLock ->
+ Onln = erlang:system_info(schedulers_online),
+ NWPs = case Onln div 3 of
+ 1 -> case Onln < 4 of
+ true -> 1;
+ false -> 2
+ end;
+ X -> X
+ end,
+ NRPs = Onln - NWPs,
+ NoLockOps = ((((50000000 div Onln)
+ div case {Blocking, WaitLocked} of
+ {false, 0} -> 1;
+ _ -> 10
+ end)
+ div (case WaitLocked == 0 of
+ true -> 1;
+ false -> WaitLocked*250
+ end))
+ div handicap()),
+ io:format("NoLockOps=~p~n", [NoLockOps]),
+ Sleep = case Blocking of
+ true -> NoLockOps;
+ false -> NoLockOps div 10
+ end,
+ WPs = lists:map(
+ fun (Sched) ->
+ spawn_opt(
+ fun () ->
+ io:format("Writer on scheduler ~p.~n",
+ [Sched]),
+ Sched = erlang:system_info(scheduler_id),
+ receive go -> gone end,
+ hammer_sched_rwlock_proc(RWLock,
+ Blocking,
+ true,
+ WaitLocked,
+ WaitUnlocked,
+ NoLockOps,
+ Sleep),
+ Sched = erlang:system_info(scheduler_id)
+ end,
+ [link, {scheduler, Sched}])
+ end,
+ lists:seq(1, NWPs)),
+ RPs = lists:map(
+ fun (Sched) ->
+ spawn_opt(
+ fun () ->
+ io:format("Reader on scheduler ~p.~n",
+ [Sched]),
+ Sched = erlang:system_info(scheduler_id),
+ receive go -> gone end,
+ hammer_sched_rwlock_proc(RWLock,
+ Blocking,
+ false,
+ WaitLocked,
+ WaitUnlocked,
+ NoLockOps,
+ Sleep),
+ Sched = erlang:system_info(scheduler_id)
+ end,
+ [link, {scheduler, Sched}])
+ end,
+ lists:seq(NWPs + 1, NWPs + NRPs)),
+ Procs = WPs ++ RPs,
+ case {Blocking, WaitLocked} of
+ {_, 0} -> ok;
+ {false, _} -> ok;
+ _ -> statistics(runtime)
+ end,
+ lists:foreach(fun (P) -> P ! go end, Procs),
+ lists:foreach(fun (P) ->
+ M = erlang:monitor(process, P),
+ receive
+ {'DOWN', M, process, P, _} ->
+ ok
+ end
+ end,
+ Procs),
+ case {Blocking, WaitLocked} of
+ {_, 0} -> ok;
+ {false, _} -> ok;
+ _ ->
+ {_, RunTime} = statistics(runtime),
+ io:format("RunTime=~p~n", [RunTime]),
+ true = RunTime < 700,
+ {comment,
+ "Run-time during test was "
+ ++ integer_to_list(RunTime)
+ ++ " ms."}
+ end
end.
hammer_sched_rwlock_proc(_RWLock,
@@ -343,9 +332,9 @@ hammer_ets_rwlock(Config) when is_list(Config) ->
3 -> {2000, 50};
_ -> {200, 50}
end,
- ?t:format("Procs=~p~nOps=~p~n", [Procs, Ops]),
+ io:format("Procs=~p~nOps=~p~n", [Procs, Ops]),
lists:foreach(fun (XOpts) ->
- ?t:format("Running with extra opts: ~p", [XOpts]),
+ io:format("Running with extra opts: ~p", [XOpts]),
hammer_ets_rwlock_test(XOpts, true, 2, Ops,
Procs, false)
end,
@@ -408,65 +397,65 @@ hammer_ets_rwlock_init(_T, _N) ->
hammer_ets_rwlock_test(XOpts, UW, C, N, NP, SC) ->
receive after 100 -> ok end,
{TP, TM} = spawn_monitor(
- fun () ->
- _L = repeat_list(
- fun () ->
- Caller = self(),
- T = fun () ->
- Parent = self(),
- hammer_ets_rwlock_put_data(),
- T=ets:new(x, [public | XOpts]),
- hammer_ets_rwlock_init(T, 0),
- Ps0 = repeat_list(
- fun () ->
- spawn_link(
- fun () ->
- hammer_ets_rwlock_put_data(),
- receive go -> ok end,
- hammer_ets_rwlock_ops(T, UW, N, C, C, N),
- Parent ! {done, self()},
- receive after infinity -> ok end
- end)
- end,
- NP - case SC of
- false -> 0;
- _ -> 1
- end),
- Ps = case SC of
- false -> Ps0;
- _ -> [spawn_link(fun () ->
- hammer_ets_rwlock_put_data(),
- receive go -> ok end,
- hammer_ets_rwlock_ops(T, UW, N, SC, SC, N),
- Parent ! {done, self()},
- receive after infinity -> ok end
- end) | Ps0]
- end,
- Start = erlang:monotonic_time(),
- lists:foreach(fun (P) -> P ! go end, Ps),
- lists:foreach(fun (P) -> receive {done, P} -> ok end end, Ps),
- Stop = erlang:monotonic_time(),
- lists:foreach(fun (P) ->
- unlink(P),
- exit(P, bang),
- M = erlang:monitor(process, P),
- receive
- {'DOWN', M, process, P, _} -> ok
- end
- end, Ps),
- Res = (Stop-Start)/erlang:convert_time_unit(1,seconds,native),
- Caller ! {?MODULE, self(), Res}
- end,
- TP = spawn_link(T),
- receive
- {?MODULE, TP, Res} ->
- Res
- end
- end,
- ?HAMMER_ETS_RWLOCK_REPEAT_TIMES)
- end),
+ fun () ->
+ _L = repeat_list(
+ fun () ->
+ Caller = self(),
+ T = fun () ->
+ Parent = self(),
+ hammer_ets_rwlock_put_data(),
+ T=ets:new(x, [public | XOpts]),
+ hammer_ets_rwlock_init(T, 0),
+ Ps0 = repeat_list(
+ fun () ->
+ spawn_link(
+ fun () ->
+ hammer_ets_rwlock_put_data(),
+ receive go -> ok end,
+ hammer_ets_rwlock_ops(T, UW, N, C, C, N),
+ Parent ! {done, self()},
+ receive after infinity -> ok end
+ end)
+ end,
+ NP - case SC of
+ false -> 0;
+ _ -> 1
+ end),
+ Ps = case SC of
+ false -> Ps0;
+ _ -> [spawn_link(fun () ->
+ hammer_ets_rwlock_put_data(),
+ receive go -> ok end,
+ hammer_ets_rwlock_ops(T, UW, N, SC, SC, N),
+ Parent ! {done, self()},
+ receive after infinity -> ok end
+ end) | Ps0]
+ end,
+ Start = erlang:monotonic_time(),
+ lists:foreach(fun (P) -> P ! go end, Ps),
+ lists:foreach(fun (P) -> receive {done, P} -> ok end end, Ps),
+ Stop = erlang:monotonic_time(),
+ lists:foreach(fun (P) ->
+ unlink(P),
+ exit(P, bang),
+ M = erlang:monitor(process, P),
+ receive
+ {'DOWN', M, process, P, _} -> ok
+ end
+ end, Ps),
+ Res = (Stop-Start)/erlang:convert_time_unit(1,second,native),
+ Caller ! {?MODULE, self(), Res}
+ end,
+ TP = spawn_link(T),
+ receive
+ {?MODULE, TP, Res} ->
+ Res
+ end
+ end,
+ ?HAMMER_ETS_RWLOCK_REPEAT_TIMES)
+ end),
receive
- {'DOWN', TM, process, TP, _} -> ok
+ {'DOWN', TM, process, TP, _} -> ok
end.
repeat_list(Fun, N) ->
@@ -480,18 +469,17 @@ repeat_list(Fun, N, Acc) ->
handicap() ->
X0 = case catch (erlang:system_info(logical_processors_available) >=
- erlang:system_info(schedulers_online)) of
- true -> 1;
- _ -> 2
- end,
+ erlang:system_info(schedulers_online)) of
+ true -> 1;
+ _ -> 2
+ end,
case erlang:system_info(build_type) of
- opt ->
- X0;
- ReallySlow when ReallySlow == debug;
- ReallySlow == valgrind;
- ReallySlow == purify ->
- X0*3;
- _Slow ->
- X0*2
+ opt ->
+ X0;
+ ReallySlow when ReallySlow == debug;
+ ReallySlow == valgrind;
+ ReallySlow == purify ->
+ X0*3;
+ _Slow ->
+ X0*2
end.
-
diff --git a/erts/emulator/test/mtx_SUITE_data/Makefile.src b/erts/emulator/test/mtx_SUITE_data/Makefile.src
index dc880118f1..1816dc6798 100644
--- a/erts/emulator/test/mtx_SUITE_data/Makefile.src
+++ b/erts/emulator/test/mtx_SUITE_data/Makefile.src
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2013. All Rights Reserved.
+# Copyright Ericsson AB 2010-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/mtx_SUITE_data/mtx_SUITE.c b/erts/emulator/test/mtx_SUITE_data/mtx_SUITE.c
index 1911291448..e011aadce9 100644
--- a/erts/emulator/test/mtx_SUITE_data/mtx_SUITE.c
+++ b/erts/emulator/test/mtx_SUITE_data/mtx_SUITE.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2010-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/multi_load_SUITE.erl b/erts/emulator/test/multi_load_SUITE.erl
new file mode 100644
index 0000000000..edf3205812
--- /dev/null
+++ b/erts/emulator/test/multi_load_SUITE.erl
@@ -0,0 +1,181 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(multi_load_SUITE).
+-export([all/0, suite/0, many/1, on_load/1, errors/1]).
+
+-include_lib("common_test/include/ct.hrl").
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [many,on_load,errors].
+
+many(_Config) ->
+ Ms = make_modules(100, fun many_module/1),
+
+ io:put_chars("Light load\n"
+ "=========="),
+ many_measure(Ms),
+
+ _ = [spawn_link(fun many_worker/0) || _ <- lists:seq(1, 8)],
+ erlang:yield(),
+ io:put_chars("Heavy load\n"
+ "=========="),
+ many_measure(Ms),
+ ok.
+
+many_module(M) ->
+ ["-module("++M++").",
+ "-compile(export_all).",
+ "f1() -> ok.",
+ "f2() -> ok.",
+ "f3() -> ok.",
+ "f4() -> ok."].
+
+many_measure(Ms) ->
+ many_purge(Ms),
+ MsPrep1 = prepare_modules(Ms),
+ Us1 = ms(fun() -> many_load_seq(MsPrep1) end),
+ many_try_call(Ms),
+ many_purge(Ms),
+ MsPrep2 = prepare_modules(Ms),
+ Us2 = ms(fun() -> many_load_par(MsPrep2) end),
+ many_try_call(Ms),
+ io:format("# modules: ~9w\n"
+ "Sequential: ~9w µs\n"
+ "Parallel: ~9w µs\n"
+ "Ratio: ~9w\n",
+ [length(Ms),Us1,Us2,divide(Us1,Us2)]),
+ ok.
+
+divide(A,B) when B > 0 -> A div B;
+divide(_,_) -> inf.
+
+many_load_seq(Ms) ->
+ [erlang:finish_loading([M]) || M <- Ms],
+ ok.
+
+many_load_par(Ms) ->
+ erlang:finish_loading(Ms).
+
+many_purge(Ms) ->
+ _ = [catch erlang:purge_module(M) || {M,_} <- Ms],
+ ok.
+
+many_try_call(Ms) ->
+ _ = [begin
+ ok = M:f1(),
+ ok = M:f2(),
+ ok = M:f3(),
+ ok = M:f4()
+ end || {M,_} <- Ms],
+ ok.
+
+many_worker() ->
+ many_worker(lists:seq(1, 100)).
+
+many_worker(L) ->
+ N0 = length(L),
+ N1 = N0 * N0 * N0,
+ N2 = N1 div (N0 * N0),
+ N3 = N2 + 10,
+ _ = N3 - 10,
+ many_worker(L).
+
+
+on_load(_Config) ->
+ On = make_modules(2, fun on_load_module/1),
+ OnPrep = prepare_modules(On),
+ {'EXIT',{system_limit,_}} = (catch erlang:finish_loading(OnPrep)),
+
+ Normal = make_modules(1, fun on_load_normal/1),
+ Mixed = Normal ++ tl(On),
+ MixedPrep = prepare_modules(Mixed),
+ {'EXIT',{system_limit,_}} = (catch erlang:finish_loading(MixedPrep)),
+
+ [false,true] = [erlang:has_prepared_code_on_load(Code) ||
+ Code <- MixedPrep],
+ {'EXIT',{badarg,_}} = (catch erlang:has_prepared_code_on_load(<<1,2,3>>)),
+ Magic = ets:match_spec_compile([{'_',[true],['$_']}]),
+ {'EXIT',{badarg,_}} = (catch erlang:has_prepared_code_on_load(Magic)),
+
+ SingleOnPrep = tl(OnPrep),
+ {on_load,[OnLoadMod]} = erlang:finish_loading(SingleOnPrep),
+ ok = erlang:call_on_load_function(OnLoadMod),
+ ok.
+
+on_load_module(M) ->
+ ["-module("++M++").",
+ "-on_load(f/0).",
+ "f() -> ok."].
+
+on_load_normal(M) ->
+ ["-module("++M++")."].
+
+
+errors(_Config) ->
+ finish_loading_badarg(x),
+ finish_loading_badarg([x|y]),
+ finish_loading_badarg([x]),
+ finish_loading_badarg([<<>>]),
+
+ Mods = make_modules(2, fun errors_module/1),
+ Ms = lists:sort([M || {M,_} <- Mods]),
+ Prep = prepare_modules(Mods),
+ {duplicated,Dups} = erlang:finish_loading(Prep ++ Prep),
+ Ms = lists:sort(Dups),
+ ok.
+
+finish_loading_badarg(Arg) ->
+ {'EXIT',{badarg,[{erlang,finish_loading,[Arg],_}|_]}} =
+ (catch erlang:finish_loading(Arg)).
+
+errors_module(M) ->
+ ["-module("++M++").",
+ "-export([f/0]).",
+ "f() -> ok."].
+
+%%%
+%%% Common utilities
+%%%
+
+ms(Fun) ->
+ {Ms,ok} = timer:tc(Fun),
+ Ms.
+
+make_modules(0, _) ->
+ [];
+make_modules(N, Fun) ->
+ U = erlang:unique_integer([positive]),
+ M0 = "m__" ++ integer_to_list(N) ++ "_" ++ integer_to_list(U),
+ Contents = Fun(M0),
+ Forms = [make_form(S) || S <- Contents],
+ {ok,M,Code} = compile:forms(Forms),
+ [{M,Code}|make_modules(N-1, Fun)].
+
+make_form(S) ->
+ {ok,Toks,_} = erl_scan:string(S),
+ {ok,Form} = erl_parse:parse_form(Toks),
+ Form.
+
+prepare_modules(Ms) ->
+ [erlang:prepare_loading(M, Code) || {M,Code} <- Ms].
diff --git a/erts/emulator/test/nested_SUITE.erl b/erts/emulator/test/nested_SUITE.erl
index 7cfa837ee5..7af2873ce2 100644
--- a/erts/emulator/test/nested_SUITE.erl
+++ b/erts/emulator/test/nested_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,90 +20,74 @@
-module(nested_SUITE).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- case_in_case/1, case_in_after/1, catch_in_catch/1, bif_in_bif/1]).
+-export([all/0, suite/0,
+ case_in_case/1, case_in_after/1, catch_in_catch/1, bif_in_bif/1]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {seconds, 10}}].
all() ->
[case_in_case, case_in_after, catch_in_catch,
bif_in_bif].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
case_in_case(suite) -> [];
case_in_case(Config) when is_list(Config) ->
- ?line done = search_any([a], [{a, 1}]),
- ?line done = search_any([x], [{a, 1}]),
+ done = search_any([a], [{a, 1}]),
+ done = search_any([x], [{a, 1}]),
ok.
search_any([Key|Rest], List) ->
- ?line case case lists:keysearch(Key, 1, List) of
- {value, _} ->
- true;
- _ ->
- false
- end of
- true ->
- ok;
- false ->
- error;
- Other ->
- test_server:fail({other_result, Other})
- end,
- ?line search_any(Rest, List);
+ case case lists:keysearch(Key, 1, List) of
+ {value, _} ->
+ true;
+ _ ->
+ false
+ end of
+ true ->
+ ok;
+ false ->
+ error;
+ Other ->
+ ct:fail({other_result, Other})
+ end,
+ search_any(Rest, List);
search_any([], _) ->
done.
case_in_after(suite) -> [];
case_in_after(Config) when is_list(Config) ->
receive
- after case {x, y, z} of
- {x, y, z} -> 0
- end ->
- ok
- end,
+ after case {x, y, z} of
+ {x, y, z} -> 0
+ end ->
+ ok
+ end,
ok.
-catch_in_catch(doc) -> "Test a catch within a catch in the same function.";
-catch_in_catch(suite) -> [];
+%% Test a catch within a catch in the same function.
catch_in_catch(Config) when is_list(Config) ->
- ?line {outer, inner_exit} = catcher(),
+ {outer, inner_exit} = catcher(),
ok.
catcher() ->
case (catch
- case (catch ?MODULE:non_existing()) of % bogus function
- {'EXIT', _} ->
- inner_exit;
- Res1 ->
- {inner, Res1}
- end) of
- {'EXIT', _} ->
- outer_exit;
- Res2 ->
- {outer, Res2}
+ case (catch ?MODULE:non_existing()) of % bogus function
+ {'EXIT', _} ->
+ inner_exit;
+ Res1 ->
+ {inner, Res1}
+ end) of
+ {'EXIT', _} ->
+ outer_exit;
+ Res2 ->
+ {outer, Res2}
end.
-bif_in_bif(doc) -> "Test a BIF call within a BIF call.";
-bif_in_bif(suite) -> [];
+%% Test a BIF call within a BIF call.
bif_in_bif(Config) when is_list(Config) ->
Self = self(),
put(pid, Self),
diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl
index 2400505159..9c1694fa8a 100644
--- a/erts/emulator/test/nif_SUITE.erl
+++ b/erts/emulator/test/nif_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -22,14 +22,14 @@
%%-define(line_trace,true).
-define(CHECK(Exp,Got), check(Exp,Got,?LINE)).
-%%-define(CHECK(Exp,Got), ?line Exp = Got).
+%%-define(CHECK(Exp,Got), Exp = Got).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2,
- end_per_testcase/2, basic/1, reload/1, upgrade/1, heap_frag/1,
+-export([all/0, suite/0,
+ init_per_testcase/2, end_per_testcase/2,
+ basic/1, reload/1, upgrade/1, heap_frag/1,
+ t_on_load/1,
types/1, many_args/1, binaries/1, get_string/1, get_atom/1,
maps/1,
api_macros/1,
@@ -39,11 +39,15 @@
is_checks/1,
get_length/1, make_atom/1, make_string/1, reverse_list_test/1,
otp_9828/1,
- otp_9668/1, consume_timeslice/1, dirty_nif/1, dirty_nif_send/1,
- dirty_nif_exception/1, call_dirty_nif_exception/1, nif_schedule/1,
+ otp_9668/1, consume_timeslice/1, nif_schedule/1,
nif_exception/1, call_nif_exception/1,
nif_nan_and_inf/1, nif_atom_too_long/1,
- nif_monotonic_time/1, nif_time_offset/1, nif_convert_time_unit/1
+ nif_monotonic_time/1, nif_time_offset/1, nif_convert_time_unit/1,
+ nif_now_time/1, nif_cpu_time/1, nif_unique_integer/1,
+ nif_is_process_alive/1, nif_is_port_alive/1,
+ nif_term_to_binary/1, nif_binary_to_term/1,
+ nif_port_command/1,
+ nif_snprintf/1
]).
-export([many_args_100/100]).
@@ -65,6 +69,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[basic, reload, upgrade, heap_frag, types, many_args,
+ t_on_load,
binaries, get_string, get_atom, maps, api_macros, from_array,
iolist_as_binary, resource, resource_binary,
resource_takeover, threading, send, send2, send3,
@@ -72,184 +77,348 @@ all() ->
make_string,reverse_list_test,
otp_9828,
otp_9668, consume_timeslice,
- nif_schedule, dirty_nif, dirty_nif_send, dirty_nif_exception,
- nif_exception, nif_nan_and_inf, nif_atom_too_long,
- nif_monotonic_time, nif_time_offset, nif_convert_time_unit
- ].
-
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
+ nif_schedule, nif_exception, nif_nan_and_inf, nif_atom_too_long,
+ nif_monotonic_time, nif_time_offset, nif_convert_time_unit,
+ nif_now_time, nif_cpu_time, nif_unique_integer,
+ nif_is_process_alive, nif_is_port_alive,
+ nif_term_to_binary, nif_binary_to_term,
+ nif_port_command,
+ nif_snprintf].
+
+init_per_testcase(t_on_load, Config) ->
+ ets:new(nif_SUITE, [named_table]),
+ Config;
init_per_testcase(_Case, Config) ->
-% ?line Dog = ?t:timetrap(?t:seconds(60*60*24)),
Config.
+end_per_testcase(t_on_load, _Config) ->
+ ets:delete(nif_SUITE),
+ testcase_cleanup();
end_per_testcase(_Func, _Config) ->
- %%Dog = ?config(watchdog, Config),
- %%?t:timetrap_cancel(Dog),
+ testcase_cleanup().
+
+testcase_cleanup() ->
P1 = code:purge(nif_mod),
Del = code:delete(nif_mod),
P2 = code:purge(nif_mod),
io:format("fin purged=~p, deleted=~p and then purged=~p\n",[P1,Del,P2]).
-basic(doc) -> ["Basic smoke test of load_nif and a simple NIF call"];
-basic(suite) -> [];
+%% Basic smoke test of load_nif and a simple NIF call
basic(Config) when is_list(Config) ->
ensure_lib_loaded(Config),
- ?line true = (lib_version() =/= undefined),
- ?line [{load,1,1,101},{lib_version,1,2,102}] = call_history(),
- ?line [] = call_history(),
- ?line true = lists:member(?MODULE, erlang:system_info(taints)),
+ true = (lib_version() =/= undefined),
+ [{load,1,1,101},{lib_version,1,2,102}] = call_history(),
+ [] = call_history(),
+ true = lists:member(?MODULE, erlang:system_info(taints)),
ok.
-reload(doc) -> ["Test reload callback in nif lib"];
-reload(suite) -> [];
+%% Test reload callback in nif lib
reload(Config) when is_list(Config) ->
TmpMem = tmpmem(),
ensure_lib_loaded(Config),
- ?line Data = ?config(data_dir, Config),
- ?line File = filename:join(Data, "nif_mod"),
- ?line {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]),
- ?line {module,nif_mod} = erlang:load_module(nif_mod,Bin),
+ Data = proplists:get_value(data_dir, Config),
+ File = filename:join(Data, "nif_mod"),
+ {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]),
+ {module,nif_mod} = erlang:load_module(nif_mod,Bin),
- ?line ok = nif_mod:load_nif_lib(Config, 1),
+ ok = nif_mod:load_nif_lib(Config, 1),
- ?line hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()),
- ?line [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(),
+ hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()),
+ [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(),
- ?line ok = nif_mod:load_nif_lib(Config, 2),
- ?line 2 = nif_mod:lib_version(),
- ?line [{reload,2,1,201},{lib_version,2,2,202}] = nif_mod_call_history(),
+ ok = nif_mod:load_nif_lib(Config, 2),
+ 2 = nif_mod:lib_version(),
+ [{reload,2,1,201},{lib_version,2,2,202}] = nif_mod_call_history(),
- ?line ok = nif_mod:load_nif_lib(Config, 1),
- ?line 1 = nif_mod:lib_version(),
- ?line [{reload,1,1,101},{lib_version,1,2,102}] = nif_mod_call_history(),
+ ok = nif_mod:load_nif_lib(Config, 1),
+ 1 = nif_mod:lib_version(),
+ [{reload,1,1,101},{lib_version,1,2,102}] = nif_mod_call_history(),
- ?line true = erlang:delete_module(nif_mod),
- ?line [] = nif_mod_call_history(),
+ true = erlang:delete_module(nif_mod),
+ [] = nif_mod_call_history(),
- %%?line false= check_process_code(Pid, nif_mod),
- ?line true = erlang:purge_module(nif_mod),
- ?line [{unload,1,3,103}] = nif_mod_call_history(),
+ %%false= check_process_code(Pid, nif_mod),
+ true = erlang:purge_module(nif_mod),
+ [{unload,1,3,103}] = nif_mod_call_history(),
- ?line true = lists:member(?MODULE, erlang:system_info(taints)),
- ?line true = lists:member(nif_mod, erlang:system_info(taints)),
- ?line verify_tmpmem(TmpMem),
+ true = lists:member(?MODULE, erlang:system_info(taints)),
+ true = lists:member(nif_mod, erlang:system_info(taints)),
+ verify_tmpmem(TmpMem),
ok.
-upgrade(doc) -> ["Test upgrade callback in nif lib"];
-upgrade(suite) -> [];
+%% Test upgrade callback in nif lib
upgrade(Config) when is_list(Config) ->
TmpMem = tmpmem(),
ensure_lib_loaded(Config),
- ?line Data = ?config(data_dir, Config),
- ?line File = filename:join(Data, "nif_mod"),
- ?line {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]),
- ?line {module,nif_mod} = erlang:load_module(nif_mod,Bin),
+ Data = proplists:get_value(data_dir, Config),
+ File = filename:join(Data, "nif_mod"),
+ {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]),
+ {module,nif_mod} = erlang:load_module(nif_mod,Bin),
- ?line ok = nif_mod:load_nif_lib(Config, 1),
- ?line {Pid,MRef} = nif_mod:start(),
- ?line 1 = call(Pid,lib_version),
+ ok = nif_mod:load_nif_lib(Config, 1),
+ {Pid,MRef} = nif_mod:start(),
+ 1 = call(Pid,lib_version),
- ?line hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()),
- ?line [{load,1,1,101},{lib_version,1,2,102},{get_priv_data_ptr,1,3,103}] = nif_mod_call_history(),
+ hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()),
+ [{load,1,1,101},{lib_version,1,2,102},{get_priv_data_ptr,1,3,103}] = nif_mod_call_history(),
%% Module upgrade with same lib-version
- ?line {module,nif_mod} = erlang:load_module(nif_mod,Bin),
- ?line undefined = nif_mod:lib_version(),
- ?line 1 = call(Pid,lib_version),
- ?line [{lib_version,1,4,104}] = nif_mod_call_history(),
+ {module,nif_mod} = erlang:load_module(nif_mod,Bin),
+ undefined = nif_mod:lib_version(),
+ 1 = call(Pid,lib_version),
+ [{lib_version,1,4,104}] = nif_mod_call_history(),
+
+ ok = nif_mod:load_nif_lib(Config, 1),
+ 1 = nif_mod:lib_version(),
+ [{upgrade,1,5,105},{lib_version,1,6,106}] = nif_mod_call_history(),
+
+ upgraded = call(Pid,upgrade),
+ false = check_process_code(Pid, nif_mod),
+ true = erlang:purge_module(nif_mod),
+ [{unload,1,7,107}] = nif_mod_call_history(),
+
+ 1 = nif_mod:lib_version(),
+ [{lib_version,1,8,108}] = nif_mod_call_history(),
+
+ true = erlang:delete_module(nif_mod),
+ [] = nif_mod_call_history(),
+
+ %% Repeat upgrade again but from old (deleted) instance
+ {module,nif_mod} = erlang:load_module(nif_mod,Bin),
+ undefined = nif_mod:lib_version(),
+ 1 = call(Pid,lib_version),
+ [{lib_version,1,9,109}] = nif_mod_call_history(),
+
+ ok = nif_mod:load_nif_lib(Config, 1),
+ 1 = nif_mod:lib_version(),
+ [{upgrade,1,10,110},{lib_version,1,11,111}] = nif_mod_call_history(),
+
+ upgraded = call(Pid,upgrade),
+ false = check_process_code(Pid, nif_mod),
+ true = erlang:purge_module(nif_mod),
+ [{unload,1,12,112}] = nif_mod_call_history(),
+
+ 1 = nif_mod:lib_version(),
+ [{lib_version,1,13,113}] = nif_mod_call_history(),
+
+ true = erlang:delete_module(nif_mod),
+ [] = nif_mod_call_history(),
+
+
+ Pid ! die,
+ {'DOWN', MRef, process, Pid, normal} = receive_any(),
+ false = check_process_code(Pid, nif_mod),
+ true = erlang:purge_module(nif_mod),
+ [{unload,1,14,114}] = nif_mod_call_history(),
+
+ %% Module upgrade with different lib version
+ {module,nif_mod} = erlang:load_module(nif_mod,Bin),
+ undefined = nif_mod:lib_version(),
+ {Pid2,MRef2} = nif_mod:start(),
+ undefined = call(Pid2,lib_version),
+
+ ok = nif_mod:load_nif_lib(Config, 1),
+ hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()),
+ 1 = call(Pid2,lib_version),
+ [{load,1,1,101},{get_priv_data_ptr,1,2,102},{lib_version,1,3,103}] = nif_mod_call_history(),
+
+ {module,nif_mod} = erlang:load_module(nif_mod,Bin),
+ undefined = nif_mod:lib_version(),
+ [] = nif_mod_call_history(),
+ 1 = call(Pid2,lib_version),
+ [{lib_version,1,4,104}] = nif_mod_call_history(),
+
+ ok = nif_mod:load_nif_lib(Config, 2),
+ 2 = nif_mod:lib_version(),
+ [{upgrade,2,1,201},{lib_version,2,2,202}] = nif_mod_call_history(),
+
+ 1 = call(Pid2,lib_version),
+ [{lib_version,1,5,105}] = nif_mod_call_history(),
+
+ upgraded = call(Pid2,upgrade),
+ false = check_process_code(Pid2, nif_mod),
+ true = erlang:purge_module(nif_mod),
+ [{unload,1,6,106}] = nif_mod_call_history(),
+
+ 2 = nif_mod:lib_version(),
+ [{lib_version,2,3,203}] = nif_mod_call_history(),
+
+ true = erlang:delete_module(nif_mod),
+ [] = nif_mod_call_history(),
+
+
+ %% Reverse upgrade but from old (deleted) instance
+ {module,nif_mod} = erlang:load_module(nif_mod,Bin),
+ undefined = nif_mod:lib_version(),
+ [] = nif_mod_call_history(),
+ 2 = call(Pid2,lib_version),
+ [{lib_version,2,4,204}] = nif_mod_call_history(),
+
+ ok = nif_mod:load_nif_lib(Config, 1),
+ 1 = nif_mod:lib_version(),
+ [{upgrade,1,1,101},{lib_version,1,2,102}] = nif_mod_call_history(),
+
+ 2 = call(Pid2,lib_version),
+ [{lib_version,2,5,205}] = nif_mod_call_history(),
+
+ upgraded = call(Pid2,upgrade),
+ false = check_process_code(Pid2, nif_mod),
+ true = erlang:purge_module(nif_mod),
+ [{unload,2,6,206}] = nif_mod_call_history(),
+
+ 1 = nif_mod:lib_version(),
+ [{lib_version,1,3,103}] = nif_mod_call_history(),
+
+ true = erlang:delete_module(nif_mod),
+ [] = nif_mod_call_history(),
+
+
+ Pid2 ! die,
+ {'DOWN', MRef2, process, Pid2, normal} = receive_any(),
+ false= check_process_code(Pid2, nif_mod),
+ true = erlang:purge_module(nif_mod),
+ [{unload,1,4,104}] = nif_mod_call_history(),
+
+ true = lists:member(?MODULE, erlang:system_info(taints)),
+ true = lists:member(nif_mod, erlang:system_info(taints)),
+ verify_tmpmem(TmpMem),
+ ok.
+
+%% Test loading/upgrade in on_load
+t_on_load(Config) when is_list(Config) ->
+ TmpMem = tmpmem(),
+ ensure_lib_loaded(Config),
+
+ Data = proplists:get_value(data_dir, Config),
+ File = filename:join(Data, "nif_mod"),
+ {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors,
+ {d,'USE_ON_LOAD'}]),
+
+ %% Use ETS to tell nif_mod:on_load what to do
+ ets:insert(nif_SUITE, {data_dir, Data}),
+ ets:insert(nif_SUITE, {lib_version, 1}),
+ {module,nif_mod} = code:load_binary(nif_mod,File,Bin),
+ hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()),
+ [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(),
+
+ {Pid,MRef} = nif_mod:start(),
+ 1 = call(Pid,lib_version),
+ [{lib_version,1,3,103}] = nif_mod_call_history(),
+
+ %% Module upgrade with same lib-version
+ {module,nif_mod} = code:load_binary(nif_mod,File,Bin),
+ 1 = nif_mod:lib_version(),
+ 1 = call(Pid,lib_version),
+ [{upgrade,1,4,104},{lib_version,1,5,105},{lib_version,1,6,106}] = nif_mod_call_history(),
+
+ upgraded = call(Pid,upgrade),
+ false = check_process_code(Pid, nif_mod),
+ true = code:soft_purge(nif_mod),
+ [{unload,1,7,107}] = nif_mod_call_history(),
+
+ 1 = nif_mod:lib_version(),
+ [{lib_version,1,8,108}] = nif_mod_call_history(),
+
+ true = code:delete(nif_mod),
+ [] = nif_mod_call_history(),
- ?line ok = nif_mod:load_nif_lib(Config, 1),
- ?line 1 = nif_mod:lib_version(),
- ?line [{upgrade,1,5,105},{lib_version,1,6,106}] = nif_mod_call_history(),
+ %% Repeat upgrade again but from old (deleted) instance
+ {module,nif_mod} = code:load_binary(nif_mod,File,Bin),
+ [{upgrade,1,9,109}] = nif_mod_call_history(),
+ 1 = nif_mod:lib_version(),
+ 1 = call(Pid,lib_version),
+ [{lib_version,1,10,110},{lib_version,1,11,111}] = nif_mod_call_history(),
- ?line upgraded = call(Pid,upgrade),
- ?line false = check_process_code(Pid, nif_mod),
- ?line true = erlang:purge_module(nif_mod),
- ?line [{unload,1,7,107}] = nif_mod_call_history(),
+ upgraded = call(Pid,upgrade),
+ false = check_process_code(Pid, nif_mod),
+ true = code:soft_purge(nif_mod),
+ [{unload,1,12,112}] = nif_mod_call_history(),
- ?line 1 = nif_mod:lib_version(),
- ?line [{lib_version,1,8,108}] = nif_mod_call_history(),
+ 1 = nif_mod:lib_version(),
+ [{lib_version,1,13,113}] = nif_mod_call_history(),
- ?line true = erlang:delete_module(nif_mod),
- ?line [] = nif_mod_call_history(),
+ true = code:delete(nif_mod),
+ [] = nif_mod_call_history(),
- ?line Pid ! die,
- ?line {'DOWN', MRef, process, Pid, normal} = receive_any(),
- ?line false = check_process_code(Pid, nif_mod),
- ?line true = erlang:purge_module(nif_mod),
- ?line [{unload,1,9,109}] = nif_mod_call_history(),
+
+ Pid ! die,
+ {'DOWN', MRef, process, Pid, normal} = receive_any(),
+ false = check_process_code(Pid, nif_mod),
+ true = code:soft_purge(nif_mod),
+ [{unload,1,14,114}] = nif_mod_call_history(),
%% Module upgrade with different lib version
- ?line {module,nif_mod} = erlang:load_module(nif_mod,Bin),
- ?line undefined = nif_mod:lib_version(),
- ?line {Pid2,MRef2} = nif_mod:start(),
- ?line undefined = call(Pid2,lib_version),
-
- ?line ok = nif_mod:load_nif_lib(Config, 1),
- ?line hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()),
- ?line 1 = call(Pid2,lib_version),
- ?line [{load,1,1,101},{get_priv_data_ptr,1,2,102},{lib_version,1,3,103}] = nif_mod_call_history(),
-
- ?line {module,nif_mod} = erlang:load_module(nif_mod,Bin),
- ?line undefined = nif_mod:lib_version(),
- ?line [] = nif_mod_call_history(),
- ?line 1 = call(Pid2,lib_version),
- ?line [{lib_version,1,4,104}] = nif_mod_call_history(),
-
- ?line ok = nif_mod:load_nif_lib(Config, 2),
- ?line 2 = nif_mod:lib_version(),
- ?line [{upgrade,2,1,201},{lib_version,2,2,202}] = nif_mod_call_history(),
-
- ?line 1 = call(Pid2,lib_version),
- ?line [{lib_version,1,5,105}] = nif_mod_call_history(),
-
- ?line upgraded = call(Pid2,upgrade),
- ?line false = check_process_code(Pid2, nif_mod),
- ?line true = erlang:purge_module(nif_mod),
- ?line [{unload,1,6,106}] = nif_mod_call_history(),
-
- ?line 2 = nif_mod:lib_version(),
- ?line [{lib_version,2,3,203}] = nif_mod_call_history(),
-
- ?line true = erlang:delete_module(nif_mod),
- ?line [] = nif_mod_call_history(),
-
- ?line Pid2 ! die,
- ?line {'DOWN', MRef2, process, Pid2, normal} = receive_any(),
- ?line false= check_process_code(Pid2, nif_mod),
- ?line true = erlang:purge_module(nif_mod),
- ?line [{unload,2,4,204}] = nif_mod_call_history(),
-
- ?line true = lists:member(?MODULE, erlang:system_info(taints)),
- ?line true = lists:member(nif_mod, erlang:system_info(taints)),
- ?line verify_tmpmem(TmpMem),
+ {module,nif_mod} = code:load_binary(nif_mod,File,Bin),
+ hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()),
+ [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(),
+
+ 1 = nif_mod:lib_version(),
+ {Pid2,MRef2} = nif_mod:start(),
+ 1 = call(Pid2,lib_version),
+ [{lib_version,1,3,103},{lib_version,1,4,104}] = nif_mod_call_history(),
+
+ true = ets:insert(nif_SUITE,{lib_version,2}),
+ {module,nif_mod} = code:load_binary(nif_mod,File,Bin),
+ [{upgrade,2,1,201}] = nif_mod_call_history(),
+
+ 2 = nif_mod:lib_version(),
+ 1 = call(Pid2,lib_version),
+ [{lib_version,2,2,202},{lib_version,1,5,105}] = nif_mod_call_history(),
+
+ upgraded = call(Pid2,upgrade),
+ false = check_process_code(Pid2, nif_mod),
+ true = code:soft_purge(nif_mod),
+ [{unload,1,6,106}] = nif_mod_call_history(),
+
+ 2 = nif_mod:lib_version(),
+ 2 = call(Pid2,lib_version),
+ [{lib_version,2,3,203},{lib_version,2,4,204}] = nif_mod_call_history(),
+
+ true = code:delete(nif_mod),
+ [] = nif_mod_call_history(),
+
+ %% Reverse upgrade but from old (deleted) instance
+ ets:insert(nif_SUITE,{lib_version,1}),
+ {module,nif_mod} = code:load_binary(nif_mod,File,Bin),
+ [{upgrade,1,1,101}] = nif_mod_call_history(),
+
+ 1 = nif_mod:lib_version(),
+ 2 = call(Pid2,lib_version),
+ [{lib_version,1,2,102},{lib_version,2,5,205}] = nif_mod_call_history(),
+
+ upgraded = call(Pid2,upgrade),
+ false = check_process_code(Pid2, nif_mod),
+ true = code:soft_purge(nif_mod),
+ [{unload,2,6,206}] = nif_mod_call_history(),
+
+ 1 = nif_mod:lib_version(),
+ [{lib_version,1,3,103}] = nif_mod_call_history(),
+
+ true = code:delete(nif_mod),
+ [] = nif_mod_call_history(),
+
+
+ Pid2 ! die,
+ {'DOWN', MRef2, process, Pid2, normal} = receive_any(),
+ false= check_process_code(Pid2, nif_mod),
+ true = code:soft_purge(nif_mod),
+ [{unload,1,4,104}] = nif_mod_call_history(),
+
+ true = lists:member(?MODULE, erlang:system_info(taints)),
+ true = lists:member(nif_mod, erlang:system_info(taints)),
+ verify_tmpmem(TmpMem),
ok.
-heap_frag(doc) -> ["Test NIF building heap fragments"];
-heap_frag(suite) -> [];
+
+%% Test NIF building heap fragments
heap_frag(Config) when is_list(Config) ->
TmpMem = tmpmem(),
ensure_lib_loaded(Config),
heap_frag_do(1,1000000),
- ?line verify_tmpmem(TmpMem),
+ verify_tmpmem(TmpMem),
ok.
heap_frag_do(N, Max) when N > Max ->
@@ -260,12 +429,11 @@ heap_frag_do(N, Max) ->
L = list_seq(N),
heap_frag_do(((N*5) div 4) + 1, Max).
-types(doc) -> ["Type tests"];
-types(suite) -> [];
+%% Type tests
types(Config) when is_list(Config) ->
TmpMem = tmpmem(),
ensure_lib_loaded(Config),
- ?line ok = type_test(),
+ ok = type_test(),
lists:foreach(fun(Tpl) ->
Lst = erlang:tuple_to_list(Tpl),
Lst = tuple_2_list(Tpl)
@@ -290,18 +458,18 @@ types(Config) when is_list(Config) ->
R1 = echo_int(I),
%%io:format("echo_int(~p) -> ~p\n", [I, R1]),
R2 = my_echo_int(I, Limits),
- ?line R1 = R2,
- ?line true = (R1 =:= R2),
- ?line true = (R1 == R2)
+ R1 = R2,
+ true = (R1 =:= R2),
+ true = (R1 == R2)
end, int_list()),
- ?line verify_tmpmem(TmpMem),
- ?line true = (compare(-1294536544000, -1178704800000) < 0),
- ?line true = (compare(-1178704800000, -1294536544000) > 0),
- ?line true = (compare(-295147905179352825856, -36893488147419103232) < 0),
- ?line true = (compare(-36893488147419103232, -295147905179352825856) > 0),
- ?line true = (compare(-29514790517935282585612345678, -36893488147419103232) < 0),
- ?line true = (compare(-36893488147419103232, -29514790517935282585612345678) > 0),
+ verify_tmpmem(TmpMem),
+ true = (compare(-1294536544000, -1178704800000) < 0),
+ true = (compare(-1178704800000, -1294536544000) > 0),
+ true = (compare(-295147905179352825856, -36893488147419103232) < 0),
+ true = (compare(-36893488147419103232, -295147905179352825856) > 0),
+ true = (compare(-29514790517935282585612345678, -36893488147419103232) < 0),
+ true = (compare(-36893488147419103232, -29514790517935282585612345678) > 0),
ok.
int_list() ->
@@ -329,15 +497,15 @@ eq_cmp(A,B) ->
eq_cmp_do({A,B},{A,B}).
eq_cmp_do(A,B) ->
- %%?t:format("compare ~p and ~p\n",[A,B]),
+ %%io:format("compare ~p and ~p\n",[A,B]),
Eq = (A =:= B),
- ?line Eq = is_identical(A,B),
- ?line Cmp = if
+ Eq = is_identical(A,B),
+ Cmp = if
A < B -> -1;
A == B -> 0;
A > B -> 1
end,
- ?line Cmp = case compare(A,B) of
+ Cmp = case compare(A,B) of
C when is_integer(C), C < 0 -> -1;
0 -> 0;
C when is_integer(C) -> 1
@@ -345,47 +513,45 @@ eq_cmp_do(A,B) ->
ok.
-many_args(doc) -> ["Test NIF with many arguments"];
-many_args(suite) -> [];
+%% Test NIF with many arguments
many_args(Config) when is_list(Config) ->
TmpMem = tmpmem(),
- ?line ensure_lib_loaded(Config ,1),
- ?line ok = apply(?MODULE,many_args_100,lists:seq(1,100)),
- ?line ok = many_args_100(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100),
- ?line verify_tmpmem(TmpMem),
+ ensure_lib_loaded(Config ,1),
+ ok = apply(?MODULE,many_args_100,lists:seq(1,100)),
+ ok = many_args_100(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100),
+ verify_tmpmem(TmpMem),
ok.
-binaries(doc) -> ["Test NIF binary handling."];
-binaries(suite) -> [];
+%% Test NIF binary handling.
binaries(Config) when is_list(Config) ->
TmpMem = tmpmem(),
- ?line ensure_lib_loaded(Config, 1),
- ?line RefcBin = list_to_binary(lists:seq(1,255)),
- ?line RefcBin = clone_bin(RefcBin),
- ?line HeapBin = list_to_binary(lists:seq(1,20)),
- ?line HeapBin = clone_bin(HeapBin),
- ?line <<_:8,Sub1:6/binary,_/binary>> = RefcBin,
- ?line <<_:8,Sub2:6/binary,_/binary>> = HeapBin,
- ?line Sub1 = Sub2,
- ?line Sub1 = clone_bin(Sub1),
- ?line Sub2 = clone_bin(Sub2),
- ?line <<_:9,Sub3:6/binary,_/bitstring>> = RefcBin,
- ?line <<_:9,Sub4:6/binary,_/bitstring>> = HeapBin,
- ?line Sub3 = Sub4,
- ?line Sub3 = clone_bin(Sub3),
- ?line Sub4 = clone_bin(Sub4),
+ ensure_lib_loaded(Config, 1),
+ RefcBin = list_to_binary(lists:seq(1,255)),
+ RefcBin = clone_bin(RefcBin),
+ HeapBin = list_to_binary(lists:seq(1,20)),
+ HeapBin = clone_bin(HeapBin),
+ <<_:8,Sub1:6/binary,_/binary>> = RefcBin,
+ <<_:8,Sub2:6/binary,_/binary>> = HeapBin,
+ Sub1 = Sub2,
+ Sub1 = clone_bin(Sub1),
+ Sub2 = clone_bin(Sub2),
+ <<_:9,Sub3:6/binary,_/bitstring>> = RefcBin,
+ <<_:9,Sub4:6/binary,_/bitstring>> = HeapBin,
+ Sub3 = Sub4,
+ Sub3 = clone_bin(Sub3),
+ Sub4 = clone_bin(Sub4),
%% When NIFs get bitstring support
- %%?line <<_:8,Sub5:27/bitstring,_/bitstring>> = RefcBin,
- %%?line <<_:8,Sub6:27/bitstring,_/bitstring>> = HeapBin,
- %%?line Sub5 = Sub6,
- %%?line Sub5 = clone_bin(Sub5),
- %%?line Sub6 = clone_bin(Sub6),
- %%?line <<_:9,Sub7:27/bitstring,_/bitstring>> = RefcBin,
- %%?line <<_:9,Sub8:27/bitstring,_/bitstring>> = HeapBin,
- %%?line Sub7 = Sub8,
- %%?line Sub7 = clone_bin(Sub7),
- %%?line Sub8 = clone_bin(Sub8),
- %%?line <<>> = clone_bin(<<>>),
+ %%<<_:8,Sub5:27/bitstring,_/bitstring>> = RefcBin,
+ %%<<_:8,Sub6:27/bitstring,_/bitstring>> = HeapBin,
+ %%Sub5 = Sub6,
+ %%Sub5 = clone_bin(Sub5),
+ %%Sub6 = clone_bin(Sub6),
+ %%<<_:9,Sub7:27/bitstring,_/bitstring>> = RefcBin,
+ %%<<_:9,Sub8:27/bitstring,_/bitstring>> = HeapBin,
+ %%Sub7 = Sub8,
+ %%Sub7 = clone_bin(Sub7),
+ %%Sub8 = clone_bin(Sub8),
+ %%<<>> = clone_bin(<<>>),
<<_:8,SubBinA:200/binary,_/binary>> = RefcBin,
<<_:9,SubBinB:200/binary,_/bitstring>> = RefcBin,
@@ -398,56 +564,53 @@ binaries(Config) when is_list(Config) ->
test_make_sub_bin(SubBinC),
test_make_sub_bin(SubBinD),
- ?line verify_tmpmem(TmpMem),
+ verify_tmpmem(TmpMem),
ok.
test_make_sub_bin(Bin) ->
Size = byte_size(Bin),
Rest10 = Size - 10,
Rest1 = Size - 1,
- ?line Bin = make_sub_bin(Bin, 0, Size),
+ Bin = make_sub_bin(Bin, 0, Size),
<<_:10/binary,Sub0:Rest10/binary>> = Bin,
- ?line Sub0 = make_sub_bin(Bin, 10, Rest10),
+ Sub0 = make_sub_bin(Bin, 10, Rest10),
<<Sub1:10/binary,_/binary>> = Bin,
- ?line Sub1 = make_sub_bin(Bin, 0, 10),
+ Sub1 = make_sub_bin(Bin, 0, 10),
<<_:7/binary,Sub2:10/binary,_/binary>> = Bin,
- ?line Sub2 = make_sub_bin(Bin, 7, 10),
- ?line <<>> = make_sub_bin(Bin, 0, 0),
- ?line <<>> = make_sub_bin(Bin, 10, 0),
- ?line <<>> = make_sub_bin(Bin, Rest1, 0),
- ?line <<>> = make_sub_bin(Bin, Size, 0),
+ Sub2 = make_sub_bin(Bin, 7, 10),
+ <<>> = make_sub_bin(Bin, 0, 0),
+ <<>> = make_sub_bin(Bin, 10, 0),
+ <<>> = make_sub_bin(Bin, Rest1, 0),
+ <<>> = make_sub_bin(Bin, Size, 0),
ok.
-get_string(doc) -> ["Test enif_get_string"];
-get_string(suite) -> [];
+%% Test enif_get_string
get_string(Config) when is_list(Config) ->
- ?line ensure_lib_loaded(Config, 1),
- ?line {7, <<"hejsan",0,_:3/binary>>} = string_to_bin("hejsan",10),
- ?line {7, <<"hejsan",0,_>>} = string_to_bin("hejsan",8),
- ?line {7, <<"hejsan",0>>} = string_to_bin("hejsan",7),
- ?line {-6, <<"hejsa",0>>} = string_to_bin("hejsan",6),
- ?line {-5, <<"hejs",0>>} = string_to_bin("hejsan",5),
- ?line {-1, <<0>>} = string_to_bin("hejsan",1),
- ?line {0, <<>>} = string_to_bin("hejsan",0),
- ?line {1, <<0>>} = string_to_bin("",1),
- ?line {0, <<>>} = string_to_bin("",0),
+ ensure_lib_loaded(Config, 1),
+ {7, <<"hejsan",0,_:3/binary>>} = string_to_bin("hejsan",10),
+ {7, <<"hejsan",0,_>>} = string_to_bin("hejsan",8),
+ {7, <<"hejsan",0>>} = string_to_bin("hejsan",7),
+ {-6, <<"hejsa",0>>} = string_to_bin("hejsan",6),
+ {-5, <<"hejs",0>>} = string_to_bin("hejsan",5),
+ {-1, <<0>>} = string_to_bin("hejsan",1),
+ {0, <<>>} = string_to_bin("hejsan",0),
+ {1, <<0>>} = string_to_bin("",1),
+ {0, <<>>} = string_to_bin("",0),
ok.
-get_atom(doc) -> ["Test enif_get_atom"];
-get_atom(suite) -> [];
+%% Test enif_get_atom
get_atom(Config) when is_list(Config) ->
- ?line ensure_lib_loaded(Config, 1),
- ?line {7, <<"hejsan",0,_:3/binary>>} = atom_to_bin(hejsan,10),
- ?line {7, <<"hejsan",0,_>>} = atom_to_bin(hejsan,8),
- ?line {7, <<"hejsan",0>>} = atom_to_bin(hejsan,7),
- ?line {0, <<_:6/binary>>} = atom_to_bin(hejsan,6),
- ?line {0, <<>>} = atom_to_bin(hejsan,0),
- ?line {1, <<0>>} = atom_to_bin('',1),
- ?line {0, <<>>} = atom_to_bin('',0),
+ ensure_lib_loaded(Config, 1),
+ {7, <<"hejsan",0,_:3/binary>>} = atom_to_bin(hejsan,10),
+ {7, <<"hejsan",0,_>>} = atom_to_bin(hejsan,8),
+ {7, <<"hejsan",0>>} = atom_to_bin(hejsan,7),
+ {0, <<_:6/binary>>} = atom_to_bin(hejsan,6),
+ {0, <<>>} = atom_to_bin(hejsan,0),
+ {1, <<0>>} = atom_to_bin('',1),
+ {0, <<>>} = atom_to_bin('',0),
ok.
-maps(doc) -> ["Test NIF maps handling."];
-maps(suite) -> [];
+%% Test NIF maps handling.
maps(Config) when is_list(Config) ->
TmpMem = tmpmem(),
Pairs = [{adam, "bert"}] ++
@@ -494,31 +657,28 @@ maps(Config) when is_list(Config) ->
ok.
-api_macros(doc) -> ["Test macros enif_make_list<N> and enif_make_tuple<N>"];
-api_macros(suite) -> [];
+%% Test macros enif_make_list<N> and enif_make_tuple<N>
api_macros(Config) when is_list(Config) ->
- ?line ensure_lib_loaded(Config, 1),
+ ensure_lib_loaded(Config, 1),
Expected = {[lists:seq(1,N) || N <- lists:seq(1,9)],
[list_to_tuple(lists:seq(1,N)) || N <- lists:seq(1,9)]
},
- ?line Expected = macros(list_to_tuple(lists:seq(1,9))),
+ Expected = macros(list_to_tuple(lists:seq(1,9))),
ok.
-from_array(doc) -> ["enif_make_[tuple|list]_from_array"];
-from_array(suite) -> [];
+%% enif_make_[tuple|list]_from_array
from_array(Config) when is_list(Config) ->
- ?line ensure_lib_loaded(Config, 1),
+ ensure_lib_loaded(Config, 1),
lists:foreach(fun(Tpl) ->
Lst = tuple_to_list(Tpl),
- ?line {Lst,Tpl} = tuple_2_list_and_tuple(Tpl)
+ {Lst,Tpl} = tuple_2_list_and_tuple(Tpl)
end,
[{}, {1,2,3}, {[4,5],[],{},{6,7}}, {{}}, {[]}]),
ok.
-iolist_as_binary(doc) -> ["enif_inspect_iolist_as_binary"];
-iolist_as_binary(suite) -> [];
+%% enif_inspect_iolist_as_binary
iolist_as_binary(Config) when is_list(Config) ->
- ?line ensure_lib_loaded(Config, 1),
+ ensure_lib_loaded(Config, 1),
TmpMem = tmpmem(),
List = [<<"hejsan">>, <<>>, [], [17], [<<>>],
[127,128,255,0],
@@ -527,18 +687,17 @@ iolist_as_binary(Config) when is_list(Config) ->
lists:foreach(fun(IoL) ->
B1 = erlang:iolist_to_binary(IoL),
- ?line B2 = iolist_2_bin(IoL),
- ?line B1 = B2
+ B2 = iolist_2_bin(IoL),
+ B1 = B2
end,
List),
- ?line verify_tmpmem(TmpMem),
+ verify_tmpmem(TmpMem),
ok.
-resource(doc) -> ["Test memory managed objects, aka 'resources'"];
-resource(suite) -> [];
+%% Test memory managed objects, aka 'resources'
resource(Config) when is_list(Config) ->
- ?line ensure_lib_loaded(Config, 1),
- ?line Type = get_resource_type(0),
+ ensure_lib_loaded(Config, 1),
+ Type = get_resource_type(0),
resource_hugo(Type),
resource_otto(Type),
resource_new(Type),
@@ -548,75 +707,75 @@ resource(Config) when is_list(Config) ->
resource_hugo(Type) ->
DtorCall = resource_hugo_do(Type),
erlang:garbage_collect(),
- ?line DtorCall = last_resource_dtor_call(),
+ DtorCall = last_resource_dtor_call(),
ok.
resource_hugo_do(Type) ->
HugoBin = <<"Hugo Hacker">>,
- ?line HugoPtr = alloc_resource(Type, HugoBin),
- ?line Hugo = make_resource(HugoPtr),
- ?line <<>> = Hugo,
+ HugoPtr = alloc_resource(Type, HugoBin),
+ Hugo = make_resource(HugoPtr),
+ <<>> = Hugo,
release_resource(HugoPtr),
erlang:garbage_collect(),
- ?line {HugoPtr,HugoBin} = get_resource(Type,Hugo),
+ {HugoPtr,HugoBin} = get_resource(Type,Hugo),
Pid = spawn_link(fun() ->
- receive {Pid, Type, Resource, Ptr, Bin} ->
- Pid ! {self(), got_it},
- receive {Pid, check_it} ->
- ?line {Ptr,Bin} = get_resource(Type,Resource),
- Pid ! {self(), ok}
- end
- end
- end),
+ receive {Pid, Type, Resource, Ptr, Bin} ->
+ Pid ! {self(), got_it},
+ receive {Pid, check_it} ->
+ {Ptr,Bin} = get_resource(Type,Resource),
+ Pid ! {self(), ok}
+ end
+ end
+ end),
Pid ! {self(), Type, Hugo, HugoPtr, HugoBin},
- ?line {Pid, got_it} = receive_any(),
+ {Pid, got_it} = receive_any(),
erlang:garbage_collect(), % just to make our ProcBin move in memory
Pid ! {self(), check_it},
- ?line {Pid, ok} = receive_any(),
- ?line [] = last_resource_dtor_call(),
- ?line {HugoPtr,HugoBin} = get_resource(Type,Hugo),
+ {Pid, ok} = receive_any(),
+ [] = last_resource_dtor_call(),
+ {HugoPtr,HugoBin} = get_resource(Type,Hugo),
{HugoPtr, HugoBin, 1}.
resource_otto(Type) ->
{OttoPtr, OttoBin} = resource_otto_do(Type),
erlang:garbage_collect(),
- ?line [] = last_resource_dtor_call(),
+ [] = last_resource_dtor_call(),
release_resource(OttoPtr),
- ?line {OttoPtr,OttoBin,1} = last_resource_dtor_call(),
+ {OttoPtr,OttoBin,1} = last_resource_dtor_call(),
ok.
resource_otto_do(Type) ->
OttoBin = <<"Otto Ordonnans">>,
- ?line OttoPtr = alloc_resource(Type, OttoBin),
- ?line Otto = make_resource(OttoPtr),
- ?line <<>> = Otto,
+ OttoPtr = alloc_resource(Type, OttoBin),
+ Otto = make_resource(OttoPtr),
+ <<>> = Otto,
%% forget resource term but keep referenced by NIF
{OttoPtr, OttoBin}.
resource_new(Type) ->
- ?line {PtrB,BinB} = resource_new_do1(Type),
+ {PtrB,BinB} = resource_new_do1(Type),
erlang:garbage_collect(),
- ?line {PtrB,BinB,1} = last_resource_dtor_call(),
+ {PtrB,BinB,1} = last_resource_dtor_call(),
ok.
resource_new_do1(Type) ->
- ?line {{PtrA,BinA}, {ResB,PtrB,BinB}} = resource_new_do2(Type),
+ {{PtrA,BinA}, {ResB,PtrB,BinB}} = resource_new_do2(Type),
erlang:garbage_collect(),
- ?line {PtrA,BinA,1} = last_resource_dtor_call(),
- ?line {PtrB,BinB} = get_resource(Type, ResB),
+ {PtrA,BinA,1} = last_resource_dtor_call(),
+ {PtrB,BinB} = get_resource(Type, ResB),
%% forget ResB and make it garbage
{PtrB,BinB}.
resource_new_do2(Type) ->
BinA = <<"NewA">>,
BinB = <<"NewB">>,
- ?line ResA = make_new_resource(Type, BinA),
- ?line ResB = make_new_resource(Type, BinB),
- ?line <<>> = ResA,
- ?line <<>> = ResB,
- ?line {PtrA,BinA} = get_resource(Type, ResA),
- ?line {PtrB,BinB} = get_resource(Type, ResB),
- ?line true = (PtrA =/= PtrB),
+ ResA = make_new_resource(Type, BinA),
+ ResB = make_new_resource(Type, BinB),
+ <<>> = ResA,
+ <<>> = ResB,
+ {PtrA,BinA} = get_resource(Type, ResA),
+ {PtrB,BinB} = get_resource(Type, ResB),
+ true = (PtrA =/= PtrB),
%% forget ResA and make it garbage
{{PtrA,BinA}, {ResB,PtrB,BinB}}.
@@ -625,22 +784,21 @@ resource_neg(TypeA) ->
catch exit(42), % dummy exception to purge saved stacktraces from earlier exception
erlang:garbage_collect(),
- ?line {_,_,2} = last_resource_dtor_call(),
+ {_,_,2} = last_resource_dtor_call(),
ok.
resource_neg_do(TypeA) ->
TypeB = get_resource_type(1),
ResA = make_new_resource(TypeA, <<"Arnold">>),
ResB= make_new_resource(TypeB, <<"Bobo">>),
- ?line {'EXIT',{badarg,_}} = (catch get_resource(TypeA, ResB)),
- ?line {'EXIT',{badarg,_}} = (catch get_resource(TypeB, ResA)),
+ {'EXIT',{badarg,_}} = (catch get_resource(TypeA, ResB)),
+ {'EXIT',{badarg,_}} = (catch get_resource(TypeB, ResA)),
ok.
-resource_binary(doc) -> ["Test enif_make_resource_binary"];
-resource_binary(suite) -> [];
+%% Test enif_make_resource_binary
resource_binary(Config) when is_list(Config) ->
- ?line ensure_lib_loaded(Config, 1),
- ?line {Ptr,Bin} = resource_binary_do(),
+ ensure_lib_loaded(Config, 1),
+ {Ptr,Bin} = resource_binary_do(),
erlang:garbage_collect(),
Last = last_resource_dtor_call(),
?CHECK({Ptr,Bin,1}, Last),
@@ -648,58 +806,57 @@ resource_binary(Config) when is_list(Config) ->
resource_binary_do() ->
Bin = <<"Hej Hopp i lingonskogen">>,
- ?line {Ptr,ResBin1} = make_new_resource_binary(Bin),
- ?line ResBin1 = Bin,
- ?line ResInfo = {Ptr,_} = get_resource(binary_resource_type,ResBin1),
+ {Ptr,ResBin1} = make_new_resource_binary(Bin),
+ ResBin1 = Bin,
+ ResInfo = {Ptr,_} = get_resource(binary_resource_type,ResBin1),
Papa = self(),
Forwarder = spawn_link(fun() -> forwarder(Papa) end),
io:format("sending to forwarder pid=~p\n",[Forwarder]),
Forwarder ! ResBin1,
ResBin2 = receive_any(),
- ?line ResBin2 = ResBin1,
- ?line ResInfo = get_resource(binary_resource_type,ResBin2),
+ ResBin2 = ResBin1,
+ ResInfo = get_resource(binary_resource_type,ResBin2),
Forwarder ! terminate,
- ?line {Forwarder, 1} = receive_any(),
+ {Forwarder, 1} = receive_any(),
erlang:garbage_collect(),
- ?line ResInfo = get_resource(binary_resource_type,ResBin1),
- ?line ResInfo = get_resource(binary_resource_type,ResBin2),
+ ResInfo = get_resource(binary_resource_type,ResBin1),
+ ResInfo = get_resource(binary_resource_type,ResBin2),
ResInfo.
-define(RT_CREATE,1).
-define(RT_TAKEOVER,2).
-resource_takeover(doc) -> ["Test resource takeover by module reload and upgrade"];
-resource_takeover(suite) -> [];
+%% Test resource takeover by module reload and upgrade
resource_takeover(Config) when is_list(Config) ->
TmpMem = tmpmem(),
ensure_lib_loaded(Config),
- ?line Data = ?config(data_dir, Config),
- ?line File = filename:join(Data, "nif_mod"),
- ?line {ok,nif_mod,ModBin} = compile:file(File, [binary,return_errors]),
- ?line {module,nif_mod} = erlang:load_module(nif_mod,ModBin),
-
- ?line ok = nif_mod:load_nif_lib(Config, 1,
- [{resource_type, 0, ?RT_CREATE, "resource_type_A",resource_dtor_A,
- ?RT_CREATE},
- {resource_type, 1, ?RT_CREATE, "resource_type_null_A",null,
- ?RT_CREATE},
- {resource_type, 2, ?RT_CREATE bor ?RT_TAKEOVER, "resource_type_A_null",resource_dtor_A,
- ?RT_CREATE},
- {resource_type, 3, ?RT_CREATE, "resource_type_B_goneX",resource_dtor_B,
- ?RT_CREATE},
- {resource_type, 4, ?RT_CREATE, "resource_type_null_goneX",null,
- ?RT_CREATE},
- {resource_type, null, ?RT_TAKEOVER, "Pink unicorn", resource_dtor_A,
- ?RT_TAKEOVER}
- ]),
-
- ?line hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()),
- ?line [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(),
+ Data = proplists:get_value(data_dir, Config),
+ File = filename:join(Data, "nif_mod"),
+ {ok,nif_mod,ModBin} = compile:file(File, [binary,return_errors]),
+ {module,nif_mod} = erlang:load_module(nif_mod,ModBin),
- ?line {Holder, _MRef} = spawn_opt(fun resource_holder/0, [link, monitor]),
+ ok = nif_mod:load_nif_lib(Config, 1,
+ [{resource_type, 0, ?RT_CREATE, "resource_type_A",resource_dtor_A,
+ ?RT_CREATE},
+ {resource_type, 1, ?RT_CREATE, "resource_type_null_A",null,
+ ?RT_CREATE},
+ {resource_type, 2, ?RT_CREATE bor ?RT_TAKEOVER, "resource_type_A_null",resource_dtor_A,
+ ?RT_CREATE},
+ {resource_type, 3, ?RT_CREATE, "resource_type_B_goneX",resource_dtor_B,
+ ?RT_CREATE},
+ {resource_type, 4, ?RT_CREATE, "resource_type_null_goneX",null,
+ ?RT_CREATE},
+ {resource_type, null, ?RT_TAKEOVER, "Pink unicorn", resource_dtor_A,
+ ?RT_TAKEOVER}
+ ]),
+
+ hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()),
+ [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(),
+
+ {Holder, _MRef} = spawn_opt(fun resource_holder/0, [link, monitor]),
{A1,BinA1} = make_resource(0,Holder,"A1"),
{A2,BinA2} = make_resource(0,Holder,"A2"),
@@ -719,91 +876,91 @@ resource_takeover(Config) when is_list(Config) ->
{NGX1,_BinNGX1} = make_resource(4,Holder,"NGX1"),
{NGX2,_BinNGX2} = make_resource(4,Holder,"NGX2"),
- ?line [] = nif_mod_call_history(),
+ [] = nif_mod_call_history(),
- ?line ok = forget_resource(A1),
- ?line [{{resource_dtor_A_v1,BinA1},1,3,103}] = nif_mod_call_history(),
+ ok = forget_resource(A1),
+ [{{resource_dtor_A_v1,BinA1},1,3,103}] = nif_mod_call_history(),
- ?line ok = forget_resource(NA1),
- ?line [] = nif_mod_call_history(), % no dtor
+ ok = forget_resource(NA1),
+ [] = nif_mod_call_history(), % no dtor
- ?line ok = forget_resource(AN1),
+ ok = forget_resource(AN1),
?CHECK([{{resource_dtor_A_v1,BinAN1},1,4,104}] , nif_mod_call_history()),
- ?line ok = forget_resource(BGX1),
+ ok = forget_resource(BGX1),
?CHECK([{{resource_dtor_B_v1,BinBGX1},1,5,105}], nif_mod_call_history()),
- ?line ok = forget_resource(NGX1),
+ ok = forget_resource(NGX1),
?CHECK([], nif_mod_call_history()), % no dtor
- ?line ok = nif_mod:load_nif_lib(Config, 2,
- [{resource_type, 0, ?RT_TAKEOVER, "resource_type_A",resource_dtor_A,
- ?RT_TAKEOVER},
- {resource_type, 1, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",resource_dtor_A,
- ?RT_TAKEOVER},
- {resource_type, 2, ?RT_TAKEOVER, "resource_type_A_null",null,
- ?RT_TAKEOVER},
- {resource_type, null, ?RT_TAKEOVER, "Pink unicorn", resource_dtor_A,
- ?RT_TAKEOVER},
- {resource_type, null, ?RT_CREATE, "resource_type_B_goneX",resource_dtor_B,
- ?RT_CREATE},
- {resource_type, null, ?RT_CREATE, "resource_type_null_goneX",null,
- ?RT_CREATE},
- {resource_type, 3, ?RT_CREATE, "resource_type_B_goneY",resource_dtor_B,
- ?RT_CREATE},
- {resource_type, 4, ?RT_CREATE, "resource_type_null_goneY",null,
- ?RT_CREATE}
- ]),
+ ok = nif_mod:load_nif_lib(Config, 2,
+ [{resource_type, 0, ?RT_TAKEOVER, "resource_type_A",resource_dtor_A,
+ ?RT_TAKEOVER},
+ {resource_type, 1, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",resource_dtor_A,
+ ?RT_TAKEOVER},
+ {resource_type, 2, ?RT_TAKEOVER, "resource_type_A_null",null,
+ ?RT_TAKEOVER},
+ {resource_type, null, ?RT_TAKEOVER, "Pink unicorn", resource_dtor_A,
+ ?RT_TAKEOVER},
+ {resource_type, null, ?RT_CREATE, "resource_type_B_goneX",resource_dtor_B,
+ ?RT_CREATE},
+ {resource_type, null, ?RT_CREATE, "resource_type_null_goneX",null,
+ ?RT_CREATE},
+ {resource_type, 3, ?RT_CREATE, "resource_type_B_goneY",resource_dtor_B,
+ ?RT_CREATE},
+ {resource_type, 4, ?RT_CREATE, "resource_type_null_goneY",null,
+ ?RT_CREATE}
+ ]),
?CHECK([{reload,2,1,201}], nif_mod_call_history()),
- ?line BinA2 = read_resource(0,A2),
- ?line ok = forget_resource(A2),
+ BinA2 = read_resource(0,A2),
+ ok = forget_resource(A2),
?CHECK([{{resource_dtor_A_v2,BinA2},2,2,202}], nif_mod_call_history()),
- ?line ok = forget_resource(NA2),
+ ok = forget_resource(NA2),
?CHECK([{{resource_dtor_A_v2,BinNA2},2,3,203}], nif_mod_call_history()),
- ?line ok = forget_resource(AN2),
+ ok = forget_resource(AN2),
?CHECK([], nif_mod_call_history()), % no dtor
- ?line ok = forget_resource(BGX2), % calling dtor in orphan library v1 still loaded
+ ok = forget_resource(BGX2), % calling dtor in orphan library v1 still loaded
?CHECK([{{resource_dtor_B_v1,BinBGX2},1,6,106}], nif_mod_call_history()),
% How to test that lib v1 is closed here?
- ?line ok = forget_resource(NGX2),
+ ok = forget_resource(NGX2),
?CHECK([], nif_mod_call_history()), % no dtor
{BGY1,BinBGY1} = make_resource(3,Holder,"BGY1"),
{NGY1,_BinNGY1} = make_resource(4,Holder,"NGY1"),
%% Module upgrade with same lib-version
- ?line {module,nif_mod} = erlang:load_module(nif_mod,ModBin),
- ?line undefined = nif_mod:lib_version(),
- ?line ok = nif_mod:load_nif_lib(Config, 2,
- [{resource_type, 2, ?RT_TAKEOVER, "resource_type_A",resource_dtor_B,
- ?RT_TAKEOVER},
- {resource_type, 0, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",null,
- ?RT_TAKEOVER},
- {resource_type, 1, ?RT_TAKEOVER, "resource_type_A_null",resource_dtor_A,
- ?RT_TAKEOVER},
- {resource_type, null, ?RT_TAKEOVER, "Pink elephant", resource_dtor_A,
- ?RT_TAKEOVER},
- {resource_type, 3, ?RT_CREATE, "resource_type_B_goneZ",resource_dtor_B,
- ?RT_CREATE},
- {resource_type, 4, ?RT_CREATE, "resource_type_null_goneZ",null,
- ?RT_CREATE}
- ]),
-
- ?line 2 = nif_mod:lib_version(),
+ {module,nif_mod} = erlang:load_module(nif_mod,ModBin),
+ undefined = nif_mod:lib_version(),
+ ok = nif_mod:load_nif_lib(Config, 2,
+ [{resource_type, 2, ?RT_TAKEOVER, "resource_type_A",resource_dtor_B,
+ ?RT_TAKEOVER},
+ {resource_type, 0, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",null,
+ ?RT_TAKEOVER},
+ {resource_type, 1, ?RT_TAKEOVER, "resource_type_A_null",resource_dtor_A,
+ ?RT_TAKEOVER},
+ {resource_type, null, ?RT_TAKEOVER, "Pink elephant", resource_dtor_A,
+ ?RT_TAKEOVER},
+ {resource_type, 3, ?RT_CREATE, "resource_type_B_goneZ",resource_dtor_B,
+ ?RT_CREATE},
+ {resource_type, 4, ?RT_CREATE, "resource_type_null_goneZ",null,
+ ?RT_CREATE}
+ ]),
+
+ 2 = nif_mod:lib_version(),
?CHECK([{upgrade,2,4,204},{lib_version,2,5,205}], nif_mod_call_history()),
- ?line ok = forget_resource(A3),
+ ok = forget_resource(A3),
?CHECK([{{resource_dtor_B_v2,BinA3},2,6,206}], nif_mod_call_history()),
- ?line ok = forget_resource(NA3),
+ ok = forget_resource(NA3),
?CHECK([], nif_mod_call_history()),
- ?line ok = forget_resource(AN3),
+ ok = forget_resource(AN3),
?CHECK([{{resource_dtor_A_v2,BinAN3},2,7,207}], nif_mod_call_history()),
{A4,BinA4} = make_resource(2,Holder, "A4"),
@@ -813,19 +970,19 @@ resource_takeover(Config) when is_list(Config) ->
{BGZ1,BinBGZ1} = make_resource(3,Holder,"BGZ1"),
{NGZ1,_BinNGZ1} = make_resource(4,Holder,"NGZ1"),
- ?line false = code:purge(nif_mod),
- ?line [] = nif_mod_call_history(),
+ false = code:purge(nif_mod),
+ [] = nif_mod_call_history(),
- ?line ok = forget_resource(NGY1),
- ?line [] = nif_mod_call_history(),
+ ok = forget_resource(NGY1),
+ [] = nif_mod_call_history(),
- ?line ok = forget_resource(BGY1), % calling dtor in orphan library v2 still loaded
- ?line [{{resource_dtor_B_v2,BinBGY1},2,8,208},{unload,2,9,209}] = nif_mod_call_history(),
+ ok = forget_resource(BGY1), % calling dtor in orphan library v2 still loaded
+ [{{resource_dtor_B_v2,BinBGY1},2,8,208},{unload,2,9,209}] = nif_mod_call_history(),
%% Module upgrade with other lib-version
- ?line {module,nif_mod} = erlang:load_module(nif_mod,ModBin),
- ?line undefined = nif_mod:lib_version(),
- ?line ok = nif_mod:load_nif_lib(Config, 1,
+ {module,nif_mod} = erlang:load_module(nif_mod,ModBin),
+ undefined = nif_mod:lib_version(),
+ ok = nif_mod:load_nif_lib(Config, 1,
[{resource_type, 2, ?RT_TAKEOVER, "resource_type_A",resource_dtor_A,
?RT_TAKEOVER},
{resource_type, 0, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",resource_dtor_A,
@@ -836,28 +993,28 @@ resource_takeover(Config) when is_list(Config) ->
?RT_TAKEOVER}
]),
- ?line 1 = nif_mod:lib_version(),
- ?line [{upgrade,1,1,101},{lib_version,1,2,102}] = nif_mod_call_history(),
+ 1 = nif_mod:lib_version(),
+ [{upgrade,1,1,101},{lib_version,1,2,102}] = nif_mod_call_history(),
- %%?line false= check_process_code(Pid, nif_mod),
- ?line false = code:purge(nif_mod),
+ %%false= check_process_code(Pid, nif_mod),
+ false = code:purge(nif_mod),
%% no unload here as we still have instances with destructors
- ?line [] = nif_mod_call_history(),
+ [] = nif_mod_call_history(),
- ?line ok = forget_resource(BGZ1), % calling dtor in orphan library v2 still loaded
- ?line [{{resource_dtor_B_v2,BinBGZ1},2,10,210},{unload,2,11,211}] = nif_mod_call_history(),
+ ok = forget_resource(BGZ1), % calling dtor in orphan library v2 still loaded
+ [{{resource_dtor_B_v2,BinBGZ1},2,10,210},{unload,2,11,211}] = nif_mod_call_history(),
- ?line ok = forget_resource(NGZ1),
- ?line [] = nif_mod_call_history(),
+ ok = forget_resource(NGZ1),
+ [] = nif_mod_call_history(),
- ?line ok = forget_resource(A4),
- ?line [{{resource_dtor_A_v1,BinA4},1,3,103}] = nif_mod_call_history(),
+ ok = forget_resource(A4),
+ [{{resource_dtor_A_v1,BinA4},1,3,103}] = nif_mod_call_history(),
- ?line ok = forget_resource(NA4),
- ?line [{{resource_dtor_A_v1,BinNA4},1,4,104}] = nif_mod_call_history(),
+ ok = forget_resource(NA4),
+ [{{resource_dtor_A_v1,BinNA4},1,4,104}] = nif_mod_call_history(),
- ?line ok = forget_resource(AN4),
- ?line [] = nif_mod_call_history(),
+ ok = forget_resource(AN4),
+ [] = nif_mod_call_history(),
%%
@@ -979,8 +1136,8 @@ resource_takeover(Config) when is_list(Config) ->
{return, 0} % SUCCESS
]),
- ?line hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()),
- ?line [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(),
+ hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()),
+ [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(),
{NA7,BinNA7} = make_resource(0, Holder, "NA7"),
{AN7,BinAN7} = make_resource(1, Holder, "AN7"),
@@ -991,15 +1148,15 @@ resource_takeover(Config) when is_list(Config) ->
ok = forget_resource(AN7),
[] = nif_mod_call_history(),
- ?line true = lists:member(?MODULE, erlang:system_info(taints)),
- ?line true = lists:member(nif_mod, erlang:system_info(taints)),
- ?line verify_tmpmem(TmpMem),
+ true = lists:member(?MODULE, erlang:system_info(taints)),
+ true = lists:member(nif_mod, erlang:system_info(taints)),
+ verify_tmpmem(TmpMem),
ok.
make_resource(Type,Holder,Str) when is_list(Str) ->
Bin = list_to_binary(Str),
A1 = make_resource_do(Type,Holder,Bin),
- ?line Bin = read_resource(Type,A1),
+ Bin = read_resource(Type,A1),
{A1,Bin}.
make_resource_do(Type, Holder, Bin) ->
@@ -1026,7 +1183,7 @@ resource_holder(List) ->
%%io:format("resource_holder got ~p with list = ~p\n", [Msg,List]),
case Msg of
{Pid, make, Type, Bin} ->
- ?line Resource = nif_mod:make_new_resource(Type, Bin),
+ Resource = nif_mod:make_new_resource(Type, Bin),
Id = {make_ref(),Bin},
Pid ! {self(), make_ok, Id},
resource_holder([{Id,Resource} | List]);
@@ -1048,7 +1205,7 @@ resource_holder(Pid,Reply,List) ->
resource_holder(List).
-threading(doc) -> ["Test the threading API functions (reuse tests from driver API)"];
+%% Test the threading API functions (reuse tests from driver API)
threading(Config) when is_list(Config) ->
case erlang:system_info(threads) of
true -> threading_do(Config);
@@ -1056,53 +1213,53 @@ threading(Config) when is_list(Config) ->
end.
threading_do(Config) ->
- ?line Data = ?config(data_dir, Config),
- ?line File = filename:join(Data, "tester"),
- ?line {ok,tester,ModBin} = compile:file(File, [binary,return_errors]),
- ?line {module,tester} = erlang:load_module(tester,ModBin),
+ Data = proplists:get_value(data_dir, Config),
+ File = filename:join(Data, "tester"),
+ {ok,tester,ModBin} = compile:file(File, [binary,return_errors]),
+ {module,tester} = erlang:load_module(tester,ModBin),
- ?line ok = tester:load_nif_lib(Config, "basic"),
- ?line ok = tester:run(),
+ ok = tester:load_nif_lib(Config, "basic"),
+ ok = tester:run(),
- ?line ok = tester:load_nif_lib(Config, "rwlock"),
- ?line ok = tester:run(),
+ ok = tester:load_nif_lib(Config, "rwlock"),
+ ok = tester:run(),
- ?line ok = tester:load_nif_lib(Config, "tsd"),
- ?line ok = tester:run().
+ ok = tester:load_nif_lib(Config, "tsd"),
+ ok = tester:run().
-send(doc) -> ["Test NIF message sending"];
+%% Test NIF message sending
send(Config) when is_list(Config) ->
ensure_lib_loaded(Config),
N = 1500,
List = lists:seq(1,N),
- ?line {ok,1} = send_list_seq(N, self),
- ?line {ok,1} = send_list_seq(N, self()),
- ?line List = receive_any(),
- ?line List = receive_any(),
+ {ok,1} = send_list_seq(N, self),
+ {ok,1} = send_list_seq(N, self()),
+ List = receive_any(),
+ List = receive_any(),
Papa = self(),
- spawn_link(fun() -> ?line {ok,1} = send_list_seq(N, Papa) end),
- ?line List = receive_any(),
+ spawn_link(fun() -> {ok,1} = send_list_seq(N, Papa) end),
+ List = receive_any(),
- ?line {ok, 1, BlobS} = send_new_blob(self(), other_term()),
- ?line BlobR = receive_any(),
+ {ok, 1, BlobS} = send_new_blob(self(), other_term()),
+ BlobR = receive_any(),
io:format("Sent ~p\nGot ~p\n", [BlobS, BlobR]),
- ?line BlobR = BlobS,
+ BlobR = BlobS,
%% send to dead pid
{DeadPid, DeadMon} = spawn_monitor(fun() -> void end),
- ?line {'DOWN', DeadMon, process, DeadPid, normal} = receive_any(),
+ {'DOWN', DeadMon, process, DeadPid, normal} = receive_any(),
{ok,0} = send_list_seq(7, DeadPid),
ok.
-send2(doc) -> ["More NIF message sending"];
+%% More NIF message sending
send2(Config) when is_list(Config) ->
ensure_lib_loaded(Config),
send2_do1(fun send_blob_dbg/2),
ok.
-send_threaded(doc) -> ["Send msg from user thread"];
+%% Send msg from user thread
send_threaded(Config) when is_list(Config) ->
case erlang:system_info(smp_support) of
true ->
@@ -1123,44 +1280,44 @@ send2_do1(SendBlobF) ->
io:format("sending to forwarder pid=~p\n",[Forwarder]),
send2_do2(SendBlobF, Forwarder),
Forwarder ! terminate,
- ?line {Forwarder, 4} = receive_any(),
+ {Forwarder, 4} = receive_any(),
ok.
send2_do2(SendBlobF, To) ->
MsgEnv = alloc_msgenv(),
repeat(50, fun(_) -> grow_blob(MsgEnv,other_term()) end, []),
- ?line {ok,1,Blob0} = SendBlobF(MsgEnv, To),
- ?line Blob1 = receive_any(),
- ?line Blob1 = Blob0,
+ {ok,1,Blob0} = SendBlobF(MsgEnv, To),
+ Blob1 = receive_any(),
+ Blob1 = Blob0,
clear_msgenv(MsgEnv),
repeat(50, fun(_) -> grow_blob(MsgEnv,other_term()) end, []),
- ?line {ok,1,Blob2} = SendBlobF(MsgEnv, To),
- ?line Blob3 = receive_any(),
- ?line Blob3 = Blob2,
+ {ok,1,Blob2} = SendBlobF(MsgEnv, To),
+ Blob3 = receive_any(),
+ Blob3 = Blob2,
clear_msgenv(MsgEnv),
repeat(50, fun(_) -> grow_blob(MsgEnv,other_term()) end, []),
clear_msgenv(MsgEnv),
repeat(50, fun(_) -> grow_blob(MsgEnv,other_term()) end, []),
- ?line {ok,1,Blob4} = SendBlobF(MsgEnv, To),
- ?line Blob5 = receive_any(),
- ?line Blob5 = Blob4,
+ {ok,1,Blob4} = SendBlobF(MsgEnv, To),
+ Blob5 = receive_any(),
+ Blob5 = Blob4,
clear_msgenv(MsgEnv),
clear_msgenv(MsgEnv),
repeat(50, fun(_) -> grow_blob(MsgEnv,other_term()) end, []),
- ?line {ok,1,Blob6} = SendBlobF(MsgEnv, To),
- ?line Blob7 = receive_any(),
- ?line Blob7 = Blob6,
+ {ok,1,Blob6} = SendBlobF(MsgEnv, To),
+ Blob7 = receive_any(),
+ Blob7 = Blob6,
ok.
send_blob_thread_and_join(MsgEnv, To) ->
- ?line {ok,Blob} = send_blob_thread_dbg(MsgEnv, To, no_join),
- ?line {ok,SendRes} = join_send_thread(MsgEnv),
+ {ok,Blob} = send_blob_thread_dbg(MsgEnv, To, no_join),
+ {ok,SendRes} = join_send_thread(MsgEnv),
{ok,SendRes,Blob}.
send_blob_dbg(MsgEnv, To) ->
@@ -1188,21 +1345,18 @@ forwarder(To, N) ->
other_term() ->
{fun(X,Y) -> X*Y end, make_ref()}.
-send3(doc) -> ["Message sending stress test"];
+%% Message sending stress test
send3(Config) when is_list(Config) ->
%% Let a number of processes send random message blobs between each other
%% using enif_send. Kill and spawn new ones randomly to keep a ~constant
%% number of workers running.
- Seed = {erlang:monotonic_time(),
- erlang:time_offset(),
- erlang:unique_integer()},
- io:format("seed: ~p\n",[Seed]),
- random:seed(Seed),
+ rand:seed(exsplus),
+ io:format("seed: ~p\n",[rand:export_seed()]),
ets:new(nif_SUITE,[named_table,public]),
- ?line true = ets:insert(nif_SUITE,{send3,0,0,0,0}),
+ true = ets:insert(nif_SUITE,{send3,0,0,0,0}),
timer:send_after(10000, timeout), % Run for 10 seconds
SpawnCnt = send3_controller(0, [], [], 20),
- ?line [{_,Rcv,SndOk,SndFail,Balance}] = ets:lookup(nif_SUITE,send3),
+ [{_,Rcv,SndOk,SndFail,Balance}] = ets:lookup(nif_SUITE,send3),
io:format("spawns=~p received=~p, sent=~p send-failure=~p balance=~p\n",
[SpawnCnt,Rcv,SndOk,SndFail,Balance]),
ets:delete(nif_SUITE).
@@ -1230,13 +1384,13 @@ send3_controller(SpawnCnt0, Mons0, Pids0, Tick) ->
after Tick ->
Max = 20,
N = length(Pids0),
- PidN = random:uniform(Max),
+ PidN = rand:uniform(Max),
%%io:format("N=~p PidN=~p Pids0=~p\n", [N,PidN,Pids0]),
case PidN > N of
true ->
{NewPid,Mon} = spawn_opt(fun send3_proc/0, [link,monitor]),
lists:foreach(fun(P) -> P ! {is_born,NewPid} end, Pids0),
- ?line Balance = ets:lookup_element(nif_SUITE,send3,5),
+ Balance = ets:lookup_element(nif_SUITE,send3,5),
Inject = (Balance =< 0),
case Inject of
true -> ok;
@@ -1262,7 +1416,7 @@ send3_proc(Pids0, Counters={Rcv,SndOk,SndFail}, State0) ->
receive
{pids, Pids1, Inject} ->
%%io:format("~p: got ~p Inject=~p\n", [self(), Pids1, Inject]),
- ?line Pids0 = [self()],
+ Pids0 = [self()],
Pids2 = [self() | Pids1],
case Inject of
true -> send3_proc_send(Pids2, Counters, State0);
@@ -1294,7 +1448,7 @@ send3_proc(Pids0, Counters={Rcv,SndOk,SndFail}, State0) ->
end.
send3_proc_send(Pids, {Rcv,SndOk,SndFail}, State0) ->
- To = lists:nth(random:uniform(length(Pids)),Pids),
+ To = lists:nth(rand:uniform(length(Pids)),Pids),
Blob = send3_make_blob(),
State1 = send3_new_state(State0,Blob),
case send3_send(To, Blob) of
@@ -1306,28 +1460,32 @@ send3_proc_send(Pids, {Rcv,SndOk,SndFail}, State0) ->
send3_make_blob() ->
- case random:uniform(20)-1 of
+ case rand:uniform(20)-1 of
0 -> {term,[]};
N ->
MsgEnv = alloc_msgenv(),
repeat(N bsr 1,
- fun(_) -> grow_blob(MsgEnv,other_term(),random:uniform(1 bsl 20))
+ fun(_) -> grow_blob(MsgEnv,other_term(),rand:uniform(1 bsl 20))
end, void),
- case (N band 1) of
+ case (N band 3) of
0 -> {term,copy_blob(MsgEnv)};
- 1 -> {msgenv,MsgEnv}
+ 1 -> {copy,copy_blob(MsgEnv)};
+ _ -> {msgenv,MsgEnv}
end
end.
send3_send(Pid, Msg) ->
%% 90% enif_send and 10% normal bang
- case random:uniform(10) of
+ case rand:uniform(10) of
1 -> send3_send_bang(Pid,Msg);
_ -> send3_send_nif(Pid,Msg)
end.
send3_send_nif(Pid, {term,Blob}) ->
%%io:format("~p send term nif\n",[self()]),
send_term(Pid, {blob, Blob}) =:= 1;
+send3_send_nif(Pid, {copy,Blob}) ->
+ %%io:format("~p send term nif\n",[self()]),
+ send_copy_term(Pid, {blob, Blob}) =:= 1;
send3_send_nif(Pid, {msgenv,MsgEnv}) ->
%%io:format("~p send blob nif\n",[self()]),
send3_blob(MsgEnv, Pid, blob) =:= 1.
@@ -1336,109 +1494,115 @@ send3_send_bang(Pid, {term,Blob}) ->
%%io:format("~p send term bang\n",[self()]),
Pid ! {blob, Blob},
true;
+send3_send_bang(Pid, {copy,Blob}) ->
+ %%io:format("~p send term bang\n",[self()]),
+ Pid ! {blob, Blob},
+ true;
send3_send_bang(Pid, {msgenv,MsgEnv}) ->
%%io:format("~p send blob bang\n",[self()]),
Pid ! {blob, copy_blob(MsgEnv)},
true.
send3_new_state(State, Blob) ->
- case random:uniform(5+2) of
+ case rand:uniform(5+2) of
N when N =< 5-> setelement(N, State, Blob);
_ -> State % Don't store blob
end.
-neg(doc) -> ["Negative testing of load_nif"];
+%% Negative testing of load_nif
neg(Config) when is_list(Config) ->
TmpMem = tmpmem(),
- ?line {'EXIT',{badarg,_}} = (catch erlang:load_nif(badarg, 0)),
- ?line {error,{load_failed,_}} = erlang:load_nif("pink_unicorn", 0),
+ {'EXIT',{badarg,_}} = (catch erlang:load_nif(badarg, 0)),
+ {error,{load_failed,_}} = erlang:load_nif("pink_unicorn", 0),
- ?line Data = ?config(data_dir, Config),
- ?line File = filename:join(Data, "nif_mod"),
- ?line {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]),
- ?line {module,nif_mod} = erlang:load_module(nif_mod,Bin),
+ Data = proplists:get_value(data_dir, Config),
+ File = filename:join(Data, "nif_mod"),
+ {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]),
+ {module,nif_mod} = erlang:load_module(nif_mod,Bin),
- ?line {error,{bad_lib,_}} = nif_mod:load_nif_lib(Config, no_init),
- ?line verify_tmpmem(TmpMem),
- ?line ok.
+ {error,{bad_lib,_}} = nif_mod:load_nif_lib(Config, no_init),
+ verify_tmpmem(TmpMem),
+ ok.
-is_checks(doc) -> ["Test all enif_is functions"];
+%% Test all enif_is functions
is_checks(Config) when is_list(Config) ->
- ?line ensure_lib_loaded(Config, 1),
- ?line ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end,
+ ensure_lib_loaded(Config, 1),
+ ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end,
self(), hd(erlang:ports()), [], [1,9,9,8],
{hejsan, "hejsan", [$h,"ejs",<<"an">>]}, 12),
- ?line ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end,
+ ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end,
self(), hd(erlang:ports()), [], [1,9,9,8],
{hejsan, "hejsan", [$h,"ejs",<<"an">>]}, -12),
- ?line ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end,
+ ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end,
self(), hd(erlang:ports()), [], [1,9,9,8],
{hejsan, "hejsan", [$h,"ejs",<<"an">>]}, 18446744073709551617),
- ?line ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end,
+ ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end,
self(), hd(erlang:ports()), [], [1,9,9,8],
{hejsan, "hejsan", [$h,"ejs",<<"an">>]}, -18446744073709551617),
- ?line ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end,
+ ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end,
self(), hd(erlang:ports()), [], [1,9,9,8],
{hejsan, "hejsan", [$h,"ejs",<<"an">>]}, 99.146),
- ?line ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end,
+ ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end,
self(), hd(erlang:ports()), [], [1,9,9,8],
{hejsan, "hejsan", [$h,"ejs",<<"an">>]}, -99.146),
- ?line ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end,
+ ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end,
self(), hd(erlang:ports()), [], [1,9,9,8],
{hejsan, "hejsan", [$h,"ejs",<<"an">>]}, 18446744073709551616.2e2),
- ?line ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end,
+ ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end,
self(), hd(erlang:ports()), [], [1,9,9,8],
{hejsan, "hejsan", [$h,"ejs",<<"an">>]}, -18446744073709551616.2e2),
try
- ?line check_is_exception(),
- ?line throw(expected_badarg)
+ check_is_exception(),
+ throw(expected_badarg)
catch
error:badarg ->
- ?line ok
+ ok
end.
-get_length(doc) -> ["Test all enif_get_length functions"];
+%% Test all enif_get_length functions
get_length(Config) when is_list(Config) ->
- ?line ensure_lib_loaded(Config, 1),
- ?line ok = length_test(hejsan, "hejsan", [], [], not_a_list).
+ ensure_lib_loaded(Config, 1),
+ ok = length_test(hejsan, "hejsan", [], [], not_a_list, [1,2|3]).
ensure_lib_loaded(Config) ->
ensure_lib_loaded(Config, 1).
ensure_lib_loaded(Config, Ver) ->
- ?line case lib_version() of
- undefined ->
- ?line Path = ?config(data_dir, Config),
- ?line Lib = "nif_SUITE." ++ integer_to_list(Ver),
- ?line ok = erlang:load_nif(filename:join(Path,Lib), []);
- Ver when is_integer(Ver) ->
- ok
- end.
+ Path = ?config(data_dir, Config),
+ case lib_version() of
+ undefined ->
+ Lib = "nif_SUITE." ++ integer_to_list(Ver),
+ ok = erlang:load_nif(filename:join(Path,Lib), []);
+ Ver when is_integer(Ver) ->
+ ok
+ end,
+ erl_ddll:try_load(Path, echo_drv, []),
+ ok.
make_atom(Config) when is_list(Config) ->
- ?line ensure_lib_loaded(Config, 1),
+ ensure_lib_loaded(Config, 1),
An0Atom = an0atom,
An0Atom0 = 'an\000atom\000',
- ?line Atoms = make_atoms(),
- ?line 7 = size(Atoms),
- ?line Atoms = {An0Atom,An0Atom,An0Atom,An0Atom0,An0Atom,An0Atom,An0Atom0}.
+ Atoms = make_atoms(),
+ 7 = size(Atoms),
+ Atoms = {An0Atom,An0Atom,An0Atom,An0Atom0,An0Atom,An0Atom,An0Atom0}.
make_string(Config) when is_list(Config) ->
- ?line ensure_lib_loaded(Config, 1),
- ?line Strings = make_strings(),
- ?line 5 = size(Strings),
+ ensure_lib_loaded(Config, 1),
+ Strings = make_strings(),
+ 5 = size(Strings),
A0String = "a0string",
A0String0 = [$a,0,$s,$t,$r,$i,$n,$g,0],
AStringWithAccents = [$E,$r,$l,$a,$n,$g,$ ,16#e4,$r,$ ,$e,$t,$t,$ ,$g,$e,$n,$e,$r,$e,$l,$l,$t,$ ,$p,$r,$o,$g,$r,$a,$m,$s,$p,$r,16#e5,$k],
- ?line Strings = {A0String,A0String,A0String,A0String0, AStringWithAccents}.
+ Strings = {A0String,A0String,A0String,A0String0, AStringWithAccents}.
reverse_list_test(Config) ->
- ?line ensure_lib_loaded(Config, 1),
+ ensure_lib_loaded(Config, 1),
List = lists:seq(1,100),
RevList = lists:reverse(List),
- ?line RevList = reverse_list(List),
- ?line badarg = reverse_list(foo).
+ RevList = reverse_list(List),
+ badarg = reverse_list(foo).
-otp_9668(doc) -> ["Memory leak of tmp-buffer when inspecting iolist or unaligned binary in unbound environment"];
+%% Memory leak of tmp-buffer when inspecting iolist or unaligned binary in unbound environment
otp_9668(Config) ->
ensure_lib_loaded(Config, 1),
TmpMem = tmpmem(),
@@ -1448,13 +1612,12 @@ otp_9668(Config) ->
<<_:5/bitstring,UnalignedBin:10/binary,_/bitstring>> = <<"Abuse me as unaligned">>,
otp_9668_nif(UnalignedBin),
- ?line verify_tmpmem(TmpMem),
+ verify_tmpmem(TmpMem),
ok.
-otp_9828(doc) -> ["Copy of writable binary"];
+%% Copy of writable binary
otp_9828(Config) ->
ensure_lib_loaded(Config, 1),
-
otp_9828_loop(<<"I'm alive!">>, 1000).
otp_9828_loop(Bin, 0) ->
@@ -1466,6 +1629,17 @@ otp_9828_loop(Bin, Val) ->
consume_timeslice(Config) when is_list(Config) ->
+ case {erlang:system_info(debug_compiled),
+ erlang:system_info(lock_checking)} of
+ {false, false} ->
+ consume_timeslice_test(Config);
+ {false, true} ->
+ {skipped, "Lock checking enabled"};
+ _ ->
+ {skipped, "Debug compiled"}
+ end.
+
+consume_timeslice_test(Config) when is_list(Config) ->
CONTEXT_REDS = 2000,
Me = self(),
Go = make_ref(),
@@ -1473,68 +1647,68 @@ consume_timeslice(Config) when is_list(Config) ->
Done = make_ref(),
DummyMFA = {?MODULE,dummy_call,1},
P = spawn(fun () ->
- receive Go -> ok end,
- {reductions, R1} = process_info(self(), reductions),
- 1 = consume_timeslice_nif(100, false),
- dummy_call(111),
- 0 = consume_timeslice_nif(90, false),
- dummy_call(222),
- 1 = consume_timeslice_nif(10, false),
- dummy_call(333),
- 0 = consume_timeslice_nif(25, false),
- 0 = consume_timeslice_nif(25, false),
- 0 = consume_timeslice_nif(25, false),
- 1 = consume_timeslice_nif(25, false),
- 0 = consume_timeslice_nif(25, false),
-
- ok = case consume_timeslice_nif(1, true) of
- Cnt when Cnt > 70, Cnt < 80 -> ok;
- Other -> Other
- end,
- dummy_call(444),
-
- {reductions, R2} = process_info(self(), reductions),
- Me ! {RedDiff, R2 - R1},
- exit(Done)
- end),
+ receive Go -> ok end,
+ {reductions, R1} = process_info(self(), reductions),
+ 1 = consume_timeslice_nif(100, false),
+ dummy_call(111),
+ 0 = consume_timeslice_nif(90, false),
+ dummy_call(222),
+ 1 = consume_timeslice_nif(10, false),
+ dummy_call(333),
+ 0 = consume_timeslice_nif(25, false),
+ 0 = consume_timeslice_nif(25, false),
+ 0 = consume_timeslice_nif(25, false),
+ 1 = consume_timeslice_nif(25, false),
+ 0 = consume_timeslice_nif(25, false),
+
+ ok = case consume_timeslice_nif(1, true) of
+ Cnt when Cnt > 70, Cnt < 80 -> ok;
+ Other -> Other
+ end,
+ dummy_call(444),
+
+ {reductions, R2} = process_info(self(), reductions),
+ Me ! {RedDiff, R2 - R1},
+ exit(Done)
+ end),
erlang:yield(),
erlang:trace_pattern(DummyMFA, [], [local]),
- ?line 1 = erlang:trace(P, true, [call, running, procs, {tracer, self()}]),
+ 1 = erlang:trace(P, true, [call, running, procs, {tracer, self()}]),
P ! Go,
%% receive Go -> ok end,
- ?line {trace, P, in, _} = next_tmsg(P),
+ {trace, P, in, _} = next_tmsg(P),
%% consume_timeslice_nif(100),
%% dummy_call(111)
- ?line {trace, P, out, _} = next_tmsg(P),
- ?line {trace, P, in, _} = next_tmsg(P),
- ?line {trace, P, call, {?MODULE,dummy_call,[111]}} = next_tmsg(P),
+ {trace, P, out, _} = next_tmsg(P),
+ {trace, P, in, _} = next_tmsg(P),
+ {trace, P, call, {?MODULE,dummy_call,[111]}} = next_tmsg(P),
%% consume_timeslice_nif(90),
%% dummy_call(222)
- ?line {trace, P, call, {?MODULE,dummy_call,[222]}} = next_tmsg(P),
+ {trace, P, call, {?MODULE,dummy_call,[222]}} = next_tmsg(P),
%% consume_timeslice_nif(10),
%% dummy_call(333)
- ?line {trace, P, out, _} = next_tmsg(P),
- ?line {trace, P, in, _} = next_tmsg(P),
- ?line {trace, P, call, {?MODULE,dummy_call,[333]}} = next_tmsg(P),
+ {trace, P, out, _} = next_tmsg(P),
+ {trace, P, in, _} = next_tmsg(P),
+ {trace, P, call, {?MODULE,dummy_call,[333]}} = next_tmsg(P),
%% 25,25,25,25, 25
- ?line {trace, P, out, {?MODULE,consume_timeslice_nif,2}} = next_tmsg(P),
- ?line {trace, P, in, {?MODULE,consume_timeslice_nif,2}} = next_tmsg(P),
+ {trace, P, out, {?MODULE,consume_timeslice_nif,2}} = next_tmsg(P),
+ {trace, P, in, {?MODULE,consume_timeslice_nif,2}} = next_tmsg(P),
%% consume_timeslice(1,true)
%% dummy_call(444)
- ?line {trace, P, out, DummyMFA} = next_tmsg(P),
- ?line {trace, P, in, DummyMFA} = next_tmsg(P),
- ?line {trace, P, call, {?MODULE,dummy_call,[444]}} = next_tmsg(P),
+ {trace, P, out, DummyMFA} = next_tmsg(P),
+ {trace, P, in, DummyMFA} = next_tmsg(P),
+ {trace, P, call, {?MODULE,dummy_call,[444]}} = next_tmsg(P),
%% exit(Done)
- ?line {trace, P, exit, Done} = next_tmsg(P),
+ {trace, P, exit, Done} = next_tmsg(P),
ExpReds = (100 + 90 + 10 + 25*5 + 75) * CONTEXT_REDS div 100,
receive
@@ -1542,7 +1716,7 @@ consume_timeslice(Config) when is_list(Config) ->
io:format("Reductions = ~p~n", [Reductions]),
ok;
{RedDiff, Reductions} ->
- ?t:fail({unexpected_reduction_count, Reductions})
+ ct:fail({unexpected_reduction_count, Reductions})
end,
none = next_msg(P),
@@ -1563,76 +1737,6 @@ nif_schedule(Config) when is_list(Config) ->
end,
ok.
-dirty_nif(Config) when is_list(Config) ->
- try erlang:system_info(dirty_cpu_schedulers) of
- N when is_integer(N) ->
- ensure_lib_loaded(Config),
- Val1 = 42,
- Val2 = "Erlang",
- Val3 = list_to_binary([Val2, 0]),
- {Val1, Val2, Val3} = call_dirty_nif(Val1, Val2, Val3),
- LargeArray = lists:duplicate(1000, ok),
- LargeArray = call_dirty_nif_zero_args(),
- ok
- catch
- error:badarg ->
- {skipped,"No dirty scheduler support"}
- end.
-
-dirty_nif_send(Config) when is_list(Config) ->
- try erlang:system_info(dirty_cpu_schedulers) of
- N when is_integer(N) ->
- ensure_lib_loaded(Config),
- Parent = self(),
- Pid = spawn_link(fun() ->
- Self = self(),
- {ok, Self} = receive_any(),
- Parent ! {ok, Self}
- end),
- {ok, Pid} = send_from_dirty_nif(Pid),
- {ok, Pid} = receive_any(),
- ok
- catch
- error:badarg ->
- {skipped,"No dirty scheduler support"}
- end.
-
-dirty_nif_exception(Config) when is_list(Config) ->
- try erlang:system_info(dirty_cpu_schedulers) of
- N when is_integer(N) ->
- ensure_lib_loaded(Config),
- try
- %% this checks that the expected exception occurs when the
- %% dirty NIF returns the result of enif_make_badarg
- %% directly
- call_dirty_nif_exception(1),
- ?t:fail(expected_badarg)
- catch
- error:badarg ->
- [{?MODULE,call_dirty_nif_exception,[1],_}|_] =
- erlang:get_stacktrace(),
- ok
- end,
- try
- %% this checks that the expected exception occurs when the
- %% dirty NIF calls enif_make_badarg at some point but then
- %% returns a value that isn't an exception
- call_dirty_nif_exception(0),
- ?t:fail(expected_badarg)
- catch
- error:badarg ->
- [{?MODULE,call_dirty_nif_exception,[0],_}|_] =
- erlang:get_stacktrace(),
- ok
- end,
- %% this checks that a dirty NIF can raise various terms as
- %% exceptions
- ok = nif_raise_exceptions(call_dirty_nif_exception)
- catch
- error:badarg ->
- {skipped,"No dirty scheduler support"}
- end.
-
nif_exception(Config) when is_list(Config) ->
ensure_lib_loaded(Config),
try
@@ -1640,7 +1744,7 @@ nif_exception(Config) when is_list(Config) ->
%% calls enif_make_badarg at some point but then tries to return a
%% value that isn't an exception
call_nif_exception(0),
- ?t:fail(expected_badarg)
+ ct:fail(expected_badarg)
catch
error:badarg ->
ok
@@ -1653,21 +1757,21 @@ nif_nan_and_inf(Config) when is_list(Config) ->
ensure_lib_loaded(Config),
try
call_nif_nan_or_inf(nan),
- ?t:fail(expected_badarg)
+ ct:fail(expected_badarg)
catch
error:badarg ->
ok
end,
try
call_nif_nan_or_inf(inf),
- ?t:fail(expected_badarg)
+ ct:fail(expected_badarg)
catch
error:badarg ->
ok
end,
try
call_nif_nan_or_inf(tuple),
- ?t:fail(expected_badarg)
+ ct:fail(expected_badarg)
catch
error:badarg ->
ok
@@ -1677,14 +1781,14 @@ nif_atom_too_long(Config) when is_list(Config) ->
ensure_lib_loaded(Config),
try
call_nif_atom_too_long(all),
- ?t:fail(expected_badarg)
+ ct:fail(expected_badarg)
catch
error:badarg ->
ok
end,
try
call_nif_atom_too_long(len),
- ?t:fail(expected_badarg)
+ ct:fail(expected_badarg)
catch
error:badarg ->
ok
@@ -1692,19 +1796,19 @@ nif_atom_too_long(Config) when is_list(Config) ->
next_msg(_Pid) ->
receive
- M -> M
+ M -> M
after 100 ->
- none
+ none
end.
next_tmsg(Pid) ->
receive TMsg when is_tuple(TMsg),
- element(1, TMsg) == trace,
- element(2, TMsg) == Pid ->
- TMsg
- after 100 ->
- none
- end.
+ element(1, TMsg) == trace,
+ element(2, TMsg) == Pid ->
+ TMsg
+ after 100 ->
+ none
+ end.
dummy_call(_) ->
ok.
@@ -1744,9 +1848,7 @@ verify_tmpmem(MemInfo) ->
ok
end;
Other ->
- io:format("Expected: ~p", [MemInfo]),
- io:format("Actual: ~p", [Other]),
- ?t:fail()
+ ct:fail("Expected: ~p\nActual: ~p", [MemInfo, Other])
end.
call(Pid,Cmd) ->
@@ -1774,20 +1876,20 @@ check(Exp,Got,Line) ->
nif_raise_exceptions(NifFunc) ->
ExcTerms = [{error, test}, "a string", <<"a binary">>,
- 42, [1,2,3,4,5], [{p,1},{p,2},{p,3}]],
+ 42, [1,2,3,4,5], [{p,1},{p,2},{p,3}]],
lists:foldl(fun(Term, ok) ->
- try
- erlang:apply(?MODULE,NifFunc,[Term]),
- ?t:fail({expected,Term})
- catch
- error:Term ->
- [{?MODULE,NifFunc,[Term],_}|_] = erlang:get_stacktrace(),
- ok
- end
- end, ok, ExcTerms).
+ try
+ erlang:apply(?MODULE,NifFunc,[Term]),
+ ct:fail({expected,Term})
+ catch
+ error:Term ->
+ [{?MODULE,NifFunc,[Term],_}|_] = erlang:get_stacktrace(),
+ ok
+ end
+ end, ok, ExcTerms).
-define(ERL_NIF_TIME_ERROR, -9223372036854775808).
--define(TIME_UNITS, [seconds, milli_seconds, micro_seconds, nano_seconds]).
+-define(TIME_UNITS, [second, millisecond, microsecond, nanosecond]).
nif_monotonic_time(Config) ->
?ERL_NIF_TIME_ERROR = monotonic_time(invalid_time_unit),
@@ -1810,7 +1912,7 @@ chk_mtime([TU|TUs]) ->
true = B =< C
catch
_ : _ ->
- ?t:fail({monotonic_time_missmatch, TU, A, B, C})
+ ct:fail({monotonic_time_missmatch, TU, A, B, C})
end,
chk_mtime(TUs).
@@ -1830,80 +1932,80 @@ chk_toffs([TU|TUs]) ->
TO = erlang:time_offset(TU),
NifTO = time_offset(TU),
case TO =:= NifTO of
- true ->
- ok;
- false ->
- case erlang:system_info(time_warp_mode) of
- no_time_warp ->
- ?t:fail({time_offset_mismatch, TU, TO, NifTO});
- _ ->
- %% Most frequent time offset change
- %% is currently only every 15:th
- %% second so this should currently
- %% work...
- NTO = erlang:time_offset(TU),
- case NifTO =:= NTO of
- true ->
- ok;
- false ->
- ?t:fail({time_offset_mismatch, TU, TO, NifTO, NTO})
- end
- end
+ true ->
+ ok;
+ false ->
+ case erlang:system_info(time_warp_mode) of
+ no_time_warp ->
+ ct:fail({time_offset_mismatch, TU, TO, NifTO});
+ _ ->
+ %% Most frequent time offset change
+ %% is currently only every 15:th
+ %% second so this should currently
+ %% work...
+ NTO = erlang:time_offset(TU),
+ case NifTO =:= NTO of
+ true ->
+ ok;
+ false ->
+ ct:fail({time_offset_mismatch, TU, TO, NifTO, NTO})
+ end
+ end
end,
chk_toffs(TUs).
nif_convert_time_unit(Config) ->
- ?ERL_NIF_TIME_ERROR = convert_time_unit(0, seconds, invalid_time_unit),
- ?ERL_NIF_TIME_ERROR = convert_time_unit(0, invalid_time_unit, seconds),
+ ?ERL_NIF_TIME_ERROR = convert_time_unit(0, second, invalid_time_unit),
+ ?ERL_NIF_TIME_ERROR = convert_time_unit(0, invalid_time_unit, second),
?ERL_NIF_TIME_ERROR = convert_time_unit(0, invalid_time_unit, invalid_time_unit),
lists:foreach(fun (Offset) ->
- lists:foreach(fun (Diff) ->
- chk_ctu(Diff+(Offset*1000*1000*1000))
- end,
- [999999999999,
- 99999999999,
- 9999999999,
- 999999999,
- 99999999,
- 9999999,
- 999999,
- 99999,
- 999,
- 99,
- 9,
- 1,
- 11,
- 101,
- 1001,
- 10001,
- 100001,
- 1000001,
- 10000001,
- 100000001,
- 1000000001,
- 100000000001,
- 1000000000001,
- 5,
- 50,
- 500,
- 5000,
- 50000,
- 500000,
- 5000000,
- 50000000,
- 500000000,
- 5000000000,
- 50000000000,
- 500000000000])
- end,
- [-4711, -1000, -475, -5, -4, -3, -2, -1, 0,
- 1, 2, 3, 4, 5, 475, 1000, 4711]),
+ lists:foreach(fun (Diff) ->
+ chk_ctu(Diff+(Offset*1000*1000*1000))
+ end,
+ [999999999999,
+ 99999999999,
+ 9999999999,
+ 999999999,
+ 99999999,
+ 9999999,
+ 999999,
+ 99999,
+ 999,
+ 99,
+ 9,
+ 1,
+ 11,
+ 101,
+ 1001,
+ 10001,
+ 100001,
+ 1000001,
+ 10000001,
+ 100000001,
+ 1000000001,
+ 100000000001,
+ 1000000000001,
+ 5,
+ 50,
+ 500,
+ 5000,
+ 50000,
+ 500000,
+ 5000000,
+ 50000000,
+ 500000000,
+ 5000000000,
+ 50000000000,
+ 500000000000])
+ end,
+ [-4711, -1000, -475, -5, -4, -3, -2, -1, 0,
+ 1, 2, 3, 4, 5, 475, 1000, 4711]),
ctu_loop(1000000).
ctu_loop(0) ->
ok;
ctu_loop(N) ->
- chk_ctu(erlang:monotonic_time(nano_seconds)),
+ chk_ctu(erlang:monotonic_time(nanosecond)),
ctu_loop(N-1).
chk_ctu(Time) ->
@@ -1918,16 +2020,148 @@ chk_ctu(Time, [FromTU|FromTUs]) ->
chk_ctu(_Time, _FromTU, []) ->
ok;
chk_ctu(Time, FromTU, [ToTU|ToTUs]) ->
- T = erlang:convert_time_unit(Time, nano_seconds, FromTU),
+ T = erlang:convert_time_unit(Time, nanosecond, FromTU),
TE = erlang:convert_time_unit(T, FromTU, ToTU),
TN = convert_time_unit(T, FromTU, ToTU),
case TE =:= TN of
false ->
- ?t:fail({conversion_mismatch, FromTU, T, ToTU, TE, TN});
+ ct:fail({conversion_mismatch, FromTU, T, ToTU, TE, TN});
true ->
chk_ctu(Time, FromTU, ToTUs)
end.
+nif_now_time(Config) ->
+ ensure_lib_loaded(Config),
+
+ N1 = now(),
+ NifN1 = now_time(),
+ NifN2 = now_time(),
+ N2 = now(),
+ true = N1 < NifN1,
+ true = NifN1 < NifN2,
+ true = NifN2 < N2.
+
+nif_cpu_time(Config) ->
+ ensure_lib_loaded(Config),
+
+ try cpu_time() of
+ {_, _, _} ->
+ ok
+ catch error:badarg ->
+ {comment, "cpu_time not supported"}
+ end.
+
+nif_unique_integer(Config) ->
+ ensure_lib_loaded(Config),
+
+ UM1 = erlang:unique_integer([monotonic]),
+ UM2 = unique_integer_nif([monotonic]),
+ UM3 = erlang:unique_integer([monotonic]),
+
+ true = UM1 < UM2,
+ true = UM2 < UM3,
+
+ UMP1 = erlang:unique_integer([monotonic, positive]),
+ UMP2 = unique_integer_nif([monotonic, positive]),
+ UMP3 = erlang:unique_integer([monotonic, positive]),
+
+ true = 0 =< UMP1,
+ true = UMP1 < UMP2,
+ true = UMP2 < UMP3,
+
+ UP1 = erlang:unique_integer([positive]),
+ UP2 = unique_integer_nif([positive]),
+ UP3 = erlang:unique_integer([positive]),
+
+ true = 0 =< UP1,
+ true = 0 =< UP2,
+ true = 0 =< UP3,
+
+ true = is_integer(unique_integer_nif([])),
+ true = is_integer(unique_integer_nif([])),
+ true = is_integer(unique_integer_nif([])).
+
+nif_is_process_alive(Config) ->
+ ensure_lib_loaded(Config),
+
+ {Pid,_} = spawn_monitor(fun() -> receive ok -> nok end end),
+ true = is_process_alive_nif(Pid),
+ exit(Pid, die),
+ receive _ -> ok end, %% Clear monitor
+ false = is_process_alive_nif(Pid).
+
+nif_is_port_alive(Config) ->
+ ensure_lib_loaded(Config),
+
+ Port = open_port({spawn,echo_drv},[eof]),
+ true = is_port_alive_nif(Port),
+ port_close(Port),
+ false = is_port_alive_nif(Port).
+
+nif_term_to_binary(Config) ->
+ ensure_lib_loaded(Config),
+ T = {#{ok => nok}, <<0:8096>>, lists:seq(1,100)},
+ Bin = term_to_binary(T),
+ ct:log("~p",[Bin]),
+ Bin = term_to_binary_nif(T, undefined),
+ true = term_to_binary_nif(T, self()),
+ receive Bin -> ok end.
+
+-define(ERL_NIF_BIN2TERM_SAFE, 16#20000000).
+
+nif_binary_to_term(Config) ->
+ ensure_lib_loaded(Config),
+ T = {#{ok => nok}, <<0:8096>>, lists:seq(1,100)},
+ Bin = term_to_binary(T),
+ Len = byte_size(Bin),
+ {Len,T} = binary_to_term_nif(Bin, undefined, 0),
+ Len = binary_to_term_nif(Bin, self(), 0),
+ T = receive M -> M after 1000 -> timeout end,
+
+ {Len, T} = binary_to_term_nif(Bin, undefined, ?ERL_NIF_BIN2TERM_SAFE),
+ false = binary_to_term_nif(<<131,100,0,14,"undefined_atom">>,
+ undefined, ?ERL_NIF_BIN2TERM_SAFE),
+ false = binary_to_term_nif(Bin, undefined, 1),
+ ok.
+
+nif_port_command(Config) ->
+ ensure_lib_loaded(Config),
+
+ Port = open_port({spawn,echo_drv},[eof]),
+ true = port_command_nif(Port, "hello\n"),
+ receive {Port,{data,"hello\n"}} -> ok
+ after 1000 -> ct:fail(timeout) end,
+
+ RefcBin = lists:flatten([lists:duplicate(100, "hello"),"\n"]),
+ true = port_command_nif(Port, iolist_to_binary(RefcBin)),
+ receive {Port,{data,RefcBin}} -> ok
+ after 1000 -> ct:fail(timeout) end,
+
+ %% Test that invalid arguments correctly returns
+ %% badarg and that the port survives.
+ {'EXIT', {badarg, _}} = (catch port_command_nif(Port, [ok])),
+
+ IoList = [lists:duplicate(100,<<"hello">>),"\n"],
+ true = port_command_nif(Port, [IoList]),
+ FlatIoList = binary_to_list(iolist_to_binary(IoList)),
+ receive {Port,{data,FlatIoList}} -> ok
+ after 1000 -> ct:fail(timeout) end,
+
+ port_close(Port),
+
+ {'EXIT', {badarg, _}} = (catch port_command_nif(Port, "hello\n")),
+ ok.
+
+nif_snprintf(Config) ->
+ ensure_lib_loaded(Config),
+ <<"ok",0>> = format_term_nif(3,ok),
+ <<"o",0>> = format_term_nif(2,ok),
+ <<"\"hello world\"",0>> = format_term_nif(14,"hello world"),
+ <<"{{hello,world,-33},3.14",_/binary>> = format_term_nif(50,{{hello,world, -33}, 3.14, self()}),
+ <<"{{hello,world,-33},",0>> = format_term_nif(20,{{hello,world, -33}, 3.14, self()}),
+ ok.
+
+
%% The NIFs:
lib_version() -> undefined.
call_history() -> ?nif_stub.
@@ -1955,7 +2189,7 @@ last_resource_dtor_call() -> ?nif_stub.
make_new_resource(_,_) -> ?nif_stub.
check_is(_,_,_,_,_,_,_,_,_,_,_) -> ?nif_stub.
check_is_exception() -> ?nif_stub.
-length_test(_,_,_,_,_) -> ?nif_stub.
+length_test(_,_,_,_,_,_) -> ?nif_stub.
make_atoms() -> ?nif_stub.
make_strings() -> ?nif_stub.
make_new_resource_binary(_) -> ?nif_stub.
@@ -1971,6 +2205,7 @@ send_blob_thread(_,_,_) -> ?nif_stub.
join_send_thread(_) -> ?nif_stub.
copy_blob(_) -> ?nif_stub.
send_term(_,_) -> ?nif_stub.
+send_copy_term(_,_) -> ?nif_stub.
reverse_list(_) -> ?nif_stub.
echo_int(_) -> ?nif_stub.
type_sizes() -> ?nif_stub.
@@ -1978,13 +2213,16 @@ otp_9668_nif(_) -> ?nif_stub.
otp_9828_nif(_) -> ?nif_stub.
consume_timeslice_nif(_,_) -> ?nif_stub.
call_nif_schedule(_,_) -> ?nif_stub.
-call_dirty_nif(_,_,_) -> ?nif_stub.
-send_from_dirty_nif(_) -> ?nif_stub.
-call_dirty_nif_exception(_) -> ?nif_stub.
-call_dirty_nif_zero_args() -> ?nif_stub.
call_nif_exception(_) -> ?nif_stub.
call_nif_nan_or_inf(_) -> ?nif_stub.
call_nif_atom_too_long(_) -> ?nif_stub.
+unique_integer_nif(_) -> ?nif_stub.
+is_process_alive_nif(_) -> ?nif_stub.
+is_port_alive_nif(_) -> ?nif_stub.
+term_to_binary_nif(_, _) -> ?nif_stub.
+binary_to_term_nif(_, _, _) -> ?nif_stub.
+port_command_nif(_, _) -> ?nif_stub.
+format_term_nif(_,_) -> ?nif_stub.
%% maps
is_map_nif(_) -> ?nif_stub.
@@ -2001,7 +2239,8 @@ sorted_list_from_maps_nif(_) -> ?nif_stub.
monotonic_time(_) -> ?nif_stub.
time_offset(_) -> ?nif_stub.
convert_time_unit(_,_,_) -> ?nif_stub.
-
+now_time() -> ?nif_stub.
+cpu_time() -> ?nif_stub.
nif_stub_error(Line) ->
exit({nif_not_loaded,module,?MODULE,line,Line}).
diff --git a/erts/emulator/test/nif_SUITE_data/Makefile.src b/erts/emulator/test/nif_SUITE_data/Makefile.src
index ab4ff77add..fbb8978771 100644
--- a/erts/emulator/test/nif_SUITE_data/Makefile.src
+++ b/erts/emulator/test/nif_SUITE_data/Makefile.src
@@ -4,8 +4,7 @@ NIF_LIBS = nif_SUITE.1@dll@ \
nif_mod.2@dll@ \
nif_mod.3@dll@
-all: $(NIF_LIBS) basic@dll@ rwlock@dll@ tsd@dll@
-
+all: $(NIF_LIBS) basic@dll@ rwlock@dll@ tsd@dll@ echo_drv@dll@
@SHLIB_RULES@
diff --git a/erts/emulator/test/nif_SUITE_data/echo_drv.c b/erts/emulator/test/nif_SUITE_data/echo_drv.c
new file mode 100644
index 0000000000..2b3510c641
--- /dev/null
+++ b/erts/emulator/test/nif_SUITE_data/echo_drv.c
@@ -0,0 +1,62 @@
+#include <stdio.h>
+#include "erl_driver.h"
+
+static ErlDrvPort erlang_port;
+static ErlDrvData echo_start(ErlDrvPort, char *);
+static void from_erlang(ErlDrvData, char*, ErlDrvSizeT);
+static ErlDrvSSizeT echo_call(ErlDrvData drv_data, unsigned int command,
+ char *buf, ErlDrvSizeT len,
+ char **rbuf, ErlDrvSizeT rlen, unsigned *ret_flags);
+static ErlDrvEntry echo_driver_entry = {
+ NULL, /* Init */
+ echo_start,
+ NULL, /* Stop */
+ from_erlang,
+ NULL, /* Ready input */
+ NULL, /* Ready output */
+ "echo_drv",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ echo_call,
+ NULL,
+ ERL_DRV_EXTENDED_MARKER,
+ ERL_DRV_EXTENDED_MAJOR_VERSION,
+ ERL_DRV_EXTENDED_MINOR_VERSION,
+ 0,
+ NULL,
+ NULL,
+ NULL
+};
+
+DRIVER_INIT(echo_drv)
+{
+ return &echo_driver_entry;
+}
+
+static ErlDrvData
+echo_start(ErlDrvPort port, char *buf)
+{
+ return (ErlDrvData) port;
+}
+
+static void
+from_erlang(ErlDrvData data, char *buf, ErlDrvSizeT count)
+{
+ driver_output((ErlDrvPort) data, buf, count);
+}
+
+static ErlDrvSSizeT
+echo_call(ErlDrvData drv_data, unsigned int command,
+ char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen,
+ unsigned *ret_flags)
+{
+ *rbuf = buf;
+ *ret_flags |= DRIVER_CALL_KEEP_BUFFER;
+ return len;
+}
+
diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
index 8ebce4fef4..f2b1ef9d24 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2014. All Rights Reserved.
+ * Copyright Ericsson AB 2009-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,9 @@
#include <string.h>
#include <assert.h>
#include <limits.h>
+#ifndef __WIN32__
+#include <unistd.h>
+#endif
#include "nif_mod.h"
@@ -30,14 +33,15 @@ static int static_cntA; /* zero by default */
static int static_cntB = NIF_SUITE_LIB_VER * 100;
static ERL_NIF_TERM atom_false;
+static ERL_NIF_TERM atom_true;
static ERL_NIF_TERM atom_self;
static ERL_NIF_TERM atom_ok;
static ERL_NIF_TERM atom_join;
static ERL_NIF_TERM atom_binary_resource_type;
-static ERL_NIF_TERM atom_seconds;
-static ERL_NIF_TERM atom_milli_seconds;
-static ERL_NIF_TERM atom_micro_seconds;
-static ERL_NIF_TERM atom_nano_seconds;
+static ERL_NIF_TERM atom_second;
+static ERL_NIF_TERM atom_millisecond;
+static ERL_NIF_TERM atom_microsecond;
+static ERL_NIF_TERM atom_nanosecond;
typedef struct
@@ -138,14 +142,15 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
msgenv_dtor,
ERL_NIF_RT_CREATE, NULL);
atom_false = enif_make_atom(env,"false");
+ atom_true = enif_make_atom(env,"true");
atom_self = enif_make_atom(env,"self");
atom_ok = enif_make_atom(env,"ok");
atom_join = enif_make_atom(env,"join");
atom_binary_resource_type = enif_make_atom(env,"binary_resource_type");
- atom_seconds = enif_make_atom(env,"seconds");
- atom_milli_seconds = enif_make_atom(env,"milli_seconds");
- atom_micro_seconds = enif_make_atom(env,"micro_seconds");
- atom_nano_seconds = enif_make_atom(env,"nano_seconds");
+ atom_second = enif_make_atom(env,"second");
+ atom_millisecond = enif_make_atom(env,"millisecond");
+ atom_microsecond = enif_make_atom(env,"microsecond");
+ atom_nanosecond = enif_make_atom(env,"nanosecond");
*priv_data = data;
return 0;
@@ -914,6 +919,7 @@ static ERL_NIF_TERM check_is_exception(ErlNifEnv* env, int argc, const ERL_NIF_T
* argv[2] empty list
* argv[3] not an atom
* argv[4] not a list
+ * argv[5] improper list
*/
static ERL_NIF_TERM length_test(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
@@ -934,6 +940,9 @@ static ERL_NIF_TERM length_test(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
if (enif_get_list_length(env, argv[4], &len))
return enif_make_badarg(env);
+ if (enif_get_list_length(env, argv[5], &len))
+ return enif_make_badarg(env);
+
return enif_make_atom(env, "ok");
}
@@ -1460,6 +1469,18 @@ static ERL_NIF_TERM send_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
return enif_make_int(env, ret);
}
+static ERL_NIF_TERM send_copy_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifEnv* menv;
+ ErlNifPid pid;
+ int ret;
+ if (!enif_get_local_pid(env, argv[0], &pid)) {
+ return enif_make_badarg(env);
+ }
+ ret = enif_send(env, &pid, NULL, argv[1]);
+ return enif_make_int(env, ret);
+}
+
static ERL_NIF_TERM reverse_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
ERL_NIF_TERM rev_list;
@@ -1556,120 +1577,6 @@ static ERL_NIF_TERM call_nif_schedule(ErlNifEnv* env, int argc, const ERL_NIF_TE
return result;
}
-#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT
-
-static int have_dirty_schedulers(void)
-{
- ErlNifSysInfo si;
- enif_system_info(&si, sizeof(si));
- return si.dirty_scheduler_support;
-}
-
-static ERL_NIF_TERM dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{
- int n;
- char s[10];
- ErlNifBinary b;
- ERL_NIF_TERM result;
- if (have_dirty_schedulers()) {
- assert(enif_is_on_dirty_scheduler(env));
- }
- assert(argc == 3);
- enif_get_int(env, argv[0], &n);
- enif_get_string(env, argv[1], s, sizeof s, ERL_NIF_LATIN1);
- enif_inspect_binary(env, argv[2], &b);
- return enif_make_tuple3(env,
- enif_make_int(env, n),
- enif_make_string(env, s, ERL_NIF_LATIN1),
- enif_make_binary(env, &b));
-}
-
-static ERL_NIF_TERM call_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{
- int n;
- char s[10];
- ErlNifBinary b;
- assert(!enif_is_on_dirty_scheduler(env));
- if (argc != 3)
- return enif_make_badarg(env);
- if (have_dirty_schedulers()) {
- if (enif_get_int(env, argv[0], &n) &&
- enif_get_string(env, argv[1], s, sizeof s, ERL_NIF_LATIN1) &&
- enif_inspect_binary(env, argv[2], &b))
- return enif_schedule_nif(env, "call_dirty_nif", ERL_NIF_DIRTY_JOB_CPU_BOUND, dirty_nif, argc, argv);
- else
- return enif_make_badarg(env);
- } else {
- return dirty_nif(env, argc, argv);
- }
-}
-
-static ERL_NIF_TERM send_from_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{
- ERL_NIF_TERM result;
- ErlNifPid pid;
- ErlNifEnv* menv;
- int res;
-
- if (!enif_get_local_pid(env, argv[0], &pid))
- return enif_make_badarg(env);
- result = enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_pid(env, &pid));
- menv = enif_alloc_env();
- res = enif_send(env, &pid, menv, result);
- enif_free_env(menv);
- if (!res)
- return enif_make_badarg(env);
- else
- return result;
-}
-
-static ERL_NIF_TERM call_dirty_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{
- switch (argc) {
- case 1: {
- int arg;
- if (enif_get_int(env, argv[0], &arg) && arg < 2) {
- ERL_NIF_TERM args[255];
- int i;
- args[0] = argv[0];
- for (i = 1; i < 255; i++)
- args[i] = enif_make_int(env, i);
- return enif_schedule_nif(env, "call_dirty_nif_exception", ERL_NIF_DIRTY_JOB_CPU_BOUND,
- call_dirty_nif_exception, 255, args);
- } else {
- return enif_raise_exception(env, argv[0]);
- }
- }
- case 2: {
- int return_badarg_directly;
- enif_get_int(env, argv[0], &return_badarg_directly);
- assert(return_badarg_directly == 1 || return_badarg_directly == 0);
- if (return_badarg_directly)
- return enif_make_badarg(env);
- else {
- /* ignore return value */ enif_make_badarg(env);
- return enif_make_atom(env, "ok");
- }
- }
- default:
- return enif_schedule_nif(env, "call_dirty_nif_exception", ERL_NIF_DIRTY_JOB_CPU_BOUND,
- call_dirty_nif_exception, argc-1, argv);
- }
-}
-
-static ERL_NIF_TERM call_dirty_nif_zero_args(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{
- int i;
- ERL_NIF_TERM result[1000];
- ERL_NIF_TERM ok = enif_make_atom(env, "ok");
- assert(argc == 0);
- for (i = 0; i < sizeof(result)/sizeof(*result); i++) {
- result[i] = ok;
- }
- return enif_make_list_from_array(env, result, i);
-}
-#endif
-
/*
* If argv[0] is the integer 0, call enif_make_badarg, but don't return its
* return value. Instead, return ok. Result should still be a badarg
@@ -1901,13 +1808,13 @@ static ERL_NIF_TERM monotonic_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM
if (argc != 1)
return atom_false;
- if (enif_compare(argv[0], atom_seconds) == 0)
+ if (enif_compare(argv[0], atom_second) == 0)
time_unit = ERL_NIF_SEC;
- else if (enif_compare(argv[0], atom_milli_seconds) == 0)
+ else if (enif_compare(argv[0], atom_millisecond) == 0)
time_unit = ERL_NIF_MSEC;
- else if (enif_compare(argv[0], atom_micro_seconds) == 0)
+ else if (enif_compare(argv[0], atom_microsecond) == 0)
time_unit = ERL_NIF_USEC;
- else if (enif_compare(argv[0], atom_nano_seconds) == 0)
+ else if (enif_compare(argv[0], atom_nanosecond) == 0)
time_unit = ERL_NIF_NSEC;
else
time_unit = 4711; /* invalid time unit */
@@ -1922,13 +1829,13 @@ static ERL_NIF_TERM time_offset(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
if (argc != 1)
return atom_false;
- if (enif_compare(argv[0], atom_seconds) == 0)
+ if (enif_compare(argv[0], atom_second) == 0)
time_unit = ERL_NIF_SEC;
- else if (enif_compare(argv[0], atom_milli_seconds) == 0)
+ else if (enif_compare(argv[0], atom_millisecond) == 0)
time_unit = ERL_NIF_MSEC;
- else if (enif_compare(argv[0], atom_micro_seconds) == 0)
+ else if (enif_compare(argv[0], atom_microsecond) == 0)
time_unit = ERL_NIF_USEC;
- else if (enif_compare(argv[0], atom_nano_seconds) == 0)
+ else if (enif_compare(argv[0], atom_nanosecond) == 0)
time_unit = ERL_NIF_NSEC;
else
time_unit = 4711; /* invalid time unit */
@@ -1949,24 +1856,24 @@ static ERL_NIF_TERM convert_time_unit(ErlNifEnv* env, int argc, const ERL_NIF_TE
val = (ErlNifTime) i64;
- if (enif_compare(argv[1], atom_seconds) == 0)
+ if (enif_compare(argv[1], atom_second) == 0)
from = ERL_NIF_SEC;
- else if (enif_compare(argv[1], atom_milli_seconds) == 0)
+ else if (enif_compare(argv[1], atom_millisecond) == 0)
from = ERL_NIF_MSEC;
- else if (enif_compare(argv[1], atom_micro_seconds) == 0)
+ else if (enif_compare(argv[1], atom_microsecond) == 0)
from = ERL_NIF_USEC;
- else if (enif_compare(argv[1], atom_nano_seconds) == 0)
+ else if (enif_compare(argv[1], atom_nanosecond) == 0)
from = ERL_NIF_NSEC;
else
from = 4711; /* invalid time unit */
- if (enif_compare(argv[2], atom_seconds) == 0)
+ if (enif_compare(argv[2], atom_second) == 0)
to = ERL_NIF_SEC;
- else if (enif_compare(argv[2], atom_milli_seconds) == 0)
+ else if (enif_compare(argv[2], atom_millisecond) == 0)
to = ERL_NIF_MSEC;
- else if (enif_compare(argv[2], atom_micro_seconds) == 0)
+ else if (enif_compare(argv[2], atom_microsecond) == 0)
to = ERL_NIF_USEC;
- else if (enif_compare(argv[2], atom_nano_seconds) == 0)
+ else if (enif_compare(argv[2], atom_nanosecond) == 0)
to = ERL_NIF_NSEC;
else
to = 4711; /* invalid time unit */
@@ -1974,6 +1881,140 @@ static ERL_NIF_TERM convert_time_unit(ErlNifEnv* env, int argc, const ERL_NIF_TE
return enif_make_int64(env, enif_convert_time_unit(val, from, to));
}
+static ERL_NIF_TERM now_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ return enif_now_time(env);
+}
+
+static ERL_NIF_TERM cpu_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ return enif_cpu_time(env);
+}
+
+static ERL_NIF_TERM unique_integer(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ERL_NIF_TERM atom_pos = enif_make_atom(env,"positive"),
+ atom_mon = enif_make_atom(env,"monotonic");
+ ERL_NIF_TERM opts = argv[0], opt;
+ ErlNifUniqueInteger properties = 0;
+
+ while (!enif_is_empty_list(env, opts)) {
+ if (!enif_get_list_cell(env, opts, &opt, &opts))
+ return enif_make_badarg(env);
+
+ if (enif_compare(opt, atom_pos) == 0)
+ properties |= ERL_NIF_UNIQUE_POSITIVE;
+ if (enif_compare(opt, atom_mon) == 0)
+ properties |= ERL_NIF_UNIQUE_MONOTONIC;
+ }
+
+ return enif_make_unique_integer(env, properties);
+}
+
+static ERL_NIF_TERM is_process_alive(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifPid pid;
+ if (!enif_get_local_pid(env, argv[0], &pid))
+ return enif_make_badarg(env);
+ if (enif_is_process_alive(env, &pid))
+ return atom_true;
+ return atom_false;
+}
+
+static ERL_NIF_TERM is_port_alive(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifPort port;
+ if (!enif_get_local_port(env, argv[0], &port))
+ return enif_make_badarg(env);
+ if (enif_is_port_alive(env, &port))
+ return atom_true;
+ return atom_false;
+}
+
+static ERL_NIF_TERM term_to_binary(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifBinary bin;
+ ErlNifPid pid;
+ ErlNifEnv *msg_env = env;
+ ERL_NIF_TERM term;
+
+ if (enif_get_local_pid(env, argv[1], &pid))
+ msg_env = enif_alloc_env();
+
+ if (!enif_term_to_binary(msg_env, argv[0], &bin))
+ return enif_make_badarg(env);
+
+ term = enif_make_binary(msg_env, &bin);
+
+ if (msg_env != env) {
+ enif_send(env, &pid, msg_env, term);
+ enif_free_env(msg_env);
+ return atom_true;
+ } else {
+ return term;
+ }
+}
+
+static ERL_NIF_TERM binary_to_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifBinary bin;
+ ERL_NIF_TERM term, ret_term;
+ ErlNifPid pid;
+ ErlNifEnv *msg_env = env;
+ unsigned int opts;
+ ErlNifUInt64 ret;
+
+ if (enif_get_local_pid(env, argv[1], &pid))
+ msg_env = enif_alloc_env();
+
+ if (!enif_inspect_binary(env, argv[0], &bin)
+ || !enif_get_uint(env, argv[2], &opts))
+ return enif_make_badarg(env);
+
+ ret = enif_binary_to_term(msg_env, bin.data, bin.size, &term,
+ (ErlNifBinaryToTerm)opts);
+ if (!ret)
+ return atom_false;
+
+ ret_term = enif_make_uint64(env, ret);
+ if (msg_env != env) {
+ enif_send(env, &pid, msg_env, term);
+ enif_free_env(msg_env);
+ return ret_term;
+ } else {
+ return enif_make_tuple2(env, ret_term, term);
+ }
+}
+
+static ERL_NIF_TERM port_command(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifPort port;
+
+ if (!enif_get_local_port(env, argv[0], &port))
+ return enif_make_badarg(env);
+
+ if (!enif_port_command(env, &port, NULL, argv[1]))
+ return enif_make_badarg(env);
+ return atom_true;
+}
+
+static ERL_NIF_TERM format_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifBinary obin;
+ unsigned int size;
+
+ if (!enif_get_uint(env, argv[0], &size))
+ return enif_make_badarg(env);
+ if (!enif_alloc_binary(size,&obin))
+ return enif_make_badarg(env);
+
+ if (enif_snprintf((char*)obin.data, (size_t)size, "%T", argv[1]) < 0)
+ return atom_false;
+
+ return enif_make_binary(env,&obin);
+}
+
+
static ErlNifFunc nif_funcs[] =
{
{"lib_version", 0, lib_version},
@@ -2002,7 +2043,7 @@ static ErlNifFunc nif_funcs[] =
{"make_new_resource", 2, make_new_resource},
{"check_is", 11, check_is},
{"check_is_exception", 0, check_is_exception},
- {"length_test", 5, length_test},
+ {"length_test", 6, length_test},
{"make_atoms", 0, make_atoms},
{"make_strings", 0, make_strings},
{"make_new_resource", 2, make_new_resource},
@@ -2019,6 +2060,7 @@ static ErlNifFunc nif_funcs[] =
{"join_send_thread", 1, join_send_thread},
{"copy_blob", 1, copy_blob},
{"send_term", 2, send_term},
+ {"send_copy_term", 2, send_copy_term},
{"reverse_list",1, reverse_list},
{"echo_int", 1, echo_int},
{"type_sizes", 0, type_sizes},
@@ -2026,12 +2068,6 @@ static ErlNifFunc nif_funcs[] =
{"otp_9828_nif", 1, otp_9828_nif},
{"consume_timeslice_nif", 2, consume_timeslice_nif},
{"call_nif_schedule", 2, call_nif_schedule},
-#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT
- {"call_dirty_nif", 3, call_dirty_nif},
- {"send_from_dirty_nif", 1, send_from_dirty_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND},
- {"call_dirty_nif_exception", 1, call_dirty_nif_exception, ERL_NIF_DIRTY_JOB_IO_BOUND},
- {"call_dirty_nif_zero_args", 0, call_dirty_nif_zero_args, ERL_NIF_DIRTY_JOB_CPU_BOUND},
-#endif
{"call_nif_exception", 1, call_nif_exception},
{"call_nif_nan_or_inf", 1, call_nif_nan_or_inf},
{"call_nif_atom_too_long", 1, call_nif_atom_too_long},
@@ -2046,8 +2082,16 @@ static ErlNifFunc nif_funcs[] =
{"sorted_list_from_maps_nif", 1, sorted_list_from_maps_nif},
{"monotonic_time", 1, monotonic_time},
{"time_offset", 1, time_offset},
- {"convert_time_unit", 3, convert_time_unit}
+ {"convert_time_unit", 3, convert_time_unit},
+ {"now_time", 0, now_time},
+ {"cpu_time", 0, cpu_time},
+ {"unique_integer_nif", 1, unique_integer},
+ {"is_process_alive_nif", 1, is_process_alive},
+ {"is_port_alive_nif", 1, is_port_alive},
+ {"term_to_binary_nif", 2, term_to_binary},
+ {"binary_to_term_nif", 3, binary_to_term},
+ {"port_command_nif", 2, port_command},
+ {"format_term_nif", 2, format_term}
};
ERL_NIF_INIT(nif_SUITE,nif_funcs,load,reload,upgrade,unload)
-
diff --git a/erts/emulator/test/nif_SUITE_data/nif_mod.c b/erts/emulator/test/nif_SUITE_data/nif_mod.c
index f7e729e2b6..fd8a0d0595 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_mod.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_mod.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2014. All Rights Reserved.
+ * Copyright Ericsson AB 2009-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/nif_SUITE_data/nif_mod.erl b/erts/emulator/test/nif_SUITE_data/nif_mod.erl
index e65d4577c7..1fcc33faa4 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_mod.erl
+++ b/erts/emulator/test/nif_SUITE_data/nif_mod.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@
-module(nif_mod).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-export([load_nif_lib/2, load_nif_lib/3, start/0, lib_version/0, call_history/0,
get_priv_data_ptr/0, make_new_resource/2, get_resource/2]).
@@ -29,11 +29,21 @@
-define(nif_stub,nif_stub_error(?LINE)).
+-ifdef(USE_ON_LOAD).
+-on_load(on_load/0).
+
+on_load() ->
+ [{data_dir, Path}] = ets:lookup(nif_SUITE, data_dir),
+ [{lib_version, Ver}] = ets:lookup(nif_SUITE, lib_version),
+ erlang:load_nif(filename:join(Path,libname(Ver)), []).
+
+-endif.
+
load_nif_lib(Config, Ver) ->
load_nif_lib(Config, Ver, []).
load_nif_lib(Config, Ver, LoadInfo) ->
- ?line Path = ?config(data_dir, Config),
+ Path = proplists:get_value(data_dir, Config),
erlang:load_nif(filename:join(Path,libname(Ver)), LoadInfo).
libname(no_init) -> libname(3);
diff --git a/erts/emulator/test/nif_SUITE_data/tester.erl b/erts/emulator/test/nif_SUITE_data/tester.erl
index b393e29b82..f955f99c9a 100644
--- a/erts/emulator/test/nif_SUITE_data/tester.erl
+++ b/erts/emulator/test/nif_SUITE_data/tester.erl
@@ -1,12 +1,11 @@
-module(tester).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-export([load_nif_lib/2, run/0]).
-
load_nif_lib(Config, LibName) ->
- ?line Path = ?config(data_dir, Config),
+ Path = proplists:get_value(data_dir, Config),
erlang:load_nif(filename:join(Path,LibName), []).
run() ->
diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl
index fecaad5232..af18545bff 100644
--- a/erts/emulator/test/node_container_SUITE.erl
+++ b/erts/emulator/test/node_container_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -28,37 +28,34 @@
-module(node_container_SUITE).
-author('[email protected]').
-%-define(line_trace, 1).
+-include_lib("common_test/include/ct.hrl").
--include_lib("test_server/include/test_server.hrl").
-
-%-compile(export_all).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2, init_per_testcase/2,
- end_per_testcase/2,
- node_container_refc_check/1]).
+-export([all/0, suite/0, init_per_suite/1, end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2,
+ node_container_refc_check/1]).
-export([term_to_binary_to_term_eq/1,
- round_trip_eq/1,
- cmp/1,
- ref_eq/1,
- node_table_gc/1,
- dist_link_refc/1,
- dist_monitor_refc/1,
- node_controller_refc/1,
- ets_refc/1,
- match_spec_refc/1,
- timer_refc/1,
- otp_4715/1,
- pid_wrap/1,
- port_wrap/1,
- bad_nc/1,
- unique_pid/1,
- iter_max_procs/1]).
-
--define(DEFAULT_TIMEOUT, ?t:minutes(10)).
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+ round_trip_eq/1,
+ cmp/1,
+ ref_eq/1,
+ node_table_gc/1,
+ dist_link_refc/1,
+ dist_monitor_refc/1,
+ node_controller_refc/1,
+ ets_refc/1,
+ match_spec_refc/1,
+ timer_refc/1,
+ otp_4715/1,
+ pid_wrap/1,
+ port_wrap/1,
+ bad_nc/1,
+ unique_pid/1,
+ iter_max_procs/1]).
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 12}}].
+
all() ->
[term_to_binary_to_term_eq, round_trip_eq, cmp, ref_eq,
@@ -67,9 +64,6 @@ all() ->
timer_refc, otp_4715, pid_wrap, port_wrap, bad_nc,
unique_pid, iter_max_procs].
-groups() ->
- [].
-
init_per_suite(Config) ->
Config.
@@ -78,36 +72,26 @@ end_per_suite(_Config) ->
erts_debug:set_internal_state(node_tab_delayed_delete, -1), %% restore original value
available_internal_state(false).
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
available_internal_state(Bool) when Bool == true; Bool == false ->
case {Bool,
- (catch erts_debug:get_internal_state(available_internal_state))} of
- {true, true} ->
- true;
- {false, true} ->
- erts_debug:set_internal_state(available_internal_state, false),
- true;
- {true, _} ->
- erts_debug:set_internal_state(available_internal_state, true),
- false;
- {false, _} ->
- false
+ (catch erts_debug:get_internal_state(available_internal_state))} of
+ {true, true} ->
+ true;
+ {false, true} ->
+ erts_debug:set_internal_state(available_internal_state, false),
+ true;
+ {true, _} ->
+ erts_debug:set_internal_state(available_internal_state, true),
+ false;
+ {false, _} ->
+ false
end.
init_per_testcase(_Case, Config) when is_list(Config) ->
- Dog = ?t:timetrap(?DEFAULT_TIMEOUT),
available_internal_state(true),
- [{watchdog, Dog}|Config].
+ Config.
end_per_testcase(_Case, Config) when is_list(Config) ->
- Dog = ?config(watchdog, Config),
- ?t:timetrap_cancel(Dog),
ok.
%%%
@@ -119,111 +103,108 @@ end_per_testcase(_Case, Config) when is_list(Config) ->
%%
%% Test case: term_to_binary_to_term_eq
%%
-term_to_binary_to_term_eq(doc) ->
- ["Tests that node container terms that are converted to external format "
- "and back stay equal to themselves."];
-term_to_binary_to_term_eq(suite) -> [];
+%% Tests that node container terms that are converted to external format
+%% and back stay equal to themselves.
term_to_binary_to_term_eq(Config) when is_list(Config) ->
- ?line ThisNode = {node(), erlang:system_info(creation)},
+ ThisNode = {node(), erlang:system_info(creation)},
% Get local node containers
- ?line LPid = self(),
- ?line LXPid = mk_pid(ThisNode, 32767, 8191),
- ?line LPort = hd(erlang:ports()),
- ?line LXPort = mk_port(ThisNode, 268435455),
- ?line LLRef = make_ref(),
- ?line LHLRef = mk_ref(ThisNode, [47, 11]),
- ?line LSRef = mk_ref(ThisNode, [4711]),
+ LPid = self(),
+ LXPid = mk_pid(ThisNode, 32767, 8191),
+ LPort = hd(erlang:ports()),
+ LXPort = mk_port(ThisNode, 268435455),
+ LLRef = make_ref(),
+ LHLRef = mk_ref(ThisNode, [47, 11]),
+ LSRef = mk_ref(ThisNode, [4711]),
% Test local nc:s
- ?line LPid = binary_to_term(term_to_binary(LPid)),
- ?line LXPid = binary_to_term(term_to_binary(LXPid)),
- ?line LPort = binary_to_term(term_to_binary(LPort)),
- ?line LXPort = binary_to_term(term_to_binary(LXPort)),
- ?line LLRef = binary_to_term(term_to_binary(LLRef)),
- ?line LHLRef = binary_to_term(term_to_binary(LHLRef)),
- ?line LSRef = binary_to_term(term_to_binary(LSRef)),
+ LPid = binary_to_term(term_to_binary(LPid)),
+ LXPid = binary_to_term(term_to_binary(LXPid)),
+ LPort = binary_to_term(term_to_binary(LPort)),
+ LXPort = binary_to_term(term_to_binary(LXPort)),
+ LLRef = binary_to_term(term_to_binary(LLRef)),
+ LHLRef = binary_to_term(term_to_binary(LHLRef)),
+ LSRef = binary_to_term(term_to_binary(LSRef)),
% Get remote node containers
- ?line RNode = {get_nodename(), 3},
- ?line RPid = mk_pid(RNode, 4711, 1),
- ?line RXPid = mk_pid(RNode, 32767, 8191),
- ?line RPort = mk_port(RNode, 4711),
- ?line RXPort = mk_port(RNode, 268435455),
- ?line RLRef = mk_ref(RNode, [4711, 4711, 4711]),
- ?line RHLRef = mk_ref(RNode, [4711, 4711]),
- ?line RSRef = mk_ref(RNode, [4711]),
+ ttbtteq_do_remote({get_nodename(), 3}),
+ ttbtteq_do_remote({get_nodename(), 4}),
+ ttbtteq_do_remote({get_nodename(), 16#adec0ded}),
+ nc_refc_check(node()),
+ ok.
+
+ttbtteq_do_remote(RNode) ->
+ RPid = mk_pid(RNode, 4711, 1),
+ RXPid = mk_pid(RNode, 32767, 8191),
+ RPort = mk_port(RNode, 4711),
+ RXPort = mk_port(RNode, 268435455),
+ RLRef = mk_ref(RNode, [4711, 4711, 4711]),
+ RHLRef = mk_ref(RNode, [4711, 4711]),
+ RSRef = mk_ref(RNode, [4711]),
% Test remote nc:s
- ?line RPid = binary_to_term(term_to_binary(RPid)),
- ?line RXPid = binary_to_term(term_to_binary(RXPid)),
- ?line RPort = binary_to_term(term_to_binary(RPort)),
- ?line RXPort = binary_to_term(term_to_binary(RXPort)),
- ?line RLRef = binary_to_term(term_to_binary(RLRef)),
- ?line RHLRef = binary_to_term(term_to_binary(RHLRef)),
- ?line RSRef = binary_to_term(term_to_binary(RSRef)),
- ?line nc_refc_check(node()),
- ?line ok.
+ RPid = binary_to_term(term_to_binary(RPid)),
+ RXPid = binary_to_term(term_to_binary(RXPid)),
+ RPort = binary_to_term(term_to_binary(RPort)),
+ RXPort = binary_to_term(term_to_binary(RXPort)),
+ RLRef = binary_to_term(term_to_binary(RLRef)),
+ RHLRef = binary_to_term(term_to_binary(RHLRef)),
+ RSRef = binary_to_term(term_to_binary(RSRef)),
+ ok.
%%
%% Test case: round_trip_eq
%%
-round_trip_eq(doc) ->
- ["Tests that node containers that are sent beteen nodes stay equal to "
- "themselves."];
-round_trip_eq(suite) -> [];
+%% Tests that node containers that are sent beteen nodes stay equal to themselves.
round_trip_eq(Config) when is_list(Config) ->
- ?line ThisNode = {node(), erlang:system_info(creation)},
- ?line NodeFirstName = get_nodefirstname(),
- ?line ?line {ok, Node} = start_node(NodeFirstName),
- ?line Self = self(),
- ?line RPid = spawn_link(Node,
- fun () ->
- receive
- {Self, Data} ->
- Self ! {self(), Data}
- end
- end),
- ?line SentPid = self(),
- ?line SentXPid = mk_pid(ThisNode, 17471, 8190),
- ?line SentPort = hd(erlang:ports()),
- ?line SentXPort = mk_port(ThisNode, 268435451),
- ?line SentLRef = make_ref(),
- ?line SentHLRef = mk_ref(ThisNode, [4711, 17]),
- ?line SentSRef = mk_ref(ThisNode, [4711]),
- ?line RPid ! {Self, {SentPid,
- SentXPid,
- SentPort,
- SentXPort,
- SentLRef,
- SentHLRef,
- SentSRef}},
+ ThisNode = {node(), erlang:system_info(creation)},
+ NodeFirstName = get_nodefirstname(),
+ {ok, Node} = start_node(NodeFirstName),
+ Self = self(),
+ RPid = spawn_link(Node,
+ fun () ->
+ receive
+ {Self, Data} ->
+ Self ! {self(), Data}
+ end
+ end),
+ SentPid = self(),
+ SentXPid = mk_pid(ThisNode, 17471, 8190),
+ SentPort = hd(erlang:ports()),
+ SentXPort = mk_port(ThisNode, 268435451),
+ SentLRef = make_ref(),
+ SentHLRef = mk_ref(ThisNode, [4711, 17]),
+ SentSRef = mk_ref(ThisNode, [4711]),
+ RPid ! {Self, {SentPid,
+ SentXPid,
+ SentPort,
+ SentXPort,
+ SentLRef,
+ SentHLRef,
+ SentSRef}},
receive
- {RPid, {RecPid,
- RecXPid,
- RecPort,
- RecXPort,
- RecLRef,
- RecHLRef,
- RecSRef}} ->
- ?line stop_node(Node),
- ?line SentPid = RecPid,
- ?line SentXPid = RecXPid,
- ?line SentPort = RecPort,
- ?line SentXPort = RecXPort,
- ?line SentLRef = RecLRef,
- ?line SentHLRef = RecHLRef,
- ?line SentSRef = RecSRef,
- ?line nc_refc_check(node()),
- ?line ok
+ {RPid, {RecPid,
+ RecXPid,
+ RecPort,
+ RecXPort,
+ RecLRef,
+ RecHLRef,
+ RecSRef}} ->
+ stop_node(Node),
+ SentPid = RecPid,
+ SentXPid = RecXPid,
+ SentPort = RecPort,
+ SentXPort = RecXPort,
+ SentLRef = RecLRef,
+ SentHLRef = RecHLRef,
+ SentSRef = RecSRef,
+ nc_refc_check(node()),
+ ok
end.
-
+
%%
%% Test case: cmp
%%
-cmp(doc) ->
- ["Tests that Erlang term comparison works as it should on node "
- "containers."];
-cmp(suite) -> [];
+%% Tests that Erlang term comparison works as it should on node containers.
cmp(Config) when is_list(Config) ->
%% Inter type comparison ---------------------------------------------------
@@ -234,103 +215,103 @@ cmp(Config) when is_list(Config) ->
IRef = make_ref(),
ERef = mk_ref({get_nodename(), 2}, [1,2,3]),
-
+
IPid = self(),
EPid = mk_pid(RNode, 1, 2),
IPort = hd(erlang:ports()),
EPort = mk_port(RNode, 1),
-
+
%% Test pids ----------------------------------------------------
- ?line true = 1 < IPid,
- ?line true = 1.3 < IPid,
- ?line true = (1 bsl 64) < IPid,
- ?line true = an_atom < IPid,
- ?line true = IRef < IPid,
- ?line true = ERef < IPid,
- ?line true = fun () -> a_fun end < IPid,
- ?line true = IPort < IPid,
- ?line true = EPort < IPid,
- ?line true = IPid < {a, tuple},
- ?line true = IPid < [],
- ?line true = IPid < [a|cons],
- ?line true = IPid < <<"a binary">>,
-
- ?line true = 1 < EPid,
- ?line true = 1.3 < EPid,
- ?line true = (1 bsl 64) < EPid,
- ?line true = an_atom < EPid,
- ?line true = IRef < EPid,
- ?line true = ERef < EPid,
- ?line true = fun () -> a_fun end < EPid,
- ?line true = IPort < EPid,
- ?line true = EPort < EPid,
- ?line true = EPid < {a, tuple},
- ?line true = EPid < [],
- ?line true = EPid < [a|cons],
- ?line true = EPid < <<"a binary">>,
+ true = 1 < IPid,
+ true = 1.3 < IPid,
+ true = (1 bsl 64) < IPid,
+ true = an_atom < IPid,
+ true = IRef < IPid,
+ true = ERef < IPid,
+ true = fun () -> a_fun end < IPid,
+ true = IPort < IPid,
+ true = EPort < IPid,
+ true = IPid < {a, tuple},
+ true = IPid < [],
+ true = IPid < [a|cons],
+ true = IPid < <<"a binary">>,
+
+ true = 1 < EPid,
+ true = 1.3 < EPid,
+ true = (1 bsl 64) < EPid,
+ true = an_atom < EPid,
+ true = IRef < EPid,
+ true = ERef < EPid,
+ true = fun () -> a_fun end < EPid,
+ true = IPort < EPid,
+ true = EPort < EPid,
+ true = EPid < {a, tuple},
+ true = EPid < [],
+ true = EPid < [a|cons],
+ true = EPid < <<"a binary">>,
%% Test ports --------------------------------------------------
- ?line true = 1 < IPort,
- ?line true = 1.3 < IPort,
- ?line true = (1 bsl 64) < IPort,
- ?line true = an_atom < IPort,
- ?line true = IRef < IPort,
- ?line true = ERef < IPort,
- ?line true = fun () -> a_fun end < IPort,
- ?line true = IPort < IPid,
- ?line true = IPort < EPid,
- ?line true = IPort < {a, tuple},
- ?line true = IPort < [],
- ?line true = IPort < [a|cons],
- ?line true = IPort < <<"a binary">>,
-
- ?line true = 1 < EPort,
- ?line true = 1.3 < EPort,
- ?line true = (1 bsl 64) < EPort,
- ?line true = an_atom < EPort,
- ?line true = IRef < EPort,
- ?line true = ERef < EPort,
- ?line true = fun () -> a_fun end < EPort,
- ?line true = EPort < IPid,
- ?line true = EPort < EPid,
- ?line true = EPort < {a, tuple},
- ?line true = EPort < [],
- ?line true = EPort < [a|cons],
- ?line true = EPort < <<"a binary">>,
+ true = 1 < IPort,
+ true = 1.3 < IPort,
+ true = (1 bsl 64) < IPort,
+ true = an_atom < IPort,
+ true = IRef < IPort,
+ true = ERef < IPort,
+ true = fun () -> a_fun end < IPort,
+ true = IPort < IPid,
+ true = IPort < EPid,
+ true = IPort < {a, tuple},
+ true = IPort < [],
+ true = IPort < [a|cons],
+ true = IPort < <<"a binary">>,
+
+ true = 1 < EPort,
+ true = 1.3 < EPort,
+ true = (1 bsl 64) < EPort,
+ true = an_atom < EPort,
+ true = IRef < EPort,
+ true = ERef < EPort,
+ true = fun () -> a_fun end < EPort,
+ true = EPort < IPid,
+ true = EPort < EPid,
+ true = EPort < {a, tuple},
+ true = EPort < [],
+ true = EPort < [a|cons],
+ true = EPort < <<"a binary">>,
%% Test refs ----------------------------------------------------
- ?line true = 1 < IRef,
- ?line true = 1.3 < IRef,
- ?line true = (1 bsl 64) < IRef,
- ?line true = an_atom < IRef,
- ?line true = IRef < fun () -> a_fun end,
- ?line true = IRef < IPort,
- ?line true = IRef < EPort,
- ?line true = IRef < IPid,
- ?line true = IRef < EPid,
- ?line true = IRef < {a, tuple},
- ?line true = IRef < [],
- ?line true = IRef < [a|cons],
- ?line true = IRef < <<"a binary">>,
-
- ?line true = 1 < ERef,
- ?line true = 1.3 < ERef,
- ?line true = (1 bsl 64) < ERef,
- ?line true = an_atom < ERef,
- ?line true = ERef < fun () -> a_fun end,
- ?line true = ERef < IPort,
- ?line true = ERef < EPort,
- ?line true = ERef < IPid,
- ?line true = ERef < EPid,
- ?line true = ERef < {a, tuple},
- ?line true = ERef < [],
- ?line true = ERef < [a|cons],
- ?line true = ERef < <<"a binary">>,
+ true = 1 < IRef,
+ true = 1.3 < IRef,
+ true = (1 bsl 64) < IRef,
+ true = an_atom < IRef,
+ true = IRef < fun () -> a_fun end,
+ true = IRef < IPort,
+ true = IRef < EPort,
+ true = IRef < IPid,
+ true = IRef < EPid,
+ true = IRef < {a, tuple},
+ true = IRef < [],
+ true = IRef < [a|cons],
+ true = IRef < <<"a binary">>,
+
+ true = 1 < ERef,
+ true = 1.3 < ERef,
+ true = (1 bsl 64) < ERef,
+ true = an_atom < ERef,
+ true = ERef < fun () -> a_fun end,
+ true = ERef < IPort,
+ true = ERef < EPort,
+ true = ERef < IPid,
+ true = ERef < EPid,
+ true = ERef < {a, tuple},
+ true = ERef < [],
+ true = ERef < [a|cons],
+ true = ERef < <<"a binary">>,
%% Intra type comparison ---------------------------------------------------
-
+
%% Test pids ----------------------------------------------------
%%
@@ -338,13 +319,13 @@ cmp(Config) when is_list(Config) ->
%% serial, number, nodename, creation
%%
- ?line Pid = mk_pid({b@b, 2}, 4711, 1),
+ Pid = mk_pid({b@b, 2}, 4711, 1),
- ?line true = mk_pid({a@b, 1}, 4710, 2) > Pid,
- ?line true = mk_pid({a@b, 1}, 4712, 1) > Pid,
- ?line true = mk_pid({c@b, 1}, 4711, 1) > Pid,
- ?line true = mk_pid({b@b, 3}, 4711, 1) > Pid,
- ?line true = mk_pid({b@b, 2}, 4711, 1) =:= Pid,
+ true = mk_pid({a@b, 1}, 4710, 2) > Pid,
+ true = mk_pid({a@b, 1}, 4712, 1) > Pid,
+ true = mk_pid({c@b, 1}, 4711, 1) > Pid,
+ true = mk_pid({b@b, 3}, 4711, 1) > Pid,
+ true = mk_pid({b@b, 2}, 4711, 1) =:= Pid,
%% Test ports ---------------------------------------------------
%%
@@ -356,12 +337,12 @@ cmp(Config) when is_list(Config) ->
%% Significance used to be: dist_slot, number,
%% creation.
- ?line Port = mk_port({b@b, 2}, 4711),
+ Port = mk_port({b@b, 2}, 4711),
- ?line true = mk_port({c@b, 1}, 4710) > Port,
- ?line true = mk_port({b@b, 3}, 4710) > Port,
- ?line true = mk_port({b@b, 2}, 4712) > Port,
- ?line true = mk_port({b@b, 2}, 4711) =:= Port,
+ true = mk_port({c@b, 1}, 4710) > Port,
+ true = mk_port({b@b, 3}, 4710) > Port,
+ true = mk_port({b@b, 2}, 4712) > Port,
+ true = mk_port({b@b, 2}, 4711) =:= Port,
%% Test refs ----------------------------------------------------
%% Significance (most -> least):
@@ -373,99 +354,96 @@ cmp(Config) when is_list(Config) ->
%% creation.
%%
- ?line Ref = mk_ref({b@b, 2}, [4711, 4711, 4711]),
+ Ref = mk_ref({b@b, 2}, [4711, 4711, 4711]),
- ?line true = mk_ref({c@b, 1}, [4710, 4710, 4710]) > Ref,
- ?line true = mk_ref({b@b, 3}, [4710, 4710, 4710]) > Ref,
- ?line true = mk_ref({b@b, 2}, [4710, 4710, 4712]) > Ref,
- ?line true = mk_ref({b@b, 2}, [4710, 4712, 4711]) > Ref,
- ?line true = mk_ref({b@b, 2}, [4712, 4711, 4711]) > Ref,
- ?line true = mk_ref({b@b, 2}, [4711, 4711, 4711]) =:= Ref,
+ true = mk_ref({c@b, 1}, [4710, 4710, 4710]) > Ref,
+ true = mk_ref({b@b, 3}, [4710, 4710, 4710]) > Ref,
+ true = mk_ref({b@b, 2}, [4710, 4710, 4712]) > Ref,
+ true = mk_ref({b@b, 2}, [4710, 4712, 4711]) > Ref,
+ true = mk_ref({b@b, 2}, [4712, 4711, 4711]) > Ref,
+ true = mk_ref({b@b, 2}, [4711, 4711, 4711]) =:= Ref,
ok.
%%
%% Test case: ref_eq
%%
-ref_eq(doc) -> ["Test that one word refs \"works\"."];
-ref_eq(suite) -> [];
+%% Test that one word refs works
ref_eq(Config) when is_list(Config) ->
- ?line ThisNode = {node(), erlang:system_info(creation)},
- ?line AnotherNode = {get_nodename(),2},
- ?line LLongRef = mk_ref(ThisNode, [4711, 0, 0]),
- ?line LHalfLongRef = mk_ref(ThisNode, [4711, 0]),
- ?line LShortRef = mk_ref(ThisNode, [4711]),
- ?line true = LLongRef =:= LShortRef,
- ?line true = LLongRef =:= LHalfLongRef,
- ?line true = LLongRef =:= LLongRef,
- ?line true = LHalfLongRef =:= LShortRef,
- ?line true = LHalfLongRef =:= LHalfLongRef,
- ?line true = LShortRef =:= LShortRef,
- ?line false = LShortRef == mk_ref(ThisNode, [4711, 0, 1]), % Not any more
- ?line RLongRef = mk_ref(AnotherNode, [4711, 0, 0]),
- ?line RHalfLongRef = mk_ref(AnotherNode, [4711, 0]),
- ?line RShortRef = mk_ref(AnotherNode, [4711]),
- ?line true = RLongRef =:= RShortRef,
- ?line true = RLongRef =:= RHalfLongRef,
- ?line true = RLongRef =:= RLongRef,
- ?line true = RHalfLongRef =:= RShortRef,
- ?line true = RHalfLongRef =:= RHalfLongRef,
- ?line true = RShortRef =:= RShortRef,
- ?line false = RShortRef == mk_ref(AnotherNode, [4711, 0, 1]), % Not any more
- ?line nc_refc_check(node()),
- ?line ok.
-
+ ThisNode = {node(), erlang:system_info(creation)},
+ AnotherNode = {get_nodename(),2},
+ LLongRef = mk_ref(ThisNode, [4711, 0, 0]),
+ LHalfLongRef = mk_ref(ThisNode, [4711, 0]),
+ LShortRef = mk_ref(ThisNode, [4711]),
+ true = LLongRef =:= LShortRef,
+ true = LLongRef =:= LHalfLongRef,
+ true = LLongRef =:= LLongRef,
+ true = LHalfLongRef =:= LShortRef,
+ true = LHalfLongRef =:= LHalfLongRef,
+ true = LShortRef =:= LShortRef,
+ false = LShortRef == mk_ref(ThisNode, [4711, 0, 1]), % Not any more
+ RLongRef = mk_ref(AnotherNode, [4711, 0, 0]),
+ RHalfLongRef = mk_ref(AnotherNode, [4711, 0]),
+ RShortRef = mk_ref(AnotherNode, [4711]),
+ true = RLongRef =:= RShortRef,
+ true = RLongRef =:= RHalfLongRef,
+ true = RLongRef =:= RLongRef,
+ true = RHalfLongRef =:= RShortRef,
+ true = RHalfLongRef =:= RHalfLongRef,
+ true = RShortRef =:= RShortRef,
+ false = RShortRef == mk_ref(AnotherNode, [4711, 0, 1]), % Not any more
+ nc_refc_check(node()),
+ ok.
+
%%
%% Test case: node_table_gc
%%
-node_table_gc(doc) ->
- ["Tests that node tables are garbage collected."];
-node_table_gc(suite) -> [];
+%% Tests that node tables are garbage collected.
node_table_gc(Config) when is_list(Config) ->
erts_debug:set_internal_state(available_internal_state, true),
erts_debug:set_internal_state(node_tab_delayed_delete, 0),
- ?line PreKnown = nodes(known),
- ?line ?t:format("PreKnown = ~p~n", [PreKnown]),
- ?line make_node_garbage(0, 200000, 1000, []),
- ?line PostKnown = nodes(known),
- ?line PostAreas = erlang:system_info(allocated_areas),
- ?line ?t:format("PostKnown = ~p~n", [PostKnown]),
- ?line ?t:format("PostAreas = ~p~n", [PostAreas]),
- ?line true = length(PostKnown) =< length(PreKnown),
- ?line nc_refc_check(node()),
+ PreKnown = nodes(known),
+ io:format("PreKnown = ~p~n", [PreKnown]),
+ make_node_garbage(0, 200000, 1000, []),
+ PostKnown = nodes(known),
+ PostAreas = erlang:system_info(allocated_areas),
+ io:format("PostKnown = ~p~n", [PostKnown]),
+ io:format("PostAreas = ~p~n", [PostAreas]),
+ true = length(PostKnown) =< length(PreKnown),
+ nc_refc_check(node()),
erts_debug:set_internal_state(node_tab_delayed_delete, -1), %% restore original value
- ?line ok.
+ ok.
make_node_garbage(N, L, I, Ps) when N < L ->
- ?line Self = self(),
- ?line P = spawn_link(fun () ->
- % Generate two node entries and one dist
- % entry per node name
- ?line PL1 = make_faked_pid_list(N,
- I div 2,
- 1),
- ?line put(a, PL1),
- ?line PL2 = make_faked_pid_list(N,
- I div 2,
- 2),
- ?line put(b, PL2),
- ?line Self ! {self(), length(nodes(known))}
- end),
- ?line receive
- {P, KnownLength} ->
- ?line true = KnownLength >= I div 2
- end,
- ?line make_node_garbage(N+(I div 2)*2, L, I, [P|Ps]);
+ Self = self(),
+ P = spawn_link(fun () ->
+ % Generate two node entries and one dist
+ % entry per node name
+ PL1 = make_faked_pid_list(N,
+ I div 2,
+ 1),
+ put(a, PL1),
+ PL2 = make_faked_pid_list(N,
+ I div 2,
+ 2),
+ put(b, PL2),
+ Self ! {self(), length(nodes(known))}
+ end),
+ receive
+ {P, KnownLength} ->
+ true = KnownLength >= I div 2
+ end,
+ make_node_garbage(N+(I div 2)*2, L, I, [P|Ps]);
make_node_garbage(_, _, _, Ps) ->
%% Cleanup garbage...
ProcIsCleanedUp
- = fun (Proc) ->
- undefined == erts_debug:get_internal_state({process_status,
- Proc})
- end,
+ = fun (Proc) ->
+ undefined == erts_debug:get_internal_state({process_status,
+ Proc})
+ end,
lists:foreach(fun (P) -> wait_until(fun () -> ProcIsCleanedUp(P) end) end,
- Ps),
- ?line ok.
+ Ps),
+ ok.
make_faked_pid_list(Start, No, Creation) ->
@@ -475,292 +453,274 @@ make_faked_pid_list(_Start, 0, _Creation, Acc) ->
Acc;
make_faked_pid_list(Start, No, Creation, Acc) ->
make_faked_pid_list(Start+1,
- No-1,
- Creation,
- [mk_pid({"faked_node-"
- ++ integer_to_list(Start rem 50000)
- ++ "@"
- ++ atom_to_list(?MODULE),
- Creation},
- 4711,
- 3) | Acc]).
+ No-1,
+ Creation,
+ [mk_pid({"faked_node-"
+ ++ integer_to_list(Start rem 50000)
+ ++ "@"
+ ++ atom_to_list(?MODULE),
+ Creation},
+ 4711,
+ 3) | Acc]).
%%
%% Test case: dist_link_refc
%%
-dist_link_refc(doc) ->
- ["Tests that external reference counts are incremented and decremented "
- "as they should for distributed links"];
-dist_link_refc(suite) -> [];
+%% Tests that external reference counts are incremented and decremented
+%% as they should for distributed links
dist_link_refc(Config) when is_list(Config) ->
- ?line NodeFirstName = get_nodefirstname(),
- ?line ?line {ok, Node} = start_node(NodeFirstName),
- ?line RP = spawn_execer(Node),
- ?line LP = spawn_link_execer(node()),
- ?line true = sync_exec(RP, fun () -> link(LP) end),
- ?line wait_until(fun () ->
- ?line {links, Links} = process_info(LP, links),
- ?line lists:member(RP, Links)
- end),
- ?line NodeCre = sync_exec(RP, fun() -> erlang:system_info(creation) end),
- ?line 1 = reference_type_count(
- link,
- refering_entity_id({process, LP},
- get_node_references({Node, NodeCre}))),
- ?line exec(RP, fun() -> exit(normal) end),
- ?line wait_until(fun () ->
- ?line {links, Links} = process_info(LP, links),
- ?line not lists:member(RP, Links)
- end),
- ?line 0 = reference_type_count(
- link,
- refering_entity_id({process, LP},
- get_node_references({Node, NodeCre}))),
- ?line exit(LP, normal),
- ?line stop_node(Node),
- ?line nc_refc_check(node()),
- ?line ok.
+ NodeFirstName = get_nodefirstname(),
+ {ok, Node} = start_node(NodeFirstName),
+ RP = spawn_execer(Node),
+ LP = spawn_link_execer(node()),
+ true = sync_exec(RP, fun () -> link(LP) end),
+ wait_until(fun () ->
+ {links, Links} = process_info(LP, links),
+ lists:member(RP, Links)
+ end),
+ NodeCre = sync_exec(RP, fun() -> erlang:system_info(creation) end),
+ 1 = reference_type_count(
+ link,
+ refering_entity_id({process, LP},
+ get_node_references({Node, NodeCre}))),
+ exec(RP, fun() -> exit(normal) end),
+ wait_until(fun () ->
+ {links, Links} = process_info(LP, links),
+ not lists:member(RP, Links)
+ end),
+ 0 = reference_type_count(
+ link,
+ refering_entity_id({process, LP},
+ get_node_references({Node, NodeCre}))),
+ exit(LP, normal),
+ stop_node(Node),
+ nc_refc_check(node()),
+ ok.
%%
%% Test case: dist_monitor_refc
%%
-dist_monitor_refc(doc) ->
- ["Tests that external reference counts are incremented and decremented "
- "as they should for distributed monitors"];
-dist_monitor_refc(suite) -> [];
+%% Tests that external reference counts are incremented and decremented
+%% as they should for distributed monitors
dist_monitor_refc(Config) when is_list(Config) ->
- ?line NodeFirstName = get_nodefirstname(),
- ?line {ok, Node} = start_node(NodeFirstName),
- ?line RP = spawn_execer(Node),
- ?line LP = spawn_link_execer(node()),
- ?line RMon = sync_exec(RP, fun () -> erlang:monitor(process, LP) end),
- ?line true = is_reference(RMon),
- ?line LMon = sync_exec(LP, fun () -> erlang:monitor(process, RP) end),
- ?line true = is_reference(LMon),
- ?line NodeCre = sync_exec(RP, fun() -> erlang:system_info(creation) end),
- ?line wait_until(fun () ->
- ?line {monitored_by, MonBy}
- = process_info(LP, monitored_by),
- ?line {monitors, Mon}
- = process_info(LP, monitors),
- ?line (lists:member(RP, MonBy)
- and lists:member({process,RP}, Mon))
- end),
- ?line 3 = reference_type_count(
- monitor,
- refering_entity_id({process, LP},
- get_node_references({Node, NodeCre}))),
- ?line exec(RP, fun () -> exit(normal) end),
- ?line wait_until(fun () ->
- ?line {monitored_by, MonBy}
- = process_info(LP, monitored_by),
- ?line {monitors, Mon}
- = process_info(LP, monitors),
- ?line ((not lists:member(RP, MonBy))
- and (not lists:member({process,RP}, Mon)))
- end),
- ?line ok = sync_exec(LP,
- fun () ->
- receive
- {'DOWN', LMon, process, _, _} ->
- ok
- end
- end),
- ?line 0 = reference_type_count(
- link,
- refering_entity_id({process, LP},
- get_node_references({Node, NodeCre}))),
- ?line exit(LP, normal),
- ?line stop_node(Node),
- ?line nc_refc_check(node()),
- ?line ok.
+ NodeFirstName = get_nodefirstname(),
+ {ok, Node} = start_node(NodeFirstName),
+ RP = spawn_execer(Node),
+ LP = spawn_link_execer(node()),
+ RMon = sync_exec(RP, fun () -> erlang:monitor(process, LP) end),
+ true = is_reference(RMon),
+ LMon = sync_exec(LP, fun () -> erlang:monitor(process, RP) end),
+ true = is_reference(LMon),
+ NodeCre = sync_exec(RP, fun() -> erlang:system_info(creation) end),
+ wait_until(fun () ->
+ {monitored_by, MonBy}
+ = process_info(LP, monitored_by),
+ {monitors, Mon}
+ = process_info(LP, monitors),
+ (lists:member(RP, MonBy)
+ and lists:member({process,RP}, Mon))
+ end),
+ 3 = reference_type_count(
+ monitor,
+ refering_entity_id({process, LP},
+ get_node_references({Node, NodeCre}))),
+ exec(RP, fun () -> exit(normal) end),
+ wait_until(fun () ->
+ {monitored_by, MonBy}
+ = process_info(LP, monitored_by),
+ {monitors, Mon}
+ = process_info(LP, monitors),
+ ((not lists:member(RP, MonBy))
+ and (not lists:member({process,RP}, Mon)))
+ end),
+ ok = sync_exec(LP,
+ fun () ->
+ receive
+ {'DOWN', LMon, process, _, _} ->
+ ok
+ end
+ end),
+ 0 = reference_type_count(
+ link,
+ refering_entity_id({process, LP},
+ get_node_references({Node, NodeCre}))),
+ exit(LP, normal),
+ stop_node(Node),
+ nc_refc_check(node()),
+ ok.
%%
%% Test case: node_controller_refc
%%
-node_controller_refc(doc) ->
- ["Tests that external reference counts are incremented and decremented "
- "as they should for entities controlling a connections."];
-node_controller_refc(suite) -> [];
+%% Tests that external reference counts are incremented and decremented
+%% as they should for entities controlling a connections.
node_controller_refc(Config) when is_list(Config) ->
erts_debug:set_internal_state(available_internal_state, true),
erts_debug:set_internal_state(node_tab_delayed_delete, 0),
- ?line NodeFirstName = get_nodefirstname(),
- ?line ?line {ok, Node} = start_node(NodeFirstName),
- ?line true = lists:member(Node, nodes()),
- ?line 1 = reference_type_count(control, get_dist_references(Node)),
- ?line P = spawn_link_execer(node()),
- ?line Node
- = sync_exec(P,
- fun () ->
- put(remote_net_kernel,
- rpc:call(Node,erlang,whereis,[net_kernel])),
- node(get(remote_net_kernel))
- end),
- ?line Creation = rpc:call(Node, erlang, system_info, [creation]),
- ?line monitor_node(Node,true),
- ?line stop_node(Node),
- ?line receive {nodedown, Node} -> ok end,
- ?line DistRefs = get_dist_references(Node),
- ?line true = reference_type_count(node, DistRefs) > 0,
- ?line 0 = reference_type_count(control, DistRefs),
+ NodeFirstName = get_nodefirstname(),
+ {ok, Node} = start_node(NodeFirstName),
+ true = lists:member(Node, nodes()),
+ 1 = reference_type_count(control, get_dist_references(Node)),
+ P = spawn_link_execer(node()),
+ Node
+ = sync_exec(P,
+ fun () ->
+ put(remote_net_kernel,
+ rpc:call(Node,erlang,whereis,[net_kernel])),
+ node(get(remote_net_kernel))
+ end),
+ Creation = rpc:call(Node, erlang, system_info, [creation]),
+ monitor_node(Node,true),
+ stop_node(Node),
+ receive {nodedown, Node} -> ok end,
+ DistRefs = get_dist_references(Node),
+ true = reference_type_count(node, DistRefs) > 0,
+ 0 = reference_type_count(control, DistRefs),
% Get rid of all references to Node
- ?line exec(P, fun () -> exit(normal) end),
- ?line wait_until(fun () -> not is_process_alive(P) end),
+ exec(P, fun () -> exit(normal) end),
+ wait_until(fun () -> not is_process_alive(P) end),
lists:foreach(fun (Proc) -> garbage_collect(Proc) end, processes()),
- ?line false = get_node_references({Node,Creation}),
- ?line false = get_dist_references(Node),
- ?line false = lists:member(Node, nodes(known)),
- ?line nc_refc_check(node()),
+ false = get_node_references({Node,Creation}),
+ false = get_dist_references(Node),
+ false = lists:member(Node, nodes(known)),
+ nc_refc_check(node()),
erts_debug:set_internal_state(node_tab_delayed_delete, -1), %% restore original value
- ?line ok.
+ ok.
%%
%% Test case: ets_refc
%%
-ets_refc(doc) ->
- ["Tests that external reference counts are incremented and decremented "
- "as they should for data stored in ets tables."];
-ets_refc(suite) -> [];
+%% Tests that external reference counts are incremented and decremented
+%% as they should for data stored in ets tables.
ets_refc(Config) when is_list(Config) ->
- ?line RNode = {get_nodename(), 1},
- ?line RPid = mk_pid(RNode, 4711, 2),
- ?line RPort = mk_port(RNode, 4711),
- ?line RRef = mk_ref(RNode, [4711, 47, 11]),
- ?line Tab = ets:new(ets_refc, []),
- ?line 0 = reference_type_count(ets, get_node_references(RNode)),
- ?line true = ets:insert(Tab, [{a, self()},
- {b, RPid},
- {c, hd(erlang:ports())},
- {d, RPort},
- {e, make_ref()}]),
- ?line 2 = reference_type_count(ets, get_node_references(RNode)),
- ?line true = ets:insert(Tab, {f, RRef}),
- ?line 3 = reference_type_count(ets, get_node_references(RNode)),
- ?line true = ets:delete(Tab, d),
- ?line 2 = reference_type_count(ets, get_node_references(RNode)),
- ?line true = ets:delete_all_objects(Tab),
- ?line 0 = reference_type_count(ets, get_node_references(RNode)),
- ?line true = ets:insert(Tab, [{b, RPid}, {e, make_ref()}]),
- ?line 1 = reference_type_count(ets, get_node_references(RNode)),
- ?line true = ets:delete(Tab),
- ?line 0 = reference_type_count(ets, get_node_references(RNode)),
- ?line nc_refc_check(node()),
- ?line ok.
+ RNode = {get_nodename(), 1},
+ RPid = mk_pid(RNode, 4711, 2),
+ RPort = mk_port(RNode, 4711),
+ RRef = mk_ref(RNode, [4711, 47, 11]),
+ Tab = ets:new(ets_refc, []),
+ 0 = reference_type_count(ets, get_node_references(RNode)),
+ true = ets:insert(Tab, [{a, self()},
+ {b, RPid},
+ {c, hd(erlang:ports())},
+ {d, RPort},
+ {e, make_ref()}]),
+ 2 = reference_type_count(ets, get_node_references(RNode)),
+ true = ets:insert(Tab, {f, RRef}),
+ 3 = reference_type_count(ets, get_node_references(RNode)),
+ true = ets:delete(Tab, d),
+ 2 = reference_type_count(ets, get_node_references(RNode)),
+ true = ets:delete_all_objects(Tab),
+ 0 = reference_type_count(ets, get_node_references(RNode)),
+ true = ets:insert(Tab, [{b, RPid}, {e, make_ref()}]),
+ 1 = reference_type_count(ets, get_node_references(RNode)),
+ true = ets:delete(Tab),
+ 0 = reference_type_count(ets, get_node_references(RNode)),
+ nc_refc_check(node()),
+ ok.
%%
%% Test case: match_spec_refc
%%
-match_spec_refc(doc) ->
- ["Tests that external reference counts are incremented and decremented "
- "as they should for data stored in match specifications."];
-match_spec_refc(suite) -> [];
+%% Tests that external reference counts are incremented and decremented
+%% as they should for data stored in match specifications.
match_spec_refc(Config) when is_list(Config) ->
- ?line RNode = {get_nodename(), 1},
- ?line RPid = mk_pid(RNode, 4711, 2),
- ?line RPort = mk_port(RNode, 4711),
- ?line RRef = mk_ref(RNode, [4711, 47, 11]),
- ?line ok = do_match_spec_test(RNode, RPid, RPort, RRef),
- ?line garbage_collect(),
- ?line NodeRefs = get_node_references(RNode),
- ?line 0 = reference_type_count(binary, NodeRefs),
- ?line 0 = reference_type_count(ets, NodeRefs),
- ?line nc_refc_check(node()),
- ?line ok.
+ RNode = {get_nodename(), 1},
+ RPid = mk_pid(RNode, 4711, 2),
+ RPort = mk_port(RNode, 4711),
+ RRef = mk_ref(RNode, [4711, 47, 11]),
+ ok = do_match_spec_test(RNode, RPid, RPort, RRef),
+ garbage_collect(),
+ NodeRefs = get_node_references(RNode),
+ 0 = reference_type_count(binary, NodeRefs),
+ 0 = reference_type_count(ets, NodeRefs),
+ nc_refc_check(node()),
+ ok.
do_match_spec_test(RNode, RPid, RPort, RRef) ->
- ?line Tab = ets:new(match_spec_refc, []),
- ?line true = ets:insert(Tab, [{a, RPid, RPort, RRef},
- {b, self(), RPort, RRef},
- {c, RPid, RPort, make_ref()},
- {d, RPid, RPort, RRef}]),
- ?line {M1, C1} = ets:select(Tab, [{{'$1',RPid,RPort,RRef},[],['$1']}], 1),
- ?line NodeRefs = get_node_references(RNode),
- ?line 3 = reference_type_count(binary, NodeRefs),
- ?line 10 = reference_type_count(ets, NodeRefs),
- ?line {M2, C2} = ets:select(C1),
- ?line '$end_of_table' = ets:select(C2),
- ?line ets:delete(Tab),
- ?line [a,d] = lists:sort(M1++M2),
- ?line ok.
-
+ Tab = ets:new(match_spec_refc, []),
+ true = ets:insert(Tab, [{a, RPid, RPort, RRef},
+ {b, self(), RPort, RRef},
+ {c, RPid, RPort, make_ref()},
+ {d, RPid, RPort, RRef}]),
+ {M1, C1} = ets:select(Tab, [{{'$1',RPid,RPort,RRef},[],['$1']}], 1),
+ NodeRefs = get_node_references(RNode),
+ 3 = reference_type_count(binary, NodeRefs),
+ 10 = reference_type_count(ets, NodeRefs),
+ {M2, C2} = ets:select(C1),
+ '$end_of_table' = ets:select(C2),
+ ets:delete(Tab),
+ [a,d] = lists:sort(M1++M2),
+ ok.
+
%%
%% Test case: ets_refc
%%
-timer_refc(doc) ->
- ["Tests that external reference counts are incremented and decremented "
- "as they should for data stored in bif timers."];
-timer_refc(suite) -> [];
+%% Tests that external reference counts are incremented and decremented
+%% as they should for data stored in bif timers.
timer_refc(Config) when is_list(Config) ->
- ?line RNode = {get_nodename(), 1},
- ?line RPid = mk_pid(RNode, 4711, 2),
- ?line RPort = mk_port(RNode, 4711),
- ?line RRef = mk_ref(RNode, [4711, 47, 11]),
- ?line 0 = reference_type_count(timer, get_node_references(RNode)),
- ?line Pid = spawn(fun () -> receive after infinity -> ok end end),
- ?line erlang:start_timer(10000, Pid, {RPid, RPort, RRef}),
- ?line 3 = reference_type_count(timer, get_node_references(RNode)),
- ?line exit(Pid, kill),
- ?line Mon = erlang:monitor(process, Pid),
- ?line receive {'DOWN', Mon, process, Pid, _} -> ok end,
- ?line 0 = reference_type_count(timer, get_node_references(RNode)),
- ?line erlang:send_after(500, Pid, {timer, RPid, RPort, RRef}),
- ?line 0 = reference_type_count(timer, get_node_references(RNode)),
- ?line erlang:send_after(500, self(), {timer, RPid, RPort, RRef}),
- ?line erlang:send_after(400, bananfluga, {timer, RPid, RPort, RRef}),
- ?line 6 = reference_type_count(timer, get_node_references(RNode)),
- ?line receive {timer, RPid, RPort, RRef} -> ok end,
- ?line 0 = reference_type_count(timer, get_node_references(RNode)),
- ?line nc_refc_check(node()),
- ?line ok.
-
-otp_4715(doc) -> [];
-otp_4715(suite) -> [];
+ RNode = {get_nodename(), 1},
+ RPid = mk_pid(RNode, 4711, 2),
+ RPort = mk_port(RNode, 4711),
+ RRef = mk_ref(RNode, [4711, 47, 11]),
+ 0 = reference_type_count(timer, get_node_references(RNode)),
+ Pid = spawn(fun () -> receive after infinity -> ok end end),
+ erlang:start_timer(10000, Pid, {RPid, RPort, RRef}),
+ 3 = reference_type_count(timer, get_node_references(RNode)),
+ exit(Pid, kill),
+ Mon = erlang:monitor(process, Pid),
+ receive {'DOWN', Mon, process, Pid, _} -> ok end,
+ 0 = reference_type_count(timer, get_node_references(RNode)),
+ erlang:send_after(500, Pid, {timer, RPid, RPort, RRef}),
+ 0 = reference_type_count(timer, get_node_references(RNode)),
+ erlang:send_after(500, self(), {timer, RPid, RPort, RRef}),
+ erlang:send_after(400, bananfluga, {timer, RPid, RPort, RRef}),
+ 6 = reference_type_count(timer, get_node_references(RNode)),
+ receive {timer, RPid, RPort, RRef} -> ok end,
+ 0 = reference_type_count(timer, get_node_references(RNode)),
+ nc_refc_check(node()),
+ ok.
+
otp_4715(Config) when is_list(Config) ->
- case ?t:is_release_available("r9b") of
- true -> otp_4715_1(Config);
- false -> {skip,"No R9B found"}
+ case test_server:is_release_available("r9b") of
+ true -> otp_4715_1(Config);
+ false -> {skip,"No R9B found"}
end.
otp_4715_1(Config) ->
case erlang:system_info(compat_rel) of
- 9 ->
- ?line run_otp_4715(Config);
- _ ->
- ?line Pa = filename:dirname(code:which(?MODULE)),
- ?line ?t:run_on_shielded_node(fun () ->
- run_otp_4715(Config)
- end,
- "+R9 -pa " ++ Pa)
+ 9 ->
+ run_otp_4715(Config);
+ _ ->
+ Pa = filename:dirname(code:which(?MODULE)),
+ test_server:run_on_shielded_node(fun () ->
+ run_otp_4715(Config)
+ end,
+ "+R9 -pa " ++ Pa)
end.
run_otp_4715(Config) when is_list(Config) ->
- ?line erts_debug:set_internal_state(available_internal_state, true),
- ?line PidList = [mk_pid({a@b, 1}, 4710, 2),
- mk_pid({a@b, 1}, 4712, 1),
- mk_pid({c@b, 1}, 4711, 1),
- mk_pid({b@b, 3}, 4711, 1),
- mk_pid({b@b, 2}, 4711, 1)],
-
- ?line R9Sorted = old_mod:sort_on_old_node(PidList),
- ?line R9Sorted = lists:sort(PidList).
-
-pid_wrap(doc) -> [];
-pid_wrap(suite) -> [];
-pid_wrap(Config) when is_list(Config) -> ?line pp_wrap(pid).
-
-port_wrap(doc) -> [];
-port_wrap(suite) -> [];
+ erts_debug:set_internal_state(available_internal_state, true),
+ PidList = [mk_pid({a@b, 1}, 4710, 2),
+ mk_pid({a@b, 1}, 4712, 1),
+ mk_pid({c@b, 1}, 4711, 1),
+ mk_pid({b@b, 3}, 4711, 1),
+ mk_pid({b@b, 2}, 4711, 1)],
+
+ R9Sorted = old_mod:sort_on_old_node(PidList),
+ R9Sorted = lists:sort(PidList).
+
+pid_wrap(Config) when is_list(Config) -> pp_wrap(pid).
+
port_wrap(Config) when is_list(Config) ->
- ?line case ?t:os_type() of
- {unix, _} ->
- ?line pp_wrap(port);
- _ ->
- ?line {skip, "Only run on unix"}
- end.
+ case os:type() of
+ {unix, _} ->
+ pp_wrap(port);
+ _ ->
+ {skip, "Only run on unix"}
+ end.
get_next_id(pid) ->
erts_debug:get_internal_state(next_pid);
@@ -773,167 +733,160 @@ set_next_id(port, N) ->
erts_debug:set_internal_state(next_port, N).
pp_wrap(What) ->
- ?line N = set_high_pp_next(What),
- ?line Cre = N + 100,
- ?line ?t:format("no creations = ~p~n", [Cre]),
- ?line PreCre = get_next_id(What),
- ?line ?t:format("pre creations = ~p~n", [PreCre]),
- ?line true = is_integer(PreCre),
- ?line do_pp_creations(What, Cre),
- ?line PostCre = get_next_id(What),
- ?line ?t:format("post creations = ~p~n", [PostCre]),
- ?line true = is_integer(PostCre),
- ?line true = PreCre > PostCre,
- ?line Now = set_next_id(What, ?MAX_PIDS_PORTS div 2),
- ?line ?t:format("reset to = ~p~n", [Now]),
- ?line true = is_integer(Now),
- ?line ok.
+ N = set_high_pp_next(What),
+ Cre = N + 100,
+ io:format("no creations = ~p~n", [Cre]),
+ PreCre = get_next_id(What),
+ io:format("pre creations = ~p~n", [PreCre]),
+ true = is_integer(PreCre),
+ do_pp_creations(What, Cre),
+ PostCre = get_next_id(What),
+ io:format("post creations = ~p~n", [PostCre]),
+ true = is_integer(PostCre),
+ true = PreCre > PostCre,
+ Now = set_next_id(What, ?MAX_PIDS_PORTS div 2),
+ io:format("reset to = ~p~n", [Now]),
+ true = is_integer(Now),
+ ok.
set_high_pp_next(What) ->
- ?line set_high_pp_next(What, ?MAX_PIDS_PORTS-1).
-
+ set_high_pp_next(What, ?MAX_PIDS_PORTS-1).
+
set_high_pp_next(What, N) ->
- ?line M = set_next_id(What, N),
- ?line true = is_integer(M),
- ?line case {M >= N, M =< ?MAX_PIDS_PORTS} of
- {true, true} ->
- ?line ?MAX_PIDS_PORTS - M + 1;
- _ ->
- ?line set_high_pp_next(What, N - 100)
- end.
+ M = set_next_id(What, N),
+ true = is_integer(M),
+ case {M >= N, M =< ?MAX_PIDS_PORTS} of
+ {true, true} ->
+ ?MAX_PIDS_PORTS - M + 1;
+ _ ->
+ set_high_pp_next(What, N - 100)
+ end.
do_pp_creations(_What, N) when is_integer(N), N =< 0 ->
- ?line done;
+ done;
do_pp_creations(pid, N) when is_integer(N) ->
%% Create new pid and make sure it works...
- ?line Me = self(),
- ?line Ref = make_ref(),
- ?line Pid = spawn_link(fun () ->
- receive
- Ref ->
- Me ! Ref
- end
- end),
- ?line Pid ! Ref,
- ?line receive
- Ref ->
- ?line do_pp_creations(pid, N - 1)
- end;
+ Me = self(),
+ Ref = make_ref(),
+ Pid = spawn_link(fun () ->
+ receive
+ Ref ->
+ Me ! Ref
+ end
+ end),
+ Pid ! Ref,
+ receive
+ Ref ->
+ do_pp_creations(pid, N - 1)
+ end;
do_pp_creations(port, N) when is_integer(N) ->
%% Create new port and make sure it works...
- ?line "hej" = os:cmd("echo hej") -- "\n",
- ?line do_pp_creations(port, N - 1).
+ "hej" = os:cmd("echo hej") -- "\n",
+ do_pp_creations(port, N - 1).
-bad_nc(doc) -> [];
-bad_nc(suite) -> [];
bad_nc(Config) when is_list(Config) ->
% Make sure emulator don't crash on bad node containers...
- ?line MaxPidNum = (1 bsl 15) - 1,
- ?line MaxPidSer = ?MAX_PIDS_PORTS bsr 15,
- ?line ThisNode = {node(), erlang:system_info(creation)},
- ?line {'EXIT', {badarg, mk_pid, _}}
- = (catch mk_pid(ThisNode, MaxPidNum + 1, 17)),
- ?line {'EXIT', {badarg, mk_pid, _}}
- = (catch mk_pid(ThisNode, 4711, MaxPidSer + 1)),
- ?line {'EXIT', {badarg, mk_port, _}}
- = (catch mk_port(ThisNode, ?MAX_PIDS_PORTS + 1)),
- ?line {'EXIT', {badarg, mk_ref, _}}
- = (catch mk_ref(ThisNode,[(1 bsl 18), 4711, 4711])),
- ?line {'EXIT', {badarg, mk_ref, _}}
- = (catch mk_ref(ThisNode, [4711, 4711, 4711, 4711, 4711, 4711, 4711])),
- ?line RemNode = {x@y, 2},
- ?line {'EXIT', {badarg, mk_pid, _}}
- = (catch mk_pid(RemNode, MaxPidNum + 1, MaxPidSer)),
- ?line {'EXIT', {badarg, mk_pid, _}}
- = (catch mk_pid(RemNode, MaxPidNum, MaxPidSer + 1)),
- ?line {'EXIT', {badarg, mk_port, _}}
- = (catch mk_port(RemNode, ?MAX_PIDS_PORTS + 1)),
- ?line {'EXIT', {badarg, mk_ref, _}}
- = (catch mk_ref(RemNode, [(1 bsl 18), 4711, 4711])),
- ?line {'EXIT', {badarg, mk_ref, _}}
- = (catch mk_ref(RemNode, [4711, 4711, 4711, 4711, 4711, 4711, 4711])),
- ?line BadNode = {x@y, 4},
- ?line {'EXIT', {badarg, mk_pid, _}}
- = (catch mk_pid(BadNode, 4711, 17)),
- ?line {'EXIT', {badarg, mk_port, _}}
- = (catch mk_port(BadNode, 4711)),
- ?line {'EXIT', {badarg, mk_ref, _}}
- = (catch mk_ref(BadNode, [4711, 4711, 17])),
- ?line ok.
+ MaxPidNum = (1 bsl 15) - 1,
+ MaxPidSer = ?MAX_PIDS_PORTS bsr 15,
+ ThisNode = {node(), erlang:system_info(creation)},
+ {'EXIT', {badarg, mk_pid, _}}
+ = (catch mk_pid(ThisNode, MaxPidNum + 1, 17)),
+ {'EXIT', {badarg, mk_pid, _}}
+ = (catch mk_pid(ThisNode, 4711, MaxPidSer + 1)),
+ {'EXIT', {badarg, mk_port, _}}
+ = (catch mk_port(ThisNode, ?MAX_PIDS_PORTS + 1)),
+ {'EXIT', {badarg, mk_ref, _}}
+ = (catch mk_ref(ThisNode,[(1 bsl 18), 4711, 4711])),
+ {'EXIT', {badarg, mk_ref, _}}
+ = (catch mk_ref(ThisNode, [4711, 4711, 4711, 4711, 4711, 4711, 4711])),
+ RemNode = {x@y, 2},
+ {'EXIT', {badarg, mk_pid, _}}
+ = (catch mk_pid(RemNode, MaxPidNum + 1, MaxPidSer)),
+ {'EXIT', {badarg, mk_pid, _}}
+ = (catch mk_pid(RemNode, MaxPidNum, MaxPidSer + 1)),
+ {'EXIT', {badarg, mk_port, _}}
+ = (catch mk_port(RemNode, ?MAX_PIDS_PORTS + 1)),
+ {'EXIT', {badarg, mk_ref, _}}
+ = (catch mk_ref(RemNode, [(1 bsl 18), 4711, 4711])),
+ {'EXIT', {badarg, mk_ref, _}}
+ = (catch mk_ref(RemNode, [4711, 4711, 4711, 4711, 4711, 4711, 4711])),
+ BadNode = {x@y, bad_creation},
+ {'EXIT', {badarg, mk_pid, _}}
+ = (catch mk_pid(BadNode, 4711, 17)),
+ {'EXIT', {badarg, mk_port, _}}
+ = (catch mk_port(BadNode, 4711)),
+ {'EXIT', {badarg, mk_ref, _}}
+ = (catch mk_ref(BadNode, [4711, 4711, 17])),
+ ok.
-define(NO_PIDS, 1000000).
-unique_pid(doc) -> [];
-unique_pid(suite) -> [];
unique_pid(Config) when is_list(Config) ->
case catch erlang:system_info(modified_timing_level) of
- Level when is_integer(Level) ->
- {skip,
- "Modified timing (level " ++ integer_to_list(Level)
- ++ ") is enabled. spawn() is too slow for this "
- " test when modified timing is enabled."};
- _ ->
- ?line ?NO_PIDS = length(lists:usort(mkpidlist(?NO_PIDS, []))),
- ?line ok
+ Level when is_integer(Level) ->
+ {skip,
+ "Modified timing (level " ++ integer_to_list(Level)
+ ++ ") is enabled. spawn() is too slow for this "
+ " test when modified timing is enabled."};
+ _ ->
+ ?NO_PIDS = length(lists:usort(mkpidlist(?NO_PIDS, []))),
+ ok
end.
-
+
mkpidlist(0, Ps) -> Ps;
mkpidlist(N, Ps) -> mkpidlist(N-1, [spawn(fun () -> ok end)|Ps]).
-iter_max_procs(doc) -> [];
-iter_max_procs(suite) -> [];
iter_max_procs(Config) when is_list(Config) ->
- ?line NoMoreTests = make_ref(),
- ?line erlang:send_after(10000, self(), NoMoreTests),
- ?line Res = chk_max_proc_line(),
- ?line Res = chk_max_proc_line(),
- ?line done = chk_max_proc_line_until(NoMoreTests, Res),
- ?line {comment,
- io_lib:format("max processes = ~p; "
- "process line length = ~p",
- [element(2, Res), element(1, Res)])}.
-
-
+ NoMoreTests = make_ref(),
+ erlang:send_after(10000, self(), NoMoreTests),
+ Res = chk_max_proc_line(),
+ Res = chk_max_proc_line(),
+ done = chk_max_proc_line_until(NoMoreTests, Res),
+ Cmt = io_lib:format("max processes = ~p; "
+ "process line length = ~p",
+ [element(2, Res), element(1, Res)]),
+ {comment, lists:flatten(Cmt)}.
+
max_proc_line(Root, Parent, N) ->
Me = self(),
case catch spawn_link(fun () -> max_proc_line(Root, Me, N+1) end) of
- {'EXIT', {system_limit, _}} when Root /= self() ->
- Root ! {proc_line_length, N, self()},
- receive remove_proc_line -> Parent ! {exiting, Me} end;
- P when is_pid(P), Root =/= self() ->
- receive {exiting, P} -> Parent ! {exiting, Me} end;
- P when is_pid(P) ->
- P;
- Unexpected ->
- exit({unexpected_spawn_result, Unexpected})
+ {'EXIT', {system_limit, _}} when Root /= self() ->
+ Root ! {proc_line_length, N, self()},
+ receive remove_proc_line -> Parent ! {exiting, Me} end;
+ P when is_pid(P), Root =/= self() ->
+ receive {exiting, P} -> Parent ! {exiting, Me} end;
+ P when is_pid(P) ->
+ P;
+ Unexpected ->
+ exit({unexpected_spawn_result, Unexpected})
end.
chk_max_proc_line() ->
- ?line Child = max_proc_line(self(), self(), 0),
- ?line receive
- {proc_line_length, PLL, End} ->
- ?line PC = erlang:system_info(process_count),
- ?line LP = length(processes()),
- ?line ?t:format("proc line length = ~p; "
- "process count = ~p; "
- "length processes = ~p~n",
- [PLL, PC, LP]),
- ?line End ! remove_proc_line,
- ?line PC = LP,
- ?line receive {exiting, Child} -> ok end,
- ?line {PLL, PC}
- end.
+ Child = max_proc_line(self(), self(), 0),
+ receive
+ {proc_line_length, PLL, End} ->
+ PC = erlang:system_info(process_count),
+ LP = length(processes()),
+ io:format("proc line length = ~p; "
+ "process count = ~p; "
+ "length processes = ~p~n",
+ [PLL, PC, LP]),
+ End ! remove_proc_line,
+ PC = LP,
+ receive {exiting, Child} -> ok end,
+ {PLL, PC}
+ end.
chk_max_proc_line_until(NoMoreTests, Res) ->
receive
- NoMoreTests ->
- ?line done
+ NoMoreTests ->
+ done
after 0 ->
- ?line Res = chk_max_proc_line(),
- ?line chk_max_proc_line_until(NoMoreTests, Res)
+ Res = chk_max_proc_line(),
+ chk_max_proc_line_until(NoMoreTests, Res)
end.
%%
@@ -950,126 +903,126 @@ node_container_refc_check(Node) when is_atom(Node) ->
nc_refc_check(Node) when is_atom(Node) ->
Ref = make_ref(),
Self = self(),
- ?t:format("Starting reference count check of node ~w~n", [Node]),
+ io:format("Starting reference count check of node ~w~n", [Node]),
spawn_link(Node,
- fun () ->
- {{node_references, NodeRefs},
- {dist_references, DistRefs}} = ?ND_REFS,
- check_nd_refc({node(), erlang:system_info(creation)},
- NodeRefs,
- DistRefs,
- fun (ErrMsg) ->
- Self ! {Ref, ErrMsg, failed},
- exit(normal)
- end),
- Self ! {Ref, succeded}
- end),
+ fun () ->
+ {{node_references, NodeRefs},
+ {dist_references, DistRefs}} = ?ND_REFS,
+ check_nd_refc({node(), erlang:system_info(creation)},
+ NodeRefs,
+ DistRefs,
+ fun (ErrMsg) ->
+ Self ! {Ref, ErrMsg, failed},
+ exit(normal)
+ end),
+ Self ! {Ref, succeded}
+ end),
receive
- {Ref, ErrorMsg, failed} ->
- ?t:format("~s~n", [ErrorMsg]),
- ?t:fail(reference_count_check_failed);
- {Ref, succeded} ->
- ?t:format("Reference count check of node ~w succeded!~n", [Node]),
- ok
+ {Ref, ErrorMsg, failed} ->
+ io:format("~s~n", [ErrorMsg]),
+ ct:fail(reference_count_check_failed);
+ {Ref, succeded} ->
+ io:format("Reference count check of node ~w succeded!~n", [Node]),
+ ok
end.
check_nd_refc({ThisNodeName, ThisCreation}, NodeRefs, DistRefs, Fail) ->
case catch begin
- check_refc(ThisNodeName,ThisCreation,"node table",NodeRefs),
- check_refc(ThisNodeName,ThisCreation,"dist table",DistRefs),
- ok
- end of
- ok ->
- ok;
- {'EXIT', Reason} ->
- {Y,Mo,D} = date(),
- {H,Mi,S} = time(),
- ErrMsg = io_lib:format("~n"
- "*** Reference count check of node ~w "
- "failed (~p) at ~w~w~w ~w:~w:~w~n"
- "*** Node table references:~n ~p~n"
- "*** Dist table references:~n ~p~n",
- [node(), Reason, Y, Mo, D, H, Mi, S,
- NodeRefs, DistRefs]),
- Fail(lists:flatten(ErrMsg))
+ check_refc(ThisNodeName,ThisCreation,"node table",NodeRefs),
+ check_refc(ThisNodeName,ThisCreation,"dist table",DistRefs),
+ ok
+ end of
+ ok ->
+ ok;
+ {'EXIT', Reason} ->
+ {Y,Mo,D} = date(),
+ {H,Mi,S} = time(),
+ ErrMsg = io_lib:format("~n"
+ "*** Reference count check of node ~w "
+ "failed (~p) at ~w~w~w ~w:~w:~w~n"
+ "*** Node table references:~n ~p~n"
+ "*** Dist table references:~n ~p~n",
+ [node(), Reason, Y, Mo, D, H, Mi, S,
+ NodeRefs, DistRefs]),
+ Fail(lists:flatten(ErrMsg))
end.
check_refc(ThisNodeName,ThisCreation,Table,EntryList) when is_list(EntryList) ->
lists:foreach(
fun ({Entry, Refc, ReferrerList}) ->
- {DelayedDeleteTimer,
- FoundRefs} =
- lists:foldl(
- fun ({Referrer, ReferencesList}, {DDT, A1}) ->
- {case Referrer of
- {system,delayed_delete_timer} ->
- true;
- _ ->
- DDT
- end,
- A1 + lists:foldl(fun ({_T,Rs},A2) ->
- A2+Rs
- end,
- 0,
- ReferencesList)}
- end,
- {false, 0},
- ReferrerList),
-
- %% Reference count equals found references?
- case {Refc, FoundRefs, DelayedDeleteTimer} of
- {X, X, _} ->
- ok;
- {0, 1, true} ->
- ok;
- _ ->
- exit({invalid_reference_count, Table, Entry})
- end,
-
- %% All entries in table referred to?
- case {Entry, Refc} of
- {ThisNodeName, 0} -> ok;
- {{ThisNodeName, ThisCreation}, 0} -> ok;
- {_, 0} when DelayedDeleteTimer == false ->
- exit({not_referred_entry_in_table, Table, Entry});
- {_, _} -> ok
- end
+ {DelayedDeleteTimer,
+ FoundRefs} =
+ lists:foldl(
+ fun ({Referrer, ReferencesList}, {DDT, A1}) ->
+ {case Referrer of
+ {system,delayed_delete_timer} ->
+ true;
+ _ ->
+ DDT
+ end,
+ A1 + lists:foldl(fun ({_T,Rs},A2) ->
+ A2+Rs
+ end,
+ 0,
+ ReferencesList)}
+ end,
+ {false, 0},
+ ReferrerList),
+
+ %% Reference count equals found references?
+ case {Refc, FoundRefs, DelayedDeleteTimer} of
+ {X, X, _} ->
+ ok;
+ {0, 1, true} ->
+ ok;
+ _ ->
+ exit({invalid_reference_count, Table, Entry})
+ end,
+
+ %% All entries in table referred to?
+ case {Entry, Refc} of
+ {ThisNodeName, 0} -> ok;
+ {{ThisNodeName, ThisCreation}, 0} -> ok;
+ {_, 0} when DelayedDeleteTimer == false ->
+ exit({not_referred_entry_in_table, Table, Entry});
+ {_, _} -> ok
+ end
end,
EntryList),
ok.
get_node_references({NodeName, Creation} = Node) when is_atom(NodeName),
- is_integer(Creation) ->
+ is_integer(Creation) ->
{{node_references, NodeRefs},
- {dist_references, DistRefs}} = ?ND_REFS,
+ {dist_references, DistRefs}} = ?ND_REFS,
check_nd_refc({node(), erlang:system_info(creation)},
- NodeRefs,
- DistRefs,
- fun (ErrMsg) ->
- ?t:format("~s", [ErrMsg]),
- ?t:fail(reference_count_check_failed)
- end),
+ NodeRefs,
+ DistRefs,
+ fun (ErrMsg) ->
+ io:format("~s", [ErrMsg]),
+ ct:fail(reference_count_check_failed)
+ end),
find_references(Node, NodeRefs).
get_dist_references(NodeName) when is_atom(NodeName) ->
- ?line {{node_references, NodeRefs},
- {dist_references, DistRefs}} = ?ND_REFS,
- ?line check_nd_refc({node(), erlang:system_info(creation)},
- NodeRefs,
- DistRefs,
- fun (ErrMsg) ->
- ?line ?t:format("~s", [ErrMsg]),
- ?line ?t:fail(reference_count_check_failed)
- end),
- ?line find_references(NodeName, DistRefs).
+ {{node_references, NodeRefs},
+ {dist_references, DistRefs}} = ?ND_REFS,
+ check_nd_refc({node(), erlang:system_info(creation)},
+ NodeRefs,
+ DistRefs,
+ fun (ErrMsg) ->
+ io:format("~s", [ErrMsg]),
+ ct:fail(reference_count_check_failed)
+ end),
+ find_references(NodeName, DistRefs).
find_references(N, NRefList) ->
case lists:keysearch(N, 1, NRefList) of
- {value, {N, _, ReferrersList}} -> ReferrersList;
- _ -> false
- end.
+ {value, {N, _, ReferrersList}} -> ReferrersList;
+ _ -> false
+ end.
%% Currently unused
% refering_entity_type(RefererType, ReferingEntities) ->
@@ -1081,7 +1034,7 @@ find_references(N, NRefList) ->
% ReferingEntities).
refering_entity_id(ReferingEntityId, [{ReferingEntityId,_} = ReferingEntity
- | _ReferingEntities]) ->
+ | _ReferingEntities]) ->
ReferingEntity;
refering_entity_id(ReferingEntityId, [_ | ReferingEntities]) ->
refering_entity_id(ReferingEntityId, ReferingEntities);
@@ -1094,34 +1047,34 @@ reference_type_count(Type, {_, _ReferenceCountList} = ReferingEntity) ->
reference_type_count(Type, [ReferingEntity]);
reference_type_count(Type, ReferingEntities) when is_list(ReferingEntities) ->
lists:foldl(fun ({_, ReferenceCountList}, Acc1) ->
- lists:foldl(fun ({T, N}, Acc2) when T == Type ->
- N + Acc2;
- (_, Acc2) ->
- Acc2
- end,
- Acc1,
- ReferenceCountList)
- end,
- 0,
- ReferingEntities).
+ lists:foldl(fun ({T, N}, Acc2) when T == Type ->
+ N + Acc2;
+ (_, Acc2) ->
+ Acc2
+ end,
+ Acc1,
+ ReferenceCountList)
+ end,
+ 0,
+ ReferingEntities).
start_node(Name, Args) ->
- ?line Pa = filename:dirname(code:which(?MODULE)),
- ?line Res = test_server:start_node(Name,
- slave,
- [{args, "-pa "++Pa++" "++Args}]),
- ?line {ok, Node} = Res,
- ?line rpc:call(Node, erts_debug, set_internal_state,
- [available_internal_state, true]),
- ?line Res.
-
+ Pa = filename:dirname(code:which(?MODULE)),
+ Res = test_server:start_node(Name,
+ slave,
+ [{args, "-pa "++Pa++" "++Args}]),
+ {ok, Node} = Res,
+ rpc:call(Node, erts_debug, set_internal_state,
+ [available_internal_state, true]),
+ Res.
+
start_node(Name) ->
- ?line start_node(Name, "").
+ start_node(Name, "").
stop_node(Node) ->
- ?line nc_refc_check(Node),
- ?line true = test_server:stop_node(Node).
+ nc_refc_check(Node),
+ true = test_server:stop_node(Node).
hostname() ->
from($@, atom_to_list(node())).
@@ -1132,25 +1085,25 @@ from(_H, []) -> [].
wait_until(Pred) ->
case Pred() of
- true -> ok;
- false -> receive after 100 -> wait_until(Pred) end
+ true -> ok;
+ false -> receive after 100 -> wait_until(Pred) end
end.
get_nodefirstname_string() ->
atom_to_list(?MODULE)
- ++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
- ++ "-"
- ++ integer_to_list(erlang:unique_integer([positive])).
+ ++ "-"
+ ++ integer_to_list(erlang:system_time(second))
+ ++ "-"
+ ++ integer_to_list(erlang:unique_integer([positive])).
get_nodefirstname() ->
list_to_atom(get_nodefirstname_string()).
get_nodename() ->
list_to_atom(get_nodefirstname_string()
- ++ "@"
- ++ hostname()).
-
+ ++ "@"
+ ++ hostname()).
+
-define(VERSION_MAGIC, 131).
@@ -1160,6 +1113,9 @@ get_nodename() ->
-define(PORT_EXT, 102).
-define(PID_EXT, 103).
-define(NEW_REFERENCE_EXT, 114).
+-define(NEW_PID_EXT, $X).
+-define(NEW_PORT_EXT, $Y).
+-define(NEWER_REFERENCE_EXT, $Z).
uint32_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 32 ->
[(Uint bsr 24) band 16#ff,
@@ -1182,18 +1138,25 @@ uint8(Uint) ->
exit({badarg, uint8, [Uint]}).
+pid_tag(bad_creation) -> ?PID_EXT;
+pid_tag(Creation) when Creation =< 3 -> ?PID_EXT;
+pid_tag(_Creation) -> ?NEW_PID_EXT.
+
+enc_creation(bad_creation) -> uint8(4);
+enc_creation(Creation) when Creation =< 3 -> uint8(Creation);
+enc_creation(Creation) -> uint32_be(Creation).
mk_pid({NodeName, Creation}, Number, Serial) when is_atom(NodeName) ->
mk_pid({atom_to_list(NodeName), Creation}, Number, Serial);
mk_pid({NodeName, Creation}, Number, Serial) ->
case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
- ?PID_EXT,
- ?ATOM_EXT,
- uint16_be(length(NodeName)),
- NodeName,
- uint32_be(Number),
- uint32_be(Serial),
- uint8(Creation)])) of
+ pid_tag(Creation),
+ ?ATOM_EXT,
+ uint16_be(length(NodeName)),
+ NodeName,
+ uint32_be(Number),
+ uint32_be(Serial),
+ enc_creation(Creation)])) of
Pid when is_pid(Pid) ->
Pid;
{'EXIT', {badarg, _}} ->
@@ -1202,16 +1165,20 @@ mk_pid({NodeName, Creation}, Number, Serial) ->
exit({unexpected_binary_to_term_result, Other})
end.
+port_tag(bad_creation) -> ?PORT_EXT;
+port_tag(Creation) when Creation =< 3 -> ?PORT_EXT;
+port_tag(_Creation) -> ?NEW_PORT_EXT.
+
mk_port({NodeName, Creation}, Number) when is_atom(NodeName) ->
mk_port({atom_to_list(NodeName), Creation}, Number);
mk_port({NodeName, Creation}, Number) ->
case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
- ?PORT_EXT,
+ port_tag(Creation),
?ATOM_EXT,
uint16_be(length(NodeName)),
NodeName,
uint32_be(Number),
- uint8(Creation)])) of
+ enc_creation(Creation)])) of
Port when is_port(Port) ->
Port;
{'EXIT', {badarg, _}} ->
@@ -1220,37 +1187,39 @@ mk_port({NodeName, Creation}, Number) ->
exit({unexpected_binary_to_term_result, Other})
end.
+ref_tag(bad_creation) -> ?NEW_REFERENCE_EXT;
+ref_tag(Creation) when Creation =< 3 -> ?NEW_REFERENCE_EXT;
+ref_tag(_Creation) -> ?NEWER_REFERENCE_EXT.
+
mk_ref({NodeName, Creation}, Numbers) when is_atom(NodeName),
- is_integer(Creation),
is_list(Numbers) ->
mk_ref({atom_to_list(NodeName), Creation}, Numbers);
mk_ref({NodeName, Creation}, [Number]) when is_list(NodeName),
- is_integer(Creation),
+ Creation =< 3,
is_integer(Number) ->
case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
- ?REFERENCE_EXT,
- ?ATOM_EXT,
- uint16_be(length(NodeName)),
- NodeName,
- uint32_be(Number),
- uint8(Creation)])) of
- Ref when is_reference(Ref) ->
- Ref;
- {'EXIT', {badarg, _}} ->
- exit({badarg, mk_ref, [{NodeName, Creation}, [Number]]});
- Other ->
- exit({unexpected_binary_to_term_result, Other})
+ ?REFERENCE_EXT,
+ ?ATOM_EXT,
+ uint16_be(length(NodeName)),
+ NodeName,
+ uint32_be(Number),
+ uint8(Creation)])) of
+ Ref when is_reference(Ref) ->
+ Ref;
+ {'EXIT', {badarg, _}} ->
+ exit({badarg, mk_ref, [{NodeName, Creation}, [Number]]});
+ Other ->
+ exit({unexpected_binary_to_term_result, Other})
end;
mk_ref({NodeName, Creation}, Numbers) when is_list(NodeName),
- is_integer(Creation),
is_list(Numbers) ->
case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
- ?NEW_REFERENCE_EXT,
+ ref_tag(Creation),
uint16_be(length(Numbers)),
?ATOM_EXT,
uint16_be(length(NodeName)),
NodeName,
- uint8(Creation),
+ enc_creation(Creation),
lists:map(fun (N) ->
uint32_be(N)
end,
@@ -1265,10 +1234,10 @@ mk_ref({NodeName, Creation}, Numbers) when is_list(NodeName),
exec_loop() ->
receive
- {exec_fun, Fun} when is_function(Fun) ->
- Fun();
- {sync_exec_fun, From, Fun} when is_pid(From), is_function(Fun) ->
- From ! {sync_exec_fun_res, self(), Fun()}
+ {exec_fun, Fun} when is_function(Fun) ->
+ Fun();
+ {sync_exec_fun, From, Fun} when is_pid(From), is_function(Fun) ->
+ From ! {sync_exec_fun_res, self(), Fun()}
end,
exec_loop().
@@ -1284,6 +1253,6 @@ exec(Pid, Fun) when is_pid(Pid), is_function(Fun) ->
sync_exec(Pid, Fun) when is_pid(Pid), is_function(Fun) ->
Pid ! {sync_exec_fun, self(), Fun},
receive
- {sync_exec_fun_res, Pid, Res} ->
- Res
+ {sync_exec_fun_res, Pid, Res} ->
+ Res
end.
diff --git a/erts/emulator/test/nofrag_SUITE.erl b/erts/emulator/test/nofrag_SUITE.erl
index 3660a58c56..8b1519ae36 100644
--- a/erts/emulator/test/nofrag_SUITE.erl
+++ b/erts/emulator/test/nofrag_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,57 +20,33 @@
-module(nofrag_SUITE).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2,end_per_testcase/2,
- error_handler/1,error_handler_apply/1,
- error_handler_fixed_apply/1,error_handler_fun/1,
- debug_breakpoint/1]).
+-export([all/0, suite/0,
+ error_handler/1,error_handler_apply/1,
+ error_handler_fixed_apply/1,error_handler_fun/1,
+ debug_breakpoint/1]).
%% Exported functions for an error_handler module.
-export([undefined_function/3,undefined_lambda/3,breakpoint/3]).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 3}}].
all() ->
[error_handler, error_handler_apply,
error_handler_fixed_apply, error_handler_fun,
debug_breakpoint].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- Dog = ?t:timetrap(?t:minutes(3)),
- [{watchdog,Dog}|Config].
-
-end_per_testcase(_Func, Config) ->
- Dog = ?config(watchdog, Config),
- ?t:timetrap_cancel(Dog).
-
error_handler(Config) when is_list(Config) ->
- ?line process_flag(error_handler, ?MODULE),
+ process_flag(error_handler, ?MODULE),
%% The term_to_binary/1 - binary_to_term/1 roundtrip is a good way
%% to traverse the entire term.
- ?line Term = collect(1024),
- ?line Term = binary_to_term(term_to_binary(Term)),
- ?line 1024 = length(Term),
- ?line [[a,b,c,d,[e,f,g]]] = lists:usort(Term),
+ Term = collect(1024),
+ Term = binary_to_term(term_to_binary(Term)),
+ 1024 = length(Term),
+ [[a,b,c,d,[e,f,g]]] = lists:usort(Term),
ok.
collect(0) ->
@@ -105,25 +81,25 @@ collect_apply(N, Mod) ->
[C|Res].
error_handler_apply(Config) when is_list(Config) ->
- ?line process_flag(error_handler, ?MODULE),
+ process_flag(error_handler, ?MODULE),
%% The term_to_binary/1 - binary_to_term/1 roundtrip is a good way
%% to traverse the entire term.
- ?line Term = collect_apply(1024, fooblurfbar),
- ?line Term = binary_to_term(term_to_binary(Term)),
- ?line 1024 = length(Term),
- ?line [[{a,42},b,c,d,[e,f,g]]] = lists:usort(Term),
+ Term = collect_apply(1024, fooblurfbar),
+ Term = binary_to_term(term_to_binary(Term)),
+ 1024 = length(Term),
+ [[{a,42},b,c,d,[e,f,g]]] = lists:usort(Term),
ok.
error_handler_fixed_apply(Config) when is_list(Config) ->
- ?line process_flag(error_handler, ?MODULE),
+ process_flag(error_handler, ?MODULE),
%% The term_to_binary/1 - binary_to_term/1 roundtrip is a good way
%% to traverse the entire term.
- ?line Term = collect_fixed_apply(1024, fooblurfbar),
- ?line Term = binary_to_term(term_to_binary(Term)),
- ?line 1024 = length(Term),
- ?line [[{a,2},b,c,d,[e,f,g]]] = lists:usort(Term),
+ Term = collect_fixed_apply(1024, fooblurfbar),
+ Term = binary_to_term(term_to_binary(Term)),
+ 1024 = length(Term),
+ [[{a,2},b,c,d,[e,f,g]]] = lists:usort(Term),
ok.
collect_fixed_apply(0, _) ->
@@ -145,19 +121,19 @@ undefined_function(_Mod, _Name, Args) ->
Args.
error_handler_fun(Config) when is_list(Config) ->
- ?line process_flag(error_handler, ?MODULE),
+ process_flag(error_handler, ?MODULE),
%% fun(A, B, C) -> {A,B,C,X} end in module foobarblurf.
B = <<131,112,0,0,0,84,3,109,96,69,208,5,175,207,75,36,93,112,218,232,222,22,251,0,
- 0,0,0,0,0,0,1,100,0,11,102,111,111,98,97,114,98,108,117,114,102,97,0,98,5,
- 244,197,144,103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,115,116,
- 0,0,0,46,0,0,0,0,0,104,3,97,1,97,2,97,3>>,
- ?line Fun = binary_to_term(B),
- ?line Term = collect_fun(1024, Fun),
- ?line Term = binary_to_term(term_to_binary(Term)),
- ?line 1024 = length(Term),
- ?line [[{foo,bar},{99,1.0},[e,f,g]]] = lists:usort(Term),
- ?line {env,[{1,2,3}]} = erlang:fun_info(Fun, env),
+ 0,0,0,0,0,0,1,100,0,11,102,111,111,98,97,114,98,108,117,114,102,97,0,98,5,
+ 244,197,144,103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,115,116,
+ 0,0,0,46,0,0,0,0,0,104,3,97,1,97,2,97,3>>,
+ Fun = binary_to_term(B),
+ Term = collect_fun(1024, Fun),
+ Term = binary_to_term(term_to_binary(Term)),
+ 1024 = length(Term),
+ [[{foo,bar},{99,1.0},[e,f,g]]] = lists:usort(Term),
+ {env,[{1,2,3}]} = erlang:fun_info(Fun, env),
ok.
collect_fun(0, _) ->
@@ -179,13 +155,13 @@ undefined_lambda(foobarblurf, Fun, Args) when is_function(Fun) ->
Args.
debug_breakpoint(Config) when is_list(Config) ->
- ?line process_flag(error_handler, ?MODULE),
- ?line erts_debug:breakpoint({?MODULE,foobar,5}, true),
- ?line Term = break_collect(1024),
- ?line Term = binary_to_term(term_to_binary(Term)),
- ?line 1024 = length(Term),
- ?line [[a,b,c,{d,e},[f,g,h]]] = lists:usort(Term),
- ?line erts_debug:breakpoint({?MODULE,foobar,5}, false),
+ process_flag(error_handler, ?MODULE),
+ erts_debug:breakpoint({?MODULE,foobar,5}, true),
+ Term = break_collect(1024),
+ Term = binary_to_term(term_to_binary(Term)),
+ 1024 = length(Term),
+ [[a,b,c,{d,e},[f,g,h]]] = lists:usort(Term),
+ erts_debug:breakpoint({?MODULE,foobar,5}, false),
ok.
break_collect(0) ->
@@ -202,5 +178,3 @@ foobar(_, _, _, _, _) ->
exit(dont_execute_me).
id(I) -> I.
-
-
diff --git a/erts/emulator/test/num_bif_SUITE.erl b/erts/emulator/test/num_bif_SUITE.erl
index 90b6a36262..f31e73c85b 100644
--- a/erts/emulator/test/num_bif_SUITE.erl
+++ b/erts/emulator/test/num_bif_SUITE.erl
@@ -1,8 +1,8 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2014. 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.
%% You may obtain a copy of the License at
@@ -14,13 +14,13 @@
%% 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(num_bif_SUITE).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
%% Tests the BIFs:
%% abs/1
@@ -36,22 +36,22 @@
%% integer_to_binary/2
%% binary_to_integer/1
--export([all/0, suite/0, groups/0, init_per_suite/1, end_per_suite/1,
+-export([all/0, suite/0, groups/0, init_per_suite/1, end_per_suite/1,
init_per_group/2, end_per_group/2, t_abs/1, t_float/1,
t_float_to_string/1, t_integer_to_string/1,
- t_string_to_integer/1,
+ t_string_to_integer/1, t_list_to_integer_edge_cases/1,
t_string_to_float_safe/1, t_string_to_float_risky/1,
t_round/1, t_trunc/1
]).
suite() -> [{ct_hooks,[ts_install_cth]}].
-all() ->
+all() ->
[t_abs, t_float, t_float_to_string, t_integer_to_string,
{group, t_string_to_float}, t_string_to_integer, t_round,
- t_trunc].
+ t_trunc, t_list_to_integer_edge_cases].
-groups() ->
+groups() ->
[{t_string_to_float, [],
[t_string_to_float_safe, t_string_to_float_risky]}].
@@ -73,7 +73,7 @@ t_abs(Config) when is_list(Config) ->
5.5 = abs(id(5.5)),
0.0 = abs(id(0.0)),
100.0 = abs(id(-100.0)),
-
+
%% Integers.
5 = abs(id(5)),
0 = abs(id(0)),
@@ -93,7 +93,7 @@ t_abs(Config) when is_list(Config) ->
BigNum = abs(BigNum),
BigNum = abs(-BigNum),
ok.
-
+
t_float(Config) when is_list(Config) ->
0.0 = float(id(0)),
2.5 = float(id(2.5)),
@@ -109,7 +109,7 @@ t_float(Config) when is_list(Config) ->
%% Extremly big bignums.
Big = id(list_to_integer(id(lists:duplicate(2000, $1)))),
{'EXIT', {badarg, _}} = (catch float(Big)),
-
+
ok.
@@ -183,7 +183,7 @@ t_float_to_string(Config) when is_list(Config) ->
test_fts("1.2300000000e+20",1.23e20, [{scientific, 10}, compact]),
test_fts("1.23000000000000000000e+20",1.23e20, []),
ok.
-
+
test_fts(Expect, Float) ->
Expect = float_to_list(Float),
BinExpect = list_to_binary(Expect),
@@ -255,7 +255,7 @@ t_round(Config) when is_list(Config) ->
256 = round(id(255.6)),
-1033 = round(id(-1033.3)),
-1034 = round(id(-1033.6)),
-
+
% OTP-3722:
X = id((1 bsl 27) - 1),
MX = -X,
@@ -293,6 +293,9 @@ t_round(Config) when is_list(Config) ->
4294967297 = round(id(4294967296.9)),
-4294967296 = -round(id(4294967296.1)),
-4294967297 = -round(id(4294967296.9)),
+
+ 6209607916799025 = round(id(6209607916799025.0)),
+ -6209607916799025 = round(id(-6209607916799025.0)),
ok.
t_trunc(Config) when is_list(Config) ->
@@ -345,9 +348,9 @@ t_integer_to_string(Config) when is_list(Config) ->
%% Invalid types
lists:foreach(fun(Value) ->
- {'EXIT', {badarg, _}} =
+ {'EXIT', {badarg, _}} =
(catch erlang:integer_to_binary(Value)),
- {'EXIT', {badarg, _}} =
+ {'EXIT', {badarg, _}} =
(catch erlang:integer_to_list(Value))
end,[atom,1.2,0.0,[$1,[$2]]]),
@@ -416,27 +419,27 @@ t_string_to_integer(Config) when is_list(Config) ->
%% Invalid types
lists:foreach(fun(Value) ->
- {'EXIT', {badarg, _}} =
+ {'EXIT', {badarg, _}} =
(catch binary_to_integer(Value)),
- {'EXIT', {badarg, _}} =
+ {'EXIT', {badarg, _}} =
(catch erlang:list_to_integer(Value))
end,[atom,1.2,0.0,[$1,[$2]]]),
-
+
% Default base error cases
lists:foreach(fun(Value) ->
- {'EXIT', {badarg, _}} =
+ {'EXIT', {badarg, _}} =
(catch erlang:binary_to_integer(
list_to_binary(Value))),
- {'EXIT', {badarg, _}} =
+ {'EXIT', {badarg, _}} =
(catch erlang:list_to_integer(Value))
end,["1.0"," 1"," -1","","+"]),
-
+
% Custom base error cases
lists:foreach(fun({Value,Base}) ->
- {'EXIT', {badarg, _}} =
+ {'EXIT', {badarg, _}} =
(catch binary_to_integer(
list_to_binary(Value),Base)),
- {'EXIT', {badarg, _}} =
+ {'EXIT', {badarg, _}} =
(catch erlang:list_to_integer(Value,Base))
end,[{" 1",1},{" 1",37},{"2",2},{"C",11},
{"1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111z",16},
@@ -449,10 +452,61 @@ t_string_to_integer(Config) when is_list(Config) ->
ok.
+%% Tests edge cases for list_to_integer; compares with known good values
+
+t_list_to_integer_edge_cases(Config) when is_list(Config) ->
+ %% Take integer literals and compare to their representation in ExtTerm
+ T = [
+ {16, "0", <<131,97,0>>},
+ {16, "-0", <<131,97,0>>},
+
+ {16, "f", <<131,97,15>>},
+ {16, "-f", <<131,98,255,255,255,241>>},
+
+ {16, "0000000000000000000000000000000000000000000000000f",
+ <<131,97,15>>},
+ {16, "-0000000000000000000000000000000000000000000000000f",
+ <<131,98,255,255,255,241>>},
+
+ {16, "ffffffff", <<131,110,4,0,255,255,255,255>>},
+ {16, "-ffffffff", <<131,110,4,1,255,255,255,255>>},
+
+ {16, "7fffffff", <<131,110,4,0,255,255,255,127>>},
+ {16, "-7fffffff", <<131,98,128,0,0,1>>},
+
+ {16, "ffffffffffffffff",
+ <<131,110,8,0,255,255,255,255,255,255,255,255>>},
+ {16, "-ffffffffffffffff",
+ <<131,110,8,1,255,255,255,255,255,255,255,255>>},
+
+ {16, "7fffffffffffffff",
+ <<131,110,8,0,255,255,255,255,255,255,255,127>>},
+ {16, "-7fffffffffffffff",
+ <<131,110,8,1,255,255,255,255,255,255,255,127>>},
+
+ %% Alleged 32-bit corner case (should not happen on 64-bit). At 32-4
+ %% bits we may corrupt sign bit and fall out of SMALL_INT range.
+ {2, "1000000000000000000000000000", <<131,98,8,0,0,0>>},
+ {2, "-1000000000000000000000000000", <<131,98,248,0,0,0>>},
+
+ %% 64-bit corner case (should not happen on 32-bit) at 64-4 bits we
+ %% corrupt sign bit and fall out of SMALL_INT range (bam! all dead)
+ {2, "100000000000000000000000000000000000000000000000000000000000",
+ <<131,110,8,0,0,0,0,0,0,0,0,8>>},
+ {2, "-100000000000000000000000000000000000000000000000000000000000",
+ <<131,110,8,1,0,0,0,0,0,0,0,8>>}
+ ],
+ [begin
+ io:format("~s base ~p vs ~p~n", [Str, Base, Bin]),
+ FromStr = list_to_integer(Str, Base),
+ FromStr = binary_to_term(Bin)
+ end || {Base, Str, Bin} <- T],
+ ok.
+
test_sti(Num) ->
[begin
io:format("Testing ~p:~p",[Num,Base]),
- test_sti(Num,Base)
+ test_sti(Num,Base)
end|| Base <- lists:seq(2,36)].
test_sti(Num,Base) ->
diff --git a/erts/emulator/test/old_mod.erl b/erts/emulator/test/old_mod.erl
index 1586a024d8..866aba79bb 100644
--- a/erts/emulator/test/old_mod.erl
+++ b/erts/emulator/test/old_mod.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -23,26 +23,26 @@
-export([sort_on_old_node/1, sorter/3]).
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
sorter(Receiver, Ref, List) ->
Receiver ! {Ref, lists:sort(List)}.
sort_on_old_node(List) when is_list(List) ->
OldVersion = "r10",
- ?line Pa = filename:dirname(code:which(?MODULE)),
- ?line {X, Y, Z} = now(),
- ?line NodeName = list_to_atom(OldVersion
- ++ "_"
- ++ integer_to_list(X)
- ++ integer_to_list(Y)
- ++ integer_to_list(Z)),
- ?line {ok, Node} = ?t:start_node(NodeName,
- peer,
- [{args, " -pa " ++ Pa},
- {erl, [{release, OldVersion++"b_patched"}]}]),
- ?line Ref = make_ref(),
- ?line spawn_link(Node, ?MODULE, sorter, [self(), Ref, List]),
- ?line SortedPids = receive {Ref, SP} -> SP end,
- ?line true = ?t:stop_node(Node),
- ?line SortedPids.
+ Pa = filename:dirname(code:which(?MODULE)),
+ {X, Y, Z} = now(),
+ NodeName = list_to_atom(OldVersion
+ ++ "_"
+ ++ integer_to_list(X)
+ ++ integer_to_list(Y)
+ ++ integer_to_list(Z)),
+ {ok, Node} = test_server:start_node(NodeName,
+ peer,
+ [{args, " -pa " ++ Pa},
+ {erl, [{release, OldVersion++"b_patched"}]}]),
+ Ref = make_ref(),
+ spawn_link(Node, ?MODULE, sorter, [self(), Ref, List]),
+ SortedPids = receive {Ref, SP} -> SP end,
+ true = test_server:stop_node(Node),
+ SortedPids.
diff --git a/erts/emulator/test/old_scheduler_SUITE.erl b/erts/emulator/test/old_scheduler_SUITE.erl
index 97c99fe07b..ffe7d40139 100644
--- a/erts/emulator/test/old_scheduler_SUITE.erl
+++ b/erts/emulator/test/old_scheduler_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,43 +20,27 @@
-module(old_scheduler_SUITE).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2, end_per_testcase/2]).
+-export([all/0, suite/0,
+ init_per_testcase/2, end_per_testcase/2]).
-export([equal/1, many_low/1, few_low/1, max/1, high/1]).
--define(default_timeout, ?t:minutes(11)).
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 11}}].
all() ->
case catch erlang:system_info(modified_timing_level) of
- Level when is_integer(Level) ->
- {skipped,
- "Modified timing (level " ++
- integer_to_list(Level) ++
- ") is enabled. Testcases gets messed "
- "up by modfied timing."};
- _ -> [equal, many_low, few_low, max, high]
+ Level when is_integer(Level) ->
+ {skipped,
+ "Modified timing (level " ++
+ integer_to_list(Level) ++
+ ") is enabled. Testcases gets messed "
+ "up by modfied timing."};
+ _ -> [equal, many_low, few_low, max, high]
end.
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
%%-----------------------------------------------------------------------------------
%% TEST SUITE DESCRIPTION
@@ -78,35 +62,30 @@ end_per_group(_GroupName, Config) ->
%%-----------------------------------------------------------------------------------
init_per_testcase(_Case, Config) ->
- ?line Dog = test_server:timetrap(?default_timeout),
%% main test process needs max prio
- ?line Prio = process_flag(priority, max),
- ?line MS = erlang:system_flag(multi_scheduling, block),
- [{prio,Prio},{watchdog,Dog},{multi_scheduling, MS}|Config].
+ Prio = process_flag(priority, max),
+ MS = erlang:system_flag(multi_scheduling, block),
+ [{prio,Prio},{multi_scheduling, MS}|Config].
end_per_testcase(_Case, Config) ->
erlang:system_flag(multi_scheduling, unblock),
- Dog=?config(watchdog, Config),
- Prio=?config(prio, Config),
+ Prio=proplists:get_value(prio, Config),
process_flag(priority, Prio),
- test_server:timetrap_cancel(Dog),
ok.
ok(Config) when is_list(Config) ->
- case ?config(multi_scheduling, Config) of
- blocked ->
- {comment,
- "Multi-scheduling blocked during test. This testcase was not "
- "written to work with multiple schedulers."};
- _ -> ok
+ case proplists:get_value(multi_scheduling, Config) of
+ blocked ->
+ {comment,
+ "Multi-scheduling blocked during test. This testcase was not "
+ "written to work with multiple schedulers."};
+ _ -> ok
end.
%% Run equal number of low and normal prio processes.
-equal(suite) -> [];
-equal(doc) -> [];
equal(Config) when is_list(Config) ->
- ?line Self = self(),
+ Self = self(),
%% specify number of test processes to run
Normal = {normal,500},
@@ -116,102 +95,96 @@ equal(Config) when is_list(Config) ->
Time = 30,
%% start controllers
- ?line Receiver =
- spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Normal, Low) end),
- ?line Starter =
- spawn(fun() -> starter(Normal, Low, Receiver) end),
+ Receiver =
+ spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Normal, Low) end),
+ Starter =
+ spawn(fun() -> starter(Normal, Low, Receiver) end),
%% receive test data from Receiver
- ?line {NRs,NAvg,LRs,LAvg,Ratio} =
- receive
- {Receiver,Res} -> Res
- end,
+ {NRs,NAvg,LRs,LAvg,Ratio} =
+ receive
+ {Receiver,Res} -> Res
+ end,
%% stop controllers and test processes
- ?line exit(Starter, kill),
- ?line exit(Receiver, kill),
+ exit(Starter, kill),
+ exit(Receiver, kill),
io:format("Reports: ~w normal (~w/proc), ~w low (~w/proc). Ratio: ~w~n",
- [NRs,NAvg,LRs,LAvg,Ratio]),
+ [NRs,NAvg,LRs,LAvg,Ratio]),
%% runtime ratio between normal and low should be ~8
if Ratio < 7.5 ; Ratio > 8.5 ->
- ?t:fail({bad_ratio,Ratio});
+ ct:fail({bad_ratio,Ratio});
true ->
- ok(Config)
+ ok(Config)
end.
%% Run many low and few normal prio processes.
-many_low(suite) -> [];
-many_low(doc) -> [];
many_low(Config) when is_list(Config) ->
- ?line Self = self(),
+ Self = self(),
Normal = {normal,1},
Low = {low,1000},
%% specify time of test (in seconds)
Time = 30,
- ?line Receiver =
- spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Normal, Low) end),
- ?line Starter =
- spawn(fun() -> starter(Normal, Low, Receiver) end),
- ?line {NRs,NAvg,LRs,LAvg,Ratio} =
- receive
- {Receiver,Res} -> Res
- end,
- ?line exit(Starter, kill),
- ?line exit(Receiver, kill),
+ Receiver =
+ spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Normal, Low) end),
+ Starter =
+ spawn(fun() -> starter(Normal, Low, Receiver) end),
+ {NRs,NAvg,LRs,LAvg,Ratio} =
+ receive
+ {Receiver,Res} -> Res
+ end,
+ exit(Starter, kill),
+ exit(Receiver, kill),
io:format("Reports: ~w normal (~w/proc), ~w low (~w/proc). Ratio: ~w~n",
- [NRs,NAvg,LRs,LAvg,Ratio]),
+ [NRs,NAvg,LRs,LAvg,Ratio]),
if Ratio < 7.5 ; Ratio > 8.5 ->
- ?t:fail({bad_ratio,Ratio});
+ ct:fail({bad_ratio,Ratio});
true ->
- ok(Config)
+ ok(Config)
end.
%% Run few low and many normal prio processes.
-few_low(suite) -> [];
-few_low(doc) -> [];
few_low(Config) when is_list(Config) ->
- ?line Self = self(),
+ Self = self(),
Normal = {normal,1000},
Low = {low,1},
%% specify time of test (in seconds)
Time = 30,
- ?line Receiver =
- spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Normal, Low) end),
- ?line Starter =
- spawn(fun() -> starter(Normal, Low, Receiver) end),
- ?line {NRs,NAvg,LRs,LAvg,Ratio} =
- receive
- {Receiver,Res} -> Res
- end,
- ?line exit(Starter, kill),
- ?line exit(Receiver, kill),
+ Receiver =
+ spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Normal, Low) end),
+ Starter =
+ spawn(fun() -> starter(Normal, Low, Receiver) end),
+ {NRs,NAvg,LRs,LAvg,Ratio} =
+ receive
+ {Receiver,Res} -> Res
+ end,
+ exit(Starter, kill),
+ exit(Receiver, kill),
io:format("Reports: ~w normal (~w/proc), ~w low (~w/proc). Ratio: ~w~n",
- [NRs,NAvg,LRs,LAvg,Ratio]),
+ [NRs,NAvg,LRs,LAvg,Ratio]),
if Ratio < 7.0 ; Ratio > 8.5 ->
- ?t:fail({bad_ratio,Ratio});
+ ct:fail({bad_ratio,Ratio});
true ->
- ok(Config)
+ ok(Config)
end.
%% Run max prio processes and verify they get at least as much
%% runtime as high, normal and low.
-max(suite) -> [];
-max(doc) -> [];
max(Config) when is_list(Config) ->
max = process_flag(priority, max), % should already be max (init_per_tc)
- ?line Self = self(),
+ Self = self(),
Max = {max,2},
High = {high,2},
Normal = {normal,100},
@@ -220,69 +193,67 @@ max(Config) when is_list(Config) ->
%% specify time of test (in seconds)
Time = 30,
- ?line Receiver1 =
- spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Max, High) end),
- ?line Starter1 =
- spawn(fun() -> starter(Max, High, Receiver1) end),
- ?line {M1Rs,M1Avg,HRs,HAvg,Ratio1} =
- receive
- {Receiver1,Res1} -> Res1
- end,
- ?line exit(Starter1, kill),
- ?line exit(Receiver1, kill),
+ Receiver1 =
+ spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Max, High) end),
+ Starter1 =
+ spawn(fun() -> starter(Max, High, Receiver1) end),
+ {M1Rs,M1Avg,HRs,HAvg,Ratio1} =
+ receive
+ {Receiver1,Res1} -> Res1
+ end,
+ exit(Starter1, kill),
+ exit(Receiver1, kill),
io:format("Reports: ~w max (~w/proc), ~w high (~w/proc). Ratio: ~w~n",
- [M1Rs,M1Avg,HRs,HAvg,Ratio1]),
+ [M1Rs,M1Avg,HRs,HAvg,Ratio1]),
if Ratio1 < 1.0 ->
- ?t:fail({bad_ratio,Ratio1});
+ ct:fail({bad_ratio,Ratio1});
true ->
- ok(Config)
+ ok(Config)
end,
- ?line Receiver2 =
- spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Max, Normal) end),
- ?line Starter2 =
- spawn(fun() -> starter(Max, Normal, Receiver2) end),
- ?line {M2Rs,M2Avg,NRs,NAvg,Ratio2} =
- receive
- {Receiver2,Res2} -> Res2
- end,
- ?line exit(Starter2, kill),
- ?line exit(Receiver2, kill),
+ Receiver2 =
+ spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Max, Normal) end),
+ Starter2 =
+ spawn(fun() -> starter(Max, Normal, Receiver2) end),
+ {M2Rs,M2Avg,NRs,NAvg,Ratio2} =
+ receive
+ {Receiver2,Res2} -> Res2
+ end,
+ exit(Starter2, kill),
+ exit(Receiver2, kill),
io:format("Reports: ~w max (~w/proc), ~w normal (~w/proc). Ratio: ~w~n",
- [M2Rs,M2Avg,NRs,NAvg,Ratio2]),
+ [M2Rs,M2Avg,NRs,NAvg,Ratio2]),
if Ratio2 < 1.0 ->
- ?t:fail({bad_ratio,Ratio2});
+ ct:fail({bad_ratio,Ratio2});
true ->
- ok
+ ok
end,
- ?line Receiver3 =
- spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Max, Low) end),
- ?line Starter3 =
- spawn(fun() -> starter(Max, Low, Receiver3) end),
- ?line {M3Rs,M3Avg,LRs,LAvg,Ratio3} =
- receive
- {Receiver3,Res3} -> Res3
- end,
- ?line exit(Starter3, kill),
- ?line exit(Receiver3, kill),
+ Receiver3 =
+ spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Max, Low) end),
+ Starter3 =
+ spawn(fun() -> starter(Max, Low, Receiver3) end),
+ {M3Rs,M3Avg,LRs,LAvg,Ratio3} =
+ receive
+ {Receiver3,Res3} -> Res3
+ end,
+ exit(Starter3, kill),
+ exit(Receiver3, kill),
io:format("Reports: ~w max (~w/proc), ~w low (~w/proc). Ratio: ~w~n",
- [M3Rs,M3Avg,LRs,LAvg,Ratio3]),
+ [M3Rs,M3Avg,LRs,LAvg,Ratio3]),
if Ratio3 < 1.0 ->
- ?t:fail({bad_ratio,Ratio3});
+ ct:fail({bad_ratio,Ratio3});
true ->
- ok(Config)
+ ok(Config)
end.
%% Run high prio processes and verify they get at least as much
%% runtime as normal and low.
-high(suite) -> [];
-high(doc) -> [];
high(Config) when is_list(Config) ->
max = process_flag(priority, max), % should already be max (init_per_tc)
- ?line Self = self(),
+ Self = self(),
High = {high,2},
Normal = {normal,100},
Low = {low,100},
@@ -290,40 +261,40 @@ high(Config) when is_list(Config) ->
%% specify time of test (in seconds)
Time = 30,
- ?line Receiver1 =
- spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, High, Normal) end),
- ?line Starter1 =
- spawn(fun() -> starter(High, Normal, Receiver1) end),
- ?line {H1Rs,H1Avg,NRs,NAvg,Ratio1} =
- receive
- {Receiver1,Res1} -> Res1
- end,
- ?line exit(Starter1, kill),
- ?line exit(Receiver1, kill),
+ Receiver1 =
+ spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, High, Normal) end),
+ Starter1 =
+ spawn(fun() -> starter(High, Normal, Receiver1) end),
+ {H1Rs,H1Avg,NRs,NAvg,Ratio1} =
+ receive
+ {Receiver1,Res1} -> Res1
+ end,
+ exit(Starter1, kill),
+ exit(Receiver1, kill),
io:format("Reports: ~w high (~w/proc), ~w normal (~w/proc). Ratio: ~w~n",
- [H1Rs,H1Avg,NRs,NAvg,Ratio1]),
+ [H1Rs,H1Avg,NRs,NAvg,Ratio1]),
if Ratio1 < 1.0 ->
- ?t:fail({bad_ratio,Ratio1});
+ ct:fail({bad_ratio,Ratio1});
true ->
- ok
+ ok
end,
- ?line Receiver2 =
- spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, High, Low) end),
- ?line Starter2 =
- spawn(fun() -> starter(High, Low, Receiver2) end),
- ?line {H2Rs,H2Avg,LRs,LAvg,Ratio2} =
- receive
- {Receiver2,Res2} -> Res2
- end,
- ?line exit(Starter2, kill),
- ?line exit(Receiver2, kill),
+ Receiver2 =
+ spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, High, Low) end),
+ Starter2 =
+ spawn(fun() -> starter(High, Low, Receiver2) end),
+ {H2Rs,H2Avg,LRs,LAvg,Ratio2} =
+ receive
+ {Receiver2,Res2} -> Res2
+ end,
+ exit(Starter2, kill),
+ exit(Receiver2, kill),
io:format("Reports: ~w high (~w/proc), ~w low (~w/proc). Ratio: ~w~n",
- [H2Rs,H2Avg,LRs,LAvg,Ratio2]),
+ [H2Rs,H2Avg,LRs,LAvg,Ratio2]),
if Ratio2 < 1.0 ->
- ?t:fail({bad_ratio,Ratio2});
+ ct:fail({bad_ratio,Ratio2});
true ->
- ok(Config)
+ ok(Config)
end.
@@ -338,38 +309,38 @@ receiver(T0, TimeSec, Main, {P1,P1N}, {P2,P2N}) ->
%% uncomment lines below to get life sign (debug)
receiver(T0, Time, Main, P1,P1N,P1Rs, P2,P2N,P2Rs, 0) ->
-% T = erlang:convert_time_unit(erlang:monotonic_time() - T0, native, milli_seconds),
-% erlang:display({round(T/1000),P1Rs,P2Rs}),
+ % T = erlang:convert_time_unit(erlang:monotonic_time() - T0, native, millisecond),
+ % erlang:display({round(T/1000),P1Rs,P2Rs}),
receiver(T0, Time, Main, P1,P1N,P1Rs, P2,P2N,P2Rs, 100000);
receiver(T0, Time, Main, P1,P1N,P1Rs, P2,P2N,P2Rs, C) ->
Remain = Time - erlang:convert_time_unit(erlang:monotonic_time() - T0,
- native, milli_seconds), % test time remaining
+ native, millisecond), % test time remaining
Remain1 = if Remain < 0 ->
- 0;
- true ->
- Remain
- end,
+ 0;
+ true ->
+ Remain
+ end,
{P1Rs1,P2Rs1} =
- receive
- {_Pid,P1} -> % report from a P1 process
- {P1Rs+1,P2Rs};
- {_Pid,P2} -> % report from a P2 process
- {P1Rs,P2Rs+1}
- after Remain1 ->
- {P1Rs,P2Rs}
- end,
+ receive
+ {_Pid,P1} -> % report from a P1 process
+ {P1Rs+1,P2Rs};
+ {_Pid,P2} -> % report from a P2 process
+ {P1Rs,P2Rs+1}
+ after Remain1 ->
+ {P1Rs,P2Rs}
+ end,
if Remain > 0 -> % keep going
- receiver(T0, Time, Main, P1,P1N,P1Rs1, P2,P2N,P2Rs1, C-1);
+ receiver(T0, Time, Main, P1,P1N,P1Rs1, P2,P2N,P2Rs1, C-1);
true -> % finish
- %% calculate results and send to main test process
- P1Avg = P1Rs1/P1N,
- P2Avg = P2Rs1/P2N,
- Ratio = if P2Avg < 1.0 -> P1Avg;
- true -> P1Avg/P2Avg
- end,
- Main ! {self(),{P1Rs1,round(P1Avg),P2Rs1,round(P2Avg),Ratio}},
- flush_loop()
+ %% calculate results and send to main test process
+ P1Avg = P1Rs1/P1N,
+ P2Avg = P2Rs1/P2N,
+ Ratio = if P2Avg < 1.0 -> P1Avg;
+ true -> P1Avg/P2Avg
+ end,
+ Main ! {self(),{P1Rs1,round(P1Avg),P2Rs1,round(P2Avg),Ratio}},
+ flush_loop()
end.
starter({P1,P1N}, {P2,P2N}, Receiver) ->
@@ -395,8 +366,8 @@ p_loop(100, Prio, Receiver) ->
receive after 0 -> ok end,
%% if Receiver gone, we're done
case is_process_alive(Receiver) of
- false -> exit(bye);
- true -> ok
+ false -> exit(bye);
+ true -> ok
end,
%% send report
Receiver ! {self(),Prio},
@@ -404,10 +375,10 @@ p_loop(100, Prio, Receiver) ->
p_loop(N, Prio, Receiver) ->
p_loop(N+1, Prio, Receiver).
-
+
flush_loop() ->
receive _ ->
- ok
+ ok
end,
flush_loop().
diff --git a/erts/emulator/test/op_SUITE.erl b/erts/emulator/test/op_SUITE.erl
index 6eda78a57b..08655d32a5 100644
--- a/erts/emulator/test/op_SUITE.erl
+++ b/erts/emulator/test/op_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2015. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,68 +20,51 @@
-module(op_SUITE).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2,end_per_testcase/2,
- bsl_bsr/1,logical/1,t_not/1,relop_simple/1,relop/1,complex_relop/1]).
+-export([all/0, suite/0,
+ bsl_bsr/1,logical/1,t_not/1,relop_simple/1,relop/1,complex_relop/1]).
-export([]).
-import(lists, [foldl/3,flatmap/2]).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 5}}].
all() ->
[bsl_bsr, logical, t_not, relop_simple, relop,
complex_relop].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
- Dog=?t:timetrap(?t:minutes(3)),
- [{watchdog, Dog}|Config].
-
-end_per_testcase(_Case, Config) ->
- Dog=?config(watchdog, Config),
- ?t:timetrap_cancel(Dog).
-
%% Test the bsl and bsr operators.
bsl_bsr(Config) when is_list(Config) ->
Vs = [unvalue(V) || V <- [-16#8000009-2,-1,0,1,2,73,16#8000000,bad,[]]],
- Cases = [{Op,X,Y} || Op <- ['bsr','bsl'], X <- Vs, Y <- Vs],
- ?line run_test_module(Cases, false),
- {comment,integer_to_list(length(Cases)) ++ " cases"}.
+ %% Try to use less memory by splitting the cases
+
+ Cases1 = [{Op,X,Y} || Op <- ['bsl'], X <- Vs, Y <- Vs],
+ N1 = length(Cases1),
+ run_test_module(Cases1, false),
-logical(doc) -> "Test the logical operators and internal BIFs.";
+ Cases2 = [{Op,X,Y} || Op <- ['bsr'], X <- Vs, Y <- Vs],
+ N2 = length(Cases2),
+ run_test_module(Cases2, false),
+ {comment,integer_to_list(N1 + N2) ++ " cases"}.
+
+%% Test the logical operators and internal BIFs.
logical(Config) when is_list(Config) ->
Vs0 = [true,false,bad],
Vs = [unvalue(V) || V <- Vs0],
Cases = [{Op,X,Y} || Op <- ['and','or','xor'], X <- Vs, Y <- Vs],
- ?line run_test_module(Cases, false),
+ run_test_module(Cases, false),
{comment,integer_to_list(length(Cases)) ++ " cases"}.
-t_not(doc) -> "Test the not operator and internal BIFs.";
+%% Test the not operator and internal BIFs.
t_not(Config) when is_list(Config) ->
- ?line Cases = [{'not',unvalue(V)} || V <- [true,false,42,bad]],
- ?line run_test_module(Cases, false),
+ Cases = [{'not',unvalue(V)} || V <- [true,false,42,bad]],
+ run_test_module(Cases, false),
{comment,integer_to_list(length(Cases)) ++ " cases"}.
-relop_simple(doc) -> "Test that simlpe relations between relation operators hold.";
+%% Test that simlpe relations between relation operators hold.
relop_simple(Config) when is_list(Config) ->
Big1 = 19738924729729787487784874,
Big2 = 38374938373887374983978484,
@@ -90,51 +73,52 @@ relop_simple(Config) when is_list(Config) ->
T1 = erlang:make_tuple(3,87),
T2 = erlang:make_tuple(3,87),
Terms = [-F2,Big2,-F1,-Big1,-33,-33.0,0,0.0,42,42.0,Big1,F1,Big2,F2,a,b,
- {T1,a},{T2,b},[T1,Big1],[T2,Big2]],
-
- ?line Combos = [{V1,V2} || V1 <- Terms, V2 <- Terms],
-
+ {T1,a},{T2,b},[T1,Big1],[T2,Big2]],
+
+ Combos = [{V1,V2} || V1 <- Terms, V2 <- Terms],
+
lists:foreach(fun({A,B}) -> relop_simple_do(A,B) end,
- Combos),
-
- repeat(fun() -> Size = random:uniform(100),
- Rnd1 = make_rand_term(Size),
- {Rnd2,0} = clone_and_mutate(Rnd1, random:uniform(Size)),
- relop_simple_do(Rnd1,Rnd2)
- end,
- 1000),
+ Combos),
+
+ repeat(fun() ->
+ Size = rand:uniform(100),
+ Rnd1 = make_rand_term(Size),
+ {Rnd2,0} = clone_and_mutate(Rnd1, rand:uniform(Size)),
+ relop_simple_do(Rnd1,Rnd2)
+ end,
+ 1000),
ok.
relop_simple_do(V1,V2) ->
%%io:format("compare ~p\n and ~p\n",[V1,V2]),
L = V1 < V2,
- ?line L = not (V1 >= V2),
- ?line L = V2 > V1,
- ?line L = not (V2 =< V1),
+ L = not (V1 >= V2),
+ L = V2 > V1,
+ L = not (V2 =< V1),
G = V1 > V2,
- ?line G = not (V1 =< V2),
- ?line G = V2 < V1,
- ?line G = not (V2 >= V1),
-
+ G = not (V1 =< V2),
+ G = V2 < V1,
+ G = not (V2 >= V1),
+
ID = V1 =:= V2,
- ?line ID = V2 =:= V1,
- ?line ID = not (V1 =/= V2),
- ?line ID = not (V2 =/= V1),
-
+ ID = V2 =:= V1,
+ ID = not (V1 =/= V2),
+ ID = not (V2 =/= V1),
+
EQ = V1 == V2,
- ?line EQ = V2 == V1,
- ?line EQ = not (V1 /= V2),
- ?line EQ = not (V2 /= V1),
-
- ?line case {L, EQ, ID, G, cmp_emu(V1,V2)} of
- { true, false, false, false, -1} -> ok;
- {false, true, false, false, 0} -> ok;
- {false, true, true, false, 0} -> ok;
- {false, false, false, true, +1} -> ok
- end.
-
+ EQ = V2 == V1,
+ EQ = not (V1 /= V2),
+ EQ = not (V2 /= V1),
+
+ case {L, EQ, ID, G, cmp_emu(V1,V2)} of
+ { true, false, false, false, -1} -> ok;
+ {false, true, false, false, 0} -> ok;
+ {false, true, true, false, 0} -> ok;
+ {false, false, false, true, +1} -> ok
+ end.
+
%% Emulate internal "cmp"
cmp_emu(A,B) when is_tuple(A), is_tuple(B) ->
SA = size(A),
@@ -145,8 +129,8 @@ cmp_emu(A,B) when is_tuple(A), is_tuple(B) ->
end;
cmp_emu([A|TA],[B|TB]) ->
case cmp_emu(A,B) of
- 0 -> cmp_emu(TA,TB);
- CMP -> CMP
+ 0 -> cmp_emu(TA,TB);
+ CMP -> CMP
end;
cmp_emu(A,B) ->
%% We cheat and use real "cmp" for the primitive types.
@@ -154,48 +138,48 @@ cmp_emu(A,B) ->
A > B -> +1;
true -> 0
end.
-
+
make_rand_term(1) ->
make_rand_term_single();
make_rand_term(Arity) ->
- case random:uniform(3) of
- 1 ->
- make_rand_list(Arity);
- 2 ->
- list_to_tuple(make_rand_list(Arity));
- 3 ->
- {Car,Rest} = make_rand_term_rand_size(Arity),
- [Car|make_rand_term(Rest)]
+ case rand:uniform(3) of
+ 1 ->
+ make_rand_list(Arity);
+ 2 ->
+ list_to_tuple(make_rand_list(Arity));
+ 3 ->
+ {Car,Rest} = make_rand_term_rand_size(Arity),
+ [Car|make_rand_term(Rest)]
end.
make_rand_term_single() ->
- Range = 1 bsl random:uniform(200),
- case random:uniform(12) of
- 1 -> random;
- 2 -> uniform;
- 3 -> random:uniform(Range) - (Range div 2);
- 4 -> Range * (random:uniform() - 0.5);
- 5 -> 0;
- 6 -> 0.0;
- 7 -> make_ref();
- 8 -> self();
- 9 -> term_to_binary(random:uniform(Range));
- 10 -> fun(X) -> X*Range end;
- 11 -> fun(X) -> X/Range end;
- 12 -> []
+ Range = 1 bsl rand:uniform(200),
+ case rand:uniform(12) of
+ 1 -> random;
+ 2 -> uniform;
+ 3 -> rand:uniform(Range) - (Range div 2);
+ 4 -> Range * (rand:uniform() - 0.5);
+ 5 -> 0;
+ 6 -> 0.0;
+ 7 -> make_ref();
+ 8 -> self();
+ 9 -> term_to_binary(rand:uniform(Range));
+ 10 -> fun(X) -> X*Range end;
+ 11 -> fun(X) -> X/Range end;
+ 12 -> []
end.
make_rand_term_rand_size(1) ->
{make_rand_term(1), 0};
make_rand_term_rand_size(MaxArity) ->
- Arity = random:uniform(MaxArity-1),
+ Arity = rand:uniform(MaxArity-1),
{make_rand_term(Arity), MaxArity-Arity}.
make_rand_list(0) -> [];
make_rand_list(Arity) ->
{Term, Rest} = make_rand_term_rand_size(Arity),
[Term | make_rand_list(Rest)].
-
+
clone_and_mutate(Term, 0) ->
{clone(Term), 0};
@@ -218,82 +202,81 @@ clone(Term) ->
my_list_to_tuple(List) ->
try list_to_tuple(List)
catch
- error:badarg ->
- %%io:format("my_list_to_tuple got badarg exception.\n"),
- list_to_tuple(purify_list(List))
+ error:badarg ->
+ %%io:format("my_list_to_tuple got badarg exception.\n"),
+ list_to_tuple(purify_list(List))
end.
-
+
purify_list(List) ->
lists:reverse(purify_list(List, [])).
purify_list([], Acc) -> Acc;
purify_list([H|T], Acc) -> purify_list(T, [H|Acc]);
purify_list(Other, Acc) -> [Other|Acc].
-
-relop(doc) -> "Test the relational operators and internal BIFs on literals.";
+
+%% Test the relational operators and internal BIFs on literals.
relop(Config) when is_list(Config) ->
Big1 = -38374938373887374983978484,
Big2 = 19738924729729787487784874,
F1 = float(Big1),
F2 = float(Big2),
Vs0 = [a,b,-33,-33.0,0,0.0,42,42.0,Big1,Big2,F1,F2],
- ?line Vs = [unvalue(V) || V <- Vs0],
+ Vs = [unvalue(V) || V <- Vs0],
Ops = ['==', '/=', '=:=', '=/=', '<', '=<', '>', '>='],
- ?line binop(Ops, Vs).
+ binop(Ops, Vs).
-complex_relop(doc) ->
- "Test the relational operators and internal BIFs on lists and tuples.";
+%% Test the relational operators and internal BIFs on lists and tuples.
complex_relop(Config) when is_list(Config) ->
Big = 99678557475484872464269855544643333,
Float = float(Big),
Vs0 = [an_atom,42.0,42,Big,Float],
Vs = flatmap(fun(X) -> [unvalue({X}),unvalue([X])] end, Vs0),
Ops = ['==', '/=', '=:=', '=/=', '<', '=<', '>', '>='],
- ?line binop(Ops, Vs).
+ binop(Ops, Vs).
binop(Ops, Vs) ->
- Run = fun(Op, N) -> ?line Cases = [{Op,V1,V2} || V1 <- Vs, V2 <- Vs],
- ?line run_test_module(Cases, true),
- N + length(Cases) end,
- ?line NumCases = foldl(Run, 0, Ops),
+ Run = fun(Op, N) -> Cases = [{Op,V1,V2} || V1 <- Vs, V2 <- Vs],
+ run_test_module(Cases, true),
+ N + length(Cases) end,
+ NumCases = foldl(Run, 0, Ops),
{comment,integer_to_list(NumCases) ++ " cases"}.
-
+
run_test_module(Cases, GuardsOk) ->
- ?line Es = [expr(C) || C <- Cases],
- ?line Ok = unvalue(ok),
- ?line Gts = case GuardsOk of
- true ->
- Ges = [guard_expr(C) || C <- Cases],
- ?line lists:foldr(fun guard_test/2, [Ok], Ges);
- false ->
- [Ok]
- end,
- ?line Fun1 = make_function(guard_tests, Gts),
- ?line Bts = lists:foldr(fun body_test/2, [Ok], Es),
- ?line Fun2 = make_function(body_tests, Bts),
- ?line Bbts = lists:foldr(fun internal_bif/2, [Ok], Es),
- ?line Fun3 = make_function(bif_tests, Bbts),
- ?line Id = {function,1,id,1,[{clause,1,[{var,1,'I'}],[],[{var,1,'I'}]}]},
+ Es = [expr(C) || C <- Cases],
+ Ok = unvalue(ok),
+ Gts = case GuardsOk of
+ true ->
+ Ges = [guard_expr(C) || C <- Cases],
+ lists:foldr(fun guard_test/2, [Ok], Ges);
+ false ->
+ [Ok]
+ end,
+ Fun1 = make_function(guard_tests, Gts),
+ Bts = lists:foldr(fun body_test/2, [Ok], Es),
+ Fun2 = make_function(body_tests, Bts),
+ Bbts = lists:foldr(fun internal_bif/2, [Ok], Es),
+ Fun3 = make_function(bif_tests, Bbts),
+ Id = {function,1,id,1,[{clause,1,[{var,1,'I'}],[],[{var,1,'I'}]}]},
Module0 = make_module(op_tests, [Fun1,Fun2,Fun3,Id]),
Module = erl_parse:new_anno(Module0),
- ?line lists:foreach(fun(F) -> io:put_chars([erl_pp:form(F),"\n"]) end, Module),
+ lists:foreach(fun(F) -> io:put_chars([erl_pp:form(F),"\n"]) end, Module),
%% Compile, load, and run the generated module.
- Native = case ?t:is_native(?MODULE) of
- true -> [native];
- false -> []
- end,
- ?line {ok,Mod,Code1} = compile:forms(Module, [time|Native]),
- ?line code:delete(Mod),
- ?line code:purge(Mod),
- ?line {module,Mod} = code:load_binary(Mod, Mod, Code1),
- ?line run_function(Mod, guard_tests),
- ?line run_function(Mod, body_tests),
- ?line run_function(Mod, bif_tests),
-
- ?line true = code:delete(Mod),
- ?line code:purge(Mod),
+ Native = case test_server:is_native(?MODULE) of
+ true -> [native];
+ false -> []
+ end,
+ {ok,Mod,Code1} = compile:forms(Module, [time|Native]),
+ code:delete(Mod),
+ code:purge(Mod),
+ {module,Mod} = code:load_binary(Mod, Mod, Code1),
+ run_function(Mod, guard_tests),
+ run_function(Mod, body_tests),
+ run_function(Mod, bif_tests),
+
+ true = code:delete(Mod),
+ code:purge(Mod),
ok.
@@ -317,19 +300,19 @@ guard_expr({Op,X,Y}) ->
run_function(Mod, Name) ->
case catch Mod:Name() of
- {'EXIT',Reason} ->
- io:format("~p", [get(last)]),
- ?t:fail({'EXIT',Reason});
- _Other ->
- ok
+ {'EXIT',Reason} ->
+ io:format("~p", [get(last)]),
+ ct:fail({'EXIT',Reason});
+ _Other ->
+ ok
end.
-
+
guard_test({E,Expr,Res}, Tail) ->
True = unvalue(true),
[save_term(Expr),
{match,1,unvalue(Res),
{'if',1,[{clause,1,[],[[E]],[True]},
- {clause,1,[],[[True]],[unvalue(false)]}]}}|Tail].
+ {clause,1,[],[[True]],[unvalue(false)]}]}}|Tail].
body_test({E,Expr,{'EXIT',_}}, Tail) ->
[save_term(Expr),
@@ -355,8 +338,8 @@ internal_bif(Op, Args, Expr, Res, Tail) ->
save_term(Term) ->
{call,1,
- {atom,1,put},
- [{atom,1,last},unvalue(Term)]}.
+ {atom,1,put},
+ [{atom,1,last},unvalue(Term)]}.
make_module(Name, Funcs) ->
[{attribute,1,module,Name},
@@ -366,18 +349,18 @@ make_module(Name, Funcs) ->
make_function(Name, Body) ->
{function,1,Name,0,[{clause,1,[],[],Body}]}.
-
+
eval(E0) ->
E = erl_parse:new_anno(E0),
- ?line case catch erl_eval:exprs(E, []) of
- {'EXIT',Reason} -> {'EXIT',Reason};
- {value,Val,_Bs} -> Val
- end.
+ case catch erl_eval:exprs(E, []) of
+ {'EXIT',Reason} -> {'EXIT',Reason};
+ {value,Val,_Bs} -> Val
+ end.
unvalue(V) ->
Abstr = erl_parse:abstract(V),
erl_parse:anno_to_term(Abstr).
-
+
value({nil,_}) -> [];
value({integer,_,X}) -> X;
value({string,_,X}) -> X;
diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl
index 3d0509a28c..23594aa8c4 100644
--- a/erts/emulator/test/port_SUITE.erl
+++ b/erts/emulator/test/port_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -74,67 +74,127 @@
%%
--export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
- init_per_testcase/2, end_per_testcase/2,
- init_per_suite/1, end_per_suite/1,
- stream_small/1, stream_big/1,
- basic_ping/1, slow_writes/1, bad_packet/1, bad_port_messages/1,
- mul_basic/1, mul_slow_writes/1,
- dying_port/1, port_program_with_path/1,
- open_input_file_port/1, open_output_file_port/1,
- iter_max_ports/1, eof/1, input_only/1, output_only/1,
- name1/1,
- t_binary/1, parallell/1, t_exit/1,
- env/1, bad_env/1, cd/1, exit_status/1,
- tps_16_bytes/1, tps_1K/1, line/1, stderr_to_stdout/1,
- otp_3906/1, otp_4389/1, win_massive/1, win_massive_client/1,
- mix_up_ports/1, otp_5112/1, otp_5119/1, otp_6224/1,
- exit_status_multi_scheduling_block/1, ports/1,
- spawn_driver/1, spawn_executable/1, close_deaf_port/1,
- port_setget_data/1,
- unregister_name/1, parallelism_option/1]).
-
--export([do_iter_max_ports/2]).
+-export([all/0, suite/0, groups/0, init_per_testcase/2, end_per_testcase/2,
+ init_per_suite/1, end_per_suite/1]).
+-export([
+ bad_args/1,
+ bad_env/1,
+ bad_packet/1,
+ bad_port_messages/1,
+ basic_ping/1,
+ cd/1,
+ cd_relative/1,
+ close_deaf_port/1,
+ count_fds/1,
+ dying_port/1,
+ env/1,
+ eof/1,
+ exit_status/1,
+ exit_status_multi_scheduling_block/1,
+ huge_env/1,
+ pipe_limit_env/1,
+ input_only/1,
+ iter_max_ports/1,
+ line/1,
+ mix_up_ports/1,
+ mon_port_invalid_type/1,
+ mon_port_bad_named/1,
+ mon_port_bad_remote_on_local/1,
+ mon_port_local/1,
+ mon_port_name_demonitor/1,
+ mon_port_named/1,
+ mon_port_origin_dies/1,
+ mon_port_owner_dies/1,
+ mon_port_pid_demonitor/1,
+ mon_port_remote_on_remote/1,
+ mon_port_driver_die/1,
+ mon_port_driver_die_demonitor/1,
+ mul_basic/1,
+ mul_slow_writes/1,
+ name1/1,
+ open_input_file_port/1,
+ open_output_file_port/1,
+ otp_3906/1,
+ otp_4389/1,
+ otp_5112/1,
+ otp_5119/1,
+ otp_6224/1,
+ output_only/1,
+ parallelism_option/1,
+ parallell/1,
+ port_program_with_path/1,
+ port_setget_data/1,
+ ports/1,
+ slow_writes/1,
+ spawn_driver/1,
+ spawn_executable/1,
+ stderr_to_stdout/1,
+ stream_big/1,
+ stream_small/1,
+ t_binary/1,
+ t_exit/1,
+ tps_16_bytes/1,
+ tps_1K/1,
+ unregister_name/1,
+ win_massive/1,
+ win_massive_client/1
+]).
+
+-export([do_iter_max_ports/2, relative_cd/0]).
%% Internal exports.
-export([tps/3]).
-export([otp_3906_forker/5, otp_3906_start_forker_starter/4]).
-export([env_slave_main/1]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include_lib("kernel/include/file.hrl").
+-include_lib("eunit/include/eunit.hrl").
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {seconds, 10}}].
-all() ->
+all() ->
[otp_6224, {group, stream}, basic_ping, slow_writes,
bad_packet, bad_port_messages, {group, options},
{group, multiple_packets}, parallell, dying_port,
port_program_with_path, open_input_file_port,
- open_output_file_port, name1, env, bad_env, cd,
- exit_status, iter_max_ports, t_exit, {group, tps}, line,
+ open_output_file_port, name1, env, huge_env, bad_env, cd,
+ cd_relative, pipe_limit_env, bad_args,
+ exit_status, iter_max_ports, count_fds, t_exit, {group, tps}, line,
stderr_to_stdout, otp_3906, otp_4389, win_massive,
mix_up_ports, otp_5112, otp_5119,
exit_status_multi_scheduling_block, ports, spawn_driver,
spawn_executable, close_deaf_port, unregister_name,
port_setget_data,
- parallelism_option].
-
-groups() ->
+ parallelism_option,
+ mon_port_invalid_type,
+ mon_port_local,
+ mon_port_remote_on_remote,
+ mon_port_bad_remote_on_local,
+ mon_port_origin_dies,
+ mon_port_owner_dies,
+ mon_port_named,
+ mon_port_bad_named,
+ mon_port_pid_demonitor,
+ mon_port_name_demonitor,
+ mon_port_driver_die,
+ mon_port_driver_die_demonitor
+ ].
+
+groups() ->
[{stream, [], [stream_small, stream_big]},
{options, [], [t_binary, eof, input_only, output_only]},
{multiple_packets, [], [mul_basic, mul_slow_writes]},
{tps, [], [tps_16_bytes, tps_1K]}].
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
--define(DEFAULT_TIMEOUT, ?t:minutes(5)).
-
+init_per_testcase(Case, Config) when Case =:= mon_port_driver_die;
+ Case =:= mon_port_driver_die_demonitor ->
+ case erlang:system_info(schedulers_online) of
+ 1 -> {skip, "Need 2 schedulers to run testcase"};
+ _ -> Config
+ end;
init_per_testcase(Case, Config) ->
[{testcase, Case} |Config].
@@ -154,69 +214,63 @@ end_per_suite(Config) when is_list(Config) ->
%% on a Windows machine given the correct environment.
win_massive(Config) when is_list(Config) ->
case os:type() of
- {win32,_} ->
- do_win_massive();
- _ ->
- {skip,"Only on Windows."}
+ {win32,_} ->
+ do_win_massive();
+ _ ->
+ {skip,"Only on Windows."}
end.
do_win_massive() ->
- Dog = test_server:timetrap(test_server:seconds(360)),
+ ct:timetrap({minutes, 6}),
SuiteDir = filename:dirname(code:which(?MODULE)),
Ports = " +Q 8192",
- {ok, Node} =
- test_server:start_node(win_massive,
- slave,
- [{args, " -pa " ++ SuiteDir ++ Ports}]),
+ {ok, Node} =
+ test_server:start_node(win_massive,
+ slave,
+ [{args, " -pa " ++ SuiteDir ++ Ports}]),
ok = rpc:call(Node,?MODULE,win_massive_client,[3000]),
test_server:stop_node(Node),
- test_server:timetrap_cancel(Dog),
ok.
-
+
win_massive_client(N) ->
- {ok,P}=gen_tcp:listen(?WIN_MASSIVE_PORT,[{reuseaddr,true}]),
+ {ok,P}=gen_tcp:listen(?WIN_MASSIVE_PORT,[{reuseaddr,true}]),
L = win_massive_loop(P,N),
Len = length(L),
lists:foreach(fun(E) ->
- gen_tcp:close(E)
- end,
- L),
+ gen_tcp:close(E)
+ end,
+ L),
case Len div 2 of
- N ->
- ok;
- _Else ->
- {too_few, Len}
+ N ->
+ ok;
+ _Else ->
+ {too_few, Len}
end.
win_massive_loop(_,0) ->
[];
win_massive_loop(P,N) ->
case (catch gen_tcp:connect("localhost",?WIN_MASSIVE_PORT,[])) of
- {ok,A} ->
- case (catch gen_tcp:accept(P)) of
- {ok,B} ->
- %erlang:display(N),
- [A,B|win_massive_loop(P,N-1)];
- _Else ->
- [A]
- end;
- _Else0 ->
- []
+ {ok,A} ->
+ case (catch gen_tcp:accept(P)) of
+ {ok,B} ->
+ %erlang:display(N),
+ [A,B|win_massive_loop(P,N-1)];
+ _Else ->
+ [A]
+ end;
+ _Else0 ->
+ []
end.
-
-
-
%% Test that we can send a stream of bytes and get it back.
%% We will send only a small amount of data, to avoid deadlock.
stream_small(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(10)),
stream_ping(Config, 512, "", []),
stream_ping(Config, 1777, "", []),
stream_ping(Config, 1777, "-s512", []),
- test_server:timetrap_cancel(Dog),
ok.
%% Send big amounts of data (much bigger than the buffer size in port test).
@@ -224,22 +278,20 @@ stream_small(Config) when is_list(Config) ->
%% non-blocking reads and writes.
stream_big(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(180)),
+ ct:timetrap({seconds, 180}),
stream_ping(Config, 43755, "", []),
stream_ping(Config, 100000, "", []),
stream_ping(Config, 77777, " -s40000", []),
- test_server:timetrap_cancel(Dog),
ok.
%% Sends packet with header size of 1, 2, and 4, with packets of various
%% sizes.
basic_ping(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(120)),
+ ct:timetrap({minutes, 2}),
ping(Config, sizes(1), 1, "", []),
ping(Config, sizes(2), 2, "", []),
ping(Config, sizes(4), 4, "", []),
- test_server:timetrap_cancel(Dog),
ok.
%% Let the port program insert delays between characters sent back to
@@ -247,17 +299,13 @@ basic_ping(Config) when is_list(Config) ->
%% small chunks rather than all at once.
slow_writes(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(20)),
ping(Config, [8], 4, "-s1", []),
ping(Config, [10], 2, "-s2", []),
- test_server:timetrap_cancel(Dog),
ok.
-bad_packet(doc) ->
- ["Test that we get {'EXIT', Port, einval} if we try to send a bigger "
- "packet than the packet header allows."];
+%% Test that we get {'EXIT', Port, einval} if we try to send a bigger
+%% packet than the packet header allows.
bad_packet(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(10)),
PortTest = port_test(Config),
process_flag(trap_exit, true),
@@ -265,16 +313,14 @@ bad_packet(Config) when is_list(Config) ->
bad_packet(PortTest, 1, 257),
bad_packet(PortTest, 2, 65536),
bad_packet(PortTest, 2, 65537),
-
- test_server:timetrap_cancel(Dog),
ok.
bad_packet(PortTest, HeaderSize, PacketSize) ->
P = open_port({spawn, PortTest}, [{packet, HeaderSize}]),
P ! {self(), {command, make_zero_packet(PacketSize)}},
receive
- {'EXIT', P, einval} -> ok;
- Other -> test_server:fail({unexpected_message, Other})
+ {'EXIT', P, einval} -> ok;
+ Other -> ct:fail({unexpected_message, Other})
end.
make_zero_packet(0) -> [];
@@ -287,7 +333,6 @@ make_zero_packet(N) ->
%% Test sending bad messages to a port.
bad_port_messages(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(10)),
PortTest = port_test(Config),
process_flag(trap_exit, true),
@@ -295,16 +340,14 @@ bad_port_messages(Config) when is_list(Config) ->
bad_message(PortTest, {a}),
bad_message(PortTest, {self(),{command,bad_command}}),
bad_message(PortTest, {self(),{connect,no_pid}}),
-
- test_server:timetrap_cancel(Dog),
ok.
-bad_message(PortTest, Message) ->
+bad_message(PortTest, Message) ->
P = open_port({spawn,PortTest}, []),
P ! Message,
receive
- {'EXIT',P,badsig} -> ok;
- Other -> test_server:fail({unexpected_message, Other})
+ {'EXIT',P,badsig} -> ok;
+ Other -> ct:fail({unexpected_message, Other})
end.
%% Tests various options (stream and {packet, Number} are implicitly
@@ -314,7 +357,7 @@ bad_message(PortTest, Message) ->
%% Tests the 'binary' option for a port.
t_binary(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(300)),
+ ct:timetrap({seconds, 300}),
%% Packet mode.
ping(Config, sizes(1), 1, "", [binary]),
@@ -325,12 +368,10 @@ t_binary(Config) when is_list(Config) ->
stream_ping(Config, 435, "", [binary]),
stream_ping(Config, 43755, "", [binary]),
stream_ping(Config, 100000, "", [binary]),
-
- test_server:timetrap_cancel(Dog),
ok.
name1(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(100)),
+ ct:timetrap({seconds, 100}),
PortTest = port_test(Config),
Command = lists:concat([PortTest, " "]),
P = open_port({spawn, Command}, []),
@@ -347,13 +388,12 @@ name1(Config) when is_list(Config) ->
{P, closed} -> ok
end,
undefined = whereis(myport),
- test_server:timetrap_cancel(Dog),
ok.
%% Test that the 'eof' option works.
eof(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(100)),
+ ct:timetrap({seconds, 100}),
PortTest = port_test(Config),
Command = lists:concat([PortTest, " -h0 -q"]),
P = open_port({spawn, Command}, [eof]),
@@ -365,47 +405,49 @@ eof(Config) when is_list(Config) ->
receive
{P, closed} -> ok
end,
- test_server:timetrap_cancel(Dog),
ok.
%% Tests that the 'in' option for a port works.
input_only(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(300)),
+ ct:timetrap({seconds, 300}),
expect_input(Config, [0, 1, 10, 13, 127, 128, 255], 1, "", [in]),
expect_input(Config, [0, 1, 255, 2048], 2, "", [in]),
expect_input(Config, [0, 1, 255, 2048], 4, "", [in]),
expect_input(Config, [0, 1, 10, 13, 127, 128, 255],
1, "", [in, binary]),
- test_server:timetrap_cancel(Dog),
ok.
%% Tests that the 'out' option for a port works.
output_only(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(100)),
- Dir = ?config(priv_dir, Config),
+ ct:timetrap({seconds, 100}),
+ Dir = proplists:get_value(priv_dir, Config),
+
+ %% First we test that the port program gets the data
Filename = filename:join(Dir, "output_only_stream"),
- output_and_verify(Config, Filename, "-h0",
- random_packet(35777, "echo")),
- test_server:timetrap_cancel(Dog),
+ Data = random_packet(35777, "echo"),
+ output_and_verify(Config, ["-h0 -o", Filename], Data),
+ Wait_time = 500,
+ test_server:sleep(Wait_time),
+ {ok, Written} = file:read_file(Filename),
+ Data = binary_to_list(Written),
+
+ %% Then we test that any writes to stdout from
+ %% the port program is not sent to erlang
+ output_and_verify(Config, ["-h0"], Data),
ok.
-output_and_verify(Config, Filename, Options, Data) ->
+output_and_verify(Config, Options, Data) ->
PortTest = port_test(Config),
- Command = lists:concat([PortTest, " ",
- Options, " -o", Filename]),
+ Command = lists:concat([PortTest, " " | Options]),
Port = open_port({spawn, Command}, [out]),
Port ! {self(), {command, Data}},
Port ! {self(), close},
receive
- {Port, closed} -> ok
- end,
- Wait_time = 500,
- test_server:sleep(Wait_time),
- {ok, Written} = file:read_file(Filename),
- Data = binary_to_list(Written),
- ok.
+ {Port, closed} -> ok;
+ Msg -> ct:fail({received_unexpected_message, Msg})
+ end.
%% Test that receiving several packages written in the same
%% write operation works.
@@ -414,11 +456,10 @@ output_and_verify(Config, Filename, Options, Data) ->
%% Basic test of receiving multiple packages, written in
%% one operation by the other end.
mul_basic(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(600)),
+ ct:timetrap({minutes, 10}),
expect_input(Config, [0, 1, 255, 10, 13], 1, "", []),
expect_input(Config, [0, 10, 13, 1600, 32767, 65535], 2, "", []),
expect_input(Config, [10, 70000], 4, "", []),
- test_server:timetrap_cancel(Dog),
ok.
%% Test reading a buffer consisting of several packets, some
@@ -427,9 +468,8 @@ mul_basic(Config) when is_list(Config) ->
%% delays in between.)
mul_slow_writes(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(250)),
+ ct:timetrap({minutes, 4}),
expect_input(Config, [0, 20, 255, 10, 1], 1, "-s64", []),
- test_server:timetrap_cancel(Dog),
ok.
%% Runs several port tests in parallell. Each individual test
@@ -437,27 +477,26 @@ mul_slow_writes(Config) when is_list(Config) ->
%% should also finish in about 5 seconds.
parallell(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(300)),
+ ct:timetrap({minutes, 5}),
Testers = [
- fun() -> stream_ping(Config, 1007, "-s100", []) end,
- fun() -> stream_ping(Config, 10007, "-s1000", []) end,
- fun() -> stream_ping(Config, 10007, "-s1000", []) end,
+ fun() -> stream_ping(Config, 1007, "-s100", []) end,
+ fun() -> stream_ping(Config, 10007, "-s1000", []) end,
+ fun() -> stream_ping(Config, 10007, "-s1000", []) end,
- fun() -> expect_input(Config, [21, 22, 23, 24, 25], 1,
- "-s10", [in]) end,
+ fun() -> expect_input(Config, [21, 22, 23, 24, 25], 1,
+ "-s10", [in]) end,
- fun() -> ping(Config, [10], 1, "-d", []) end,
- fun() -> ping(Config, [20000], 2, "-d", []) end,
- fun() -> ping(Config, [101], 1, "-s10", []) end,
- fun() -> ping(Config, [1001], 2, "-s100", []) end,
- fun() -> ping(Config, [10001], 4, "-s1000", []) end,
+ fun() -> ping(Config, [10], 1, "-d", []) end,
+ fun() -> ping(Config, [20000], 2, "-d", []) end,
+ fun() -> ping(Config, [101], 1, "-s10", []) end,
+ fun() -> ping(Config, [1001], 2, "-s100", []) end,
+ fun() -> ping(Config, [10001], 4, "-s1000", []) end,
- fun() -> ping(Config, [501, 501], 2, "-s100", []) end,
- fun() -> ping(Config, [11, 12, 13, 14, 11], 1, "-s5", []) end],
+ fun() -> ping(Config, [501, 501], 2, "-s100", []) end,
+ fun() -> ping(Config, [11, 12, 13, 14, 11], 1, "-s5", []) end],
process_flag(trap_exit, true),
Pids = lists:map(fun fun_spawn/1, Testers),
wait_for(Pids),
- test_server:timetrap_cancel(Dog),
ok.
wait_for([]) ->
@@ -465,18 +504,17 @@ wait_for([]) ->
wait_for(Pids) ->
io:format("Waiting for ~p", [Pids]),
receive
- {'EXIT', Pid, normal} ->
- wait_for(lists:delete(Pid, Pids));
- Other ->
- test_server:fail({bad_exit, Other})
+ {'EXIT', Pid, normal} ->
+ wait_for(lists:delete(Pid, Pids));
+ Other ->
+ ct:fail({bad_exit, Other})
end.
%% Tests starting port programs that terminate by themselves.
%% This used to cause problems on Windows.
-dying_port(suite) -> [];
dying_port(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(150)),
+ ct:timetrap({minutes, 2}),
process_flag(trap_exit, true),
P1 = make_dying_port(Config),
@@ -497,14 +535,12 @@ dying_port(Config) when is_list(Config) ->
wait_for_port_exit(P3),
wait_for_port_exit(P4),
wait_for_port_exit(P5),
-
- test_server:timetrap_cancel(Dog),
ok.
wait_for_port_exit(Port) ->
receive
- {'EXIT', Port, _} ->
- ok
+ {'EXIT', Port, _} ->
+ ok
end.
make_dying_port(Config) when is_list(Config) ->
@@ -523,22 +559,21 @@ make_dying_port(Config) when is_list(Config) ->
%%
%% This testcase works on Unix, but is not very useful.
-port_program_with_path(suite) -> [];
port_program_with_path(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(100)),
- DataDir = ?config(data_dir, Config),
- PrivDir = ?config(priv_dir, Config),
-
+ ct:timetrap({minutes, 2}),
+ DataDir = proplists:get_value(data_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
+
%% Create a copy of the port test program in a directory not
%% included in PATH (i.e. in priv_dir), with the name 'my_port_test.exe'.
%% Also, place a file named 'my_port_test' in the same directory.
%% This used to confuse the CreateProcess() call in spawn driver.
%% (On Unix, there will be a single file created, which will be
%% a copy of the port program.)
-
+
PortTest = os:find_executable("port_test", DataDir),
io:format("os:find_executable(~p, ~p) returned ~p",
- ["port_test", DataDir, PortTest]),
+ ["port_test", DataDir, PortTest]),
{ok, PortTestPgm} = file:read_file(PortTest),
NewName = filename:join(PrivDir, filename:basename(PortTest)),
RedHerring = filename:rootname(NewName),
@@ -546,12 +581,12 @@ port_program_with_path(Config) when is_list(Config) ->
ok = file:write_file(NewName, PortTestPgm),
ok = file:write_file_info(NewName, #file_info{mode=8#111}),
PgmWithPathAndNoExt = filename:rootname(NewName),
-
+
%% Open the port using the path to the copied port test program,
%% but without the .exe extension, and verified that it was started.
%%
%% If the bug is present the open_port call will fail with badarg.
-
+
Command = lists:concat([PgmWithPathAndNoExt, " -h2"]),
P = open_port({spawn, Command}, [{packet, 2}]),
Message = "echo back to me",
@@ -560,17 +595,14 @@ port_program_with_path(Config) when is_list(Config) ->
{P, {data, Message}} ->
ok
end,
- test_server:timetrap_cancel(Dog),
ok.
%% Tests that files can be read using open_port(Filename, [in]).
%% This used to fail on Windows.
-open_input_file_port(suite) -> [];
open_input_file_port(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(10)),
- PrivDir = ?config(priv_dir, Config),
-
+ PrivDir = proplists:get_value(priv_dir, Config),
+
%% Create a file with the file driver and read it back using
%% open_port/2.
@@ -578,20 +610,18 @@ open_input_file_port(Config) when is_list(Config) ->
FileData1 = "An input file",
ok = file:write_file(MyFile1, FileData1),
case open_port(MyFile1, [in]) of
- InputPort when is_port(InputPort) ->
- receive
- {InputPort, {data, FileData1}} ->
- ok
- end
+ InputPort when is_port(InputPort) ->
+ receive
+ {InputPort, {data, FileData1}} ->
+ ok
+ end
end,
- test_server:timetrap_cancel(Dog),
ok.
%% Tests that files can be written using open_port(Filename, [out]).
-open_output_file_port(suite) -> [];
open_output_file_port(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(100)),
- PrivDir = ?config(priv_dir, Config),
+ ct:timetrap({minutes, 2}),
+ PrivDir = proplists:get_value(priv_dir, Config),
%% Create a file with open_port/2 and read it back with
%% the file driver.
@@ -606,16 +636,44 @@ open_output_file_port(Config) when is_list(Config) ->
OutputPort ! {self(), close},
{ok, Bin} = file:read_file(MyFile2),
FileData2 = binary_to_list(Bin),
-
- test_server:timetrap_cancel(Dog),
ok.
+%% Tests that all appropriate fd's have been closed in the port program
+count_fds(Config) when is_list(Config) ->
+ case os:type() of
+ {unix, _} ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ Filename = filename:join(PrivDir, "my_fd_counter"),
+
+ RunTest = fun(PortOpts) ->
+ PortTest = port_test(Config),
+ Command = lists:concat([PortTest, " -n -f -o", Filename]),
+ Port = open_port({spawn, Command}, PortOpts),
+ Port ! {self(), close},
+ receive
+ {Port, closed} -> ok
+ end,
+ test_server:sleep(500),
+ {ok, Written} = file:read_file(Filename),
+ Written
+ end,
+ <<4:32/native>> = RunTest([out, nouse_stdio]),
+ <<4:32/native>> = RunTest([in, nouse_stdio]),
+ <<5:32/native>> = RunTest([in, out, nouse_stdio]),
+ <<3:32/native>> = RunTest([out, use_stdio]),
+ <<3:32/native>> = RunTest([in, use_stdio]),
+ <<3:32/native>> = RunTest([in, out, use_stdio]),
+ <<3:32/native>> = RunTest([in, out, use_stdio, stderr_to_stdout]),
+ <<3:32/native>> = RunTest([out, use_stdio, stderr_to_stdout]);
+ _ ->
+ {skip, "Skipped on windows"}
+ end.
+
%%
%% Open as many ports as possible. Do this several times and check
%% that we get the same number of ports every time.
%%
-iter_max_ports(suite) -> [];
iter_max_ports(Config) when is_list(Config) ->
%% The child_setup program might dump core if we get out of memory.
%% This is hard to do anything about and is harmless. We run this test
@@ -624,31 +682,30 @@ iter_max_ports(Config) when is_list(Config) ->
%%
Config2 = ignore_cores:setup(?MODULE, iter_max_ports, Config, true),
try
- iter_max_ports_test(Config2)
+ iter_max_ports_test(Config2)
after
- ignore_cores:restore(Config2)
+ ignore_cores:restore(Config2)
end.
-
-
+
+
iter_max_ports_test(Config) ->
- Dog = test_server:timetrap(test_server:minutes(30)),
+ ct:timetrap({minutes, 30}),
PortTest = port_test(Config),
Command = lists:concat([PortTest, " -h0 -q"]),
Iters = case os:type() of
- {win32,_} -> 4;
- _ -> 10
- end,
+ {win32,_} -> 4;
+ _ -> 10
+ end,
%% Run on a different node in order to limit the effect if this test fails.
Dir = filename:dirname(code:which(?MODULE)),
{ok,Node} = test_server:start_node(test_iter_max_socks,slave,
- [{args,"+Q 2048 -pa " ++ Dir}]),
+ [{args,"+Q 2048 -pa " ++ Dir}]),
L = rpc:call(Node,?MODULE,do_iter_max_ports,[Iters, Command]),
test_server:stop_node(Node),
io:format("Result: ~p",[L]),
all_equal(L),
all_equal(L),
- test_server:timetrap_cancel(Dog),
{comment, "Max ports: " ++ integer_to_list(hd(L))}.
do_iter_max_ports(N, Command) when N > 0 ->
@@ -672,46 +729,54 @@ max_ports(Command) ->
close_ports([P|Ps]) ->
P ! {self(), close},
receive
- {P,closed} ->
- ok
+ {P,closed} ->
+ ok
end,
close_ports(Ps);
close_ports([]) ->
ok.
open_ports(Name, Settings) ->
- test_server:sleep(5),
+ case os:type() of
+ {unix, freebsd} ->
+ %% FreeBsd has issues with sendmsg/recvmsg in fork
+ %% implementation and we therefor have to spawn
+ %% slower to make sure that we always hit the same
+ %% make roof.
+ test_server:sleep(10);
+ _ ->
+ test_server:sleep(5)
+ end,
case catch open_port(Name, Settings) of
- P when is_port(P) ->
- [P| open_ports(Name, Settings)];
- {'EXIT', {Code, _}} ->
- case Code of
- enfile ->
- [];
- emfile ->
- [];
- system_limit ->
- [];
- enomem ->
- [];
- Other ->
- test_server:fail({open_ports, Other})
- end;
- Other ->
- test_server:fail({open_ports, Other})
+ P when is_port(P) ->
+ [P| open_ports(Name, Settings)];
+ {'EXIT', {Code, _}} ->
+ case Code of
+ enfile ->
+ [];
+ emfile ->
+ [];
+ system_limit ->
+ [];
+ enomem ->
+ [];
+ Other ->
+ ct:fail({open_ports, Other})
+ end;
+ Other ->
+ ct:fail({open_ports, Other})
end.
%% Tests that exit(Port, Term) works (has been known to crash the emulator).
-t_exit(suite) -> [];
t_exit(Config) when is_list(Config) ->
process_flag(trap_exit, true),
Pid = fun_spawn(fun suicide_port/1, [Config]),
receive
- {'EXIT', Pid, die} ->
- ok;
- Other ->
- test_server:fail({bad_message, Other})
+ {'EXIT', Pid, die} ->
+ ok;
+ Other ->
+ ct:fail({bad_message, Other})
end.
suicide_port(Config) when is_list(Config) ->
@@ -720,118 +785,105 @@ suicide_port(Config) when is_list(Config) ->
receive after infinity -> ok end.
-tps_16_bytes(doc) -> "";
-tps_16_bytes(suite) -> [];
tps_16_bytes(Config) when is_list(Config) ->
tps(16, Config).
-tps_1K(doc) -> "";
-tps_1K(suite) -> [];
tps_1K(Config) when is_list(Config) ->
tps(1024, Config).
tps(Size, Config) ->
- Dog = test_server:timetrap(test_server:seconds(300)),
+ ct:timetrap({minutes, 5}),
PortTest = port_test(Config),
Packet = list_to_binary(random_packet(Size, "e")),
Port = open_port({spawn, PortTest}, [binary, {packet, 2}]),
Transactions = 10000,
{Elapsed, ok} = test_server:timecall(?MODULE, tps,
- [Port, Packet, Transactions]),
- test_server:timetrap_cancel(Dog),
+ [Port, Packet, Transactions]),
{comment, integer_to_list(trunc(Transactions/Elapsed+0.5)) ++ " transactions/s"}.
tps(_Port, _Packet, 0) -> ok;
tps(Port, Packet, N) ->
port_command(Port, Packet),
receive
- {Port, {data, Packet}} ->
- tps(Port, Packet, N-1);
- Other ->
- test_server:fail({bad_message, Other})
+ {Port, {data, Packet}} ->
+ tps(Port, Packet, N-1);
+ Other ->
+ ct:fail({bad_message, Other})
end.
%% Line I/O test
line(Config) when is_list(Config) ->
+ ct:timetrap({minutes, 5}),
Siz = 110,
- Dog = test_server:timetrap(test_server:seconds(300)),
Packet1 = random_packet(Siz),
Packet2 = random_packet(Siz div 2),
%% Test that packets are split into lines
port_expect(Config,[{lists:append([Packet1, io_lib:nl(), Packet2,
- io_lib:nl()]),
- [{eol, Packet1}, {eol, Packet2}]}],
- 0, "", [{line,Siz}]),
+ io_lib:nl()]),
+ [{eol, Packet1}, {eol, Packet2}]}],
+ 0, "", [{line,Siz}]),
%% Test the same for binaries
port_expect(Config,[{lists:append([Packet1, io_lib:nl(), Packet2,
- io_lib:nl()]),
- [{eol, Packet1}, {eol, Packet2}]}],
- 0, "", [{line,Siz},binary]),
+ io_lib:nl()]),
+ [{eol, Packet1}, {eol, Packet2}]}],
+ 0, "", [{line,Siz},binary]),
%% Test that too long lines get split
port_expect(Config,[{lists:append([Packet1, io_lib:nl(), Packet1,
- Packet2, io_lib:nl()]),
- [{eol, Packet1}, {noeol, Packet1},
- {eol, Packet2}]}], 0, "", [{line,Siz}]),
+ Packet2, io_lib:nl()]),
+ [{eol, Packet1}, {noeol, Packet1},
+ {eol, Packet2}]}], 0, "", [{line,Siz}]),
%% Test that last output from closing port program gets received.
L1 = lists:append([Packet1, io_lib:nl(), Packet2]),
S1 = lists:flatten(io_lib:format("-l~w", [length(L1)])),
io:format("S1 = ~w, L1 = ~w~n", [S1,L1]),
port_expect(Config,[{L1,
- [{eol, Packet1}, {noeol, Packet2}, eof]}], 0,
- S1, [{line,Siz},eof]),
+ [{eol, Packet1}, {noeol, Packet2}, eof]}], 0,
+ S1, [{line,Siz},eof]),
%% Test that lonely <CR> Don't get treated as newlines
port_expect(Config,[{lists:append([Packet1, [13], Packet2,
- io_lib:nl()]),
- [{noeol, Packet1}, {eol, [13 |Packet2]}]}],
- 0, "", [{line,Siz}]),
+ io_lib:nl()]),
+ [{noeol, Packet1}, {eol, [13 |Packet2]}]}],
+ 0, "", [{line,Siz}]),
%% Test that packets get built up to lines (delayed output from
%% port program)
port_expect(Config,[{Packet2,[]},
- {lists:append([Packet2, io_lib:nl(),
- Packet1, io_lib:nl()]),
- [{eol, lists:append(Packet2, Packet2)},
- {eol, Packet1}]}], 0, "-d", [{line,Siz}]),
+ {lists:append([Packet2, io_lib:nl(),
+ Packet1, io_lib:nl()]),
+ [{eol, lists:append(Packet2, Packet2)},
+ {eol, Packet1}]}], 0, "-d", [{line,Siz}]),
%% Test that we get badarg if trying both packet and line
bad_argument(Config, [{packet, 5}, {line, 5}]),
- test_server:timetrap_cancel(Dog),
ok.
-%%% Redirection of stderr test
-stderr_to_stdout(suite) ->
- [];
-stderr_to_stdout(doc) ->
- "Test that redirection of standard error to standard output works.";
+%% Test that redirection of standard error to standard output works.
stderr_to_stdout(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(60)),
+ ct:timetrap({minutes, 1}),
%% See that it works
Packet = random_packet(10),
port_expect(Config,[{Packet,[Packet]}], 0, "-e -l10",
- [stderr_to_stdout]),
+ [stderr_to_stdout]),
%% stream_ping(Config, 10, "-e", [stderr_to_stdout]),
%% See that it doesn't always happen (will generate garbage on stderr)
port_expect(Config,[{Packet,[eof]}], 0, "-e -l10", [line,eof]),
- test_server:timetrap_cancel(Dog),
ok.
bad_argument(Config, ArgList) ->
PortTest = port_test(Config),
case catch open_port({spawn, PortTest}, ArgList) of
- {'EXIT', {badarg, _}} ->
- ok
+ {'EXIT', {badarg, _}} ->
+ ok
end.
-
+
%% 'env' option
%% (Can perhaps be made smaller by calling the other utility functions
%% in this module.)
-env(suite) ->
- [];
-env(doc) ->
- ["Test that the 'env' option works"];
+%%
+%% Test that the 'env' option works
env(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(60)),
- Priv = ?config(priv_dir, Config),
+ ct:timetrap({minutes, 1}),
+ Priv = proplists:get_value(priv_dir, Config),
Temp = filename:join(Priv, "env_fun.bin"),
PluppVal = "dirty monkey",
@@ -841,36 +893,34 @@ env(Config) when is_list(Config) ->
os:putenv(Long, "nisse"),
env_slave(Temp, [{"plupp",PluppVal},
- {"DIR_PLUPP","###glurfrik"}],
- fun() ->
- PluppVal = os:getenv("plupp"),
- "###glurfrik" = os:getenv("DIR_PLUPP"),
- "nisse" = os:getenv(Long)
- end),
+ {"DIR_PLUPP","###glurfrik"}],
+ fun() ->
+ PluppVal = os:getenv("plupp"),
+ "###glurfrik" = os:getenv("DIR_PLUPP"),
+ "nisse" = os:getenv(Long)
+ end),
env_slave(Temp, [{"must_define_something","some_value"},
- {"certainly_not_existing",false},
- {"ends_with_equal", "value="},
- {Long,false},
- {"glurf","a glorfy string"}]),
+ {"certainly_not_existing",false},
+ {"ends_with_equal", "value="},
+ {Long,false},
+ {"glurf","a glorfy string"}]),
%% A lot of non existing variables (mingled with existing)
- NotExistingList = [{lists:flatten(io_lib:format("V~p_not_existing",[X])),false}
- || X <- lists:seq(1,150)],
- ExistingList = [{lists:flatten(io_lib:format("V~p_existing",[X])),"a_value"}
- || X <- lists:seq(1,150)],
+ NotExistingList = [{lists:flatten(io_lib:format("V~p_not_existing",[X])),false}
+ || X <- lists:seq(1,150)],
+ ExistingList = [{lists:flatten(io_lib:format("V~p_existing",[X])),"a_value"}
+ || X <- lists:seq(1,150)],
env_slave(Temp, lists:sort(ExistingList ++ NotExistingList)),
-
- test_server:timetrap_cancel(Dog),
ok.
env_slave(File, Env) ->
F = fun() ->
- lists:foreach(fun({Name,Val}) ->
- Val = os:getenv(Name)
- end, Env)
- end,
+ lists:foreach(fun({Name,Val}) ->
+ Val = os:getenv(Name)
+ end, Env)
+ end,
env_slave(File, Env, F).
env_slave(File, Env, Body) ->
@@ -878,27 +928,26 @@ env_slave(File, Env, Body) ->
Program = atom_to_list(lib:progname()),
Dir = filename:dirname(code:which(?MODULE)),
Cmd = Program ++ " -pz " ++ Dir ++
- " -noinput -run " ++ ?MODULE_STRING ++ " env_slave_main " ++
- File ++ " -run erlang halt",
+ " -noinput -run " ++ ?MODULE_STRING ++ " env_slave_main " ++
+ File ++ " -run erlang halt",
Port = open_port({spawn, Cmd}, [{env,Env},{line,256}]),
receive
- {Port,{data,{eol,"ok"}}} ->
- ok;
- {Port,{data,{eol,Error}}} ->
- io:format("~p\n", [Error]),
- test_server:fail();
- Other ->
- test_server:fail(Other)
+ {Port,{data,{eol,"ok"}}} ->
+ ok;
+ {Port,{data,{eol,Error}}} ->
+ ct:fail("eol error ~p\n", [Error]);
+ Other ->
+ ct:fail(Other)
end.
env_slave_main([File]) ->
{ok,Body0} = file:read_file(File),
Body = binary_to_term(Body0),
case Body() of
- {'EXIT',Reason} ->
- io:format("Error: ~p\n", [Reason]);
- _ ->
- io:format("ok\n")
+ {'EXIT',Reason} ->
+ io:format("Error: ~p\n", [Reason]);
+ _ ->
+ io:format("ok\n")
end,
init:stop().
@@ -918,64 +967,232 @@ bad_env(Config) when is_list(Config) ->
ok.
try_bad_env(Env) ->
- try open_port({spawn,"ls"}, [{env,Env}])
- catch
- error:badarg -> ok
+ badarg = try open_port({spawn,"ls"}, [{env,Env}])
+ catch
+ error:badarg -> badarg
+ end.
+
+
+%% Test that we can handle a very very large environment gracefully.
+huge_env(Config) when is_list(Config) ->
+ ct:timetrap({minutes, 2}),
+ {Vars, Cmd} = case os:type() of
+ {win32,_} -> {500, "cmd /q /c ls"};
+ _ ->
+ %% We create a huge environment,
+ %% 20000 variables is about 25MB
+ %% which seems to be the limit on Linux.
+ {20000, "ls"}
+ end,
+ Env = [{[$a + I div (25*25*25*25) rem 25,
+ $a + I div (25*25*25) rem 25,
+ $a + I div (25*25) rem 25,
+ $a+I div 25 rem 25, $a+I rem 25],
+ lists:duplicate(100,$a+I rem 25)}
+ || I <- lists:seq(1,Vars)],
+ try erlang:open_port({spawn,Cmd},[exit_status, {env, Env}]) of
+ P ->
+ receive
+ {P, {exit_status,N}} = M ->
+ %% We test that the exit status is an integer, this means
+ %% that the child program has started. If we get an atom
+ %% something went wrong in the driver which is not ok.
+ ct:log("Got ~p",[M]),
+ true = is_integer(N)
+ end
+ catch E:R ->
+ %% Have to catch the error here, as printing the stackdump
+ %% in the ct log is way to heavy for some test machines.
+ ct:fail("Open port failed ~p:~p",[E,R])
+ end.
+
+%% Test to spawn program with command payload buffer
+%% just around pipe capacity (9f779819f6bda734c5953468f7798)
+pipe_limit_env(Config) when is_list(Config) ->
+ Cmd = case os:type() of
+ {win32,_} -> "cmd /q /c true";
+ _ -> "true"
+ end,
+ CmdSize = command_payload_size(Cmd),
+ Limits = [4096, 16384, 65536], % Try a couple of common pipe buffer sizes
+
+ lists:foreach(fun(Lim) ->
+ lists:foreach(fun(L) -> pipe_limit_env_do(L, Cmd, CmdSize)
+ end, lists:seq(Lim-5, Lim+5))
+ end, Limits),
+ ok.
+
+pipe_limit_env_do(Bytes, Cmd, CmdSize) ->
+ case env_of_bytes(Bytes-CmdSize) of
+ [] -> skip;
+ Env ->
+ try erlang:open_port({spawn,Cmd},[exit_status, {env, Env}]) of
+ P ->
+ receive
+ {P, {exit_status,N}} ->
+ %% Bug caused exit_status 150 (EINVAL+128)
+ 0 = N
+ end
+ catch E:R ->
+ %% Have to catch the error here, as printing the stackdump
+ %% in the ct log is way to heavy for some test machines.
+ ct:fail("Open port failed ~p:~p",[E,R])
+ end
end.
+%% environ format: KEY=VALUE\0
+env_of_bytes(Bytes) when Bytes > 3 ->
+ Env = [{"X",lists:duplicate(Bytes-3, $x)}];
+env_of_bytes(_) -> [].
+
+%% White box assumption about payload written to pipe
+%% for Cmd and current environment (see spawn_start in sys_driver.c)
+command_payload_size(Cmd) ->
+ EnvSize = lists:foldl(fun(E,Acc) -> length(E) + 1 + Acc end,
+ 0, os:getenv()),
+ {ok, PWD} = file:get_cwd(),
+ (4 % buffsz
+ + 4 % flags
+ + 5 + length(Cmd) + 1 % "exec $Cmd"
+ + length(PWD) + 1 % $PWD
+ + 1 % nullbuff
+ + 4 % env_len
+ + EnvSize).
+
+%% Test bad 'args' options.
+bad_args(Config) when is_list(Config) ->
+ try_bad_args({args, [self()]}),
+ try_bad_args({args, ["head" | "tail"]}),
+ try_bad_args({args, ["head", "body" | "tail"]}),
+ try_bad_args({args, [<<"head">>, <<"body">> | <<"tail">>]}),
+ try_bad_args({args, not_a_list}),
+ try_bad_args({args, ["string",<<"binary">>, 1472, "string"]}),
+ try_bad_args({args, ["string",<<"binary">>], "element #3"}),
+ ok.
+
+try_bad_args(Args) ->
+ badarg = try open_port({spawn_executable,"ls"}, [Args])
+ catch
+ error:badarg -> badarg
+ end.
+
+
+
%% 'cd' option
%% (Can perhaps be made smaller by calling the other utility functions
%% in this module.)
-cd(suite) ->
- [];
-cd(doc) ->
- ["Test that the 'cd' option works"];
+%%
+%% Test that the 'cd' option works
cd(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(60)),
+ ct:timetrap({minutes, 1}),
Program = atom_to_list(lib:progname()),
- DataDir = ?config(data_dir, Config),
+ DataDir = proplists:get_value(data_dir, Config),
TestDir = filename:join(DataDir, "dir"),
Cmd = Program ++ " -pz " ++ DataDir ++
- " -noshell -s port_test pwd -s erlang halt",
+ " -noshell -s port_test pwd -s erlang halt",
_ = open_port({spawn, Cmd},
- [{cd, TestDir},
- {line, 256}]),
+ [{cd, TestDir}, {line, 256}]),
receive
- {_, {data, {eol, String}}} ->
- case filename_equal(String, TestDir) of
- true ->
- ok;
- false ->
- test_server:fail({cd, String})
- end;
- Other2 ->
- test_server:fail({env, Other2})
+ {_, {data, {eol, String}}} ->
+ case filename_equal(String, TestDir) of
+ true ->
+ ok;
+ false ->
+ ct:fail({cd, String})
+ end;
+ Other2 ->
+ ct:fail({env, Other2})
end,
_ = open_port({spawn, Cmd},
- [{cd, unicode:characters_to_binary(TestDir)},
- {line, 256}]),
+ [{cd, unicode:characters_to_binary(TestDir)},
+ {line, 256}]),
receive
- {_, {data, {eol, String2}}} ->
- case filename_equal(String2, TestDir) of
- true ->
- ok;
- false ->
- test_server:fail({cd, String2})
- end;
- Other3 ->
- test_server:fail({env, Other3})
+ {_, {data, {eol, String2}}} ->
+ case filename_equal(String2, TestDir) of
+ true ->
+ ok;
+ false ->
+ ct:fail({cd, String2})
+ end;
+ Other3 ->
+ ct:fail({env, Other3})
end,
- test_server:timetrap_cancel(Dog),
- ok.
+ InvalidDir = filename:join(DataDir, "invaliddir"),
+ try open_port({spawn, Cmd},
+ [{cd, InvalidDir}, exit_status, {line, 256}]) of
+ _ ->
+ receive
+ {_, {exit_status, _}} ->
+ ok;
+ Other4 ->
+ ct:fail({env, Other4})
+ end
+ catch error:eacces ->
+ %% This happens on Windows
+ ok
+ end,
+
+ %% Check that there are no lingering messages
+ receive
+ Other5 ->
+ ct:fail({env, Other5})
+ after 10 ->
+ ok
+ end.
+
+%% Test that an emulator that has set it's cwd to
+%% something other then when it started, can use
+%% relative {cd,"./"} to open port and that cd will
+%% be relative the new cwd and not the original
+cd_relative(Config) ->
+
+ Program = atom_to_list(lib:progname()),
+ DataDir = proplists:get_value(data_dir, Config),
+ TestDir = filename:join(DataDir, "dir"),
+
+ Cmd = Program ++ " -pz " ++ filename:dirname(code:where_is_file("port_SUITE.beam")) ++
+ " -noshell -s port_SUITE relative_cd -s erlang halt",
+
+ _ = open_port({spawn, Cmd}, [{line, 256}, {cd, TestDir}]),
+
+ receive
+ {_, {data, {eol, String}}} ->
+ case filename_equal(String, TestDir) of
+ true ->
+ ok;
+ false ->
+ ct:fail({cd_relative, String})
+ end;
+ Other ->
+ ct:fail(Other)
+ end.
+
+relative_cd() ->
+
+ Program = atom_to_list(lib:progname()),
+ ok = file:set_cwd(".."),
+ {ok, Cwd} = file:get_cwd(),
+
+ Cmd = Program ++ " -pz " ++ Cwd ++
+ " -noshell -s port_test pwd -s erlang halt",
+
+ _ = open_port({spawn, Cmd}, [{line, 256}, {cd, "./dir"}, exit_status]),
+
+ receive
+ {_, {data, {eol, String}}} ->
+ io:format("~s~n",[String]);
+ Other ->
+ io:format("ERROR: ~p~n",[Other])
+ end.
filename_equal(A, B) ->
case os:type() of
- {win32, _} ->
- win_filename_equal(A, B);
- _ ->
- A == B
+ {win32, _} ->
+ win_filename_equal(A, B);
+ _ ->
+ A == B
end.
win_filename_equal([], []) ->
@@ -986,10 +1203,10 @@ win_filename_equal(_, []) ->
false;
win_filename_equal([C1 | Rest1], [C2 | Rest2]) ->
case tolower(C1) == tolower(C2) of
- true ->
- win_filename_equal(Rest1, Rest2);
- false ->
- false
+ true ->
+ win_filename_equal(Rest1, Rest2);
+ false ->
+ false
end.
tolower(C) when C >= $A, C =< $Z ->
@@ -997,17 +1214,14 @@ tolower(C) when C >= $A, C =< $Z ->
tolower(C) ->
C.
-otp_3906(suite) ->
- [];
-otp_3906(doc) ->
- ["Tests that child process deaths are managed correctly when there are "
- " a large amount of concurrently dying children. See ticket OTP-3906."];
+%% Tests that child process deaths are managed correctly when there are
+%% a large amount of concurrently dying children. See ticket OTP-3906.
otp_3906(Config) when is_list(Config) ->
case os:type() of
- {unix, OSName} ->
- otp_3906(Config, OSName);
- _ ->
- {skipped, "Only run on Unix systems"}
+ {unix, OSName} ->
+ otp_3906(Config, OSName);
+ _ ->
+ {skipped, "Only run on Unix systems"}
end.
-define(OTP_3906_CHILDREN, 1000).
@@ -1020,83 +1234,83 @@ otp_3906(Config) when is_list(Config) ->
otp_3906(Config, OSName) ->
DataDir = filename:dirname(proplists:get_value(data_dir,Config)),
{ok, Variables} = file:consult(
- filename:join([DataDir,"..","..",
- "test_server","variables"])),
+ filename:join([DataDir,"..","..",
+ "test_server","variables"])),
case lists:keysearch('CC', 1, Variables) of
- {value,{'CC', CC}} ->
- SuiteDir = filename:dirname(code:which(?MODULE)),
- PrivDir = ?config(priv_dir, Config),
- Prog = otp_3906_make_prog(CC, PrivDir),
- {ok, Node} = test_server:start_node(otp_3906,
- slave,
- [{args, " -pa " ++ SuiteDir},
- {linked, false}]),
- OP = process_flag(priority, max),
- OTE = process_flag(trap_exit, true),
- FS = spawn_link(Node,
- ?MODULE,
- otp_3906_start_forker_starter,
- [?OTP_3906_CHILDREN, [], self(), Prog]),
- Result = receive
- {'EXIT', _ForkerStarter, Reason} ->
- {failed, Reason};
- {emulator_pid, EmPid} ->
- case otp_3906_wait_result(FS, 0, 0) of
- {succeded,
- ?OTP_3906_CHILDREN,
- ?OTP_3906_CHILDREN} ->
- succeded;
- {succeded, Forked, Exited} ->
- otp_3906_list_defunct(EmPid, OSName),
- {failed,
- {mismatch,
- {forked, Forked},
- {exited, Exited}}};
- Res ->
- otp_3906_list_defunct(EmPid, OSName),
- Res
- end
- end,
- process_flag(trap_exit, OTE),
- process_flag(priority, OP),
- test_server:stop_node(Node),
- case Result of
- succeded ->
- ok;
- _ ->
- test_server:fail(Result)
- end;
- _ ->
- {skipped, "No C compiler found"}
+ {value,{'CC', CC}} ->
+ SuiteDir = filename:dirname(code:which(?MODULE)),
+ PrivDir = proplists:get_value(priv_dir, Config),
+ Prog = otp_3906_make_prog(CC, PrivDir),
+ {ok, Node} = test_server:start_node(otp_3906,
+ slave,
+ [{args, " -pa " ++ SuiteDir},
+ {linked, false}]),
+ OP = process_flag(priority, max),
+ OTE = process_flag(trap_exit, true),
+ FS = spawn_link(Node,
+ ?MODULE,
+ otp_3906_start_forker_starter,
+ [?OTP_3906_CHILDREN, [], self(), Prog]),
+ Result = receive
+ {'EXIT', _ForkerStarter, Reason} ->
+ {failed, Reason};
+ {emulator_pid, EmPid} ->
+ case otp_3906_wait_result(FS, 0, 0) of
+ {succeded,
+ ?OTP_3906_CHILDREN,
+ ?OTP_3906_CHILDREN} ->
+ succeded;
+ {succeded, Forked, Exited} ->
+ otp_3906_list_defunct(EmPid, OSName),
+ {failed,
+ {mismatch,
+ {forked, Forked},
+ {exited, Exited}}};
+ Res ->
+ otp_3906_list_defunct(EmPid, OSName),
+ Res
+ end
+ end,
+ process_flag(trap_exit, OTE),
+ process_flag(priority, OP),
+ test_server:stop_node(Node),
+ case Result of
+ succeded ->
+ ok;
+ _ ->
+ ct:fail(Result)
+ end;
+ _ ->
+ {skipped, "No C compiler found"}
end.
otp_3906_list_defunct(EmPid, OSName) ->
% Guess ps switches to use and what to grep for (could be improved)
{Switches, Zombie} = case OSName of
- BSD when BSD == darwin;
- BSD == openbsd;
- BSD == netbsd;
- BSD == freebsd ->
- {"-ajx", "Z"};
- _ ->
- {"-ef", "[dD]efunct"}
- end,
- test_server:format("Emulator pid: ~s~n"
- "Listing of zombie processes:~n"
- "~s~n",
- [EmPid,
- otp_3906_htmlize(os:cmd("ps "
- ++ Switches
- ++ " | grep "
- ++ Zombie))]).
+ BSD when BSD == darwin;
+ BSD == openbsd;
+ BSD == netbsd;
+ BSD == freebsd ->
+ {"-ajx", "Z"};
+ _ ->
+ {"-ef", "[dD]efunct"}
+ end,
+ io:format("Emulator pid: ~s~n"
+ "Listing of zombie processes:~n"
+ "~s~n",
+ [EmPid,
+ otp_3906_htmlize(os:cmd("ps "
+ ++ Switches
+ ++ " | grep "
+ ++ Zombie))]).
otp_3906_htmlize([]) ->
[];
otp_3906_htmlize([C | Cs]) ->
case [C] of
- "<" -> "&lt;" ++ otp_3906_htmlize(Cs);
- ">" -> "&gt;" ++ otp_3906_htmlize(Cs);
- _ -> [C | otp_3906_htmlize(Cs)]
+ "<" -> "&lt;" ++ otp_3906_htmlize(Cs);
+ ">" -> "&gt;" ++ otp_3906_htmlize(Cs);
+ _ -> [C | otp_3906_htmlize(Cs)]
end.
otp_3906_make_prog(CC, PrivDir) ->
@@ -1104,12 +1318,12 @@ otp_3906_make_prog(CC, PrivDir) ->
TrgtFileName = filename:join(PrivDir, ?OTP_3906_PROGNAME),
{ok, SrcFile} = file:open(SrcFileName, write),
io:format(SrcFile,
- "int ~n"
- "main(void) ~n"
- "{ ~n"
- " return ~p; ~n"
- "} ~n",
- [?OTP_3906_EXIT_STATUS]),
+ "int ~n"
+ "main(void) ~n"
+ "{ ~n"
+ " return ~p; ~n"
+ "} ~n",
+ [?OTP_3906_EXIT_STATUS]),
file:close(SrcFile),
os:cmd(CC ++ " " ++ SrcFileName ++ " -o " ++ TrgtFileName),
TrgtFileName.
@@ -1117,21 +1331,21 @@ otp_3906_make_prog(CC, PrivDir) ->
otp_3906_wait_result(ForkerStarter, F, E) ->
receive
- {'EXIT', ForkerStarter, Reason} ->
- {failed, {Reason, {forked, F}, {exited, E}}};
- forked ->
- otp_3906_wait_result(ForkerStarter, F+1, E);
- exited ->
- otp_3906_wait_result(ForkerStarter, F, E+1);
- tick ->
- otp_3906_wait_result(ForkerStarter, F, E);
- succeded ->
- {succeded, F, E}
+ {'EXIT', ForkerStarter, Reason} ->
+ {failed, {Reason, {forked, F}, {exited, E}}};
+ forked ->
+ otp_3906_wait_result(ForkerStarter, F+1, E);
+ exited ->
+ otp_3906_wait_result(ForkerStarter, F, E+1);
+ tick ->
+ otp_3906_wait_result(ForkerStarter, F, E);
+ succeded ->
+ {succeded, F, E}
after
- ?OTP_3906_TICK_TIMEOUT ->
- unlink(ForkerStarter),
- exit(ForkerStarter, timeout),
- {failed, {timeout, {forked, F}, {exited, E}}}
+ ?OTP_3906_TICK_TIMEOUT ->
+ unlink(ForkerStarter),
+ exit(ForkerStarter, timeout),
+ {failed, {timeout, {forked, F}, {exited, E}}}
end.
otp_3906_collect([], _) ->
@@ -1141,17 +1355,17 @@ otp_3906_collect(RefList, Sup) ->
otp_3906_collect_one(RefList, Sup) ->
receive
- Ref when is_reference(Ref) ->
- Sup ! tick,
- lists:delete(Ref, RefList)
+ Ref when is_reference(Ref) ->
+ Sup ! tick,
+ lists:delete(Ref, RefList)
end.
-
+
otp_3906_start_forker(N, Sup, Prog) ->
Ref = make_ref(),
spawn_opt(?MODULE,
- otp_3906_forker,
- [N, self(), Ref, Sup, Prog],
- [link, {priority, max}]),
+ otp_3906_forker,
+ [N, self(), Ref, Sup, Prog],
+ [link, {priority, max}]),
Ref.
otp_3906_start_forker_starter(N, RefList, Sup, Prog) ->
@@ -1170,18 +1384,18 @@ otp_3906_forker_starter(N, RefList, Sup, Prog)
otp_3906_forker_starter(N, RefList, Sup, Prog)
when is_integer(N), N > ?OTP_3906_OSP_P_ERLP ->
otp_3906_forker_starter(N-?OTP_3906_OSP_P_ERLP,
- [otp_3906_start_forker(?OTP_3906_OSP_P_ERLP,
- Sup,
- Prog)|RefList],
- Sup,
- Prog);
+ [otp_3906_start_forker(?OTP_3906_OSP_P_ERLP,
+ Sup,
+ Prog)|RefList],
+ Sup,
+ Prog);
otp_3906_forker_starter(N, RefList, Sup, Prog) when is_integer(N) ->
otp_3906_forker_starter(0,
- [otp_3906_start_forker(N,
- Sup,
- Prog)|RefList],
- Sup,
- Prog).
+ [otp_3906_start_forker(N,
+ Sup,
+ Prog)|RefList],
+ Sup,
+ Prog).
otp_3906_forker(0, Parent, Ref, _, _) ->
unlink(Parent),
@@ -1190,183 +1404,165 @@ otp_3906_forker(N, Parent, Ref, Sup, Prog) ->
Port = erlang:open_port({spawn, Prog}, [exit_status, in]),
Sup ! forked,
receive
- {Port, {exit_status, ?OTP_3906_EXIT_STATUS}} ->
- Sup ! exited,
- otp_3906_forker(N-1, Parent, Ref, Sup, Prog);
- {Port, Res} ->
- exit(Res);
- Other ->
- exit(Other)
+ {Port, {exit_status, ?OTP_3906_EXIT_STATUS}} ->
+ Sup ! exited,
+ otp_3906_forker(N-1, Parent, Ref, Sup, Prog);
+ {Port, Res} ->
+ exit(Res);
+ Other ->
+ exit(Other)
end.
-otp_4389(suite) -> [];
-otp_4389(doc) -> [];
otp_4389(Config) when is_list(Config) ->
case os:type() of
- {unix, _} ->
- Dog = test_server:timetrap(test_server:seconds(240)),
- TCR = self(),
- case get_true_cmd() of
- True when is_list(True) ->
- lists:foreach(
- fun (P) ->
- receive
- {P, ok} -> ok;
- {P, Err} -> ?t:fail(Err)
- end
- end,
- lists:map(
- fun(_) ->
- spawn_link(
- fun() ->
- process_flag(trap_exit, true),
- case catch open_port({spawn, True},
- [stream,exit_status]) of
- P when is_port(P) ->
- receive
- {P,{exit_status,_}} ->
- TCR ! {self(),ok};
- {'EXIT',_,{R2,_}} when R2 == emfile;
- R2 == eagain ->
- TCR ! {self(),ok};
- Err2 ->
- TCR ! {self(),{msg,Err2}}
- end;
- {'EXIT',{R1,_}} when R1 == emfile;
- R1 == eagain ->
- TCR ! {self(),ok};
- Err1 ->
- TCR ! {self(), {open_port,Err1}}
- end
- end)
- end,
- lists:duplicate(1000,[]))),
- test_server:timetrap_cancel(Dog),
- {comment,
- "This test case doesn't always fail when the bug that "
- "it tests for is present (it is most likely to fail on"
- " a multi processor machine). If the test case fails it"
- " will fail by deadlocking the emulator."};
- _ ->
- {skipped, "\"true\" command not found"}
- end;
- _ ->
- {skip,"Only run on Unix"}
+ {unix, _} ->
+ ct:timetrap({minutes, 4}),
+ TCR = self(),
+ case get_true_cmd() of
+ True when is_list(True) ->
+ lists:foreach(
+ fun (P) ->
+ receive
+ {P, ok} -> ok;
+ {P, Err} -> ct:fail(Err)
+ end
+ end,
+ lists:map(
+ fun(_) ->
+ spawn_link(
+ fun() ->
+ process_flag(trap_exit, true),
+ case catch open_port({spawn, True},
+ [stream,exit_status]) of
+ P when is_port(P) ->
+ receive
+ {P,{exit_status,_}} ->
+ TCR ! {self(),ok};
+ {'EXIT',_,{R2,_}} when R2 == emfile;
+ R2 == eagain;
+ R2 == enomem ->
+ TCR ! {self(),ok};
+ Err2 ->
+ TCR ! {self(),{msg,Err2}}
+ end;
+ {'EXIT',{R1,_}} when R1 == emfile;
+ R1 == eagain;
+ R1 == enomem ->
+ TCR ! {self(),ok};
+ Err1 ->
+ TCR ! {self(), {open_port,Err1}}
+ end
+ end)
+ end,
+ lists:duplicate(1000,[]))),
+ {comment,
+ "This test case doesn't always fail when the bug that "
+ "it tests for is present (it is most likely to fail on"
+ " a multi processor machine). If the test case fails it"
+ " will fail by deadlocking the emulator."};
+ _ ->
+ {skipped, "\"true\" command not found"}
+ end;
+ _ ->
+ {skip,"Only run on Unix"}
end.
get_true_cmd() ->
DoFileExist = fun (FileName) ->
- case file:read_file_info(FileName) of
- {ok, _} -> throw(FileName);
- _ -> not_found
- end
- end,
+ case file:read_file_info(FileName) of
+ {ok, _} -> throw(FileName);
+ _ -> not_found
+ end
+ end,
catch begin
- %% First check in /usr/bin and /bin
- DoFileExist("/usr/bin/true"),
- DoFileExist("/bin/true"),
- %% Try which
- case filename:dirname(os:cmd("which true")) of
- "." -> not_found;
- TrueDir -> filename:join(TrueDir, "true")
- end
- end.
+ %% First check in /usr/bin and /bin
+ DoFileExist("/usr/bin/true"),
+ DoFileExist("/bin/true"),
+ %% Try which
+ case filename:dirname(os:cmd("which true")) of
+ "." -> not_found;
+ TrueDir -> filename:join(TrueDir, "true")
+ end
+ end.
%% 'exit_status' option
-exit_status(suite) ->
- [];
-exit_status(doc) ->
- ["Test that the 'exit_status' option works"];
+%%
+%% Test that the 'exit_status' option works
exit_status(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(60)),
- port_expect(Config,[{"x",
- [{exit_status, 5}]}],
- 1, "", [exit_status]),
- test_server:timetrap_cancel(Dog),
+ ct:timetrap({minutes, 1}),
+ port_expect(Config,
+ [{"x", [{exit_status, 5}]}],
+ 1, "", [exit_status]),
ok.
-spawn_driver(suite) ->
- [];
-spawn_driver(doc) ->
- ["Test spawning a driver specifically"];
+%% Test spawning a driver specifically
spawn_driver(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(10)),
- Path = ?config(data_dir, Config),
+ Path = proplists:get_value(data_dir, Config),
ok = load_driver(Path, "echo_drv"),
Port = erlang:open_port({spawn_driver, "echo_drv"}, []),
Port ! {self(), {command, "Hello port!"}},
- receive
- {Port, {data, "Hello port!"}} = Msg1 ->
- io:format("~p~n", [Msg1]),
- ok;
- Other ->
- test_server:fail({unexpected, Other})
- end,
+ receive
+ {Port, {data, "Hello port!"}} = Msg1 ->
+ io:format("~p~n", [Msg1]),
+ ok;
+ Other ->
+ ct:fail({unexpected, Other})
+ end,
Port ! {self(), close},
receive {Port, closed} -> ok end,
- Port2 = erlang:open_port({spawn_driver, "echo_drv -Hello port?"},
- []),
- receive
- {Port2, {data, "Hello port?"}} = Msg2 ->
- io:format("~p~n", [Msg2]),
- ok;
- Other2 ->
- test_server:fail({unexpected2, Other2})
- end,
+ Port2 = erlang:open_port({spawn_driver, "echo_drv -Hello port?"},
+ []),
+ receive
+ {Port2, {data, "Hello port?"}} = Msg2 ->
+ io:format("~p~n", [Msg2]),
+ ok;
+ Other2 ->
+ ct:fail({unexpected2, Other2})
+ end,
Port2 ! {self(), close},
receive {Port2, closed} -> ok end,
{'EXIT',{badarg,_}} = (catch erlang:open_port({spawn_driver, "ls"}, [])),
{'EXIT',{badarg,_}} = (catch erlang:open_port({spawn_driver, "cmd"}, [])),
{'EXIT',{badarg,_}} = (catch erlang:open_port({spawn_driver, os:find_executable("erl")}, [])),
- test_server:timetrap_cancel(Dog),
ok.
-parallelism_option(suite) ->
- [];
-parallelism_option(doc) ->
- ["Test parallelism option of open_port"];
+%% Test parallelism option of open_port
parallelism_option(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Path = ?config(data_dir, Config),
- ?line ok = load_driver(Path, "echo_drv"),
- ?line Port = erlang:open_port({spawn_driver, "echo_drv"},
- [{parallelism, true}]),
- ?line {parallelism, true} = erlang:port_info(Port, parallelism),
- ?line Port ! {self(), {command, "Hello port!"}},
- ?line receive
- {Port, {data, "Hello port!"}} = Msg1 ->
- io:format("~p~n", [Msg1]),
- ok;
- Other ->
- test_server:fail({unexpected, Other})
- end,
- ?line Port ! {self(), close},
- ?line receive {Port, closed} -> ok end,
-
- ?line Port2 = erlang:open_port({spawn_driver, "echo_drv -Hello port?"},
- [{parallelism, false}]),
- ?line {parallelism, false} = erlang:port_info(Port2, parallelism),
- ?line receive
- {Port2, {data, "Hello port?"}} = Msg2 ->
- io:format("~p~n", [Msg2]),
- ok;
- Other2 ->
- test_server:fail({unexpected2, Other2})
- end,
- ?line Port2 ! {self(), close},
- ?line receive {Port2, closed} -> ok end,
- ?line test_server:timetrap_cancel(Dog),
+ Path = proplists:get_value(data_dir, Config),
+ ok = load_driver(Path, "echo_drv"),
+ Port = erlang:open_port({spawn_driver, "echo_drv"},
+ [{parallelism, true}]),
+ {parallelism, true} = erlang:port_info(Port, parallelism),
+ Port ! {self(), {command, "Hello port!"}},
+ receive
+ {Port, {data, "Hello port!"}} = Msg1 ->
+ io:format("~p~n", [Msg1]),
+ ok;
+ Other ->
+ ct:fail({unexpected, Other})
+ end,
+ Port ! {self(), close},
+ receive {Port, closed} -> ok end,
+
+ Port2 = erlang:open_port({spawn_driver, "echo_drv -Hello port?"},
+ [{parallelism, false}]),
+ {parallelism, false} = erlang:port_info(Port2, parallelism),
+ receive
+ {Port2, {data, "Hello port?"}} = Msg2 ->
+ io:format("~p~n", [Msg2]),
+ ok;
+ Other2 ->
+ ct:fail({unexpected2, Other2})
+ end,
+ Port2 ! {self(), close},
+ receive {Port2, closed} -> ok end,
ok.
-spawn_executable(suite) ->
- [];
-spawn_executable(doc) ->
- ["Test spawning an executable specifically"];
+%% Test spawning an executable specifically
spawn_executable(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(10)),
- DataDir = ?config(data_dir, Config),
+ DataDir = proplists:get_value(data_dir, Config),
EchoArgs1 = filename:join([DataDir,"echo_args"]),
ExactFile1 = filename:nativename(os:find_executable(EchoArgs1)),
[ExactFile1] = run_echo_args(DataDir,[]),
@@ -1375,26 +1571,26 @@ spawn_executable(Config) when is_list(Config) ->
["echo_args"] = run_echo_args(DataDir,[binary, "echo_args"]),
["echo_arguments"] = run_echo_args(DataDir,["echo_arguments"]),
["echo_arguments"] = run_echo_args(DataDir,[binary, "echo_arguments"]),
- [ExactFile1,"hello world","dlrow olleh"] =
- run_echo_args(DataDir,[ExactFile1,"hello world","dlrow olleh"]),
+ [ExactFile1,"hello world","dlrow olleh"] =
+ run_echo_args(DataDir,[ExactFile1,"hello world","dlrow olleh"]),
[ExactFile1] = run_echo_args(DataDir,[default]),
[ExactFile1] = run_echo_args(DataDir,[binary, default]),
- [ExactFile1,"hello world","dlrow olleh"] =
- run_echo_args(DataDir,[switch_order,ExactFile1,"hello world",
- "dlrow olleh"]),
- [ExactFile1,"hello world","dlrow olleh"] =
- run_echo_args(DataDir,[binary,switch_order,ExactFile1,"hello world",
- "dlrow olleh"]),
[ExactFile1,"hello world","dlrow olleh"] =
- run_echo_args(DataDir,[default,"hello world","dlrow olleh"]),
+ run_echo_args(DataDir,[switch_order,ExactFile1,"hello world",
+ "dlrow olleh"]),
+ [ExactFile1,"hello world","dlrow olleh"] =
+ run_echo_args(DataDir,[binary,switch_order,ExactFile1,"hello world",
+ "dlrow olleh"]),
+ [ExactFile1,"hello world","dlrow olleh"] =
+ run_echo_args(DataDir,[default,"hello world","dlrow olleh"]),
- [ExactFile1,"hello world","dlrow olleh"] =
- run_echo_args_2("\""++ExactFile1++"\" "++"\"hello world\" \"dlrow olleh\""),
[ExactFile1,"hello world","dlrow olleh"] =
- run_echo_args_2(unicode:characters_to_binary("\""++ExactFile1++"\" "++"\"hello world\" \"dlrow olleh\"")),
+ run_echo_args_2("\""++ExactFile1++"\" "++"\"hello world\" \"dlrow olleh\""),
+ [ExactFile1,"hello world","dlrow olleh"] =
+ run_echo_args_2(unicode:characters_to_binary("\""++ExactFile1++"\" "++"\"hello world\" \"dlrow olleh\"")),
- PrivDir = ?config(priv_dir, Config),
- SpaceDir =filename:join([PrivDir,"With Spaces"]),
+ PrivDir = proplists:get_value(priv_dir, Config),
+ SpaceDir = filename:join([PrivDir,"With Spaces"]),
file:make_dir(SpaceDir),
Executable = filename:basename(ExactFile1),
file:copy(ExactFile1,filename:join([SpaceDir,Executable])),
@@ -1404,34 +1600,33 @@ spawn_executable(Config) when is_list(Config) ->
[ExactFile2] = run_echo_args(SpaceDir,[]),
["echo_args"] = run_echo_args(SpaceDir,["echo_args"]),
["echo_arguments"] = run_echo_args(SpaceDir,["echo_arguments"]),
- [ExactFile2,"hello world","dlrow olleh"] =
- run_echo_args(SpaceDir,[ExactFile2,"hello world","dlrow olleh"]),
[ExactFile2,"hello world","dlrow olleh"] =
- run_echo_args(SpaceDir,[binary, ExactFile2,"hello world","dlrow olleh"]),
+ run_echo_args(SpaceDir,[ExactFile2,"hello world","dlrow olleh"]),
+ [ExactFile2,"hello world","dlrow olleh"] =
+ run_echo_args(SpaceDir,[binary, ExactFile2,"hello world","dlrow olleh"]),
[ExactFile2,"hello \"world\"","\"dlrow\" olleh"] =
- run_echo_args(SpaceDir,[binary, ExactFile2,"hello \"world\"","\"dlrow\" olleh"]),
+ run_echo_args(SpaceDir,[binary, ExactFile2,"hello \"world\"","\"dlrow\" olleh"]),
[ExactFile2,"hello \"world\"","\"dlrow\" olleh"] =
- run_echo_args(SpaceDir,[binary, ExactFile2,"hello \"world\"","\"dlrow\" olleh"]),
+ run_echo_args(SpaceDir,[binary, ExactFile2,"hello \"world\"","\"dlrow\" olleh"]),
[ExactFile2] = run_echo_args(SpaceDir,[default]),
- [ExactFile2,"hello world","dlrow olleh"] =
- run_echo_args(SpaceDir,[switch_order,ExactFile2,"hello world",
- "dlrow olleh"]),
- [ExactFile2,"hello world","dlrow olleh"] =
- run_echo_args(SpaceDir,[default,"hello world","dlrow olleh"]),
- [ExactFile2,"hello world","dlrow olleh"] =
- run_echo_args_2("\""++ExactFile2++"\" "++"\"hello world\" \"dlrow olleh\""),
[ExactFile2,"hello world","dlrow olleh"] =
- run_echo_args_2(unicode:characters_to_binary("\""++ExactFile2++"\" "++"\"hello world\" \"dlrow olleh\"")),
-
- ExeExt =
- case string:to_lower(lists:last(string:tokens(ExactFile2,"."))) of
- "exe" ->
- ".exe";
- _ ->
- ""
- end,
+ run_echo_args(SpaceDir,[switch_order,ExactFile2,"hello world", "dlrow olleh"]),
+ [ExactFile2,"hello world","dlrow olleh"] =
+ run_echo_args(SpaceDir,[default,"hello world","dlrow olleh"]),
+ [ExactFile2,"hello world","dlrow olleh"] =
+ run_echo_args_2("\""++ExactFile2++"\" "++"\"hello world\" \"dlrow olleh\""),
+ [ExactFile2,"hello world","dlrow olleh"] =
+ run_echo_args_2(unicode:characters_to_binary("\""++ExactFile2++"\" "++"\"hello world\" \"dlrow olleh\"")),
+
+ ExeExt =
+ case string:to_lower(lists:last(string:tokens(ExactFile2,"."))) of
+ "exe" ->
+ ".exe";
+ _ ->
+ ""
+ end,
Executable2 = "spoky name"++ExeExt,
file:copy(ExactFile1,filename:join([SpaceDir,Executable2])),
ExactFile3 = filename:nativename(filename:join([SpaceDir,Executable2])),
@@ -1439,39 +1634,38 @@ spawn_executable(Config) when is_list(Config) ->
[ExactFile3] = run_echo_args(SpaceDir,Executable2,[]),
["echo_args"] = run_echo_args(SpaceDir,Executable2,["echo_args"]),
["echo_arguments"] = run_echo_args(SpaceDir,Executable2,["echo_arguments"]),
- [ExactFile3,"hello world","dlrow olleh"] =
- run_echo_args(SpaceDir,Executable2,[ExactFile3,"hello world","dlrow olleh"]),
+ [ExactFile3,"hello world","dlrow olleh"] =
+ run_echo_args(SpaceDir,Executable2,[ExactFile3,"hello world","dlrow olleh"]),
[ExactFile3] = run_echo_args(SpaceDir,Executable2,[default]),
- [ExactFile3,"hello world","dlrow olleh"] =
- run_echo_args(SpaceDir,Executable2,
- [switch_order,ExactFile3,"hello world",
- "dlrow olleh"]),
- [ExactFile3,"hello world","dlrow olleh"] =
- run_echo_args(SpaceDir,Executable2,
- [default,"hello world","dlrow olleh"]),
- [ExactFile3,"hello world","dlrow olleh"] =
- run_echo_args_2("\""++ExactFile3++"\" "++"\"hello world\" \"dlrow olleh\""),
[ExactFile3,"hello world","dlrow olleh"] =
- run_echo_args_2(unicode:characters_to_binary("\""++ExactFile3++"\" "++"\"hello world\" \"dlrow olleh\"")),
+ run_echo_args(SpaceDir,Executable2,
+ [switch_order,ExactFile3,"hello world",
+ "dlrow olleh"]),
+ [ExactFile3,"hello world","dlrow olleh"] =
+ run_echo_args(SpaceDir,Executable2,
+ [default,"hello world","dlrow olleh"]),
+ [ExactFile3,"hello world","dlrow olleh"] =
+ run_echo_args_2("\""++ExactFile3++"\" "++"\"hello world\" \"dlrow olleh\""),
+ [ExactFile3,"hello world","dlrow olleh"] =
+ run_echo_args_2(unicode:characters_to_binary("\""++ExactFile3++"\" "++"\"hello world\" \"dlrow olleh\"")),
{'EXIT',{enoent,_}} = (catch run_echo_args(SpaceDir,"fnurflmonfi",
- [default,"hello world",
- "dlrow olleh"])),
+ [default,"hello world",
+ "dlrow olleh"])),
NonExec = "kronxfrt"++ExeExt,
file:write_file(filename:join([SpaceDir,NonExec]),
- <<"Not an executable">>),
+ <<"Not an executable">>),
{'EXIT',{eacces,_}} = (catch run_echo_args(SpaceDir,NonExec,
- [default,"hello world",
- "dlrow olleh"])),
+ [default,"hello world",
+ "dlrow olleh"])),
{'EXIT',{enoent,_}} = (catch open_port({spawn_executable,"cmd"},[])),
{'EXIT',{enoent,_}} = (catch open_port({spawn_executable,"sh"},[])),
case os:type() of
- {win32,_} ->
- test_bat_file(SpaceDir);
- {unix,_} ->
- test_sh_file(SpaceDir)
+ {win32,_} ->
+ test_bat_file(SpaceDir);
+ {unix,_} ->
+ test_sh_file(SpaceDir)
end,
- test_server:timetrap_cancel(Dog),
ok.
unregister_name(Config) when is_list(Config) ->
@@ -1482,29 +1676,29 @@ test_bat_file(Dir) ->
FN = "tf.bat",
Full = filename:join([Dir,FN]),
D = [<<"@echo off\r\n">>,
- <<"echo argv[0]:^|%0^|\r\n">>,
- <<"if \"%1\" == \"\" goto done\r\n">>,
- <<"echo argv[1]:^|%1^|\r\n">>,
- <<"if \"%2\" == \"\" goto done\r\n">>,
- <<"echo argv[2]:^|%2^|\r\n">>,
- <<"if \"%3\" == \"\" goto done\r\n">>,
- <<"echo argv[3]:^|%3^|\r\n">>,
- <<"if \"%4\" == \"\" goto done\r\n">>,
- <<"echo argv[4]:^|%4^|\r\n">>,
- <<"if \"%5\" == \"\" goto done\r\n">>,
- <<"echo argv[5]:^|%5^|\r\n">>,
- <<"\r\n">>,
- <<":done\r\n">>,
- <<"\r\n">>],
+ <<"echo argv[0]:^|%0^|\r\n">>,
+ <<"if \"%1\" == \"\" goto done\r\n">>,
+ <<"echo argv[1]:^|%1^|\r\n">>,
+ <<"if \"%2\" == \"\" goto done\r\n">>,
+ <<"echo argv[2]:^|%2^|\r\n">>,
+ <<"if \"%3\" == \"\" goto done\r\n">>,
+ <<"echo argv[3]:^|%3^|\r\n">>,
+ <<"if \"%4\" == \"\" goto done\r\n">>,
+ <<"echo argv[4]:^|%4^|\r\n">>,
+ <<"if \"%5\" == \"\" goto done\r\n">>,
+ <<"echo argv[5]:^|%5^|\r\n">>,
+ <<"\r\n">>,
+ <<":done\r\n">>,
+ <<"\r\n">>],
file:write_file(Full,list_to_binary(D)),
EF = filename:basename(FN),
- [DN,"hello","world"] =
- run_echo_args(Dir,FN,
- [default,"hello","world"]),
+ [DN,"hello","world"] =
+ run_echo_args(Dir,FN,
+ [default,"hello","world"]),
%% The arg0 argumant should be ignored when running batch files
- [DN,"hello","world"] =
- run_echo_args(Dir,FN,
- ["knaskurt","hello","world"]),
+ [DN,"hello","world"] =
+ run_echo_args(Dir,FN,
+ ["knaskurt","hello","world"]),
EF = filename:basename(DN),
ok.
@@ -1512,40 +1706,40 @@ test_sh_file(Dir) ->
FN = "tf.sh",
Full = filename:join([Dir,FN]),
D = [<<"#! /bin/sh\n">>,
- <<"echo 'argv[0]:|'$0'|'\n">>,
- <<"i=1\n">>,
- <<"while [ '!' -z \"$1\" ]; do\n">>,
- <<" echo 'argv['$i']:|'\"$1\"'|'\n">>,
- <<" shift\n">>,
- <<" i=`expr $i + 1`\n">>,
- <<"done\n">>],
+ <<"echo 'argv[0]:|'$0'|'\n">>,
+ <<"i=1\n">>,
+ <<"while [ '!' -z \"$1\" ]; do\n">>,
+ <<" echo 'argv['$i']:|'\"$1\"'|'\n">>,
+ <<" shift\n">>,
+ <<" i=`expr $i + 1`\n">>,
+ <<"done\n">>],
file:write_file(Full,list_to_binary(D)),
chmodplusx(Full),
- [Full,"hello","world"] =
- run_echo_args(Dir,FN,
- [default,"hello","world"]),
- [Full,"hello","world of spaces"] =
- run_echo_args(Dir,FN,
- [default,"hello","world of spaces"]),
+ [Full,"hello","world"] =
+ run_echo_args(Dir,FN,
+ [default,"hello","world"]),
+ [Full,"hello","world of spaces"] =
+ run_echo_args(Dir,FN,
+ [default,"hello","world of spaces"]),
file:write_file(filename:join([Dir,"testfile1"]),<<"testdata1">>),
file:write_file(filename:join([Dir,"testfile2"]),<<"testdata2">>),
Pattern = filename:join([Dir,"testfile*"]),
L = filelib:wildcard(Pattern),
2 = length(L),
- [Full,"hello",Pattern] =
- run_echo_args(Dir,FN,
- [default,"hello",Pattern]),
- ok.
+ [Full,"hello",Pattern] =
+ run_echo_args(Dir,FN,
+ [default,"hello",Pattern]),
+ ok.
+
-
chmodplusx(Filename) ->
case file:read_file_info(Filename) of
- {ok,FI} ->
- FI2 = FI#file_info{mode = ((FI#file_info.mode) bor 8#00100)},
- file:write_file_info(Filename,FI2);
- _ ->
- ok
+ {ok,FI} ->
+ FI2 = FI#file_info{mode = ((FI#file_info.mode) bor 8#00100)},
+ file:write_file_info(Filename,FI2);
+ _ ->
+ ok
end.
run_echo_args_2(FullnameAndArgs) ->
@@ -1554,7 +1748,7 @@ run_echo_args_2(FullnameAndArgs) ->
Port ! {self(), close},
receive {Port, closed} -> ok end,
parse_echo_args_output(Data).
-
+
run_echo_args(Where,Args) ->
run_echo_args(Where,"echo_args",Args).
@@ -1562,9 +1756,9 @@ run_echo_args(Where,Prog,Args) ->
{Binary, ArgvArg} = pack_argv(Args),
Command0 = filename:join([Where,Prog]),
Command = case Binary of
- true -> unicode:characters_to_binary(Command0);
- false -> Command0
- end,
+ true -> unicode:characters_to_binary(Command0);
+ false -> Command0
+ end,
Port = open_port({spawn_executable,Command},ArgvArg++[eof]),
Data = collect_data(Port),
Port ! {self(), close},
@@ -1578,14 +1772,14 @@ pack_argv(Args) ->
pack_argv(Args, Binary) ->
case Args of
- [] ->
- [];
- [default|T] ->
- [{args,[make_bin(Arg,Binary) || Arg <- T]}];
- [switch_order,H|T] ->
- [{args,[make_bin(Arg,Binary) || Arg <- T]},{arg0,make_bin(H,Binary)}];
- [H|T] ->
- [{arg0,make_bin(H,Binary)},{args,[make_bin(Arg,Binary) || Arg <- T]}]
+ [] ->
+ [];
+ [default|T] ->
+ [{args,[make_bin(Arg,Binary) || Arg <- T]}];
+ [switch_order,H|T] ->
+ [{args,[make_bin(Arg,Binary) || Arg <- T]},{arg0,make_bin(H,Binary)}];
+ [H|T] ->
+ [{arg0,make_bin(H,Binary)},{args,[make_bin(Arg,Binary) || Arg <- T]}]
end.
make_bin(Str, false) -> Str;
@@ -1593,54 +1787,49 @@ make_bin(Str, true) -> unicode:characters_to_binary(Str).
collect_data(Port) ->
receive
- {Port, {data, Data}} ->
- Data ++ collect_data(Port);
- {Port, eof} ->
- []
+ {Port, {data, Data}} ->
+ Data ++ collect_data(Port);
+ {Port, eof} ->
+ []
end.
parse_echo_args_output(Data) ->
[lists:last(string:tokens(S,"|")) || S <- string:tokens(Data,"\r\n")].
-mix_up_ports(suite) ->
- [];
-mix_up_ports(doc) ->
- ["Test that the emulator does not mix up ports when the port table wraps"];
+%% Test that the emulator does not mix up ports when the port table wraps
mix_up_ports(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(10)),
- Path = ?config(data_dir, Config),
+ Path = proplists:get_value(data_dir, Config),
ok = load_driver(Path, "echo_drv"),
Port = erlang:open_port({spawn, "echo_drv"}, []),
Port ! {self(), {command, "Hello port!"}},
- receive
- {Port, {data, "Hello port!"}} = Msg1 ->
- io:format("~p~n", [Msg1]),
- ok;
- Other ->
- test_server:fail({unexpected, Other})
- end,
+ receive
+ {Port, {data, "Hello port!"}} = Msg1 ->
+ io:format("~p~n", [Msg1]),
+ ok;
+ Other ->
+ ct:fail({unexpected, Other})
+ end,
Port ! {self(), close},
receive {Port, closed} -> ok end,
loop(start, done,
- fun(P) ->
- Q =
- (catch erlang:open_port({spawn, "echo_drv"}, [])),
-%% io:format("~p ", [Q]),
- if is_port(Q) ->
- Q;
- true ->
- io:format("~p~n", [P]),
- done
- end
- end),
+ fun(P) ->
+ Q =
+ (catch erlang:open_port({spawn, "echo_drv"}, [])),
+ %% io:format("~p ", [Q]),
+ if is_port(Q) ->
+ Q;
+ true ->
+ io:format("~p~n", [P]),
+ done
+ end
+ end),
Port ! {self(), {command, "Hello again port!"}},
- receive
- Msg2 ->
- test_server:fail({unexpected, Msg2})
- after 1000 ->
- ok
- end,
- test_server:timetrap_cancel(Dog),
+ receive
+ Msg2 ->
+ ct:fail({unexpected, Msg2})
+ after 1000 ->
+ ok
+ end,
ok.
loop(Stop, Stop, Fun) when is_function(Fun) ->
@@ -1649,131 +1838,118 @@ loop(Start, Stop, Fun) when is_function(Fun) ->
loop(Fun(Start), Stop, Fun).
-otp_5112(suite) ->
- [];
-otp_5112(doc) ->
- ["Test that link to connected process is taken away when port calls",
- "driver_exit() also when the port index has wrapped"];
+%% Test that link to connected process is taken away when port calls
+%% driver_exit() also when the port index has wrapped
otp_5112(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(10)),
- Path = ?config(data_dir, Config),
+ Path = proplists:get_value(data_dir, Config),
ok = load_driver(Path, "exit_drv"),
Port = otp_5112_get_wrapped_port(),
- ?t:format("Max ports: ~p~n",[max_ports()]),
- ?t:format("Port: ~p~n",[Port]),
+ io:format("Max ports: ~p~n",[max_ports()]),
+ io:format("Port: ~p~n",[Port]),
{links, Links1} = process_info(self(),links),
- ?t:format("Links1: ~p~n",[Links1]),
+ io:format("Links1: ~p~n",[Links1]),
true = lists:member(Port, Links1),
Port ! {self(), {command, ""}},
- ?line wait_until(fun () -> lists:member(Port, erlang:ports()) == false end),
+ wait_until(fun () -> lists:member(Port, erlang:ports()) == false end),
{links, Links2} = process_info(self(),links),
- ?t:format("Links2: ~p~n",[Links2]),
+ io:format("Links2: ~p~n",[Links2]),
false = lists:member(Port, Links2), %% This used to fail
- test_server:timetrap_cancel(Dog),
ok.
otp_5112_get_wrapped_port() ->
P1 = erlang:open_port({spawn, "exit_drv"}, []),
case port_ix(P1) < max_ports() of
- true ->
- ?t:format("Need to wrap port index (~p)~n", [P1]),
- otp_5112_wrap_port_ix([P1]),
- P2 = erlang:open_port({spawn, "exit_drv"}, []),
- false = port_ix(P2) < max_ports(),
- P2;
- false ->
- ?t:format("Port index already wrapped (~p)~n", [P1]),
- P1
- end.
+ true ->
+ io:format("Need to wrap port index (~p)~n", [P1]),
+ otp_5112_wrap_port_ix([P1]),
+ P2 = erlang:open_port({spawn, "exit_drv"}, []),
+ false = port_ix(P2) < max_ports(),
+ P2;
+ false ->
+ io:format("Port index already wrapped (~p)~n", [P1]),
+ P1
+ end.
otp_5112_wrap_port_ix(Ports) ->
case (catch erlang:open_port({spawn, "exit_drv"}, [])) of
- Port when is_port(Port) ->
- otp_5112_wrap_port_ix([Port|Ports]);
- _ ->
- %% Port table now full; empty port table
- lists:foreach(fun (P) -> P ! {self(), close} end,
- Ports),
- ok
- end.
+ Port when is_port(Port) ->
+ otp_5112_wrap_port_ix([Port|Ports]);
+ _ ->
+ %% Port table now full; empty port table
+ lists:foreach(fun (P) -> P ! {self(), close} end,
+ Ports),
+ ok
+ end.
-otp_5119(suite) ->
- [];
-otp_5119(doc) ->
- ["Test that port index is not unnecessarily wrapped"];
+%% Test that port index is not unnecessarily wrapped
otp_5119(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(10)),
- Path = ?config(data_dir, Config),
+ Path = proplists:get_value(data_dir, Config),
ok = load_driver(Path, "exit_drv"),
PI1 = port_ix(otp_5119_fill_empty_port_tab([])),
Port2 = erlang:open_port({spawn, "exit_drv"}, []),
PI2 = port_ix(Port2),
{PortIx1, PortIx2} = case PI2 > PI1 of
- true ->
- {PI1, PI2};
- false ->
- {port_ix(otp_5119_fill_empty_port_tab([Port2])),
- port_ix(erlang:open_port({spawn, "exit_drv"}, []))}
- end,
+ true ->
+ {PI1, PI2};
+ false ->
+ {port_ix(otp_5119_fill_empty_port_tab([Port2])),
+ port_ix(erlang:open_port({spawn, "exit_drv"}, []))}
+ end,
MaxPorts = max_ports(),
- ?t:format("PortIx1 = ~p ~p~n", [PI1, PortIx1]),
- ?t:format("PortIx2 = ~p ~p~n", [PI2, PortIx2]),
- ?t:format("MaxPorts = ~p~n", [MaxPorts]),
+ io:format("PortIx1 = ~p ~p~n", [PI1, PortIx1]),
+ io:format("PortIx2 = ~p ~p~n", [PI2, PortIx2]),
+ io:format("MaxPorts = ~p~n", [MaxPorts]),
true = PortIx2 > PortIx1,
true = PortIx2 =< PortIx1 + MaxPorts,
- test_server:timetrap_cancel(Dog),
ok.
otp_5119_fill_empty_port_tab(Ports) ->
case (catch erlang:open_port({spawn, "exit_drv"}, [])) of
- Port when is_port(Port) ->
- otp_5119_fill_empty_port_tab([Port|Ports]);
- _ ->
- %% Port table now full; empty port table
- lists:foreach(fun (P) -> P ! {self(), close} end,
- Ports),
- [LastPort|_] = Ports,
- LastPort
- end.
+ Port when is_port(Port) ->
+ otp_5119_fill_empty_port_tab([Port|Ports]);
+ _ ->
+ %% Port table now full; empty port table
+ lists:foreach(fun (P) -> P ! {self(), close} end,
+ Ports),
+ [LastPort|_] = Ports,
+ LastPort
+ end.
max_ports() ->
erlang:system_info(port_limit).
port_ix(Port) when is_port(Port) ->
["#Port",_,PortIxStr] = string:tokens(erlang:port_to_list(Port),
- "<.>"),
+ "<.>"),
list_to_integer(PortIxStr).
-otp_6224(doc) -> ["Check that port command failure doesn't crash the emulator"];
-otp_6224(suite) -> [];
+%% Check that port command failure doesn't crash the emulator
otp_6224(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(10)),
- Path = ?config(data_dir, Config),
+ Path = proplists:get_value(data_dir, Config),
ok = load_driver(Path, "failure_drv"),
Go = make_ref(),
Failer = spawn(fun () ->
- receive Go -> ok end,
- Port = open_port({spawn, "failure_drv"},
- []),
- Port ! {self(), {command, "Fail, please!"}},
- otp_6224_loop()
- end),
+ receive Go -> ok end,
+ Port = open_port({spawn, "failure_drv"},
+ []),
+ Port ! {self(), {command, "Fail, please!"}},
+ otp_6224_loop()
+ end),
Mon = erlang:monitor(process, Failer),
Failer ! Go,
receive
- {'DOWN', Mon, process, Failer, Reason} ->
- case Reason of
- {driver_failed, _} -> ok;
- driver_failed -> ok;
- _ -> ?t:fail({unexpected_exit_reason,
- Reason})
- end
- end,
- test_server:timetrap_cancel(Dog),
+ {'DOWN', Mon, process, Failer, Reason} ->
+ case Reason of
+ {driver_failed, _} -> ok;
+ driver_failed -> ok;
+ _ -> ct:fail({unexpected_exit_reason,
+ Reason})
+ end
+ end,
ok.
-
+
otp_6224_loop() ->
receive _ -> ok after 0 -> ok end,
otp_6224_loop().
@@ -1782,163 +1958,160 @@ otp_6224_loop() ->
-define(EXIT_STATUS_MSB_MAX_PROCS, 64).
-define(EXIT_STATUS_MSB_MAX_PORTS, 300).
-exit_status_multi_scheduling_block(doc) -> [];
-exit_status_multi_scheduling_block(suite) -> [];
exit_status_multi_scheduling_block(Config) when is_list(Config) ->
Repeat = 3,
- case ?t:os_type() of
- {unix, _} ->
- Dog = ?t:timetrap(test_server:minutes(2*Repeat)),
- SleepSecs = 6,
- try
- lists:foreach(fun (_) ->
- exit_status_msb_test(Config,
- SleepSecs)
- end,
- lists:seq(1, Repeat))
- after
- %% Wait for the system to recover (regardless
- %% of success or not) otherwise later testcases
- %% may unnecessarily fail.
- ?t:timetrap_cancel(Dog),
- receive after SleepSecs+500 -> ok end
- end;
- _ -> {skip, "Not implemented for this OS"}
- end.
+ case os:type() of
+ {unix, _} ->
+ ct:timetrap({minutes, 2*Repeat}),
+ SleepSecs = 6,
+ try
+ lists:foreach(fun (_) ->
+ exit_status_msb_test(Config,
+ SleepSecs)
+ end,
+ lists:seq(1, Repeat))
+ after
+ %% Wait for the system to recover (regardless
+ %% of success or not) otherwise later testcases
+ %% may unnecessarily fail.
+ receive after SleepSecs+500 -> ok end
+ end;
+ _ -> {skip, "Not implemented for this OS"}
+ end.
exit_status_msb_test(Config, SleepSecs) when is_list(Config) ->
%%
%% We want to start port programs from as many schedulers as possible
%% and we want these port programs to terminate while multi-scheduling
%% is blocked.
- %%
+ %%
NoSchedsOnln = erlang:system_info(schedulers_online),
Parent = self(),
- ?t:format("SleepSecs = ~p~n", [SleepSecs]),
+ io:format("SleepSecs = ~p~n", [SleepSecs]),
PortProg = "sleep " ++ integer_to_list(SleepSecs),
- Start = erlang:monotonic_time(micro_seconds),
+ Start = erlang:monotonic_time(microsecond),
NoProcs = case NoSchedsOnln of
- NProcs when NProcs < ?EXIT_STATUS_MSB_MAX_PROCS ->
- NProcs;
- _ ->
- ?EXIT_STATUS_MSB_MAX_PROCS
- end,
+ NProcs when NProcs < ?EXIT_STATUS_MSB_MAX_PROCS ->
+ NProcs;
+ _ ->
+ ?EXIT_STATUS_MSB_MAX_PROCS
+ end,
NoPortsPerProc = case 20*NoProcs of
- TNPorts when TNPorts < ?EXIT_STATUS_MSB_MAX_PORTS -> 20;
- _ -> ?EXIT_STATUS_MSB_MAX_PORTS div NoProcs
- end,
- ?t:format("NoProcs = ~p~nNoPortsPerProc = ~p~n",
- [NoProcs, NoPortsPerProc]),
+ TNPorts when TNPorts < ?EXIT_STATUS_MSB_MAX_PORTS -> 20;
+ _ -> ?EXIT_STATUS_MSB_MAX_PORTS div NoProcs
+ end,
+ io:format("NoProcs = ~p~nNoPortsPerProc = ~p~n",
+ [NoProcs, NoPortsPerProc]),
ProcFun
- = fun () ->
- PrtSIds = lists:map(
- fun (_) ->
- erlang:yield(),
- case catch open_port({spawn, PortProg},
- [exit_status]) of
- Prt when is_port(Prt) ->
- {Prt,
- erlang:system_info(scheduler_id)};
- {'EXIT', {Err, _}} when Err == eagain;
- Err == emfile ->
- noop;
- {'EXIT', Err} when Err == eagain;
- Err == emfile ->
- noop;
- Error ->
- ?t:fail(Error)
- end
- end,
- lists:seq(1, NoPortsPerProc)),
- SIds = lists:filter(fun (noop) -> false;
- (_) -> true
- end,
- lists:map(fun (noop) -> noop;
- ({_, SId}) -> SId
- end,
- PrtSIds)),
- process_flag(scheduler, 0),
- Parent ! {self(), started, SIds},
- lists:foreach(
- fun (noop) ->
- noop;
- ({Port, _}) ->
- receive
- {Port, {exit_status, 0}} ->
- ok;
- {Port, {exit_status, Status}} when Status > 128 ->
- %% Sometimes happens when we have created
- %% too many ports.
- ok;
- {Port, {exit_status, _}} = ESMsg ->
- {Port, {exit_status, 0}} = ESMsg
- end
- end,
- PrtSIds),
- Parent ! {self(), done}
- end,
+ = fun () ->
+ PrtSIds = lists:map(
+ fun (_) ->
+ erlang:yield(),
+ case catch open_port({spawn, PortProg},
+ [exit_status]) of
+ Prt when is_port(Prt) ->
+ {Prt,
+ erlang:system_info(scheduler_id)};
+ {'EXIT', {Err, _}} when Err == eagain;
+ Err == emfile;
+ Err == enomem ->
+ noop;
+ {'EXIT', Err} when Err == eagain;
+ Err == emfile;
+ Err == enomem ->
+ noop;
+ Error ->
+ ct:fail(Error)
+ end
+ end,
+ lists:seq(1, NoPortsPerProc)),
+ SIds = lists:filter(fun (noop) -> false;
+ (_) -> true
+ end,
+ lists:map(fun (noop) -> noop;
+ ({_, SId}) -> SId
+ end,
+ PrtSIds)),
+ process_flag(scheduler, 0),
+ Parent ! {self(), started, SIds},
+ lists:foreach(
+ fun (noop) ->
+ noop;
+ ({Port, _}) ->
+ receive
+ {Port, {exit_status, 0}} ->
+ ok;
+ {Port, {exit_status, Status}} when Status > 128 ->
+ %% Sometimes happens when we have created
+ %% too many ports.
+ ok;
+ {Port, {exit_status, _}} = ESMsg ->
+ {Port, {exit_status, 0}} = ESMsg
+ end
+ end,
+ PrtSIds),
+ Parent ! {self(), done}
+ end,
Procs = lists:map(fun (N) ->
- spawn_opt(ProcFun,
- [link,
- {scheduler,
- (N rem NoSchedsOnln)+1}])
- end,
- lists:seq(1, NoProcs)),
+ spawn_opt(ProcFun,
+ [link,
+ {scheduler,
+ (N rem NoSchedsOnln)+1}])
+ end,
+ lists:seq(1, NoProcs)),
SIds = lists:map(fun (P) ->
- receive {P, started, SIds} -> SIds end
- end,
- Procs),
- StartedTime = (erlang:monotonic_time(micro_seconds) - Start)/1000000,
- ?t:format("StartedTime = ~p~n", [StartedTime]),
+ receive {P, started, SIds} -> SIds end
+ end,
+ Procs),
+ StartedTime = (erlang:monotonic_time(microsecond) - Start)/1000000,
+ io:format("StartedTime = ~p~n", [StartedTime]),
true = StartedTime < SleepSecs,
erlang:system_flag(multi_scheduling, block),
lists:foreach(fun (P) -> receive {P, done} -> ok end end, Procs),
- DoneTime = (erlang:monotonic_time(micro_seconds) - Start)/1000000,
- ?t:format("DoneTime = ~p~n", [DoneTime]),
+ DoneTime = (erlang:monotonic_time(microsecond) - Start)/1000000,
+ io:format("DoneTime = ~p~n", [DoneTime]),
true = DoneTime > SleepSecs,
ok = verify_multi_scheduling_blocked(),
erlang:system_flag(multi_scheduling, unblock),
case {length(lists:usort(lists:flatten(SIds))), NoSchedsOnln} of
- {N, N} ->
- ok;
- {N, M} ->
- ?t:fail("Failed to create ports on all"
- ++ integer_to_list(M) ++ " available"
- "schedulers. Only created ports on "
- ++ integer_to_list(N) ++ " schedulers.")
- end.
+ {N, N} ->
+ ok;
+ {N, M} ->
+ ct:fail("Failed to create ports on all ~w available"
+ "schedulers. Only created ports on ~w schedulers.", [M, N])
+ end.
save_sid(SIds) ->
SId = erlang:system_info(scheduler_id),
case lists:member(SId, SIds) of
- true -> SIds;
- false -> [SId|SIds]
+ true -> SIds;
+ false -> [SId|SIds]
end.
sid_proc(SIds) ->
NewSIds = save_sid(SIds),
receive
- {From, want_sids} ->
- From ! {self(), sids, NewSIds}
+ {From, want_sids} ->
+ From ! {self(), sids, NewSIds}
after 0 ->
- sid_proc(NewSIds)
+ sid_proc(NewSIds)
end.
verify_multi_scheduling_blocked() ->
Procs = lists:map(fun (_) ->
- spawn_link(fun () -> sid_proc([]) end)
- end,
- lists:seq(1, 3*erlang:system_info(schedulers_online))),
+ spawn_link(fun () -> sid_proc([]) end)
+ end,
+ lists:seq(1, 3*erlang:system_info(schedulers_online))),
receive after 1000 -> ok end,
SIds = lists:map(fun (P) ->
- P ! {self(), want_sids},
- receive {P, sids, PSIds} -> PSIds end
- end,
- Procs),
+ P ! {self(), want_sids},
+ receive {P, sids, PSIds} -> PSIds end
+ end,
+ Procs),
1 = length(lists:usort(lists:flatten(SIds))),
ok.
-
-
+
+
%%% Pinging functions.
stream_ping(Config, Size, CmdLine, Options) ->
@@ -1947,10 +2120,10 @@ stream_ping(Config, Size, CmdLine, Options) ->
ping(Config, Sizes, HSize, CmdLine, Options) ->
Actions = lists:map(fun(Size) ->
- [$p|Packet] = random_packet(Size, "ping"),
- {[$p|Packet], [[$P|Packet]]}
- end,
- Sizes),
+ [$p|Packet] = random_packet(Size, "ping"),
+ {[$p|Packet], [[$P|Packet]]}
+ end,
+ Sizes),
port_expect(Config, Actions, HSize, CmdLine, Options).
%% expect_input(Sizes, HSize, CmdLine, Options)
@@ -1972,7 +2145,7 @@ expect_input1(Config, [Size|Rest], Params, Expect, ReplyCommand) ->
expect_input1(Config, [], {HSize, CmdLine0, Options}, Expect, ReplyCommand) ->
CmdLine = build_cmd_line(CmdLine0, ReplyCommand, []),
port_expect(Config, [{false, lists:reverse(Expect)}],
- HSize, CmdLine, Options).
+ HSize, CmdLine, Options).
build_cmd_line(FixedCmdLine, [Cmd|Rest], []) ->
build_cmd_line(FixedCmdLine, Rest, [Cmd]);
@@ -1995,15 +2168,15 @@ build_cmd_line(FixedCmdLine, [], Result) ->
%% Returns the port.
port_expect(Config, Actions, HSize, CmdLine, Options0) ->
-% io:format("port_expect(~p, ~p, ~p, ~p)",
-% [Actions, HSize, CmdLine, Options0]),
+ % io:format("port_expect(~p, ~p, ~p, ~p)",
+ % [Actions, HSize, CmdLine, Options0]),
PortTest = port_test(Config),
Cmd = lists:concat([PortTest, " -h", HSize, " ", CmdLine]),
PortType =
- case HSize of
- 0 -> stream;
- _ -> {packet, HSize}
- end,
+ case HSize of
+ 0 -> stream;
+ _ -> {packet, HSize}
+ end,
Options = [PortType|Options0],
io:format("open_port({spawn, ~p}, ~p)", [Cmd, Options]),
Port = open_port({spawn, Cmd}, Options),
@@ -2014,11 +2187,11 @@ port_expect(Port, [{Send, Expects}|Rest], Options) when is_list(Expects) ->
port_send(Port, Send),
IsBinaryPort = lists:member(binary, Options),
Receiver =
- case {lists:member(stream, Options), line_option(Options)} of
- {false, _} -> fun receive_all/2;
- {true,false} -> fun stream_receive_all/2;
- {_, true} -> fun receive_all/2
- end,
+ case {lists:member(stream, Options), line_option(Options)} of
+ {false, _} -> fun receive_all/2;
+ {true,false} -> fun stream_receive_all/2;
+ {_, true} -> fun receive_all/2
+ end,
Receiver(Port, maybe_to_binary(Expects, IsBinaryPort)),
port_expect(Port, Rest, Options);
port_expect(_, [], _) ->
@@ -2046,34 +2219,34 @@ maybe_to_binary(Expects, false) ->
port_send(_Port, false) -> ok;
port_send(Port, Send) when is_list(Send) ->
-% io:format("port_send(~p, ~p)", [Port, Send]),
+ % io:format("port_send(~p, ~p)", [Port, Send]),
Port ! {self(), {command, Send}}.
receive_all(Port, [Expect|Rest]) ->
-% io:format("receive_all(~p, [~p|Rest])", [Port, Expect]),
+ % io:format("receive_all(~p, [~p|Rest])", [Port, Expect]),
receive
- {Port, {data, Expect}} ->
- io:format("Received ~s", [format(Expect)]),
- ok;
- {Port, {data, Other}} ->
- io:format("Received ~s; expected ~s",
- [format(Other), format(Expect)]),
- test_server:fail(bad_message);
- Other ->
- %% (We're not yet prepared for receiving both 'eol' and
- %% 'exit_status'; remember that they may appear in any order.)
- case {Expect, Rest, Other} of
- {eof, [], {Port, eof}} ->
- io:format("Received soft EOF.",[]),
- ok;
- {{exit_status, S}, [], {Port, {exit_status, S}}} ->
- io:format("Received exit status ~p.",[S]),
- ok;
- _ ->
-%%% io:format("Unexpected message: ~s", [format(Other)]),
- io:format("Unexpected message: ~w", [Other]),
- test_server:fail(unexpected_message)
- end
+ {Port, {data, Expect}} ->
+ io:format("Received ~s", [format(Expect)]),
+ ok;
+ {Port, {data, Other}} ->
+ io:format("Received ~s; expected ~s",
+ [format(Other), format(Expect)]),
+ ct:fail(bad_message);
+ Other ->
+ %% (We're not yet prepared for receiving both 'eol' and
+ %% 'exit_status'; remember that they may appear in any order.)
+ case {Expect, Rest, Other} of
+ {eof, [], {Port, eof}} ->
+ io:format("Received soft EOF.",[]),
+ ok;
+ {{exit_status, S}, [], {Port, {exit_status, S}}} ->
+ io:format("Received exit status ~p.",[S]),
+ ok;
+ _ ->
+ %%% io:format("Unexpected message: ~s", [format(Other)]),
+ io:format("Unexpected message: ~w", [Other]),
+ ct:fail(unexpected_message)
+ end
end,
receive_all(Port, Rest);
receive_all(_Port, []) ->
@@ -2088,30 +2261,30 @@ stream_receive_all1(_Port, []) ->
ok;
stream_receive_all1(Port, Expect) ->
receive
- {Port, {data, Data}} ->
- Remaining = compare(Data, Expect),
- stream_receive_all1(Port, Remaining);
- Other ->
- test_server:fail({bad_message, Other})
+ {Port, {data, Data}} ->
+ Remaining = compare(Data, Expect),
+ stream_receive_all1(Port, Remaining);
+ Other ->
+ ct:fail({bad_message, Other})
end.
compare(B1, B2) when is_binary(B1), is_binary(B2), byte_size(B1) =< byte_size(B2) ->
case split_binary(B2, size(B1)) of
- {B1,Remaining} ->
- Remaining;
- _Other ->
- test_server:fail(nomatch)
+ {B1,Remaining} ->
+ Remaining;
+ _Other ->
+ ct:fail(nomatch)
end;
compare(B1, B2) when is_binary(B1), is_binary(B2) ->
- test_server:fail(too_much_data);
+ ct:fail(too_much_data);
compare([X|Rest1], [X|Rest2]) ->
compare(Rest1, Rest2);
compare([_|_], [_|_]) ->
- test_server:fail(nomatch);
+ ct:fail(nomatch);
compare([], Remaining) ->
Remaining;
compare(_Data, []) ->
- test_server:fail(too_much_data).
+ ct:fail(too_much_data).
maybe_to_list(Bin) when is_binary(Bin) ->
binary_to_list(Bin);
@@ -2119,13 +2292,13 @@ maybe_to_list(List) ->
List.
format({Eol,List}) ->
- io_lib:format("tuple<~w,~s>",[Eol, maybe_to_list(List)]);
+ io_lib:format("tuple<~w,~w>",[Eol, maybe_to_list(List)]);
format(List) when is_list(List) ->
case list_at_least(50, List) of
- true ->
- io_lib:format("\"~-50s...\"", [List]);
- false ->
- io_lib:format("~p", [List])
+ true ->
+ io_lib:format("\"~-50s...\"", [List]);
+ false ->
+ io_lib:format("~p", [List])
end;
format(Bin) when is_binary(Bin), size(Bin) >= 50 ->
io_lib:format("binary<~-50s...>", [binary_to_list(Bin, 1, 50)]);
@@ -2152,17 +2325,17 @@ build_packet(0, Result, _NextChar) ->
lists:reverse(Result);
build_packet(Left, Result, NextChar0) ->
NextChar =
- if
- NextChar0 >= 126 ->
- 33;
- true ->
- NextChar0+1
- end,
+ if
+ NextChar0 >= 126 ->
+ 33;
+ true ->
+ NextChar0+1
+ end,
build_packet(Left-1, [NextChar0|Result], NextChar).
sizes() ->
[10, 13, 64, 127, 128, 255, 256, 1023, 1024,
- 32767, 32768, 65535, 65536].
+ 32767, 32768, 65535, 65536].
sizes(Header_Size) ->
sizes(Header_Size, sizes(), []).
@@ -2183,15 +2356,14 @@ random_char(Chars) ->
lists:nth(uniform(length(Chars)), Chars).
uniform(N) ->
- case get(random_seed) of
- undefined ->
- {X, Y, Z} = Seed = time(),
- io:format("Random seed = ~p\n",[Seed]),
- random:seed(X, Y, Z);
- _ ->
- ok
+ case rand:export_seed() of
+ undefined ->
+ rand:seed(exsplus),
+ io:format("Random seed = ~p\n", [rand:export_seed()]);
+ _ ->
+ ok
end,
- random:uniform(N).
+ rand:uniform(N).
fun_spawn(Fun) ->
fun_spawn(Fun, []).
@@ -2200,13 +2372,11 @@ fun_spawn(Fun, Args) ->
spawn_link(erlang, apply, [Fun, Args]).
port_test(Config) when is_list(Config) ->
- filename:join(?config(data_dir, Config), "port_test").
-
+ filename:join(proplists:get_value(data_dir, Config), "port_test").
-ports(doc) -> "Test that erlang:ports/0 returns a consistent snapshot of ports";
-ports(suite) -> [];
+%% Test that erlang:ports/0 returns a consistent snapshot of ports
ports(Config) when is_list(Config) ->
- Path = ?config(data_dir, Config),
+ Path = proplists:get_value(data_dir, Config),
ok = load_driver(Path, "exit_drv"),
receive after 1000 -> ok end, % Wait for other ports to stabilize
@@ -2226,14 +2396,14 @@ ports_snapshots(0, _, _) ->
ok;
ports_snapshots(Iter, TrafficPid, OtherPorts) ->
- TrafficPid ! start,
+ TrafficPid ! start,
receive after 1 -> ok end,
Snapshot = erlang:ports(),
TrafficPid ! {self(), stop},
receive {TrafficPid, EventList, TrafficPorts} -> ok end,
-
+
%%io:format("Snapshot=~p\n", [Snapshot]),
ports_verify(Snapshot, OtherPorts ++ TrafficPorts, EventList),
@@ -2245,78 +2415,77 @@ ports_traffic(MaxPorts) ->
ports_traffic_stopped(MaxPorts, {PortList, PortCnt}) ->
receive
- start ->
- %%io:format("Traffic started in ~p\n",[self()]),
- ports_traffic_started(MaxPorts, {PortList, PortCnt}, []);
- {Pid,die} ->
- lists:foreach(fun(Port)-> erlang:port_close(Port) end,
- PortList),
- Pid ! {self(),dead}
+ start ->
+ %%io:format("Traffic started in ~p\n",[self()]),
+ ports_traffic_started(MaxPorts, {PortList, PortCnt}, []);
+ {Pid,die} ->
+ lists:foreach(fun(Port)-> erlang:port_close(Port) end,
+ PortList),
+ Pid ! {self(),dead}
end.
ports_traffic_started(MaxPorts, {PortList, PortCnt}, EventList) ->
- receive
- {Pid, stop} ->
- %%io:format("Traffic stopped in ~p\n",[self()]),
- Pid ! {self(), EventList, PortList},
- ports_traffic_stopped(MaxPorts, {PortList, PortCnt})
+ receive
+ {Pid, stop} ->
+ %%io:format("Traffic stopped in ~p\n",[self()]),
+ Pid ! {self(), EventList, PortList},
+ ports_traffic_stopped(MaxPorts, {PortList, PortCnt})
after 0 ->
- ports_traffic_do(MaxPorts, {PortList, PortCnt}, EventList)
+ ports_traffic_do(MaxPorts, {PortList, PortCnt}, EventList)
end.
ports_traffic_do(MaxPorts, {PortList, PortCnt}, EventList) ->
N = uniform(MaxPorts),
case N > PortCnt of
- true -> % Open port
- P = open_port({spawn, "exit_drv"}, []),
- %%io:format("Created port ~p\n",[P]),
- ports_traffic_started(MaxPorts, {[P|PortList], PortCnt+1},
- [{open,P}|EventList]);
-
- false -> % Close port
- P = lists:nth(N, PortList),
- %%io:format("Close port ~p\n",[P]),
- true = erlang:port_close(P),
- ports_traffic_started(MaxPorts, {lists:delete(P,PortList), PortCnt-1},
- [{close,P}|EventList])
+ true -> % Open port
+ P = open_port({spawn, "exit_drv"}, []),
+ %%io:format("Created port ~p\n",[P]),
+ ports_traffic_started(MaxPorts, {[P|PortList], PortCnt+1},
+ [{open,P}|EventList]);
+
+ false -> % Close port
+ P = lists:nth(N, PortList),
+ %%io:format("Close port ~p\n",[P]),
+ true = erlang:port_close(P),
+ ports_traffic_started(MaxPorts, {lists:delete(P,PortList), PortCnt-1},
+ [{close,P}|EventList])
end.
-ports_verify(Ports, PortsAfter, EventList) ->
+ports_verify(Ports, PortsAfter, EventList) ->
%%io:format("Candidate=~p\nEvents=~p\n", [PortsAfter, EventList]),
case lists:sort(Ports) =:= lists:sort(PortsAfter) of
- true ->
- io:format("Snapshot of ~p ports verified ok.\n",[length(Ports)]),
- ok;
- false ->
- %% Note that we track the event list "backwards", undoing open/close:
- case EventList of
- [{open,P} | Tail] ->
- ports_verify(Ports, lists:delete(P,PortsAfter), Tail);
-
- [{close,P} | Tail] ->
- ports_verify(Ports, [P | PortsAfter], Tail);
-
- [] ->
- test_server:fail("Inconsistent snapshot from erlang:ports()")
- end
+ true ->
+ io:format("Snapshot of ~p ports verified ok.\n",[length(Ports)]),
+ ok;
+ false ->
+ %% Note that we track the event list "backwards", undoing open/close:
+ case EventList of
+ [{open,P} | Tail] ->
+ ports_verify(Ports, lists:delete(P,PortsAfter), Tail);
+
+ [{close,P} | Tail] ->
+ ports_verify(Ports, [P | PortsAfter], Tail);
+
+ [] ->
+ ct:fail("Inconsistent snapshot from erlang:ports()")
+ end
end.
load_driver(Dir, Driver) ->
case erl_ddll:load_driver(Dir, Driver) of
- ok -> ok;
- {error, Error} = Res ->
- io:format("~s\n", [erl_ddll:format_error(Error)]),
- Res
+ ok -> ok;
+ {error, Error} = Res ->
+ io:format("~s\n", [erl_ddll:format_error(Error)]),
+ Res
end.
-close_deaf_port(doc) -> ["Send data to port program that does not read it, then close port."
- "Primary targeting Windows to test threaded_handle_closer in sys.c"];
-close_deaf_port(suite) -> [];
+%% Send data to port program that does not read it, then close port.
+%% Primary targeting Windows to test threaded_handle_closer in sys.c
close_deaf_port(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(100)),
- DataDir = ?config(data_dir, Config),
+ ct:timetrap({minutes, 2}),
+ DataDir = proplists:get_value(data_dir, Config),
DeadPort = os:find_executable("dead_port", DataDir),
Port = open_port({spawn,DeadPort++" 60"},[]),
erlang:port_command(Port,"Hello, can you hear me!?!?"),
@@ -2325,83 +2494,329 @@ close_deaf_port(Config) when is_list(Config) ->
Res = close_deaf_port_1(0, DeadPort),
io:format("Waiting for OS procs to terminate...\n"),
receive after 5*1000 -> ok end,
- test_server:timetrap_cancel(Dog),
Res.
close_deaf_port_1(200, _) ->
ok;
close_deaf_port_1(N, Cmd) ->
- Timeout = integer_to_list(random:uniform(5*1000)),
+ Timeout = integer_to_list(rand:uniform(5*1000)),
try open_port({spawn_executable,Cmd},[{args,[Timeout]}]) of
- Port ->
- erlang:port_command(Port,"Hello, can you hear me!?!?"),
- port_close(Port),
- close_deaf_port_1(N+1, Cmd)
+ Port ->
+ erlang:port_command(Port,"Hello, can you hear me!?!?"),
+ port_close(Port),
+ close_deaf_port_1(N+1, Cmd)
catch
- _:eagain ->
- {comment, "Could not spawn more than " ++ integer_to_list(N) ++ " OS processes."}
+ _:eagain ->
+ {comment, "Could not spawn more than " ++ integer_to_list(N) ++ " OS processes."}
end.
%% Test undocumented port_set_data/2 and port_get_data/1
%% Hammer from multiple processes a while
%% and then abrubtly close the port (OTP-12208).
port_setget_data(Config) when is_list(Config) ->
- ok = load_driver(?config(data_dir, Config), "echo_drv"),
+ ok = load_driver(proplists:get_value(data_dir, Config), "echo_drv"),
Port = erlang:open_port({spawn_driver, "echo_drv"}, []),
NSched = erlang:system_info(schedulers_online),
HeapData = {1,2,3,<<"A heap binary">>,fun()->"This is fun"end,
- list_to_binary(lists:seq(1,100))},
+ list_to_binary(lists:seq(1,100))},
PRs = lists:map(fun(I) ->
- spawn_opt(fun() -> port_setget_data_hammer(Port,HeapData,false,1) end,
- [monitor, {scheduler, I rem NSched}])
- end,
- lists:seq(1,10)),
+ spawn_opt(fun() -> port_setget_data_hammer(Port,HeapData,false,1) end,
+ [monitor, {scheduler, I rem NSched}])
+ end,
+ lists:seq(1,10)),
receive after 100 -> ok end,
Papa = self(),
lists:foreach(fun({Pid,_}) -> Pid ! {Papa,prepare_for_close} end, PRs),
lists:foreach(fun({Pid,_}) ->
- receive {Pid,prepare_for_close} -> ok end
- end,
- PRs),
+ receive {Pid,prepare_for_close} -> ok end
+ end,
+ PRs),
port_close(Port),
lists:foreach(fun({Pid,Ref}) ->
- receive {'DOWN', Ref, process, Pid, normal} -> ok end
- end,
- PRs),
+ receive {'DOWN', Ref, process, Pid, normal} -> ok end
+ end,
+ PRs),
ok.
port_setget_data_hammer(Port, HeapData, IsSet0, N) ->
- Rand = random:uniform(3),
+ Rand = rand:uniform(3),
IsSet1 = try case Rand of
- 1 -> true = erlang:port_set_data(Port, atom), true;
- 2 -> true = erlang:port_set_data(Port, HeapData), true;
- 3 -> case erlang:port_get_data(Port) of
- atom -> true;
- HeapData -> true;
- undefined -> false=IsSet0
- end
- end
- catch
- error:badarg ->
- true = get(prepare_for_close),
- io:format("~p did ~p rounds before port closed\n", [self(), N]),
- exit(normal)
- end,
+ 1 -> true = erlang:port_set_data(Port, atom), true;
+ 2 -> true = erlang:port_set_data(Port, HeapData), true;
+ 3 -> case erlang:port_get_data(Port) of
+ atom -> true;
+ HeapData -> true;
+ undefined -> false=IsSet0
+ end
+ end
+ catch
+ error:badarg ->
+ true = get(prepare_for_close),
+ io:format("~p did ~p rounds before port closed\n", [self(), N]),
+ exit(normal)
+ end,
receive {Papa, prepare_for_close} ->
- put(prepare_for_close, true),
- Papa ! {self(),prepare_for_close}
+ put(prepare_for_close, true),
+ Papa ! {self(),prepare_for_close}
after 0 ->
- ok
+ ok
end,
port_setget_data_hammer(Port, HeapData, IsSet1, N+1).
wait_until(Fun) ->
case catch Fun() of
- true ->
- ok;
- _ ->
- receive after 100 -> ok end,
- wait_until(Fun)
+ true ->
+ ok;
+ _ ->
+ receive after 100 -> ok end,
+ wait_until(Fun)
end.
+
+%% Attempt to monitor pid as port, and port as pid
+mon_port_invalid_type(_Config) ->
+ Port = hd(erlang:ports()),
+ ?assertError(badarg, erlang:monitor(port, self())),
+ ?assertError(badarg, erlang:monitor(process, Port)),
+ ok.
+
+%% With local port
+mon_port_local(Config) ->
+ Port1 = create_port(Config, ["-h1", "-q"]), % will close after we send 1 byte
+ Ref1 = erlang:monitor(port, Port1),
+ ?assertMatch({proc_monitors, true, port_monitored_by, true},
+ port_is_monitored(self(), Port1)),
+ Port1 ! {self(), {command, <<"1">>}}, % port test will close self immediately
+ receive ExitP1 -> ?assertMatch({'DOWN', Ref1, port, Port1, _}, ExitP1)
+ after 1000 -> ?assert(false) end,
+ ?assertMatch({proc_monitors, false, port_monitored_by, false},
+ port_is_monitored(self(), Port1)),
+
+ %% Trying to re-monitor a port which exists but is not healthy will
+ %% succeed but then will immediately send DOWN
+ Ref2 = erlang:monitor(port, Port1),
+ receive ExitP2 -> ?assertMatch({'DOWN', Ref2, port, Port1, _}, ExitP2)
+ after 1000 -> ?assert(false) end,
+ ok.
+
+%% With remote port on remote node (should fail)
+mon_port_remote_on_remote(_Config) ->
+ Port3 = binary_to_term(<<131, 102, % Ext term format: PORT_EXT
+ 100, 0, 13, "fgsfds@fgsfds", % Node :: ATOM_EXT
+ 1:32/big, % Id
+ 0>>), % Creation
+ ?assertError(badarg, erlang:monitor(port, Port3)),
+ ok.
+
+%% Remote port belongs to this node and does not exist
+%% Port4 produces #Port<0.167772160> which should not exist in a test run
+mon_port_bad_remote_on_local(_Config) ->
+ Port4 = binary_to_term(<<131, 102, % Ext term format: PORT_EXT
+ 100, 0, 13, "nonode@nohost", % Node
+ 167772160:32/big, % Id
+ 0>>), % Creation
+ ?assertError(badarg, erlang:monitor(port, Port4)),
+ ok.
+
+%% Monitor owner (origin) dies before port is closed
+mon_port_origin_dies(Config) ->
+ Port5 = create_port(Config, ["-h1", "-q"]), % will close after we send 1 byte
+ Self5 = self(),
+ Proc5 = spawn(fun() ->
+ Self5 ! test5_started,
+ erlang:monitor(port, Port5),
+ receive stop -> ok end
+ end),
+ erlang:monitor(process, Proc5), % we want to sync with its death
+ receive test5_started -> ok
+ after 1000 -> ?assert(false) end,
+ ?assertMatch({proc_monitors, true, port_monitored_by, true},
+ port_is_monitored(Proc5, Port5)),
+ Proc5 ! stop,
+ % receive from monitor (removing race condition)
+ receive ExitP5 -> ?assertMatch({'DOWN', _, process, Proc5, _}, ExitP5)
+ after 1000 -> ?assert(false) end,
+ ?assertMatch({proc_monitors, false, port_monitored_by, false},
+ port_is_monitored(Proc5, Port5)),
+ Port5 ! {self(), {command, <<"1">>}}, % make port quit
+ ok.
+
+%% Port and Monitor owner dies before port is closed
+%% This testcase checks for a regression memory leak in erts
+%% when the controlling and monitoring process is the same process
+%% and the process dies
+mon_port_owner_dies(Config) ->
+ Self = self(),
+ Proc = spawn(fun() ->
+ Port = create_port(Config, ["-h1", "-q"]),
+ Self ! {test_started, Port},
+ erlang:monitor(port, Port),
+ receive stop -> ok end
+ end),
+ erlang:monitor(process, Proc), % we want to sync with its death
+ Port = receive {test_started,P} -> P
+ after 1000 -> ?assert(false) end,
+ ?assertMatch({proc_monitors, true, port_monitored_by, true},
+ port_is_monitored(Proc, Port)),
+ Proc ! stop,
+ %% receive from monitor
+ receive ExitP5 -> ?assertMatch({'DOWN', _, process, Proc, _}, ExitP5)
+ after 1000 -> ?assert(false) end,
+ ok.
+
+%% Monitor a named port
+mon_port_named(Config) ->
+ Name6 = test_port6,
+ Port6 = create_port(Config, ["-h1", "-q"]), % will close after we send 1 byte
+ erlang:register(Name6, Port6),
+ erlang:monitor(port, Name6),
+ ?assertMatch({proc_monitors, true, port_monitored_by, true},
+ port_is_monitored(self(), Name6)),
+ Port6 ! {self(), {command, <<"1">>}}, % port test will close self immediately
+ receive ExitP6 -> ?assertMatch({'DOWN', _, port, {Name6, _}, _}, ExitP6)
+ after 1000 -> ?assert(false) end,
+ ?assertMatch({proc_monitors, false, port_monitored_by, false},
+ port_is_monitored(self(), Name6)),
+ ok.
+
+%% Named does not exist: Should succeed but immediately send 'DOWN'
+mon_port_bad_named(_Config) ->
+ Name7 = test_port7,
+ erlang:monitor(port, Name7),
+ receive {'DOWN', _, port, {Name7, _}, noproc} -> ok
+ after 1000 -> ?assert(false) end,
+ ok.
+
+%% Monitor a pid and demonitor by ref
+mon_port_pid_demonitor(Config) ->
+ Port8 = create_port(Config, ["-h1", "-q"]), % will close after we send 1 byte
+ Ref8 = erlang:monitor(port, Port8),
+ ?assertMatch({proc_monitors, true, port_monitored_by, true},
+ port_is_monitored(self(), Port8)),
+ erlang:demonitor(Ref8),
+ ?assertMatch({proc_monitors, false, port_monitored_by, false},
+ port_is_monitored(self(), Port8)),
+ Port8 ! {self(), {command, <<"1">>}}, % port test will close self immediately
+ ok.
+
+%% Monitor by name and demonitor by ref
+mon_port_name_demonitor(Config) ->
+ Name9 = test_port9,
+ Port9 = create_port(Config, ["-h1", "-q"]), % will close after we send 1 byte
+ erlang:register(Name9, Port9),
+ Ref9 = erlang:monitor(port, Name9),
+ ?assertMatch({proc_monitors, true, port_monitored_by, true},
+ port_is_monitored(self(), Name9)),
+ erlang:demonitor(Ref9),
+ ?assertMatch({proc_monitors, false, port_monitored_by, false},
+ port_is_monitored(self(), Name9)),
+ Port9 ! {self(), {command, <<"1">>}}, % port test will close self immediately
+ ok.
+
+%% 1. Spawn a port which will sleep 3 seconds
+%% 2. Port driver and dies horribly (via C driver_failure call). This should
+%% mark port as exiting or something.
+%% 3. While the command happens, a monitor is requested on the port
+mon_port_driver_die(Config) ->
+ erlang:process_flag(scheduler, 1),
+
+ Path = proplists:get_value(data_dir, Config),
+ ok = load_driver(Path, "sleep_failure_drv"),
+ Port = open_port({spawn, "sleep_failure_drv"}, []),
+
+ Self = self(),
+ erlang:spawn_opt(fun() ->
+ timer:sleep(250),
+ Ref = erlang:monitor(port, Port),
+ %% Now check that msg actually arrives
+ receive
+ {'DOWN', Ref, _Port2, _, _} = M -> Self ! M
+ after 3000 -> Self ! no_down_message
+ end
+ end,[{scheduler, 2}]),
+ Port ! {self(), {command, "Fail, please!"}},
+ receive
+ A when is_atom(A) -> ?assertEqual(A, 'A_should_be_printed');
+ {'DOWN', _R, port, Port, noproc} -> ok;
+ {'DOWN', _R, _P, _, _} = M -> ct:fail({got_wrong_down,M})
+ after 5000 -> ?assert(false)
+ end,
+ ok.
+
+
+%% 1. Spawn a port which will sleep 3 seconds
+%% 2. Monitor port
+%% 3. Port driver and dies horribly (via C driver_failure call). This should
+%% mark port as exiting or something.
+%% 4. While the command happens, a demonitor is requested on the port
+mon_port_driver_die_demonitor(Config) ->
+ erlang:process_flag(scheduler, 1),
+
+ Path = proplists:get_value(data_dir, Config),
+ ok = load_driver(Path, "sleep_failure_drv"),
+ Port = open_port({spawn, "sleep_failure_drv"}, []),
+
+ Self = self(),
+ erlang:spawn_opt(
+ fun() ->
+ Ref = erlang:monitor(port, Port),
+ Self ! Ref,
+ timer:sleep(250),
+ erlang:demonitor(Ref),
+ %% Now check that msg still arrives,
+ %% the demon should have arrived after
+ %% the port exited
+ receive
+ {'DOWN', Ref, _Port2, _, _} = M -> Self ! M
+ after 3000 -> Self ! no_down_message
+ end
+ end,[{scheduler, 2}]),
+ Ref = receive R -> R end,
+ Port ! {self(), {command, "Fail, please!"}},
+ receive
+ {'DOWN', Ref, port, Port, normal} -> ok;
+ {'DOWN', _R, _P, _, _} = M -> ct:fail({got_wrong_down,M})
+ after 5000 -> ?assert(false)
+ end,
+ ok.
+
+%% @doc Makes a controllable port for testing. Underlying mechanism of this
+%% port is not important, only important is our ability to close/kill it or
+%% have it monitored.
+create_port(Config, Args) ->
+ DataDir = ?config(data_dir, Config),
+ %% Borrow port test utility from port SUITE
+ Program = filename:join([DataDir, "port_test"]),
+ erlang:open_port({spawn_executable, Program}, [{args, Args}]).
+
+%% @doc Checks if process Pid exists, and if so, if its monitoring (or not)
+%% the Port (or if port doesn't exist, we assume answer is no).
+port_is_monitored(Pid, Port) when is_pid(Pid), is_port(Port) ->
+ %% Variant for when port is a port id (port())
+ A = case erlang:process_info(Pid, monitors) of
+ undefined -> false;
+ {monitors, ProcMTargets} -> lists:member({port, Port}, ProcMTargets)
+ end,
+ B = case erlang:port_info(Port, monitored_by) of
+ undefined -> false;
+ {monitored_by, PortMonitors} -> lists:member(Pid, PortMonitors)
+ end,
+ {proc_monitors, A, port_monitored_by, B};
+port_is_monitored(Pid, PortName) when is_pid(Pid), is_atom(PortName) ->
+ %% Variant for when port is an atom
+ A = case erlang:process_info(Pid, monitors) of
+ undefined -> false;
+ {monitors, ProcMTargets} ->
+ lists:member({port, {PortName, node()}}, ProcMTargets)
+ end,
+ B = case erlang:whereis(PortName) of
+ undefined -> false; % name is not registered or is dead
+ PortId ->
+ case erlang:port_info(PortId, monitored_by) of
+ undefined -> false; % is dead
+ {monitored_by, PortMonitors} ->
+ lists:member(Pid, PortMonitors)
+ end
+ end,
+ {proc_monitors, A, port_monitored_by, B}.
diff --git a/erts/emulator/test/port_SUITE_data/Makefile.src b/erts/emulator/test/port_SUITE_data/Makefile.src
index ff822ae720..3a343e6d17 100644
--- a/erts/emulator/test/port_SUITE_data/Makefile.src
+++ b/erts/emulator/test/port_SUITE_data/Makefile.src
@@ -4,7 +4,7 @@ CFLAGS = @CFLAGS@ -I@erl_include@ @DEFS@
CROSSLDFLAGS = @CROSSLDFLAGS@
PROGS = port_test@exe@ echo_args@exe@ dead_port@exe@
-DRIVERS = echo_drv@dll@ exit_drv@dll@ failure_drv@dll@
+DRIVERS = echo_drv@dll@ exit_drv@dll@ failure_drv@dll@ sleep_failure_drv@dll@
all: $(PROGS) $(DRIVERS) port_test.@EMULATOR@
@@ -20,6 +20,12 @@ echo_args@exe@: echo_args@obj@
echo_args@obj@: echo_args.c
$(CC) -c -o echo_args@obj@ $(CFLAGS) echo_args.c
+dead_port@exe@: dead_port@obj@
+ $(LD) $(CROSSLDFLAGS) -o dead_port dead_port@obj@ @LIBS@
+
+dead_port@obj@: dead_port.c
+ $(CC) -c -o dead_port@obj@ $(CFLAGS) dead_port.c
+
port_test.@EMULATOR@: port_test.erl
@erl_name@ -compile port_test
diff --git a/erts/emulator/test/port_SUITE_data/dead_port.c b/erts/emulator/test/port_SUITE_data/dead_port.c
index c859dbc402..26f09f33c7 100644
--- a/erts/emulator/test/port_SUITE_data/dead_port.c
+++ b/erts/emulator/test/port_SUITE_data/dead_port.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/port_SUITE_data/port_test.c b/erts/emulator/test/port_SUITE_data/port_test.c
index 7abefab2e3..e199a0fc13 100644
--- a/erts/emulator/test/port_SUITE_data/port_test.c
+++ b/erts/emulator/test/port_SUITE_data/port_test.c
@@ -10,9 +10,11 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
+#include <ctype.h>
#ifndef __WIN32__
#include <unistd.h>
+#include <limits.h>
#include <sys/time.h>
@@ -32,7 +34,7 @@
exit(1); \
}
-#define MAIN(argc, argv) main(argc, argv)
+#define ASSERT(e) ((void) ((e) ? 1 : abort()))
extern int errno;
@@ -48,6 +50,7 @@ typedef struct {
* after reading the header for a packet
* before reading the rest.
*/
+ int fd_count; /* Count the number of open fds */
int break_mode; /* If set, this program will close standard
* input, which should case broken pipe
* error in the writer.
@@ -103,11 +106,9 @@ int err;
#endif
-MAIN(argc, argv)
-int argc;
-char *argv[];
+int main(int argc, char *argv[])
{
- int ret;
+ int ret, fd_count;
if((port_data = (PORT_TEST_DATA *) malloc(sizeof(PORT_TEST_DATA))) == NULL) {
fprintf(stderr, "Couldn't malloc for port_data");
exit(1);
@@ -115,6 +116,7 @@ char *argv[];
port_data->header_size = 0;
port_data->io_buf_size = 0;
port_data->delay_mode = 0;
+ port_data->fd_count = 0;
port_data->break_mode = 0;
port_data->quit_mode = 0;
port_data->slow_writes = 0;
@@ -144,6 +146,9 @@ char *argv[];
case 'e':
port_data->fd_to_erl = 2;
break;
+ case 'f':
+ port_data->fd_count = 1;
+ break;
case 'h': /* Header size for packets. */
switch (argv[1][2]) {
case '0': port_data->header_size = 0; break;
@@ -189,18 +194,31 @@ char *argv[];
/* XXX Add error printout here */
}
+ if (port_data->fd_count) {
+#ifdef __WIN32__
+ DWORD handles;
+ GetProcessHandleCount(GetCurrentProcess(), &handles);
+ fd_count = handles;
+#else
+ int i;
+ for (i = 0, fd_count = 0; i < 1024; i++)
+ if (fcntl(i, F_GETFD) >= 0) {
+ fd_count++;
+ }
+#endif
+ }
+
+ if (port_data->output_file)
+ replace_stdout(port_data->output_file);
+
+ if (port_data->fd_count)
+ reply(&fd_count, sizeof(fd_count));
+
if (port_data->no_packet_loop){
free(port_data);
exit(0);
}
- /*
- * If an output file was given, let it replace standard output.
- */
-
- if (port_data->output_file)
- replace_stdout(port_data->output_file);
-
ret = packet_loop();
if(port_data->io_buf_size > 0)
free(port_data->io_buf);
@@ -358,9 +376,11 @@ write_reply(buf, size)
int size; /* Size of buffer to send. */
{
int n; /* Temporary to hold size. */
+ int rv;
if (port_data->slow_writes <= 0) { /* Normal, "fast", write. */
- write(port_data->fd_to_erl, buf, size);
+ rv = write(port_data->fd_to_erl, buf, size);
+ ASSERT(rv == size);
} else {
/*
* Write chunks with delays in between.
@@ -368,7 +388,8 @@ write_reply(buf, size)
while (size > 0) {
n = size > port_data->slow_writes ? port_data->slow_writes : size;
- write(port_data->fd_to_erl, buf, n);
+ rv = write(port_data->fd_to_erl, buf, n);
+ ASSERT(rv == n);
size -= n;
buf += n;
if (size)
@@ -539,7 +560,7 @@ char* spec; /* Specification for reply. */
buf = (char *) malloc(total_size);
if (buf == NULL) {
fprintf(stderr, "%s: insufficent memory for reply buffer of size %d\n",
- port_data->progname, total_size);
+ port_data->progname, (int)total_size);
exit(1);
}
diff --git a/erts/emulator/test/port_SUITE_data/port_test.erl b/erts/emulator/test/port_SUITE_data/port_test.erl
index b07038e73d..406d376b26 100644
--- a/erts/emulator/test/port_SUITE_data/port_test.erl
+++ b/erts/emulator/test/port_SUITE_data/port_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/port_SUITE_data/sleep_failure_drv.c b/erts/emulator/test/port_SUITE_data/sleep_failure_drv.c
new file mode 100644
index 0000000000..1f52646572
--- /dev/null
+++ b/erts/emulator/test/port_SUITE_data/sleep_failure_drv.c
@@ -0,0 +1,76 @@
+#include <stdio.h>
+#include "erl_driver.h"
+#ifdef __WIN32__
+# include <windows.h>
+#else
+# include <unistd.h>
+#endif
+
+typedef struct _erl_drv_data FailureDrvData;
+
+static FailureDrvData *failure_drv_start(ErlDrvPort, char *);
+static void failure_drv_stop(FailureDrvData *);
+static void failure_drv_output(ErlDrvData, char *, ErlDrvSizeT);
+static void failure_drv_finish(void);
+
+static ErlDrvEntry failure_drv_entry = {
+ NULL, /* init */
+ failure_drv_start,
+ failure_drv_stop,
+ failure_drv_output,
+ NULL, /* ready_input */
+ NULL, /* ready_output */
+ "sleep_failure_drv",
+ NULL, /* finish */
+ NULL, /* handle */
+ NULL, /* control */
+ NULL, /* timeout */
+ NULL, /* outputv */
+ NULL, /* ready_async */
+ NULL,
+ NULL,
+ NULL,
+ ERL_DRV_EXTENDED_MARKER,
+ ERL_DRV_EXTENDED_MAJOR_VERSION,
+ ERL_DRV_EXTENDED_MINOR_VERSION,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+};
+
+
+
+/* -------------------------------------------------------------------------
+** Entry functions
+**/
+
+DRIVER_INIT(failure_drv)
+{
+ return &failure_drv_entry;
+}
+
+static FailureDrvData *failure_drv_start(ErlDrvPort port, char *command) {
+ void *void_ptr;
+
+ return void_ptr = port;
+}
+
+static void failure_drv_stop(FailureDrvData *data_p) {
+}
+
+static void failure_drv_output(ErlDrvData drv_data, char *buf, ErlDrvSizeT len) {
+ FailureDrvData *data_p = (FailureDrvData *) drv_data;
+ void *void_ptr;
+ ErlDrvPort port = void_ptr = data_p;
+
+#ifdef __WIN32__
+ Sleep(3000);
+#else
+ sleep(3);
+#endif
+ driver_failure(port, 0);
+}
+
+static void failure_drv_finish() {
+}
diff --git a/erts/emulator/test/port_bif_SUITE.erl b/erts/emulator/test/port_bif_SUITE.erl
index b65a22a528..e1e1ec9fb9 100644
--- a/erts/emulator/test/port_bif_SUITE.erl
+++ b/erts/emulator/test/port_bif_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -21,8 +21,8 @@
-module(port_bif_SUITE).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2, command/1,
+-export([all/0, suite/0, groups/0,
+ command/1,
command_e_1/1, command_e_2/1, command_e_3/1, command_e_4/1,
port_info1/1, port_info2/1,
port_info_os_pid/1, port_info_race/1,
@@ -30,11 +30,11 @@
-export([do_command_e_1/1, do_command_e_2/1, do_command_e_4/1]).
--export([init_per_testcase/2, end_per_testcase/2]).
+-include_lib("common_test/include/ct.hrl").
--include_lib("test_server/include/test_server.hrl").
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 10}}].
all() ->
[command, {group, port_info}, connect, control,
@@ -46,27 +46,6 @@ groups() ->
{port_info, [],
[port_info1, port_info2, port_info_os_pid, port_info_race]}].
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-
-init_per_testcase(_Func, Config) when is_list(Config) ->
- Dog=test_server:timetrap(test_server:minutes(10)),
- [{watchdog, Dog}|Config].
-end_per_testcase(_Func, Config) when is_list(Config) ->
- Dog=?config(watchdog, Config),
- test_server:timetrap_cancel(Dog).
-
command(Config) when is_list(Config) ->
load_control_drv(Config),
@@ -87,17 +66,17 @@ do_command(P, Data) ->
{P,{data,Data0}} ->
case {list_to_binary(Data0),list_to_binary([Data])} of
{B,B} -> ok;
- _ -> test_server:fail({unexpected_data,Data0})
+ _ -> ct:fail({unexpected_data,Data0})
end;
Other ->
- test_server:fail({unexpected_message,Other})
+ ct:fail({unexpected_message,Other})
end.
%% port_command/2: badarg 1st arg
command_e_1(Config) when is_list(Config) ->
- DataDir = ?config(data_dir, Config),
+ DataDir = proplists:get_value(data_dir, Config),
Program = filename:join(DataDir, "port_test"),
process_flag(trap_exit, true),
@@ -106,9 +85,9 @@ command_e_1(Config) when is_list(Config) ->
{'EXIT', Pid, {badarg, _}} when is_pid(Pid) ->
ok;
Other ->
- test_server:fail(Other)
+ ct:fail(Other)
after 10000 ->
- test_server:fail(timeout)
+ ct:fail(timeout)
end,
ok.
@@ -119,7 +98,7 @@ do_command_e_1(Program) ->
%% port_command/2: badarg 2nd arg
command_e_2(Config) when is_list(Config) ->
- DataDir = ?config(data_dir, Config),
+ DataDir = proplists:get_value(data_dir, Config),
Program = filename:join(DataDir, "port_test"),
process_flag(trap_exit, true),
@@ -128,9 +107,9 @@ command_e_2(Config) when is_list(Config) ->
{'EXIT', Pid, {badarg, _}} when is_pid(Pid) ->
ok;
Other ->
- test_server:fail(Other)
+ ct:fail(Other)
after 10000 ->
- test_server:fail(timeout)
+ ct:fail(timeout)
end,
ok.
@@ -141,7 +120,7 @@ do_command_e_2(Program) ->
%% port_command/2: Posix signals trapped
command_e_3(Config) when is_list(Config) ->
- DataDir = ?config(data_dir, Config),
+ DataDir = proplists:get_value(data_dir, Config),
Program = filename:join(DataDir, "port_test"),
process_flag(trap_exit, true),
@@ -152,15 +131,15 @@ command_e_3(Config) when is_list(Config) ->
{'EXIT', Port, einval} when is_port(Port) ->
ok;
Other ->
- test_server:fail(Other)
+ ct:fail(Other)
after 10000 ->
- test_server:fail(timeout)
+ ct:fail(timeout)
end,
ok.
%% port_command/2: Posix exit signals not trapped
command_e_4(Config) when is_list(Config) ->
- DataDir = ?config(data_dir, Config),
+ DataDir = proplists:get_value(data_dir, Config),
Program = filename:join(DataDir, "port_test"),
process_flag(trap_exit, true),
@@ -169,9 +148,9 @@ command_e_4(Config) when is_list(Config) ->
{'EXIT', Pid, {einval, _}} when is_pid(Pid) ->
ok;
Other ->
- test_server:fail(Other)
+ ct:fail(Other)
after 10000 ->
- test_server:fail(timeout)
+ ct:fail(timeout)
end,
ok.
@@ -248,7 +227,7 @@ do_port_info_os_pid() ->
{os_pid, InfoOSPid} = erlang:port_info(P, os_pid),
EchoPidStr = receive
{P, {data, EchoPidStr0}} -> EchoPidStr0
- after 10000 -> test_server:fail(timeout)
+ after 10000 -> ct:fail(timeout)
end,
{ok, [EchoPid], []} = io_lib:fread("~u\n", EchoPidStr),
{value,{os_pid, InfoOSPid}}=lists:keysearch(os_pid, 1, A),
@@ -257,7 +236,7 @@ do_port_info_os_pid() ->
ok.
port_info_race(Config) when is_list(Config) ->
- DataDir = ?config(data_dir, Config),
+ DataDir = proplists:get_value(data_dir, Config),
Program = filename:join(DataDir, "port_test"),
Top = self(),
P1 = open_port({spawn,Program}, [{packet,1}]),
@@ -283,10 +262,9 @@ output_test(_, _, Input, Output) when Output > 16#1fffffff ->
output_test(P, Bin, Input0, Output0) ->
erlang:port_command(P, Bin),
receive
- {P,{data,Bin}} -> ok;
- Other ->
- io:format("~p", [Other]),
- ?t:fail()
+ {P,{data,Bin}} -> ok;
+ Other ->
+ ct:fail("~p", [Other])
end,
Input = Input0 + size(Bin),
Output = Output0 + size(Bin),
@@ -296,8 +274,8 @@ output_test(P, Bin, Input0, Output0) ->
%% We can't test much here, but hopefully a debug-built emulator will crasch
%% if there is something wrong with the heap allocation.
case erlang:statistics(io) of
- {{input,In},{output,Out}} when is_integer(In), is_integer(Out) ->
- ok
+ {{input,In},{output,Out}} when is_integer(In), is_integer(Out) ->
+ ok
end,
output_test(P, Bin, Input, Output).
@@ -345,7 +323,7 @@ connect(Config) when is_list(Config) ->
exit(P, you_should_die),
receive
{'EXIT',RecPid,you_should_die} -> ok;
- Other -> ?line ?t:fail({bad_message,Other})
+ Other -> ct:fail({bad_message,Other})
end,
%% Done.
@@ -410,7 +388,7 @@ test_op(P, Op) ->
<<Op:32>> = list_to_binary(R).
echo_to_busy(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(10)),
+ ct:timetrap({seconds, 10}),
load_control_drv(Config),
P = open_port({spawn, control_drv}, []),
erlang:port_control(P, $b, [1]), % Set to busy.
@@ -422,11 +400,10 @@ echo_to_busy(Config) when is_list(Config) ->
{Echoer, done} ->
ok;
{Echoer, Other} ->
- test_server:fail(Other);
+ ct:fail(Other);
Other ->
- test_server:fail({unexpected_message, Other})
+ ct:fail({unexpected_message, Other})
end,
- test_server:timetrap_cancel(Dog),
ok.
echoer(P, ReplyTo) ->
@@ -451,7 +428,7 @@ echo(P, Size) ->
Packet = erlang:port_control(P, $e, [unaligned_sub_bin(Bin)]).
load_control_drv(Config) when is_list(Config) ->
- DataDir = ?config(data_dir, Config),
+ DataDir = proplists:get_value(data_dir, Config),
erl_ddll:start(),
ok = load_driver(DataDir, "control_drv").
@@ -485,14 +462,7 @@ random_char(Chars) ->
lists:nth(uniform(length(Chars)), Chars).
uniform(N) ->
- case get(random_seed) of
- undefined ->
- {X, Y, Z} = time(),
- random:seed(X, Y, Z);
- _ ->
- ok
- end,
- random:uniform(N).
+ rand:uniform(N).
unaligned_sub_bin(Bin0) ->
Bin1 = <<0:3,Bin0/binary,31:5>>,
diff --git a/erts/emulator/test/port_trace_SUITE.erl b/erts/emulator/test/port_trace_SUITE.erl
new file mode 100644
index 0000000000..03efdc15db
--- /dev/null
+++ b/erts/emulator/test/port_trace_SUITE.erl
@@ -0,0 +1,652 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(port_trace_SUITE).
+
+-export([all/0, suite/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2]).
+-export([port_specs/1, ports/1, open_close/1,
+ command/1, control/1, connect/1, call/1,
+ output/1, output2/1, output_binary/1,
+ outputv/1, set_timer/1, failure_eof/1,
+ failure_atom/1, failure_posix/1,
+ failure/1, output_term/1,
+ driver_output_term/1,
+ send_term/1, driver_send_term/1,
+ driver_remote_send_term/1]).
+
+-define(ECHO_DRV_NOOP, 0).
+-define(ECHO_DRV_OUTPUT, 1).
+-define(ECHO_DRV_OUTPUT2, 2).
+-define(ECHO_DRV_OUTPUT_BINARY, 3).
+-define(ECHO_DRV_OUTPUTV, 4).
+-define(ECHO_DRV_SET_TIMER, 5).
+-define(ECHO_DRV_FAILURE_EOF, 6).
+-define(ECHO_DRV_FAILURE_ATOM, 7).
+-define(ECHO_DRV_FAILURE_POSIX, 8).
+-define(ECHO_DRV_FAILURE, 9).
+-define(ECHO_DRV_OUTPUT_TERM, 10).
+-define(ECHO_DRV_DRIVER_OUTPUT_TERM, 11).
+-define(ECHO_DRV_SEND_TERM, 12).
+-define(ECHO_DRV_DRIVER_SEND_TERM, 13).
+-define(ECHO_DRV_SAVE_CALLER, 14).
+-define(ECHO_DRV_REMOTE_SEND_TERM, 15).
+
+suite() -> [{ct_hooks,[ts_install_cth]},
+ {timetrap, {seconds, 30}}].
+
+all() ->
+ [port_specs, ports, open_close,
+ command, control, connect, call,
+ output, output2, output_binary,
+ outputv, set_timer, failure_eof,
+ failure_atom, failure_posix,
+ failure, output_term,
+ driver_output_term,
+ send_term, driver_send_term,
+ driver_remote_send_term].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+init_per_testcase(driver_remote_send_term, Config) ->
+ case erlang:system_info(smp_support) of
+ false ->
+ {skip,"Only supported on smp systems"};
+ true ->
+ init_per_testcase(driver_remote_send_term_smp, Config)
+ end;
+init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
+ erlang:trace(all, false, [all]),
+ os:unsetenv("OUTPUTV"),
+ reload_drv(Config),
+ Config.
+
+end_per_testcase(_Func, _Config) ->
+ erlang:trace(all, false, [all]),
+ ok.
+
+%% Test the first argument of trace/3
+port_specs(_Config) ->
+
+ S = self(),
+
+ Tracer = fun F() ->
+ receive
+ stop ->
+ ok;
+ M ->
+ S ! M,
+ F()
+ end
+ end,
+
+ Test = fun(TraceSpec, Info1, Info2) ->
+ {TracerPid,Ref} = spawn_monitor(Tracer),
+ Prt1 = erlang:open_port({spawn, echo_drv}, [binary]),
+ erlang:trace(TraceSpec, true, ['receive', {tracer, TracerPid}]),
+ %% We disable trace messages from the testcase process
+ erlang:trace(self(), false, ['receive']),
+ Prt2 = erlang:open_port({spawn, echo_drv}, [binary]),
+
+ InfoCheck =
+ fun(Info, Prt) ->
+ if
+ Info ->
+ {tracer, TracerPid} = erlang:trace_info(Prt, tracer),
+ {flags,['receive']} = erlang:trace_info(Prt, flags);
+ not Info ->
+ {tracer,[]} = erlang:trace_info(Prt, tracer),
+ {flags,[]} = erlang:trace_info(Prt, flags)
+ end
+ end,
+ InfoCheck(Info1, Prt1),
+ InfoCheck(Info2, Prt2),
+
+ %% These may create trace messages
+ erlang:port_command(Prt1, <<?ECHO_DRV_NOOP>>),
+ erlang:port_command(Prt2, <<?ECHO_DRV_NOOP>>),
+
+ %% Test what happens when the tracer dies
+ trace_delivered(),
+ TracerPid ! stop,
+ receive {'DOWN', Ref, process, TracerPid, normal} -> ok end,
+
+ %% These should not generate any trace messages
+ erlang:port_command(Prt1, <<?ECHO_DRV_NOOP>>),
+ erlang:port_command(Prt2, <<?ECHO_DRV_NOOP>>),
+
+ InfoCheck(false, Prt1),
+ InfoCheck(false, Prt2),
+
+ erlang:port_close(Prt1),
+ erlang:port_close(Prt2),
+ erlang:trace(all, false, [all]),
+ {Prt1, Prt2}
+ end,
+
+ {_Prt11, Prt12} = Test(new, false, true),
+ [{trace, Prt12, 'receive', {S, {command,<<?ECHO_DRV_NOOP>>}}}]
+ = flush(Prt12),
+
+ {_Prt21, Prt22} = Test(new_ports, false, true),
+ [{trace, Prt22, 'receive', {S, {command,<<?ECHO_DRV_NOOP>>}}}]
+ = flush(Prt22),
+
+ {Prt31, _Prt32} = Test(existing, true, false),
+ [{trace, Prt31, 'receive', {S, {command,<<?ECHO_DRV_NOOP>>}}}]
+ = flush(Prt31),
+
+ {Prt41, _Prt42} = Test(existing_ports, true, false),
+ [{trace, Prt41, 'receive', {S, {command,<<?ECHO_DRV_NOOP>>}}}]
+ = flush(Prt41),
+
+ {Prt51, Prt52} = Test(all, true, true),
+ [{trace, Prt51, 'receive', {S, {command,<<?ECHO_DRV_NOOP>>}}}]
+ = flush(Prt51),
+ [{trace, Prt52, 'receive', {S, {command,<<?ECHO_DRV_NOOP>>}}}]
+ = flush(Prt52),
+
+ {Prt61, Prt62} = Test(ports, true, true),
+ [{trace, Prt61, 'receive', {S, {command,<<?ECHO_DRV_NOOP>>}}}]
+ = flush(Prt61),
+ [{trace, Prt62, 'receive', {S, {command,<<?ECHO_DRV_NOOP>>}}}]
+ = flush(Prt62),
+
+ ok.
+
+%% Test that the 'ports' trace flag works
+ports(_Config) ->
+
+ {Prt, S} = trace_and_open([ports],[binary]),
+
+ [{trace, Prt, open, S, echo_drv},
+ {trace, Prt, getting_linked, S}] = flush(),
+
+ register(?MODULE, Prt),
+ unregister(?MODULE),
+ register(?MODULE, Prt),
+
+ [{trace,Prt,register,port_trace_SUITE},
+ {trace,Prt,unregister,port_trace_SUITE},
+ {trace,Prt,register,port_trace_SUITE}] = flush(),
+
+ unlink(Prt),
+ link(Prt),
+
+ [{trace,Prt,getting_unlinked,S},
+ {trace,Prt,getting_linked,S}] = flush(),
+
+ erlang:port_close(Prt),
+
+ [{trace,Prt,closed,normal},
+ {trace,Prt,unregister,port_trace_SUITE},
+ {trace,Prt,unlink,S}] = flush(),
+
+ ok.
+
+%% Test that port_close and ! close generate correct trace messages
+open_close(_Config) ->
+
+ S = trace_ports([send,'receive']),
+
+ Prt = erlang:open_port({spawn, echo_drv}, [binary]),
+ erlang:port_close(Prt),
+ [{trace, Prt, 'receive', {S, close}}] = flush(),
+
+ Prt2 = erlang:open_port({spawn, echo_drv}, [binary]),
+ Prt2 ! {S, close},
+ recv({Prt2, closed}),
+ [{trace, Prt2, 'receive', {S, close}},
+ {trace, Prt2, send, closed, S}] = flush(),
+
+ catch erlang:port_close(Prt2),
+ [] = flush(),
+
+ ok.
+
+%% Test that port_command and ! command generate correct trace messages
+command(Config) ->
+
+ Flags = [send,'receive'],
+ S = trace_ports(Flags),
+ Prt = erlang:open_port({spawn, echo_drv}, [binary]),
+
+ erlang:port_command(Prt, <<?ECHO_DRV_NOOP:8>>),
+ [{trace, Prt, 'receive', {S, {command, <<?ECHO_DRV_NOOP:8>>}}}] = flush(),
+
+ erlang:port_command(Prt, [?ECHO_DRV_NOOP, <<0:8>>]),
+ [{trace, Prt, 'receive', {S, {command, <<?ECHO_DRV_NOOP:8,0:8>>}}}] = flush(),
+
+ Prt ! {S, {command, <<?ECHO_DRV_NOOP:8>>}},
+ [{trace, Prt, 'receive', {S, {command, <<?ECHO_DRV_NOOP:8>>}}}] = flush(),
+
+ OutputMsg = <<?ECHO_DRV_NOOP:8,0:(8*512)>>,
+ Prt ! {S, {command, OutputMsg}},
+ [{trace, Prt, 'receive', {S, {command, OutputMsg}}}] = flush(),
+
+ close(Prt, Flags),
+
+ os:putenv("OUTPUTV","true"),
+ reload_drv(Config),
+
+ Prt2 = erlang:open_port({spawn, echo_drv}, [binary]),
+ OutputvMsg = [<<0:8>>,<<0:(8*512)>>,<<0:(8*256)>>,<<0:8>>],
+
+ erlang:port_command(Prt2, OutputvMsg),
+ [{trace, Prt2, 'receive', {S, {command, OutputvMsg}}}] = flush(),
+
+ Prt2 ! {S, {command, OutputvMsg}},
+ [{trace, Prt2, 'receive', {S, {command, OutputvMsg}}}] = flush(),
+
+ close(Prt2, Flags),
+
+ os:unsetenv("OUTPUTV"),
+
+ ok.
+
+%% Test that port_control generate correct trace messages
+control(_Config) ->
+
+ Flags = [send,'receive'],
+ {Prt, S} = trace_and_open(Flags,[binary]),
+
+ [0] = erlang:port_control(Prt, 1, <<?ECHO_DRV_NOOP:8, 0:8>>),
+ [{trace, Prt, 'receive', {S, {control, {1, <<?ECHO_DRV_NOOP:8, 0:8>>}}}},
+ {trace, Prt, send, {Prt, {control, <<0:8>>}}, S}] = flush(),
+
+ [0] = erlang:port_control(Prt, (1 bsl 32) - 1, <<?ECHO_DRV_NOOP:8, 0:8>>),
+ [{trace, Prt, 'receive', {S, {control, {(1 bsl 32) - 1, <<?ECHO_DRV_NOOP:8, 0:8>>}}}},
+ {trace, Prt, send, {Prt, {control, <<0:8>>}}, S}] = flush(),
+
+ Msg = <<?ECHO_DRV_NOOP:8, 0:(8*512)>>,
+ Pat = lists:duplicate(512, 0),
+ Pat = erlang:port_control(Prt, 1, Msg),
+ [{trace, Prt, 'receive', {S, {control, {1, Msg}}}},
+ {trace, Prt, send, {Prt, {control, <<0:(8*512)>>}}, S}] = flush(),
+
+ close(Prt, Flags),
+
+ ok.
+
+%% Test that port_connect and ! connect generate correct trace messages
+%% This includes that the proper getting_linked messages are sent
+connect(_Config) ->
+
+
+ {Prt, S} = trace_and_open([send, 'receive', ports],[binary]),
+
+ flush(),
+
+ {Pid,Ref} = spawn_monitor(
+ fun() ->
+ receive
+ go ->
+ Prt ! {self(), {connect, S}},
+ receive {Prt, connected} -> unlink(Prt) end
+ end
+ end),
+ erlang:trace(Pid, true, [send, 'receive', procs]),
+
+ erlang:port_connect(Prt, Pid),
+ unlink(Prt),
+
+ [{trace,Prt,getting_linked,Pid},
+ {trace,Prt,'receive',{S,{connect,Pid}}},
+ {trace,Prt,send,{Prt,connected},S},
+ {trace,Prt,getting_unlinked, S}] = flush(Prt),
+
+ [{trace,Pid,getting_linked,Prt}] = flush(),
+
+ Pid ! go,
+ recv({'DOWN',Ref,process,Pid,normal}),
+
+ [{trace,Prt,'receive',{Pid,{connect,S}}},
+ {trace,Prt,send,{Prt,connected},Pid},
+ {trace,Prt,getting_unlinked,Pid}] = flush(Prt),
+
+ [{trace,Pid,'receive',go},
+ {trace,Pid,send,{Pid,{connect,S}}, Prt},
+ {trace,Pid,'receive',{Prt,connected}},
+ {trace,Pid,unlink,Prt},
+ {trace,Pid,exit,normal}] = flush(),
+
+ erlang:port_close(Prt),
+ [{trace, Prt, 'receive', {S, close}},
+ {trace, Prt, closed, normal}] = flush(),
+ ok.
+
+%% Test that port_call generate correct trace messages
+call(_Config) ->
+
+ Flags = [send,'receive'],
+ {Prt, S} = trace_and_open(Flags,[binary]),
+
+ Test = fun(Msg) ->
+ BinMsg = term_to_binary(Msg),
+
+ Msg = erlang:port_call(Prt, 0, Msg),
+ [{trace, Prt, 'receive', {S, {call, {0, BinMsg}}}},
+ {trace, Prt, send, {Prt, {call, BinMsg}}, S}] = flush()
+ end,
+
+ Test({hello, world, make_ref()}),
+ Test({hello, world, lists:seq(1,1000)}),
+
+ close(Prt, Flags),
+
+ ok.
+
+%% Test that driver_output generate correct trace messages
+output(_Config) ->
+
+ Flags = [send],
+ {Prt, S} = trace_and_open(Flags,[binary]),
+
+ erlang:port_command(Prt, <<?ECHO_DRV_OUTPUT, 123456:32>>),
+ recv({Prt,{data,<<123456:32>>}}),
+
+ [{trace, Prt, send, {Prt, {data, <<123456:32>>}}, S}] = flush(),
+
+ close(Prt, Flags),
+
+ ok.
+
+%% Test that driver_output2 generate correct trace messages
+output2(_Config) ->
+
+ Flags = [send],
+ {Prt, S} = trace_and_open(Flags,[binary]),
+
+ erlang:port_command(Prt, <<?ECHO_DRV_OUTPUT2, 123456:32>>),
+ recv({Prt,{data,[$a|<<123456:32>>]}}),
+ [{trace, Prt, send, {Prt, {data, [$a|<<123456:32>>]}}, S}] = flush(),
+
+ close(Prt, Flags),
+
+ ok.
+
+%% Test that driver_output_binary generate correct trace messages
+output_binary(_Config) ->
+
+ Flags = [send],
+ {Prt, S} = trace_and_open(Flags,[binary]),
+
+ erlang:port_command(Prt, <<?ECHO_DRV_OUTPUT_BINARY, 0, 123456:32>>),
+ recv({Prt,{data,[$a|<<123456:32>>]}}),
+ [{trace, Prt, send, {Prt, {data, [$a|<<123456:32>>]}}, S}] = flush(),
+
+ close(Prt, Flags),
+
+ ok.
+
+%% Test that driver_outputv generate correct trace messages
+outputv(_Config) ->
+
+ Flags = [send],
+ {Prt, S} = trace_and_open(Flags,[binary]),
+
+ erlang:port_command(Prt, <<?ECHO_DRV_OUTPUTV, 123456:32>>),
+ recv({Prt,{data,[$a|<<123456:32>>]}}),
+
+ [{trace, Prt, send, {Prt, {data, [$a|<<123456:32>>]}}, S}] = flush(),
+
+ erlang:port_close(Prt),
+ [] = flush(),
+
+ ok.
+
+%% Test that driver_set_timer generate correct trace messages
+set_timer(_Config) ->
+
+ Flags = [send,'receive'],
+ {Prt, S} = trace_and_open(Flags,[binary]),
+
+ erlang:port_command(Prt, <<?ECHO_DRV_SET_TIMER>>),
+ timer:sleep(100),
+ [{trace, Prt, 'receive', {S, {command, <<?ECHO_DRV_SET_TIMER>>}}},
+ {trace, Prt, 'receive', timeout}] = flush(),
+
+ close(Prt, Flags),
+
+ ok.
+
+%% Test that driver_failure* generate correct trace messages
+failure_eof(_Config) ->
+
+ Flags = [send,'receive', ports],
+ S = trace_ports(Flags),
+
+ Prt = erlang:open_port({spawn, echo_drv}, [eof, binary]),
+ [{trace, Prt, open, S, echo_drv},
+ {trace, Prt, getting_linked, S}] = flush(),
+
+ erlang:port_command(Prt, <<?ECHO_DRV_FAILURE_EOF>>),
+ recv({Prt,eof}),
+ [{trace, Prt, 'receive', {S, {command, <<?ECHO_DRV_FAILURE_EOF>>}}},
+ {trace, Prt, send, {Prt, eof}, S}] = flush(),
+
+ close(Prt, Flags),
+
+ %% Run same test without eof option
+ failure_test(<<?ECHO_DRV_FAILURE_EOF>>, normal).
+
+failure_atom(_Config) ->
+ failure_test(<<?ECHO_DRV_FAILURE_ATOM, "failure\0">>, failure).
+failure_posix(_Config) ->
+ failure_test(<<?ECHO_DRV_FAILURE_POSIX>>, eagain).
+failure(_Config) ->
+ failure_test(<<?ECHO_DRV_FAILURE, 1>>, 1).
+
+failure_test(Failure, Reason) ->
+
+ {Prt, S} = trace_and_open([send, 'receive', ports],[binary]),
+
+ [{trace, Prt, open, S, echo_drv},
+ {trace, Prt, getting_linked, S}] = flush(),
+
+ process_flag(trap_exit, true),
+ erlang:port_command(Prt, Failure),
+ try
+ recv({'EXIT',Prt,Reason})
+ after
+ process_flag(trap_exit, false)
+ end,
+ [{trace, Prt, 'receive', {S, {command, Failure}}},
+ {trace, Prt, closed, Reason},
+ {trace, Prt, unlink, S}] = flush(),
+
+ ok.
+
+%% Test that erl_drv_output_term generate correct trace messages
+output_term(_Config) ->
+
+ Flags = [send],
+ {Prt, S} = trace_and_open(Flags,[binary]),
+
+ erlang:port_command(Prt, <<?ECHO_DRV_OUTPUT_TERM, 123456:32>>),
+ recv({echo, Prt, <<123456:32>>}),
+ [{trace, Prt, send, {echo, Prt, <<123456:32>>}, S}] = flush(),
+
+ close(Prt, Flags),
+
+ ok.
+
+%% Test that driver_output_term generate correct trace messages
+driver_output_term(_Config) ->
+
+ Flags = [send],
+ {Prt, S} = trace_and_open(Flags,[binary]),
+
+ erlang:port_command(Prt, <<?ECHO_DRV_DRIVER_OUTPUT_TERM, 123456:32>>),
+ recv({echo, Prt, <<123456:32>>}),
+ [{trace, Prt, send, {echo, Prt, <<123456:32>>}, S}] = flush(),
+
+ close(Prt, Flags),
+
+ ok.
+
+%% Test that erl_drv_send_term generate correct trace messages
+send_term(_Config) ->
+
+ Flags = [send],
+ {Prt, S} = trace_and_open(Flags,[binary]),
+
+ erlang:port_command(Prt, <<?ECHO_DRV_SEND_TERM, 123456:32>>),
+ recv({echo, Prt, <<123456:32>>}),
+ [{trace, Prt, send, {echo, Prt, <<123456:32>>}, S}] = flush(),
+
+ {Pid, Ref} = spawn_monitor(fun() -> erlang:port_command(Prt, <<?ECHO_DRV_SAVE_CALLER>>) end),
+ recv({'DOWN',Ref,process,Pid,normal}),
+ erlang:port_command(Prt, <<?ECHO_DRV_SEND_TERM, 123456:32>>),
+ [{trace, Prt, send_to_non_existing_process, {echo, Prt, <<123456:32>>}, Pid}] = flush(),
+
+ close(Prt, Flags),
+
+ ok.
+
+%% Test that driver_send_term generate correct trace messages
+driver_send_term(_Config) ->
+
+ Flags = [send],
+ {Prt, S} = trace_and_open(Flags,[binary]),
+
+ erlang:port_command(Prt, <<?ECHO_DRV_DRIVER_SEND_TERM, 123456:32>>),
+ recv({echo, Prt, <<123456:32>>}),
+ [{trace, Prt, send, {echo, Prt, <<123456:32>>}, S}] = flush(),
+
+ {Pid, Ref} = spawn_monitor(fun() -> erlang:port_command(Prt, <<?ECHO_DRV_SAVE_CALLER>>) end),
+ recv({'DOWN',Ref,process,Pid,normal}),
+ erlang:port_command(Prt, <<?ECHO_DRV_SEND_TERM, 123456:32>>),
+ [{trace, Prt, send_to_non_existing_process, {echo, Prt, <<123456:32>>}, Pid}] = flush(),
+
+ close(Prt, Flags),
+
+ ok.
+
+%% Test that driver_send_term from non-scheduler thread does not
+%% generate trace messages.
+driver_remote_send_term(_Config) ->
+
+ Flags = [send],
+ {Prt, S} = trace_and_open(Flags,[binary]),
+
+ erlang:port_command(Prt, <<?ECHO_DRV_REMOTE_SEND_TERM, 123456:32>>),
+ recv({echo, Prt, <<123456:32>>}),
+ [] = flush(),
+
+ Pid = spawn_link(
+ fun() ->
+ erlang:port_command(Prt, <<?ECHO_DRV_SAVE_CALLER>>),
+ S ! ok,
+ receive M -> S ! M end
+ end),
+ recv(ok),
+ erlang:trace(Pid, true, ['receive']),
+
+ erlang:port_command(Prt, <<?ECHO_DRV_REMOTE_SEND_TERM, 123456:32>>),
+ recv({echo, Prt, <<123456:32>>}),
+ [{trace, Pid, 'receive', {echo, Prt, <<123456:32>>}}] = flush(),
+
+ close(Prt, Flags),
+
+ ok.
+
+%%%%%%%%%%%%%%%%%%%
+%% Helper functions
+%%%%%%%%%%%%%%%%%%%
+
+trace_ports(TraceFlags) ->
+ erlang:trace(new_ports, true, TraceFlags),
+ self().
+
+trace_and_open(TraceFlags, OpenFlags) ->
+ S = self(),
+ Ports = proplists:get_value(ports, TraceFlags),
+ [trace_ports(TraceFlags) || Ports],
+ Prt = erlang:open_port({spawn, echo_drv}, OpenFlags),
+ [erlang:trace(Prt, true, TraceFlags) || Ports == undefined],
+ {Prt, S}.
+
+close(Prt, Flags) ->
+ Recv = proplists:get_value('receive', Flags),
+ Ports = proplists:get_value(ports, Flags),
+ S = self(),
+
+ erlang:port_close(Prt),
+
+ if Recv, Ports ->
+ [{trace, Prt, 'receive', {S, close}},
+ {trace, Prt, closed, normal},
+ {trace, Prt, unlink, S}] = flush();
+ Recv ->
+ [{trace, Prt, 'receive', {S, close}}] = flush();
+ Ports ->
+ [{trace, Prt, closed, normal},
+ {trace, Prt, unlink, S}] = flush();
+ true ->
+ [] = flush()
+ end.
+
+trace_delivered() ->
+ Ref = erlang:trace_delivered(all),
+ receive {trace_delivered, all, Ref} -> ok end.
+
+flush() ->
+ flush(all).
+flush(From) ->
+ trace_delivered(),
+ f(From).
+
+f(From) ->
+ receive
+ M when From =:= all; element(2, M) == From ->
+ [M | f(From)]
+ after 0 ->
+ []
+ end.
+
+recv(Msg) ->
+ receive Msg -> ok after 1000 -> ct:fail({did_not_get_data,Msg,flush()}) end.
+
+load_drv(Config) ->
+ Path = proplists:get_value(data_dir, Config),
+ case erl_ddll:load_driver(Path, echo_drv) of
+ ok -> ok;
+ {error, Error} = Res ->
+ io:format("~s\n", [erl_ddll:format_error(Error)]),
+ ct:fail(Res)
+ end.
+
+reload_drv(Config) ->
+ erl_ddll:unload_driver(echo_drv),
+ load_drv(Config).
diff --git a/erts/emulator/test/port_trace_SUITE_data/Makefile.src b/erts/emulator/test/port_trace_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..c1bf142ccf
--- /dev/null
+++ b/erts/emulator/test/port_trace_SUITE_data/Makefile.src
@@ -0,0 +1,3 @@
+all: echo_drv@dll@
+
+@SHLIB_RULES@
diff --git a/erts/emulator/test/port_trace_SUITE_data/echo_drv.c b/erts/emulator/test/port_trace_SUITE_data/echo_drv.c
new file mode 100644
index 0000000000..20ec33a594
--- /dev/null
+++ b/erts/emulator/test/port_trace_SUITE_data/echo_drv.c
@@ -0,0 +1,296 @@
+#include <stdio.h>
+#include "erl_driver.h"
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+
+
+/* -------------------------------------------------------------------------
+** Data types
+**/
+
+struct my_thread {
+ struct my_thread* next;
+ ErlDrvTid tid;
+};
+
+typedef struct _erl_drv_data {
+ ErlDrvPort erlang_port;
+ ErlDrvTermData caller;
+ struct my_thread* threads;
+} EchoDrvData;
+
+struct remote_send_term {
+ struct my_thread thread;
+ ErlDrvTermData port;
+ ErlDrvTermData caller;
+ int len;
+ char buf[1]; /* buf[len] */
+};
+
+#define ECHO_DRV_NOOP 0
+#define ECHO_DRV_OUTPUT 1
+#define ECHO_DRV_OUTPUT2 2
+#define ECHO_DRV_OUTPUT_BINARY 3
+#define ECHO_DRV_OUTPUTV 4
+#define ECHO_DRV_SET_TIMER 5
+#define ECHO_DRV_FAILURE_EOF 6
+#define ECHO_DRV_FAILURE_ATOM 7
+#define ECHO_DRV_FAILURE_POSIX 8
+#define ECHO_DRV_FAILURE 9
+#define ECHO_DRV_OUTPUT_TERM 10
+#define ECHO_DRV_DRIVER_OUTPUT_TERM 11
+#define ECHO_DRV_SEND_TERM 12
+#define ECHO_DRV_DRIVER_SEND_TERM 13
+#define ECHO_DRV_SAVE_CALLER 14
+#define ECHO_DRV_REMOTE_SEND_TERM 15
+
+
+/* -------------------------------------------------------------------------
+** Entry struct
+**/
+
+static EchoDrvData *echo_drv_start(ErlDrvPort port, char *command);
+static void echo_drv_stop(ErlDrvData drv_data);
+static void echo_drv_output(ErlDrvData drv_data, char *buf,
+ ErlDrvSizeT len);
+static void echo_drv_outputv(ErlDrvData drv_data, ErlIOVec *iov);
+static void echo_drv_finish(void);
+static ErlDrvSSizeT echo_drv_control(ErlDrvData drv_data,
+ unsigned int command,
+ char *buf, ErlDrvSizeT len,
+ char **rbuf, ErlDrvSizeT rlen);
+static void echo_drv_timeout(ErlDrvData drv_data);
+static ErlDrvSSizeT echo_drv_call(ErlDrvData drv_data,
+ unsigned int command,
+ char *buf, ErlDrvSizeT len,
+ char **rbuf, ErlDrvSizeT rlen,
+ unsigned int *flags);
+
+static ErlDrvEntry echo_drv_entry = {
+ NULL, /* init */
+ echo_drv_start,
+ echo_drv_stop,
+ echo_drv_output,
+ NULL, /* ready_input */
+ NULL, /* ready_output */
+ "echo_drv",
+ echo_drv_finish,
+ NULL, /* handle */
+ echo_drv_control,
+ echo_drv_timeout, /* timeout */
+ NULL, /* outputv */
+ NULL, /* ready_async */
+ NULL, /* flush */
+ echo_drv_call, /* call */
+ NULL, /* event */
+ ERL_DRV_EXTENDED_MARKER,
+ ERL_DRV_EXTENDED_MAJOR_VERSION,
+ ERL_DRV_EXTENDED_MINOR_VERSION,
+ 0,
+ NULL,
+ NULL,
+ NULL
+};
+
+static void* send_term_thread(void *);
+
+/* -------------------------------------------------------------------------
+** Entry functions
+**/
+
+DRIVER_INIT(echo_drv)
+{
+ char buff[5];
+ size_t size = sizeof(buff);
+
+ if (erl_drv_getenv("OUTPUTV", buff, &size) == -1) {
+ echo_drv_entry.outputv = NULL;
+ } else {
+ echo_drv_entry.outputv = echo_drv_outputv;
+ }
+
+ return &echo_drv_entry;
+}
+
+static EchoDrvData *echo_drv_start(ErlDrvPort port, char *command)
+{
+ EchoDrvData *echo_drv_data_p = driver_alloc(sizeof(EchoDrvData));
+ echo_drv_data_p->erlang_port = port;
+ echo_drv_data_p->caller = driver_caller(port);
+ echo_drv_data_p->threads = NULL;
+ return echo_drv_data_p;
+}
+
+static void echo_drv_stop(EchoDrvData *data_p)
+{
+ struct my_thread* thr = data_p->threads;
+
+ while (thr) {
+ struct my_thread* next = thr->next;
+ void* exit_value;
+ int ret = erl_drv_thread_join(thr->tid, &exit_value);
+ assert(ret == 0 && exit_value == NULL);
+ driver_free(thr);
+ thr = next;
+ }
+ driver_free(data_p);
+}
+
+static void echo_drv_outputv(ErlDrvData drv_data, ErlIOVec *iov)
+{
+ return;
+}
+
+static void echo_drv_output(ErlDrvData drv_data, char *buf, ErlDrvSizeT len) {
+ EchoDrvData* data_p = (EchoDrvData *) drv_data;
+ ErlDrvPort port = data_p->erlang_port;
+
+ switch (buf[0]) {
+ case ECHO_DRV_OUTPUT:
+ {
+ driver_output(port, buf+1, len-1);
+ break;
+ }
+ case ECHO_DRV_OUTPUT2:
+ {
+ driver_output2(port, "a", 1, buf+1, len-1);
+ break;
+ }
+ case ECHO_DRV_OUTPUT_BINARY:
+ {
+ ErlDrvBinary *bin = driver_alloc_binary(len-1);
+ memcpy(&bin->orig_bytes, buf+1, len-1);
+ driver_output_binary(port, "a", 1, bin, 1, len - 2);
+ driver_free_binary(bin);
+ break;
+ }
+ case ECHO_DRV_OUTPUTV:
+ {
+ ErlIOVec iov;
+ ErlDrvSizeT sz;
+ driver_enq(port, buf + 1, len - 1);
+ sz = driver_peekqv(port, &iov);
+ driver_outputv(port, "a", 1, &iov, 0);
+ driver_deq(port, sz);
+ break;
+ }
+ case ECHO_DRV_SET_TIMER:
+ {
+ driver_set_timer(port, 10);
+ break;
+ }
+ case ECHO_DRV_FAILURE_EOF:
+ {
+ driver_failure_eof(port);
+ break;
+ }
+ case ECHO_DRV_FAILURE_ATOM:
+ {
+ driver_failure_atom(port, buf+1);
+ break;
+ }
+ case ECHO_DRV_FAILURE_POSIX:
+ {
+ driver_failure_posix(port, EAGAIN);
+ break;
+ }
+ case ECHO_DRV_FAILURE:
+ {
+ driver_failure(port, buf[1]);
+ break;
+ }
+ case ECHO_DRV_OUTPUT_TERM:
+ case ECHO_DRV_DRIVER_OUTPUT_TERM:
+ case ECHO_DRV_SEND_TERM:
+ case ECHO_DRV_DRIVER_SEND_TERM:
+ {
+ ErlDrvTermData term[] = {
+ ERL_DRV_ATOM, driver_mk_atom("echo"),
+ ERL_DRV_PORT, driver_mk_port(port),
+ ERL_DRV_BUF2BINARY, (ErlDrvTermData)(buf+1),
+ (ErlDrvTermData)(len - 1),
+ ERL_DRV_TUPLE, 3};
+ switch (buf[0]) {
+ case ECHO_DRV_OUTPUT_TERM:
+ erl_drv_output_term(driver_mk_port(port), term, sizeof(term) / sizeof(ErlDrvTermData));
+ break;
+ case ECHO_DRV_DRIVER_OUTPUT_TERM:
+ driver_output_term(port, term, sizeof(term) / sizeof(ErlDrvTermData));
+ break;
+ case ECHO_DRV_SEND_TERM:
+ driver_send_term(port, data_p->caller,
+ term, sizeof(term) / sizeof(ErlDrvTermData));
+ break;
+ case ECHO_DRV_DRIVER_SEND_TERM:
+ erl_drv_send_term(driver_mk_port(port), data_p->caller,
+ term, sizeof(term) / sizeof(ErlDrvTermData));
+ break;
+ }
+ break;
+ }
+ case ECHO_DRV_REMOTE_SEND_TERM:
+ {
+ struct remote_send_term *t = driver_alloc(sizeof(struct remote_send_term) + len);
+ t->len = len-1;
+ t->port = driver_mk_port(port);
+ t->caller = data_p->caller;
+ memcpy(t->buf, buf+1, t->len);
+ erl_drv_thread_create("tmp_thread", &t->thread.tid, send_term_thread, t, NULL);
+ t->thread.next = data_p->threads;
+ data_p->threads = &t->thread;
+ break;
+ }
+ case ECHO_DRV_SAVE_CALLER:
+ data_p->caller = driver_caller(port);
+ break;
+ default:
+ break;
+ }
+}
+
+static void echo_drv_finish() {
+
+}
+
+static ErlDrvSSizeT echo_drv_control(ErlDrvData drv_data,
+ unsigned int command,
+ char *buf, ErlDrvSizeT len,
+ char **rbuf, ErlDrvSizeT rlen)
+{
+ if ((len - 1) > rlen)
+ *rbuf = driver_alloc(len - 1);
+ memcpy(*rbuf, buf+1, len-1);
+ return len-1;
+}
+
+static void echo_drv_timeout(ErlDrvData drv_data)
+{
+
+}
+
+static ErlDrvSSizeT echo_drv_call(ErlDrvData drv_data,
+ unsigned int command,
+ char *buf, ErlDrvSizeT len,
+ char **rbuf, ErlDrvSizeT rlen,
+ unsigned int *flags)
+{
+ if ((len - command) > rlen)
+ *rbuf = driver_alloc(len - command);
+ memcpy(*rbuf, buf+command, len-command);
+ return len-command;
+}
+
+static void* send_term_thread(void *a)
+{
+ struct remote_send_term *t = (struct remote_send_term*)a;
+ ErlDrvTermData term[] = {
+ ERL_DRV_ATOM, driver_mk_atom("echo"),
+ ERL_DRV_PORT, t->port,
+ ERL_DRV_BUF2BINARY, (ErlDrvTermData)(t->buf),
+ (ErlDrvTermData)(t->len),
+ ERL_DRV_TUPLE, 3};
+ erl_drv_send_term(t->port, t->caller,
+ term, sizeof(term) / sizeof(ErlDrvTermData));
+ return NULL;
+}
diff --git a/erts/emulator/test/prim_eval_SUITE.erl b/erts/emulator/test/prim_eval_SUITE.erl
new file mode 100644
index 0000000000..3f4965f96d
--- /dev/null
+++ b/erts/emulator/test/prim_eval_SUITE.erl
@@ -0,0 +1,78 @@
+%%
+%% %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(prim_eval_SUITE).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2,
+ init_per_group/2, end_per_group/2]).
+
+-export(['ERL-365'/1]).
+
+init_per_testcase(_Case, Config) ->
+ Config.
+
+end_per_testcase(_Case, _Config) ->
+ ok.
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{minutes,1}}].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+all() ->
+ ['ERL-365'].
+
+'ERL-365'(Config) when is_list(Config) ->
+ %% def_arg_reg[0] is used for storage of timeout instruction
+ %% when a 'receive after' is executed. When a process was
+ %% scheduled out inside prim_eval:'receive'/0 due to a function
+ %% call, def_arg_reg[0] was overwritten due to storage of live
+ %% registers.
+ P = spawn_link(fun () ->
+ prim_eval:'receive'(fun (_M) ->
+ erlang:bump_reductions((1 bsl 27)-1),
+ id(true),
+ nomatch
+ end,
+ 200)
+ end),
+ receive after 100 -> ok end,
+ P ! {wont, match},
+ receive after 200 -> ok end,
+ ok.
+
+
+
+id(X) ->
+ X.
diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl
index 97aa5e573e..ab1587a90c 100644
--- a/erts/emulator/test/process_SUITE.erl
+++ b/erts/emulator/test/process_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. 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.
@@ -26,7 +26,7 @@
%% process_info/1,2
%% register/2 (partially)
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-define(heap_binary_size, 64).
@@ -41,11 +41,12 @@
process_info_2_list/1, process_info_lock_reschedule/1,
process_info_lock_reschedule2/1,
process_info_lock_reschedule3/1,
+ process_info_garbage_collection/1,
bump_reductions/1, low_prio/1, binary_owner/1, yield/1, yield2/1,
process_status_exiting/1,
otp_4725/1, bad_register/1, garbage_collect/1, otp_6237/1,
process_info_messages/1, process_flag_badarg/1, process_flag_heap_size/1,
- spawn_opt_heap_size/1,
+ spawn_opt_heap_size/1, spawn_opt_max_heap_size/1,
processes_large_tab/1, processes_default_tab/1, processes_small_tab/1,
processes_this_tab/1, processes_apply_trap/1,
processes_last_call_trap/1, processes_gc_trap/1,
@@ -59,14 +60,16 @@
system_task_on_suspended/1,
gc_request_when_gc_disabled/1,
gc_request_blast_when_gc_disabled/1]).
--export([prio_server/2, prio_client/2]).
+-export([prio_server/2, prio_client/2, init/1, handle_event/2]).
-export([init_per_testcase/2, end_per_testcase/2]).
-export([hangaround/2, processes_bif_test/0, do_processes/1,
processes_term_proc_list_test/1]).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 9}}].
all() ->
[spawn_with_binaries, t_exit_1, {group, t_exit_2},
@@ -75,11 +78,14 @@ all() ->
process_info_other_dist_msg, process_info_2_list,
process_info_lock_reschedule,
process_info_lock_reschedule2,
- process_info_lock_reschedule3, process_status_exiting,
+ process_info_lock_reschedule3,
+ process_info_garbage_collection,
+ process_status_exiting,
bump_reductions, low_prio, yield, yield2, otp_4725,
bad_register, garbage_collect, process_info_messages,
process_flag_badarg, process_flag_heap_size,
- spawn_opt_heap_size, otp_6237, {group, processes_bif},
+ spawn_opt_heap_size, spawn_opt_max_heap_size, otp_6237,
+ {group, processes_bif},
{group, otp_7738}, garb_other_running,
{group, system_task}].
@@ -113,7 +119,7 @@ init_per_suite(Config) ->
[{started_apps, A}|Config].
end_per_suite(Config) ->
- As = ?config(started_apps, Config),
+ As = proplists:get_value(started_apps, Config),
lists:foreach(fun (A) -> application:stop(A) end, As),
catch erts_debug:set_internal_state(available_internal_state, false),
Config.
@@ -125,12 +131,10 @@ end_per_group(_GroupName, Config) ->
Config.
init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- Dog=?t:timetrap(?t:minutes(10)),
- [{watchdog, Dog},{testcase, Func}|Config].
+ [{testcase, Func}|Config].
end_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- Dog=?config(watchdog, Config),
- ?t:timetrap_cancel(Dog).
+ ok.
fun_spawn(Fun) ->
spawn_link(erlang, apply, [Fun, []]).
@@ -143,11 +147,7 @@ spawn_with_binaries(Config) when is_list(Config) ->
TwoMeg = lists:duplicate(1024, L),
Fun = fun() -> spawn(?MODULE, binary_owner, [list_to_binary(TwoMeg)]),
receive after 1 -> ok end end,
- Iter = case test_server:purify_is_running() of
- true -> 10;
- false -> 150
- end,
- test_server:do_times(Iter, Fun),
+ test_server:do_times(150, Fun),
ok.
binary_owner(Bin) when is_binary(Bin) ->
@@ -155,11 +155,10 @@ binary_owner(Bin) when is_binary(Bin) ->
%% Tests exit/1 with a big message.
t_exit_1(Config) when is_list(Config) ->
+ ct:timetrap({seconds, 20}),
start_spawner(),
- Dog = test_server:timetrap(test_server:seconds(20)),
process_flag(trap_exit, true),
test_server:do_times(10, fun t_exit_1/0),
- test_server:timetrap_cancel(Dog),
stop_spawner(),
ok.
@@ -173,11 +172,10 @@ t_exit_1() ->
%% Tests exit/2 with a lot of data in the exit message.
t_exit_2_other(Config) when is_list(Config) ->
+ ct:timetrap({seconds, 20}),
start_spawner(),
- Dog = test_server:timetrap(test_server:seconds(20)),
process_flag(trap_exit, true),
test_server:do_times(10, fun t_exit_2_other/0),
- test_server:timetrap_cancel(Dog),
stop_spawner(),
ok.
@@ -191,34 +189,32 @@ t_exit_2_other() ->
%% Tests that exit(Pid, normal) does not kill another process.;
t_exit_2_other_normal(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(20)),
+ ct:timetrap({seconds, 20}),
process_flag(trap_exit, true),
Pid = fun_spawn(fun() -> receive x -> ok end end),
exit(Pid, normal),
receive
{'EXIT', Pid, Reason} ->
- test_server:fail({process_died, Reason})
+ ct:fail({process_died, Reason})
after 1000 ->
ok
end,
case process_info(Pid) of
undefined ->
- test_server:fail(process_died_on_normal);
+ ct:fail(process_died_on_normal);
List when is_list(List) ->
ok
end,
exit(Pid, kill),
- test_server:timetrap_cancel(Dog),
ok.
%% Tests that we can trap an exit message sent with exit/2 from
%% the same process.
self_exit(Config) when is_list(Config) ->
+ ct:timetrap({seconds, 10}),
start_spawner(),
- Dog = test_server:timetrap(test_server:seconds(10)),
process_flag(trap_exit, true),
test_server:do_times(200, fun self_exit/0),
- test_server:timetrap_cancel(Dog),
stop_spawner(),
ok.
@@ -237,7 +233,7 @@ normal_suicide_exit(Config) when is_list(Config) ->
Pid = fun_spawn(fun() -> exit(self(), normal) end),
receive
{'EXIT', Pid, normal} -> ok;
- Other -> test_server:fail({bad_message, Other})
+ Other -> ct:fail({bad_message, Other})
end.
%% Tests exit(self(), Term) is equivalent to exit(Term) for a process
@@ -248,7 +244,7 @@ abnormal_suicide_exit(Config) when is_list(Config) ->
Pid = fun_spawn(fun() -> exit(self(), Garbage) end),
receive
{'EXIT', Pid, Garbage} -> ok;
- Other -> test_server:fail({bad_message, Other})
+ Other -> ct:fail({bad_message, Other})
end.
%% Tests that exit(self(), die) cannot be catched.
@@ -257,21 +253,20 @@ t_exit_2_catch(Config) when is_list(Config) ->
Pid = fun_spawn(fun() -> catch exit(self(), die) end),
receive
{'EXIT', Pid, normal} ->
- test_server:fail(catch_worked);
+ ct:fail(catch_worked);
{'EXIT', Pid, die} ->
ok;
Other ->
- test_server:fail({bad_message, Other})
+ ct:fail({bad_message, Other})
end.
%% Tests trapping of an 'EXIT' message generated by a bad argument to
%% the abs/1 bif. The 'EXIT' message will intentionally be very big.
trap_exit_badarg(Config) when is_list(Config) ->
+ ct:timetrap({seconds, 10}),
start_spawner(),
- Dog = test_server:timetrap(test_server:seconds(10)),
process_flag(trap_exit, true),
test_server:do_times(10, fun trap_exit_badarg/0),
- test_server:timetrap_cancel(Dog),
stop_spawner(),
ok.
@@ -285,7 +280,7 @@ trap_exit_badarg() ->
ok;
Other ->
ok = io:format("Bad EXIT message: ~P", [Other, 30]),
- test_server:fail(bad_exit_message)
+ ct:fail(bad_exit_message)
end.
bad_guy(Arg) ->
@@ -317,10 +312,9 @@ big_binary(N, Acc) ->
%% Test receiving an EXIT message when spawning a BIF with bad arguments.
trap_exit_badarg_in_bif(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(10)),
+ ct:timetrap({seconds, 10}),
process_flag(trap_exit, true),
test_server:do_times(10, fun trap_exit_badarg_bif/0),
- test_server:timetrap_cancel(Dog),
ok.
trap_exit_badarg_bif() ->
@@ -329,7 +323,7 @@ trap_exit_badarg_bif() ->
{'EXIT', Pid, {badarg, _}} ->
ok;
Other ->
- test_server:fail({unexpected, Other})
+ ct:fail({unexpected, Other})
end.
%% The following sequences of events have crasched Beam.
@@ -342,15 +336,13 @@ trap_exit_badarg_bif() ->
%% 3) The process will crash the next time it executes 'receive'.
exit_and_timeout(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(20)),
+ ct:timetrap({seconds, 20}),
process_flag(trap_exit, true),
Parent = self(),
Low = fun_spawn(fun() -> eat_low(Parent) end),
High = fun_spawn(fun() -> eat_high(Low) end),
eat_wait_for(Low, High),
-
- test_server:timetrap_cancel(Dog),
ok.
@@ -361,7 +353,7 @@ eat_wait_for(Low, High) ->
{'EXIT', High, normal} ->
eat_wait_for(Low, High);
Other ->
- test_server:fail({bad_message, Other})
+ ct:fail({bad_message, Other})
end.
eat_low(_Parent) ->
@@ -380,7 +372,7 @@ eat_high(Low) ->
process_flag(priority, high),
receive after 1000 -> ok end,
exit(Low, {you, are, dead}),
- loop(erlang:monotonic_time() + erlang:convert_time_unit(5,seconds,native)).
+ loop(erlang:monotonic_time() + erlang:convert_time_unit(5,second,native)).
%% Busy loop for 5 seconds.
@@ -394,14 +386,12 @@ loop(StopTime) ->
%% Tries to send two different exit messages to a process.
%% (The second one should be ignored.)
exit_twice(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(20)),
+ ct:timetrap({seconds, 20}),
process_flag(trap_exit, true),
Low = fun_spawn(fun etwice_low/0),
High = fun_spawn(fun() -> etwice_high(Low) end),
etwice_wait_for(Low, High),
-
- test_server:timetrap_cancel(Dog),
ok.
etwice_wait_for(Low, High) ->
@@ -409,11 +399,11 @@ etwice_wait_for(Low, High) ->
{'EXIT', Low, first} ->
ok;
{'EXIT', Low, Other} ->
- test_server:fail({wrong_exit_reason, Other});
+ ct:fail({wrong_exit_reason, Other});
{'EXIT', High, normal} ->
etwice_wait_for(Low, High);
Other ->
- test_server:fail({bad_message, Other})
+ ct:fail({bad_message, Other})
end.
etwice_low() ->
@@ -432,6 +422,8 @@ t_process_info(Config) when is_list(Config) ->
{status, running} = process_info(self(), status),
{min_heap_size, 233} = process_info(self(), min_heap_size),
{min_bin_vheap_size,46422} = process_info(self(), min_bin_vheap_size),
+ {max_heap_size, #{ size := 0, kill := true, error_logger := true}} =
+ process_info(self(), max_heap_size),
{current_function,{?MODULE,t_process_info,1}} =
process_info(self(), current_function),
{current_function,{?MODULE,t_process_info,1}} =
@@ -445,11 +437,22 @@ t_process_info(Config) when is_list(Config) ->
verify_loc(Line2, Res2),
pi_stacktrace([{?MODULE,t_process_info,1,?LINE}]),
+ verify_stacktrace_depth(),
+
Gleader = group_leader(),
{group_leader, Gleader} = process_info(self(), group_leader),
{'EXIT',{badarg,_Info}} = (catch process_info('not_a_pid')),
ok.
+verify_stacktrace_depth() ->
+ CS = current_stacktrace,
+ OldDepth = erlang:system_flag(backtrace_depth, 0),
+ {CS,[]} = erlang:process_info(self(), CS),
+ _ = erlang:system_flag(backtrace_depth, 8),
+ {CS,[{?MODULE,verify_stacktrace_depth,0,_},_|_]} =
+ erlang:process_info(self(), CS),
+ _ = erlang:system_flag(backtrace_depth, OldDepth).
+
pi_stacktrace(Expected0) ->
{Line,Res} = {?LINE,erlang:process_info(self(), current_stacktrace)},
{current_stacktrace,Stack} = Res,
@@ -571,6 +574,8 @@ process_info_other_msg(Config) when is_list(Config) ->
{min_heap_size, 233} = process_info(Pid, min_heap_size),
{min_bin_vheap_size, 46422} = process_info(Pid, min_bin_vheap_size),
+ {max_heap_size, #{ size := 0, kill := true, error_logger := true}} =
+ process_info(self(), max_heap_size),
Pid ! stop,
ok.
@@ -666,10 +671,6 @@ chk_pi_order([],[]) ->
chk_pi_order([{Arg, _}| Values], [Arg|Args]) ->
chk_pi_order(Values, Args).
-process_info_2_list(doc) ->
- [];
-process_info_2_list(suite) ->
- [];
process_info_2_list(Config) when is_list(Config) ->
Proc = spawn(fun () -> receive after infinity -> ok end end),
register(process_SUITE_process_info_2_list1, self()),
@@ -702,10 +703,6 @@ process_info_2_list(Config) when is_list(Config) ->
lists:foreach(fun ({backtrace, _}) -> ok end, V3),
ok.
-process_info_lock_reschedule(doc) ->
- [];
-process_info_lock_reschedule(suite) ->
- [];
process_info_lock_reschedule(Config) when is_list(Config) ->
%% We need a process that is running and an item that requires
%% process_info to take the main process lock.
@@ -738,7 +735,7 @@ process_info_lock_reschedule(Config) when is_list(Config) ->
exit(Target2, bang),
OkStatus;
{status, BadStatus} ->
- ?t:fail(BadStatus)
+ ct:fail(BadStatus)
end.
pi_loop(_Name, _Pid, 0) ->
@@ -747,10 +744,6 @@ pi_loop(Name, Pid, N) ->
{registered_name, Name} = process_info(Pid, registered_name),
pi_loop(Name, Pid, N-1).
-process_info_lock_reschedule2(doc) ->
- [];
-process_info_lock_reschedule2(suite) ->
- [];
process_info_lock_reschedule2(Config) when is_list(Config) ->
Parent = self(),
Fun = fun () ->
@@ -806,10 +799,6 @@ do_pi_msg_len(PT, AT) ->
lists:map(fun (_) -> ok end, [a,b,c,d]),
{message_queue_len, _} = process_info(element(2,PT), element(2,AT)).
-process_info_lock_reschedule3(doc) ->
- [];
-process_info_lock_reschedule3(suite) ->
- [];
process_info_lock_reschedule3(Config) when is_list(Config) ->
%% We need a process that is running and an item that requires
%% process_info to take the main process lock.
@@ -840,7 +829,7 @@ process_info_lock_reschedule3(Config) when is_list(Config) ->
exit(Target2, bang),
OkStatus;
{status, BadStatus} ->
- ?t:fail(BadStatus)
+ ct:fail(BadStatus)
end.
process_status_exiting(Config) when is_list(Config) ->
@@ -932,6 +921,73 @@ start_spawner() ->
stop_spawner() ->
ok.
+%% Tests erlang:process_info(Pid, garbage_collection_info)
+process_info_garbage_collection(_Config) ->
+ Parent = self(),
+ Pid = spawn_link(
+ fun() ->
+ %% We set mqd to off_heap and send an tuple
+ %% to process in order to force mbuf_size
+ %% to be used
+ process_flag(message_queue_data, off_heap),
+ receive go -> ok end,
+ (fun F(0) ->
+ Parent ! deep,
+ receive {ok,_} -> ok end,
+ [];
+ F(N) ->
+ timer:sleep(1),
+ [lists:seq(1,100) | F(N-1)]
+ end)(1000),
+ Parent ! shallow,
+ receive done -> ok end
+ end),
+ [{garbage_collection_info, Before},{total_heap_size, THSBefore}] =
+ erlang:process_info(Pid, [garbage_collection_info, total_heap_size]),
+ Pid ! go, receive deep -> ok end,
+ [{_, Deep},{_,THSDeep}] =
+ erlang:process_info(Pid, [garbage_collection_info, total_heap_size]),
+ Pid ! {ok, make_ref()}, receive shallow -> ok end,
+ [{_, After},{_, THSAfter}] =
+ erlang:process_info(Pid, [garbage_collection_info, total_heap_size]),
+ Pid ! done,
+
+ %% Do some general checks to see if everything seems to be roughly correct
+ ct:log("Before: ~p",[Before]),
+ ct:log("Deep: ~p",[Deep]),
+ ct:log("After: ~p",[After]),
+ ct:log("Before THS: ~p",[THSBefore]),
+ ct:log("Deep THS: ~p",[THSDeep]),
+ ct:log("After THS: ~p",[THSAfter]),
+
+ %% Check stack_size
+ true = gv(stack_size, Before) < gv(stack_size, Deep),
+ true = gv(stack_size, After) < gv(stack_size, Deep),
+
+ %% Check used heap size
+ true = gv(heap_size, Before) + gv(old_heap_size, Before)
+ < gv(heap_size, Deep) + gv(old_heap_size, Deep),
+ true = gv(heap_size, Before) + gv(old_heap_size, Before)
+ < gv(heap_size, After) + gv(old_heap_size, After),
+
+ %% Check that total_heap_size == heap_block_size + old_heap_block_size + mbuf_size
+ THSBefore = gv(heap_block_size, Before)
+ + gv(old_heap_block_size, Before)
+ + gv(mbuf_size, Before),
+
+ THSDeep = gv(heap_block_size, Deep)
+ + gv(old_heap_block_size, Deep)
+ + gv(mbuf_size, Deep),
+
+ THSAfter = gv(heap_block_size, After)
+ + gv(old_heap_block_size, After)
+ + gv(mbuf_size, After),
+
+ ok.
+
+gv(Key,List) ->
+ proplists:get_value(Key,List).
+
%% Tests erlang:bump_reductions/1.
bump_reductions(Config) when is_list(Config) ->
erlang:garbage_collect(),
@@ -942,10 +998,10 @@ bump_reductions(Config) when is_list(Config) ->
case R2-R1 of
Diff when Diff < 100 ->
ok = io:format("R1 = ~w, R2 = ~w", [R1, R2]),
- test_server:fail({small_diff, Diff});
+ ct:fail({small_diff, Diff});
Diff when Diff > 110 ->
ok = io:format("R1 = ~w, R2 = ~w", [R1, R2]),
- test_server:fail({big_diff, Diff});
+ ct:fail({big_diff, Diff});
Diff ->
io:format("~p\n", [Diff]),
ok
@@ -984,7 +1040,7 @@ low_prio_test(Config) when is_list(Config) ->
process_flag(trap_exit, true),
S = spawn_link(?MODULE, prio_server, [0, 0]),
PCs = spawn_prio_clients(S, erlang:system_info(schedulers_online)),
- timer:sleep(2000),
+ ct:sleep({seconds,3}),
lists:foreach(fun (P) -> exit(P, kill) end, PCs),
S ! exit,
receive {'EXIT', S, {A, B}} -> check_prio(A, B) end,
@@ -1032,8 +1088,7 @@ make_unaligned_sub_binary(Bin0) ->
<<0:3,Bin:Sz/binary,31:5>> = id(Bin1),
Bin.
-yield(doc) ->
- "Tests erlang:yield/1.";
+%% Tests erlang:yield/1
yield(Config) when is_list(Config) ->
case catch erlang:system_info(modified_timing_level) of
Level when is_integer(Level) ->
@@ -1074,7 +1129,7 @@ yield_test() ->
{Diff, _} ->
ok = io:format("R1 = ~w, R2 = ~w, Schedcnt = ~w",
[R1, R2, Schedcnt]),
- test_server:fail({measurement_error, Diff, Schedcnt})
+ ct:fail({measurement_error, Diff, Schedcnt})
end.
call_yield() ->
@@ -1111,8 +1166,6 @@ schedcnt(stop, {Ref, Pid}) when is_reference(Ref), is_pid(Pid) ->
Cnt
end.
-yield2(doc) -> [];
-yield2(suite) -> [];
yield2(Config) when is_list(Config) ->
Me = self(),
Go = make_ref(),
@@ -1163,7 +1216,7 @@ yield2(Config) when is_list(Config) ->
io:format("Reductions = ~p~n", [Reductions]),
ok;
{RedDiff, Reductions} ->
- ?t:fail({unexpected_reduction_count, Reductions})
+ ct:fail({unexpected_reduction_count, Reductions})
end,
none = next_tmsg(P),
@@ -1204,8 +1257,6 @@ fail_register(Name, Process) ->
{'EXIT',{badarg,_}} = (catch Name ! anything_goes),
ok.
-garbage_collect(doc) -> [];
-garbage_collect(suite) -> [];
garbage_collect(Config) when is_list(Config) ->
Prio = process_flag(priority, high),
true = erlang:garbage_collect(),
@@ -1244,10 +1295,7 @@ garbage_collect(Config) when is_list(Config) ->
process_flag(priority, Prio),
ok.
-process_info_messages(doc) ->
- ["This used to cause the nofrag emulator to dump core"];
-process_info_messages(suite) ->
- [];
+%% This used to cause the nofrag emulator to dump core
process_info_messages(Config) when is_list(Config) ->
process_info_messages_test(),
ok.
@@ -1305,10 +1353,6 @@ process_info_messages_test() ->
chk_badarg(Fun) ->
try Fun(), exit(no_badarg) catch error:badarg -> ok end.
-process_flag_badarg(doc) ->
- [];
-process_flag_badarg(suite) ->
- [];
process_flag_badarg(Config) when is_list(Config) ->
chk_badarg(fun () -> process_flag(gurka, banan) end),
chk_badarg(fun () -> process_flag(trap_exit, gurka) end),
@@ -1316,6 +1360,28 @@ process_flag_badarg(Config) when is_list(Config) ->
chk_badarg(fun () -> process_flag(min_heap_size, gurka) end),
chk_badarg(fun () -> process_flag(min_bin_vheap_size, gurka) end),
chk_badarg(fun () -> process_flag(min_bin_vheap_size, -1) end),
+
+ chk_badarg(fun () -> process_flag(max_heap_size, gurka) end),
+ chk_badarg(fun () -> process_flag(max_heap_size, -1) end),
+ chk_badarg(fun () ->
+ {_,Min} = process_info(self(), min_heap_size),
+ process_flag(max_heap_size, Min - 1)
+ end),
+ chk_badarg(fun () ->
+ {_,Min} = process_info(self(), min_heap_size),
+ process_flag(max_heap_size, #{size => Min - 1})
+ end),
+ chk_badarg(fun () -> process_flag(max_heap_size, #{}) end),
+ chk_badarg(fun () -> process_flag(max_heap_size, #{ kill => true }) end),
+ chk_badarg(fun () -> process_flag(max_heap_size, #{ size => 233,
+ kill => gurka }) end),
+ chk_badarg(fun () -> process_flag(max_heap_size, #{ size => 233,
+ error_logger => gurka }) end),
+ chk_badarg(fun () -> process_flag(max_heap_size, #{ size => 233,
+ kill => true,
+ error_logger => gurka }) end),
+ chk_badarg(fun () -> process_flag(max_heap_size, #{ size => 1 bsl 64 }) end),
+
chk_badarg(fun () -> process_flag(priority, 4711) end),
chk_badarg(fun () -> process_flag(save_calls, hmmm) end),
P= spawn_link(fun () -> receive die -> ok end end),
@@ -1326,8 +1392,6 @@ process_flag_badarg(Config) when is_list(Config) ->
-include_lib("stdlib/include/ms_transform.hrl").
-otp_6237(doc) -> [];
-otp_6237(suite) -> [];
otp_6237(Config) when is_list(Config) ->
Slctrs = lists:map(fun (_) ->
spawn_link(fun () ->
@@ -1394,10 +1458,6 @@ otp_6237_select_loop() ->
conses_per_red,
debug_level}).
-processes_large_tab(doc) ->
- [];
-processes_large_tab(suite) ->
- [];
processes_large_tab(Config) when is_list(Config) ->
sys_mem_cond_run(2048, fun () -> processes_large_tab_test(Config) end).
@@ -1425,7 +1485,7 @@ processes_large_tab_test(Config) ->
#ptab_list_bif_info{debug_level = Lvl} when Lvl > MaxDbgLvl ->
20;
#ptab_list_bif_info{debug_level = Lvl} when Lvl < 0 ->
- ?t:fail({debug_level, Lvl});
+ ct:fail({debug_level, Lvl});
#ptab_list_bif_info{debug_level = Lvl} ->
Lvl
end,
@@ -1443,15 +1503,11 @@ processes_large_tab_test(Config) ->
[processes_bif_info]) of
#ptab_list_bif_info{tab_chunks = Chunks} when is_integer(Chunks),
Chunks > 1 -> ok;
- PBInfo -> ?t:fail(PBInfo)
+ PBInfo -> ct:fail(PBInfo)
end,
stop_node(LargeNode),
chk_processes_bif_test_res(Res).
-processes_default_tab(doc) ->
- [];
-processes_default_tab(suite) ->
- [];
processes_default_tab(Config) when is_list(Config) ->
sys_mem_cond_run(1024, fun () -> processes_default_tab_test(Config) end).
@@ -1461,10 +1517,6 @@ processes_default_tab_test(Config) ->
stop_node(DefaultNode),
chk_processes_bif_test_res(Res).
-processes_small_tab(doc) ->
- [];
-processes_small_tab(suite) ->
- [];
processes_small_tab(Config) when is_list(Config) ->
{ok, SmallNode} = start_node(Config, "+P 1024"),
Res = rpc:call(SmallNode, ?MODULE, processes_bif_test, []),
@@ -1473,10 +1525,6 @@ processes_small_tab(Config) when is_list(Config) ->
true = PBInfo#ptab_list_bif_info.tab_chunks < 10,
chk_processes_bif_test_res(Res).
-processes_this_tab(doc) ->
- [];
-processes_this_tab(suite) ->
- [];
processes_this_tab(Config) when is_list(Config) ->
Mem = case {erlang:system_info(build_type),
erlang:system_info(allocator)} of
@@ -1490,7 +1538,7 @@ processes_this_tab(Config) when is_list(Config) ->
chk_processes_bif_test_res(ok) -> ok;
chk_processes_bif_test_res({comment, _} = Comment) -> Comment;
-chk_processes_bif_test_res(Failure) -> ?t:fail(Failure).
+chk_processes_bif_test_res(Failure) -> ct:fail(Failure).
print_processes_bif_info(#ptab_list_bif_info{min_start_reds = MinStartReds,
tab_chunks = TabChunks,
@@ -1501,7 +1549,7 @@ print_processes_bif_info(#ptab_list_bif_info{min_start_reds = MinStartReds,
term_procs_max_reds = TPMaxReds,
conses_per_red = ConsesPerRed,
debug_level = DbgLvl}) ->
- ?t:format("processes/0 bif info on node ~p:~n"
+ io:format("processes/0 bif info on node ~p:~n"
"Min start reductions = ~p~n"
"Process table chunks = ~p~n"
"Process table chunks size = ~p~n"
@@ -1542,7 +1590,7 @@ processes_unexpected_result(CorrectProcs, Procs) ->
status,
priority],
MissingProcs = CorrectProcs -- Procs,
- ?t:format("Missing processes: ~p",
+ io:format("Missing processes: ~p",
[lists:map(fun (Pid) ->
[{pid, Pid}
| case process_info(Pid, ProcInfo) of
@@ -1552,7 +1600,7 @@ processes_unexpected_result(CorrectProcs, Procs) ->
end,
MissingProcs)]),
SuperfluousProcs = Procs -- CorrectProcs,
- ?t:format("Superfluous processes: ~p",
+ io:format("Superfluous processes: ~p",
[lists:map(fun (Pid) ->
[{pid, Pid}
| case process_info(Pid, ProcInfo) of
@@ -1561,7 +1609,7 @@ processes_unexpected_result(CorrectProcs, Procs) ->
end]
end,
SuperfluousProcs)]),
- ?t:fail(unexpected_result).
+ ct:fail(unexpected_result).
hangaround(Cleaner, Type) ->
%% Type is only used to distinguish different processes from
@@ -1574,6 +1622,7 @@ spawn_initial_hangarounds(_Cleaner, NP, Max, Len, HAs) when NP > Max ->
{Len, HAs};
spawn_initial_hangarounds(Cleaner, NP, Max, Len, HAs) ->
Skip = 30,
+ wait_for_proc_slots(Skip+3),
HA1 = spawn_opt(?MODULE, hangaround, [Cleaner, initial_hangaround],
[{priority, low}]),
HA2 = spawn_opt(?MODULE, hangaround, [Cleaner, initial_hangaround],
@@ -1583,6 +1632,15 @@ spawn_initial_hangarounds(Cleaner, NP, Max, Len, HAs) ->
spawn_drop(Skip),
spawn_initial_hangarounds(Cleaner, NP+Skip, Max, Len+3, [HA1,HA2,HA3|HAs]).
+wait_for_proc_slots(MinFreeSlots) ->
+ case erlang:system_info(process_limit) - erlang:system_info(process_count) of
+ FreeSlots when FreeSlots < MinFreeSlots ->
+ receive after 10 -> ok end,
+ wait_for_proc_slots(MinFreeSlots);
+ _FreeSlots ->
+ ok
+ end.
+
spawn_drop(N) when N =< 0 ->
ok;
spawn_drop(N) ->
@@ -1666,7 +1724,7 @@ do_processes_bif_test(WantReds, DieTest, Processes) ->
DoIt = make_ref(),
GetGoing = make_ref(),
{NoTestProcs, TestProcs} = spawn_initial_hangarounds(Cleaner),
- ?t:format("Testing with ~p processes~n", [NoTestProcs]),
+ io:format("Testing with ~p processes~n", [NoTestProcs]),
SpawnHangAround = fun () ->
spawn(?MODULE, hangaround, [Cleaner, new_hangaround])
end,
@@ -1708,7 +1766,7 @@ do_processes_bif_test(WantReds, DieTest, Processes) ->
Procs = lists:sort(Procs0),
CorrectProcs = lists:sort(CorrectProcs0),
LengthCorrectProcs = length(CorrectProcs),
- ?t:format("~p = length(CorrectProcs)~n", [LengthCorrectProcs]),
+ io:format("~p = length(CorrectProcs)~n", [LengthCorrectProcs]),
true = LengthCorrectProcs > NoTestProcs,
case CorrectProcs =:= Procs of
true ->
@@ -1729,12 +1787,12 @@ do_processes_bif_test(WantReds, DieTest, Processes) ->
do_processes_bif_die_test(false, _Processes) ->
- ?t:format("Skipping test killing process executing processes/0~n",[]),
+ io:format("Skipping test killing process executing processes/0~n",[]),
ok;
do_processes_bif_die_test(true, Processes) ->
do_processes_bif_die_test(5, Processes);
do_processes_bif_die_test(N, Processes) ->
- ?t:format("Doing test killing process executing processes/0~n",[]),
+ io:format("Doing test killing process executing processes/0~n",[]),
try
Tester = self(),
Oooh_Nooooooo = make_ref(),
@@ -1784,8 +1842,8 @@ do_processes_bif_die_test(N, Processes) ->
ok
catch
throw:{kill_in_trap, R} when N > 0 ->
- ?t:format("Failed to kill in trap: ~p~n", [R]),
- ?t:format("Trying again~n", []),
+ io:format("Failed to kill in trap: ~p~n", [R]),
+ io:format("Trying again~n", []),
do_processes_bif_die_test(N-1, Processes)
end.
@@ -1815,7 +1873,7 @@ wait_until_system_recover(Tmr) ->
receive
{timeout, Tmr, _} ->
Comment = "WARNING: Test processes still hanging around!",
- ?t:format("~s~n", [Comment]),
+ io:format("~s~n", [Comment]),
put(processes_bif_testcase_comment, Comment),
lists:foreach(
fun (P) when P == self() ->
@@ -1823,7 +1881,7 @@ wait_until_system_recover(Tmr) ->
(P) ->
case process_info(P, initial_call) of
{initial_call,{?MODULE, _, _} = MFA} ->
- ?t:format("~p ~p~n", [P, MFA]);
+ io:format("~p ~p~n", [P, MFA]);
{initial_call,{_, _, _}} ->
ok;
undefined ->
@@ -1839,10 +1897,6 @@ wait_until_system_recover(Tmr) ->
receive {timeout, Tmr, _} -> ok after 0 -> ok end,
ok.
-processes_last_call_trap(doc) ->
- [];
-processes_last_call_trap(suite) ->
- [];
processes_last_call_trap(Config) when is_list(Config) ->
enable_internal_state(),
Processes = fun () -> processes() end,
@@ -1865,10 +1919,6 @@ processes_last_call_trap(Config) when is_list(Config) ->
my_processes() ->
processes().
-processes_apply_trap(doc) ->
- [];
-processes_apply_trap(suite) ->
- [];
processes_apply_trap(Config) when is_list(Config) ->
enable_internal_state(),
PBInfo = erts_debug:get_internal_state(processes_bif_info),
@@ -1883,10 +1933,6 @@ processes_apply_trap(Config) when is_list(Config) ->
apply(erlang, processes, [])
end, lists:seq(1,100)).
-processes_gc_trap(doc) ->
- [];
-processes_gc_trap(suite) ->
- [];
processes_gc_trap(Config) when is_list(Config) ->
Tester = self(),
enable_internal_state(),
@@ -1925,10 +1971,6 @@ processes_gc_trap(Config) when is_list(Config) ->
exit(Suspendee, bang),
ok.
-process_flag_heap_size(doc) ->
- [];
-process_flag_heap_size(suite) ->
- [];
process_flag_heap_size(Config) when is_list(Config) ->
HSize = 2586, % must be gc fib+ number
VHSize = 318187, % must be gc fib+ number
@@ -1940,10 +1982,6 @@ process_flag_heap_size(Config) when is_list(Config) ->
VHSize = erlang:process_flag(min_bin_vheap_size, OldVHmin),
ok.
-spawn_opt_heap_size(doc) ->
- [];
-spawn_opt_heap_size(suite) ->
- [];
spawn_opt_heap_size(Config) when is_list(Config) ->
HSize = 987, % must be gc fib+ number
VHSize = 46422, % must be gc fib+ number
@@ -1954,10 +1992,110 @@ spawn_opt_heap_size(Config) when is_list(Config) ->
Pid ! stop,
ok.
-processes_term_proc_list(doc) ->
- [];
-processes_term_proc_list(suite) ->
- [];
+spawn_opt_max_heap_size(_Config) ->
+
+ error_logger:add_report_handler(?MODULE, self()),
+
+ %% Test that numerical limit works
+ max_heap_size_test(1024, 1024, true, true),
+
+ %% Test that map limit works
+ max_heap_size_test(#{ size => 1024 }, 1024, true, true),
+
+ %% Test that no kill is sent
+ max_heap_size_test(#{ size => 1024, kill => false }, 1024, false, true),
+
+ %% Test that no error_logger report is sent
+ max_heap_size_test(#{ size => 1024, error_logger => false }, 1024, true, false),
+
+ %% Test that system_flag works
+ erlang:system_flag(max_heap_size, #{ size => 0, kill => false,
+ error_logger => true}),
+ max_heap_size_test(#{ size => 1024 }, 1024, false, true),
+ max_heap_size_test(#{ size => 1024, kill => true }, 1024, true, true),
+
+ erlang:system_flag(max_heap_size, #{ size => 0, kill => true,
+ error_logger => false}),
+ max_heap_size_test(#{ size => 1024 }, 1024, true, false),
+ max_heap_size_test(#{ size => 1024, error_logger => true }, 1024, true, true),
+
+ erlang:system_flag(max_heap_size, #{ size => 1 bsl 20, kill => true,
+ error_logger => true}),
+ max_heap_size_test(#{ }, 1 bsl 20, true, true),
+
+ erlang:system_flag(max_heap_size, #{ size => 0, kill => true,
+ error_logger => true}),
+
+ %% Test that ordinary case works as expected again
+ max_heap_size_test(1024, 1024, true, true),
+
+ ok.
+
+max_heap_size_test(Option, Size, Kill, ErrorLogger)
+ when map_size(Option) == 0 ->
+ max_heap_size_test([], Size, Kill, ErrorLogger);
+max_heap_size_test(Option, Size, Kill, ErrorLogger)
+ when is_map(Option); is_integer(Option) ->
+ max_heap_size_test([{max_heap_size, Option}], Size, Kill, ErrorLogger);
+max_heap_size_test(Option, Size, Kill, ErrorLogger) ->
+ OomFun = fun F() -> timer:sleep(5),[lists:seq(1,1000)|F()] end,
+ Pid = spawn_opt(OomFun, Option),
+ {max_heap_size, MHSz} = erlang:process_info(Pid, max_heap_size),
+ ct:log("Default: ~p~nOption: ~p~nProc: ~p~n",
+ [erlang:system_info(max_heap_size), Option, MHSz]),
+
+ #{ size := Size} = MHSz,
+
+ Ref = erlang:monitor(process, Pid),
+ if Kill ->
+ receive
+ {'DOWN', Ref, process, Pid, killed} ->
+ ok
+ end;
+ true ->
+ ok
+ end,
+ if ErrorLogger ->
+ receive
+ {error, _, {emulator, _, [Pid|_]}} ->
+ ok
+ end;
+ true ->
+ ok
+ end,
+ if not Kill ->
+ exit(Pid, die),
+ receive
+ {'DOWN', Ref, process, Pid, die} ->
+ ok
+ end,
+ flush();
+ true ->
+ ok
+ end,
+ receive
+ M ->
+ ct:fail({unexpected_message, M})
+ after 10 ->
+ ok
+ end.
+
+flush() ->
+ receive
+ _M ->
+ flush()
+ after 1000 ->
+ ok
+ end.
+
+%% error_logger report handler proxy
+init(Pid) ->
+ {ok, Pid}.
+
+handle_event(Event, Pid) ->
+ Pid ! Event,
+ {ok, Pid}.
+
processes_term_proc_list(Config) when is_list(Config) ->
Tester = self(),
as_expected = processes_term_proc_list_test(false),
@@ -2107,24 +2245,12 @@ processes_term_proc_list_test(MustChk) ->
as_expected.
-otp_7738_waiting(doc) ->
- [];
-otp_7738_waiting(suite) ->
- [];
otp_7738_waiting(Config) when is_list(Config) ->
otp_7738_test(waiting).
-otp_7738_suspended(doc) ->
- [];
-otp_7738_suspended(suite) ->
- [];
otp_7738_suspended(Config) when is_list(Config) ->
otp_7738_test(suspended).
-otp_7738_resume(doc) ->
- [];
-otp_7738_resume(suite) ->
- [];
otp_7738_resume(Config) when is_list(Config) ->
otp_7738_test(resume).
@@ -2193,8 +2319,8 @@ do_otp_7738_test(Type) ->
ok
after 2000 ->
I = process_info(R, [status, message_queue_len]),
- ?t:format("~p~n", [I]),
- ?t:fail(no_progress)
+ io:format("~p~n", [I]),
+ ct:fail(no_progress)
end,
ok.
@@ -2271,7 +2397,7 @@ no_priority_inversion2(Config) when is_list(Config) ->
[{priority, max}, monitor, link])
end,
lists:seq(1, 2*erlang:system_info(schedulers))),
- receive after 500 -> ok end,
+ receive after 2000 -> ok end,
{PL, ML} = spawn_opt(fun () ->
tok_loop()
end,
@@ -2281,7 +2407,7 @@ no_priority_inversion2(Config) when is_list(Config) ->
RH = request_gc(PL, high),
receive
{garbage_collect, _, _} ->
- ?t:fail(unexpected_gc)
+ ct:fail(unexpected_gc)
after 1000 ->
ok
end,
@@ -2390,7 +2516,7 @@ gc_request_when_gc_disabled(Config) when is_list(Config) ->
async = garbage_collect(P, [{async, ReqId}]),
receive
{garbage_collect, ReqId, Result} ->
- ?t:fail({unexpected_gc, Result});
+ ct:fail({unexpected_gc, Result});
{P, gc_state, true} ->
ok
end,
@@ -2460,15 +2586,15 @@ start_node(Config, Args) when is_list(Config) ->
Pa = filename:dirname(code:which(?MODULE)),
Name = list_to_atom(atom_to_list(?MODULE)
++ "-"
- ++ atom_to_list(?config(testcase, Config))
+ ++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))),
- ?t:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]).
+ test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]).
stop_node(Node) ->
- ?t:stop_node(Node).
+ test_server:stop_node(Node).
enable_internal_state() ->
case catch erts_debug:get_internal_state(available_internal_state) of
@@ -2476,7 +2602,10 @@ enable_internal_state() ->
_ -> erts_debug:set_internal_state(available_internal_state, true)
end.
-sys_mem_cond_run(ReqSizeMB, TestFun) when is_integer(ReqSizeMB) ->
+sys_mem_cond_run(OrigReqSizeMB, TestFun) when is_integer(OrigReqSizeMB) ->
+ %% Debug normally needs more memory, so double the requirement
+ Debug = erlang:system_info(debug_compiled),
+ ReqSizeMB = if Debug -> OrigReqSizeMB * 2; true -> OrigReqSizeMB end,
case total_memory() of
TotMem when is_integer(TotMem), TotMem >= ReqSizeMB ->
TestFun();
diff --git a/erts/emulator/test/pseudoknot_SUITE.erl b/erts/emulator/test/pseudoknot_SUITE.erl
index 58ef3cd563..ed4d40ac65 100644
--- a/erts/emulator/test/pseudoknot_SUITE.erl
+++ b/erts/emulator/test/pseudoknot_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/random_iolist.erl b/erts/emulator/test/random_iolist.erl
index 9a0f034e72..555f063e0a 100644
--- a/erts/emulator/test/random_iolist.erl
+++ b/erts/emulator/test/random_iolist.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -36,7 +36,7 @@ run2(Iter,Fun1,Fun2) ->
compare2(Iter,Fun1,Fun2).
random_byte() ->
- random:uniform(256) - 1.
+ rand:uniform(256) - 1.
random_list(0,Acc) ->
Acc;
@@ -45,7 +45,7 @@ random_list(N,Acc) ->
random_binary(N) ->
B = list_to_binary(random_list(N,[])),
- case {random:uniform(2),size(B)} of
+ case {rand:uniform(2),size(B)} of
{2,M} when M > 1 ->
S = M-1,
<<_:3,C:S/binary,_:5>> = B,
@@ -57,7 +57,7 @@ random_list(N) ->
random_list(N,[]).
front() ->
- case random:uniform(10) of
+ case rand:uniform(10) of
10 ->
false;
_ ->
@@ -65,7 +65,7 @@ front() ->
end.
any_type() ->
- case random:uniform(10) of
+ case rand:uniform(10) of
1 ->
list;
2 ->
@@ -77,7 +77,7 @@ any_type() ->
end.
tail_type() ->
- case random:uniform(5) of
+ case rand:uniform(5) of
1 ->
list;
2 ->
@@ -90,9 +90,9 @@ random_length(N) ->
UpperLimit = 255,
case N of
M when M > UpperLimit ->
- random:uniform(UpperLimit+1) - 1;
+ rand:uniform(UpperLimit+1) - 1;
_ ->
- random:uniform(N+1) - 1
+ rand:uniform(N+1) - 1
end.
random_iolist(0,Acc) ->
@@ -139,7 +139,7 @@ random_iolist(N) ->
standard_seed() ->
- random:seed(1201,855653,380975).
+ rand:seed(exsplus, {1201,855653,380975}).
do_comp(List,F1,F2) ->
X = F1(List),
diff --git a/erts/emulator/test/receive_SUITE.erl b/erts/emulator/test/receive_SUITE.erl
index ccae0df72e..83653a7a36 100644
--- a/erts/emulator/test/receive_SUITE.erl
+++ b/erts/emulator/test/receive_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -22,15 +22,14 @@
%% Tests receive after.
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
+-export([all/0, suite/0,
call_with_huge_message_queue/1,receive_in_between/1]).
--export([init_per_testcase/2,end_per_testcase/2]).
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 3}}].
all() ->
[call_with_huge_message_queue, receive_in_between].
@@ -38,27 +37,6 @@ all() ->
groups() ->
[].
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- Dog=?t:timetrap(?t:minutes(3)),
- [{watchdog, Dog}|Config].
-
-end_per_testcase(_Func, Config) ->
- Dog=?config(watchdog, Config),
- ?t:timetrap_cancel(Dog).
-
call_with_huge_message_queue(Config) when is_list(Config) ->
Pid = spawn_link(fun echo_loop/0),
@@ -77,8 +55,7 @@ call_with_huge_message_queue(Config) when is_list(Config) ->
Q when Q < 10 ->
ok;
Q ->
- io:format("Best Q = ~p", [Q]),
- ?t:fail()
+ ct:fail("Best Q = ~p", [Q])
end,
ok.
diff --git a/erts/emulator/test/ref_SUITE.erl b/erts/emulator/test/ref_SUITE.erl
index 1042c23d65..5f519d522e 100644
--- a/erts/emulator/test/ref_SUITE.erl
+++ b/erts/emulator/test/ref_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,54 +20,29 @@
-module(ref_SUITE).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2,end_per_testcase/2]).
+-export([all/0, suite/0]).
-export([wrap_1/1]).
-export([loop_ref/1]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-init_per_testcase(_, Config) ->
- ?line Dog=test_server:timetrap(test_server:minutes(2)),
- [{watchdog, Dog}|Config].
-
-end_per_testcase(_, Config) ->
- Dog=?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
- ok.
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 2}}].
all() ->
[wrap_1].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-wrap_1(doc) -> "Check that refs don't wrap around easily.";
+%% Check that refs don't wrap around easily.
wrap_1(Config) when is_list(Config) ->
- ?line spawn_link(?MODULE, loop_ref, [self()]),
- ?line receive
- done ->
- test_server:fail(wrapfast)
- after 30000 ->
- ok
- end,
+ spawn_link(?MODULE, loop_ref, [self()]),
+ receive
+ done ->
+ ct:fail(wrapfast)
+ after 30000 ->
+ ok
+ end,
ok.
loop_ref(Parent) ->
diff --git a/erts/emulator/test/register_SUITE.erl b/erts/emulator/test/register_SUITE.erl
index 5ecca0f547..43ae749498 100644
--- a/erts/emulator/test/register_SUITE.erl
+++ b/erts/emulator/test/register_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -23,47 +23,20 @@
%-define(line_trace, 1).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
%-compile(export_all).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2, end_per_testcase/2]).
+-export([all/0, suite/0]).
-export([otp_8099/1]).
--define(DEFAULT_TIMEOUT, ?t:minutes(2)).
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 2}}].
all() ->
[otp_8099].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-init_per_testcase(Case, Config) when is_list(Config) ->
- Dog = ?t:timetrap(?DEFAULT_TIMEOUT),
- [{watchdog, Dog}, {testcase, Case} | Config].
-
-end_per_testcase(_Case, Config) when is_list(Config) ->
- Dog = ?config(watchdog, Config),
- ?t:timetrap_cancel(Dog),
- ok.
-
%%
%% Test cases
%%
@@ -83,25 +56,20 @@ otp_8099(Config) when is_list(Config) ->
otp_8099_test(0) ->
ok;
otp_8099_test(N) ->
- ?line P = spawn(fun () -> otp_8099_proc() end),
- ?line case catch register(?OTP_8099_NAME, P) of
+ P = spawn(fun () -> otp_8099_proc() end),
+ case catch register(?OTP_8099_NAME, P) of
true ->
- ?line ok;
+ ok;
_ ->
- ?line OP = whereis(?OTP_8099_NAME),
- ?line (catch unregister(?OTP_8099_NAME)),
- ?line (catch exit(OP, kill)),
- ?line true = (catch register(?OTP_8099_NAME, P))
+ OP = whereis(?OTP_8099_NAME),
+ (catch unregister(?OTP_8099_NAME)),
+ (catch exit(OP, kill)),
+ true = (catch register(?OTP_8099_NAME, P))
end,
- ?line P = whereis(?OTP_8099_NAME),
- ?line exit(P, kill),
- ?line otp_8099_test(N-1).
+ P = whereis(?OTP_8099_NAME),
+ exit(P, kill),
+ otp_8099_test(N-1).
otp_8099_proc() ->
receive _ -> ok end,
otp_8099_proc().
-
-%%
-%% Utils
-%%
-
diff --git a/erts/emulator/test/save_calls_SUITE.erl b/erts/emulator/test/save_calls_SUITE.erl
index 4e50fdc898..aae7651f6d 100644
--- a/erts/emulator/test/save_calls_SUITE.erl
+++ b/erts/emulator/test/save_calls_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,12 +20,9 @@
-module(save_calls_SUITE).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
--export([all/0, suite/0,groups/0,
- init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2,end_per_testcase/2]).
+-export([all/0, suite/0, init_per_testcase/2,end_per_testcase/2]).
-export([save_calls_1/1,dont_break_reductions/1]).
@@ -36,36 +33,21 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[save_calls_1, dont_break_reductions].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
init_per_testcase(dont_break_reductions,Config) ->
%% Skip on --enable-native-libs as hipe rescedules after each
%% function call.
case erlang:system_info(hipe_architecture) of
- undefined ->
- Config;
- Architecture ->
- {lists, ListsBinary, _ListsFilename} = code:get_object_code(lists),
- ChunkName = hipe_unified_loader:chunk_name(Architecture),
- NativeChunk = beam_lib:chunks(ListsBinary, [ChunkName]),
- case NativeChunk of
- {ok,{_,[{_,Bin}]}} when is_binary(Bin) ->
- {skip,"Does not work for --enable-native-libs"};
- {error, beam_lib, _} -> Config
- end
+ undefined ->
+ Config;
+ Architecture ->
+ {lists, ListsBinary, _ListsFilename} = code:get_object_code(lists),
+ ChunkName = hipe_unified_loader:chunk_name(Architecture),
+ NativeChunk = beam_lib:chunks(ListsBinary, [ChunkName]),
+ case NativeChunk of
+ {ok,{_,[{_,Bin}]}} when is_binary(Bin) ->
+ {skip,"Does not work for --enable-native-libs"};
+ {error, beam_lib, _} -> Config
+ end
end;
init_per_testcase(_,Config) ->
Config.
@@ -73,92 +55,89 @@ init_per_testcase(_,Config) ->
end_per_testcase(_,_Config) ->
ok.
-dont_break_reductions(suite) ->
- [];
-dont_break_reductions(doc) ->
- ["Check that save_calls dont break reduction-based scheduling"];
+%% Check that save_calls dont break reduction-based scheduling
dont_break_reductions(Config) when is_list(Config) ->
- ?line RPS1 = reds_per_sched(0),
- ?line RPS2 = reds_per_sched(20),
- ?line Diff = abs(RPS1 - RPS2),
- ?line true = (Diff < (0.05 * RPS1)),
+ RPS1 = reds_per_sched(0),
+ RPS2 = reds_per_sched(20),
+ Diff = abs(RPS1 - RPS2),
+ true = (Diff < (0.2 * RPS1)),
ok.
reds_per_sched(SaveCalls) ->
- ?line Parent = self(),
- ?line HowMany = 10000,
- ?line Pid = spawn(fun() ->
- process_flag(save_calls,SaveCalls),
- receive
- go ->
- carmichaels_below(HowMany),
- Parent ! erlang:process_info(self(),reductions)
- end
- end),
- ?line TH = spawn(fun() -> trace_handler(0,Parent,Pid) end),
- ?line erlang:trace(Pid, true,[running,procs,{tracer,TH}]),
- ?line Pid ! go,
- ?line {Sched,Reds} = receive
- {accumulated,X} ->
- receive {reductions,Y} ->
- {X,Y}
- after 30000 ->
- timeout
- end
- after 30000 ->
- timeout
- end,
- ?line Reds div Sched.
+ Parent = self(),
+ HowMany = 10000,
+ Pid = spawn(fun() ->
+ process_flag(save_calls,SaveCalls),
+ receive
+ go ->
+ carmichaels_below(HowMany),
+ Parent ! erlang:process_info(self(),reductions)
+ end
+ end),
+ TH = spawn(fun() -> trace_handler(0,Parent,Pid) end),
+ erlang:trace(Pid, true,[running,procs,{tracer,TH}]),
+ Pid ! go,
+ {Sched,Reds} = receive
+ {accumulated,X} ->
+ receive {reductions,Y} ->
+ {X,Y}
+ after 30000 ->
+ timeout
+ end
+ after 30000 ->
+ timeout
+ end,
+ Reds div Sched.
trace_handler(Acc,Parent,Client) ->
receive
- {trace,Client,out,_} ->
- trace_handler(Acc+1,Parent,Client);
- {trace,Client,exit,_} ->
- Parent ! {accumulated, Acc};
- _ ->
- trace_handler(Acc,Parent,Client)
+ {trace,Client,out,_} ->
+ trace_handler(Acc+1,Parent,Client);
+ {trace,Client,exit,_} ->
+ Parent ! {accumulated, Acc};
+ _ ->
+ trace_handler(Acc,Parent,Client)
after 10000 ->
- ok
+ ok
end.
-save_calls_1(doc) -> "Test call saving.";
+%% Test call saving.
save_calls_1(Config) when is_list(Config) ->
case test_server:is_native(?MODULE) of
- true -> {skipped,"Native code"};
- false -> save_calls_1()
+ true -> {skipped,"Native code"};
+ false -> save_calls_1()
end.
-
+
save_calls_1() ->
- ?line erlang:process_flag(self(), save_calls, 0),
- ?line {last_calls, false} = process_info(self(), last_calls),
-
- ?line erlang:process_flag(self(), save_calls, 10),
- ?line {last_calls, _L1} = process_info(self(), last_calls),
- ?line ?MODULE:do_bipp(),
- ?line {last_calls, L2} = process_info(self(), last_calls),
- ?line L21 = lists:filter(fun is_local_function/1, L2),
- ?line case L21 of
- [{?MODULE,do_bipp,0},
- timeout,
- 'send',
- {?MODULE,do_bopp,1},
- 'receive',
- timeout,
- {?MODULE,do_bepp,0}] ->
- ok;
- X ->
- test_server:fail({l21, X})
- end,
-
- ?line erlang:process_flag(self(), save_calls, 10),
- ?line {last_calls, L3} = process_info(self(), last_calls),
+ erlang:process_flag(self(), save_calls, 0),
+ {last_calls, false} = process_info(self(), last_calls),
+
+ erlang:process_flag(self(), save_calls, 10),
+ {last_calls, _L1} = process_info(self(), last_calls),
+ ?MODULE:do_bipp(),
+ {last_calls, L2} = process_info(self(), last_calls),
+ L21 = lists:filter(fun is_local_function/1, L2),
+ case L21 of
+ [{?MODULE,do_bipp,0},
+ timeout,
+ 'send',
+ {?MODULE,do_bopp,1},
+ 'receive',
+ timeout,
+ {?MODULE,do_bepp,0}] ->
+ ok;
+ X ->
+ ct:fail({l21, X})
+ end,
+
+ erlang:process_flag(self(), save_calls, 10),
+ {last_calls, L3} = process_info(self(), last_calls),
true = (L3 /= false),
- ?line L31 = lists:filter(fun is_local_function/1, L3),
- ?line [] = L31,
+ L31 = lists:filter(fun is_local_function/1, L3),
+ [] = L31,
erlang:process_flag(self(), save_calls, 0),
%% Also check that it works on another process ...
@@ -183,7 +162,7 @@ do_bapp() ->
do_bopp(T) ->
receive
- X -> X
+ X -> X
after T -> ok
end.
@@ -200,25 +179,25 @@ is_local_function(_) ->
% Number crunching for reds test.
carmichaels_below(N) ->
- random:seed(3172,9814,20125),
+ rand:seed(exsplus, {3172,9814,20125}),
carmichaels_below(1,N).
carmichaels_below(N,N2) when N >= N2 ->
0;
carmichaels_below(N,N2) ->
X = case fast_prime(N,10) of
- false -> 0;
- true ->
- case fast_prime2(N,10) of
- true ->
- %io:format("Prime: ~p~n",[N]),
- 0;
- false ->
- io:format("Carmichael: ~p (dividable by ~p)~n",
- [N,smallest_divisor(N)]),
- 1
- end
- end,
+ false -> 0;
+ true ->
+ case fast_prime2(N,10) of
+ true ->
+ %io:format("Prime: ~p~n",[N]),
+ 0;
+ false ->
+ io:format("Carmichael: ~p (dividable by ~p)~n",
+ [N,smallest_divisor(N)]),
+ 1
+ end
+ end,
X+carmichaels_below(N+2,N2).
expmod(_,E,_) when E == 0 ->
@@ -230,7 +209,7 @@ expmod(Base,Exp,Mod) ->
(Base * expmod(Base,Exp - 1,Mod)) rem Mod.
uniform(N) ->
- random:uniform(N-1).
+ rand:uniform(N-1).
fermat(N) ->
R = uniform(N),
@@ -242,30 +221,30 @@ do_fast_prime(_N,0) ->
true;
do_fast_prime(N,Times) ->
case fermat(N) of
- true ->
- do_fast_prime(N,Times-1);
- false ->
- false
+ true ->
+ do_fast_prime(N,Times-1);
+ false ->
+ false
end.
-
+
fast_prime(N,T) ->
do_fast_prime(N,T).
expmod2(_,E,_) when E == 0 ->
1;
expmod2(Base,Exp,Mod) when (Exp rem 2) == 0 ->
-%% Uncomment the code below to simulate scheduling bug!
-% case erlang:process_info(self(),last_calls) of
-% {last_calls,false} -> ok;
-% _ -> erlang:yield()
-% end,
+ %% Uncomment the code below to simulate scheduling bug!
+ % case erlang:process_info(self(),last_calls) of
+ % {last_calls,false} -> ok;
+ % _ -> erlang:yield()
+ % end,
X = expmod2(Base,Exp div 2,Mod),
Y=(X*X) rem Mod,
if
- Y == 1, X =/= 1, X =/= (Mod - 1) ->
- 0;
- true ->
- Y rem Mod
+ Y == 1, X =/= 1, X =/= (Mod - 1) ->
+ 0;
+ true ->
+ Y rem Mod
end;
expmod2(Base,Exp,Mod) ->
(Base * expmod2(Base,Exp - 1,Mod)) rem Mod.
@@ -280,12 +259,12 @@ do_fast_prime2(_N,0) ->
true;
do_fast_prime2(N,Times) ->
case miller_rabbin(N) of
- true ->
- do_fast_prime2(N,Times-1);
- false ->
- false
+ true ->
+ do_fast_prime2(N,Times-1);
+ false ->
+ false
end.
-
+
fast_prime2(N,T) ->
do_fast_prime2(N,T).
@@ -294,17 +273,16 @@ smallest_divisor(N) ->
find_divisor(N,TD) ->
if
- TD*TD > N ->
- N;
- true ->
- case divides(TD,N) of
- true ->
- TD;
- false ->
- find_divisor(N,TD+1)
- end
+ TD*TD > N ->
+ N;
+ true ->
+ case divides(TD,N) of
+ true ->
+ TD;
+ false ->
+ find_divisor(N,TD+1)
+ end
end.
divides(A,B) ->
(B rem A) == 0.
-
diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl
index 986a73ebb1..3aee15a8fc 100644
--- a/erts/emulator/test/scheduler_SUITE.erl
+++ b/erts/emulator/test/scheduler_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -31,12 +31,12 @@
%-define(line_trace, 1).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
%-compile(export_all).
--export([all/0, suite/0,groups/0,init_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2, end_per_testcase/2, end_per_suite/1]).
+-export([all/0, suite/0, groups/0,
+ init_per_suite/1, end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2]).
-export([equal/1,
few_low/1,
@@ -54,22 +54,23 @@
sct_cmd/1,
sbt_cmd/1,
scheduler_threads/1,
+ scheduler_suspend_basic/1,
scheduler_suspend/1,
dirty_scheduler_threads/1,
reader_groups/1]).
--define(DEFAULT_TIMEOUT, ?t:minutes(15)).
-
--define(MIN_SCHEDULER_TEST_TIMEOUT, ?t:minutes(1)).
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 15}}].
all() ->
[equal, few_low, many_low, equal_with_part_time_high,
equal_with_part_time_max,
equal_and_high_with_part_time_max, equal_with_high,
- equal_with_high_max, bound_process,
- {group, scheduler_bind}, scheduler_threads, scheduler_suspend,
+ equal_with_high_max,
+ bound_process,
+ {group, scheduler_bind}, scheduler_threads,
+ scheduler_suspend_basic, scheduler_suspend,
dirty_scheduler_threads,
reader_groups].
@@ -85,12 +86,6 @@ end_per_suite(Config) ->
catch erts_debug:set_internal_state(available_internal_state, false),
Config.
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
init_per_testcase(update_cpu_info, Config) ->
case os:find_executable("taskset") of
false ->
@@ -102,15 +97,12 @@ init_per_testcase(Case, Config) when is_list(Config) ->
init_per_tc(Case, Config).
init_per_tc(Case, Config) ->
- Dog = ?t:timetrap(?DEFAULT_TIMEOUT),
process_flag(priority, max),
erlang:display({'------------', ?MODULE, Case, '------------'}),
OkRes = ok,
- [{watchdog, Dog}, {testcase, Case}, {ok_res, OkRes} |Config].
+ [{testcase, Case}, {ok_res, OkRes} |Config].
end_per_testcase(_Case, Config) when is_list(Config) ->
- Dog = ?config(watchdog, Config),
- ?t:timetrap_cancel(Dog),
ok.
-define(ERTS_RUNQ_CHECK_BALANCE_REDS_PER_SCHED, (2000*2000)).
@@ -130,130 +122,130 @@ many_low(Config) when is_list(Config) ->
low_normal_test(Config, 2*active_schedulers(), 1000).
low_normal_test(Config, NW, LW) ->
- ?line Tracer = start_tracer(),
- ?line Low = workers(LW, low),
- ?line Normal = workers(NW, normal),
- ?line Res = do_it(Tracer, Low, Normal, [], []),
- ?line chk_result(Res, LW, NW, 0, 0, true, false, false),
- ?line workers_exit([Low, Normal]),
- ?line ok(Res, Config).
+ Tracer = start_tracer(),
+ Low = workers(LW, low),
+ Normal = workers(NW, normal),
+ Res = do_it(Tracer, Low, Normal, [], []),
+ chk_result(Res, LW, NW, 0, 0, true, false, false),
+ workers_exit([Low, Normal]),
+ ok(Res, Config).
equal_with_part_time_high(Config) when is_list(Config) ->
- ?line NW = 500,
- ?line LW = 500,
- ?line HW = 1,
- ?line Tracer = start_tracer(),
- ?line Normal = workers(NW, normal),
- ?line Low = workers(LW, low),
- ?line High = part_time_workers(HW, high),
- ?line Res = do_it(Tracer, Low, Normal, High, []),
- ?line chk_result(Res, LW, NW, HW, 0, true, true, false),
- ?line workers_exit([Low, Normal, High]),
- ?line ok(Res, Config).
+ NW = 500,
+ LW = 500,
+ HW = 1,
+ Tracer = start_tracer(),
+ Normal = workers(NW, normal),
+ Low = workers(LW, low),
+ High = part_time_workers(HW, high),
+ Res = do_it(Tracer, Low, Normal, High, []),
+ chk_result(Res, LW, NW, HW, 0, true, true, false),
+ workers_exit([Low, Normal, High]),
+ ok(Res, Config).
equal_and_high_with_part_time_max(Config) when is_list(Config) ->
- ?line NW = 500,
- ?line LW = 500,
- ?line HW = 500,
- ?line MW = 1,
- ?line Tracer = start_tracer(),
- ?line Low = workers(LW, low),
- ?line Normal = workers(NW, normal),
- ?line High = workers(HW, high),
- ?line Max = part_time_workers(MW, max),
- ?line Res = do_it(Tracer, Low, Normal, High, Max),
- ?line chk_result(Res, LW, NW, HW, MW, false, true, true),
- ?line workers_exit([Low, Normal, Max]),
- ?line ok(Res, Config).
+ NW = 500,
+ LW = 500,
+ HW = 500,
+ MW = 1,
+ Tracer = start_tracer(),
+ Low = workers(LW, low),
+ Normal = workers(NW, normal),
+ High = workers(HW, high),
+ Max = part_time_workers(MW, max),
+ Res = do_it(Tracer, Low, Normal, High, Max),
+ chk_result(Res, LW, NW, HW, MW, false, true, true),
+ workers_exit([Low, Normal, Max]),
+ ok(Res, Config).
equal_with_part_time_max(Config) when is_list(Config) ->
- ?line NW = 500,
- ?line LW = 500,
- ?line MW = 1,
- ?line Tracer = start_tracer(),
- ?line Low = workers(LW, low),
- ?line Normal = workers(NW, normal),
- ?line Max = part_time_workers(MW, max),
- ?line Res = do_it(Tracer, Low, Normal, [], Max),
- ?line chk_result(Res, LW, NW, 0, MW, true, false, true),
- ?line workers_exit([Low, Normal, Max]),
- ?line ok(Res, Config).
+ NW = 500,
+ LW = 500,
+ MW = 1,
+ Tracer = start_tracer(),
+ Low = workers(LW, low),
+ Normal = workers(NW, normal),
+ Max = part_time_workers(MW, max),
+ Res = do_it(Tracer, Low, Normal, [], Max),
+ chk_result(Res, LW, NW, 0, MW, true, false, true),
+ workers_exit([Low, Normal, Max]),
+ ok(Res, Config).
equal_with_high(Config) when is_list(Config) ->
- ?line NW = 500,
- ?line LW = 500,
- ?line HW = 1,
- ?line Tracer = start_tracer(),
- ?line Low = workers(LW, low),
- ?line Normal = workers(NW, normal),
- ?line High = workers(HW, high),
- ?line Res = do_it(Tracer, Low, Normal, High, []),
- ?line LNExe = case active_schedulers() of
+ NW = 500,
+ LW = 500,
+ HW = 1,
+ Tracer = start_tracer(),
+ Low = workers(LW, low),
+ Normal = workers(NW, normal),
+ High = workers(HW, high),
+ Res = do_it(Tracer, Low, Normal, High, []),
+ LNExe = case active_schedulers() of
S when S =< HW -> false;
_ -> true
end,
- ?line chk_result(Res, LW, NW, HW, 0, LNExe, true, false),
- ?line workers_exit([Low, Normal, High]),
- ?line ok(Res, Config).
+ chk_result(Res, LW, NW, HW, 0, LNExe, true, false),
+ workers_exit([Low, Normal, High]),
+ ok(Res, Config).
equal_with_high_max(Config) when is_list(Config) ->
- ?line NW = 500,
- ?line LW = 500,
- ?line HW = 1,
- ?line MW = 1,
- ?line Tracer = start_tracer(),
- ?line Normal = workers(NW, normal),
- ?line Low = workers(LW, low),
- ?line High = workers(HW, high),
- ?line Max = workers(MW, max),
- ?line Res = do_it(Tracer, Low, Normal, High, Max),
- ?line {LNExe, HExe} = case active_schedulers() of
+ NW = 500,
+ LW = 500,
+ HW = 1,
+ MW = 1,
+ Tracer = start_tracer(),
+ Normal = workers(NW, normal),
+ Low = workers(LW, low),
+ High = workers(HW, high),
+ Max = workers(MW, max),
+ Res = do_it(Tracer, Low, Normal, High, Max),
+ {LNExe, HExe} = case active_schedulers() of
S when S =< MW -> {false, false};
S when S =< (MW + HW) -> {false, true};
_ -> {true, true}
end,
- ?line chk_result(Res, LW, NW, HW, MW, LNExe, HExe, true),
- ?line workers_exit([Low, Normal, Max]),
- ?line ok(Res, Config).
+ chk_result(Res, LW, NW, HW, MW, LNExe, HExe, true),
+ workers_exit([Low, Normal, Max]),
+ ok(Res, Config).
bound_process(Config) when is_list(Config) ->
case erlang:system_info(run_queues) == erlang:system_info(schedulers) of
- true ->
- ?line NStartBase = 20000,
- ?line NStart = case {erlang:system_info(debug_compiled),
- erlang:system_info(lock_checking)} of
- {true, true} -> NStartBase div 100;
- {_, true} -> NStartBase div 10;
- _ -> NStartBase
- end,
- ?line MStart = 100,
- ?line Seq = lists:seq(1, 100),
- ?line Tester = self(),
- ?line Procs = lists:map(
- fun (N) when N rem 2 == 0 ->
- spawn_opt(fun () ->
- bound_loop(NStart,
- NStart,
- MStart,
- 1),
- Tester ! {self(), done}
- end,
- [{scheduler, 1}, link]);
- (_N) ->
- spawn_link(fun () ->
- bound_loop(NStart,
- NStart,
- MStart,
- false),
- Tester ! {self(), done}
- end)
- end,
- Seq),
- ?line lists:foreach(fun (P) -> receive {P, done} -> ok end end,
- Procs),
- ?line ok;
- false ->
- {skipped, "Functionality not supported"}
+ true ->
+ NStartBase = 20000,
+ NStart = case {erlang:system_info(debug_compiled),
+ erlang:system_info(lock_checking)} of
+ {true, true} -> NStartBase div 100;
+ {_, true} -> NStartBase div 10;
+ _ -> NStartBase
+ end,
+ MStart = 100,
+ Seq = lists:seq(1, 100),
+ Tester = self(),
+ Procs = lists:map(
+ fun (N) when N rem 2 == 0 ->
+ spawn_opt(fun () ->
+ bound_loop(NStart,
+ NStart,
+ MStart,
+ 1),
+ Tester ! {self(), done}
+ end,
+ [{scheduler, 1}, link]);
+ (_N) ->
+ spawn_link(fun () ->
+ bound_loop(NStart,
+ NStart,
+ MStart,
+ false),
+ Tester ! {self(), done}
+ end)
+ end,
+ Seq),
+ lists:foreach(fun (P) -> receive {P, done} -> ok end end,
+ Procs),
+ ok;
+ false ->
+ {skipped, "Functionality not supported"}
end.
bound_loop(_, 0, 0, _) ->
@@ -487,59 +479,59 @@ bound_loop(NS, N, M, Sched) ->
":L30-31t0-1c15n3p0").
-define(TOPOLOGY_F_TERM,
- [{processor,[{node,[{core,[{thread,{logical,0}},
- {thread,{logical,1}}]},
- {core,[{thread,{logical,2}},
- {thread,{logical,3}}]},
- {core,[{thread,{logical,4}},
- {thread,{logical,5}}]},
- {core,[{thread,{logical,6}},
- {thread,{logical,7}}]}]},
- {node,[{core,[{thread,{logical,8}},
- {thread,{logical,9}}]},
- {core,[{thread,{logical,10}},
- {thread,{logical,11}}]},
- {core,[{thread,{logical,12}},
- {thread,{logical,13}}]},
- {core,[{thread,{logical,14}},
- {thread,{logical,15}}]}]},
- {node,[{core,[{thread,{logical,16}},
- {thread,{logical,17}}]},
- {core,[{thread,{logical,18}},
- {thread,{logical,19}}]},
- {core,[{thread,{logical,20}},
- {thread,{logical,21}}]},
- {core,[{thread,{logical,22}},
- {thread,{logical,23}}]}]},
- {node,[{core,[{thread,{logical,24}},
- {thread,{logical,25}}]},
- {core,[{thread,{logical,26}},
- {thread,{logical,27}}]},
- {core,[{thread,{logical,28}},
- {thread,{logical,29}}]},
- {core,[{thread,{logical,30}},
- {thread,{logical,31}}]}]}]}]).
+ [{processor,[{node,[{core,[{thread,{logical,0}},
+ {thread,{logical,1}}]},
+ {core,[{thread,{logical,2}},
+ {thread,{logical,3}}]},
+ {core,[{thread,{logical,4}},
+ {thread,{logical,5}}]},
+ {core,[{thread,{logical,6}},
+ {thread,{logical,7}}]}]},
+ {node,[{core,[{thread,{logical,8}},
+ {thread,{logical,9}}]},
+ {core,[{thread,{logical,10}},
+ {thread,{logical,11}}]},
+ {core,[{thread,{logical,12}},
+ {thread,{logical,13}}]},
+ {core,[{thread,{logical,14}},
+ {thread,{logical,15}}]}]},
+ {node,[{core,[{thread,{logical,16}},
+ {thread,{logical,17}}]},
+ {core,[{thread,{logical,18}},
+ {thread,{logical,19}}]},
+ {core,[{thread,{logical,20}},
+ {thread,{logical,21}}]},
+ {core,[{thread,{logical,22}},
+ {thread,{logical,23}}]}]},
+ {node,[{core,[{thread,{logical,24}},
+ {thread,{logical,25}}]},
+ {core,[{thread,{logical,26}},
+ {thread,{logical,27}}]},
+ {core,[{thread,{logical,28}},
+ {thread,{logical,29}}]},
+ {core,[{thread,{logical,30}},
+ {thread,{logical,31}}]}]}]}]).
bindings(Node, BindType) ->
Parent = self(),
Ref = make_ref(),
Pid = spawn_link(Node,
- fun () ->
- enable_internal_state(),
- Res = (catch erts_debug:get_internal_state(
- {fake_scheduler_bindings,
- BindType})),
- Parent ! {Ref, Res}
- end),
+ fun () ->
+ enable_internal_state(),
+ Res = (catch erts_debug:get_internal_state(
+ {fake_scheduler_bindings,
+ BindType})),
+ Parent ! {Ref, Res}
+ end),
receive
- {Ref, Res} ->
- ?t:format("~p: ~p~n", [BindType, Res]),
- unlink(Pid),
- Res
+ {Ref, Res} ->
+ io:format("~p: ~p~n", [BindType, Res]),
+ unlink(Pid),
+ Res
end.
scheduler_bind_types(Config) when is_list(Config) ->
- ?line OldRelFlags = clear_erl_rel_flags(),
+ OldRelFlags = clear_erl_rel_flags(),
try
scheduler_bind_types_test(Config,
?TOPOLOGY_A_TERM,
@@ -568,267 +560,267 @@ scheduler_bind_types(Config) when is_list(Config) ->
after
restore_erl_rel_flags(OldRelFlags)
end,
- ?line ok.
+ ok.
scheduler_bind_types_test(Config, Topology, CmdLine, TermLetter) ->
- ?line ?t:format("Testing (~p): ~p~n", [TermLetter, Topology]),
- ?line {ok, Node0} = start_node(Config),
- ?line _ = rpc:call(Node0, erlang, system_flag, [cpu_topology, Topology]),
- ?line cmp(Topology, rpc:call(Node0, erlang, system_info, [cpu_topology])),
- ?line check_bind_types(Node0, TermLetter),
- ?line stop_node(Node0),
- ?line {ok, Node1} = start_node(Config, CmdLine),
- ?line cmp(Topology, rpc:call(Node1, erlang, system_info, [cpu_topology])),
- ?line check_bind_types(Node1, TermLetter),
- ?line stop_node(Node1).
+ io:format("Testing (~p): ~p~n", [TermLetter, Topology]),
+ {ok, Node0} = start_node(Config),
+ _ = rpc:call(Node0, erlang, system_flag, [cpu_topology, Topology]),
+ cmp(Topology, rpc:call(Node0, erlang, system_info, [cpu_topology])),
+ check_bind_types(Node0, TermLetter),
+ stop_node(Node0),
+ {ok, Node1} = start_node(Config, CmdLine),
+ cmp(Topology, rpc:call(Node1, erlang, system_info, [cpu_topology])),
+ check_bind_types(Node1, TermLetter),
+ stop_node(Node1).
check_bind_types(Node, a) ->
- ?line {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}
- = bindings(Node, no_spread),
- ?line {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15}
- = bindings(Node, thread_spread),
- ?line {0,4,8,12,2,6,10,14,1,5,9,13,3,7,11,15}
- = bindings(Node, processor_spread),
- ?line {0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15}
- = bindings(Node, spread),
- ?line {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15}
- = bindings(Node, no_node_thread_spread),
- ?line {0,4,2,6,1,5,3,7,8,12,10,14,9,13,11,15}
- = bindings(Node, no_node_processor_spread),
- ?line {0,4,2,6,8,12,10,14,1,5,3,7,9,13,11,15}
- = bindings(Node, thread_no_node_processor_spread),
- ?line {0,4,2,6,8,12,10,14,1,5,3,7,9,13,11,15}
- = bindings(Node, default_bind),
- ?line ok;
+ {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}
+ = bindings(Node, no_spread),
+ {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15}
+ = bindings(Node, thread_spread),
+ {0,4,8,12,2,6,10,14,1,5,9,13,3,7,11,15}
+ = bindings(Node, processor_spread),
+ {0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15}
+ = bindings(Node, spread),
+ {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15}
+ = bindings(Node, no_node_thread_spread),
+ {0,4,2,6,1,5,3,7,8,12,10,14,9,13,11,15}
+ = bindings(Node, no_node_processor_spread),
+ {0,4,2,6,8,12,10,14,1,5,3,7,9,13,11,15}
+ = bindings(Node, thread_no_node_processor_spread),
+ {0,4,2,6,8,12,10,14,1,5,3,7,9,13,11,15}
+ = bindings(Node, default_bind),
+ ok;
check_bind_types(Node, b) ->
- ?line {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}
- = bindings(Node, no_spread),
- ?line {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15}
- = bindings(Node, thread_spread),
- ?line {0,8,2,10,4,12,6,14,1,9,3,11,5,13,7,15}
- = bindings(Node, processor_spread),
- ?line {0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15}
- = bindings(Node, spread),
- ?line {0,2,1,3,4,6,5,7,8,10,9,11,12,14,13,15}
- = bindings(Node, no_node_thread_spread),
- ?line {0,2,1,3,4,6,5,7,8,10,9,11,12,14,13,15}
- = bindings(Node, no_node_processor_spread),
- ?line {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15}
- = bindings(Node, thread_no_node_processor_spread),
- ?line {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15}
- = bindings(Node, default_bind),
- ?line ok;
+ {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}
+ = bindings(Node, no_spread),
+ {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15}
+ = bindings(Node, thread_spread),
+ {0,8,2,10,4,12,6,14,1,9,3,11,5,13,7,15}
+ = bindings(Node, processor_spread),
+ {0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15}
+ = bindings(Node, spread),
+ {0,2,1,3,4,6,5,7,8,10,9,11,12,14,13,15}
+ = bindings(Node, no_node_thread_spread),
+ {0,2,1,3,4,6,5,7,8,10,9,11,12,14,13,15}
+ = bindings(Node, no_node_processor_spread),
+ {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15}
+ = bindings(Node, thread_no_node_processor_spread),
+ {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15}
+ = bindings(Node, default_bind),
+ ok;
check_bind_types(Node, c) ->
- ?line {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
- 25,26,27,28,29,30,31} = bindings(Node, no_spread),
- ?line {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15,
- 17,19,21,23,25,27,29,31} = bindings(Node, thread_spread),
- ?line {0,4,8,16,20,24,2,6,10,18,22,26,12,28,14,30,1,5,9,17,21,25,
- 3,7,11,19,23,27,13,29,15,31} = bindings(Node, processor_spread),
- ?line {0,8,16,24,4,20,12,28,2,10,18,26,6,22,14,30,1,9,17,25,5,21,13,29,3,11,
- 19,27,7,23,15,31} = bindings(Node, spread),
- ?line {0,2,4,6,1,3,5,7,8,10,9,11,12,14,13,15,16,18,20,22,17,19,21,23,24,26,
- 25,27,28,30,29,31} = bindings(Node, no_node_thread_spread),
- ?line {0,4,2,6,1,5,3,7,8,10,9,11,12,14,13,15,16,20,18,22,17,21,19,23,24,26,
- 25,27,28,30,29,31} = bindings(Node, no_node_processor_spread),
- ?line {0,4,2,6,8,10,12,14,16,20,18,22,24,26,28,30,1,5,3,7,9,11,13,15,17,21,
- 19,23,25,27,29,31} = bindings(Node, thread_no_node_processor_spread),
- ?line {0,4,2,6,8,10,12,14,16,20,18,22,24,26,28,30,1,5,3,7,9,11,13,15,17,21,
- 19,23,25,27,29,31} = bindings(Node, default_bind),
- ?line ok;
+ {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
+ 25,26,27,28,29,30,31} = bindings(Node, no_spread),
+ {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15,
+ 17,19,21,23,25,27,29,31} = bindings(Node, thread_spread),
+ {0,4,8,16,20,24,2,6,10,18,22,26,12,28,14,30,1,5,9,17,21,25,
+ 3,7,11,19,23,27,13,29,15,31} = bindings(Node, processor_spread),
+ {0,8,16,24,4,20,12,28,2,10,18,26,6,22,14,30,1,9,17,25,5,21,13,29,3,11,
+ 19,27,7,23,15,31} = bindings(Node, spread),
+ {0,2,4,6,1,3,5,7,8,10,9,11,12,14,13,15,16,18,20,22,17,19,21,23,24,26,
+ 25,27,28,30,29,31} = bindings(Node, no_node_thread_spread),
+ {0,4,2,6,1,5,3,7,8,10,9,11,12,14,13,15,16,20,18,22,17,21,19,23,24,26,
+ 25,27,28,30,29,31} = bindings(Node, no_node_processor_spread),
+ {0,4,2,6,8,10,12,14,16,20,18,22,24,26,28,30,1,5,3,7,9,11,13,15,17,21,
+ 19,23,25,27,29,31} = bindings(Node, thread_no_node_processor_spread),
+ {0,4,2,6,8,10,12,14,16,20,18,22,24,26,28,30,1,5,3,7,9,11,13,15,17,21,
+ 19,23,25,27,29,31} = bindings(Node, default_bind),
+ ok;
check_bind_types(Node, d) ->
- ?line {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
- 25,26,27,28,29,30,31} = bindings(Node, no_spread),
- ?line {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15,
- 17,19,21,23,25,27,29,31} = bindings(Node, thread_spread),
- ?line {0,8,12,16,24,28,2,10,14,18,26,30,4,20,6,22,1,9,13,17,25,29,3,11,15,
- 19,27,31,5,21,7,23} = bindings(Node, processor_spread),
- ?line {0,8,16,24,12,28,4,20,2,10,18,26,14,30,6,22,1,9,17,25,13,29,5,21,3,11,
- 19,27,15,31,7,23} = bindings(Node, spread),
- ?line {0,2,1,3,4,6,5,7,8,10,12,14,9,11,13,15,16,18,17,19,20,22,21,23,24,26,
- 28,30,25,27,29,31} = bindings(Node, no_node_thread_spread),
- ?line {0,2,1,3,4,6,5,7,8,12,10,14,9,13,11,15,16,18,17,19,20,22,21,23,24,28,
- 26,30,25,29,27,31} = bindings(Node, no_node_processor_spread),
- ?line {0,2,4,6,8,12,10,14,16,18,20,22,24,28,26,30,1,3,5,7,9,13,11,15,17,19,
- 21,23,25,29,27,31} = bindings(Node, thread_no_node_processor_spread),
- ?line {0,2,4,6,8,12,10,14,16,18,20,22,24,28,26,30,1,3,5,7,9,13,11,15,17,19,
- 21,23,25,29,27,31} = bindings(Node, default_bind),
- ?line ok;
+ {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
+ 25,26,27,28,29,30,31} = bindings(Node, no_spread),
+ {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15,
+ 17,19,21,23,25,27,29,31} = bindings(Node, thread_spread),
+ {0,8,12,16,24,28,2,10,14,18,26,30,4,20,6,22,1,9,13,17,25,29,3,11,15,
+ 19,27,31,5,21,7,23} = bindings(Node, processor_spread),
+ {0,8,16,24,12,28,4,20,2,10,18,26,14,30,6,22,1,9,17,25,13,29,5,21,3,11,
+ 19,27,15,31,7,23} = bindings(Node, spread),
+ {0,2,1,3,4,6,5,7,8,10,12,14,9,11,13,15,16,18,17,19,20,22,21,23,24,26,
+ 28,30,25,27,29,31} = bindings(Node, no_node_thread_spread),
+ {0,2,1,3,4,6,5,7,8,12,10,14,9,13,11,15,16,18,17,19,20,22,21,23,24,28,
+ 26,30,25,29,27,31} = bindings(Node, no_node_processor_spread),
+ {0,2,4,6,8,12,10,14,16,18,20,22,24,28,26,30,1,3,5,7,9,13,11,15,17,19,
+ 21,23,25,29,27,31} = bindings(Node, thread_no_node_processor_spread),
+ {0,2,4,6,8,12,10,14,16,18,20,22,24,28,26,30,1,3,5,7,9,13,11,15,17,19,
+ 21,23,25,29,27,31} = bindings(Node, default_bind),
+ ok;
check_bind_types(Node, e) ->
- ?line {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}
- = bindings(Node, no_spread),
- ?line {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15}
- = bindings(Node, thread_spread),
- ?line {0,8,2,10,4,12,6,14,1,9,3,11,5,13,7,15}
- = bindings(Node, processor_spread),
- ?line {0,8,2,10,4,12,6,14,1,9,3,11,5,13,7,15}
- = bindings(Node, spread),
- ?line {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15}
- = bindings(Node, no_node_thread_spread),
- ?line {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15}
- = bindings(Node, no_node_processor_spread),
- ?line {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15}
- = bindings(Node, thread_no_node_processor_spread),
- ?line {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15}
- = bindings(Node, default_bind),
- ?line ok;
+ {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}
+ = bindings(Node, no_spread),
+ {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15}
+ = bindings(Node, thread_spread),
+ {0,8,2,10,4,12,6,14,1,9,3,11,5,13,7,15}
+ = bindings(Node, processor_spread),
+ {0,8,2,10,4,12,6,14,1,9,3,11,5,13,7,15}
+ = bindings(Node, spread),
+ {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15}
+ = bindings(Node, no_node_thread_spread),
+ {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15}
+ = bindings(Node, no_node_processor_spread),
+ {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15}
+ = bindings(Node, thread_no_node_processor_spread),
+ {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15}
+ = bindings(Node, default_bind),
+ ok;
check_bind_types(Node, f) ->
- ?line {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
- 25,26,27,28,29,30,31} = bindings(Node, no_spread),
- ?line {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15,
- 17,19,21,23,25,27,29,31} = bindings(Node, thread_spread),
- ?line {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,
- 15,17,19,21,23,25,27,29,31} = bindings(Node, processor_spread),
- ?line {0,8,16,24,2,10,18,26,4,12,20,28,6,14,22,30,1,9,17,25,3,11,19,27,5,13,
- 21,29,7,15,23,31} = bindings(Node, spread),
- ?line {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15,16,18,20,22,17,19,21,23,24,26,
- 28,30,25,27,29,31} = bindings(Node, no_node_thread_spread),
- ?line {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15,16,18,20,22,17,19,21,23,24,26,
- 28,30,25,27,29,31} = bindings(Node, no_node_processor_spread),
- ?line {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15,17,19,
- 21,23,25,27,29,31} = bindings(Node, thread_no_node_processor_spread),
- ?line {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15,17,19,
- 21,23,25,27,29,31} = bindings(Node, default_bind),
- ?line ok;
+ {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
+ 25,26,27,28,29,30,31} = bindings(Node, no_spread),
+ {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15,
+ 17,19,21,23,25,27,29,31} = bindings(Node, thread_spread),
+ {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,
+ 15,17,19,21,23,25,27,29,31} = bindings(Node, processor_spread),
+ {0,8,16,24,2,10,18,26,4,12,20,28,6,14,22,30,1,9,17,25,3,11,19,27,5,13,
+ 21,29,7,15,23,31} = bindings(Node, spread),
+ {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15,16,18,20,22,17,19,21,23,24,26,
+ 28,30,25,27,29,31} = bindings(Node, no_node_thread_spread),
+ {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15,16,18,20,22,17,19,21,23,24,26,
+ 28,30,25,27,29,31} = bindings(Node, no_node_processor_spread),
+ {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15,17,19,
+ 21,23,25,27,29,31} = bindings(Node, thread_no_node_processor_spread),
+ {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15,17,19,
+ 21,23,25,27,29,31} = bindings(Node, default_bind),
+ ok;
check_bind_types(Node, _) ->
- ?line bindings(Node, no_spread),
- ?line bindings(Node, thread_spread),
- ?line bindings(Node, processor_spread),
- ?line bindings(Node, spread),
- ?line bindings(Node, no_node_thread_spread),
- ?line bindings(Node, no_node_processor_spread),
- ?line bindings(Node, thread_no_node_processor_spread),
- ?line bindings(Node, default_bind),
- ?line ok.
+ bindings(Node, no_spread),
+ bindings(Node, thread_spread),
+ bindings(Node, processor_spread),
+ bindings(Node, spread),
+ bindings(Node, no_node_thread_spread),
+ bindings(Node, no_node_processor_spread),
+ bindings(Node, thread_no_node_processor_spread),
+ bindings(Node, default_bind),
+ ok.
cpu_topology(Config) when is_list(Config) ->
- ?line OldRelFlags = clear_erl_rel_flags(),
+ OldRelFlags = clear_erl_rel_flags(),
try
- ?line cpu_topology_test(
- Config,
- [{node,[{processor,[{core,{logical,0}},
- {core,{logical,1}}]}]},
- {processor,[{node,[{core,{logical,2}},
- {core,{logical,3}}]}]},
- {node,[{processor,[{core,{logical,4}},
- {core,{logical,5}}]}]},
- {processor,[{node,[{core,{logical,6}},
- {core,{logical,7}}]}]}],
- "+sct "
- "L0-1c0-1p0n0"
- ":L2-3c0-1n1p1"
- ":L4-5c0-1p2n2"
- ":L6-7c0-1n3p3"),
- ?line cpu_topology_test(
- Config,
- [{node,[{processor,[{core,{logical,0}},
- {core,{logical,1}}]},
- {processor,[{core,{logical,2}},
- {core,{logical,3}}]}]},
- {processor,[{node,[{core,{logical,4}},
- {core,{logical,5}}]},
- {node,[{core,{logical,6}},
- {core,{logical,7}}]}]},
- {node,[{processor,[{core,{logical,8}},
- {core,{logical,9}}]},
- {processor,[{core,{logical,10}},
- {core,{logical,11}}]}]},
- {processor,[{node,[{core,{logical,12}},
- {core,{logical,13}}]},
- {node,[{core,{logical,14}},
- {core,{logical,15}}]}]}],
- "+sct "
- "L0-1c0-1p0n0"
- ":L2-3c0-1p1n0"
- ":L4-5c0-1n1p2"
- ":L6-7c2-3n2p2"
- ":L8-9c0-1p3n3"
- ":L10-11c0-1p4n3"
- ":L12-13c0-1n4p5"
- ":L14-15c2-3n5p5"),
- ?line cpu_topology_test(
- Config,
- [{node,[{processor,[{core,{logical,0}},
- {core,{logical,1}}]}]},
- {processor,[{node,[{core,{logical,2}},
- {core,{logical,3}}]}]},
- {processor,[{node,[{core,{logical,4}},
- {core,{logical,5}}]}]},
- {node,[{processor,[{core,{logical,6}},
- {core,{logical,7}}]}]},
- {node,[{processor,[{core,{logical,8}},
- {core,{logical,9}}]}]},
- {processor,[{node,[{core,{logical,10}},
- {core,{logical,11}}]}]}],
- "+sct "
- "L0-1c0-1p0n0"
- ":L2-3c0-1n1p1"
- ":L4-5c0-1n2p2"
- ":L6-7c0-1p3n3"
- ":L8-9c0-1p4n4"
- ":L10-11c0-1n5p5")
+ cpu_topology_test(
+ Config,
+ [{node,[{processor,[{core,{logical,0}},
+ {core,{logical,1}}]}]},
+ {processor,[{node,[{core,{logical,2}},
+ {core,{logical,3}}]}]},
+ {node,[{processor,[{core,{logical,4}},
+ {core,{logical,5}}]}]},
+ {processor,[{node,[{core,{logical,6}},
+ {core,{logical,7}}]}]}],
+ "+sct "
+ "L0-1c0-1p0n0"
+ ":L2-3c0-1n1p1"
+ ":L4-5c0-1p2n2"
+ ":L6-7c0-1n3p3"),
+ cpu_topology_test(
+ Config,
+ [{node,[{processor,[{core,{logical,0}},
+ {core,{logical,1}}]},
+ {processor,[{core,{logical,2}},
+ {core,{logical,3}}]}]},
+ {processor,[{node,[{core,{logical,4}},
+ {core,{logical,5}}]},
+ {node,[{core,{logical,6}},
+ {core,{logical,7}}]}]},
+ {node,[{processor,[{core,{logical,8}},
+ {core,{logical,9}}]},
+ {processor,[{core,{logical,10}},
+ {core,{logical,11}}]}]},
+ {processor,[{node,[{core,{logical,12}},
+ {core,{logical,13}}]},
+ {node,[{core,{logical,14}},
+ {core,{logical,15}}]}]}],
+ "+sct "
+ "L0-1c0-1p0n0"
+ ":L2-3c0-1p1n0"
+ ":L4-5c0-1n1p2"
+ ":L6-7c2-3n2p2"
+ ":L8-9c0-1p3n3"
+ ":L10-11c0-1p4n3"
+ ":L12-13c0-1n4p5"
+ ":L14-15c2-3n5p5"),
+ cpu_topology_test(
+ Config,
+ [{node,[{processor,[{core,{logical,0}},
+ {core,{logical,1}}]}]},
+ {processor,[{node,[{core,{logical,2}},
+ {core,{logical,3}}]}]},
+ {processor,[{node,[{core,{logical,4}},
+ {core,{logical,5}}]}]},
+ {node,[{processor,[{core,{logical,6}},
+ {core,{logical,7}}]}]},
+ {node,[{processor,[{core,{logical,8}},
+ {core,{logical,9}}]}]},
+ {processor,[{node,[{core,{logical,10}},
+ {core,{logical,11}}]}]}],
+ "+sct "
+ "L0-1c0-1p0n0"
+ ":L2-3c0-1n1p1"
+ ":L4-5c0-1n2p2"
+ ":L6-7c0-1p3n3"
+ ":L8-9c0-1p4n4"
+ ":L10-11c0-1n5p5")
after
- restore_erl_rel_flags(OldRelFlags)
+ restore_erl_rel_flags(OldRelFlags)
end,
- ?line ok.
+ ok.
cpu_topology_test(Config, Topology, Cmd) ->
- ?line ?t:format("Testing~n ~p~n ~p~n", [Topology, Cmd]),
- ?line cpu_topology_bif_test(Config, Topology),
- ?line cpu_topology_cmdline_test(Config, Topology, Cmd),
- ?line ok.
+ io:format("Testing~n ~p~n ~p~n", [Topology, Cmd]),
+ cpu_topology_bif_test(Config, Topology),
+ cpu_topology_cmdline_test(Config, Topology, Cmd),
+ ok.
cpu_topology_bif_test(_Config, false) ->
- ?line ok;
+ ok;
cpu_topology_bif_test(Config, Topology) ->
- ?line {ok, Node} = start_node(Config),
- ?line _ = rpc:call(Node, erlang, system_flag, [cpu_topology, Topology]),
- ?line cmp(Topology, rpc:call(Node, erlang, system_info, [cpu_topology])),
- ?line stop_node(Node),
- ?line ok.
+ {ok, Node} = start_node(Config),
+ _ = rpc:call(Node, erlang, system_flag, [cpu_topology, Topology]),
+ cmp(Topology, rpc:call(Node, erlang, system_info, [cpu_topology])),
+ stop_node(Node),
+ ok.
cpu_topology_cmdline_test(_Config, _Topology, false) ->
- ?line ok;
+ ok;
cpu_topology_cmdline_test(Config, Topology, Cmd) ->
- ?line {ok, Node} = start_node(Config, Cmd),
- ?line cmp(Topology, rpc:call(Node, erlang, system_info, [cpu_topology])),
- ?line stop_node(Node),
- ?line ok.
+ {ok, Node} = start_node(Config, Cmd),
+ cmp(Topology, rpc:call(Node, erlang, system_info, [cpu_topology])),
+ stop_node(Node),
+ ok.
update_cpu_info(Config) when is_list(Config) ->
- ?line OldOnline = erlang:system_info(schedulers_online),
- ?line OldAff = get_affinity_mask(),
- ?line ?t:format("START - Affinity mask: ~p - Schedulers online: ~p - Scheduler bindings: ~p~n",
+ OldOnline = erlang:system_info(schedulers_online),
+ OldAff = get_affinity_mask(),
+ io:format("START - Affinity mask: ~p - Schedulers online: ~p - Scheduler bindings: ~p~n",
[OldAff, OldOnline, erlang:system_info(scheduler_bindings)]),
- ?line case {erlang:system_info(logical_processors_available), OldAff} of
- {Avail, _} when Avail == unknown; OldAff == unknown ->
+ case {erlang:system_info(logical_processors_available), OldAff} of
+ {Avail, _} when Avail == unknown; OldAff == unknown; OldAff == 1 ->
%% Nothing much to test; just a smoke test
case erlang:system_info(update_cpu_info) of
- unchanged -> ?line ok;
- changed -> ?line ok
+ unchanged -> ok;
+ changed -> ok
end;
- _ ->
+ {Avail, _} ->
try
- ?line adjust_schedulers_online(),
+ adjust_schedulers_online(),
case erlang:system_info(schedulers_online) of
1 ->
%% Nothing much to test; just a smoke test
- ?line ok;
+ ok;
Onln0 ->
%% unset least significant bit
- ?line Aff = (OldAff band (OldAff - 1)),
- ?line set_affinity_mask(Aff),
- ?line Onln1 = Onln0 - 1,
- ?line case adjust_schedulers_online() of
+ Aff = (OldAff band (OldAff - 1)),
+ set_affinity_mask(Aff),
+ Onln1 = Avail - 1,
+ case adjust_schedulers_online() of
{Onln0, Onln1} ->
- ?line Onln1 = erlang:system_info(schedulers_online),
- ?line receive after 500 -> ok end,
- ?line ?t:format("TEST - Affinity mask: ~p - Schedulers online: ~p - Scheduler bindings: ~p~n",
+ Onln1 = erlang:system_info(schedulers_online),
+ receive after 500 -> ok end,
+ io:format("TEST - Affinity mask: ~p - Schedulers online: ~p - Scheduler bindings: ~p~n",
[Aff, Onln1, erlang:system_info(scheduler_bindings)]),
- ?line unchanged = adjust_schedulers_online(),
- ?line ok;
+ unchanged = adjust_schedulers_online(),
+ ok;
Fail ->
- ?line ?t:fail(Fail)
+ ct:fail(Fail)
end
end
after
@@ -836,7 +828,7 @@ update_cpu_info(Config) when is_list(Config) ->
adjust_schedulers_online(),
erlang:system_flag(schedulers_online, OldOnline),
receive after 500 -> ok end,
- ?t:format("END - Affinity mask: ~p - Schedulers online: ~p - Scheduler bindings: ~p~n",
+ io:format("END - Affinity mask: ~p - Schedulers online: ~p - Scheduler bindings: ~p~n",
[get_affinity_mask(),
erlang:system_info(schedulers_online),
erlang:system_info(scheduler_bindings)])
@@ -883,7 +875,7 @@ get_affinity_mask(_Port, _Status, Affinity) ->
Affinity.
get_affinity_mask() ->
- case ?t:os_type() of
+ case os:type() of
{unix, linux} ->
case catch open_port({spawn, "taskset -p " ++ os:getpid()},
[exit_status]) of
@@ -927,21 +919,21 @@ set_affinity_mask(Mask) ->
end.
sct_cmd(Config) when is_list(Config) ->
- ?line Topology = ?TOPOLOGY_A_TERM,
- ?line OldRelFlags = clear_erl_rel_flags(),
+ Topology = ?TOPOLOGY_A_TERM,
+ OldRelFlags = clear_erl_rel_flags(),
try
- ?line {ok, Node} = start_node(Config, ?TOPOLOGY_A_CMD),
- ?line cmp(Topology,
+ {ok, Node} = start_node(Config, ?TOPOLOGY_A_CMD),
+ cmp(Topology,
rpc:call(Node, erlang, system_info, [cpu_topology])),
- ?line cmp(Topology,
+ cmp(Topology,
rpc:call(Node, erlang, system_flag, [cpu_topology, Topology])),
- ?line cmp(Topology,
+ cmp(Topology,
rpc:call(Node, erlang, system_info, [cpu_topology])),
- ?line stop_node(Node)
+ stop_node(Node)
after
restore_erl_rel_flags(OldRelFlags)
end,
- ?line ok.
+ ok.
-define(BIND_TYPES,
[{"u", unbound},
@@ -965,7 +957,7 @@ sbt_cmd(Config) when is_list(Config) ->
end,
case Bind of
notsup ->
- ?line {skipped, "Binding of schedulers not supported"};
+ {skipped, "Binding of schedulers not supported"};
go_for_it ->
CpuTCmd = case erlang:system_info({cpu_topology,detected}) of
undefined ->
@@ -988,14 +980,14 @@ sbt_cmd(Config) when is_list(Config) ->
end,
case CpuTCmd of
false ->
- ?line {skipped, "Don't know how to create cpu topology"};
+ {skipped, "Don't know how to create cpu topology"};
_ ->
case erlang:system_info(logical_processors) of
LP when is_integer(LP) ->
OldRelFlags = clear_erl_rel_flags(),
try
lists:foreach(fun ({ClBt, Bt}) ->
- ?line sbt_test(Config,
+ sbt_test(Config,
CpuTCmd,
ClBt,
Bt,
@@ -1005,44 +997,44 @@ sbt_cmd(Config) when is_list(Config) ->
after
restore_erl_rel_flags(OldRelFlags)
end,
- ?line ok;
+ ok;
_ ->
- ?line {skipped,
+ {skipped,
"Don't know the amount of logical processors"}
end
end
end.
sbt_test(Config, CpuTCmd, ClBt, Bt, LP) ->
- ?line ?t:format("Testing +sbt ~s (~p)~n", [ClBt, Bt]),
- ?line LPS = integer_to_list(LP),
- ?line Cmd = CpuTCmd++" +sbt "++ClBt++" +S"++LPS++":"++LPS,
- ?line {ok, Node} = start_node(Config, Cmd),
- ?line Bt = rpc:call(Node,
+ io:format("Testing +sbt ~s (~p)~n", [ClBt, Bt]),
+ LPS = integer_to_list(LP),
+ Cmd = CpuTCmd++" +sbt "++ClBt++" +S"++LPS++":"++LPS,
+ {ok, Node} = start_node(Config, Cmd),
+ Bt = rpc:call(Node,
erlang,
system_info,
[scheduler_bind_type]),
- ?line SB = rpc:call(Node,
+ SB = rpc:call(Node,
erlang,
system_info,
[scheduler_bindings]),
- ?line ?t:format("scheduler bindings: ~p~n", [SB]),
- ?line BS = case {Bt, erlang:system_info(logical_processors_available)} of
+ io:format("scheduler bindings: ~p~n", [SB]),
+ BS = case {Bt, erlang:system_info(logical_processors_available)} of
{unbound, _} -> 0;
{_, Int} when is_integer(Int) -> Int;
{_, _} -> LP
end,
- ?line lists:foldl(fun (S, 0) ->
- ?line unbound = S,
+ lists:foldl(fun (S, 0) ->
+ unbound = S,
0;
(S, N) ->
- ?line true = is_integer(S),
+ true = is_integer(S),
N-1
end,
BS,
tuple_to_list(SB)),
- ?line stop_node(Node),
- ?line ok.
+ stop_node(Node),
+ ok.
scheduler_threads(Config) when is_list(Config) ->
SmpSupport = erlang:system_info(smp_support),
@@ -1080,20 +1072,27 @@ scheduler_threads(Config) when is_list(Config) ->
{Sched, HalfSchedOnln, _} = get_sstate(Config, "+SP:50"),
%% Configure 2x scheduler threads only
{TwiceSched, SchedOnln, _} = get_sstate(Config, "+SP 200"),
- %% Test resetting the scheduler counts
- ResetCmd = "+S "++FourSched++":"++FourSchedOnln++" +S 0:0",
- {Sched, SchedOnln, _} = get_sstate(Config, ResetCmd),
- %% Test negative +S settings, but only for SMP-enabled emulators
- case SmpSupport of
- false -> ok;
- true ->
- SchedMinus1 = Sched-1,
- SchedOnlnMinus1 = SchedOnln-1,
- {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1"),
- {Sched, SchedOnlnMinus1, _} = get_sstate(Config, "+S :-1"),
- {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1:-1")
- end,
- ok.
+ case {erlang:system_info(logical_processors),
+ erlang:system_info(logical_processors_available)} of
+ {LProc, LProcAvail} when is_integer(LProc), is_integer(LProcAvail) ->
+ %% Test resetting the scheduler counts
+ ResetCmd = "+S "++FourSched++":"++FourSchedOnln++" +S 0:0",
+ {LProc, LProcAvail, _} = get_sstate(Config, ResetCmd),
+ %% Test negative +S settings, but only for SMP-enabled emulators
+ case {SmpSupport, LProc > 1, LProcAvail > 1} of
+ {true, true, true} ->
+ SchedMinus1 = LProc-1,
+ SchedOnlnMinus1 = LProcAvail-1,
+ {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1"),
+ {LProc, SchedOnlnMinus1, _} = get_sstate(Config, "+S :-1"),
+ {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1:-1"),
+ ok;
+ _ ->
+ {comment, "Skipped reduced amount of schedulers test due to too few logical processors"}
+ end;
+ _ -> %% Skipped when missing info about logical processors...
+ {comment, "Skipped reset amount of schedulers test, and reduced amount of schedulers test due to too unknown amount of logical processors"}
+ end.
dirty_scheduler_threads(Config) when is_list(Config) ->
SmpSupport = erlang:system_info(smp_support),
@@ -1130,6 +1129,7 @@ dirty_schedulers_online_test(true) ->
dirty_schedulers_online_smp_test(erlang:system_info(schedulers_online)).
dirty_schedulers_online_smp_test(SchedOnln) when SchedOnln < 4 -> ok;
dirty_schedulers_online_smp_test(SchedOnln) ->
+ receive after 500 -> ok end,
DirtyCPUSchedOnln = erlang:system_info(dirty_cpu_schedulers_online),
SchedOnln = DirtyCPUSchedOnln,
HalfSchedOnln = SchedOnln div 2,
@@ -1138,9 +1138,11 @@ dirty_schedulers_online_smp_test(SchedOnln) ->
HalfDirtyCPUSchedOnln = erlang:system_flag(schedulers_online, SchedOnln),
DirtyCPUSchedOnln = erlang:system_flag(dirty_cpu_schedulers_online,
HalfDirtyCPUSchedOnln),
+ receive after 500 -> ok end,
HalfDirtyCPUSchedOnln = erlang:system_info(dirty_cpu_schedulers_online),
QrtrDirtyCPUSchedOnln = HalfDirtyCPUSchedOnln div 2,
SchedOnln = erlang:system_flag(schedulers_online, HalfSchedOnln),
+ receive after 500 -> ok end,
QrtrDirtyCPUSchedOnln = erlang:system_info(dirty_cpu_schedulers_online),
ok.
@@ -1166,50 +1168,184 @@ get_dsstate(Config, Cmd) ->
stop_node(Node),
{DSCPU, DSCPUOnln, DSIO}.
+scheduler_suspend_basic(Config) when is_list(Config) ->
+ case erlang:system_info(multi_scheduling) of
+ disabled ->
+ {skip, "Nothing to test"};
+ _ ->
+ Onln = erlang:system_info(schedulers_online),
+ try
+ scheduler_suspend_basic_test()
+ after
+ erlang:system_flag(schedulers_online, Onln)
+ end
+ end.
+
+scheduler_suspend_basic_test() ->
+ %% The receives after setting scheduler states are there
+ %% since the operation is not fully synchronous. For example,
+ %% we do not wait for dirty cpu schedulers online to complete
+ %% before returning from erlang:system_flag(schedulers_online, _).
+
+ erlang:system_flag(schedulers_online,
+ erlang:system_info(schedulers)),
+ try
+ erlang:system_flag(dirty_cpu_schedulers_online,
+ erlang:system_info(dirty_cpu_schedulers)),
+ receive after 500 -> ok end
+ catch
+ _ : _ ->
+ ok
+ end,
+
+ S0 = sched_state(),
+ io:format("~p~n", [S0]),
+ {{normal,NTot0,NOnln0,NAct0},
+ {dirty_cpu,DCTot0,DCOnln0,DCAct0},
+ {dirty_io,DITot0,DIOnln0,DIAct0}} = S0,
+ enabled = erlang:system_info(multi_scheduling),
+
+ DCOne = case DCTot0 of
+ 0 -> 0;
+ _ -> 1
+ end,
+
+ blocked_normal = erlang:system_flag(multi_scheduling, block_normal),
+ blocked_normal = erlang:system_info(multi_scheduling),
+ {{normal,NTot0,NOnln0,1},
+ {dirty_cpu,DCTot0,DCOnln0,DCAct0},
+ {dirty_io,DITot0,DIOnln0,DIAct0}} = sched_state(),
+
+ NOnln0 = erlang:system_flag(schedulers_online, 1),
+ receive after 500 -> ok end,
+ {{normal,NTot0,1,1},
+ {dirty_cpu,DCTot0,DCOne,DCOne},
+ {dirty_io,DITot0,DIOnln0,DIAct0}} = sched_state(),
+
+ 1 = erlang:system_flag(schedulers_online, NOnln0),
+ receive after 500 -> ok end,
+ {{normal,NTot0,NOnln0,1},
+ {dirty_cpu,DCTot0,DCOnln0,DCAct0},
+ {dirty_io,DITot0,DIOnln0,DIAct0}} = sched_state(),
+
+ blocked = erlang:system_flag(multi_scheduling, block),
+ blocked = erlang:system_info(multi_scheduling),
+ receive after 500 -> ok end,
+ {{normal,NTot0,NOnln0,1},
+ {dirty_cpu,DCTot0,DCOnln0,0},
+ {dirty_io,DITot0,DIOnln0,0}} = sched_state(),
+
+ NOnln0 = erlang:system_flag(schedulers_online, 1),
+ receive after 500 -> ok end,
+ {{normal,NTot0,1,1},
+ {dirty_cpu,DCTot0,DCOne,0},
+ {dirty_io,DITot0,DIOnln0,0}} = sched_state(),
+
+ 1 = erlang:system_flag(schedulers_online, NOnln0),
+ receive after 500 -> ok end,
+ {{normal,NTot0,NOnln0,1},
+ {dirty_cpu,DCTot0,DCOnln0,0},
+ {dirty_io,DITot0,DIOnln0,0}} = sched_state(),
+
+ blocked = erlang:system_flag(multi_scheduling, unblock_normal),
+ blocked = erlang:system_info(multi_scheduling),
+ {{normal,NTot0,NOnln0,1},
+ {dirty_cpu,DCTot0,DCOnln0,0},
+ {dirty_io,DITot0,DIOnln0,0}} = sched_state(),
+
+ enabled = erlang:system_flag(multi_scheduling, unblock),
+ enabled = erlang:system_info(multi_scheduling),
+ receive after 500 -> ok end,
+ {{normal,NTot0,NOnln0,NAct0},
+ {dirty_cpu,DCTot0,DCOnln0,DCAct0},
+ {dirty_io,DITot0,DIOnln0,DIAct0}} = sched_state(),
+
+ NOnln0 = erlang:system_flag(schedulers_online, 1),
+ receive after 500 -> ok end,
+ {{normal,NTot0,1,1},
+ {dirty_cpu,DCTot0,DCOne,DCOne},
+ {dirty_io,DITot0,DIOnln0,DIAct0}} = sched_state(),
+
+ 1 = erlang:system_flag(schedulers_online, NOnln0),
+ receive after 500 -> ok end,
+ {{normal,NTot0,NOnln0,NAct0},
+ {dirty_cpu,DCTot0,DCOnln0,DCAct0},
+ {dirty_io,DITot0,DIOnln0,DIAct0}} = sched_state(),
+
+ ok.
+
+
scheduler_suspend(Config) when is_list(Config) ->
- ?line Dog = ?t:timetrap(?t:minutes(5)),
- ?line lists:foreach(fun (S) -> scheduler_suspend_test(Config, S) end,
+ ct:timetrap({minutes, 5}),
+ lists:foreach(fun (S) -> scheduler_suspend_test(Config, S) end,
[64, 32, 16, default]),
- ?line ?t:timetrap_cancel(Dog),
- ?line ok.
+ ok.
scheduler_suspend_test(Config, Schedulers) ->
- ?line Cmd = case Schedulers of
+ Cmd = case Schedulers of
default ->
"";
_ ->
S = integer_to_list(Schedulers),
"+S"++S++":"++S
end,
- ?line {ok, Node} = start_node(Config, Cmd),
- ?line [SState] = mcall(Node, [fun () ->
- erlang:system_info(schedulers_state)
- end]),
- ?line ?t:format("SState=~p~n", [SState]),
- ?line {Sched, SchedOnln, _SchedAvail} = SState,
- ?line true = is_integer(Sched),
- ?line [ok] = mcall(Node, [fun () -> sst0_loop(300) end]),
- ?line [ok] = mcall(Node, [fun () -> sst1_loop(300) end]),
- ?line [ok] = mcall(Node, [fun () -> sst2_loop(300) end]),
- ?line [ok, ok, ok, ok, ok] = mcall(Node,
- [fun () -> sst0_loop(200) end,
- fun () -> sst1_loop(200) end,
- fun () -> sst2_loop(200) end,
- fun () -> sst2_loop(200) end,
- fun () -> sst3_loop(Sched, 200) end]),
- ?line [SState] = mcall(Node, [fun () ->
- case Sched == SchedOnln of
- false ->
- Sched = erlang:system_flag(
- schedulers_online,
- SchedOnln);
- true ->
- ok
- end,
- erlang:system_info(schedulers_state)
- end]),
- ?line stop_node(Node),
- ?line ok.
-
+ {ok, Node} = start_node(Config, Cmd),
+ [SState] = mcall(Node, [fun () ->
+ erlang:system_info(schedulers_state)
+ end]),
+
+ io:format("SState=~p~n", [SState]),
+ {Sched, SchedOnln, _SchedAvail} = SState,
+ true = is_integer(Sched),
+ [ok] = mcall(Node, [fun () -> sst0_loop(300) end]),
+ [ok] = mcall(Node, [fun () -> sst1_loop(300) end]),
+ [ok] = mcall(Node, [fun () -> sst2_loop(300) end]),
+ [ok] = mcall(Node, [fun () -> sst4_loop(300) end]),
+ [ok] = mcall(Node, [fun () -> sst5_loop(300) end]),
+ [ok, ok, ok, ok,
+ ok, ok, ok] = mcall(Node,
+ [fun () -> sst0_loop(200) end,
+ fun () -> sst1_loop(200) end,
+ fun () -> sst2_loop(200) end,
+ fun () -> sst2_loop(200) end,
+ fun () -> sst3_loop(Sched, 200) end,
+ fun () -> sst4_loop(200) end,
+ fun () -> sst5_loop(200) end]),
+ [SState] = mcall(Node, [fun () ->
+ case Sched == SchedOnln of
+ false ->
+ Sched = erlang:system_flag(
+ schedulers_online,
+ SchedOnln);
+ true ->
+ ok
+ end,
+ until(fun () ->
+ {_A, B, C} = erlang:system_info(
+ schedulers_state),
+ B == C
+ end,
+ erlang:monotonic_time()
+ + erlang:convert_time_unit(1,
+ seconds,
+ native)),
+ erlang:system_info(schedulers_state)
+ end]),
+ stop_node(Node),
+ ok.
+
+until(Pred, MaxTime) ->
+ case Pred() of
+ true ->
+ true;
+ false ->
+ case erlang:monotonic_time() > MaxTime of
+ true ->
+ false;
+ false ->
+ receive after 100 -> ok end,
+ until(Pred, MaxTime)
+ end
+ end.
sst0_loop(0) ->
ok;
@@ -1272,270 +1408,283 @@ sst3_loop_with_dirty_schedulers(S, DS, N) ->
erlang:system_flag(dirty_cpu_schedulers_online, DS),
sst3_loop_with_dirty_schedulers(S, DS, N-1).
+sst4_loop(0) ->
+ ok;
+sst4_loop(N) ->
+ erlang:system_flag(multi_scheduling, block_normal),
+ erlang:system_flag(multi_scheduling, unblock_normal),
+ sst4_loop(N-1).
+
+sst5_loop(0) ->
+ ok;
+sst5_loop(N) ->
+ erlang:system_flag(multi_scheduling, block_normal),
+ erlang:system_flag(multi_scheduling, unblock_normal),
+ sst5_loop(N-1).
+
reader_groups(Config) when is_list(Config) ->
%% White box testing. These results are correct, but other results
%% could be too...
%% The actual tilepro64 topology
CPUT0 = [{processor,[{node,[{core,{logical,0}},
- {core,{logical,1}},
- {core,{logical,2}},
- {core,{logical,8}},
- {core,{logical,9}},
- {core,{logical,10}},
- {core,{logical,11}},
- {core,{logical,16}},
- {core,{logical,17}},
- {core,{logical,18}},
- {core,{logical,19}},
- {core,{logical,24}},
- {core,{logical,25}},
- {core,{logical,27}},
- {core,{logical,29}}]},
- {node,[{core,{logical,3}},
- {core,{logical,4}},
- {core,{logical,5}},
- {core,{logical,6}},
- {core,{logical,7}},
- {core,{logical,12}},
- {core,{logical,13}},
- {core,{logical,14}},
- {core,{logical,15}},
- {core,{logical,20}},
- {core,{logical,21}},
- {core,{logical,22}},
- {core,{logical,23}},
- {core,{logical,28}},
- {core,{logical,30}}]},
- {node,[{core,{logical,31}},
- {core,{logical,36}},
- {core,{logical,37}},
- {core,{logical,38}},
- {core,{logical,44}},
- {core,{logical,45}},
- {core,{logical,46}},
- {core,{logical,47}},
- {core,{logical,51}},
- {core,{logical,52}},
- {core,{logical,53}},
- {core,{logical,54}},
- {core,{logical,55}},
- {core,{logical,60}},
- {core,{logical,61}}]},
- {node,[{core,{logical,26}},
- {core,{logical,32}},
- {core,{logical,33}},
- {core,{logical,34}},
- {core,{logical,35}},
- {core,{logical,39}},
- {core,{logical,40}},
- {core,{logical,41}},
- {core,{logical,42}},
- {core,{logical,43}},
- {core,{logical,48}},
- {core,{logical,49}},
- {core,{logical,50}},
- {core,{logical,58}}]}]}],
-
- ?line [{0,1},{1,1},{2,1},{3,3},{4,3},{5,3},{6,3},{7,3},{8,1},{9,1},{10,1},
- {11,1},{12,3},{13,3},{14,4},{15,4},{16,2},{17,2},{18,2},{19,2},
- {20,4},{21,4},{22,4},{23,4},{24,2},{25,2},{26,7},{27,2},{28,4},
- {29,2},{30,4},{31,5},{32,7},{33,7},{34,7},{35,7},{36,5},{37,5},
- {38,5},{39,7},{40,7},{41,8},{42,8},{43,8},{44,5},{45,5},{46,5},
- {47,6},{48,8},{49,8},{50,8},{51,6},{52,6},{53,6},{54,6},{55,6},
- {58,8},{60,6},{61,6}]
- = reader_groups_map(CPUT0, 8),
+ {core,{logical,1}},
+ {core,{logical,2}},
+ {core,{logical,8}},
+ {core,{logical,9}},
+ {core,{logical,10}},
+ {core,{logical,11}},
+ {core,{logical,16}},
+ {core,{logical,17}},
+ {core,{logical,18}},
+ {core,{logical,19}},
+ {core,{logical,24}},
+ {core,{logical,25}},
+ {core,{logical,27}},
+ {core,{logical,29}}]},
+ {node,[{core,{logical,3}},
+ {core,{logical,4}},
+ {core,{logical,5}},
+ {core,{logical,6}},
+ {core,{logical,7}},
+ {core,{logical,12}},
+ {core,{logical,13}},
+ {core,{logical,14}},
+ {core,{logical,15}},
+ {core,{logical,20}},
+ {core,{logical,21}},
+ {core,{logical,22}},
+ {core,{logical,23}},
+ {core,{logical,28}},
+ {core,{logical,30}}]},
+ {node,[{core,{logical,31}},
+ {core,{logical,36}},
+ {core,{logical,37}},
+ {core,{logical,38}},
+ {core,{logical,44}},
+ {core,{logical,45}},
+ {core,{logical,46}},
+ {core,{logical,47}},
+ {core,{logical,51}},
+ {core,{logical,52}},
+ {core,{logical,53}},
+ {core,{logical,54}},
+ {core,{logical,55}},
+ {core,{logical,60}},
+ {core,{logical,61}}]},
+ {node,[{core,{logical,26}},
+ {core,{logical,32}},
+ {core,{logical,33}},
+ {core,{logical,34}},
+ {core,{logical,35}},
+ {core,{logical,39}},
+ {core,{logical,40}},
+ {core,{logical,41}},
+ {core,{logical,42}},
+ {core,{logical,43}},
+ {core,{logical,48}},
+ {core,{logical,49}},
+ {core,{logical,50}},
+ {core,{logical,58}}]}]}],
+
+ [{0,1},{1,1},{2,1},{3,3},{4,3},{5,3},{6,3},{7,3},{8,1},{9,1},{10,1},
+ {11,1},{12,3},{13,3},{14,4},{15,4},{16,2},{17,2},{18,2},{19,2},
+ {20,4},{21,4},{22,4},{23,4},{24,2},{25,2},{26,7},{27,2},{28,4},
+ {29,2},{30,4},{31,5},{32,7},{33,7},{34,7},{35,7},{36,5},{37,5},
+ {38,5},{39,7},{40,7},{41,8},{42,8},{43,8},{44,5},{45,5},{46,5},
+ {47,6},{48,8},{49,8},{50,8},{51,6},{52,6},{53,6},{54,6},{55,6},
+ {58,8},{60,6},{61,6}]
+ = reader_groups_map(CPUT0, 8),
CPUT1 = [n([p([c([t(l(0)),t(l(1)),t(l(2)),t(l(3))]),
- c([t(l(4)),t(l(5)),t(l(6)),t(l(7))]),
- c([t(l(8)),t(l(9)),t(l(10)),t(l(11))]),
- c([t(l(12)),t(l(13)),t(l(14)),t(l(15))])]),
- p([c([t(l(16)),t(l(17)),t(l(18)),t(l(19))]),
- c([t(l(20)),t(l(21)),t(l(22)),t(l(23))]),
- c([t(l(24)),t(l(25)),t(l(26)),t(l(27))]),
- c([t(l(28)),t(l(29)),t(l(30)),t(l(31))])])]),
- n([p([c([t(l(32)),t(l(33)),t(l(34)),t(l(35))]),
- c([t(l(36)),t(l(37)),t(l(38)),t(l(39))]),
- c([t(l(40)),t(l(41)),t(l(42)),t(l(43))]),
- c([t(l(44)),t(l(45)),t(l(46)),t(l(47))])]),
- p([c([t(l(48)),t(l(49)),t(l(50)),t(l(51))]),
- c([t(l(52)),t(l(53)),t(l(54)),t(l(55))]),
- c([t(l(56)),t(l(57)),t(l(58)),t(l(59))]),
- c([t(l(60)),t(l(61)),t(l(62)),t(l(63))])])]),
- n([p([c([t(l(64)),t(l(65)),t(l(66)),t(l(67))]),
- c([t(l(68)),t(l(69)),t(l(70)),t(l(71))]),
- c([t(l(72)),t(l(73)),t(l(74)),t(l(75))]),
- c([t(l(76)),t(l(77)),t(l(78)),t(l(79))])]),
- p([c([t(l(80)),t(l(81)),t(l(82)),t(l(83))]),
- c([t(l(84)),t(l(85)),t(l(86)),t(l(87))]),
- c([t(l(88)),t(l(89)),t(l(90)),t(l(91))]),
- c([t(l(92)),t(l(93)),t(l(94)),t(l(95))])])]),
- n([p([c([t(l(96)),t(l(97)),t(l(98)),t(l(99))]),
- c([t(l(100)),t(l(101)),t(l(102)),t(l(103))]),
- c([t(l(104)),t(l(105)),t(l(106)),t(l(107))]),
- c([t(l(108)),t(l(109)),t(l(110)),t(l(111))])]),
- p([c([t(l(112)),t(l(113)),t(l(114)),t(l(115))]),
- c([t(l(116)),t(l(117)),t(l(118)),t(l(119))]),
- c([t(l(120)),t(l(121)),t(l(122)),t(l(123))]),
- c([t(l(124)),t(l(125)),t(l(126)),t(l(127))])])])],
-
- ?line [{0,1},{1,1},{2,1},{3,1},{4,2},{5,2},{6,2},{7,2},{8,3},{9,3},
- {10,3},{11,3},{12,4},{13,4},{14,4},{15,4},{16,5},{17,5},{18,5},
- {19,5},{20,6},{21,6},{22,6},{23,6},{24,7},{25,7},{26,7},{27,7},
- {28,8},{29,8},{30,8},{31,8},{32,9},{33,9},{34,9},{35,9},{36,10},
- {37,10},{38,10},{39,10},{40,11},{41,11},{42,11},{43,11},{44,12},
- {45,12},{46,12},{47,12},{48,13},{49,13},{50,13},{51,13},{52,14},
- {53,14},{54,14},{55,14},{56,15},{57,15},{58,15},{59,15},{60,16},
- {61,16},{62,16},{63,16},{64,17},{65,17},{66,17},{67,17},{68,18},
- {69,18},{70,18},{71,18},{72,19},{73,19},{74,19},{75,19},{76,20},
- {77,20},{78,20},{79,20},{80,21},{81,21},{82,21},{83,21},{84,22},
- {85,22},{86,22},{87,22},{88,23},{89,23},{90,23},{91,23},{92,24},
- {93,24},{94,24},{95,24},{96,25},{97,25},{98,25},{99,25},{100,26},
- {101,26},{102,26},{103,26},{104,27},{105,27},{106,27},{107,27},
- {108,28},{109,28},{110,28},{111,28},{112,29},{113,29},{114,29},
- {115,29},{116,30},{117,30},{118,30},{119,30},{120,31},{121,31},
- {122,31},{123,31},{124,32},{125,32},{126,32},{127,32}]
- = reader_groups_map(CPUT1, 128),
-
- ?line [{0,1},{1,1},{2,1},{3,1},{4,1},{5,1},{6,1},{7,1},{8,1},{9,1},{10,1},
- {11,1},{12,1},{13,1},{14,1},{15,1},{16,1},{17,1},{18,1},{19,1},
- {20,1},{21,1},{22,1},{23,1},{24,1},{25,1},{26,1},{27,1},{28,1},
- {29,1},{30,1},{31,1},{32,1},{33,1},{34,1},{35,1},{36,1},{37,1},
- {38,1},{39,1},{40,1},{41,1},{42,1},{43,1},{44,1},{45,1},{46,1},
- {47,1},{48,1},{49,1},{50,1},{51,1},{52,1},{53,1},{54,1},{55,1},
- {56,1},{57,1},{58,1},{59,1},{60,1},{61,1},{62,1},{63,1},{64,2},
- {65,2},{66,2},{67,2},{68,2},{69,2},{70,2},{71,2},{72,2},{73,2},
- {74,2},{75,2},{76,2},{77,2},{78,2},{79,2},{80,2},{81,2},{82,2},
- {83,2},{84,2},{85,2},{86,2},{87,2},{88,2},{89,2},{90,2},{91,2},
- {92,2},{93,2},{94,2},{95,2},{96,2},{97,2},{98,2},{99,2},{100,2},
- {101,2},{102,2},{103,2},{104,2},{105,2},{106,2},{107,2},{108,2},
- {109,2},{110,2},{111,2},{112,2},{113,2},{114,2},{115,2},{116,2},
- {117,2},{118,2},{119,2},{120,2},{121,2},{122,2},{123,2},{124,2},
- {125,2},{126,2},{127,2}]
- = reader_groups_map(CPUT1, 2),
-
- ?line [{0,1},{1,1},{2,1},{3,1},{4,2},{5,2},{6,2},{7,2},{8,3},{9,3},{10,3},
- {11,3},{12,3},{13,3},{14,3},{15,3},{16,4},{17,4},{18,4},{19,4},
- {20,4},{21,4},{22,4},{23,4},{24,5},{25,5},{26,5},{27,5},{28,5},
- {29,5},{30,5},{31,5},{32,6},{33,6},{34,6},{35,6},{36,6},{37,6},
- {38,6},{39,6},{40,7},{41,7},{42,7},{43,7},{44,7},{45,7},{46,7},
- {47,7},{48,8},{49,8},{50,8},{51,8},{52,8},{53,8},{54,8},{55,8},
- {56,9},{57,9},{58,9},{59,9},{60,9},{61,9},{62,9},{63,9},{64,10},
- {65,10},{66,10},{67,10},{68,10},{69,10},{70,10},{71,10},{72,11},
- {73,11},{74,11},{75,11},{76,11},{77,11},{78,11},{79,11},{80,12},
- {81,12},{82,12},{83,12},{84,12},{85,12},{86,12},{87,12},{88,13},
- {89,13},{90,13},{91,13},{92,13},{93,13},{94,13},{95,13},{96,14},
- {97,14},{98,14},{99,14},{100,14},{101,14},{102,14},{103,14},
- {104,15},{105,15},{106,15},{107,15},{108,15},{109,15},{110,15},
- {111,15},{112,16},{113,16},{114,16},{115,16},{116,16},{117,16},
- {118,16},{119,16},{120,17},{121,17},{122,17},{123,17},{124,17},
- {125,17},{126,17},{127,17}]
- = reader_groups_map(CPUT1, 17),
-
- ?line [{0,1},{1,1},{2,1},{3,1},{4,1},{5,1},{6,1},{7,1},{8,1},{9,1},{10,1},
- {11,1},{12,1},{13,1},{14,1},{15,1},{16,2},{17,2},{18,2},{19,2},
- {20,2},{21,2},{22,2},{23,2},{24,2},{25,2},{26,2},{27,2},{28,2},
- {29,2},{30,2},{31,2},{32,3},{33,3},{34,3},{35,3},{36,3},{37,3},
- {38,3},{39,3},{40,3},{41,3},{42,3},{43,3},{44,3},{45,3},{46,3},
- {47,3},{48,4},{49,4},{50,4},{51,4},{52,4},{53,4},{54,4},{55,4},
- {56,4},{57,4},{58,4},{59,4},{60,4},{61,4},{62,4},{63,4},{64,5},
- {65,5},{66,5},{67,5},{68,5},{69,5},{70,5},{71,5},{72,5},{73,5},
- {74,5},{75,5},{76,5},{77,5},{78,5},{79,5},{80,6},{81,6},{82,6},
- {83,6},{84,6},{85,6},{86,6},{87,6},{88,6},{89,6},{90,6},{91,6},
- {92,6},{93,6},{94,6},{95,6},{96,7},{97,7},{98,7},{99,7},{100,7},
- {101,7},{102,7},{103,7},{104,7},{105,7},{106,7},{107,7},{108,7},
- {109,7},{110,7},{111,7},{112,7},{113,7},{114,7},{115,7},{116,7},
- {117,7},{118,7},{119,7},{120,7},{121,7},{122,7},{123,7},{124,7},
- {125,7},{126,7},{127,7}]
- = reader_groups_map(CPUT1, 7),
-
- ?line CPUT2 = [p([c(l(0)),c(l(1)),c(l(2)),c(l(3)),c(l(4))]),
- p([t(l(5)),t(l(6)),t(l(7)),t(l(8)),t(l(9))]),
- p([t(l(10))]),
- p([c(l(11)),c(l(12)),c(l(13))]),
- p([c(l(14)),c(l(15))])],
-
- ?line [{0,1},{1,1},{2,1},{3,1},{4,1},
- {5,2},{6,2},{7,2},{8,2},{9,2},
- {10,3},
- {11,4},{12,4},{13,4},
- {14,5},{15,5}] = reader_groups_map(CPUT2, 5),
-
-
- ?line [{0,1},{1,1},{2,2},{3,2},{4,2},
- {5,3},{6,3},{7,3},{8,3},{9,3},
- {10,4},
- {11,5},{12,5},{13,5},
- {14,6},{15,6}] = reader_groups_map(CPUT2, 6),
-
- ?line [{0,1},{1,1},{2,2},{3,2},{4,2},
- {5,3},{6,3},{7,3},{8,3},{9,3},
- {10,4},
- {11,5},{12,6},{13,6},
- {14,7},{15,7}] = reader_groups_map(CPUT2, 7),
-
- ?line [{0,1},{1,1},{2,2},{3,2},{4,2},
- {5,3},{6,3},{7,3},{8,3},{9,3},
- {10,4},
- {11,5},{12,6},{13,6},
- {14,7},{15,8}] = reader_groups_map(CPUT2, 8),
-
- ?line [{0,1},{1,2},{2,2},{3,3},{4,3},
- {5,4},{6,4},{7,4},{8,4},{9,4},
- {10,5},
- {11,6},{12,7},{13,7},
- {14,8},{15,9}] = reader_groups_map(CPUT2, 9),
-
- ?line [{0,1},{1,2},{2,2},{3,3},{4,3},
- {5,4},{6,4},{7,4},{8,4},{9,4},
- {10,5},
- {11,6},{12,7},{13,8},
- {14,9},{15,10}] = reader_groups_map(CPUT2, 10),
-
- ?line [{0,1},{1,2},{2,3},{3,4},{4,4},
- {5,5},{6,5},{7,5},{8,5},{9,5},
- {10,6},
- {11,7},{12,8},{13,9},
- {14,10},{15,11}] = reader_groups_map(CPUT2, 11),
-
- ?line [{0,1},{1,2},{2,3},{3,4},{4,5},
- {5,6},{6,6},{7,6},{8,6},{9,6},
- {10,7},
- {11,8},{12,9},{13,10},
- {14,11},{15,12}] = reader_groups_map(CPUT2, 100),
+ c([t(l(4)),t(l(5)),t(l(6)),t(l(7))]),
+ c([t(l(8)),t(l(9)),t(l(10)),t(l(11))]),
+ c([t(l(12)),t(l(13)),t(l(14)),t(l(15))])]),
+ p([c([t(l(16)),t(l(17)),t(l(18)),t(l(19))]),
+ c([t(l(20)),t(l(21)),t(l(22)),t(l(23))]),
+ c([t(l(24)),t(l(25)),t(l(26)),t(l(27))]),
+ c([t(l(28)),t(l(29)),t(l(30)),t(l(31))])])]),
+ n([p([c([t(l(32)),t(l(33)),t(l(34)),t(l(35))]),
+ c([t(l(36)),t(l(37)),t(l(38)),t(l(39))]),
+ c([t(l(40)),t(l(41)),t(l(42)),t(l(43))]),
+ c([t(l(44)),t(l(45)),t(l(46)),t(l(47))])]),
+ p([c([t(l(48)),t(l(49)),t(l(50)),t(l(51))]),
+ c([t(l(52)),t(l(53)),t(l(54)),t(l(55))]),
+ c([t(l(56)),t(l(57)),t(l(58)),t(l(59))]),
+ c([t(l(60)),t(l(61)),t(l(62)),t(l(63))])])]),
+ n([p([c([t(l(64)),t(l(65)),t(l(66)),t(l(67))]),
+ c([t(l(68)),t(l(69)),t(l(70)),t(l(71))]),
+ c([t(l(72)),t(l(73)),t(l(74)),t(l(75))]),
+ c([t(l(76)),t(l(77)),t(l(78)),t(l(79))])]),
+ p([c([t(l(80)),t(l(81)),t(l(82)),t(l(83))]),
+ c([t(l(84)),t(l(85)),t(l(86)),t(l(87))]),
+ c([t(l(88)),t(l(89)),t(l(90)),t(l(91))]),
+ c([t(l(92)),t(l(93)),t(l(94)),t(l(95))])])]),
+ n([p([c([t(l(96)),t(l(97)),t(l(98)),t(l(99))]),
+ c([t(l(100)),t(l(101)),t(l(102)),t(l(103))]),
+ c([t(l(104)),t(l(105)),t(l(106)),t(l(107))]),
+ c([t(l(108)),t(l(109)),t(l(110)),t(l(111))])]),
+ p([c([t(l(112)),t(l(113)),t(l(114)),t(l(115))]),
+ c([t(l(116)),t(l(117)),t(l(118)),t(l(119))]),
+ c([t(l(120)),t(l(121)),t(l(122)),t(l(123))]),
+ c([t(l(124)),t(l(125)),t(l(126)),t(l(127))])])])],
+
+ [{0,1},{1,1},{2,1},{3,1},{4,2},{5,2},{6,2},{7,2},{8,3},{9,3},
+ {10,3},{11,3},{12,4},{13,4},{14,4},{15,4},{16,5},{17,5},{18,5},
+ {19,5},{20,6},{21,6},{22,6},{23,6},{24,7},{25,7},{26,7},{27,7},
+ {28,8},{29,8},{30,8},{31,8},{32,9},{33,9},{34,9},{35,9},{36,10},
+ {37,10},{38,10},{39,10},{40,11},{41,11},{42,11},{43,11},{44,12},
+ {45,12},{46,12},{47,12},{48,13},{49,13},{50,13},{51,13},{52,14},
+ {53,14},{54,14},{55,14},{56,15},{57,15},{58,15},{59,15},{60,16},
+ {61,16},{62,16},{63,16},{64,17},{65,17},{66,17},{67,17},{68,18},
+ {69,18},{70,18},{71,18},{72,19},{73,19},{74,19},{75,19},{76,20},
+ {77,20},{78,20},{79,20},{80,21},{81,21},{82,21},{83,21},{84,22},
+ {85,22},{86,22},{87,22},{88,23},{89,23},{90,23},{91,23},{92,24},
+ {93,24},{94,24},{95,24},{96,25},{97,25},{98,25},{99,25},{100,26},
+ {101,26},{102,26},{103,26},{104,27},{105,27},{106,27},{107,27},
+ {108,28},{109,28},{110,28},{111,28},{112,29},{113,29},{114,29},
+ {115,29},{116,30},{117,30},{118,30},{119,30},{120,31},{121,31},
+ {122,31},{123,31},{124,32},{125,32},{126,32},{127,32}]
+ = reader_groups_map(CPUT1, 128),
+
+ [{0,1},{1,1},{2,1},{3,1},{4,1},{5,1},{6,1},{7,1},{8,1},{9,1},{10,1},
+ {11,1},{12,1},{13,1},{14,1},{15,1},{16,1},{17,1},{18,1},{19,1},
+ {20,1},{21,1},{22,1},{23,1},{24,1},{25,1},{26,1},{27,1},{28,1},
+ {29,1},{30,1},{31,1},{32,1},{33,1},{34,1},{35,1},{36,1},{37,1},
+ {38,1},{39,1},{40,1},{41,1},{42,1},{43,1},{44,1},{45,1},{46,1},
+ {47,1},{48,1},{49,1},{50,1},{51,1},{52,1},{53,1},{54,1},{55,1},
+ {56,1},{57,1},{58,1},{59,1},{60,1},{61,1},{62,1},{63,1},{64,2},
+ {65,2},{66,2},{67,2},{68,2},{69,2},{70,2},{71,2},{72,2},{73,2},
+ {74,2},{75,2},{76,2},{77,2},{78,2},{79,2},{80,2},{81,2},{82,2},
+ {83,2},{84,2},{85,2},{86,2},{87,2},{88,2},{89,2},{90,2},{91,2},
+ {92,2},{93,2},{94,2},{95,2},{96,2},{97,2},{98,2},{99,2},{100,2},
+ {101,2},{102,2},{103,2},{104,2},{105,2},{106,2},{107,2},{108,2},
+ {109,2},{110,2},{111,2},{112,2},{113,2},{114,2},{115,2},{116,2},
+ {117,2},{118,2},{119,2},{120,2},{121,2},{122,2},{123,2},{124,2},
+ {125,2},{126,2},{127,2}]
+ = reader_groups_map(CPUT1, 2),
+
+ [{0,1},{1,1},{2,1},{3,1},{4,2},{5,2},{6,2},{7,2},{8,3},{9,3},{10,3},
+ {11,3},{12,3},{13,3},{14,3},{15,3},{16,4},{17,4},{18,4},{19,4},
+ {20,4},{21,4},{22,4},{23,4},{24,5},{25,5},{26,5},{27,5},{28,5},
+ {29,5},{30,5},{31,5},{32,6},{33,6},{34,6},{35,6},{36,6},{37,6},
+ {38,6},{39,6},{40,7},{41,7},{42,7},{43,7},{44,7},{45,7},{46,7},
+ {47,7},{48,8},{49,8},{50,8},{51,8},{52,8},{53,8},{54,8},{55,8},
+ {56,9},{57,9},{58,9},{59,9},{60,9},{61,9},{62,9},{63,9},{64,10},
+ {65,10},{66,10},{67,10},{68,10},{69,10},{70,10},{71,10},{72,11},
+ {73,11},{74,11},{75,11},{76,11},{77,11},{78,11},{79,11},{80,12},
+ {81,12},{82,12},{83,12},{84,12},{85,12},{86,12},{87,12},{88,13},
+ {89,13},{90,13},{91,13},{92,13},{93,13},{94,13},{95,13},{96,14},
+ {97,14},{98,14},{99,14},{100,14},{101,14},{102,14},{103,14},
+ {104,15},{105,15},{106,15},{107,15},{108,15},{109,15},{110,15},
+ {111,15},{112,16},{113,16},{114,16},{115,16},{116,16},{117,16},
+ {118,16},{119,16},{120,17},{121,17},{122,17},{123,17},{124,17},
+ {125,17},{126,17},{127,17}]
+ = reader_groups_map(CPUT1, 17),
+
+ [{0,1},{1,1},{2,1},{3,1},{4,1},{5,1},{6,1},{7,1},{8,1},{9,1},{10,1},
+ {11,1},{12,1},{13,1},{14,1},{15,1},{16,2},{17,2},{18,2},{19,2},
+ {20,2},{21,2},{22,2},{23,2},{24,2},{25,2},{26,2},{27,2},{28,2},
+ {29,2},{30,2},{31,2},{32,3},{33,3},{34,3},{35,3},{36,3},{37,3},
+ {38,3},{39,3},{40,3},{41,3},{42,3},{43,3},{44,3},{45,3},{46,3},
+ {47,3},{48,4},{49,4},{50,4},{51,4},{52,4},{53,4},{54,4},{55,4},
+ {56,4},{57,4},{58,4},{59,4},{60,4},{61,4},{62,4},{63,4},{64,5},
+ {65,5},{66,5},{67,5},{68,5},{69,5},{70,5},{71,5},{72,5},{73,5},
+ {74,5},{75,5},{76,5},{77,5},{78,5},{79,5},{80,6},{81,6},{82,6},
+ {83,6},{84,6},{85,6},{86,6},{87,6},{88,6},{89,6},{90,6},{91,6},
+ {92,6},{93,6},{94,6},{95,6},{96,7},{97,7},{98,7},{99,7},{100,7},
+ {101,7},{102,7},{103,7},{104,7},{105,7},{106,7},{107,7},{108,7},
+ {109,7},{110,7},{111,7},{112,7},{113,7},{114,7},{115,7},{116,7},
+ {117,7},{118,7},{119,7},{120,7},{121,7},{122,7},{123,7},{124,7},
+ {125,7},{126,7},{127,7}]
+ = reader_groups_map(CPUT1, 7),
+
+ CPUT2 = [p([c(l(0)),c(l(1)),c(l(2)),c(l(3)),c(l(4))]),
+ p([t(l(5)),t(l(6)),t(l(7)),t(l(8)),t(l(9))]),
+ p([t(l(10))]),
+ p([c(l(11)),c(l(12)),c(l(13))]),
+ p([c(l(14)),c(l(15))])],
+
+ [{0,1},{1,1},{2,1},{3,1},{4,1},
+ {5,2},{6,2},{7,2},{8,2},{9,2},
+ {10,3},
+ {11,4},{12,4},{13,4},
+ {14,5},{15,5}] = reader_groups_map(CPUT2, 5),
+
+
+ [{0,1},{1,1},{2,2},{3,2},{4,2},
+ {5,3},{6,3},{7,3},{8,3},{9,3},
+ {10,4},
+ {11,5},{12,5},{13,5},
+ {14,6},{15,6}] = reader_groups_map(CPUT2, 6),
+
+ [{0,1},{1,1},{2,2},{3,2},{4,2},
+ {5,3},{6,3},{7,3},{8,3},{9,3},
+ {10,4},
+ {11,5},{12,6},{13,6},
+ {14,7},{15,7}] = reader_groups_map(CPUT2, 7),
+
+ [{0,1},{1,1},{2,2},{3,2},{4,2},
+ {5,3},{6,3},{7,3},{8,3},{9,3},
+ {10,4},
+ {11,5},{12,6},{13,6},
+ {14,7},{15,8}] = reader_groups_map(CPUT2, 8),
+
+ [{0,1},{1,2},{2,2},{3,3},{4,3},
+ {5,4},{6,4},{7,4},{8,4},{9,4},
+ {10,5},
+ {11,6},{12,7},{13,7},
+ {14,8},{15,9}] = reader_groups_map(CPUT2, 9),
+
+ [{0,1},{1,2},{2,2},{3,3},{4,3},
+ {5,4},{6,4},{7,4},{8,4},{9,4},
+ {10,5},
+ {11,6},{12,7},{13,8},
+ {14,9},{15,10}] = reader_groups_map(CPUT2, 10),
+
+ [{0,1},{1,2},{2,3},{3,4},{4,4},
+ {5,5},{6,5},{7,5},{8,5},{9,5},
+ {10,6},
+ {11,7},{12,8},{13,9},
+ {14,10},{15,11}] = reader_groups_map(CPUT2, 11),
+
+ [{0,1},{1,2},{2,3},{3,4},{4,5},
+ {5,6},{6,6},{7,6},{8,6},{9,6},
+ {10,7},
+ {11,8},{12,9},{13,10},
+ {14,11},{15,12}] = reader_groups_map(CPUT2, 100),
CPUT3 = [p([t(l(5)),t(l(6)),t(l(7)),t(l(8)),t(l(9))]),
- p([t(l(10))]),
- p([c(l(11)),c(l(12)),c(l(13))]),
- p([c(l(14)),c(l(15))]),
- p([c(l(0)),c(l(1)),c(l(2)),c(l(3)),c(l(4))])],
+ p([t(l(10))]),
+ p([c(l(11)),c(l(12)),c(l(13))]),
+ p([c(l(14)),c(l(15))]),
+ p([c(l(0)),c(l(1)),c(l(2)),c(l(3)),c(l(4))])],
- ?line [{0,5},{1,5},{2,6},{3,6},{4,6},
- {5,1},{6,1},{7,1},{8,1},{9,1},
- {10,2},{11,3},{12,3},{13,3},
- {14,4},{15,4}] = reader_groups_map(CPUT3, 6),
+ [{0,5},{1,5},{2,6},{3,6},{4,6},
+ {5,1},{6,1},{7,1},{8,1},{9,1},
+ {10,2},{11,3},{12,3},{13,3},
+ {14,4},{15,4}] = reader_groups_map(CPUT3, 6),
CPUT4 = [p([t(l(0)),t(l(1)),t(l(2)),t(l(3)),t(l(4))]),
- p([t(l(5))]),
- p([c(l(6)),c(l(7)),c(l(8))]),
- p([c(l(9)),c(l(10))]),
- p([c(l(11)),c(l(12)),c(l(13)),c(l(14)),c(l(15))])],
-
- ?line [{0,1},{1,1},{2,1},{3,1},{4,1},
- {5,2},
- {6,3},{7,3},{8,3},
- {9,4},{10,4},
- {11,5},{12,5},{13,6},{14,6},{15,6}] = reader_groups_map(CPUT4, 6),
-
- ?line [{0,1},{1,1},{2,1},{3,1},{4,1},
- {5,2},
- {6,3},{7,4},{8,4},
- {9,5},{10,5},
- {11,6},{12,6},{13,7},{14,7},{15,7}] = reader_groups_map(CPUT4, 7),
-
- ?line [{0,1},{65535,2}] = reader_groups_map([c(l(0)),c(l(65535))], 10),
-
- ?line ok.
+ p([t(l(5))]),
+ p([c(l(6)),c(l(7)),c(l(8))]),
+ p([c(l(9)),c(l(10))]),
+ p([c(l(11)),c(l(12)),c(l(13)),c(l(14)),c(l(15))])],
+
+ [{0,1},{1,1},{2,1},{3,1},{4,1},
+ {5,2},
+ {6,3},{7,3},{8,3},
+ {9,4},{10,4},
+ {11,5},{12,5},{13,6},{14,6},{15,6}] = reader_groups_map(CPUT4, 6),
+
+ [{0,1},{1,1},{2,1},{3,1},{4,1},
+ {5,2},
+ {6,3},{7,4},{8,4},
+ {9,5},{10,5},
+ {11,6},{12,6},{13,7},{14,7},{15,7}] = reader_groups_map(CPUT4, 7),
+
+ [{0,1},{65535,2}] = reader_groups_map([c(l(0)),c(l(65535))], 10),
+ ok.
reader_groups_map(CPUT, Groups) ->
@@ -1550,6 +1699,34 @@ reader_groups_map(CPUT, Groups) ->
%% Utils
%%
+sched_state() ->
+ sched_state(erlang:system_info(all_schedulers_state),
+ undefined,
+ {dirty_cpu,0,0,0},
+ {dirty_io,0,0,0}).
+
+
+sched_state([], N, DC, DI) ->
+ try
+ chk_basic(N),
+ chk_basic(DC),
+ chk_basic(DI),
+ {N, DC, DI}
+ catch
+ _ : _ ->
+ ct:fail({inconsisten_scheduler_state, {N, DC, DI}})
+ end;
+sched_state([{normal, _, _, _} = S | Rest], _S, DC, DI) ->
+ sched_state(Rest, S, DC, DI);
+sched_state([{dirty_cpu, _, _, _} = DC | Rest], S, _DC, DI) ->
+ sched_state(Rest, S, DC, DI);
+sched_state([{dirty_io, _, _, _} = DI | Rest], S, DC, _DI) ->
+ sched_state(Rest, S, DC, DI).
+
+chk_basic({_Type, Tot, Onln, Act}) ->
+ true = Tot >= Onln,
+ true = Onln >= Act.
+
l(Id) ->
{logical, Id}.
@@ -1568,21 +1745,21 @@ n(X) ->
mcall(Node, Funs) ->
Parent = self(),
Refs = lists:map(fun (Fun) ->
- Ref = make_ref(),
- spawn_link(Node,
- fun () ->
- Res = Fun(),
- unlink(Parent),
- Parent ! {Ref, Res}
- end),
- Ref
- end, Funs),
+ Ref = make_ref(),
+ spawn_link(Node,
+ fun () ->
+ Res = Fun(),
+ unlink(Parent),
+ Parent ! {Ref, Res}
+ end),
+ Ref
+ end, Funs),
lists:map(fun (Ref) ->
- receive
- {Ref, Res} ->
- Res
- end
- end, Refs).
+ receive
+ {Ref, Res} ->
+ Res
+ end
+ end, Refs).
erl_rel_flag_var() ->
"ERL_OTP"++erlang:system_info(otp_release)++"_FLAGS".
@@ -1606,101 +1783,101 @@ restore_erl_rel_flags(OldValue) ->
ok(too_slow, _Config) ->
{comment, "Too slow system to do any actual testing..."};
ok(_Res, Config) ->
- ?config(ok_res, Config).
+ proplists:get_value(ok_res, Config).
chk_result(too_slow,
- _LWorkers,
- _NWorkers,
- _HWorkers,
- _MWorkers,
- _LNShouldWork,
- _HShouldWork,
- _MShouldWork) ->
- ?line ok;
+ _LWorkers,
+ _NWorkers,
+ _HWorkers,
+ _MWorkers,
+ _LNShouldWork,
+ _HShouldWork,
+ _MShouldWork) ->
+ ok;
chk_result([{low, L, Lmin, _Lmax},
- {normal, N, Nmin, _Nmax},
- {high, H, Hmin, _Hmax},
- {max, M, Mmin, _Mmax}] = Res,
- LWorkers,
- NWorkers,
- HWorkers,
- MWorkers,
- LNShouldWork,
- HShouldWork,
- MShouldWork) ->
- ?line ?t:format("~p~n", [Res]),
- ?line Relax = relax_limits(),
+ {normal, N, Nmin, _Nmax},
+ {high, H, Hmin, _Hmax},
+ {max, M, Mmin, _Mmax}] = Res,
+ LWorkers,
+ NWorkers,
+ HWorkers,
+ MWorkers,
+ LNShouldWork,
+ HShouldWork,
+ MShouldWork) ->
+ io:format("~p~n", [Res]),
+ Relax = relax_limits(),
case {L, N} of
- {0, 0} ->
- ?line false = LNShouldWork;
- _ ->
- ?line {LminRatioLim,
- NminRatioLim,
- LNRatioLimMin,
- LNRatioLimMax} = case Relax of
- false -> {0.5, 0.5, 0.05, 0.25};
- true -> {0.05, 0.05, 0.01, 0.4}
- end,
- ?line Lavg = L/LWorkers,
- ?line Navg = N/NWorkers,
- ?line Ratio = Lavg/Navg,
- ?line LminRatio = Lmin/Lavg,
- ?line NminRatio = Nmin/Navg,
- ?line ?t:format("low min ratio=~p~n"
- "normal min ratio=~p~n"
- "low avg=~p~n"
- "normal avg=~p~n"
- "low/normal ratio=~p~n",
- [LminRatio, NminRatio, Lavg, Navg, Ratio]),
- erlang:display({low_min_ratio, LminRatio}),
- erlang:display({normal_min_ratio, NminRatio}),
- erlang:display({low_avg, Lavg}),
- erlang:display({normal_avg, Navg}),
- erlang:display({low_normal_ratio, Ratio}),
- ?line chk_lim(LminRatioLim, LminRatio, 1.0, low_min_ratio),
- ?line chk_lim(NminRatioLim, NminRatio, 1.0, normal_min_ratio),
- ?line chk_lim(LNRatioLimMin, Ratio, LNRatioLimMax, low_normal_ratio),
- ?line true = LNShouldWork,
- ?line ok
+ {0, 0} ->
+ false = LNShouldWork;
+ _ ->
+ {LminRatioLim,
+ NminRatioLim,
+ LNRatioLimMin,
+ LNRatioLimMax} = case Relax of
+ false -> {0.5, 0.5, 0.05, 0.25};
+ true -> {0.05, 0.05, 0.01, 0.4}
+ end,
+ Lavg = L/LWorkers,
+ Navg = N/NWorkers,
+ Ratio = Lavg/Navg,
+ LminRatio = Lmin/Lavg,
+ NminRatio = Nmin/Navg,
+ io:format("low min ratio=~p~n"
+ "normal min ratio=~p~n"
+ "low avg=~p~n"
+ "normal avg=~p~n"
+ "low/normal ratio=~p~n",
+ [LminRatio, NminRatio, Lavg, Navg, Ratio]),
+ erlang:display({low_min_ratio, LminRatio}),
+ erlang:display({normal_min_ratio, NminRatio}),
+ erlang:display({low_avg, Lavg}),
+ erlang:display({normal_avg, Navg}),
+ erlang:display({low_normal_ratio, Ratio}),
+ chk_lim(LminRatioLim, LminRatio, 1.0, low_min_ratio),
+ chk_lim(NminRatioLim, NminRatio, 1.0, normal_min_ratio),
+ chk_lim(LNRatioLimMin, Ratio, LNRatioLimMax, low_normal_ratio),
+ true = LNShouldWork,
+ ok
end,
case H of
- 0 ->
- ?line false = HShouldWork;
- _ ->
- ?line HminRatioLim = case Relax of
- false -> 0.5;
- true -> 0.1
- end,
- ?line Havg = H/HWorkers,
- ?line HminRatio = Hmin/Havg,
- erlang:display({high_min_ratio, HminRatio}),
- ?line chk_lim(HminRatioLim, HminRatio, 1.0, high_min_ratio),
- ?line true = HShouldWork,
- ?line ok
+ 0 ->
+ false = HShouldWork;
+ _ ->
+ HminRatioLim = case Relax of
+ false -> 0.5;
+ true -> 0.1
+ end,
+ Havg = H/HWorkers,
+ HminRatio = Hmin/Havg,
+ erlang:display({high_min_ratio, HminRatio}),
+ chk_lim(HminRatioLim, HminRatio, 1.0, high_min_ratio),
+ true = HShouldWork,
+ ok
end,
case M of
- 0 ->
- ?line false = MShouldWork;
- _ ->
- ?line MminRatioLim = case Relax of
- false -> 0.5;
- true -> 0.1
- end,
- ?line Mavg = M/MWorkers,
- ?line MminRatio = Mmin/Mavg,
- erlang:display({max_min_ratio, MminRatio}),
- ?line chk_lim(MminRatioLim, MminRatio, 1.0, max_min_ratio),
- ?line true = MShouldWork,
- ?line ok
+ 0 ->
+ false = MShouldWork;
+ _ ->
+ MminRatioLim = case Relax of
+ false -> 0.5;
+ true -> 0.1
+ end,
+ Mavg = M/MWorkers,
+ MminRatio = Mmin/Mavg,
+ erlang:display({max_min_ratio, MminRatio}),
+ chk_lim(MminRatioLim, MminRatio, 1.0, max_min_ratio),
+ true = MShouldWork,
+ ok
end,
- ?line ok.
+ ok.
chk_lim(Min, V, Max, _What) when Min =< V, V =< Max ->
ok;
chk_lim(_Min, V, _Max, What) ->
- ?t:fail({bad, What, V}).
+ ct:fail({bad, What, V}).
snd(_Msg, []) ->
[];
@@ -1711,7 +1888,7 @@ snd(Msg, [P|Ps]) ->
relax_limits() ->
case strange_system_scale() of
Scale when Scale > 1 ->
- ?t:format("Relaxing limits~n", []),
+ io:format("Relaxing limits~n", []),
true;
_ ->
false
@@ -1830,14 +2007,14 @@ do_it(Tracer, Low, Normal, High, Max) ->
do_it(Tracer, Low, Normal, High, Max, RedsPerSchedLimit) ->
OldPrio = process_flag(priority, max),
go_work(Low, Normal, High, Max),
- StartWait = erlang:monotonic_time(milli_seconds),
+ StartWait = erlang:monotonic_time(millisecond),
%% Give the emulator a chance to balance the load...
wait_balance(5),
- EndWait = erlang:monotonic_time(milli_seconds),
+ EndWait = erlang:monotonic_time(millisecond),
BalanceWait = EndWait-StartWait,
erlang:display({balance_wait, BalanceWait}),
- Timeout = ?DEFAULT_TIMEOUT - ?t:minutes(4) - BalanceWait,
- Res = case Timeout < ?MIN_SCHEDULER_TEST_TIMEOUT of
+ Timeout = (15 - 4)*60*1000 - BalanceWait,
+ Res = case Timeout < 60*1000 of
true ->
stop_work(Low, Normal, High, Max),
too_slow;
@@ -1907,55 +2084,55 @@ part_time_workers(N, Prio) ->
tracer(Low, Normal, High, Max) ->
receive
- {tracees, Prio, Tracees} ->
- save_tracees(Prio, Tracees),
- case Prio of
- low -> tracer(Tracees++Low, Normal, High, Max);
- normal -> tracer(Low, Tracees++Normal, High, Max);
- high -> tracer(Low, Normal, Tracees++High, Max);
- max -> tracer(Low, Normal, High, Tracees++Max)
- end;
- {get_result, Ref, Who} ->
- Delivered = erlang:trace_delivered(all),
- receive
- {trace_delivered, all, Delivered} ->
- ok
- end,
- {Lc, Nc, Hc, Mc} = read_trace(),
- GetMinMax
- = fun (Prio, Procs) ->
- LargeNum = 1 bsl 64,
- case lists:foldl(fun (P, {Mn, Mx} = MnMx) ->
- {Prio, C} = get(P),
- case C < Mn of
- true ->
- case C > Mx of
- true ->
- {C, C};
- false ->
- {C, Mx}
- end;
- false ->
- case C > Mx of
- true -> {Mn, C};
- false -> MnMx
- end
- end
- end,
- {LargeNum, 0},
- Procs) of
- {LargeNum, 0} -> {0, 0};
- Res -> Res
- end
- end,
- {Lmin, Lmax} = GetMinMax(low, Low),
- {Nmin, Nmax} = GetMinMax(normal, Normal),
- {Hmin, Hmax} = GetMinMax(high, High),
- {Mmin, Mmax} = GetMinMax(max, Max),
- Who ! {trace_result, Ref, [{low, Lc, Lmin, Lmax},
- {normal, Nc, Nmin, Nmax},
- {high, Hc, Hmin, Hmax},
- {max, Mc, Mmin, Mmax}]}
+ {tracees, Prio, Tracees} ->
+ save_tracees(Prio, Tracees),
+ case Prio of
+ low -> tracer(Tracees++Low, Normal, High, Max);
+ normal -> tracer(Low, Tracees++Normal, High, Max);
+ high -> tracer(Low, Normal, Tracees++High, Max);
+ max -> tracer(Low, Normal, High, Tracees++Max)
+ end;
+ {get_result, Ref, Who} ->
+ Delivered = erlang:trace_delivered(all),
+ receive
+ {trace_delivered, all, Delivered} ->
+ ok
+ end,
+ {Lc, Nc, Hc, Mc} = read_trace(),
+ GetMinMax
+ = fun (Prio, Procs) ->
+ LargeNum = 1 bsl 64,
+ case lists:foldl(fun (P, {Mn, Mx} = MnMx) ->
+ {Prio, C} = get(P),
+ case C < Mn of
+ true ->
+ case C > Mx of
+ true ->
+ {C, C};
+ false ->
+ {C, Mx}
+ end;
+ false ->
+ case C > Mx of
+ true -> {Mn, C};
+ false -> MnMx
+ end
+ end
+ end,
+ {LargeNum, 0},
+ Procs) of
+ {LargeNum, 0} -> {0, 0};
+ Res -> Res
+ end
+ end,
+ {Lmin, Lmax} = GetMinMax(low, Low),
+ {Nmin, Nmax} = GetMinMax(normal, Normal),
+ {Hmin, Hmax} = GetMinMax(high, High),
+ {Mmin, Mmax} = GetMinMax(max, Max),
+ Who ! {trace_result, Ref, [{low, Lc, Lmin, Lmax},
+ {normal, Nc, Nmin, Nmax},
+ {high, Hc, Hmin, Hmax},
+ {max, Mc, Mmin, Mmax}]}
end.
read_trace() ->
@@ -2031,15 +2208,15 @@ start_node(Config, Args) when is_list(Config) ->
Pa = filename:dirname(code:which(?MODULE)),
Name = list_to_atom(atom_to_list(?MODULE)
++ "-"
- ++ atom_to_list(?config(testcase, Config))
+ ++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))),
- ?line ?t:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]).
+ test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]).
stop_node(Node) ->
- ?t:stop_node(Node).
+ test_server:stop_node(Node).
enable_internal_state() ->
@@ -2051,7 +2228,7 @@ enable_internal_state() ->
cmp(X, X) ->
ok;
cmp(X, Y) ->
- ?t:format("cmp failed:~n X=~p~n Y=~p~n", [X,Y]),
+ io:format("cmp failed:~n X=~p~n Y=~p~n", [X,Y]),
cmp_aux(X, Y).
@@ -2063,7 +2240,7 @@ cmp_aux(T0, T1) when is_tuple(T0), is_tuple(T1), size(T0) == size(T1) ->
cmp_aux(X, X) ->
ok;
cmp_aux(F0, F1) ->
- ?t:fail({no_match, F0, F1}).
+ ct:fail({no_match, F0, F1}).
cmp_tuple(_T0, _T1, N, Sz) when N > Sz ->
ok;
diff --git a/erts/emulator/test/send_term_SUITE.erl b/erts/emulator/test/send_term_SUITE.erl
index 670865cd3f..8afe4e4ac1 100644
--- a/erts/emulator/test/send_term_SUITE.erl
+++ b/erts/emulator/test/send_term_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,49 +20,25 @@
-module(send_term_SUITE).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,basic/1]).
--export([init_per_testcase/2,end_per_testcase/2]).
+-export([all/0, suite/0, basic/1]).
-export([generate_external_terms_files/1]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- Dog=?t:timetrap(?t:minutes(3)),
- [{watchdog, Dog}|Config].
-
-end_per_testcase(_Func, Config) ->
- Dog=?config(watchdog, Config),
- ?t:timetrap_cancel(Dog).
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 3}}].
all() ->
[basic].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
basic(Config) when is_list(Config) ->
Drv = "send_term_drv",
- ?line P = start_driver(Config, Drv),
+ P = start_driver(Config, Drv),
- ?line [] = term(P, 0),
- ?line Self = self(),
+ [] = term(P, 0),
+ Self = self(),
{blurf,42,[],[-42,{}|"abc"++P],"kalle",3.1416,Self,#{}} = term(P, 1),
Map41 = maps:from_list([{blurf, 42},
@@ -76,36 +52,36 @@ basic(Config) when is_list(Config) ->
{3.1416, Self},
{#{}, blurf}]),
Map42 = term(P, 42),
- ?line Deep = lists:seq(0, 199),
- ?line Deep = term(P, 2),
- ?line {B1,B2} = term(P, 3),
- ?line B1 = list_to_binary(lists:seq(0, 255)),
- ?line B2 = list_to_binary(lists:seq(23, 255-17)),
+ Deep = lists:seq(0, 199),
+ Deep = term(P, 2),
+ {B1,B2} = term(P, 3),
+ B1 = list_to_binary(lists:seq(0, 255)),
+ B2 = list_to_binary(lists:seq(23, 255-17)),
%% Pid sending. We need another process.
- ?line Child = spawn_link(fun() ->
+ Child = spawn_link(fun() ->
erlang:port_command(P, [4])
end),
- ?line {Self,Child} = receive_any(),
+ {Self,Child} = receive_any(),
%% ERL_DRV_EXT2TERM
- ?line ExpectExt2Term = expected_ext2term_drv(?config(data_dir, Config)),
- ?line ExpectExt2Term = term(P, 5),
+ ExpectExt2Term = expected_ext2term_drv(proplists:get_value(data_dir, Config)),
+ ExpectExt2Term = term(P, 5),
%% ERL_DRV_INT, ERL_DRV_UINT
- ?line case erlang:system_info({wordsize, external}) of
+ case erlang:system_info({wordsize, external}) of
4 ->
- ?line {-1, 4294967295} = term(P, 6);
+ {-1, 4294967295} = term(P, 6);
8 ->
- ?line {-1, 18446744073709551615} = term(P, 6)
+ {-1, 18446744073709551615} = term(P, 6)
end,
%% ERL_DRV_BUF2BINARY
- ?line ExpectedBinTup = {<<>>,
+ ExpectedBinTup = {<<>>,
<<>>,
list_to_binary(lists:duplicate(17,17)),
list_to_binary(lists:duplicate(1024,17))},
- ?line ExpectedBinTup = term(P, 7),
+ ExpectedBinTup = term(P, 7),
%% single terms
Singles = [{[], 8}, % ERL_DRV_NIL
@@ -140,35 +116,34 @@ basic(Config) when is_list(Config) ->
{-20233590931456, 38}, % ERL_DRV_INT64
{-9223372036854775808, 39},
{#{}, 40}], % ERL_DRV_MAP
- ?line {Terms, Ops} = lists:unzip(Singles),
- ?line Terms = term(P,Ops),
+ {Terms, Ops} = lists:unzip(Singles),
+ Terms = term(P,Ops),
AFloat = term(P, 26), % ERL_DRV_FLOAT
- ?line true = AFloat < 0.001,
- ?line true = AFloat > -0.001,
+ true = AFloat < 0.001,
+ true = AFloat > -0.001,
%% Failure cases.
- ?line [] = term(P, 127),
- ?line receive
+ [] = term(P, 127),
+ receive
Any ->
- ?line io:format("Unexpected: ~p\n", [Any]),
- ?line ?t:fail()
+ ct:fail("Unexpected: ~p\n", [Any])
after 0 ->
ok
end,
- ?line ok = chk_temp_alloc(),
+ ok = chk_temp_alloc(),
%% In a private heap system, verify that there are no binaries
%% left for the process.
- ?line erlang:garbage_collect(), %Get rid of binaries.
+ erlang:garbage_collect(), %Get rid of binaries.
case erlang:system_info(heap_type) of
private ->
- ?line {binary,[]} = process_info(self(), binary);
+ {binary,[]} = process_info(self(), binary);
_ -> ok
end,
- ?line stop_driver(P, Drv),
+ stop_driver(P, Drv),
ok.
term(P, Op) ->
@@ -184,28 +159,28 @@ chk_temp_alloc() ->
case erlang:system_info({allocator,temp_alloc}) of
false ->
%% Temp alloc is not enabled
- ?line ok;
+ ok;
TIL ->
%% Verify that we havn't got anything allocated by temp_alloc
lists:foreach(
fun ({instance, _, TI}) ->
- ?line {value, {mbcs, MBCInfo}}
+ {value, {mbcs, MBCInfo}}
= lists:keysearch(mbcs, 1, TI),
- ?line {value, {blocks, 0, _, _}}
+ {value, {blocks, 0, _, _}}
= lists:keysearch(blocks, 1, MBCInfo),
- ?line {value, {sbcs, SBCInfo}}
+ {value, {sbcs, SBCInfo}}
= lists:keysearch(sbcs, 1, TI),
- ?line {value, {blocks, 0, _, _}}
+ {value, {blocks, 0, _, _}}
= lists:keysearch(blocks, 1, SBCInfo)
end,
TIL),
- ?line ok
+ ok
end.
%% Start/stop drivers.
start_driver(Config, Name) ->
- Path = ?config(data_dir, Config),
+ Path = proplists:get_value(data_dir, Config),
erl_ddll:start(),
ok = load_driver(Path, Name),
open_port({spawn, Name}, []).
@@ -219,17 +194,17 @@ load_driver(Dir, Driver) ->
end.
stop_driver(Port, Name) ->
- ?line true = erlang:port_close(Port),
+ true = erlang:port_close(Port),
receive
{Port,Message} ->
- ?t:fail({strange_message_from_port,Message})
+ ct:fail({strange_message_from_port,Message})
after 0 ->
ok
end,
%% Unload the driver.
ok = erl_ddll:unload_driver(Name),
- ?line ok = erl_ddll:stop().
+ ok = erl_ddll:stop().
get_external_terms(DataDir) ->
{ok, Bin} = file:read_file([DataDir, "ext_terms.bin"]),
@@ -261,37 +236,36 @@ generate_external_terms_files(BaseDir) ->
RPort = hd(rpc:call(Node, erlang, ports, [])),
true = is_port(RPort),
slave:stop(Node),
- Terms =
- [{4711, -4711, [an_atom, "a list"]},
- [1000000000000000000000,-1111111111111111, "blupp!", blipp],
- {RPid, {RRef, RPort}, self(), hd(erlang:ports()), make_ref()},
- {{}, [], [], fun () -> ok end, <<"hej hopp trallalaaaaaaaaaaaaaaa">>},
- [44444444444444444444444,-44444444444, "b!", blippppppp],
- {4711, RPid, {RRef, RPort}, -4711, [an_atom, "a list"]},
- {RPid, {RRef, RPort}, hd(processes()), hd(erlang:ports())},
- {4711, -4711, [an_atom, "a list"]},
- {4711, -4711, [atom, "list"]},
- {RPid, {RRef, RPort}, hd(processes()), hd(erlang:ports())},
- {4444444444444444444,-44444, {{{{{{{{{{{{}}}}}}}}}}}}, make_ref()},
- {444444444444444444444,-44444, [[[[[[[[[[[1]]]]]]]]]]], make_ref()},
- {444444444444444444,-44444, {{{{{{{{{{{{2}}}}}}}}}}}}, make_ref()},
- {4444444444444444444444,-44444, {{{{{{{{{{{{3}}}}}}}}}}}}, make_ref()},
- {44444444444444444444,-44444, {{{{{{{{{{{{4}}}}}}}}}}}}, make_ref()},
- {4444444444444444,-44444, [[[[[[[[[[[5]]]]]]]]]]], make_ref()},
- {444444444444444444444,-44444, {{{{{{{{{{{{6}}}}}}}}}}}}, make_ref()},
- {444444444444444,-44444, {{{{{{{{{{{{7}}}}}}}}}}}}, make_ref()},
- {4444444444444444444,-44444, {{{{{{{{{{{{8}}}}}}}}}}}}, make_ref()},
- #{},
- #{1 => 11, 2 => 22, 3 => 33},
- maps:from_list([{K,K*11} || K <- lists:seq(1,100)])],
+ Terms = [{4711, -4711, [an_atom, "a list"]},
+ [1000000000000000000000,-1111111111111111, "blupp!", blipp],
+ {RPid, {RRef, RPort}, self(), hd(erlang:ports()), make_ref()},
+ {{}, [], [], fun () -> ok end, <<"hej hopp trallalaaaaaaaaaaaaaaa">>},
+ [44444444444444444444444,-44444444444, "b!", blippppppp],
+ {4711, RPid, {RRef, RPort}, -4711, [an_atom, "a list"]},
+ {RPid, {RRef, RPort}, hd(processes()), hd(erlang:ports())},
+ {4711, -4711, [an_atom, "a list"]},
+ {4711, -4711, [atom, "list"]},
+ {RPid, {RRef, RPort}, hd(processes()), hd(erlang:ports())},
+ {4444444444444444444,-44444, {{{{{{{{{{{{}}}}}}}}}}}}, make_ref()},
+ {444444444444444444444,-44444, [[[[[[[[[[[1]]]]]]]]]]], make_ref()},
+ {444444444444444444,-44444, {{{{{{{{{{{{2}}}}}}}}}}}}, make_ref()},
+ {4444444444444444444444,-44444, {{{{{{{{{{{{3}}}}}}}}}}}}, make_ref()},
+ {44444444444444444444,-44444, {{{{{{{{{{{{4}}}}}}}}}}}}, make_ref()},
+ {4444444444444444,-44444, [[[[[[[[[[[5]]]]]]]]]]], make_ref()},
+ {444444444444444444444,-44444, {{{{{{{{{{{{6}}}}}}}}}}}}, make_ref()},
+ {444444444444444,-44444, {{{{{{{{{{{{7}}}}}}}}}}}}, make_ref()},
+ {4444444444444444444,-44444, {{{{{{{{{{{{8}}}}}}}}}}}}, make_ref()},
+ #{},
+ #{1 => 11, 2 => 22, 3 => 33},
+ maps:from_list([{K,K*11} || K <- lists:seq(1,100)])],
ok = file:write_file(filename:join([BaseDir,
- "send_term_SUITE_data",
- "ext_terms.bin"]),
- term_to_binary(Terms, [compressed])),
+ "send_term_SUITE_data",
+ "ext_terms.bin"]),
+ term_to_binary(Terms, [compressed])),
{ok, IoDev} = file:open(filename:join([BaseDir,
- "send_term_SUITE_data",
- "ext_terms.h"]),
- [write]),
+ "send_term_SUITE_data",
+ "ext_terms.h"]),
+ [write]),
write_ext_terms_h(IoDev, Terms),
file:close(IoDev).
@@ -301,12 +275,12 @@ write_ext_terms_h(IoDev, Terms) ->
io:format(IoDev, "#define EXT_TERMS_H__~n",[]),
{ExtTerms, MaxSize} = make_ext_terms(Terms),
io:format(IoDev,
- "static struct {~n"
- " unsigned char ext[~p];~n"
- " int ext_size;~n"
- " unsigned char cext[~p];~n"
- " int cext_size;~n"
- "} ext_terms[] = {~n",[MaxSize, MaxSize]),
+ "static struct {~n"
+ " unsigned char ext[~p];~n"
+ " int ext_size;~n"
+ " unsigned char cext[~p];~n"
+ " int cext_size;~n"
+ "} ext_terms[] = {~n",[MaxSize, MaxSize]),
E = write_ext_terms_h(IoDev, ExtTerms, 0),
io:format(IoDev, "};~n",[]),
io:format(IoDev, "#define NO_OF_EXT_TERMS ~p~n", [E]),
diff --git a/erts/emulator/test/sensitive_SUITE.erl b/erts/emulator/test/sensitive_SUITE.erl
index 2e51712737..c3e303bbd1 100644
--- a/erts/emulator/test/sensitive_SUITE.erl
+++ b/erts/emulator/test/sensitive_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,28 +20,20 @@
-module(sensitive_SUITE).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2,end_per_testcase/2,
- stickiness/1,send_trace/1,recv_trace/1,proc_trace/1,call_trace/1,
- meta_trace/1,running_trace/1,gc_trace/1,seq_trace/1,
- t_process_info/1,t_process_display/1,save_calls/1]).
+-export([all/0, suite/0,
+ stickiness/1,send_trace/1,recv_trace/1,proc_trace/1,call_trace/1,
+ meta_trace/1,running_trace/1,gc_trace/1,seq_trace/1,
+ t_process_info/1,t_process_display/1,save_calls/1]).
-export([remote_process_display/0,an_exported_function/1]).
-import(lists, [keysearch/3,foreach/2,sort/1]).
-init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- Dog = ?t:timetrap(?t:minutes(5)),
- [{watchdog,Dog}|Config].
-
-end_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- Dog = ?config(watchdog, Config),
- ?t:timetrap_cancel(Dog).
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 5}}].
all() ->
[stickiness, send_trace, recv_trace, proc_trace,
@@ -49,164 +41,148 @@ all() ->
seq_trace, t_process_info, t_process_display,
save_calls].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
stickiness(Config) when is_list(Config) ->
- ?line {Tracer,Mref} = spawn_monitor(fun() ->
- receive after infinity -> ok end
- end),
- ?line false = process_flag(sensitive, true),
+ {Tracer,Mref} = spawn_monitor(fun() ->
+ receive after infinity -> ok end
+ end),
+ false = process_flag(sensitive, true),
put(foo, bar),
Flags = sort([send,'receive',procs,call,running,garbage_collection,
- set_on_spawn,set_on_first_spawn,set_on_link,set_on_first_link]),
- ?line foreach(fun(F) ->
- 1 = erlang:trace(self(), true, [F,{tracer,Tracer}])
- end, Flags),
- ?line foreach(fun(F) ->
- 1 = erlang:trace(self(), false, [F,{tracer,Tracer}])
- end, Flags),
- ?line 1 = erlang:trace(self(), true, [{tracer,Tracer}|Flags]),
- ?line 1 = erlang:trace(self(), false, [{tracer,Tracer}|Flags]),
-
- ?line {messages,[]} = process_info(Tracer, messages),
+ set_on_spawn,set_on_first_spawn,set_on_link,set_on_first_link]),
+ foreach(fun(F) ->
+ 1 = erlang:trace(self(), true, [F,{tracer,Tracer}])
+ end, Flags),
+ foreach(fun(F) ->
+ 1 = erlang:trace(self(), false, [F,{tracer,Tracer}])
+ end, Flags),
+ 1 = erlang:trace(self(), true, [{tracer,Tracer}|Flags]),
+ 1 = erlang:trace(self(), false, [{tracer,Tracer}|Flags]),
+
+ {messages,[]} = process_info(Tracer, messages),
exit(Tracer, kill),
receive {'DOWN',Mref,_,_,_} -> ok end,
-
+
case process_info(self(), dictionary) of
- {dictionary,[]} -> ok;
- {dictionary,_} -> ?line ?t:fail(sensitive_flag_cleared)
+ {dictionary,[]} -> ok;
+ {dictionary,_} -> ct:fail(sensitive_flag_cleared)
end,
NewTracer = spawn_link(fun() -> receive after infinity -> ok end end),
- ?line 1 = erlang:trace(self(), true, [{tracer,NewTracer}|Flags]),
- ?line Flags = sort(element(2, erlang:trace_info(self(), flags))),
- ?line {tracer,NewTracer} = erlang:trace_info(self(), tracer),
+ 1 = erlang:trace(self(), true, [{tracer,NewTracer}|Flags]),
+ Flags = sort(element(2, erlang:trace_info(self(), flags))),
+ {tracer,NewTracer} = erlang:trace_info(self(), tracer),
%% Process still sensitive. Tracer should disappear when we clear
%% all trace flags.
- ?line 1 = erlang:trace(self(), false, [{tracer,NewTracer}|Flags]),
- ?line {tracer,[]} = erlang:trace_info(self(), tracer),
+ 1 = erlang:trace(self(), false, [{tracer,NewTracer}|Flags]),
+ {tracer,[]} = erlang:trace_info(self(), tracer),
- ?line unlink(NewTracer), exit(NewTracer, kill),
+ unlink(NewTracer), exit(NewTracer, kill),
ok.
send_trace(Config) when is_list(Config) ->
- ?line {Dead,Mref} = spawn_monitor(fun() -> ok end),
+ {Dead,Mref} = spawn_monitor(fun() -> ok end),
receive {'DOWN',Mref,_,_,_} -> ok end,
- ?line Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
- ?line Sink = spawn_link(fun() -> receive after infinity -> ok end end),
+ Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
+ Sink = spawn_link(fun() -> receive after infinity -> ok end end),
Self = self(),
- ?line 1 = erlang:trace(self(), true, [send,{tracer,Tracer}]),
- ?line Dead ! before,
- ?line Sink ! before,
- ?line false = process_flag(sensitive, true),
- ?line Sink ! {blurf,lists:seq(1, 50)},
- ?line true = process_flag(sensitive, true),
- ?line Sink ! lists:seq(1, 100),
- ?line Dead ! forget_me,
- ?line true = process_flag(sensitive, false),
- ?line Sink ! after1,
- ?line false = process_flag(sensitive, false),
- ?line Sink ! after2,
- ?line Dead ! after2,
- ?line wait_trace(Self),
-
- ?line {messages,Messages} = process_info(Tracer, messages),
- ?line [{trace,Self,send_to_non_existing_process,before,Dead},
- {trace,Self,send,before,Sink},
- {trace,Self,send,after1,Sink},
- {trace,Self,send,after2,Sink},
- {trace,Self,send_to_non_existing_process,after2,Dead}] = Messages,
-
- ?line unlink(Tracer), exit(Tracer, kill),
- ?line unlink(Sink), exit(Sink, kill),
+ 1 = erlang:trace(self(), true, [send,{tracer,Tracer}]),
+ Dead ! before,
+ Sink ! before,
+ false = process_flag(sensitive, true),
+ Sink ! {blurf,lists:seq(1, 50)},
+ true = process_flag(sensitive, true),
+ Sink ! lists:seq(1, 100),
+ Dead ! forget_me,
+ true = process_flag(sensitive, false),
+ Sink ! after1,
+ false = process_flag(sensitive, false),
+ Sink ! after2,
+ Dead ! after2,
+ wait_trace(Self),
+
+ {messages,Messages} = process_info(Tracer, messages),
+ [{trace,Self,send_to_non_existing_process,before,Dead},
+ {trace,Self,send,before,Sink},
+ {trace,Self,send,after1,Sink},
+ {trace,Self,send,after2,Sink},
+ {trace,Self,send_to_non_existing_process,after2,Dead}] = Messages,
+
+ unlink(Tracer), exit(Tracer, kill),
+ unlink(Sink), exit(Sink, kill),
ok.
recv_trace(Config) when is_list(Config) ->
Parent = self(),
- ?line Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
- ?line Sender = spawn_link(fun() -> recv_trace_sender(Parent) end),
+ Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
+ Sender = spawn_link(fun() -> recv_trace_sender(Parent) end),
- ?line 1 = erlang:trace(self(), true, ['receive',{tracer,Tracer}]),
+ 1 = erlang:trace(self(), true, ['receive',{tracer,Tracer}]),
Sender ! 1,
receive a -> wait_trace(Sender) end,
- ?line false = process_flag(sensitive, true),
+ false = process_flag(sensitive, true),
Sender ! 2,
receive {b,[x,y,z]} -> wait_trace(Sender) end,
-
- ?line true = process_flag(sensitive, false),
+
+ true = process_flag(sensitive, false),
Sender ! 3,
receive c -> wait_trace(Sender) end,
-
- ?line {messages,Messages} = process_info(Tracer, messages),
+
+ {messages,Messages} = process_info(Tracer, messages),
[{trace,Parent,'receive',a},
{trace,Parent,'receive',{trace_delivered,_,_}},
{trace,Parent,'receive',c}|_] = Messages,
- ?line unlink(Tracer), exit(Tracer, kill),
- ?line unlink(Sender), exit(Sender, kill),
+ unlink(Tracer), exit(Tracer, kill),
+ unlink(Sender), exit(Sender, kill),
ok.
recv_trace_sender(Pid) ->
receive
- 1 -> Pid ! a;
- 2 -> Pid ! {b,[x,y,z]};
- 3 -> Pid ! c
+ 1 -> Pid ! a;
+ 2 -> Pid ! {b,[x,y,z]};
+ 3 -> Pid ! c
end,
recv_trace_sender(Pid).
proc_trace(Config) when is_list(Config) ->
Self = self(),
- ?line Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
+ Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
- ?line 1 = erlang:trace(self(), true, [procs,{tracer,Tracer}]),
- ?line false = process_flag(sensitive, true),
+ 1 = erlang:trace(self(), true, [procs,{tracer,Tracer}]),
+ false = process_flag(sensitive, true),
spawn(fun() -> ok end),
- ?line register(nisse, self()),
- ?line unregister(nisse),
- ?line link(Tracer),
- ?line unlink(Tracer),
- ?line Linker0 = spawn_link(fun() -> ok end),
+ register(nisse, self()),
+ unregister(nisse),
+ link(Tracer),
+ unlink(Tracer),
+ Linker0 = spawn_link(fun() -> ok end),
Mref0 = erlang:monitor(process, Linker0),
- ?line {_,Mref} = spawn_monitor(fun() -> link(Self), unlink(Self) end),
+ {_,Mref} = spawn_monitor(fun() -> link(Self), unlink(Self) end),
receive {'DOWN',Mref0,_,_,_} -> ok end,
receive {'DOWN',Mref,_,_,_} -> ok end,
- ?line true = process_flag(sensitive, false),
+ true = process_flag(sensitive, false),
Dead = spawn(fun() -> ok end),
- ?line register(arne, self()),
- ?line unregister(arne),
- ?line {Linker,Mref2} = spawn_monitor(fun() -> link(Self), unlink(Self) end),
+ register(arne, self()),
+ unregister(arne),
+ {Linker,Mref2} = spawn_monitor(fun() -> link(Self), unlink(Self) end),
receive {'DOWN',Mref2,_,_,_} -> ok end,
- ?line Last = spawn_link(fun() -> ok end),
+ Last = spawn_link(fun() -> ok end),
receive after 10 -> ok end,
- ?line wait_trace(all),
- ?line {messages,Messages} = process_info(Tracer, messages),
+ wait_trace(all),
+ {messages,Messages} = process_info(Tracer, messages),
[{trace,Self,spawn,Dead,{erlang,apply,_}},
{trace,Self,register,arne},
{trace,Self,unregister,arne},
@@ -217,80 +193,80 @@ proc_trace(Config) when is_list(Config) ->
{trace,Self,link,Last},
{trace,Self,getting_unlinked,Last}] = Messages,
- ?line unlink(Tracer), exit(Tracer, kill),
+ unlink(Tracer), exit(Tracer, kill),
ok.
call_trace(Config) when is_list(Config) ->
Self = self(),
- ?line Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
-
- ?line 1 = erlang:trace(self(), true, [call,{tracer,Tracer}]),
- ?line 1 = erlang:trace_pattern({?MODULE,an_exported_function,1},
- true, [global]),
- ?line 1 = erlang:trace_pattern({erlang,list_to_binary,1}, true, [global]),
- ?line 1 = erlang:trace_pattern({erlang,binary_to_list,1}, true, [local]),
- ?line Local = erlang:trace_pattern({?MODULE,'_','_'}, true, [local]),
-
- ?line false = process_flag(sensitive, true),
- ?line {ok,42} = a_local_function(42),
- ?line 7 = an_exported_function(6),
- ?line <<7,8,9,10>> = list_to_binary(id([7,8,9,10])),
- ?line [42,43] = binary_to_list(id(<<42,43>>)),
- ?line true = process_flag(sensitive, false),
-
- ?line {ok,{a,b}} = a_local_function({a,b}),
- ?line 1 = an_exported_function(0),
- ?line <<1,2,3>> = list_to_binary(id([1,2,3])),
- ?line [42,43,44] = binary_to_list(id(<<42,43,44>>)),
-
- ?line wait_trace(Self),
-
- ?line {messages,Messages} = process_info(Tracer, messages),
- ?line [{trace,Self,call,{?MODULE,a_local_function,[{a,b}]}},
- {trace,Self,call,{?MODULE,an_exported_function,[0]}},
- {trace,Self,call,{?MODULE,id,[_]}},
- {trace,Self,call,{erlang,list_to_binary,[[1,2,3]]}},
- {trace,Self,call,{sensitive_SUITE,id,[<<42,43,44>>]}},
- {trace,Self,call,{erlang,binary_to_list,[<<42,43,44>>]}},
- {trace,Self,call,{?MODULE,wait_trace,[Self]}}] = Messages,
-
- ?line Local = erlang:trace_pattern({?MODULE,'_','_'}, false, [local]),
- ?line erlang:trace_pattern({erlang,'_','_'}, false, [local]),
- ?line erlang:trace_pattern({'_','_','_'}, false, [global]),
-
- ?line unlink(Tracer), exit(Tracer, kill),
+ Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
+
+ 1 = erlang:trace(self(), true, [call,{tracer,Tracer}]),
+ 1 = erlang:trace_pattern({?MODULE,an_exported_function,1},
+ true, [global]),
+ 1 = erlang:trace_pattern({erlang,list_to_binary,1}, true, [global]),
+ 1 = erlang:trace_pattern({erlang,binary_to_list,1}, true, [local]),
+ Local = erlang:trace_pattern({?MODULE,'_','_'}, true, [local]),
+
+ false = process_flag(sensitive, true),
+ {ok,42} = a_local_function(42),
+ 7 = an_exported_function(6),
+ <<7,8,9,10>> = list_to_binary(id([7,8,9,10])),
+ [42,43] = binary_to_list(id(<<42,43>>)),
+ true = process_flag(sensitive, false),
+
+ {ok,{a,b}} = a_local_function({a,b}),
+ 1 = an_exported_function(0),
+ <<1,2,3>> = list_to_binary(id([1,2,3])),
+ [42,43,44] = binary_to_list(id(<<42,43,44>>)),
+
+ wait_trace(Self),
+
+ {messages,Messages} = process_info(Tracer, messages),
+ [{trace,Self,call,{?MODULE,a_local_function,[{a,b}]}},
+ {trace,Self,call,{?MODULE,an_exported_function,[0]}},
+ {trace,Self,call,{?MODULE,id,[_]}},
+ {trace,Self,call,{erlang,list_to_binary,[[1,2,3]]}},
+ {trace,Self,call,{sensitive_SUITE,id,[<<42,43,44>>]}},
+ {trace,Self,call,{erlang,binary_to_list,[<<42,43,44>>]}},
+ {trace,Self,call,{?MODULE,wait_trace,[Self]}}] = Messages,
+
+ Local = erlang:trace_pattern({?MODULE,'_','_'}, false, [local]),
+ erlang:trace_pattern({erlang,'_','_'}, false, [local]),
+ erlang:trace_pattern({'_','_','_'}, false, [global]),
+
+ unlink(Tracer), exit(Tracer, kill),
ok.
meta_trace(Config) when is_list(Config) ->
Self = self(),
- ?line Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
-
- ?line Local = erlang:trace_pattern({?MODULE,'_','_'}, true, [{meta,Tracer}]),
- ?line 1 = erlang:trace_pattern({erlang,list_to_binary,1}, true, [{meta,Tracer}]),
-
- ?line false = process_flag(sensitive, true),
- ?line {ok,blurf} = a_local_function(blurf),
- ?line 100 = an_exported_function(99),
- ?line <<8,9,10>> = list_to_binary(id([8,9,10])),
- ?line true = process_flag(sensitive, false),
-
- ?line {ok,{x,y}} = a_local_function({x,y}),
- ?line 1 = an_exported_function(0),
- ?line <<10>> = list_to_binary(id([10])),
- ?line wait_trace(Self),
-
- ?line Local = erlang:trace_pattern({?MODULE,'_','_'}, false, [meta]),
- ?line 1 = erlang:trace_pattern({erlang,list_to_binary,1}, false, [meta]),
- ?line a_local_function(0),
-
- ?line {messages,Messages} = process_info(Tracer, messages),
- ?line [{trace_ts,Self,call,{?MODULE,a_local_function,[{x,y}]},{_,_,_}},
- {trace_ts,Self,call,{?MODULE,an_exported_function,[0]},{_,_,_}},
- {trace_ts,Self,call,{?MODULE,id,[_]},{_,_,_}},
- {trace_ts,Self,call,{erlang,list_to_binary,[[10]]},{_,_,_}},
- {trace_ts,Self,call,{?MODULE,wait_trace,[Self]},{_,_,_}}] = Messages,
-
- ?line unlink(Tracer), exit(Tracer, kill),
+ Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
+
+ Local = erlang:trace_pattern({?MODULE,'_','_'}, true, [{meta,Tracer}]),
+ 1 = erlang:trace_pattern({erlang,list_to_binary,1}, true, [{meta,Tracer}]),
+
+ false = process_flag(sensitive, true),
+ {ok,blurf} = a_local_function(blurf),
+ 100 = an_exported_function(99),
+ <<8,9,10>> = list_to_binary(id([8,9,10])),
+ true = process_flag(sensitive, false),
+
+ {ok,{x,y}} = a_local_function({x,y}),
+ 1 = an_exported_function(0),
+ <<10>> = list_to_binary(id([10])),
+ wait_trace(Self),
+
+ Local = erlang:trace_pattern({?MODULE,'_','_'}, false, [meta]),
+ 1 = erlang:trace_pattern({erlang,list_to_binary,1}, false, [meta]),
+ a_local_function(0),
+
+ {messages,Messages} = process_info(Tracer, messages),
+ [{trace_ts,Self,call,{?MODULE,a_local_function,[{x,y}]},{_,_,_}},
+ {trace_ts,Self,call,{?MODULE,an_exported_function,[0]},{_,_,_}},
+ {trace_ts,Self,call,{?MODULE,id,[_]},{_,_,_}},
+ {trace_ts,Self,call,{erlang,list_to_binary,[[10]]},{_,_,_}},
+ {trace_ts,Self,call,{?MODULE,wait_trace,[Self]},{_,_,_}}] = Messages,
+
+ unlink(Tracer), exit(Tracer, kill),
ok.
a_local_function(A) ->
@@ -301,66 +277,66 @@ an_exported_function(X) ->
running_trace(Config) when is_list(Config) ->
Self = self(),
- ?line Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
+ Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
- ?line false = process_flag(sensitive, true),
- ?line 1 = erlang:trace(Self, true, [running,{tracer,Tracer}]),
+ false = process_flag(sensitive, true),
+ 1 = erlang:trace(Self, true, [running,{tracer,Tracer}]),
erlang:yield(), erlang:yield(), erlang:yield(), erlang:yield(),
erlang:yield(), erlang:yield(), erlang:yield(), erlang:yield(),
- ?line true = process_flag(sensitive, false),
+ true = process_flag(sensitive, false),
erlang:yield(),
- ?line 1 = erlang:trace(Self, false, [running,{tracer,Tracer}]),
+ 1 = erlang:trace(Self, false, [running,{tracer,Tracer}]),
- ?line wait_trace(Self),
- ?line {messages,Messages} = process_info(Tracer, messages),
- ?line [{trace,Self,out,{sensitive_SUITE,running_trace,1}},
- {trace,Self,in,{sensitive_SUITE,running_trace,1}}] = Messages,
+ wait_trace(Self),
+ {messages,Messages} = process_info(Tracer, messages),
+ [{trace,Self,out,{sensitive_SUITE,running_trace,1}},
+ {trace,Self,in,{sensitive_SUITE,running_trace,1}}] = Messages,
- ?line unlink(Tracer), exit(Tracer, kill),
+ unlink(Tracer), exit(Tracer, kill),
ok.
gc_trace(Config) when is_list(Config) ->
Self = self(),
- ?line Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
+ Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
- ?line false = process_flag(sensitive, true),
- ?line 1 = erlang:trace(Self, true, [garbage_collection,{tracer,Tracer}]),
+ false = process_flag(sensitive, true),
+ 1 = erlang:trace(Self, true, [garbage_collection,{tracer,Tracer}]),
erlang:garbage_collect(), erlang:garbage_collect(),
erlang:garbage_collect(), erlang:garbage_collect(),
erlang:garbage_collect(), erlang:garbage_collect(),
erlang:garbage_collect(), erlang:garbage_collect(),
- ?line true = process_flag(sensitive, false),
+ true = process_flag(sensitive, false),
erlang:garbage_collect(),
- ?line 1 = erlang:trace(Self, false, [garbage_collection,{tracer,Tracer}]),
+ 1 = erlang:trace(Self, false, [garbage_collection,{tracer,Tracer}]),
- ?line wait_trace(Self),
- ?line {messages,Messages} = process_info(Tracer, messages),
- ?line [{trace,Self,gc_start,_},{trace,Self,gc_end,_}] = Messages,
+ wait_trace(Self),
+ {messages,Messages} = process_info(Tracer, messages),
+ [{trace,Self,gc_major_start,_},{trace,Self,gc_major_end,_}] = Messages,
- ?line unlink(Tracer), exit(Tracer, kill),
+ unlink(Tracer), exit(Tracer, kill),
ok.
seq_trace(Config) when is_list(Config) ->
Self = self(),
- ?line Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
- ?line seq_trace:set_system_tracer(Tracer),
-
- ?line false = process_flag(sensitive, true),
-
- ?line Echo = spawn_link(fun() ->
- receive
- {Pid,Message} ->
- Pid ! {reply,Message}
- end
- end),
- ?line Sender = spawn_link(fun() ->
- seq_trace:set_token(label, 42),
- seq_trace:set_token('receive', true),
- seq_trace:set_token(send, true),
- seq_trace:set_token(print, true),
- seq_trace:print(42, "trace started"),
- Self ! blurf
- end),
+ Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
+ seq_trace:set_system_tracer(Tracer),
+
+ false = process_flag(sensitive, true),
+
+ Echo = spawn_link(fun() ->
+ receive
+ {Pid,Message} ->
+ Pid ! {reply,Message}
+ end
+ end),
+ Sender = spawn_link(fun() ->
+ seq_trace:set_token(label, 42),
+ seq_trace:set_token('receive', true),
+ seq_trace:set_token(send, true),
+ seq_trace:set_token(print, true),
+ seq_trace:print(42, "trace started"),
+ Self ! blurf
+ end),
seq_trace:set_token(label, 17),
seq_trace:set_token('receive', true),
seq_trace:set_token(send, true),
@@ -370,49 +346,49 @@ seq_trace(Config) when is_list(Config) ->
receive {reply,hello} -> ok end,
receive blurf -> ok end,
- ?line wait_trace(all),
+ wait_trace(all),
+
+ {messages,Messages} = process_info(Tracer, messages),
+ [{seq_trace,17,{'receive',{0,2},Self,Echo,{Self,hello}}},
+ {seq_trace,17,{send,{2,3},Echo,Self,{reply,hello}}}] =
+ [M || {seq_trace,17,_}=M <- Messages],
- ?line {messages,Messages} = process_info(Tracer, messages),
- ?line [{seq_trace,17,{'receive',{0,2},Self,Echo,{Self,hello}}},
- {seq_trace,17,{send,{2,3},Echo,Self,{reply,hello}}}] =
- [M || {seq_trace,17,_}=M <- Messages],
+ [{seq_trace,42,{print,{0,1},Sender,[],"trace started"}},
+ {seq_trace,42,{send,{0,2},Sender,Self,blurf}}] =
+ [M || {seq_trace,42,_}=M <- Messages],
- ?line [{seq_trace,42,{print,{0,1},Sender,[],"trace started"}},
- {seq_trace,42,{send,{0,2},Sender,Self,blurf}}] =
- [M || {seq_trace,42,_}=M <- Messages],
-
- ?line unlink(Tracer), exit(Tracer, kill),
- ?line unlink(Echo), exit(Echo, kill),
- ?line unlink(Sender), exit(Sender, kill),
+ unlink(Tracer), exit(Tracer, kill),
+ unlink(Echo), exit(Echo, kill),
+ unlink(Sender), exit(Sender, kill),
ok.
t_process_info(Config) when is_list(Config) ->
Parent = self(),
- ?line Pid = spawn_link(fun() ->
- put(foo, bar),
- false = process_flag(sensitive, true),
- Parent ! go,
- receive
- revert ->
- true = process_flag(sensitive, false),
- Parent ! go_again,
- receive never -> ok end
- end end),
+ Pid = spawn_link(fun() ->
+ put(foo, bar),
+ false = process_flag(sensitive, true),
+ Parent ! go,
+ receive
+ revert ->
+ true = process_flag(sensitive, false),
+ Parent ! go_again,
+ receive never -> ok end
+ end end),
receive go -> ok end,
- ?line put(foo, bar),
- ?line self() ! Pid ! {i,am,a,message},
+ put(foo, bar),
+ self() ! Pid ! {i,am,a,message},
- ?line false = process_flag(sensitive, true),
- ?line t_process_info_suppressed(self()),
- ?line t_process_info_suppressed(Pid),
+ false = process_flag(sensitive, true),
+ t_process_info_suppressed(self()),
+ t_process_info_suppressed(Pid),
- ?line true = process_flag(sensitive, false),
+ true = process_flag(sensitive, false),
Pid ! revert,
receive go_again -> ok end,
- ?line t_process_info_normal(self()),
- ?line t_process_info_normal(Pid),
+ t_process_info_normal(self()),
+ t_process_info_normal(Pid),
ok.
t_process_info_suppressed(Pid) ->
@@ -423,7 +399,7 @@ t_process_info_suppressed(Pid) ->
t_process_info_normal(Pid) ->
{value,{foo,bar}} = keysearch(foo, 1, my_process_info(Pid, dictionary)),
case process_info(Pid, backtrace) of
- {backtrace,Bin} when size(Bin) > 20 -> ok
+ {backtrace,Bin} when size(Bin) > 20 -> ok
end,
[{i,am,a,message}] = my_process_info(Pid, messages).
@@ -431,16 +407,16 @@ my_process_info(Pid, Tag) ->
{Tag,Value} = process_info(Pid, Tag),
All = process_info(Pid),
case keysearch(Tag, 1, All) of
- false -> Value;
- {value,{Tag,Value}} -> Value
+ false -> Value;
+ {value,{Tag,Value}} -> Value
end.
t_process_display(Config) when is_list(Config) ->
- ?line Dir = filename:dirname(code:which(?MODULE)),
- ?line Cmd = atom_to_list(lib:progname()) ++ " -noinput -pa " ++ Dir ++
- " -run " ++ ?MODULE_STRING ++ " remote_process_display",
- ?line io:put_chars(Cmd),
- ?line P = open_port({spawn,Cmd}, [in,stderr_to_stdout,eof]),
+ Dir = filename:dirname(code:which(?MODULE)),
+ Cmd = atom_to_list(lib:progname()) ++ " -noinput -pa " ++ Dir ++
+ " -run " ++ ?MODULE_STRING ++ " remote_process_display",
+ io:put_chars(Cmd),
+ P = open_port({spawn,Cmd}, [in,stderr_to_stdout,eof]),
<<"done",_/binary>> = get_all(P),
ok.
@@ -456,27 +432,26 @@ get_all(P) ->
get_all(P, Acc) ->
receive
- {P,{data,S}} ->
- get_all(P, [Acc|S]);
- {P,eof} ->
- iolist_to_binary(Acc)
+ {P,{data,S}} ->
+ get_all(P, [Acc|S]);
+ {P,eof} ->
+ iolist_to_binary(Acc)
end.
save_calls(Config) when is_list(Config) ->
process_flag(save_calls, 10),
false = process_flag(sensitive, true),
- ?line {last_calls,LastCalls} = process_info(self(), last_calls),
- ?line [{erlang,process_flag,2}] = LastCalls,
- ?line [2,4,6] = lists:map(fun(E) -> 2*E end, [1,2,3]),
- ?line {last_calls,LastCalls} = process_info(self(), last_calls),
+ {last_calls,LastCalls} = process_info(self(), last_calls),
+ [{erlang,process_flag,2}] = LastCalls,
+ [2,4,6] = lists:map(fun(E) -> 2*E end, [1,2,3]),
+ {last_calls,LastCalls} = process_info(self(), last_calls),
ok.
wait_trace(Pid) ->
Ref = erlang:trace_delivered(Pid),
receive
- {trace_delivered,Pid,Ref} -> ok
+ {trace_delivered,Pid,Ref} -> ok
end.
-
+
id(I) -> I.
-
diff --git a/erts/emulator/test/signal_SUITE.erl b/erts/emulator/test/signal_SUITE.erl
index 4aa690fb0f..7e516176f7 100644
--- a/erts/emulator/test/signal_SUITE.erl
+++ b/erts/emulator/test/signal_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -28,12 +28,10 @@
-module(signal_SUITE).
-author('[email protected]').
--define(DEFAULT_TIMEOUT_SECONDS, 120).
-
%-define(line_trace, 1).
--include_lib("test_server/include/test_server.hrl").
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2]).
+-include_lib("common_test/include/ct.hrl").
+-export([all/0, suite/0,init_per_suite/1, end_per_suite/1]).
+-export([init_per_testcase/2, end_per_testcase/2]).
% Test cases
-export([xm_sig_order/1,
@@ -51,16 +49,12 @@
pending_exit_group_leader/1,
exit_before_pending_exit/1]).
--export([init_per_testcase/2, end_per_testcase/2]).
-
init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- ?line Dog = ?t:timetrap(?t:seconds(?DEFAULT_TIMEOUT_SECONDS)),
available_internal_state(true),
- ?line [{testcase, Func},{watchdog, Dog}|Config].
+ [{testcase, Func}|Config].
end_per_testcase(_Func, Config) ->
- ?line Dog = ?config(watchdog, Config),
- ?line ?t:timetrap_cancel(Dog).
+ ok.
init_per_suite(Config) ->
Config.
@@ -70,7 +64,9 @@ end_per_suite(_Config) ->
catch erts_debug:set_internal_state(not_running_optimization, true),
available_internal_state(false).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 2}}].
all() ->
[xm_sig_order, pending_exit_unlink_process,
@@ -83,41 +79,30 @@ all() ->
pending_exit_process_info_2, pending_exit_group_leader,
exit_before_pending_exit].
-groups() ->
- [].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-xm_sig_order(doc) -> ["Test that exit signals and messages are received "
- "in correct order"];
-xm_sig_order(suite) -> [];
+%% Test that exit signals and messages are received in correct order
xm_sig_order(Config) when is_list(Config) ->
- ?line LNode = node(),
- ?line repeat(fun () -> xm_sig_order_test(LNode) end, 1000),
- ?line {ok, RNode} = start_node(Config),
- ?line repeat(fun () -> xm_sig_order_test(RNode) end, 1000),
- ?line stop_node(RNode),
- ?line ok.
+ LNode = node(),
+ repeat(fun () -> xm_sig_order_test(LNode) end, 1000),
+ {ok, RNode} = start_node(Config),
+ repeat(fun () -> xm_sig_order_test(RNode) end, 1000),
+ stop_node(RNode),
+ ok.
xm_sig_order_test(Node) ->
- ?line P = spawn(Node, fun () -> xm_sig_order_proc() end),
- ?line M = erlang:monitor(process, P),
- ?line P ! may_reach,
- ?line P ! may_reach,
- ?line P ! may_reach,
- ?line exit(P, good_signal_order),
- ?line P ! may_not_reach,
- ?line P ! may_not_reach,
- ?line P ! may_not_reach,
- ?line receive
+ P = spawn(Node, fun () -> xm_sig_order_proc() end),
+ M = erlang:monitor(process, P),
+ P ! may_reach,
+ P ! may_reach,
+ P ! may_reach,
+ exit(P, good_signal_order),
+ P ! may_not_reach,
+ P ! may_not_reach,
+ P ! may_not_reach,
+ receive
{'DOWN', M, process, P, R} ->
- ?line good_signal_order = R
+ good_signal_order = R
end.
xm_sig_order_proc() ->
@@ -128,168 +113,149 @@ xm_sig_order_proc() ->
end,
xm_sig_order_proc().
-pending_exit_unlink_process(doc) -> [];
-pending_exit_unlink_process(suite) -> [];
pending_exit_unlink_process(Config) when is_list(Config) ->
- ?line pending_exit_test(self(), unlink).
+ pending_exit_test(self(), unlink).
-pending_exit_unlink_dist_process(doc) -> [];
-pending_exit_unlink_dist_process(suite) -> [];
pending_exit_unlink_dist_process(Config) when is_list(Config) ->
- ?line {ok, Node} = start_node(Config),
- ?line From = spawn(Node, fun () -> receive after infinity -> ok end end),
- ?line Res = pending_exit_test(From, unlink),
- ?line stop_node(Node),
- ?line Res.
-
-pending_exit_unlink_port(doc) -> [];
-pending_exit_unlink_port(suite) -> [];
+ {ok, Node} = start_node(Config),
+ From = spawn(Node, fun () -> receive after infinity -> ok end end),
+ Res = pending_exit_test(From, unlink),
+ stop_node(Node),
+ Res.
+
pending_exit_unlink_port(Config) when is_list(Config) ->
- ?line pending_exit_test(hd(erlang:ports()), unlink).
+ pending_exit_test(hd(erlang:ports()), unlink).
-pending_exit_trap_exit(doc) -> [];
-pending_exit_trap_exit(suite) -> [];
pending_exit_trap_exit(Config) when is_list(Config) ->
- ?line pending_exit_test(self(), trap_exit).
+ pending_exit_test(self(), trap_exit).
-pending_exit_receive(doc) -> [];
-pending_exit_receive(suite) -> [];
pending_exit_receive(Config) when is_list(Config) ->
- ?line pending_exit_test(self(), 'receive').
+ pending_exit_test(self(), 'receive').
-pending_exit_exit(doc) -> [];
-pending_exit_exit(suite) -> [];
pending_exit_exit(Config) when is_list(Config) ->
- ?line pending_exit_test(self(), exit).
+ pending_exit_test(self(), exit).
-pending_exit_gc(doc) -> [];
-pending_exit_gc(suite) -> [];
pending_exit_gc(Config) when is_list(Config) ->
- ?line pending_exit_test(self(), gc).
+ pending_exit_test(self(), gc).
pending_exit_test(From, Type) ->
- ?line case catch erlang:system_info(smp_support) of
- true ->
- ?line OTE = process_flag(trap_exit, true),
- ?line Ref = make_ref(),
- ?line Master = self(),
- ?line ExitBySignal = case Type of
- gc ->
- lists:duplicate(10000,
- exit_by_signal);
- _ ->
- exit_by_signal
- end,
- ?line Pid = spawn_link(
- fun () ->
- receive go -> ok end,
- false = have_pending_exit(),
- exit = fake_exit(From,
- self(),
- ExitBySignal),
- true = have_pending_exit(),
- Master ! {self(), Ref, Type},
- case Type of
- gc ->
- force_gc(),
- erlang:yield();
- unlink ->
- unlink(From);
- trap_exit ->
- process_flag(trap_exit, true);
- 'receive' ->
- receive _ -> ok
- after 0 -> ok
- end;
- exit ->
- ok
- end,
- exit(exit_by_myself)
- end),
- ?line Mon = erlang:monitor(process, Pid),
- ?line Pid ! go,
- ?line Reason = receive
- {'DOWN', Mon, process, Pid, R} ->
- ?line receive
- {Pid, Ref, Type} ->
- ?line ok
- after 0 ->
- ?line ?t:fail(premature_exit)
- end,
- ?line case Type of
- exit ->
- ?line exit_by_myself = R;
- _ ->
- ?line ExitBySignal = R
- end
- end,
- ?line receive
- {'EXIT', Pid, R2} ->
- ?line Reason = R2
- end,
- ?line process_flag(trap_exit, OTE),
- ?line ok,
- {comment,
- "Test only valid with current SMP emulator."};
- _ ->
- {skipped,
- "SMP support not enabled. "
- "Test only valid with current SMP emulator."}
- end.
+ case catch erlang:system_info(smp_support) of
+ true ->
+ OTE = process_flag(trap_exit, true),
+ Ref = make_ref(),
+ Master = self(),
+ ExitBySignal = case Type of
+ gc ->
+ lists:duplicate(10000,
+ exit_by_signal);
+ _ ->
+ exit_by_signal
+ end,
+ Pid = spawn_link(
+ fun () ->
+ receive go -> ok end,
+ false = have_pending_exit(),
+ exit = fake_exit(From,
+ self(),
+ ExitBySignal),
+ true = have_pending_exit(),
+ Master ! {self(), Ref, Type},
+ case Type of
+ gc ->
+ force_gc(),
+ erlang:yield();
+ unlink ->
+ unlink(From);
+ trap_exit ->
+ process_flag(trap_exit, true);
+ 'receive' ->
+ receive _ -> ok
+ after 0 -> ok
+ end;
+ exit ->
+ ok
+ end,
+ exit(exit_by_myself)
+ end),
+ Mon = erlang:monitor(process, Pid),
+ Pid ! go,
+ Reason = receive
+ {'DOWN', Mon, process, Pid, R} ->
+ receive
+ {Pid, Ref, Type} ->
+ ok
+ after 0 ->
+ ct:fail(premature_exit)
+ end,
+ case Type of
+ exit ->
+ exit_by_myself = R;
+ _ ->
+ ExitBySignal = R
+ end
+ end,
+ receive
+ {'EXIT', Pid, R2} ->
+ Reason = R2
+ end,
+ process_flag(trap_exit, OTE),
+ ok,
+ {comment, "Test only valid with current SMP emulator."};
+ _ ->
+ {skipped, "SMP support not enabled. Test only valid with current SMP emulator."}
+ end.
-exit_before_pending_exit(doc) -> [];
-exit_before_pending_exit(suite) -> [];
exit_before_pending_exit(Config) when is_list(Config) ->
%% This is a testcase testcase very specific to the smp
%% implementation as it is of the time of writing.
%%
%% The testcase tries to check that a process can
%% exit by itself even though it has a pending exit.
- ?line OTE = process_flag(trap_exit, true),
- ?line Master = self(),
- ?line Tester = spawn_link(
- fun () ->
- Opts = case {erlang:system_info(run_queues),
- erlang:system_info(schedulers_online)} of
- {RQ, SO} when RQ =:= 1; SO =:= 1 -> [];
- _ ->
- process_flag(scheduler, 1),
- [{scheduler, 2}]
- end,
- P = self(),
- Exiter = spawn_opt(fun () ->
- receive
- {exit_me, P, R} ->
- exit(P, R)
- end
- end, Opts),
- erlang:yield(),
- Exiter ! {exit_me, self(), exited_by_exiter},
- %% We want to get a pending exit
- %% before we exit ourselves. We
- %% don't want to be scheduled out
- %% since we will then see the
- %% pending exit.
- %%
- %% Do something that takes
- %% relatively long time but
- %% consumes few reductions...
- repeat(fun() -> erlang:system_info(procs) end,10),
- %% ... then exit.
- Master ! {self(),
- pending_exit,
- have_pending_exit()},
- exit(exited_by_myself)
- end),
- ?line PendingExit = receive {Tester, pending_exit, PE} -> PE end,
- ?line receive
+ OTE = process_flag(trap_exit, true),
+ Master = self(),
+ Tester = spawn_link(
+ fun () ->
+ Opts = case {erlang:system_info(run_queues),
+ erlang:system_info(schedulers_online)} of
+ {RQ, SO} when RQ =:= 1; SO =:= 1 -> [];
+ _ ->
+ process_flag(scheduler, 1),
+ [{scheduler, 2}]
+ end,
+ P = self(),
+ Exiter = spawn_opt(fun () ->
+ receive
+ {exit_me, P, R} ->
+ exit(P, R)
+ end
+ end, Opts),
+ erlang:yield(),
+ Exiter ! {exit_me, self(), exited_by_exiter},
+ %% We want to get a pending exit
+ %% before we exit ourselves. We
+ %% don't want to be scheduled out
+ %% since we will then see the
+ %% pending exit.
+ %%
+ %% Do something that takes
+ %% relatively long time but
+ %% consumes few reductions...
+ repeat(fun() -> erlang:system_info(procs) end,10),
+ %% ... then exit.
+ Master ! {self(),
+ pending_exit,
+ have_pending_exit()},
+ exit(exited_by_myself)
+ end),
+ PendingExit = receive {Tester, pending_exit, PE} -> PE end,
+ receive
{'EXIT', Tester, exited_by_myself} ->
- ?line process_flag(trap_exit, OTE),
- ?line ok;
+ process_flag(trap_exit, OTE),
+ ok;
Msg ->
- ?line ?t:fail({unexpected_message, Msg})
+ ct:fail({unexpected_message, Msg})
end,
NoScheds = integer_to_list(erlang:system_info(schedulers_online)),
{comment,
@@ -304,101 +270,101 @@ exit_before_pending_exit(Config) when is_list(Config) ->
-define(PE_INFO_REPEAT, 100).
pending_exit_is_process_alive(Config) when is_list(Config) ->
- ?line S = exit_op_test_init(),
- ?line TestFun = fun (P) -> false = is_process_alive(P) end,
- ?line repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT),
- ?line verify_pending_exit_success(S),
- ?line comment().
+ S = exit_op_test_init(),
+ TestFun = fun (P) -> false = is_process_alive(P) end,
+ repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT),
+ verify_pending_exit_success(S),
+ comment().
pending_exit_process_info_1(Config) when is_list(Config) ->
- ?line S = exit_op_test_init(),
- ?line TestFun = fun (P) ->
+ S = exit_op_test_init(),
+ TestFun = fun (P) ->
undefined = process_info(P)
end,
- ?line repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT),
- ?line verify_pending_exit_success(S),
- ?line comment().
+ repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT),
+ verify_pending_exit_success(S),
+ comment().
pending_exit_process_info_2(Config) when is_list(Config) ->
- ?line S0 = exit_op_test_init(),
- ?line repeated_exit_op_test(fun (P) ->
+ S0 = exit_op_test_init(),
+ repeated_exit_op_test(fun (P) ->
undefined = process_info(P, messages)
end, ?PE_INFO_REPEAT),
- ?line S1 = verify_pending_exit_success(S0),
- ?line repeated_exit_op_test(fun (P) ->
+ S1 = verify_pending_exit_success(S0),
+ repeated_exit_op_test(fun (P) ->
undefined = process_info(P, status)
end, ?PE_INFO_REPEAT),
- ?line S2 = verify_pending_exit_success(S1),
- ?line repeated_exit_op_test(fun (P) ->
+ S2 = verify_pending_exit_success(S1),
+ repeated_exit_op_test(fun (P) ->
undefined = process_info(P, links)
end, ?PE_INFO_REPEAT),
- ?line S3 = verify_pending_exit_success(S2),
- ?line repeated_exit_op_test(fun (P) ->
+ S3 = verify_pending_exit_success(S2),
+ repeated_exit_op_test(fun (P) ->
undefined = process_info(P, [messages])
end, ?PE_INFO_REPEAT),
- ?line S4 = verify_pending_exit_success(S3),
- ?line repeated_exit_op_test(fun (P) ->
+ S4 = verify_pending_exit_success(S3),
+ repeated_exit_op_test(fun (P) ->
undefined = process_info(P, [status])
end, ?PE_INFO_REPEAT),
- ?line S5 = verify_pending_exit_success(S4),
- ?line repeated_exit_op_test(fun (P) ->
+ S5 = verify_pending_exit_success(S4),
+ repeated_exit_op_test(fun (P) ->
undefined = process_info(P, [links])
end, ?PE_INFO_REPEAT),
- ?line S6 = verify_pending_exit_success(S5),
- ?line repeated_exit_op_test(fun (P) ->
+ S6 = verify_pending_exit_success(S5),
+ repeated_exit_op_test(fun (P) ->
undefined = process_info(P, [status,
links])
end, ?PE_INFO_REPEAT),
- ?line S7 = verify_pending_exit_success(S6),
- ?line repeated_exit_op_test(fun (P) ->
+ S7 = verify_pending_exit_success(S6),
+ repeated_exit_op_test(fun (P) ->
undefined = process_info(P, [messages,
status])
end, ?PE_INFO_REPEAT),
- ?line S8 = verify_pending_exit_success(S7),
- ?line repeated_exit_op_test(fun (P) ->
+ S8 = verify_pending_exit_success(S7),
+ repeated_exit_op_test(fun (P) ->
undefined = process_info(P, [messages,
links])
end, ?PE_INFO_REPEAT),
- ?line S9 = verify_pending_exit_success(S8),
- ?line repeated_exit_op_test(
+ S9 = verify_pending_exit_success(S8),
+ repeated_exit_op_test(
fun (P) ->
undefined = process_info(P, [message_queue_len,
status])
end, ?PE_INFO_REPEAT),
- ?line S10 = verify_pending_exit_success(S9),
- ?line repeated_exit_op_test(fun (P) ->
+ S10 = verify_pending_exit_success(S9),
+ repeated_exit_op_test(fun (P) ->
undefined = process_info(P, [messages,
links,
status])
end, ?PE_INFO_REPEAT),
- ?line verify_pending_exit_success(S10),
- ?line comment().
+ verify_pending_exit_success(S10),
+ comment().
pending_exit_process_display(Config) when is_list(Config) ->
- ?line S = exit_op_test_init(),
- ?line TestFun = fun (P) ->
+ S = exit_op_test_init(),
+ TestFun = fun (P) ->
badarg = try
erlang:process_display(P, backtrace)
catch
error:badarg -> badarg
end
end,
- ?line repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT),
- ?line verify_pending_exit_success(S),
- ?line comment().
+ repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT),
+ verify_pending_exit_success(S),
+ comment().
pending_exit_group_leader(Config) when is_list(Config) ->
- ?line S = exit_op_test_init(),
- ?line TestFun = fun (P) ->
+ S = exit_op_test_init(),
+ TestFun = fun (P) ->
badarg = try
group_leader(self(), P)
catch
error:badarg -> badarg
end
end,
- ?line repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT),
- ?line verify_pending_exit_success(S),
- ?line comment().
+ repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT),
+ verify_pending_exit_success(S),
+ comment().
%%
%% -- Internal utils --------------------------------------------------------
@@ -517,14 +483,14 @@ repeat(Fun, N) when is_integer(N) ->
start_node(Config) ->
Name = list_to_atom(atom_to_list(?MODULE)
- ++ "-" ++ atom_to_list(?config(testcase, Config))
- ++ "-" ++ integer_to_list(erlang:system_time(seconds))
+ ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config))
+ ++ "-" ++ integer_to_list(erlang:system_time(second))
++ "-" ++ integer_to_list(erlang:unique_integer([positive]))),
Pa = filename:dirname(code:which(?MODULE)),
- ?t:start_node(Name, slave, [{args, "-pa " ++ Pa}]).
+ test_server:start_node(Name, slave, [{args, "-pa " ++ Pa}]).
stop_node(Node) ->
- ?t:stop_node(Node).
+ test_server:stop_node(Node).
have_pending_exit() ->
have_pending_exit(self()).
@@ -540,15 +506,15 @@ fake_exit(From, To, Reason) ->
available_internal_state(Bool) when Bool == true; Bool == false ->
case {Bool,
- (catch erts_debug:get_internal_state(available_internal_state))} of
- {true, true} ->
- true;
- {false, true} ->
- erts_debug:set_internal_state(available_internal_state, false),
- true;
- {true, _} ->
- erts_debug:set_internal_state(available_internal_state, true),
- false;
- {false, _} ->
- false
+ (catch erts_debug:get_internal_state(available_internal_state))} of
+ {true, true} ->
+ true;
+ {false, true} ->
+ erts_debug:set_internal_state(available_internal_state, false),
+ true;
+ {true, _} ->
+ erts_debug:set_internal_state(available_internal_state, true),
+ false;
+ {false, _} ->
+ false
end.
diff --git a/erts/emulator/test/smoke_test_SUITE.erl b/erts/emulator/test/smoke_test_SUITE.erl
index 5bb98e5ad9..5eccdc562b 100644
--- a/erts/emulator/test/smoke_test_SUITE.erl
+++ b/erts/emulator/test/smoke_test_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,38 +20,21 @@
-module(smoke_test_SUITE).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
%-compile(export_all).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
+-export([all/0, suite/0,
init_per_testcase/2, end_per_testcase/2]).
-export([boot_combo/1, native_atomics/1, jump_table/1]).
--define(DEFAULT_TIMEOUT, ?t:minutes(2)).
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 2}}].
all() ->
[boot_combo, native_atomics, jump_table].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
init_per_testcase(boot_combo = Case, Config) when is_list(Config) ->
case erlang:system_info(build_type) of
opt ->
@@ -63,12 +46,9 @@ init_per_testcase(Case, Config) when is_list(Config) ->
init_per_tc(Case, Config).
init_per_tc(Case, Config) ->
- Dog = ?t:timetrap(?DEFAULT_TIMEOUT),
- [{testcase, Case},{watchdog, Dog}|Config].
+ [{testcase, Case}|Config].
end_per_testcase(_Case, Config) when is_list(Config) ->
- Dog = ?config(watchdog, Config),
- ?t:timetrap_cancel(Dog),
ok.
%%%
@@ -111,13 +91,13 @@ native_atomics(Config) when is_list(Config) ->
NA64Key = "64-bit native atomics",
DWNAKey = "Double word native atomics",
EthreadInfo = erlang:system_info(ethread_info),
- ?t:format("~p~n", [EthreadInfo]),
+ io:format("~p~n", [EthreadInfo]),
{value,{NA32Key, NA32, _}} = lists:keysearch(NA32Key, 1, EthreadInfo),
{value,{NA64Key, NA64, _}} = lists:keysearch(NA64Key, 1, EthreadInfo),
{value,{DWNAKey, DWNA, _}} = lists:keysearch(DWNAKey, 1, EthreadInfo),
case {erlang:system_info(build_type), erlang:system_info(smp_support), NA32, NA64, DWNA} of
{opt, true, "no", "no", _} ->
- ?t:fail(optimized_smp_runtime_without_native_atomics);
+ ct:fail(optimized_smp_runtime_without_native_atomics);
{_, false, "no", "no", _} ->
{comment, "No native atomics"};
_ ->
@@ -134,7 +114,7 @@ jump_table(Config) when is_list(Config) ->
false ->
case erlang:system_info(build_type) of
opt ->
- ?t:fail(optimized_without_beam_jump_table);
+ ct:fail(optimized_without_beam_jump_table);
BT ->
{comment, "No beam jump table, but build type is " ++ atom_to_list(BT)}
end
@@ -149,7 +129,7 @@ chk_boot(Config, Args, Fun) ->
true = os:putenv("ERL_ZFLAGS", Args),
Success = make_ref(),
Parent = self(),
- ?t:format("--- Testing ~s~n", [Args]),
+ io:format("--- Testing ~s~n", [Args]),
{ok, Node} = start_node(Config),
Pid = spawn_link(Node, fun () ->
Fun(),
@@ -159,7 +139,7 @@ chk_boot(Config, Args, Fun) ->
{Pid, Success} ->
Node = node(Pid),
stop_node(Node),
- ?t:format("--- Success!~n", []),
+ io:format("--- Success!~n", []),
ok
end.
@@ -170,14 +150,14 @@ start_node(Config, Args) when is_list(Config) ->
Pa = filename:dirname(code:which(?MODULE)),
Name = list_to_atom(atom_to_list(?MODULE)
++ "-"
- ++ atom_to_list(?config(testcase, Config))
+ ++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))),
Opts = [{args, "-pa "++Pa++" "++Args}],
- ?t:start_node(Name, slave, Opts).
+ test_server:start_node(Name, slave, Opts).
stop_node(Node) ->
- ?t:stop_node(Node).
+ test_server:stop_node(Node).
diff --git a/erts/emulator/test/statistics_SUITE.erl b/erts/emulator/test/statistics_SUITE.erl
index a6305d453c..a1f12ba93c 100644
--- a/erts/emulator/test/statistics_SUITE.erl
+++ b/erts/emulator/test/statistics_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -22,40 +22,31 @@
%% Tests the statistics/1 bif.
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2,
- end_per_testcase/2,
+-export([all/0, suite/0, groups/0,
wall_clock_zero_diff/1, wall_clock_update/1,
runtime_zero_diff/1,
runtime_update/1, runtime_diff/1,
run_queue_one/1,
scheduler_wall_time/1,
reductions/1, reductions_big/1, garbage_collection/1, io/1,
- badarg/1, run_queues_lengths_active_tasks/1]).
+ badarg/1, run_queues_lengths_active_tasks/1, msacc/1]).
%% Internal exports.
-export([hog/1]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-init_per_testcase(_, Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(300)),
- [{watchdog, Dog}|Config].
-
-end_per_testcase(_, Config) ->
- Dog = ?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
- ok.
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 4}}].
all() ->
[{group, wall_clock}, {group, runtime}, reductions,
reductions_big, {group, run_queue}, scheduler_wall_time,
garbage_collection, io, badarg,
- run_queues_lengths_active_tasks].
+ run_queues_lengths_active_tasks,
+ msacc].
groups() ->
[{wall_clock, [],
@@ -64,61 +55,42 @@ groups() ->
[runtime_zero_diff, runtime_update, runtime_diff]},
{run_queue, [], [run_queue_one]}].
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-
%%% Testing statistics(wall_clock).
-
-
-wall_clock_zero_diff(doc) ->
- "Tests that the 'Wall clock since last call' element of the result "
- "is zero when statistics(runtime) is called twice in succession.";
+%% Tests that the 'Wall clock since last call' element of the result
+%% is zero when statistics(runtime) is called twice in succession.
wall_clock_zero_diff(Config) when is_list(Config) ->
wall_clock_zero_diff1(16).
wall_clock_zero_diff1(N) when N > 0 ->
- ?line {Time, _} = statistics(wall_clock),
- ?line case statistics(wall_clock) of
- {Time, 0} -> ok;
- _ -> wall_clock_zero_diff1(N-1)
+ {Time, _} = statistics(wall_clock),
+ case statistics(wall_clock) of
+ {Time, 0} -> ok;
+ _ -> wall_clock_zero_diff1(N-1)
end;
wall_clock_zero_diff1(0) ->
- ?line test_server:fail("Difference never zero.").
+ ct:fail("Difference never zero.").
-wall_clock_update(doc) ->
- "Test that the time differences returned by two calls to "
- "statistics(wall_clock) are compatible, and are within a small number "
- "of ms of the amount of real time we waited for.";
+%% Test that the time differences returned by two calls to
+%% statistics(wall_clock) are compatible, and are within a small number
+%% of ms of the amount of real time we waited for.
wall_clock_update(Config) when is_list(Config) ->
wall_clock_update1(6).
wall_clock_update1(N) when N > 0 ->
- ?line {T1_wc_time, _} = statistics(wall_clock),
- ?line receive after 1000 -> ok end,
- ?line {T2_wc_time, Wc_Diff} = statistics(wall_clock),
-
- ?line Wc_Diff = T2_wc_time - T1_wc_time,
- ?line test_server:format("Wall clock diff = ~p; should be = 1000..1040~n",
- [Wc_Diff]),
- case ?t:is_debug() of
- false ->
- ?line true = Wc_Diff =< 1040;
- true ->
- ?line true = Wc_Diff =< 2000 %Be more tolerant in debug-compiled emulator.
+ {T1_wc_time, _} = statistics(wall_clock),
+ receive after 1000 -> ok end,
+ {T2_wc_time, Wc_Diff} = statistics(wall_clock),
+
+ Wc_Diff = T2_wc_time - T1_wc_time,
+ io:format("Wall clock diff = ~p; should be = 1000..1040~n", [Wc_Diff]),
+ case test_server:is_debug() of
+ false ->
+ true = Wc_Diff =< 1040;
+ true ->
+ true = Wc_Diff =< 2000 %Be more tolerant in debug-compiled emulator.
end,
- ?line true = Wc_Diff >= 1000,
+ true = Wc_Diff >= 1000,
wall_clock_update1(N-1);
wall_clock_update1(0) ->
ok.
@@ -127,64 +99,64 @@ wall_clock_update1(0) ->
%%% Test statistics(runtime).
-runtime_zero_diff(doc) ->
- "Tests that the difference between the times returned from two consectuitive "
- "calls to statistics(runtime) is zero.";
+%% Tests that the difference between the times returned from two consectuitive
+%% calls to statistics(runtime) is zero.
runtime_zero_diff(Config) when is_list(Config) ->
- ?line runtime_zero_diff1(16).
+ runtime_zero_diff1(16).
runtime_zero_diff1(N) when N > 0 ->
- ?line {T1, _} = statistics(runtime),
- ?line case statistics(runtime) of
- {T1, 0} -> ok;
- _ -> runtime_zero_diff1(N-1)
- end;
+ {T1, _} = statistics(runtime),
+ case statistics(runtime) of
+ {T1, 0} -> ok;
+ _ -> runtime_zero_diff1(N-1)
+ end;
runtime_zero_diff1(0) ->
- ?line test_server:fail("statistics(runtime) never returned zero difference").
+ ct:fail("statistics(runtime) never returned zero difference").
-runtime_update(doc) ->
- "Test that the statistics(runtime) returns a substanstially "
- "updated difference after running a process that takes all CPU "
- " power of the Erlang process for a second.";
+%% Test that the statistics(runtime) returns a substanstially
+%% updated difference after running a process that takes all CPU
+%% power of the Erlang process for a second.
runtime_update(Config) when is_list(Config) ->
- case ?t:is_cover() of
- false ->
- ?line process_flag(priority, high),
- do_runtime_update(10);
- true ->
- {skip,"Cover-compiled"}
+ case test_server:is_cover() of
+ false ->
+ process_flag(priority, high),
+ do_runtime_update(10);
+ true ->
+ {skip,"Cover-compiled"}
end.
do_runtime_update(0) ->
{comment,"Never close enough"};
do_runtime_update(N) ->
- ?line {T1,Diff0} = statistics(runtime),
- ?line spawn_link(fun cpu_heavy/0),
+ {T1,Diff0} = statistics(runtime),
+ {CPUHog, CPUHogMon} = spawn_opt(fun cpu_heavy/0,[link,monitor]),
receive after 1000 -> ok end,
- ?line {T2,Diff} = statistics(runtime),
- ?line true = is_integer(T1+T2+Diff0+Diff),
- ?line test_server:format("T1 = ~p, T2 = ~p, Diff = ~p, T2-T1 = ~p",
- [T1,T2,Diff,T2-T1]),
- ?line if
- T2 - T1 =:= Diff, 900 =< Diff, Diff =< 1500 -> ok;
- true -> do_runtime_update(N-1)
- end.
+ {T2,Diff} = statistics(runtime),
+ unlink(CPUHog),
+ exit(CPUHog, kill),
+ true = is_integer(T1+T2+Diff0+Diff),
+ io:format("T1 = ~p, T2 = ~p, Diff = ~p, T2-T1 = ~p", [T1,T2,Diff,T2-T1]),
+ receive {'DOWN',CPUHogMon,process,CPUHog,_} -> ok end,
+ if
+ T2 - T1 =:= Diff, 900 =< Diff, Diff =< 1500 -> ok;
+ true -> do_runtime_update(N-1)
+ end.
+
cpu_heavy() ->
cpu_heavy().
-runtime_diff(doc) ->
- "Test that the difference between two consecutive absolute runtimes is "
- "equal to the last relative runtime. The loop runs a lot of times since "
- "the bug which this test case tests for showed up only rarely.";
+%% Test that the difference between two consecutive absolute runtimes is
+%% equal to the last relative runtime. The loop runs a lot of times since
+%% the bug which this test case tests for showed up only rarely.
runtime_diff(Config) when is_list(Config) ->
runtime_diff1(1000).
runtime_diff1(N) when N > 0 ->
- ?line {T1_wc_time, _} = statistics(runtime),
- ?line do_much(),
- ?line {T2_wc_time, Wc_Diff} = statistics(runtime),
- ?line Wc_Diff = T2_wc_time - T1_wc_time,
+ {T1_wc_time, _} = statistics(runtime),
+ do_much(),
+ {T2_wc_time, Wc_Diff} = statistics(runtime),
+ Wc_Diff = T2_wc_time - T1_wc_time,
runtime_diff1(N-1);
runtime_diff1(0) ->
ok.
@@ -202,10 +174,9 @@ do_much(N) ->
do_much(N-1).
-reductions(doc) ->
- "Test that statistics(reductions) is callable, and that "
- "Total_Reductions and Reductions_Since_Last_Call make sense. "
- "(This to fail on pre-R3A version of JAM.";
+%% Test that statistics(reductions) is callable, and that
+%% Total_Reductions and Reductions_Since_Last_Call make sense.
+%% This to fail on pre-R3A version of JAM.
reductions(Config) when is_list(Config) ->
{Reductions, _} = statistics(reductions),
@@ -218,13 +189,13 @@ reductions(Config) when is_list(Config) ->
reductions(300, Reductions, Mask).
reductions(N, Previous, Mask) when N > 0 ->
- ?line {Reductions, Diff} = statistics(reductions),
- ?line build_some_garbage(),
- ?line if Reductions > 0 -> ok end,
- ?line if Diff >= 0 -> ok end,
+ {Reductions, Diff} = statistics(reductions),
+ build_some_garbage(),
+ if Reductions > 0 -> ok end,
+ if Diff >= 0 -> ok end,
io:format("Previous = ~p, Reductions = ~p, Diff = ~p, DiffShouldBe = ~p",
- [Previous, Reductions, Diff, (Reductions-Previous) band Mask]),
- ?line if Reductions == ((Previous+Diff) band Mask) -> reductions(N-1, Reductions, Mask) end;
+ [Previous, Reductions, Diff, (Reductions-Previous) band Mask]),
+ if Reductions == ((Previous+Diff) band Mask) -> reductions(N-1, Reductions, Mask) end;
reductions(0, _, _) ->
ok.
@@ -233,126 +204,132 @@ build_some_garbage() ->
%% a garbage collection in the scheduler.
processes().
-reductions_big(doc) ->
- "Test that the number of reductions can be returned as a big number.";
+%% Test that the number of reductions can be returned as a big number.
reductions_big(Config) when is_list(Config) ->
- ?line reductions_big_loop(),
+ reductions_big_loop(),
ok.
reductions_big_loop() ->
erlang:yield(),
case statistics(reductions) of
- {Red, Diff} when Red >= 16#7ffFFFF ->
- ok = io:format("Reductions = ~w, Diff = ~w", [Red, Diff]);
- _ ->
- reductions_big_loop()
+ {Red, Diff} when Red >= 16#7ffFFFF ->
+ ok = io:format("Reductions = ~w, Diff = ~w", [Red, Diff]);
+ _ ->
+ reductions_big_loop()
end.
%%% Tests of statistics(run_queue).
-run_queue_one(doc) ->
- "Tests that statistics(run_queue) returns 1 if we start a "
- "CPU-bound process.";
+%% Tests that statistics(run_queue) returns 1 if we start a
+%% CPU-bound process.
run_queue_one(Config) when is_list(Config) ->
- ?line MS = erlang:system_flag(multi_scheduling, block),
- ?line run_queue_one_test(Config),
- ?line erlang:system_flag(multi_scheduling, unblock),
+ MS = erlang:system_flag(multi_scheduling, block),
+ run_queue_one_test(Config),
+ erlang:system_flag(multi_scheduling, unblock),
case MS of
- blocked ->
- {comment,
- "Multi-scheduling blocked during test. This test-case "
- "was not written to work with multiple schedulers."};
- _ -> ok
+ blocked ->
+ {comment,
+ "Multi-scheduling blocked during test. This test-case "
+ "was not written to work with multiple schedulers."};
+ _ -> ok
end.
-
+
run_queue_one_test(Config) when is_list(Config) ->
- ?line _Hog = spawn_link(?MODULE, hog, [self()]),
- ?line receive
- hog_started -> ok
- end,
- ?line receive after 100 -> ok end, % Give hog a head start.
- ?line case statistics(run_queue) of
- N when N >= 1 -> ok;
- Other -> ?line ?t:fail({unexpected,Other})
- end,
+ _Hog = spawn_link(?MODULE, hog, [self()]),
+ receive
+ hog_started -> ok
+ end,
+ receive after 100 -> ok end, % Give hog a head start.
+ case statistics(run_queue) of
+ N when N >= 1 -> ok;
+ Other -> ct:fail({unexpected,Other})
+ end,
ok.
%% CPU-bound process, going at low priority. It will always be ready
%% to run.
hog(Pid) ->
- ?line process_flag(priority, low),
- ?line Pid ! hog_started,
- ?line Mon = erlang:monitor(process, Pid),
- ?line hog_iter(0, Mon).
+ process_flag(priority, low),
+ Pid ! hog_started,
+ Mon = erlang:monitor(process, Pid),
+ hog_iter(0, Mon).
hog_iter(N, Mon) when N > 0 ->
receive
- {'DOWN', Mon, _, _, _} -> ok
+ {'DOWN', Mon, _, _, _} -> ok
after 0 ->
- ?line hog_iter(N-1, Mon)
+ hog_iter(N-1, Mon)
end;
hog_iter(0, Mon) ->
- ?line hog_iter(10000, Mon).
+ hog_iter(10000, Mon).
%%% Tests of statistics(scheduler_wall_time).
-scheduler_wall_time(doc) ->
- "Tests that statistics(scheduler_wall_time) works as intended";
+%% Tests that statistics(scheduler_wall_time) works as intended
scheduler_wall_time(Config) when is_list(Config) ->
%% Should return undefined if system_flag is not turned on yet
undefined = statistics(scheduler_wall_time),
%% Turn on statistics
false = erlang:system_flag(scheduler_wall_time, true),
try
- Schedulers = erlang:system_info(schedulers_online),
- %% Let testserver and everyone else finish their work
- timer:sleep(1500),
- %% Empty load
- EmptyLoad = get_load(),
- {false, _} = {lists:any(fun(Load) -> Load > 50 end, EmptyLoad),EmptyLoad},
- MeMySelfAndI = self(),
- StartHog = fun() ->
- Pid = spawn(?MODULE, hog, [self()]),
- receive hog_started -> MeMySelfAndI ! go end,
- Pid
- end,
- P1 = StartHog(),
- %% Max on one, the other schedulers empty (hopefully)
- %% Be generous the process can jump between schedulers
- %% which is ok and we don't want the test to fail for wrong reasons
- _L1 = [S1Load|EmptyScheds1] = get_load(),
- {true,_} = {S1Load > 50,S1Load},
- {false,_} = {lists:any(fun(Load) -> Load > 50 end, EmptyScheds1),EmptyScheds1},
- {true,_} = {lists:sum(EmptyScheds1) < 60,EmptyScheds1},
-
- %% 50% load
- HalfHogs = [StartHog() || _ <- lists:seq(1, (Schedulers-1) div 2)],
- HalfLoad = lists:sum(get_load()) div Schedulers,
- if Schedulers < 2, HalfLoad > 80 -> ok; %% Ok only one scheduler online and one hog
- %% We want roughly 50% load
- HalfLoad > 40, HalfLoad < 60 -> ok;
- true -> exit({halfload, HalfLoad})
- end,
-
- %% 100% load
- LastHogs = [StartHog() || _ <- lists:seq(1, Schedulers div 2)],
- FullScheds = get_load(),
- {false,_} = {lists:any(fun(Load) -> Load < 80 end, FullScheds),FullScheds},
- FullLoad = lists:sum(FullScheds) div Schedulers,
- if FullLoad > 90 -> ok;
- true -> exit({fullload, FullLoad})
- end,
-
- [exit(Pid, kill) || Pid <- [P1|HalfHogs++LastHogs]],
- AfterLoad = get_load(),
- {false,_} = {lists:any(fun(Load) -> Load > 25 end, AfterLoad),AfterLoad},
- true = erlang:system_flag(scheduler_wall_time, false)
+ Schedulers = erlang:system_info(schedulers_online),
+ %% Let testserver and everyone else finish their work
+ timer:sleep(1500),
+ %% Empty load
+ EmptyLoad = get_load(),
+ {false, _} = {lists:any(fun(Load) -> Load > 50 end, EmptyLoad),EmptyLoad},
+ MeMySelfAndI = self(),
+ StartHog = fun() ->
+ Pid = spawn(?MODULE, hog, [self()]),
+ receive hog_started -> MeMySelfAndI ! go end,
+ Pid
+ end,
+ P1 = StartHog(),
+ %% Max on one, the other schedulers empty (hopefully)
+ %% Be generous the process can jump between schedulers
+ %% which is ok and we don't want the test to fail for wrong reasons
+ _L1 = [S1Load|EmptyScheds1] = get_load(),
+ {true,_} = {S1Load > 50,S1Load},
+ {false,_} = {lists:any(fun(Load) -> Load > 50 end, EmptyScheds1),EmptyScheds1},
+ {true,_} = {lists:sum(EmptyScheds1) < 60,EmptyScheds1},
+
+ %% 50% load
+ HalfHogs = [StartHog() || _ <- lists:seq(1, (Schedulers-1) div 2)],
+ HalfLoad = lists:sum(get_load()) div Schedulers,
+ if Schedulers < 2, HalfLoad > 80 -> ok; %% Ok only one scheduler online and one hog
+ %% We want roughly 50% load
+ HalfLoad > 40, HalfLoad < 60 -> ok;
+ true -> exit({halfload, HalfLoad})
+ end,
+
+ %% 100% load
+ LastHogs = [StartHog() || _ <- lists:seq(1, Schedulers div 2)],
+ FullScheds = get_load(),
+ {false,_} = {lists:any(fun(Load) -> Load < 80 end, FullScheds),FullScheds},
+ FullLoad = lists:sum(FullScheds) div Schedulers,
+ if FullLoad > 90 -> ok;
+ true -> exit({fullload, FullLoad})
+ end,
+
+ KillHog = fun (HP) ->
+ HPM = erlang:monitor(process, HP),
+ exit(HP, kill),
+ receive
+ {'DOWN', HPM, process, HP, killed} ->
+ ok
+ end
+ end,
+ [KillHog(Pid) || Pid <- [P1|HalfHogs++LastHogs]],
+ AfterLoad = get_load(),
+ io:format("AfterLoad=~p~n", [AfterLoad]),
+ {false,_} = {lists:any(fun(Load) -> Load > 25 end, AfterLoad),AfterLoad},
+ true = erlang:system_flag(scheduler_wall_time, false)
after
- erlang:system_flag(scheduler_wall_time, false)
+ erlang:system_flag(scheduler_wall_time, false)
end.
get_load() ->
@@ -366,62 +343,59 @@ load_percentage([{Id, WN, TN}|Ss], [{Id, WP, TP}|Ps]) ->
load_percentage([], []) -> [].
-garbage_collection(doc) ->
- "Tests that statistics(garbage_collection) is callable. "
- "It is not clear how to test anything more.";
+%% Tests that statistics(garbage_collection) is callable.
+%% It is not clear how to test anything more.
garbage_collection(Config) when is_list(Config) ->
- ?line Bin = list_to_binary(lists:duplicate(19999, 42)),
- ?line case statistics(garbage_collection) of
- {Gcs0,R,0} when is_integer(Gcs0), is_integer(R) ->
- ?line io:format("Reclaimed: ~p", [R]),
- ?line Gcs = garbage_collection_1(Gcs0, Bin),
- ?line io:format("Reclaimed: ~p",
- [element(2, statistics(garbage_collection))]),
- {comment,integer_to_list(Gcs-Gcs0)++" GCs"}
- end.
+ Bin = list_to_binary(lists:duplicate(19999, 42)),
+ case statistics(garbage_collection) of
+ {Gcs0,R,0} when is_integer(Gcs0), is_integer(R) ->
+ io:format("Reclaimed: ~p", [R]),
+ Gcs = garbage_collection_1(Gcs0, Bin),
+ io:format("Reclaimed: ~p",
+ [element(2, statistics(garbage_collection))]),
+ {comment,integer_to_list(Gcs-Gcs0)++" GCs"}
+ end.
garbage_collection_1(Gcs0, Bin) ->
case statistics(garbage_collection) of
- {Gcs,Reclaimed,0} when Gcs >= Gcs0 ->
- if
- Reclaimed > 16#7ffffff ->
- Gcs;
- true ->
- _ = binary_to_list(Bin),
- erlang:garbage_collect(),
- garbage_collection_1(Gcs, Bin)
- end
+ {Gcs,Reclaimed,0} when Gcs >= Gcs0 ->
+ if
+ Reclaimed > 16#7ffffff ->
+ Gcs;
+ true ->
+ _ = binary_to_list(Bin),
+ erlang:garbage_collect(),
+ garbage_collection_1(Gcs, Bin)
+ end
end.
-io(doc) ->
- "Tests that statistics(io) is callable. "
- "This could be improved to test something more.";
+%% Tests that statistics(io) is callable.
+%% This could be improved to test something more.
io(Config) when is_list(Config) ->
- ?line case statistics(io) of
- {{input,In},{output,Out}} when is_integer(In), is_integer(Out) -> ok
- end.
+ case statistics(io) of
+ {{input,In},{output,Out}} when is_integer(In), is_integer(Out) -> ok
+ end.
-badarg(doc) ->
- "Tests that some illegal arguments to statistics fails.";
+%% Tests that some illegal arguments to statistics fails.
badarg(Config) when is_list(Config) ->
- ?line case catch statistics(1) of
- {'EXIT', {badarg, _}} -> ok
- end,
- ?line case catch statistics(bad_atom) of
- {'EXIT', {badarg, _}} -> ok
- end.
+ case catch statistics(1) of
+ {'EXIT', {badarg, _}} -> ok
+ end,
+ case catch statistics(bad_atom) of
+ {'EXIT', {badarg, _}} -> ok
+ end.
tok_loop() ->
tok_loop().
run_queues_lengths_active_tasks(Config) ->
TokLoops = lists:map(fun (_) ->
- spawn_opt(fun () ->
- tok_loop()
- end,
- [link, {priority, low}])
- end,
- lists:seq(1,10)),
+ spawn_opt(fun () ->
+ tok_loop()
+ end,
+ [link, {priority, low}])
+ end,
+ lists:seq(1,10)),
TRQLs0 = statistics(total_run_queue_lengths),
TATs0 = statistics(total_active_tasks),
@@ -464,9 +438,119 @@ run_queues_lengths_active_tasks(Config) ->
erlang:system_flag(schedulers_online, SO),
lists:foreach(fun (P) ->
- unlink(P),
- exit(P, bang)
- end,
- TokLoops),
+ unlink(P),
+ exit(P, bang)
+ end,
+ TokLoops),
ok.
+
+%% Tests that statistics(microstate_statistics) works.
+msacc(Config) ->
+
+ %% Test if crypto nif is available
+ Niff = try crypto:strong_rand_bytes(1), ok catch _:_ -> nok end,
+ TmpFile = filename:join(proplists:get_value(priv_dir,Config),"file.tmp"),
+
+ false = erlang:system_flag(microstate_accounting, true),
+
+ msacc_test(TmpFile),
+
+ true = erlang:system_flag(microstate_accounting, false),
+
+ MsaccStats = erlang:statistics(microstate_accounting),
+
+ case os:type() of
+ {win32, _} ->
+ %% Some windows have a very poor accuracy on their
+ %% timing primitives, so we just make sure that
+ %% some state besides sleep has been triggered.
+ Sum = lists:sum(
+ lists:map(fun({sleep, _V}) -> 0;
+ ({_, V}) -> V
+ end, maps:to_list(msacc_sum_states()))
+ ),
+ if Sum > 0 ->
+ ok;
+ true ->
+ ct:fail({no_states_triggered, MsaccStats})
+ end;
+ _ ->
+
+ %% Make sure that all states were triggered at least once
+ maps:map(fun(nif, 0) ->
+ case Niff of
+ ok ->
+ ct:fail({zero_state,nif});
+ nok ->
+ ok
+ end;
+ (aux, 0) ->
+ %% aux will be zero if we do not have smp support
+ %% or no async threads
+ case erlang:system_info(smp_support) orelse
+ erlang:system_info(thread_pool_size) > 0
+ of
+ false ->
+ ok;
+ true ->
+ ct:log("msacc: ~p",[MsaccStats]),
+ ct:fail({zero_state,aux})
+ end;
+ (Key, 0) ->
+ ct:log("msacc: ~p",[MsaccStats]),
+ ct:fail({zero_state,Key});
+ (_,_) -> ok
+ end, msacc_sum_states())
+ end,
+
+ erlang:system_flag(microstate_accounting, reset),
+
+ msacc_test(TmpFile),
+
+ %% Make sure all counters are zero after stopping and resetting
+ maps:map(fun(_Key, 0) -> ok;
+ (Key,_) ->
+ ct:log("msacc: ~p",[erlang:statistics(microstate_accounting)]),
+ ct:fail({non_zero_state,Key})
+ end,msacc_sum_states()).
+
+%% This test tries to make sure to trigger all of the different available states
+msacc_test(TmpFile) ->
+
+ %% We write some data
+ [file:write_file(TmpFile,<<0:(1024*1024*8)>>,[raw]) || _ <- lists:seq(1,100)],
+
+ %% Do some ETS operations
+ Tid = ets:new(table, []),
+ ets:insert(Tid, {1, hello}),
+ ets:delete(Tid),
+
+ %% Collect some garbage
+ [erlang:garbage_collect() || _ <- lists:seq(1,100)],
+
+ %% Send some messages
+ [begin self() ! {hello},receive _ -> ok end end || _ <- lists:seq(1,100)],
+
+ %% Setup some timers
+ Refs = [erlang:send_after(10000,self(),ok) || _ <- lists:seq(1,100)],
+
+ %% Do some nif work
+ catch [crypto:strong_rand_bytes(128) || _ <- lists:seq(1,100)],
+
+ %% Cancel some timers
+ [erlang:cancel_timer(R) || R <- Refs],
+
+ %% Wait for a while
+ timer:sleep(100).
+
+msacc_sum_states() ->
+ Stats = erlang:statistics(microstate_accounting),
+ [#{ counters := C }|_] = Stats,
+ InitialCounters = maps:map(fun(_,_) -> 0 end,C),
+ lists:foldl(fun(#{ counters := Counters }, Cnt) ->
+ maps:fold(fun(Key, Value, Acc) ->
+ NewValue = Value+maps:get(Key,Acc),
+ maps:update(Key, NewValue, Acc)
+ end, Cnt, Counters)
+ end,InitialCounters,Stats).
diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl
index bee42c07d9..a4aedb31f6 100644
--- a/erts/emulator/test/system_info_SUITE.erl
+++ b/erts/emulator/test/system_info_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -31,55 +31,25 @@
%-define(line_trace, 1).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-%-compile(export_all).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2, end_per_testcase/2]).
+-export([all/0, suite/0]).
--export([process_count/1, system_version/1, misc_smoke_tests/1, heap_size/1, wordsize/1, memory/1,
- ets_limit/1]).
+-export([process_count/1, system_version/1, misc_smoke_tests/1,
+ heap_size/1, wordsize/1, memory/1, ets_limit/1]).
--define(DEFAULT_TIMEOUT, ?t:minutes(2)).
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 2}}].
all() ->
[process_count, system_version, misc_smoke_tests,
heap_size, wordsize, memory, ets_limit].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-init_per_testcase(_Case, Config) when is_list(Config) ->
- Dog = ?t:timetrap(?DEFAULT_TIMEOUT),
- [{watchdog, Dog}|Config].
-
-end_per_testcase(_Case, Config) when is_list(Config) ->
- Dog = ?config(watchdog, Config),
- ?t:timetrap_cancel(Dog),
- ok.
-
%%%
%%% The test cases -------------------------------------------------------------
%%%
-process_count(doc) -> [];
-process_count(suite) -> [];
process_count(Config) when is_list(Config) ->
case catch erlang:system_info(modified_timing_level) of
Level when is_integer(Level) ->
@@ -92,37 +62,37 @@ process_count(Config) when is_list(Config) ->
end.
process_count_test() ->
- ?line OldPrio = process_flag(priority, max),
- ?line check_procs(10),
- ?line check_procs(11234),
- ?line check_procs(57),
- ?line check_procs(1030),
- ?line check_procs(687),
- ?line check_procs(7923),
- ?line check_procs(5302),
- ?line check_procs(12456),
- ?line check_procs(14),
- ?line check_procs(1125),
- ?line check_procs(236),
- ?line check_procs(125),
- ?line check_procs(2346),
- ?line process_flag(priority, OldPrio),
- ?line ok.
+ OldPrio = process_flag(priority, max),
+ check_procs(10),
+ check_procs(11234),
+ check_procs(57),
+ check_procs(1030),
+ check_procs(687),
+ check_procs(7923),
+ check_procs(5302),
+ check_procs(12456),
+ check_procs(14),
+ check_procs(1125),
+ check_procs(236),
+ check_procs(125),
+ check_procs(2346),
+ process_flag(priority, OldPrio),
+ ok.
check_procs(N) ->
- ?line CP = length(processes()),
- ?line Procs = start_procs(N),
- ?line check_pc(CP+N),
- ?line stop_procs(Procs),
- ?line check_pc(CP).
+ CP = length(processes()),
+ Procs = start_procs(N),
+ check_pc(CP+N),
+ stop_procs(Procs),
+ check_pc(CP).
check_pc(E) ->
- ?line P = length(processes()),
- ?line SI = erlang:system_info(process_count),
- ?line ?t:format("E=~p; P=~p; SI=~p~n", [E, P, SI]),
- ?line E = P,
- ?line P = SI.
+ P = length(processes()),
+ SI = erlang:system_info(process_count),
+ io:format("E=~p; P=~p; SI=~p~n", [E, P, SI]),
+ E = P,
+ P = SI.
start_procs(N) ->
lists:map(fun (_) ->
@@ -143,55 +113,44 @@ stop_procs(PMs) ->
end, PMs).
-system_version(doc) -> [];
-system_version(suite) -> [];
system_version(Config) when is_list(Config) ->
- ?line {comment, erlang:system_info(system_version)}.
+ {comment, erlang:system_info(system_version)}.
-misc_smoke_tests(doc) -> [];
-misc_smoke_tests(suite) -> [];
misc_smoke_tests(Config) when is_list(Config) ->
- ?line true = is_binary(erlang:system_info(info)),
- ?line true = is_binary(erlang:system_info(procs)),
- ?line true = is_binary(erlang:system_info(loaded)),
- ?line true = is_binary(erlang:system_info(dist)),
- ?line ok = try erlang:system_info({cpu_topology,erts_get_cpu_topology_error_case}), fail catch error:badarg -> ok end,
+ true = is_binary(erlang:system_info(info)),
+ true = is_binary(erlang:system_info(procs)),
+ true = is_binary(erlang:system_info(loaded)),
+ true = is_binary(erlang:system_info(dist)),
+ ok = try erlang:system_info({cpu_topology,erts_get_cpu_topology_error_case}), fail catch error:badarg -> ok end,
true = lists:member(erlang:system_info(tolerant_timeofday), [enabled, disabled]),
- ?line ok.
+ ok.
-heap_size(doc) -> [];
-heap_size(suite) -> [];
heap_size(Config) when is_list(Config) ->
- ?line {min_bin_vheap_size, VHmin} = erlang:system_info(min_bin_vheap_size),
- ?line {min_heap_size, Hmin} = erlang:system_info(min_heap_size),
- ?line GCinf = erlang:system_info(garbage_collection),
- ?line VHmin = proplists:get_value(min_bin_vheap_size, GCinf),
- ?line Hmin = proplists:get_value(min_heap_size, GCinf),
+ {min_bin_vheap_size, VHmin} = erlang:system_info(min_bin_vheap_size),
+ {min_heap_size, Hmin} = erlang:system_info(min_heap_size),
+ GCinf = erlang:system_info(garbage_collection),
+ VHmin = proplists:get_value(min_bin_vheap_size, GCinf),
+ Hmin = proplists:get_value(min_heap_size, GCinf),
ok.
-wordsize(suite) ->
- [];
-wordsize(doc) ->
- ["Tests the various wordsize variants"];
+%% Tests the various wordsize variants
wordsize(Config) when is_list(Config) ->
- ?line A = erlang:system_info(wordsize),
- ?line true = is_integer(A),
- ?line A = erlang:system_info({wordsize,internal}),
- ?line B = erlang:system_info({wordsize,external}),
- ?line true = A =< B,
+ A = erlang:system_info(wordsize),
+ true = is_integer(A),
+ A = erlang:system_info({wordsize,internal}),
+ B = erlang:system_info({wordsize,external}),
+ true = A =< B,
case {B,A} of
{4,4} ->
{comment, "True 32-bit emulator"};
{8,8} ->
{comment, "True 64-bit emulator"};
- {8,4} ->
- {comment, "Halfword 64-bit emulator"};
Other ->
exit({unexpected_wordsizes,Other})
end.
-memory(doc) -> ["Verify that erlang:memory/0 and memory results in crashdump produce are similar"];
+%% Verify that erlang:memory/0 and memory results in crashdump produce are similar
memory(Config) when is_list(Config) ->
%%
%% Verify that erlang:memory/0 and memory results in
@@ -246,8 +205,7 @@ memory_test(_Config) ->
end)
end,
1000 div erlang:system_info(schedulers_online))
- end,
- []),
+ end, []),
cmp_memory(MWs, "spawn procs"),
Ps = lists:flatten(DPs),
@@ -255,14 +213,12 @@ memory_test(_Config) ->
mem_workers_call(MWs,
fun () ->
lists:foreach(fun (P) -> link(P) end, Ps)
- end,
- []),
+ end, []),
cmp_memory(MWs, "link procs"),
mem_workers_call(MWs,
fun () ->
lists:foreach(fun (P) -> unlink(P) end, Ps)
- end,
- []),
+ end, []),
cmp_memory(MWs, "unlink procs"),
mem_workers_call(MWs,
@@ -279,8 +235,7 @@ memory_test(_Config) ->
true = is_reference(Tmr),
put('BIF_TMRS', [Tmr|Tmrs])
end, Ps)
- end,
- []),
+ end, []),
cmp_memory(MWs, "start BIF timer procs"),
mem_workers_call(MWs,
@@ -291,8 +246,7 @@ memory_test(_Config) ->
end, get('BIF_TMRS')),
put('BIF_TMRS', undefined),
garbage_collect()
- end,
- []),
+ end, []),
erts_debug:set_internal_state(wait, deallocations),
cmp_memory(MWs, "cancel BIF timer procs"),
@@ -301,8 +255,7 @@ memory_test(_Config) ->
lists:map(fun (P) ->
monitor(process, P)
end, Ps)
- end,
- []),
+ end, []),
cmp_memory(MWs, "monitor procs"),
Ms = lists:flatten(DMs),
mem_workers_call(MWs,
@@ -310,8 +263,7 @@ memory_test(_Config) ->
lists:foreach(fun (M) ->
demonitor(M)
end, Ms)
- end,
- []),
+ end, []),
cmp_memory(MWs, "demonitor procs"),
mem_workers_call(MWs,
@@ -319,8 +271,7 @@ memory_test(_Config) ->
lists:foreach(fun (P) ->
P ! {a, "message", make_ref()}
end, Ps)
- end,
- []),
+ end, []),
cmp_memory(MWs, "message procs"),
mem_workers_call(MWs,
@@ -343,8 +294,7 @@ memory_test(_Config) ->
fun () ->
put(binary_data,
mapn(fun (_) -> list_to_binary(lists:duplicate(256,$?)) end, 100))
- end,
- []),
+ end, []),
cmp_memory(MWs, "store binary data"),
@@ -352,8 +302,7 @@ memory_test(_Config) ->
fun () ->
put(binary_data, false),
garbage_collect()
- end,
- []),
+ end, []),
cmp_memory(MWs, "release binary data"),
mem_workers_call(MWs,
@@ -361,8 +310,7 @@ memory_test(_Config) ->
list_to_atom("an ugly atom "++integer_to_list(erlang:system_info(scheduler_id))),
list_to_atom("another ugly atom "++integer_to_list(erlang:system_info(scheduler_id))),
list_to_atom("yet another ugly atom "++integer_to_list(erlang:system_info(scheduler_id)))
- end,
- []),
+ end, []),
cmp_memory(MWs, "new atoms"),
@@ -373,16 +321,14 @@ memory_test(_Config) ->
ets:insert(T, {banan, lists:seq(1,1024)}),
ets:insert(T, {appelsin, make_ref()}),
put(ets_id, T)
- end,
- []),
+ end, []),
cmp_memory(MWs, "store ets data"),
mem_workers_call(MWs,
fun () ->
ets:delete(get(ets_id)),
put(ets_id, false)
- end,
- []),
+ end, []),
cmp_memory(MWs, "remove ets data"),
lists:foreach(fun (MW) ->
@@ -392,8 +338,7 @@ memory_test(_Config) ->
receive
{'DOWN', Mon, _, _, _} -> ok
end
- end,
- MWs),
+ end, MWs),
ok.
mem_worker() ->
@@ -408,22 +353,19 @@ mem_worker() ->
mem_workers_call(MWs, Fun, Args) ->
lists:foreach(fun (MW) ->
- MW ! {call, self(), Fun, Args}
- end,
- MWs),
+ MW ! {call, self(), Fun, Args}
+ end, MWs),
lists:map(fun (MW) ->
- receive
- {reply, MW, Res} ->
- Res
- end
- end,
- MWs).
+ receive
+ {reply, MW, Res} ->
+ Res
+ end
+ end, MWs).
mem_workers_cast(MWs, Fun, Args) ->
lists:foreach(fun (MW) ->
MW ! {cast, self(), Fun, Args}
- end,
- MWs).
+ end, MWs).
spawn_mem_workers() ->
spawn_mem_workers(erlang:system_info(schedulers_online)).
@@ -436,7 +378,6 @@ spawn_mem_workers(N) ->
link]) | spawn_mem_workers(N-1)].
-
mem_get(X, Mem) ->
case lists:keyfind(X, 1, Mem) of
{X, Val} -> Val;
@@ -504,25 +445,25 @@ cmp_memory(MWs, Str) ->
"crash dump memory = ~p~n",
[Str, EM, EDM]),
- ?line check_sane_memory(EM),
- ?line check_sane_memory(EDM),
+ check_sane_memory(EM),
+ check_sane_memory(EDM),
%% We expect these to always give us exactly the same result
- ?line cmp_memory(atom, EM, EDM, 1),
- ?line cmp_memory(atom_used, EM, EDM, 1),
- ?line cmp_memory(binary, EM, EDM, 1),
- ?line cmp_memory(code, EM, EDM, 1),
- ?line cmp_memory(ets, EM, EDM, 1),
+ cmp_memory(atom, EM, EDM, 1),
+ cmp_memory(atom_used, EM, EDM, 1),
+ cmp_memory(binary, EM, EDM, 1),
+ cmp_memory(code, EM, EDM, 1),
+ cmp_memory(ets, EM, EDM, 1),
%% Total, processes, processes_used, and system will seldom
%% give us exactly the same result since the two readings
%% aren't taken atomically.
- ?line cmp_memory(total, EM, EDM, 1.05),
- ?line cmp_memory(processes, EM, EDM, 1.05),
- ?line cmp_memory(processes_used, EM, EDM, 1.05),
- ?line cmp_memory(system, EM, EDM, 1.05),
+ cmp_memory(total, EM, EDM, 1.05),
+ cmp_memory(processes, EM, EDM, 1.05),
+ cmp_memory(processes_used, EM, EDM, 1.05),
+ cmp_memory(system, EM, EDM, 1.05),
ok.
@@ -531,9 +472,7 @@ mapn(_Fun, 0) ->
mapn(Fun, N) ->
[Fun(N) | mapn(Fun, N-1)].
-ets_limit(doc) ->
- "Verify system_info(ets_limit) reflects max ETS table settings.";
-ets_limit(suite) -> [];
+%% Verify system_info(ets_limit) reflects max ETS table settings.
ets_limit(Config0) when is_list(Config0) ->
Config = [{testcase,ets_limit}|Config0],
true = is_integer(get_ets_limit(Config)),
@@ -567,12 +506,12 @@ start_node(Config, Envs) when is_list(Config) ->
Pa = filename:dirname(code:which(?MODULE)),
Name = list_to_atom(atom_to_list(?MODULE)
++ "-"
- ++ atom_to_list(?config(testcase, Config))
+ ++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))),
- ?t:start_node(Name, peer, [{args, "-pa "++Pa}, {env, Envs}]).
+ test_server:start_node(Name, peer, [{args, "-pa "++Pa}, {env, Envs}]).
stop_node(Node) ->
- ?t:stop_node(Node).
+ test_server:stop_node(Node).
diff --git a/erts/emulator/test/system_profile_SUITE.erl b/erts/emulator/test/system_profile_SUITE.erl
index 2ecb4a28a7..2e359b11ce 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-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -23,61 +23,29 @@
-module(system_profile_SUITE).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
+-export([all/0, suite/0,
system_profile_on_and_off/1,
- runnable_procs/1,
- runnable_ports/1,
+ runnable_procs/1, runnable_ports/1,
dont_profile_profiler/1,
- scheduler/1
- ]).
-
--export([init_per_testcase/2, end_per_testcase/2]).
+ scheduler/1, sane_location/1]).
-export([profiler_process/1, ring_loop/1, port_echo_start/0,
list_load/0, run_load/2]).
--include_lib("test_server/include/test_server.hrl").
-
--define(default_timeout, ?t:minutes(1)).
+-include_lib("common_test/include/ct.hrl").
-init_per_testcase(_Case, Config) ->
- Dog=?t:timetrap(?default_timeout),
- [{watchdog, Dog}|Config].
-end_per_testcase(_Case, Config) ->
- Dog=?config(watchdog, Config),
- ?t:timetrap_cancel(Dog),
- ok.
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 1}}].
all() ->
[system_profile_on_and_off, runnable_procs,
- runnable_ports, scheduler, dont_profile_profiler].
-
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
+ runnable_ports, scheduler, dont_profile_profiler,
+ sane_location].
%% No specification clause needed for an init function in a conf case!!!
%% Test switching system_profiling on and off.
-system_profile_on_and_off(suite) ->
- [];
-system_profile_on_and_off(doc) ->
- ["Tests switching system_profiling on and off."];
system_profile_on_and_off(Config) when is_list(Config) ->
Pid = start_profiler_process(),
@@ -106,12 +74,7 @@ system_profile_on_and_off(Config) when is_list(Config) ->
exit(Pid,kill),
ok.
-%% Test runnable_procs
-
-runnable_procs(suite) ->
- [];
-runnable_procs(doc) ->
- ["Tests system_profiling with runnable_procs."];
+%% Tests system_profiling with runnable_procs.
runnable_procs(Config) when is_list(Config) ->
lists:foreach(fun (TsType) ->
Arg = case TsType of
@@ -148,10 +111,7 @@ do_runnable_procs({TsType, TsTypeFlag}) ->
exit(Pid,kill),
ok.
-runnable_ports(suite) ->
- [];
-runnable_ports(doc) ->
- ["Tests system_profiling with runnable_port."];
+%% Tests system_profiling with runnable_port.
runnable_ports(Config) when is_list(Config) ->
lists:foreach(fun (TsType) ->
Arg = case TsType of
@@ -184,10 +144,7 @@ do_runnable_ports({TsType, TsTypeFlag}, Config) ->
exit(Pid,kill),
ok.
-scheduler(suite) ->
- [];
-scheduler(doc) ->
- ["Tests system_profiling with scheduler."];
+%% Tests system_profiling with scheduler.
scheduler(Config) when is_list(Config) ->
case {erlang:system_info(smp_support), erlang:system_info(schedulers_online)} of
{false,_} -> {skipped, "No need for scheduler test when smp support is disabled."};
@@ -209,11 +166,7 @@ scheduler(Config) when is_list(Config) ->
strict_monotonic_timestamp])
end.
-% the profiler pid should not be profiled
-dont_profile_profiler(suite) ->
- [];
-dont_profile_profiler(doc) ->
- ["Ensure system profiler process is not profiled."];
+%% Ensure system profiler process is not profiled.
dont_profile_profiler(Config) when is_list(Config) ->
Pid = start_profiler_process(),
@@ -231,6 +184,33 @@ dont_profile_profiler(Config) when is_list(Config) ->
exit(Pid,kill),
ok.
+%% Check sane location (of exits)
+sane_location(Config) when is_list(Config) ->
+ Check = spawn_link(fun() -> flush_sane_location() end),
+ erlang:system_profile(Check, [runnable_procs]),
+ Me = self(),
+ Pids = [spawn_link(fun() -> wat(Me) end) || _ <- lists:seq(1,100)],
+ [receive {Pid,ok} -> ok end || Pid <- Pids],
+ Check ! {Me, done},
+ receive {Check,ok} -> ok end,
+ ok.
+
+wat(Pid) ->
+ Pid ! {self(), ok}.
+
+flush_sane_location() ->
+ receive
+ {profile,_,_,{M,F,A},_} when is_atom(M), is_atom(F),
+ is_integer(A) ->
+ flush_sane_location();
+ {profile,_,_,0,_} ->
+ flush_sane_location();
+ {Pid,done} when is_pid(Pid) ->
+ Pid ! {self(), ok};
+ M ->
+ ct:fail({badness,M})
+ end.
+
%%% Check scheduler profiling
@@ -421,7 +401,7 @@ ring_loop(RelayTo) ->
%% API
echo(Config) ->
- Path = ?config(data_dir, Config),
+ Path = proplists:get_value(data_dir, Config),
erl_ddll:load_driver(Path, echo_drv),
Pid = spawn_link(?MODULE, port_echo_start, []),
Pid ! {self(), get_ports},
@@ -481,7 +461,7 @@ check_ts(no_timestamp, Ts) ->
no_timestamp = Ts
catch
_ : _ ->
- ?t:fail({unexpected_timestamp, Ts})
+ ct:fail({unexpected_timestamp, Ts})
end,
ok;
check_ts(timestamp, Ts) ->
@@ -492,7 +472,7 @@ check_ts(timestamp, Ts) ->
true = is_integer(Us)
catch
_ : _ ->
- ?t:fail({unexpected_timestamp, Ts})
+ ct:fail({unexpected_timestamp, Ts})
end,
ok;
check_ts(monotonic_timestamp, Ts) ->
@@ -500,7 +480,7 @@ check_ts(monotonic_timestamp, Ts) ->
true = is_integer(Ts)
catch
_ : _ ->
- ?t:fail({unexpected_timestamp, Ts})
+ ct:fail({unexpected_timestamp, Ts})
end,
ok;
check_ts(strict_monotonic_timestamp, Ts) ->
@@ -510,7 +490,7 @@ check_ts(strict_monotonic_timestamp, Ts) ->
true = is_integer(UMI)
catch
_ : _ ->
- ?t:fail({unexpected_timestamp, Ts})
+ ct:fail({unexpected_timestamp, Ts})
end,
ok.
@@ -534,7 +514,7 @@ run_load(N, Pids) ->
run_load(N - 1, [Pid | Pids]).
list_load() ->
- ok = case math:sin(random:uniform(32451)) of
+ ok = case math:sin(rand:uniform(32451)) of
A when is_float(A) -> ok;
_ -> ok
end,
diff --git a/erts/emulator/test/time_SUITE.erl b/erts/emulator/test/time_SUITE.erl
index 787870588d..9501569814 100644
--- a/erts/emulator/test/time_SUITE.erl
+++ b/erts/emulator/test/time_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -48,7 +48,7 @@
-export([local_to_univ_utc/1]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-export([linear_time/1]).
@@ -69,7 +69,7 @@
init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
[{testcase, Func}|Config].
-end_per_testcase(_Func, Config) ->
+end_per_testcase(_Func, _Config) ->
ok.
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -103,32 +103,29 @@ end_per_group(_GroupName, Config) ->
Config.
-local_to_univ_utc(suite) ->
- [];
-local_to_univ_utc(doc) ->
- ["Test that DST = true on timezones without DST is ignored"];
+%% Test that DST = true on timezones without DST is ignored
local_to_univ_utc(Config) when is_list(Config) ->
case os:type() of
{unix,_} ->
%% TZ variable has a meaning
- ?line {ok, Node} =
+ {ok, Node} =
test_server:start_node(local_univ_utc,peer,
[{args, "-env TZ UTC"}]),
- ?line {{2008,8,1},{0,0,0}} =
+ {{2008,8,1},{0,0,0}} =
rpc:call(Node,
erlang,localtime_to_universaltime,
[{{2008, 8, 1}, {0, 0, 0}},
false]),
- ?line {{2008,8,1},{0,0,0}} =
+ {{2008,8,1},{0,0,0}} =
rpc:call(Node,
erlang,localtime_to_universaltime,
[{{2008, 8, 1}, {0, 0, 0}},
true]),
- ?line [{{2008,8,1},{0,0,0}}] =
+ [{{2008,8,1},{0,0,0}}] =
rpc:call(Node,
calendar,local_time_to_universal_time_dst,
[{{2008, 8, 1}, {0, 0, 0}}]),
- ?line test_server:stop_node(Node),
+ test_server:stop_node(Node),
ok;
_ ->
{skip,"Only valid on Unix"}
@@ -138,24 +135,24 @@ local_to_univ_utc(Config) when is_list(Config) ->
%% Tests conversion from univeral to local time.
univ_to_local(Config) when is_list(Config) ->
- ?line test_univ_to_local(test_data()).
+ test_univ_to_local(test_data()).
test_univ_to_local([{Utc, Local}|Rest]) ->
- ?line io:format("Testing ~p => ~p~n", [Local, Utc]),
- ?line Local = erlang:universaltime_to_localtime(Utc),
- ?line test_univ_to_local(Rest);
+ io:format("Testing ~p => ~p~n", [Local, Utc]),
+ Local = erlang:universaltime_to_localtime(Utc),
+ test_univ_to_local(Rest);
test_univ_to_local([]) ->
ok.
%% Tests conversion from local to universal time.
local_to_univ(Config) when is_list(Config) ->
- ?line test_local_to_univ(test_data()).
+ test_local_to_univ(test_data()).
test_local_to_univ([{Utc, Local}|Rest]) ->
- ?line io:format("Testing ~p => ~p~n", [Utc, Local]),
- ?line Utc = erlang:localtime_to_universaltime(Local),
- ?line test_local_to_univ(Rest);
+ io:format("Testing ~p => ~p~n", [Utc, Local]),
+ Utc = erlang:localtime_to_universaltime(Local),
+ test_local_to_univ(Rest);
test_local_to_univ([]) ->
ok.
@@ -163,11 +160,11 @@ test_local_to_univ([]) ->
%% generate a badarg.
bad_univ_to_local(Config) when is_list(Config) ->
- ?line bad_test_univ_to_local(bad_dates()).
+ bad_test_univ_to_local(bad_dates()).
bad_test_univ_to_local([Utc|Rest]) ->
- ?line io:format("Testing ~p~n", [Utc]),
- ?line case catch erlang:universaltime_to_localtime(Utc) of
+ io:format("Testing ~p~n", [Utc]),
+ case catch erlang:universaltime_to_localtime(Utc) of
{'EXIT', {badarg, _}} -> bad_test_univ_to_local(Rest)
end;
bad_test_univ_to_local([]) ->
@@ -177,11 +174,11 @@ bad_test_univ_to_local([]) ->
%% generate a badarg.
bad_local_to_univ(Config) when is_list(Config) ->
- ?line bad_test_local_to_univ(bad_dates()).
+ bad_test_local_to_univ(bad_dates()).
bad_test_local_to_univ([Local|Rest]) ->
- ?line io:format("Testing ~p~n", [Local]),
- ?line case catch erlang:localtime_to_universaltime(Local) of
+ io:format("Testing ~p~n", [Local]),
+ case catch erlang:localtime_to_universaltime(Local) of
{'EXIT', {badarg, _}} -> bad_test_local_to_univ(Rest)
end;
bad_test_local_to_univ([]) ->
@@ -212,28 +209,22 @@ test_seconds_to_univ([]) ->
%% Test that the the different time functions return
-%% consistent results. (See the test case for assumptions
-%% and limitations.)
-consistency(Config) when is_list(Config) ->
- %% Test the following equations:
- %% date() & time() == erlang:localtime()
- %% erlang:universaltime() + timezone == erlang:localtime()
+%% consistent results.
+consistency(_Config) ->
+ %% Test that:
+ %% * date() & time() gives the same time as erlang:localtime()
%%
- %% Assumptions:
- %% Middle-European time zone, EU rules for daylight-saving time.
- %%
- %% Limitations:
- %% Localtime and universaltime must be in the same month.
- %% Daylight-saving calculations are incorrect from the last
- %% Sunday of March and October to the end of the month.
+ %% * the difference between erlang:universaltime() and
+ %% erlang:localtime() is reasonable (with assuming any
+ %% particular timezone)
- ?line ok = compare_date_time_and_localtime(16),
- ?line ok = compare_local_and_universal(16).
+ ok = compare_date_time_and_localtime(16),
+ compare_local_and_universal(16).
compare_date_time_and_localtime(Times) when Times > 0 ->
- ?line {Year, Mon, Day} = date(),
- ?line {Hour, Min, Sec} = time(),
- ?line case erlang:localtime() of
+ {Year, Mon, Day} = date(),
+ {Hour, Min, Sec} = time(),
+ case erlang:localtime() of
{{Year, Mon, Day}, {Hour, Min, Sec}} -> ok;
_ -> compare_date_time_and_localtime(Times-1)
end;
@@ -241,22 +232,18 @@ compare_date_time_and_localtime(0) ->
error.
compare_local_and_universal(Times) when Times > 0 ->
- case compare(erlang:universaltime(), erlang:localtime()) of
- true -> ok;
- false -> compare_local_and_universal(Times-1)
- end;
-compare_local_and_universal(0) ->
- error.
+ Utc = erlang:universaltime(),
+ Local = erlang:localtime(),
+ io:format("local = ~p, utc = ~p", [Local,Utc]),
-compare(Utc0, Local) ->
- io:format("local = ~p, utc = ~p", [Local, Utc0]),
- Utc = linear_time(Utc0)+effective_timezone(Utc0)*3600,
- case linear_time(Local) of
- Utc -> true;
- Other ->
- io:format("Failed: local = ~p, utc = ~p~n",
- [Other, Utc]),
- false
+ AcceptableDiff = 14*3600,
+ case linear_time(Utc) - linear_time(Local) of
+ Diff when abs(Diff) < AcceptableDiff ->
+ ok;
+ Diff ->
+ io:format("More than ~p seconds difference betwen "
+ "local and universal time", [Diff]),
+ ct:fail(huge_diff)
end.
%% This function converts a date and time to a linear time.
@@ -283,42 +270,10 @@ days_in_february(Year) ->
_ -> 28
end.
-%% This functions returns either the normal timezone or the
-%% the DST timezone, depending on the given UTC time.
-%%
-%% XXX This function uses an approximation of the EU rule for
-%% daylight saving time. This function will fail in the
-%% following intervals: After the last Sunday in March upto
-%% the end of March, and after the last Sunday in October
-%% upto the end of October.
-
-effective_timezone(Time) ->
- case os:type() of
- {unix,_} ->
- case os:cmd("date '+%Z'") of
- "SAST"++_ ->
- 2;
- _ ->
- effective_timezone1(Time)
- end;
- _ ->
- effective_timezone1(Time)
- end.
-
-effective_timezone1({{_Year,Mon,_Day}, _}) when Mon < 4 ->
- ?timezone;
-effective_timezone1({{_Year,Mon,_Day}, _}) when Mon > 10 ->
- ?timezone;
-effective_timezone1(_) ->
- ?dst_timezone.
-
%% Test (the bif) os:timestamp/0, which is something quite like, but not
%% similar to erlang:now...
-timestamp(suite) ->
- [];
-timestamp(doc) ->
- ["Test that os:timestamp works."];
+%% Test that os:timestamp works.
timestamp(Config) when is_list(Config) ->
try
repeating_timestamp_check(100000)
@@ -334,13 +289,13 @@ timestamp(Config) when is_list(Config) ->
true ->
{skip, "Seems to be time warp test run..."};
false ->
- test_server:fail(Failure)
+ ct:fail(Failure)
end
end.
os_system_time_offset() ->
erlang:convert_time_unit(os:system_time() - erlang:monotonic_time(),
- native, micro_seconds).
+ native, microsecond).
had_time_warp(Secs) ->
had_time_warp(os_system_time_offset(), Secs).
@@ -368,9 +323,7 @@ repeating_timestamp_check(N) ->
C < 1000000 ->
ok;
true ->
- test_server:fail(
- lists:flatten(
- io_lib:format("Strange return from os:timestamp/0 ~w~n",[TS])))
+ ct:fail("Strange return from os:timestamp/0 ~w~n",[TS])
end,
%% I assume the now and timestamp should not differ more than 1 hour,
%% which is safe assuming the system has not had a large time-warp
@@ -403,22 +356,22 @@ repeating_timestamp_check(N) ->
%% times (in microseconds).
now_unique(Config) when is_list(Config) ->
- ?line now_unique(1000, now(), []),
- ?line fast_now_unique(100000, now()).
+ now_unique(1000, now(), []),
+ fast_now_unique(100000, now()).
now_unique(N, Previous, Result) when N > 0 ->
- ?line case now() of
+ case now() of
Previous ->
- test_server:fail("now/0 returned the same value twice");
+ ct:fail("now/0 returned the same value twice");
New ->
now_unique(N-1, New, [New|Result])
end;
now_unique(0, _, [Then|Rest]) ->
- ?line now_calc_increment(Rest, microsecs(Then), []).
+ now_calc_increment(Rest, microsecs(Then), []).
now_calc_increment([Then|Rest], Previous, _Result) ->
- ?line This = microsecs(Then),
- ?line now_calc_increment(Rest, This, [Previous-This]);
+ This = microsecs(Then),
+ now_calc_increment(Rest, This, [Previous-This]);
now_calc_increment([], _, Differences) ->
{comment, "Median increment: " ++ integer_to_list(median(Differences))}.
@@ -426,15 +379,15 @@ fast_now_unique(0, _) -> ok;
fast_now_unique(N, Then) ->
case now() of
Then ->
- ?line ?t:fail("now/0 returned the same value twice");
+ ct:fail("now/0 returned the same value twice");
Now ->
fast_now_unique(N-1, Now)
end.
median(Unsorted_List) ->
- ?line Length = length(Unsorted_List),
- ?line List = lists:sort(Unsorted_List),
- ?line case Length rem 2 of
+ Length = length(Unsorted_List),
+ List = lists:sort(Unsorted_List),
+ case Length rem 2 of
0 -> % Even length.
[A, B] = lists:nthtail((Length div 2)-1, List),
(A+B)/2;
@@ -450,31 +403,30 @@ microsecs({Mega_Secs, Secs, Microsecs}) ->
%% calls to erlang:localtime().
now_update(Config) when is_list(Config) ->
- case ?t:is_debug() of
- false -> ?line now_update1(10);
+ case test_server:is_debug() of
+ false -> now_update1(10);
true -> {skip,"Unreliable in DEBUG build"}
end.
now_update1(N) when N > 0 ->
- ?line T1_linear = linear_time(erlang:localtime()),
- ?line T1_now = microsecs(now()),
+ T1_linear = linear_time(erlang:localtime()),
+ T1_now = microsecs(now()),
- ?line receive after 1008 -> ok end,
+ receive after 1008 -> ok end,
- ?line T2_linear = linear_time(erlang:localtime()),
- ?line T2_now = microsecs(now()),
+ T2_linear = linear_time(erlang:localtime()),
+ T2_now = microsecs(now()),
- ?line Linear_Diff = (T2_linear-T1_linear)*1000000,
- ?line Now_Diff = T2_now-T1_now,
- test_server:format("Localtime diff = ~p; now() diff = ~p",
- [Linear_Diff, Now_Diff]),
- ?line case abs(Linear_Diff - Now_Diff) of
+ Linear_Diff = (T2_linear-T1_linear)*1000000,
+ Now_Diff = T2_now-T1_now,
+ io:format("Localtime diff = ~p; now() diff = ~p", [Linear_Diff, Now_Diff]),
+ case abs(Linear_Diff - Now_Diff) of
Abs_Delta when Abs_Delta =< 40000 -> ok;
_ -> now_update1(N-1)
end;
now_update1(0) ->
- ?line test_server:fail().
+ ct:fail("now_update zero").
time_warp_modes(Config) when is_list(Config) ->
%% All time warp modes always supported in
@@ -536,12 +488,12 @@ check_time_warp_mode(Config, TimeCorrection, TimeWarpMode) ->
MonotonicTimeUnit = rpc:call(Node,
erlang,
convert_time_unit,
- [1, seconds, native]),
+ [1, second, native]),
UpMilliSeconds = erlang:convert_time_unit(MonotonicTime - StartTime,
MonotonicTimeUnit,
- milli_seconds),
+ millisecond),
io:format("UpMilliSeconds=~p~n", [UpMilliSeconds]),
- End = erlang:monotonic_time(milli_seconds),
+ End = erlang:monotonic_time(millisecond),
stop_node(Node),
try
true = (UpMilliSeconds > (98*MonotonicityTimeout) div 100),
@@ -551,14 +503,14 @@ check_time_warp_mode(Config, TimeCorrection, TimeWarpMode) ->
io:format("Uptime inconsistency", []),
case {TimeCorrection, erlang:system_info(time_correction)} of
{true, true} ->
- ?t:fail(uptime_inconsistency);
+ ct:fail(uptime_inconsistency);
{true, false} ->
_ = erlang:time_offset(),
receive
{'CHANGE', Mon, time_offset, clock_service, _} ->
ignore
after 1000 ->
- ?t:fail(uptime_inconsistency)
+ ct:fail(uptime_inconsistency)
end;
_ ->
ignore
@@ -728,10 +680,10 @@ check_time_offset_res_conv(Mon, Res) ->
TORes2 ->
case check_time_offset_change(Mon, TO, 1000) of
{TO, false} ->
- ?t:fail({time_unit_conversion_inconsistency,
+ ct:fail({time_unit_conversion_inconsistency,
TO, TORes, TORes2});
{_NewTO, true} ->
- ?t:format("time_offset changed", []),
+ io:format("time_offset changed", []),
check_time_offset_res_conv(Mon, Res)
end
end.
@@ -776,25 +728,21 @@ chk_strc(Res0, Res1) ->
ok.
chk_random_values(FR, TR) ->
-% case (FR rem TR == 0) orelse (TR rem FR == 0) of
-% true ->
- io:format("rand values ~p -> ~p~n", [FR, TR]),
- random:seed(268438039, 268440479, 268439161),
- Values = lists:map(fun (_) -> random:uniform(1 bsl 65) - (1 bsl 64) end,
- lists:seq(1, 100000)),
- CheckFun = fun (V) ->
- CV = erlang:convert_time_unit(V, FR, TR),
- case {(FR*CV) div TR =< V,
- (FR*(CV+1)) div TR >= V} of
- {true, true} ->
- ok;
- Failure ->
- ?t:fail({Failure, CV, V, FR, TR})
- end
- end,
- lists:foreach(CheckFun, Values).%;
-% false -> ok
-% end.
+ io:format("rand values ~p -> ~p~n", [FR, TR]),
+ rand:seed(exsplus, {268438039,268440479,268439161}),
+ Values = lists:map(fun (_) -> rand:uniform(1 bsl 65) - (1 bsl 64) end,
+ lists:seq(1, 100000)),
+ CheckFun = fun (V) ->
+ CV = erlang:convert_time_unit(V, FR, TR),
+ case {(FR*CV) div TR =< V,
+ (FR*(CV+1)) div TR >= V} of
+ {true, true} ->
+ ok;
+ Failure ->
+ ct:fail({Failure, CV, V, FR, TR})
+ end
+ end,
+ lists:foreach(CheckFun, Values).
chk_values_per_value(_FromRes, _ToRes,
@@ -805,7 +753,7 @@ chk_values_per_value(_FromRes, _ToRes,
case ((MinFromValuesPerToValue =< FromValueCount)
andalso (FromValueCount =< MaxFromValuesPerToValue)) of
false ->
- ?t:fail({MinFromValuesPerToValue,
+ ct:fail({MinFromValuesPerToValue,
FromValueCount,
MaxFromValuesPerToValue});
true ->
@@ -815,28 +763,28 @@ chk_values_per_value(FromRes, ToRes, Value, EndValue,
MinFromValuesPerToValue, MaxFromValuesPerToValue,
ToValue, FromValueCount) ->
case erlang:convert_time_unit(Value, FromRes, ToRes) of
- ToValue ->
- chk_values_per_value(FromRes, ToRes,
- Value+1, EndValue,
- MinFromValuesPerToValue,
- MaxFromValuesPerToValue,
- ToValue, FromValueCount+1);
- NewToValue ->
- case ((MinFromValuesPerToValue =< FromValueCount)
- andalso (FromValueCount =< MaxFromValuesPerToValue)) of
- false ->
- ?t:fail({MinFromValuesPerToValue,
- FromValueCount,
- MaxFromValuesPerToValue});
- true ->
-% io:format("~p -> ~p [~p]~n",
-% [Value, NewToValue, FromValueCount]),
- chk_values_per_value(FromRes, ToRes,
- Value+1, EndValue,
- MinFromValuesPerToValue,
- MaxFromValuesPerToValue,
- NewToValue, 1)
- end
+ ToValue ->
+ chk_values_per_value(FromRes, ToRes,
+ Value+1, EndValue,
+ MinFromValuesPerToValue,
+ MaxFromValuesPerToValue,
+ ToValue, FromValueCount+1);
+ NewToValue ->
+ case ((MinFromValuesPerToValue =< FromValueCount)
+ andalso (FromValueCount =< MaxFromValuesPerToValue)) of
+ false ->
+ ct:fail({MinFromValuesPerToValue,
+ FromValueCount,
+ MaxFromValuesPerToValue});
+ true ->
+ % io:format("~p -> ~p [~p]~n",
+ % [Value, NewToValue, FromValueCount]),
+ chk_values_per_value(FromRes, ToRes,
+ Value+1, EndValue,
+ MinFromValuesPerToValue,
+ MaxFromValuesPerToValue,
+ NewToValue, 1)
+ end
end.
erlang_timestamp(Config) when is_list(Config) ->
@@ -849,11 +797,11 @@ erlang_timestamp(Config) when is_list(Config) ->
check_erlang_timestamp(Done, Mon, TO) ->
receive
- {timeout, Done, timeout} ->
- erlang:demonitor(Mon, [flush]),
- ok
+ {timeout, Done, timeout} ->
+ erlang:demonitor(Mon, [flush]),
+ ok
after 0 ->
- do_check_erlang_timestamp(Done, Mon, TO)
+ do_check_erlang_timestamp(Done, Mon, TO)
end.
do_check_erlang_timestamp(Done, Mon, TO) ->
@@ -862,10 +810,10 @@ do_check_erlang_timestamp(Done, Mon, TO) ->
MaxMon = erlang:monotonic_time(),
TsMin = erlang:convert_time_unit(MinMon+TO,
native,
- micro_seconds),
+ microsecond),
TsMax = erlang:convert_time_unit(MaxMon+TO,
native,
- micro_seconds),
+ microsecond),
TsTime = (MegaSec*1000000+Sec)*1000000+MicroSec,
case (TsMin =< TsTime) andalso (TsTime =< TsMax) of
true ->
@@ -878,13 +826,13 @@ do_check_erlang_timestamp(Done, Mon, TO) ->
check_erlang_timestamp(Done, Mon, NewTO);
false ->
io:format("TsMin=~p TsTime=~p TsMax=~p~n", [TsMin, TsTime, TsMax]),
- ?t:format("Detected inconsistency; "
+ io:format("Detected inconsistency; "
"checking for time_offset change...", []),
case check_time_offset_change(Mon, TO, 1000) of
{TO, false} ->
- ?t:fail(timestamp_inconsistency);
+ ct:fail(timestamp_inconsistency);
{NewTO, true} ->
- ?t:format("time_offset changed", []),
+ io:format("time_offset changed", []),
check_erlang_timestamp(Done, Mon, NewTO)
end
end.
@@ -925,13 +873,13 @@ test_data() ->
_ ->
{?timezone,?dst_timezone}
end,
- ?line test_data(nondst_dates(), TZ) ++
+ test_data(nondst_dates(), TZ) ++
test_data(dst_dates(), DSTTZ) ++
crossover_test_data(crossover_dates(), TZ).
%% test_data1() ->
-%% ?line test_data(nondst_dates(), ?timezone) ++
+%% test_data(nondst_dates(), ?timezone) ++
%% test_data(dst_dates(), ?dst_timezone) ++
%% crossover_test_data(crossover_dates(), ?timezone).
@@ -939,16 +887,16 @@ crossover_test_data([{Year, Month, Day}|Rest], TimeZone) when TimeZone > 0 ->
Hour = 23,
Min = 35,
Sec = 55,
- ?line Utc = {{Year, Month, Day}, {Hour, Min, Sec}},
- ?line Local = {{Year, Month, Day+1}, {Hour+TimeZone-24, Min, Sec}},
- ?line [{Utc, Local}|crossover_test_data(Rest, TimeZone)];
+ Utc = {{Year, Month, Day}, {Hour, Min, Sec}},
+ Local = {{Year, Month, Day+1}, {Hour+TimeZone-24, Min, Sec}},
+ [{Utc, Local}|crossover_test_data(Rest, TimeZone)];
crossover_test_data([{Year, Month, Day}|Rest], TimeZone) when TimeZone < 0 ->
Hour = 0,
Min = 23,
Sec = 12,
- ?line Utc = {{Year, Month, Day}, {Hour, Min, Sec}},
- ?line Local = {{Year, Month, Day-1}, {Hour+TimeZone+24, Min, Sec}},
- ?line [{Utc, Local}|crossover_test_data(Rest, TimeZone)];
+ Utc = {{Year, Month, Day}, {Hour, Min, Sec}},
+ Local = {{Year, Month, Day-1}, {Hour+TimeZone+24, Min, Sec}},
+ [{Utc, Local}|crossover_test_data(Rest, TimeZone)];
crossover_test_data([], _) ->
[].
@@ -956,9 +904,9 @@ test_data([Date|Rest], TimeZone) ->
Hour = 12,
Min = 45,
Sec = 7,
- ?line Utc = {Date, {Hour, Min, Sec}},
- ?line Local = {Date, {Hour+TimeZone, Min, Sec}},
- ?line [{Utc, Local}|test_data(Rest, TimeZone)];
+ Utc = {Date, {Hour, Min, Sec}},
+ Local = {Date, {Hour+TimeZone, Min, Sec}},
+ [{Utc, Local}|test_data(Rest, TimeZone)];
test_data([], _) ->
[].
@@ -1049,7 +997,7 @@ start_node(Config) ->
start_node(Config, "").
start_node(Config, Args) ->
- TestCase = ?config(testcase, Config),
+ TestCase = proplists:get_value(testcase, Config),
PA = filename:dirname(code:which(?MODULE)),
ESTime = erlang:monotonic_time(1) + erlang:time_offset(1),
Unique = erlang:unique_integer([positive]),
diff --git a/erts/emulator/test/timer_bif_SUITE.erl b/erts/emulator/test/timer_bif_SUITE.erl
index 51d59f09f3..7cbd93a0f3 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-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,8 +20,7 @@
-module(timer_bif_SUITE).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
+-export([all/0, suite/0, init_per_suite/1, end_per_suite/1,
init_per_testcase/2,end_per_testcase/2]).
-export([start_timer_1/1, send_after_1/1, send_after_2/1, send_after_3/1,
cancel_timer_1/1,
@@ -33,23 +32,20 @@
% same_time_yielding_with_cancel_other_accessor/1,
auto_cancel_yielding/1]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-define(SHORT_TIMEOUT, 5000). %% Bif timers as short as this may be pre-allocated
-define(TIMEOUT_YIELD_LIMIT, 100).
-define(AUTO_CANCEL_YIELD_LIMIT, 100).
init_per_testcase(_Case, Config) ->
- ?line Dog=test_server:timetrap(test_server:seconds(30)),
case catch erts_debug:get_internal_state(available_internal_state) of
true -> ok;
_ -> erts_debug:set_internal_state(available_internal_state, true)
end,
- [{watchdog, Dog}|Config].
+ Config.
-end_per_testcase(_Case, Config) ->
- Dog = ?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
+end_per_testcase(_Case, _Config) ->
ok.
init_per_suite(Config) ->
@@ -59,7 +55,9 @@ init_per_suite(Config) ->
end_per_suite(_Config) ->
catch erts_debug:set_internal_state(available_internal_state, false).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 5}}].
all() ->
[start_timer_1, send_after_1, send_after_2,
@@ -72,20 +70,11 @@ all() ->
% same_time_yielding_with_cancel_other_accessor,
auto_cancel_yielding].
-groups() ->
- [].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-start_timer_1(doc) -> ["Basic start_timer/3 functionality"];
+%% Basic start_timer/3 functionality
start_timer_1(Config) when is_list(Config) ->
Ref1 = erlang:start_timer(1000, self(), plopp),
- ok = get(1100, {timeout, Ref1, plopp}),
+ ok = get(1400, {timeout, Ref1, plopp}),
false = erlang:read_timer(Ref1),
false = erlang:cancel_timer(Ref1),
@@ -94,62 +83,62 @@ start_timer_1(Config) when is_list(Config) ->
Ref2 = erlang:start_timer(1000, self(), plapp),
Left2 = erlang:cancel_timer(Ref2),
UpperLimit = 1000,
- true = (Left2 > 900) and (Left2 =< UpperLimit),
+ true = (Left2 > 600) and (Left2 =< UpperLimit),
empty = get_msg(),
false = erlang:cancel_timer(Ref2),
Ref3 = erlang:start_timer(1000, self(), plopp),
- no_message = get(900, {timeout, Ref3, plopp}),
+ no_message = get(600, {timeout, Ref3, plopp}),
ok.
-send_after_1(doc) -> ["Basic send_after/3 functionality"];
+%% Basic send_after/3 functionality
send_after_1(Config) when is_list(Config) ->
- ?line Ref3 = erlang:send_after(1000, self(), plipp),
- ?line ok = get(1500, plipp),
- ?line false = erlang:read_timer(Ref3),
+ Ref3 = erlang:send_after(1000, self(), plipp),
+ ok = get(1500, plipp),
+ false = erlang:read_timer(Ref3),
ok.
-start_timer_big(doc) -> ["Big timeouts for start_timer/3"];
+%% Big timeouts for start_timer/3
start_timer_big(Config) when is_list(Config) ->
- ?line Big = 1 bsl 31,
- ?line R = erlang:start_timer(Big, self(), hej),
- ?line timer:sleep(200),
- ?line Left = erlang:cancel_timer(R),
- ?line case Big - Left of
- Diff when Diff >= 200, Diff < 10000 ->
- ok;
- _Diff ->
- test_server:fail({big, Big, Left})
- end,
+ Big = 1 bsl 31,
+ R = erlang:start_timer(Big, self(), hej),
+ timer:sleep(200),
+ Left = erlang:cancel_timer(R),
+ case Big - Left of
+ Diff when Diff >= 200, Diff < 10000 ->
+ ok;
+ _Diff ->
+ ct:fail({big, Big, Left})
+ end,
ok.
-send_after_big(doc) -> ["Big timeouts for send_after/3"];
+%% Big timeouts for send_after/3
send_after_big(Config) when is_list(Config) ->
- ?line Big = 1 bsl 31,
- ?line R = erlang:send_after(Big, self(), hej),
- ?line timer:sleep(200),
- ?line Left = erlang:cancel_timer(R),
- ?line case Big - Left of
- Diff when Diff >= 200, Diff < 10000 ->
- ok;
- _Diff ->
- test_server:fail({big, Big, Left})
- end,
+ Big = 1 bsl 31,
+ R = erlang:send_after(Big, self(), hej),
+ timer:sleep(200),
+ Left = erlang:cancel_timer(R),
+ case Big - Left of
+ Diff when Diff >= 200, Diff < 10000 ->
+ ok;
+ _Diff ->
+ ct:fail({big, Big, Left})
+ end,
ok.
-send_after_2(doc) -> ["send_after/3: messages in the right order, kind version"];
+%% send_after/3: messages in the right order, kind version
send_after_2(Config) when is_list(Config) ->
- ?line _ = erlang:send_after(5000, self(), last),
- ?line _ = erlang:send_after(0, self(), a0),
- ?line _ = erlang:send_after(200, self(), a2),
- ?line _ = erlang:send_after(100, self(), a1),
- ?line _ = erlang:send_after(500, self(), a5),
- ?line _ = erlang:send_after(300, self(), a3),
- ?line _ = erlang:send_after(400, self(), a4),
- ?line [a0,a1,a2,a3,a4,a5,last] = collect(last),
+ _ = erlang:send_after(5000, self(), last),
+ _ = erlang:send_after(0, self(), a0),
+ _ = erlang:send_after(200, self(), a2),
+ _ = erlang:send_after(100, self(), a1),
+ _ = erlang:send_after(500, self(), a5),
+ _ = erlang:send_after(300, self(), a3),
+ _ = erlang:send_after(400, self(), a4),
+ [a0,a1,a2,a3,a4,a5,last] = collect(last),
ok.
-send_after_3(doc) -> ["send_after/3: messages in the right order, worse than send_after_2"];
+%% send_after/3: messages in the right order, worse than send_after_2
send_after_3(Config) when is_list(Config) ->
_ = erlang:send_after(100, self(), b1),
_ = erlang:send_after(101, self(), b2),
@@ -157,74 +146,70 @@ send_after_3(Config) when is_list(Config) ->
_ = erlang:send_after(103, self(), last),
[b1, b2, b3, last] = collect(last),
-% This behaviour is not guaranteed:
-% ?line _ = erlang:send_after(100, self(), c1),
-% ?line _ = erlang:send_after(100, self(), c2),
-% ?line _ = erlang:send_after(100, self(), c3),
-% ?line _ = erlang:send_after(100, self(), last),
-% ?line [c1, c2, c3, last] = collect(last),
+ % This behaviour is not guaranteed:
+ % _ = erlang:send_after(100, self(), c1),
+ % _ = erlang:send_after(100, self(), c2),
+ % _ = erlang:send_after(100, self(), c3),
+ % _ = erlang:send_after(100, self(), last),
+ % [c1, c2, c3, last] = collect(last),
ok.
-cancel_timer_1(doc) -> ["Check trivial cancel_timer/1 behaviour"];
+%% Check trivial cancel_timer/1 behaviour
cancel_timer_1(Config) when is_list(Config) ->
- ?line false = erlang:cancel_timer(make_ref()),
+ false = erlang:cancel_timer(make_ref()),
ok.
-start_timer_e(doc) -> ["Error cases for start_timer/3"];
+%% Error cases for start_timer/3
start_timer_e(Config) when is_list(Config) ->
- ?line {'EXIT', _} = (catch erlang:start_timer(-4, self(), hej)),
- ?line {'EXIT', _} = (catch erlang:start_timer(1 bsl 64,
- self(), hej)),
+ {'EXIT', _} = (catch erlang:start_timer(-4, self(), hej)),
+ {'EXIT', _} = (catch erlang:start_timer(1 bsl 64,
+ self(), hej)),
- ?line {'EXIT', _} = (catch erlang:start_timer(4.5, self(), hej)),
- ?line {'EXIT', _} = (catch erlang:start_timer(a, self(), hej)),
+ {'EXIT', _} = (catch erlang:start_timer(4.5, self(), hej)),
+ {'EXIT', _} = (catch erlang:start_timer(a, self(), hej)),
- ?line Node = start_slave(),
- ?line Pid = spawn(Node, timer, sleep, [10000]),
- ?line {'EXIT', _} = (catch erlang:start_timer(1000, Pid, hej)),
- ?line stop_slave(Node),
+ Node = start_slave(),
+ Pid = spawn(Node, timer, sleep, [10000]),
+ {'EXIT', _} = (catch erlang:start_timer(1000, Pid, hej)),
+ stop_slave(Node),
ok.
-send_after_e(doc) -> ["Error cases for send_after/3"];
-send_after_e(suite) -> [];
+%% Error cases for send_after/3
send_after_e(Config) when is_list(Config) ->
- ?line {'EXIT', _} = (catch erlang:send_after(-4, self(), hej)),
- ?line {'EXIT', _} = (catch erlang:send_after(1 bsl 64,
- self(), hej)),
+ {'EXIT', _} = (catch erlang:send_after(-4, self(), hej)),
+ {'EXIT', _} = (catch erlang:send_after(1 bsl 64,
+ self(), hej)),
- ?line {'EXIT', _} = (catch erlang:send_after(4.5, self(), hej)),
- ?line {'EXIT', _} = (catch erlang:send_after(a, self(), hej)),
+ {'EXIT', _} = (catch erlang:send_after(4.5, self(), hej)),
+ {'EXIT', _} = (catch erlang:send_after(a, self(), hej)),
- ?line Node = start_slave(),
- ?line Pid = spawn(Node, timer, sleep, [10000]),
- ?line {'EXIT', _} = (catch erlang:send_after(1000, Pid, hej)),
- ?line stop_slave(Node),
+ Node = start_slave(),
+ Pid = spawn(Node, timer, sleep, [10000]),
+ {'EXIT', _} = (catch erlang:send_after(1000, Pid, hej)),
+ stop_slave(Node),
ok.
-cancel_timer_e(doc) -> ["Error cases for cancel_timer/1"];
-cancel_timer_e(suite) -> [];
+%% Error cases for cancel_timer/1
cancel_timer_e(Config) when is_list(Config) ->
- ?line {'EXIT', _} = (catch erlang:cancel_timer(1)),
- ?line {'EXIT', _} = (catch erlang:cancel_timer(self())),
- ?line {'EXIT', _} = (catch erlang:cancel_timer(a)),
+ {'EXIT', _} = (catch erlang:cancel_timer(1)),
+ {'EXIT', _} = (catch erlang:cancel_timer(self())),
+ {'EXIT', _} = (catch erlang:cancel_timer(a)),
ok.
-read_timer_trivial(doc) -> ["Trivial and error test cases for read_timer/1."];
-read_timer_trivial(suite) -> [];
+%% Trivial and error test cases for read_timer/1.
read_timer_trivial(Config) when is_list(Config) ->
- ?line false = erlang:read_timer(make_ref()),
- ?line {'EXIT', _} = (catch erlang:read_timer(42)),
- ?line {'EXIT', _} = (catch erlang:read_timer(423497834744444444457667444444)),
- ?line {'EXIT', _} = (catch erlang:read_timer(self())),
- ?line {'EXIT', _} = (catch erlang:read_timer(ab)),
+ false = erlang:read_timer(make_ref()),
+ {'EXIT', _} = (catch erlang:read_timer(42)),
+ {'EXIT', _} = (catch erlang:read_timer(423497834744444444457667444444)),
+ {'EXIT', _} = (catch erlang:read_timer(self())),
+ {'EXIT', _} = (catch erlang:read_timer(ab)),
ok.
-read_timer(doc) -> ["Test that read_timer/1 seems to return the correct values."];
-read_timer(suite) -> [];
+%% Test that read_timer/1 seems to return the correct values.
read_timer(Config) when is_list(Config) ->
process_flag(scheduler, 1),
Big = 1 bsl 31,
@@ -234,22 +219,21 @@ read_timer(Config) when is_list(Config) ->
Left = erlang:read_timer(R),
Left2 = erlang:cancel_timer(R),
case Left == Left2 of
- true -> ok;
- false -> Left = Left2 + 1
+ true -> ok;
+ false -> Left = Left2 + 1
end,
false = erlang:read_timer(R),
case Big - Left of
- Diff when Diff >= 200, Diff < 10000 ->
- ok;
- _Diff ->
- test_server:fail({big, Big, Left})
+ Diff when Diff >= 200, Diff < 10000 ->
+ ok;
+ _Diff ->
+ ct:fail({big, Big, Left})
end,
process_flag(scheduler, 0),
ok.
-read_timer_async(doc) -> ["Test that read_timer/1 seems to return the correct values."];
-read_timer_async(suite) -> [];
+%% Test that read_timer/1 seems to return the correct values.
read_timer_async(Config) when is_list(Config) ->
process_flag(scheduler, 1),
Big = 1 bsl 33,
@@ -266,73 +250,69 @@ read_timer_async(Config) when is_list(Config) ->
{read_timer, R, Left} = receive_one(),
{cancel_timer, R, Left2} = receive_one(),
case Left == Left2 of
- true -> ok;
- false -> Left = Left2 + 1
+ true -> ok;
+ false -> Left = Left2 + 1
end,
{read_timer, R, false} = receive_one(),
case Big - Left of
- Diff when Diff >= 200, Diff < 10000 ->
- ok;
- _Diff ->
- test_server:fail({big, Big, Left})
+ Diff when Diff >= 200, Diff < 10000 ->
+ ok;
+ _Diff ->
+ ct:fail({big, Big, Left})
end,
process_flag(scheduler, 0),
ok.
-cleanup(doc) -> [];
-cleanup(suite) -> [];
cleanup(Config) when is_list(Config) ->
- ?line Mem = mem(),
+ Mem = mem(),
%% Timer on dead process
- ?line P1 = spawn(fun () -> ok end),
- ?line wait_until(fun () -> process_is_cleaned_up(P1) end),
- ?line T1 = erlang:start_timer(?SHORT_TIMEOUT*2, P1, "hej"),
- ?line T2 = erlang:send_after(?SHORT_TIMEOUT*2, P1, "hej"),
+ P1 = spawn(fun () -> ok end),
+ wait_until(fun () -> process_is_cleaned_up(P1) end),
+ T1 = erlang:start_timer(?SHORT_TIMEOUT*2, P1, "hej"),
+ T2 = erlang:send_after(?SHORT_TIMEOUT*2, P1, "hej"),
receive after 1000 -> ok end,
- ?line Mem = mem(),
- ?line false = erlang:read_timer(T1),
- ?line false = erlang:read_timer(T2),
- ?line Mem = mem(),
+ Mem = mem(),
+ false = erlang:read_timer(T1),
+ false = erlang:read_timer(T2),
+ Mem = mem(),
%% Process dies before timeout
- ?line P2 = spawn(fun () -> receive after (?SHORT_TIMEOUT div 10) -> ok end end),
- ?line T3 = erlang:start_timer(?SHORT_TIMEOUT*2, P2, "hej"),
- ?line T4 = erlang:send_after(?SHORT_TIMEOUT*2, P2, "hej"),
- ?line true = mem_larger_than(Mem),
- ?line true = is_integer(erlang:read_timer(T3)),
- ?line true = is_integer(erlang:read_timer(T4)),
- ?line wait_until(fun () -> process_is_cleaned_up(P2) end),
+ P2 = spawn(fun () -> receive after (?SHORT_TIMEOUT div 10) -> ok end end),
+ T3 = erlang:start_timer(?SHORT_TIMEOUT*2, P2, "hej"),
+ T4 = erlang:send_after(?SHORT_TIMEOUT*2, P2, "hej"),
+ true = mem_larger_than(Mem),
+ true = is_integer(erlang:read_timer(T3)),
+ true = is_integer(erlang:read_timer(T4)),
+ wait_until(fun () -> process_is_cleaned_up(P2) end),
receive after 1000 -> ok end,
- ?line false = erlang:read_timer(T3),
- ?line false = erlang:read_timer(T4),
- ?line Mem = mem(),
+ false = erlang:read_timer(T3),
+ false = erlang:read_timer(T4),
+ Mem = mem(),
%% Cancel timer
- ?line P3 = spawn(fun () -> receive after ?SHORT_TIMEOUT*4 -> ok end end),
- ?line T5 = erlang:start_timer(?SHORT_TIMEOUT*2, P3, "hej"),
- ?line T6 = erlang:send_after(?SHORT_TIMEOUT*2, P3, "hej"),
- ?line true = mem_larger_than(Mem),
- ?line true = is_integer(erlang:cancel_timer(T5)),
- ?line true = is_integer(erlang:cancel_timer(T6)),
- ?line false = erlang:read_timer(T5),
- ?line false = erlang:read_timer(T6),
- ?line exit(P3, kill),
- ?line wait_until(fun () -> process_is_cleaned_up(P3) end),
- ?line Mem = mem(),
+ P3 = spawn(fun () -> receive after ?SHORT_TIMEOUT*4 -> ok end end),
+ T5 = erlang:start_timer(?SHORT_TIMEOUT*2, P3, "hej"),
+ T6 = erlang:send_after(?SHORT_TIMEOUT*2, P3, "hej"),
+ true = mem_larger_than(Mem),
+ true = is_integer(erlang:cancel_timer(T5)),
+ true = is_integer(erlang:cancel_timer(T6)),
+ false = erlang:read_timer(T5),
+ false = erlang:read_timer(T6),
+ exit(P3, kill),
+ wait_until(fun () -> process_is_cleaned_up(P3) end),
+ Mem = mem(),
%% Timeout
- ?line Ref = make_ref(),
- ?line T7 = erlang:start_timer(?SHORT_TIMEOUT+1, self(), Ref),
- ?line T8 = erlang:send_after(?SHORT_TIMEOUT+1, self(), Ref),
- ?line true = mem_larger_than(Mem),
- ?line true = is_integer(erlang:read_timer(T7)),
- ?line true = is_integer(erlang:read_timer(T8)),
- ?line receive {timeout, T7, Ref} -> ok end,
- ?line receive Ref -> ok end,
- ?line Mem = mem(),
- ?line ok.
-
-
-evil_timers(doc) -> [];
-evil_timers(suite) -> [];
+ Ref = make_ref(),
+ T7 = erlang:start_timer(?SHORT_TIMEOUT+1, self(), Ref),
+ T8 = erlang:send_after(?SHORT_TIMEOUT+1, self(), Ref),
+ true = mem_larger_than(Mem),
+ true = is_integer(erlang:read_timer(T7)),
+ true = is_integer(erlang:read_timer(T8)),
+ receive {timeout, T7, Ref} -> ok end,
+ receive Ref -> ok end,
+ Mem = mem(),
+ ok.
+
+
evil_timers(Config) when is_list(Config) ->
%% Create a composite term consisting of at least:
%% * externals (remote pids, ports, and refs)
@@ -344,38 +324,38 @@ evil_timers(Config) when is_list(Config) ->
%% * lists
%% since data of these types have to be adjusted if moved
%% in memory
- ?line Self = self(),
- ?line R1 = make_ref(),
- ?line Node = start_slave(),
- ?line spawn_link(Node,
- fun () ->
- Self ! {R1,
- [lists:sublist(erlang:ports(), 3),
- [make_ref(), make_ref(), make_ref()],
- lists:sublist(processes(), 3),
- [fun () -> gurka end,
- fun (A) -> A + 1 end,
- fun (A, B) -> A + B end]]}
- end),
- ?line ExtList = receive {R1, L} -> L end,
- ?line stop_slave(Node),
- ?line BinList = [<<"bla">>,
- <<"blipp">>,
- <<"blupp">>,
- list_to_binary(lists:duplicate(1000000,$a)),
- list_to_binary(lists:duplicate(1000000,$b)),
- list_to_binary(lists:duplicate(1000000,$c))],
- ?line FunList = [fun () -> gurka end,
- fun (A) -> A + 1 end,
- fun (A, B) -> A + B end],
- ?line PidList = lists:sublist(processes(), 3),
- ?line PortList = lists:sublist(erlang:ports(), 3),
- ?line RefList = [make_ref(), make_ref(), make_ref()],
- ?line BigList = [111111111111, 22222222222222, 333333333333333333],
- ?line Msg = {BinList,[FunList,{RefList,ExtList,PidList,PortList,BigList}]},
- %% ?line ?t:format("Msg=~p~n",[Msg]),
-
- ?line Prio = process_flag(priority, max),
+ Self = self(),
+ R1 = make_ref(),
+ Node = start_slave(),
+ spawn_link(Node,
+ fun () ->
+ Self ! {R1,
+ [lists:sublist(erlang:ports(), 3),
+ [make_ref(), make_ref(), make_ref()],
+ lists:sublist(processes(), 3),
+ [fun () -> gurka end,
+ fun (A) -> A + 1 end,
+ fun (A, B) -> A + B end]]}
+ end),
+ ExtList = receive {R1, L} -> L end,
+ stop_slave(Node),
+ BinList = [<<"bla">>,
+ <<"blipp">>,
+ <<"blupp">>,
+ list_to_binary(lists:duplicate(1000000,$a)),
+ list_to_binary(lists:duplicate(1000000,$b)),
+ list_to_binary(lists:duplicate(1000000,$c))],
+ FunList = [fun () -> gurka end,
+ fun (A) -> A + 1 end,
+ fun (A, B) -> A + B end],
+ PidList = lists:sublist(processes(), 3),
+ PortList = lists:sublist(erlang:ports(), 3),
+ RefList = [make_ref(), make_ref(), make_ref()],
+ BigList = [111111111111, 22222222222222, 333333333333333333],
+ Msg = {BinList,[FunList,{RefList,ExtList,PidList,PortList,BigList}]},
+ %% io:format("Msg=~p~n",[Msg]),
+
+ Prio = process_flag(priority, max),
%%
%% In the smp case there are four major cases we want to test:
%%
@@ -388,8 +368,8 @@ evil_timers(Config) when is_list(Config) ->
%% be allocated in the previously allocated message buffer along
%% with Msg, i.e. the previously allocated message buffer will be
%% reallocated and potentially moved.
- ?line TimeOutMsgs0 = evil_setup_timers(200, Self, Msg),
- ?line RecvTimeOutMsgs0 = evil_recv_timeouts(200),
+ TimeOutMsgs0 = evil_setup_timers(200, Self, Msg),
+ RecvTimeOutMsgs0 = evil_recv_timeouts(200),
%% 2. A timer started with erlang:start_timer(Time, Receiver, Msg),
%% where Msg is an immediate term, expires, and the receivers main
%% lock *can not* be acquired immediately (typically when the
@@ -397,8 +377,8 @@ evil_timers(Config) when is_list(Config) ->
%%
%% The wrap tuple will in this case be allocated in a new
%% message buffer.
- ?line TimeOutMsgs1 = evil_setup_timers(200, Self, immediate),
- ?line RecvTimeOutMsgs1 = evil_recv_timeouts(200),
+ TimeOutMsgs1 = evil_setup_timers(200, Self, immediate),
+ RecvTimeOutMsgs1 = evil_recv_timeouts(200),
%% 3. A timer started with erlang:start_timer(Time, Receiver, Msg),
%% where Msg is a composite term, expires, and the receivers main
%% lock *can* be acquired immediately (typically when the receiver
@@ -407,13 +387,13 @@ evil_timers(Config) when is_list(Config) ->
%% The wrap tuple will in this case be allocated on the receivers
%% heap, and Msg is passed in the previously allocated message
%% buffer.
- ?line R2 = make_ref(),
- ?line spawn_link(fun () ->
- Self ! {R2, evil_setup_timers(200, Self, Msg)}
- end),
- ?line receive after 1000 -> ok end,
- ?line TimeOutMsgs2 = receive {R2, TOM2} -> TOM2 end,
- ?line RecvTimeOutMsgs2 = evil_recv_timeouts(200),
+ R2 = make_ref(),
+ spawn_link(fun () ->
+ Self ! {R2, evil_setup_timers(200, Self, Msg)}
+ end),
+ receive after 1000 -> ok end,
+ TimeOutMsgs2 = receive {R2, TOM2} -> TOM2 end,
+ RecvTimeOutMsgs2 = evil_recv_timeouts(200),
%% 4. A timer started with erlang:start_timer(Time, Receiver, Msg),
%% where Msg is an immediate term, expires, and the Receivers main
%% lock *can* be acquired immediately (typically when the receiver
@@ -421,109 +401,107 @@ evil_timers(Config) when is_list(Config) ->
%%
%% The wrap tuple will in this case be allocated on the receivers
%% heap.
- ?line R3 = make_ref(),
- ?line spawn_link(fun () ->
- Self ! {R3, evil_setup_timers(200,Self,immediate)}
- end),
- ?line receive after 1000 -> ok end,
- ?line TimeOutMsgs3 = receive {R3, TOM3} -> TOM3 end,
- ?line RecvTimeOutMsgs3 = evil_recv_timeouts(200),
+ R3 = make_ref(),
+ spawn_link(fun () ->
+ Self ! {R3, evil_setup_timers(200,Self,immediate)}
+ end),
+ receive after 1000 -> ok end,
+ TimeOutMsgs3 = receive {R3, TOM3} -> TOM3 end,
+ RecvTimeOutMsgs3 = evil_recv_timeouts(200),
%% Garge collection will hopefully crash the emulator if something
%% is wrong...
- ?line garbage_collect(),
- ?line garbage_collect(),
- ?line garbage_collect(),
+ garbage_collect(),
+ garbage_collect(),
+ garbage_collect(),
%% Make sure we got the timeouts we expected
%%
%% Note timeouts are *not* guaranteed to be delivered in order
- ?line ok = match(lists:sort(RecvTimeOutMsgs0), lists:sort(TimeOutMsgs0)),
- ?line ok = match(lists:sort(RecvTimeOutMsgs1), lists:sort(TimeOutMsgs1)),
- ?line ok = match(lists:sort(RecvTimeOutMsgs2), lists:sort(TimeOutMsgs2)),
- ?line ok = match(lists:sort(RecvTimeOutMsgs3), lists:sort(TimeOutMsgs3)),
+ ok = match(lists:sort(RecvTimeOutMsgs0), lists:sort(TimeOutMsgs0)),
+ ok = match(lists:sort(RecvTimeOutMsgs1), lists:sort(TimeOutMsgs1)),
+ ok = match(lists:sort(RecvTimeOutMsgs2), lists:sort(TimeOutMsgs2)),
+ ok = match(lists:sort(RecvTimeOutMsgs3), lists:sort(TimeOutMsgs3)),
- ?line process_flag(priority, Prio),
- ?line ok.
+ process_flag(priority, Prio),
+ ok.
evil_setup_timers(N, Receiver, Msg) ->
- ?line evil_setup_timers(0, N, Receiver, Msg, []).
+ evil_setup_timers(0, N, Receiver, Msg, []).
evil_setup_timers(N, N, _Receiver, _Msg, TOs) ->
- ?line TOs;
+ TOs;
evil_setup_timers(N, Max, Receiver, Msg, TOs) ->
- ?line TRef = erlang:start_timer(N, Receiver, Msg),
- ?line evil_setup_timers(N+1, Max, Receiver, Msg, [{timeout,TRef,Msg}|TOs]).
+ TRef = erlang:start_timer(N, Receiver, Msg),
+ evil_setup_timers(N+1, Max, Receiver, Msg, [{timeout,TRef,Msg}|TOs]).
evil_recv_timeouts(M) ->
- ?line evil_recv_timeouts([], 0, M).
+ evil_recv_timeouts([], 0, M).
evil_recv_timeouts(TOs, N, N) ->
- ?line TOs;
+ TOs;
evil_recv_timeouts(TOs, N, M) ->
- ?line receive
- {timeout, _, _} = TO ->
- ?line evil_recv_timeouts([TO|TOs], N+1, M)
- after 0 ->
- ?line evil_recv_timeouts(TOs, N, M)
- end.
-
-registered_process(doc) -> [];
-registered_process(suite) -> [];
+ receive
+ {timeout, _, _} = TO ->
+ evil_recv_timeouts([TO|TOs], N+1, M)
+ after 0 ->
+ evil_recv_timeouts(TOs, N, M)
+ end.
+
registered_process(Config) when is_list(Config) ->
- ?line Mem = mem(),
+ Mem = mem(),
%% Cancel
- ?line T1 = erlang:start_timer(?SHORT_TIMEOUT+1, ?MODULE, "hej"),
- ?line T2 = erlang:send_after(?SHORT_TIMEOUT+1, ?MODULE, "hej"),
- ?line undefined = whereis(?MODULE),
- ?line true = mem_larger_than(Mem),
- ?line true = is_integer(erlang:cancel_timer(T1)),
- ?line true = is_integer(erlang:cancel_timer(T2)),
- ?line false = erlang:read_timer(T1),
- ?line false = erlang:read_timer(T2),
- ?line Mem = mem(),
+ T1 = erlang:start_timer(?SHORT_TIMEOUT+1, ?MODULE, "hej"),
+ T2 = erlang:send_after(?SHORT_TIMEOUT+1, ?MODULE, "hej"),
+ undefined = whereis(?MODULE),
+ true = mem_larger_than(Mem),
+ true = is_integer(erlang:cancel_timer(T1)),
+ true = is_integer(erlang:cancel_timer(T2)),
+ false = erlang:read_timer(T1),
+ false = erlang:read_timer(T2),
+ Mem = mem(),
%% Timeout register after start
- ?line Ref1 = make_ref(),
- ?line T3 = erlang:start_timer(?SHORT_TIMEOUT+1, ?MODULE, Ref1),
- ?line T4 = erlang:send_after(?SHORT_TIMEOUT+1, ?MODULE, Ref1),
- ?line undefined = whereis(?MODULE),
- ?line true = mem_larger_than(Mem),
- ?line true = is_integer(erlang:read_timer(T3)),
- ?line true = is_integer(erlang:read_timer(T4)),
- ?line true = register(?MODULE, self()),
- ?line receive {timeout, T3, Ref1} -> ok end,
- ?line receive Ref1 -> ok end,
- ?line Mem = mem(),
+ Ref1 = make_ref(),
+ T3 = erlang:start_timer(?SHORT_TIMEOUT+1, ?MODULE, Ref1),
+ T4 = erlang:send_after(?SHORT_TIMEOUT+1, ?MODULE, Ref1),
+ undefined = whereis(?MODULE),
+ true = mem_larger_than(Mem),
+ true = is_integer(erlang:read_timer(T3)),
+ true = is_integer(erlang:read_timer(T4)),
+ true = register(?MODULE, self()),
+ receive {timeout, T3, Ref1} -> ok end,
+ receive Ref1 -> ok end,
+ Mem = mem(),
%% Timeout register before start
- ?line Ref2 = make_ref(),
- ?line T5 = erlang:start_timer(?SHORT_TIMEOUT+1, ?MODULE, Ref2),
- ?line T6 = erlang:send_after(?SHORT_TIMEOUT+1, ?MODULE, Ref2),
- ?line true = mem_larger_than(Mem),
- ?line true = is_integer(erlang:read_timer(T5)),
- ?line true = is_integer(erlang:read_timer(T6)),
- ?line receive {timeout, T5, Ref2} -> ok end,
- ?line receive Ref2 -> ok end,
- ?line Mem = mem(),
- ?line true = unregister(?MODULE),
- ?line ok.
+ Ref2 = make_ref(),
+ T5 = erlang:start_timer(?SHORT_TIMEOUT+1, ?MODULE, Ref2),
+ T6 = erlang:send_after(?SHORT_TIMEOUT+1, ?MODULE, Ref2),
+ true = mem_larger_than(Mem),
+ true = is_integer(erlang:read_timer(T5)),
+ true = is_integer(erlang:read_timer(T6)),
+ receive {timeout, T5, Ref2} -> ok end,
+ receive Ref2 -> ok end,
+ Mem = mem(),
+ true = unregister(?MODULE),
+ ok.
same_time_yielding(Config) when is_list(Config) ->
Mem = mem(),
SchdlrsOnln = erlang:system_info(schedulers_online),
- Tmo = erlang:monotonic_time(milli_seconds) + 3000,
+ Tmo = erlang:monotonic_time(millisecond) + 3000,
Tmrs = lists:map(fun (I) ->
- process_flag(scheduler, (I rem SchdlrsOnln) + 1),
- erlang:start_timer(Tmo, self(), hej, [{abs, true}])
- end,
- lists:seq(1, (?TIMEOUT_YIELD_LIMIT*3+1)*SchdlrsOnln)),
+ process_flag(scheduler, (I rem SchdlrsOnln) + 1),
+ erlang:start_timer(Tmo, self(), hej, [{abs, true}])
+ end,
+ lists:seq(1, (?TIMEOUT_YIELD_LIMIT*3+1)*SchdlrsOnln)),
true = mem_larger_than(Mem),
lists:foreach(fun (Tmr) -> receive {timeout, Tmr, hej} -> ok end end, Tmrs),
- Done = erlang:monotonic_time(milli_seconds),
+ Done = erlang:monotonic_time(millisecond),
true = Done >= Tmo,
case erlang:system_info(build_type) of
- opt -> true = Done < Tmo + 200;
- _ -> true = Done < Tmo + 1000
+ opt -> true = Done < Tmo + 200;
+ _ -> true = Done < Tmo + 1000
end,
Mem = mem(),
ok.
@@ -539,78 +517,78 @@ same_time_yielding_with_cancel_other(Config) when is_list(Config) ->
do_cancel_tmrs(Tmo, Tmrs, Tester) ->
BeginCancel = erlang:convert_time_unit(Tmo,
- milli_seconds,
- micro_seconds) - 100,
+ millisecond,
+ microsecond) - 100,
busy_wait_until(fun () ->
- erlang:monotonic_time(micro_seconds) >= BeginCancel
- end),
+ erlang:monotonic_time(microsecond) >= BeginCancel
+ end),
lists:foreach(fun (Tmr) ->
- erlang:cancel_timer(Tmr,
- [{async, true},
- {info, true}])
- end, Tmrs),
+ erlang:cancel_timer(Tmr,
+ [{async, true},
+ {info, true}])
+ end, Tmrs),
case Tester == self() of
- true -> ok;
- false -> forward_msgs(Tester)
+ true -> ok;
+ false -> forward_msgs(Tester)
end.
same_time_yielding_with_cancel_test(Other, Accessor) ->
Mem = mem(),
SchdlrsOnln = erlang:system_info(schedulers_online),
- Tmo = erlang:monotonic_time(milli_seconds) + 3000,
+ Tmo = erlang:monotonic_time(millisecond) + 3000,
Tester = self(),
Cancelor = case Other of
- false ->
- Tester;
- true ->
- spawn(fun () ->
- receive
- {timers, Tmrs} ->
- do_cancel_tmrs(Tmo, Tmrs, Tester)
- end
- end)
- end,
+ false ->
+ Tester;
+ true ->
+ spawn(fun () ->
+ receive
+ {timers, Tmrs} ->
+ do_cancel_tmrs(Tmo, Tmrs, Tester)
+ end
+ end)
+ end,
Opts = case Accessor of
- false -> [{abs, true}];
- true -> [{accessor, Cancelor}, {abs, true}]
- end,
+ false -> [{abs, true}];
+ true -> [{accessor, Cancelor}, {abs, true}]
+ end,
Tmrs = lists:map(fun (I) ->
- process_flag(scheduler, (I rem SchdlrsOnln) + 1),
- erlang:start_timer(Tmo, self(), hej, Opts)
- end,
- lists:seq(1, (?TIMEOUT_YIELD_LIMIT*3+1)*SchdlrsOnln)),
+ process_flag(scheduler, (I rem SchdlrsOnln) + 1),
+ erlang:start_timer(Tmo, self(), hej, Opts)
+ end,
+ lists:seq(1, (?TIMEOUT_YIELD_LIMIT*3+1)*SchdlrsOnln)),
true = mem_larger_than(Mem),
case Other of
- false ->
- do_cancel_tmrs(Tmo, Tmrs, Tester);
- true ->
- Cancelor ! {timers, Tmrs}
+ false ->
+ do_cancel_tmrs(Tmo, Tmrs, Tester);
+ true ->
+ Cancelor ! {timers, Tmrs}
end,
{Tmos, Cncls} = lists:foldl(fun (Tmr, {T, C}) ->
- receive
- {timeout, Tmr, hej} ->
- receive
- {cancel_timer, Tmr, Info} ->
- false = Info,
- {T+1, C}
- end;
- {cancel_timer, Tmr, false} ->
- receive
- {timeout, Tmr, hej} ->
- {T+1, C}
- end;
- {cancel_timer, Tmr, TimeLeft} ->
- true = is_integer(TimeLeft),
- {T, C+1}
- end
- end,
- {0, 0},
- Tmrs),
+ receive
+ {timeout, Tmr, hej} ->
+ receive
+ {cancel_timer, Tmr, Info} ->
+ false = Info,
+ {T+1, C}
+ end;
+ {cancel_timer, Tmr, false} ->
+ receive
+ {timeout, Tmr, hej} ->
+ {T+1, C}
+ end;
+ {cancel_timer, Tmr, TimeLeft} ->
+ true = is_integer(TimeLeft),
+ {T, C+1}
+ end
+ end,
+ {0, 0},
+ Tmrs),
io:format("Timeouts: ~p Cancels: ~p~n", [Tmos, Cncls]),
Mem = mem(),
case Other of
- true -> exit(Cancelor, bang);
- false -> ok
+ true -> exit(Cancelor, bang);
+ false -> ok
end,
{comment,
"Timeouts: " ++ integer_to_list(Tmos) ++ " Cancels: "
@@ -620,16 +598,16 @@ auto_cancel_yielding(Config) when is_list(Config) ->
Mem = mem(),
SchdlrsOnln = erlang:system_info(schedulers_online),
P = spawn(fun () ->
- lists:foreach(
- fun (I) ->
- process_flag(scheduler, (I rem SchdlrsOnln)+1),
- erlang:start_timer((1 bsl 28)+I*10, self(), hej)
- end,
- lists:seq(1,
- ((?AUTO_CANCEL_YIELD_LIMIT*3+1)
- *SchdlrsOnln))),
- receive after infinity -> ok end
- end),
+ lists:foreach(
+ fun (I) ->
+ process_flag(scheduler, (I rem SchdlrsOnln)+1),
+ erlang:start_timer((1 bsl 28)+I*10, self(), hej)
+ end,
+ lists:seq(1,
+ ((?AUTO_CANCEL_YIELD_LIMIT*3+1)
+ *SchdlrsOnln))),
+ receive after infinity -> ok end
+ end),
true = mem_larger_than(Mem),
exit(P, bang),
wait_until(fun () -> process_is_cleaned_up(P) end),
@@ -641,46 +619,46 @@ process_is_cleaned_up(P) when is_pid(P) ->
wait_until(Pred) when is_function(Pred) ->
case catch Pred() of
- true -> ok;
- _ -> receive after 50 -> ok end, wait_until(Pred)
+ true -> ok;
+ _ -> receive after 50 -> ok end, wait_until(Pred)
end.
busy_wait_until(Pred) when is_function(Pred) ->
case catch Pred() of
- true -> ok;
- _ -> busy_wait_until(Pred)
+ true -> ok;
+ _ -> busy_wait_until(Pred)
end.
forward_msgs(To) ->
receive
- Msg ->
- To ! Msg
+ Msg ->
+ To ! Msg
end,
forward_msgs(To).
get(Time, Msg) ->
receive
- Msg ->
- ok
+ Msg ->
+ ok
after Time
- ->
- no_message
+ ->
+ no_message
end.
get_msg() ->
receive
- Msg ->
- {ok, Msg}
+ Msg ->
+ {ok, Msg}
after 0 ->
- empty
+ empty
end.
start_slave() ->
- ?line Pa = filename:dirname(code:which(?MODULE)),
- ?line Name = atom_to_list(?MODULE)
- ++ "-" ++ integer_to_list(erlang:system_time(seconds))
- ++ "-" ++ integer_to_list(erlang:unique_integer([positive])),
- {ok, Node} = ?t:start_node(Name, slave, [{args, "-pa " ++ Pa}]),
+ Pa = filename:dirname(code:which(?MODULE)),
+ Name = atom_to_list(?MODULE)
+ ++ "-" ++ integer_to_list(erlang:system_time(second))
+ ++ "-" ++ integer_to_list(erlang:unique_integer([positive])),
+ {ok, Node} = test_server:start_node(Name, slave, [{args, "-pa " ++ Pa}]),
Node.
stop_slave(Node) ->
@@ -691,18 +669,18 @@ collect(Last) ->
receive_one() ->
receive
- Msg ->
- Msg
+ Msg ->
+ Msg
end.
collect(Last, Msgs0) ->
Msg = receive_one(),
Msgs = Msgs0 ++ [Msg],
case Msg of
- Last ->
- Msgs;
- _ ->
- collect(Last, Msgs)
+ Last ->
+ Msgs;
+ _ ->
+ collect(Last, Msgs)
end.
match(X, X) ->
@@ -751,8 +729,8 @@ mem() ->
erts_debug:set_internal_state(wait, timer_cancellations),
erts_debug:set_internal_state(wait, deallocations),
case mem_get() of
- {-1, -1} -> no_fix_alloc;
- {A, U} -> io:format("mem = ~p ~p~n", [A, U]), U
+ {-1, -1} -> no_fix_alloc;
+ {A, U} -> io:format("mem = ~p ~p~n", [A, U]), U
end.
mem_get() ->
@@ -765,8 +743,8 @@ mem_recv(0, _Ref, AU) ->
AU;
mem_recv(N, Ref, AU) ->
receive
- {Ref, _, IL} ->
- mem_recv(N-1, Ref, mem_parse_ilists(IL, AU))
+ {Ref, _, IL} ->
+ mem_recv(N-1, Ref, mem_parse_ilists(IL, AU))
end.
@@ -779,19 +757,19 @@ mem_parse_ilist({fix_alloc, false}, _) ->
{-1, -1};
mem_parse_ilist({fix_alloc, _, IDL}, {A, U}) ->
case lists:keyfind(fix_types, 1, IDL) of
- {fix_types, TL} ->
- {ThisA, ThisU} = mem_get_btm_aus(TL, 0, 0),
- {ThisA + A, ThisU + U};
- {fix_types, Mask, TL} ->
- {ThisA, ThisU} = mem_get_btm_aus(TL, 0, 0),
- {(ThisA + A) band Mask , (ThisU + U) band Mask}
+ {fix_types, TL} ->
+ {ThisA, ThisU} = mem_get_btm_aus(TL, 0, 0),
+ {ThisA + A, ThisU + U};
+ {fix_types, Mask, TL} ->
+ {ThisA, ThisU} = mem_get_btm_aus(TL, 0, 0),
+ {(ThisA + A) band Mask , (ThisU + U) band Mask}
end.
mem_get_btm_aus([], A, U) ->
{A, U};
mem_get_btm_aus([{BtmType, BtmA, BtmU} | Types],
- A, U) when BtmType == bif_timer;
- BtmType == accessor_bif_timer ->
+ A, U) when BtmType == bif_timer;
+ BtmType == accessor_bif_timer ->
mem_get_btm_aus(Types, BtmA+A, BtmU+U);
mem_get_btm_aus([_|Types], A, U) ->
mem_get_btm_aus(Types, A, U).
diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl
index 6eae182e45..f846b0f4b9 100644
--- a/erts/emulator/test/trace_SUITE.erl
+++ b/erts/emulator/test/trace_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -24,97 +24,164 @@
%%% Tests the trace BIF.
%%%
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2, receive_trace/1, self_send/1,
+-export([all/0, suite/0, link_receive_call_correlation/0,
+ receive_trace/1, link_receive_call_correlation/1, self_send/1,
timeout_trace/1, send_trace/1,
- procs_trace/1, dist_procs_trace/1,
+ procs_trace/1, dist_procs_trace/1, procs_new_trace/1,
suspend/1, mutual_suspend/1, suspend_exit/1, suspender_exit/1,
suspend_system_limit/1, suspend_opts/1, suspend_waiting/1,
- new_clear/1, existing_clear/1,
+ new_clear/1, existing_clear/1, tracer_die/1,
set_on_spawn/1, set_on_first_spawn/1, cpu_timestamp/1,
+ set_on_link/1, set_on_first_link/1,
system_monitor_args/1, more_system_monitor_args/1,
system_monitor_long_gc_1/1, system_monitor_long_gc_2/1,
system_monitor_large_heap_1/1, system_monitor_large_heap_2/1,
system_monitor_long_schedule/1,
bad_flag/1, trace_delivered/1]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
%%% Internal exports
-export([process/1]).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {seconds, 5}}].
all() ->
- [cpu_timestamp, receive_trace, self_send, timeout_trace,
+ [cpu_timestamp, receive_trace, link_receive_call_correlation,
+ self_send, timeout_trace,
send_trace, procs_trace, dist_procs_trace, suspend,
mutual_suspend, suspend_exit, suspender_exit,
suspend_system_limit, suspend_opts, suspend_waiting,
- new_clear, existing_clear, set_on_spawn,
- set_on_first_spawn, system_monitor_args,
+ new_clear, existing_clear, tracer_die, set_on_spawn,
+ set_on_first_spawn, set_on_link, set_on_first_link,
+ system_monitor_args,
more_system_monitor_args, system_monitor_long_gc_1,
system_monitor_long_gc_2, system_monitor_large_heap_1,
- system_monitor_long_schedule,
+ system_monitor_long_schedule,
system_monitor_large_heap_2, bad_flag, trace_delivered].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
%% No longer testing anything, just reporting whether cpu_timestamp
%% is enabled or not.
cpu_timestamp(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
-
%% Test whether cpu_timestamp is implemented on this platform.
- ?line Works = try erlang:trace(all, true, [cpu_timestamp]) of
- _ ->
- ?line erlang:trace(all, false, [cpu_timestamp]),
- true
- catch
- error:badarg -> false
- end,
-
- ?line test_server:timetrap_cancel(Dog),
+ Works = try erlang:trace(all, true, [cpu_timestamp]) of
+ _ ->
+ erlang:trace(all, false, [cpu_timestamp]),
+ true
+ catch
+ error:badarg -> false
+ end,
{comment,case Works of
- false -> "cpu_timestamp is NOT implemented/does not work";
- true -> "cpu_timestamp works"
- end}.
+ false -> "cpu_timestamp is NOT implemented/does not work";
+ true -> "cpu_timestamp works"
+ end}.
%% Tests that trace(Pid, How, ['receive']) works.
receive_trace(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
- ?line Receiver = fun_spawn(fun receiver/0),
- ?line process_flag(trap_exit, true),
+ Receiver = fun_spawn(fun receiver/0),
%% Trace the process; make sure that we receive the trace messages.
- ?line 1 = erlang:trace(Receiver, true, ['receive']),
- ?line Hello = {hello, world},
- ?line Receiver ! Hello,
- ?line {trace, Receiver, 'receive', Hello} = receive_first(),
- ?line Hello2 = {hello, again, world},
- ?line Receiver ! Hello2,
- ?line {trace, Receiver, 'receive', Hello2} = receive_first(),
- ?line receive_nothing(),
+ 1 = erlang:trace(Receiver, true, ['receive']),
+ Hello = {hello, world},
+ Receiver ! Hello,
+ {trace, Receiver, 'receive', Hello} = receive_first_trace(),
+ Hello2 = {hello, again, world},
+ Receiver ! Hello2,
+ {trace, Receiver, 'receive', Hello2} = receive_first_trace(),
+ receive_nothing(),
+
+ %% Test 'receive' with matchspec
+ F1 = fun ({Pat, IsMatching}) ->
+ set_trace_pattern('receive', Pat, []),
+ Receiver ! Hello,
+ case IsMatching of
+ true ->
+ {trace, Receiver, 'receive', Hello} = receive_first_trace();
+ false ->
+ ok
+ end,
+ receive_nothing()
+ end,
+ From = self(),
+ Node = node(),
+ lists:foreach(F1, [{no, true},
+ {[{[Node, undefined,"Unexpected"],[],[]}], false},
+ {[{[Node, From,'_'],[],[]}], true},
+ {[{[Node, '$1','_'],[{'=/=','$1',From}],[]}], false},
+ {[{['$1', '_','_'],[{'=:=','$1',Node}],[]}], true},
+ {false, false},
+ {true, true}]),
+
+ %% Remote messages
+ OtherName = atom_to_list(?MODULE)++"_receive_trace",
+ {ok, OtherNode} = start_node(OtherName),
+ RemoteProc = spawn_link(OtherNode, ?MODULE, process, [self()]),
+ io:format("RemoteProc = ~p ~n", [RemoteProc]),
+
+ RemoteProc ! {send_please, Receiver, Hello},
+ {trace, Receiver, 'receive', Hello} = receive_first_trace(),
+ RemoteProc ! {send_please, Receiver, 99},
+ {trace, Receiver, 'receive', 99} = receive_first_trace(),
+
+ %% Remote with matchspec
+ F2 = fun (To, {Pat, IsMatching}) ->
+ set_trace_pattern('receive', Pat, []),
+ RemoteProc ! {send_please, To, Hello},
+ case IsMatching of
+ true ->
+ {trace, Receiver, 'receive', Hello} = receive_first_trace();
+ false ->
+ ok
+ end,
+ receive_nothing()
+ end,
+ F2(Receiver, {no, true}),
+ F2(Receiver, {[{[OtherNode, undefined,"Unexpected"],[],[]}], false}),
+ F2(Receiver, {[{[OtherNode, RemoteProc,'_'],[],[]},
+ {[OtherNode, undefined,'_'],[],[]}], true}),
+ F2(Receiver, {[{[OtherNode, '$1','_'],
+ [{'orelse',{'=:=','$1',undefined},{'=/=',{node,'$1'},{node}}}],
+ []}], true}),
+ F2(Receiver, {[{['$1', '_','_'], [{'=:=','$1',OtherNode}], []}], true}),
+ F2(Receiver, {false, false}),
+ F2(Receiver, {true, true}),
+
+ %% Remote to named with matchspec
+ Name = trace_SUITE_receiver,
+ register(Name, Receiver),
+ NN = {Name, node()},
+ F2(NN, {no, true}),
+ F2(NN, {[{[OtherNode, undefined,"Unexpected"],[],[]}], false}),
+ F2(NN, {[{[OtherNode, RemoteProc,'_'],[],[]},
+ {[OtherNode, undefined,'_'],[],[]}], true}),
+ F2(NN, {[{[OtherNode, '$1','_'],
+ [{'orelse',{'=:=','$1',undefined},{'=/=',{node,'$1'},{node}}}],
+ []}], true}),
+ F2(NN, {[{['$1', '_','_'], [{'==','$1',OtherNode}], []}], true}),
+ F2(NN, {false, false}),
+ F2(NN, {true, true}),
+
+ unlink(RemoteProc),
+ true = stop_node(OtherNode),
+
+ %% Timeout
+ Receiver ! {set_timeout, 10},
+ {trace, Receiver, 'receive', {set_timeout, 10}} = receive_first_trace(),
+ {trace, Receiver, 'receive', timeout} = receive_first_trace(),
+ erlang:trace_pattern('receive', [{[clock_service,undefined,timeout], [], []}], []),
+ Receiver ! {set_timeout, 7},
+ {trace, Receiver, 'receive', timeout} = receive_first_trace(),
+ erlang:trace_pattern('receive', true, []),
%% Another process should not be able to trace Receiver.
- ?line Intruder = fun_spawn(fun() -> erlang:trace(Receiver, true, ['receive']) end),
- ?line {'EXIT', Intruder, {badarg, _}} = receive_first(),
+ process_flag(trap_exit, true),
+ Intruder = fun_spawn(fun() -> erlang:trace(Receiver, true, ['receive']) end),
+ {'EXIT', Intruder, {badarg, _}} = receive_first(),
%% Untrace the process; we should not receive anything.
?line 1 = erlang:trace(Receiver, false, ['receive']),
@@ -122,400 +189,663 @@ receive_trace(Config) when is_list(Config) ->
?line Receiver ! any_garbage,
?line receive_nothing(),
- %% Done.
- ?line test_server:timetrap_cancel(Dog),
+ %% Verify restrictions in matchspec for 'receive'
+ F3 = fun (Pat) -> {'EXIT', {badarg,_}} = (catch erlang:trace_pattern('receive', Pat, [])) end,
+ WC = ['_','_','_'],
+ F3([{WC,[],[{message, {process_dump}}]}]),
+ F3([{WC,[{is_seq_trace}],[]}]),
+ F3([{WC,[],[{set_seq_token,label,4711}]}]),
+ F3([{WC,[],[{get_seq_token}]}]),
+ F3([{WC,[],[{enable_trace,call}]}]),
+ F3([{WC,[],[{enable_trace,self(),call}]}]),
+ F3([{WC,[],[{disable_trace,call}]}]),
+ F3([{WC,[],[{disable_trace,self(),call}]}]),
+ F3([{WC,[],[{trace,[call],[]}]}]),
+ F3([{WC,[],[{trace,self(),[],[call]}]}]),
+ F3([{WC,[],[{caller}]}]),
+ F3([{WC,[],[{silent,true}]}]),
+
ok.
-self_send(doc) -> ["Test that traces are generated for messages sent ",
- "and received to/from self()."];
+%% Tests that receive of a message always happens before a call with
+%% that message and that links/unlinks are ordered together with the
+%% 'receive'.
+link_receive_call_correlation() ->
+ [{timetrap, {minutes, 5}}].
+link_receive_call_correlation(Config) when is_list(Config) ->
+ Receiver = fun_spawn(fun F() ->
+ receive
+ stop -> ok;
+ M -> receive_msg(M), F()
+ end
+ end),
+ process_flag(trap_exit, true),
+
+ %% Trace the process; make sure that we receive the trace messages.
+ 1 = erlang:trace(Receiver, true, ['receive', procs, call, timestamp, scheduler_id]),
+ 1 = erlang:trace_pattern({?MODULE, receive_msg, '_'}, [], [local]),
+
+ Num = 100000,
+
+ (fun F(0) -> [];
+ F(N) ->
+ if N rem 2 == 0 ->
+ link(Receiver);
+ true ->
+ unlink(Receiver)
+ end,
+ [Receiver ! N | F(N-1)]
+ end)(Num),
+
+ Receiver ! stop,
+ MonRef = erlang:monitor(process, Receiver),
+ receive {'DOWN', MonRef, _, _, _} -> ok end,
+ Ref = erlang:trace_delivered(Receiver),
+ receive {trace_delivered, _, Ref} -> ok end,
+
+ Msgs = (fun F() -> receive M -> [M | F()] after 1 -> [] end end)(),
+
+ case check_consistent(Receiver, Num, Num, Num, Msgs) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ ct:log("~p", [Msgs]),
+ ct:fail({error, Reason})
+ end.
+
+-define(schedid, , _).
+
+check_consistent(_Pid, Recv, Call, _LU, [Msg | _]) when Recv > Call ->
+ {error, Msg};
+check_consistent(Pid, Recv, Call, LU, [Msg | Msgs]) ->
+
+ case Msg of
+ {trace, Pid, 'receive', Recv ?schedid} ->
+ check_consistent(Pid,Recv - 1, Call, LU, Msgs);
+ {trace_ts, Pid, 'receive', Recv ?schedid, _} ->
+ check_consistent(Pid,Recv - 1, Call, LU, Msgs);
+
+ {trace, Pid, call, {?MODULE, receive_msg, [Call]} ?schedid} ->
+ check_consistent(Pid,Recv, Call - 1, LU, Msgs);
+ {trace_ts, Pid, call, {?MODULE, receive_msg, [Call]} ?schedid, _} ->
+ check_consistent(Pid,Recv, Call - 1, LU, Msgs);
+
+ %% We check that for each receive we have gotten a
+ %% getting_linked or getting_unlinked message. Also
+ %% if we receive a getting_linked, then the next
+ %% message we expect to receive is an even number
+ %% and odd number for getting_unlinked.
+ {trace, Pid, getting_linked, _Self ?schedid}
+ when Recv rem 2 == 0, Recv == LU ->
+ check_consistent(Pid, Recv, Call, LU - 1, Msgs);
+ {trace_ts, Pid, getting_linked, _Self ?schedid, _}
+ when Recv rem 2 == 0, Recv == LU ->
+ check_consistent(Pid, Recv, Call, LU - 1, Msgs);
+
+ {trace, Pid, getting_unlinked, _Self ?schedid}
+ when Recv rem 2 == 1, Recv == LU ->
+ check_consistent(Pid, Recv, Call, LU - 1, Msgs);
+ {trace_ts, Pid, getting_unlinked, _Self ?schedid, _}
+ when Recv rem 2 == 1, Recv == LU ->
+ check_consistent(Pid, Recv, Call, LU - 1, Msgs);
+
+ {trace,Pid,'receive',Ignore ?schedid}
+ when Ignore == stop; Ignore == timeout ->
+ check_consistent(Pid, Recv, Call, LU, Msgs);
+ {trace_ts,Pid,'receive',Ignore ?schedid,_}
+ when Ignore == stop; Ignore == timeout ->
+ check_consistent(Pid, Recv, Call, LU, Msgs);
+
+ {trace, Pid, exit, normal ?schedid} ->
+ check_consistent(Pid, Recv, Call, LU, Msgs);
+ {trace_ts, Pid, exit, normal ?schedid, _} ->
+ check_consistent(Pid, Recv, Call, LU, Msgs);
+ {'EXIT', Pid, normal} ->
+ check_consistent(Pid, Recv, Call, LU, Msgs);
+ Msg ->
+ {error, Msg}
+ end;
+check_consistent(_, 0, 0, 0, []) ->
+ ok;
+check_consistent(_, Recv, Call, LU, []) ->
+ {error,{Recv, Call, LU}}.
+
+receive_msg(M) ->
+ M.
+
+%% Test that traces are generated for messages sent
+%% and received to/from self().
self_send(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
- ?line Fun =
- fun(Self, Parent) -> receive
- go_ahead ->
- self() ! from_myself,
- Self(Self, Parent);
- from_myself ->
- Parent ! done
- end
- end,
- ?line Self = self(),
- ?line SelfSender = fun_spawn(Fun, [Fun, Self]),
- ?line erlang:trace(SelfSender, true, ['receive', 'send']),
- ?line SelfSender ! go_ahead,
- ?line receive {trace, SelfSender, 'receive', go_ahead} -> ok end,
- ?line receive {trace, SelfSender, 'receive', from_myself} -> ok end,
- ?line receive
- {trace,SelfSender,send,from_myself,SelfSender} -> ok
- end,
- ?line receive {trace,SelfSender,send,done,Self} -> ok end,
- ?line receive done -> ok end,
-
- ?line test_server:timetrap_cancel(Dog),
+ Fun =
+ fun(Self, Parent) -> receive
+ go_ahead ->
+ self() ! from_myself,
+ Self(Self, Parent);
+ from_myself ->
+ Parent ! done
+ end
+ end,
+ Self = self(),
+ SelfSender = fun_spawn(Fun, [Fun, Self]),
+ erlang:trace(SelfSender, true, ['receive', 'send']),
+ SelfSender ! go_ahead,
+ receive {trace, SelfSender, 'receive', go_ahead} -> ok end,
+ receive {trace, SelfSender, 'receive', from_myself} -> ok end,
+ receive
+ {trace,SelfSender,send,from_myself,SelfSender} -> ok
+ end,
+ receive {trace,SelfSender,send,done,Self} -> ok end,
+ receive done -> ok end,
ok.
%% Test that we can receive timeout traces.
timeout_trace(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
-
- ?line Process = fun_spawn(fun process/0),
- ?line 1 = erlang:trace(Process, true, ['receive']),
- ?line Process ! timeout_please,
- ?line {trace, Process, 'receive', timeout_please} = receive_first(),
- ?line {trace, Process, 'receive', timeout} = receive_first(),
- ?line receive_nothing(),
-
- ?line test_server:timetrap_cancel(Dog),
+ Process = fun_spawn(fun process/0),
+ 1 = erlang:trace(Process, true, ['receive']),
+ Process ! timeout_please,
+ {trace, Process, 'receive', timeout_please} = receive_first_trace(),
+ {trace, Process, 'receive', timeout} = receive_first_trace(),
+ receive_nothing(),
ok.
%% Tests that trace(Pid, How, [send]) works.
send_trace(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
- ?line process_flag(trap_exit, true),
- ?line Sender = fun_spawn(fun sender/0),
- ?line Receiver = fun_spawn(fun receiver/0),
+ process_flag(trap_exit, true),
+ Sender = fun_spawn(fun sender/0),
+ Receiver = fun_spawn(fun receiver/0),
%% Check that a message sent to another process is traced.
- ?line 1 = erlang:trace(Sender, true, [send]),
- ?line Sender ! {send_please, Receiver, to_receiver},
- ?line {trace, Sender, send, to_receiver, Receiver} = receive_first(),
- ?line receive_nothing(),
+ 1 = erlang:trace(Sender, true, [send]),
+ F1 = fun (Pat) ->
+ set_trace_pattern(send, Pat, []),
+ Sender ! {send_please, Receiver, to_receiver},
+ {trace, Sender, send, to_receiver, Receiver} = receive_first_trace(),
+ receive_nothing()
+ end,
+ lists:foreach(F1, [no,
+ [{[Receiver,to_receiver],[],[]}],
+ [{['_','_'],[],[]}],
+ [{['$1','_'],[{is_pid,'$1'}],[]}],
+ [{['_','$1'],[{is_atom,'$1'}],[]}],
+ true]),
+
+ %% Test {message, Msg}
+ F1m = fun ({Pat, Msg}) ->
+ set_trace_pattern(send, Pat, []),
+ Sender ! {send_please, Receiver, to_receiver},
+ {trace, Sender, send, to_receiver, Receiver, Msg} = receive_first_trace(),
+ receive_nothing()
+ end,
+ lists:foreach(F1m, [{[{['_','_'],[],[{message, 4711}]}], 4711},
+ {[{['_','_'],[],[{message, "4711"}]}], "4711"}
+ ]),
+
+ %% Test {message, {process_dump}}
+ set_trace_pattern(send, [{['_','_'],[],[{message, {process_dump}}]}], []),
+ Sender ! {send_please, Receiver, to_receiver},
+ {trace, Sender, send, to_receiver, Receiver, ProcDump} = receive_first_trace(),
+ true = is_binary(ProcDump),
+ receive_nothing(),
+
+ %% Same test with false match spec
+ F2 = fun (Pat) ->
+ set_trace_pattern(send, Pat, []),
+ Sender ! {send_please, Receiver, to_receiver},
+ receive_nothing()
+ end,
+ lists:foreach(F2, [[{[Sender,to_receiver],[],[]}],
+ [{[Receiver,nomatch],[],[]}],
+ [{['$1','_'],[{is_atom,'$1'}],[]}],
+ [{['_','$1'],[{is_pid,'$1'}],[]}],
+ false,
+ [{['_','_'],[],[{message,false}]}],
+ [{['_','_'],[],[{silent,true}]}]]),
+ erlang:trace_pattern(send, true, []),
+ erlang:trace(Sender, false, [silent]),
%% Check that a message sent to another registered process is traced.
register(?MODULE,Receiver),
- Sender ! {send_please, ?MODULE, to_receiver},
- {trace, Sender, send, to_receiver, ?MODULE} = receive_first(),
- receive_nothing(),
+ F3 = fun (Pat) ->
+ set_trace_pattern(send, Pat, []),
+ Sender ! {send_please, ?MODULE, to_receiver},
+ {trace, Sender, send, to_receiver, ?MODULE} = receive_first_trace(),
+ receive_nothing()
+ end,
+ lists:foreach(F3, [no,
+ [{[?MODULE,to_receiver],[],[]}],
+ [{['_','_'],[],[]}],
+ [{['$1','_'],[{is_atom,'$1'}],[]}],
+ [{['_','$1'],[{is_atom,'$1'}],[]}],
+ true]),
+ %% Again with false match spec
+ F4 = fun (Pat) ->
+ set_trace_pattern(send, Pat, []),
+ Sender ! {send_please, ?MODULE, to_receiver},
+ receive_nothing()
+ end,
+ lists:foreach(F4, [[{[nomatch,to_receiver],[],[]}],
+ [{[?MODULE,nomatch],[],[]}],
+ [{['$1','_'],[{is_pid,'$1'}],[]}],
+ [{['_','$1'],[{is_pid,'$1'}],[]}],
+ [{['_','_'],[],[{message,false}]}],
+ [{['_','_'],[],[{silent,true}]}]
+ ]),
unregister(?MODULE),
+ erlang:trace_pattern(send, true, []),
+ erlang:trace(Sender, false, [silent]),
%% Check that a message sent to this process is traced.
- ?line Sender ! {send_please, self(), to_myself},
- ?line receive to_myself -> ok end,
- ?line Self = self(),
- ?line {trace, Sender, send, to_myself, Self} = receive_first(),
- ?line receive_nothing(),
+ F5 = fun (Pat) ->
+ set_trace_pattern(send, Pat, []),
+ Sender ! {send_please, self(), to_myself},
+ receive to_myself -> ok end,
+ Self = self(),
+ {trace, Sender, send, to_myself, Self} = receive_first_trace(),
+ receive_nothing()
+ end,
+ lists:foreach(F5, [no,
+ [{[self(),to_myself],[],[]}],
+ [{['_','_'],[],[]}],
+ true]),
%% Check that a message sent to dead process is traced.
{Pid,Ref} = spawn_monitor(fun() -> ok end),
receive {'DOWN',Ref,_,_,_} -> ok end,
- Sender ! {send_please, Pid, to_dead},
- {trace, Sender, send_to_non_existing_process, to_dead, Pid} = receive_first(),
- receive_nothing(),
+ F6 = fun (Pat) ->
+ set_trace_pattern(send, Pat, []),
+ Sender ! {send_please, Pid, to_dead},
+ {trace, Sender, send_to_non_existing_process, to_dead, Pid} = receive_first_trace(),
+ receive_nothing()
+ end,
+ lists:foreach(F6, [no,
+ [{[Pid,to_dead],[],[]}],
+ [{['_','_'],[],[]}],
+ true]),
%% Check that a message sent to unknown registrated process is traced.
BadargSender = fun_spawn(fun sender/0),
1 = erlang:trace(BadargSender, true, [send]),
unlink(BadargSender),
BadargSender ! {send_please, not_registered, to_unknown},
- {trace, BadargSender, send, to_unknown, not_registered} = receive_first(),
+ {trace, BadargSender, send, to_unknown, not_registered} = receive_first_trace(),
receive_nothing(),
%% Another process should not be able to trace Sender.
- ?line Intruder = fun_spawn(fun() -> erlang:trace(Sender, true, [send]) end),
- ?line {'EXIT', Intruder, {badarg, _}} = receive_first(),
+ Intruder = fun_spawn(fun() -> erlang:trace(Sender, true, [send]) end),
+ {'EXIT', Intruder, {badarg, _}} = receive_first(),
%% Untrace the sender process and make sure that we receive no more
%% trace messages.
- ?line 1 = erlang:trace(Sender, false, [send]),
- ?line Sender ! {send_please, Receiver, to_receiver},
- ?line Sender ! {send_please, self(), to_myself_again},
- ?line receive to_myself_again -> ok end,
- ?line receive_nothing(),
+ 1 = erlang:trace(Sender, false, [send]),
+ Sender ! {send_please, Receiver, to_receiver},
+ Sender ! {send_please, self(), to_myself_again},
+ receive to_myself_again -> ok end,
+ receive_nothing(),
+ {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, true, [global])),
+ {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, true, [local])),
+ {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, true, [meta])),
+ {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, true, [{meta,self()}])),
+ {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, true, [call_count])),
+ {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, true, [call_time])),
+ {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, restart, [])),
+ {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, pause, [])),
+ {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, [{['_','_'],[],[{caller}]}], [])),
+
%% Done.
- ?line test_server:timetrap_cancel(Dog),
ok.
+set_trace_pattern(_, no, _) -> 0;
+set_trace_pattern(MFA, Pat, Flg) ->
+ R = erlang:trace_pattern(MFA, Pat, Flg),
+ {match_spec, Pat} = erlang:trace_info(MFA, match_spec),
+ R.
+
%% Test trace(Pid, How, [procs]).
procs_trace(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
- ?line Name = list_to_atom(atom_to_list(?MODULE)++"_procs_trace"),
- ?line Self = self(),
- ?line process_flag(trap_exit, true),
+ Name = list_to_atom(atom_to_list(?MODULE)++"_procs_trace"),
+ Self = self(),
+ process_flag(trap_exit, true),
%%
- ?line Proc1 = spawn_link(?MODULE, process, [Self]),
- ?line io:format("Proc1 = ~p ~n", [Proc1]),
- ?line Proc2 = spawn(?MODULE, process, [Self]),
- ?line io:format("Proc2 = ~p ~n", [Proc2]),
+ Proc1 = spawn_link(?MODULE, process, [Self]),
+ io:format("Proc1 = ~p ~n", [Proc1]),
+ Proc2 = spawn(?MODULE, process, [Self]),
+ io:format("Proc2 = ~p ~n", [Proc2]),
%%
- ?line 1 = erlang:trace(Proc1, true, [procs]),
- ?line MFA = {?MODULE, process, [Self]},
+ 1 = erlang:trace(Proc1, true, [procs, set_on_first_spawn]),
+ MFA = {?MODULE, process, [Self]},
%%
%% spawn, link
- ?line Proc1 ! {spawn_link_please, Self, MFA},
- ?line Proc3 = receive {spawned, Proc1, P3} -> P3 end,
- ?line {trace, Proc1, spawn, Proc3, MFA} = receive_first(),
- ?line io:format("Proc3 = ~p ~n", [Proc3]),
- ?line {trace, Proc1, link, Proc3} = receive_first(),
- ?line receive_nothing(),
+ Proc1 ! {spawn_link_please, Self, MFA},
+ Proc3 = receive {spawned, Proc1, P3} -> P3 end,
+ receive {trace, Proc3, spawned, Proc1, MFA} -> ok end,
+ receive {trace, Proc3, getting_linked, Proc1} -> ok end,
+ {trace, Proc1, spawn, Proc3, MFA} = receive_first_trace(),
+ io:format("Proc3 = ~p ~n", [Proc3]),
+ {trace, Proc1, link, Proc3} = receive_first_trace(),
+ receive_nothing(),
%%
%% getting_unlinked by exit()
- ?line Proc1 ! {trap_exit_please, true},
- ?line Reason3 = make_ref(),
- ?line Proc1 ! {send_please, Proc3, {exit_please, Reason3}},
- ?line receive {Proc1, {'EXIT', Proc3, Reason3}} -> ok end,
- ?line {trace, Proc1, getting_unlinked, Proc3} = receive_first(),
- ?line Proc1 ! {trap_exit_please, false},
- ?line receive_nothing(),
+ Proc1 ! {trap_exit_please, true},
+ Reason3 = make_ref(),
+ Proc1 ! {send_please, Proc3, {exit_please, Reason3}},
+ receive {Proc1, {'EXIT', Proc3, Reason3}} -> ok end,
+ receive {trace, Proc3, exit, Reason3} -> ok end,
+ {trace, Proc1, getting_unlinked, Proc3} = receive_first_trace(),
+ Proc1 ! {trap_exit_please, false},
+ receive_nothing(),
%%
%% link
- ?line Proc1 ! {link_please, Proc2},
- ?line {trace, Proc1, link, Proc2} = receive_first(),
- ?line receive_nothing(),
+ Proc1 ! {link_please, Proc2},
+ {trace, Proc1, link, Proc2} = receive_first_trace(),
+ receive_nothing(),
%%
%% unlink
- ?line Proc1 ! {unlink_please, Proc2},
- ?line {trace, Proc1, unlink, Proc2} = receive_first(),
- ?line receive_nothing(),
+ Proc1 ! {unlink_please, Proc2},
+ {trace, Proc1, unlink, Proc2} = receive_first_trace(),
+ receive_nothing(),
%%
%% getting_linked
- ?line Proc2 ! {link_please, Proc1},
- ?line {trace, Proc1, getting_linked, Proc2} = receive_first(),
- ?line receive_nothing(),
+ Proc2 ! {link_please, Proc1},
+ {trace, Proc1, getting_linked, Proc2} = receive_first_trace(),
+ receive_nothing(),
%%
%% getting_unlinked
- ?line Proc2 ! {unlink_please, Proc1},
- ?line {trace, Proc1, getting_unlinked, Proc2} = receive_first(),
- ?line receive_nothing(),
+ Proc2 ! {unlink_please, Proc1},
+ {trace, Proc1, getting_unlinked, Proc2} = receive_first_trace(),
+ receive_nothing(),
%%
%% register
- ?line true = register(Name, Proc1),
- ?line {trace, Proc1, register, Name} = receive_first(),
- ?line receive_nothing(),
+ true = register(Name, Proc1),
+ {trace, Proc1, register, Name} = receive_first_trace(),
+ receive_nothing(),
%%
%% unregister
- ?line true = unregister(Name),
- ?line {trace, Proc1, unregister, Name} = receive_first(),
- ?line receive_nothing(),
+ true = unregister(Name),
+ {trace, Proc1, unregister, Name} = receive_first_trace(),
+ receive_nothing(),
%%
%% exit (with registered name, due to link)
- ?line Reason4 = make_ref(),
- ?line Proc1 ! {spawn_link_please, Self, MFA},
- ?line Proc4 = receive {spawned, Proc1, P4} -> P4 end,
- ?line {trace, Proc1, spawn, Proc4, MFA} = receive_first(),
- ?line io:format("Proc4 = ~p ~n", [Proc4]),
- ?line {trace, Proc1, link, Proc4} = receive_first(),
- ?line Proc1 ! {register_please, Name, Proc1},
- ?line {trace, Proc1, register, Name} = receive_first(),
- ?line Proc4 ! {exit_please, Reason4},
- ?line receive {'EXIT', Proc1, Reason4} -> ok end,
- ?line {trace, Proc1, exit, Reason4} = receive_first(),
- ?line {trace, Proc1, unregister, Name} = receive_first(),
- ?line receive_nothing(),
+ Reason4 = make_ref(),
+ Proc1 ! {spawn_link_please, Self, MFA},
+ Proc4 = receive {spawned, Proc1, P4} -> P4 end,
+ {trace, Proc1, spawn, Proc4, MFA} = receive_first_trace(),
+ io:format("Proc4 = ~p ~n", [Proc4]),
+ {trace, Proc1, link, Proc4} = receive_first_trace(),
+ Proc1 ! {register_please, Name, Proc1},
+ {trace, Proc1, register, Name} = receive_first_trace(),
+ Proc4 ! {exit_please, Reason4},
+ receive {'EXIT', Proc1, Reason4} -> ok end,
+ {trace, Proc1, exit, Reason4} = receive_first_trace(),
+ {trace, Proc1, unregister, Name} = receive_first_trace(),
+ receive_nothing(),
%%
%% exit (not linked to tracing process)
- ?line 1 = erlang:trace(Proc2, true, [procs]),
- ?line Reason2 = make_ref(),
- ?line Proc2 ! {exit_please, Reason2},
- ?line {trace, Proc2, exit, Reason2} = receive_first(),
- ?line receive_nothing(),
- %%
- %% Done.
- ?line test_server:timetrap_cancel(Dog),
+ 1 = erlang:trace(Proc2, true, [procs]),
+ Reason2 = make_ref(),
+ Proc2 ! {exit_please, Reason2},
+ {trace, Proc2, exit, Reason2} = receive_first_trace(),
+ receive_nothing(),
ok.
dist_procs_trace(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(15)),
- ?line OtherName = atom_to_list(?MODULE)++"_dist_procs_trace",
- ?line {ok, OtherNode} = start_node(OtherName),
- ?line Self = self(),
- ?line process_flag(trap_exit, true),
+ ct:timetrap({seconds, 15}),
+ OtherName = atom_to_list(?MODULE)++"_dist_procs_trace",
+ {ok, OtherNode} = start_node(OtherName),
+ Self = self(),
+ process_flag(trap_exit, true),
%%
- ?line Proc1 = spawn_link(?MODULE, process, [Self]),
- ?line io:format("Proc1 = ~p ~n", [Proc1]),
- ?line Proc2 = spawn(OtherNode, ?MODULE, process, [Self]),
- ?line io:format("Proc2 = ~p ~n", [Proc2]),
+ Proc1 = spawn_link(?MODULE, process, [Self]),
+ io:format("Proc1 = ~p ~n", [Proc1]),
+ Proc2 = spawn(OtherNode, ?MODULE, process, [Self]),
+ io:format("Proc2 = ~p ~n", [Proc2]),
%%
- ?line 1 = erlang:trace(Proc1, true, [procs]),
- ?line MFA = {?MODULE, process, [Self]},
+ 1 = erlang:trace(Proc1, true, [procs]),
+ MFA = {?MODULE, process, [Self]},
%%
%% getting_unlinked by exit()
- ?line Proc1 ! {spawn_link_please, Self, OtherNode, MFA},
- ?line Proc1 ! {trap_exit_please, true},
- ?line Proc3 = receive {spawned, Proc1, P3} -> P3 end,
- ?line io:format("Proc3 = ~p ~n", [Proc3]),
- ?line {trace, Proc1, getting_linked, Proc3} = receive_first(),
- ?line Reason3 = make_ref(),
- ?line Proc1 ! {send_please, Proc3, {exit_please, Reason3}},
- ?line receive {Proc1, {'EXIT', Proc3, Reason3}} -> ok end,
- ?line {trace, Proc1, getting_unlinked, Proc3} = receive_first(),
- ?line Proc1 ! {trap_exit_please, false},
- ?line receive_nothing(),
+ Proc1 ! {spawn_link_please, Self, OtherNode, MFA},
+ Proc1 ! {trap_exit_please, true},
+ Proc3 = receive {spawned, Proc1, P3} -> P3 end,
+ io:format("Proc3 = ~p ~n", [Proc3]),
+ {trace, Proc1, getting_linked, Proc3} = receive_first_trace(),
+ Reason3 = make_ref(),
+ Proc1 ! {send_please, Proc3, {exit_please, Reason3}},
+ receive {Proc1, {'EXIT', Proc3, Reason3}} -> ok end,
+ {trace, Proc1, getting_unlinked, Proc3} = receive_first_trace(),
+ Proc1 ! {trap_exit_please, false},
+ receive_nothing(),
%%
%% link
- ?line Proc1 ! {link_please, Proc2},
- ?line {trace, Proc1, link, Proc2} = receive_first(),
- ?line receive_nothing(),
+ Proc1 ! {link_please, Proc2},
+ {trace, Proc1, link, Proc2} = receive_first_trace(),
+ receive_nothing(),
%%
%% unlink
- ?line Proc1 ! {unlink_please, Proc2},
- ?line {trace, Proc1, unlink, Proc2} = receive_first(),
- ?line receive_nothing(),
+ Proc1 ! {unlink_please, Proc2},
+ {trace, Proc1, unlink, Proc2} = receive_first_trace(),
+ receive_nothing(),
%%
%% getting_linked
- ?line Proc2 ! {link_please, Proc1},
- ?line {trace, Proc1, getting_linked, Proc2} = receive_first(),
- ?line receive_nothing(),
+ Proc2 ! {link_please, Proc1},
+ {trace, Proc1, getting_linked, Proc2} = receive_first_trace(),
+ receive_nothing(),
%%
%% getting_unlinked
- ?line Proc2 ! {unlink_please, Proc1},
- ?line {trace, Proc1, getting_unlinked, Proc2} = receive_first(),
- ?line receive_nothing(),
+ Proc2 ! {unlink_please, Proc1},
+ {trace, Proc1, getting_unlinked, Proc2} = receive_first_trace(),
+ receive_nothing(),
+
%%
%% exit (with registered name, due to link)
- ?line Name = list_to_atom(OtherName),
- ?line Reason2 = make_ref(),
- ?line Proc1 ! {link_please, Proc2},
- ?line {trace, Proc1, link, Proc2} = receive_first(),
- ?line Proc1 ! {register_please, Name, Proc1},
- ?line {trace, Proc1, register, Name} = receive_first(),
- ?line Proc2 ! {exit_please, Reason2},
- ?line receive {'EXIT', Proc1, Reason2} -> ok end,
- ?line {trace, Proc1, exit, Reason2} = receive_first(),
- ?line {trace, Proc1, unregister, Name} = receive_first(),
- ?line receive_nothing(),
+ Name = list_to_atom(OtherName),
+ Reason2 = make_ref(),
+ Proc1 ! {link_please, Proc2},
+ {trace, Proc1, link, Proc2} = receive_first_trace(),
+ Proc1 ! {register_please, Name, Proc1},
+ {trace, Proc1, register, Name} = receive_first_trace(),
+ Proc2 ! {exit_please, Reason2},
+ receive {'EXIT', Proc1, Reason2} -> ok end,
+ {trace, Proc1, exit, Reason2} = receive_first_trace(),
+ {trace, Proc1, unregister, Name} = receive_first_trace(),
+ receive_nothing(),
%%
%% Done.
- ?line true = stop_node(OtherNode),
- ?line test_server:timetrap_cancel(Dog),
+ true = stop_node(OtherNode),
ok.
+%% Test trace(new, How, [procs]).
+procs_new_trace(Config) when is_list(Config) ->
+ Self = self(),
+ process_flag(trap_exit, true),
+ %%
+ Proc1 = spawn_link(?MODULE, process, [Self]),
+ io:format("Proc1 = ~p ~n", [Proc1]),
+ %%
+ 0 = erlang:trace(new, true, [procs]),
+
+ MFA = {?MODULE, process, [Self]},
+ %%
+ %% spawn, link
+ Proc1 ! {spawn_link_please, Self, MFA},
+ Proc3 = receive {spawned, Proc1, P3} -> P3 end,
+ receive {trace, Proc3, spawned, Proc1, MFA} -> ok end,
+ receive {trace, Proc3, getting_linked, Proc1} -> ok end,
+ io:format("Proc3 = ~p ~n", [Proc3]),
+ receive_nothing(),
+ %%
+ %%
+ %% exit (not linked to tracing process)
+ Reason1 = make_ref(),
+ Proc1 ! {exit_please, Reason1},
+ receive {'EXIT', Proc1, Reason1} -> ok end,
+ {trace, Proc3, exit, Reason1} = receive_first_trace(),
+ receive_nothing(),
+ ok.
%% Tests trace(Pid, How, [set_on_spawn]).
set_on_spawn(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
- ?line Listener = fun_spawn(fun process/0),
+ Listener = fun_spawn(fun process/0),
%% Create and trace a process with the set_on_spawn flag.
%% Make sure it is traced.
- ?line Father_SOS = fun_spawn(fun process/0),
- ?line 1 = erlang:trace(Father_SOS, true, [send, set_on_spawn]),
- ?line true = is_send_traced(Father_SOS, Listener, sos_father),
+ Father_SOS = fun_spawn(fun process/0),
+ 1 = erlang:trace(Father_SOS, true, [send, set_on_spawn]),
+ true = is_send_traced(Father_SOS, Listener, sos_father),
%% Have the process spawn of two children and test that they
%% are traced.
- ?line [Child1, Child2] = spawn_children(Father_SOS, 2),
- ?line true = is_send_traced(Child1, Listener, child1),
- ?line true = is_send_traced(Child2, Listener, child2),
+ [Child1, Child2] = spawn_children(Father_SOS, 2),
+ true = is_send_traced(Child1, Listener, child1),
+ true = is_send_traced(Child2, Listener, child2),
%% Second generation.
[Child11, Child12] = spawn_children(Child1, 2),
- ?line true = is_send_traced(Child11, Listener, child11),
- ?line true = is_send_traced(Child12, Listener, child12),
-
- %% Done.
- ?line test_server:timetrap_cancel(Dog),
+ true = is_send_traced(Child11, Listener, child11),
+ true = is_send_traced(Child12, Listener, child12),
ok.
%% Tests trace(Pid, How, [set_on_first_spawn]).
set_on_first_spawn(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Listener = fun_spawn(fun process/0),
+ ct:timetrap({seconds, 10}),
+ Listener = fun_spawn(fun process/0),
%% Create and trace a process with the set_on_first_spawn flag.
%% Make sure it is traced.
- ?line Parent = fun_spawn(fun process/0),
- ?line 1 = erlang:trace(Parent, true, [send, set_on_first_spawn]),
- ?line is_send_traced(Parent, Listener, sos_father),
+ Parent = fun_spawn(fun process/0),
+ 1 = erlang:trace(Parent, true, [send, set_on_first_spawn]),
+ is_send_traced(Parent, Listener, sos_father),
%% Have the process spawn off three children and test that the
%% first is traced.
- ?line [Child1, Child2, Child3] = spawn_children(Parent, 3),
- ?line true = is_send_traced(Child1, Listener, child1),
- ?line false = is_send_traced(Child2, Listener, child2),
- ?line false = is_send_traced(Child3, Listener, child3),
- ?line receive_nothing(),
+ [Child1, Child2, Child3] = spawn_children(Parent, 3),
+ true = is_send_traced(Child1, Listener, child1),
+ false = is_send_traced(Child2, Listener, child2),
+ false = is_send_traced(Child3, Listener, child3),
+ receive_nothing(),
+ ok.
- %% Done.
- ?line test_server:timetrap_cancel(Dog),
+%% Tests trace(Pid, How, [set_on_link]).
+
+set_on_link(Config) ->
+ Listener = fun_spawn(fun process/0),
+
+ %% Create and trace a process with the set_on_link flag.
+ %% Make sure it is traced.
+ Father_SOL = fun_spawn(fun process/0),
+ 1 = erlang:trace(Father_SOL, true, [send, set_on_link]),
+ true = is_send_traced(Father_SOL, Listener, sol_father),
+
+ %% Have the process spawn of two children and test that they
+ %% are traced.
+ [Child1, Child2] = spawn_children(Father_SOL, 2),
+ true = is_send_traced(Child1, Listener, child1),
+ true = is_send_traced(Child2, Listener, child2),
+
+ %% Second generation.
+ [Child11, Child12] = spawn_children(Child1, 2),
+ true = is_send_traced(Child11, Listener, child11),
+ true = is_send_traced(Child12, Listener, child12),
+ ok.
+
+%% Tests trace(Pid, How, [set_on_first_spawn]).
+
+set_on_first_link(Config) ->
+ ct:timetrap({seconds, 10}),
+ Listener = fun_spawn(fun process/0),
+
+ %% Create and trace a process with the set_on_first_spawn flag.
+ %% Make sure it is traced.
+ Parent = fun_spawn(fun process/0),
+ 1 = erlang:trace(Parent, true, [send, set_on_first_link]),
+ is_send_traced(Parent, Listener, sol_father),
+
+ %% Have the process spawn off three children and test that the
+ %% first is traced.
+ [Child1, Child2, Child3] = spawn_children(Parent, 3),
+ true = is_send_traced(Child1, Listener, child1),
+ false = is_send_traced(Child2, Listener, child2),
+ false = is_send_traced(Child3, Listener, child3),
+ receive_nothing(),
ok.
-system_monitor_args(doc) ->
- ["Tests arguments to erlang:system_monitor/0-2)"];
+
+%% Tests arguments to erlang:system_monitor/0,1,2
system_monitor_args(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
- ?line Self = self(),
+ Self = self(),
%%
- ?line OldMonitor = erlang:system_monitor(undefined),
- ?line undefined = erlang:system_monitor(Self, [{long_gc,0}]),
- ?line MinT = case erlang:system_monitor() of
- {Self,[{long_gc,T}]} when is_integer(T), T > 0 -> T;
- Other1 -> test_server:fault(Other1)
- end,
- ?line {Self,[{long_gc,MinT}]} = erlang:system_monitor(),
- ?line {Self,[{long_gc,MinT}]} =
- erlang:system_monitor({Self,[{large_heap,0}]}),
- ?line MinN = case erlang:system_monitor() of
- {Self,[{large_heap,N}]} when is_integer(N), N > 0 -> N;
- Other2 -> test_server:fault(Other2)
- end,
- ?line {Self,[{large_heap,MinN}]} = erlang:system_monitor(),
- ?line {Self,[{large_heap,MinN}]} =
- erlang:system_monitor(Self, [busy_port]),
- ?line {Self,[busy_port]} = erlang:system_monitor(),
- ?line {Self,[busy_port]} =
- erlang:system_monitor({Self,[busy_dist_port]}),
- ?line {Self,[busy_dist_port]} = erlang:system_monitor(),
- ?line All = lists:sort([busy_port,busy_dist_port,
- {long_gc,1},{large_heap,65535}]),
- ?line {Self,[busy_dist_port]} = erlang:system_monitor(Self, All),
- ?line {Self,A1} = erlang:system_monitor(),
- ?line All = lists:sort(A1),
- ?line {Self,A1} = erlang:system_monitor(Self, []),
- ?line Pid = spawn(fun () -> receive {Self,die} -> exit(die) end end),
- ?line Mref = erlang:monitor(process, Pid),
- ?line undefined = erlang:system_monitor(Pid, All),
- ?line {Pid,A2} = erlang:system_monitor(),
- ?line All = lists:sort(A2),
- ?line Pid ! {Self,die},
- ?line receive {'DOWN',Mref,_,_,_} -> ok end,
- ?line undefined = erlang:system_monitor(OldMonitor),
- ?line erlang:yield(),
- ?line OldMonitor = erlang:system_monitor(),
+ OldMonitor = erlang:system_monitor(undefined),
+ undefined = erlang:system_monitor(Self, [{long_gc,0}]),
+ MinT = case erlang:system_monitor() of
+ {Self,[{long_gc,T}]} when is_integer(T), T > 0 -> T;
+ Other1 -> test_server:fault(Other1)
+ end,
+ {Self,[{long_gc,MinT}]} = erlang:system_monitor(),
+ {Self,[{long_gc,MinT}]} =
+ erlang:system_monitor({Self,[{large_heap,0}]}),
+ MinN = case erlang:system_monitor() of
+ {Self,[{large_heap,N}]} when is_integer(N), N > 0 -> N;
+ Other2 -> test_server:fault(Other2)
+ end,
+ {Self,[{large_heap,MinN}]} = erlang:system_monitor(),
+ {Self,[{large_heap,MinN}]} =
+ erlang:system_monitor(Self, [busy_port]),
+ {Self,[busy_port]} = erlang:system_monitor(),
+ {Self,[busy_port]} =
+ erlang:system_monitor({Self,[busy_dist_port]}),
+ {Self,[busy_dist_port]} = erlang:system_monitor(),
+ All = lists:sort([busy_port,busy_dist_port,
+ {long_gc,1},{large_heap,65535}]),
+ {Self,[busy_dist_port]} = erlang:system_monitor(Self, All),
+ {Self,A1} = erlang:system_monitor(),
+ All = lists:sort(A1),
+ {Self,A1} = erlang:system_monitor(Self, []),
+ Pid = spawn(fun () -> receive {Self,die} -> exit(die) end end),
+ Mref = erlang:monitor(process, Pid),
+ undefined = erlang:system_monitor(Pid, All),
+ {Pid,A2} = erlang:system_monitor(),
+ All = lists:sort(A2),
+ Pid ! {Self,die},
+ receive {'DOWN',Mref,_,_,_} -> ok end,
+ undefined = erlang:system_monitor(OldMonitor),
+ erlang:yield(),
+ OldMonitor = erlang:system_monitor(),
%%
- ?line {'EXIT',{badarg,_}} = (catch erlang:system_monitor(atom)),
- ?line {'EXIT',{badarg,_}} = (catch erlang:system_monitor({})),
- ?line {'EXIT',{badarg,_}} = (catch erlang:system_monitor({1})),
- ?line {'EXIT',{badarg,_}} = (catch erlang:system_monitor({1,2,3})),
- ?line {'EXIT',{badarg,_}} =
- (catch erlang:system_monitor({Self,atom})),
- ?line {'EXIT',{badarg,_}} =
- (catch erlang:system_monitor(atom, atom)),
- ?line {'EXIT',{badarg,_}} =
- (catch erlang:system_monitor({Self,[busy_port|busy_dist_port]})),
- ?line {'EXIT',{badarg,_}} =
- (catch erlang:system_monitor(Self, [{long_gc,-1}])),
- ?line {'EXIT',{badarg,_}} =
- (catch erlang:system_monitor({Self,[{long_gc,atom}]})),
- ?line {'EXIT',{badarg,_}} =
- (catch erlang:system_monitor(Self,[{large_heap,-1}])),
- ?line {'EXIT',{badarg,_}} =
- (catch erlang:system_monitor({Self,[{large_heap,atom}]})),
- %% Done.
- ?line test_server:timetrap_cancel(Dog),
+ {'EXIT',{badarg,_}} = (catch erlang:system_monitor(atom)),
+ {'EXIT',{badarg,_}} = (catch erlang:system_monitor({})),
+ {'EXIT',{badarg,_}} = (catch erlang:system_monitor({1})),
+ {'EXIT',{badarg,_}} = (catch erlang:system_monitor({1,2,3})),
+ {'EXIT',{badarg,_}} =
+ (catch erlang:system_monitor({Self,atom})),
+ {'EXIT',{badarg,_}} =
+ (catch erlang:system_monitor(atom, atom)),
+ {'EXIT',{badarg,_}} =
+ (catch erlang:system_monitor({Self,[busy_port|busy_dist_port]})),
+ {'EXIT',{badarg,_}} =
+ (catch erlang:system_monitor(Self, [{long_gc,-1}])),
+ {'EXIT',{badarg,_}} =
+ (catch erlang:system_monitor({Self,[{long_gc,atom}]})),
+ {'EXIT',{badarg,_}} =
+ (catch erlang:system_monitor(Self,[{large_heap,-1}])),
+ {'EXIT',{badarg,_}} =
+ (catch erlang:system_monitor({Self,[{large_heap,atom}]})),
ok.
-more_system_monitor_args(doc) ->
- ["Tests arguments to erlang:system_monitor/0-2)"];
+%% Tests arguments to erlang:system_monitor/0,1,2
more_system_monitor_args(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
-
- ?line try_l(64000),
- ?line try_l(16#7ffffff),
- ?line try_l(16#3fffffff),
- ?line try_l(16#7fffffff),
- ?line try_l(16#ffffffff),
-
- %% Done.
- ?line test_server:timetrap_cancel(Dog),
+ try_l(64000),
+ try_l(16#7ffffff),
+ try_l(16#3fffffff),
+ try_l(16#7fffffff),
+ try_l(16#ffffffff),
ok.
try_l(Val) ->
@@ -523,29 +853,29 @@ try_l(Val) ->
Arbitrary1 = 77777,
Arbitrary2 = 88888,
- ?line erlang:system_monitor(undefined),
+ erlang:system_monitor(undefined),
- ?line undefined = erlang:system_monitor(Self, [{long_gc,Val},{large_heap,Arbitrary1}]),
+ undefined = erlang:system_monitor(Self, [{long_gc,Val},{large_heap,Arbitrary1}]),
- ?line {Self,Comb0} = erlang:system_monitor(Self, [{long_gc,Arbitrary2},{large_heap,Val}]),
- ?line [{large_heap,Arbitrary1},{long_gc,Val}] = lists:sort(Comb0),
+ {Self,Comb0} = erlang:system_monitor(Self, [{long_gc,Arbitrary2},{large_heap,Val}]),
+ [{large_heap,Arbitrary1},{long_gc,Val}] = lists:sort(Comb0),
- ?line {Self,Comb1} = erlang:system_monitor(undefined),
- ?line [{large_heap,Val},{long_gc,Arbitrary2}] = lists:sort(Comb1).
+ {Self,Comb1} = erlang:system_monitor(undefined),
+ [{large_heap,Val},{long_gc,Arbitrary2}] = lists:sort(Comb1).
monitor_sys(Parent) ->
receive
- {monitor,Pid,long_schedule,Data} when is_pid(Pid) ->
- io:format("Long schedule of ~w: ~w~n",[Pid,Data]),
- Parent ! {Pid,Data},
- monitor_sys(Parent);
- {monitor,Port,long_schedule,Data} when is_port(Port) ->
- {name,Name} = erlang:port_info(Port,name),
- io:format("Long schedule of ~w (~p): ~w~n",[Port,Name,Data]),
- Parent ! {Port,Data},
- monitor_sys(Parent);
- Other ->
- erlang:display(Other)
+ {monitor,Pid,long_schedule,Data} when is_pid(Pid) ->
+ io:format("Long schedule of ~w: ~w~n",[Pid,Data]),
+ Parent ! {Pid,Data},
+ monitor_sys(Parent);
+ {monitor,Port,long_schedule,Data} when is_port(Port) ->
+ {name,Name} = erlang:port_info(Port,name),
+ io:format("Long schedule of ~w (~p): ~w~n",[Port,Name,Data]),
+ Parent ! {Port,Data},
+ monitor_sys(Parent);
+ Other ->
+ erlang:display(Other)
end.
start_monitor() ->
@@ -555,18 +885,15 @@ start_monitor() ->
erlang:yield(), % Need to be rescheduled for the trace to take
ok.
-system_monitor_long_schedule(suite) ->
- [];
-system_monitor_long_schedule(doc) ->
- ["Tests erlang:system_monitor(Pid, [{long_schedule,Time}])"];
+%% Tests erlang:system_monitor(Pid, [{long_schedule,Time}])
system_monitor_long_schedule(Config) when is_list(Config) ->
- Path = ?config(data_dir, Config),
+ Path = proplists:get_value(data_dir, Config),
erl_ddll:start(),
case (catch load_driver(Path, slow_drv)) of
- ok ->
- do_system_monitor_long_schedule();
- _Error ->
- {skip, "Unable to load slow_drv (windows or no usleep()?)"}
+ ok ->
+ do_system_monitor_long_schedule();
+ _Error ->
+ {skip, "Unable to load slow_drv (windows or no usleep()?)"}
end.
do_system_monitor_long_schedule() ->
start_monitor(),
@@ -574,18 +901,18 @@ do_system_monitor_long_schedule() ->
"ok" = erlang:port_control(Port,0,[]),
Self = self(),
receive
- {Self,L} when is_list(L) ->
- ok
+ {Self,L} when is_list(L) ->
+ ok
after 1000 ->
- ?t:fail(no_trace_of_pid)
+ ct:fail(no_trace_of_pid)
end,
"ok" = erlang:port_control(Port,1,[]),
"ok" = erlang:port_control(Port,2,[]),
receive
- {Port,LL} when is_list(LL) ->
- ok
+ {Port,LL} when is_list(LL) ->
+ ok
after 1000 ->
- ?t:fail(no_trace_of_port)
+ ct:fail(no_trace_of_port)
end,
port_close(Port),
erlang:system_monitor(undefined),
@@ -594,214 +921,200 @@ do_system_monitor_long_schedule() ->
-define(LONG_GC_SLEEP, 670).
-system_monitor_long_gc_1(suite) ->
- [];
-system_monitor_long_gc_1(doc) ->
- ["Tests erlang:system_monitor(Pid, [{long_gc,Time}])"];
+%% Tests erlang:system_monitor(Pid, [{long_gc,Time}])
system_monitor_long_gc_1(Config) when is_list(Config) ->
erts_debug:set_internal_state(available_internal_state, true),
try
- case erts_debug:get_internal_state(force_heap_frags) of
- true ->
- {skip,"emulator with FORCE_HEAP_FRAGS defined"};
- false ->
- %% Add ?LONG_GC_SLEEP ms to all gc
- ?line erts_debug:set_internal_state(test_long_gc_sleep,
- ?LONG_GC_SLEEP),
- ?line LoadFun = fun () ->
- garbage_collect(),
- self()
- end,
- ?line long_gc(LoadFun, false)
- end
+ case erts_debug:get_internal_state(force_heap_frags) of
+ true ->
+ {skip,"emulator with FORCE_HEAP_FRAGS defined"};
+ false ->
+ %% Add ?LONG_GC_SLEEP ms to all gc
+ erts_debug:set_internal_state(test_long_gc_sleep,
+ ?LONG_GC_SLEEP),
+ LoadFun = fun () ->
+ garbage_collect(),
+ self()
+ end,
+ long_gc(LoadFun, false)
+ end
after
- erts_debug:set_internal_state(test_long_gc_sleep, 0),
- erts_debug:set_internal_state(available_internal_state, false)
+ erts_debug:set_internal_state(test_long_gc_sleep, 0),
+ erts_debug:set_internal_state(available_internal_state, false)
end.
-system_monitor_long_gc_2(suite) ->
- [];
-system_monitor_long_gc_2(doc) ->
- ["Tests erlang:system_monitor(Pid, [{long_gc,Time}])"];
+%% Tests erlang:system_monitor(Pid, [{long_gc,Time}])
system_monitor_long_gc_2(Config) when is_list(Config) ->
erts_debug:set_internal_state(available_internal_state, true),
try
- case erts_debug:get_internal_state(force_heap_frags) of
- true ->
- {skip,"emulator with FORCE_HEAP_FRAGS defined"};
- false ->
- %% Add ?LONG_GC_SLEEP ms to all gc
- ?line erts_debug:set_internal_state(test_long_gc_sleep,
- ?LONG_GC_SLEEP),
- ?line Parent = self(),
- ?line LoadFun =
- fun () ->
- Ref = make_ref(),
- Pid =
- spawn_link(
- fun () ->
- garbage_collect(),
- Parent ! {Ref, self()}
- end),
- receive {Ref, Pid} -> Pid end
- end,
- ?line long_gc(LoadFun, true),
- ?line long_gc(LoadFun, true),
- ?line long_gc(LoadFun, true)
- end
+ case erts_debug:get_internal_state(force_heap_frags) of
+ true ->
+ {skip,"emulator with FORCE_HEAP_FRAGS defined"};
+ false ->
+ %% Add ?LONG_GC_SLEEP ms to all gc
+ erts_debug:set_internal_state(test_long_gc_sleep,
+ ?LONG_GC_SLEEP),
+ Parent = self(),
+ LoadFun =
+ fun () ->
+ Ref = make_ref(),
+ Pid =
+ spawn_link(
+ fun () ->
+ garbage_collect(),
+ Parent ! {Ref, self()}
+ end),
+ receive {Ref, Pid} -> Pid end
+ end,
+ long_gc(LoadFun, true),
+ long_gc(LoadFun, true),
+ long_gc(LoadFun, true)
+ end
after
- erts_debug:set_internal_state(test_long_gc_sleep, 0),
- erts_debug:set_internal_state(available_internal_state, false)
+ erts_debug:set_internal_state(test_long_gc_sleep, 0),
+ erts_debug:set_internal_state(available_internal_state, false)
end.
long_gc(LoadFun, ExpectMonMsg) ->
- ?line Self = self(),
- ?line Time = 1,
- ?line OldMonitor = erlang:system_monitor(Self, [{long_gc,Time}]),
- ?line Pid = LoadFun(),
- ?line Ref = erlang:trace_delivered(Pid),
- ?line receive {trace_delivered, Pid, Ref} -> ok end,
- ?line {Self,[{long_gc,Time}]} = erlang:system_monitor(OldMonitor),
- ?line case {long_gc_check(Pid, Time, undefined), ExpectMonMsg} of
- {ok, true} when Pid =/= Self ->
- ok;
- {ok, false} ->
- ?line ?t:fail(unexpected_system_monitor_message_received);
- {undefined, false} ->
- ok;
- {undefined, true} ->
- ?line ?t:fail(no_system_monitor_message_received)
- end.
+ Self = self(),
+ Time = 1,
+ OldMonitor = erlang:system_monitor(Self, [{long_gc,Time}]),
+ Pid = LoadFun(),
+ Ref = erlang:trace_delivered(Pid),
+ receive {trace_delivered, Pid, Ref} -> ok end,
+ {Self,[{long_gc,Time}]} = erlang:system_monitor(OldMonitor),
+ case {long_gc_check(Pid, Time, undefined), ExpectMonMsg} of
+ {ok, true} when Pid =/= Self ->
+ ok;
+ {ok, false} ->
+ ct:fail(unexpected_system_monitor_message_received);
+ {undefined, false} ->
+ ok;
+ {undefined, true} ->
+ ct:fail(no_system_monitor_message_received)
+ end.
long_gc_check(Pid, Time, Result) ->
receive
- {monitor,Pid,long_gc,L} = Monitor ->
- case lists:foldl(
- fun (_, error) ->
- error;
- ({timeout,T}, N) when is_integer(T),
- Time =< T, T =< 10*?LONG_GC_SLEEP ->
- %% OTP-7622. The time T must be within reasonable limits
- %% for the test to pass.
- N-1;
- ({heap_size,_}, N) ->
- N-1;
- ({old_heap_size,_}, N) ->
- N-1;
- ({stack_size,_}, N) ->
- N-1;
- ({mbuf_size,_}, N) ->
- N-1;
- ({heap_block_size,_}, N) ->
- N-1;
- ({old_heap_block_size,_}, N) ->
- N-1;
- (_, _) ->
- error
- end, 7, L) of
- 0 ->
- long_gc_check(Pid, Time, ok);
- error ->
- {error,Monitor}
- end;
- {monitor,_,long_gc,_} ->
- long_gc_check(Pid, Time, Result);
- Other ->
- {error,Other}
+ {monitor,Pid,long_gc,L} = Monitor ->
+ case lists:foldl(
+ fun (_, error) ->
+ error;
+ ({timeout,T}, N) when is_integer(T),
+ Time =< T, T =< 10*?LONG_GC_SLEEP ->
+ %% OTP-7622. The time T must be within reasonable limits
+ %% for the test to pass.
+ N-1;
+ ({heap_size,_}, N) ->
+ N-1;
+ ({old_heap_size,_}, N) ->
+ N-1;
+ ({stack_size,_}, N) ->
+ N-1;
+ ({mbuf_size,_}, N) ->
+ N-1;
+ ({heap_block_size,_}, N) ->
+ N-1;
+ ({old_heap_block_size,_}, N) ->
+ N-1;
+ (_, _) ->
+ error
+ end, 7, L) of
+ 0 ->
+ long_gc_check(Pid, Time, ok);
+ error ->
+ {error,Monitor}
+ end;
+ {monitor,_,long_gc,_} ->
+ long_gc_check(Pid, Time, Result);
+ Other ->
+ {error,Other}
after 0 ->
- Result
+ Result
end.
-system_monitor_large_heap_1(suite) ->
- [];
-system_monitor_large_heap_1(doc) ->
- ["Tests erlang:system_monitor(Pid, [{large_heap,Size}])"];
+%% Tests erlang:system_monitor(Pid, [{large_heap,Size}])
system_monitor_large_heap_1(Config) when is_list(Config) ->
- ?line LoadFun =
- fun (Size) ->
- List = seq(1,2*Size),
- garbage_collect(),
- true = lists:prefix([1], List),
- self()
- end,
- ?line large_heap(LoadFun, false).
-
-system_monitor_large_heap_2(suite) ->
- [];
-system_monitor_large_heap_2(doc) ->
- ["Tests erlang:system_monitor(Pid, [{large_heap,Size}])"];
+ LoadFun =
+ fun (Size) ->
+ List = seq(1,2*Size),
+ garbage_collect(),
+ true = lists:prefix([1], List),
+ self()
+ end,
+ large_heap(LoadFun, false).
+
+%% Tests erlang:system_monitor(Pid, [{large_heap,Size}])
system_monitor_large_heap_2(Config) when is_list(Config) ->
- ?line Parent = self(),
- ?line LoadFun =
- fun (Size) ->
- Ref = make_ref(),
- Pid =
- spawn_opt(fun () ->
- garbage_collect(),
- Parent ! {Ref, self()}
- end,
- [link, {min_heap_size, 2*Size}]),
- receive {Ref, Pid} -> Pid end
- end,
- ?line large_heap(LoadFun, true).
+ Parent = self(),
+ LoadFun =
+ fun (Size) ->
+ Ref = make_ref(),
+ Pid =
+ spawn_opt(fun () ->
+ garbage_collect(),
+ Parent ! {Ref, self()}
+ end,
+ [link, {min_heap_size, 2*Size}]),
+ receive {Ref, Pid} -> Pid end
+ end,
+ large_heap(LoadFun, true).
large_heap(LoadFun, ExpectMonMsg) ->
- ?line Dog = test_server:timetrap(test_server:seconds(20)),
+ ct:timetrap({seconds, 20}),
%%
- ?line Size = 65535,
- ?line Self = self(),
- ?line NewMonitor = {Self,[{large_heap,Size}]},
- ?line OldMonitor = erlang:system_monitor(NewMonitor),
- ?line Pid = LoadFun(Size),
- ?line Ref = erlang:trace_delivered(Pid),
- ?line receive {trace_delivered, Pid, Ref} -> ok end,
- ?line {Self,[{large_heap,Size}]} = erlang:system_monitor(OldMonitor),
- ?line case {large_heap_check(Pid, Size, undefined), ExpectMonMsg} of
- {ok, true} when Pid =/= Self ->
- ?line ok;
- {ok, false} ->
- ?line ?t:fail(unexpected_system_monitor_message_received);
- {undefined, false} ->
- ?line ok;
- {undefined, true} ->
- ?line ?t:fail(no_system_monitor_message_received)
- end,
- %%
- ?line test_server:timetrap_cancel(Dog),
+ Size = 65535,
+ Self = self(),
+ NewMonitor = {Self,[{large_heap,Size}]},
+ OldMonitor = erlang:system_monitor(NewMonitor),
+ Pid = LoadFun(Size),
+ Ref = erlang:trace_delivered(Pid),
+ receive {trace_delivered, Pid, Ref} -> ok end,
+ {Self,[{large_heap,Size}]} = erlang:system_monitor(OldMonitor),
+ case {large_heap_check(Pid, Size, undefined), ExpectMonMsg} of
+ {ok, true} when Pid =/= Self ->
+ ok;
+ {ok, false} ->
+ ct:fail(unexpected_system_monitor_message_received);
+ {undefined, false} ->
+ ok;
+ {undefined, true} ->
+ ct:fail(no_system_monitor_message_received)
+ end,
ok.
large_heap_check(Pid, Size, Result) ->
receive
- {monitor,Pid,large_heap,L} = Monitor ->
- case lists:foldl(
- fun (_, error) ->
- error;
- ({heap_size,_}, N) ->
- N-1;
- ({old_heap_size,_}, N) ->
- N-1;
- ({stack_size,_}, N) ->
- N-1;
- ({mbuf_size,_}, N) ->
- N-1;
- ({heap_block_size,_}, N) ->
- N-1;
- ({old_heap_block_size,_}, N) ->
- N-1;
- (_, _) ->
- error
- end, 6, L) of
- 0 ->
- large_heap_check(Pid, Size, ok);
- error ->
- {error,Monitor}
- end;
- {monitor,_,large_heap,_} ->
- large_heap_check(Pid, Size, Result);
- Other ->
- {error,Other}
+ {monitor,Pid,large_heap,L} = Monitor ->
+ case lists:foldl(
+ fun (_, error) ->
+ error;
+ ({heap_size,_}, N) ->
+ N-1;
+ ({old_heap_size,_}, N) ->
+ N-1;
+ ({stack_size,_}, N) ->
+ N-1;
+ ({mbuf_size,_}, N) ->
+ N-1;
+ ({heap_block_size,_}, N) ->
+ N-1;
+ ({old_heap_block_size,_}, N) ->
+ N-1;
+ (_, _) ->
+ error
+ end, 6, L) of
+ 0 ->
+ large_heap_check(Pid, Size, ok);
+ error ->
+ {error,Monitor}
+ end;
+ {monitor,_,large_heap,_} ->
+ large_heap_check(Pid, Size, Result);
+ Other ->
+ {error,Other}
after 0 ->
- Result
+ Result
end.
seq(N, M) ->
@@ -816,11 +1129,11 @@ seq(N, M, R) ->
is_send_traced(Pid, Listener, Msg) ->
Pid ! {send_please, Listener, Msg},
receive
- Any ->
- {trace, Pid, send, Msg, Listener} = Any,
- true
+ Any ->
+ {trace, Pid, send, Msg, Listener} = Any,
+ true
after 1000 ->
- false
+ false
end.
%% This procedure assumes that the Parent process is send traced.
@@ -834,146 +1147,131 @@ spawn_children(Parent, Number, Result) ->
Self = self(),
Parent ! {spawn_please, Self, fun process/0},
Child =
- receive
- {trace, Parent, send, {spawned, Pid}, Self} -> Pid
- end,
receive
- {spawned, Child} ->
- spawn_children(Parent, Number-1, [Child|Result])
+ {trace, Parent, send, {spawned, Pid}, Self} -> Pid
+ end,
+ receive
+ {spawned, Child} ->
+ spawn_children(Parent, Number-1, [Child|Result])
end.
-suspend(doc) -> "Test erlang:suspend/1 and erlang:resume/1.";
+%% Test erlang:suspend/1 and erlang:resume/1.
suspend(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:minutes(2)),
-
- ?line Worker = fun_spawn(fun worker/0),
+ ct:timetrap({minutes,2}),
+ Worker = fun_spawn(fun worker/0),
%% Suspend a process and test that it is suspended.
- ?line ok = do_suspend(Worker, 10000),
-
- %% Done.
- ?line test_server:timetrap_cancel(Dog),
+ ok = do_suspend(Worker, 10000),
ok.
do_suspend(_Pid, 0) ->
- ?line ok;
+ ok;
do_suspend(Pid, N) ->
%% Suspend a process and test that it is suspended.
- ?line true = erlang:suspend_process(Pid),
- ?line {status, suspended} = process_info(Pid, status),
+ true = erlang:suspend_process(Pid),
+ {status, suspended} = process_info(Pid, status),
%% Unsuspend the process and make sure it starts working.
- ?line true = erlang:resume_process(Pid),
- ?line case process_info(Pid, status) of
- {status, runnable} -> ?line ok;
- {status, running} -> ?line ok;
- {status, garbage_collecting} -> ?line ok;
- ST -> ?line ?t:fail(ST)
- end,
- ?line erlang:yield(),
- ?line do_suspend(Pid, N-1).
-
-
-
-mutual_suspend(doc) ->
- [];
-mutual_suspend(suite) ->
- [];
+ true = erlang:resume_process(Pid),
+ case process_info(Pid, status) of
+ {status, runnable} -> ok;
+ {status, running} -> ok;
+ {status, garbage_collecting} -> ok;
+ ST -> ct:fail(ST)
+ end,
+ erlang:yield(),
+ do_suspend(Pid, N-1).
+
+
+
mutual_suspend(Config) when is_list(Config) ->
- ?line TimeoutSecs = 5*60,
- ?line Dog = test_server:timetrap(test_server:minutes(TimeoutSecs)),
- ?line Parent = self(),
- ?line Fun = fun () ->
- receive
- {go, Pid} ->
- do_mutual_suspend(Pid, 100000)
- end,
- Parent ! {done, self()},
- receive after infinity -> ok end
- end,
- ?line P1 = spawn_link(Fun),
- ?line P2 = spawn_link(Fun),
- ?line T1 = erlang:start_timer((TimeoutSecs - 5)*1000, self(), oops),
- ?line T2 = erlang:start_timer((TimeoutSecs - 5)*1000, self(), oops),
- ?line P1 ! {go, P2},
- ?line P2 ! {go, P1},
- ?line Res1 = receive
- {done, P1} -> done;
- {timeout,T1,_} -> timeout
- end,
- ?line Res2 = receive
- {done, P2} -> done;
- {timeout,T2,_} -> timeout
- end,
- ?line P1S = process_info(P1, status),
- ?line P2S = process_info(P2, status),
- ?line ?t:format("P1S=~p P2S=~p", [P1S, P2S]),
- ?line false = {status, suspended} == P1S,
- ?line false = {status, suspended} == P2S,
- ?line unlink(P1), exit(P1, bang),
- ?line unlink(P2), exit(P2, bang),
- ?line done = Res1,
- ?line done = Res2,
- %% Done.
- ?line test_server:timetrap_cancel(Dog),
- ?line ok.
-
+ TimeoutSecs = 5*60,
+ ct:timetrap({seconds, TimeoutSecs}),
+ Parent = self(),
+ Fun = fun () ->
+ receive
+ {go, Pid} ->
+ do_mutual_suspend(Pid, 100000)
+ end,
+ Parent ! {done, self()},
+ receive after infinity -> ok end
+ end,
+ P1 = spawn_link(Fun),
+ P2 = spawn_link(Fun),
+ T1 = erlang:start_timer((TimeoutSecs - 5)*1000, self(), oops),
+ T2 = erlang:start_timer((TimeoutSecs - 5)*1000, self(), oops),
+ P1 ! {go, P2},
+ P2 ! {go, P1},
+ Res1 = receive
+ {done, P1} -> done;
+ {timeout,T1,_} -> timeout
+ end,
+ Res2 = receive
+ {done, P2} -> done;
+ {timeout,T2,_} -> timeout
+ end,
+ P1S = process_info(P1, status),
+ P2S = process_info(P2, status),
+ io:format("P1S=~p P2S=~p", [P1S, P2S]),
+ false = {status, suspended} == P1S,
+ false = {status, suspended} == P2S,
+ unlink(P1), exit(P1, bang),
+ unlink(P2), exit(P2, bang),
+ done = Res1,
+ done = Res2,
+ ok.
+
do_mutual_suspend(_Pid, 0) ->
- ?line ok;
+ ok;
do_mutual_suspend(Pid, N) ->
%% Suspend a process and test that it is suspended.
- ?line true = erlang:suspend_process(Pid),
- ?line {status, suspended} = process_info(Pid, status),
+ true = erlang:suspend_process(Pid),
+ {status, suspended} = process_info(Pid, status),
%% Unsuspend the process.
- ?line true = erlang:resume_process(Pid),
- ?line do_mutual_suspend(Pid, N-1).
+ true = erlang:resume_process(Pid),
+ do_mutual_suspend(Pid, N-1).
-suspend_exit(doc) ->
- [];
-suspend_exit(suite) ->
- [];
suspend_exit(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:minutes(2)),
- ?line random:seed(4711,17,4711),
- ?line do_suspend_exit(5000),
- ?line test_server:timetrap_cancel(Dog),
- ?line ok.
+ ct:timetrap({minutes, 2}),
+ rand:seed(exsplus, {4711,17,4711}),
+ do_suspend_exit(5000),
+ ok.
do_suspend_exit(0) ->
- ?line ok;
+ ok;
do_suspend_exit(N) ->
- ?line Work = random:uniform(50),
- ?line Parent = self(),
- ?line {Suspendee, Mon2}
- = spawn_monitor(fun () ->
- suspend_exit_work(Work),
- exit(normal)
- end),
- ?line {Suspender, Mon1}
- = spawn_monitor(
- fun () ->
- suspend_exit_work(Work div 2),
- Parent ! {doing_suspend, self()},
- case catch erlang:suspend_process(Suspendee) of
- {'EXIT', _} ->
- ok;
- true ->
- ?line erlang:resume_process(Suspendee)
- end
- end),
- ?line receive
- {doing_suspend, Suspender} ->
- case N rem 2 of
- 0 -> exit(Suspender, bang);
- 1 -> ok
- end
- end,
- ?line receive {'DOWN', Mon1, process, Suspender, _} -> ok end,
- ?line receive {'DOWN', Mon2, process, Suspendee, _} -> ok end,
- ?line do_suspend_exit(N-1).
-
-
-
-
+ Work = rand:uniform(50),
+ Parent = self(),
+ {Suspendee, Mon2}
+ = spawn_monitor(fun () ->
+ suspend_exit_work(Work),
+ exit(normal)
+ end),
+ {Suspender, Mon1}
+ = spawn_monitor(
+ fun () ->
+ suspend_exit_work(Work div 2),
+ Parent ! {doing_suspend, self()},
+ case catch erlang:suspend_process(Suspendee) of
+ {'EXIT', _} ->
+ ok;
+ true ->
+ erlang:resume_process(Suspendee)
+ end
+ end),
+ receive
+ {doing_suspend, Suspender} ->
+ case N rem 2 of
+ 0 -> exit(Suspender, bang);
+ 1 -> ok
+ end
+ end,
+ receive {'DOWN', Mon1, process, Suspender, _} -> ok end,
+ receive {'DOWN', Mon2, process, Suspendee, _} -> ok end,
+ do_suspend_exit(N-1).
+
+
+
+
suspend_exit_work(0) ->
ok;
suspend_exit_work(N) ->
@@ -985,320 +1283,305 @@ suspend_exit_work(N) ->
chk_suspended(P, Bool, Line) ->
{Bool, Line} = {({status, suspended} == process_info(P, status)), Line}.
-suspender_exit(doc) ->
- [];
-suspender_exit(suite) ->
- [];
suspender_exit(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:minutes(3)),
- ?line P1 = spawn_link(fun () -> receive after infinity -> ok end end),
- ?line {'EXIT', _} = (catch erlang:resume_process(P1)),
- ?line {P2, M2} = spawn_monitor(
- fun () ->
- ?CHK_SUSPENDED(P1, false),
- erlang:suspend_process(P1),
- ?CHK_SUSPENDED(P1, true),
- erlang:suspend_process(P1),
- erlang:suspend_process(P1),
- erlang:suspend_process(P1),
- ?CHK_SUSPENDED(P1, true),
- erlang:resume_process(P1),
- erlang:resume_process(P1),
- erlang:resume_process(P1),
- ?CHK_SUSPENDED(P1, true),
- erlang:resume_process(P1),
- ?CHK_SUSPENDED(P1, false),
- erlang:suspend_process(P1),
- erlang:suspend_process(P1),
- erlang:suspend_process(P1),
- ?CHK_SUSPENDED(P1, true),
- exit(bang)
- end),
- ?line receive
- {'DOWN', M2,process,P2,R2} ->
- ?line bang = R2,
- ?line ?CHK_SUSPENDED(P1, false)
- end,
- ?line Parent = self(),
- ?line {P3, M3} = spawn_monitor(
- fun () ->
- erlang:suspend_process(P1),
- ?CHK_SUSPENDED(P1, true),
- Parent ! self(),
- receive after infinity -> ok end
- end),
- ?line {P4, M4} = spawn_monitor(
- fun () ->
- erlang:suspend_process(P1),
- ?CHK_SUSPENDED(P1, true),
- Parent ! self(),
- receive after infinity -> ok end
- end),
- ?line {P5, M5} = spawn_monitor(
- fun () ->
- erlang:suspend_process(P1),
- ?CHK_SUSPENDED(P1, true),
- Parent ! self(),
- receive after infinity -> ok end
- end),
- ?line {P6, M6} = spawn_monitor(
- fun () ->
- erlang:suspend_process(P1),
- ?CHK_SUSPENDED(P1, true),
- Parent ! self(),
- receive after infinity -> ok end
- end),
- ?line {P7, M7} = spawn_monitor(
- fun () ->
- erlang:suspend_process(P1),
- ?CHK_SUSPENDED(P1, true),
- Parent ! self(),
- receive after infinity -> ok end
- end),
- ?line receive P3 -> ok end,
- ?line receive P4 -> ok end,
- ?line receive P5 -> ok end,
- ?line receive P6 -> ok end,
- ?line receive P7 -> ok end,
- ?line ?CHK_SUSPENDED(P1, true),
- ?line exit(P3, bang),
- ?line receive
- {'DOWN',M3,process,P3,R3} ->
- ?line bang = R3,
- ?line ?CHK_SUSPENDED(P1, true)
- end,
- ?line exit(P4, bang),
- ?line receive
- {'DOWN',M4,process,P4,R4} ->
- ?line bang = R4,
- ?line ?CHK_SUSPENDED(P1, true)
- end,
- ?line exit(P5, bang),
- ?line receive
- {'DOWN',M5,process,P5,R5} ->
- ?line bang = R5,
- ?line ?CHK_SUSPENDED(P1, true)
- end,
- ?line exit(P6, bang),
- ?line receive
- {'DOWN',M6,process,P6,R6} ->
- ?line bang = R6,
- ?line ?CHK_SUSPENDED(P1, true)
- end,
- ?line exit(P7, bang),
- ?line receive
- {'DOWN',M7,process,P7,R7} ->
- ?line bang = R7,
- ?line ?CHK_SUSPENDED(P1, false)
- end,
- ?line unlink(P1),
- ?line exit(P1, bong),
- ?line test_server:timetrap_cancel(Dog),
- ?line ok.
-
-suspend_system_limit(doc) ->
- [];
-suspend_system_limit(suite) ->
- [];
+ ct:timetrap({minutes, 3}),
+ P1 = spawn_link(fun () -> receive after infinity -> ok end end),
+ {'EXIT', _} = (catch erlang:resume_process(P1)),
+ {P2, M2} = spawn_monitor(
+ fun () ->
+ ?CHK_SUSPENDED(P1, false),
+ erlang:suspend_process(P1),
+ ?CHK_SUSPENDED(P1, true),
+ erlang:suspend_process(P1),
+ erlang:suspend_process(P1),
+ erlang:suspend_process(P1),
+ ?CHK_SUSPENDED(P1, true),
+ erlang:resume_process(P1),
+ erlang:resume_process(P1),
+ erlang:resume_process(P1),
+ ?CHK_SUSPENDED(P1, true),
+ erlang:resume_process(P1),
+ ?CHK_SUSPENDED(P1, false),
+ erlang:suspend_process(P1),
+ erlang:suspend_process(P1),
+ erlang:suspend_process(P1),
+ ?CHK_SUSPENDED(P1, true),
+ exit(bang)
+ end),
+ receive
+ {'DOWN', M2,process,P2,R2} ->
+ bang = R2,
+ ?CHK_SUSPENDED(P1, false)
+ end,
+ Parent = self(),
+ {P3, M3} = spawn_monitor(
+ fun () ->
+ erlang:suspend_process(P1),
+ ?CHK_SUSPENDED(P1, true),
+ Parent ! self(),
+ receive after infinity -> ok end
+ end),
+ {P4, M4} = spawn_monitor(
+ fun () ->
+ erlang:suspend_process(P1),
+ ?CHK_SUSPENDED(P1, true),
+ Parent ! self(),
+ receive after infinity -> ok end
+ end),
+ {P5, M5} = spawn_monitor(
+ fun () ->
+ erlang:suspend_process(P1),
+ ?CHK_SUSPENDED(P1, true),
+ Parent ! self(),
+ receive after infinity -> ok end
+ end),
+ {P6, M6} = spawn_monitor(
+ fun () ->
+ erlang:suspend_process(P1),
+ ?CHK_SUSPENDED(P1, true),
+ Parent ! self(),
+ receive after infinity -> ok end
+ end),
+ {P7, M7} = spawn_monitor(
+ fun () ->
+ erlang:suspend_process(P1),
+ ?CHK_SUSPENDED(P1, true),
+ Parent ! self(),
+ receive after infinity -> ok end
+ end),
+ receive P3 -> ok end,
+ receive P4 -> ok end,
+ receive P5 -> ok end,
+ receive P6 -> ok end,
+ receive P7 -> ok end,
+ ?CHK_SUSPENDED(P1, true),
+ exit(P3, bang),
+ receive
+ {'DOWN',M3,process,P3,R3} ->
+ bang = R3,
+ ?CHK_SUSPENDED(P1, true)
+ end,
+ exit(P4, bang),
+ receive
+ {'DOWN',M4,process,P4,R4} ->
+ bang = R4,
+ ?CHK_SUSPENDED(P1, true)
+ end,
+ exit(P5, bang),
+ receive
+ {'DOWN',M5,process,P5,R5} ->
+ bang = R5,
+ ?CHK_SUSPENDED(P1, true)
+ end,
+ exit(P6, bang),
+ receive
+ {'DOWN',M6,process,P6,R6} ->
+ bang = R6,
+ ?CHK_SUSPENDED(P1, true)
+ end,
+ exit(P7, bang),
+ receive
+ {'DOWN',M7,process,P7,R7} ->
+ bang = R7,
+ ?CHK_SUSPENDED(P1, false)
+ end,
+ unlink(P1),
+ exit(P1, bong),
+ ok.
+
suspend_system_limit(Config) when is_list(Config) ->
case os:getenv("ERL_EXTREME_TESTING") of
- "true" ->
- ?line Dog = test_server:timetrap(test_server:minutes(3*60)),
- ?line P = spawn_link(fun () -> receive after infinity -> ok end end),
- ?line suspend_until_system_limit(P),
- ?line unlink(P),
- ?line exit(P, bye),
- ?line test_server:timetrap_cancel(Dog),
- ?line ok;
- _ ->
- {skip, "Takes too long time for normal testing"}
+ "true" ->
+ ct:timetrap({minutes, 3*60}),
+ P = spawn_link(fun () -> receive after infinity -> ok end end),
+ suspend_until_system_limit(P),
+ unlink(P),
+ exit(P, bye),
+ ok;
+ _ ->
+ {skip, "Takes too long time for normal testing"}
end.
suspend_until_system_limit(P) ->
- ?line suspend_until_system_limit(P, 0, 0).
+ suspend_until_system_limit(P, 0, 0).
suspend_until_system_limit(P, N, M) ->
NewM = case M of
- 1 ->
- ?line ?CHK_SUSPENDED(P, true), 2;
- 1000000 ->
- erlang:display(N), 1;
- _ ->
- M+1
- end,
- ?line case catch erlang:suspend_process(P) of
- true ->
- suspend_until_system_limit(P, N+1, NewM);
- {'EXIT', R} when R == system_limit;
- element(1, R) == system_limit ->
- ?line ?t:format("system limit at ~p~n", [N]),
- ?line resume_from_system_limit(P, N, 0);
- Error ->
- ?line ?t:fail(Error)
- end.
+ 1 ->
+ ?CHK_SUSPENDED(P, true), 2;
+ 1000000 ->
+ erlang:display(N), 1;
+ _ ->
+ M+1
+ end,
+ case catch erlang:suspend_process(P) of
+ true ->
+ suspend_until_system_limit(P, N+1, NewM);
+ {'EXIT', R} when R == system_limit;
+ element(1, R) == system_limit ->
+ io:format("system limit at ~p~n", [N]),
+ resume_from_system_limit(P, N, 0);
+ Error ->
+ ct:fail(Error)
+ end.
resume_from_system_limit(P, 0, _) ->
- ?line ?CHK_SUSPENDED(P, false),
- ?line {'EXIT', _} = (catch erlang:resume_process(P)),
- ?line ok;
+ ?CHK_SUSPENDED(P, false),
+ {'EXIT', _} = (catch erlang:resume_process(P)),
+ ok;
resume_from_system_limit(P, N, M) ->
- ?line NewM = case M of
- 1 ->
- ?line ?CHK_SUSPENDED(P, true), 2;
- 1000000 ->
- erlang:display(N), 1;
- _ ->
- M+1
- end,
- ?line erlang:resume_process(P),
- ?line resume_from_system_limit(P, N-1, NewM).
+ NewM = case M of
+ 1 ->
+ ?CHK_SUSPENDED(P, true), 2;
+ 1000000 ->
+ erlang:display(N), 1;
+ _ ->
+ M+1
+ end,
+ erlang:resume_process(P),
+ resume_from_system_limit(P, N-1, NewM).
-record(susp_info, {async = 0,
- dbl_async = 0,
- synced = 0,
- async_once = 0}).
-
-suspend_opts(doc) ->
- [];
-suspend_opts(suite) ->
- [];
+ dbl_async = 0,
+ synced = 0,
+ async_once = 0}).
+
suspend_opts(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:minutes(3)),
- ?line Self = self(),
- ?line wait_for_empty_runq(10),
- ?line Tok = spawn_link(fun () ->
- Self ! self(),
- tok_trace_loop(Self, 0, 1000000000)
- end),
- ?line TC = 1000,
- ?line receive Tok -> ok end,
- ?line SF = fun (N, #susp_info {async = A,
- dbl_async = AA,
- synced = S,
- async_once = AO} = Acc) ->
- ?line erlang:suspend_process(Tok, [asynchronous]),
- ?line Res = case {suspend_count(Tok), N rem 4} of
- {0, 2} ->
- ?line erlang:suspend_process(Tok,
- [asynchronous]),
- case suspend_count(Tok) of
- 2 ->
- ?line erlang:resume_process(Tok),
- ?line Acc#susp_info{async = A+1};
- 0 ->
- ?line erlang:resume_process(Tok),
- ?line Acc#susp_info{async = A+1,
- dbl_async = AA+1}
- end;
- {0, 1} ->
- ?line erlang:suspend_process(Tok,
- [asynchronous,
- unless_suspending]),
- case suspend_count(Tok) of
- 1 ->
- ?line Acc#susp_info{async = A+1};
- 0 ->
- ?line Acc#susp_info{async = A+1,
- async_once = AO+1}
- end;
- {0, 0} ->
- ?line erlang:suspend_process(Tok,
- [unless_suspending]),
- ?line 1 = suspend_count(Tok),
- ?line Acc#susp_info{async = A+1,
- synced = S+1};
- {0, _} ->
- ?line Acc#susp_info{async = A+1};
- _ ->
- Acc
- end,
- ?line erlang:resume_process(Tok),
- ?line erlang:yield(),
- ?line Res
- end,
- ?line SI = repeat_acc(SF, TC, #susp_info{}),
- ?line erlang:suspend_process(Tok, [asynchronous]),
+ ct:timetrap({minutes, 3}),
+ Self = self(),
+ wait_for_empty_runq(10),
+ Tok = spawn_link(fun () ->
+ Self ! self(),
+ tok_trace_loop(Self, 0, 1000000000)
+ end),
+ TC = 1000,
+ receive Tok -> ok end,
+ SF = fun (N, #susp_info {async = A,
+ dbl_async = AA,
+ synced = S,
+ async_once = AO} = Acc) ->
+ erlang:suspend_process(Tok, [asynchronous]),
+ Res = case {suspend_count(Tok), N rem 4} of
+ {0, 2} ->
+ erlang:suspend_process(Tok,
+ [asynchronous]),
+ case suspend_count(Tok) of
+ 2 ->
+ erlang:resume_process(Tok),
+ Acc#susp_info{async = A+1};
+ 0 ->
+ erlang:resume_process(Tok),
+ Acc#susp_info{async = A+1,
+ dbl_async = AA+1}
+ end;
+ {0, 1} ->
+ erlang:suspend_process(Tok,
+ [asynchronous,
+ unless_suspending]),
+ case suspend_count(Tok) of
+ 1 ->
+ Acc#susp_info{async = A+1};
+ 0 ->
+ Acc#susp_info{async = A+1,
+ async_once = AO+1}
+ end;
+ {0, 0} ->
+ erlang:suspend_process(Tok,
+ [unless_suspending]),
+ 1 = suspend_count(Tok),
+ Acc#susp_info{async = A+1,
+ synced = S+1};
+ {0, _} ->
+ Acc#susp_info{async = A+1};
+ _ ->
+ Acc
+ end,
+ erlang:resume_process(Tok),
+ erlang:yield(),
+ Res
+ end,
+ SI = repeat_acc(SF, TC, #susp_info{}),
+ erlang:suspend_process(Tok, [asynchronous]),
%% Verify that it eventually suspends
- ?line WaitTime0 = 10,
- ?line WaitTime1 = case {erlang:system_info(debug_compiled),
- erlang:system_info(lock_checking)} of
- {false, false} ->
- WaitTime0;
- {false, true} ->
- WaitTime0*5;
- _ ->
- WaitTime0*10
- end,
- ?line WaitTime = case {erlang:system_info(schedulers_online),
- erlang:system_info(logical_processors)} of
- {Schdlrs, CPUs} when is_integer(CPUs),
- Schdlrs =< CPUs ->
- WaitTime1;
- _ ->
- WaitTime1*10
- end,
- ?line receive after WaitTime -> ok end,
- ?line 1 = suspend_count(Tok),
- ?line erlang:suspend_process(Tok, [asynchronous]),
- ?line 2 = suspend_count(Tok),
- ?line erlang:suspend_process(Tok, [asynchronous]),
- ?line 3 = suspend_count(Tok),
- ?line erlang:suspend_process(Tok),
- ?line 4 = suspend_count(Tok),
- ?line erlang:suspend_process(Tok),
- ?line 5 = suspend_count(Tok),
- ?line erlang:suspend_process(Tok, [unless_suspending]),
- ?line 5 = suspend_count(Tok),
- ?line erlang:suspend_process(Tok, [unless_suspending,
- asynchronous]),
- ?line 5 = suspend_count(Tok),
- ?line erlang:resume_process(Tok),
- ?line erlang:resume_process(Tok),
- ?line erlang:resume_process(Tok),
- ?line erlang:resume_process(Tok),
- ?line 1 = suspend_count(Tok),
- ?line ?t:format("Main suspends: ~p~n"
- "Main async: ~p~n"
- "Double async: ~p~n"
- "Async once: ~p~n"
- "Synced: ~p~n",
- [TC,
- SI#susp_info.async,
- SI#susp_info.dbl_async,
- SI#susp_info.async_once,
- SI#susp_info.synced]),
- ?line case erlang:system_info(schedulers_online) of
- 1 ->
- ?line ok;
- _ ->
- ?line true = SI#susp_info.async =/= 0
- end,
- ?line unlink(Tok),
- ?line exit(Tok, bang),
- ?line test_server:timetrap_cancel(Dog),
- ?line ok.
+ WaitTime0 = 10,
+ WaitTime1 = case {erlang:system_info(debug_compiled),
+ erlang:system_info(lock_checking)} of
+ {false, false} ->
+ WaitTime0;
+ {false, true} ->
+ WaitTime0*5;
+ _ ->
+ WaitTime0*10
+ end,
+ WaitTime = case {erlang:system_info(schedulers_online),
+ erlang:system_info(logical_processors)} of
+ {Schdlrs, CPUs} when is_integer(CPUs),
+ Schdlrs =< CPUs ->
+ WaitTime1;
+ _ ->
+ WaitTime1*10
+ end,
+ receive after WaitTime -> ok end,
+ 1 = suspend_count(Tok),
+ erlang:suspend_process(Tok, [asynchronous]),
+ 2 = suspend_count(Tok),
+ erlang:suspend_process(Tok, [asynchronous]),
+ 3 = suspend_count(Tok),
+ erlang:suspend_process(Tok),
+ 4 = suspend_count(Tok),
+ erlang:suspend_process(Tok),
+ 5 = suspend_count(Tok),
+ erlang:suspend_process(Tok, [unless_suspending]),
+ 5 = suspend_count(Tok),
+ erlang:suspend_process(Tok, [unless_suspending,
+ asynchronous]),
+ 5 = suspend_count(Tok),
+ erlang:resume_process(Tok),
+ erlang:resume_process(Tok),
+ erlang:resume_process(Tok),
+ erlang:resume_process(Tok),
+ 1 = suspend_count(Tok),
+ io:format("Main suspends: ~p~n"
+ "Main async: ~p~n"
+ "Double async: ~p~n"
+ "Async once: ~p~n"
+ "Synced: ~p~n",
+ [TC,
+ SI#susp_info.async,
+ SI#susp_info.dbl_async,
+ SI#susp_info.async_once,
+ SI#susp_info.synced]),
+ case erlang:system_info(schedulers_online) of
+ 1 ->
+ ok;
+ _ ->
+ true = SI#susp_info.async =/= 0
+ end,
+ unlink(Tok),
+ exit(Tok, bang),
+ ok.
suspend_count(Suspendee) ->
suspend_count(self(), Suspendee).
suspend_count(Suspender, Suspendee) ->
{suspending, SList} = process_info(Suspender, suspending),
-
+
case lists:keysearch(Suspendee, 1, SList) of
- {value, {_Suspendee, 0, 0}} ->
- ?line ?t:fail({bad_suspendee_list, SList});
- {value, {Suspendee, Count, 0}} when is_integer(Count), Count > 0 ->
- {status, suspended} = process_info(Suspendee, status),
- Count;
- {value, {Suspendee, 0, Outstanding}} when is_integer(Outstanding),
- Outstanding > 0 ->
- 0;
- false ->
- 0;
- Error ->
- ?line ?t:fail({bad_suspendee_list, Error, SList})
+ {value, {_Suspendee, 0, 0}} ->
+ ct:fail({bad_suspendee_list, SList});
+ {value, {Suspendee, Count, 0}} when is_integer(Count), Count > 0 ->
+ {status, suspended} = process_info(Suspendee, status),
+ Count;
+ {value, {Suspendee, 0, Outstanding}} when is_integer(Outstanding),
+ Outstanding > 0 ->
+ 0;
+ false ->
+ 0;
+ Error ->
+ ct:fail({bad_suspendee_list, Error, SList})
end.
-
+
repeat_acc(Fun, N, Acc) ->
repeat_acc(Fun, 0, N, Acc).
@@ -1306,121 +1589,129 @@ repeat_acc(_Fun, N, N, Acc) ->
Acc;
repeat_acc(Fun, N, M, Acc) ->
repeat_acc(Fun, N+1, M, Fun(N, Acc)).
-
+
%% Tests that waiting process can be suspended
%% (bug in R2D and earlier; see OTP-1488).
-suspend_waiting(doc) -> "Test that a waiting process can be suspended.";
+%% Test that a waiting process can be suspended.
suspend_waiting(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
-
- ?line Process = fun_spawn(fun process/0),
- ?line receive after 1 -> ok end,
- ?line true = erlang:suspend_process(Process),
- ?line {status, suspended} = process_info(Process, status),
-
- %% Done.
- ?line test_server:timetrap_cancel(Dog),
+ Process = fun_spawn(fun process/0),
+ receive after 1 -> ok end,
+ true = erlang:suspend_process(Process),
+ {status, suspended} = process_info(Process, status),
ok.
-
-new_clear(doc) ->
- "Test that erlang:trace(new, true, ...) is cleared when tracer dies.";
+%% Test that erlang:trace(new, true, ...) is cleared when tracer dies.
new_clear(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
-
- ?line Tracer = spawn(fun receiver/0),
- ?line 0 = erlang:trace(new, true, [send, {tracer, Tracer}]),
- ?line {flags, [send]} = erlang:trace_info(new, flags),
- ?line {tracer, Tracer} = erlang:trace_info(new, tracer),
- ?line Mref = erlang:monitor(process, Tracer),
- ?line true = exit(Tracer, done),
+ Tracer = spawn(fun receiver/0),
+ 0 = erlang:trace(new, true, [send, {tracer, Tracer}]),
+ {flags, [send]} = erlang:trace_info(new, flags),
+ {tracer, Tracer} = erlang:trace_info(new, tracer),
+ Mref = erlang:monitor(process, Tracer),
+ true = exit(Tracer, done),
receive
- {'DOWN',Mref,_,_,_} -> ok
+ {'DOWN',Mref,_,_,_} -> ok
end,
- ?line {flags, []} = erlang:trace_info(new, flags),
- ?line {tracer, []} = erlang:trace_info(new, tracer),
-
- %% Done.
- ?line test_server:timetrap_cancel(Dog),
-
+ {flags, []} = erlang:trace_info(new, flags),
+ {tracer, []} = erlang:trace_info(new, tracer),
ok.
-existing_clear(doc) ->
- "Test that erlang:trace(all, false, ...) works without tracer.";
+%% Test that erlang:trace(all, false, ...) works without tracer.
existing_clear(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
- ?line Self = self(),
-
- ?line Tracer = fun_spawn(fun receiver/0),
- ?line N = erlang:trace(existing, true, [send, {tracer, Tracer}]),
- ?line {flags, [send]} = erlang:trace_info(Self, flags),
- ?line {tracer, Tracer} = erlang:trace_info(Self, tracer),
- ?line M = erlang:trace(all, false, [all]),
- ?line io:format("Started trace on ~p processes and stopped on ~p~n",
- [N, M]),
- ?line {flags, []} = erlang:trace_info(Self, flags),
- ?line {tracer, []} = erlang:trace_info(Self, tracer),
- ?line M = N + 1, % Since trace could not be enabled on the tracer.
+ Self = self(),
+
+ Tracer = fun_spawn(fun receiver/0),
+ N = erlang:trace(existing, true, [send, {tracer, Tracer}]),
+ {flags, [send]} = erlang:trace_info(Self, flags),
+ {tracer, Tracer} = erlang:trace_info(Self, tracer),
+ M = erlang:trace(all, false, [all]),
+ io:format("Started trace on ~p processes and stopped on ~p~n",
+ [N, M]),
+ {flags, []} = erlang:trace_info(Self, flags),
+ {tracer, []} = erlang:trace_info(Self, tracer),
+ M = N, % Used to be N + 1, but from 19.0 the tracer is also traced
+
+ ok.
+
+%% Test that erlang:trace/3 can be called on processes where the
+%% tracer has died. OTP-13928
+tracer_die(Config) when is_list(Config) ->
+ Proc = spawn(fun receiver/0),
+
+ Tracer = spawn(fun receiver/0),
+ timer:sleep(1),
+ N = erlang:trace(existing, true, [send, {tracer, Tracer}]),
+ {flags, [send]} = erlang:trace_info(Proc, flags),
+ {tracer, Tracer} = erlang:trace_info(Proc, tracer),
+ exit(Tracer, die),
+
+ Tracer2 = spawn(fun receiver/0),
+ timer:sleep(1),
+ N = erlang:trace(existing, true, [send, {tracer, Tracer2}]),
+ {flags, [send]} = erlang:trace_info(Proc, flags),
+ {tracer, Tracer2} = erlang:trace_info(Proc, tracer),
+ exit(Tracer2, die),
+
+ Tracer3 = spawn(fun receiver/0),
+ timer:sleep(1),
+ 1 = erlang:trace(Proc, true, [send, {tracer, Tracer3}]),
+ {flags, [send]} = erlang:trace_info(Proc, flags),
+ {tracer, Tracer3} = erlang:trace_info(Proc, tracer),
+ exit(Tracer3, die),
- %% Done.
- ?line test_server:timetrap_cancel(Dog),
ok.
-bad_flag(doc) -> "Test that an invalid flag cause badarg";
-bad_flag(suite) -> [];
+%% Test that an invalid flag cause badarg
bad_flag(Config) when is_list(Config) ->
%% A bad flag could deadlock the SMP emulator in erts-5.5
- ?line {'EXIT', {badarg, _}} = (catch erlang:trace(new,
- true,
- [not_a_valid_flag])),
- ?line ok.
+ {'EXIT', {badarg, _}} = (catch erlang:trace(new,
+ true,
+ [not_a_valid_flag])),
+ ok.
-trace_delivered(doc) -> "Test erlang:trace_delivered/1";
-trace_delivered(suite) -> [];
+%% Test erlang:trace_delivered/1
trace_delivered(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(60)),
- ?line TokLoops = 10000,
- ?line Go = make_ref(),
- ?line Parent = self(),
- ?line Tok = spawn(fun () ->
- receive Go -> gone end,
- tok_trace_loop(Parent, 0, TokLoops)
- end),
- ?line 1 = erlang:trace(Tok, true, [procs]),
- ?line Mon = erlang:monitor(process, Tok),
- ?line NoOfTraceMessages = 4*TokLoops + 1,
- ?line io:format("Expect a total of ~p trace messages~n",
- [NoOfTraceMessages]),
- ?line Tok ! Go,
- ?line NoOfTraceMessages = drop_trace_until_down(Tok, Mon),
- ?line receive
- Msg ->
- ?line ?t:fail({unexpected_message, Msg})
- after 1000 ->
- ?line test_server:timetrap_cancel(Dog),
- ?line ok
- end.
+ ct:timetrap({minutes, 1}),
+ TokLoops = 10000,
+ Go = make_ref(),
+ Parent = self(),
+ Tok = spawn(fun () ->
+ receive Go -> gone end,
+ tok_trace_loop(Parent, 0, TokLoops)
+ end),
+ 1 = erlang:trace(Tok, true, [procs]),
+ Mon = erlang:monitor(process, Tok),
+ NoOfTraceMessages = 4*TokLoops + 1,
+ io:format("Expect a total of ~p trace messages~n",
+ [NoOfTraceMessages]),
+ Tok ! Go,
+ NoOfTraceMessages = drop_trace_until_down(Tok, Mon),
+ receive
+ Msg ->
+ ct:fail({unexpected_message, Msg})
+ after 1000 ->
+ ok
+ end.
drop_trace_until_down(Proc, Mon) ->
drop_trace_until_down(Proc, Mon, false, 0, 0).
drop_trace_until_down(Proc, Mon, TDRef, N, D) ->
case receive Msg -> Msg end of
- {trace_delivered, Proc, TDRef} ->
- io:format("~p trace messages on 'DOWN'~n", [D]),
- io:format("Got a total of ~p trace messages~n", [N]),
- N;
- {'DOWN', Mon, process, Proc, _} ->
- Ref = erlang:trace_delivered(Proc),
- drop_trace_until_down(Proc, Mon, Ref, N, N);
- Trace when is_tuple(Trace),
- element(1, Trace) == trace,
- element(2, Trace) == Proc ->
- drop_trace_until_down(Proc, Mon, TDRef, N+1, D)
+ {trace_delivered, Proc, TDRef} ->
+ io:format("~p trace messages on 'DOWN'~n", [D]),
+ io:format("Got a total of ~p trace messages~n", [N]),
+ N;
+ {'DOWN', Mon, process, Proc, _} ->
+ Ref = erlang:trace_delivered(Proc),
+ drop_trace_until_down(Proc, Mon, Ref, N, N);
+ Trace when is_tuple(Trace),
+ element(1, Trace) == trace,
+ element(2, Trace) == Proc ->
+ drop_trace_until_down(Proc, Mon, TDRef, N+1, D)
end.
tok_trace_loop(_, N, N) ->
@@ -1437,16 +1728,23 @@ tok_trace_loop(Parent, N, M) ->
receive_first() ->
receive
- Any -> Any
+ Any -> Any
+ end.
+
+%% Waits for and returns the first message in the message queue.
+
+receive_first_trace() ->
+ receive
+ Any when element(1,Any) =:= trace; element(1,Any) =:= trace_ts -> Any
end.
%% Ensures that there is no message in the message queue.
receive_nothing() ->
receive
- Any ->
- test_server:fail({unexpected_message, Any})
- after 200 ->
+ Any ->
+ ct:fail({unexpected_message, Any})
+ after 100 ->
ok
end.
@@ -1455,39 +1753,39 @@ receive_nothing() ->
process(Dest) ->
receive
- {send_please, To, What} ->
- To ! What,
- process(Dest);
- {spawn_link_please, ReplyTo, {M, F, A}} ->
- Pid = spawn_link(M, F, A),
- ReplyTo ! {spawned, self(), Pid},
- process(Dest);
- {spawn_link_please, ReplyTo, Node, {M, F, A}} ->
- Pid = spawn_link(Node, M, F, A),
- ReplyTo ! {spawned, self(), Pid},
- process(Dest);
- {link_please, Pid} ->
- link(Pid),
- process(Dest);
- {unlink_please, Pid} ->
- unlink(Pid),
- process(Dest);
- {register_please, Name, Pid} ->
- register(Name, Pid),
- process(Dest);
- {unregister_please, Name} ->
- unregister(Name),
- process(Dest);
- {exit_please, Reason} ->
- exit(Reason);
- {trap_exit_please, State} ->
- process_flag(trap_exit, State),
- process(Dest);
- Other ->
- Dest ! {self(), Other},
- process(Dest)
+ {send_please, To, What} ->
+ To ! What,
+ process(Dest);
+ {spawn_link_please, ReplyTo, {M, F, A}} ->
+ Pid = spawn_link(M, F, A),
+ ReplyTo ! {spawned, self(), Pid},
+ process(Dest);
+ {spawn_link_please, ReplyTo, Node, {M, F, A}} ->
+ Pid = spawn_link(Node, M, F, A),
+ ReplyTo ! {spawned, self(), Pid},
+ process(Dest);
+ {link_please, Pid} ->
+ link(Pid),
+ process(Dest);
+ {unlink_please, Pid} ->
+ unlink(Pid),
+ process(Dest);
+ {register_please, Name, Pid} ->
+ register(Name, Pid),
+ process(Dest);
+ {unregister_please, Name} ->
+ unregister(Name),
+ process(Dest);
+ {exit_please, Reason} ->
+ exit(Reason);
+ {trap_exit_please, State} ->
+ process_flag(trap_exit, State),
+ process(Dest);
+ Other ->
+ Dest ! {self(), Other},
+ process(Dest)
after 3000 ->
- exit(timeout)
+ exit(timeout)
end.
@@ -1495,17 +1793,17 @@ process(Dest) ->
process() ->
receive
- {spawn_please, ReplyTo, Fun} ->
- Pid = fun_spawn(Fun),
- ReplyTo ! {spawned, Pid},
- process();
- {send_please, To, What} ->
- To ! What,
- process();
- timeout_please ->
- receive after 1 -> process() end;
- _Other ->
- process()
+ {spawn_please, ReplyTo, Fun} ->
+ Pid = fun_spawn(Fun),
+ ReplyTo ! {spawned, Pid},
+ process();
+ {send_please, To, What} ->
+ To ! What,
+ process();
+ timeout_please ->
+ receive after 1 -> process() end;
+ _Other ->
+ process()
end.
@@ -1513,18 +1811,23 @@ process() ->
sender() ->
receive
- {send_please, To, What} ->
- To ! What,
- sender()
+ {send_please, To, What} ->
+ To ! What,
+ sender()
end.
%% Just consumes messages from its message queue.
receiver() ->
- receive
- _Any -> receiver()
- end.
+ receiver(infinity).
+
+receiver(Timeout) ->
+ receiver(receive
+ {set_timeout, NewTimeout} -> NewTimeout;
+ _Any -> Timeout
+ after Timeout -> infinity %% reset
+ end).
%% Works as long as it receives CPU time. Will always be RUNNABLE.
@@ -1544,8 +1847,8 @@ fun_spawn(Fun, Args) ->
start_node(Name) ->
Pa = filename:dirname(code:which(?MODULE)),
Cookie = atom_to_list(erlang:get_cookie()),
- test_server:start_node(Name, slave,
- [{args, "-setcookie " ++ Cookie ++" -pa " ++ Pa}]).
+ test_server:start_node(Name, slave,
+ [{args, "-setcookie " ++ Cookie ++" -pa " ++ Pa}]).
stop_node(Node) ->
test_server:stop_node(Node).
@@ -1553,11 +1856,11 @@ stop_node(Node) ->
wait_for_empty_runq(DeadLine) ->
case statistics(run_queue) of
- 0 -> true;
- RQLen ->
- erlang:display("Waiting for empty run queue"),
- MSDL = DeadLine*1000,
- wait_for_empty_runq(MSDL, MSDL, RQLen)
+ 0 -> true;
+ RQLen ->
+ erlang:display("Waiting for empty run queue"),
+ MSDL = DeadLine*1000,
+ wait_for_empty_runq(MSDL, MSDL, RQLen)
end.
wait_for_empty_runq(DeadLine, Left, RQLen) when Left =< 0 ->
@@ -1568,48 +1871,48 @@ wait_for_empty_runq(DeadLine, Left, _RQLen) ->
UntilDeadLine = Left - Wait,
receive after Wait -> ok end,
case statistics(run_queue) of
- 0 ->
- erlang:display("Waited for "
- ++ integer_to_list(DeadLine
- - UntilDeadLine)
- ++ " ms for empty run queue."),
- true;
- NewRQLen ->
- wait_for_empty_runq(DeadLine,
- UntilDeadLine,
- NewRQLen)
+ 0 ->
+ erlang:display("Waited for "
+ ++ integer_to_list(DeadLine
+ - UntilDeadLine)
+ ++ " ms for empty run queue."),
+ true;
+ NewRQLen ->
+ wait_for_empty_runq(DeadLine,
+ UntilDeadLine,
+ NewRQLen)
end.
issue_non_empty_runq_warning(DeadLine, RQLen) ->
PIs = lists:foldl(
- fun (P, Acc) ->
- case process_info(P,
- [status,
- initial_call,
- current_function,
- registered_name,
- reductions,
- message_queue_len]) of
- [{status, Runnable} | _] = PI when Runnable /= waiting,
- Runnable /= suspended ->
- [[{pid, P} | PI] | Acc];
- _ ->
- Acc
- end
- end,
- [],
- processes()),
- ?t:format("WARNING: Unexpected runnable processes in system (waited ~p sec).~n"
- " Run queue length: ~p~n"
- " Self: ~p~n"
- " Processes info: ~p~n",
- [DeadLine div 1000, RQLen, self(), PIs]),
+ fun (P, Acc) ->
+ case process_info(P,
+ [status,
+ initial_call,
+ current_function,
+ registered_name,
+ reductions,
+ message_queue_len]) of
+ [{status, Runnable} | _] = PI when Runnable /= waiting,
+ Runnable /= suspended ->
+ [[{pid, P} | PI] | Acc];
+ _ ->
+ Acc
+ end
+ end,
+ [],
+ processes()),
+ io:format("WARNING: Unexpected runnable processes in system (waited ~p sec).~n"
+ " Run queue length: ~p~n"
+ " Self: ~p~n"
+ " Processes info: ~p~n",
+ [DeadLine div 1000, RQLen, self(), PIs]),
receive after 1000 -> ok end.
load_driver(Dir, Driver) ->
case erl_ddll:load_driver(Dir, Driver) of
- ok -> ok;
- {error, Error} = Res ->
- io:format("~s\n", [erl_ddll:format_error(Error)]),
- Res
+ ok -> ok;
+ {error, Error} = Res ->
+ io:format("~s\n", [erl_ddll:format_error(Error)]),
+ Res
end.
diff --git a/erts/emulator/test/trace_bif_SUITE.erl b/erts/emulator/test/trace_bif_SUITE.erl
index 96b7dd159f..f60c777ba1 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-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,10 +20,9 @@
-module(trace_bif_SUITE).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2]).
+-export([all/0, suite/0]).
-export([trace_bif/1, trace_bif_timestamp/1, trace_on_and_off/1,
trace_bif_local/1,
trace_bif_timestamp_local/1, trace_bif_return/1, not_run/1,
@@ -42,99 +41,82 @@ all() ->
trace_bif_return, trace_info_old_code]
end.
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
not_run(Config) when is_list(Config) ->
{skipped,"Native code"}.
-trace_on_and_off(doc) ->
- "Tests switching tracing on and off.";
+%% Tests switching tracing on and off.
trace_on_and_off(Config) when is_list(Config) ->
- ?line Pid = spawn(?MODULE, bif_process, []),
- ?line Self = self(),
- ?line 1 = erlang:trace(Pid, true, [call,timestamp]),
- ?line {flags, Flags} = erlang:trace_info(Pid,flags),
- ?line [call,timestamp] = lists:sort(Flags),
- ?line {tracer, Self} = erlang:trace_info(Pid,tracer),
- ?line 1 = erlang:trace(Pid, false, [timestamp]),
- ?line {flags,[call]} = erlang:trace_info(Pid,flags),
- ?line {tracer, Self} = erlang:trace_info(Pid,tracer),
- ?line 1 = erlang:trace(Pid, false, [call]),
- ?line {flags,[]} = erlang:trace_info(Pid,flags),
- ?line {tracer, []} = erlang:trace_info(Pid,tracer),
- ?line exit(Pid,kill),
+ Pid = spawn(?MODULE, bif_process, []),
+ Self = self(),
+ 1 = erlang:trace(Pid, true, [call,timestamp]),
+ {flags, Flags} = erlang:trace_info(Pid,flags),
+ [call,timestamp] = lists:sort(Flags),
+ {tracer, Self} = erlang:trace_info(Pid,tracer),
+ 1 = erlang:trace(Pid, false, [timestamp]),
+ {flags,[call]} = erlang:trace_info(Pid,flags),
+ {tracer, Self} = erlang:trace_info(Pid,tracer),
+ 1 = erlang:trace(Pid, false, [call]),
+ {flags,[]} = erlang:trace_info(Pid,flags),
+ {tracer, []} = erlang:trace_info(Pid,tracer),
+ exit(Pid,kill),
ok.
-trace_bif(doc) -> "Test tracing BIFs.";
+%% Test tracing BIFs.
trace_bif(Config) when is_list(Config) ->
do_trace_bif([]).
-trace_bif_local(doc) -> "Test tracing BIFs with local flag.";
+%% Test tracing BIFs with local flag.
trace_bif_local(Config) when is_list(Config) ->
do_trace_bif([local]).
do_trace_bif(Flags) ->
- ?line Pid = spawn(?MODULE, bif_process, []),
- ?line 1 = erlang:trace(Pid, true, [call]),
- ?line erlang:trace_pattern({erlang,'_','_'}, [], Flags),
- ?line Pid ! {do_bif, time, []},
- ?line receive_trace_msg({trace,Pid,call,{erlang,time, []}}),
- ?line Pid ! {do_bif, statistics, [runtime]},
- ?line receive_trace_msg({trace,Pid,call,
- {erlang,statistics, [runtime]}}),
-
- ?line Pid ! {do_time_bif},
- ?line receive_trace_msg({trace,Pid,call,
- {erlang,time, []}}),
-
- ?line Pid ! {do_statistics_bif},
- ?line receive_trace_msg({trace,Pid,call,
- {erlang,statistics, [runtime]}}),
-
- ?line 1 = erlang:trace(Pid, false, [call]),
- ?line erlang:trace_pattern({erlang,'_','_'}, false, Flags),
- ?line exit(Pid, die),
+ Pid = spawn(?MODULE, bif_process, []),
+ 1 = erlang:trace(Pid, true, [call]),
+ erlang:trace_pattern({erlang,'_','_'}, [], Flags),
+ Pid ! {do_bif, time, []},
+ receive_trace_msg({trace,Pid,call,{erlang,time, []}}),
+ Pid ! {do_bif, statistics, [runtime]},
+ receive_trace_msg({trace,Pid,call,
+ {erlang,statistics, [runtime]}}),
+
+ Pid ! {do_time_bif},
+ receive_trace_msg({trace,Pid,call,
+ {erlang,time, []}}),
+
+ Pid ! {do_statistics_bif},
+ receive_trace_msg({trace,Pid,call,
+ {erlang,statistics, [runtime]}}),
+
+ 1 = erlang:trace(Pid, false, [call]),
+ erlang:trace_pattern({erlang,'_','_'}, false, Flags),
+ exit(Pid, die),
ok.
-trace_bif_timestamp(doc) -> "Test tracing BIFs with timestamps.";
+%% Test tracing BIFs with timestamps.
trace_bif_timestamp(Config) when is_list(Config) ->
do_trace_bif_timestamp([], timestamp, [timestamp]),
do_trace_bif_timestamp([], timestamp,
- [timestamp,
- monotonic_timestamp,
- strict_monotonic_timestamp]),
+ [timestamp,
+ monotonic_timestamp,
+ strict_monotonic_timestamp]),
do_trace_bif_timestamp([], strict_monotonic_timestamp,
- [strict_monotonic_timestamp]),
+ [strict_monotonic_timestamp]),
do_trace_bif_timestamp([], strict_monotonic_timestamp,
- [monotonic_timestamp, strict_monotonic_timestamp]),
+ [monotonic_timestamp, strict_monotonic_timestamp]),
do_trace_bif_timestamp([], monotonic_timestamp, [monotonic_timestamp]).
-
-trace_bif_timestamp_local(doc) ->
- "Test tracing BIFs with timestamps and local flag.";
+
+%% Test tracing BIFs with timestamps and local flag.
trace_bif_timestamp_local(Config) when is_list(Config) ->
do_trace_bif_timestamp([local], timestamp, [timestamp]),
do_trace_bif_timestamp([local], timestamp,
- [timestamp,
- monotonic_timestamp,
- strict_monotonic_timestamp]),
+ [timestamp,
+ monotonic_timestamp,
+ strict_monotonic_timestamp]),
do_trace_bif_timestamp([local], strict_monotonic_timestamp,
- [strict_monotonic_timestamp]),
+ [strict_monotonic_timestamp]),
do_trace_bif_timestamp([local], strict_monotonic_timestamp,
- [monotonic_timestamp, strict_monotonic_timestamp]),
+ [monotonic_timestamp, strict_monotonic_timestamp]),
do_trace_bif_timestamp([local], monotonic_timestamp, [monotonic_timestamp]).
do_trace_bif_timestamp(Flags, TsType, TsFlags) ->
@@ -146,22 +128,22 @@ do_trace_bif_timestamp(Flags, TsType, TsFlags) ->
Ts0 = make_ts(TsType),
Pid ! {do_bif, time, []},
Ts1 = receive_trace_msg_ts({trace_ts,Pid,call,{erlang,time,[]}},
- Ts0,TsType),
+ Ts0,TsType),
Pid ! {do_bif, statistics, [runtime]},
Ts2 = receive_trace_msg_ts({trace_ts,Pid,call,
- {erlang,statistics, [runtime]}},
- Ts1, TsType),
+ {erlang,statistics, [runtime]}},
+ Ts1, TsType),
Pid ! {do_time_bif},
Ts3 = receive_trace_msg_ts({trace_ts,Pid,call,
- {erlang,time, []}},
- Ts2, TsType),
+ {erlang,time, []}},
+ Ts2, TsType),
Pid ! {do_statistics_bif},
Ts4 = receive_trace_msg_ts({trace_ts,Pid,call,
- {erlang,statistics, [runtime]}},
- Ts3, TsType),
+ {erlang,statistics, [runtime]}},
+ Ts3, TsType),
check_ts(TsType, Ts4, make_ts(TsType)),
@@ -170,11 +152,11 @@ do_trace_bif_timestamp(Flags, TsType, TsFlags) ->
Pid ! {do_statistics_bif},
receive_trace_msg({trace,Pid,call,
- {erlang,statistics, [runtime]}}),
+ {erlang,statistics, [runtime]}}),
Pid ! {do_bif, statistics, [runtime]},
receive_trace_msg({trace,Pid,call,
- {erlang,statistics, [runtime]}}),
+ {erlang,statistics, [runtime]}}),
1 = erlang:trace(Pid, false, [call]),
erlang:trace_pattern({erlang,'_','_'}, false, Flags),
@@ -182,18 +164,17 @@ do_trace_bif_timestamp(Flags, TsType, TsFlags) ->
exit(Pid, die),
ok.
-trace_bif_return(doc) ->
- "Test tracing BIF's with return/return_to trace.";
+%% Test tracing BIF's with return/return_to trace.
trace_bif_return(Config) when is_list(Config) ->
do_trace_bif_return(timestamp, [timestamp]),
do_trace_bif_return(timestamp,
- [timestamp,
- monotonic_timestamp,
- strict_monotonic_timestamp]),
+ [timestamp,
+ monotonic_timestamp,
+ strict_monotonic_timestamp]),
do_trace_bif_return(strict_monotonic_timestamp,
- [strict_monotonic_timestamp]),
+ [strict_monotonic_timestamp]),
do_trace_bif_return(strict_monotonic_timestamp,
- [monotonic_timestamp, strict_monotonic_timestamp]),
+ [monotonic_timestamp, strict_monotonic_timestamp]),
do_trace_bif_return(monotonic_timestamp, [monotonic_timestamp]).
do_trace_bif_return(TsType, TsFlags) ->
@@ -201,122 +182,116 @@ do_trace_bif_return(TsType, TsFlags) ->
Pid=spawn(?MODULE, bif_process, []),
1 = erlang:trace(Pid, true, [call,return_to]++TsFlags),
erlang:trace_pattern({erlang,'_','_'}, [{'_',[],[{return_trace}]}],
- [local]),
+ [local]),
Ts0 = make_ts(TsType),
Pid ! {do_bif, time, []},
Ts1 = receive_trace_msg_ts({trace_ts,Pid,call,{erlang,time,[]}},
- Ts0, TsType),
+ Ts0, TsType),
Ts2 = receive_trace_msg_ts_return_from({trace_ts,Pid,return_from,
- {erlang,time,0}},
- Ts1, TsType),
+ {erlang,time,0}},
+ Ts1, TsType),
Ts3 = receive_trace_msg_ts_return_to({trace_ts,Pid,return_to,
- {?MODULE, bif_process,0}},
- Ts2, TsType),
-
-
+ {?MODULE, bif_process,0}},
+ Ts2, TsType),
+
+
Pid ! {do_bif, statistics, [runtime]},
Ts4 = receive_trace_msg_ts({trace_ts,Pid,call,
- {erlang,statistics, [runtime]}},
- Ts3, TsType),
+ {erlang,statistics, [runtime]}},
+ Ts3, TsType),
Ts5 = receive_trace_msg_ts_return_from({trace_ts,Pid,return_from,
- {erlang,statistics,1}},
- Ts4, TsType),
+ {erlang,statistics,1}},
+ Ts4, TsType),
Ts6 = receive_trace_msg_ts_return_to({trace_ts,Pid,return_to,
- {?MODULE, bif_process,0}},
- Ts5, TsType),
-
-
+ {?MODULE, bif_process,0}},
+ Ts5, TsType),
+
+
Pid ! {do_time_bif},
Ts7 = receive_trace_msg_ts({trace_ts,Pid,call,
- {erlang,time, []}},
- Ts6, TsType),
+ {erlang,time, []}},
+ Ts6, TsType),
Ts8 = receive_trace_msg_ts_return_from({trace_ts,Pid,return_from,
- {erlang,time,0}},
- Ts7, TsType),
+ {erlang,time,0}},
+ Ts7, TsType),
Ts9 = receive_trace_msg_ts_return_to({trace_ts,Pid,return_to,
- {?MODULE, bif_process,0}},
- Ts8, TsType),
+ {?MODULE, bif_process,0}},
+ Ts8, TsType),
Pid ! {do_statistics_bif},
Ts10 = receive_trace_msg_ts({trace_ts,Pid,call,
- {erlang,statistics, [runtime]}},
- Ts9, TsType),
+ {erlang,statistics, [runtime]}},
+ Ts9, TsType),
Ts11 = receive_trace_msg_ts_return_from({trace_ts,Pid,return_from,
- {erlang,statistics,1}},
- Ts10, TsType),
+ {erlang,statistics,1}},
+ Ts10, TsType),
Ts12 = receive_trace_msg_ts_return_to({trace_ts,Pid,return_to,
- {?MODULE, bif_process,0}},
- Ts11, TsType),
+ {?MODULE, bif_process,0}},
+ Ts11, TsType),
check_ts(TsType, Ts12, make_ts(TsType)),
+ erlang:trace_pattern({erlang,'_','_'}, false, [local]),
ok.
-
-
+
+
receive_trace_msg(Mess) ->
receive
- Mess ->
- ok;
- Other ->
- io:format("Expected: ~p,~nGot: ~p~n", [Mess, Other]),
- ?t:fail()
+ Mess ->
+ ok;
+ Other ->
+ ct:fail("Expected: ~p,~nGot: ~p~n", [Mess, Other])
after 5000 ->
- io:format("Expected: ~p,~nGot: timeout~n", [Mess]),
- ?t:fail()
+ ct:fail("Expected: ~p,~nGot: timeout~n", [Mess])
end.
receive_trace_msg_ts({trace_ts, Pid, call, {erlang,F,A}}, PrevTs, TsType) ->
receive
- {trace_ts, Pid, call, {erlang, F, A}, Ts} ->
- check_ts(TsType, PrevTs, Ts),
- Ts;
- Other ->
- io:format("Expected: {trace, ~p, call, {~p, ~p, ~p}, TimeStamp}},~n"
- "Got: ~p~n",
- [Pid, erlang, F, A, Other]),
- ?t:fail()
- after 5000 ->
- io:format("Got timeout~n", []),
- ?t:fail()
+ {trace_ts, Pid, call, {erlang, F, A}, Ts} = M ->
+ io:format("~p (PrevTs: ~p)~n",[M, PrevTs]),
+ check_ts(TsType, PrevTs, Ts),
+ Ts;
+ Other ->
+ ct:fail("Expected: {trace, ~p, call, {~p, ~p, ~p}, TimeStamp}},~n"
+ "Got: ~p~n",
+ [Pid, erlang, F, A, Other])
+ after 5000 ->
+ ct:fail("Got timeout~n", [])
end.
receive_trace_msg_ts_return_from({trace_ts, Pid, return_from, {erlang,F,A}}, PrevTs, TsType) ->
receive
- {trace_ts, Pid, return_from, {erlang, F, A}, _Value, Ts} ->
- check_ts(TsType, PrevTs, Ts),
- Ts;
- Other ->
- io:format("Expected: {trace_ts, ~p, return_from, {~p, ~p, ~p}, Value, TimeStamp}},~n"
- "Got: ~p~n",
- [Pid, erlang, F, A, Other]),
- ?t:fail()
- after 5000 ->
- io:format("Got timeout~n", []),
- ?t:fail()
+ {trace_ts, Pid, return_from, {erlang, F, A}, _Value, Ts} = M ->
+ io:format("~p (PrevTs: ~p)~n",[M, PrevTs]),
+ check_ts(TsType, PrevTs, Ts),
+ Ts;
+ Other ->
+ ct:fail("Expected: {trace_ts, ~p, return_from, {~p, ~p, ~p}, Value, TimeStamp}},~n"
+ "Got: ~p~n", [Pid, erlang, F, A, Other])
+ after 5000 ->
+ ct:fail("Got timeout~n", [])
end.
receive_trace_msg_ts_return_to({trace_ts, Pid, return_to, {M,F,A}}, PrevTs, TsType) ->
receive
- {trace_ts, Pid, return_to, {M, F, A}, Ts} ->
- check_ts(TsType, PrevTs, Ts),
- Ts;
- Other ->
- io:format("Expected: {trace_ts, ~p, return_to, {~p, ~p, ~p}, TimeStamp}},~n"
- "Got: ~p~n",
- [Pid, M, F, A, Other]),
- ?t:fail()
- after 5000 ->
- io:format("Got timeout~n", []),
- ?t:fail()
+ {trace_ts, Pid, return_to, {M, F, A}, Ts} = Msg ->
+ io:format("~p (PrevTs: ~p)~n",[Msg, PrevTs]),
+ check_ts(TsType, PrevTs, Ts),
+ Ts;
+ Other ->
+ ct:fail("Expected: {trace_ts, ~p, return_to, {~p, ~p, ~p}, TimeStamp}},~n"
+ "Got: ~p~n", [Pid, M, F, A, Other])
+ after 5000 ->
+ ct:fail("Got timeout~n", [])
end.
make_ts(timestamp) ->
erlang:now();
make_ts(monotonic_timestamp) ->
- erlang:monotonic_time(nano_seconds);
+ erlang:monotonic_time(nanosecond);
make_ts(strict_monotonic_timestamp) ->
- MT = erlang:monotonic_time(nano_seconds),
+ MT = erlang:monotonic_time(nanosecond),
UMI = erlang:unique_integer([monotonic]),
{MT, UMI}.
@@ -340,37 +315,37 @@ check_ts(strict_monotonic_timestamp, PrevTs, Ts) ->
bif_process() ->
receive
- {do_bif, Name, Args} ->
- apply(erlang, Name, Args),
- bif_process();
- {do_time_bif} ->
- %% Match the return value to ensure that the time() call
- %% is not optimized away.
- {_,_,_} = time(),
- bif_process();
- {do_statistics_bif} ->
- statistics(runtime),
- bif_process();
- _Stuff ->
- bif_process()
+ {do_bif, Name, Args} ->
+ apply(erlang, Name, Args),
+ bif_process();
+ {do_time_bif} ->
+ %% Match the return value to ensure that the time() call
+ %% is not optimized away.
+ {_,_,_} = time(),
+ bif_process();
+ {do_statistics_bif} ->
+ statistics(runtime),
+ bif_process();
+ _Stuff ->
+ bif_process()
end.
-
-trace_info_old_code(doc) -> "trace_info on deleted module (OTP-5057).";
+
+%% trace_info on deleted module (OTP-5057).
trace_info_old_code(Config) when is_list(Config) ->
- ?line MFA = {M,F,0} = {test,foo,0},
- ?line Fname = atom_to_list(M)++".erl",
- ?line AbsForms =
- [{attribute,a(1),module,M}, % -module(M).
- {attribute,a(2),export,[{F,0}]}, % -export([F/0]).
- {function,a(3),F,0, % F() ->
- [{clause,a(4),[],[],[{atom,a(4),F}]}]}], % F.
+ MFA = {M,F,0} = {test,foo,0},
+ Fname = atom_to_list(M)++".erl",
+ AbsForms =
+ [{attribute,a(1),module,M}, % -module(M).
+ {attribute,a(2),export,[{F,0}]}, % -export([F/0]).
+ {function,a(3),F,0, % F() ->
+ [{clause,a(4),[],[],[{atom,a(4),F}]}]}], % F.
%%
- ?line {ok,M,Mbin} = compile:forms(AbsForms),
- ?line {module,M} = code:load_binary(M, Fname, Mbin),
- ?line true = erlang:delete_module(M),
- ?line {traced,undefined} = erlang:trace_info(MFA, traced),
+ {ok,M,Mbin} = compile:forms(AbsForms),
+ {module,M} = code:load_binary(M, Fname, Mbin),
+ true = erlang:delete_module(M),
+ {traced,undefined} = erlang:trace_info(MFA, traced),
ok.
a(L) ->
diff --git a/erts/emulator/test/trace_call_count_SUITE.erl b/erts/emulator/test/trace_call_count_SUITE.erl
index c7881bbd70..5f871835bc 100644
--- a/erts/emulator/test/trace_call_count_SUITE.erl
+++ b/erts/emulator/test/trace_call_count_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -43,7 +43,7 @@
-define(config(A,B),config(A,B)).
-export([config/2]).
-else.
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-endif.
-ifdef(debug).
@@ -70,18 +70,17 @@ config(priv_dir,_) ->
pause_and_restart/1, combo/1]).
init_per_testcase(_Case, Config) ->
- ?line Dog=test_server:timetrap(test_server:seconds(30)),
- [{watchdog, Dog}|Config].
+ Config.
-end_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, _Config) ->
erlang:trace_pattern({'_','_','_'}, false, [local,meta,call_count]),
erlang:trace_pattern(on_load, false, [local,meta,call_count]),
erlang:trace(all, false, [all]),
- Dog=?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
ok.
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 4}}].
all() ->
case test_server:is_native(trace_call_count_SUITE) of
@@ -109,38 +108,23 @@ end_per_group(_GroupName, Config) ->
not_run(Config) when is_list(Config) ->
{skipped,"Native code"}.
-basic(suite) ->
- [];
-basic(doc) ->
- ["Tests basic call count trace"];
+%% Tests basic call count trace
basic(Config) when is_list(Config) ->
basic_test().
-on_and_off(suite) ->
- [];
-on_and_off(doc) ->
- ["Tests turning trace parameters on and off"];
+%% Tests turning trace parameters on and off
on_and_off(Config) when is_list(Config) ->
on_and_off_test().
-info(suite) ->
- [];
-info(doc) ->
- ["Tests the trace_info BIF"];
+%% Tests the trace_info BIF
info(Config) when is_list(Config) ->
info_test().
-pause_and_restart(suite) ->
- [];
-pause_and_restart(doc) ->
- ["Tests pausing and restarting call counters"];
+%% Tests pausing and restarting call counters
pause_and_restart(Config) when is_list(Config) ->
pause_and_restart_test().
-combo(suite) ->
- [];
-combo(doc) ->
- ["Tests combining local call trace and meta trace with call count trace"];
+%% Tests combining local call trace and meta trace with call count trace
combo(Config) when is_list(Config) ->
combo_test().
@@ -161,168 +145,168 @@ combo(Config) when is_list(Config) ->
%%%
basic_test() ->
- ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
- ?line M = 1000,
+ P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
+ M = 1000,
%%
- ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, true, [call_count]),
- ?line 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, true, [call_count]),
- ?line L = seq(1, M, fun(X) -> X+1 end),
- ?line {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
- ?line {call_count,0} = erlang:trace_info({?MODULE,seq_r,3}, call_count),
- ?line Lr = seq_r(1, M, fun(X) -> X+1 end),
- ?line {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
- ?line {call_count,1} = erlang:trace_info({?MODULE,seq_r,3}, call_count),
- ?line {call_count,M} = erlang:trace_info({?MODULE,seq_r,4}, call_count),
- ?line L = lists:reverse(Lr),
+ 1 = erlang:trace_pattern({?MODULE,seq,'_'}, true, [call_count]),
+ 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, true, [call_count]),
+ L = seq(1, M, fun(X) -> X+1 end),
+ {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
+ {call_count,0} = erlang:trace_info({?MODULE,seq_r,3}, call_count),
+ Lr = seq_r(1, M, fun(X) -> X+1 end),
+ {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
+ {call_count,1} = erlang:trace_info({?MODULE,seq_r,3}, call_count),
+ {call_count,M} = erlang:trace_info({?MODULE,seq_r,4}, call_count),
+ L = lists:reverse(Lr),
%%
- ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
+ P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
on_and_off_test() ->
- ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
- ?line M = 100,
+ P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
+ M = 100,
%%
- ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, true, [call_count]),
- ?line L = seq(1, M, fun(X) -> X+1 end),
- ?line {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
- ?line N = erlang:trace_pattern({?MODULE,'_','_'}, true, [call_count]),
- ?line L = seq(1, M, fun(X) -> X+1 end),
- ?line {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
- ?line P = erlang:trace_pattern({'_','_','_'}, true, [call_count]),
- ?line L = seq(1, M, fun(X) -> X+1 end),
- ?line {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
- ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, false, [call_count]),
- ?line {call_count,false} = erlang:trace_info({?MODULE,seq,3}, call_count),
- ?line L = seq(1, M, fun(X) -> X+1 end),
- ?line {call_count,false} = erlang:trace_info({?MODULE,seq,3}, call_count),
- ?line {call_count,0} = erlang:trace_info({?MODULE,seq_r,4}, call_count),
- ?line Lr = seq_r(1, M, fun(X) -> X+1 end),
- ?line {call_count,M} = erlang:trace_info({?MODULE,seq_r,4}, call_count),
- ?line N = erlang:trace_pattern({?MODULE,'_','_'}, false, [call_count]),
- ?line {call_count,false} = erlang:trace_info({?MODULE,seq_r,4}, call_count),
- ?line Lr = seq_r(1, M, fun(X) -> X+1 end),
- ?line {call_count,false} = erlang:trace_info({?MODULE,seq_r,4}, call_count),
- ?line L = lists:reverse(Lr),
+ 1 = erlang:trace_pattern({?MODULE,seq,'_'}, true, [call_count]),
+ L = seq(1, M, fun(X) -> X+1 end),
+ {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
+ N = erlang:trace_pattern({?MODULE,'_','_'}, true, [call_count]),
+ L = seq(1, M, fun(X) -> X+1 end),
+ {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
+ P = erlang:trace_pattern({'_','_','_'}, true, [call_count]),
+ L = seq(1, M, fun(X) -> X+1 end),
+ {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
+ 1 = erlang:trace_pattern({?MODULE,seq,'_'}, false, [call_count]),
+ {call_count,false} = erlang:trace_info({?MODULE,seq,3}, call_count),
+ L = seq(1, M, fun(X) -> X+1 end),
+ {call_count,false} = erlang:trace_info({?MODULE,seq,3}, call_count),
+ {call_count,0} = erlang:trace_info({?MODULE,seq_r,4}, call_count),
+ Lr = seq_r(1, M, fun(X) -> X+1 end),
+ {call_count,M} = erlang:trace_info({?MODULE,seq_r,4}, call_count),
+ N = erlang:trace_pattern({?MODULE,'_','_'}, false, [call_count]),
+ {call_count,false} = erlang:trace_info({?MODULE,seq_r,4}, call_count),
+ Lr = seq_r(1, M, fun(X) -> X+1 end),
+ {call_count,false} = erlang:trace_info({?MODULE,seq_r,4}, call_count),
+ L = lists:reverse(Lr),
%%
- ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
+ P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
info_test() ->
- ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
+ P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
%%
- ?line 1 = erlang:trace_pattern({?MODULE,seq,3}, true, [call_count]),
- ?line {call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count),
- ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, pause, [call_count]),
- ?line {call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count),
- ?line {all,[_|_]=L} = erlang:trace_info({?MODULE,seq,3}, all),
- ?line {value,{call_count,0}} = lists:keysearch(call_count, 1, L),
- ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, restart, [call_count]),
- ?line {call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count),
- ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, false, [call_count]),
- ?line {call_count,false} = erlang:trace_info({?MODULE,seq,3}, call_count),
- ?line {all,false} = erlang:trace_info({?MODULE,seq,3}, all),
+ 1 = erlang:trace_pattern({?MODULE,seq,3}, true, [call_count]),
+ {call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count),
+ 1 = erlang:trace_pattern({?MODULE,seq,'_'}, pause, [call_count]),
+ {call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count),
+ {all,[_|_]=L} = erlang:trace_info({?MODULE,seq,3}, all),
+ {value,{call_count,0}} = lists:keysearch(call_count, 1, L),
+ 1 = erlang:trace_pattern({?MODULE,seq,'_'}, restart, [call_count]),
+ {call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count),
+ 1 = erlang:trace_pattern({?MODULE,seq,'_'}, false, [call_count]),
+ {call_count,false} = erlang:trace_info({?MODULE,seq,3}, call_count),
+ {all,false} = erlang:trace_info({?MODULE,seq,3}, all),
%%
- ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
+ P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
pause_and_restart_test() ->
- ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
- ?line M = 100,
+ P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
+ M = 100,
%%
- ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, true, [call_count]),
- ?line {call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count),
- ?line L = seq(1, M, fun(X) -> X+1 end),
- ?line {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
- ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, pause, [call_count]),
- ?line {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
- ?line L = seq(1, M, fun(X) -> X+1 end),
- ?line {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
- ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, restart, [call_count]),
- ?line {call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count),
- ?line L = seq(1, M, fun(X) -> X+1 end),
- ?line {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
+ 1 = erlang:trace_pattern({?MODULE,seq,'_'}, true, [call_count]),
+ {call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count),
+ L = seq(1, M, fun(X) -> X+1 end),
+ {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
+ 1 = erlang:trace_pattern({?MODULE,seq,'_'}, pause, [call_count]),
+ {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
+ L = seq(1, M, fun(X) -> X+1 end),
+ {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
+ 1 = erlang:trace_pattern({?MODULE,seq,'_'}, restart, [call_count]),
+ {call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count),
+ L = seq(1, M, fun(X) -> X+1 end),
+ {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
%%
- ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
+ P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
combo_test() ->
- ?line Self = self(),
-
- ?line MetaMatchSpec = [{'_',[],[{return_trace}]}],
- ?line Flags = lists:sort([call, return_to]),
- ?line LocalTracer = spawn_link(fun () -> relay_n(5, Self) end),
- ?line MetaTracer = spawn_link(fun () -> relay_n(9, Self) end),
- ?line 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, [], [local]),
- ?line 2 = erlang:trace_pattern({?MODULE,seq_r,'_'},
- MetaMatchSpec,
- [{meta,MetaTracer}, call_count]),
- ?line 1 = erlang:trace(Self, true, [{tracer,LocalTracer} | Flags]),
+ Self = self(),
+
+ MetaMatchSpec = [{'_',[],[{return_trace}]}],
+ Flags = lists:sort([call, return_to]),
+ LocalTracer = spawn_link(fun () -> relay_n(5, Self) end),
+ MetaTracer = spawn_link(fun () -> relay_n(9, Self) end),
+ 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, [], [local]),
+ 2 = erlang:trace_pattern({?MODULE,seq_r,'_'},
+ MetaMatchSpec,
+ [{meta,MetaTracer}, call_count]),
+ 1 = erlang:trace(Self, true, [{tracer,LocalTracer} | Flags]),
%%
- ?line {traced,local} =
- erlang:trace_info({?MODULE,seq_r,3}, traced),
- ?line {match_spec,[]} =
- erlang:trace_info({?MODULE,seq_r,3}, match_spec),
- ?line {meta,MetaTracer} =
- erlang:trace_info({?MODULE,seq_r,3}, meta),
- ?line {meta_match_spec,MetaMatchSpec} =
- erlang:trace_info({?MODULE,seq_r,3}, meta_match_spec),
- ?line {call_count,0} =
- erlang:trace_info({?MODULE,seq_r,3}, call_count),
+ {traced,local} =
+ erlang:trace_info({?MODULE,seq_r,3}, traced),
+ {match_spec,[]} =
+ erlang:trace_info({?MODULE,seq_r,3}, match_spec),
+ {meta,MetaTracer} =
+ erlang:trace_info({?MODULE,seq_r,3}, meta),
+ {meta_match_spec,MetaMatchSpec} =
+ erlang:trace_info({?MODULE,seq_r,3}, meta_match_spec),
+ {call_count,0} =
+ erlang:trace_info({?MODULE,seq_r,3}, call_count),
%%
- ?line {all,[_|_]=TraceInfo} =
- erlang:trace_info({?MODULE,seq_r,3}, all),
- ?line {value,{traced,local}} =
- lists:keysearch(traced, 1, TraceInfo),
- ?line {value,{match_spec,[]}} =
- lists:keysearch(match_spec, 1, TraceInfo),
- ?line {value,{meta,MetaTracer}} =
- lists:keysearch(meta, 1, TraceInfo),
- ?line {value,{meta_match_spec,MetaMatchSpec}} =
- lists:keysearch(meta_match_spec, 1, TraceInfo),
- ?line {value,{call_count,0}} =
- lists:keysearch(call_count, 1, TraceInfo),
+ {all,[_|_]=TraceInfo} =
+ erlang:trace_info({?MODULE,seq_r,3}, all),
+ {value,{traced,local}} =
+ lists:keysearch(traced, 1, TraceInfo),
+ {value,{match_spec,[]}} =
+ lists:keysearch(match_spec, 1, TraceInfo),
+ {value,{meta,MetaTracer}} =
+ lists:keysearch(meta, 1, TraceInfo),
+ {value,{meta_match_spec,MetaMatchSpec}} =
+ lists:keysearch(meta_match_spec, 1, TraceInfo),
+ {value,{call_count,0}} =
+ lists:keysearch(call_count, 1, TraceInfo),
%%
- ?line [3,2,1] = seq_r(1, 3, fun(X) -> X+1 end),
+ [3,2,1] = seq_r(1, 3, fun(X) -> X+1 end),
%%
- ?line List = collect(100),
- ?line {MetaR, LocalR} =
- lists:foldl(
- fun ({P,X}, {M,L}) when P == MetaTracer ->
- {[X|M],L};
- ({P,X}, {M,L}) when P == LocalTracer ->
- {M,[X|L]}
- end,
- {[],[]},
- List),
- ?line Meta = lists:reverse(MetaR),
- ?line Local = lists:reverse(LocalR),
- ?line [?CTT(Self,{?MODULE,seq_r,[1,3,_]}),
- ?CTT(Self,{?MODULE,seq_r,[1,3,_,[]]}),
- ?CTT(Self,{?MODULE,seq_r,[2,3,_,[1]]}),
- ?CTT(Self,{?MODULE,seq_r,[3,3,_,[2,1]]}),
- ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]),
- ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]),
- ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]),
- ?RFT(Self,{?MODULE,seq_r,3},[3,2,1])] = Meta,
- ?line [?CT(Self,{?MODULE,seq_r,[1,3,_]}),
- ?CT(Self,{?MODULE,seq_r,[1,3,_,[]]}),
- ?CT(Self,{?MODULE,seq_r,[2,3,_,[1]]}),
- ?CT(Self,{?MODULE,seq_r,[3,3,_,[2,1]]}),
- ?RT(Self,{?MODULE,combo_test,0})] = Local,
- ?line {call_count,1} = erlang:trace_info({?MODULE,seq_r,3}, call_count),
- ?line {call_count,3} = erlang:trace_info({?MODULE,seq_r,4}, call_count),
+ List = collect(100),
+ {MetaR, LocalR} =
+ lists:foldl(
+ fun ({P,X}, {M,L}) when P == MetaTracer ->
+ {[X|M],L};
+ ({P,X}, {M,L}) when P == LocalTracer ->
+ {M,[X|L]}
+ end,
+ {[],[]},
+ List),
+ Meta = lists:reverse(MetaR),
+ Local = lists:reverse(LocalR),
+ [?CTT(Self,{?MODULE,seq_r,[1,3,_]}),
+ ?CTT(Self,{?MODULE,seq_r,[1,3,_,[]]}),
+ ?CTT(Self,{?MODULE,seq_r,[2,3,_,[1]]}),
+ ?CTT(Self,{?MODULE,seq_r,[3,3,_,[2,1]]}),
+ ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]),
+ ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]),
+ ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]),
+ ?RFT(Self,{?MODULE,seq_r,3},[3,2,1])] = Meta,
+ [?CT(Self,{?MODULE,seq_r,[1,3,_]}),
+ ?CT(Self,{?MODULE,seq_r,[1,3,_,[]]}),
+ ?CT(Self,{?MODULE,seq_r,[2,3,_,[1]]}),
+ ?CT(Self,{?MODULE,seq_r,[3,3,_,[2,1]]}),
+ ?RT(Self,{?MODULE,combo_test,0})] = Local,
+ {call_count,1} = erlang:trace_info({?MODULE,seq_r,3}, call_count),
+ {call_count,3} = erlang:trace_info({?MODULE,seq_r,4}, call_count),
%%
- ?line erlang:trace_pattern({'_','_','_'}, false, [local,meta,call_count]),
- ?line erlang:trace_pattern(on_load, false, [local,meta,call_count]),
- ?line erlang:trace(all, false, [all]),
+ erlang:trace_pattern({'_','_','_'}, false, [local,meta,call_count]),
+ erlang:trace_pattern(on_load, false, [local,meta,call_count]),
+ erlang:trace(all, false, [all]),
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -352,8 +336,8 @@ relay_n(0, _) ->
ok;
relay_n(N, Dest) ->
receive Msg ->
- Dest ! {self(), Msg},
- relay_n(N-1, Dest)
+ Dest ! {self(), Msg},
+ relay_n(N-1, Dest)
end.
@@ -367,15 +351,15 @@ collect(Time) ->
collect(A, 0) ->
receive
- Mess ->
- collect([Mess | A], 0)
+ Mess ->
+ collect([Mess | A], 0)
after 0 ->
- A
+ A
end;
collect(A, Ref) ->
receive
- {timeout, Ref, done} ->
- collect(A, 0);
- Mess ->
- collect([Mess | A], Ref)
+ {timeout, Ref, done} ->
+ collect(A, 0);
+ Mess ->
+ collect([Mess | A], Ref)
end.
diff --git a/erts/emulator/test/trace_call_time_SUITE.erl b/erts/emulator/test/trace_call_time_SUITE.erl
index f359e1bd80..26f96a1766 100644
--- a/erts/emulator/test/trace_call_time_SUITE.erl
+++ b/erts/emulator/test/trace_call_time_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -58,32 +58,30 @@
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
%% When run in test server.
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
+-export([all/0, suite/0,
init_per_testcase/2, end_per_testcase/2, not_run/1]).
-export([basic/1, on_and_off/1, info/1,
pause_and_restart/1, scheduling/1, called_function/1, combo/1,
bif/1, nif/1]).
init_per_testcase(_Case, Config) ->
- ?line Dog=test_server:timetrap(test_server:seconds(400)),
erlang:trace_pattern({'_','_','_'}, false, [local,meta,call_time,call_count]),
erlang:trace_pattern(on_load, false, [local,meta,call_time,call_count]),
timer:now_diff(now(),now()),
- [{watchdog, Dog}|Config].
+ Config.
-end_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, _Config) ->
erlang:trace_pattern({'_','_','_'}, false, [local,meta,call_time,call_count]),
erlang:trace_pattern(on_load, false, [local,meta,call_time,call_count]),
erlang:trace(all, false, [all]),
- Dog=?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
ok.
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 10}}].
all() ->
case test_server:is_native(trace_call_time_SUITE) of
@@ -93,382 +91,339 @@ all() ->
combo, bif, nif, called_function, dead_tracer]
end.
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
not_run(Config) when is_list(Config) ->
{skipped,"Native code"}.
-basic(suite) ->
- [];
-basic(doc) ->
- ["Tests basic call count trace"];
+%% Tests basic call time trace
basic(Config) when is_list(Config) ->
- ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
- ?line M = 1000,
+ P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
+ M = 1000,
%%
- ?line 1 = erlang:trace_pattern({?MODULE,seq, '_'}, true, [call_time]),
- ?line 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, true, [call_time]),
- ?line Pid = setup(),
- ?line {L, T1} = execute(Pid, fun() -> seq(1, M, fun(X) -> (X+1) end) end),
- ?line ok = check_trace_info({?MODULE, seq, 3}, [{Pid, M, 0, 0}], T1),
- ?line ok = check_trace_info({?MODULE, seq_r, 3}, [], none),
-
- ?line {Lr, T2} = execute(Pid, fun() -> seq_r(1, M, fun(X) -> (X+1) end) end),
- ?line ok = check_trace_info({?MODULE, seq, 3}, [{Pid, M, 0, 0}], T1),
- ?line ok = check_trace_info({?MODULE, seq_r, 3}, [{Pid, 1, 0, 0}], T2/M),
- ?line ok = check_trace_info({?MODULE, seq_r, 4}, [{Pid, M, 0, 0}], T2),
- ?line L = lists:reverse(Lr),
+ 1 = erlang:trace_pattern({?MODULE,seq, '_'}, true, [call_time]),
+ 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, true, [call_time]),
+ Pid = setup(),
+ {L, T1} = execute(Pid, fun() -> seq(1, M, fun(X) -> (X+1) end) end),
+ ok = check_trace_info({?MODULE, seq, 3}, [{Pid, M, 0, 0}], T1),
+ ok = check_trace_info({?MODULE, seq_r, 3}, [], none),
+
+ {Lr, T2} = execute(Pid, fun() -> seq_r(1, M, fun(X) -> (X+1) end) end),
+ ok = check_trace_info({?MODULE, seq, 3}, [{Pid, M, 0, 0}], T1),
+ ok = check_trace_info({?MODULE, seq_r, 3}, [{Pid, 1, 0, 0}], T2/M),
+ ok = check_trace_info({?MODULE, seq_r, 4}, [{Pid, M, 0, 0}], T2),
+ L = lists:reverse(Lr),
%%
- ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
- ?line Pid ! quit,
+ P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
+ Pid ! quit,
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-on_and_off(suite) ->
- [];
-on_and_off(doc) ->
- ["Tests turning trace parameters on and off"];
+%% "Tests turning trace parameters on and off
on_and_off(Config) when is_list(Config) ->
- ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
- ?line M = 100,
+ P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
+ M = 100,
%%
- ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, true, [call_time]),
- ?line Pid = setup(),
- ?line {L, T1} = execute(Pid, {?MODULE, seq, [1, M, fun(X) -> X+1 end]}),
- ?line ok = check_trace_info({?MODULE, seq, 3}, [{Pid, M, 0, 0}], T1),
-
- ?line N = erlang:trace_pattern({?MODULE,'_','_'}, true, [call_time]),
- ?line {L, T2} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end),
- ?line ok = check_trace_info({?MODULE, seq, 3}, [{Pid, M, 0, 0}], T2),
-
- ?line P = erlang:trace_pattern({'_','_','_'}, true, [call_time]),
- ?line {L, T3} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end),
- ?line ok = check_trace_info({?MODULE, seq, 3}, [{Pid, M, 0, 0}], T3),
-
- ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, false, [call_time]),
- ?line ok = check_trace_info({?MODULE, seq, 3}, false, none),
- ?line {L, _T4} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end),
- ?line ok = check_trace_info({?MODULE, seq, 3}, false, none),
- ?line ok = check_trace_info({?MODULE, seq_r, 4}, [], none),
- ?line {Lr, T5} = execute(Pid, fun() -> seq_r(1, M, fun(X) -> X+1 end) end),
- ?line ok = check_trace_info({?MODULE, seq_r, 4}, [{Pid,M,0,0}], T5),
-
- ?line N = erlang:trace_pattern({?MODULE,'_','_'}, false, [call_time]),
- ?line ok = check_trace_info({?MODULE, seq_r, 4}, false, none),
- ?line {Lr, _T6} = execute(Pid, fun() -> seq_r(1, M, fun(X) -> X+1 end) end),
- ?line ok = check_trace_info({?MODULE, seq_r, 4}, false, none),
- ?line L = lists:reverse(Lr),
+ 1 = erlang:trace_pattern({?MODULE,seq,'_'}, true, [call_time]),
+ Pid = setup(),
+ {L, T1} = execute(Pid, {?MODULE, seq, [1, M, fun(X) -> X+1 end]}),
+ ok = check_trace_info({?MODULE, seq, 3}, [{Pid, M, 0, 0}], T1),
+
+ N = erlang:trace_pattern({?MODULE,'_','_'}, true, [call_time]),
+ {L, T2} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end),
+ ok = check_trace_info({?MODULE, seq, 3}, [{Pid, M, 0, 0}], T2),
+
+ P = erlang:trace_pattern({'_','_','_'}, true, [call_time]),
+ {L, T3} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end),
+ ok = check_trace_info({?MODULE, seq, 3}, [{Pid, M, 0, 0}], T3),
+
+ 1 = erlang:trace_pattern({?MODULE,seq,'_'}, false, [call_time]),
+ ok = check_trace_info({?MODULE, seq, 3}, false, none),
+ {L, _T4} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end),
+ ok = check_trace_info({?MODULE, seq, 3}, false, none),
+ ok = check_trace_info({?MODULE, seq_r, 4}, [], none),
+ {Lr, T5} = execute(Pid, fun() -> seq_r(1, M, fun(X) -> X+1 end) end),
+ ok = check_trace_info({?MODULE, seq_r, 4}, [{Pid,M,0,0}], T5),
+
+ N = erlang:trace_pattern({?MODULE,'_','_'}, false, [call_time]),
+ ok = check_trace_info({?MODULE, seq_r, 4}, false, none),
+ {Lr, _T6} = execute(Pid, fun() -> seq_r(1, M, fun(X) -> X+1 end) end),
+ ok = check_trace_info({?MODULE, seq_r, 4}, false, none),
+ L = lists:reverse(Lr),
%%
- ?line Pid ! quit,
- ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
+ Pid ! quit,
+ P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-info(suite) ->
- [];
-info(doc) ->
- ["Tests the trace_info BIF"];
+%% Tests the trace_info BIF
info(Config) when is_list(Config) ->
- ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
+ P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
%%
- ?line 1 = erlang:trace_pattern({?MODULE,seq,3}, true, [call_time]),
- ?line {call_time,[]} = erlang:trace_info({?MODULE,seq,3}, call_time),
- ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, pause, [call_time]),
- ?line {call_time,[]} = erlang:trace_info({?MODULE,seq,3}, call_time),
- ?line {all,[_|_]=L} = erlang:trace_info({?MODULE,seq,3}, all),
- ?line {value,{call_time,[]}} = lists:keysearch(call_time, 1, L),
- ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, restart, [call_time]),
- ?line {call_time,[]} = erlang:trace_info({?MODULE,seq,3}, call_time),
- ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, false, [call_time]),
- ?line {call_time,false} = erlang:trace_info({?MODULE,seq,3}, call_time),
- ?line {all,false} = erlang:trace_info({?MODULE,seq,3}, all),
+ 1 = erlang:trace_pattern({?MODULE,seq,3}, true, [call_time]),
+ {call_time,[]} = erlang:trace_info({?MODULE,seq,3}, call_time),
+ 1 = erlang:trace_pattern({?MODULE,seq,'_'}, pause, [call_time]),
+ {call_time,[]} = erlang:trace_info({?MODULE,seq,3}, call_time),
+ {all,[_|_]=L} = erlang:trace_info({?MODULE,seq,3}, all),
+ {value,{call_time,[]}} = lists:keysearch(call_time, 1, L),
+ 1 = erlang:trace_pattern({?MODULE,seq,'_'}, restart, [call_time]),
+ {call_time,[]} = erlang:trace_info({?MODULE,seq,3}, call_time),
+ 1 = erlang:trace_pattern({?MODULE,seq,'_'}, false, [call_time]),
+ {call_time,false} = erlang:trace_info({?MODULE,seq,3}, call_time),
+ {all,false} = erlang:trace_info({?MODULE,seq,3}, all),
%%
- ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
+ P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-pause_and_restart(suite) ->
- [];
-pause_and_restart(doc) ->
- ["Tests pausing and restarting call time counters"];
+%% Tests pausing and restarting call time counters
pause_and_restart(Config) when is_list(Config) ->
- ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
- ?line M = 100,
- ?line Pid = setup(),
+ P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
+ M = 100,
+ Pid = setup(),
%%
- ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, true, [call_time]),
- ?line ok = check_trace_info({?MODULE, seq, 3}, [], none),
- ?line {L, T1} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end),
- ?line ok = check_trace_info({?MODULE, seq, 3}, [{Pid,M,0,0}], T1),
- ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, pause, [call_time]),
- ?line ok = check_trace_info({?MODULE, seq, 3}, [{Pid,M,0,0}], T1),
- ?line {L, T2} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end),
- ?line ok = check_trace_info({?MODULE, seq, 3}, [{Pid,M,0,0}], T2),
- ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, restart, [call_time]),
- ?line ok = check_trace_info({?MODULE, seq, 3}, [], none),
- ?line {L, T3} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end),
- ?line ok = check_trace_info({?MODULE, seq, 3}, [{Pid,M,0,0}], T3),
+ 1 = erlang:trace_pattern({?MODULE,seq,'_'}, true, [call_time]),
+ ok = check_trace_info({?MODULE, seq, 3}, [], none),
+ {L, T1} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end),
+ ok = check_trace_info({?MODULE, seq, 3}, [{Pid,M,0,0}], T1),
+ 1 = erlang:trace_pattern({?MODULE,seq,'_'}, pause, [call_time]),
+ ok = check_trace_info({?MODULE, seq, 3}, [{Pid,M,0,0}], T1),
+ {L, T2} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end),
+ ok = check_trace_info({?MODULE, seq, 3}, [{Pid,M,0,0}], T2),
+ 1 = erlang:trace_pattern({?MODULE,seq,'_'}, restart, [call_time]),
+ ok = check_trace_info({?MODULE, seq, 3}, [], none),
+ {L, T3} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end),
+ ok = check_trace_info({?MODULE, seq, 3}, [{Pid,M,0,0}], T3),
%%
- ?line Pid ! quit,
- ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
+ Pid ! quit,
+ P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-scheduling(suite) ->
- [];
-scheduling(doc) ->
- ["Tests in/out scheduling of call time counters"];
+%% Tests in/out scheduling of call time counters
scheduling(Config) when is_list(Config) ->
- ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
- ?line M = 1000000,
- ?line Np = erlang:system_info(schedulers_online),
- ?line F = 12,
+ P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
+ M = 1000000,
+ Np = erlang:system_info(schedulers_online),
+ F = 12,
%% setup load processes
%% (single, no internal calls)
- ?line erlang:trace_pattern({?MODULE,loaded,1}, true, [call_time]),
+ erlang:trace_pattern({?MODULE,loaded,1}, true, [call_time]),
- ?line Pids = [setup() || _ <- lists:seq(1, F*Np)],
- ?line {_Ls,T1} = execute(Pids, {?MODULE,loaded,[M]}),
- ?line [Pid ! quit || Pid <- Pids],
+ Pids = [setup() || _ <- lists:seq(1, F*Np)],
+ {_Ls,T1} = execute(Pids, {?MODULE,loaded,[M]}),
+ [Pid ! quit || Pid <- Pids],
%% logic dictates that each process will get ~ 1/F of the schedulers time
- ?line {call_time, CT} = erlang:trace_info({?MODULE,loaded,1}, call_time),
-
- ?line lists:foreach(fun (Pid) ->
- ?line ok = case check_process_time(lists:keysearch(Pid, 1, CT), M, F, T1) of
- schedule_time_error ->
- test_server:comment("Warning: Failed time ratio"),
- ok;
- Other -> Other
- end
- end, Pids),
- ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
+ {call_time, CT} = erlang:trace_info({?MODULE,loaded,1}, call_time),
+
+ lists:foreach(fun (Pid) ->
+ ok = case check_process_time(lists:keysearch(Pid, 1, CT), M, F, T1) of
+ schedule_time_error ->
+ test_server:comment("Warning: Failed time ratio"),
+ ok;
+ Other -> Other
+ end
+ end, Pids),
+ P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-combo(suite) ->
- [];
-combo(doc) ->
- ["Tests combining local call trace and meta trace with call time trace"];
+%% "Tests combining local call trace and meta trace with call time trace
combo(Config) when is_list(Config) ->
- ?line Self = self(),
- ?line Nbc = 3,
- ?line MetaMs = [{'_',[],[{return_trace}]}],
- ?line Flags = lists:sort([call, return_to]),
- ?line LocalTracer = spawn_link(fun () -> relay_n(5 + Nbc + 3, Self) end),
- ?line MetaTracer = spawn_link(fun () -> relay_n(9 + Nbc + 3, Self) end),
- ?line 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, [], [local]),
- ?line 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, true, [call_time]),
- ?line 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, MetaMs, [{meta,MetaTracer}]),
- ?line 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, true, [call_count]),
+ Self = self(),
+ Nbc = 3,
+ MetaMs = [{'_',[],[{return_trace}]}],
+ Flags = lists:sort([call, return_to]),
+ LocalTracer = spawn_link(fun () -> relay_n(5 + Nbc + 3, Self) end),
+ MetaTracer = spawn_link(fun () -> relay_n(9 + Nbc + 3, Self) end),
+ 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, [], [local]),
+ 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, true, [call_time]),
+ 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, MetaMs, [{meta,MetaTracer}]),
+ 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, true, [call_count]),
% bifs
- ?line 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, [], [local]),
- ?line 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_time]),
- ?line 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, MetaMs, [{meta,MetaTracer}]),
+ 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, [], [local]),
+ 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_time]),
+ 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, MetaMs, [{meta,MetaTracer}]),
%% not implemented
- %?line 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_count]),
+ %2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_count]),
- ?line 1 = erlang:trace(Self, true, [{tracer,LocalTracer} | Flags]),
+ 1 = erlang:trace(Self, true, [{tracer,LocalTracer} | Flags]),
%%
- ?line {traced,local} =
- erlang:trace_info({?MODULE,seq_r,3}, traced),
- ?line {match_spec,[]} =
- erlang:trace_info({?MODULE,seq_r,3}, match_spec),
- ?line {meta,MetaTracer} =
- erlang:trace_info({?MODULE,seq_r,3}, meta),
- ?line {meta_match_spec,MetaMs} =
- erlang:trace_info({?MODULE,seq_r,3}, meta_match_spec),
- ?line ok = check_trace_info({?MODULE, seq_r, 3}, [], none),
+ {traced,local} =
+ erlang:trace_info({?MODULE,seq_r,3}, traced),
+ {match_spec,[]} =
+ erlang:trace_info({?MODULE,seq_r,3}, match_spec),
+ {meta,MetaTracer} =
+ erlang:trace_info({?MODULE,seq_r,3}, meta),
+ {meta_match_spec,MetaMs} =
+ erlang:trace_info({?MODULE,seq_r,3}, meta_match_spec),
+ ok = check_trace_info({?MODULE, seq_r, 3}, [], none),
%% check empty trace_info for ?MODULE:seq_r/3
- ?line {all,[_|_]=TraceInfo} = erlang:trace_info({?MODULE,seq_r,3}, all),
- ?line {value,{traced,local}} = lists:keysearch(traced, 1, TraceInfo),
- ?line {value,{match_spec,[]}} = lists:keysearch(match_spec, 1, TraceInfo),
- ?line {value,{meta,MetaTracer}} = lists:keysearch(meta, 1, TraceInfo),
- ?line {value,{meta_match_spec,MetaMs}} = lists:keysearch(meta_match_spec, 1, TraceInfo),
- ?line {value,{call_count,0}} = lists:keysearch(call_count, 1, TraceInfo),
- ?line {value,{call_time,[]}} = lists:keysearch(call_time, 1, TraceInfo),
+ {all,[_|_]=TraceInfo} = erlang:trace_info({?MODULE,seq_r,3}, all),
+ {value,{traced,local}} = lists:keysearch(traced, 1, TraceInfo),
+ {value,{match_spec,[]}} = lists:keysearch(match_spec, 1, TraceInfo),
+ {value,{meta,MetaTracer}} = lists:keysearch(meta, 1, TraceInfo),
+ {value,{meta_match_spec,MetaMs}} = lists:keysearch(meta_match_spec, 1, TraceInfo),
+ {value,{call_count,0}} = lists:keysearch(call_count, 1, TraceInfo),
+ {value,{call_time,[]}} = lists:keysearch(call_time, 1, TraceInfo),
%% check empty trace_info for erlang:term_to_binary/1
- ?line {all, [_|_] = TraceInfoBif} = erlang:trace_info({erlang, term_to_binary, 1}, all),
- ?line {value,{traced,local}} = lists:keysearch(traced, 1, TraceInfoBif),
- ?line {value,{match_spec,[]}} = lists:keysearch(match_spec, 1, TraceInfoBif),
- ?line {value,{meta, MetaTracer}} = lists:keysearch(meta, 1, TraceInfoBif),
- ?line {value,{meta_match_spec,MetaMs}} = lists:keysearch(meta_match_spec, 1, TraceInfoBif),
+ {all, [_|_] = TraceInfoBif} = erlang:trace_info({erlang, term_to_binary, 1}, all),
+ {value,{traced,local}} = lists:keysearch(traced, 1, TraceInfoBif),
+ {value,{match_spec,[]}} = lists:keysearch(match_spec, 1, TraceInfoBif),
+ {value,{meta, MetaTracer}} = lists:keysearch(meta, 1, TraceInfoBif),
+ {value,{meta_match_spec,MetaMs}} = lists:keysearch(meta_match_spec, 1, TraceInfoBif),
%% not implemented
- ?line {value,{call_count,false}} = lists:keysearch(call_count, 1, TraceInfoBif),
- %?line {value,{call_count,0}} = lists:keysearch(call_count, 1, TraceInfoBif),
- ?line {value,{call_time,[]}} = lists:keysearch(call_time, 1, TraceInfoBif),
+ {value,{call_count,false}} = lists:keysearch(call_count, 1, TraceInfoBif),
+ %{value,{call_count,0}} = lists:keysearch(call_count, 1, TraceInfoBif),
+ {value,{call_time,[]}} = lists:keysearch(call_time, 1, TraceInfoBif),
%%
- ?line [3,2,1] = seq_r(1, 3, fun(X) -> X+1 end),
- ?line T0 = erlang:monotonic_time(),
- ?line with_bif(Nbc),
- ?line T1 = erlang:monotonic_time(),
- ?line TimeB = erlang:convert_time_unit(T1-T0, native, micro_seconds),
+ [3,2,1] = seq_r(1, 3, fun(X) -> X+1 end),
+ T0 = erlang:monotonic_time(),
+ with_bif(Nbc),
+ T1 = erlang:monotonic_time(),
+ TimeB = erlang:convert_time_unit(T1-T0, native, microsecond),
%%
- ?line List = collect(100),
- ?line {MetaR, LocalR} =
- lists:foldl(
- fun ({P,X}, {M,L}) when P == MetaTracer ->
- {[X|M],L};
- ({P,X}, {M,L}) when P == LocalTracer ->
- {M,[X|L]}
- end,
- {[],[]},
- List),
- ?line Meta = lists:reverse(MetaR),
- ?line Local = lists:reverse(LocalR),
-
- ?line [?CTT(Self,{?MODULE,seq_r,[1,3,_]}),
- ?CTT(Self,{?MODULE,seq_r,[1,3,_,[]]}),
- ?CTT(Self,{?MODULE,seq_r,[2,3,_,[1]]}),
- ?CTT(Self,{?MODULE,seq_r,[3,3,_,[2,1]]}),
- ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]),
- ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]),
- ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]),
- ?RFT(Self,{?MODULE,seq_r,3},[3,2,1]),
- ?CTT(Self,{erlang,term_to_binary,[3]}), % bif
- ?RFT(Self,{erlang,term_to_binary,1},<<131,97,3>>),
- ?CTT(Self,{erlang,term_to_binary,[2]}),
- ?RFT(Self,{erlang,term_to_binary,1},<<131,97,2>>)
- ] = Meta,
-
- ?line [?CT(Self,{?MODULE,seq_r,[1,3,_]}),
- ?CT(Self,{?MODULE,seq_r,[1,3,_,[]]}),
- ?CT(Self,{?MODULE,seq_r,[2,3,_,[1]]}),
- ?CT(Self,{?MODULE,seq_r,[3,3,_,[2,1]]}),
- ?RT(Self,{?MODULE,combo,1}),
- ?CT(Self,{erlang,term_to_binary,[3]}), % bif
- ?RT(Self,{?MODULE,with_bif,1}),
- ?CT(Self,{erlang,term_to_binary,[2]}),
- ?RT(Self,{?MODULE,with_bif,1})
- ] = Local,
-
- ?line ok = check_trace_info({?MODULE, seq_r, 3}, [{Self,1,0,0}], 1),
- ?line ok = check_trace_info({?MODULE, seq_r, 4}, [{Self,3,0,0}], 1),
- ?line ok = check_trace_info({?MODULE, seq_r, 3}, [{Self,1,0,0}], 1),
- ?line ok = check_trace_info({?MODULE, seq_r, 4}, [{Self,3,0,0}], 1),
- ?line ok = check_trace_info({erlang, term_to_binary, 1}, [{self(), Nbc - 1, 0, 0}], TimeB),
+ List = collect(100),
+ {MetaR, LocalR} =
+ lists:foldl(
+ fun ({P,X}, {M,L}) when P == MetaTracer ->
+ {[X|M],L};
+ ({P,X}, {M,L}) when P == LocalTracer ->
+ {M,[X|L]}
+ end,
+ {[],[]},
+ List),
+ Meta = lists:reverse(MetaR),
+ Local = lists:reverse(LocalR),
+
+ [?CTT(Self,{?MODULE,seq_r,[1,3,_]}),
+ ?CTT(Self,{?MODULE,seq_r,[1,3,_,[]]}),
+ ?CTT(Self,{?MODULE,seq_r,[2,3,_,[1]]}),
+ ?CTT(Self,{?MODULE,seq_r,[3,3,_,[2,1]]}),
+ ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]),
+ ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]),
+ ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]),
+ ?RFT(Self,{?MODULE,seq_r,3},[3,2,1]),
+ ?CTT(Self,{erlang,term_to_binary,[3]}), % bif
+ ?RFT(Self,{erlang,term_to_binary,1},<<131,97,3>>),
+ ?CTT(Self,{erlang,term_to_binary,[2]}),
+ ?RFT(Self,{erlang,term_to_binary,1},<<131,97,2>>)
+ ] = Meta,
+
+ [?CT(Self,{?MODULE,seq_r,[1,3,_]}),
+ ?CT(Self,{?MODULE,seq_r,[1,3,_,[]]}),
+ ?CT(Self,{?MODULE,seq_r,[2,3,_,[1]]}),
+ ?CT(Self,{?MODULE,seq_r,[3,3,_,[2,1]]}),
+ ?RT(Self,{?MODULE,combo,1}),
+ ?CT(Self,{erlang,term_to_binary,[3]}), % bif
+ ?RT(Self,{?MODULE,with_bif,1}),
+ ?CT(Self,{erlang,term_to_binary,[2]}),
+ ?RT(Self,{?MODULE,with_bif,1})
+ ] = Local,
+
+ ok = check_trace_info({?MODULE, seq_r, 3}, [{Self,1,0,0}], 1),
+ ok = check_trace_info({?MODULE, seq_r, 4}, [{Self,3,0,0}], 1),
+ ok = check_trace_info({?MODULE, seq_r, 3}, [{Self,1,0,0}], 1),
+ ok = check_trace_info({?MODULE, seq_r, 4}, [{Self,3,0,0}], 1),
+ ok = check_trace_info({erlang, term_to_binary, 1}, [{self(), Nbc - 1, 0, 0}], TimeB),
%%
- ?line erlang:trace_pattern({'_','_','_'}, false, [local,meta,call_time]),
- ?line erlang:trace_pattern(on_load, false, [local,meta,call_time]),
- ?line erlang:trace(all, false, [all]),
+ erlang:trace_pattern({'_','_','_'}, false, [local,meta,call_time]),
+ erlang:trace_pattern(on_load, false, [local,meta,call_time]),
+ erlang:trace(all, false, [all]),
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-bif(suite) ->
- [];
-bif(doc) ->
- ["Tests tracing of bifs"];
+%% Tests tracing of bifs
bif(Config) when is_list(Config) ->
- ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
- ?line M = 1000000,
+ P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
+ M = 5000000,
%%
- ?line 2 = erlang:trace_pattern({erlang, binary_to_term, '_'}, true, [call_time]),
- ?line 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_time]),
- ?line Pid = setup(),
- ?line {L, T1} = execute(Pid, fun() -> with_bif(M) end),
+ 2 = erlang:trace_pattern({erlang, binary_to_term, '_'}, true, [call_time]),
+ 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_time]),
+ Pid = setup(),
+ {L, T1} = execute(Pid, fun() -> with_bif(M) end),
- ?line ok = check_trace_info({erlang, binary_to_term, 1}, [{Pid, M - 1, 0, 0}], T1/2),
- ?line ok = check_trace_info({erlang, term_to_binary, 1}, [{Pid, M - 1, 0, 0}], T1/2),
+ ok = check_trace_info({erlang, binary_to_term, 1}, [{Pid, M - 1, 0, 0}], T1/2),
+ ok = check_trace_info({erlang, term_to_binary, 1}, [{Pid, M - 1, 0, 0}], T1/2),
% disable term2binary
- ?line 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, false, [call_time]),
+ 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, false, [call_time]),
- ?line {L, T2} = execute(Pid, fun() -> with_bif(M) end),
+ {L, T2} = execute(Pid, fun() -> with_bif(M) end),
- ?line ok = check_trace_info({erlang, binary_to_term, 1}, [{Pid, M*2 - 2, 0, 0}], T1/2 + T2),
- ?line ok = check_trace_info({erlang, term_to_binary, 1}, false, none),
+ ok = check_trace_info({erlang, binary_to_term, 1}, [{Pid, M*2 - 2, 0, 0}], T1/2 + T2),
+ ok = check_trace_info({erlang, term_to_binary, 1}, false, none),
%%
- ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
- ?line Pid ! quit,
+ P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
+ Pid ! quit,
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-nif(suite) ->
- [];
-nif(doc) ->
- ["Tests tracing of nifs"];
+%% Tests tracing of nifs
nif(Config) when is_list(Config) ->
load_nif(Config),
- ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
- ?line M = 1000000,
+ P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
+ M = 5000000,
%%
- ?line 1 = erlang:trace_pattern({?MODULE, nif_dec, '_'}, true, [call_time]),
- ?line 1 = erlang:trace_pattern({?MODULE, with_nif, '_'}, true, [call_time]),
- ?line Pid = setup(),
- ?line {_, T1} = execute(Pid, fun() -> with_nif(M) end),
+ 1 = erlang:trace_pattern({?MODULE, nif_dec, '_'}, true, [call_time]),
+ 1 = erlang:trace_pattern({?MODULE, with_nif, '_'}, true, [call_time]),
+ Pid = setup(),
+ {_, T1} = execute(Pid, fun() -> with_nif(M) end),
% the nif is called M - 1 times, the last time the function with 'with_nif'
% returns ok and does not call the nif.
- ?line ok = check_trace_info({?MODULE, nif_dec, 1}, [{Pid, M-1, 0, 0}], T1/5*4),
- ?line ok = check_trace_info({?MODULE, with_nif, 1}, [{Pid, M, 0, 0}], T1/5),
+ ok = check_trace_info({?MODULE, nif_dec, 1}, [{Pid, M-1, 0, 0}], T1/5*4),
+ ok = check_trace_info({?MODULE, with_nif, 1}, [{Pid, M, 0, 0}], T1/5),
%%
- ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
- ?line Pid ! quit,
+ P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
+ Pid ! quit,
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-called_function(suite) ->
- [];
-called_function(doc) ->
- ["Tests combining nested function calls and that the time accumulates to the right function"];
+%% Tests combining nested function calls and that the time accumulates to the right function
called_function(Config) when is_list(Config) ->
- ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
- ?line M = 2100,
- ?line Pid = setup(),
+ P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
+ M = 2100,
+ Pid = setup(),
%%
- ?line 1 = erlang:trace_pattern({?MODULE,a_function,'_'}, true, [call_time]),
- ?line {L, T1} = execute(Pid, {?MODULE, a_function, [M]}),
- ?line ok = check_trace_info({?MODULE, a_function, 1}, [{Pid, M, 0, 0}], T1),
+ 1 = erlang:trace_pattern({?MODULE,a_function,'_'}, true, [call_time]),
+ {L, T1} = execute(Pid, {?MODULE, a_function, [M]}),
+ ok = check_trace_info({?MODULE, a_function, 1}, [{Pid, M, 0, 0}], T1),
- ?line 1 = erlang:trace_pattern({?MODULE,a_called_function,'_'}, true, [call_time]),
- ?line {L, T2} = execute(Pid, {?MODULE, a_function, [M]}),
- ?line ok = check_trace_info({?MODULE, a_function, 1}, [{Pid, M+M, 0, 0}], T1 + M*?SINGLE_CALL_US_TIME),
- ?line ok = check_trace_info({?MODULE, a_called_function, 1}, [{Pid, M, 0, 0}], T2),
+ 1 = erlang:trace_pattern({?MODULE,a_called_function,'_'}, true, [call_time]),
+ {L, T2} = execute(Pid, {?MODULE, a_function, [M]}),
+ ok = check_trace_info({?MODULE, a_function, 1}, [{Pid, M+M, 0, 0}], T1 + M*?SINGLE_CALL_US_TIME),
+ ok = check_trace_info({?MODULE, a_called_function, 1}, [{Pid, M, 0, 0}], T2),
- ?line 1 = erlang:trace_pattern({?MODULE,dec,'_'}, true, [call_time]),
- ?line {L, T3} = execute(Pid, {?MODULE, a_function, [M]}),
- ?line ok = check_trace_info({?MODULE, a_function, 1}, [{Pid, M+M+M, 0, 0}], T1 + (M+M)*?SINGLE_CALL_US_TIME),
- ?line ok = check_trace_info({?MODULE, a_called_function, 1}, [{Pid, M+M, 0, 0}], T2 + M*?SINGLE_CALL_US_TIME ),
- ?line ok = check_trace_info({?MODULE, dec, 1}, [{Pid, M, 0, 0}], T3),
+ 1 = erlang:trace_pattern({?MODULE,dec,'_'}, true, [call_time]),
+ {L, T3} = execute(Pid, {?MODULE, a_function, [M]}),
+ ok = check_trace_info({?MODULE, a_function, 1}, [{Pid, M+M+M, 0, 0}], T1 + (M+M)*?SINGLE_CALL_US_TIME),
+ ok = check_trace_info({?MODULE, a_called_function, 1}, [{Pid, M+M, 0, 0}], T2 + M*?SINGLE_CALL_US_TIME ),
+ ok = check_trace_info({?MODULE, dec, 1}, [{Pid, M, 0, 0}], T3),
- ?line Pid ! quit,
- ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
+ Pid ! quit,
+ P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -482,8 +437,8 @@ dead_tracer(Config) when is_list(Config) ->
Ref = erlang:monitor(process, FirstTracer),
FirstTracer ! quit,
receive
- {'DOWN',Ref,process,FirstTracer,normal} ->
- ok
+ {'DOWN',Ref,process,FirstTracer,normal} ->
+ ok
end,
erlang:yield(),
@@ -513,26 +468,26 @@ dead_tracer(Config) when is_list(Config) ->
other_than_self(Info) ->
[{Pid,MFA} || {MFA,[{Pid,_,_,_}]} <- Info,
- Pid =/= self()].
+ Pid =/= self()].
tell_tracer(Tracer, Fun) ->
Tracer ! {execute,self(),Fun},
receive
- {Tracer,executed} ->
- ok
+ {Tracer,executed} ->
+ ok
end.
tracer() ->
spawn_link(fun Loop() ->
- receive
- quit ->
- ok;
- {execute,From,Fun} ->
- Fun(),
- From ! {self(),executed},
- Loop()
- end
- end).
+ receive
+ quit ->
+ ok;
+ {execute,From,Fun} ->
+ Fun(),
+ From ! {self(),executed},
+ Loop()
+ end
+ end).
turn_on_tracing(Pid) ->
_ = erlang:trace(Pid, true, [call,set_on_spawn]),
@@ -542,18 +497,18 @@ turn_on_tracing(Pid) ->
collect_all_info() ->
collect_all_info([{?MODULE,F,A} || {F,A} <- module_info(functions)] ++
- erlang:system_info(snifs)).
+ erlang:system_info(snifs)).
collect_all_info([MFA|T]) ->
CallTime = erlang:trace_info(MFA, call_time),
erlang:trace_pattern(MFA, restart, [call_time]),
case CallTime of
- {call_time,false} ->
- collect_all_info(T);
- {call_time,[]} ->
- collect_all_info(T);
- {call_time,[_|_]=List} ->
- [{MFA,List}|collect_all_info(T)]
+ {call_time,false} ->
+ collect_all_info(T);
+ {call_time,[]} ->
+ collect_all_info(T);
+ {call_time,[_|_]=List} ->
+ [{MFA,List}|collect_all_info(T)]
end;
collect_all_info([]) -> [].
@@ -566,8 +521,8 @@ collect_all_info([]) -> [].
%% Local helpers
load_nif(Config) ->
- ?line Path = ?config(data_dir, Config),
- ?line ok = erlang:load_nif(filename:join(Path,"trace_nif"), 0).
+ Path = proplists:get_value(data_dir, Config),
+ ok = erlang:load_nif(filename:join(Path,"trace_nif"), 0).
%% Stack recursive seq
@@ -614,39 +569,39 @@ seq_r(Start, Stop, Succ, R) ->
% Check call time tracing data and print mismatches
check_trace_info(Mfa, [{Pid, C,_,_}] = Expect, Time) ->
case erlang:trace_info(Mfa, call_time) of
- % Time tests are somewhat problematic. We want to know if Time (EXPECTED_TIME) and S*1000000 + Us (ACTUAL_TIME)
- % is the same.
- % If the ratio EXPECTED_TIME/ACTUAL_TIME is ~ 1 or if EXPECTED_TIME - ACTUAL_TIME is near zero, the test is ok.
- {call_time,[{Pid,C,S,Us}]} when S >= 0, Us >= 0, abs(1 - Time/(S*1000000 + Us)) < ?R_ERROR; abs(Time - S*1000000 - Us) < ?US_ERROR ->
- ok;
- {call_time,[{Pid,C,S,Us}]} ->
- Sum = S*1000000 + Us,
- io:format("Expected ~p -> {call_time, ~p (Time ~p us)}~n - got ~w s. ~w us. = ~w us. - ~w -> delta ~w (ratio ~.2f, should be 1.0)~n",
- [Mfa, Expect, Time, S, Us, Sum, Time, Sum - Time, Time/Sum]),
- time_error;
- Other ->
- io:format("Expected ~p -> {call_time, ~p (Time ~p us)}~n - got ~p~n", [ Mfa, Expect, Time, Other]),
- time_count_error
+ % Time tests are somewhat problematic. We want to know if Time (EXPECTED_TIME) and S*1000000 + Us (ACTUAL_TIME)
+ % is the same.
+ % If the ratio EXPECTED_TIME/ACTUAL_TIME is ~ 1 or if EXPECTED_TIME - ACTUAL_TIME is near zero, the test is ok.
+ {call_time,[{Pid,C,S,Us}]} when S >= 0, Us >= 0, abs(1 - Time/(S*1000000 + Us)) < ?R_ERROR; abs(Time - S*1000000 - Us) < ?US_ERROR ->
+ ok;
+ {call_time,[{Pid,C,S,Us}]} ->
+ Sum = S*1000000 + Us,
+ io:format("Expected ~p -> {call_time, ~p (Time ~p us)}~n - got ~w s. ~w us. = ~w us. - ~w -> delta ~w (ratio ~.2f, should be 1.0)~n",
+ [Mfa, Expect, Time, S, Us, Sum, Time, Sum - Time, Time/Sum]),
+ time_error;
+ Other ->
+ io:format("Expected ~p -> {call_time, ~p (Time ~p us)}~n - got ~p~n", [ Mfa, Expect, Time, Other]),
+ time_count_error
end;
check_trace_info(Mfa, Expect, _) ->
case erlang:trace_info(Mfa, call_time) of
- {call_time, Expect} ->
- ok;
- Other ->
- io:format("Expected ~p -> {call_time, ~p}~n - got ~p~n", [Mfa, Expect, Other]),
- result_not_expected_error
+ {call_time, Expect} ->
+ ok;
+ Other ->
+ io:format("Expected ~p -> {call_time, ~p}~n - got ~p~n", [Mfa, Expect, Other]),
+ result_not_expected_error
end.
%check process time
check_process_time({value,{Pid, M, S, Us}}, M, F, Time) ->
- ?line Sum = S*1000000 + Us,
+ Sum = S*1000000 + Us,
if
- abs(1 - (F/(Time/Sum))) < ?R_ERROR ->
- ok;
- true ->
- io:format("- Pid ~p, Got ratio ~.2f, expected ratio ~w~n", [Pid, Time/Sum,F]),
- schedule_time_error
+ abs(1 - (F/(Time/Sum))) < ?R_ERROR ->
+ ok;
+ true ->
+ io:format("- Pid ~p, Got ratio ~.2f, expected ratio ~w~n", [Pid, Time/Sum,F]),
+ schedule_time_error
end;
check_process_time(Other, M, _, _) ->
io:format(" - Got ~p, expected count ~w~n", [Other, M]),
@@ -659,8 +614,8 @@ relay_n(0, _) ->
ok;
relay_n(N, Dest) ->
receive Msg ->
- Dest ! {self(), Msg},
- relay_n(N-1, Dest)
+ Dest ! {self(), Msg},
+ relay_n(N-1, Dest)
end.
@@ -674,17 +629,17 @@ collect(Time) ->
collect(A, 0) ->
receive
- Mess ->
- collect([Mess | A], 0)
+ Mess ->
+ collect([Mess | A], 0)
after 0 ->
- A
+ A
end;
collect(A, Ref) ->
receive
- {timeout, Ref, done} ->
- collect(A, 0);
- Mess ->
- collect([Mess | A], Ref)
+ {timeout, Ref, done} ->
+ collect(A, 0);
+ Mess ->
+ collect([Mess | A], Ref)
end.
setup() ->
@@ -700,24 +655,24 @@ execute(Pids, Mfa) when is_list(Pids) ->
[P ! {self(), execute, Mfa} || P <- Pids],
As = [receive {P, answer, Answer} -> Answer end || P <- Pids],
T1 = erlang:monotonic_time(),
- {As, erlang:convert_time_unit(T1-T0, native, micro_seconds)};
+ {As, erlang:convert_time_unit(T1-T0, native, microsecond)};
execute(P, Mfa) ->
T0 = erlang:monotonic_time(),
P ! {self(), execute, Mfa},
A = receive {P, answer, Answer} -> Answer end,
T1 = erlang:monotonic_time(),
- {A, erlang:convert_time_unit(T1-T0, native, micro_seconds)}.
+ {A, erlang:convert_time_unit(T1-T0, native, microsecond)}.
loop() ->
receive
- quit ->
- ok;
- {Pid, execute, Fun } when is_function(Fun) ->
- Pid ! {self(), answer, erlang:apply(Fun, [])},
- loop();
- {Pid, execute, {M, F, A}} ->
- Pid ! {self(), answer, erlang:apply(M, F, A)},
- loop()
+ quit ->
+ ok;
+ {Pid, execute, Fun } when is_function(Fun) ->
+ Pid ! {self(), answer, erlang:apply(Fun, [])},
+ loop();
+ {Pid, execute, {M, F, A}} ->
+ Pid ! {self(), answer, erlang:apply(M, F, A)},
+ loop()
end.
diff --git a/erts/emulator/test/trace_local_SUITE.erl b/erts/emulator/test/trace_local_SUITE.erl
index 7431099340..c297acd78b 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-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -29,69 +29,44 @@
-export([exported/1, exported_wrap/1, loop/4, apply_slave_async/5,
match/2, clause/2, id/1, undef/1, lists_reverse/2]).
-%%
-%% Define to run outside of test server
-%%
-%% (rotten feature)
-%%
-%%-define(STANDALONE,1).
-
+
%%
%% Define for debug output
%%
%%-define(debug,1).
--ifdef(STANDALONE).
--define(config(A,B),config(A,B)).
--export([config/2]).
--define(DEFAULT_RECEIVE_TIMEOUT, 1000).
--else.
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-define(DEFAULT_RECEIVE_TIMEOUT, infinity).
--endif.
-
+
-ifdef(debug).
--ifdef(STANDALONE).
--define(line, erlang:display({?MODULE,?LINE}), ).
--endif.
-define(dbgformat(A,B),io:format(A,B)).
-else.
--ifdef(STANDALONE).
--define(line, noop, ).
--endif.
-define(dbgformat(A,B),noop).
-endif.
-
--ifdef(STANDALONE).
-config(priv_dir,_) ->
- ".".
--else.
%%% When run in test server %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2, basic/1, bit_syntax/1,
- return/1, on_and_off/1, systematic_on_off/1,
- stack_grow/1,info/1, delete/1,
- exception/1, exception_apply/1,
- exception_function/1, exception_apply_function/1,
- exception_nocatch/1, exception_nocatch_apply/1,
- exception_nocatch_function/1, exception_nocatch_apply_function/1,
- exception_meta/1, exception_meta_apply/1,
- exception_meta_function/1, exception_meta_apply_function/1,
- exception_meta_nocatch/1, exception_meta_nocatch_apply/1,
- exception_meta_nocatch_function/1,
- exception_meta_nocatch_apply_function/1,
- concurrency/1,
- init_per_testcase/2, end_per_testcase/2]).
+-export([all/0, suite/0,
+ basic/1, bit_syntax/1,
+ return/1, on_and_off/1, systematic_on_off/1,
+ stack_grow/1,info/1, delete/1,
+ exception/1, exception_apply/1,
+ exception_function/1, exception_apply_function/1,
+ exception_nocatch/1, exception_nocatch_apply/1,
+ exception_nocatch_function/1, exception_nocatch_apply_function/1,
+ exception_meta/1, exception_meta_apply/1,
+ exception_meta_function/1, exception_meta_apply_function/1,
+ exception_meta_nocatch/1, exception_meta_nocatch_apply/1,
+ exception_meta_nocatch_function/1,
+ exception_meta_nocatch_apply_function/1,
+ concurrency/1,
+ init_per_testcase/2, end_per_testcase/2]).
+
init_per_testcase(_Case, Config) ->
- ?line Dog=test_server:timetrap(test_server:minutes(2)),
- [{watchdog, Dog}|Config].
+ Config.
end_per_testcase(_Case, Config) ->
shutdown(),
- Dog=?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
%% Reloading the module will clear all trace patterns, and
%% in a debug-compiled emulator run assertions of the counters
@@ -99,168 +74,127 @@ end_per_testcase(_Case, Config) ->
c:l(?MODULE).
-
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 2}}].
all() ->
case test_server:is_native(trace_local_SUITE) of
- true -> [not_run];
- false ->
- [basic, bit_syntax, return, on_and_off, systematic_on_off,
- stack_grow,
- info, delete, exception, exception_apply,
- exception_function, exception_apply_function,
- exception_nocatch, exception_nocatch_apply,
- exception_nocatch_function,
- exception_nocatch_apply_function, exception_meta,
- exception_meta_apply, exception_meta_function,
- exception_meta_apply_function, exception_meta_nocatch,
- exception_meta_nocatch_apply,
- exception_meta_nocatch_function,
- exception_meta_nocatch_apply_function,
- concurrency]
+ true -> [not_run];
+ false ->
+ [basic, bit_syntax, return, on_and_off, systematic_on_off,
+ stack_grow,
+ info, delete, exception, exception_apply,
+ exception_function, exception_apply_function,
+ exception_nocatch, exception_nocatch_apply,
+ exception_nocatch_function,
+ exception_nocatch_apply_function, exception_meta,
+ exception_meta_apply, exception_meta_function,
+ exception_meta_apply_function, exception_meta_nocatch,
+ exception_meta_nocatch_apply,
+ exception_meta_nocatch_function,
+ exception_meta_nocatch_apply_function,
+ concurrency]
end.
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
not_run(Config) when is_list(Config) ->
{skipped,"Native code"}.
-basic(doc) ->
- ["Tests basic local call-trace"];
+%% Tests basic local call-trace
basic(Config) when is_list(Config) ->
basic_test().
-bit_syntax(doc) ->
- "OTP-7399: Make sure that code that uses the optimized bit syntax matching "
- "can be traced without crashing the emulator.";
+%% OTP-7399: Make sure that code that uses the optimized bit syntax matching
+%% can be traced without crashing the emulator.
bit_syntax(Config) when is_list(Config) ->
bit_syntax_test().
-return(doc) ->
- ["Tests the different types of return trace"];
+%% Tests the different types of return trace
return(Config) when is_list(Config) ->
return_test().
-
-on_and_off(doc) ->
- ["Tests turning trace parameters on and off, "
- "both for trace and trace_pattern"];
+
+%% Tests turning trace parameters on and off,
+%% both for trace and trace_pattern
on_and_off(Config) when is_list(Config) ->
on_and_off_test().
-
-stack_grow(doc) ->
- ["Tests the stack growth during return traces"];
+
+%% Tests the stack growth during return traces
stack_grow(Config) when is_list(Config) ->
stack_grow_test().
-
-info(doc) ->
- ["Tests the trace_info BIF"];
+
+%% Tests the trace_info BIF
info(Config) when is_list(Config) ->
info_test().
-
-delete(doc) ->
- ["Tests putting trace on deleted modules"];
+
+%% Tests putting trace on deleted modules
delete(Config) when is_list(Config) ->
delete_test(Config).
-exception(doc) ->
- ["Tests exception_trace"];
+%% Tests exception_trace
exception(Config) when is_list(Config) ->
exception_test([]).
-exception_apply(doc) ->
- ["Tests exception_trace"];
+%% Tests exception_trace
exception_apply(Config) when is_list(Config) ->
exception_test([apply]).
-exception_function(doc) ->
- ["Tests exception_trace"];
+%% Tests exception_trace
exception_function(Config) when is_list(Config) ->
exception_test([function]).
-exception_apply_function(doc) ->
- ["Tests exception_trace"];
+%% Tests exception_trace
exception_apply_function(Config) when is_list(Config) ->
exception_test([apply,function]).
-exception_nocatch(doc) ->
- ["Tests exception_trace"];
+%% Tests exception_trace
exception_nocatch(Config) when is_list(Config) ->
exception_test([nocatch]).
-exception_nocatch_apply(doc) ->
- ["Tests exception_trace"];
+%% Tests exception_trace
exception_nocatch_apply(Config) when is_list(Config) ->
exception_test([nocatch,apply]).
-exception_nocatch_function(doc) ->
- ["Tests exception_trace"];
+%% Tests exception_trace
exception_nocatch_function(Config) when is_list(Config) ->
exception_test([nocatch,function]).
-exception_nocatch_apply_function(doc) ->
- ["Tests exception_trace"];
+%% Tests exception_trace
exception_nocatch_apply_function(Config) when is_list(Config) ->
exception_test([nocatch,apply,function]).
-exception_meta(doc) ->
- ["Tests meta exception_trace"];
+%% Tests meta exception_trace
exception_meta(Config) when is_list(Config) ->
exception_test([meta]).
-exception_meta_apply(doc) ->
- ["Tests meta exception_trace"];
+%% Tests meta exception_trace
exception_meta_apply(Config) when is_list(Config) ->
exception_test([meta,apply]).
-exception_meta_function(doc) ->
- ["Tests meta exception_trace"];
+%% Tests meta exception_trace
exception_meta_function(Config) when is_list(Config) ->
exception_test([meta,function]).
-exception_meta_apply_function(doc) ->
- ["Tests meta exception_trace"];
+%% Tests meta exception_trace
exception_meta_apply_function(Config) when is_list(Config) ->
exception_test([meta,apply,function]).
-exception_meta_nocatch(doc) ->
- ["Tests meta exception_trace"];
+%% Tests meta exception_trace
exception_meta_nocatch(Config) when is_list(Config) ->
exception_test([meta,nocatch]).
-exception_meta_nocatch_apply(doc) ->
- ["Tests meta exception_trace"];
+%% Tests meta exception_trace
exception_meta_nocatch_apply(Config) when is_list(Config) ->
exception_test([meta,nocatch,apply]).
-exception_meta_nocatch_function(doc) ->
- ["Tests meta exception_trace"];
+%% Tests meta exception_trace
exception_meta_nocatch_function(Config) when is_list(Config) ->
exception_test([meta,nocatch,function]).
-exception_meta_nocatch_apply_function(doc) ->
- ["Tests meta exception_trace"];
+%% Tests meta exception_trace
exception_meta_nocatch_apply_function(Config) when is_list(Config) ->
exception_test([meta,nocatch,apply,function]).
--endif.
-
-
%%% Message patterns and expect functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -309,28 +243,28 @@ expect_pid(Pid, Msg) when is_tuple(Msg) ->
same(Msg, expect_receive(Pid));
expect_pid(Pid, Fun) when is_function(Fun, 1) ->
case Fun(expect_receive(Pid)) of
- next ->
- expect_pid(Pid, Fun);
- done ->
- ok;
- Other ->
- expect_pid(Pid, Other)
+ next ->
+ expect_pid(Pid, Fun);
+ done ->
+ ok;
+ Other ->
+ expect_pid(Pid, Other)
end.
expect_receive(Pid) when is_pid(Pid) ->
receive
- Msg when is_tuple(Msg),
- element(1, Msg) == trace,
- element(2, Msg) =/= Pid;
- %%
- is_tuple(Msg),
- element(1, Msg) == trace_ts,
- element(2, Msg) =/= Pid ->
- expect_receive(Pid);
- Msg ->
- expect_msg(Pid, Msg)
+ Msg when is_tuple(Msg),
+ element(1, Msg) == trace,
+ element(2, Msg) =/= Pid;
+ %%
+ is_tuple(Msg),
+ element(1, Msg) == trace_ts,
+ element(2, Msg) =/= Pid ->
+ expect_receive(Pid);
+ Msg ->
+ expect_msg(Pid, Msg)
after 100 ->
- {nm}
+ {nm}
end.
expect_msg(P, ?pCT(P,M,F,Args)) -> {ct,{M,F},Args};
@@ -343,18 +277,18 @@ expect_msg(P, ?pRT(P,M,F,Arity)) -> {rt,{M,F,Arity}};
expect_msg(P, ?pRTT(P,M,F,Arity)) -> {rtt,{M,F,Arity}};
expect_msg(P, Msg) when is_tuple(Msg) ->
case tuple_to_list(Msg) of
- [trace,P|T] ->
- list_to_tuple([trace|T]);
- [trace_ts,P|[_|_]=T] ->
- list_to_tuple([trace_ts|reverse(tl(reverse(T)))]);
- _ ->
- Msg
+ [trace,P|T] ->
+ list_to_tuple([trace|T]);
+ [trace_ts,P|[_|_]=T] ->
+ list_to_tuple([trace_ts|reverse(tl(reverse(T)))]);
+ _ ->
+ Msg
end.
same(A, B) ->
case [A|B] of
- [X|X] ->
- ok
+ [X|X] ->
+ ok
end.
@@ -362,73 +296,73 @@ same(A, B) ->
%%% tests %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
basic_test() ->
- ?line setup([call]),
+ setup([call]),
NumMatches = erlang:trace_pattern({?MODULE,'_','_'},[],[local]),
NumMatches = erlang:trace_pattern({?MODULE,'_','_'},[],[local]),
- ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
- ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
- ?line ?CT(?MODULE,exported_wrap,[1]),
- ?line ?CT(?MODULE,exported,[1]),
- ?line ?CT(?MODULE,local,[1]),
- ?line ?CT(?MODULE,local2,[1]),
- ?line ?CT(?MODULE,local_tail,[1]),
- ?line erlang:trace_pattern({?MODULE,'_','_'},[],[]),
- ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
- ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
- ?line ?CT(?MODULE,exported_wrap,[1]),
- ?line [1,1,1,1] = lambda_slave(fun() ->
- exported_wrap(1)
- end),
- ?line ?NM,
- ?line erlang:trace_pattern({?MODULE,'_','_'},[],[local]),
- ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
- ?line [1,1,1,1] = lambda_slave(fun() ->
- exported_wrap(1)
- end),
- ?line ?CT(?MODULE,_,_), %% The fun
- ?line ?CT(?MODULE,exported_wrap,[1]),
- ?line ?CT(?MODULE,exported,[1]),
- ?line ?CT(?MODULE,local,[1]),
- ?line ?CT(?MODULE,local2,[1]),
- ?line ?CT(?MODULE,local_tail,[1]),
- ?line erlang:trace_pattern({?MODULE,'_','_'},false,[local]),
- ?line shutdown(),
- ?line ?NM,
+ erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
+ [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
+ ?CT(?MODULE,exported_wrap,[1]),
+ ?CT(?MODULE,exported,[1]),
+ ?CT(?MODULE,local,[1]),
+ ?CT(?MODULE,local2,[1]),
+ ?CT(?MODULE,local_tail,[1]),
+ erlang:trace_pattern({?MODULE,'_','_'},[],[]),
+ erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
+ [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
+ ?CT(?MODULE,exported_wrap,[1]),
+ [1,1,1,1] = lambda_slave(fun() ->
+ exported_wrap(1)
+ end),
+ ?NM,
+ erlang:trace_pattern({?MODULE,'_','_'},[],[local]),
+ erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
+ [1,1,1,1] = lambda_slave(fun() ->
+ exported_wrap(1)
+ end),
+ ?CT(?MODULE,_,_), %% The fun
+ ?CT(?MODULE,exported_wrap,[1]),
+ ?CT(?MODULE,exported,[1]),
+ ?CT(?MODULE,local,[1]),
+ ?CT(?MODULE,local2,[1]),
+ ?CT(?MODULE,local_tail,[1]),
+ erlang:trace_pattern({?MODULE,'_','_'},false,[local]),
+ shutdown(),
+ ?NM,
ok.
%% OTP-7399.
bit_syntax_test() ->
- ?line setup([call]),
- ?line erlang:trace_pattern({?MODULE,'_','_'},[],[local]),
- ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
-
- ?line lambda_slave(fun() ->
- 6 = bs_sum_a(<<1,2,3>>, 0),
- 10 = bs_sum_b(0, <<1,2,3,4>>),
- 26 = bs_sum_c(<<3:4,5:4,7:4,11:4>>, 0)
- end),
- ?line ?CT(?MODULE,_,[]), %Ignore call to the fun.
-
- ?line ?CT(?MODULE,bs_sum_a,[<<1,2,3>>,0]),
- ?line ?CT(?MODULE,bs_sum_a,[<<2,3>>,1]),
- ?line ?CT(?MODULE,bs_sum_a,[<<3>>,3]),
- ?line ?CT(?MODULE,bs_sum_a,[<<>>,6]),
-
- ?line ?CT(?MODULE,bs_sum_b,[0,<<1,2,3,4>>]),
- ?line ?CT(?MODULE,bs_sum_b,[1,<<2,3,4>>]),
- ?line ?CT(?MODULE,bs_sum_b,[3,<<3,4>>]),
- ?line ?CT(?MODULE,bs_sum_b,[6,<<4>>]),
- ?line ?CT(?MODULE,bs_sum_b,[10,<<>>]),
-
- ?line ?CT(?MODULE,bs_sum_c,[<<3:4,5:4,7:4,11:4>>, 0]),
- ?line ?CT(?MODULE,bs_sum_c,[<<5:4,7:4,11:4>>, 3]),
- ?line ?CT(?MODULE,bs_sum_c,[<<7:4,11:4>>, 8]),
- ?line ?CT(?MODULE,bs_sum_c,[<<11:4>>, 15]),
- ?line ?CT(?MODULE,bs_sum_c,[<<>>, 26]),
-
- ?line erlang:trace_pattern({?MODULE,'_','_'},false,[local]),
- ?line shutdown(),
- ?line ?NM,
+ setup([call]),
+ erlang:trace_pattern({?MODULE,'_','_'},[],[local]),
+ erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
+
+ lambda_slave(fun() ->
+ 6 = bs_sum_a(<<1,2,3>>, 0),
+ 10 = bs_sum_b(0, <<1,2,3,4>>),
+ 26 = bs_sum_c(<<3:4,5:4,7:4,11:4>>, 0)
+ end),
+ ?CT(?MODULE,_,[]), %Ignore call to the fun.
+
+ ?CT(?MODULE,bs_sum_a,[<<1,2,3>>,0]),
+ ?CT(?MODULE,bs_sum_a,[<<2,3>>,1]),
+ ?CT(?MODULE,bs_sum_a,[<<3>>,3]),
+ ?CT(?MODULE,bs_sum_a,[<<>>,6]),
+
+ ?CT(?MODULE,bs_sum_b,[0,<<1,2,3,4>>]),
+ ?CT(?MODULE,bs_sum_b,[1,<<2,3,4>>]),
+ ?CT(?MODULE,bs_sum_b,[3,<<3,4>>]),
+ ?CT(?MODULE,bs_sum_b,[6,<<4>>]),
+ ?CT(?MODULE,bs_sum_b,[10,<<>>]),
+
+ ?CT(?MODULE,bs_sum_c,[<<3:4,5:4,7:4,11:4>>, 0]),
+ ?CT(?MODULE,bs_sum_c,[<<5:4,7:4,11:4>>, 3]),
+ ?CT(?MODULE,bs_sum_c,[<<7:4,11:4>>, 8]),
+ ?CT(?MODULE,bs_sum_c,[<<11:4>>, 15]),
+ ?CT(?MODULE,bs_sum_c,[<<>>, 26]),
+
+ erlang:trace_pattern({?MODULE,'_','_'},false,[local]),
+ shutdown(),
+ ?NM,
ok.
@@ -442,149 +376,157 @@ bs_sum_c(<<H:4,T/bits>>, Acc) -> bs_sum_c(T, H+Acc);
bs_sum_c(<<>>, Acc) -> Acc.
return_test() ->
- ?line setup([call]),
- ?line erlang:trace_pattern({?MODULE,'_','_'},[{'_',[],[{return_trace}]}],
- [local]),
- ?line erlang:trace_pattern({erlang,hash,'_'},[{'_',[],[{return_trace}]}],
- [local]),
- ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
- ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
- ?line ?CT(?MODULE,exported_wrap,[1]),
- ?line ?CT(?MODULE,exported,[1]),
- ?line ?CT(?MODULE,local,[1]),
- ?line ?CT(?MODULE,local2,[1]),
- ?line ?CT(?MODULE,local_tail,[1]),
- ?line ?CT(erlang,hash,[1,1]),
- ?line ?RF(erlang,hash,2,1),
- ?line ?RF(?MODULE,local_tail,1,[1,1]),
- ?line ?RF(?MODULE,local2,1,[1,1]),
- ?line ?RF(?MODULE,local,1,[1,1,1]),
- ?line ?RF(?MODULE,exported,1,[1,1,1,1]),
- ?line ?RF(?MODULE,exported_wrap,1,[1,1,1,1]),
- ?line shutdown(),
- ?line setup([call,return_to]),
- ?line erlang:trace_pattern({?MODULE,'_','_'},[],
- [local]),
- ?line erlang:trace_pattern({erlang,hash,'_'},[],
- [local]),
- ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
- ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
- ?line ?CT(?MODULE,exported_wrap,[1]),
- ?line ?CT(?MODULE,exported,[1]),
- ?line ?CT(?MODULE,local,[1]),
- ?line ?CT(?MODULE,local2,[1]),
- ?line ?CT(?MODULE,local_tail,[1]),
- ?line ?CT(erlang,hash,[1,1]),
- ?line ?RT(?MODULE,local_tail,1),
- ?line ?RT(?MODULE,local,1),
- ?line ?RT(?MODULE,exported,1),
- ?line ?RT(?MODULE,slave,2),
- ?line shutdown(),
- ?line setup([call,return_to]),
- ?line erlang:trace_pattern({?MODULE,'_','_'},[{'_',[],[{return_trace}]}],
- [local]),
- ?line erlang:trace_pattern({erlang,hash,'_'},[{'_',[],[{return_trace}]}],
- [local]),
- ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
- ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
- ?line ?CT(?MODULE,exported_wrap,[1]),
- ?line ?CT(?MODULE,exported,[1]),
- ?line ?CT(?MODULE,local,[1]),
- ?line ?CT(?MODULE,local2,[1]),
- ?line ?CT(?MODULE,local_tail,[1]),
- ?line ?CT(erlang,hash,[1,1]),
- ?line ?RF(erlang,hash,2,1),
- ?line ?RT(?MODULE,local_tail,1),
- ?line ?RF(?MODULE,local_tail,1,[1,1]),
- ?line ?RF(?MODULE,local2,1,[1,1]),
- ?line ?RT(?MODULE,local,1),
- ?line ?RF(?MODULE,local,1,[1,1,1]),
- ?line ?RT(?MODULE,exported,1),
- ?line ?RF(?MODULE,exported,1,[1,1,1,1]),
- ?line ?RF(?MODULE,exported_wrap,1,[1,1,1,1]),
- ?line ?RT(?MODULE,slave,2),
- ?line shutdown(),
- ?line ?NM,
+ setup([call]),
+ erlang:trace_pattern({?MODULE,'_','_'},[{'_',[],[{return_trace}]}],
+ [local]),
+ erlang:trace_pattern({erlang,hash,'_'},[{'_',[],[{return_trace}]}],
+ [local]),
+ erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
+ [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
+ ?CT(?MODULE,exported_wrap,[1]),
+ ?CT(?MODULE,exported,[1]),
+ ?CT(?MODULE,local,[1]),
+ ?CT(?MODULE,local2,[1]),
+ ?CT(?MODULE,local_tail,[1]),
+ ?CT(erlang,hash,[1,1]),
+ ?RF(erlang,hash,2,1),
+ ?RF(?MODULE,local_tail,1,[1,1]),
+ ?RF(?MODULE,local2,1,[1,1]),
+ ?RF(?MODULE,local,1,[1,1,1]),
+ ?RF(?MODULE,exported,1,[1,1,1,1]),
+ ?RF(?MODULE,exported_wrap,1,[1,1,1,1]),
+ shutdown(),
+ setup([call,return_to]),
+ erlang:trace_pattern({?MODULE,'_','_'},[],
+ [local]),
+ erlang:trace_pattern({erlang,hash,'_'},[],
+ [local]),
+ erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
+ [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
+ ?CT(?MODULE,exported_wrap,[1]),
+ ?CT(?MODULE,exported,[1]),
+ ?CT(?MODULE,local,[1]),
+ ?CT(?MODULE,local2,[1]),
+ ?CT(?MODULE,local_tail,[1]),
+ ?CT(erlang,hash,[1,1]),
+ ?RT(?MODULE,local_tail,1),
+ ?RT(?MODULE,local,1),
+ ?RT(?MODULE,exported,1),
+ ?RT(?MODULE,slave,2),
+ shutdown(),
+ setup([call,return_to]),
+ erlang:trace_pattern({?MODULE,'_','_'},[{'_',[],[{return_trace}]}],
+ [local]),
+ erlang:trace_pattern({erlang,hash,'_'},[{'_',[],[{return_trace}]}],
+ [local]),
+ erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
+ [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
+ ?CT(?MODULE,exported_wrap,[1]),
+ ?CT(?MODULE,exported,[1]),
+ ?CT(?MODULE,local,[1]),
+ ?CT(?MODULE,local2,[1]),
+ ?CT(?MODULE,local_tail,[1]),
+ ?CT(erlang,hash,[1,1]),
+ ?RF(erlang,hash,2,1),
+ ?RT(?MODULE,local_tail,1),
+ ?RF(?MODULE,local_tail,1,[1,1]),
+ ?RF(?MODULE,local2,1,[1,1]),
+ ?RT(?MODULE,local,1),
+ ?RF(?MODULE,local,1,[1,1,1]),
+ ?RT(?MODULE,exported,1),
+ ?RF(?MODULE,exported,1,[1,1,1,1]),
+ ?RF(?MODULE,exported_wrap,1,[1,1,1,1]),
+ ?RT(?MODULE,slave,2),
+ shutdown(),
+ ?NM,
+
+ %% Test a regression where turning off return_to tracing
+ %% on yourself would cause a segfault.
+ Pid = setup([call,return_to]),
+ erlang:trace_pattern({'_','_','_'},[],[local]),
+ apply_slave(erlang,trace,[Pid, false, [all]]),
+ shutdown(),
+
ok.
on_and_off_test() ->
- ?line Pid = setup([call]),
- ?line 1 = erlang:trace_pattern({?MODULE,local_tail,1},[],[local]),
- ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
- ?line LocalTail = fun() ->
- local_tail(1)
- end,
- ?line [1,1] = lambda_slave(LocalTail),
- ?line ?CT(?MODULE,local_tail,[1]),
- ?line erlang:trace(Pid,true,[return_to]),
- ?line [1,1] = lambda_slave(LocalTail),
- ?line ?CT(?MODULE,local_tail,[1]),
- ?line ?RT(?MODULE,_,_),
- ?line 0 = erlang:trace_pattern({?MODULE,local_tail,1},[],[global]),
- ?line [1,1] = lambda_slave(LocalTail),
- ?line ?NM,
- ?line 1 = erlang:trace_pattern({?MODULE,exported_wrap,1},[],[global]),
- ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
- ?line ?CT(?MODULE,exported_wrap,[1]),
- ?line 1 = erlang:trace_pattern({?MODULE,exported_wrap,1},[],[local]),
- ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
- ?line ?CT(?MODULE,exported_wrap,[1]),
- ?line ?RT(?MODULE,slave,2),
- ?line 1 = erlang:trace_pattern({erlang,hash,2},[],[local]),
- ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
- ?line ?CT(?MODULE,exported_wrap,[1]),
- ?line ?CT(erlang,hash,[1,1]),
- ?line ?RT(?MODULE,local_tail,1),
- ?line ?RT(?MODULE,slave,2),
- ?line erlang:trace(Pid,true,[timestamp]),
- ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
- ?line ?CTT(?MODULE,exported_wrap,[1]),
- ?line ?CTT(erlang,hash,[1,1]),
- ?line ?RTT(?MODULE,local_tail,1),
- ?line ?RTT(?MODULE,slave,2),
- ?line erlang:trace(Pid,false,[return_to,timestamp]),
- ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
- ?line ?CT(?MODULE,exported_wrap,[1]),
- ?line ?CT(erlang,hash,[1,1]),
- ?line erlang:trace(Pid,true,[return_to]),
- ?line 1 = erlang:trace_pattern({erlang,hash,2},[],[]),
- ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
- ?line ?CT(?MODULE,exported_wrap,[1]),
- ?line ?CT(erlang,hash,[1,1]),
- ?line ?RT(?MODULE,slave,2),
- ?line 1 = erlang:trace_pattern({?MODULE,exported_wrap,1},[],[]),
- ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
- ?line ?CT(?MODULE,exported_wrap,[1]),
- ?line ?CT(erlang,hash,[1,1]),
- ?line shutdown(),
- ?line erlang:trace_pattern({'_','_','_'},false,[local]),
- ?line N = erlang:trace_pattern({erlang,'_','_'},true,[local]),
- ?line case erlang:trace_pattern({erlang,'_','_'},false,[local]) of
- N ->
- ok;
- Else ->
- exit({number_mismatch, {expected, N}, {got, Else}})
- end,
- ?line case erlang:trace_pattern({erlang,'_','_'},false,[local]) of
- N ->
- ok;
- Else2 ->
- exit({number_mismatch, {expected, N}, {got, Else2}})
- end,
- ?line M = erlang:trace_pattern({erlang,'_','_'},true,[]),
- ?line case erlang:trace_pattern({erlang,'_','_'},false,[]) of
- M ->
- ok;
- Else3 ->
- exit({number_mismatch, {expected, N}, {got, Else3}})
- end,
- ?line case erlang:trace_pattern({erlang,'_','_'},false,[]) of
- M ->
- ok;
- Else4 ->
- exit({number_mismatch, {expected, N}, {got, Else4}})
- end,
- ?line ?NM,
+ Pid = setup([call]),
+ 1 = erlang:trace_pattern({?MODULE,local_tail,1},[],[local]),
+ erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
+ LocalTail = fun() ->
+ local_tail(1)
+ end,
+ [1,1] = lambda_slave(LocalTail),
+ ?CT(?MODULE,local_tail,[1]),
+ erlang:trace(Pid,true,[return_to]),
+ [1,1] = lambda_slave(LocalTail),
+ ?CT(?MODULE,local_tail,[1]),
+ ?RT(?MODULE,_,_),
+ 0 = erlang:trace_pattern({?MODULE,local_tail,1},[],[global]),
+ [1,1] = lambda_slave(LocalTail),
+ ?NM,
+ 1 = erlang:trace_pattern({?MODULE,exported_wrap,1},[],[global]),
+ [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
+ ?CT(?MODULE,exported_wrap,[1]),
+ 1 = erlang:trace_pattern({?MODULE,exported_wrap,1},[],[local]),
+ [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
+ ?CT(?MODULE,exported_wrap,[1]),
+ ?RT(?MODULE,slave,2),
+ 1 = erlang:trace_pattern({erlang,hash,2},[],[local]),
+ [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
+ ?CT(?MODULE,exported_wrap,[1]),
+ ?CT(erlang,hash,[1,1]),
+ ?RT(?MODULE,local_tail,1),
+ ?RT(?MODULE,slave,2),
+ erlang:trace(Pid,true,[timestamp]),
+ [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
+ ?CTT(?MODULE,exported_wrap,[1]),
+ ?CTT(erlang,hash,[1,1]),
+ ?RTT(?MODULE,local_tail,1),
+ ?RTT(?MODULE,slave,2),
+ erlang:trace(Pid,false,[return_to,timestamp]),
+ [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
+ ?CT(?MODULE,exported_wrap,[1]),
+ ?CT(erlang,hash,[1,1]),
+ erlang:trace(Pid,true,[return_to]),
+ 1 = erlang:trace_pattern({erlang,hash,2},[],[]),
+ [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
+ ?CT(?MODULE,exported_wrap,[1]),
+ ?CT(erlang,hash,[1,1]),
+ ?RT(?MODULE,slave,2),
+ 1 = erlang:trace_pattern({?MODULE,exported_wrap,1},[],[]),
+ [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
+ ?CT(?MODULE,exported_wrap,[1]),
+ ?CT(erlang,hash,[1,1]),
+ shutdown(),
+ erlang:trace_pattern({'_','_','_'},false,[local]),
+ N = erlang:trace_pattern({erlang,'_','_'},true,[local]),
+ case erlang:trace_pattern({erlang,'_','_'},false,[local]) of
+ N ->
+ ok;
+ Else ->
+ exit({number_mismatch, {expected, N}, {got, Else}})
+ end,
+ case erlang:trace_pattern({erlang,'_','_'},false,[local]) of
+ N ->
+ ok;
+ Else2 ->
+ exit({number_mismatch, {expected, N}, {got, Else2}})
+ end,
+ M = erlang:trace_pattern({erlang,'_','_'},true,[]),
+ case erlang:trace_pattern({erlang,'_','_'},false,[]) of
+ M ->
+ ok;
+ Else3 ->
+ exit({number_mismatch, {expected, N}, {got, Else3}})
+ end,
+ case erlang:trace_pattern({erlang,'_','_'},false,[]) of
+ M ->
+ ok;
+ Else4 ->
+ exit({number_mismatch, {expected, N}, {got, Else4}})
+ end,
+ ?NM,
ok.
systematic_on_off(Config) when is_list(Config) ->
@@ -634,12 +576,12 @@ systematic_on_off_1(Local) ->
verify_trace_info(Global, Local) ->
case erlang:trace_info({?MODULE,exported_wrap,1}, all) of
- {all,false} ->
- false = Global,
- [] = Local;
- {all,Ps} ->
- io:format("~p\n", [Ps]),
- [verify_trace_info(P, Global, Local) || P <- Ps]
+ {all,false} ->
+ false = Global,
+ [] = Local;
+ {all,Ps} ->
+ io:format("~p\n", [Ps]),
+ [verify_trace_info(P, Global, Local) || P <- Ps]
end,
global_call(Global, Local),
local_call(Local),
@@ -651,12 +593,10 @@ verify_trace_info({match_spec,[]}, _, _) -> ok;
verify_trace_info({meta_match_spec,[]}, _, _) -> ok;
verify_trace_info({LocalFlag,Bool}, _, Local) when is_boolean(Bool) ->
try
- Bool = lists:member(LocalFlag, Local)
+ Bool = lists:member(LocalFlag, Local)
catch
- error:_ ->
- io:format("Line ~p: {~p,~p}, false, ~p\n",
- [?LINE,LocalFlag,Bool,Local]),
- ?t:fail()
+ error:_ ->
+ ct:fail("Line ~p: {~p,~p}, false, ~p\n", [?LINE,LocalFlag,Bool,Local])
end;
verify_trace_info({meta,Pid}, false, Local) when is_pid(Pid) ->
true = lists:member(meta, Local);
@@ -668,10 +608,10 @@ verify_trace_info({call_count,_}, false, Local) ->
global_call(Global, Local) ->
apply_slave(?MODULE, exported_wrap, [global_call]),
case Global of
- false ->
- recv_local_call(Local, [global_call]);
- true ->
- ?CT(?MODULE, exported_wrap, [global_call])
+ false ->
+ recv_local_call(Local, [global_call]);
+ true ->
+ ?CT(?MODULE, exported_wrap, [global_call])
end.
local_call(Local) ->
@@ -680,16 +620,16 @@ local_call(Local) ->
recv_local_call(Local, Args) ->
case lists:member(local, Local) of
- false ->
- ok;
- true ->
- ?CT(?MODULE, exported_wrap, Args)
+ false ->
+ ok;
+ true ->
+ ?CT(?MODULE, exported_wrap, Args)
end,
case lists:member(meta, Local) of
- false ->
- ok;
- true ->
- ?CTT(?MODULE, exported_wrap, Args)
+ false ->
+ ok;
+ true ->
+ ?CTT(?MODULE, exported_wrap, Args)
end,
ok.
@@ -698,99 +638,99 @@ combinations([_]=One) ->
combinations([H|T]) ->
Cs = combinations(T),
[[H|C] || C <- Cs] ++ Cs.
-
+
stack_grow_test() ->
- ?line setup([call,return_to]),
- ?line 1 = erlang:trace_pattern({?MODULE,loop,4},
- [{'_',[],[{return_trace}]}],[local]),
- ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
- ?line Num = 1 bsl 15,
- ?line Fun =
- fun(_F,0) -> ok;
- (F,N) ->
- receive _A ->
- receive _B ->
- receive _C ->
- F(F,N-1)
- end
- end
- end
- end,
- ?line apply_slave_async(?MODULE,loop,[{hej,hopp},[a,b,c],4.5,Num]),
- ?line Fun(Fun,Num + 1),
- ?line ?NM,
+ setup([call,return_to]),
+ 1 = erlang:trace_pattern({?MODULE,loop,4},
+ [{'_',[],[{return_trace}]}],[local]),
+ erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
+ Num = 1 bsl 15,
+ Fun =
+ fun(_F,0) -> ok;
+ (F,N) ->
+ receive _A ->
+ receive _B ->
+ receive _C ->
+ F(F,N-1)
+ end
+ end
+ end
+ end,
+ apply_slave_async(?MODULE,loop,[{hej,hopp},[a,b,c],4.5,Num]),
+ Fun(Fun,Num + 1),
+ ?NM,
ok.
info_test() ->
- ?line Flags1 = lists:sort([call,return_to]),
- ?line Pid = setup(Flags1),
- ?line Prog = [{['$1'],[{is_integer,'$1'}],[{message, false}]},
- {'_',[],[]}],
- ?line erlang:trace_pattern({?MODULE,exported_wrap,1},Prog,[local]),
- ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
- ?line Self = self(),
- ?line {flags,L} = erlang:trace_info(Pid,flags),
- ?line case lists:sort(L) of
- Flags1 ->
- ok;
- Wrong1 ->
- exit({bad_result, {erlang,trace_info,[Pid,flags]},
- {expected, Flags1}, {got, Wrong1}})
- end,
- ?line {tracer,Tracer} = erlang:trace_info(Pid,tracer),
- ?line case Tracer of
- Self ->
- ok;
- Wrong2 ->
- exit({bad_result, {erlang,trace_info,[Pid,tracer]},
- {expected, Self}, {got, Wrong2}})
- end,
- ?line {traced,local} = erlang:trace_info({?MODULE,exported_wrap,1},traced),
- ?line {match_spec, MS} =
- erlang:trace_info({?MODULE,exported_wrap,1},match_spec),
- ?line case MS of
- Prog ->
- ok;
- Wrong3 ->
- exit({bad_result, {erlang,trace_info,
- [{?MODULE,exported_wrap,1},
- match_spec]},
- {expected, Prog}, {got, Wrong3}})
- end,
- ?line erlang:garbage_collect(self()),
- ?line receive
- after 1 ->
- ok
- end,
- ?line io:format("~p~n",[MS]),
- ?line {match_spec,MS2} =
- erlang:trace_info({?MODULE,exported_wrap,1},match_spec),
- ?line io:format("~p~n",[MS2]),
- ?line erlang:trace_pattern({?MODULE,exported_wrap,1},[],[]),
- ?line {traced,global} =
- erlang:trace_info({?MODULE,exported_wrap,1},traced),
- ?line {match_spec,[]} =
- erlang:trace_info({?MODULE,exported_wrap,1},match_spec),
- ?line {traced,undefined} =
- erlang:trace_info({?MODULE,exported_wrap,2},traced),
- ?line {match_spec,undefined} =
- erlang:trace_info({?MODULE,exported_wrap,2},match_spec),
- ?line {traced,false} = erlang:trace_info({?MODULE,exported,1},traced),
- ?line {match_spec,false} =
- erlang:trace_info({?MODULE,exported,1},match_spec),
- ?line shutdown(),
+ Flags1 = lists:sort([call,return_to]),
+ Pid = setup(Flags1),
+ Prog = [{['$1'],[{is_integer,'$1'}],[{message, false}]},
+ {'_',[],[]}],
+ erlang:trace_pattern({?MODULE,exported_wrap,1},Prog,[local]),
+ erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
+ Self = self(),
+ {flags,L} = erlang:trace_info(Pid,flags),
+ case lists:sort(L) of
+ Flags1 ->
+ ok;
+ Wrong1 ->
+ exit({bad_result, {erlang,trace_info,[Pid,flags]},
+ {expected, Flags1}, {got, Wrong1}})
+ end,
+ {tracer,Tracer} = erlang:trace_info(Pid,tracer),
+ case Tracer of
+ Self ->
+ ok;
+ Wrong2 ->
+ exit({bad_result, {erlang,trace_info,[Pid,tracer]},
+ {expected, Self}, {got, Wrong2}})
+ end,
+ {traced,local} = erlang:trace_info({?MODULE,exported_wrap,1},traced),
+ {match_spec, MS} =
+ erlang:trace_info({?MODULE,exported_wrap,1},match_spec),
+ case MS of
+ Prog ->
+ ok;
+ Wrong3 ->
+ exit({bad_result, {erlang,trace_info,
+ [{?MODULE,exported_wrap,1},
+ match_spec]},
+ {expected, Prog}, {got, Wrong3}})
+ end,
+ erlang:garbage_collect(self()),
+ receive
+ after 1 ->
+ ok
+ end,
+ io:format("~p~n",[MS]),
+ {match_spec,MS2} =
+ erlang:trace_info({?MODULE,exported_wrap,1},match_spec),
+ io:format("~p~n",[MS2]),
+ erlang:trace_pattern({?MODULE,exported_wrap,1},[],[]),
+ {traced,global} =
+ erlang:trace_info({?MODULE,exported_wrap,1},traced),
+ {match_spec,[]} =
+ erlang:trace_info({?MODULE,exported_wrap,1},match_spec),
+ {traced,undefined} =
+ erlang:trace_info({?MODULE,exported_wrap,2},traced),
+ {match_spec,undefined} =
+ erlang:trace_info({?MODULE,exported_wrap,2},match_spec),
+ {traced,false} = erlang:trace_info({?MODULE,exported,1},traced),
+ {match_spec,false} =
+ erlang:trace_info({?MODULE,exported,1},match_spec),
+ shutdown(),
ok.
delete_test(Config) ->
- ?line Priv = ?config(priv_dir, Config),
- ?line Data = ?config(data_dir, Config),
- ?line File = filename:join(Data, "trace_local_dummy"),
- ?line {ok,trace_local_dummy} = c:c(File, [{outdir,Priv}]),
- ?line code:purge(trace_local_dummy),
- ?line code:delete(trace_local_dummy),
- ?line 0 = erlang:trace_pattern({trace_local_dummy,'_','_'},true,[local]),
- ?line ?NM,
+ Priv = proplists:get_value(priv_dir, Config),
+ Data = proplists:get_value(data_dir, Config),
+ File = filename:join(Data, "trace_local_dummy"),
+ {ok,trace_local_dummy} = c:c(File, [{outdir,Priv}]),
+ code:purge(trace_local_dummy),
+ code:delete(trace_local_dummy),
+ 0 = erlang:trace_pattern({trace_local_dummy,'_','_'},true,[local]),
+ ?NM,
ok.
@@ -798,34 +738,34 @@ delete_test(Config) ->
%%% exception_test %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
exception_test(Opts) ->
- ?line {ProcFlags,PatFlags} =
- case proplists:get_bool(meta, Opts) of
- true -> {[timestamp],[meta]};
- false -> {[call,return_to,timestamp],[local]}
- end,
- ?line case proplists:get_bool(nocatch, Opts) of
- false ->
- ?line Exceptions = exceptions(),
- ?line exception_test_setup(ProcFlags, PatFlags),
- ?line lists:foreach(
- fun ({Func,Args}) ->
- ?line exception_test(Opts, Func, Args)
- end,
- Exceptions),
- ?line shutdown();
- true ->
- ?line Exceptions = exceptions(),
- ?line lists:foreach(
- fun ({Func,Args}) ->
- ?line exception_test_setup(
- [procs|ProcFlags],
- PatFlags),
- ?line exception_test(Opts, Func, Args),
- ?line shutdown()
- end,
- Exceptions)
- end,
- ?line ok.
+ {ProcFlags,PatFlags} =
+ case proplists:get_bool(meta, Opts) of
+ true -> {[timestamp],[meta]};
+ false -> {[call,return_to,timestamp],[local]}
+ end,
+ case proplists:get_bool(nocatch, Opts) of
+ false ->
+ Exceptions = exceptions(),
+ exception_test_setup(ProcFlags, PatFlags),
+ lists:foreach(
+ fun ({Func,Args}) ->
+ exception_test(Opts, Func, Args)
+ end,
+ Exceptions),
+ shutdown();
+ true ->
+ Exceptions = exceptions(),
+ lists:foreach(
+ fun ({Func,Args}) ->
+ exception_test_setup(
+ [procs|ProcFlags],
+ PatFlags),
+ exception_test(Opts, Func, Args),
+ shutdown()
+ end,
+ Exceptions)
+ end,
+ ok.
exceptions() ->
Ref = make_ref(),
@@ -848,65 +788,65 @@ exceptions() ->
{{?MODULE,lists_reverse}, [LL,[]]}].
exception_test_setup(ProcFlags, PatFlags) ->
- ?line Pid = setup(ProcFlags),
- ?line io:format("=== exception_test_setup(~p, ~p): ~p~n",
- [ProcFlags,PatFlags,Pid]),
- ?line Mprog = [{'_',[],[{exception_trace}]}],
- ?line erlang:trace_pattern({?MODULE,'_','_'}, Mprog, PatFlags),
- ?line erlang:trace_pattern({?MODULE,slave,'_'},false,PatFlags),
- ?line [1,1,1,1,1] =
- [erlang:trace_pattern({erlang,F,A}, Mprog, PatFlags)
- || {F,A} <- [{exit,1},{error,1},{error,2},{throw,1},{'++',2}]],
- ?line 1 = erlang:trace_pattern({lists,reverse,2}, Mprog, PatFlags),
- ?line ok.
+ Pid = setup(ProcFlags),
+ io:format("=== exception_test_setup(~p, ~p): ~p~n",
+ [ProcFlags,PatFlags,Pid]),
+ Mprog = [{'_',[],[{exception_trace}]}],
+ erlang:trace_pattern({?MODULE,'_','_'}, Mprog, PatFlags),
+ erlang:trace_pattern({?MODULE,slave,'_'},false,PatFlags),
+ [1,1,1,1,1] =
+ [erlang:trace_pattern({erlang,F,A}, Mprog, PatFlags)
+ || {F,A} <- [{exit,1},{error,1},{error,2},{throw,1},{'++',2}]],
+ 1 = erlang:trace_pattern({lists,reverse,2}, Mprog, PatFlags),
+ ok.
-record(exc_opts, {nocatch=false, meta=false}).
exception_test(Opts, Func0, Args0) ->
- ?line io:format("=== exception_test(~p, ~p, ~p)~n",
- [Opts,Func0,abbr(Args0)]),
- ?line Apply = proplists:get_bool(apply, Opts),
- ?line Function = proplists:get_bool(function, Opts),
- ?line Nocatch = proplists:get_bool(nocatch, Opts),
- ?line Meta = proplists:get_bool(meta, Opts),
- ?line ExcOpts = #exc_opts{nocatch=Nocatch,meta=Meta},
-
+ io:format("=== exception_test(~p, ~p, ~p)~n",
+ [Opts,Func0,abbr(Args0)]),
+ Apply = proplists:get_bool(apply, Opts),
+ Function = proplists:get_bool(function, Opts),
+ Nocatch = proplists:get_bool(nocatch, Opts),
+ Meta = proplists:get_bool(meta, Opts),
+ ExcOpts = #exc_opts{nocatch=Nocatch,meta=Meta},
+
%% Func0 and Args0 are for the innermost call, now we will
%% wrap them in wrappers...
- ?line {Func1,Args1} =
- case Function of
- true -> {fun exc/2,[Func0,Args0]};
- false -> {Func0,Args0}
- end,
-
- ?line {Func,Args} =
- case Apply of
- true -> {{erlang,apply},[Func1,Args1]};
- false -> {Func1,Args1}
- end,
-
- ?line R1 = exc_slave(ExcOpts, Func, Args),
- ?line Stack2 = [{?MODULE,exc_top,3,[]},{?MODULE,slave,2,[]}],
- ?line Stack3 = [{?MODULE,exc,2,[]}|Stack2],
- ?line Rs =
- case x_exc_top(ExcOpts, Func, Args) of % Emulation
- {crash,{Reason,Stack}}=R when is_list(Stack) ->
- [R,
- {crash,{Reason,Stack++Stack2}},
- {crash,{Reason,Stack++Stack3}}];
- R ->
- [R]
- end,
- ?line exception_validate(R1, Rs),
- ?line case R1 of
- {crash,Crash} ->
- ?line expect({trace_ts,exit,Crash});
- _ when not Meta ->
- ?line expect({rtt,{?MODULE,slave,2}});
- _ ->
- ok
- end,
- ?line expect({nm}).
+ {Func1,Args1} =
+ case Function of
+ true -> {fun exc/2,[Func0,Args0]};
+ false -> {Func0,Args0}
+ end,
+
+ {Func,Args} =
+ case Apply of
+ true -> {{erlang,apply},[Func1,Args1]};
+ false -> {Func1,Args1}
+ end,
+
+ R1 = exc_slave(ExcOpts, Func, Args),
+ Stack2 = [{?MODULE,exc_top,3,[]},{?MODULE,slave,2,[]}],
+ Stack3 = [{?MODULE,exc,2,[]}|Stack2],
+ Rs =
+ case x_exc_top(ExcOpts, Func, Args) of % Emulation
+ {crash,{Reason,Stack}}=R when is_list(Stack) ->
+ [R,
+ {crash,{Reason,Stack++Stack2}},
+ {crash,{Reason,Stack++Stack3}}];
+ R ->
+ [R]
+ end,
+ exception_validate(R1, Rs),
+ case R1 of
+ {crash,Crash} ->
+ expect({trace_ts,exit,Crash});
+ _ when not Meta ->
+ expect({rtt,{?MODULE,slave,2}});
+ _ ->
+ ok
+ end,
+ expect({nm}).
exception_validate(R0, Rs0) ->
R = clean_location(R0),
@@ -915,16 +855,16 @@ exception_validate(R0, Rs0) ->
exception_validate_1(R1, [R2|Rs]) ->
case [R1|R2] of
- [R|R] ->
- ok;
- [{crash,{badarg,[{lists,reverse,[L1a,L1b],_}|T]}}|
- {crash,{badarg,[{lists,reverse,[L2a,L2b],_}|T]}}] ->
- same({crash,{badarg,[{lists,reverse,
- [lists:reverse(L1b, L1a),[]],[]}|T]}},
- {crash,{badarg,[{lists,reverse,
- [lists:reverse(L2b, L2a),[]],[]}|T]}});
- _ when is_list(Rs), Rs =/= [] ->
- exception_validate(R1, Rs)
+ [R|R] ->
+ ok;
+ [{crash,{badarg,[{lists,reverse,[L1a,L1b],_}|T]}}|
+ {crash,{badarg,[{lists,reverse,[L2a,L2b],_}|T]}}] ->
+ same({crash,{badarg,[{lists,reverse,
+ [lists:reverse(L1b, L1a),[]],[]}|T]}},
+ {crash,{badarg,[{lists,reverse,
+ [lists:reverse(L2b, L2a),[]],[]}|T]}});
+ _ when is_list(Rs), Rs =/= [] ->
+ exception_validate(R1, Rs)
end.
clean_location({crash,{Reason,Stk0}}) ->
@@ -942,20 +882,20 @@ concurrency(_Config) ->
%% if an aligned word-sized write is not atomic.
Ps0 = [spawn_monitor(fun() -> infinite_loop() end) ||
- _ <- lists:seq(1, 2*N)],
+ _ <- lists:seq(1, 2*N)],
OnAndOff = fun() -> concurrency_on_and_off() end,
Ps1 = [spawn_monitor(OnAndOff)|Ps0],
- ?t:sleep(1000),
+ timer:sleep(1000),
%% Now spawn off N more processes that turn on off and off
%% a local trace pattern.
Ps = [spawn_monitor(OnAndOff) || _ <- lists:seq(1, N)] ++ Ps1,
- ?t:sleep(1000),
+ timer:sleep(1000),
%% Clean up.
[exit(Pid, kill) || {Pid,_} <- Ps],
[receive
- {'DOWN',Ref,process,Pid,killed} -> ok
+ {'DOWN',Ref,process,Pid,killed} -> ok
end || {Pid,Ref} <- Ps],
erlang:trace_pattern({?MODULE,infinite_loop,0}, false, [local]),
ok.
@@ -999,16 +939,16 @@ local_tail(Val) ->
exc_top(ExcOpts, Func, Args) ->
case ExcOpts#exc_opts.nocatch of
- false ->
- try exc_jump(Func, Args) of
- Value ->
- {value,Value}
- catch
- Class:Reason ->
- {Class,Reason}
- end;
- true ->
- {value,exc_jump(Func, Args)}
+ false ->
+ try exc_jump(Func, Args) of
+ Value ->
+ {value,Value}
+ catch
+ Class:Reason ->
+ {Class,Reason}
+ end;
+ true ->
+ {value,exc_jump(Func, Args)}
end.
%% x_* functions emulate the non-x_* ones.
@@ -1017,42 +957,42 @@ exc_top(ExcOpts, Func, Args) ->
%% The only possible place for exception
%% is below exc/2.
x_exc_top(ExcOpts, Func, Args) ->
- ?line Rtt = not ExcOpts#exc_opts.meta,
- ?line expect({ctt,{?MODULE,exc_top},[ExcOpts,Func,Args]}),
- ?line case x_exc_jump(ExcOpts, Func, Args) of
- Result when not ExcOpts#exc_opts.nocatch ->
- ?line expect([Rtt,{rtt,{?MODULE,exc_top,3}},
- ?LINE,{rft,{?MODULE,exc_top,3},Result}]),
- ?line Result;
- {value,_}=Result ->
-
- ?line expect([Rtt,{rtt,{?MODULE,exc_top,3}},
- ?LINE,{rft,{?MODULE,exc_top,3},Result}]),
- ?line Result;
- {exit,Reason}=CR ->
- ?line expect({eft,{?MODULE,exc_top,3},CR}),
- ?line {crash,Reason};
- {error,Reason}=CR ->
- ?line expect({eft,{?MODULE,exc_top,3},CR}),
- ?line {crash,{Reason,x_exc_stacktrace()}};
- CR ->
- ?line expect({eft,{?MODULE,exc_top,3},CR}),
- ?line {crash,CR}
- end.
+ Rtt = not ExcOpts#exc_opts.meta,
+ expect({ctt,{?MODULE,exc_top},[ExcOpts,Func,Args]}),
+ case x_exc_jump(ExcOpts, Func, Args) of
+ Result when not ExcOpts#exc_opts.nocatch ->
+ expect([Rtt,{rtt,{?MODULE,exc_top,3}},
+ ?LINE,{rft,{?MODULE,exc_top,3},Result}]),
+ Result;
+ {value,_}=Result ->
+
+ expect([Rtt,{rtt,{?MODULE,exc_top,3}},
+ ?LINE,{rft,{?MODULE,exc_top,3},Result}]),
+ Result;
+ {exit,Reason}=CR ->
+ expect({eft,{?MODULE,exc_top,3},CR}),
+ {crash,Reason};
+ {error,Reason}=CR ->
+ expect({eft,{?MODULE,exc_top,3},CR}),
+ {crash,{Reason,x_exc_stacktrace()}};
+ CR ->
+ expect({eft,{?MODULE,exc_top,3},CR}),
+ {crash,CR}
+ end.
exc_jump(Func, Args) ->
exc(Func, Args, jump).
x_exc_jump(ExcOpts, Func, Args) ->
- ?line expect({ctt,{?MODULE,exc_jump},[Func,Args]}),
- ?line case x_exc(ExcOpts, Func, Args, jump) of
- {value,Value}=Result ->
- ?line expect({rft,{?MODULE,exc_jump,2},Value}),
- ?line Result;
- CR ->
- ?line expect({eft,{?MODULE,exc_jump,2},CR}),
- ?line CR
- end.
+ expect({ctt,{?MODULE,exc_jump},[Func,Args]}),
+ case x_exc(ExcOpts, Func, Args, jump) of
+ {value,Value}=Result ->
+ expect({rft,{?MODULE,exc_jump,2},Value}),
+ Result;
+ CR ->
+ expect({eft,{?MODULE,exc_jump,2},CR}),
+ CR
+ end.
exc(Func, Args, jump) ->
exc(Func, Args, do);
@@ -1060,25 +1000,25 @@ exc(Func, Args, do) ->
exc(Func, Args).
x_exc(ExcOpts, Func, Args, jump) ->
- ?line expect({ctt,{?MODULE,exc},[Func,Args,jump]}),
- ?line case x_exc(ExcOpts, Func, Args, do) of
- {value,Value}=Result ->
- ?line expect({rft,{?MODULE,exc,3},Value}),
- ?line Result;
- CR ->
- ?line expect({eft,{?MODULE,exc,3},CR}),
- ?line CR
- end;
+ expect({ctt,{?MODULE,exc},[Func,Args,jump]}),
+ case x_exc(ExcOpts, Func, Args, do) of
+ {value,Value}=Result ->
+ expect({rft,{?MODULE,exc,3},Value}),
+ Result;
+ CR ->
+ expect({eft,{?MODULE,exc,3},CR}),
+ CR
+ end;
x_exc(ExcOpts, Func, Args, do) ->
- ?line expect({ctt,{?MODULE,exc},[Func,Args,do]}),
- ?line case x_exc(ExcOpts, Func, Args) of
- {value,Value}=Result ->
- ?line expect({rft,{?MODULE,exc,3},Value}),
- ?line Result;
- CR ->
- ?line expect({eft,{?MODULE,exc,3},CR}),
- ?line CR
- end.
+ expect({ctt,{?MODULE,exc},[Func,Args,do]}),
+ case x_exc(ExcOpts, Func, Args) of
+ {value,Value}=Result ->
+ expect({rft,{?MODULE,exc,3},Value}),
+ Result;
+ CR ->
+ expect({eft,{?MODULE,exc,3},CR}),
+ CR
+ end.
exc({erlang,apply}, [{M,F},A]) ->
erlang:apply(M, F, id(A));
@@ -1108,114 +1048,114 @@ exc(Func, [A,B]) when is_function(Func, 2) ->
Func(A, id(B)).
x_exc(ExcOpts, {erlang,apply}=Func0, [{_,_}=Func,Args]=Args0) ->
- ?line expect({ctt,{?MODULE,exc},[Func0,Args0]}),
- ?line x_exc_body(ExcOpts, Func, Args, true);
+ expect({ctt,{?MODULE,exc},[Func0,Args0]}),
+ x_exc_body(ExcOpts, Func, Args, true);
x_exc(ExcOpts, {erlang,apply}=Func0, [Func,Args]=Args0)
when is_function(Func, 2)->
- ?line expect({ctt,{?MODULE,exc},[Func0,Args0]}),
- ?line x_exc_func(ExcOpts, Func, Args, Args);
+ expect({ctt,{?MODULE,exc},[Func0,Args0]}),
+ x_exc_func(ExcOpts, Func, Args, Args);
x_exc(ExcOpts, {_,_}=Func, Args) ->
- ?line expect({ctt,{?MODULE,exc},[Func,Args]}),
- ?line x_exc_body(ExcOpts, Func, Args, false);
+ expect({ctt,{?MODULE,exc},[Func,Args]}),
+ x_exc_body(ExcOpts, Func, Args, false);
x_exc(ExcOpts, Func0, [_,Args]=Args0)
when is_function(Func0, 2) ->
- ?line expect({ctt,{?MODULE,exc},[Func0,Args0]}),
- ?line x_exc_func(ExcOpts, Func0, Args0, Args).
+ expect({ctt,{?MODULE,exc},[Func0,Args0]}),
+ x_exc_func(ExcOpts, Func0, Args0, Args).
x_exc_func(ExcOpts, Func, [Func1,Args1]=Args, Id) ->
%% Assumes the called fun =:= fun exc/2,
%% will utterly fail otherwise.
- ?line Rtt = not ExcOpts#exc_opts.meta,
- ?line {module,M} = erlang:fun_info(Func, module),
- ?line {name,F} = erlang:fun_info(Func, name),
- ?line expect([{ctt,{?MODULE,id},[Id]},
- ?LINE,{rft,{?MODULE,id,1},Id},
- ?LINE,Rtt,{rtt,{?MODULE,exc,2}},
- ?LINE,{ctt,{M,F},Args}]),
- ?line case x_exc(ExcOpts, Func1, Args1) of
- {value,Value}=Result ->
- ?line expect([{rft,{M,F,2},Value},
- ?LINE,{rft,{?MODULE,exc,2},Value}]),
- ?line Result;
- CR ->
- ?line expect([{eft,{M,F,2},CR},
- ?LINE,{eft,{?MODULE,exc,2},CR}]),
- ?line CR
- end.
+ Rtt = not ExcOpts#exc_opts.meta,
+ {module,M} = erlang:fun_info(Func, module),
+ {name,F} = erlang:fun_info(Func, name),
+ expect([{ctt,{?MODULE,id},[Id]},
+ ?LINE,{rft,{?MODULE,id,1},Id},
+ ?LINE,Rtt,{rtt,{?MODULE,exc,2}},
+ ?LINE,{ctt,{M,F},Args}]),
+ case x_exc(ExcOpts, Func1, Args1) of
+ {value,Value}=Result ->
+ expect([{rft,{M,F,2},Value},
+ ?LINE,{rft,{?MODULE,exc,2},Value}]),
+ Result;
+ CR ->
+ expect([{eft,{M,F,2},CR},
+ ?LINE,{eft,{?MODULE,exc,2},CR}]),
+ CR
+ end.
x_exc_body(ExcOpts, {M,F}=Func, Args, Apply) ->
- ?line Nocatch = ExcOpts#exc_opts.nocatch,
- ?line Rtt = not ExcOpts#exc_opts.meta,
- ?line Id = case Apply of
- true -> Args;
- false -> lists:last(Args)
- end,
- ?line expect([{ctt,{?MODULE,id},[Id]},
- ?LINE,{rft,{?MODULE,id,1},Id},
- ?LINE,Rtt,{rtt,{?MODULE,exc,2}},
- ?LINE,{ctt,{M,F},Args}]),
- ?line Arity = length(Args),
- ?line try exc(Func, Args) of
- Value ->
- ?line x_exc_value(Rtt, M, F, Args, Arity, Value),
- ?line case expect() of
- {rtt,{M,F,Arity}} when Rtt, Apply ->
- %% We may get the above when
- %% applying a BIF.
- ?line expect({rft,{?MODULE,exc,2},Value});
- {rtt,{?MODULE,exc,2}} when Rtt, not Apply ->
- %% We may get the above when
- %% calling a BIF.
- ?line expect({rft,{?MODULE,exc,2},Value});
- {rft,{?MODULE,exc,2},Value} ->
- ?line ok
- end,
- ?line {value,Value}
- catch
- Thrown when Nocatch ->
- ?line CR = {error,{nocatch,Thrown}},
- ?line x_exc_exception(Rtt, M, F, Args, Arity, CR),
- ?line expect({eft,{?MODULE,exc,2},CR}),
- ?line CR;
- Class:Reason ->
- ?line CR = {Class,Reason},
- ?line x_exc_exception(Rtt, M, F, Args, Arity, CR),
- ?line expect({eft,{?MODULE,exc,2},CR}),
- ?line CR
- end.
+ Nocatch = ExcOpts#exc_opts.nocatch,
+ Rtt = not ExcOpts#exc_opts.meta,
+ Id = case Apply of
+ true -> Args;
+ false -> lists:last(Args)
+ end,
+ expect([{ctt,{?MODULE,id},[Id]},
+ ?LINE,{rft,{?MODULE,id,1},Id},
+ ?LINE,Rtt,{rtt,{?MODULE,exc,2}},
+ ?LINE,{ctt,{M,F},Args}]),
+ Arity = length(Args),
+ try exc(Func, Args) of
+ Value ->
+ x_exc_value(Rtt, M, F, Args, Arity, Value),
+ case expect() of
+ {rtt,{M,F,Arity}} when Rtt, Apply ->
+ %% We may get the above when
+ %% applying a BIF.
+ expect({rft,{?MODULE,exc,2},Value});
+ {rtt,{?MODULE,exc,2}} when Rtt, not Apply ->
+ %% We may get the above when
+ %% calling a BIF.
+ expect({rft,{?MODULE,exc,2},Value});
+ {rft,{?MODULE,exc,2},Value} ->
+ ok
+ end,
+ {value,Value}
+ catch
+ Thrown when Nocatch ->
+ CR = {error,{nocatch,Thrown}},
+ x_exc_exception(Rtt, M, F, Args, Arity, CR),
+ expect({eft,{?MODULE,exc,2},CR}),
+ CR;
+ Class:Reason ->
+ CR = {Class,Reason},
+ x_exc_exception(Rtt, M, F, Args, Arity, CR),
+ expect({eft,{?MODULE,exc,2},CR}),
+ CR
+ end.
x_exc_value(Rtt, ?MODULE, lists_reverse, [La,Lb], 2, R) ->
- ?line L = lists:reverse(Lb, La),
- ?line expect([fun ({ctt,{lists,reverse},[L1,L2]}) ->
- ?line same(L, lists:reverse(L2, L1)),
- ?line next;
- (Msg) ->
- ?line same({rft,{lists,reverse,2},R}, Msg),
- ?line same(R, lists:reverse(L, [])),
- ?line done
- end,
- ?LINE,Rtt,{rtt,{?MODULE,lists_reverse,2}},
- ?LINE,{rft,{?MODULE,lists_reverse,2},R}]);
+ L = lists:reverse(Lb, La),
+ expect([fun ({ctt,{lists,reverse},[L1,L2]}) ->
+ same(L, lists:reverse(L2, L1)),
+ next;
+ (Msg) ->
+ same({rft,{lists,reverse,2},R}, Msg),
+ same(R, lists:reverse(L, [])),
+ done
+ end,
+ ?LINE,Rtt,{rtt,{?MODULE,lists_reverse,2}},
+ ?LINE,{rft,{?MODULE,lists_reverse,2},R}]);
x_exc_value(_Rtt, M, F, _, Arity, Value) ->
- ?line expect({rft,{M,F,Arity},Value}).
+ expect({rft,{M,F,Arity},Value}).
x_exc_exception(_Rtt, ?MODULE, lists_reverse, [La,Lb], 2, CR) ->
- ?line L = lists:reverse(Lb, La),
- ?line expect([fun ({ctt,{lists,reverse},[L1,L2]}) ->
- ?line same(L, lists:reverse(L2, L1)),
- ?line next;
- (Msg) ->
- ?line same({eft,{lists,reverse,2},CR}, Msg),
- ?line done
- end,
- ?LINE,{eft,{?MODULE,lists_reverse,2},CR}]);
+ L = lists:reverse(Lb, La),
+ expect([fun ({ctt,{lists,reverse},[L1,L2]}) ->
+ same(L, lists:reverse(L2, L1)),
+ next;
+ (Msg) ->
+ same({eft,{lists,reverse,2},CR}, Msg),
+ done
+ end,
+ ?LINE,{eft,{?MODULE,lists_reverse,2},CR}]);
x_exc_exception(Rtt, ?MODULE, undef, [_], 1, {Class,Reason}=CR) ->
- ?line expect([{ctt,{erlang,Class},[Reason]},
- ?LINE,{eft,{erlang,Class,1},CR},
- ?LINE,Rtt,{rtt,{error_handler,crash,1}},
- ?LINE,{eft,{?MODULE,undef,1},CR}]);
+ expect([{ctt,{erlang,Class},[Reason]},
+ ?LINE,{eft,{erlang,Class,1},CR},
+ ?LINE,Rtt,{rtt,{error_handler,crash,1}},
+ ?LINE,{eft,{?MODULE,undef,1},CR}]);
x_exc_exception(_Rtt, M, F, _, Arity, CR) ->
- ?line expect({eft,{M,F,Arity},CR}).
+ expect({eft,{M,F,Arity},CR}).
x_exc_stacktrace() ->
x_exc_stacktrace(erlang:get_stacktrace()).
@@ -1252,24 +1192,24 @@ lists_reverse(A, B) ->
slave(Dest, Sync) ->
Dest ! Sync,
receive
- {From,Tag,{apply,M,F,A}} when is_pid(From) ->
- ?line ?dbgformat("Apply: ~p:~p/~p (~p)~n",[M,F,length(A),A]),
- ?line Res = apply(M,F,A),
- ?line ?dbgformat("done Apply: ~p:~p/~p (~p)~n",[M,F,length(A),A]),
- From ! {Tag,Res},
- slave(From, Tag);
- {From,Tag,{lambda,Fun}} when is_pid(From) ->
- Res = Fun(),
- From ! {Tag,Res},
- slave(From, Tag);
- {From,Tag,{exc_top,Catch,Func,Args}} when is_pid(From) ->
- ?line ?dbgformat("Exc: ~p ~p~p ~n",[Catch,Func,Args]),
- ?line Res = exc_top(Catch, Func, Args),
- ?line ?dbgformat("done Exc: ~p ~p~p ~n",[Catch,Func,Args]),
- From ! {Tag,Res},
- slave(From,Tag);
- die ->
- exit(normal)
+ {From,Tag,{apply,M,F,A}} when is_pid(From) ->
+ ?dbgformat("Apply: ~p:~p/~p (~p)~n",[M,F,length(A),A]),
+ Res = apply(M,F,A),
+ ?dbgformat("done Apply: ~p:~p/~p (~p)~n",[M,F,length(A),A]),
+ From ! {Tag,Res},
+ slave(From, Tag);
+ {From,Tag,{lambda,Fun}} when is_pid(From) ->
+ Res = Fun(),
+ From ! {Tag,Res},
+ slave(From, Tag);
+ {From,Tag,{exc_top,Catch,Func,Args}} when is_pid(From) ->
+ ?dbgformat("Exc: ~p ~p~p ~n",[Catch,Func,Args]),
+ Res = exc_top(Catch, Func, Args),
+ ?dbgformat("done Exc: ~p ~p~p ~n",[Catch,Func,Args]),
+ From ! {Tag,Res},
+ slave(From,Tag);
+ die ->
+ exit(normal)
end.
setup(ProcFlags) ->
@@ -1280,30 +1220,34 @@ setup(ProcFlags) ->
Pid = spawn(fun () -> slave(Self, Sync) end),
Mref = erlang:monitor(process, Pid),
receive
- Sync ->
- put(slave, {Pid,Mref}),
- case ProcFlags of
- [] -> ok;
- _ ->
- erlang:trace(Pid, true, ProcFlags)
- end,
- Pid
+ Sync ->
+ put(slave, {Pid,Mref}),
+ case ProcFlags of
+ [] -> ok;
+ _ ->
+ erlang:trace(Pid, true, ProcFlags)
+ end,
+ Pid
end.
shutdown() ->
trace_off(),
- {Pid,Mref} = get(slave),
- try erlang:is_process_alive(Pid) of
- true ->
- Pid ! die,
- receive
- {'DOWN',Mref,process,Pid,Reason} ->
- Reason
- end;
- _ ->
- not_alive
- catch _:_ ->
- undefined
+ case get(slave) of
+ {Pid,Mref} ->
+ try erlang:is_process_alive(Pid) of
+ true ->
+ Pid ! die,
+ receive
+ {'DOWN',Mref,process,Pid,Reason} ->
+ Reason
+ end;
+ _ ->
+ not_alive
+ catch _:_ ->
+ undefined
+ end;
+ _ ->
+ undefined
end.
trace_off() ->
@@ -1311,7 +1255,7 @@ trace_off() ->
erlang:trace_pattern({'_','_','_'},false,[local]),
erlang:trace_pattern({'_','_','_'},false,[meta]),
erlang:trace(all, false, [all]).
-
+
apply_slave_async(M,F,A) ->
{Pid,Mref} = get(slave),
@@ -1332,8 +1276,8 @@ lambda_slave(Fun) ->
exc_slave(Opts, Func, Args) ->
try request({exc_top,Opts,Func,Args})
catch
- Reason ->
- {crash,Reason}
+ Reason ->
+ {crash,Reason}
end.
request(Request) ->
@@ -1344,13 +1288,13 @@ request(Request) ->
result(Tag, Mref) ->
receive
- {Tag,Result} ->
- receive
- Tag ->
- Result
- end;
- {'DOWN',Mref,process,_Pid,Reason} ->
- throw(Reason)
+ {Tag,Result} ->
+ receive
+ Tag ->
+ Result
+ end;
+ {'DOWN',Mref,process,_Pid,Reason} ->
+ throw(Reason)
end.
@@ -1363,25 +1307,25 @@ receive_next() ->
receive_next(TO) ->
receive
- M ->
- M
+ M ->
+ M
after TO ->
- ?t:fail(timeout)
+ ct:fail(timeout)
end.
receive_no_next(TO) ->
receive M ->
- ?t:fail({unexpected_message,[M|flush(TO)]})
+ ct:fail({unexpected_message,[M|flush(TO)]})
after TO ->
- ok
+ ok
end.
flush(T) ->
receive
- M ->
- [M|flush(T)]
+ M ->
+ [M|flush(T)]
after T ->
- []
+ []
end.
@@ -1416,19 +1360,19 @@ abbr(Term, _) -> Term.
%%
abbr_tuple(Tuple, N, J) when J =< size(Tuple) ->
if J > N; N =< 0 ->
- ['...'];
+ ['...'];
true ->
- [abbr(element(J, Tuple), N-1)|abbr_tuple(Tuple, J+1, N)]
+ [abbr(element(J, Tuple), N-1)|abbr_tuple(Tuple, J+1, N)]
end;
abbr_tuple(_, _, _) ->
[].
%%
abbr_list(_, 0, R) ->
case io_lib:printable_list(R) of
- true ->
- reverse(R, "...");
- false ->
- reverse(R, '...')
+ true ->
+ reverse(R, "...");
+ false ->
+ reverse(R, '...')
end;
abbr_list([H|T], N, R) ->
M = N-1,
diff --git a/erts/emulator/test/trace_local_SUITE_data/trace_local_dummy.erl b/erts/emulator/test/trace_local_SUITE_data/trace_local_dummy.erl
index a5947de4aa..a886323302 100644
--- a/erts/emulator/test/trace_local_SUITE_data/trace_local_dummy.erl
+++ b/erts/emulator/test/trace_local_SUITE_data/trace_local_dummy.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/trace_meta_SUITE.erl b/erts/emulator/test/trace_meta_SUITE.erl
index 3b105ec6fe..b6a6fd5404 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -46,7 +46,7 @@
-define(config(A,B),config(A,B)).
-export([config/2]).
-else.
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-endif.
-ifdef(debug).
@@ -66,22 +66,21 @@ config(priv_dir,_) ->
".".
-else.
%% When run in test server.
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
+-export([all/0, suite/0,
init_per_testcase/2, end_per_testcase/2, not_run/1]).
-export([basic/1, return/1, on_and_off/1, stack_grow/1,
info/1, tracer/1, combo/1, nosilent/1]).
init_per_testcase(_Case, Config) ->
- Dog=test_server:timetrap(test_server:minutes(5)),
- [{watchdog, Dog}|Config].
+ Config.
end_per_testcase(_Case, Config) ->
shutdown(),
- Dog=?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
ok.
-suite() -> [{ct_hooks,[ts_install_cth]}].
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 5}}].
all() ->
case test_server:is_native(trace_meta_SUITE) of
@@ -91,74 +90,39 @@ case test_server:is_native(trace_meta_SUITE) of
combo, nosilent]
end.
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
not_run(Config) when is_list(Config) ->
{skipped,"Native code"}.
-basic(suite) ->
- [];
-basic(doc) ->
- ["Tests basic meta trace"];
+%% Tests basic meta trace
basic(Config) when is_list(Config) ->
basic_test().
-return(suite) ->
- [];
-return(doc) ->
- ["Tests return trace"];
+%% Tests return trace
return(Config) when is_list(Config) ->
return_test().
-on_and_off(suite) ->
- [];
-on_and_off(doc) ->
- ["Tests turning trace parameters on and off"];
+%% Tests turning trace parameters on and off
on_and_off(Config) when is_list(Config) ->
on_and_off_test().
-stack_grow(doc) ->
- ["Tests the stack growth during return traces"];
+%% Tests the stack growth during return traces
stack_grow(Config) when is_list(Config) ->
stack_grow_test().
-info(doc) ->
- ["Tests the trace_info BIF"];
+%% Tests the trace_info BIF
info(Config) when is_list(Config) ->
info_test().
-tracer(suite) ->
- [];
-tracer(doc) ->
- ["Tests stopping and changing tracer process"];
+%% Tests stopping and changing tracer process
tracer(Config) when is_list(Config) ->
tracer_test().
-combo(suite) ->
- [];
-combo(doc) ->
- ["Tests combining local call trace with meta trace"];
+%% Tests combining local call trace with meta trace
combo(Config) when is_list(Config) ->
combo_test().
-nosilent(suite) ->
- [];
-nosilent(doc) ->
- ["Tests that meta trace is not silenced by the silent process flag"];
+%% Tests that meta trace is not silenced by the silent process flag
nosilent(Config) when is_list(Config) ->
nosilent_test().
@@ -546,7 +510,7 @@ combo_test() ->
{?RT(Slave,{?MODULE,receiver,1}),
?RF(Slave,{erlang,phash2,2},0)} ->
ok;
- Error1 -> ?t:fail({unexpected_message, Error1})
+ Error1 -> ct:fail({unexpected_message, Error1})
end,
case {receive_next_bytag(LocalTracer),
receive_next_bytag(LocalTracer)} of
@@ -556,7 +520,7 @@ combo_test() ->
{?RT(Slave,{?MODULE,slave,1}),
?RF(Slave,{?MODULE,receiver,1},Ref)} ->
ok;
- Error2 -> ?t:fail({unexpected_message, Error2})
+ Error2 -> ct:fail({unexpected_message, Error2})
end,
shutdown(),
?NM,
@@ -745,13 +709,13 @@ receive_next(TO) ->
M ->
M
after TO ->
- ?t:fail(timeout)
+ ct:fail(timeout)
end.
receive_no_next(TO) ->
receive
M ->
- ?t:fail({unexpected_message, M})
+ ct:fail({unexpected_message, M})
after
TO ->
ok
diff --git a/erts/emulator/test/trace_nif_SUITE.erl b/erts/emulator/test/trace_nif_SUITE.erl
index 1cd50350e3..8d5bff2a48 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,10 +20,9 @@
-module(trace_nif_SUITE).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2]).
+-export([all/0, suite/0]).
-export([trace_nif/1,
trace_nif_timestamp/1,
trace_nif_local/1,
@@ -45,259 +44,230 @@ all() ->
trace_nif_return]
end.
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
not_run(Config) when is_list(Config) ->
{skipped,"Native code"}.
-trace_nif(doc) -> "Test tracing NIFs.";
+%% Test tracing NIFs.
trace_nif(Config) when is_list(Config) ->
load_nif(Config),
-
+
do_trace_nif([]).
-trace_nif_local(doc) -> "Test tracing NIFs with local flag.";
+%% Test tracing NIFs with local flag.
trace_nif_local(Config) when is_list(Config) ->
load_nif(Config),
do_trace_nif([local]).
-trace_nif_meta(doc) -> "Test tracing NIFs with meta flag.";
+%% Test tracing NIFs with meta flag.
trace_nif_meta(Config) when is_list(Config) ->
load_nif(Config),
- ?line Pid=spawn_link(?MODULE, nif_process, []),
- ?line erlang:trace_pattern({?MODULE,nif,'_'}, [], [meta]),
-
- ?line Pid ! {apply_nif, nif, []},
- ?line receive_trace_msg_ts({trace_ts,Pid,call,{?MODULE,nif,[]}}),
-
- ?line Pid ! {apply_nif, nif, ["Arg1"]},
- ?line receive_trace_msg_ts({trace_ts,Pid,call,
- {?MODULE,nif, ["Arg1"]}}),
-
- ?line Pid ! {call_nif, nif, []},
- ?line receive_trace_msg_ts({trace_ts,Pid,call,
- {?MODULE,nif, []}}),
-
- ?line Pid ! {call_nif, nif, ["Arg1"]},
- ?line receive_trace_msg_ts({trace_ts,Pid,call,
- {?MODULE,nif, ["Arg1"]}}),
+ Pid=spawn_link(?MODULE, nif_process, []),
+ erlang:trace_pattern({?MODULE,nif,'_'}, [], [meta]),
+
+ Pid ! {apply_nif, nif, []},
+ receive_trace_msg_ts({trace_ts,Pid,call,{?MODULE,nif,[]}}),
+
+ Pid ! {apply_nif, nif, ["Arg1"]},
+ receive_trace_msg_ts({trace_ts,Pid,call,
+ {?MODULE,nif, ["Arg1"]}}),
+
+ Pid ! {call_nif, nif, []},
+ receive_trace_msg_ts({trace_ts,Pid,call,
+ {?MODULE,nif, []}}),
+
+ Pid ! {call_nif, nif, ["Arg1"]},
+ receive_trace_msg_ts({trace_ts,Pid,call,
+ {?MODULE,nif, ["Arg1"]}}),
ok.
do_trace_nif(Flags) ->
- ?line Pid = spawn(?MODULE, nif_process, []),
- ?line 1 = erlang:trace(Pid, true, [call]),
- ?line erlang:trace_pattern({?MODULE,nif,'_'}, [], Flags),
- ?line Pid ! {apply_nif, nif, []},
- ?line receive_trace_msg({trace,Pid,call,{?MODULE,nif, []}}),
- ?line Pid ! {apply_nif, nif, ["Arg1"]},
- ?line receive_trace_msg({trace,Pid,call,{?MODULE,nif, ["Arg1"]}}),
+ Pid = spawn(?MODULE, nif_process, []),
+ 1 = erlang:trace(Pid, true, [call]),
+ erlang:trace_pattern({?MODULE,nif,'_'}, [], Flags),
+ Pid ! {apply_nif, nif, []},
+ receive_trace_msg({trace,Pid,call,{?MODULE,nif, []}}),
+ Pid ! {apply_nif, nif, ["Arg1"]},
+ receive_trace_msg({trace,Pid,call,{?MODULE,nif, ["Arg1"]}}),
+
+ Pid ! {call_nif, nif, []},
+ receive_trace_msg({trace, Pid, call, {?MODULE,nif, []}}),
- ?line Pid ! {call_nif, nif, []},
- ?line receive_trace_msg({trace, Pid, call, {?MODULE,nif, []}}),
+ Pid ! {call_nif, nif, ["Arg1"]},
+ receive_trace_msg({trace, Pid, call, {?MODULE,nif, ["Arg1"]}}),
- ?line Pid ! {call_nif, nif, ["Arg1"]},
- ?line receive_trace_msg({trace, Pid, call, {?MODULE,nif, ["Arg1"]}}),
-
%% Switch off
- ?line 1 = erlang:trace(Pid, false, [call]),
+ 1 = erlang:trace(Pid, false, [call]),
- ?line Pid ! {apply_nif, nif, []},
+ Pid ! {apply_nif, nif, []},
receive_nothing(),
- ?line Pid ! {apply_nif, nif, ["Arg1"]},
+ Pid ! {apply_nif, nif, ["Arg1"]},
receive_nothing(),
- ?line Pid ! {call_nif, nif, []},
+ Pid ! {call_nif, nif, []},
receive_nothing(),
- ?line Pid ! {call_nif, nif, ["Arg1"]},
+ Pid ! {call_nif, nif, ["Arg1"]},
receive_nothing(),
%% Switch on again
- ?line 1 = erlang:trace(Pid, true, [call]),
- ?line erlang:trace_pattern({?MODULE,nif,'_'}, [], Flags),
- ?line Pid ! {apply_nif, nif, []},
- ?line receive_trace_msg({trace,Pid,call,{?MODULE,nif, []}}),
- ?line Pid ! {apply_nif, nif, ["Arg1"]},
- ?line receive_trace_msg({trace,Pid,call,{?MODULE,nif, ["Arg1"]}}),
-
- ?line Pid ! {call_nif, nif, []},
- ?line receive_trace_msg({trace, Pid, call, {?MODULE,nif, []}}),
-
- ?line Pid ! {call_nif, nif, ["Arg1"]},
- ?line receive_trace_msg({trace, Pid, call, {?MODULE,nif, ["Arg1"]}}),
-
- ?line 1 = erlang:trace(Pid, false, [call]),
- ?line erlang:trace_pattern({?MODULE,nif,'_'}, false, Flags),
- ?line exit(Pid, die),
+ 1 = erlang:trace(Pid, true, [call]),
+ erlang:trace_pattern({?MODULE,nif,'_'}, [], Flags),
+ Pid ! {apply_nif, nif, []},
+ receive_trace_msg({trace,Pid,call,{?MODULE,nif, []}}),
+ Pid ! {apply_nif, nif, ["Arg1"]},
+ receive_trace_msg({trace,Pid,call,{?MODULE,nif, ["Arg1"]}}),
+
+ Pid ! {call_nif, nif, []},
+ receive_trace_msg({trace, Pid, call, {?MODULE,nif, []}}),
+
+ Pid ! {call_nif, nif, ["Arg1"]},
+ receive_trace_msg({trace, Pid, call, {?MODULE,nif, ["Arg1"]}}),
+
+ 1 = erlang:trace(Pid, false, [call]),
+ erlang:trace_pattern({?MODULE,nif,'_'}, false, Flags),
+ exit(Pid, die),
ok.
-trace_nif_timestamp(doc) -> "Test tracing NIFs with timestamps.";
+%% Test tracing NIFs with timestamps.
trace_nif_timestamp(Config) when is_list(Config) ->
load_nif(Config),
do_trace_nif_timestamp([]).
-trace_nif_timestamp_local(doc) ->
- "Test tracing NIFs with timestamps and local flag.";
+%% Test tracing NIFs with timestamps and local flag.
trace_nif_timestamp_local(Config) when is_list(Config) ->
load_nif(Config),
do_trace_nif_timestamp([local]).
do_trace_nif_timestamp(Flags) ->
- ?line Pid=spawn(?MODULE, nif_process, []),
- ?line 1 = erlang:trace(Pid, true, [call,timestamp]),
- ?line erlang:trace_pattern({?MODULE,nif,'_'}, [], Flags),
-
- ?line Pid ! {apply_nif, nif, []},
- ?line receive_trace_msg_ts({trace_ts,Pid,call,{?MODULE,nif,[]}}),
-
- ?line Pid ! {apply_nif, nif, ["Arg1"]},
- ?line receive_trace_msg_ts({trace_ts,Pid,call,
- {?MODULE,nif, ["Arg1"]}}),
-
- ?line Pid ! {call_nif, nif, []},
- ?line receive_trace_msg_ts({trace_ts,Pid,call,
- {?MODULE,nif, []}}),
-
- ?line Pid ! {call_nif, nif, ["Arg1"]},
- ?line receive_trace_msg_ts({trace_ts,Pid,call,
- {?MODULE,nif, ["Arg1"]}}),
-
+ Pid=spawn(?MODULE, nif_process, []),
+ 1 = erlang:trace(Pid, true, [call,timestamp]),
+ erlang:trace_pattern({?MODULE,nif,'_'}, [], Flags),
+
+ Pid ! {apply_nif, nif, []},
+ receive_trace_msg_ts({trace_ts,Pid,call,{?MODULE,nif,[]}}),
+
+ Pid ! {apply_nif, nif, ["Arg1"]},
+ receive_trace_msg_ts({trace_ts,Pid,call,
+ {?MODULE,nif, ["Arg1"]}}),
+
+ Pid ! {call_nif, nif, []},
+ receive_trace_msg_ts({trace_ts,Pid,call,
+ {?MODULE,nif, []}}),
+
+ Pid ! {call_nif, nif, ["Arg1"]},
+ receive_trace_msg_ts({trace_ts,Pid,call,
+ {?MODULE,nif, ["Arg1"]}}),
+
%% We should be able to turn off the timestamp.
- ?line 1 = erlang:trace(Pid, false, [timestamp]),
-
- ?line Pid ! {call_nif, nif, []},
- ?line receive_trace_msg({trace,Pid,call,
- {?MODULE,nif, []}}),
-
- ?line Pid ! {apply_nif, nif, ["tjoho"]},
- ?line receive_trace_msg({trace,Pid,call,
- {?MODULE,nif, ["tjoho"]}}),
-
- ?line 1 = erlang:trace(Pid, false, [call]),
- ?line erlang:trace_pattern({erlang,'_','_'}, false, Flags),
-
- ?line exit(Pid, die),
+ 1 = erlang:trace(Pid, false, [timestamp]),
+
+ Pid ! {call_nif, nif, []},
+ receive_trace_msg({trace,Pid,call,
+ {?MODULE,nif, []}}),
+
+ Pid ! {apply_nif, nif, ["tjoho"]},
+ receive_trace_msg({trace,Pid,call,
+ {?MODULE,nif, ["tjoho"]}}),
+
+ 1 = erlang:trace(Pid, false, [call]),
+ erlang:trace_pattern({erlang,'_','_'}, false, Flags),
+
+ exit(Pid, die),
ok.
-trace_nif_return(doc) ->
- "Test tracing NIF's with return/return_to trace.";
+%% Test tracing NIF's with return/return_to trace.
trace_nif_return(Config) when is_list(Config) ->
load_nif(Config),
- ?line Pid=spawn(?MODULE, nif_process, []),
- ?line 1 = erlang:trace(Pid, true, [call,timestamp,return_to]),
- ?line erlang:trace_pattern({?MODULE,nif,'_'}, [{'_',[],[{return_trace}]}],
- [local]),
-
- ?line Pid ! {apply_nif, nif, []},
- ?line receive_trace_msg_ts({trace_ts,Pid,call,{?MODULE,nif,[]}}),
- ?line receive_trace_msg_ts_return_from({trace_ts,Pid,return_from,
- {?MODULE,nif,0}}),
- ?line receive_trace_msg_ts_return_to({trace_ts,Pid,return_to,
- {?MODULE, nif_process,0}}),
-
- ?line Pid ! {call_nif, nif, ["Arg1"]},
- ?line receive_trace_msg_ts({trace_ts,Pid,call,
- {?MODULE,nif, ["Arg1"]}}),
- ?line receive_trace_msg_ts_return_from({trace_ts,Pid,return_from,
- {?MODULE,nif,1}}),
- ?line receive_trace_msg_ts_return_to({trace_ts,Pid,return_to,
- {?MODULE, nif_process,0}}),
+ Pid=spawn(?MODULE, nif_process, []),
+ 1 = erlang:trace(Pid, true, [call,timestamp,return_to]),
+ erlang:trace_pattern({?MODULE,nif,'_'}, [{'_',[],[{return_trace}]}],
+ [local]),
+
+ Pid ! {apply_nif, nif, []},
+ receive_trace_msg_ts({trace_ts,Pid,call,{?MODULE,nif,[]}}),
+ receive_trace_msg_ts_return_from({trace_ts,Pid,return_from,
+ {?MODULE,nif,0}}),
+ receive_trace_msg_ts_return_to({trace_ts,Pid,return_to,
+ {?MODULE, nif_process,0}}),
+
+ Pid ! {call_nif, nif, ["Arg1"]},
+ receive_trace_msg_ts({trace_ts,Pid,call,
+ {?MODULE,nif, ["Arg1"]}}),
+ receive_trace_msg_ts_return_from({trace_ts,Pid,return_from,
+ {?MODULE,nif,1}}),
+ receive_trace_msg_ts_return_to({trace_ts,Pid,return_to,
+ {?MODULE, nif_process,0}}),
ok.
receive_trace_msg(Mess) ->
receive
- Mess ->
- ok;
- Other ->
- io:format("Expected: ~p,~nGot: ~p~n", [Mess, Other]),
- ?t:fail()
+ Mess ->
+ ok;
+ Other ->
+ ct:fail("Expected: ~p,~nGot: ~p~n", [Mess, Other])
after 5000 ->
- io:format("Expected: ~p,~nGot: timeout~n", [Mess]),
- ?t:fail()
+ ct:fail("Expected: ~p,~nGot: timeout~n", [Mess])
end.
receive_nothing() ->
- ?line timeout = receive M -> M after 100 -> timeout end.
+ timeout = receive M -> M after 100 -> timeout end.
receive_trace_msg_ts({trace_ts, Pid, call, {M,F,A}}) ->
receive
- {trace_ts, Pid, call, {M, F, A}, _Ts} ->
- ok;
- Other ->
- io:format("Expected: {trace, ~p, call, {~p, ~p, ~p}, TimeStamp}},~n"
- "Got: ~p~n",
- [Pid, M, F, A, Other]),
- ?t:fail()
+ {trace_ts, Pid, call, {M, F, A}, _Ts} ->
+ ok;
+ Other ->
+ ct:fail("Expected: {trace, ~p, call, {~p, ~p, ~p}, TimeStamp}},~n"
+ "Got: ~p~n", [Pid, M, F, A, Other])
after 5000 ->
- io:format("Got timeout~n", []),
- ?t:fail()
+ ct:fail("Got timeout~n", [])
end.
receive_trace_msg_ts_return_from({trace_ts, Pid, return_from, {M,F,A}}) ->
receive
- {trace_ts, Pid, return_from, {M, F, A}, _Value, _Ts} ->
- ok;
- Other ->
- io:format("Expected: {trace_ts, ~p, return_from, {~p, ~p, ~p}, Value, TimeStamp}},~n"
- "Got: ~p~n",
- [Pid, M, F, A, Other]),
- ?t:fail()
+ {trace_ts, Pid, return_from, {M, F, A}, _Value, _Ts} ->
+ ok;
+ Other ->
+ ct:fail("Expected: {trace_ts, ~p, return_from, {~p, ~p, ~p}, Value, TimeStamp}},~n"
+ "Got: ~p~n", [Pid, M, F, A, Other])
after 5000 ->
- io:format("Got timeout~n", []),
- ?t:fail()
+ ct:fail("Got timeout~n", [])
end.
receive_trace_msg_ts_return_to({trace_ts, Pid, return_to, {M,F,A}}) ->
receive
- {trace_ts, Pid, return_to, {M, F, A}, _Ts} ->
- ok;
- Other ->
- io:format("Expected: {trace_ts, ~p, return_to, {~p, ~p, ~p}, TimeStamp}},~n"
- "Got: ~p~n",
- [Pid, M, F, A, Other]),
- ?t:fail()
+ {trace_ts, Pid, return_to, {M, F, A}, _Ts} ->
+ ok;
+ Other ->
+ ct:fail("Expected: {trace_ts, ~p, return_to, {~p, ~p, ~p}, TimeStamp}},~n"
+ "Got: ~p~n", [Pid, M, F, A, Other])
after 5000 ->
- io:format("Got timeout~n", []),
- ?t:fail()
+ ct:fail("Got timeout~n", [])
end.
nif_process() ->
receive
- {apply_nif, Name, Args} ->
- ?line {ok,Args} = apply(?MODULE, Name, Args);
-
- {call_nif, Name, []} ->
- ?line {ok, []} = ?MODULE:Name();
-
- {call_nif, Name, [A1]} ->
- ?line {ok, [A1]} = ?MODULE:Name(A1);
-
- {call_nif, Name, [A1,A2]} ->
- ?line {ok,[A1,A2]} = ?MODULE:Name(A1,A2);
-
- {call_nif, Name, [A1,A2,A3]} ->
- ?line {ok,[A1,A2,A3]} = ?MODULE:Name(A1,A2,A3)
+ {apply_nif, Name, Args} ->
+ {ok,Args} = apply(?MODULE, Name, Args);
+
+ {call_nif, Name, []} ->
+ {ok, []} = ?MODULE:Name();
+
+ {call_nif, Name, [A1]} ->
+ {ok, [A1]} = ?MODULE:Name(A1);
+
+ {call_nif, Name, [A1,A2]} ->
+ {ok,[A1,A2]} = ?MODULE:Name(A1,A2);
+
+ {call_nif, Name, [A1,A2,A3]} ->
+ {ok,[A1,A2,A3]} = ?MODULE:Name(A1,A2,A3)
end,
nif_process().
load_nif(Config) ->
- ?line Path = ?config(data_dir, Config),
-
- ?line ok = erlang:load_nif(filename:join(Path,"trace_nif"), 0).
+ Path = proplists:get_value(data_dir, Config),
+
+ ok = erlang:load_nif(filename:join(Path,"trace_nif"), 0).
nif() ->
@@ -305,4 +275,3 @@ nif() ->
nif(A1) ->
{"Stub1",[A1]}. %exit(["nif/1 stub called",A1]).
-
diff --git a/erts/emulator/test/trace_port_SUITE.erl b/erts/emulator/test/trace_port_SUITE.erl
index a77710205e..e4db368ea1 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-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -21,239 +21,227 @@
-module(trace_port_SUITE).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2,end_per_testcase/2,
+-export([all/0, suite/0,
call_trace/1,
return_trace/1,
send/1,
receive_trace/1,
+ receive_trace_non_scheduler/1,
process_events/1,
schedule/1,
- fake_schedule/1,
- fake_schedule_after_register/1,
- fake_schedule_after_getting_linked/1,
- fake_schedule_after_getting_unlinked/1,
gc/1,
default_tracer/1,
tracer_port_crash/1]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-test_cases() ->
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {seconds, 30}}].
+
+all() ->
[call_trace, return_trace, send, receive_trace,
- process_events, schedule, fake_schedule,
- fake_schedule_after_register,
- fake_schedule_after_getting_linked,
- fake_schedule_after_getting_unlinked, gc,
+ receive_trace_non_scheduler,
+ process_events, schedule, gc,
default_tracer, tracer_port_crash].
-suite() -> [{ct_hooks,[ts_install_cth]}].
-
-all() ->
- test_cases().
-
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- Dog = ?t:timetrap(?t:seconds(30)),
- [{watchdog, Dog}|Config].
-
-end_per_testcase(_Func, Config) ->
- Dog = ?config(watchdog, Config),
- ?t:timetrap_cancel(Dog).
-
-call_trace(doc) -> "Test sending call trace messages to a port.";
+%% Test sending call trace messages to a port.
call_trace(Config) when is_list(Config) ->
case test_server:is_native(?MODULE) orelse
- test_server:is_native(lists) of
- true ->
- {skip,"Native code"};
- false ->
- ?line start_tracer(Config),
- Self = self(),
- ?line trace_func({lists,reverse,1}, []),
- ?line trace_pid(Self, true, [call]),
- ?line trace_info(Self, flags),
- ?line trace_info(Self, tracer),
- ?line [b,a] = lists:reverse([a,b]),
- ?line expect({trace,Self,call,{lists,reverse,[[a,b]]}}),
-
- ?line trace_pid(Self, true, [timestamp]),
- ?line trace_info(Self, flags),
- ?line Huge = huge_data(),
- ?line lists:reverse(Huge),
- ?line expect({trace_ts,Self,call,{lists,reverse,[Huge]},ts}),
-
- ?line trace_pid(Self, true, [arity]),
- ?line trace_info(Self, flags),
- ?line [y,x] = lists:reverse([x,y]),
- ?line expect({trace_ts,Self,call,{lists,reverse,1},ts}),
-
- ?line trace_pid(Self, false, [timestamp]),
- ?line trace_info(Self, flags),
- ?line [z,y,x] = lists:reverse([x,y,z]),
- ?line expect({trace,Self,call,{lists,reverse,1}}),
-
- %% OTP-7399. Delayed sub-binary creation optimization.
- ?line trace_pid(Self, false, [arity]),
- ?line trace_info(Self, flags),
- ?line trace_func({?MODULE,bs_sum_c,2}, [], [local]),
- ?line 26 = bs_sum_c(<<3:4,5:4,7:4,11:4>>, 0),
- ?line trace_func({?MODULE,bs_sum_c,2}, false, [local]),
- ?line expect({trace,Self,call,{?MODULE,bs_sum_c,[<<3:4,5:4,7:4,11:4>>,0]}}),
- ?line expect({trace,Self,call,{?MODULE,bs_sum_c,[<<5:4,7:4,11:4>>,3]}}),
- ?line expect({trace,Self,call,{?MODULE,bs_sum_c,[<<7:4,11:4>>,8]}}),
- ?line expect({trace,Self,call,{?MODULE,bs_sum_c,[<<11:4>>,15]}}),
- ?line expect({trace,Self,call,{?MODULE,bs_sum_c,[<<>>,26]}}),
-
- ?line trace_func({lists,reverse,1}, false),
- ok
+ test_server:is_native(lists) of
+ true ->
+ {skip,"Native code"};
+ false ->
+ start_tracer(Config),
+ Self = self(),
+ trace_func({lists,reverse,1}, []),
+ trace_pid(Self, true, [call]),
+ trace_info(Self, flags),
+ trace_info(Self, tracer),
+ [b,a] = lists:reverse([a,b]),
+ expect({trace,Self,call,{lists,reverse,[[a,b]]}}),
+
+ trace_pid(Self, true, [timestamp]),
+ trace_info(Self, flags),
+ Huge = huge_data(),
+ lists:reverse(Huge),
+ expect({trace_ts,Self,call,{lists,reverse,[Huge]},ts}),
+
+ trace_pid(Self, true, [arity]),
+ trace_info(Self, flags),
+ [y,x] = lists:reverse([x,y]),
+ expect({trace_ts,Self,call,{lists,reverse,1},ts}),
+
+ trace_pid(Self, false, [timestamp]),
+ trace_info(Self, flags),
+ [z,y,x] = lists:reverse([x,y,z]),
+ expect({trace,Self,call,{lists,reverse,1}}),
+
+ %% OTP-7399. Delayed sub-binary creation optimization.
+ trace_pid(Self, false, [arity]),
+ trace_info(Self, flags),
+ trace_func({?MODULE,bs_sum_c,2}, [], [local]),
+ 26 = bs_sum_c(<<3:4,5:4,7:4,11:4>>, 0),
+ trace_func({?MODULE,bs_sum_c,2}, false, [local]),
+ expect({trace,Self,call,{?MODULE,bs_sum_c,[<<3:4,5:4,7:4,11:4>>,0]}}),
+ expect({trace,Self,call,{?MODULE,bs_sum_c,[<<5:4,7:4,11:4>>,3]}}),
+ expect({trace,Self,call,{?MODULE,bs_sum_c,[<<7:4,11:4>>,8]}}),
+ expect({trace,Self,call,{?MODULE,bs_sum_c,[<<11:4>>,15]}}),
+ expect({trace,Self,call,{?MODULE,bs_sum_c,[<<>>,26]}}),
+
+ trace_func({lists,reverse,1}, false),
+ ok
end.
bs_sum_c(<<H:4,T/bits>>, Acc) -> bs_sum_c(T, H+Acc);
bs_sum_c(<<>>, Acc) -> Acc.
-return_trace(doc) -> "Test the new return trace.";
+%% Test the new return trace.
return_trace(Config) when is_list(Config) ->
case test_server:is_native(?MODULE) orelse
- test_server:is_native(lists) of
- true ->
- {skip,"Native code"};
- false ->
- ?line start_tracer(Config),
- Self = self(),
- MFA = {lists,reverse,1},
-
- %% Plain (no timestamp, small data).
-
- ?line trace_func(MFA, [{['$1'],[],[{return_trace},
- {message,false}]}]),
- ?line trace_pid(Self, true, [call]),
- ?line trace_info(Self, flags),
- ?line trace_info(Self, tracer),
- ?line trace_info(MFA, match_spec),
- ?line {traced,global} = trace_info(MFA, traced),
- ?line [b,a] = lists:reverse([a,b]),
- ?line expect({trace,Self,return_from,MFA,[b,a]}),
-
- %% Timestamp, huge data.
- ?line trace_pid(Self, true, [timestamp]),
- ?line Result = lists:reverse(huge_data()),
- ?line expect({trace_ts,Self,return_from,MFA,Result,ts}),
-
- %% Turn off trace.
- ?line trace_func(MFA, false),
- ?line trace_info(MFA, match_spec),
- ?line {traced,false} = trace_info(MFA, traced),
- ok
+ test_server:is_native(lists) of
+ true ->
+ {skip,"Native code"};
+ false ->
+ start_tracer(Config),
+ Self = self(),
+ MFA = {lists,reverse,1},
+
+ %% Plain (no timestamp, small data).
+
+ trace_func(MFA, [{['$1'],[],[{return_trace},
+ {message,false}]}]),
+ trace_pid(Self, true, [call]),
+ trace_info(Self, flags),
+ trace_info(Self, tracer),
+ trace_info(MFA, match_spec),
+ {traced,global} = trace_info(MFA, traced),
+ [b,a] = lists:reverse([a,b]),
+ expect({trace,Self,return_from,MFA,[b,a]}),
+
+ %% Timestamp, huge data.
+ trace_pid(Self, true, [timestamp]),
+ Result = lists:reverse(huge_data()),
+ expect({trace_ts,Self,return_from,MFA,Result,ts}),
+
+ %% Turn off trace.
+ trace_func(MFA, false),
+ trace_info(MFA, match_spec),
+ {traced,false} = trace_info(MFA, traced),
+ ok
end.
-send(doc) -> "Test sending send trace messages to a port.";
+%% Test sending send trace messages to a port.
send(Config) when is_list(Config) ->
- ?line Tracer = start_tracer(Config),
+ Tracer = start_tracer(Config),
Self = self(),
- ?line Sender = fun_spawn(fun sender/0),
- ?line trac(Sender, true, [send]),
+ Sender = fun_spawn(fun sender/0),
+ trac(Sender, true, [send]),
%% Simple message, no timestamp.
- ?line Bin = list_to_binary(lists:seq(1, 10)),
- ?line Msg = {some_data,Bin},
+ Bin = list_to_binary(lists:seq(1, 10)),
+ Msg = {some_data,Bin},
Sender ! {send_please,self(),Msg},
receive Msg -> ok end,
- ?line expect({trace,Sender,send,Msg,Self}),
+ expect({trace,Sender,send,Msg,Self}),
%% Timestamp.
BiggerMsg = {even_bigger,Msg},
- ?line trac(Sender, true, [send,timestamp]),
+ trac(Sender, true, [send,timestamp]),
Sender ! {send_please,self(),BiggerMsg},
receive BiggerMsg -> ok end,
- ?line expect({trace_ts,Sender,send,BiggerMsg,Self,ts}),
+ expect({trace_ts,Sender,send,BiggerMsg,Self,ts}),
%% Huge message.
- ?line HugeMsg = huge_data(),
+ HugeMsg = huge_data(),
Sender ! {send_please,self(),HugeMsg},
receive HugeMsg -> ok end,
- ?line expect({trace_ts,Sender,send,HugeMsg,Self,ts}),
+ expect({trace_ts,Sender,send,HugeMsg,Self,ts}),
%% Kill trace port and force a trace. The emulator should not crasch.
- ?line unlink(Tracer),
- ?line exit(Tracer, kill),
+ unlink(Tracer),
+ exit(Tracer, kill),
erlang:yield(), % Make sure that port gets killed.
Sender ! {send_please,Self,good_bye},
receive good_bye -> ok end,
ok.
-receive_trace(doc) -> "Test sending receive traces to a port.";
+%% Test sending receive traces to a port.
receive_trace(Config) when is_list(Config) ->
- ?line start_tracer(Config),
- ?line Receiver = fun_spawn(fun receiver/0),
- ?line trac(Receiver, true, ['receive']),
+ start_tracer(Config),
+ Receiver = fun_spawn(fun receiver/0),
+ trac(Receiver, true, ['receive']),
Receiver ! {hello,world},
- ?line expect({trace,Receiver,'receive',{hello,world}}),
+ expect({trace,Receiver,'receive',{hello,world}}),
- ?line trac(Receiver, true, ['receive',timestamp]),
+ trac(Receiver, true, ['receive',timestamp]),
Huge = {hello,huge_data()},
Receiver ! {hello,huge_data()},
- ?line expect({trace_ts,Receiver,'receive',Huge,ts}),
+ expect({trace_ts,Receiver,'receive',Huge,ts}),
ok.
-process_events(doc) -> "Tests a few process events (like getting linked).";
+%% Test sending receive traces to a port.
+receive_trace_non_scheduler(Config) when is_list(Config) ->
+ start_tracer(Config),
+ S = self(),
+ Receiver = spawn(
+ fun() ->
+ receive
+ go ->
+ Ref = S ! erlang:trace_delivered(all),
+ receive {trace_delivered, Ref, all} -> ok end
+ end
+ end),
+ trac(Receiver, true, ['receive']),
+ Receiver ! go,
+ Ref = receive R -> R end,
+ expect({trace,Receiver,'receive',go}),
+ expect({trace,Receiver,'receive',{trace_delivered, all, Ref}}),
+
+ ok.
+
+%% Tests a few process events (like getting linked).
process_events(Config) when is_list(Config) ->
- ?line start_tracer(Config),
+ start_tracer(Config),
Self = self(),
- ?line Receiver = fun_spawn(fun receiver/0),
- ?line trac(Receiver, true, [procs]),
+ Receiver = fun_spawn(fun receiver/0),
+ trac(Receiver, true, [procs]),
unlink(Receiver), %It is already linked.
- ?line expect({trace,Receiver,getting_unlinked,Self}),
+ expect({trace,Receiver,getting_unlinked,Self}),
link(Receiver),
- ?line expect({trace,Receiver,getting_linked,Self}),
- ?line trac(Receiver, true, [procs,timestamp]),
+ expect({trace,Receiver,getting_linked,Self}),
+ trac(Receiver, true, [procs,timestamp]),
unlink(Receiver),
- ?line expect({trace_ts,Receiver,getting_unlinked,Self,ts}),
+ expect({trace_ts,Receiver,getting_unlinked,Self,ts}),
link(Receiver),
- ?line expect({trace_ts,Receiver,getting_linked,Self,ts}),
+ expect({trace_ts,Receiver,getting_linked,Self,ts}),
unlink(Receiver),
- ?line expect({trace_ts,Receiver,getting_unlinked,Self,ts}),
+ expect({trace_ts,Receiver,getting_unlinked,Self,ts}),
Huge = huge_data(),
exit(Receiver, Huge),
- ?line expect({trace_ts,Receiver,exit,Huge,ts}),
+ expect({trace_ts,Receiver,exit,Huge,ts}),
ok.
-schedule(doc) -> "Test sending scheduling events to a port.";
+%% Test sending scheduling events to a port.
schedule(Config) when is_list(Config) ->
- ?line start_tracer(Config),
- ?line Receiver = fun_spawn(fun receiver/0),
- ?line trac(Receiver, true, [running]),
+ start_tracer(Config),
+ Receiver = fun_spawn(fun receiver/0),
+ trac(Receiver, true, [running]),
Receiver ! hi,
expect({trace,Receiver,in,{?MODULE,receiver,0}}),
expect({trace,Receiver,out,{?MODULE,receiver,0}}),
- ?line trac(Receiver, true, [running,timestamp]),
+ trac(Receiver, true, [running,timestamp]),
Receiver ! hi_again,
expect({trace_ts,Receiver,in,{?MODULE,receiver,0},ts}),
@@ -261,253 +249,93 @@ schedule(Config) when is_list(Config) ->
ok.
-run_fake_sched_test(Fun, Config) when is_function(Fun), is_list(Config) ->
- ?line case catch erlang:system_info(smp_support) of
- true ->
- ?line {skipped,
- "No need for faked schedule out/in trace messages "
- "when smp support is enabled"};
- _ ->
- ?line Fun(Config)
- end.
-
-fake_schedule(doc) -> "Tests time compensating fake out/in scheduling.";
-fake_schedule(Config) when is_list(Config) ->
- ?line run_fake_sched_test(fun fake_schedule_test/1, Config).
-
-fake_schedule_test(Config) when is_list(Config) ->
- ?line Tracer = start_tracer(Config),
- ?line Port = get(tracer_port),
- ?line General = fun_spawn(fun general/0),
- %%
- ?line trac(General, true, [send, running]),
- %%
- %% Test that fake out/in scheduling is not generated unless
- %% both 'running' and 'timestamp' is active.
- ?line [] = erlang:port_control(Port, $h, []),
- ?line General ! nop,
- ?line expect({trace, General, in, {?MODULE, general, 0}}),
- ?line expect({trace, General, out, {?MODULE, general, 0}}),
- ?line expect(),
- %%
- ?line trac(General, false, [running]),
- ?line trac(General, true, [timestamp]),
- %%
- ?line Ref1 = make_ref(),
- ?line Msg1 = {Port, {data, term_to_binary(Ref1)}},
- ?line [] = erlang:port_control(Port, $h, []),
- ?line General ! {send, Tracer, Msg1},
- ?line expect({trace_ts, General, send, Msg1, Tracer, ts}),
- ?line expect(Ref1),
- ?line expect(),
- %%
- ?line trac(General, true, [running]),
- %%
- %% Test that fake out/in scheduling can be generated by the driver
- ?line Ref2 = make_ref(),
- ?line Msg2 = {Port, {data, term_to_binary(Ref2)}},
- ?line [] = erlang:port_control(Port, $h, []),
- ?line General ! {send, Tracer, Msg2},
- ?line {_,_,_,_,Ts} =
- expect({trace_ts, General, in, {?MODULE, general, 0}, ts}),
- ?line expect({trace_ts, General, out, 0, Ts}),
- ?line expect({trace_ts, General, in, 0, ts}),
- ?line expect({trace_ts, General, send, Msg2, Tracer, ts}),
- ?line expect(Ref2),
- ?line expect({trace_ts, General, out, {?MODULE, general, 0}, ts}),
- ?line expect(),
- %%
- %% Test that fake out/in scheduling is not generated after an
- %% 'out' scheduling event
- ?line Ref3 = make_ref(),
- ?line Msg3 = {Port, {data, term_to_binary(Ref3)}},
- ?line General ! {apply, {erlang, port_control, [Port, $h, []]}},
- ?line expect({trace_ts, General, in, {?MODULE, general, 0}, ts}),
- ?line expect({trace_ts, General, out, {?MODULE, general, 0}, ts}),
- ?line General ! {send, Tracer, Msg3},
- ?line expect({trace_ts, General, in, {?MODULE, general, 0}, ts}),
- ?line expect({trace_ts, General, send, Msg3, Tracer, ts}),
- ?line expect(Ref3),
- ?line expect({trace_ts, General, out, {?MODULE, general, 0}, ts}),
- ?line expect(),
- %%
- ok.
-
-fake_schedule_after_register(doc) ->
- "Tests fake out/in scheduling contents.";
-fake_schedule_after_register(Config) when is_list(Config) ->
- ?line run_fake_sched_test(fun fake_schedule_after_register_test/1, Config).
-
-fake_schedule_after_register_test(Config) when is_list(Config) ->
- ?line start_tracer(Config),
- ?line Port = get(tracer_port),
- ?line G1 = fun_spawn(fun general/0),
- ?line G2 = fun_spawn(fun general/0),
- %%
- ?line trac(G1, true, [running, timestamp, procs]),
- ?line trac(G2, true, [running, timestamp]),
- %%
- %% Test fake out/in scheduling after certain messages
- ?line erlang:yield(),
- ?line G2 ! {apply, {erlang, port_control, [Port, $h, []]}},
- ?line G2 ! {apply, {erlang, register, [fake_schedule_after_register, G1]}},
- ?line expect({trace_ts, G2, in, {?MODULE, general, 0}, ts}),
- ?line {_,_,_,_,Ts} =
- expect({trace_ts, G1, register, fake_schedule_after_register, ts}),
- ?line expect({trace_ts, G2, out, 0, Ts}),
- ?line expect({trace_ts, G2, in, 0, ts}),
- ?line expect({trace_ts, G2, out, {?MODULE, general, 0}, ts}),
- ?line expect(),
- %%
- ok.
-
-fake_schedule_after_getting_linked(doc) ->
- "Tests fake out/in scheduling contents.";
-fake_schedule_after_getting_linked(Config) when is_list(Config) ->
- ?line run_fake_sched_test(fun fake_schedule_after_getting_linked_test/1,
- Config).
-
-fake_schedule_after_getting_linked_test(Config) when is_list(Config) ->
- ?line start_tracer(Config),
- ?line Port = get(tracer_port),
- ?line G1 = fun_spawn(fun general/0),
- ?line G2 = fun_spawn(fun general/0),
- %%
- ?line trac(G1, true, [running, timestamp, procs]),
- ?line trac(G2, true, [running, timestamp]),
- %%
- %% Test fake out/in scheduling after certain messages
- ?line erlang:yield(),
- ?line G2 ! {apply, {erlang, port_control, [Port, $h, []]}},
- ?line G2 ! {apply, {erlang, link, [G1]}},
- ?line expect({trace_ts, G2, in, {?MODULE, general, 0}, ts}),
- ?line {_,_,_,_,Ts} =
- expect({trace_ts, G1, getting_linked, G2, ts}),
- ?line expect({trace_ts, G2, out, 0, Ts}),
- ?line expect({trace_ts, G2, in, 0, ts}),
- ?line expect({trace_ts, G2, out, {?MODULE, general, 0}, ts}),
- ?line expect(),
- %%
- ok.
-
-fake_schedule_after_getting_unlinked(doc) ->
- "Tests fake out/in scheduling contents.";
-fake_schedule_after_getting_unlinked(Config) when is_list(Config) ->
- ?line run_fake_sched_test(fun fake_schedule_after_getting_unlinked_test/1,
- Config).
-
-fake_schedule_after_getting_unlinked_test(Config) when is_list(Config) ->
- ?line start_tracer(Config),
- ?line Port = get(tracer_port),
- ?line G1 = fun_spawn(fun general/0),
- ?line G2 = fun_spawn(fun general/0),
- %%
- ?line trac(G1, true, [running, procs]),
- ?line trac(G2, true, [running, timestamp]),
- %%
- %% Test fake out/in scheduling after certain messages
- ?line erlang:yield(),
- ?line G2 ! {apply, {erlang, link, [G1]}},
- ?line G2 ! {apply, {erlang, port_control, [Port, $h, []]}},
- ?line G2 ! {apply, {erlang, unlink, [G1]}},
- ?line expect({trace_ts, G2, in, {?MODULE, general, 0}, ts}),
- ?line expect({trace, G1, getting_linked, G2}),
- ?line expect({trace, G1, getting_unlinked, G2}),
- ?line expect({trace_ts, G2, out, 0, ts}),
- ?line expect({trace_ts, G2, in, 0, ts}),
- ?line expect({trace_ts, G2, out, {?MODULE, general, 0}, ts}),
- ?line expect(),
- %%
- ok.
-
-gc(doc) -> "Test sending garbage collection events to a port.";
+%% Test sending garbage collection events to a port.
gc(Config) when is_list(Config) ->
- ?line start_tracer(Config),
- ?line Garber = fun_spawn(fun garber/0, [{min_heap_size, 5000}]),
- ?line trac(Garber, true, [garbage_collection]),
- ?line trace_info(Garber, flags),
+ start_tracer(Config),
+ Garber = fun_spawn(fun garber/0, [{min_heap_size, 5000}]),
+ trac(Garber, true, [garbage_collection]),
+ trace_info(Garber, flags),
- ?line trace_info(Garber, flags),
+ trace_info(Garber, flags),
Garber ! hi,
- expect({trace,Garber,gc_start,info}),
- expect({trace,Garber,gc_end,info}),
+ expect({trace,Garber,gc_major_start,info}),
+ expect({trace,Garber,gc_major_end,info}),
- ?line trac(Garber, true, [garbage_collection,timestamp]),
+ trac(Garber, true, [garbage_collection,timestamp]),
Garber ! hi,
- expect({trace_ts,Garber,gc_start,info,ts}),
- expect({trace_ts,Garber,gc_end,info,ts}),
+ expect({trace_ts,Garber,gc_major_start,info,ts}),
+ expect({trace_ts,Garber,gc_major_end,info,ts}),
ok.
-default_tracer(doc) ->
- "Test a port as default tracer.";
+%% Test a port as default tracer.
default_tracer(Config) when is_list(Config) ->
- ?line Tracer = start_tracer(Config),
- ?line TracerMonitor = erlang:monitor(process, Tracer),
- ?line Port = get(tracer_port),
+ Tracer = start_tracer(Config),
+ TracerMonitor = erlang:monitor(process, Tracer),
+ Port = get(tracer_port),
%%
- ?line N = erlang:trace(all, true, [send, {tracer, Port}]),
- ?line {flags, [send]} = erlang:trace_info(self(), flags),
- ?line {tracer, Port} = erlang:trace_info(self(), tracer),
- ?line {flags, [send]} = erlang:trace_info(new, flags),
- ?line {tracer, Port} = erlang:trace_info(new, tracer),
- ?line G1 = fun_spawn(fun general/0),
- ?line {flags, [send]} = erlang:trace_info(G1, flags),
- ?line {tracer, Port} = erlang:trace_info(G1, tracer),
- ?line unlink(Tracer),
- ?line exit(Port, done),
- ?line receive
- {'DOWN', TracerMonitor, process, Tracer, TracerExitReason} ->
- ?line done = TracerExitReason
- end,
- ?line {flags, []} = erlang:trace_info(self(), flags),
- ?line {tracer, []} = erlang:trace_info(self(), tracer),
- ?line {flags, []} = erlang:trace_info(new, flags),
- ?line {tracer, []} = erlang:trace_info(new, tracer),
- ?line M = erlang:trace(all, false, [all]),
- ?line {flags, []} = erlang:trace_info(self(), flags),
- ?line {tracer, []} = erlang:trace_info(self(), tracer),
- ?line {flags, []} = erlang:trace_info(G1, flags),
- ?line {tracer, []} = erlang:trace_info(G1, tracer),
- ?line G1 ! {apply,{erlang,exit,[normal]}},
- ?line io:format("~p = ~p.~n", [M, N]),
- ?line M = N,
+ N = erlang:trace(all, true, [send, {tracer, Port}]),
+ {flags, [send]} = erlang:trace_info(self(), flags),
+ {tracer, Port} = erlang:trace_info(self(), tracer),
+ {flags, [send]} = erlang:trace_info(new, flags),
+ {tracer, Port} = erlang:trace_info(new, tracer),
+ G1 = fun_spawn(fun general/0),
+ {flags, [send]} = erlang:trace_info(G1, flags),
+ {tracer, Port} = erlang:trace_info(G1, tracer),
+ unlink(Tracer),
+ exit(Port, done),
+ receive
+ {'DOWN', TracerMonitor, process, Tracer, TracerExitReason} ->
+ done = TracerExitReason
+ end,
+ {flags, []} = erlang:trace_info(self(), flags),
+ {tracer, []} = erlang:trace_info(self(), tracer),
+ {flags, []} = erlang:trace_info(new, flags),
+ {tracer, []} = erlang:trace_info(new, tracer),
+ M = erlang:trace(all, false, [all]),
+ {flags, []} = erlang:trace_info(self(), flags),
+ {tracer, []} = erlang:trace_info(self(), tracer),
+ {flags, []} = erlang:trace_info(G1, flags),
+ {tracer, []} = erlang:trace_info(G1, tracer),
+ G1 ! {apply,{erlang,exit,[normal]}},
+ io:format("~p = ~p.~n", [M, N]),
+ M = N - 1, % G1 has been started, but Tracer and Port have died
ok.
tracer_port_crash(Config) when is_list(Config) ->
case test_server:is_native(?MODULE) orelse
- test_server:is_native(lists) of
- true ->
- {skip,"Native code"};
- false ->
- Tr = start_tracer(Config),
- Port = get(tracer_port),
- Tracee = spawn(fun () ->
- register(trace_port_linker, self()),
- link(Port),
- receive go -> ok end,
- lists:reverse([1,b,c]),
- receive die -> ok end
- end),
- Tr ! {unlink_tracer_port, self()},
- receive {unlinked_tracer_port, Tr} -> ok end,
- port_control(Port, $c, []), %% Make port commands crash tracer port...
- trace_func({lists,reverse,1}, []),
- trace_pid(Tracee, true, [call]),
- trace_info(Tracee, flags),
- trace_info(self(), tracer),
- Tracee ! go,
- receive after 1000 -> ok end,
- case whereis(trace_port_linker) of
- undefined ->
- ok;
- Id ->
-% erts_debug:set_internal_state(available_internal_state, true),
-% erts_debug:set_internal_state(abort, {trace_port_linker, Id})
- ?t:fail({trace_port_linker, Id})
- end,
- undefined = process_info(Tracee),
- ok
+ test_server:is_native(lists) of
+ true ->
+ {skip,"Native code"};
+ false ->
+ Tr = start_tracer(Config),
+ Port = get(tracer_port),
+ Tracee = spawn(fun () ->
+ register(trace_port_linker, self()),
+ link(Port),
+ receive go -> ok end,
+ lists:reverse([1,b,c]),
+ receive die -> ok end
+ end),
+ Tr ! {unlink_tracer_port, self()},
+ receive {unlinked_tracer_port, Tr} -> ok end,
+ port_control(Port, $c, []), %% Make port commands crash tracer port...
+ trace_func({lists,reverse,1}, []),
+ trace_pid(Tracee, true, [call]),
+ trace_info(Tracee, flags),
+ trace_info(self(), tracer),
+ Tracee ! go,
+ receive after 1000 -> ok end,
+ case whereis(trace_port_linker) of
+ undefined ->
+ ok;
+ Id ->
+ % erts_debug:set_internal_state(available_internal_state, true),
+ % erts_debug:set_internal_state(abort, {trace_port_linker, Id})
+ ct:fail({trace_port_linker, Id})
+ end,
+ undefined = process_info(Tracee),
+ ok
end.
%%% Help functions.
@@ -523,106 +351,106 @@ huge_data(N) ->
expect() ->
receive
- Other ->
- ok = io:format("Unexpected; got ~p", [Other]),
- test_server:fail({unexpected, Other})
+ Other ->
+ ok = io:format("Unexpected; got ~p", [Other]),
+ ct:fail({unexpected, Other})
after 200 ->
- ok
+ ok
end.
expect({trace_ts,E1,E2,info,ts}=Message) ->
receive
- {trace_ts,E1,E2,_Info,_Ts}=MessageTs ->
- ok = io:format("Expected and got ~p", [MessageTs]),
- MessageTs;
- Other ->
- io:format("Expected ~p; got ~p", [Message,Other]),
- test_server:fail({unexpected,Other})
+ {trace_ts,E1,E2,_Info,_Ts}=MessageTs ->
+ ok = io:format("Expected and got ~p", [MessageTs]),
+ MessageTs;
+ Other ->
+ io:format("Expected ~p; got ~p", [Message,Other]),
+ ct:fail({unexpected,Other})
after 5000 ->
- io:format("Expected ~p; got nothing", [Message]),
- test_server:fail(no_trace_message)
+ io:format("Expected ~p; got nothing", [Message]),
+ ct:fail(no_trace_message)
end;
expect({trace,E1,E2,info}=Message) ->
receive
- {trace,E1,E2,_Info}=MessageTs ->
- ok = io:format("Expected and got ~p", [MessageTs]),
- MessageTs;
- Other ->
- io:format("Expected ~p; got ~p", [Message,Other]),
- test_server:fail({unexpected,Other})
+ {trace,E1,E2,_Info}=MessageTs ->
+ ok = io:format("Expected and got ~p", [MessageTs]),
+ MessageTs;
+ Other ->
+ io:format("Expected ~p; got ~p", [Message,Other]),
+ ct:fail({unexpected,Other})
after 5000 ->
- io:format("Expected ~p; got nothing", [Message]),
- test_server:fail(no_trace_message)
+ io:format("Expected ~p; got nothing", [Message]),
+ ct:fail(no_trace_message)
end;
expect({trace_ts,E1,E2,E3,ts}=Message) ->
receive
- {trace_ts,E1,E2,E3,_Ts}=MessageTs ->
- ok = io:format("Expected and got ~p", [MessageTs]),
- MessageTs;
- Other ->
- io:format("Expected ~p; got ~p", [Message,Other]),
- test_server:fail({unexpected,Other})
+ {trace_ts,E1,E2,E3,_Ts}=MessageTs ->
+ ok = io:format("Expected and got ~p", [MessageTs]),
+ MessageTs;
+ Other ->
+ io:format("Expected ~p; got ~p", [Message,Other]),
+ ct:fail({unexpected,Other})
after 5000 ->
- io:format("Expected ~p; got nothing", [Message]),
- test_server:fail(no_trace_message)
+ io:format("Expected ~p; got nothing", [Message]),
+ ct:fail(no_trace_message)
end;
expect({trace_ts,E1,E2,E3,E4,ts}=Message) ->
receive
- {trace_ts,E1,E2,E3,E4,_Ts}=MessageTs ->
- ok = io:format("Expected and got ~p", [MessageTs]),
- MessageTs;
- Other ->
- io:format("Expected ~p; got ~p", [Message,Other]),
- test_server:fail({unexpected,Other})
+ {trace_ts,E1,E2,E3,E4,_Ts}=MessageTs ->
+ ok = io:format("Expected and got ~p", [MessageTs]),
+ MessageTs;
+ Other ->
+ io:format("Expected ~p; got ~p", [Message,Other]),
+ ct:fail({unexpected,Other})
after 5000 ->
- io:format("Expected ~p; got nothing", [Message]),
- test_server:fail(no_trace_message)
+ io:format("Expected ~p; got nothing", [Message]),
+ ct:fail(no_trace_message)
end;
expect(Message) ->
receive
- Message ->
- ok = io:format("Expected and got ~p", [Message]),
- Message;
- Other ->
- io:format("Expected ~p; got ~p", [Message,Other]),
- test_server:fail({unexpected,Other})
+ Message ->
+ ok = io:format("Expected and got ~p", [Message]),
+ Message;
+ Other ->
+ io:format("Expected ~p; got ~p", [Message,Other]),
+ ct:fail({unexpected,Other})
after 5000 ->
- io:format("Expected ~p; got nothing", [Message]),
- test_server:fail(no_trace_message)
+ io:format("Expected ~p; got nothing", [Message]),
+ ct:fail(no_trace_message)
end.
trac(What, On, Flags0) ->
Flags = [{tracer,get(tracer_port)}|Flags0],
get(tracer) ! {apply,self(),{erlang,trace,[What,On,Flags]}},
Res = receive
- {apply_result,Result} -> Result
- end,
+ {apply_result,Result} -> Result
+ end,
ok = io:format("erlang:trace(~p, ~p, ~p) -> ~p",
- [What,On,Flags,Res]),
+ [What,On,Flags,Res]),
Res.
-
+
trace_info(What, Key) ->
get(tracer) ! {apply,self(),{erlang,trace_info,[What,Key]}},
Res = receive
- {apply_result,Result} -> Result
- end,
+ {apply_result,Result} -> Result
+ end,
ok = io:format("erlang:trace_info(~p, ~p) -> ~p",
- [What,Key,Res]),
+ [What,Key,Res]),
Res.
-
+
trace_func(MFA, MatchProg) ->
get(tracer) ! {apply,self(),{erlang,trace_pattern,[MFA,MatchProg]}},
Res = receive
- {apply_result,Result} -> Result
- end,
+ {apply_result,Result} -> Result
+ end,
ok = io:format("erlang:trace_pattern(~p, ~p) -> ~p", [MFA,MatchProg,Res]),
Res.
trace_func(MFA, MatchProg, Flags) ->
get(tracer) ! {apply,self(),{erlang,trace_pattern,[MFA,MatchProg,Flags]}},
Res = receive
- {apply_result,Result} -> Result
- end,
+ {apply_result,Result} -> Result
+ end,
ok = io:format("erlang:trace_pattern(~p, ~p) -> ~p", [MFA,MatchProg,Res]),
Res.
@@ -630,29 +458,29 @@ trace_pid(Pid, On, Flags0) ->
Flags = [{tracer,get(tracer_port)}|Flags0],
get(tracer) ! {apply,self(),{erlang,trace,[Pid,On,Flags]}},
Res = receive
- {apply_result,Result} -> Result
- end,
+ {apply_result,Result} -> Result
+ end,
ok = io:format("erlang:trace(~p, ~p, ~p) -> ~p",
- [Pid,On,Flags,Res]),
+ [Pid,On,Flags,Res]),
Res.
start_tracer(Config) ->
- Path = ?config(data_dir, Config),
+ Path = proplists:get_value(data_dir, Config),
ok = load_driver(Path, echo_drv),
Self = self(),
put(tracer, fun_spawn(fun() -> tracer(Self) end)),
receive
- {started,Port} ->
- put(tracer_port, Port)
+ {started,Port} ->
+ put(tracer_port, Port)
end,
get(tracer).
load_driver(Dir, Driver) ->
case erl_ddll:load_driver(Dir, Driver) of
- ok -> ok;
- {error, Error} = Res ->
- io:format("~s\n", [erl_ddll:format_error(Error)]),
- Res
+ ok -> ok;
+ {error, Error} = Res ->
+ io:format("~s\n", [erl_ddll:format_error(Error)]),
+ Res
end.
tracer(RelayTo) ->
@@ -662,18 +490,18 @@ tracer(RelayTo) ->
tracer_loop(RelayTo, Port) ->
receive
- {apply,From,{M,F,A}} ->
- From ! {apply_result,apply(M, F, A)},
- tracer_loop(RelayTo, Port);
- {Port,{data,Msg}} ->
- RelayTo ! binary_to_term(Msg),
- tracer_loop(RelayTo, Port);
- {unlink_tracer_port, From} ->
- unlink(Port),
- From ! {unlinked_tracer_port, self()},
- tracer_loop(RelayTo, Port);
- Other ->
- exit({bad_message,Other})
+ {apply,From,{M,F,A}} ->
+ From ! {apply_result,apply(M, F, A)},
+ tracer_loop(RelayTo, Port);
+ {Port,{data,Msg}} ->
+ RelayTo ! binary_to_term(Msg),
+ tracer_loop(RelayTo, Port);
+ {unlink_tracer_port, From} ->
+ unlink(Port),
+ From ! {unlinked_tracer_port, self()},
+ tracer_loop(RelayTo, Port);
+ Other ->
+ exit({bad_message,Other})
end.
fun_spawn(Fun) ->
@@ -697,43 +525,43 @@ fun_spawn(Fun, Opts) ->
sender() ->
receive
- {send_please, To, What} ->
- To ! What,
- sender()
+ {send_please, To, What} ->
+ To ! What,
+ sender()
end.
%% Just consumes messages from its message queue.
receiver() ->
receive
- _Any -> receiver()
+ _Any -> receiver()
end.
%% Does a garbage collection when it receives a message.
garber() ->
receive
- _Any ->
- lists:seq(1, 100),
- erlang:garbage_collect(),
- garber()
+ _Any ->
+ lists:seq(1, 100),
+ erlang:garbage_collect(),
+ garber()
end.
%% All-purpose process
general() ->
receive
- {apply, {M, F, Args}} ->
- erlang:apply(M, F, Args),
- general();
- {send, Dest, Msg} ->
- Dest ! Msg,
- general();
- {call_f_1, Arg} ->
- f(Arg),
- general();
- nop ->
- general()
+ {apply, {M, F, Args}} ->
+ erlang:apply(M, F, Args),
+ general();
+ {send, Dest, Msg} ->
+ Dest ! Msg,
+ general();
+ {call_f_1, Arg} ->
+ f(Arg),
+ general();
+ nop ->
+ general()
end.
f(Arg) ->
diff --git a/erts/emulator/test/tracer_SUITE.erl b/erts/emulator/test/tracer_SUITE.erl
new file mode 100644
index 0000000000..730c43d8c2
--- /dev/null
+++ b/erts/emulator/test/tracer_SUITE.erl
@@ -0,0 +1,660 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(tracer_SUITE).
+
+%%%
+%%% Tests the tracer module interface
+%%%
+
+-export([all/0, suite/0,groups/0, init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2, init_per_testcase/2,
+ end_per_testcase/2]).
+-export([load/1, unload/1, reload/1, invalid_tracers/1]).
+-export([send/1, recv/1, call/1, call_return/1, spawn/1, exit/1,
+ link/1, unlink/1, getting_linked/1, getting_unlinked/1,
+ register/1, unregister/1, in/1, out/1, gc_start/1, gc_end/1]).
+
+suite() -> [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 1}}].
+
+all() ->
+ [load, unload, reload, invalid_tracers, {group, basic}].
+
+groups() ->
+ [{ basic, [], [send, recv, call, call_return, spawn, exit,
+ link, unlink, getting_linked, getting_unlinked,
+ register, unregister, in, out, gc_start, gc_end]}].
+
+init_per_suite(Config) ->
+ erlang:trace_pattern({'_','_','_'}, false, [local]),
+ erlang:trace_pattern({'_','_','_'}, false, []),
+ purge(),
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+init_per_testcase(TC, Config) when TC =:= load; TC =:= reload ->
+
+ DataDir = proplists:get_value(data_dir, Config),
+
+ Pid = erlang:spawn(fun F() ->
+ receive
+ {get, Pid} ->
+ Pid ! DataDir,
+ F()
+ end
+ end),
+ register(tracer_test_config, Pid),
+ Config;
+init_per_testcase(_, Config) ->
+ DataDir = proplists:get_value(data_dir, Config),
+ case catch tracer_test:enabled(trace_status, self(), self()) of
+ discard ->
+ ok;
+ _ ->
+ tracer_test:load(DataDir)
+ end,
+ Config.
+
+end_per_testcase(TC, _Config) when TC =:= load; TC =:= reload ->
+ purge(),
+ exit(whereis(tracer_test_config), kill),
+ ok;
+end_per_testcase(_, _Config) ->
+ purge(),
+ ok.
+
+load(_Config) ->
+ purge(),
+ 1 = erlang:trace(self(), true, [{tracer, tracer_test, []}, call]),
+ purge(),
+ 1 = erlang:trace_pattern({?MODULE, all, 0}, [],
+ [{meta, tracer_test, []}]),
+ ok.
+
+unload(_Config) ->
+
+ ServerFun = fun F(0, undefined) ->
+ receive
+ {N, Pid} -> F(N, Pid)
+ end;
+ F(0, Pid) ->
+ Pid ! done,
+ F(0, undefined);
+ F(N, Pid) ->
+ ?MODULE:all(),
+ F(N-1, Pid)
+ end,
+
+ Pid = erlang:spawn_link(fun() -> ServerFun(0, undefined) end),
+
+
+ Tc = fun(N) ->
+ Pid ! {N, self()},
+ receive done -> ok after 1000 -> ct:fail(timeout) end,
+ trace_delivered(Pid)
+ end,
+
+ 1 = erlang:trace(Pid, true, [{tracer, tracer_test,
+ {#{ call => trace }, self(), []}},
+ call]),
+ 1 = erlang:trace_pattern({?MODULE, all, 0}, [], []),
+
+ Tc(1),
+ receive _M -> ok after 0 -> ct:fail(timeout) end,
+ receive M0 -> ct:fail({unexpected_message0, M0}) after 0 -> ok end,
+
+ code:purge(tracer_test),
+ code:delete(tracer_test),
+
+ Tc(1),
+ receive M1 -> ct:fail({unexpected_message1, M1}) after 0 -> ok end,
+
+ code:purge(tracer_test),
+
+ Tc(1),
+ receive M2 -> ct:fail({unexpected_message2, M2}) after 0 -> ok end,
+
+ ok.
+
+%% This testcase is here to make sure there are not
+%% segfaults when reloading the current nifs.
+reload(_Config) ->
+
+ Tracer = spawn_opt(fun F() -> receive _M -> F() end end,
+ [{message_queue_data, off_heap}]),
+ erlang:link(Tracer),
+ Tracee = spawn_link(fun reload_loop/0),
+
+ [begin
+ Ref = make_ref(),
+ State = {#{ call => trace }, Tracer, [Ref]},
+ erlang:trace(Tracee, true, [{tracer, tracer_test,State}, call]),
+ erlang:trace_pattern({?MODULE, all, 0}, []),
+
+ false = code:purge(tracer_test),
+ {module, _} = code:load_file(tracer_test),
+
+ %% There is a race involved in between when the internal nif cache
+ %% is purged and when the reload_loop needs the tracer module
+ %% so the tracer may be removed or still there.
+ case erlang:trace_info(Tracee, tracer) of
+ {tracer, []} -> ok;
+ {tracer, {tracer_test, State}} -> ok
+ end,
+
+ false = code:purge(tracer_test),
+ true = code:delete(tracer_test),
+ false = code:purge(tracer_test),
+ timer:sleep(10)
+ end || _ <- lists:seq(1,15)],
+
+ ok.
+
+reload_loop() ->
+ ?MODULE:all(),
+ ?MODULE:all(),
+ ?MODULE:all(),
+ ?MODULE:all(),
+ ?MODULE:all(),
+ timer:sleep(1),
+ reload_loop().
+
+invalid_tracers(_Config) ->
+ FailTrace = fun(A) ->
+ try erlang:trace(self(), true, A) of
+ _ -> ct:fail(A)
+ catch _:_ -> ok end
+ end,
+
+ FailTrace([{tracer, foobar}, call]),
+ FailTrace([{tracer, foobar, []}, call]),
+ FailTrace([{tracer, make_ref(), []}, call]),
+ FailTrace([{tracer, lists, []}, call]),
+
+ FailTP = fun(MS,FL) ->
+ try erlang:trace_pattern({?MODULE,all,0}, MS, FL) of
+ _ -> ct:fail({MS, FL})
+ catch _:_ -> ok end
+ end,
+
+ FailTP([],[{meta, foobar}]),
+ FailTP([],[{meta, foobar, []}]),
+ FailTP([],[{meta, make_ref(), []}]),
+ FailTP([],[{meta, lists, []}]),
+
+ ok.
+
+
+
+send(_Config) ->
+
+ Self = self(),
+ Tc = fun(Pid) ->
+ Pid ! fun() -> Self ! ok end,
+ receive ok -> ok after 100 -> ct:fail(timeout) end
+ end,
+
+ Expect = fun(Pid, State, EOpts) ->
+ receive
+ Msg ->
+ {send, State, Pid, ok, Opts} = Msg,
+ check_opts(EOpts, Opts, Self)
+ end
+ end,
+ test(send, Tc, Expect).
+
+
+recv(_Config) ->
+
+ Tc = fun(Pid) ->
+ Pid ! ok
+ end,
+
+ Expect = fun(Pid, State, EOpts) ->
+ receive
+ Msg ->
+ {'receive', State, Pid, ok, Opts} = Msg,
+ check_opts(EOpts, Opts)
+ end
+ end,
+
+ test('receive', Tc, Expect, false).
+
+call(_Config) ->
+
+ Self = self(),
+ Tc = fun(Pid) ->
+ Pid ! fun() -> call_test(Self), Self ! ok end,
+ receive ok -> ok after 100 -> ct:fail(timeout) end
+ end,
+
+ erlang:trace_pattern({?MODULE, call_test, 1}, [], [local]),
+
+ Expect = fun(Pid, State, EOpts) ->
+ receive
+ Msg ->
+ {call, State, Pid, {?MODULE, call_test, [Self]}, Opts} = Msg,
+ check_opts(EOpts, Opts)
+ end
+ end,
+ test(call, Tc, Expect).
+
+call_return(_Config) ->
+
+ Self = self(),
+ Tc = fun(Pid) ->
+ Pid ! fun() -> call_test(undefined), Self ! ok end,
+ receive ok -> ok after 100 -> ct:fail(timeout) end
+ end,
+
+ 1 = erlang:trace_pattern({?MODULE, call_test, 1}, [{'_',[],[{return_trace}]}], [local]),
+
+ Expect = fun(Pid, State, EOpts) ->
+ receive
+ CallMsg ->
+ {call, State, Pid, {?MODULE, call_test, [undefined]}, COpts} = CallMsg,
+ check_opts(EOpts, COpts)
+ end,
+ receive
+ RetMsg ->
+ {return_from, State, Pid, {?MODULE, call_test, 1}, ROpts} = RetMsg,
+ check_opts(EOpts, ROpts, undefined)
+ end
+ end,
+ test(call, Tc, Expect).
+
+call_test(Arg) ->
+ Arg.
+
+spawn(_Config) ->
+
+ Tc = fun(Pid) ->
+ Pid ! fun() -> erlang:spawn(lists,seq,[1,10]), ok end
+ end,
+
+ Expect =
+ fun(Pid, State, EOpts) ->
+ receive
+ Msg ->
+ {spawn, State, Pid, NewPid, Opts} = Msg,
+ check_opts(EOpts, Opts, {lists,seq,[1,10]}),
+ true = is_pid(NewPid) andalso NewPid /= Pid
+ end
+ end,
+
+ test(spawn, procs, Tc, Expect, false).
+
+exit(_Config) ->
+ Tc = fun(Pid) ->
+ Pid ! fun() -> exit end
+ end,
+
+ Expect =
+ fun(Pid, State, EOpts) ->
+ receive
+ Msg ->
+ {exit, State, Pid, normal, Opts} = Msg,
+ check_opts(EOpts, Opts)
+ end
+ end,
+
+ test(exit, procs, Tc, Expect, true, true).
+
+link(_Config) ->
+
+ Tc = fun(Pid) ->
+ Pid ! fun() ->
+ SPid = erlang:spawn(fun() -> receive _ -> ok end end),
+ erlang:link(SPid),
+ ok
+ end
+ end,
+
+ Expect =
+ fun(Pid, State, EOpts) ->
+ receive
+ Msg ->
+ {link, State, Pid, NewPid, Opts} = Msg,
+ check_opts(EOpts, Opts),
+ true = is_pid(NewPid) andalso NewPid /= Pid
+ end
+ end,
+
+ test(link, procs, Tc, Expect, false).
+
+unlink(_Config) ->
+
+ Tc = fun(Pid) ->
+ Pid ! fun() ->
+ SPid = erlang:spawn(fun() -> receive _ -> ok end end),
+ erlang:link(SPid),
+ erlang:unlink(SPid),
+ ok
+ end
+ end,
+
+ Expect =
+ fun(Pid, State, EOpts) ->
+ receive
+ Msg ->
+ {unlink, State, Pid, NewPid, Opts} = Msg,
+ check_opts(EOpts, Opts),
+ true = is_pid(NewPid) andalso NewPid /= Pid
+ end
+ end,
+
+ test(unlink, procs, Tc, Expect, false).
+
+getting_linked(_Config) ->
+
+ Tc = fun(Pid) ->
+ Pid ! fun() ->
+ Self = self(),
+ erlang:spawn(fun() -> erlang:link(Self) end),
+ ok
+ end
+ end,
+
+ Expect =
+ fun(Pid, State, EOpts) ->
+ receive
+ Msg ->
+ {getting_linked, State, Pid, NewPid, Opts} = Msg,
+ check_opts(EOpts, Opts),
+ true = is_pid(NewPid) andalso NewPid /= Pid
+ end
+ end,
+
+ test(getting_linked, procs, Tc, Expect, false).
+
+getting_unlinked(_Config) ->
+ Tc = fun(Pid) ->
+ Pid ! fun() ->
+ Self = self(),
+ erlang:spawn(fun() ->
+ erlang:link(Self),
+ erlang:unlink(Self)
+ end),
+ ok
+ end
+ end,
+
+ Expect =
+ fun(Pid, State, EOpts) ->
+ receive
+ Msg ->
+ {getting_unlinked, State, Pid, NewPid, Opts} = Msg,
+ check_opts(EOpts, Opts),
+ true = is_pid(NewPid) andalso NewPid /= Pid
+ end
+ end,
+
+ test(getting_unlinked, procs, Tc, Expect, false).
+
+register(_Config) ->
+
+ Tc = fun(Pid) ->
+ Pid ! fun() ->
+ erlang:register(?MODULE, self()),
+ erlang:unregister(?MODULE),
+ ok
+ end
+ end,
+
+ Expect =
+ fun(Pid, State, EOpts) ->
+ receive
+ Msg ->
+ {register, State, Pid, ?MODULE, Opts} = Msg,
+ check_opts(EOpts, Opts)
+ end
+ end,
+
+ test(register, procs, Tc, Expect, false).
+
+unregister(_Config) ->
+
+ Tc = fun(Pid) ->
+ Pid ! fun() ->
+ erlang:register(?MODULE, self()),
+ erlang:unregister(?MODULE),
+ ok
+ end
+ end,
+
+ Expect =
+ fun(Pid, State, EOpts) ->
+ receive
+ Msg ->
+ {unregister, State, Pid, ?MODULE, Opts} = Msg,
+ check_opts(EOpts, Opts)
+ end
+ end,
+
+ test(unregister, procs, Tc, Expect, false).
+
+in(_Config) ->
+
+ Tc = fun(Pid) ->
+ Self = self(),
+ Pid ! fun() -> receive after 10 -> Self ! ok end end,
+ receive ok -> ok end
+ end,
+
+ Expect =
+ fun(Pid, State, EOpts) ->
+ N = (fun F(N) ->
+ receive
+ Msg ->
+ {in, State, Pid, _, Opts} = Msg,
+ check_opts(EOpts, Opts),
+ F(N+1)
+ after 0 -> N
+ end
+ end)(0),
+ true = N > 0
+ end,
+
+ test(in, running, Tc, Expect, false).
+
+out(_Config) ->
+ Tc = fun(Pid) ->
+ Pid ! fun() -> receive after 10 -> exit end end,
+ Ref = erlang:monitor(process, Pid),
+ receive {'DOWN', Ref, _, _, _} -> ok end
+ end,
+
+ Expect =
+ fun(Pid, State, EOpts) ->
+ %% We cannot predict how many out schedules there will be
+ N = (fun F(N) ->
+ receive
+ Msg ->
+ {out, State, Pid, _, Opts} = Msg,
+ check_opts(EOpts, Opts),
+ F(N+1)
+ after 0 -> N
+ end
+ end)(0),
+ true = N > 0
+ end,
+
+ test(out, running, Tc, Expect, false, true).
+
+gc_start(_Config) ->
+
+ Tc = fun(Pid) ->
+ Pid ! fun() ->
+ erlang:garbage_collect(),
+ ok
+ end
+ end,
+
+ Expect =
+ fun(Pid, State, EOpts) ->
+ receive
+ Msg ->
+ {gc_major_start, State, Pid, _, Opts} = Msg,
+ check_opts(EOpts, Opts)
+ end
+ end,
+
+ test(gc_major_start, garbage_collection, Tc, Expect, false).
+
+gc_end(_Config) ->
+
+ Tc = fun(Pid) ->
+ Pid ! fun() ->
+ erlang:garbage_collect(),
+ ok
+ end
+ end,
+
+ Expect =
+ fun(Pid, State, EOpts) ->
+ receive
+ Msg ->
+ {gc_major_end, State, Pid, _, Opts} = Msg,
+ check_opts(EOpts, Opts)
+ end
+ end,
+
+ test(gc_major_end, garbage_collection, Tc, Expect, false).
+
+test(Event, Tc, Expect) ->
+ test(Event, Tc, Expect, false).
+test(Event, Tc, Expect, Removes) ->
+ test(Event, Event, Tc, Expect, Removes).
+test(Event, TraceFlag, Tc, Expect, Removes) ->
+ test(Event, TraceFlag, Tc, Expect, Removes, false).
+test(Event, TraceFlag, Tc, Expect, _Removes, Dies) ->
+
+ ComplexState = {fun() -> ok end, <<0:(128*8)>>},
+ Opts = #{ },
+
+ %% Test that trace works
+ State1 = {#{ Event => trace }, self(), ComplexState},
+ Pid1 = start_tracee(),
+ 1 = erlang:trace(Pid1, true, [TraceFlag, {tracer, tracer_test, State1}]),
+ Tc(Pid1),
+ ok = trace_delivered(Pid1),
+
+ Expect(Pid1, State1, Opts),
+ receive M11 -> ct:fail({unexpected, M11}) after 0 -> ok end,
+ if not Dies ->
+ {flags, [TraceFlag]} = erlang:trace_info(Pid1, flags),
+ {tracer, {tracer_test, State1}} = erlang:trace_info(Pid1, tracer),
+ erlang:trace(Pid1, false, [TraceFlag]);
+ true -> ok
+ end,
+
+ %% Test that trace works with scheduler id and timestamp
+ Pid1T = start_tracee(),
+ 1 = erlang:trace(Pid1T, true, [TraceFlag, {tracer, tracer_test, State1},
+ timestamp, scheduler_id]),
+ Tc(Pid1T),
+ ok = trace_delivered(Pid1T),
+
+ Expect(Pid1T, State1, Opts#{ scheduler_id => number,
+ timestamp => timestamp}),
+ receive M11T -> ct:fail({unexpected, M11T}) after 0 -> ok end,
+ if not Dies ->
+ {flags, [scheduler_id, TraceFlag, timestamp]}
+ = erlang:trace_info(Pid1T, flags),
+ {tracer, {tracer_test, State1}} = erlang:trace_info(Pid1T, tracer),
+ erlang:trace(Pid1T, false, [TraceFlag]);
+ true -> ok
+ end,
+
+ %% Test that discard works
+ Pid2 = start_tracee(),
+ State2 = {#{ Event => discard }, self(), ComplexState},
+ 1 = erlang:trace(Pid2, true, [TraceFlag, {tracer, tracer_test, State2}]),
+ Tc(Pid2),
+ ok = trace_delivered(Pid2),
+ receive M2 -> ct:fail({unexpected, M2}) after 0 -> ok end,
+ if not Dies ->
+ {flags, [TraceFlag]} = erlang:trace_info(Pid2, flags),
+ {tracer, {tracer_test, State2}} = erlang:trace_info(Pid2, tracer),
+ erlang:trace(Pid2, false, [TraceFlag]);
+ true ->
+ ok
+ end,
+
+ ok.
+
+check_opts(E, O, Extra) ->
+ check_opts(E#{ extra => Extra }, O).
+check_opts(#{ scheduler_id := number } = E, #{ scheduler_id := N } = O)
+ when is_integer(N) ->
+ E1 = maps:remove(scheduler_id, E),
+ O1 = maps:remove(scheduler_id, O),
+ if E1 == O1 -> ok;
+ true -> ct:fail({invalid_opts, E, O})
+ end;
+check_opts(Opts, Opts) ->
+ ok;
+check_opts(E,O) ->
+ ct:fail({invalid_opts, E, O}).
+
+start_tracee() ->
+ spawn_link(
+ fun F() ->
+ receive
+ Action when is_function(Action) ->
+ case Action() of
+ ok ->
+ F();
+ Err ->
+ Err
+ end;
+ _ ->
+ F()
+ end
+ end).
+
+trace_delivered(Pid) ->
+ Ref = erlang:trace_delivered(Pid),
+ receive
+ {trace_delivered, Pid, Ref} ->
+ ok
+ after 1000 ->
+ timeout
+ end.
+
+purge() ->
+ %% Make sure module is not loaded
+ case erlang:module_loaded(tracer_test) of
+ true ->
+ code:purge(tracer_test),
+ true = code:delete(tracer_test),
+ code:purge(tracer_test);
+ _ ->
+ ok
+ end.
diff --git a/erts/emulator/test/tracer_SUITE_data/Makefile.src b/erts/emulator/test/tracer_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..154bd70ccc
--- /dev/null
+++ b/erts/emulator/test/tracer_SUITE_data/Makefile.src
@@ -0,0 +1,8 @@
+
+NIF_LIBS = tracer_test@dll@
+
+all: $(NIF_LIBS)
+
+@SHLIB_RULES@
+
+$(NIF_LIBS): tracer_test.c
diff --git a/erts/emulator/test/tracer_SUITE_data/tracer_test.c b/erts/emulator/test/tracer_SUITE_data/tracer_test.c
new file mode 100644
index 0000000000..d9543b7ab9
--- /dev/null
+++ b/erts/emulator/test/tracer_SUITE_data/tracer_test.c
@@ -0,0 +1,116 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2009-2016. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#include "erl_nif.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <limits.h>
+
+/* NIF interface declarations */
+static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info);
+static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info);
+static void unload(ErlNifEnv* env, void* priv_data);
+
+/* The NIFs: */
+static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+
+static ErlNifFunc nif_funcs[] = {
+ {"enabled", 3, enabled},
+ {"trace", 5, trace}
+};
+
+ERL_NIF_INIT(tracer_test, nif_funcs, load, NULL, upgrade, unload)
+
+static ERL_NIF_TERM atom_discard;
+static ERL_NIF_TERM atom_ok;
+
+#define ASSERT(expr) assert(expr)
+
+static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
+{
+
+ atom_discard = enif_make_atom(env, "discard");
+ atom_ok = enif_make_atom(env, "ok");
+
+ *priv_data = NULL;
+
+ return 0;
+}
+
+static void unload(ErlNifEnv* env, void* priv_data)
+{
+
+}
+
+static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data,
+ ERL_NIF_TERM load_info)
+{
+ if (*old_priv_data != NULL) {
+ return -1; /* Don't know how to do that */
+ }
+ if (*priv_data != NULL) {
+ return -1; /* Don't know how to do that */
+ }
+ if (load(env, priv_data, load_info)) {
+ return -1;
+ }
+ return 0;
+}
+
+static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ int state_arity;
+ const ERL_NIF_TERM *state_tuple;
+ ERL_NIF_TERM value;
+ ASSERT(argc == 3);
+
+ if (!enif_get_tuple(env, argv[1], &state_arity, &state_tuple))
+ return atom_discard;
+
+ if (enif_get_map_value(env, state_tuple[0], argv[0], &value)) {
+ return value;
+ } else {
+ return atom_discard;
+ }
+}
+
+static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ int state_arity;
+ ErlNifPid self, to;
+ ERL_NIF_TERM *tuple, msg;
+ const ERL_NIF_TERM *state_tuple;
+ ASSERT(argc == 5);
+
+ enif_get_tuple(env, argv[1], &state_arity, &state_tuple);
+
+ tuple = enif_alloc(sizeof(ERL_NIF_TERM)*(argc));
+ memcpy(tuple,argv,sizeof(ERL_NIF_TERM)*argc);
+
+ msg = enif_make_tuple_from_array(env, tuple, argc);
+ enif_get_local_pid(env, state_tuple[1], &to);
+ enif_send(env, &to, NULL, msg);
+ enif_free(tuple);
+
+ return atom_ok;
+}
diff --git a/erts/emulator/test/tracer_test.erl b/erts/emulator/test/tracer_test.erl
new file mode 100644
index 0000000000..a82fd04d2e
--- /dev/null
+++ b/erts/emulator/test/tracer_test.erl
@@ -0,0 +1,55 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(tracer_test).
+
+%%%
+%%% Test tracer
+%%%
+
+-export([enabled/3, trace/5]).
+-export([load/1, load/2]).
+-on_load(load/0).
+
+enabled(_, _, _) ->
+ erlang:nif_error(nif_not_loaded).
+
+trace(_, _, _, _, _) ->
+ erlang:nif_error(nif_not_loaded).
+
+load() ->
+ case whereis(tracer_test_config) of
+ undefined ->
+ ok;
+ Pid ->
+ Pid ! {get, self()},
+ receive
+ {Conf, Postfix} ->
+ load(Conf, Postfix);
+ Conf ->
+ load(Conf)
+ end
+ end.
+
+load(DataDir) ->
+ load(DataDir, "").
+load(DataDir, Postfix) ->
+ SoFile = atom_to_list(?MODULE) ++ Postfix,
+ erlang:load_nif(filename:join(DataDir, SoFile) , 0).
diff --git a/erts/emulator/test/tuple_SUITE.erl b/erts/emulator/test/tuple_SUITE.erl
index f1f077be6b..79b681b4d1 100644
--- a/erts/emulator/test/tuple_SUITE.erl
+++ b/erts/emulator/test/tuple_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -26,7 +26,7 @@
t_make_tuple_2/1, t_make_upper_boundry_tuple_2/1, t_make_tuple_3/1,
t_append_element/1, t_append_element_upper_boundry/1,
build_and_match/1, tuple_with_case/1, tuple_in_guard/1]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
%% Tests tuples and the BIFs:
%%
@@ -64,7 +64,7 @@ init_per_suite(Config) ->
[{started_apps, A}|Config].
end_per_suite(Config) ->
- As = ?config(started_apps, Config),
+ As = proplists:get_value(started_apps, Config),
lists:foreach(fun (A) -> application:stop(A) end, As),
Config.
@@ -259,7 +259,7 @@ t_make_tuple(Size, Element) ->
lists:foreach(fun(El) when El =:= Element ->
ok;
(Other) ->
- test_server:fail({got, Other, expected, Element})
+ ct:fail({got, Other, expected, Element})
end, tuple_to_list(Tuple)).
%% Tests the erlang:make_tuple/3 BIF.
@@ -385,14 +385,14 @@ tuple_in_guard(Config) when is_list(Config) ->
Tuple1 == {element(1, Tuple2),element(2, Tuple2)} ->
ok;
true ->
- test_server:fail()
+ ct:fail("failed")
end,
if
Tuple2 == {element(1, Tuple2),element(2, Tuple2),
element(3, Tuple2)} ->
ok;
true ->
- test_server:fail()
+ ct:fail("failed")
end,
ok.
diff --git a/erts/emulator/test/unique_SUITE.erl b/erts/emulator/test/unique_SUITE.erl
index 6fa634b886..c5aa80c7b4 100644
--- a/erts/emulator/test/unique_SUITE.erl
+++ b/erts/emulator/test/unique_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2014. All Rights Reserved.
+%% Copyright Ericsson AB 2014-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,38 +20,25 @@
-module(unique_SUITE).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2,end_per_testcase/2]).
+-export([all/0, suite/0, init_per_suite/1, end_per_suite/1]).
-export([unique_monotonic_integer_white_box/1,
unique_integer_white_box/1]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
%-define(P(V), V).
-define(P(V), print_ret_val(?FILE, ?LINE, V)).
-define(PRINT(V), print_ret_val(?FILE, ?LINE, V)).
-
-init_per_testcase(Case, Config) ->
- ?line Dog=test_server:timetrap(test_server:minutes(2)),
- [{watchdog, Dog}, {testcase, Case}|Config].
-
-end_per_testcase(_, Config) ->
- Dog=?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
- ok.
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 4}}].
all() ->
[unique_monotonic_integer_white_box,
unique_integer_white_box].
-groups() ->
- [].
-
init_per_suite(Config) ->
erts_debug:set_internal_state(available_internal_state, true),
Config.
@@ -60,12 +47,6 @@ end_per_suite(_Config) ->
erts_debug:set_internal_state(available_internal_state, false),
ok.
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
%%
%%
%% Unique counter white box test case
@@ -80,15 +61,15 @@ unique_monotonic_integer_white_box(Config) when is_list(Config) ->
%% the system when moving the strict monotonic counter
%% around in a non-strict monotonic way...
Test = spawn(Node,
- fun () ->
- unique_monotonic_integer_white_box_test(TestServer, Success)
- end),
+ fun () ->
+ unique_monotonic_integer_white_box_test(TestServer, Success)
+ end),
Mon = erlang:monitor(process, Test),
receive
- {'DOWN', Mon, process, Test, Error} ->
- ?t:fail(Error);
- Success ->
- ok
+ {'DOWN', Mon, process, Test, Error} ->
+ ct:fail(Error);
+ Success ->
+ ok
end,
erlang:demonitor(Mon, [flush]),
stop_node(Node),
@@ -96,9 +77,9 @@ unique_monotonic_integer_white_box(Config) when is_list(Config) ->
set_unique_monotonic_integer_state(MinCounter, NextValue) ->
true = erts_debug:set_internal_state(unique_monotonic_integer_state,
- NextValue-MinCounter-1).
-
-
+ NextValue-MinCounter-1).
+
+
unique_monotonic_integer_white_box_test(TestServer, Success) ->
erts_debug:set_internal_state(available_internal_state, true),
@@ -130,10 +111,10 @@ unique_monotonic_integer_white_box_test(TestServer, Success) ->
?PRINT({max_counter, MaxCounter}),
case WordSize of
- 4 ->
- MinCounter = MinSint64;
- 8 ->
- MinCounter = MinSmall
+ 4 ->
+ MinCounter = MinSint64;
+ 8 ->
+ MinCounter = MinSmall
end,
StartState = erts_debug:get_internal_state(unique_monotonic_integer_state),
@@ -141,20 +122,20 @@ unique_monotonic_integer_white_box_test(TestServer, Success) ->
%% Verify that we get expected results over all internal limits...
case MinCounter < MinSmall of
- false ->
- 8 = WordSize,
- ok;
- true ->
- 4 = WordSize,
- ?PRINT(over_min_small),
- set_unique_monotonic_integer_state(MinCounter, MinSmall-2),
- true = (?P(erlang:unique_integer([monotonic])) == MinSmall - 2),
- true = (?P(erlang:unique_integer([monotonic])) == MinSmall - 1),
- true = (?P(erlang:unique_integer([monotonic])) == MinSmall),
- true = (?P(erlang:unique_integer([monotonic])) == MinSmall + 1),
- true = (?P(erlang:unique_integer([monotonic])) == MinSmall + 2),
- garbage_collect(),
- ok
+ false ->
+ 8 = WordSize,
+ ok;
+ true ->
+ 4 = WordSize,
+ ?PRINT(over_min_small),
+ set_unique_monotonic_integer_state(MinCounter, MinSmall-2),
+ true = (?P(erlang:unique_integer([monotonic])) == MinSmall - 2),
+ true = (?P(erlang:unique_integer([monotonic])) == MinSmall - 1),
+ true = (?P(erlang:unique_integer([monotonic])) == MinSmall),
+ true = (?P(erlang:unique_integer([monotonic])) == MinSmall + 1),
+ true = (?P(erlang:unique_integer([monotonic])) == MinSmall + 2),
+ garbage_collect(),
+ ok
end,
?PRINT(over_zero), %% Not really an interesting limit, but...
@@ -176,27 +157,27 @@ unique_monotonic_integer_white_box_test(TestServer, Success) ->
garbage_collect(),
case MaxCounter > MaxSint64 of
- false ->
- 4 = WordSize,
- ok;
- true ->
- 8 = WordSize,
- ?PRINT(over_max_sint64),
- set_unique_monotonic_integer_state(MinCounter, MaxSint64-2),
- true = (?P(erlang:unique_integer([monotonic])) == MaxSint64 - 2),
- true = (?P(erlang:unique_integer([monotonic])) == MaxSint64 - 1),
- true = (?P(erlang:unique_integer([monotonic])) == MaxSint64),
- true = (?P(erlang:unique_integer([monotonic])) == MaxSint64 + 1),
- true = (?P(erlang:unique_integer([monotonic])) == MaxSint64 + 2),
- garbage_collect()
+ false ->
+ 4 = WordSize,
+ ok;
+ true ->
+ 8 = WordSize,
+ ?PRINT(over_max_sint64),
+ set_unique_monotonic_integer_state(MinCounter, MaxSint64-2),
+ true = (?P(erlang:unique_integer([monotonic])) == MaxSint64 - 2),
+ true = (?P(erlang:unique_integer([monotonic])) == MaxSint64 - 1),
+ true = (?P(erlang:unique_integer([monotonic])) == MaxSint64),
+ true = (?P(erlang:unique_integer([monotonic])) == MaxSint64 + 1),
+ true = (?P(erlang:unique_integer([monotonic])) == MaxSint64 + 2),
+ garbage_collect()
end,
?PRINT(over_max_min_counter),
set_unique_monotonic_integer_state(MinCounter, if MaxCounter == MaxSint64 ->
- MaxCounter-2;
- true ->
- MinCounter-3
- end),
+ MaxCounter-2;
+ true ->
+ MinCounter-3
+ end),
true = (?P(erlang:unique_integer([monotonic])) == MaxCounter - 2),
true = (?P(erlang:unique_integer([monotonic])) == MaxCounter - 1),
true = (?P(erlang:unique_integer([monotonic])) == MaxCounter),
@@ -208,7 +189,7 @@ unique_monotonic_integer_white_box_test(TestServer, Success) ->
%% Restore initial state and hope we didn't mess it up for the
%% system...
true = erts_debug:set_internal_state(unique_monotonic_integer_state,
- StartState),
+ StartState),
TestServer ! Success.
@@ -219,16 +200,16 @@ unique_monotonic_integer_white_box_test(TestServer, Success) ->
%%
-record(uniqint_info, {min_int,
- max_int,
- max_small,
- schedulers,
- sched_bits}).
+ max_int,
+ max_small,
+ schedulers,
+ sched_bits}).
unique_integer_white_box(Config) when is_list(Config) ->
UinqintInfo = init_uniqint_info(),
#uniqint_info{min_int = MinInt,
- max_int = MaxInt,
- max_small = MaxSmall} = UinqintInfo,
+ max_int = MaxInt,
+ max_small = MaxSmall} = UinqintInfo,
io:format("****************************************************~n", []),
io:format("*** Around MIN_UNIQ_INT ~p ***~n", [MinInt]),
io:format("****************************************************~n", []),
@@ -258,7 +239,7 @@ unique_integer_white_box(Config) when is_list(Config) ->
io:format("****************************************************~n", []),
check_unique_integer_around(MaxInt, UinqintInfo),
ok.
-
+
%%% Internal unique_integer_white_box/1 test case
@@ -267,10 +248,21 @@ calc_sched_bits(NoScheds, Shift) when NoScheds < 1 bsl Shift ->
calc_sched_bits(NoScheds, Shift) ->
calc_sched_bits(NoScheds, Shift+1).
+schedulers() ->
+ S = erlang:system_info(schedulers),
+ try
+ DCPUS = erlang:system_info(dirty_cpu_schedulers),
+ DIOS = erlang:system_info(dirty_io_schedulers),
+ S+DCPUS+DIOS
+ catch
+ _ : _ ->
+ S
+ end.
+
init_uniqint_info() ->
SmallBits = erlang:system_info({wordsize, internal})*8-4,
io:format("SmallBits=~p~n", [SmallBits]),
- Schedulers = erlang:system_info(schedulers),
+ Schedulers = schedulers(),
io:format("Schedulers=~p~n", [Schedulers]),
MinSmall = -1*(1 bsl (SmallBits-1)),
io:format("MinSmall=~p~n", [MinSmall]),
@@ -281,33 +273,33 @@ init_uniqint_info() ->
MaxInt = ((((1 bsl 64) - 1) bsl SchedBits) bor Schedulers) + MinSmall,
io:format("MaxInt=~p~n", [MaxInt]),
#uniqint_info{min_int = MinSmall,
- max_int = MaxInt,
- max_small = MaxSmall,
- schedulers = Schedulers,
- sched_bits = SchedBits}.
+ max_int = MaxInt,
+ max_small = MaxSmall,
+ schedulers = Schedulers,
+ sched_bits = SchedBits}.
valid_uniqint(Int, #uniqint_info{min_int = MinInt} = UinqintInfo) when Int < MinInt ->
valid_uniqint(MinInt, UinqintInfo);
valid_uniqint(Int, #uniqint_info{min_int = MinInt,
- sched_bits = SchedBits,
- schedulers = Scheds}) ->
+ sched_bits = SchedBits,
+ schedulers = Scheds}) ->
Int1 = Int - MinInt,
{Inc, ThreadNo} = case Int1 band ((1 bsl SchedBits) - 1) of
- TN when TN > Scheds ->
- {1, Scheds};
- TN ->
- {0, TN}
- end,
+ TN when TN > Scheds ->
+ {1, Scheds};
+ TN ->
+ {0, TN}
+ end,
Counter = ((Int1 bsr SchedBits) + Inc) rem (1 bsl 64),
((Counter bsl SchedBits) bor ThreadNo) + MinInt.
smaller_valid_uniqint(Int, UinqintInfo) ->
Cand = Int-1,
case valid_uniqint(Cand, UinqintInfo) of
- RI when RI < Int ->
- RI;
- _ ->
- smaller_valid_uniqint(Cand, UinqintInfo)
+ RI when RI < Int ->
+ RI;
+ _ ->
+ smaller_valid_uniqint(Cand, UinqintInfo)
end.
int32_to_bigendian_list(Int) ->
@@ -318,7 +310,7 @@ int32_to_bigendian_list(Int) ->
Int band 16#ff].
mk_uniqint(Int, #uniqint_info {min_int = MinInt,
- sched_bits = SchedBits} = _UinqintInfo) ->
+ sched_bits = SchedBits} = _UinqintInfo) ->
Int1 = Int - MinInt,
ThrId = Int1 band ((1 bsl SchedBits) - 1),
Value = (Int1 bsr SchedBits) band ((1 bsl 64) - 1),
@@ -334,36 +326,36 @@ check_uniqint(Int, UinqintInfo) ->
UniqInt = mk_uniqint(Int, UinqintInfo),
io:format("UniqInt=~p ", [UniqInt]),
case UniqInt =:= Int of
- true ->
- io:format("OK~n~n", []);
- false ->
- io:format("result UniqInt=~p FAILED~n", [UniqInt]),
- exit(badres)
+ true ->
+ io:format("OK~n~n", []);
+ false ->
+ io:format("result Int=~p FAILED~n", [Int]),
+ exit(badres)
end.
check_unique_integer_around(Int, #uniqint_info{min_int = MinInt,
- max_int = MaxInt} = UinqintInfo) ->
+ max_int = MaxInt} = UinqintInfo) ->
{Start, End} = case {Int =< MinInt+100, Int >= MaxInt-100} of
- {true, false} ->
- {MinInt, MinInt+100};
- {false, false} ->
- {smaller_valid_uniqint(Int-100, UinqintInfo),
- valid_uniqint(Int+100, UinqintInfo)};
- {false, true} ->
- {MaxInt-100, MaxInt}
- end,
+ {true, false} ->
+ {MinInt, MinInt+100};
+ {false, false} ->
+ {smaller_valid_uniqint(Int-100, UinqintInfo),
+ valid_uniqint(Int+100, UinqintInfo)};
+ {false, true} ->
+ {MaxInt-100, MaxInt}
+ end,
lists:foldl(fun (I, OldRefInt) ->
- RefInt = valid_uniqint(I, UinqintInfo),
- case OldRefInt =:= RefInt of
- true ->
- ok;
- false ->
- check_uniqint(RefInt, UinqintInfo)
- end,
- RefInt
- end,
- none,
- lists:seq(Start, End)).
+ RefInt = valid_uniqint(I, UinqintInfo),
+ case OldRefInt =:= RefInt of
+ true ->
+ ok;
+ false ->
+ check_uniqint(RefInt, UinqintInfo)
+ end,
+ RefInt
+ end,
+ none,
+ lists:seq(Start, End)).
%% helpers
@@ -375,17 +367,17 @@ print_ret_val(File, Line, Value) ->
start_node(Config) ->
start_node(Config, []).
start_node(Config, Opts) when is_list(Config), is_list(Opts) ->
- ?line Pa = filename:dirname(code:which(?MODULE)),
- ?line A = erlang:monotonic_time(1) + erlang:time_offset(1),
- ?line B = erlang:unique_integer([positive]),
- ?line Name = list_to_atom(atom_to_list(?MODULE)
- ++ "-"
- ++ atom_to_list(?config(testcase, Config))
- ++ "-"
- ++ integer_to_list(A)
- ++ "-"
- ++ integer_to_list(B)),
- ?line ?t:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]).
+ Pa = filename:dirname(code:which(?MODULE)),
+ A = erlang:monotonic_time(1) + erlang:time_offset(1),
+ B = erlang:unique_integer([positive]),
+ Name = list_to_atom(atom_to_list(?MODULE)
+ ++ "-"
+ ++ atom_to_list(proplists:get_value(testcase, Config))
+ ++ "-"
+ ++ integer_to_list(A)
+ ++ "-"
+ ++ integer_to_list(B)),
+ test_server:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]).
stop_node(Node) ->
- ?t:stop_node(Node).
+ test_server:stop_node(Node).
diff --git a/erts/emulator/test/z_SUITE.erl b/erts/emulator/test/z_SUITE.erl
index f4d9030255..ab56018373 100644
--- a/erts/emulator/test/z_SUITE.erl
+++ b/erts/emulator/test/z_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -30,148 +30,117 @@
%-define(line_trace, 1).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-%-compile(export_all).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2, init_per_testcase/2,
- end_per_testcase/2]).
+-export([all/0, suite/0]).
-export([schedulers_alive/1, node_container_refc_check/1,
long_timers/1, pollset_size/1,
check_io_debug/1, get_check_io_info/0]).
--define(DEFAULT_TIMEOUT, ?t:minutes(5)).
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 5}}].
all() ->
[schedulers_alive, node_container_refc_check,
long_timers, pollset_size, check_io_debug].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-init_per_testcase(_Case, Config) when is_list(Config) ->
- Dog = ?t:timetrap(?DEFAULT_TIMEOUT),
- [{watchdog, Dog}|Config].
-
-end_per_testcase(_Case, Config) when is_list(Config) ->
- Dog = ?config(watchdog, Config),
- ?t:timetrap_cancel(Dog),
- ok.
-
%%%
%%% The test cases -------------------------------------------------------------
%%%
-schedulers_alive(doc) -> ["Tests that all schedulers are actually used"];
-schedulers_alive(suite) -> [];
+%% Tests that all schedulers are actually used
schedulers_alive(Config) when is_list(Config) ->
- ?line Master = self(),
- ?line NoSchedulersOnline = erlang:system_flag(
- schedulers_online,
- erlang:system_info(schedulers)),
- ?line NoSchedulers = erlang:system_info(schedulers),
+ Master = self(),
+ NoSchedulersOnline = erlang:system_flag(
+ schedulers_online,
+ erlang:system_info(schedulers)),
+ NoSchedulers = erlang:system_info(schedulers),
UsedScheds =
- try
- ?line ?t:format("Number of schedulers configured: ~p~n", [NoSchedulers]),
- ?line case erlang:system_info(multi_scheduling) of
- blocked ->
- ?line ?t:fail(multi_scheduling_blocked);
- disabled ->
- ?line ok;
- enabled ->
- ?t:format("Testing blocking process exit~n"),
- BF = fun () ->
- blocked = erlang:system_flag(multi_scheduling,
- block),
- Master ! {self(), blocking},
- receive after infinity -> ok end
- end,
- ?line Blocker = spawn_link(BF),
- ?line Mon = erlang:monitor(process, Blocker),
- ?line receive {Blocker, blocking} -> ok end,
- ?line [Blocker]
- = erlang:system_info(multi_scheduling_blockers),
- ?line unlink(Blocker),
- ?line exit(Blocker, kill),
- ?line receive {'DOWN', Mon, _, _, _} -> ok end,
- ?line enabled = erlang:system_info(multi_scheduling),
- ?line [] = erlang:system_info(multi_scheduling_blockers),
- ?line ok
- end,
- ?t:format("Testing blocked~n"),
- ?line erlang:system_flag(multi_scheduling, block),
- ?line case erlang:system_info(multi_scheduling) of
- enabled ->
- ?line ?t:fail(multi_scheduling_enabled);
- blocked ->
- ?line [Master] = erlang:system_info(multi_scheduling_blockers);
- disabled -> ?line ok
- end,
- ?line Ps = lists:map(
- fun (_) ->
- spawn_link(fun () ->
- run_on_schedulers(none,
- [],
- Master)
- end)
- end,
- lists:seq(1,NoSchedulers)),
- ?line receive after 1000 -> ok end,
- ?line {_, 1} = verify_all_schedulers_used({[],0}, 1),
- ?line lists:foreach(fun (P) ->
- unlink(P),
- exit(P, bang)
- end,
- Ps),
- ?line case erlang:system_flag(multi_scheduling, unblock) of
- blocked -> ?line ?t:fail(multi_scheduling_blocked);
- disabled -> ?line ok;
- enabled -> ?line ok
- end,
- erts_debug:set_internal_state(available_internal_state, true),
- %% node_and_dist_references will use emulator interal thread blocking...
- erts_debug:get_internal_state(node_and_dist_references),
- erts_debug:set_internal_state(available_internal_state, false),
- ?t:format("Testing not blocked~n"),
- ?line Ps2 = lists:map(
- fun (_) ->
- spawn_link(fun () ->
- run_on_schedulers(none,
- [],
- Master)
- end)
- end,
- lists:seq(1,NoSchedulers)),
- ?line receive after 1000 -> ok end,
- ?line {_, NoSIDs} = verify_all_schedulers_used({[],0},NoSchedulers),
- ?line lists:foreach(fun (P) ->
- unlink(P),
- exit(P, bang)
- end,
- Ps2),
- NoSIDs
- after
- NoSchedulers = erlang:system_flag(schedulers_online,
- NoSchedulersOnline),
- NoSchedulersOnline = erlang:system_info(schedulers_online)
- end,
- ?line {comment, "Number of schedulers " ++ integer_to_list(UsedScheds)}.
+ try
+ io:format("Number of schedulers configured: ~p~n", [NoSchedulers]),
+ case erlang:system_info(multi_scheduling) of
+ blocked ->
+ ct:fail(multi_scheduling_blocked);
+ disabled ->
+ ok;
+ enabled ->
+ io:format("Testing blocking process exit~n"),
+ BF = fun () ->
+ blocked = erlang:system_flag(multi_scheduling,
+ block),
+ Master ! {self(), blocking},
+ receive after infinity -> ok end
+ end,
+ Blocker = spawn_link(BF),
+ Mon = erlang:monitor(process, Blocker),
+ receive {Blocker, blocking} -> ok end,
+ [Blocker]
+ = erlang:system_info(multi_scheduling_blockers),
+ unlink(Blocker),
+ exit(Blocker, kill),
+ receive {'DOWN', Mon, _, _, _} -> ok end,
+ enabled = erlang:system_info(multi_scheduling),
+ [] = erlang:system_info(multi_scheduling_blockers),
+ ok
+ end,
+ io:format("Testing blocked~n"),
+ erlang:system_flag(multi_scheduling, block),
+ case erlang:system_info(multi_scheduling) of
+ enabled ->
+ ct:fail(multi_scheduling_enabled);
+ blocked ->
+ [Master] = erlang:system_info(multi_scheduling_blockers);
+ disabled -> ok
+ end,
+ Ps = lists:map(
+ fun (_) ->
+ spawn_link(fun () ->
+ run_on_schedulers(none,
+ [],
+ Master)
+ end)
+ end,
+ lists:seq(1,NoSchedulers)),
+ receive after 1000 -> ok end,
+ {_, 1} = verify_all_schedulers_used({[],0}, 1),
+ lists:foreach(fun (P) ->
+ unlink(P),
+ exit(P, bang)
+ end, Ps),
+ case erlang:system_flag(multi_scheduling, unblock) of
+ blocked -> ct:fail(multi_scheduling_blocked);
+ disabled -> ok;
+ enabled -> ok
+ end,
+ erts_debug:set_internal_state(available_internal_state, true),
+ %% node_and_dist_references will use emulator interal thread blocking...
+ erts_debug:get_internal_state(node_and_dist_references),
+ erts_debug:set_internal_state(available_internal_state, false),
+ io:format("Testing not blocked~n"),
+ Ps2 = lists:map(
+ fun (_) ->
+ spawn_link(fun () ->
+ run_on_schedulers(none,
+ [],
+ Master)
+ end)
+ end,
+ lists:seq(1,NoSchedulers)),
+ receive after 1000 -> ok end,
+ {_, NoSIDs} = verify_all_schedulers_used({[],0},NoSchedulers),
+ lists:foreach(fun (P) ->
+ unlink(P),
+ exit(P, bang)
+ end, Ps2),
+ NoSIDs
+ after
+ NoSchedulers = erlang:system_flag(schedulers_online,
+ NoSchedulersOnline),
+ NoSchedulersOnline = erlang:system_info(schedulers_online)
+ end,
+ {comment, "Number of schedulers " ++ integer_to_list(UsedScheds)}.
run_on_schedulers(LastSID, SIDs, ReportTo) ->
@@ -198,65 +167,62 @@ wait_on_used_scheduler({SIDs, SIDsLen} = State) ->
true ->
wait_on_used_scheduler(State);
false ->
- ?t:format("Scheduler ~p used~n", [SID]),
+ io:format("Scheduler ~p used~n", [SID]),
{[SID|SIDs], SIDsLen+1}
end
end.
verify_all_schedulers_used({UsedSIDs, UsedSIDsLen} = State, NoSchedulers) ->
- ?line case NoSchedulers of
+ case NoSchedulers of
UsedSIDsLen ->
- ?line State;
+ State;
NoSchdlrs when NoSchdlrs < UsedSIDsLen ->
- ?line ?t:fail({more_schedulers_used_than_exist,
+ ct:fail({more_schedulers_used_than_exist,
{existing_schedulers, NoSchdlrs},
{used_schedulers, UsedSIDsLen},
{used_scheduler_ids, UsedSIDs}});
_ ->
- ?line NewState = wait_on_used_scheduler(State),
- ?line verify_all_schedulers_used(NewState, NoSchedulers)
+ NewState = wait_on_used_scheduler(State),
+ verify_all_schedulers_used(NewState, NoSchedulers)
end.
-node_container_refc_check(doc) -> [];
-node_container_refc_check(suite) -> [];
node_container_refc_check(Config) when is_list(Config) ->
- ?line node_container_SUITE:node_container_refc_check(node()),
- ?line ok.
+ node_container_SUITE:node_container_refc_check(node()),
+ ok.
-long_timers(doc) ->
- [];
-long_timers(suite) ->
- [];
long_timers(Config) when is_list(Config) ->
- ?line ok = long_timers_test:check_result().
+ case long_timers_test:check_result() of
+ ok -> ok;
+ high_cpu -> {comment, "Ignored failures due to high CPU utilization"};
+ missing_cpu_info -> {comment, "Ignored failures due to missing CPU utilization information"};
+ Fail -> ct:fail(Fail)
+ end.
+
-pollset_size(doc) ->
- [];
-pollset_size(suite) ->
- [];
pollset_size(Config) when is_list(Config) ->
- ?line Name = pollset_size_testcase_initial_state_holder,
- ?line Mon = erlang:monitor(process, Name),
- ?line (catch Name ! {get_initial_check_io_result, self()}),
- ?line InitChkIo = receive
+ Name = pollset_size_testcase_initial_state_holder,
+ Mon = erlang:monitor(process, Name),
+ (catch Name ! {get_initial_check_io_result, self()}),
+ InitChkIo = receive
{initial_check_io_result, ICIO} ->
- ?line erlang:demonitor(Mon, [flush]),
- ?line ICIO;
+ erlang:demonitor(Mon, [flush]),
+ ICIO;
{'DOWN', Mon, _, _, Reason} ->
- ?line ?t:fail({non_existing, Name, Reason})
+ ct:fail({non_existing, Name, Reason})
end,
- ?line FinChkIo = get_check_io_info(),
- ?line io:format("Initial: ~p~nFinal: ~p~n", [InitChkIo, FinChkIo]),
- ?line InitPollsetSize = lists:keysearch(total_poll_set_size, 1, InitChkIo),
- ?line FinPollsetSize = lists:keysearch(total_poll_set_size, 1, FinChkIo),
- ?line case InitPollsetSize =:= FinPollsetSize of
+ FinChkIo = get_check_io_info(),
+ io:format("Initial: ~p~nFinal: ~p~n", [InitChkIo, FinChkIo]),
+ InitPollsetSize = lists:keysearch(total_poll_set_size, 1, InitChkIo),
+ FinPollsetSize = lists:keysearch(total_poll_set_size, 1, FinChkIo),
+ HasGethost = case has_gethost() of true -> 1; _ -> 0 end,
+ case InitPollsetSize =:= FinPollsetSize of
true ->
case InitPollsetSize of
{value, {total_poll_set_size, Size}} ->
- ?line {comment,
+ {comment,
"Pollset size: " ++ integer_to_list(Size)};
_ ->
- ?line {skipped,
+ {skipped,
"Pollset size information not available"}
end;
false ->
@@ -265,40 +231,59 @@ pollset_size(Config) when is_list(Config) ->
%% that is ok as long as there are at least 2
%% descriptors (dist listen socket and
%% epmd socket) in the pollset.
- ?line {value, {total_poll_set_size, InitSize}}
+ {value, {total_poll_set_size, InitSize}}
= InitPollsetSize,
- ?line {value, {total_poll_set_size, FinSize}}
+ {value, {total_poll_set_size, FinSize}}
= FinPollsetSize,
- ?line true = FinSize < InitSize,
- ?line true = 2 =< FinSize,
- ?line {comment,
+ true = FinSize < (InitSize + HasGethost),
+ true = 2 =< FinSize,
+ {comment,
"Start pollset size: "
++ integer_to_list(InitSize)
++ " End pollset size: "
++ integer_to_list(FinSize)}
end.
-check_io_debug(doc) ->
- [];
-check_io_debug(suite) ->
- [];
check_io_debug(Config) when is_list(Config) ->
- ?line case lists:keysearch(name, 1, erlang:system_info(check_io)) of
- {value, {name, erts_poll}} -> ?line check_io_debug_test();
- _ -> ?line {skipped, "Not implemented in this emulator"}
+ case lists:keysearch(name, 1, erlang:system_info(check_io)) of
+ {value, {name, erts_poll}} -> check_io_debug_test();
+ _ -> {skipped, "Not implemented in this emulator"}
end.
check_io_debug_test() ->
- ?line erlang:display(get_check_io_info()),
- ?line erts_debug:set_internal_state(available_internal_state, true),
- ?line {NoErrorFds, NoUsedFds, NoDrvSelStructs, NoDrvEvStructs}
+ erlang:display(get_check_io_info()),
+ erts_debug:set_internal_state(available_internal_state, true),
+ {NoErrorFds, NoUsedFds, NoDrvSelStructs, NoDrvEvStructs} = CheckIoDebug
= erts_debug:get_internal_state(check_io_debug),
- ?line erts_debug:set_internal_state(available_internal_state, false),
- ?line 0 = NoErrorFds,
- ?line NoUsedFds = NoDrvSelStructs,
- ?line 0 = NoDrvEvStructs,
- ?line ok.
+ erts_debug:set_internal_state(available_internal_state, false),
+ HasGetHost = has_gethost(),
+ ct:log("check_io_debug: ~p~n"
+ "HasGetHost: ~p",[CheckIoDebug, HasGetHost]),
+ 0 = NoErrorFds,
+ if
+ NoUsedFds == NoDrvSelStructs ->
+ ok;
+ HasGetHost andalso (NoUsedFds == (NoDrvSelStructs - 1)) ->
+ %% If the inet_gethost port is alive, we may have
+ %% one extra used fd that is not selected on.
+ %% This happens when the initial setup of the
+ %% port returns an EAGAIN
+ ok
+ end,
+ 0 = NoDrvEvStructs,
+ ok.
+has_gethost() ->
+ has_gethost(erlang:ports()).
+has_gethost([P|T]) ->
+ case erlang:port_info(P, name) of
+ {name,"inet_gethost"++_} ->
+ true;
+ _ ->
+ has_gethost(T)
+ end;
+has_gethost([]) ->
+ false.
%%
@@ -332,6 +317,3 @@ get_check_io_info() ->
receive after 100 -> ok end,
get_check_io_info()
end.
-
-
-
diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops
index 9a8c3585e6..4407f7e289 100755
--- a/erts/emulator/utils/beam_makeops
+++ b/erts/emulator/utils/beam_makeops
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2012. All Rights Reserved.
+# Copyright Ericsson AB 1998-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -54,6 +54,11 @@ $pack_mask[4] = ['BEAM_LOOSE_MASK', # Only for 64 bit wordsize
'BEAM_LOOSE_MASK',
$WHOLE_WORD];
+# Mapping from packagable arguments to number of packed arguments per
+# word. Initialized after the wordsize is known.
+
+my @args_per_word;
+
# There are two types of instructions: generic and specific.
# The generic instructions are those generated by the Beam compiler.
# Corresponding to each generic instruction, there is generally a
@@ -108,7 +113,6 @@ my @if_line;
#
my $te_max_vars = 0; # Max number of variables ever needed.
my %gen_transform;
-my %min_window;
my %match_engine_ops; # All opcodes for the match engine.
my %gen_transform_offset;
my @transformations;
@@ -176,11 +180,12 @@ sub define_type_bit {
}
# Composed types.
- define_type_bit('d', $type_bit{'x'} | $type_bit{'y'} | $type_bit{'r'});
+ define_type_bit('d', $type_bit{'x'} | $type_bit{'y'});
define_type_bit('c', $type_bit{'i'} | $type_bit{'a'} |
$type_bit{'n'} | $type_bit{'q'});
define_type_bit('s', $type_bit{'d'} | $type_bit{'i'} |
- $type_bit{'a'} | $type_bit{'n'});
+ $type_bit{'a'} | $type_bit{'n'} |
+ $type_bit{'q'});
define_type_bit('j', $type_bit{'f'} | $type_bit{'p'});
# Aliases (for matching purposes).
@@ -236,6 +241,21 @@ while (@ARGV && $ARGV[0] =~ /^-(.*)/) {
}
#
+# Initialize number of arguments per packed word.
+#
+
+$args_per_word[2] = 2;
+$args_per_word[3] = 3;
+$args_per_word[4] = 2;
+$args_per_word[5] = 3;
+$args_per_word[6] = 3;
+
+if ($wordsize == 64) {
+ $pack_mask[3] = ['BEAM_TIGHT_MASK', 'BEAM_TIGHT_MASK', $WHOLE_WORD];
+ $args_per_word[4] = 4;
+}
+
+#
# Parse the input files.
#
@@ -362,7 +382,6 @@ while (<>) {
$gen_arity{$name} = $arity;
$gen_to_spec{"$name/$arity"} = undef;
$num_specific{"$name/$arity"} = 0;
- $min_window{"$name/$arity"} = 255;
$obsolete[$op_num] = defined $obsolete;
} else { # Unnumbered generic operation.
push(@unnumbered_generic, [$name, $arity]);
@@ -420,7 +439,6 @@ $num_file_opcodes = @gen_opname;
$gen_arity{$name} = $arity;
$gen_to_spec{"$name/$arity"} = undef;
$num_specific{"$name/$arity"} = 0;
- $min_window{"$name/$arity"} = 255;
}
}
@@ -527,12 +545,16 @@ sub emulator_output {
my(@bits) = (0) x ($max_spec_operands/2);
my($i);
+ my $involves_r = 0;
for ($i = 0; $i < $max_spec_operands && defined $args[$i]; $i++) {
my $t = $args[$i];
- if (defined $type_bit{$t}) {
- my $shift = $max_genop_types * ($i % 2);
- $bits[int($i/2)] |= $type_bit{$t} << $shift;
+ my $bits = $type_bit{$t};
+ if ($t eq 'r') {
+ $bits |= $type_bit{'x'};
+ $involves_r |= 1 << $i;
}
+ my $shift = $max_genop_types * ($i % 2);
+ $bits[int($i/2)] |= $bits << $shift;
}
printf "/* %3d */ ", $spec_opnum;
@@ -544,7 +566,7 @@ sub emulator_output {
$sep = ",";
}
$init .= "}";
- &init_item($print_name, $init, $size, $pack, $sign, 0);
+ init_item($print_name, $init, $involves_r, $size, $pack, $sign, 0);
$op_to_name[$spec_opnum] = $instr;
$spec_opnum++;
}
@@ -583,7 +605,7 @@ sub emulator_output {
$is_transformed{$name,$arity} or
error("instruction $key has no specific instruction");
$spec_op = -1 unless defined $spec_op;
- &init_item($name, $arity, $spec_op, $num_specific, $tr, $min_window{$key});
+ &init_item($name, $arity, $spec_op, $num_specific, $tr);
}
}
print "};\n";
@@ -601,24 +623,27 @@ sub emulator_output {
print "#define MAX_GENERIC_OPCODE ", $num_file_opcodes-1, "\n";
print "#define NUM_GENERIC_OPS ", scalar(@gen_opname), "\n";
print "#define NUM_SPECIFIC_OPS ", scalar(@op_to_name), "\n";
+ print "#define SCRATCH_X_REG 1023\n";
print "\n";
- print "#ifdef ARCH_64\n";
- print "# define BEAM_WIDE_MASK 0xFFFFUL\n";
- print "# define BEAM_LOOSE_MASK 0x1FFFUL\n";
- print "#if HALFWORD_HEAP\n";
- print "# define BEAM_TIGHT_MASK 0x1FFCUL\n";
- print "#else\n";
- print "# define BEAM_TIGHT_MASK 0x1FF8UL\n";
- print "#endif\n";
- print "# define BEAM_WIDE_SHIFT 32\n";
- print "# define BEAM_LOOSE_SHIFT 16\n";
- print "# define BEAM_TIGHT_SHIFT 16\n";
- print "#else\n";
- print "# define BEAM_LOOSE_MASK 0xFFF\n";
- print "# define BEAM_TIGHT_MASK 0xFFC\n";
- print "# define BEAM_LOOSE_SHIFT 16\n";
- print "# define BEAM_TIGHT_SHIFT 10\n";
- print "#endif\n";
+ if ($wordsize == 32) {
+ print "#if defined(ARCH_64)\n";
+ print qq[ #error "32-bit architecture assumed, but ARCH_64 is defined"\n];
+ print "#endif\n";
+ print "#define BEAM_LOOSE_MASK 0xFFF\n";
+ print "#define BEAM_TIGHT_MASK 0xFFC\n";
+ print "#define BEAM_LOOSE_SHIFT 16\n";
+ print "#define BEAM_TIGHT_SHIFT 10\n";
+ } elsif ($wordsize == 64) {
+ print "#if !defined(ARCH_64)\n";
+ print qq[ #error "64-bit architecture assumed, but ARCH_64 not defined"\n];
+ print "#endif\n";
+ print "#define BEAM_WIDE_MASK 0xFFFFUL\n";
+ print "#define BEAM_LOOSE_MASK 0xFFFFUL\n";
+ print "#define BEAM_TIGHT_MASK 0xFFFFUL\n";
+ print "#define BEAM_WIDE_SHIFT 32\n";
+ print "#define BEAM_LOOSE_SHIFT 16\n";
+ print "#define BEAM_TIGHT_SHIFT 16\n";
+ }
print "\n";
#
@@ -734,7 +759,7 @@ sub init_item {
print "${sep}NULL";
} elsif (/^\{/) {
print "$sep$_";
- } elsif (/^-?\d/) {
+ } elsif (/^-?\d+$/) {
print "$sep$_";
} else {
print "$sep\"$_\"";
@@ -896,6 +921,7 @@ sub basic_generator {
my($var_decls) = '';
my($gen_dest_arg) = 'StoreSimpleDest';
my($i);
+ my($no_prefetch) = 0;
# The following argument types should be included as macro arguments.
my(%incl_arg) = ('c' => 1,
@@ -994,6 +1020,7 @@ sub basic_generator {
#
$flags =~ /-fail_action/ and do {
+ $no_prefetch = 1;
if (!defined $fail_type) {
my($i);
for ($i = 0; $i < @f_types; $i++) {
@@ -1040,6 +1067,12 @@ sub basic_generator {
"I += $size + 1;",
"goto $goto;",
"}");
+ } elsif ($no_prefetch) {
+ $code = join("\n",
+ "{ $var_decls",
+ $macro_code,
+ "Next($size);",
+ "}", "");
} else {
$code = join("\n",
"{ $var_decls",
@@ -1061,6 +1094,7 @@ sub do_pack {
my($packable_args) = 0;
my @is_packable; # Packability (boolean) for each argument.
my $wide_packing = 0;
+ my(@orig_args) = @args;
#
# Count the number of packable arguments. If we encounter any 's' or 'd'
@@ -1081,6 +1115,18 @@ sub do_pack {
}
} elsif ($arg =~ /^[sd]/) {
return ('', '', @args);
+ } elsif ($arg =~ /^[scq]/ and $packable_args > 0) {
+ # When packing, this operand will be picked up from the
+ # code array, put onto the packing stack, and later put
+ # back into a different location in the code. The problem
+ # is that if this operand is a literal, the original
+ # location in the code would have been remembered in a
+ # literal patch. For packing to work, we would have to
+ # adjust the position in the literal patch. For the
+ # moment, adding additional instructions to the packing
+ # engine to handle this does not seem worth it, so we will
+ # just turn off packing.
+ return ('', '', @args);
} else {
push @is_packable, 0;
}
@@ -1090,7 +1136,6 @@ sub do_pack {
# Get out of here if too few or too many arguments.
#
return ('', '', @args) if $packable_args < 2;
- &error("too many packable arguments") if $packable_args > 4;
my($size) = 0;
my($pack_prefix) = '';
@@ -1098,14 +1143,8 @@ sub do_pack {
# beginning).
my($up) = ''; # Pack commands (storing back while
# moving forward).
- my $args_per_word;
- if ($packable_args < 4 or $wordsize == 64) {
- $args_per_word = $packable_args;
- } else {
- # 4 packable argument, 32 bit wordsize. Need 2 words.
- $args_per_word = 2;
- }
+ my $args_per_word = $args_per_word[$packable_args];
my @shift;
my @mask;
my @instr;
@@ -1299,6 +1338,8 @@ sub tr_parse_op {
foreach (split('', $type)) {
&error("bad type in $op")
unless defined $type_bit{$_} or $type eq '*';
+ $_ eq 'r' and
+ error("$op: 'r' is not allowed in transformations")
}
}
@@ -1332,7 +1373,10 @@ sub tr_parse_op {
}
# Get an optional value. (In destination.)
+ $type_val = $type eq 'x' ? 1023 : 0;
if (/^=(.*)/) {
+ error("value not allowed in source: $op")
+ if $src;
$type_val = $1;
$_ = '';
}
@@ -1351,11 +1395,6 @@ sub tr_parse_op {
if $var && $type;
}
- # Test that source has no values.
- if ($src) {
- error("value not allowed in source: $op")
- if $type_val;
- }
($var,$type,$type_val,$cond,$cond_val);
}
@@ -1370,8 +1409,7 @@ sub tr_gen {
foreach $ref (@g) {
my($line, $orig_transform, $from_ref, $to_ref) = @$ref;
- my $used_ref = used_vars($from_ref, $to_ref);
- my $so_far = tr_gen_from($line, $used_ref, @$from_ref);
+ my $so_far = tr_gen_from($line, @$from_ref);
tr_gen_to($line, $orig_transform, $so_far, @$to_ref);
}
@@ -1422,58 +1460,14 @@ sub tr_gen {
print "};\n\n";
}
-sub used_vars {
- my($from_ref,$to_ref) = @_;
- my %used;
- my %seen;
-
- foreach my $ref (@$from_ref) {
- my($name,$arity,@ops) = @$ref;
- if ($name =~ /^[.]/) {
- foreach my $var (@ops) {
- $used{$var} = 1;
- }
- } else {
- # Any variable that is used at least twice on the
- # left-hand side is used. (E.g. "move R R".)
- foreach my $op (@ops) {
- my($var, $type, $type_val) = @$op;
- next if $var eq '';
- $used{$var} = 1 if $seen{$var};
- $seen{$var} = 1;
- }
- }
- }
-
- foreach my $ref (@$to_ref) {
- my($name, $arity, @ops) = @$ref;
- if ($name =~ /^[.]/) {
- foreach my $var (@ops) {
- $used{$var} = 1;
- }
- } else {
- foreach my $op (@ops) {
- my($var, $type, $type_val) = @$op;
- next if $var eq '';
- $used{$var} = 1;
- }
- }
- }
- \%used;
-}
-
sub tr_gen_from {
- my($line,$used_ref,@tr) = @_;
+ my($line,@tr) = @_;
my(%var) = ();
my(%var_type);
my($var_num) = 0;
my(@code);
- my($min_window) = 0;
- my(@fix_rest_args);
- my(@fix_pred_funcs);
my($op, $ref); # Loop variables.
my $where = "left side of transformation in line $line: ";
- my %var_used = %$used_ref;
my $may_fail = 0;
my $is_first = 1;
@@ -1495,8 +1489,20 @@ sub tr_gen_from {
my $var;
my(@args);
- push(@fix_pred_funcs, scalar(@code));
- push(@code, [$name, @ops]);
+ foreach $var (@ops) {
+ error($where, "variable '$var' unbound")
+ unless defined $var{$var};
+ if ($var_type{$var} eq 'scalar') {
+ push(@args, "var[$var{$var}]");
+ } else {
+ push(@args, "rest_args");
+ }
+ }
+ my $pi = tr_next_index(\@pred_table, \%pred_table, $name, @args);
+ my $op = make_op("$name()", 'pred', $pi);
+ my @slots = grep(/^\d+/, map { $var{$_} } @ops);
+ op_slot_usage($op, @slots);
+ push(@code, $op);
next;
}
@@ -1509,7 +1515,6 @@ sub tr_gen_from {
$opnum = $gen_opnum{$name,$arity};
push(@code, make_op("$name/$arity", 'next_instr', $opnum));
- $min_window++;
foreach $op (@ops) {
my($var, $type, $type_val, $cond, $val) = @$op;
my $ignored_var = "$var (ignored)";
@@ -1558,15 +1563,21 @@ sub tr_gen_from {
if (defined $var{$var}) {
$ignored_var = '';
$may_fail = 1;
- push(@code, &make_op($var, 'is_same_var', $var{$var}));
+ my $op = make_op($var, 'is_same_var', $var{$var});
+ op_slot_usage($op, $var{$var});
+ push(@code, $op);
} elsif ($type eq '*') {
- #
- # Reserve a hole for a 'rest_args' instruction.
- #
+ foreach my $type (values %var_type) {
+ error("only one use of a '*' variable is " .
+ "allowed on the left hand side of " .
+ "a transformation")
+ if $type eq 'array';
+ }
$ignored_var = '';
- push(@fix_rest_args, scalar(@code));
- push(@code, $var);
- } elsif ($var_used{$var}) {
+ $var{$var} = 'unnumbered';
+ $var_type{$var} = 'array';
+ push(@code, make_op($var, 'rest_args'));
+ } else {
$ignored_var = '';
$var_type{$var} = 'scalar';
$var{$var} = $var_num;
@@ -1594,46 +1605,14 @@ sub tr_gen_from {
#
push(@code, make_op($may_fail ? '' : 'always reached', 'commit'));
- #
- # If there is an rest_args instruction, we must insert its correct
- # variable number (higher than any other).
- #
- my $index;
- &error("only one use of a '*' variable is allowed on the left hand side of a transformation")
- if @fix_rest_args > 1;
- foreach $index (@fix_rest_args) {
- my $var = $code[$index];
- $var{$var} = $var_num++;
- $var_type{$var} = 'array';
- splice(@code, $index, 1, &make_op($var, 'rest_args', $var{$var}));
- }
-
- foreach $index (@fix_pred_funcs) {
- my($name, @ops) = @{$code[$index]};
- my(@args);
- my $var;
-
- foreach $var (@ops) {
- &error($where, "variable '$var' unbound")
- unless defined $var{$var};
- if ($var_type{$var} eq 'scalar') {
- push(@args, "var[$var{$var}]");
- } else {
- push(@args, "var+$var{$var}");
- }
- }
- my $pi = tr_next_index(\@pred_table, \%pred_table, $name, @args);
- splice(@code, $index, 1, make_op("$name()", 'pred', $pi));
- }
-
$te_max_vars = $var_num
if $te_max_vars < $var_num;
- [$min_window, \%var, \%var_type, \@code];
+ [\%var, \%var_type, \@code];
}
sub tr_gen_to {
my($line, $orig_transform, $so_far, @tr) = @_;
- my($min_window, $var_ref, $var_type_ref, $code_ref) = @$so_far;
+ my($var_ref, $var_type_ref, $code_ref) = @$so_far;
my(%var) = %$var_ref;
my(%var_type) = %$var_type_ref;
my(@code) = @$code_ref;
@@ -1662,13 +1641,16 @@ sub tr_gen_to {
if ($var_type{$var} eq 'scalar') {
push(@args, "var[$var{$var}]");
} else {
- push(@args, "var+$var{$var}");
+ push(@args, "rest_args");
}
}
pop(@code); # Get rid of 'commit' instruction
my $index = tr_next_index(\@call_table, \%call_table,
$name, @args);
- push(@code, make_op("$name()", 'call_end', $index));
+ my $op = make_op("$name()", 'call_end', $index);
+ my @slots = grep(/^\d+/, map { $var{$_} } @ops);
+ op_slot_usage($op, @slots);
+ push(@code, $op);
last;
}
@@ -1690,11 +1672,13 @@ sub tr_gen_to {
my($var, $type, $type_val) = @$op;
if ($type eq '*') {
- push(@code, make_op($var, 'store_rest_args', $var{$var}));
+ push(@code, make_op($var, 'store_rest_args'));
} elsif ($var ne '') {
&error($where, "variable '$var' unbound")
unless defined $var{$var};
- push(@code, &make_op($var, 'store_var_next_arg', $var{$var}));
+ my $op = make_op($var, 'store_var_next_arg', $var{$var});
+ op_slot_usage($op, $var{$var});
+ push(@code, $op);
} elsif ($type ne '') {
push(@code, &make_op('', 'store_type', "TAG_$type"));
if ($type_val) {
@@ -1709,6 +1693,10 @@ sub tr_gen_to {
push(@code, make_op('', 'end'))
unless is_instr($code[$#code], 'call_end');
+ tr_maybe_keep(\@code);
+ tr_maybe_rename(\@code);
+ tr_remove_unused(\@code);
+
#
# Chain together all codes segments having the same first operation.
#
@@ -1717,8 +1705,6 @@ sub tr_gen_to {
my($dummy, $arity);
($dummy, $op, $arity) = @$first;
my($comment) = "\n/*\n * Line $line:\n * $orig_transform\n */\n\n";
- $min_window{$key} = $min_window
- if $min_window{$key} > $min_window;
my $prev_last;
$prev_last = pop(@{$gen_transform{$key}})
@@ -1736,6 +1722,148 @@ sub tr_gen_to {
push(@{$gen_transform{$key}}, @code),
}
+sub tr_maybe_keep {
+ my($ref) = @_;
+ my @last_instr;
+ my $pos;
+ my $reused_instr;
+
+ for (my $i = 0; $i < @$ref; $i++) {
+ my $instr = $$ref[$i];
+ my($size, $instr_ref, $comment) = @$instr;
+ my($op, @args) = @$instr_ref;
+ if ($op eq 'next_instr') {
+ @last_instr = ($args[0]);
+ } elsif ($op eq 'set_var_next_arg') {
+ push @last_instr, $args[0];
+ } elsif ($op eq 'next_arg') {
+ push @last_instr, 'ignored';
+ } elsif ($op eq 'new_instr') {
+ unless (defined $pos) {
+ # 'new_instr' immediately after 'commit'.
+ $reused_instr = $args[0];
+ return unless shift(@last_instr) == $reused_instr;
+ $pos = $i - 1;
+ } else {
+ # Second 'new_instr' after 'commit'. The instructions
+ # from $pos up to and including $i - 1 rebuilds the
+ # existing instruction exactly.
+ my $name = $gen_opname[$reused_instr];
+ my $arity = $gen_arity[$reused_instr];
+ my $reuse = make_op("$name/$arity", 'keep');
+ splice @$ref, $pos, $i-$pos, ($reuse);
+ return;
+ }
+ } elsif ($op eq 'store_var_next_arg') {
+ return unless shift(@last_instr) eq $args[0];
+ } elsif (defined $pos) {
+ return;
+ }
+ }
+}
+
+sub tr_maybe_rename {
+ my($ref) = @_;
+ my $s = 'left';
+ my $a = 0;
+ my $num_args = 0;
+ my $new_instr;
+ my $first;
+ my $i;
+
+ for ($i = 1; $i < @$ref; $i++) {
+ my $instr = $$ref[$i];
+ my($size, $instr_ref, $comment) = @$instr;
+ my($op, @args) = @$instr_ref;
+
+ if ($s eq 'left') {
+ if ($op eq 'set_var_next_arg') {
+ if ($num_args == $a and $args[0] == $a) {
+ $num_args++;
+ }
+ $a++;
+ } elsif ($op eq 'next_arg') {
+ $a++;
+ } elsif ($op eq 'commit') {
+ $a = 0;
+ $first = $i;
+ $s = 'committed';
+ } elsif ($op eq 'next_instr') {
+ return;
+ }
+ } elsif ($s eq 'committed') {
+ if ($op eq 'new_instr') {
+ $new_instr = $args[0];
+ $a = 0;
+ $s = 'right';
+ } else {
+ return;
+ }
+ } elsif ($s eq 'right') {
+ if ($op eq 'store_var_next_arg' && $args[0] == $a) {
+ $a++;
+ } elsif ($op eq 'end' && $a <= $num_args) {
+ my $name = $gen_opname[$new_instr];
+ my $arity = $gen_arity[$new_instr];
+ my $new_op = make_op("$name/$arity", 'rename', $new_instr);
+ splice @$ref, $first, $i-$first+1, ($new_op);
+ return;
+ } else {
+ return;
+ }
+ }
+ }
+}
+
+sub tr_remove_unused {
+ my($ref) = @_;
+ my %used;
+
+ # Collect all used variables.
+ for my $instr (@$ref) {
+ my $uref = $$instr[3];
+ for my $slot (@$uref) {
+ $used{$slot} = 1;
+ }
+ }
+
+ # Replace 'set_var_next_arg' with 'next_arg' if the variable
+ # is never used.
+ for my $instr (@$ref) {
+ my($size, $instr_ref, $comment) = @$instr;
+ my($op, @args) = @$instr_ref;
+ if ($op eq 'set_var_next_arg') {
+ my $var = $args[0];
+ next if $used{$var};
+ $instr = make_op("$comment (ignored)", 'next_arg');
+ }
+ }
+
+ # Delete a sequence of 'next_arg' instructions when they are
+ # redundant before instructions such as 'commit'.
+ my @opcode;
+ my %ending = (call_end => 1,
+ commit => 1,
+ next_instr => 1,
+ pred => 1,
+ rename => 1,
+ keep => 1);
+ for (my $i = 0; $i < @$ref; $i++) {
+ my $instr = $$ref[$i];
+ my($size, $instr_ref, $comment) = @$instr;
+ my($opcode) = @$instr_ref;
+
+ if ($ending{$opcode}) {
+ my $first = $i;
+ $first-- while $first > 0 and $opcode[$first-1] eq 'next_arg';
+ my $n = $i - $first;
+ splice @$ref, $first, $n;
+ $i -= $n;
+ }
+ $opcode[$i] = $opcode;
+ }
+}
+
sub tr_code_len {
my($sum) = 0;
my($ref);
@@ -1748,7 +1876,12 @@ sub tr_code_len {
sub make_op {
my($comment, @op) = @_;
- [scalar(@op), [@op], $comment];
+ [scalar(@op), [@op], $comment, []];
+}
+
+sub op_slot_usage {
+ my($op_ref, @slots) = @_;
+ $$op_ref[3] = \@slots;
}
sub is_instr {
diff --git a/erts/emulator/utils/beam_strip b/erts/emulator/utils/beam_strip
index 0e7bc46b63..2a2d761940 100755
--- a/erts/emulator/utils/beam_strip
+++ b/erts/emulator/utils/beam_strip
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2009. All Rights Reserved.
+# Copyright Ericsson AB 2001-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/emulator/utils/count b/erts/emulator/utils/count
index c4dd42c949..e0a5e8bb9a 100755
--- a/erts/emulator/utils/count
+++ b/erts/emulator/utils/count
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/emulator/utils/loaded b/erts/emulator/utils/loaded
index ab77ffdb6c..5e0a29c014 100644
--- a/erts/emulator/utils/loaded
+++ b/erts/emulator/utils/loaded
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/emulator/utils/make_alloc_types b/erts/emulator/utils/make_alloc_types
index 925b9d5810..33afe139a2 100755
--- a/erts/emulator/utils/make_alloc_types
+++ b/erts/emulator/utils/make_alloc_types
@@ -3,7 +3,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2009. All Rights Reserved.
+# Copyright Ericsson AB 2003-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/emulator/utils/make_compiler_flags b/erts/emulator/utils/make_compiler_flags
index d75ea0817e..47491a4832 100755
--- a/erts/emulator/utils/make_compiler_flags
+++ b/erts/emulator/utils/make_compiler_flags
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2009. All Rights Reserved.
+# Copyright Ericsson AB 1999-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/emulator/utils/make_driver_tab b/erts/emulator/utils/make_driver_tab
index 3203c7110a..ffb5f58ebf 100755
--- a/erts/emulator/utils/make_driver_tab
+++ b/erts/emulator/utils/make_driver_tab
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2013. All Rights Reserved.
+# Copyright Ericsson AB 1999-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -31,7 +31,7 @@ my $file = "";
my $nif = "";
my @emu_drivers = ();
my @static_drivers = ();
-my @nifs = ();
+my @static_nifs = ();
my $mode = 1;
while (@ARGV) {
@@ -55,9 +55,14 @@ while (@ARGV) {
push(@static_drivers, $d);
}
if ($mode == 2) {
- push(@nifs, $d);
+ push(@static_nifs, $d);
}
next;
+ } elsif ($mode == 2) {
+ $d = basename $d;
+ $d =~ s/_nif(\..*|)$//; # strip nif.* or just nif
+ push(@static_nifs, $d);
+ next;
}
$d = basename $d;
$d =~ s/drv(\..*|)$//; # strip drv.* or just drv
@@ -120,7 +125,7 @@ typedef struct ErtsStaticNifEntry_ {
EOF
# prototypes
-foreach (@nifs) {
+foreach (@static_nifs) {
my $d = ${_};
$d =~ s/\.debug//; # strip .debug
print "void *".$d."_nif_init(void);\n";
@@ -129,7 +134,7 @@ foreach (@nifs) {
# The array itself
print "static ErtsStaticNifEntry static_nif_tab[] =\n{\n";
-foreach (@nifs) {
+foreach (@static_nifs) {
my $d = ${_};
$d =~ s/\.debug//; # strip .debug
print "{\"${_}\",&".$d."_nif_init},\n";
diff --git a/erts/emulator/utils/make_preload b/erts/emulator/utils/make_preload
index 62c4419589..f489bc2a39 100755
--- a/erts/emulator/utils/make_preload
+++ b/erts/emulator/utils/make_preload
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2012. All Rights Reserved.
+# Copyright Ericsson AB 1999-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/emulator/utils/make_tables b/erts/emulator/utils/make_tables
index 233e95f176..c158778f43 100755
--- a/erts/emulator/utils/make_tables
+++ b/erts/emulator/utils/make_tables
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2013. All Rights Reserved.
+# Copyright Ericsson AB 1999-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/emulator/utils/make_version b/erts/emulator/utils/make_version
index 37bdff181a..e02a42c66d 100755
--- a/erts/emulator/utils/make_version
+++ b/erts/emulator/utils/make_version
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2009. All Rights Reserved.
+# Copyright Ericsson AB 1999-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/emulator/utils/mkver.c b/erts/emulator/utils/mkver.c
index 6641873712..6183246433 100644
--- a/erts/emulator/utils/mkver.c
+++ b/erts/emulator/utils/mkver.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/emulator/valgrind/suppress.halfword b/erts/emulator/valgrind/suppress.halfword
deleted file mode 100644
index 8fe448d897..0000000000
--- a/erts/emulator/valgrind/suppress.halfword
+++ /dev/null
@@ -1,56 +0,0 @@
-# Extra suppressions specific for the halfword emulator.
-
-# --- Suppress all offheap binaries ---
-# Valgrinds leak check does not recognize pointers that are stored
-# at unaligned addresses. In halfword emulator we store 64-bit pointers
-# to offheap data on 32-bit aligned heaps.
-# We solve this by suppressing allocation of all offheap structures
-# that are not referenced by other tables (ie binaries).
-
-{
-Halfword erts_bin_nrml_alloc
-Memcheck:Leak
-...
-fun:erts_bin_nrml_alloc
-...
-}
-
-{
-Halfword erts_bin_realloc
-Memcheck:Leak
-...
-fun:erts_bin_realloc
-...
-}
-
-{
-Halfword erts_bin_realloc_fnf
-Memcheck:Leak
-...
-fun:erts_bin_realloc_fnf
-...
-}
-
-{
-Halfword erts_bin_drv_alloc
-Memcheck:Leak
-...
-fun:erts_bin_drv_alloc
-...
-}
-
-{
-Halfword erts_bin_drv_alloc_fnf
-Memcheck:Leak
-...
-fun:erts_bin_drv_alloc_fnf
-...
-}
-
-{
-Halfword erts_create_magic_binary
-Memcheck:Leak
-...
-fun:erts_create_magic_binary
-...
-}
diff --git a/erts/emulator/zlib/zlib.mk b/erts/emulator/zlib/zlib.mk
index 53d7badd64..3f0d64d250 100644
--- a/erts/emulator/zlib/zlib.mk
+++ b/erts/emulator/zlib/zlib.mk
@@ -4,7 +4,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2011-2012. All Rights Reserved.
+# Copyright Ericsson AB 2011-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/epmd/Makefile b/erts/epmd/Makefile
index 25a33462ee..d3308ddedc 100644
--- a/erts/epmd/Makefile
+++ b/erts/epmd/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2009. All Rights Reserved.
+# Copyright Ericsson AB 1998-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/epmd/epmd.mk b/erts/epmd/epmd.mk
index 08245b784e..b1fd04dc04 100644
--- a/erts/epmd/epmd.mk
+++ b/erts/epmd/epmd.mk
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2009. All Rights Reserved.
+# Copyright Ericsson AB 1998-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/epmd/src/Makefile b/erts/epmd/src/Makefile
index 3e09a40566..4ae13fe05a 100644
--- a/erts/epmd/src/Makefile
+++ b/erts/epmd/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2009. All Rights Reserved.
+# Copyright Ericsson AB 1998-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/epmd/src/Makefile.in b/erts/epmd/src/Makefile.in
index b6e3ba7762..da4370d5f9 100644
--- a/erts/epmd/src/Makefile.in
+++ b/erts/epmd/src/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2012. All Rights Reserved.
+# Copyright Ericsson AB 1998-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -19,10 +19,6 @@
#
include $(ERL_TOP)/make/target.mk
-ifeq ($(findstring ose,$(TARGET)),ose)
-include $(ERL_TOP)/make/$(TARGET)/ose_lm.mk
-endif
-
ifeq ($(TYPE),debug)
PURIFY =
TYPEMARKER = .debug
@@ -32,21 +28,13 @@ else
ifeq ($(TYPE),purify)
PURIFY = purify
TYPEMARKER =
-ifeq ($(findstring ose,$(TARGET)),ose)
- TYPE_FLAGS = -DPURIFY
-else
- TYPE_FLAGS = -O2 -DPURIFY
-endif
+TYPE_FLAGS = -O2 -DPURIFY
else
override TYPE = opt
PURIFY =
TYPEMARKER =
-ifeq ($(findstring ose,$(TARGET)),ose)
- TYPE_FLAGS =
-else
- TYPE_FLAGS = -O2
-endif
+TYPE_FLAGS = -O2
endif
endif
@@ -68,13 +56,9 @@ else
ifeq ($(findstring vxworks,$(TARGET)),vxworks)
ERTS_INTERNAL_LIBS=-L../../lib/internal/$(TARGET) -lerts_internal$(ERTS_LIB_TYPEMARKER) @ERTS_INTERNAL_X_LIBS@
else
-ifeq ($(findstring ose,$(TARGET)),ose)
-ERTS_INTERNAL_LIBS=-L../../lib/internal/$(TARGET) -lerts_internal$(ERTS_LIB_TYPEMARKER) @ERTS_INTERNAL_X_LIBS@
-else
ERTS_INTERNAL_LIBS=-L../../lib/internal/$(TARGET) -lerts_internal$(ERTS_LIB_TYPEMARKER) @ERTS_INTERNAL_X_LIBS@ -lm
endif
endif
-endif
ERTS_LIB = $(ERL_TOP)/erts/lib_src/obj/$(TARGET)/$(TYPE)/MADE
@@ -82,11 +66,7 @@ CC = @CC@
WFLAGS = @WFLAGS@
CFLAGS = @CFLAGS@ @DEFS@ $(TYPE_FLAGS) $(WFLAGS) $(ERTS_INCL)
LD = @LD@
-ifeq ($(findstring ose,$(TARGET)),ose)
-LIBS = $(ERTS_INTERNAL_LIBS) @LIBS@
-else
LIBS = @LIBS@ @SYSTEMD_DAEMON_LIBS@ $(ERTS_INTERNAL_LIBS)
-endif
LDFLAGS = @LDFLAGS@
@@ -135,25 +115,12 @@ clean:
rm -f *.o
rm -f *~ core
-ifeq ($(findstring ose,$(TARGET)),ose)
-$(OBJDIR)/ose_confd.o: $(OSE_CONFD)
- $(V_CC) $(CFLAGS) -o $@ -c $<
-$(OBJDIR)/crt0_lm.o: $(CRT0_LM)
- $(V_CC) $(CFLAGS) -o $@ -c $<
-OSE_LM_OBJS += $(OBJDIR)/ose_confd.o $(OBJDIR)/crt0_lm.o
-endif
-
#
# Objects & executables
#
-ifeq ($(findstring ose,$(TARGET)),ose)
-$(BINDIR)/$(EPMD): $(EPMD_OBJS) $(ERTS_LIB) $(OSE_LM_OBJS)
- $(call build-ose-load-module, $@, $(EPMD_OBJS) $(OSE_LM_OBJS), $(LIBS), $(EPMD_LMCONF))
-else
$(BINDIR)/$(EPMD): $(EPMD_OBJS) $(ERTS_LIB)
$(ld_verbose)$(PURIFY) $(LD) $(LDFLAGS) -o $@ $(EPMD_OBJS) $(LIBS)
-endif
$(OBJDIR)/%.o: %.c epmd.h epmd_int.h
$(V_CC) $(CFLAGS) $(EPMD_FLAGS) -o $@ -c $<
diff --git a/erts/epmd/src/epmd.c b/erts/epmd/src/epmd.c
index 5513cb2d7e..44e997e609 100644
--- a/erts/epmd/src/epmd.c
+++ b/erts/epmd/src/epmd.c
@@ -2,7 +2,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -397,7 +397,7 @@ static void run_daemon(EpmdVars *g)
}
#endif
-#if defined(VXWORKS) || defined(__OSE__)
+#if defined(VXWORKS)
static void run_daemon(EpmdVars *g)
{
run(g);
@@ -592,8 +592,10 @@ void epmd_cleanup_exit(EpmdVars *g, int exitval)
free(g->argv);
}
#ifdef HAVE_SYSTEMD_DAEMON
- sd_notifyf(0, "STATUS=Exited.\n"
- "ERRNO=%i", exitval);
+ if (g->is_systemd){
+ sd_notifyf(0, "STATUS=Exited.\n"
+ "ERRNO=%i", exitval);
+ }
#endif /* HAVE_SYSTEMD_DAEMON */
exit(exitval);
}
diff --git a/erts/epmd/src/epmd.h b/erts/epmd/src/epmd.h
index 10483bb5a2..cffcd4ae7a 100644
--- a/erts/epmd/src/epmd.h
+++ b/erts/epmd/src/epmd.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2010. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/epmd/src/epmd_cli.c b/erts/epmd/src/epmd_cli.c
index 6fc05e153e..6fd27d46ea 100644
--- a/erts/epmd/src/epmd_cli.c
+++ b/erts/epmd/src/epmd_cli.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/epmd/src/epmd_int.h b/erts/epmd/src/epmd_int.h
index 09317094c7..ed9bbdb8cd 100644
--- a/erts/epmd/src/epmd_int.h
+++ b/erts/epmd/src/epmd_int.h
@@ -2,7 +2,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,13 +37,6 @@
#define DONT_USE_MAIN
#endif
-#ifdef __OSE__
-# define NO_DAEMON
-# define NO_SYSLOG
-# define NO_SYSCONF
-# define NO_FCNTL
-#endif
-
/* ************************************************************************ */
/* Standard includes */
@@ -101,12 +94,7 @@
#endif /* ! WIN32 */
#include <ctype.h>
-
-#if !defined(__OSE__)
-# include <signal.h>
-#endif
-
-
+#include <signal.h>
#include <errno.h>
#ifdef HAVE_SYSLOG_H
@@ -123,10 +111,6 @@
#include <stdarg.h>
-#ifdef __OSE__
-# include "sys/select.h"
-#endif
-
#ifdef HAVE_SYSTEMD_DAEMON
# include <systemd/sd-daemon.h>
#endif /* HAVE_SYSTEMD_DAEMON */
@@ -253,8 +237,8 @@ static const struct in6_addr in6addr_loopback =
#define EPMD_TRUE 1
/* If no activity we let select() return every IDLE_TIMEOUT second
- A file descriptor that are idle for CLOSE_TIMEOUT seconds and
- isn't a ALIVE socket is probably hanging and we close it */
+ A file descriptor that has been idle for CLOSE_TIMEOUT seconds and
+ isn't an ALIVE socket has probably hanged and should be closed */
#define IDLE_TIMEOUT 5
#define CLOSE_TIMEOUT 60
diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c
index e1bac99ef9..66c10a65bc 100644
--- a/erts/epmd/src/epmd_srv.c
+++ b/erts/epmd/src/epmd_srv.c
@@ -2,7 +2,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,11 +30,6 @@
# define INADDR_NONE 0xffffffff
#endif
-#if defined(__OSE__)
-# include "sys/ioctl.h"
-# define sleep(x) delay(x*1000)
-#endif
-
/*
*
* This server is a local name server for Erlang nodes. Erlang nodes can
@@ -335,7 +330,7 @@ void run(EpmdVars *g)
}
#endif /* HAVE_SYSTEMD_DAEMON */
-#if !defined(__WIN32__) && !defined(__OSE__)
+#if !defined(__WIN32__)
/* We ignore the SIGPIPE signal that is raised when we call write
twice on a socket closed by the other end. */
signal(SIGPIPE, SIG_IGN);
@@ -452,9 +447,11 @@ void run(EpmdVars *g)
num_sockets = bound;
#ifdef HAVE_SYSTEMD_DAEMON
}
- sd_notifyf(0, "READY=1\n"
- "STATUS=Processing port mapping requests...\n"
- "MAINPID=%lu", (unsigned long) getpid());
+ if (g->is_systemd) {
+ sd_notifyf(0, "READY=1\n"
+ "STATUS=Processing port mapping requests...\n"
+ "MAINPID=%lu", (unsigned long) getpid());
+ }
#endif /* HAVE_SYSTEMD_DAEMON */
dbg_tty_printf(g,2,"entering the main select() loop");
diff --git a/erts/epmd/test/Makefile b/erts/epmd/test/Makefile
index 7c5302a2f1..ad1315440f 100644
--- a/erts/epmd/test/Makefile
+++ b/erts/epmd/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2012. All Rights Reserved.
+# Copyright Ericsson AB 1998-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/epmd/test/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl
index d5837e5b8c..0f0a5acde7 100644
--- a/erts/epmd/test/epmd_SUITE.erl
+++ b/erts/epmd/test/epmd_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -18,69 +18,63 @@
%% %CopyrightEnd%
%%
-module(epmd_SUITE).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include_lib("kernel/include/file.hrl").
% Timeout for test cases (rather long to work on slow machines)
--define(SHORT_TEST_TIMEOUT, ?t:seconds(30)). % Default
--define(MEDIUM_TEST_TIMEOUT, ?t:minutes(3)).
--define(LONG_TEST_TIMEOUT, ?t:minutes(10)).
+-define(MEDIUM_TEST_TIMEOUT, {minutes,3}).
+-define(LONG_TEST_TIMEOUT, {minutes,10}).
% Delay inserted into code
-define(SHORT_PAUSE, 100).
--define(MEDIUM_PAUSE, ?t:seconds(1)).
--define(LONG_PAUSE, ?t:seconds(5)).
+-define(MEDIUM_PAUSE, 1000).
+-define(LONG_PAUSE, 5000).
% Information about nodes
-record(node_info, {port, node_type, prot, lvsn, hvsn, node_name, extra}).
% Test server specific exports
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2, end_per_testcase/2]).
-
--export(
- [
- register_name/1,
- register_name_ipv6/1,
- register_names_1/1,
- register_names_2/1,
- register_duplicate_name/1,
- unicode_name/1,
- long_unicode_name/1,
- get_port_nr/1,
- slow_get_port_nr/1,
- unregister_others_name_1/1,
- unregister_others_name_2/1,
- register_overflow/1,
- name_with_null_inside/1,
- name_null_terminated/1,
- stupid_names_req/1,
-
- no_data/1,
- one_byte/1,
- two_bytes/1,
- partial_packet/1,
- zero_length/1,
- too_large/1,
- alive_req_too_small_1/1,
- alive_req_too_small_2/1,
- alive_req_too_large/1,
-
- returns_valid_empty_extra/1,
- returns_valid_populated_extra_with_nulls/1,
-
- names_stdout/1,
-
- buffer_overrun_1/1,
- buffer_overrun_2/1,
- no_nonlocal_register/1,
- no_nonlocal_kill/1,
- no_live_killing/1,
-
- socket_reset_before_alive2_reply_is_written/1
- ]).
+-export([all/0, suite/0, groups/0, init_per_testcase/2, end_per_testcase/2]).
+
+-export([register_name/1,
+ register_name_ipv6/1,
+ register_names_1/1,
+ register_names_2/1,
+ register_duplicate_name/1,
+ unicode_name/1,
+ long_unicode_name/1,
+ get_port_nr/1,
+ slow_get_port_nr/1,
+ unregister_others_name_1/1,
+ unregister_others_name_2/1,
+ register_overflow/1,
+ name_with_null_inside/1,
+ name_null_terminated/1,
+ stupid_names_req/1,
+
+ no_data/1,
+ one_byte/1,
+ two_bytes/1,
+ partial_packet/1,
+ zero_length/1,
+ too_large/1,
+ alive_req_too_small_1/1,
+ alive_req_too_small_2/1,
+ alive_req_too_large/1,
+
+ returns_valid_empty_extra/1,
+ returns_valid_populated_extra_with_nulls/1,
+
+ names_stdout/1,
+
+ buffer_overrun_1/1,
+ buffer_overrun_2/1,
+ no_nonlocal_register/1,
+ no_nonlocal_kill/1,
+ no_live_killing/1,
+
+ socket_reset_before_alive2_reply_is_written/1]).
% Port we use for testing
@@ -88,7 +82,7 @@
-define(EPMDARGS,"-packet_timeout 1").
-define(DUMMY_PORT, 1000). % Port number to register
- % not in real use.
+% not in real use.
% Timeouts etc inside test cases. Time is in milliseconds.
-define(CONN_RETRY, 4). % Times to retry connecting
@@ -111,7 +105,9 @@
%% all/1
%%
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, ?MEDIUM_TEST_TIMEOUT}].
all() ->
[register_name, register_name_ipv6,
@@ -134,105 +130,71 @@ groups() ->
[{buffer_overrun, [],
[buffer_overrun_1, buffer_overrun_2]}].
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
%%
%% Run before and after each test case
%%
init_per_testcase(_Func, Config) ->
- Dog = test_server:timetrap(?MEDIUM_TEST_TIMEOUT),
cleanup(),
- [{watchdog, Dog} | Config].
+ Config.
-end_per_testcase(_Func, Config) ->
+end_per_testcase(_Func, _Config) ->
cleanup(),
- Dog = ?config(watchdog, Config),
- catch test_server:timetrap_cancel(Dog), % We may have canceled already
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-register_name(doc) ->
- ["Register a name"];
-register_name(suite) ->
- [];
+%% Register a name
register_name(Config) when is_list(Config) ->
- ?line ok = epmdrun(),
- ?line {ok,Sock} = register_node("foobar"),
- ?line ok = close(Sock), % Unregister
+ ok = epmdrun(),
+ {ok,Sock} = register_node("foobar"),
+ ok = close(Sock), % Unregister
ok.
-register_name_ipv6(doc) ->
- ["Register a name over IPv6"];
-register_name_ipv6(suite) ->
- [];
+%% Register a name over IPv6
register_name_ipv6(Config) when is_list(Config) ->
% Test if the host has an IPv6 loopback address
Res = gen_tcp:listen(0, [inet6, {ip, {0,0,0,0,0,0,0,1}}]),
case Res of
- {ok,LSock} ->
- gen_tcp:close(LSock),
- ?line ok = epmdrun(),
- ?line {ok,Sock} = register_node6("foobar6"),
- ?line ok = close(Sock), % Unregister
- ok;
- _Error ->
- {skip, "Host does not have an IPv6 loopback address"}
+ {ok,LSock} ->
+ gen_tcp:close(LSock),
+ ok = epmdrun(),
+ {ok,Sock} = register_node6("foobar6"),
+ ok = close(Sock), % Unregister
+ ok;
+ _Error ->
+ {skip, "Host does not have an IPv6 loopback address"}
end.
-register_names_1(doc) ->
- ["Register and unregister two nodes"];
-register_names_1(suite) ->
- [];
+%% Register and unregister two nodes
register_names_1(Config) when is_list(Config) ->
- ?line ok = epmdrun(),
- ?line {ok,Sock1} = register_node("foobar"),
- ?line {ok,Sock2} = register_node("foozap"),
- ?line ok = close(Sock1), % Unregister
- ?line ok = close(Sock2), % Unregister
+ ok = epmdrun(),
+ {ok,Sock1} = register_node("foobar"),
+ {ok,Sock2} = register_node("foozap"),
+ ok = close(Sock1), % Unregister
+ ok = close(Sock2), % Unregister
ok.
-register_names_2(doc) ->
- ["Register and unregister two nodes"];
-register_names_2(suite) ->
- [];
+%% Register and unregister two nodes
register_names_2(Config) when is_list(Config) ->
- ?line ok = epmdrun(),
- ?line {ok,Sock1} = register_node("foobar"),
- ?line {ok,Sock2} = register_node("foozap"),
- ?line ok = close(Sock2), % Unregister
- ?line ok = close(Sock1), % Unregister
+ ok = epmdrun(),
+ {ok,Sock1} = register_node("foobar"),
+ {ok,Sock2} = register_node("foozap"),
+ ok = close(Sock2), % Unregister
+ ok = close(Sock1), % Unregister
ok.
-register_duplicate_name(doc) ->
- ["Two nodes with the same name"];
-register_duplicate_name(suite) ->
- [];
+%% Two nodes with the same name
register_duplicate_name(Config) when is_list(Config) ->
- ?line ok = epmdrun(),
- ?line {ok,Sock} = register_node("foobar"),
- ?line error = register_node("foobar"),
- ?line ok = close(Sock), % Unregister
+ ok = epmdrun(),
+ {ok,Sock} = register_node("foobar"),
+ error = register_node("foobar"),
+ ok = close(Sock), % Unregister
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-unicode_name(doc) ->
- ["Check that we can register and lookup a unicode name"];
-unicode_name(suite) ->
- [];
+%% Check that we can register and lookup a unicode name
unicode_name(Config) when is_list(Config) ->
ok = epmdrun(),
NodeName = [16#1f608],
@@ -244,10 +206,7 @@ unicode_name(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-long_unicode_name(doc) ->
- ["Check that we can register and lookup a long unicode name"];
-long_unicode_name(suite) ->
- [];
+%% Check that we can register and lookup a long unicode name
long_unicode_name(Config) when is_list(Config) ->
ok = epmdrun(),
BaseChar = 16#1f600,
@@ -273,101 +232,85 @@ register_node_v2(Port, NodeType, Prot, HVsn, LVsn, Name, Extra) ->
register_node_v2(Addr, Port, NodeType, Prot, HVsn, LVsn, Name, Extra) ->
Req = alive2_req(Port, NodeType, Prot, HVsn, LVsn, Name, Extra),
case send_req(Req, Addr) of
- {ok,Sock} ->
- case recv(Sock,4) of
- {ok, [?EPMD_ALIVE2_RESP,_Res=0,_C0,_C1]} ->
- {ok,Sock};
- Other ->
- test_server:format("recv on sock ~w: ~p~n",
- [Sock,Other]),
- error
- end;
- error ->
- error
+ {ok,Sock} ->
+ case recv(Sock,4) of
+ {ok, [?EPMD_ALIVE2_RESP,_Res=0,_C0,_C1]} ->
+ {ok,Sock};
+ Other ->
+ io:format("recv on sock ~w: ~p~n", [Sock,Other]),
+ error
+ end;
+ error ->
+ error
end.
% Internal function to fetch information about a node
port_please_v2(Name) ->
case send_req([?EPMD_PORT_PLEASE2_REQ,
- binary_to_list(unicode:characters_to_binary(Name))]) of
- {ok,Sock} ->
- case recv_until_sock_closes(Sock) of
- {ok, Resp} ->
- parse_port2_resp(Resp);
- Other ->
- test_server:format("recv on sock ~w: ~p~n",
- [Sock,Other]),
- error
- end;
- error ->
- error
+ binary_to_list(unicode:characters_to_binary(Name))]) of
+ {ok,Sock} ->
+ case recv_until_sock_closes(Sock) of
+ {ok, Resp} ->
+ parse_port2_resp(Resp);
+ Other ->
+ io:format("recv on sock ~w: ~p~n", [Sock,Other]),
+ error
+ end;
+ error ->
+ error
end.
parse_port2_resp(Resp) ->
case list_to_binary(Resp) of
- <<?EPMD_PORT2_RESP,Res,Port:16,NodeType,Prot,HVsn:16,LVsn:16,
- NLen:16,NodeName:NLen/binary,
- ELen:16,Extra:ELen/binary>> when Res =:= 0 ->
- {ok, #node_info{port=Port,node_type=NodeType,prot=Prot,
- hvsn=HVsn,lvsn=LVsn,
- node_name=unicode:characters_to_list(NodeName),
- extra=binary_to_list(Extra)}};
- _Other ->
- test_server:format("invalid port2 resp: ~p~n",
- [Resp]),
- error
+ <<?EPMD_PORT2_RESP,Res,Port:16,NodeType,Prot,HVsn:16,LVsn:16,
+ NLen:16,NodeName:NLen/binary,
+ ELen:16,Extra:ELen/binary>> when Res =:= 0 ->
+ {ok, #node_info{port=Port,node_type=NodeType,prot=Prot,
+ hvsn=HVsn,lvsn=LVsn,
+ node_name=unicode:characters_to_list(NodeName),
+ extra=binary_to_list(Extra)}};
+ _Other ->
+ io:format("invalid port2 resp: ~p~n", [Resp]),
+ error
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-name_with_null_inside(doc) ->
- ["Register a name with a null char in it"];
-name_with_null_inside(suite) ->
- [];
+%% Register a name with a null char in it
name_with_null_inside(Config) when is_list(Config) ->
- ?line ok = epmdrun(),
- ?line error = register_node("foo\000bar"),
+ ok = epmdrun(),
+ error = register_node("foo\000bar"),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-name_null_terminated(doc) ->
- ["Register a name with terminating null byte"];
-name_null_terminated(suite) ->
- [];
+%% Register a name with terminating null byte
name_null_terminated(Config) when is_list(Config) ->
- ?line ok = epmdrun(),
- ?line error = register_node("foobar\000"),
+ ok = epmdrun(),
+ error = register_node("foobar\000"),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-stupid_names_req(doc) ->
- ["Read names from epmd in a stupid way"];
-stupid_names_req(suite) ->
- [];
+%% Read names from epmd in a stupid way
stupid_names_req(Config) when is_list(Config) ->
- Dog = ?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
- LongDog = test_server:timetrap(?MEDIUM_TEST_TIMEOUT),
- ?line ok = epmdrun(),
- ?line [FirstConn | Conn] = register_many(1, ?REG_REPEAT_LIM, "foo"),
- ?line unregister_many([FirstConn]),
+ ok = epmdrun(),
+ [FirstConn | Conn] = register_many(1, ?REG_REPEAT_LIM, "foo"),
+ unregister_many([FirstConn]),
sleep(?MEDIUM_PAUSE),
- ?line ok = check_names(Conn),
- ?line ok = unregister_many(Conn),
- test_server:timetrap_cancel(LongDog),
+ ok = check_names(Conn),
+ ok = unregister_many(Conn),
ok.
check_names(Conn) ->
- ?line {ok,Sock} = connect_active(),
- ?line {ok,Reply} = do_get_names(Sock),
- ?line SortConn = lists:sort(Conn),
- ?line SortReply = lists:sort(Reply),
- ?line ok = check_names_cmp(SortConn, SortReply),
+ {ok,Sock} = connect_active(),
+ {ok,Reply} = do_get_names(Sock),
+ SortConn = lists:sort(Conn),
+ SortReply = lists:sort(Reply),
+ ok = check_names_cmp(SortConn, SortReply),
ok.
-
+
% Compare if the result was the same as was registered
@@ -381,43 +324,43 @@ check_names_cmp([{Name,Port,_Sock} | Conn], [{Name,Port} | Reply]) ->
-define(int16(X), [(X bsr 8) band 16#ff, X band 16#ff]).
-define(u32(X1,X2,X3,X4),
- (((X1) bsl 24) bor ((X2) bsl 16) bor ((X3) bsl 8) bor X4)).
+ (((X1) bsl 24) bor ((X2) bsl 16) bor ((X3) bsl 8) bor X4)).
do_get_names(Socket) ->
inet_tcp:send(Socket, [?int16(1),?EPMD_NAMES_REQ]),
receive
- {tcp, Socket, [P0,P1,P2,P3 | T]} ->
- EpmdPort = ?u32(P0,P1,P2,P3),
- if EpmdPort == ?PORT ->
- names_loop(Socket, T, []);
- true ->
- close(Socket),
- {error, address}
- end;
- {tcp_closed, Socket} ->
- {ok, []}
+ {tcp, Socket, [P0,P1,P2,P3 | T]} ->
+ EpmdPort = ?u32(P0,P1,P2,P3),
+ if EpmdPort == ?PORT ->
+ names_loop(Socket, T, []);
+ true ->
+ close(Socket),
+ {error, address}
+ end;
+ {tcp_closed, Socket} ->
+ {ok, []}
end.
names_loop(Socket, Acc, Ps) ->
receive
- {tcp, Socket, Bytes} ->
- {NAcc, NPs} = scan_names(Acc ++ Bytes, Ps),
- names_loop(Socket, NAcc, NPs);
- {tcp_closed, Socket} ->
- {_, NPs} = scan_names(Acc, Ps), % Really needed?
- {ok, NPs}
+ {tcp, Socket, Bytes} ->
+ {NAcc, NPs} = scan_names(Acc ++ Bytes, Ps),
+ names_loop(Socket, NAcc, NPs);
+ {tcp_closed, Socket} ->
+ {_, NPs} = scan_names(Acc, Ps), % Really needed?
+ {ok, NPs}
end.
scan_names(Buf, Ps) ->
case scan_line(Buf, []) of
- {Line, NBuf} ->
- case parse_line(Line) of
- {ok, Entry} ->
- scan_names(NBuf, [Entry | Ps]);
- error ->
- scan_names(NBuf, Ps)
- end;
- [] -> {Buf, Ps}
+ {Line, NBuf} ->
+ case parse_line(Line) of
+ {ok, Entry} ->
+ scan_names(NBuf, [Entry | Ps]);
+ error ->
+ scan_names(NBuf, Ps)
+ end;
+ [] -> {Buf, Ps}
end.
scan_line([$\n | Buf], Line) -> {lists:reverse(Line), Buf};
@@ -426,16 +369,16 @@ scan_line([], _) -> [].
parse_line([$n,$a,$m,$e,$ | Buf0]) ->
case parse_name(Buf0, []) of
- {Name, Buf1} ->
- case Buf1 of
- [$a,$t,$ ,$p,$o,$r,$t,$ | Buf2] ->
- case catch list_to_integer(Buf2) of
- {'EXIT', _} -> error;
- Port -> {ok, {Name, Port}}
- end;
- _ -> error
- end;
- error -> error
+ {Name, Buf1} ->
+ case Buf1 of
+ [$a,$t,$ ,$p,$o,$r,$t,$ | Buf2] ->
+ case catch list_to_integer(Buf2) of
+ {'EXIT', _} -> error;
+ Port -> {ok, {Name, Port}}
+ end;
+ _ -> error
+ end;
+ error -> error
end;
parse_line(_) -> error.
@@ -447,17 +390,11 @@ parse_name([], _Name) -> error.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-get_port_nr(doc) ->
- ["Register a name on a port and ask about port nr"];
-get_port_nr(suite) ->
- [];
+%% Register a name on a port and ask about port nr
get_port_nr(Config) when is_list(Config) ->
port_request([?EPMD_PORT_PLEASE2_REQ,"foo"]).
-slow_get_port_nr(doc) ->
- ["Register with slow write and ask about port nr"];
-slow_get_port_nr(suite) ->
- [];
+%% Register with slow write and ask about port nr
slow_get_port_nr(Config) when is_list(Config) ->
port_request([?EPMD_PORT_PLEASE2_REQ,d,$f,d,$o,d,$o]).
@@ -465,142 +402,127 @@ slow_get_port_nr(Config) when is_list(Config) ->
% Internal function used above
port_request(M) ->
- ?line ok = epmdrun(),
+ ok = epmdrun(),
Port = 1042,
- ?line {ok,RSock} = register_node("foo", Port),
- ?line {ok,Sock} = connect(),
- ?line ok = send(Sock,[size16(M),M]),
- ?line case recv_until_sock_closes(Sock) of
- {ok, Resp} ->
- ?line close(RSock),
- ?line {ok,Rec} = parse_port2_resp(Resp),
- ?line Port = Rec#node_info.port,
- ok;
- Other ->
- ?line close(RSock),
- ?line test_server:format("recv on sock ~w: ~p~n",
- [Sock,Other]),
- ?line throw({error,Other})
- end,
+ {ok,RSock} = register_node("foo", Port),
+ {ok,Sock} = connect(),
+ ok = send(Sock,[size16(M),M]),
+ case recv_until_sock_closes(Sock) of
+ {ok, Resp} ->
+ close(RSock),
+ {ok,Rec} = parse_port2_resp(Resp),
+ Port = Rec#node_info.port,
+ ok;
+ Other ->
+ close(RSock),
+ io:format("recv on sock ~w: ~p~n", [Sock,Other]),
+ throw({error,Other})
+ end,
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-unregister_others_name_1(doc) ->
- ["Unregister name of other node"];
-unregister_others_name_1(suite) ->
- [];
+%% Unregister name of other node
unregister_others_name_1(Config) when is_list(Config) ->
- ?line ok = epmdrun("-relaxed_command_check"),
- ?line {ok,RSock} = register_node("foo"),
- ?line {ok,Sock} = connect(),
+ ok = epmdrun("-relaxed_command_check"),
+ {ok,RSock} = register_node("foo"),
+ {ok,Sock} = connect(),
M = [?EPMD_STOP_REQ,"foo"],
- ?line ok = send(Sock,[size16(M),M]),
+ ok = send(Sock,[size16(M),M]),
R = "STOPPED",
- ?line {ok,R} = recv(Sock,length(R)),
- ?line ok = close(RSock),
+ {ok,R} = recv(Sock,length(R)),
+ ok = close(RSock),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-unregister_others_name_2(doc) ->
- ["Unregister name of other node"];
-unregister_others_name_2(suite) ->
- [];
+%% Unregister name of other node
unregister_others_name_2(Config) when is_list(Config) ->
- ?line ok = epmdrun("-relaxed_command_check"),
- ?line {ok,Sock} = connect(),
+ ok = epmdrun("-relaxed_command_check"),
+ {ok,Sock} = connect(),
M = [?EPMD_STOP_REQ,"xxx42"],
- ?line ok = send(Sock,[size16(M),M]),
+ ok = send(Sock,[size16(M),M]),
R = "NOEXIST",
- ?line {ok,R} = recv(Sock,length(R)),
+ {ok,R} = recv(Sock,length(R)),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-register_overflow(doc) ->
- ["Register too many, clean and redo 10 times"];
-register_overflow(suite) ->
- [];
+%% Register too many, clean and redo 10 times
register_overflow(Config) when is_list(Config) ->
- Dog = ?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
- LongDog = test_server:timetrap(?LONG_TEST_TIMEOUT),
- ?line ok = epmdrun(),
- ?line Conn = register_many(1, ?REG_REPEAT_LIM, "foo"),
+ ct:timetrap(?LONG_TEST_TIMEOUT),
+ ok = epmdrun(),
+ Conn = register_many(1, ?REG_REPEAT_LIM, "foo"),
Count = length(Conn),
- ?line ok = unregister_many(Conn),
+ ok = unregister_many(Conn),
sleep(?MEDIUM_PAUSE),
- test_server:format("Limit was ~w names, now reg/unreg all 10 times~n",
- [Count]),
- ?line ok = register_repeat(Count),
+ io:format("Limit was ~w names, now reg/unreg all 10 times~n", [Count]),
+ ok = register_repeat(Count),
sleep(?MEDIUM_PAUSE),
- ?line ok = rregister_repeat(Count),
+ ok = rregister_repeat(Count),
sleep(?MEDIUM_PAUSE),
- ?line ok = register_repeat(Count),
+ ok = register_repeat(Count),
sleep(?MEDIUM_PAUSE),
- ?line ok = rregister_repeat(Count),
+ ok = rregister_repeat(Count),
sleep(?MEDIUM_PAUSE),
- ?line ok = register_repeat(Count),
+ ok = register_repeat(Count),
sleep(?MEDIUM_PAUSE),
- ?line ok = rregister_repeat(Count),
+ ok = rregister_repeat(Count),
sleep(?MEDIUM_PAUSE),
- ?line ok = register_repeat(Count),
+ ok = register_repeat(Count),
sleep(?MEDIUM_PAUSE),
- ?line ok = rregister_repeat(Count),
+ ok = rregister_repeat(Count),
sleep(?MEDIUM_PAUSE),
- ?line ok = register_repeat(Count),
+ ok = register_repeat(Count),
sleep(?MEDIUM_PAUSE),
- ?line ok = rregister_repeat(Count),
- test_server:timetrap_cancel(LongDog),
+ ok = rregister_repeat(Count),
ok.
register_repeat(Count) ->
Conn = register_many(1, ?REG_REPEAT_LIM, "foo"),
ok = unregister_many(Conn),
if
- length(Conn) == Count ->
- ok;
- true ->
- error
+ length(Conn) == Count ->
+ ok;
+ true ->
+ error
end.
rregister_repeat(Count) ->
Conn = register_many(1, ?REG_REPEAT_LIM, "foo"),
ok = unregister_many(lists:reverse(Conn)),
if
- length(Conn) == Count ->
- ok;
- true ->
- error
+ length(Conn) == Count ->
+ ok;
+ true ->
+ error
end.
% Return count of successful registrations
register_many(I, N, _Prefix) when I > N ->
- test_server:format("Done with all ~n", []),
+ io:format("Done with all ~n", []),
[];
register_many(I, N, Prefix) ->
Name = gen_name(Prefix, I),
Port = ?DUMMY_PORT + I, % Just make it up
case register_node(Name, Port) of
- {ok,Sock} ->
- [{Name,Port,Sock} | register_many(I + 1, N, Prefix)];
- Any ->
- test_server:format("Can't register: ~w of 1..~w ~w~n",
- [Name,N,Any]),
- []
+ {ok,Sock} ->
+ [{Name,Port,Sock} | register_many(I + 1, N, Prefix)];
+ Any ->
+ test_server:format("Can't register: ~w of 1..~w ~w~n", [Name,N,Any]),
+ []
end.
unregister_many([]) ->
ok;
unregister_many([{Name,_Port,Sock} | Socks]) ->
case close(Sock) of
- ok ->
- unregister_many(Socks);
- Any ->
- test_server:format("Can't unregister: ~w reason ~w~n", [Name,Any]),
- error
+ ok ->
+ unregister_many(Socks);
+ Any ->
+ test_server:format("Can't unregister: ~w reason ~w~n", [Name,Any]),
+ error
end.
gen_name(Str,Int) ->
@@ -608,246 +530,203 @@ gen_name(Str,Int) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-no_data(doc) ->
- ["Open but send no data"];
-no_data(suite) ->
- [];
+%% Open but send no data
no_data(Config) when is_list(Config) ->
- ?line ok = epmdrun(),
- ?line {ok,Sock} = connect(),
+ ok = epmdrun(),
+ {ok,Sock} = connect(),
sleep(?LONG_PAUSE),
- ?line closed = recv(Sock,1),
+ closed = recv(Sock,1),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-one_byte(doc) ->
- ["Send one byte only"];
-one_byte(suite) ->
- [];
+%% Send one byte only
one_byte(Config) when is_list(Config) ->
- ?line ok = epmdrun(),
- ?line {ok,Sock} = connect(),
- ?line ok = send(Sock,[0]),
+ ok = epmdrun(),
+ {ok,Sock} = connect(),
+ ok = send(Sock,[0]),
sleep(?LONG_PAUSE),
- ?line closed = recv(Sock,1),
+ closed = recv(Sock,1),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-two_bytes(doc) ->
- ["Send packet size only"];
-two_bytes(suite) ->
- [];
+%% Send packet size only
two_bytes(Config) when is_list(Config) ->
- ?line ok = epmdrun(),
- ?line {ok,Sock} = connect(),
- ?line ok = send(Sock,[put16(3)]),
+ ok = epmdrun(),
+ {ok,Sock} = connect(),
+ ok = send(Sock,[put16(3)]),
sleep(?LONG_PAUSE),
- ?line closed = recv(Sock,1),
+ closed = recv(Sock,1),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-partial_packet(doc) ->
- ["Got only part of a packet"];
-partial_packet(suite) ->
- [];
+%% Got only part of a packet
partial_packet(Config) when is_list(Config) ->
- ?line ok = epmdrun(),
- ?line {ok,Sock} = connect(),
- ?line ok = send(Sock,[put16(100),"only a few bytes"]),
+ ok = epmdrun(),
+ {ok,Sock} = connect(),
+ ok = send(Sock,[put16(100),"only a few bytes"]),
sleep(?LONG_PAUSE),
- ?line closed = recv(Sock,1),
+ closed = recv(Sock,1),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-zero_length(doc) ->
- ["Invalid zero packet size"];
-zero_length(suite) ->
- [];
+%% Invalid zero packet size
zero_length(Config) when is_list(Config) ->
- ?line ok = epmdrun(),
- ?line {ok,Sock} = connect(),
- ?line ok = send(Sock,[0,0,0,0,0,0,0,0,0,0]),
+ ok = epmdrun(),
+ {ok,Sock} = connect(),
+ ok = send(Sock,[0,0,0,0,0,0,0,0,0,0]),
sleep(?MEDIUM_PAUSE),
- ?line closed = recv(Sock,1),
+ closed = recv(Sock,1),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-too_large(doc) ->
- ["Invalid large packet"];
-too_large(suite) ->
- [];
+%% Invalid large packet
too_large(Config) when is_list(Config) ->
- ?line ok = epmdrun(),
- ?line {ok,Sock} = connect(),
+ ok = epmdrun(),
+ {ok,Sock} = connect(),
Size = 63000,
M = lists:duplicate(Size, $z),
- ?line ok = send(Sock,[put16(Size),M]),
+ ok = send(Sock,[put16(Size),M]),
sleep(?MEDIUM_PAUSE),
% With such a large packet, even the writes can fail as the
% daemon closes before everything is delivered -> econnaborted
case recv(Sock,1) of
- closed -> ok;
- {error,econnaborted} -> ok;
- Other -> exit({unexpected,Other})
+ closed -> ok;
+ {error,econnaborted} -> ok;
+ Other -> exit({unexpected,Other})
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-alive_req_too_small_1(doc) ->
- ["Try to register but not enough data"];
-alive_req_too_small_1(suite) ->
- [];
+%% Try to register but not enough data
alive_req_too_small_1(Config) when is_list(Config) ->
- ?line ok = epmdrun(),
- ?line {ok,Sock} = connect(),
+ ok = epmdrun(),
+ {ok,Sock} = connect(),
M = [?EPMD_ALIVE2_REQ, put16(?DUMMY_PORT),$M,0, put16(5),
- put16(5),put16(0)],
- ?line ok = send(Sock, [size16(M), M]),
+ put16(5),put16(0)],
+ ok = send(Sock, [size16(M), M]),
sleep(?MEDIUM_PAUSE),
- ?line closed = recv(Sock,1),
+ closed = recv(Sock,1),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-alive_req_too_small_2(doc) ->
- ["Try to register but not enough data"];
-alive_req_too_small_2(suite) ->
- [];
+%% Try to register but not enough data
alive_req_too_small_2(Config) when is_list(Config) ->
- ?line ok = epmdrun(),
- ?line {ok,Sock} = connect(),
+ ok = epmdrun(),
+ {ok,Sock} = connect(),
M = [?EPMD_ALIVE2_REQ, put16(?DUMMY_PORT),$M,0, put16(5),
- put16(5)],
- ?line ok = send(Sock, [size16(M), M]),
+ put16(5)],
+ ok = send(Sock, [size16(M), M]),
sleep(?MEDIUM_PAUSE),
- ?line closed = recv(Sock,1),
+ closed = recv(Sock,1),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-alive_req_too_large(doc) ->
- ["Try to register but node name too large"];
-alive_req_too_large(suite) ->
- [];
+%% Try to register but node name too large
alive_req_too_large(Config) when is_list(Config) ->
- ?line ok = epmdrun(),
- ?line {ok,Sock} = connect(),
- L = [
- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
- ],
+ ok = epmdrun(),
+ {ok,Sock} = connect(),
+ L = ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],
S = length(lists:flatten(L)),
M = [?EPMD_ALIVE2_REQ, put16(?DUMMY_PORT),$M,0, put16(5),
- put16(5), put16(S),L,put16(0)],
- ?line ok = send(Sock, [size16(M), M]),
+ put16(5), put16(S),L,put16(0)],
+ ok = send(Sock, [size16(M), M]),
sleep(?MEDIUM_PAUSE),
- ?line {ok,[?EPMD_ALIVE2_RESP,1]} = recv(Sock,2),
+ {ok,[?EPMD_ALIVE2_RESP,1]} = recv(Sock,2),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-returns_valid_empty_extra(doc) ->
- ["Check that an empty extra is prefixed by a two byte length"];
-returns_valid_empty_extra(suite) ->
- [];
+%% Check that an empty extra is prefixed by a two byte length
returns_valid_empty_extra(Config) when is_list(Config) ->
- ?line ok = epmdrun(),
- ?line {ok,Sock} = register_node_v2(4711, 72, 0, 5, 5, "foo", []),
- ?line {ok,#node_info{extra=[]}} = port_please_v2("foo"),
- ?line ok = close(Sock),
+ ok = epmdrun(),
+ {ok,Sock} = register_node_v2(4711, 72, 0, 5, 5, "foo", []),
+ {ok,#node_info{extra=[]}} = port_please_v2("foo"),
+ ok = close(Sock),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-returns_valid_populated_extra_with_nulls(doc) ->
- ["Check a populated extra with embedded null characters"];
-returns_valid_populated_extra_with_nulls(suite) ->
- [];
+%% Check a populated extra with embedded null characters
returns_valid_populated_extra_with_nulls(Config) when is_list(Config) ->
- ?line ok = epmdrun(),
- ?line {ok,Sock} = register_node_v2(4711, 72, 0, 5, 5, "foo", "ABC\000\000"),
- ?line {ok,#node_info{extra="ABC\000\000"}} = port_please_v2("foo"),
- ?line ok = close(Sock),
+ ok = epmdrun(),
+ {ok,Sock} = register_node_v2(4711, 72, 0, 5, 5, "foo", "ABC\000\000"),
+ {ok,#node_info{extra="ABC\000\000"}} = port_please_v2("foo"),
+ ok = close(Sock),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-names_stdout(doc) ->
- ["Test that epmd -names prints registered nodes to stdout"];
-names_stdout(suite) ->
- [];
+%% Test that epmd -names prints registered nodes to stdout
names_stdout(Config) when is_list(Config) ->
- ?line ok = epmdrun(),
- ?line {ok,Sock} = register_node("foobar"),
- ?line ok = epmdrun("-names"),
- ?line {ok, Data} = receive {_Port, {data, D}} -> {ok, D}
- after 10000 -> {error, timeout}
- end,
- ?line {match,_} = re:run(Data, "^epmd: up and running", [multiline]),
- ?line {match,_} = re:run(Data, "^name foobar at port", [multiline]),
- ?line ok = close(Sock),
+ ok = epmdrun(),
+ {ok,Sock} = register_node("foobar"),
+ ok = epmdrun("-names"),
+ {ok, Data} = receive {_Port, {data, D}} -> {ok, D}
+ after 10000 -> {error, timeout}
+ end,
+ {match,_} = re:run(Data, "^epmd: up and running", [multiline]),
+ {match,_} = re:run(Data, "^name foobar at port", [multiline]),
+ ok = close(Sock),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-buffer_overrun_1(suite) ->
- [];
-buffer_overrun_1(doc) ->
- ["Test security vulnerability in fake extra lengths in alive2_req"];
+%% Test security vulnerability in fake extra lengths in alive2_req
buffer_overrun_1(Config) when is_list(Config) ->
- ?line ok = epmdrun(),
- ?line true = alltrue([hostile(N) || N <- lists:seq(1,10000)]),
+ ok = epmdrun(),
+ true = alltrue([hostile(N) || N <- lists:seq(1,10000)]),
ok.
-buffer_overrun_2(suite) ->
- [];
-buffer_overrun_2(doc) ->
- ["Test security vulnerability in fake extra lengths in alive2_req"];
+
+%% Test security vulnerability in fake extra lengths in alive2_req
buffer_overrun_2(Config) when is_list(Config) ->
- ?line ok = epmdrun(),
- ?line [false | Rest] = [hostile2(N) || N <- lists:seq(255*4,10000)],
- ?line true = alltrue(Rest),
+ ok = epmdrun(),
+ [false | Rest] = [hostile2(N) || N <- lists:seq(255*4,10000)],
+ true = alltrue(Rest),
ok.
hostile(N) ->
try
- Bin= <<$x:8,4747:16,$M:8,0:8,5:16,5:16,5:16,"gurka",N:16>>,
- S = size(Bin),
- {ok,E}=connect_sturdy(),
- gen_tcp:send(E,[<<S:16>>,Bin]),
- closed = recv(E,1),
- gen_tcp:close(E),
- true
+ Bin= <<$x:8,4747:16,$M:8,0:8,5:16,5:16,5:16,"gurka",N:16>>,
+ S = size(Bin),
+ {ok,E}=connect_sturdy(),
+ gen_tcp:send(E,[<<S:16>>,Bin]),
+ closed = recv(E,1),
+ gen_tcp:close(E),
+ true
catch
- _:_ ->
- false
+ _:_ ->
+ false
end.
hostile2(N) ->
try
- B2 = list_to_binary(lists:duplicate(N,255)),
- Bin= <<$x:8,4747:16,$M:8,0:8,5:16,5:16,5:16,"gurka",N:16,B2/binary>>,
- S = size(Bin),
- {ok,E}=connect_sturdy(),
- gen_tcp:send(E,[<<S:16>>,Bin]),
- Z = recv(E,2),
- gen_tcp:close(E),
- (Z =:= closed) or (Z =:= {ok, [$y,1]})
+ B2 = list_to_binary(lists:duplicate(N,255)),
+ Bin= <<$x:8,4747:16,$M:8,0:8,5:16,5:16,5:16,"gurka",N:16,B2/binary>>,
+ S = size(Bin),
+ {ok,E}=connect_sturdy(),
+ gen_tcp:send(E,[<<S:16>>,Bin]),
+ Z = recv(E,2),
+ gen_tcp:close(E),
+ (Z =:= closed) or (Z =:= {ok, [$y,1]})
catch
- _A:_B ->
- false
+ _A:_B ->
+ false
end.
alltrue([]) ->
@@ -857,134 +736,124 @@ alltrue([true|T]) ->
alltrue([_|_]) ->
false.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-no_nonlocal_register(suite) ->
- [];
-no_nonlocal_register(doc) ->
- ["Ensure that we cannot register throug a nonlocal connection"];
+
+%% Ensure that we cannot register throug a nonlocal connection
no_nonlocal_register(Config) when is_list(Config) ->
- ?line case {os:find_executable("ssh"),ct:get_config(ssh_proxy_host)} of
- {SSH,Name} when is_list(Name), is_list(SSH) ->
- do_no_nonlocal_register(Config,Name);
- {false,_} ->
- {skip, "No ssh command found to create proxy"};
- _ ->
- {skip, "No ssh_proxy_host configured in ts.config"}
- end.
+ case {os:find_executable("ssh"),ct:get_config(ssh_proxy_host)} of
+ {SSH,Name} when is_list(Name), is_list(SSH) ->
+ do_no_nonlocal_register(Config,Name);
+ {false,_} ->
+ {skip, "No ssh command found to create proxy"};
+ _ ->
+ {skip, "No ssh_proxy_host configured in ts.config"}
+ end.
do_no_nonlocal_register(Config,SSHHost) when is_list(Config) ->
- ?line ok = epmdrun(),
- ?line ProxyPort = proxy_port(),
- ?line ok = ssh_proxy(SSHHost,ProxyPort),
+ ok = epmdrun(),
+ ProxyPort = proxy_port(),
+ ok = ssh_proxy(SSHHost,ProxyPort),
Res = try
- ?line Name = "gurka_"
- %++
- %integer_to_list(A1)++"_"++
- %integer_to_list(A2)++"_"++
- %integer_to_list(A3)++"_"++
- %integer_to_list(A4)
- ,
- ?line Bname = list_to_binary(Name),
- ?line NameS = byte_size(Bname),
- ?line Bin= <<$x:8,4747:16,$M:8,0:8,5:16,
- 5:16,NameS:16,Bname/binary,
- 0:16>>,
- ?line S = size(Bin),
- ?line {ok, E} = connect("localhost",ProxyPort,passive),
- ?line gen_tcp:send(E,[<<S:16>>,Bin]),
- ?line closed = recv(E,1),
- ?line gen_tcp:close(E),
- true
- catch
- _:_ ->
- false
- end,
+ Name = "gurka_"
+ %++
+ %integer_to_list(A1)++"_"++
+ %integer_to_list(A2)++"_"++
+ %integer_to_list(A3)++"_"++
+ %integer_to_list(A4)
+ ,
+ Bname = list_to_binary(Name),
+ NameS = byte_size(Bname),
+ Bin= <<$x:8,4747:16,$M:8,0:8,5:16,
+ 5:16,NameS:16,Bname/binary,
+ 0:16>>,
+ S = size(Bin),
+ {ok, E} = connect("localhost",ProxyPort,passive),
+ gen_tcp:send(E,[<<S:16>>,Bin]),
+ closed = recv(E,1),
+ gen_tcp:close(E),
+ true
+ catch
+ _:_ ->
+ false
+ end,
%erlang:display(Res),
true = Res,
ok.
-no_nonlocal_kill(suite) ->
- [];
-no_nonlocal_kill(doc) ->
- ["Ensure that we cannot kill through nonlocal connection"];
+%% Ensure that we cannot kill through nonlocal connection
no_nonlocal_kill(Config) when is_list(Config) ->
- ?line case {os:find_executable("ssh"),ct:get_config(ssh_proxy_host)} of
- {SSH,Name} when is_list(Name), is_list(SSH) ->
- do_no_nonlocal_kill(Config,Name);
- {false,_} ->
- {skip, "No ssh command found to create proxy"};
- _ ->
- {skip, "No ssh_proxy_host configured in ts.config"}
- end.
+ case {os:find_executable("ssh"),ct:get_config(ssh_proxy_host)} of
+ {SSH,Name} when is_list(Name), is_list(SSH) ->
+ do_no_nonlocal_kill(Config,Name);
+ {false,_} ->
+ {skip, "No ssh command found to create proxy"};
+ _ ->
+ {skip, "No ssh_proxy_host configured in ts.config"}
+ end.
do_no_nonlocal_kill(Config,SSHHost) when is_list(Config) ->
- ?line ok = epmdrun(),
- ?line ProxyPort = proxy_port(),
- ?line ok = ssh_proxy(SSHHost,ProxyPort),
+ ok = epmdrun(),
+ ProxyPort = proxy_port(),
+ ok = ssh_proxy(SSHHost,ProxyPort),
Res = try
- {ok, E} = connect("localhost",ProxyPort,passive),
- M = [?EPMD_KILL_REQ],
- send(E, [size16(M), M]),
- closed = recv(E,2),
- gen_tcp:close(E),
- sleep(?MEDIUM_PAUSE),
- {ok, E2} = connect("localhost",ProxyPort,passive),
- gen_tcp:close(E2),
- true
- catch
- _:_ ->
- false
- end,
+ {ok, E} = connect("localhost",ProxyPort,passive),
+ M = [?EPMD_KILL_REQ],
+ send(E, [size16(M), M]),
+ closed = recv(E,2),
+ gen_tcp:close(E),
+ sleep(?MEDIUM_PAUSE),
+ {ok, E2} = connect("localhost",ProxyPort,passive),
+ gen_tcp:close(E2),
+ true
+ catch
+ _:_ ->
+ false
+ end,
%erlang:display(Res),
true = Res,
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-no_live_killing(doc) ->
- ["Dont allow killing with live nodes or any unregistering w/o -relaxed_command_check"];
-no_live_killing(suite) ->
- [];
+
+%% Dont allow killing with live nodes or any unregistering w/o -relaxed_command_check
no_live_killing(Config) when is_list(Config) ->
- ?line ok = epmdrun(),
- ?line {ok,RSock} = register_node("foo"),
- ?line {ok,Sock} = connect(),
- ?line M = [?EPMD_KILL_REQ],
- ?line ok = send(Sock,[size16(M),M]),
- ?line {ok,"NO"} = recv(Sock,2),
- ?line close(Sock),
- ?line {ok,Sock2} = connect(),
- ?line M2 = [?EPMD_STOP_REQ,"foo"],
- ?line ok = send(Sock2,[size16(M2),M2]),
- ?line closed = recv(Sock2,1),
- ?line close(Sock2),
- ?line close(RSock),
- ?line sleep(?MEDIUM_PAUSE),
- ?line {ok,Sock3} = connect(),
- ?line M3 = [?EPMD_KILL_REQ],
- ?line ok = send(Sock3,[size16(M3),M3]),
- ?line {ok,"OK"} = recv(Sock3,2),
- ?line close(Sock3),
+ ok = epmdrun(),
+ {ok,RSock} = register_node("foo"),
+ {ok,Sock} = connect(),
+ M = [?EPMD_KILL_REQ],
+ ok = send(Sock,[size16(M),M]),
+ {ok,"NO"} = recv(Sock,2),
+ close(Sock),
+ {ok,Sock2} = connect(),
+ M2 = [?EPMD_STOP_REQ,"foo"],
+ ok = send(Sock2,[size16(M2),M2]),
+ closed = recv(Sock2,1),
+ close(Sock2),
+ close(RSock),
+ sleep(?MEDIUM_PAUSE),
+ {ok,Sock3} = connect(),
+ M3 = [?EPMD_KILL_REQ],
+ ok = send(Sock3,[size16(M3),M3]),
+ {ok,"OK"} = recv(Sock3,2),
+ close(Sock3),
ok.
-socket_reset_before_alive2_reply_is_written(doc) ->
- ["Check for regression - don't make zombie from node which "
- "sends TCP RST at wrong time"];
-socket_reset_before_alive2_reply_is_written(suite) ->
- [];
+%% Check for regression - don't make zombie from node which
+%% sends TCP RST at wrong time
socket_reset_before_alive2_reply_is_written(Config) when is_list(Config) ->
%% - delay_write for easier triggering of race condition
%% - relaxed_command_check for gracefull shutdown of epmd even if there
%% is stuck node.
- ?line ok = epmdrun("-delay_write 1 -relaxed_command_check"),
+ ok = epmdrun("-delay_write 1 -relaxed_command_check"),
%% We can't use send_req/1 directly as we want to do inet:setopts/2
%% on our socket.
- ?line {ok, Sock} = connect(),
+ {ok, Sock} = connect(),
%% Issuing close/1 on such socket will result in immediate RST packet.
- ?line ok = inet:setopts(Sock, [{linger, {true, 0}}]),
+ ok = inet:setopts(Sock, [{linger, {true, 0}}]),
Req = alive2_req(4711, 77, 0, 5, 5, "test", []),
- ?line ok = send(Sock, [size16(Req), Req]),
+ ok = send(Sock, [size16(Req), Req]),
timer:sleep(500), %% Wait for the first 1/2 of delay_write before closing
- ?line ok = close(Sock),
+ ok = close(Sock),
timer:sleep(500 + ?SHORT_PAUSE), %% Wait for the other 1/2 of delay_write
@@ -992,11 +861,11 @@ socket_reset_before_alive2_reply_is_written(Config) when is_list(Config) ->
%% Should be removed when this is issue is fixed there.
timer:sleep(1000),
- ?line {ok, SockForNames} = connect_active(),
+ {ok, SockForNames} = connect_active(),
%% And there should be no stuck nodes
- ?line {ok, []} = do_get_names(SockForNames),
- ?line ok = close(SockForNames),
+ {ok, []} = do_get_names(SockForNames),
+ ok = close(SockForNames),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1005,14 +874,14 @@ socket_reset_before_alive2_reply_is_written(Config) when is_list(Config) ->
cleanup() ->
sleep(?MEDIUM_PAUSE),
case connect() of
- {ok,Sock} ->
- M = [?EPMD_KILL_REQ],
- send(Sock, [size16(M), M]),
- recv(Sock,length("OK")),
- close(Sock),
- sleep(?MEDIUM_PAUSE);
- _ ->
- true
+ {ok,Sock} ->
+ M = [?EPMD_KILL_REQ],
+ send(Sock, [size16(M), M]),
+ recv(Sock,length("OK")),
+ close(Sock),
+ sleep(?MEDIUM_PAUSE);
+ _ ->
+ true
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1022,11 +891,11 @@ proxy_port() ->
?PORT+1.
ssh_proxy(SSHHost,ProxyPort) ->
- ?line Host = lists:nth(2,string:tokens(atom_to_list(node()),"@")),
+ Host = lists:nth(2,string:tokens(atom_to_list(node()),"@")),
% Requires proxy to be a unix host with the command 'read' accessible
- ?line osrun("ssh -L "++integer_to_list(ProxyPort)++":"++Host++":"
- ++integer_to_list(?PORT)++" "++SSHHost++" read").
-
+ osrun("ssh -L "++integer_to_list(ProxyPort)++":"++Host++":"
+ ++integer_to_list(?PORT)++" "++SSHHost++" read").
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1036,21 +905,21 @@ epmdrun() ->
epmdrun([]).
epmdrun(Args) ->
case os:find_executable(epmd) of
- false ->
- {error, {could_not_find_epmd_in_path}};
- Path ->
- epmdrun(Path,Args)
+ false ->
+ {error, {could_not_find_epmd_in_path}};
+ Path ->
+ epmdrun(Path,Args)
end.
epmdrun(Epmd,Args0) ->
- %% test_server:format("epmdrun() => Epmd = ~p",[Epmd]),
+ %% test_server:format("epmdrun() => Epmd = ~p",[Epmd]),
Args = case Args0 of
- [] ->
- [];
- O ->
- " "++O
- end,
- osrun("\"" ++ Epmd ++ "\"" ++ " " ?EPMDARGS " -port " ++ integer_to_list(?PORT) ++ Args).
+ [] ->
+ [];
+ O ->
+ " "++O
+ end,
+ osrun("\"" ++ Epmd ++ "\"" ++ " " ?EPMDARGS " -port " ++ integer_to_list(?PORT) ++ Args).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1059,7 +928,7 @@ epmdrun(Epmd,Args0) ->
osrun(Cmd) ->
_ = open_port({spawn, Cmd}, []),
ok.
-
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Wrappers of TCP functions
@@ -1084,16 +953,16 @@ connect(Addr, Port, Mode) ->
connect(Addr, Port, Mode, ?CONN_SLEEP, ?CONN_RETRY).
connect(Addr, Port, Mode, Sleep, Retry) ->
case connect_repeat(Addr, Retry, Port, Mode, Sleep) of
- {ok,Sock} ->
- {ok,Sock};
- {error,timeout} ->
- timeout;
- {error,Reason} ->
- test_server:format("connect: error: ~w~n",[Reason]),
- error;
- Any ->
- test_server:format("connect: unknown message: ~w~n",[Any]),
- exit(1)
+ {ok,Sock} ->
+ {ok,Sock};
+ {error,timeout} ->
+ timeout;
+ {error,Reason} ->
+ test_server:format("connect: error: ~w~n",[Reason]),
+ error;
+ Any ->
+ test_server:format("connect: unknown message: ~w~n",[Any]),
+ exit(1)
end.
@@ -1104,33 +973,33 @@ connect_repeat(Addr, 1, Port, Mode, _Sleep) ->
connect_mode(Addr,Port, Mode);
connect_repeat(Addr,Retry, Port, Mode, Sleep) ->
case connect_mode(Addr,Port, Mode) of
- {ok,Sock} ->
- {ok,Sock};
- {error,Reason} ->
- test_server:format("connect: error: ~w~n",[Reason]),
- timer:sleep(Sleep),
- connect_repeat(Addr, Retry - 1, Port, Mode, Sleep);
- Any ->
- test_server:format("connect: unknown message: ~w~n",[Any]),
- exit(1)
+ {ok,Sock} ->
+ {ok,Sock};
+ {error,Reason} ->
+ test_server:format("connect: error: ~w~n",[Reason]),
+ timer:sleep(Sleep),
+ connect_repeat(Addr, Retry - 1, Port, Mode, Sleep);
+ Any ->
+ test_server:format("connect: unknown message: ~w~n",[Any]),
+ exit(1)
end.
connect_mode(Addr,Port, active) ->
gen_tcp:connect(Addr, Port, [{packet, 0}], ?CONN_TIMEOUT);
connect_mode(Addr, Port, passive) ->
gen_tcp:connect(Addr, Port, [{packet, 0}, {active, false}],
- ?CONN_TIMEOUT).
+ ?CONN_TIMEOUT).
close(Sock) ->
case gen_tcp:close(Sock) of
- {error,_} ->
- error;
- ok ->
- ok;
- Any ->
- test_server:format("unknown message: ~w~n",[Any]),
- exit(1)
+ {error,_} ->
+ error;
+ ok ->
+ ok;
+ Any ->
+ test_server:format("unknown message: ~w~n",[Any]),
+ exit(1)
end.
recv(Sock, Len) ->
@@ -1138,19 +1007,19 @@ recv(Sock, Len) ->
recv(Sock, Len, Timeout) ->
case gen_tcp:recv(Sock, Len, Timeout) of
- {ok,[]} -> % Should not be the case
- recv(Sock, 1, 1); % any longer
- {ok,Data} ->
- {ok,Data};
- {error,timeout} ->
- timeout;
- {error,closed} ->
- closed;
- {error,_}=Error ->
- Error;
- Any ->
- test_server:format("unknown message: ~w~n",[Any]),
- exit(1)
+ {ok,[]} -> % Should not be the case
+ recv(Sock, 1, 1); % any longer
+ {ok,Data} ->
+ {ok,Data};
+ {error,timeout} ->
+ timeout;
+ {error,closed} ->
+ closed;
+ {error,_}=Error ->
+ Error;
+ Any ->
+ test_server:format("unknown message: ~w~n",[Any]),
+ exit(1)
end.
%% Send data to socket. The list can be non flat and contain
@@ -1159,12 +1028,12 @@ recv(Sock, Len, Timeout) ->
send(Sock, SendSpec) ->
case send(SendSpec, [], Sock) of
- {ok,[]} ->
- ok;
- {ok,RevBytes} ->
- send_direct(Sock, lists:reverse(RevBytes));
- Any ->
- Any
+ {ok,[]} ->
+ ok;
+ {ok,RevBytes} ->
+ send_direct(Sock, lists:reverse(RevBytes));
+ Any ->
+ Any
end.
@@ -1179,54 +1048,54 @@ send([Byte | Spec], RevBytes, Sock) when is_integer(Byte) ->
send(Spec, [Byte | RevBytes], Sock);
send([List | Spec], RevBytes, Sock) when is_list(List) ->
case send(List, RevBytes, Sock) of
- {ok,Left} ->
- send(Spec, Left, Sock);
- Other ->
- Other
+ {ok,Left} ->
+ send(Spec, Left, Sock);
+ Other ->
+ Other
end;
send([d | Spec], RevBytes, Sock) ->
send([{d,1000} | Spec], RevBytes, Sock);
send([{d,S} | Spec], RevBytes, Sock) ->
case send_direct(Sock, lists:reverse(RevBytes)) of
- ok ->
- timer:sleep(S),
- send(Spec, [], Sock);
- Any ->
- Any
+ ok ->
+ timer:sleep(S),
+ send(Spec, [], Sock);
+ Any ->
+ Any
end.
%%%%
send_direct(Sock, Bytes) ->
case gen_tcp:send(Sock, Bytes) of
- ok ->
- ok;
- {error, closed} ->
- closed;
- {error, _Reason} ->
- error;
- Any ->
- test_server:format("unknown message: ~w~n",[Any]),
- Any
+ ok ->
+ ok;
+ {error, closed} ->
+ closed;
+ {error, _Reason} ->
+ error;
+ Any ->
+ test_server:format("unknown message: ~w~n",[Any]),
+ Any
end.
send_req(Req) ->
send_req(Req, "localhost").
send_req(Req, Addr) ->
case connect(Addr) of
- {ok,Sock} ->
- case send(Sock, [size16(Req), Req]) of
- ok ->
- {ok,Sock};
- Other ->
- test_server:format("Failed to send ~w on sock ~w: ~w~n",
- [Req,Sock,Other]),
- error
- end;
- Other ->
- test_server:format("Connect failed when sending ~w: ~p~n",
- [Req, Other]),
- error
+ {ok,Sock} ->
+ case send(Sock, [size16(Req), Req]) of
+ ok ->
+ {ok,Sock};
+ Other ->
+ test_server:format("Failed to send ~w on sock ~w: ~w~n",
+ [Req,Sock,Other]),
+ error
+ end;
+ Other ->
+ test_server:format("Connect failed when sending ~w: ~p~n",
+ [Req, Other]),
+ error
end.
recv_until_sock_closes(Sock) ->
@@ -1234,12 +1103,12 @@ recv_until_sock_closes(Sock) ->
recv_until_sock_closes_2(Sock,AccData) ->
case recv(Sock,0) of
- {ok,Data} ->
- recv_until_sock_closes_2(Sock,AccData++Data);
- closed ->
- {ok,AccData};
- Other ->
- Other
+ {ok,Data} ->
+ recv_until_sock_closes_2(Sock,AccData++Data);
+ closed ->
+ {ok,AccData};
+ Other ->
+ Other
end.
sleep(MilliSeconds) ->
@@ -1261,7 +1130,7 @@ flat_count([H|T], N) when is_list(H) ->
flat_count([_|T], N) ->
flat_count(T, N);
flat_count([], N) -> N.
-
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/erts/etc/Makefile b/erts/etc/Makefile
index 9a14cee89c..788dfff132 100644
--- a/erts/etc/Makefile
+++ b/erts/etc/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2009. All Rights Reserved.
+# Copyright Ericsson AB 1999-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/common/Makefile b/erts/etc/common/Makefile
index bbf51d0efd..bad1d3ddb0 100644
--- a/erts/etc/common/Makefile
+++ b/erts/etc/common/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/common/Makefile.in b/erts/etc/common/Makefile.in
index 8e55fa78c9..cb053a1b7c 100644
--- a/erts/etc/common/Makefile.in
+++ b/erts/etc/common/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2014. All Rights Reserved.
+# Copyright Ericsson AB 1996-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -21,10 +21,6 @@
include $(ERL_TOP)/make/output.mk
include $(ERL_TOP)/make/target.mk
-ifeq ($(findstring ose,$(TARGET)),ose)
-include $(ERL_TOP)/make/$(TARGET)/ose_lm.mk
-endif
-
ERTS_LIB_TYPEMARKER=.$(TYPE)
USING_MINGW=@MIXED_CYGWIN_MINGW@
@@ -85,18 +81,13 @@ EMUOSDIR = $(ERL_TOP)/erts/emulator/@ERLANG_OSTYPE@
SYSDIR = $(ERL_TOP)/erts/emulator/sys/@ERLANG_OSTYPE@
DRVDIR = $(ERL_TOP)/erts/emulator/drivers/@ERLANG_OSTYPE@
UXETC = ../unix
-OSEETC = ../ose
WINETC = ../win32
ifeq ($(TARGET), win32)
ETC = $(WINETC)
else
-ifeq ($(findstring ose,$(TARGET)),ose)
-ETC = $(OSEETC)
-else
ETC = $(UXETC)
endif
-endif
ifeq ($(TARGET), win32)
ERLEXEC = erlexec.dll
@@ -180,25 +171,6 @@ PORT_ENTRY_POINT=erl_port_entry
ENTRY_LDFLAGS=-entry:$(PORT_ENTRY_POINT)
else
-ifeq ($(findstring ose,$(TARGET)),ose)
-ENTRY_LDFLAGS=
-ENTRY_OBJ=
-ERLSRV_OBJECTS=
-MC_OUTPUTS=
-INET_GETHOST =
-INSTALL_EMBEDDED_PROGS = $(BINDIR)/run_erl_lm
-INSTALL_EMBEDDED_DATA =
-INSTALL_TOP = Install
-INSTALL_TOP_BIN =
-INSTALL_MISC =
-INSTALL_SRC =
-ERLEXECDIR = .
-INSTALL_LIBS =
-INSTALL_OBJS =
-INSTALL_INCLUDES =
-TEXTFILES = Install erl.src
-INSTALL_PROGS = $(INSTALL_EMBEDDED_PROGS)
-else # UNIX (!win32 && !ose)
ENTRY_LDFLAGS=
ENTRY_OBJ=
ERLSRV_OBJECTS=
@@ -223,7 +195,6 @@ INSTALL_PROGS = \
$(BINDIR)/$(ERLEXEC) \
$(INSTALL_EMBEDDED_PROGS)
endif
-endif
.PHONY: etc
etc: $(ENTRY_OBJ) $(INSTALL_PROGS) $(INSTALL_LIBS) $(TEXTFILES) $(INSTALL_TOP_BIN)
@@ -269,8 +240,8 @@ endif
rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/to_erl.o
rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/dyn_erl.o
rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/safe_string.o
- rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/run_erl_common.o
- rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/to_erl_common.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/run_erl.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/to_erl.o
rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/typer.o
rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/ct_run.o
rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/vxcall.o
@@ -423,28 +394,24 @@ $(BINDIR)/inet_gethost@EXEEXT@: $(OBJDIR)/inet_gethost.o $(ENTRY_OBJ) $(ERTS_LIB
$(ld_verbose)$(PURIFY) $(LD) $(LDFLAGS) $(ENTRY_LDFLAGS) -o $@ $(OBJDIR)/inet_gethost.o $(ENTRY_OBJ) $(LIBS) $(ERTS_INTERNAL_LIBS)
# run_erl
-$(BINDIR)/run_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/run_erl.o $(OBJDIR)/run_erl_common.o
- $(V_LD) $(LDFLAGS) -o $@ $^ $(LIBS)
-$(OBJDIR)/run_erl.o: $(ETC)/run_erl.c ../common/run_erl_common.h $(RC_GENERATED)
- $(V_CC) $(CFLAGS) -I ../common/ -o $@ -c $(ETC)/run_erl.c
-$(OBJDIR)/run_erl_common.o: ../common/run_erl_common.c ../common/run_erl_common.h $(RC_GENERATED)
- $(V_CC) $(CFLAGS) -o $@ -c $<
+$(BINDIR)/run_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/run_erl.o
+ $(V_LD) $(LDFLAGS) -o $@ $(OBJDIR)/safe_string.o $(OBJDIR)/run_erl.o $(LIBS)
+$(OBJDIR)/run_erl.o: $(ETC)/run_erl.c $(RC_GENERATED)
+ $(V_CC) $(CFLAGS) -o $@ -c $(ETC)/run_erl.c
# to_erl
-$(BINDIR)/to_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/to_erl.o $(OBJDIR)/to_erl_common.o
- $(V_LD) $(LDFLAGS) -o $@ $^
-$(OBJDIR)/to_erl.o: $(ETC)/to_erl.c ../common/safe_string.h $(RC_GENERATED)
- $(V_CC) $(CFLAGS) -I ../common/ -o $@ -c $(ETC)/to_erl.c
-$(OBJDIR)/to_erl_common.o: ../common/to_erl_common.c ../common/to_erl_common.h $(RC_GENERATED)
- $(V_CC) $(CFLAGS) -o $@ -c $<
+$(BINDIR)/to_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/to_erl.o
+ $(V_LD) $(LDFLAGS) -o $@ $(OBJDIR)/safe_string.o $(OBJDIR)/to_erl.o
+$(OBJDIR)/to_erl.o: $(ETC)/to_erl.c $(RC_GENERATED)
+ $(V_CC) $(CFLAGS) -o $@ -c $(ETC)/to_erl.c
# dyn_erl
$(BINDIR)/dyn_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/dyn_erl.o
$(V_LD) $(LDFLAGS) -o $@ $(OBJDIR)/safe_string.o $(OBJDIR)/dyn_erl.o
$(OBJDIR)/dyn_erl.o: $(UXETC)/dyn_erl.c $(RC_GENERATED)
$(V_CC) $(CFLAGS) -o $@ -c $(UXETC)/dyn_erl.c
-$(OBJDIR)/safe_string.o: ../common/safe_string.c $(RC_GENERATED)
- $(V_CC) $(CFLAGS) -o $@ -c ../common/safe_string.c
+$(OBJDIR)/safe_string.o: $(ETC)/safe_string.c $(RC_GENERATED)
+ $(V_CC) $(CFLAGS) -o $@ -c $(ETC)/safe_string.c
ifneq ($(TARGET),win32)
$(BINDIR)/$(ERLEXEC): $(OBJDIR)/$(ERLEXEC).o $(ERTS_LIB)
@@ -499,30 +466,6 @@ erl.src: $(UXETC)/erl.src.src ../../vsn.mk $(TARGET)/Makefile
-e 's;%VSN%;$(VSN);' \
$(UXETC)/erl.src.src > erl.src
-#---------------------------------------------------------
-# OSE specific targets
-#---------------------------------------------------------
-ifeq ($(findstring ose,$(TARGET)),ose)
-$(OBJDIR)/ose_confd.o: $(OSE_CONFD)
- $(V_CC) $(CFLAGS) -o $@ -c $<
-$(OBJDIR)/crt0_lm.o: $(CRT0_LM)
- $(V_CC) $(CFLAGS) -o $@ -c $<
-OSE_LM_OBJS += $(OBJDIR)/ose_confd.o $(OBJDIR)/crt0_lm.o
-
-$(BINDIR)/run_erl_lm: $(OBJDIR)/run_erl_main.o $(OBJDIR)/safe_string.o $(OBJDIR)/run_erl.o $(OBJDIR)/run_erl_common.o $(OBJDIR)/to_erl_common.o $(OSE_LM_OBJS)
- $(call build-ose-load-module, $@, $^, $(LIBS), $(RUN_ERL_LMCONF))
-
-
-$(OBJDIR)/run_erl_main.o: $(OSEETC)/run_erl_main.c $(OSEETC)/run_erl.h ../common/to_erl_common.h $(RC_GENERATED)
- $(V_CC) $(CFLAGS) -I ../common/ -o $@ -c $(OSEETC)/run_erl_main.c
-
-endif
-
-#---------------------------------------------------------
-# End of ose specific targets.
-#---------------------------------------------------------
-
-
# ----------------------------------------------------
# Release Target
# ----------------------------------------------------
@@ -537,11 +480,9 @@ endif
$(INSTALL_DIR) "$(RELEASE_PATH)/erts-$(VSN)/bin"
ifneq ($(TARGET), win32)
ifneq ($(findstring vxworks,$(TARGET)), vxworks)
-ifneq ($(findstring ose,$(TARGET)), ose)
$(INSTALL_SCRIPT) erl.src "$(RELEASE_PATH)/erts-$(VSN)/bin"
endif
endif
-endif
ifneq ($(INSTALL_PROGS),)
$(INSTALL_PROGRAM) $(INSTALL_PROGS) "$(RELEASE_PATH)/erts-$(VSN)/bin"
endif
diff --git a/erts/etc/common/ct_run.c b/erts/etc/common/ct_run.c
index 548514ee6c..acdfa8c8b8 100644
--- a/erts/etc/common/ct_run.c
+++ b/erts/etc/common/ct_run.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2010-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -81,13 +81,14 @@ static int eargc; /* Number of arguments in eargv. */
*/
static void error(char* format, ...);
-static char* emalloc(size_t size);
+static void* emalloc(size_t size);
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__
static char* possibly_quote(char* arg);
+static void* erealloc(void *p, size_t size);
#endif
/*
@@ -141,10 +142,10 @@ int main(int argc, char** argv)
int i;
int len;
/* Convert argv to utf8 */
- argv = malloc((argc+1) * sizeof(char*));
+ argv = emalloc((argc+1) * sizeof(char*));
for (i=0; i<argc; i++) {
len = WideCharToMultiByte(CP_UTF8, 0, wcargv[i], -1, NULL, 0, NULL, NULL);
- argv[i] = malloc(len*sizeof(char));
+ argv[i] = emalloc(len*sizeof(char));
WideCharToMultiByte(CP_UTF8, 0, wcargv[i], -1, argv[i], len, NULL, NULL);
}
argv[argc] = NULL;
@@ -334,7 +335,7 @@ wchar_t *make_commandline(char **argv)
buff = (wchar_t *) emalloc(siz*sizeof(wchar_t));
} else if (siz < num) {
siz = num;
- buff = (wchar_t *) realloc(buff,siz*sizeof(wchar_t));
+ buff = (wchar_t *) erealloc(buff,siz*sizeof(wchar_t));
}
p = buff;
num=0;
@@ -437,15 +438,26 @@ error(char* format, ...)
exit(1);
}
-static char*
+static void*
emalloc(size_t size)
{
- char *p = malloc(size);
+ void *p = malloc(size);
if (p == NULL)
error("Insufficient memory");
return p;
}
+#ifdef __WIN32__
+static void *
+erealloc(void *p, size_t size)
+{
+ void *res = realloc(p, size);
+ if (res == NULL)
+ error("Insufficient memory");
+ return res;
+}
+#endif
+
static char*
strsave(char* string)
{
diff --git a/erts/etc/common/dialyzer.c b/erts/etc/common/dialyzer.c
index c45626606c..6ba3605422 100644
--- a/erts/etc/common/dialyzer.c
+++ b/erts/etc/common/dialyzer.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -63,13 +63,14 @@ static int eargc; /* Number of arguments in eargv. */
*/
static void error(char* format, ...);
-static char* emalloc(size_t size);
+static void* emalloc(size_t size);
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__
static char* possibly_quote(char* arg);
+static void* erealloc(void *p, size_t size);
#endif
/*
@@ -164,10 +165,10 @@ int main(int argc, char** argv)
#ifdef __WIN32__
int len;
/* Convert argv to utf8 */
- argv = malloc((argc+1) * sizeof(char*));
+ argv = emalloc((argc+1) * sizeof(char*));
for (i=0; i<argc; i++) {
len = WideCharToMultiByte(CP_UTF8, 0, wcargv[i], -1, NULL, 0, NULL, NULL);
- argv[i] = malloc(len*sizeof(char));
+ argv[i] = emalloc(len*sizeof(char));
WideCharToMultiByte(CP_UTF8, 0, wcargv[i], -1, argv[i], len, NULL, NULL);
}
argv[argc] = NULL;
@@ -310,7 +311,7 @@ wchar_t *make_commandline(char **argv)
buff = (wchar_t *) emalloc(siz*sizeof(wchar_t));
} else if (siz < num) {
siz = num;
- buff = (wchar_t *) realloc(buff,siz*sizeof(wchar_t));
+ buff = (wchar_t *) erealloc(buff,siz*sizeof(wchar_t));
}
p = buff;
num=0;
@@ -413,15 +414,26 @@ error(char* format, ...)
exit(1);
}
-static char*
+static void*
emalloc(size_t size)
{
- char *p = malloc(size);
+ void *p = malloc(size);
if (p == NULL)
error("Insufficient memory");
return p;
}
+#ifdef __WIN32__
+static void *
+erealloc(void *p, size_t size)
+{
+ void *res = realloc(p, size);
+ if (res == NULL)
+ error("Insufficient memory");
+ return res;
+}
+#endif
+
static char*
strsave(char* string)
{
diff --git a/erts/etc/common/erlc.c b/erts/etc/common/erlc.c
index f9d909e01c..b54cb31bef 100644
--- a/erts/etc/common/erlc.c
+++ b/erts/etc/common/erlc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -71,13 +71,14 @@ static int pause_after_execution = 0;
static char* process_opt(int* pArgc, char*** pArgv, int offset);
static void error(char* format, ...);
-static char* emalloc(size_t size);
+static void* emalloc(size_t size);
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__
static char* possibly_quote(char* arg);
+static void* erealloc(void *p, size_t size);
#endif
/*
@@ -171,10 +172,10 @@ int main(int argc, char** argv)
int i;
int len;
/* Convert argv to utf8 */
- argv = malloc((argc+1) * sizeof(char*));
+ argv = emalloc((argc+1) * sizeof(char*));
for (i=0; i<argc; i++) {
len = WideCharToMultiByte(CP_UTF8, 0, wcargv[i], -1, NULL, 0, NULL, NULL);
- argv[i] = malloc(len*sizeof(char));
+ argv[i] = emalloc(len*sizeof(char));
WideCharToMultiByte(CP_UTF8, 0, wcargv[i], -1, argv[i], len, NULL, NULL);
}
argv[argc] = NULL;
@@ -370,7 +371,7 @@ wchar_t *make_commandline(char **argv)
buff = (wchar_t *) emalloc(siz*sizeof(wchar_t));
} else if (siz < num) {
siz = num;
- buff = (wchar_t *) realloc(buff,siz*sizeof(wchar_t));
+ buff = (wchar_t *) erealloc(buff,siz*sizeof(wchar_t));
}
p = buff;
num=0;
@@ -478,15 +479,26 @@ error(char* format, ...)
exit(1);
}
-static char*
+static void*
emalloc(size_t size)
{
- char *p = malloc(size);
+ void *p = malloc(size);
if (p == NULL)
error("Insufficient memory");
return p;
}
+#ifdef __WIN32__
+static void *
+erealloc(void *p, size_t size)
+{
+ void *res = realloc(p, size);
+ if (res == NULL)
+ error("Insufficient memory");
+ return res;
+}
+#endif
+
static char*
strsave(char* string)
{
diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c
index af1c198281..2b2e0e480a 100644
--- a/erts/etc/common/erlexec.c
+++ b/erts/etc/common/erlexec.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -65,6 +65,7 @@
static const char plusM_au_allocs[]= {
'u', /* all alloc_util allocators */
'B', /* binary_alloc */
+ 'I', /* literal_alloc */
'D', /* std_alloc */
'E', /* ets_alloc */
'F', /* fix_alloc */
@@ -73,6 +74,7 @@ static const char plusM_au_allocs[]= {
'R', /* driver_alloc */
'S', /* sl_alloc */
'T', /* temp_alloc */
+ 'X', /* exec_alloc */
'Z', /* test_alloc */
'\0'
};
@@ -121,6 +123,8 @@ static char *plusM_other_switches[] = {
"Ym",
"Ytp",
"Ytt",
+ "Iscs",
+ "Xscs",
NULL
};
@@ -146,6 +150,10 @@ static char *plush_val_switches[] = {
"ms",
"mbs",
"pds",
+ "max",
+ "maxk",
+ "maxel",
+ "mqd",
"",
NULL
};
@@ -187,6 +195,7 @@ static char *plusz_val_switches[] = {
#endif
void usage(const char *switchname);
+static void usage_format(char *format, ...);
void start_epmd(char *epmd);
void error(char* format, ...);
@@ -787,6 +796,24 @@ int main(int argc, char **argv)
get_start_erl_data((char *) NULL);
}
#endif
+ else if (strcmp(argv[i], "-start_epmd") == 0) {
+ if (i+1 >= argc)
+ usage("-start_epmd");
+
+ if (strcmp(argv[i+1], "true") == 0) {
+ /* The default */
+ no_epmd = 0;
+ }
+ else if (strcmp(argv[i+1], "false") == 0) {
+ no_epmd = 1;
+ }
+ else
+ usage_format("Expected boolean argument for \'-start_epmd\'.\n");
+
+ add_arg(argv[i]);
+ add_arg(argv[i+1]);
+ i++;
+ }
else
add_arg(argv[i]);
@@ -837,7 +864,6 @@ int main(int argc, char **argv)
if (argv[i][3] != '\0')
goto the_default;
}
-#ifdef ERTS_DIRTY_SCHEDULERS
else if (argv[i][2] == 'D') {
char* type = argv[i]+3;
if (strncmp(type, "cpu", 3) != 0 &&
@@ -849,7 +875,6 @@ int main(int argc, char **argv)
(argv[i][3] == 'i' && argv[i][5] != '\0'))
goto the_default;
}
-#endif
else if (argv[i][2] != '\0')
goto the_default;
if (i+1 >= argc)
@@ -1167,7 +1192,7 @@ usage_aux(void)
"]"
#endif
"] "
- "[-make] [-man [manopts] MANPAGE] [-x] [-emu_args] "
+ "[-make] [-man [manopts] MANPAGE] [-x] [-emu_args] [-start_epmd BOOLEAN] "
"[-args_file FILENAME] [+A THREADS] [+a SIZE] [+B[c|d|i]] [+c [BOOLEAN]] "
"[+C MODE] [+h HEAP_SIZE_OPTION] [+K BOOLEAN] "
"[+l] [+M<SUBSWITCH> <ARGUMENT>] [+P MAX_PROCS] [+Q MAX_PORTS] "
diff --git a/erts/etc/common/escript.c b/erts/etc/common/escript.c
index 7fd02ed436..71c278881c 100644
--- a/erts/etc/common/escript.c
+++ b/erts/etc/common/escript.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2007-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2007-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -71,7 +71,7 @@ static int eargc; /* Number of arguments in eargv. */
*/
static void error(char* format, ...);
-static char* emalloc(size_t size);
+static void* emalloc(size_t size);
static void efree(void *p);
static char* strsave(char* string);
static void push_words(char* src);
@@ -79,6 +79,7 @@ static int run_erlang(char* name, char** argv);
static char* get_default_emulator(char* progname);
#ifdef __WIN32__
static char* possibly_quote(char* arg);
+static void* erealloc(void *p, size_t size);
#endif
/*
@@ -418,10 +419,10 @@ main(int argc, char** argv)
int i;
int len;
/* Convert argv to utf8 */
- argv = malloc((argc+1) * sizeof(char*));
+ argv = emalloc((argc+1) * sizeof(char*));
for (i=0; i<argc; i++) {
len = WideCharToMultiByte(CP_UTF8, 0, wcargv[i], -1, NULL, 0, NULL, NULL);
- argv[i] = malloc(len*sizeof(char));
+ argv[i] = emalloc(len*sizeof(char));
WideCharToMultiByte(CP_UTF8, 0, wcargv[i], -1, argv[i], len, NULL, NULL);
}
argv[argc] = NULL;
@@ -594,7 +595,7 @@ wchar_t *make_commandline(char **argv)
buff = (wchar_t *) emalloc(siz*sizeof(wchar_t));
} else if (siz < num) {
siz = num;
- buff = (wchar_t *) realloc(buff,siz*sizeof(wchar_t));
+ buff = (wchar_t *) erealloc(buff,siz*sizeof(wchar_t));
}
p = buff;
num=0;
@@ -694,15 +695,26 @@ error(char* format, ...)
exit(1);
}
-static char*
+static void*
emalloc(size_t size)
{
- char *p = malloc(size);
+ void *p = malloc(size);
if (p == NULL)
error("Insufficient memory");
return p;
}
+#ifdef __WIN32__
+static void *
+erealloc(void *p, size_t size)
+{
+ void *res = realloc(p, size);
+ if (res == NULL)
+ error("Insufficient memory");
+ return res;
+}
+#endif
+
static void
efree(void *p)
{
diff --git a/erts/etc/common/heart.c b/erts/etc/common/heart.c
index 1a826221fb..bc353e384e 100644
--- a/erts/etc/common/heart.c
+++ b/erts/etc/common/heart.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -48,13 +48,10 @@
*
* HEART_BEATING
*
- * This program expects a heart beat messages. If it does not receive a
- * heart beat message from Erlang within heart_beat_timeout seconds, it
- * reboots the system. The variable heart_beat_timeout is exported (so
- * that it can be set from the shell in VxWorks, as is the variable
- * heart_beat_report_delay). When using Solaris, the system is rebooted
- * by executing the command stored in the environment variable
- * HEART_COMMAND.
+ * This program expects a heart beat message. If it does not receive a
+ * heart beat message from Erlang within heart_beat_timeout seconds, it
+ * reboots the system. The system is rebooted by executing the command
+ * stored in the environment variable HEART_COMMAND.
*
* BLOCKING DESCRIPTORS
*
@@ -119,6 +116,8 @@
#define HEART_COMMAND_ENV "HEART_COMMAND"
#define ERL_CRASH_DUMP_SECONDS_ENV "ERL_CRASH_DUMP_SECONDS"
#define HEART_KILL_SIGNAL "HEART_KILL_SIGNAL"
+#define HEART_NO_KILL "HEART_NO_KILL"
+
#define MSG_HDR_SIZE (2)
#define MSG_HDR_PLUS_OP_SIZE (3)
@@ -147,27 +146,17 @@ struct msg {
/* Maybe interesting to change */
/* Times in seconds */
-#define HEART_BEAT_BOOT_DELAY 60 /* 1 minute */
#define SELECT_TIMEOUT 5 /* Every 5 seconds we reset the
watchdog timer */
/* heart_beat_timeout is the maximum gap in seconds between two
- consecutive heart beat messages from Erlang, and HEART_BEAT_BOOT_DELAY
- is the the extra delay that wd_keeper allows for, to give heart a
- chance to reboot in the "normal" way before the hardware watchdog
- enters the scene. heart_beat_report_delay is the time allowed for reporting
- before rebooting under VxWorks. */
+ consecutive heart beat messages from Erlang. */
int heart_beat_timeout = 60;
-int heart_beat_report_delay = 30;
-int heart_beat_boot_delay = HEART_BEAT_BOOT_DELAY;
/* All current platforms have a process identifier that
fits in an unsigned long and where 0 is an impossible or invalid value */
unsigned long heart_beat_kill_pid = 0;
-#define VW_WD_TIMEOUT (heart_beat_timeout+heart_beat_report_delay+heart_beat_boot_delay)
-#define SOL_WD_TIMEOUT (heart_beat_timeout+heart_beat_boot_delay)
-
/* reasons for reboot */
#define R_TIMEOUT (1)
#define R_CLOSED (2)
@@ -295,7 +284,6 @@ free_env_val(char *value)
static void get_arguments(int argc, char** argv) {
int i = 1;
int h;
- int w;
unsigned long p;
while (i < argc) {
@@ -311,15 +299,6 @@ static void get_arguments(int argc, char** argv) {
i++;
}
break;
- case 'w':
- if (strcmp(argv[i], "-wt") == 0)
- if (sscanf(argv[i+1],"%i",&w) ==1)
- if ((w > 10) && (w <= 65535)) {
- heart_beat_boot_delay = w;
- fprintf(stderr,"heart_beat_boot_delay = %d\n",w);
- i++;
- }
- break;
case 'p':
if (strcmp(argv[i], "-pid") == 0)
if (sscanf(argv[i+1],"%lu",&p) ==1){
@@ -345,7 +324,7 @@ static void get_arguments(int argc, char** argv) {
}
i++;
}
- debugf("arguments -ht %d -wt %d -pid %lu\n",h,w,p);
+ debugf("arguments -ht %d -pid %lu\n",h,p);
}
int main(int argc, char **argv) {
@@ -524,6 +503,12 @@ static void
kill_old_erlang(void){
HANDLE erlh;
DWORD exit_code;
+ char* envvar = NULL;
+
+ envvar = get_env(HEART_NO_KILL);
+ if (envvar && strcmp(envvar, "TRUE") == 0)
+ return;
+
if(heart_beat_kill_pid != 0){
if((erlh = OpenProcess(PROCESS_TERMINATE |
SYNCHRONIZE |
@@ -555,10 +540,14 @@ kill_old_erlang(void){
pid_t pid;
int i, res;
int sig = SIGKILL;
- char *sigenv = NULL;
+ char *envvar = NULL;
+
+ envvar = get_env(HEART_NO_KILL);
+ if (envvar && strcmp(envvar, "TRUE") == 0)
+ return;
- sigenv = get_env(HEART_KILL_SIGNAL);
- if (sigenv && strcmp(sigenv, "SIGABRT") == 0) {
+ envvar = get_env(HEART_KILL_SIGNAL);
+ if (envvar && strcmp(envvar, "SIGABRT") == 0) {
print_error("kill signal SIGABRT requested");
sig = SIGABRT;
}
@@ -662,11 +651,6 @@ void win_system(char *command)
*/
static void
do_terminate(int erlin_fd, int reason) {
- /*
- When we get here, we have HEART_BEAT_BOOT_DELAY secs to finish
- (plus heart_beat_report_delay if under VxWorks), so we don't need
- to call wd_reset().
- */
int ret = 0, tmo=0;
char *tmo_env;
diff --git a/erts/etc/common/inet_gethost.c b/erts/etc/common/inet_gethost.c
index e298c5e7f7..bc4893b0eb 100644
--- a/erts/etc/common/inet_gethost.c
+++ b/erts/etc/common/inet_gethost.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2012. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -2646,7 +2646,7 @@ static void *my_realloc(void *old, size_t size)
BOOL create_mesq(MesQ **q)
{
- MesQ *tmp = malloc(sizeof(MesQ));
+ MesQ *tmp = ALLOC(sizeof(MesQ));
tmp->data_present = CreateEvent(NULL, TRUE, FALSE,NULL);
if (tmp->data_present == NULL) {
free(tmp);
diff --git a/erts/etc/common/run_erl_common.c b/erts/etc/common/run_erl_common.c
deleted file mode 100644
index c03d5e3ae2..0000000000
--- a/erts/etc/common/run_erl_common.c
+++ /dev/null
@@ -1,696 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 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%
- */
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-
-#ifdef __ANDROID__
-# include <termios.h>
-#endif
-
-#ifdef HAVE_SYSLOG_H
-# include <syslog.h>
-#endif
-
-#ifdef HAVE_SYS_IOCTL_H
-# include <sys/ioctl.h>
-#endif
-
-#ifdef __OSE__
-# include "ramlog.h"
-#endif
-
-#include "run_erl_common.h"
-#include "safe_string.h"
-
-#define DEFAULT_LOG_GENERATIONS 5
-#define LOG_MAX_GENERATIONS 1000 /* No more than 1000 log files */
-#define LOG_MIN_GENERATIONS 2 /* At least two to switch between */
-#define DEFAULT_LOG_MAXSIZE 100000
-#define LOG_MIN_MAXSIZE 1000 /* Smallast value for changing log file */
-#define LOG_STUBNAME "erlang.log."
-#define LOG_PERM 0664
-#define DEFAULT_LOG_ACTIVITY_MINUTES 5
-#define DEFAULT_LOG_ALIVE_MINUTES 15
-#define DEFAULT_LOG_ALIVE_FORMAT "%a %b %e %T %Z %Y"
-#define ALIVE_BUFFSIZ 1024
-
-#define STATUSFILENAME "/run_erl.log"
-
-#define PIPE_STUBNAME "erlang.pipe"
-#define PIPE_STUBLEN strlen(PIPE_STUBNAME)
-#define PERM (S_IWUSR | S_IRUSR | S_IWOTH | S_IROTH | S_IWGRP | S_IRGRP)
-
-/* OSE has defined O_SYNC but it is not recognized by open */
-#if !defined(O_SYNC) || defined(__OSE__)
-#undef O_SYNC
-#define O_SYNC 0
-#define USE_FSYNC 1
-#endif
-
-/* Global variable definitions
- * We need this complex way of handling global variables because of how
- * OSE works here. We want to make it possible to run the shell command
- * run_erl multiple times with different global variables without them
- * effecting eachother.
- */
-
-#define STATUSFILE (RE_DATA->statusfile)
-#define LOG_DIR (RE_DATA->log_dir)
-#define STDSTATUS (RE_DATA->stdstatus)
-#define LOG_GENERATIONS (RE_DATA->log_generations)
-#define LOG_MAXSIZE (RE_DATA->log_maxsize)
-#define LOG_ACTIVITY_MINUTES (RE_DATA->log_activity_minutes)
-#define LOG_ALIVE_IN_GMT (RE_DATA->log_alive_in_gmt)
-#define LOG_ALIVE_FORMAT (RE_DATA->log_alive_format)
-#define RUN_DAEMON (RE_DATA->run_daemon)
-#define LOG_ALIVE_MINUTES (RE_DATA->log_alive_minutes)
-#define LOG_NUM (RE_DATA->log_num)
-#define LFD (RE_DATA->lfd)
-#define PROTOCOL_VER (RE_DATA->protocol_ver)
-
-struct run_erl_ {
- /* constant config data */
- char statusfile[FILENAME_BUFSIZ];
- char log_dir[FILENAME_BUFSIZ];
- FILE *stdstatus;
- int log_generations;
- int log_maxsize;
- int log_activity_minutes;
- int log_alive_in_gmt;
- char log_alive_format[ALIVE_BUFFSIZ+1];
- int run_daemon;
- int log_alive_minutes;
- /* Current log number and log fd */
- int log_num;
- int lfd;
- unsigned protocol_ver;
-};
-
-typedef struct run_erl_ run_erl;
-
-#ifdef __OSE__
-static OSPPDKEY run_erl_pp_key;
-#define RE_DATA (*(run_erl**)ose_get_ppdata(run_erl_pp_key))
-#else
-static run_erl re;
-#define RE_DATA (&re)
-#endif
-
-/* prototypes */
-
-static int next_log(int log_num);
-static int prev_log(int log_num);
-static int find_next_log_num(void);
-static int open_log(int log_num, int flags);
-
-/*
- * getenv_int:
- */
-static char *getenv_int(const char *name) {
-#ifdef __OSE__
- return get_env(get_bid(current_process()),name);
-#else
- return getenv(name);
-#endif
-}
-
-/*
- * next_log:
- * Returns the index number that follows the given index number.
- * (Wrapping after log_generations)
- */
-static int next_log(int log_num) {
- return log_num>=LOG_GENERATIONS?1:log_num+1;
-}
-
-/*
- * prev_log:
- * Returns the index number that precedes the given index number.
- * (Wrapping after log_generations)
- */
-static int prev_log(int log_num) {
- return log_num<=1?LOG_GENERATIONS:log_num-1;
-}
-
-/*
- * find_next_log_num()
- * Searches through the log directory to check which logs that already
- * exist. It finds the "hole" in the sequence, and returns the index
- * number for the last log in the log sequence. If there is no hole, index
- * 1 is returned.
- */
-static int find_next_log_num(void) {
- int i, next_gen, log_gen;
- DIR *dirp;
- struct dirent *direntp;
- int log_exists[LOG_MAX_GENERATIONS+1];
- int stub_len = strlen(LOG_STUBNAME);
-
- /* Initialize exiting log table */
-
- for(i=LOG_GENERATIONS; i>=0; i--)
- log_exists[i] = 0;
- dirp = opendir(LOG_DIR);
- if(!dirp) {
- ERRNO_ERR1(LOG_ERR,"Can't access log directory '%s'", LOG_DIR);
- exit(1);
- }
-
- /* Check the directory for existing logs */
-
- while((direntp=readdir(dirp)) != NULL) {
- if(strncmp(direntp->d_name,LOG_STUBNAME,stub_len)==0) {
- int num = atoi(direntp->d_name+stub_len);
- if(num < 1 || num > LOG_GENERATIONS)
- continue;
- log_exists[num] = 1;
- }
- }
- closedir(dirp);
-
- /* Find out the next available log file number */
-
- next_gen = 0;
- for(i=LOG_GENERATIONS; i>=0; i--) {
- if(log_exists[i])
- if(next_gen)
- break;
- else
- ;
- else
- next_gen = i;
- }
-
- /* Find out the current log file number */
-
- if(next_gen)
- log_gen = prev_log(next_gen);
- else
- log_gen = 1;
-
- return log_gen;
-} /* find_next_log_num() */
-
-static int open_log(int log_num, int flags)
-{
- char buf[FILENAME_MAX];
- time_t now;
- struct tm *tmptr;
- char log_buffer[ALIVE_BUFFSIZ+1];
-
- /* Remove the next log (to keep a "hole" in the log sequence) */
- sn_printf(buf, sizeof(buf), "%s/%s%d",
- LOG_DIR, LOG_STUBNAME, next_log(log_num));
- unlink(buf);
-
- /* Create or continue on the current log file */
- sn_printf(buf, sizeof(buf), "%s/%s%d", LOG_DIR, LOG_STUBNAME, log_num);
-
- LFD = sf_open(buf, flags, LOG_PERM);
-
- if(LFD <0){
- ERRNO_ERR1(LOG_ERR,"Can't open log file '%s'.", buf);
- exit(1);
- }
-
- /* Write a LOGGING STARTED and time stamp into the log file */
- time(&now);
- if (LOG_ALIVE_IN_GMT) {
- tmptr = gmtime(&now);
- } else {
- tmptr = localtime(&now);
- }
- if (!strftime(log_buffer, ALIVE_BUFFSIZ, LOG_ALIVE_FORMAT,
- tmptr)) {
- strn_cpy(log_buffer, sizeof(log_buffer),
- "(could not format time in 256 positions "
- "with current format string.)");
- }
- log_buffer[ALIVE_BUFFSIZ] = '\0';
-
- sn_printf(buf, sizeof(buf), "\n=====\n===== LOGGING STARTED %s\n=====\n",
- log_buffer);
- if (erts_run_erl_write_all(LFD, buf, strlen(buf)) < 0)
- erts_run_erl_log_status("Error in writing to log.\n");
-
-#if USE_FSYNC
- fsync(LFD);
-#endif
-
- return LFD;
-}
-
-/* Instead of making sure basename exists, we do our own */
-char *simple_basename(char *path)
-{
- char *ptr;
- for (ptr = path; *ptr != '\0'; ++ptr) {
- if (*ptr == '/') {
- path = ptr + 1;
- }
- }
- return path;
-}
-
-ssize_t sf_read(int fd, void *buffer, size_t len) {
- ssize_t n = 0;
-
- do { n = read(fd, buffer, len); } while (n < 0 && errno == EINTR);
-
- return n;
-}
-
-ssize_t sf_write(int fd, const void *buffer, size_t len) {
- ssize_t n = 0;
-
- do { n = write(fd, buffer, len); } while (n < 0 && errno == EINTR);
-
- return n;
-}
-
-int sf_open(const char *path, int type, mode_t mode) {
- int fd = 0;
-
- do { fd = open(path, type, mode); } while(fd < 0 && errno == EINTR);
-
- return fd;
-}
-
-int sf_close(int fd) {
- int res = 0;
-
- do { res = close(fd); } while(res < 0 && errno == EINTR);
-
- return res;
-}
-
-/* Call write() until entire buffer has been written or error.
- * Return len or -1.
- */
-int erts_run_erl_write_all(int fd, const char* buf, int len)
-{
- int left = len;
- int written;
- for (;;) {
- do {
- written = write(fd,buf,left);
- } while (written < 0 && errno == EINTR);
- if (written == left) {
- return len;
- }
- if (written < 0) {
- return -1;
- }
- left -= written;
- buf += written;
- }
- return written;
-}
-
-/* erts_run_erl_log_status()
- * Prints the arguments to a status file
- * Works like printf (see vfrpintf)
- */
-void erts_run_erl_log_status(const char *format,...)
-{
- va_list args;
- time_t now;
-
- if (STDSTATUS == NULL)
- STDSTATUS = fopen(STATUSFILE, "w");
- if (STDSTATUS == NULL)
- return;
- now = time(NULL);
- fprintf(STDSTATUS, "run_erl [%d] %s",
-#ifdef __OSE__
- (int)current_process(),
-#else
- (int)getpid(),
-#endif
- ctime(&now));
- va_start(args, format);
- vfprintf(STDSTATUS, format, args);
- va_end(args);
- fflush(STDSTATUS);
- return;
-}
-
-/* Fetch the current log alive minutes */
-int erts_run_erl_log_alive_minutes() {
- return LOG_ALIVE_MINUTES;
-}
-
-/* error_logf()
- * Prints the arguments to stderr or syslog
- * Works like printf (see vfprintf)
- */
-void erts_run_erl_log_error(int priority, int line, const char *format, ...)
-{
- va_list args;
- va_start(args, format);
-
-#ifdef HAVE_SYSLOG_H
- if (RUN_DAEMON) {
- vsyslog(priority,format,args);
- }
- else
-#endif
-#ifdef __OSE__
- if (RUN_DAEMON) {
- char *buff = malloc(sizeof(char)*1024);
- vsnprintf(buff,1024,format, args);
- ramlog_printf(buff);
- }
- else
-#endif
- {
- time_t now = time(NULL);
- fprintf(stderr, "run_erl:%d [%d] %s", line,
-#ifdef __OSE__
- (int)current_process(),
-#else
- (int)getpid(),
-#endif
- ctime(&now));
- vfprintf(stderr, format, args);
- }
- va_end(args);
-}
-
-/* erts_run_erl_log_write()
- * Writes a message to lfd. If the current log file is full,
- * a new log file is opened.
- */
-int erts_run_erl_log_write(char* buf, size_t len)
-{
- int size;
- ssize_t res;
- /* Decide if new logfile needed, and open if so */
-
- size = lseek(LFD,0,SEEK_END);
- if(size+len > LOG_MAXSIZE) {
- int res;
- do {
- res = close(LFD);
- } while (res < 0 && errno == EINTR);
- LOG_NUM = next_log(LOG_NUM);
- LFD = open_log(LOG_NUM, O_RDWR|O_CREAT|O_TRUNC|O_SYNC);
- }
-
- /* Write to log file */
-
- if ((res = erts_run_erl_write_all(LFD, buf, len)) < 0) {
- erts_run_erl_log_status("Error in writing to log.\n");
- }
-
-#if USE_FSYNC
- fsync(LFD);
-#endif
- return res;
-}
-
-int erts_run_erl_log_activity(int timeout,time_t now,time_t last_activity) {
- char log_alive_buffer[ALIVE_BUFFSIZ+1];
- char buf[BUFSIZ];
-
- if (timeout || now - last_activity > LOG_ACTIVITY_MINUTES*60) {
- /* Either a time out: 15 minutes without action, */
- /* or something is coming in right now, but it's a long time */
- /* since last time, so let's write a time stamp this message */
- struct tm *tmptr;
- if (LOG_ALIVE_IN_GMT) {
- tmptr = gmtime(&now);
- } else {
- tmptr = localtime(&now);
- }
- if (!strftime(log_alive_buffer, ALIVE_BUFFSIZ, LOG_ALIVE_FORMAT,
- tmptr)) {
- strn_cpy(log_alive_buffer, sizeof(log_alive_buffer),
- "(could not format time in 256 positions "
- "with current format string.)");
- }
- log_alive_buffer[ALIVE_BUFFSIZ] = '\0';
-
- sn_printf(buf, sizeof(buf), "\n===== %s%s\n",
- timeout?"ALIVE ":"", log_alive_buffer);
- return erts_run_erl_log_write(buf, strlen(buf));
- }
- return 0;
-}
-
-int erts_run_erl_log_open() {
-
- LOG_NUM = find_next_log_num();
- LFD = open_log(LOG_NUM, O_RDWR|O_APPEND|O_CREAT|O_SYNC);
- return 0;
-}
-
-int erts_run_erl_log_init(int daemon, char* logdir) {
- char *p;
-
-#ifdef __OSE__
- run_erl **re_pp;
- if (!run_erl_pp_key)
- ose_create_ppdata("run_erl_ppdata",&run_erl_pp_key);
- re_pp = (run_erl **)ose_get_ppdata(run_erl_pp_key);
- *re_pp = malloc(sizeof(run_erl));
-#endif
-
- STDSTATUS = NULL;
- LOG_GENERATIONS = DEFAULT_LOG_GENERATIONS;
- LOG_MAXSIZE = DEFAULT_LOG_MAXSIZE;
- LOG_ACTIVITY_MINUTES = DEFAULT_LOG_ACTIVITY_MINUTES;
- LOG_ALIVE_IN_GMT = 0;
- RUN_DAEMON = 0;
- LOG_ALIVE_MINUTES = DEFAULT_LOG_ALIVE_MINUTES;
- LFD = 0;
- PROTOCOL_VER = RUN_ERL_LO_VER; /* assume lowest to begin with */
-
- /* Get values for LOG file handling from the environment */
- if ((p = getenv_int("RUN_ERL_LOG_ALIVE_MINUTES"))) {
- LOG_ALIVE_MINUTES = atoi(p);
- if (!LOG_ALIVE_MINUTES) {
- ERROR1(LOG_ERR,"Minimum value for RUN_ERL_LOG_ALIVE_MINUTES is 1 "
- "(current value is %s)",p);
- }
- LOG_ACTIVITY_MINUTES = LOG_ALIVE_MINUTES / 3;
- if (!LOG_ACTIVITY_MINUTES) {
- ++LOG_ACTIVITY_MINUTES;
- }
- }
- if ((p = getenv_int(
- "RUN_ERL_LOG_ACTIVITY_MINUTES"))) {
- LOG_ACTIVITY_MINUTES = atoi(p);
- if (!LOG_ACTIVITY_MINUTES) {
- ERROR1(LOG_ERR,"Minimum value for RUN_ERL_LOG_ACTIVITY_MINUTES is 1 "
- "(current value is %s)",p);
- }
- }
- if ((p = getenv_int("RUN_ERL_LOG_ALIVE_FORMAT"))) {
- if (strlen(p) > ALIVE_BUFFSIZ) {
- ERROR1(LOG_ERR, "RUN_ERL_LOG_ALIVE_FORMAT can contain a maximum of "
- "%d characters", ALIVE_BUFFSIZ);
- }
- strn_cpy(LOG_ALIVE_FORMAT, sizeof(LOG_ALIVE_FORMAT), p);
- } else {
- strn_cpy(LOG_ALIVE_FORMAT, sizeof(LOG_ALIVE_FORMAT),
- DEFAULT_LOG_ALIVE_FORMAT);
- }
- if ((p = getenv_int("RUN_ERL_LOG_ALIVE_IN_UTC"))
- && strcmp(p,"0")) {
- ++LOG_ALIVE_IN_GMT;
- }
- if ((p = getenv_int("RUN_ERL_LOG_GENERATIONS"))) {
- LOG_GENERATIONS = atoi(p);
- if (LOG_GENERATIONS < LOG_MIN_GENERATIONS)
- ERROR1(LOG_ERR,"Minimum RUN_ERL_LOG_GENERATIONS is %d",
- LOG_MIN_GENERATIONS);
- if (LOG_GENERATIONS > LOG_MAX_GENERATIONS)
- ERROR1(LOG_ERR,"Maximum RUN_ERL_LOG_GENERATIONS is %d",
- LOG_MAX_GENERATIONS);
- }
-
- if ((p = getenv_int("RUN_ERL_LOG_MAXSIZE"))) {
- LOG_MAXSIZE = atoi(p);
- if (LOG_MAXSIZE < LOG_MIN_MAXSIZE)
- ERROR1(LOG_ERR,"Minimum RUN_ERL_LOG_MAXSIZE is %d", LOG_MIN_MAXSIZE);
- }
-
- RUN_DAEMON = daemon;
-
- strn_cpy(LOG_DIR, sizeof(LOG_DIR), logdir);
- strn_cpy(STATUSFILE, sizeof(STATUSFILE), LOG_DIR);
- strn_cat(STATUSFILE, sizeof(STATUSFILE), STATUSFILENAME);
-
- return 0;
-}
-
-/* create_fifo()
- * Creates a new fifo with the given name and permission.
- */
-static int create_fifo(char *name, int perm)
-{
- if ((mkfifo(name, perm) < 0) && (errno != EEXIST))
- return -1;
- return 0;
-}
-
-/*
- * w- and r_pipename have to be pre-allocated of atleast FILENAME_MAX size
- */
-int erts_run_erl_open_fifo(char *pipename,char *w_pipename,char *r_pipename) {
- int calculated_pipename = 0;
- int highest_pipe_num = 0;
- int fd;
-
- /*
- * Create FIFOs and open them
- */
-
- if(*pipename && pipename[strlen(pipename)-1] == '/') {
- /* The user wishes us to find a unique pipe name in the specified */
- /* directory */
- DIR *dirp;
- struct dirent *direntp;
-
- calculated_pipename = 1;
- dirp = opendir(pipename);
- if(!dirp) {
- ERRNO_ERR1(LOG_ERR,"Can't access pipe directory '%s'.", pipename);
- return 1;
- }
-
- /* Check the directory for existing pipes */
-
- while((direntp=readdir(dirp)) != NULL) {
- if(strncmp(direntp->d_name,PIPE_STUBNAME,PIPE_STUBLEN)==0) {
- int num = atoi(direntp->d_name+PIPE_STUBLEN+1);
- if(num > highest_pipe_num)
- highest_pipe_num = num;
- }
- }
- closedir(dirp);
- strn_catf(pipename, BUFSIZ, "%s.%d",
- PIPE_STUBNAME, highest_pipe_num+1);
- } /* if */
-
- for(;;) {
- /* write FIFO - is read FIFO for `to_erl' program */
- strn_cpy(w_pipename, BUFSIZ, pipename);
- strn_cat(w_pipename, BUFSIZ, ".r");
- if (create_fifo(w_pipename, PERM) < 0) {
- ERRNO_ERR1(LOG_ERR,"Cannot create FIFO %s for writing.",
- w_pipename);
- return 1;
- }
-
- /* read FIFO - is write FIFO for `to_erl' program */
- strn_cpy(r_pipename, BUFSIZ, pipename);
- strn_cat(r_pipename, BUFSIZ, ".w");
-
- /* Check that nobody is running run_erl already */
- if ((fd = sf_open(r_pipename, O_WRONLY|DONT_BLOCK_PLEASE, 0)) >= 0) {
- /* Open as client succeeded -- run_erl is already running! */
- sf_close(fd);
- if (calculated_pipename) {
- ++highest_pipe_num;
- strn_catf(pipename, BUFSIZ, "%s.%d",
- PIPE_STUBNAME, highest_pipe_num+1);
- continue;
- }
- ERROR1(LOG_ERR, "Erlang already running on pipe %s.\n", pipename);
- unlink(w_pipename);
- return 1;
- }
- if (create_fifo(r_pipename, PERM) < 0) {
- unlink(w_pipename);
- ERRNO_ERR1(LOG_ERR,"Cannot create FIFO %s for reading.",
- r_pipename);
- return 1;
- }
- break;
- }
- return 0;
-}
-
-/* Extract any control sequences that are ment only for run_erl
- * and should not be forwarded to the pty.
- */
-int erts_run_erl_extract_ctrl_seq(char* buf, int len, int mfd)
-{
- static const char prefix[] = "\033_";
- static const char suffix[] = "\033\\";
- char* bufend = buf + len;
- char* start = buf;
- char* command;
- char* end;
-
- for (;;) {
- start = find_str(start, bufend-start, prefix);
- if (!start) break;
-
- command = start + strlen(prefix);
- end = find_str(command, bufend-command, suffix);
- if (end) {
- unsigned col, row;
- if (sscanf(command,"version=%u", &PROTOCOL_VER)==1) {
- /*fprintf(stderr,"to_erl v%u\n", protocol_ver);*/
- }
- else if (sscanf(command,"winsize=%u,%u", &col, &row)==2) {
-#ifdef TIOCSWINSZ
- struct winsize ws;
- ws.ws_col = col;
- ws.ws_row = row;
- if (ioctl(mfd, TIOCSWINSZ, &ws) < 0) {
- ERRNO_ERR0(LOG_ERR,"Failed to set window size");
- }
-#endif
- }
- else {
- ERROR2(LOG_ERR, "Ignoring unknown ctrl command '%.*s'\n",
- (int)(end-command), command);
- }
-
- /* Remove ctrl sequence from buf */
- end += strlen(suffix);
- memmove(start, end, bufend-end);
- bufend -= end - start;
- }
- else {
- ERROR2(LOG_ERR, "Missing suffix in ctrl sequence '%.*s'\n",
- (int)(bufend-start), start);
- break;
- }
- }
- return bufend - buf;
-}
diff --git a/erts/etc/common/run_erl_common.h b/erts/etc/common/run_erl_common.h
deleted file mode 100644
index cecf7521f9..0000000000
--- a/erts/etc/common/run_erl_common.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2013. 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%
- */
-/*
- * Functions that are common to both OSE and unix implementations of run_erl
- */
-#ifndef ERL_RUN_ERL_LOG_H
-#define ERL_RUN_ERL_LOG_H
-
-#include <stdio.h>
-#include <time.h>
-#include <unistd.h>
-
-#include "run_erl_vsn.h"
-
-/* Log handling */
-int erts_run_erl_log_init(int run_daemon, char* logdir);
-int erts_run_erl_log_open(void);
-int erts_run_erl_log_close(void);
-int erts_run_erl_log_write(char *buff, size_t len);
-int erts_run_erl_log_activity(int timeout, time_t now, time_t last_activity);
-
-void erts_run_erl_log_status(const char *format,...);
-void erts_run_erl_log_error(int priority, int line, const char *format,...);
-
-int erts_run_erl_open_fifo(char *pipename,char *w_pipename,char *r_pipename);
-int erts_run_erl_log_alive_minutes(void);
-int erts_run_erl_extract_ctrl_seq(char* buf, int len, int mfd);
-
-/* File operations */
-ssize_t sf_read(int fd, void *buffer, size_t len);
-ssize_t sf_write(int fd, const void *buffer, size_t len);
-int sf_open(const char *path, int type, mode_t mode);
-int sf_close(int fd);
-int erts_run_erl_write_all(int fd, const char* buf, int len);
-char *simple_basename(char *path);
-
-#ifndef LOG_ERR
-#ifdef __OSE__
-#define LOG_ERR 0
-#else
-#define LOG_ERR NULL
-#endif
-#endif
-
-#define ERROR0(Prio,Format) erts_run_erl_log_error(Prio,__LINE__,Format"\n")
-#define ERROR1(Prio,Format,A1) erts_run_erl_log_error(Prio,__LINE__,Format"\n",A1)
-#define ERROR2(Prio,Format,A1,A2) erts_run_erl_log_error(Prio,__LINE__,Format"\n",A1,A2)
-
-#ifdef HAVE_STRERROR
-# define ADD_ERRNO(Format) "errno=%d '%s'\n"Format"\n",errno,strerror(errno)
-#else
-# define ADD_ERRNO(Format) "errno=%d\n"Format"\n",errno
-#endif
-#define ERRNO_ERR0(Prio,Format) erts_run_erl_log_error(Prio,__LINE__,ADD_ERRNO(Format))
-#define ERRNO_ERR1(Prio,Format,A1) erts_run_erl_log_error(Prio,__LINE__,ADD_ERRNO(Format),A1)
-#define ERRNO_ERR2(Prio,Format,A1,A2) erts_run_erl_log_error(Prio,__LINE__,ADD_ERRNO(Format),A1,A2)
-
-#define RUN_ERL_USAGE \
- "%s (pipe_name|pipe_dir/) log_dir \"command [parameters ...]\"" \
- "\n\nDESCRIPTION:\n" \
- "You may also set the environment variables RUN_ERL_LOG_GENERATIONS\n" \
- "and RUN_ERL_LOG_MAXSIZE to the number of log files to use and the\n" \
- "size of the log file when to switch to the next log file\n"
-
-#ifndef FILENAME_MAX
-#define FILENAME_MAX 250
-#endif
-
-#define FILENAME_BUFSIZ FILENAME_MAX
-
-#ifdef O_NONBLOCK
-# define DONT_BLOCK_PLEASE O_NONBLOCK
-#else
-# define DONT_BLOCK_PLEASE O_NDELAY
-# ifndef EAGAIN
-# define EAGAIN -3898734
-# endif
-#endif
-
-#endif
diff --git a/erts/etc/common/to_erl_common.c b/erts/etc/common/to_erl_common.c
deleted file mode 100644
index 8aa94ccfa4..0000000000
--- a/erts/etc/common/to_erl_common.c
+++ /dev/null
@@ -1,717 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2013. 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: to_erl.c
- *
- * This module implements a process that opens two specified FIFOs, one
- * for reading and one for writing; reads from its stdin, and writes what
- * it has read to the write FIF0; reads from the read FIFO, and writes to
- * its stdout.
- *
- ________ _________
- | |--<-- pipe.r (fifo1) --<--| |
- | to_erl | | run_erl | (parent)
- |________|-->-- pipe.w (fifo2) -->--|_________|
- ^ master pty
- |
- | slave pty
- ____V____
- | |
- | "erl" | (child)
- |_________|
- */
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <dirent.h>
-#include <errno.h>
-
-#ifdef __OSE__
-#include <aio.h>
-#include "ose.h"
-#include "efs.h"
-#include "ose_spi/fm.sig"
-#else /* __UNIX__ */
-#include <termios.h>
-#include <signal.h>
-#endif
-
-#ifdef HAVE_SYS_IOCTL_H
-# include <sys/ioctl.h>
-#endif
-
-#include "to_erl_common.h"
-#include "run_erl_vsn.h"
-#include "safe_string.h" /* strn_cpy, strn_catf, sn_printf, etc. */
-
-#if defined(O_NONBLOCK)
-# define DONT_BLOCK_PLEASE O_NONBLOCK
-#else
-# define DONT_BLOCK_PLEASE O_NDELAY
-# if !defined(EAGAIN)
-# define EAGAIN -3898734
-# endif
-#endif
-
-#ifdef HAVE_STRERROR
-# define STRERROR(x) strerror(x)
-#else
-# define STRERROR(x) ""
-#endif
-
-#define noDEBUG
-
-#ifdef __OSE__
-#define PIPE_DIR "/pipe/"
-#else
-#define PIPE_DIR "/tmp/"
-#endif
-#define PIPE_STUBNAME "erlang.pipe"
-#define PIPE_STUBLEN strlen(PIPE_STUBNAME)
-
-#ifdef DEBUG
-#define STATUS(s) { fprintf(stderr, (s)); fflush(stderr); }
-#else
-#define STATUS(s)
-#endif
-
-#ifndef FILENAME_MAX
-#define FILENAME_MAX 250
-#endif
-
-static int tty_eof = 0;
-static int protocol_ver = RUN_ERL_LO_VER; /* assume lowest to begin with */
-
-static int write_all(int fd, const char* buf, int len);
-static int version_handshake(char* buf, int len, int wfd);
-
-
-#ifdef __OSE__
-
-#define SET_AIO(REQ,FD,SIZE,BUFF) \
- /* Make sure to clean data structure of previous request */ \
- memset(&(REQ),0,sizeof(REQ)); \
- (REQ).aio_fildes = FD; \
- (REQ).aio_offset = FM_POSITION_CURRENT; \
- (REQ).aio_nbytes = SIZE; \
- (REQ).aio_buf = BUFF; \
- (REQ).aio_sigevent.sigev_notify = SIGEV_NONE
-
-#define READ_AIO(REQ,FD,SIZE,BUFF) \
- SET_AIO(REQ,FD,SIZE,BUFF); \
- if (aio_read(&(REQ)) != 0) \
- fprintf(stderr,"aio_read of child_read_req(%d) failed" \
- "with error %d\n",FD,errno)
-
-union SIGNAL {
- SIGSELECT signo;
- struct FmReadPtr fm_read_ptr;
-};
-
-#else /* __UNIX__ */
-static int recv_sig = 0;
-static struct termios tty_smode, tty_rmode;
-static int window_size_seq(char* buf, size_t bufsz);
-#ifdef DEBUG
-static void show_terminal_settings(struct termios *);
-#endif
-
-static void handle_ctrlc(int sig)
-{
- /* Reinstall the handler, and signal break flag */
- signal(SIGINT,handle_ctrlc);
- recv_sig = SIGINT;
-}
-
-static void handle_sigwinch(int sig)
-{
- recv_sig = SIGWINCH;
-}
-#endif
-
-static void usage(char *pname)
-{
- fprintf(stderr, "Usage: ");
- fprintf(stderr,TO_ERL_USAGE,pname);
-}
-
-int to_erl(int argc, char **argv)
-{
- char FIFO1[FILENAME_MAX], FIFO2[FILENAME_MAX];
- int i, len, wfd, rfd;
- char pipename[FILENAME_MAX];
- int pipeIx = 1;
- int force_lock = 0;
- int got_some = 0;
-
-#ifdef __OSE__
- struct aiocb stdin_read_req, pipe_read_req;
- FmHandle stdin_fh, pipe_fh;
- char *stdin_buf, *pipe_buf;
- char *buf;
- union SIGNAL *sig;
-#else /* __UNIX__ */
- char buf[BUFSIZ];
- fd_set readfds;
-#endif
-
- if (argc >= 2 && argv[1][0]=='-') {
- switch (argv[1][1]) {
- case 'h':
- usage(argv[0]);
- exit(1);
- case 'F':
- force_lock = 1;
- break;
- default:
- fprintf(stderr,"Invalid option '%s'\n",argv[1]);
- exit(1);
- }
- pipeIx = 2;
- }
-
-#ifdef DEBUG
- fprintf(stderr, "%s: pid is : %d\n", argv[0],(int)
-#ifdef __OSE__
- current_process()
-#else /* __UNIX__ */
- getpid()
-#endif
- );
-#endif
-
- strn_cpy(pipename, sizeof(pipename),
- (argv[pipeIx] ? argv[pipeIx] : PIPE_DIR));
-
- if(*pipename && pipename[strlen(pipename)-1] == '/') {
- /* The user wishes us to find a pipe name in the specified */
- /* directory */
- int highest_pipe_num = 0;
- DIR *dirp;
- struct dirent *direntp;
-
- dirp = opendir(pipename);
- if(!dirp) {
- fprintf(stderr, "Can't access pipe directory %s: %s\n", pipename, strerror(errno));
- exit(1);
- }
-
- /* Check the directory for existing pipes */
-
- while((direntp=readdir(dirp)) != NULL) {
- if(strncmp(direntp->d_name,PIPE_STUBNAME,PIPE_STUBLEN)==0) {
- int num = atoi(direntp->d_name+PIPE_STUBLEN+1);
- if(num > highest_pipe_num)
- highest_pipe_num = num;
- }
- }
- closedir(dirp);
- strn_catf(pipename, sizeof(pipename), (highest_pipe_num?"%s.%d":"%s"),
- PIPE_STUBNAME, highest_pipe_num);
- } /* if */
-
- /* read FIFO */
- sn_printf(FIFO1,sizeof(FIFO1),"%s.r",pipename);
- /* write FIFO */
- sn_printf(FIFO2,sizeof(FIFO2),"%s.w",pipename);
-
-#ifndef __OSE__
- /* Check that nobody is running to_erl on this pipe already */
- if ((wfd = open (FIFO1, O_WRONLY|DONT_BLOCK_PLEASE, 0)) >= 0) {
- /* Open as server succeeded -- to_erl is already running! */
- close(wfd);
- fprintf(stderr, "Another to_erl process already attached to pipe "
- "%s.\n", pipename);
- if (force_lock) {
- fprintf(stderr, "But we proceed anyway by force (-F).\n");
- }
- else {
- exit(1);
- }
- }
-#endif
-
- if ((rfd = open (FIFO1, O_RDONLY|DONT_BLOCK_PLEASE, 0)) < 0) {
-#ifdef DEBUG
- fprintf(stderr, "Could not open FIFO %s for reading.\n", FIFO1);
-#endif
- fprintf(stderr, "No running Erlang on pipe %s: %s\n", pipename, strerror(errno));
- exit(1);
- }
-#ifdef DEBUG
- fprintf(stderr, "to_erl: %s opened for reading\n", FIFO1);
-#endif
-
- if ((wfd = open (FIFO2, O_WRONLY|DONT_BLOCK_PLEASE, 0)) < 0) {
-#ifdef DEBUG
- fprintf(stderr, "Could not open FIFO %s for writing.\n", FIFO2);
-#endif
- fprintf(stderr, "No running Erlang on pipe %s: %s\n", pipename, strerror(errno));
- close(rfd);
- exit(1);
- }
-#ifdef DEBUG
- fprintf(stderr, "to_erl: %s opened for writing\n", FIFO2);
-#endif
-
-#ifndef __OSE__
- fprintf(stderr, "Attaching to %s (^D to exit)\n\n", pipename);
-#else
- fprintf(stderr, "Attaching to %s (^C to exit)\n\n", pipename);
-#endif
-
-#ifndef __OSE__
- /* Set break handler to our handler */
- signal(SIGINT,handle_ctrlc);
-
- /*
- * Save the current state of the terminal, and set raw mode.
- */
- if (tcgetattr(0, &tty_rmode) , 0) {
- fprintf(stderr, "Cannot get terminals current mode\n");
- exit(-1);
- }
- tty_smode = tty_rmode;
- tty_eof = '\004'; /* Ctrl+D to exit */
-#ifdef DEBUG
- show_terminal_settings(&tty_rmode);
-#endif
- tty_smode.c_iflag =
- 1*BRKINT |/*Signal interrupt on break.*/
- 1*IGNPAR |/*Ignore characters with parity errors.*/
- 1*ISTRIP |/*Strip character.*/
- 0;
-
-#if 0
-0*IGNBRK |/*Ignore break condition.*/
-0*PARMRK |/*Mark parity errors.*/
-0*INPCK |/*Enable input parity check.*/
-0*INLCR |/*Map NL to CR on input.*/
-0*IGNCR |/*Ignore CR.*/
-0*ICRNL |/*Map CR to NL on input.*/
-0*IUCLC |/*Map upper-case to lower-case on input.*/
-0*IXON |/*Enable start/stop output control.*/
-0*IXANY |/*Enable any character to restart output.*/
-0*IXOFF |/*Enable start/stop input control.*/
-0*IMAXBEL|/*Echo BEL on input line too long.*/
-#endif
-
- tty_smode.c_oflag =
- 1*OPOST |/*Post-process output.*/
- 1*ONLCR |/*Map NL to CR-NL on output.*/
-#ifdef XTABS
- 1*XTABS |/*Expand tabs to spaces. (Linux)*/
-#endif
-#ifdef OXTABS
- 1*OXTABS |/*Expand tabs to spaces. (FreeBSD)*/
-#endif
-#ifdef NL0
- 1*NL0 |/*Select newline delays*/
-#endif
-#ifdef CR0
- 1*CR0 |/*Select carriage-return delays*/
-#endif
-#ifdef TAB0
- 1*TAB0 |/*Select horizontal tab delays*/
-#endif
-#ifdef BS0
- 1*BS0 |/*Select backspace delays*/
-#endif
-#ifdef VT0
- 1*VT0 |/*Select vertical tab delays*/
-#endif
-#ifdef FF0
- 1*FF0 |/*Select form feed delays*/
-#endif
- 0;
-
-#if 0
-0*OLCUC |/*Map lower case to upper on output.*/
-0*OCRNL |/*Map CR to NL on output.*/
-0*ONOCR |/*No CR output at column 0.*/
-0*ONLRET |/*NL performs CR function.*/
-0*OFILL |/*Use fill characters for delay.*/
-0*OFDEL |/*Fill is DEL, else NULL.*/
-0*NL1 |
-0*CR1 |
-0*CR2 |
-0*CR3 |
-0*TAB1 |
-0*TAB2 |
-0*TAB3 |/*Expand tabs to spaces.*/
-0*BS1 |
-0*VT1 |
-0*FF1 |
-#endif
-
- /* JALI: removed setting the tty_smode.c_cflag flags, since this is not */
- /* advisable if this is a *real* terminal, such as the console. In fact */
- /* this may hang the entire machine, deep, deep down (signalling break */
- /* or toggling the abort switch doesn't help) */
-
- tty_smode.c_lflag =
- 0;
-
-#if 0
-0*ISIG |/*Enable signals.*/
-0*ICANON |/*Canonical input (erase and kill processing).*/
-0*XCASE |/*Canonical upper/lower presentation.*/
-0*ECHO |/*Enable echo.*/
-0*ECHOE |/*Echo erase character as BS-SP-BS.*/
-0*ECHOK |/*Echo NL after kill character.*/
-0*ECHONL |/*Echo NL.*/
-0*NOFLSH |/*Disable flush after interrupt or quit.*/
-0*TOSTOP |/*Send SIGTTOU for background output.*/
-0*ECHOCTL|/*Echo control characters as ^char, delete as ^?.*/
-0*ECHOPRT|/*Echo erase character as character erased.*/
-0*ECHOKE |/*BS-SP-BS erase entire line on line kill.*/
-0*FLUSHO |/*Output is being flushed.*/
-0*PENDIN |/*Retype pending input at next read or input character.*/
-0*IEXTEN |/*Enable extended (implementation-defined) functions.*/
-#endif
-
- tty_smode.c_cc[VMIN] =0;/* Note that VMIN is the same as VEOF! */
- tty_smode.c_cc[VTIME] =0;/* Note that VTIME is the same as VEOL! */
- tty_smode.c_cc[VINTR] =3;
-
- tcsetattr(0, TCSADRAIN, &tty_smode);
-
-#ifdef DEBUG
- show_terminal_settings(&tty_smode);
-#endif
-
-#endif /* !__OSE__ */
- /*
- * "Write a ^L to the FIFO which causes the other end to redisplay
- * the input line."
- * This does not seem to work as was intended in old comment above.
- * However, this control character is now (R12B-3) used by run_erl
- * to trigger the version handshaking between to_erl and run_erl
- * at the start of every new to_erl-session.
- */
-
- if (write(wfd, "\014", 1) < 0) {
- fprintf(stderr, "Error in writing ^L to FIFO.\n");
- }
-
-#ifdef __OSE__
- /* we have a tiny stack so we malloc the buffers */
- stdin_buf = malloc(sizeof(char) * BUFSIZ);
- pipe_buf = malloc(sizeof(char) * BUFSIZ);
-
- efs_examine_fd(rfd,FLIB_FD_HANDLE,&pipe_fh);
- efs_examine_fd(0,FLIB_FD_HANDLE,&stdin_fh);
- READ_AIO(stdin_read_req,0,BUFSIZ,stdin_buf);
- READ_AIO(pipe_read_req,rfd,BUFSIZ,pipe_buf);
-#endif
-
- /*
- * read and write
- */
- while (1) {
-#ifndef __OSE__
- FD_ZERO(&readfds);
- FD_SET(0, &readfds);
- FD_SET(rfd, &readfds);
- if (select(rfd + 1, &readfds, NULL, NULL, NULL) < 0) {
- if (recv_sig) {
- FD_ZERO(&readfds);
- }
- else {
- fprintf(stderr, "Error in select.\n");
- break;
- }
- }
- len = 0;
-
- /*
- * Read from terminal and write to FIFO
- */
- if (recv_sig) {
- switch (recv_sig) {
- case SIGINT:
- fprintf(stderr, "[Break]\n\r");
- buf[0] = '\003';
- len = 1;
- break;
- case SIGWINCH:
- len = window_size_seq(buf,sizeof(buf));
- break;
- default:
- fprintf(stderr,"Unexpected signal: %u\n",recv_sig);
- }
- recv_sig = 0;
- }
- else
-#else /* __OSE__ */
- SIGSELECT sigsel[] = {0};
- sig = receive(sigsel);
- len = 0;
-#endif
-#ifndef __OSE__
- if (FD_ISSET(0,&readfds)) {
- len = read(0, buf, sizeof(buf));
-#else /* __OSE__ */
- if (sig->signo == FM_READ_PTR_REPLY &&
- sig->fm_read_ptr.handle == stdin_fh) {
- len = sig->fm_read_ptr.status == EFS_SUCCESS ? sig->fm_read_ptr.actual : -1;
- buf = sig->fm_read_ptr.buffer;
-#endif
- if (len <= 0) {
- close(rfd);
- close(wfd);
- if (len < 0) {
- fprintf(stderr, "Error in reading from stdin.\n");
- } else {
- fprintf(stderr, "[EOF]\n\r");
- }
- break;
- }
- /* check if there is an eof character in input */
- for (i = 0; i < len-1 && buf[i] != tty_eof; i++);
- if (buf[i] == tty_eof) {
- fprintf(stderr, "[Quit]\n\r");
- break;
- }
- }
-
- if (len) {
-#ifdef DEBUG
- if(write(1, buf, len));
-#endif
- if (write_all(wfd, buf, len) != len) {
- fprintf(stderr, "Error in writing to FIFO.\n");
- close(rfd);
- close(wfd);
- break;
- }
- STATUS("\" OK\r\n");
-#ifdef __OSE__
- aio_dispatch(sig);
- READ_AIO(stdin_read_req, 0, BUFSIZ, stdin_buf);
-#endif
- }
-
- /*
- * Read from FIFO, write to terminal.
- */
-#ifndef __OSE__
- if (FD_ISSET(rfd, &readfds)) {
- STATUS("FIFO read: ");
- len = read(rfd, buf, BUFSIZ);
-#else /* __OSE__ */
- if (sig->signo == FM_READ_PTR_REPLY &&
- sig->fm_read_ptr.handle == pipe_fh) {
- len = sig->fm_read_ptr.status == EFS_SUCCESS ? sig->fm_read_ptr.actual : -1;
- buf = sig->fm_read_ptr.buffer;
-#endif
- if (len < 0 && errno == EAGAIN) {
- /*
- * No data this time, but the writing end of the FIFO is still open.
- * Do nothing.
- */
- ;
- } else if (len <= 0) {
- /*
- * Either an error or end of file. In either case, break out
- * of the loop.
- */
- close(rfd);
- close(wfd);
- if (len < 0) {
- fprintf(stderr, "Error in reading from FIFO.\n");
- } else
- fprintf(stderr, "[End]\n\r");
- break;
- } else {
- if (!got_some) {
- if ((len=version_handshake(buf,len,wfd)) < 0) {
- close(rfd);
- close(wfd);
- break;
- }
-#ifndef __OSE__
- if (protocol_ver >= 1) {
- /* Tell run_erl size of terminal window */
- signal(SIGWINCH, handle_sigwinch);
- raise(SIGWINCH);
- }
-#endif
- got_some = 1;
- }
-
- /*
- * We successfully read at least one character. Write what we got.
- */
- STATUS("Terminal write: \"");
- if (write_all(1, buf, len) != len) {
- fprintf(stderr, "Error in writing to terminal.\n");
- close(rfd);
- close(wfd);
- break;
- }
- STATUS("\" OK\r\n");
-#ifdef __OSE__
- aio_dispatch(sig);
- READ_AIO(pipe_read_req, rfd, BUFSIZ, pipe_buf);
-#endif
- }
- }
- }
-
-#ifndef __OSE__
- /*
- * Reset terminal characterstics
- * XXX
- */
- tcsetattr(0, TCSADRAIN, &tty_rmode);
-#endif
- return 0;
-}
-
-/* Call write() until entire buffer has been written or error.
- * Return len or -1.
- */
-static int write_all(int fd, const char* buf, int len)
-{
- int left = len;
- int written;
- while (left) {
- written = write(fd,buf,left);
- if (written < 0) {
- return -1;
- }
- left -= written;
- buf += written;
- }
- return len;
-}
-
-#ifndef __OSE__
-static int window_size_seq(char* buf, size_t bufsz)
-{
-#ifdef TIOCGWINSZ
- struct winsize ws;
- static const char prefix[] = "\033_";
- static const char suffix[] = "\033\\";
- /* This Esc sequence is called "Application Program Command"
- and seems suitable to use for our own customized stuff. */
-
- if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0) {
- int len = sn_printf(buf, bufsz, "%swinsize=%u,%u%s",
- prefix, ws.ws_col, ws.ws_row, suffix);
- return len;
- }
-#endif /* TIOCGWINSZ */
- return 0;
-}
-#endif /* !__OSE__ */
-
-/* to_erl run_erl
- * | |
- * |---------- '\014' -------->| (session start)
- * | |
- * |<---- "[run_erl v1-0]" ----| (version interval)
- * | |
- * |--- Esc_"version=1"Esc\ -->| (common version)
- * | |
- */
-static int version_handshake(char* buf, int len, int wfd)
-{
- unsigned re_high=0, re_low;
- char *end = find_str(buf,len,"]\n");
-
- if (end && sscanf(buf,"[run_erl v%u-%u",&re_high,&re_low)==2) {
- char wbuf[30];
- int wlen;
-
- if (re_low > RUN_ERL_HI_VER || re_high < RUN_ERL_LO_VER) {
- fprintf(stderr,"Incompatible versions: to_erl=v%u-%u run_erl=v%u-%u\n",
- RUN_ERL_HI_VER, RUN_ERL_LO_VER, re_high, re_low);
- return -1;
- }
- /* Choose highest common version */
- protocol_ver = re_high < RUN_ERL_HI_VER ? re_high : RUN_ERL_HI_VER;
-
- wlen = sn_printf(wbuf, sizeof(wbuf), "\033_version=%u\033\\",
- protocol_ver);
- if (write_all(wfd, wbuf, wlen) < 0) {
- fprintf(stderr,"Failed to send version handshake\n");
- return -1;
- }
- end += 2;
- len -= (end-buf);
- memmove(buf,end,len);
-
- }
- else { /* we assume old run_erl without version handshake */
- protocol_ver = 0;
- }
-
- if (re_high != RUN_ERL_HI_VER) {
- fprintf(stderr,"run_erl has different version, "
- "using common protocol level %u\n", protocol_ver);
- }
-
- return len;
-}
-
-
-#if defined(DEBUG) && !defined(__OSE__)
-#define S(x) ((x) > 0 ? 1 : 0)
-
-static void show_terminal_settings(struct termios *t)
-{
- fprintf(stderr,"c_iflag:\n");
- fprintf(stderr,"Signal interrupt on break: BRKINT %d\n", S(t->c_iflag & BRKINT));
- fprintf(stderr,"Map CR to NL on input: ICRNL %d\n", S(t->c_iflag & ICRNL));
- fprintf(stderr,"Ignore break condition: IGNBRK %d\n", S(t->c_iflag & IGNBRK));
- fprintf(stderr,"Ignore CR: IGNCR %d\n", S(t->c_iflag & IGNCR));
- fprintf(stderr,"Ignore char with par. err's: IGNPAR %d\n", S(t->c_iflag & IGNPAR));
- fprintf(stderr,"Map NL to CR on input: INLCR %d\n", S(t->c_iflag & INLCR));
- fprintf(stderr,"Enable input parity check: INPCK %d\n", S(t->c_iflag & INPCK));
- fprintf(stderr,"Strip character ISTRIP %d\n", S(t->c_iflag & ISTRIP));
- fprintf(stderr,"Enable start/stop input ctrl IXOFF %d\n", S(t->c_iflag & IXOFF));
- fprintf(stderr,"ditto output ctrl IXON %d\n", S(t->c_iflag & IXON));
- fprintf(stderr,"Mark parity errors PARMRK %d\n", S(t->c_iflag & PARMRK));
- fprintf(stderr,"\n");
- fprintf(stderr,"c_oflag:\n");
- fprintf(stderr,"Perform output processing OPOST %d\n", S(t->c_oflag & OPOST));
- fprintf(stderr,"\n");
- fprintf(stderr,"c_cflag:\n");
- fprintf(stderr,"Ignore modem status lines CLOCAL %d\n", S(t->c_cflag & CLOCAL));
- fprintf(stderr,"\n");
- fprintf(stderr,"c_local:\n");
- fprintf(stderr,"Enable echo ECHO %d\n", S(t->c_lflag & ECHO));
- fprintf(stderr,"\n");
- fprintf(stderr,"c_cc:\n");
- fprintf(stderr,"c_cc[VEOF] %d\n", t->c_cc[VEOF]);
-}
-#endif /* DEBUG && !__OSE__ */
diff --git a/erts/etc/common/to_erl_common.h b/erts/etc/common/to_erl_common.h
deleted file mode 100644
index a139da418f..0000000000
--- a/erts/etc/common/to_erl_common.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2013. 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 ERL_TO_ERL_H
-#define ERL_TO_ERL_H
-
-#define TO_ERL_USAGE "to_erl [-h|-F] %s\n" \
- "\t-h\tThis help text.\n" \
- "\t-f\tForce connection even though pipe is locked by other to_erl process."
-
-int to_erl(int argc, char **argv);
-
-#endif
diff --git a/erts/etc/common/typer.c b/erts/etc/common/typer.c
index 0aa0996808..77a95ccded 100644
--- a/erts/etc/common/typer.c
+++ b/erts/etc/common/typer.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -63,13 +63,14 @@ static int eargc; /* Number of arguments in eargv. */
*/
static void error(char* format, ...);
-static char* emalloc(size_t size);
+static void* emalloc(size_t size);
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__
static char* possibly_quote(char* arg);
+static void* erealloc(void *p, size_t size);
#endif
/*
@@ -118,10 +119,10 @@ main(int argc, char** argv)
int i;
int len;
/* Convert argv to utf8 */
- argv = malloc((argc+1) * sizeof(char*));
+ argv = emalloc((argc+1) * sizeof(char*));
for (i=0; i<argc; i++) {
len = WideCharToMultiByte(CP_UTF8, 0, wcargv[i], -1, NULL, 0, NULL, NULL);
- argv[i] = malloc(len*sizeof(char));
+ argv[i] = emalloc(len*sizeof(char));
WideCharToMultiByte(CP_UTF8, 0, wcargv[i], -1, argv[i], len, NULL, NULL);
}
argv[argc] = NULL;
@@ -232,7 +233,7 @@ wchar_t *make_commandline(char **argv)
buff = (wchar_t *) emalloc(siz*sizeof(wchar_t));
} else if (siz < num) {
siz = num;
- buff = (wchar_t *) realloc(buff,siz*sizeof(wchar_t));
+ buff = (wchar_t *) erealloc(buff,siz*sizeof(wchar_t));
}
p = buff;
num=0;
@@ -332,15 +333,26 @@ error(char* format, ...)
exit(1);
}
-static char*
+static void*
emalloc(size_t size)
{
- char *p = malloc(size);
+ void *p = malloc(size);
if (p == NULL)
error("Insufficient memory");
return p;
}
+#ifdef __WIN32__
+static void *
+erealloc(void *p, size_t size)
+{
+ void *res = realloc(p, size);
+ if (res == NULL)
+ error("Insufficient memory");
+ return res;
+}
+#endif
+
static char*
strsave(char* string)
{
diff --git a/erts/etc/ose/etc.lmconf b/erts/etc/ose/etc.lmconf
deleted file mode 100644
index b402b325b1..0000000000
--- a/erts/etc/ose/etc.lmconf
+++ /dev/null
@@ -1,20 +0,0 @@
-OSE_LM_STACK_SIZES=256,512,1024,2048,4096,8192,16384,65536
-OSE_LM_SIGNAL_SIZES=31,63,127,255,1023,4095,16383,65535
-OSE_LM_POOL_SIZE=0x200000
-OSE_LM_MAIN_NAME=main
-OSE_LM_MAIN_STACK_SIZE=0xF000
-OSE_LM_MAIN_PRIORITY=20
-## Has to be of a type that allows MAM
-OSE_LM_PROGRAM_TYPE=APP_RAM
-OSE_LM_DATA_INIT=YES
-OSE_LM_BSS_INIT=YES
-OSE_LM_EXEC_MODEL=SHARED
-HEAP_MAX_SIZE=1000000000
-HEAP_SMALL_BUF_INIT_SIZE=64000000
-HEAP_LARGE_BUF_THRESHOLD=16000000
-HEAP_LOCK_TYPE=2
-
-# Setting the environment variable EFS_RESOLVE_TMO on the block to 0.
-# This will eliminiate delays when trying to open files on not mounted
-# volumes.
-EFS_RESOLVE_TMO=0
diff --git a/erts/etc/ose/run_erl.c b/erts/etc/ose/run_erl.c
deleted file mode 100644
index 6775525297..0000000000
--- a/erts/etc/ose/run_erl.c
+++ /dev/null
@@ -1,664 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2013. 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: run_erl.c
- *
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-/* System includes */
-#include <aio.h>
-#include <errno.h>
-#include <dirent.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-/* OSE includes */
-#include "ose.h"
-#include "ose_spi/ose_spi.h"
-#include "efs.h"
-#include "pm.h"
-#include "ose_spi/fm.sig"
-
-/* erts includes */
-#include "run_erl.h"
-#include "run_erl_common.h"
-#include "safe_string.h" /* sn_printf, strn_cpy, strn_cat, etc */
-
-typedef struct RunErlSetup_ {
- SIGSELECT signo;
- int run_daemon;
- char *logdir;
- char *command;
- char *pipename;
- char *blockname;
-} RunErlSetup;
-
-typedef struct ProgramState_ {
- /* child process */
- int ifd, ofd;
- OSDOMAIN domain;
- PROCESS progpid, mainbid;
- struct PmProgramInfo *info;
- /* to_erl */
- char w_pipe[FILENAME_BUFSIZ],
- r_pipe[FILENAME_BUFSIZ];
-} ProgramState;
-
-union SIGNAL {
- SIGSELECT signo;
- RunErlSetup setup;
- struct FmReadPtr fm_read_ptr;
- struct FmWritePtr fm_write_ptr;
-};
-
-static OSBOOLEAN hunt_in_block(char *block_name,
- char *process_name,
- PROCESS *pid);
-static int create_child_process(char *command_string, char *blockname,
- ProgramState *state);
-
-
-static OSBOOLEAN hunt_in_block(char *block_name,
- char *process_name,
- PROCESS *pid) {
- struct OS_pid_list *list;
- PROCESS block_id = OSE_ILLEGAL_PROCESS;
- int i;
- char *name;
-
- *pid = OSE_ILLEGAL_PROCESS;
-
- list = get_bid_list(0);
-
- if (!list)
- return 0;
-
- for (i = 0; i < list->count; i++) {
-
- if (list->list[i] == get_bid(current_process()))
- continue;
-
- name = (char*)get_pid_info(list->list[i], OSE_PI_NAME);
- if (name) {
- if (strcmp(name,block_name) == 0) {
- block_id = list->list[i];
- free_buf((union SIGNAL**)&name);
- break;
- }
- free_buf((union SIGNAL**)&name);
- }
- }
-
- free_buf((union SIGNAL**)&list);
-
- if (block_id == OSE_ILLEGAL_PROCESS)
- return 0;
-
- list = get_pid_list(block_id);
-
- if (!list)
- return 0;
-
- for (i = 0; i < list->count; i++) {
- name = (char*)get_pid_info(list->list[i], OSE_PI_NAME);
- if (name) {
- if (strcmp(name,process_name) == 0) {
- *pid = list->list[i];
- free_buf((union SIGNAL**)&name);
- break;
- }
- free_buf((union SIGNAL**)&name);
- }
- }
-
- free_buf((union SIGNAL**)&list);
-
- if (*pid == OSE_ILLEGAL_PROCESS)
- return 0;
-
- return 1;
-
-}
-
-
-static int create_child_process(char *command_string, char *blockname,
- ProgramState *state) {
- char *command = command_string;
- char *argv;
- int i = 0;
- int ret_status;
- PmStatus pm_status;
- int tmp_io[2];
- int fd_arr[3];
- int ifd[2], ofd[2];
- char *handle;
- struct PmLoadModuleInfoReply *mod_info;
-
- /* Parse out cmd and argv from the command string */
- while (1) {
- if (command[i] == ' ' || command[i] == '\0') {
- if (command[i] == '\0')
- argv = NULL;
- else {
- command[i] = '\0';
- argv = command_string + i + 1;
- }
- break;
- }
- i++;
- }
-
- if (blockname)
- handle = blockname;
- else
- handle = simple_basename(command);
-
- if (ose_pm_load_module_info(handle,&mod_info) == PM_SUCCESS) {
- /* Already installed */
- free_buf((union SIGNAL**)&mod_info);
- } else if ((pm_status = ose_pm_install_load_module(0,"ELF",command,handle,0,0,NULL))
- != PM_SUCCESS) {
- ERROR1(LOG_ERR,"ose_pm_install_load_module failed - pmstatus: 0x%08x\n",
- pm_status);
- return 0;
- }
-
- state->domain = PM_NEW_DOMAIN;
-
- pm_status = ose_pm_create_program(&state->domain, handle, 0, 0 , NULL,
- &state->progpid, &state->mainbid);
-
- if (pm_status != PM_SUCCESS) {
- if (pm_status == PM_EINSTALL_HANDLE_IN_USE)
- ERROR1(LOG_ERR,"ose_pm_create_program failed - "
- "install handle \"%s\" is in use. You can specify another "
- "install handle by using the -block option to run_erl.\n",handle);
- else
- ERROR1(LOG_ERR,"ose_pm_create_program failed - pmstatus: 0x%08x\n",
- pm_status);
- return 0;
- }
-
- pm_status = ose_pm_program_info(state->progpid, &state->info);
- /* FIXME don't forget to free this ((union SIGNAL **)&info) */
- if (pm_status != PM_SUCCESS) {
- ERROR1(LOG_ERR,"ose_pm_program_info failed - pmstatus: 0x%08x\n",
- pm_status);
- return 0;
- }
-
- /* We only clone stdin+stdout, what about stderr? */
-
- /* create pipes */
- if (pipe(ifd) < 0) {
- if (errno == ENOENT)
- ERRNO_ERR0(LOG_ERR,"The /pipe file system is not available\n");
- else
- ERRNO_ERR0(LOG_ERR,"pipe ifd failed\n");
- return 0;
- }
-
- if (pipe(ofd) < 0) {
- ERRNO_ERR0(LOG_ERR,"pipe ofd failed\n");
- return 0;
- }
-
- /* FIXME Lock? */
-
- /* backup our stdin stdout */
- if ((tmp_io[0] = dup(0)) < 0) {
- ERRNO_ERR0(LOG_ERR,"dup 0 failed\n");
- return 0;
- }
-
- if ((tmp_io[1] = dup(1)) < 0) {
- ERRNO_ERR0(LOG_ERR,"dup 1 failed\n");
- return 0;
- }
-
- /* set new pipe to fd 0,1 */
- if (dup2(ifd[1], 1) < 0) {
- ERRNO_ERR0(LOG_ERR,"dup2 1 failed\n");
- return 0;
- }
-
- if (dup2(ofd[0], 0) < 0) {
- ERRNO_ERR0(LOG_ERR,"dup2 0 failed\n");
- return 0;
- }
-
- /* clone array to newly created */
- fd_arr[0] = 2; /* Number of fd's */
- fd_arr[1] = 0;
- fd_arr[2] = 1;
-
- if ((ret_status = efs_clone_array(state->info->main_process, fd_arr))
- != EFS_SUCCESS) {
- ERROR1(LOG_ERR,"efs_close_array filed, errcode: %d\n", ret_status);
- return 0;
- }
-
- if (dup2(tmp_io[1], 1) < 0) {
- ERRNO_ERR0(LOG_ERR,"restoring dup2 1 failed\n");
- return 0;
- }
-
- if (dup2(tmp_io[0], 0) < 0) {
- ERRNO_ERR0(LOG_ERR,"restoring dup2 1 failed\n");
- return 0;
- }
-
- /* close loose-ends */
- sf_close(tmp_io[0]);
- sf_close(tmp_io[1]);
- sf_close(ifd[1]);
- sf_close(ofd[0]);
- state->ifd = ifd[0];
- state->ofd = ofd[1];
-
- if (argv && set_env(state->progpid, "ARGV", argv)) {
- ERRNO_ERR0(LOG_ERR,"something went wrong with set_env\n");
- }
-
- /*
- * Start the program.
- */
- pm_status = ose_pm_start_program(state->progpid);
- if (pm_status != PM_SUCCESS) {
- ERROR1(LOG_ERR,"ose_pm_install_load_module failed - pmstatus: 0x%08x\n",
- pm_status);
- return 0;
- }
-
- return 1;
-}
-
-#define SET_AIO(REQ,FD,SIZE,BUFF) \
- /* Make sure to clean data structure of previous request */ \
- memset(&(REQ),0,sizeof(REQ)); \
- (REQ).aio_fildes = FD; \
- (REQ).aio_offset = FM_POSITION_CURRENT; \
- (REQ).aio_nbytes = SIZE; \
- (REQ).aio_buf = BUFF; \
- (REQ).aio_sigevent.sigev_notify = SIGEV_NONE
-
-#define READ_AIO(REQ,FD,SIZE,BUFF) do { \
- SET_AIO(REQ,FD,SIZE,BUFF); \
- if (aio_read(&(REQ)) != 0) \
- ERRNO_ERR1(LOG_ERR,"aio_read of child_read_req(%d) failed\n",FD); \
- } while (0)
-
-#define WRITE_AIO(FD,SIZE,BUFF) do { \
- struct aiocb *write_req = malloc(sizeof(struct aiocb)); \
- char *write_buff = malloc(sizeof(char)*SIZE); \
- memcpy(write_buff,BUFF,SIZE); \
- SET_AIO(*write_req,FD,SIZE,write_buff); \
- if (aio_write(write_req) != 0) \
- ERRNO_ERR1(LOG_ERR,"aio_write of write_req(%d) failed\n",FD); \
- } while(0)
-
-int pass_on(ProgramState *state);
-int pass_on(ProgramState *s) {
- SIGSELECT sigsel[] = {0,FM_READ_PTR_REPLY};
- union SIGNAL *sig;
- char child_read_buff[BUFSIZ], pipe_read_buff[BUFSIZ];
- struct aiocb child_read_req, pipe_read_req;
- int rfd, wfd = 0;
- FmHandle rfh, child_rfh;
- int outstanding_writes = 0, got_some = 0, child_done = 0;
-
- if ((rfd = sf_open(s->r_pipe, O_RDONLY, 0)) < 0) {
- ERRNO_ERR1(LOG_ERR,"Could not open FIFO '%s' for reading.\n", s->r_pipe);
- rfd = 0;
- return 1;
- }
-
- attach(NULL,s->progpid);
-
- /* Open the log file */
- erts_run_erl_log_open();
-
- efs_examine_fd(rfd,FLIB_FD_HANDLE,&rfh);
- efs_examine_fd(s->ifd,FLIB_FD_HANDLE,&child_rfh);
-
- READ_AIO(child_read_req,s->ifd,BUFSIZ,child_read_buff);
- READ_AIO(pipe_read_req,rfd,BUFSIZ,pipe_read_buff);
-
- while (1) {
- time_t now,last_activity;
-
- time(&last_activity);
- sig = receive_w_tmo(erts_run_erl_log_alive_minutes()*60000,sigsel);
-
- time(&now);
-
- if (sig) {
- erts_run_erl_log_activity(0,now,last_activity);
- } else {
- /* timeout */
- erts_run_erl_log_activity(1,now,last_activity);
- continue;
- }
-
- switch (sig->signo) {
- case OS_ATTACH_SIG: {
- if (rfd) { sf_close(rfd); rfd = 0; }
- free_buf(&sig);
- child_done = 1;
- /* Make sure to to let all outstanding write request finish */
- if (outstanding_writes)
- break;
- if (wfd) sf_close(wfd);
- return 0;
- }
- case FM_WRITE_PTR_REPLY: {
- if (sig->fm_write_ptr.status == EFS_SUCCESS) {
- if (sig->fm_write_ptr.actual < sig->fm_write_ptr.requested) {
- WRITE_AIO(wfd, sig->fm_write_ptr.requested-sig->fm_write_ptr.actual,
- sig->fm_write_ptr.buffer+sig->fm_write_ptr.actual);
- }
- } else {
- /* Assume to_erl has terminated. */
- sf_close(wfd);
- wfd = 0;
- }
- free((char*)sig->fm_write_ptr.buffer);
- aio_dispatch(sig);
- if ((--outstanding_writes == 0) && child_done) {
- if (wfd) sf_close(wfd);
- return 0;
- }
- break;
- }
- case FM_READ_PTR_REPLY: {
- /* Child fd */
- if (sig->fm_read_ptr.handle == child_rfh) {
-
- /* Child terminated */
- if (sig->fm_read_ptr.status != EFS_SUCCESS ||
- sig->fm_read_ptr.actual == 0) {
-
- if (rfd) { sf_close(rfd); rfd = 0; }
-
- if (sig->fm_read_ptr.status != EFS_SUCCESS) {
- ERROR0(LOG_ERR,"Erlang closed the connection.");
- aio_dispatch(sig);
- return 1;
- }
-
- /* child closed connection gracefully */
- aio_dispatch(sig);
- if (outstanding_writes) {
- child_done = 1;
- break;
- }
-
- if (wfd) sf_close(wfd);
-
- return 0;
- } else {
- erts_run_erl_log_write(sig->fm_read_ptr.buffer,
- sig->fm_read_ptr.actual);
- if (wfd) {
- WRITE_AIO(wfd, sig->fm_read_ptr.actual, sig->fm_read_ptr.buffer);
- outstanding_writes++;
- }
- aio_dispatch(sig);
- READ_AIO(child_read_req, s->ifd,BUFSIZ, child_read_buff);
- }
- /* pipe fd */
- } else if (sig->fm_read_ptr.handle == rfh) {
- if (sig->fm_read_ptr.status != EFS_SUCCESS) {
- if(rfd) sf_close(rfd);
- if(wfd) sf_close(wfd);
- aio_dispatch(sig);
- ERRNO_ERR0(LOG_ERR,"Error in reading from FIFO.");
- return 1;
- }
- if (sig->fm_read_ptr.actual == 0) {
- /* to_erl closed its end of the pipe */
- aio_dispatch(sig);
- sf_close(rfd);
- rfd = sf_open(s->r_pipe,O_RDONLY|DONT_BLOCK_PLEASE, 0);
- if (rfd < 0) {
- ERRNO_ERR1(LOG_ERR,"Could not open FIFO '%s' for reading.",
- s->r_pipe);
- rfd = 0;
- } else {
- READ_AIO(pipe_read_req,rfd,BUFSIZ,pipe_read_buff);
- }
- got_some = 0; /* reset for next session */
- } else {
- int len = sig->fm_read_ptr.actual;
- char *buffer = sig->fm_read_ptr.buffer;
- if (!wfd) {
- /* Try to open the write pipe to to_erl. Now that we got some data
- * from to_erl, to_erl should already be reading this pipe - open
- * should succeed. But in case of error, we just ignore it.
- */
- if ((wfd = sf_open(s->w_pipe, O_WRONLY|DONT_BLOCK_PLEASE, 0)) < 0) {
- erts_run_erl_log_status("Client expected on FIFO %s, "
- "but can't open (len=%d)\n",
- s->w_pipe, sig->fm_read_ptr.actual);
- sf_close(rfd);
- rfd = sf_open(s->r_pipe, O_RDONLY|DONT_BLOCK_PLEASE, 0);
- if (rfd < 0) {
- ERRNO_ERR1(LOG_ERR,"Could not open FIFO '%s' for reading.",
- s->r_pipe);
- return 1;
- }
- wfd = 0;
- } else {
-#ifdef DEBUG
- erts_run_erl_log_status("run_erl: %s opened for writing\n",
- s->w_pipe);
-#endif
- }
- }
-
- if (!got_some && wfd && buffer[0] == '\014') {
- char wbuf[30];
- int wlen = sn_printf(wbuf,sizeof(wbuf),"[run_erl v%u-%u]\n",
- RUN_ERL_HI_VER, RUN_ERL_LO_VER);
- /* For some reason this, the first write aio seems to
- not get an FM_WRITE_PTR_REPLY, so we do not do:
- outstanding_writes++;
- */
- WRITE_AIO(wfd, wlen, wbuf);
- }
- got_some = 1;
-
- /* Write the message */
-#ifdef DEBUG
- erts_run_erl_log_status("Pty master write; ");
-#endif
- len = erts_run_erl_extract_ctrl_seq(buffer,len, s->ofd);
-
- if (len > 0) {
- int wlen = erts_run_erl_write_all(s->ofd, buffer, len);
- if (wlen != len) {
- aio_dispatch(sig);
- ERRNO_ERR0(LOG_ERR,"Error in writing to terminal.");
- if(rfd) sf_close(rfd);
- if(wfd) sf_close(wfd);
- return 1;
- }
- }
-#ifdef DEBUG
- erts_run_erl_log_status("OK\n");
-#endif
- aio_dispatch(sig);
- READ_AIO(pipe_read_req,rfd,BUFSIZ,pipe_read_buff);
- }
- }
- break;
- }
- default: {
- free_buf(&sig);
- break;
- }
- }
- }
-}
-
-OS_PROCESS(run_erl_process) {
- char *logdir, *command, *blockname;
- SIGSELECT sigsel[] = {1,ERTS_SIGNAL_RUN_ERL_SETUP};
- union SIGNAL *sig = receive(sigsel);
- ProgramState state;
- char pipename[FILENAME_BUFSIZ];
-
- state.info = NULL;
-
- logdir = strdup(sig->setup.logdir);
- command = strdup(sig->setup.command);
- strn_cpy(pipename,sizeof(pipename),sig->setup.pipename);
-
- if (sig->setup.blockname)
- blockname = strdup(sig->setup.blockname);
- else
- blockname = NULL;
-
- erts_run_erl_log_init(sig->setup.run_daemon, logdir);
-
- free_buf(&sig);
-
- if (erts_run_erl_open_fifo(pipename,state.w_pipe,state.r_pipe))
- kill_proc(current_process());
-
- if (create_child_process(command,blockname,&state))
- pass_on(&state);
-
- free(logdir);
- free(command);
- if (blockname)
- free(blockname);
-
- if (state.info)
- free_buf(((union SIGNAL**)&state.info));
-
- sf_close(state.ifd);
- sf_close(state.ofd);
-
- unlink(state.w_pipe);
- unlink(state.r_pipe);
-
- kill_proc(current_process());
-}
-
-int run_erl(int argc,char **argv) {
- char *pipename, *logdir, *command, *blockname = NULL;
- int pipename_len, logdir_len, command_len, blockname_len = 0;
- int i = 1, run_daemon = 0;
- PROCESS pid;
- SIGSELECT sigsel[] = {0};
- union SIGNAL *sig;
-
- if(argc < 4) {
- fprintf(stderr,RUN_ERL_USAGE,"run_erl");
- return 1;
- }
-
- while (1) {
- if (argv[i][0] != '-')
- break;
- if (!strcmp(argv[i],"-daemon")) {
- run_daemon = 1;
- i++;
- continue;
- }
- if (!strcmp(argv[i],"-block")) {
- blockname = argv[i+1];
- blockname_len = strlen(argv[i+1]) + 1;
- i+=2;
- continue;
- }
- fprintf(stderr,RUN_ERL_USAGE,"run_erl");
- return 1;
- }
-
- pipename = argv[i++];
- logdir = argv[i++];
- command = argv[i++];
-
- /* + 1 to include NULL at end */
- logdir_len = strlen(logdir) + 1;
- command_len = strlen(command) + 1;
- pipename_len = strlen(pipename) + 1;
-
- if (run_daemon) {
- /* We request that the run_erl_process should be started from the
- main process so that it does not die when the shell command
- returns */
- PROCESS main_pid;
- hunt_in_block("run_erl","main",&main_pid);
- sig = alloc(sizeof(*sig),ERTS_SIGNAL_RUN_ERL_DAEMON);
- send(&sig,main_pid);
- sig = receive(sigsel);
- pid = sender(&sig);
- free_buf(&sig);
- } else {
- pid = create_process(OS_BG_PROC,"run_erl_process",
- run_erl_process, 0x800,
- 0, 0, 0, NULL, 0, 0);
- }
-
- sig = alloc(sizeof(RunErlSetup)+
- logdir_len+command_len+pipename_len+blockname_len,
- ERTS_SIGNAL_RUN_ERL_SETUP);
- sig->setup.run_daemon = run_daemon;
- sig->setup.logdir = ((char*)sig)+sizeof(RunErlSetup);
- sig->setup.command = ((char*)sig)+sizeof(RunErlSetup)+logdir_len;
- sig->setup.pipename = ((char*)sig)+sizeof(RunErlSetup)+logdir_len+command_len;
- if (blockname)
- sig->setup.blockname = ((char*)sig)+sizeof(RunErlSetup)+
- logdir_len+command_len+pipename_len;
- else
- sig->setup.blockname = NULL;
-
- strcpy(sig->setup.logdir,logdir);
- strcpy(sig->setup.command,command);
- strcpy(sig->setup.pipename,pipename);
- if (blockname) strcpy(sig->setup.blockname,blockname);
-
- send(&sig,pid);
-
- if (run_daemon) {
- /* We are a daemon, error msgs will be sent to ramlog */
- start(pid);
- return 1;
- }
-
- /* We are not daemon, error msgs will be sent to stderr and we block here */
- efs_clone(pid);
- start(pid);
-
- attach(NULL,pid);
- sig = receive(sigsel);
-
- return 1;
-}
diff --git a/erts/etc/ose/run_erl_main.c b/erts/etc/ose/run_erl_main.c
deleted file mode 100644
index 8895c773a1..0000000000
--- a/erts/etc/ose/run_erl_main.c
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2013. 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: run_erl_main.c
- *
- * Container for load module that installs both run_erl and to_erl command.
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include <stdio.h>
-
-#include "ose.h"
-#include "shell.h"
-
-#include "run_erl_common.h"
-#include "run_erl.h"
-#include "to_erl_common.h"
-
-union SIGNAL {
- SIGSELECT signo;
-};
-
-int main(int argc, char **argv)
-{
-
- char run_erl_usage[320],
- to_erl_usage[120];
-
- (void)stdin;(void)stdout;(void)stderr;
-
- sprintf(run_erl_usage,RUN_ERL_USAGE,"run_erl [-daemon] [-block blockname]");
- sprintf(to_erl_usage,TO_ERL_USAGE,"pipename");
-
- shell_add_cmd_attrs(
- "run_erl",run_erl_usage,
- "Redirect Erlang input and output streams",
- run_erl,DEFAULT_PROC_TYPE,DEFAULT_PRIORITY,DEFAULT_STACK_SIZE);
-
- shell_add_cmd_attrs(
- "to_erl",to_erl_usage,
- "Attach to redirected Erlang input and output streams",
- to_erl,DEFAULT_PROC_TYPE,DEFAULT_PRIORITY,DEFAULT_STACK_SIZE);
-
- while (1) {
- static const SIGSELECT sigsel[] = {0};
- union SIGNAL *sig = receive(sigsel);
-
- if (sig->signo == ERTS_SIGNAL_RUN_ERL_DAEMON) {
- PROCESS pid = create_process(OS_BG_PROC,"run_erl_daemon",
- run_erl_process, 0x800,
- 0, 0, 0, NULL, 0, 0);
- send_w_s(&sig,pid,sender(&sig));
- } else {
- printf("Got unexpected signal!");
- free_buf(&sig);
- }
- }
-
- return 1;
-}
diff --git a/erts/etc/unix/Install.src b/erts/etc/unix/Install.src
index 6634ae31d3..e71308edbe 100644
--- a/erts/etc/unix/Install.src
+++ b/erts/etc/unix/Install.src
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2013. All Rights Reserved.
+# Copyright Ericsson AB 1996-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/unix/Makefile b/erts/etc/unix/Makefile
index 04ae11de3b..2fa9cd047b 100644
--- a/erts/etc/unix/Makefile
+++ b/erts/etc/unix/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2013. All Rights Reserved.
+# Copyright Ericsson AB 2013-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/unix/README b/erts/etc/unix/README
index 6bda610a03..adc6db4300 100644
--- a/erts/etc/unix/README
+++ b/erts/etc/unix/README
@@ -1,7 +1,7 @@
%CopyrightBegin%
- Copyright Ericsson AB 1996-2009. All Rights Reserved.
+ Copyright Ericsson AB 1996-2016. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/erts/etc/unix/RELNOTES b/erts/etc/unix/RELNOTES
index 629867d2ae..7b4a1746fe 100644
--- a/erts/etc/unix/RELNOTES
+++ b/erts/etc/unix/RELNOTES
@@ -1,7 +1,7 @@
%CopyrightBegin%
- Copyright Ericsson AB 1996-2009. All Rights Reserved.
+ Copyright Ericsson AB 1996-2016. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src
index 2a806bb2f1..30f2d831b5 100644
--- a/erts/etc/unix/cerl.src
+++ b/erts/etc/unix/cerl.src
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2013. All Rights Reserved.
+# Copyright Ericsson AB 2003-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -399,19 +399,29 @@ elif [ "x$GDB" = "xdump" ]; then
cmdfile="/tmp/.cerlgdb.$$"
case "x$core" in
x/*)
- gdbcmd="$EMU_NAME ${core}"
;;
*)
dir=`pwd`
- gdbcmd="$EMU_NAME ${dir}/${core}"
+ core="${dir}/${core}"
;;
esac
- echo "set width 0
+ case `uname` in
+ Darwin)
+ echo "
+thread backtrace all
+quit
+" > $cmdfile
+ exec lldb -s $cmdfile -c ${core} $EMU_NAME
+ ;;
+ *)
+ echo "set width 0
set height 0
set verbose off
source $ROOTDIR/erts/etc/unix/etp-commands
thread apply all bt
" > $cmdfile
- exec gdb --batch --command=$cmdfile $gdbcmd
+ exec gdb --batch --command=$cmdfile $EMU_NAME $core
+ ;;
+ esac
fi
diff --git a/erts/etc/unix/dyn_erl.c b/erts/etc/unix/dyn_erl.c
index 4eebfae50a..d6d2201648 100644
--- a/erts/etc/unix/dyn_erl.c
+++ b/erts/etc/unix/dyn_erl.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009. All Rights Reserved.
+ * Copyright Ericsson AB 2009-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/etc/unix/erl.src.src b/erts/etc/unix/erl.src.src
index 94c6f9f854..959c099e8f 100644
--- a/erts/etc/unix/erl.src.src
+++ b/erts/etc/unix/erl.src.src
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2012. All Rights Reserved.
+# Copyright Ericsson AB 1996-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in
index 19d67de92f..f2babc48d2 100644
--- a/erts/etc/unix/etp-commands.in
+++ b/erts/etc/unix/etp-commands.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2014. All Rights Reserved.
+# Copyright Ericsson AB 2005-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -52,7 +52,7 @@ document etp-help
% etpf-cons, etpf-boxed,
%
% Special commands for not really terms:
-% etp-mfa, etp-cp,
+% etp-mfa, etp-cp, etp-disasm,
% etp-msgq, etpf-msgq,
% etp-stacktrace, etp-stackdump, etpf-stackdump, etp-dictdump
% etp-process-info, etp-process-memory-info
@@ -774,9 +774,9 @@ define etp-pid-1
#
set $etp_pid_1 = (Eterm)($arg0)
if ($etp_pid_1 & 0xF) == 0x3
- if (etp_arch_bits == 64 && etp_halfword == 0)
+ if (etp_arch_bits == 64)
if (etp_big_endian)
- set $etp_pid_data = (unsigned) ((((Uint64) $etp_pid_1) >> 36) & 0x0fffffff)
+ set $etp_pid_data = (unsigned) ((((Uint64) $etp_pid_1) >> 35) & 0x0fffffff)
else
set $etp_pid_data = (unsigned) ((((Uint64) $etp_pid_1) >> 4) & 0x0fffffff)
end
@@ -834,7 +834,7 @@ define etp-port-1
#
set $etp_port_1 = (Eterm)($arg0)
if ($etp_port_1 & 0xF) == 0x7
- if (etp_arch_bits == 64 && etp_halfword == 0)
+ if (etp_arch_bits == 64)
if (etp_big_endian)
set $etp_port_data = (unsigned) ((((Uint64) $etp_port_1) >> 36) & 0x0fffffff)
else
@@ -1090,12 +1090,10 @@ document etp-mfa
%---------------------------------------------------------------------------
end
-
-
-define etp-cp-1
+define etp-cp-func-info-1
# Args: Eterm cp
#
-# Non-reentrant
+# Non-reentrant, takes cp, sets $etp_cp_p to MFA in func_info
#
set $etp_cp = (Eterm)($arg0)
set $etp_ranges = &r[(int)the_active_code_index]
@@ -1137,8 +1135,21 @@ define etp-cp-1
end
end
if $etp_cp_p
+ set $cp_cp_p_offset = ($etp_cp-((Eterm)($etp_cp_p-2)))
+ else
+ set $cp_cp_p_offset = 0
+ end
+end
+
+define etp-cp-1
+# Args: Eterm cp
+#
+# Non-reentrant
+#
+ etp-cp-func-info-1 $arg0
+ if $etp_cp_p
printf "#Cp"
- etp-mfa-1 ($etp_cp_p) ($etp_cp-((Eterm)($etp_cp_p-2)))
+ etp-mfa-1 $etp_cp_p $cp_cp_p_offset
else
if $etp_cp == beam_apply+1
printf "#Cp<terminate process normally>"
@@ -1311,7 +1322,11 @@ define etp-stacktrace
set $etp_stacktrace_p = ($arg0)->stop
set $etp_stacktrace_end = ($arg0)->hend
printf "%% Stacktrace (%u): ", $etp_stacktrace_end-$etp_stacktrace_p
- etp ($arg0)->cp
+ if ($arg0)->cp == 0x0
+ printf "NULL\n"
+ else
+ etp ($arg0)->cp
+ end
while $etp_stacktrace_p < $etp_stacktrace_end
if ($etp_stacktrace_p[0] & 0x3) == 0x0
# Continuation pointer
@@ -1339,7 +1354,11 @@ define etp-stackdump
set $etp_stackdump_p = ($arg0)->stop
set $etp_stackdump_end = ($arg0)->hend
printf "%% Stackdump (%u): ", $etp_stackdump_end-$etp_stackdump_p
- etp ($arg0)->cp
+ if ($arg0)->cp == 0x0
+ printf "NULL\n"
+ else
+ etp ($arg0)->cp
+ end
while $etp_stackdump_p < $etp_stackdump_end
etp $etp_stackdump_p[0]
set $etp_stackdump_p++
@@ -1577,7 +1596,7 @@ define etp-term-dump-pid
#
set $etp_pid_1 = (Eterm)($arg0)
if ($etp_pid_1 & 0xF) == 0x3
- if (etp_arch_bits == 64 && etp_halfword == 0)
+ if (etp_arch_bits == 64)
if (etp_big_endian)
set $etp_pid_data = (unsigned) ((((Uint64) $etp_pid_1) >> 36) & 0x0fffffff)
else
@@ -1622,7 +1641,7 @@ end
define etp-pid2pix-1
# Args: Eterm
#
- if (etp_arch_bits == 64 && etp_halfword == 0)
+ if (etp_arch_bits == 64)
if (etp_big_endian)
set $etp_pix = (int) (((Uint64) $arg0) & 0x0fffffff)
else
@@ -1657,8 +1676,29 @@ end
define etp-proc-state-int
# Args: int
#
- if ($arg0 & 0xff000000)
- printf "GARBAGE | "
+ if ($arg0 & 0x80000000)
+ printf "GARBAGE<0x80000000> | "
+ end
+ if ($arg0 & 0x40000000)
+ printf "dirty-running-sys | "
+ end
+ if ($arg0 & 0x20000000)
+ printf "dirty-running | "
+ end
+ if ($arg0 & 0x10000000)
+ printf "dirty-active-sys | "
+ end
+ if ($arg0 & 0x8000000)
+ printf "dirty-io-proc | "
+ end
+ if ($arg0 & 0x4000000)
+ printf "dirty-cpu-proc | "
+ end
+ if ($arg0 & 0x2000000)
+ printf "on-heap-msgq | "
+ end
+ if ($arg0 & 0x1000000)
+ printf "off-heap-msgq | "
end
if ($arg0 & 0x800000)
printf "delayed-sys | "
@@ -1786,52 +1826,53 @@ define etp-process-info
# Args: Process*
#
printf " Pid: "
- etp-1 ($arg0)->common.id
+ set $etp_proc = ((Process*)$arg0)
+ etp-1 $etp_proc->common.id
printf "\n State: "
- etp-proc-state $arg0
+ etp-proc-state $etp_proc
if $proxy_process != 0
- printf " Pointer: (Process *) %p\n", $arg0
+ printf " Pointer: (Process *) %p\n", $etp_proc
printf " *** PROXY process struct *** refer to: \n"
- etp-pid2proc-1 $arg0->common.id
+ etp-pid2proc-1 $etp_proc->common.id
etp-process-info $proc
else
- if (*(((Uint32 *) &(((Process *) $arg0)->state))) & 0x4) == 0
- if ($arg0->common.u.alive.reg)
+ if (*(((Uint32 *) &($etp_proc->state))) & 0x4) == 0
+ if ($etp_proc->common.u.alive.reg)
printf " Registered name: "
- etp-1 $arg0->common.u.alive.reg->name
+ etp-1 $etp_proc->common.u.alive.reg->name
printf "\n"
end
end
- if ($arg0->current)
+ if ($etp_proc->current)
printf " Current function: "
- etp-1 $arg0->current[0]
+ etp-1 $etp_proc->current[0]
printf ":"
- etp-1 $arg0->current[1]
- printf "/%d\n", $arg0->current[2]
+ etp-1 $etp_proc->current[1]
+ printf "/%d\n", $etp_proc->current[2]
end
- if ($arg0->cp)
+ if ($etp_proc->cp)
printf " CP: "
- etp-cp-1 $arg0->cp
+ etp-cp-1 $etp_proc->cp
printf "\n"
end
- if ($arg0->i)
+ if ($etp_proc->i)
printf " I: "
- etp-cp-1 $arg0->i
+ etp-cp-1 $etp_proc->i
printf "\n"
end
- printf " Heap size: %ld\n", $arg0->heap_sz
- if ($arg0->old_heap)
- printf " Old-heap size: %ld\n", $arg0->old_hend - $arg0->old_heap
+ printf " Heap size: %ld\n", $etp_proc->heap_sz
+ if ($etp_proc->old_heap)
+ printf " Old-heap size: %ld\n", $etp_proc->old_hend - $etp_proc->old_heap
end
- printf " Mbuf size: %ld\n", $arg0->mbuf_sz
+ printf " Mbuf size: %ld\n", $etp_proc->mbuf_sz
if (etp_smp_compiled)
- printf " Msgq len: %ld (inner=%ld, outer=%ld)\n", ($arg0->msg.len + $arg0->msg_inq.len), $arg0->msg.len, $arg0->msg_inq.len
+ printf " Msgq len: %ld (inner=%ld, outer=%ld)\n", ($etp_proc->msg.len + $etp_proc->msg_inq.len), $etp_proc->msg.len, $etp_proc->msg_inq.len
else
- printf " Msgq len: %d\n", $arg0->msg.len
+ printf " Msgq len: %d\n", $etp_proc->msg.len
end
printf " Parent: "
- etp-1 $arg0->parent
- printf "\n Pointer: (Process *) %p\n", $arg0
+ etp-1 $etp_proc->parent
+ printf "\n Pointer: (Process *) %p\n", $etp_proc
end
end
@@ -1897,57 +1938,58 @@ end
define etp-process-memory-info
# Args: Process*
#
- if ((*(((Uint32 *) &(((Process *) $arg0)->state)))) & 0x400000)
+ set $etp_pmem_proc = ((Process *) $arg0)
+ if ((*(((Uint32 *) &($etp_pmem_proc->state)))) & 0x400000)
set $proxy_process = 1
else
set $proxy_process = 0
end
printf " "
- etp-1 $arg0->common.id
- printf ": (Process *) %p ", $arg0
+ etp-1 $etp_pmem_proc->common.id
+ printf ": (Process *) %p ", $etp_pmem_proc
if $proxy_process != 0
- printf "(Process *) %p ", $arg0
+ printf "(Process *) %p ", $etp_pmem_proc
printf " *** PROXY process struct *** refer to next: \n"
- etp-pid2proc-1 $arg0->common.id
+ etp-pid2proc-1 $etp_pmem_proc->common.id
printf " -"
etp-process-memory-info $proc
else
- printf " [Heap: %5ld", $arg0->heap_sz
- if ($arg0->old_heap)
- printf " | %5ld", $arg0->old_hend - $arg0->old_heap
+ printf " [Heap: %5ld", $etp_pmem_proc->heap_sz
+ if ($etp_pmem_proc->old_heap)
+ printf " | %5ld", $etp_pmem_proc->old_hend - $etp_pmem_proc->old_heap
else
printf " | none "
end
- printf "] [Mbuf: %5ld", $arg0->mbuf_sz
+ printf "] [Mbuf: %5ld", $etp_pmem_proc->mbuf_sz
if (etp_smp_compiled)
- printf " | %3ld (%3ld | %3ld)", ($arg0->msg.len + $arg0->msg_inq.len), $arg0->msg.len, $arg0->msg_inq.len
+ printf " | %3ld (%3ld | %3ld)", ($etp_pmem_proc->msg.len + $etp_pmem_proc->msg_inq.len), $etp_pmem_proc->msg.len, $etp_pmem_proc->msg_inq.len
else
- printf " | %3ld", $arg0->msg.len
+ printf " | %3ld", $etp_pmem_proc->msg.len
end
printf "] "
- if ($arg0->i)
+ if ($etp_pmem_proc->i)
printf " I: "
- etp-cp-1 $arg0->i
+ etp-cp-1 $etp_pmem_proc->i
printf " "
end
- if ($arg0->current)
- etp-1 $arg0->current[0]
+ if ($etp_pmem_proc->current)
+ etp-1 $etp_pmem_proc->current[0]
printf ":"
- etp-1 $arg0->current[1]
- printf "/%d ", $arg0->current[2]
+ etp-1 $etp_pmem_proc->current[1]
+ printf "/%d ", $etp_pmem_proc->current[2]
end
- if (*(((Uint32 *) &(((Process *) $arg0)->state))) & 0x4) == 0
- if ($arg0->common.u.alive.reg)
- etp-1 $arg0->common.u.alive.reg->name
+ if (*(((Uint32 *) &(((Process *) $etp_pmem_proc)->state))) & 0x4) == 0
+ if ($etp_pmem_proc->common.u.alive.reg)
+ etp-1 $etp_pmem_proc->common.u.alive.reg->name
printf " "
end
end
- if ($arg0->cp)
+ if ($etp_pmem_proc->cp)
printf " CP: "
- etp-cp-1 $arg0->cp
+ etp-cp-1 $etp_pmem_proc->cp
printf " "
end
printf "\n"
@@ -1965,7 +2007,7 @@ end
define etp-port-id2pix-1
# Args: Eterm
#
- if (etp_arch_bits == 64 && etp_halfword == 0)
+ if (etp_arch_bits == 64)
if (etp_big_endian)
set $etp_pix = (int) (((Uint64) $arg0) & 0x0fffffff)
elser
@@ -2145,23 +2187,24 @@ define etp-port-info
# Args: Port*
#
printf " Port: "
- etp-1 $arg0->common.id
- printf "\n Name: %s\n", $arg0->name
+ set $etp_pinfo_port = ((Port*)$arg0)
+ etp-1 $etp_pinfo_port->common.id
+ printf "\n Name: %s\n", $etp_pinfo_port->name
printf " State:"
- etp-port-state $arg0
+ etp-port-state $etp_pinfo_port
printf " Scheduler flags:"
- etp-port-sched-flags $arg0
- if (*(((Uint32 *) &(((Port *) $arg0)->state))) & 0x5C00) == 0
- if ($arg0->common.u.alive.reg)
+ etp-port-sched-flags $etp_pinfo_port
+ if (*(((Uint32 *) &($etp_pinfo_port->state))) & 0x5C00) == 0
+ if ($etp_pinfo_port->common.u.alive.reg)
printf " Registered name: "
- etp-1 $arg0->common.u.alive.reg->name
+ etp-1 $etp_pinfo_port->common.u.alive.reg->name
printf "\n"
end
end
printf " Connected: "
- set $connected = *(((Eterm *) &(((Port *) $arg0)->connected)))
+ set $connected = *(((Eterm *) &(((Port *) $etp_pinfo_port)->connected)))
etp-1 $connected
- printf "\n Pointer: (Port *) %p\n", $arg0
+ printf "\n Pointer: (Port *) %p\n", $etp_pinfo_port
end
document etp-port-info
@@ -2408,57 +2451,155 @@ define etp-schedulers
if (!erts_initialized)
printf "No schedulers, since system isn't initialized!\n"
else
+ set $sched_type = 0
set $sched_ix = 0
while $sched_ix < erts_no_schedulers
- printf "--- Scheduler %d ---\n", $sched_ix+1
- printf " IX: %d\n", $sched_ix
- if (erts_aligned_scheduler_data[$sched_ix].esd.cpu_id < 0)
- printf " CPU Binding: unbound\n"
- else
- printf " CPU Binding: %d\n", erts_aligned_scheduler_data[$sched_ix].esd.cpu_id
- end
- printf " Aux work Flags:"
- set $aux_work_flags = *((Uint32 *) &erts_aligned_scheduler_data[$sched_ix].esd.ssi->aux_work)
- etp-aux-work-flags $aux_work_flags
- printf " Sleep Info Flags:"
- set $ssi_flags = *((Uint32 *) &erts_aligned_scheduler_data[$sched_ix].esd.ssi->flags)
- etp-ssi-flags $ssi_flags
- printf " Pointer: (ErtsSchedulerData *) %p\n", &erts_aligned_scheduler_data[$sched_ix].esd
- printf " - Run Queue -\n"
- if (etp_smp_compiled)
- set $runq = erts_aligned_scheduler_data[$sched_ix].esd.run_queue
- else
- set $runq = &erts_aligned_run_queues[0].runq
- end
- printf " Length: total=%d", *((Uint32 *) &($runq->len))
- printf ", max=%d", *((Uint32 *) &($runq->procs.prio_info[0].len))
- printf ", high=%d", *((Uint32 *) &($runq->procs.prio_info[1].len))
- printf ", normal=%d", *((Uint32 *) &($runq->procs.prio_info[2].len))
- printf ", low=%d", *((Uint32 *) &($runq->procs.prio_info[3].len))
- printf ", port=%d\n", *((Uint32 *) &($runq->ports.info.len))
- if ($runq->misc.start)
- printf " Misc Jobs: yes\n"
- else
- printf " Misc Jobs: no\n"
- end
- set $rq_flags = *((Uint32 *) &($runq->flags))
- etp-rq-flags-int $rq_flags
- printf " Pointer: (ErtsRunQueue *) %p\n", $runq
-
+ etp-scheduler-info-internal
+ etp-run-queue-info-internal
set $sched_ix++
end
- printf "-------------------\n",
+ printf "---------------------\n"
+ if (erts_no_dirty_cpu_schedulers)
+ printf "\n\n"
+ set $sched_type = 1
+ set $sched_ix = 0
+ while $sched_ix < erts_no_dirty_cpu_schedulers
+ etp-scheduler-info-internal
+ set $sched_ix++
+ end
+ etp-run-queue-info-internal
+ printf "---------------------\n"
+ end
+ if (erts_no_dirty_io_schedulers)
+ printf "\n\n"
+ set $sched_type = 2
+ set $sched_ix = 0
+ while $sched_ix < erts_no_dirty_io_schedulers
+ etp-scheduler-info-internal
+ set $sched_ix++
+ end
+ etp-run-queue-info-internal
+ printf "---------------------\n"
+ end
end
end
document etp-schedulers
%---------------------------------------------------------------------------
% etp-schedulers
-%
+%
% Print misc info about all schedulers
%---------------------------------------------------------------------------
end
+define etp-scheduler-info-internal
+ if ($sched_type == 0)
+ printf "--- Scheduler %d ---\n", $sched_ix+1
+ set $sched_data=&erts_aligned_scheduler_data[$sched_ix].esd
+ else
+ if ($sched_type == 1)
+ printf "--- Dirty CPU Scheduler %d ---\n", $sched_ix+1
+ set $sched_data=&erts_aligned_dirty_cpu_scheduler_data[$sched_ix].esd
+ else
+ printf "--- Dirty I/O Scheduler %d ---\n", $sched_ix+1
+ set $sched_data=&erts_aligned_dirty_io_scheduler_data[$sched_ix].esd
+ end
+ end
+ printf " IX: %d\n", $sched_ix
+ if ($sched_data->cpu_id < 0)
+ printf " CPU Binding: unbound\n"
+ else
+ printf " CPU Binding: %d\n", $sched_data->cpu_id
+ end
+ printf " Aux work Flags:"
+ set $aux_work_flags = *((Uint32 *) &$sched_data->ssi->aux_work)
+ etp-aux-work-flags $aux_work_flags
+ printf " Sleep Info Flags:"
+ set $ssi_flags = *((Uint32 *) &$sched_data->ssi->flags)
+ etp-ssi-flags $ssi_flags
+ printf " Pointer: (ErtsSchedulerData *) %p\n", $sched_data
+end
+
+define etp-run-queue-info-internal
+ if ($sched_type == 0)
+ printf " - Run Queue -\n"
+ if (etp_smp_compiled)
+ set $runq = erts_aligned_scheduler_data[$sched_ix].esd.run_queue
+ else
+ set $runq = &erts_aligned_run_queues[0].runq
+ end
+ else
+ if ($sched_type == 1)
+ printf "\n--- Dirty CPU Run Queue ---\n"
+ set $runq = &erts_aligned_run_queues[-1].runq
+ else
+ printf "\n--- Dirty I/O Run Queue ---\n"
+ set $runq = &erts_aligned_run_queues[-2].runq
+ end
+ end
+ printf " Length: total=%d", *((Uint32 *) &($runq->len))
+ printf ", max=%d", *((Uint32 *) &($runq->procs.prio_info[0].len))
+ printf ", high=%d", *((Uint32 *) &($runq->procs.prio_info[1].len))
+ printf ", normal=%d", *((Uint32 *) &($runq->procs.prio_info[2].len))
+ printf ", low=%d", *((Uint32 *) &($runq->procs.prio_info[3].len))
+ printf ", port=%d\n", *((Uint32 *) &($runq->ports.info.len))
+ if ($runq->misc.start)
+ printf " Misc Jobs: yes\n"
+ else
+ printf " Misc Jobs: no\n"
+ end
+ set $rq_flags = *((Uint32 *) &($runq->flags))
+ etp-rq-flags-int $rq_flags
+ printf " Pointer: (ErtsRunQueue *) %p\n", $runq
+
+
+define etp-disasm-1
+ set $code_ptr = ((BeamInstr*)$arg0)
+ set $addr = *$code_ptr
+ set $i = 0
+ while $i < (sizeof(opc) / sizeof(OpEntry))
+ if $addr == beam_ops[$i]
+ printf "%s %d", opc[$i].name, opc[$i].sz
+ set $next_i = $code_ptr + opc[$i].sz
+ set $i += 4999
+ end
+ set $i++
+ end
+end
+
+define etp-disasm
+ etp-cp-func-info-1 $arg0
+ if $etp_cp_p == 0
+ printf "invalid argument"
+ else
+ etp-mfa-1 $etp_cp_p $cp_cp_p_offset
+ printf ": "
+ etp-disasm-1 $arg0
+ printf "\r\n"
+ while $next_i < ((BeamInstr*)$arg1)
+ set $prev_i = $next_i
+ etp-cp-func-info-1 $next_i
+ etp-mfa-1 $etp_cp_p $cp_cp_p_offset
+ printf ": "
+ etp-disasm-1 $next_i
+ if $prev_i == $next_i
+ # ptr did not advance, we are inside some strange opcode with argument
+ set $next_i++
+ printf "instr argument"
+ end
+ printf "\r\n"
+ end
+ end
+end
+
+document etp-disasm
+%---------------------------------------------------------------------------
+% etp-disasm StartI EndI
+%
+% Disassemble the code inbetween StartI and EndI
+%---------------------------------------------------------------------------
+end
+
define etp-migration-info
set $minfo = (ErtsMigrationPaths *) *((UWord *) &erts_migration_paths)
set $rq_ix = 0
@@ -2493,12 +2634,6 @@ define etp-system-info
printf "Little\n"
end
printf "Word size: %d-bit\n", etp_arch_bits
- printf "Halfword: "
- if (etp_halfword)
- printf "yes\n"
- else
- printf "no\n"
- end
printf "HiPE support: "
if (etp_hipe)
printf "yes\n"
@@ -3563,9 +3698,24 @@ document etp-block
%---------------------------------------------------------------------------
end
+define etp-smp-atomic
+ if (etp_smp_compiled)
+ set $arg1 = (($arg0).counter)
+ else
+ set $arg1 = ($arg0)
+ end
+end
+
+document etp-smp-atomic
+%---------------------------------------------------------------------------
+% Read an erts_smp_atomic_t value from $arg0 into $arg1
+%---------------------------------------------------------------------------
+end
+
define etp-carrier-blocks
set $etp_crr = (Carrier_t*) $arg0
- set $etp_alc = (Allctr_t*)($etp_crr->allctr.counter & ~7)
+ etp-smp-atomic $etp_crr->allctr $etp_alc
+ set $etp_alc = (Allctr_t*)($etp_alc & ~7)
set $etp_crr_end = ((char*)$etp_crr + ($etp_crr->chdr & ~7) - (sizeof(void*) & ~8))
set $etp_blk = (Block_t*) ((char*)$etp_crr + $etp_alc->mbc_header_size)
set $etp_prev_blk = 0
@@ -3679,6 +3829,26 @@ document etp-address-to-beam-opcode
%---------------------------------------------------------------------------
end
+define etp-compile-debug
+ shell (cd $ERL_TOP && make emulator FLAVOR=smp TYPE=debug)
+end
+
+document etp-compile-debug
+%---------------------------------------------------------------------------
+% Re-compile the debug erlang emulator
+%---------------------------------------------------------------------------
+end
+
+define etp-compile
+ shell (cd $ERL_TOP && make emulator)
+end
+
+document etp-compile
+%---------------------------------------------------------------------------
+% Re-compile the erlang emulator
+%---------------------------------------------------------------------------
+end
+
############################################################################
# Toolbox parameter handling
diff --git a/erts/etc/unix/etp-thr.py b/erts/etc/unix/etp-thr.py
index 16bc7f4016..fb82dcaf1f 100644
--- a/erts/etc/unix/etp-thr.py
+++ b/erts/etc/unix/etp-thr.py
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2013. All Rights Reserved.
+# Copyright Ericsson AB 2013-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/unix/etp_commands.erl b/erts/etc/unix/etp_commands.erl
index fe16a71876..fe8f2dd556 100644
--- a/erts/etc/unix/etp_commands.erl
+++ b/erts/etc/unix/etp_commands.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/etc/unix/etp_commands.mk b/erts/etc/unix/etp_commands.mk
index def6f7bda0..983ee9f919 100644
--- a/erts/etc/unix/etp_commands.mk
+++ b/erts/etc/unix/etp_commands.mk
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2009. All Rights Reserved.
+# Copyright Ericsson AB 2005-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/unix/format_man_pages b/erts/etc/unix/format_man_pages
index 7abe65cecb..0afff13571 100644
--- a/erts/etc/unix/format_man_pages
+++ b/erts/etc/unix/format_man_pages
@@ -3,7 +3,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2010. All Rights Reserved.
+# Copyright Ericsson AB 1996-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -107,7 +107,7 @@ case :"$TARGET" in
if [ -f $file ]; then
name=`echo $file | sed 's/\.[^.]*$//'`
sec=`echo $file | sed 's/.*\.//'`
- /usr/bin/groff -Tascii -mandoc $ERL_ROOT/man/man$sec/$file \
+ /usr/bin/groff -Tutf8 -mandoc $ERL_ROOT/man/man$sec/$file \
> $ERL_ROOT/man/cat$sec/$file
fi
done
diff --git a/erts/etc/unix/run_erl.c b/erts/etc/unix/run_erl.c
index c8414030ca..debd643da7 100644
--- a/erts/etc/unix/run_erl.c
+++ b/erts/etc/unix/run_erl.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -41,13 +41,16 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
-
#ifdef HAVE_WORKING_POSIX_OPENPT
-#ifndef _XOPEN_SOURCE
-#define _XOPEN_SOURCE 600
-#endif
+# ifndef _XOPEN_SOURCE
+ /* On OS X and BSD, we must leave _XOPEN_SOURCE undefined in order for
+ * the prototype of vsyslog() to be included.
+ */
+# if !(defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__))
+# define _XOPEN_SOURCE 600
+# endif
+# endif
#endif
-
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
@@ -65,6 +68,7 @@
#include <dirent.h>
#include <termios.h>
#include <time.h>
+
#ifdef HAVE_SYSLOG_H
# include <syslog.h>
#endif
@@ -74,6 +78,9 @@
#ifdef HAVE_UTMP_H
# include <utmp.h>
#endif
+#ifdef HAVE_LIBUTIL_H
+# include <libutil.h>
+#endif
#ifdef HAVE_UTIL_H
# include <util.h>
#endif
@@ -84,25 +91,81 @@
# include <stropts.h>
#endif
-#include "run_erl_common.h"
+#include "run_erl.h"
#include "safe_string.h" /* sn_printf, strn_cpy, strn_cat, etc */
+#ifdef O_NONBLOCK
+# define DONT_BLOCK_PLEASE O_NONBLOCK
+#else
+# define DONT_BLOCK_PLEASE O_NDELAY
+# ifndef EAGAIN
+# define EAGAIN -3898734
+# endif
+#endif
+
+#define noDEBUG
+
+#define DEFAULT_LOG_GENERATIONS 5
+#define LOG_MAX_GENERATIONS 1000 /* No more than 1000 log files */
+#define LOG_MIN_GENERATIONS 2 /* At least two to switch between */
+#define DEFAULT_LOG_MAXSIZE 100000
+#define LOG_MIN_MAXSIZE 1000 /* Smallast value for changing log file */
+#define LOG_STUBNAME "erlang.log."
+#define LOG_PERM 0664
+#define DEFAULT_LOG_ACTIVITY_MINUTES 5
+#define DEFAULT_LOG_ALIVE_MINUTES 15
+#define DEFAULT_LOG_ALIVE_FORMAT "%a %b %e %T %Z %Y"
+#define ALIVE_BUFFSIZ 256
+
+#define PERM 0600
+#define STATUSFILENAME "/run_erl.log"
+#define PIPE_STUBNAME "erlang.pipe"
+#define PIPE_STUBLEN strlen(PIPE_STUBNAME)
+
+#ifndef FILENAME_MAX
+#define FILENAME_MAX 250
+#endif
+
+#ifndef O_SYNC
+#define O_SYNC 0
+#define USE_FSYNC 1
+#endif
+
#define MAX(x,y) ((x) > (y) ? (x) : (y))
+#define FILENAME_BUFSIZ FILENAME_MAX
+
/* prototypes */
static void usage(char *);
+static int create_fifo(char *name, int perm);
static int open_pty_master(char **name, int *sfd);
static int open_pty_slave(char *name);
static void pass_on(pid_t);
static void exec_shell(char **);
+static void status(const char *format,...);
+static void error_logf(int priority, int line, const char *format,...);
static void catch_sigchild(int);
+static int next_log(int log_num);
+static int prev_log(int log_num);
+static int find_next_log_num(void);
+static int open_log(int log_num, int flags);
+static void write_to_log(int* lfd, int* log_num, char* buf, int len);
static void daemon_init(void);
+static char *simple_basename(char *path);
static void init_outbuf(void);
static int outbuf_size(void);
static void clear_outbuf(void);
static char* outbuf_first(void);
static void outbuf_delete(int bytes);
static void outbuf_append(const char* bytes, int n);
+static int write_all(int fd, const char* buf, int len);
+static int extract_ctrl_seq(char* buf, int len);
+static void set_window_size(unsigned col, unsigned row);
+
+static ssize_t sf_write(int fd, const void *buffer, size_t len);
+static ssize_t sf_read(int fd, void *buffer, size_t len);
+static int sf_open(const char *path, int flags, mode_t mode);
+static int sf_close(int fd);
#ifdef DEBUG
static void show_terminal_settings(struct termios *t);
@@ -110,11 +173,20 @@ static void show_terminal_settings(struct termios *t);
/* static data */
static char fifo1[FILENAME_BUFSIZ], fifo2[FILENAME_BUFSIZ];
+static char statusfile[FILENAME_BUFSIZ];
+static char log_dir[FILENAME_BUFSIZ];
static char pipename[FILENAME_BUFSIZ];
static FILE *stdstatus = NULL;
+static int log_generations = DEFAULT_LOG_GENERATIONS;
+static int log_maxsize = DEFAULT_LOG_MAXSIZE;
+static int log_alive_minutes = DEFAULT_LOG_ALIVE_MINUTES;
+static int log_activity_minutes = DEFAULT_LOG_ACTIVITY_MINUTES;
+static int log_alive_in_gmt = 0;
+static char log_alive_format[ALIVE_BUFFSIZ+1];
static int run_daemon = 0;
static char *program_name;
static int mfd; /* master pty fd */
+static unsigned protocol_ver = RUN_ERL_LO_VER; /* assume lowest to begin with */
/*
* Output buffer.
@@ -145,13 +217,30 @@ static char* outbuf_in;
LOG_PID|LOG_CONS|LOG_NOWAIT,LOG_USER)
#endif
+#define ERROR0(Prio,Format) error_logf(Prio,__LINE__,Format"\n")
+#define ERROR1(Prio,Format,A1) error_logf(Prio,__LINE__,Format"\n",A1)
+#define ERROR2(Prio,Format,A1,A2) error_logf(Prio,__LINE__,Format"\n",A1,A2)
+
+#ifdef HAVE_STRERROR
+# define ADD_ERRNO(Format) "errno=%d '%s'\n"Format"\n",errno,strerror(errno)
+#else
+# define ADD_ERRNO(Format) "errno=%d\n"Format"\n",errno
+#endif
+#define ERRNO_ERR0(Prio,Format) error_logf(Prio,__LINE__,ADD_ERRNO(Format))
+#define ERRNO_ERR1(Prio,Format,A1) error_logf(Prio,__LINE__,ADD_ERRNO(Format),A1)
+
+
int main(int argc, char **argv)
{
int childpid;
int sfd = -1;
- char *ptyslave=NULL;
+ int fd;
+ char *p, *ptyslave=NULL;
int i = 1;
int off_argv;
+ int calculated_pipename = 0;
+ int highest_pipe_num = 0;
+ int sleepy_child = 0;
program_name = argv[0];
@@ -162,6 +251,11 @@ int main(int argc, char **argv)
init_outbuf();
+ if (!strcmp(argv[1],"-sleepy-child")) { /* For test purpose only */
+ sleepy_child = 1;
+ ++i;
+ }
+
if (!strcmp(argv[1],"-daemon")) {
daemon_init();
++i;
@@ -169,16 +263,122 @@ int main(int argc, char **argv)
off_argv = i;
strn_cpy(pipename, sizeof(pipename), argv[i++]);
-
- erts_run_erl_log_init(run_daemon,argv[i]);
+ strn_cpy(log_dir, sizeof(log_dir), argv[i]);
+ strn_cpy(statusfile, sizeof(statusfile), log_dir);
+ strn_cat(statusfile, sizeof(statusfile), STATUSFILENAME);
#ifdef DEBUG
- erts_run_erl_log_status("%s: pid is : %d\n", argv[0], getpid());
+ status("%s: pid is : %d\n", argv[0], getpid());
#endif
- /* Open read and write fifo */
- if (erts_run_erl_open_fifo(pipename,fifo1,fifo2))
- exit(1);
+ /* Get values for LOG file handling from the environment */
+ if ((p = getenv("RUN_ERL_LOG_ALIVE_MINUTES"))) {
+ log_alive_minutes = atoi(p);
+ if (!log_alive_minutes) {
+ ERROR1(LOG_ERR,"Minimum value for RUN_ERL_LOG_ALIVE_MINUTES is 1 "
+ "(current value is %s)",p);
+ }
+ log_activity_minutes = log_alive_minutes / 3;
+ if (!log_activity_minutes) {
+ ++log_activity_minutes;
+ }
+ }
+ if ((p = getenv("RUN_ERL_LOG_ACTIVITY_MINUTES"))) {
+ log_activity_minutes = atoi(p);
+ if (!log_activity_minutes) {
+ ERROR1(LOG_ERR,"Minimum value for RUN_ERL_LOG_ACTIVITY_MINUTES is 1 "
+ "(current value is %s)",p);
+ }
+ }
+ if ((p = getenv("RUN_ERL_LOG_ALIVE_FORMAT"))) {
+ if (strlen(p) > ALIVE_BUFFSIZ) {
+ ERROR1(LOG_ERR, "RUN_ERL_LOG_ALIVE_FORMAT can contain a maximum of "
+ "%d characters", ALIVE_BUFFSIZ);
+ }
+ strn_cpy(log_alive_format, sizeof(log_alive_format), p);
+ } else {
+ strn_cpy(log_alive_format, sizeof(log_alive_format), DEFAULT_LOG_ALIVE_FORMAT);
+ }
+ if ((p = getenv("RUN_ERL_LOG_ALIVE_IN_UTC")) && strcmp(p,"0")) {
+ ++log_alive_in_gmt;
+ }
+ if ((p = getenv("RUN_ERL_LOG_GENERATIONS"))) {
+ log_generations = atoi(p);
+ if (log_generations < LOG_MIN_GENERATIONS)
+ ERROR1(LOG_ERR,"Minimum RUN_ERL_LOG_GENERATIONS is %d", LOG_MIN_GENERATIONS);
+ if (log_generations > LOG_MAX_GENERATIONS)
+ ERROR1(LOG_ERR,"Maximum RUN_ERL_LOG_GENERATIONS is %d", LOG_MAX_GENERATIONS);
+ }
+
+ if ((p = getenv("RUN_ERL_LOG_MAXSIZE"))) {
+ log_maxsize = atoi(p);
+ if (log_maxsize < LOG_MIN_MAXSIZE)
+ ERROR1(LOG_ERR,"Minimum RUN_ERL_LOG_MAXSIZE is %d", LOG_MIN_MAXSIZE);
+ }
+
+ /*
+ * Create FIFOs and open them
+ */
+
+ if(*pipename && pipename[strlen(pipename)-1] == '/') {
+ /* The user wishes us to find a unique pipe name in the specified */
+ /* directory */
+ DIR *dirp;
+ struct dirent *direntp;
+
+ calculated_pipename = 1;
+ dirp = opendir(pipename);
+ if(!dirp) {
+ ERRNO_ERR1(LOG_ERR,"Can't access pipe directory '%s'.", pipename);
+ exit(1);
+ }
+
+ /* Check the directory for existing pipes */
+
+ while((direntp=readdir(dirp)) != NULL) {
+ if(strncmp(direntp->d_name,PIPE_STUBNAME,PIPE_STUBLEN)==0) {
+ int num = atoi(direntp->d_name+PIPE_STUBLEN+1);
+ if(num > highest_pipe_num)
+ highest_pipe_num = num;
+ }
+ }
+ closedir(dirp);
+ strn_catf(pipename, sizeof(pipename), "%s.%d",
+ PIPE_STUBNAME, highest_pipe_num+1);
+ } /* if */
+
+ for(;;) {
+ /* write FIFO - is read FIFO for `to_erl' program */
+ strn_cpy(fifo1, sizeof(fifo1), pipename);
+ strn_cat(fifo1, sizeof(fifo1), ".r");
+ if (create_fifo(fifo1, PERM) < 0) {
+ ERRNO_ERR1(LOG_ERR,"Cannot create FIFO %s for writing.", fifo1);
+ exit(1);
+ }
+
+ /* read FIFO - is write FIFO for `to_erl' program */
+ strn_cpy(fifo2, sizeof(fifo2), pipename);
+ strn_cat(fifo2, sizeof(fifo2), ".w");
+
+ /* Check that nobody is running run_erl already */
+ if ((fd = sf_open(fifo2, O_WRONLY|DONT_BLOCK_PLEASE, 0)) >= 0) {
+ /* Open as client succeeded -- run_erl is already running! */
+ sf_close(fd);
+ if (calculated_pipename) {
+ ++highest_pipe_num;
+ strn_catf(pipename, sizeof(pipename), "%s.%d",
+ PIPE_STUBNAME, highest_pipe_num+1);
+ continue;
+ }
+ fprintf(stderr, "Erlang already running on pipe %s.\n", pipename);
+ exit(1);
+ }
+ if (create_fifo(fifo2, PERM) < 0) {
+ ERRNO_ERR1(LOG_ERR,"Cannot create FIFO %s for reading.", fifo2);
+ exit(1);
+ }
+ break;
+ }
/*
* Open master pseudo-terminal
@@ -198,6 +398,9 @@ int main(int argc, char **argv)
exit(1);
}
if (childpid == 0) {
+ if (sleepy_child)
+ sleep(1);
+
/* Child */
sf_close(mfd);
/* disassociate from control terminal */
@@ -250,7 +453,7 @@ int main(int argc, char **argv)
sf_close(2);
if (dup(sfd) != 0 || dup(sfd) != 1 || dup(sfd) != 2) {
- erts_run_erl_log_status("Cannot dup\n");
+ status("Cannot dup\n");
}
sf_close(sfd);
exec_shell(argv+off_argv); /* exec_shell expects argv[2] to be */
@@ -293,7 +496,9 @@ static void pass_on(pid_t childpid)
struct timeval timeout;
time_t last_activity;
char buf[BUFSIZ];
- int rfd, wfd=0;
+ char log_alive_buffer[ALIVE_BUFFSIZ+1];
+ int lognum;
+ int rfd, wfd=0, lfd=0;
int maxfd;
int ready;
int got_some = 0; /* from to_erl */
@@ -308,12 +513,13 @@ static void pass_on(pid_t childpid)
}
#ifdef DEBUG
- erts_run_erl_log_status("run_erl: %s opened for reading\n", fifo2);
+ status("run_erl: %s opened for reading\n", fifo2);
#endif
/* Open the log file */
- erts_run_erl_log_open();
+ lognum = find_next_log_num();
+ lfd = open_log(lognum, O_RDWR|O_APPEND|O_CREAT|O_SYNC);
/* Enter the work loop */
@@ -332,8 +538,7 @@ static void pass_on(pid_t childpid)
writefds_ptr = &writefds;
}
time(&last_activity);
- /* don't assume old BSD bug */
- timeout.tv_sec = erts_run_erl_log_alive_minutes()*60;
+ timeout.tv_sec = log_alive_minutes*60; /* don't assume old BSD bug */
timeout.tv_usec = 0;
ready = select(maxfd + 1, &readfds, writefds_ptr, NULL, &timeout);
if (ready < 0) {
@@ -363,7 +568,28 @@ static void pass_on(pid_t childpid)
/* Check how long time we've been inactive */
time(&now);
- erts_run_erl_log_activity(!ready,now,last_activity);
+ if(!ready || now - last_activity > log_activity_minutes*60) {
+ /* Either a time out: 15 minutes without action, */
+ /* or something is coming in right now, but it's a long time */
+ /* since last time, so let's write a time stamp this message */
+ struct tm *tmptr;
+ if (log_alive_in_gmt) {
+ tmptr = gmtime(&now);
+ } else {
+ tmptr = localtime(&now);
+ }
+ if (!strftime(log_alive_buffer, ALIVE_BUFFSIZ, log_alive_format,
+ tmptr)) {
+ strn_cpy(log_alive_buffer, sizeof(log_alive_buffer),
+ "(could not format time in 256 positions "
+ "with current format string.)");
+ }
+ log_alive_buffer[ALIVE_BUFFSIZ] = '\0';
+
+ sn_printf(buf, sizeof(buf), "\n===== %s%s\n",
+ ready?"":"ALIVE ", log_alive_buffer);
+ write_to_log(&lfd, &lognum, buf, strlen(buf));
+ }
}
/*
@@ -398,7 +624,7 @@ static void pass_on(pid_t childpid)
*/
if (FD_ISSET(mfd, &readfds)) {
#ifdef DEBUG
- erts_run_erl_log_status("Pty master read; ");
+ status("Pty master read; ");
#endif
if ((len = sf_read(mfd, buf, BUFSIZ)) <= 0) {
sf_close(rfd);
@@ -416,7 +642,7 @@ static void pass_on(pid_t childpid)
exit(0);
}
- erts_run_erl_log_write(buf, len);
+ write_to_log(&lfd, &lognum, buf, len);
/*
* Save in the output queue.
@@ -432,7 +658,7 @@ static void pass_on(pid_t childpid)
*/
if (FD_ISSET(rfd, &readfds)) {
#ifdef DEBUG
- erts_run_erl_log_status("FIFO read; ");
+ status("FIFO read; ");
#endif
if ((len = sf_read(rfd, buf, BUFSIZ)) < 0) {
sf_close(rfd);
@@ -461,7 +687,7 @@ static void pass_on(pid_t childpid)
* should succeed. But in case of error, we just ignore it.
*/
if ((wfd = sf_open(fifo1, O_WRONLY|DONT_BLOCK_PLEASE, 0)) < 0) {
- erts_run_erl_log_status("Client expected on FIFO %s, but can't open (len=%d)\n",
+ status("Client expected on FIFO %s, but can't open (len=%d)\n",
fifo1, len);
sf_close(rfd);
rfd = sf_open(fifo2, O_RDONLY|DONT_BLOCK_PLEASE, 0);
@@ -473,7 +699,7 @@ static void pass_on(pid_t childpid)
}
else {
#ifdef DEBUG
- erts_run_erl_log_status("run_erl: %s opened for writing\n", fifo1);
+ status("run_erl: %s opened for writing\n", fifo1);
#endif
}
}
@@ -489,15 +715,14 @@ static void pass_on(pid_t childpid)
/* Write the message */
#ifdef DEBUG
- erts_run_erl_log_status("Pty master write; ");
+ status("Pty master write; ");
#endif
- len = erts_run_erl_extract_ctrl_seq(buf, len, mfd);
+ len = extract_ctrl_seq(buf, len);
if(len==1 && buf[0] == '\003') {
kill(childpid,SIGINT);
- }
- else if (len>0 && erts_run_erl_write_all(mfd, buf, len) != len)
- {
+ }
+ else if (len>0 && write_all(mfd, buf, len) != len) {
ERRNO_ERR0(LOG_ERR,"Error in writing to terminal.");
sf_close(rfd);
if(wfd) sf_close(wfd);
@@ -506,7 +731,7 @@ static void pass_on(pid_t childpid)
}
}
#ifdef DEBUG
- erts_run_erl_log_status("OK\n");
+ status("OK\n");
#endif
}
}
@@ -516,25 +741,204 @@ static void catch_sigchild(int sig)
{
}
+/*
+ * next_log:
+ * Returns the index number that follows the given index number.
+ * (Wrapping after log_generations)
+ */
+static int next_log(int log_num) {
+ return log_num>=log_generations?1:log_num+1;
+}
+
+/*
+ * prev_log:
+ * Returns the index number that precedes the given index number.
+ * (Wrapping after log_generations)
+ */
+static int prev_log(int log_num) {
+ return log_num<=1?log_generations:log_num-1;
+}
+
+/*
+ * find_next_log_num()
+ * Searches through the log directory to check which logs that already
+ * exist. It finds the "hole" in the sequence, and returns the index
+ * number for the last log in the log sequence. If there is no hole, index
+ * 1 is returned.
+ */
+static int find_next_log_num(void) {
+ int i, next_gen, log_gen;
+ DIR *dirp;
+ struct dirent *direntp;
+ int log_exists[LOG_MAX_GENERATIONS+1];
+ int stub_len = strlen(LOG_STUBNAME);
+
+ /* Initialize exiting log table */
+
+ for(i=log_generations; i>=0; i--)
+ log_exists[i] = 0;
+ dirp = opendir(log_dir);
+ if(!dirp) {
+ ERRNO_ERR1(LOG_ERR,"Can't access log directory '%s'", log_dir);
+ exit(1);
+ }
+
+ /* Check the directory for existing logs */
+
+ while((direntp=readdir(dirp)) != NULL) {
+ if(strncmp(direntp->d_name,LOG_STUBNAME,stub_len)==0) {
+ int num = atoi(direntp->d_name+stub_len);
+ if(num < 1 || num > log_generations)
+ continue;
+ log_exists[num] = 1;
+ }
+ }
+ closedir(dirp);
+
+ /* Find out the next available log file number */
+
+ next_gen = 0;
+ for(i=log_generations; i>=0; i--) {
+ if(log_exists[i])
+ if(next_gen)
+ break;
+ else
+ ;
+ else
+ next_gen = i;
+ }
+
+ /* Find out the current log file number */
+
+ if(next_gen)
+ log_gen = prev_log(next_gen);
+ else
+ log_gen = 1;
+
+ return log_gen;
+} /* find_next_log_num() */
+
+/* open_log()
+ * Opens a log file (with given index) for writing. Writing may be
+ * at the end or a trucnating write, according to flags.
+ * A LOGGING STARTED and time stamp message is inserted into the log file
+ */
+static int open_log(int log_num, int flags)
+{
+ char buf[FILENAME_MAX];
+ time_t now;
+ struct tm *tmptr;
+ char log_buffer[ALIVE_BUFFSIZ+1];
+ int lfd;
+
+ /* Remove the next log (to keep a "hole" in the log sequence) */
+ sn_printf(buf, sizeof(buf), "%s/%s%d",
+ log_dir, LOG_STUBNAME, next_log(log_num));
+ unlink(buf);
+
+ /* Create or continue on the current log file */
+ sn_printf(buf, sizeof(buf), "%s/%s%d", log_dir, LOG_STUBNAME, log_num);
+ if((lfd = sf_open(buf, flags, LOG_PERM))<0){
+ ERRNO_ERR1(LOG_ERR,"Can't open log file '%s'.", buf);
+ exit(1);
+ }
+
+ /* Write a LOGGING STARTED and time stamp into the log file */
+ time(&now);
+ if (log_alive_in_gmt) {
+ tmptr = gmtime(&now);
+ } else {
+ tmptr = localtime(&now);
+ }
+ if (!strftime(log_buffer, ALIVE_BUFFSIZ, log_alive_format,
+ tmptr)) {
+ strn_cpy(log_buffer, sizeof(log_buffer),
+ "(could not format time in 256 positions "
+ "with current format string.)");
+ }
+ log_buffer[ALIVE_BUFFSIZ] = '\0';
+
+ sn_printf(buf, sizeof(buf), "\n=====\n===== LOGGING STARTED %s\n=====\n",
+ log_buffer);
+ if (write_all(lfd, buf, strlen(buf)) < 0)
+ status("Error in writing to log.\n");
+
+#if USE_FSYNC
+ fsync(lfd);
+#endif
+
+ return lfd;
+}
+
+/* write_to_log()
+ * Writes a message to a log file. If the current log file is full,
+ * a new log file is opened.
+ */
+static void write_to_log(int* lfd, int* log_num, char* buf, int len)
+{
+ int size;
+
+ /* Decide if new logfile needed, and open if so */
+
+ size = lseek(*lfd,0,SEEK_END);
+ if(size+len > log_maxsize) {
+ sf_close(*lfd);
+ *log_num = next_log(*log_num);
+ *lfd = open_log(*log_num, O_RDWR|O_CREAT|O_TRUNC|O_SYNC);
+ }
+
+ /* Write to log file */
+
+ if (write_all(*lfd, buf, len) < 0) {
+ status("Error in writing to log.\n");
+ }
+
+#if USE_FSYNC
+ fsync(*lfd);
+#endif
+}
+
+/* create_fifo()
+ * Creates a new fifo with the given name and permission.
+ */
+static int create_fifo(char *name, int perm)
+{
+ if ((mkfifo(name, perm) < 0) && (errno != EEXIST))
+ return -1;
+ return 0;
+}
+
/* open_pty_master()
* Find a master device, open and return fd and slave device name.
*/
+#ifdef HAVE_WORKING_POSIX_OPENPT
+ /*
+ * Use openpty() on OpenBSD even if we have posix_openpt()
+ * as there is a race when read from master pty returns 0
+ * if child has not yet opened slave pty.
+ * (maybe other BSD's have the same problem?)
+ */
+# if !(defined(__OpenBSD__) && defined(HAVE_OPENPTY))
+# define TRY_POSIX_OPENPT
+# endif
+#endif
+
static int open_pty_master(char **ptyslave, int *sfdp)
{
int mfd;
/* Use the posix_openpt if working, as this guarantees creation of the
slave device properly. */
-#if defined(HAVE_WORKING_POSIX_OPENPT) || (defined(__sun) && defined(__SVR4))
-# ifdef HAVE_WORKING_POSIX_OPENPT
- if ((mfd = posix_openpt(O_RDWR)) >= 0) {
+#if defined(TRY_POSIX_OPENPT) || (defined(__sun) && defined(__SVR4))
+# ifdef TRY_POSIX_OPENPT
+ mfd = posix_openpt(O_RDWR);
# elif defined(__sun) && defined(__SVR4)
mfd = sf_open("/dev/ptmx", O_RDWR, 0);
+# endif
if (mfd >= 0) {
-# endif
if ((*ptyslave = ptsname(mfd)) != NULL &&
grantpt(mfd) == 0 &&
unlockpt(mfd) == 0) {
@@ -712,9 +1116,9 @@ static void exec_shell(char **argv)
else
argv[0] = sh;
argv[1] = "-c";
- erts_run_erl_log_status("Args before exec of shell:\n");
+ status("Args before exec of shell:\n");
for (vp = argv, i = 0; *vp; vp++, i++)
- erts_run_erl_log_status("argv[%d] = %s\n", i, *vp);
+ status("argv[%d] = %s\n", i, *vp);
if (stdstatus) {
fclose(stdstatus);
}
@@ -725,6 +1129,26 @@ static void exec_shell(char **argv)
ERRNO_ERR0(LOG_ERR,"Could not execv");
}
+/* status()
+ * Prints the arguments to a status file
+ * Works like printf (see vfrpintf)
+ */
+static void status(const char *format,...)
+{
+ va_list args;
+ time_t now;
+
+ if (stdstatus == NULL)
+ stdstatus = fopen(statusfile, "w");
+ if (stdstatus == NULL)
+ return;
+ now = time(NULL);
+ fprintf(stdstatus, "run_erl [%d] %s", (int)getpid(), ctime(&now));
+ va_start(args, format);
+ vfprintf(stdstatus, format, args);
+ va_end(args);
+ fflush(stdstatus);
+}
static void daemon_init(void)
/* As R Stevens wants it, to a certain extent anyway... */
@@ -764,10 +1188,47 @@ static void daemon_init(void)
run_daemon = 1;
}
+/* error_logf()
+ * Prints the arguments to stderr or syslog
+ * Works like printf (see vfprintf)
+ */
+static void error_logf(int priority, int line, const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+
+#ifdef HAVE_SYSLOG_H
+ if (run_daemon) {
+ vsyslog(priority,format,args);
+ }
+ else
+#endif
+ {
+ time_t now = time(NULL);
+ fprintf(stderr, "run_erl:%d [%d] %s", line, (int)getpid(), ctime(&now));
+ vfprintf(stderr, format, args);
+ }
+ va_end(args);
+}
+
static void usage(char *pname)
{
- fprintf(stderr, "Usage: ");
- fprintf(stderr, RUN_ERL_USAGE, pname);
+ fprintf(stderr, "Usage: %s (pipe_name|pipe_dir/) log_dir \"command [parameters ...]\"\n", pname);
+ fprintf(stderr, "\nYou may also set the environment variables RUN_ERL_LOG_GENERATIONS\n");
+ fprintf(stderr, "and RUN_ERL_LOG_MAXSIZE to the number of log files to use and the\n");
+ fprintf(stderr, "size of the log file when to switch to the next log file\n");
+}
+
+/* Instead of making sure basename exists, we do our own */
+static char *simple_basename(char *path)
+{
+ char *ptr;
+ for (ptr = path; *ptr != '\0'; ++ptr) {
+ if (*ptr == '/') {
+ path = ptr + 1;
+ }
+ }
+ return path;
}
static void init_outbuf(void)
@@ -838,6 +1299,114 @@ static void outbuf_append(const char* buf, int n)
outbuf_in += n;
}
+/* Call write() until entire buffer has been written or error.
+ * Return len or -1.
+ */
+static int write_all(int fd, const char* buf, int len)
+{
+ int left = len;
+ int written;
+ for (;;) {
+ written = sf_write(fd,buf,left);
+ if (written == left) {
+ return len;
+ }
+ if (written < 0) {
+ return -1;
+ }
+ left -= written;
+ buf += written;
+ }
+}
+
+static ssize_t sf_read(int fd, void *buffer, size_t len) {
+ ssize_t n = 0;
+
+ do { n = read(fd, buffer, len); } while (n < 0 && errno == EINTR);
+
+ return n;
+}
+
+static ssize_t sf_write(int fd, const void *buffer, size_t len) {
+ ssize_t n = 0;
+
+ do { n = write(fd, buffer, len); } while (n < 0 && errno == EINTR);
+
+ return n;
+}
+
+static int sf_open(const char *path, int type, mode_t mode) {
+ int fd = 0;
+
+ do { fd = open(path, type, mode); } while(fd < 0 && errno == EINTR);
+
+ return fd;
+}
+static int sf_close(int fd) {
+ int res = 0;
+
+ do { res = close(fd); } while(fd < 0 && errno == EINTR);
+
+ return res;
+}
+/* Extract any control sequences that are ment only for run_erl
+ * and should not be forwarded to the pty.
+ */
+static int extract_ctrl_seq(char* buf, int len)
+{
+ static const char prefix[] = "\033_";
+ static const char suffix[] = "\033\\";
+ char* bufend = buf + len;
+ char* start = buf;
+ char* command;
+ char* end;
+
+ for (;;) {
+ start = find_str(start, bufend-start, prefix);
+ if (!start) break;
+
+ command = start + strlen(prefix);
+ end = find_str(command, bufend-command, suffix);
+ if (end) {
+ unsigned col, row;
+ if (sscanf(command,"version=%u", &protocol_ver)==1) {
+ /*fprintf(stderr,"to_erl v%u\n", protocol_ver);*/
+ }
+ else if (sscanf(command,"winsize=%u,%u", &col, &row)==2) {
+ set_window_size(col,row);
+ }
+ else {
+ ERROR2(LOG_ERR, "Ignoring unknown ctrl command '%.*s'\n",
+ (int)(end-command), command);
+ }
+
+ /* Remove ctrl sequence from buf */
+ end += strlen(suffix);
+ memmove(start, end, bufend-end);
+ bufend -= end - start;
+ }
+ else {
+ ERROR2(LOG_ERR, "Missing suffix in ctrl sequence '%.*s'\n",
+ (int)(bufend-start), start);
+ break;
+ }
+ }
+ return bufend - buf;
+}
+
+static void set_window_size(unsigned col, unsigned row)
+{
+#ifdef TIOCSWINSZ
+ struct winsize ws;
+ ws.ws_col = col;
+ ws.ws_row = row;
+ if (ioctl(mfd, TIOCSWINSZ, &ws) < 0) {
+ ERRNO_ERR0(LOG_ERR,"Failed to set window size");
+ }
+#endif
+}
+
+
#ifdef DEBUG
#define S(x) ((x) > 0 ? 1 : 0)
diff --git a/erts/etc/common/run_erl_vsn.h b/erts/etc/unix/run_erl.h
index 2c3e67e81c..83bdfdfb19 100644
--- a/erts/etc/common/run_erl_vsn.h
+++ b/erts/etc/unix/run_erl.h
@@ -1,8 +1,8 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 2008-2009. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB 2008-2016. All Rights Reserved.
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
@@ -14,7 +14,7 @@
* 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%
*/
@@ -28,3 +28,4 @@
* 0: Older, without version handshake
* 1: R12B-3, version handshake + window size ctrl
*/
+
diff --git a/erts/etc/common/safe_string.c b/erts/etc/unix/safe_string.c
index cdcdbf16f0..666022dc61 100644
--- a/erts/etc/common/safe_string.c
+++ b/erts/etc/unix/safe_string.c
@@ -1,8 +1,8 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 2008-2009. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB 2008-2016. All Rights Reserved.
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
@@ -14,12 +14,12 @@
* 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: safe_string.c
- *
+ *
* This is a bunch of generic string operation
* that are safe regarding buffer overflow.
*
@@ -121,3 +121,4 @@ void* memmove(void *dest, const void *src, size_t n)
return dest;
}
#endif /* HAVE_MEMMOVE */
+
diff --git a/erts/etc/common/safe_string.h b/erts/etc/unix/safe_string.h
index f9d2b2023a..cafd3fc71a 100644
--- a/erts/etc/common/safe_string.h
+++ b/erts/etc/unix/safe_string.h
@@ -1,8 +1,8 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 2008-2009. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB 2008-2016. All Rights Reserved.
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
@@ -14,12 +14,12 @@
* 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: safe_string.h
- *
+ *
* This is an interface to a bunch of generic string operation
* that are safe regarding buffer overflow.
*
@@ -63,3 +63,4 @@ char* find_str(const char* haystack, int size, const char* needle);
#ifndef HAVE_MEMMOVE
void* memmove(void *dest, const void *src, size_t n);
#endif
+
diff --git a/erts/etc/unix/setuid_socket_wrap.c b/erts/etc/unix/setuid_socket_wrap.c
index 59ed8eae6f..461c69ee3e 100644
--- a/erts/etc/unix/setuid_socket_wrap.c
+++ b/erts/etc/unix/setuid_socket_wrap.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/etc/unix/start.src b/erts/etc/unix/start.src
index 377f5e85c8..bdd146951f 100644
--- a/erts/etc/unix/start.src
+++ b/erts/etc/unix/start.src
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/unix/start_erl.src b/erts/etc/unix/start_erl.src
index b889101783..34e0369710 100644
--- a/erts/etc/unix/start_erl.src
+++ b/erts/etc/unix/start_erl.src
@@ -3,7 +3,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/unix/to_erl.c b/erts/etc/unix/to_erl.c
index 82d3218964..0bd469727c 100644
--- a/erts/etc/unix/to_erl.c
+++ b/erts/etc/unix/to_erl.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1996-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.
@@ -17,9 +17,592 @@
*
* %CopyrightEnd%
*/
+/*
+ * Module: to_erl.c
+ *
+ * This module implements a process that opens two specified FIFOs, one
+ * for reading and one for writing; reads from its stdin, and writes what
+ * it has read to the write FIF0; reads from the read FIFO, and writes to
+ * its stdout.
+ *
+ ________ _________
+ | |--<-- pipe.r (fifo1) --<--| |
+ | to_erl | | run_erl | (parent)
+ |________|-->-- pipe.w (fifo2) -->--|_________|
+ ^ master pty
+ |
+ | slave pty
+ ____V____
+ | |
+ | "erl" | (child)
+ |_________|
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <dirent.h>
+#include <signal.h>
+#include <errno.h>
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+
+#include "run_erl.h"
+#include "safe_string.h" /* strn_cpy, strn_catf, sn_printf, etc. */
+
+#if defined(O_NONBLOCK)
+# define DONT_BLOCK_PLEASE O_NONBLOCK
+#else
+# define DONT_BLOCK_PLEASE O_NDELAY
+# if !defined(EAGAIN)
+# define EAGAIN -3898734
+# endif
+#endif
+
+#ifdef HAVE_STRERROR
+# define STRERROR(x) strerror(x)
+#else
+# define STRERROR(x) ""
+#endif
+
+#define noDEBUG
+
+#define PIPE_DIR "/tmp/"
+#define PIPE_STUBNAME "erlang.pipe"
+#define PIPE_STUBLEN strlen(PIPE_STUBNAME)
+
+#ifdef DEBUG
+#define STATUS(s) { fprintf(stderr, (s)); fflush(stderr); }
+#else
+#define STATUS(s)
+#endif
+
+#ifndef FILENAME_MAX
+#define FILENAME_MAX 250
+#endif
+
+static struct termios tty_smode, tty_rmode;
+static int tty_eof = 0;
+static int recv_sig = 0;
+static int protocol_ver = RUN_ERL_LO_VER; /* assume lowest to begin with */
+
+static int write_all(int fd, const char* buf, int len);
+static int window_size_seq(char* buf, size_t bufsz);
+static int version_handshake(char* buf, int len, int wfd);
+#ifdef DEBUG
+static void show_terminal_settings(struct termios *);
+#endif
+
+static void handle_ctrlc(int sig)
+{
+ /* Reinstall the handler, and signal break flag */
+ signal(SIGINT,handle_ctrlc);
+ recv_sig = SIGINT;
+}
+
+static void handle_sigwinch(int sig)
+{
+ recv_sig = SIGWINCH;
+}
+
+static void usage(char *pname)
+{
+ fprintf(stderr, "Usage: %s [-h|-F] [pipe_name|pipe_dir/]\n", pname);
+ fprintf(stderr, "\t-h\tThis help text.\n");
+ fprintf(stderr, "\t-F\tForce connection even though pipe is locked by other to_erl process.\n");
+}
+
+int main(int argc, char **argv)
+{
+ char FIFO1[FILENAME_MAX], FIFO2[FILENAME_MAX];
+ int i, len, wfd, rfd;
+ fd_set readfds;
+ char buf[BUFSIZ];
+ char pipename[FILENAME_MAX];
+ int pipeIx = 1;
+ int force_lock = 0;
+ int got_some = 0;
+
+ if (argc >= 2 && argv[1][0]=='-') {
+ switch (argv[1][1]) {
+ case 'h':
+ usage(argv[0]);
+ exit(1);
+ case 'F':
+ force_lock = 1;
+ break;
+ default:
+ fprintf(stderr,"Invalid option '%s'\n",argv[1]);
+ exit(1);
+ }
+ pipeIx = 2;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "%s: pid is : %d\n", argv[0], (int)getpid());
+#endif
+
+ strn_cpy(pipename, sizeof(pipename),
+ (argv[pipeIx] ? argv[pipeIx] : PIPE_DIR));
+
+ if(*pipename && pipename[strlen(pipename)-1] == '/') {
+ /* The user wishes us to find a pipe name in the specified */
+ /* directory */
+ int highest_pipe_num = 0;
+ DIR *dirp;
+ struct dirent *direntp;
+
+ dirp = opendir(pipename);
+ if(!dirp) {
+ fprintf(stderr, "Can't access pipe directory %s: %s\n", pipename, strerror(errno));
+ exit(1);
+ }
+
+ /* Check the directory for existing pipes */
+
+ while((direntp=readdir(dirp)) != NULL) {
+ if(strncmp(direntp->d_name,PIPE_STUBNAME,PIPE_STUBLEN)==0) {
+ int num = atoi(direntp->d_name+PIPE_STUBLEN+1);
+ if(num > highest_pipe_num)
+ highest_pipe_num = num;
+ }
+ }
+ closedir(dirp);
+ strn_catf(pipename, sizeof(pipename), (highest_pipe_num?"%s.%d":"%s"),
+ PIPE_STUBNAME, highest_pipe_num);
+ } /* if */
+
+ /* read FIFO */
+ sn_printf(FIFO1,sizeof(FIFO1),"%s.r",pipename);
+ /* write FIFO */
+ sn_printf(FIFO2,sizeof(FIFO2),"%s.w",pipename);
+
+ /* Check that nobody is running to_erl on this pipe already */
+ if ((wfd = open (FIFO1, O_WRONLY|DONT_BLOCK_PLEASE, 0)) >= 0) {
+ /* Open as server succeeded -- to_erl is already running! */
+ close(wfd);
+ fprintf(stderr, "Another to_erl process already attached to pipe "
+ "%s.\n", pipename);
+ if (force_lock) {
+ fprintf(stderr, "But we proceed anyway by force (-F).\n");
+ }
+ else {
+ exit(1);
+ }
+ }
+
+ if ((rfd = open (FIFO1, O_RDONLY|DONT_BLOCK_PLEASE, 0)) < 0) {
+#ifdef DEBUG
+ fprintf(stderr, "Could not open FIFO %s for reading.\n", FIFO1);
+#endif
+ fprintf(stderr, "No running Erlang on pipe %s: %s\n", pipename, strerror(errno));
+ exit(1);
+ }
+#ifdef DEBUG
+ fprintf(stderr, "to_erl: %s opened for reading\n", FIFO1);
+#endif
+
+ if ((wfd = open (FIFO2, O_WRONLY|DONT_BLOCK_PLEASE, 0)) < 0) {
+#ifdef DEBUG
+ fprintf(stderr, "Could not open FIFO %s for writing.\n", FIFO2);
+#endif
+ fprintf(stderr, "No running Erlang on pipe %s: %s\n", pipename, strerror(errno));
+ close(rfd);
+ exit(1);
+ }
+#ifdef DEBUG
+ fprintf(stderr, "to_erl: %s opened for writing\n", FIFO2);
+#endif
+
+ fprintf(stderr, "Attaching to %s (^D to exit)\n\n", pipename);
+
+ /* Set break handler to our handler */
+ signal(SIGINT,handle_ctrlc);
+
+ /*
+ * Save the current state of the terminal, and set raw mode.
+ */
+ if (tcgetattr(0, &tty_rmode) , 0) {
+ fprintf(stderr, "Cannot get terminals current mode\n");
+ exit(-1);
+ }
+ tty_smode = tty_rmode;
+ tty_eof = '\004'; /* Ctrl+D to exit */
+#ifdef DEBUG
+ show_terminal_settings(&tty_rmode);
+#endif
+ tty_smode.c_iflag =
+ 1*BRKINT |/*Signal interrupt on break.*/
+ 1*IGNPAR |/*Ignore characters with parity errors.*/
+ 1*ISTRIP |/*Strip character.*/
+ 0;
+
+#if 0
+0*IGNBRK |/*Ignore break condition.*/
+0*PARMRK |/*Mark parity errors.*/
+0*INPCK |/*Enable input parity check.*/
+0*INLCR |/*Map NL to CR on input.*/
+0*IGNCR |/*Ignore CR.*/
+0*ICRNL |/*Map CR to NL on input.*/
+0*IUCLC |/*Map upper-case to lower-case on input.*/
+0*IXON |/*Enable start/stop output control.*/
+0*IXANY |/*Enable any character to restart output.*/
+0*IXOFF |/*Enable start/stop input control.*/
+0*IMAXBEL|/*Echo BEL on input line too long.*/
+#endif
+
+ tty_smode.c_oflag =
+ 1*OPOST |/*Post-process output.*/
+ 1*ONLCR |/*Map NL to CR-NL on output.*/
+#ifdef XTABS
+ 1*XTABS |/*Expand tabs to spaces. (Linux)*/
+#endif
+#ifdef OXTABS
+ 1*OXTABS |/*Expand tabs to spaces. (FreeBSD)*/
+#endif
+#ifdef NL0
+ 1*NL0 |/*Select newline delays*/
+#endif
+#ifdef CR0
+ 1*CR0 |/*Select carriage-return delays*/
+#endif
+#ifdef TAB0
+ 1*TAB0 |/*Select horizontal tab delays*/
+#endif
+#ifdef BS0
+ 1*BS0 |/*Select backspace delays*/
+#endif
+#ifdef VT0
+ 1*VT0 |/*Select vertical tab delays*/
+#endif
+#ifdef FF0
+ 1*FF0 |/*Select form feed delays*/
+#endif
+ 0;
+
+#if 0
+0*OLCUC |/*Map lower case to upper on output.*/
+0*OCRNL |/*Map CR to NL on output.*/
+0*ONOCR |/*No CR output at column 0.*/
+0*ONLRET |/*NL performs CR function.*/
+0*OFILL |/*Use fill characters for delay.*/
+0*OFDEL |/*Fill is DEL, else NULL.*/
+0*NL1 |
+0*CR1 |
+0*CR2 |
+0*CR3 |
+0*TAB1 |
+0*TAB2 |
+0*TAB3 |/*Expand tabs to spaces.*/
+0*BS1 |
+0*VT1 |
+0*FF1 |
+#endif
+
+ /* JALI: removed setting the tty_smode.c_cflag flags, since this is not */
+ /* advisable if this is a *real* terminal, such as the console. In fact */
+ /* this may hang the entire machine, deep, deep down (signalling break */
+ /* or toggling the abort switch doesn't help) */
+
+ tty_smode.c_lflag =
+ 0;
+
+#if 0
+0*ISIG |/*Enable signals.*/
+0*ICANON |/*Canonical input (erase and kill processing).*/
+0*XCASE |/*Canonical upper/lower presentation.*/
+0*ECHO |/*Enable echo.*/
+0*ECHOE |/*Echo erase character as BS-SP-BS.*/
+0*ECHOK |/*Echo NL after kill character.*/
+0*ECHONL |/*Echo NL.*/
+0*NOFLSH |/*Disable flush after interrupt or quit.*/
+0*TOSTOP |/*Send SIGTTOU for background output.*/
+0*ECHOCTL|/*Echo control characters as ^char, delete as ^?.*/
+0*ECHOPRT|/*Echo erase character as character erased.*/
+0*ECHOKE |/*BS-SP-BS erase entire line on line kill.*/
+0*FLUSHO |/*Output is being flushed.*/
+0*PENDIN |/*Retype pending input at next read or input character.*/
+0*IEXTEN |/*Enable extended (implementation-defined) functions.*/
+#endif
+
+ tty_smode.c_cc[VMIN] =0;/* Note that VMIN is the same as VEOF! */
+ tty_smode.c_cc[VTIME] =0;/* Note that VTIME is the same as VEOL! */
+ tty_smode.c_cc[VINTR] =3;
+
+ tcsetattr(0, TCSADRAIN, &tty_smode);
+
+#ifdef DEBUG
+ show_terminal_settings(&tty_smode);
+#endif
+ /*
+ * "Write a ^L to the FIFO which causes the other end to redisplay
+ * the input line."
+ * This does not seem to work as was intended in old comment above.
+ * However, this control character is now (R12B-3) used by run_erl
+ * to trigger the version handshaking between to_erl and run_erl
+ * at the start of every new to_erl-session.
+ */
+
+ if (write(wfd, "\014", 1) < 0) {
+ fprintf(stderr, "Error in writing ^L to FIFO.\n");
+ }
+
+ /*
+ * read and write
+ */
+ while (1) {
+ FD_ZERO(&readfds);
+ FD_SET(0, &readfds);
+ FD_SET(rfd, &readfds);
+ if (select(rfd + 1, &readfds, NULL, NULL, NULL) < 0) {
+ if (recv_sig) {
+ FD_ZERO(&readfds);
+ }
+ else {
+ fprintf(stderr, "Error in select.\n");
+ break;
+ }
+ }
+ len = 0;
+
+ /*
+ * Read from terminal and write to FIFO
+ */
+ if (recv_sig) {
+ switch (recv_sig) {
+ case SIGINT:
+ fprintf(stderr, "[Break]\n\r");
+ buf[0] = '\003';
+ len = 1;
+ break;
+ case SIGWINCH:
+ len = window_size_seq(buf,sizeof(buf));
+ break;
+ default:
+ fprintf(stderr,"Unexpected signal: %u\n",recv_sig);
+ }
+ recv_sig = 0;
+ }
+ else if (FD_ISSET(0, &readfds)) {
+ len = read(0, buf, sizeof(buf));
+ if (len <= 0) {
+ close(rfd);
+ close(wfd);
+ if (len < 0) {
+ fprintf(stderr, "Error in reading from stdin.\n");
+ } else {
+ fprintf(stderr, "[EOF]\n\r");
+ }
+ break;
+ }
+ /* check if there is an eof character in input */
+ for (i = 0; i < len && buf[i] != tty_eof; i++);
+ if (buf[i] == tty_eof) {
+ fprintf(stderr, "[Quit]\n\r");
+ break;
+ }
+ }
+
+ if (len) {
+#ifdef DEBUG
+ if(write(1, buf, len));
+#endif
+ if (write_all(wfd, buf, len) != len) {
+ fprintf(stderr, "Error in writing to FIFO.\n");
+ close(rfd);
+ close(wfd);
+ break;
+ }
+ STATUS("\" OK\r\n");
+ }
+
+ /*
+ * Read from FIFO, write to terminal.
+ */
+ if (FD_ISSET(rfd, &readfds)) {
+ STATUS("FIFO read: ");
+ len = read(rfd, buf, BUFSIZ);
+ if (len < 0 && errno == EAGAIN) {
+ /*
+ * No data this time, but the writing end of the FIFO is still open.
+ * Do nothing.
+ */
+ ;
+ } else if (len <= 0) {
+ /*
+ * Either an error or end of file. In either case, break out
+ * of the loop.
+ */
+ close(rfd);
+ close(wfd);
+ if (len < 0) {
+ fprintf(stderr, "Error in reading from FIFO.\n");
+ } else
+ fprintf(stderr, "[End]\n\r");
+ break;
+ } else {
+ if (!got_some) {
+ if ((len=version_handshake(buf,len,wfd)) < 0) {
+ close(rfd);
+ close(wfd);
+ break;
+ }
+ if (protocol_ver >= 1) {
+ /* Tell run_erl size of terminal window */
+ signal(SIGWINCH, handle_sigwinch);
+ raise(SIGWINCH);
+ }
+ got_some = 1;
+ }
+
+ /*
+ * We successfully read at least one character. Write what we got.
+ */
+ STATUS("Terminal write: \"");
+ if (write_all(1, buf, len) != len) {
+ fprintf(stderr, "Error in writing to terminal.\n");
+ close(rfd);
+ close(wfd);
+ break;
+ }
+ STATUS("\" OK\r\n");
+ }
+ }
+ }
+
+ /*
+ * Reset terminal characterstics
+ * XXX
+ */
+ tcsetattr(0, TCSADRAIN, &tty_rmode);
+ return 0;
+}
+
+/* Call write() until entire buffer has been written or error.
+ * Return len or -1.
+ */
+static int write_all(int fd, const char* buf, int len)
+{
+ int left = len;
+ int written;
+ while (left) {
+ written = write(fd,buf,left);
+ if (written < 0) {
+ return -1;
+ }
+ left -= written;
+ buf += written;
+ }
+ return len;
+}
+
+static int window_size_seq(char* buf, size_t bufsz)
+{
+#ifdef TIOCGWINSZ
+ struct winsize ws;
+ static const char prefix[] = "\033_";
+ static const char suffix[] = "\033\\";
+ /* This Esc sequence is called "Application Program Command"
+ and seems suitable to use for our own customized stuff. */
+
+ if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0) {
+ int len = sn_printf(buf, bufsz, "%swinsize=%u,%u%s",
+ prefix, ws.ws_col, ws.ws_row, suffix);
+ return len;
+ }
+#endif /* TIOCGWINSZ */
+ return 0;
+}
+
+/* to_erl run_erl
+ * | |
+ * |---------- '\014' -------->| (session start)
+ * | |
+ * |<---- "[run_erl v1-0]" ----| (version interval)
+ * | |
+ * |--- Esc_"version=1"Esc\ -->| (common version)
+ * | |
+ */
+static int version_handshake(char* buf, int len, int wfd)
+{
+ unsigned re_high=0, re_low;
+ char *end = find_str(buf,len,"]\n");
+
+ if (end && sscanf(buf,"[run_erl v%u-%u",&re_high,&re_low)==2) {
+ char wbuf[30];
+ int wlen;
+
+ if (re_low > RUN_ERL_HI_VER || re_high < RUN_ERL_LO_VER) {
+ fprintf(stderr,"Incompatible versions: to_erl=v%u-%u run_erl=v%u-%u\n",
+ RUN_ERL_HI_VER, RUN_ERL_LO_VER, re_high, re_low);
+ return -1;
+ }
+ /* Choose highest common version */
+ protocol_ver = re_high < RUN_ERL_HI_VER ? re_high : RUN_ERL_HI_VER;
+
+ wlen = sn_printf(wbuf, sizeof(wbuf), "\033_version=%u\033\\",
+ protocol_ver);
+ if (write_all(wfd, wbuf, wlen) < 0) {
+ fprintf(stderr,"Failed to send version handshake\n");
+ return -1;
+ }
+ end += 2;
+ len -= (end-buf);
+ memmove(buf,end,len);
+
+ }
+ else { /* we assume old run_erl without version handshake */
+ protocol_ver = 0;
+ }
+
+ if (re_high != RUN_ERL_HI_VER) {
+ fprintf(stderr,"run_erl has different version, "
+ "using common protocol level %u\n", protocol_ver);
+ }
+
+ return len;
+}
+
-#include "to_erl_common.h"
+#ifdef DEBUG
+#define S(x) ((x) > 0 ? 1 : 0)
-int main(int argc,char **argv) {
- return to_erl(argc,argv);
+static void show_terminal_settings(struct termios *t)
+{
+ fprintf(stderr,"c_iflag:\n");
+ fprintf(stderr,"Signal interrupt on break: BRKINT %d\n", S(t->c_iflag & BRKINT));
+ fprintf(stderr,"Map CR to NL on input: ICRNL %d\n", S(t->c_iflag & ICRNL));
+ fprintf(stderr,"Ignore break condition: IGNBRK %d\n", S(t->c_iflag & IGNBRK));
+ fprintf(stderr,"Ignore CR: IGNCR %d\n", S(t->c_iflag & IGNCR));
+ fprintf(stderr,"Ignore char with par. err's: IGNPAR %d\n", S(t->c_iflag & IGNPAR));
+ fprintf(stderr,"Map NL to CR on input: INLCR %d\n", S(t->c_iflag & INLCR));
+ fprintf(stderr,"Enable input parity check: INPCK %d\n", S(t->c_iflag & INPCK));
+ fprintf(stderr,"Strip character ISTRIP %d\n", S(t->c_iflag & ISTRIP));
+ fprintf(stderr,"Enable start/stop input ctrl IXOFF %d\n", S(t->c_iflag & IXOFF));
+ fprintf(stderr,"ditto output ctrl IXON %d\n", S(t->c_iflag & IXON));
+ fprintf(stderr,"Mark parity errors PARMRK %d\n", S(t->c_iflag & PARMRK));
+ fprintf(stderr,"\n");
+ fprintf(stderr,"c_oflag:\n");
+ fprintf(stderr,"Perform output processing OPOST %d\n", S(t->c_oflag & OPOST));
+ fprintf(stderr,"\n");
+ fprintf(stderr,"c_cflag:\n");
+ fprintf(stderr,"Ignore modem status lines CLOCAL %d\n", S(t->c_cflag & CLOCAL));
+ fprintf(stderr,"\n");
+ fprintf(stderr,"c_local:\n");
+ fprintf(stderr,"Enable echo ECHO %d\n", S(t->c_lflag & ECHO));
+ fprintf(stderr,"\n");
+ fprintf(stderr,"c_cc:\n");
+ fprintf(stderr,"c_cc[VEOF] %d\n", t->c_cc[VEOF]);
}
+#endif
diff --git a/erts/etc/win32/Install.c b/erts/etc/win32/Install.c
index 82bae947d4..43930ff284 100644
--- a/erts/etc/win32/Install.c
+++ b/erts/etc/win32/Install.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2003-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/Makefile b/erts/etc/win32/Makefile
index 12c04fc9a5..c6376ebe74 100644
--- a/erts/etc/win32/Makefile
+++ b/erts/etc/win32/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2012. All Rights Reserved.
+# Copyright Ericsson AB 1996-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/Nmakefile.start_erl b/erts/etc/win32/Nmakefile.start_erl
index cf83713bab..00d22461fb 100644
--- a/erts/etc/win32/Nmakefile.start_erl
+++ b/erts/etc/win32/Nmakefile.start_erl
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2009. All Rights Reserved.
+# Copyright Ericsson AB 1998-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/beam.rc b/erts/etc/win32/beam.rc
index 9e137ecd62..0aaabf1097 100644
--- a/erts/etc/win32/beam.rc
+++ b/erts/etc/win32/beam.rc
@@ -1,7 +1,7 @@
//
// %CopyrightBegin%
//
-// Copyright Ericsson AB 1997-2009. All Rights Reserved.
+// Copyright Ericsson AB 1997-2016. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/cygwin_tools/erl b/erts/etc/win32/cygwin_tools/erl
index 51a7be5584..897bbfac87 100755
--- a/erts/etc/win32/cygwin_tools/erl
+++ b/erts/etc/win32/cygwin_tools/erl
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2009. All Rights Reserved.
+# Copyright Ericsson AB 2002-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/cygwin_tools/erlc b/erts/etc/win32/cygwin_tools/erlc
index 588b53b1be..eda9fdb7fc 100755
--- a/erts/etc/win32/cygwin_tools/erlc
+++ b/erts/etc/win32/cygwin_tools/erlc
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2009. All Rights Reserved.
+# Copyright Ericsson AB 2002-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/cygwin_tools/javac.sh b/erts/etc/win32/cygwin_tools/javac.sh
index f2f97ef152..f53e5a3cc6 100755
--- a/erts/etc/win32/cygwin_tools/javac.sh
+++ b/erts/etc/win32/cygwin_tools/javac.sh
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2009. All Rights Reserved.
+# Copyright Ericsson AB 2002-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/cygwin_tools/make_bootstrap_ini.sh b/erts/etc/win32/cygwin_tools/make_bootstrap_ini.sh
index 0f87f3d077..377c0dda6c 100755
--- a/erts/etc/win32/cygwin_tools/make_bootstrap_ini.sh
+++ b/erts/etc/win32/cygwin_tools/make_bootstrap_ini.sh
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2009. All Rights Reserved.
+# Copyright Ericsson AB 2003-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/cygwin_tools/make_local_ini.sh b/erts/etc/win32/cygwin_tools/make_local_ini.sh
index 0bcb362ffe..e5db98468b 100755
--- a/erts/etc/win32/cygwin_tools/make_local_ini.sh
+++ b/erts/etc/win32/cygwin_tools/make_local_ini.sh
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2009. All Rights Reserved.
+# Copyright Ericsson AB 2003-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/cygwin_tools/mingw/ar.sh b/erts/etc/win32/cygwin_tools/mingw/ar.sh
index 2ebfe4e435..de166284d8 100755
--- a/erts/etc/win32/cygwin_tools/mingw/ar.sh
+++ b/erts/etc/win32/cygwin_tools/mingw/ar.sh
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2006-2009. All Rights Reserved.
+# Copyright Ericsson AB 2006-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/cygwin_tools/mingw/cc.sh b/erts/etc/win32/cygwin_tools/mingw/cc.sh
index 5993f70686..0b321a5099 100755
--- a/erts/etc/win32/cygwin_tools/mingw/cc.sh
+++ b/erts/etc/win32/cygwin_tools/mingw/cc.sh
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2006-2009. All Rights Reserved.
+# Copyright Ericsson AB 2006-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/cygwin_tools/mingw/coffix.c b/erts/etc/win32/cygwin_tools/mingw/coffix.c
index 383c422008..ff3f3de3d1 100644
--- a/erts/etc/win32/cygwin_tools/mingw/coffix.c
+++ b/erts/etc/win32/cygwin_tools/mingw/coffix.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/cygwin_tools/mingw/emu_cc.sh b/erts/etc/win32/cygwin_tools/mingw/emu_cc.sh
index 8da91dd0ef..5f510467fe 100755
--- a/erts/etc/win32/cygwin_tools/mingw/emu_cc.sh
+++ b/erts/etc/win32/cygwin_tools/mingw/emu_cc.sh
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2006-2009. All Rights Reserved.
+# Copyright Ericsson AB 2006-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/cygwin_tools/mingw/ld.sh b/erts/etc/win32/cygwin_tools/mingw/ld.sh
index 16caf0b6d2..8b7e1a54c4 100755
--- a/erts/etc/win32/cygwin_tools/mingw/ld.sh
+++ b/erts/etc/win32/cygwin_tools/mingw/ld.sh
@@ -3,7 +3,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2006-2009. All Rights Reserved.
+# Copyright Ericsson AB 2006-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/cygwin_tools/mingw/mc.sh b/erts/etc/win32/cygwin_tools/mingw/mc.sh
index 4462bfc5d3..8228f8699e 100755
--- a/erts/etc/win32/cygwin_tools/mingw/mc.sh
+++ b/erts/etc/win32/cygwin_tools/mingw/mc.sh
@@ -3,7 +3,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2006-2009. All Rights Reserved.
+# Copyright Ericsson AB 2006-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/cygwin_tools/mingw/rc.sh b/erts/etc/win32/cygwin_tools/mingw/rc.sh
index b8a2d2fbcf..de1b15a432 100755
--- a/erts/etc/win32/cygwin_tools/mingw/rc.sh
+++ b/erts/etc/win32/cygwin_tools/mingw/rc.sh
@@ -3,7 +3,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2006-2009. All Rights Reserved.
+# Copyright Ericsson AB 2006-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/cygwin_tools/vc/ar.sh b/erts/etc/win32/cygwin_tools/vc/ar.sh
index e0bd1bd5ca..0989cc878b 100755
--- a/erts/etc/win32/cygwin_tools/vc/ar.sh
+++ b/erts/etc/win32/cygwin_tools/vc/ar.sh
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2009. All Rights Reserved.
+# Copyright Ericsson AB 2002-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/cygwin_tools/vc/cc.sh b/erts/etc/win32/cygwin_tools/vc/cc.sh
index 651b6e098d..9eeb004f0e 100755
--- a/erts/etc/win32/cygwin_tools/vc/cc.sh
+++ b/erts/etc/win32/cygwin_tools/vc/cc.sh
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2009. All Rights Reserved.
+# Copyright Ericsson AB 2002-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/cygwin_tools/vc/cc_wrap.c b/erts/etc/win32/cygwin_tools/vc/cc_wrap.c
index b42e0e1037..198f3b5649 100644
--- a/erts/etc/win32/cygwin_tools/vc/cc_wrap.c
+++ b/erts/etc/win32/cygwin_tools/vc/cc_wrap.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/cygwin_tools/vc/coffix.c b/erts/etc/win32/cygwin_tools/vc/coffix.c
index 0633c6ddea..bf1096c425 100644
--- a/erts/etc/win32/cygwin_tools/vc/coffix.c
+++ b/erts/etc/win32/cygwin_tools/vc/coffix.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/cygwin_tools/vc/emu_cc.sh b/erts/etc/win32/cygwin_tools/vc/emu_cc.sh
index fb6ee2d7a2..343cd366a6 100755
--- a/erts/etc/win32/cygwin_tools/vc/emu_cc.sh
+++ b/erts/etc/win32/cygwin_tools/vc/emu_cc.sh
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2011. All Rights Reserved.
+# Copyright Ericsson AB 2002-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/cygwin_tools/vc/ld.sh b/erts/etc/win32/cygwin_tools/vc/ld.sh
index ff538122b2..2b7d7c6694 100755
--- a/erts/etc/win32/cygwin_tools/vc/ld.sh
+++ b/erts/etc/win32/cygwin_tools/vc/ld.sh
@@ -3,7 +3,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2010. All Rights Reserved.
+# Copyright Ericsson AB 2002-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/cygwin_tools/vc/ld_wrap.c b/erts/etc/win32/cygwin_tools/vc/ld_wrap.c
index 000c13befd..94b5c38751 100644
--- a/erts/etc/win32/cygwin_tools/vc/ld_wrap.c
+++ b/erts/etc/win32/cygwin_tools/vc/ld_wrap.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/cygwin_tools/vc/mc.sh b/erts/etc/win32/cygwin_tools/vc/mc.sh
index 2de5cbba9b..c88f2ff1a7 100755
--- a/erts/etc/win32/cygwin_tools/vc/mc.sh
+++ b/erts/etc/win32/cygwin_tools/vc/mc.sh
@@ -3,7 +3,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2009. All Rights Reserved.
+# Copyright Ericsson AB 2002-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/cygwin_tools/vc/rc.sh b/erts/etc/win32/cygwin_tools/vc/rc.sh
index 414ffa0448..286ebb03f0 100755
--- a/erts/etc/win32/cygwin_tools/vc/rc.sh
+++ b/erts/etc/win32/cygwin_tools/vc/rc.sh
@@ -3,7 +3,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2010. All Rights Reserved.
+# Copyright Ericsson AB 2002-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/erl.c b/erts/etc/win32/erl.c
index 59693955a5..b230aa6a40 100644
--- a/erts/etc/win32/erl.c
+++ b/erts/etc/win32/erl.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2003-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/erl.rc b/erts/etc/win32/erl.rc
index e8848e7969..772213ac86 100644
--- a/erts/etc/win32/erl.rc
+++ b/erts/etc/win32/erl.rc
@@ -1,7 +1,7 @@
//
// %CopyrightBegin%
//
-// Copyright Ericsson AB 1998-2009. All Rights Reserved.
+// Copyright Ericsson AB 1998-2016. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/erl_log.c b/erts/etc/win32/erl_log.c
index 2a873dffac..de0d8e39f0 100644
--- a/erts/etc/win32/erl_log.c
+++ b/erts/etc/win32/erl_log.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/erlsrv/erlsrv_global.h b/erts/etc/win32/erlsrv/erlsrv_global.h
index f535599cf1..fc6166fc7c 100644
--- a/erts/etc/win32/erlsrv/erlsrv_global.h
+++ b/erts/etc/win32/erlsrv/erlsrv_global.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/erlsrv/erlsrv_interactive.c b/erts/etc/win32/erlsrv/erlsrv_interactive.c
index d2236ac9f7..c616ef86e3 100644
--- a/erts/etc/win32/erlsrv/erlsrv_interactive.c
+++ b/erts/etc/win32/erlsrv/erlsrv_interactive.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/erlsrv/erlsrv_interactive.h b/erts/etc/win32/erlsrv/erlsrv_interactive.h
index a83f5a4b85..03cf4b3339 100644
--- a/erts/etc/win32/erlsrv/erlsrv_interactive.h
+++ b/erts/etc/win32/erlsrv/erlsrv_interactive.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/erlsrv/erlsrv_main.c b/erts/etc/win32/erlsrv/erlsrv_main.c
index caca18de00..521e7687f4 100644
--- a/erts/etc/win32/erlsrv/erlsrv_main.c
+++ b/erts/etc/win32/erlsrv/erlsrv_main.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/erlsrv/erlsrv_registry.c b/erts/etc/win32/erlsrv/erlsrv_registry.c
index f95f4ef074..eb2a8c567c 100644
--- a/erts/etc/win32/erlsrv/erlsrv_registry.c
+++ b/erts/etc/win32/erlsrv/erlsrv_registry.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/erlsrv/erlsrv_registry.h b/erts/etc/win32/erlsrv/erlsrv_registry.h
index 3aa265686a..885d021497 100644
--- a/erts/etc/win32/erlsrv/erlsrv_registry.h
+++ b/erts/etc/win32/erlsrv/erlsrv_registry.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/erlsrv/erlsrv_service.c b/erts/etc/win32/erlsrv/erlsrv_service.c
index d9fa165355..f5d5c0b174 100644
--- a/erts/etc/win32/erlsrv/erlsrv_service.c
+++ b/erts/etc/win32/erlsrv/erlsrv_service.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/erlsrv/erlsrv_service.h b/erts/etc/win32/erlsrv/erlsrv_service.h
index c87292325c..4e1148cac2 100644
--- a/erts/etc/win32/erlsrv/erlsrv_service.h
+++ b/erts/etc/win32/erlsrv/erlsrv_service.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/erlsrv/erlsrv_util.c b/erts/etc/win32/erlsrv/erlsrv_util.c
index 800395ff12..f719082d37 100644
--- a/erts/etc/win32/erlsrv/erlsrv_util.c
+++ b/erts/etc/win32/erlsrv/erlsrv_util.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/erlsrv/erlsrv_util.h b/erts/etc/win32/erlsrv/erlsrv_util.h
index 1afcd1dd7e..97cc91834f 100644
--- a/erts/etc/win32/erlsrv/erlsrv_util.h
+++ b/erts/etc/win32/erlsrv/erlsrv_util.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/init_file.c b/erts/etc/win32/init_file.c
index 93d82b1823..147e299798 100644
--- a/erts/etc/win32/init_file.c
+++ b/erts/etc/win32/init_file.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2003-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/init_file.h b/erts/etc/win32/init_file.h
index 404b5fd03b..df33d23a35 100644
--- a/erts/etc/win32/init_file.h
+++ b/erts/etc/win32/init_file.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2003-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/msys_tools/erl b/erts/etc/win32/msys_tools/erl
index 110d48c769..a2fe924e78 100644
--- a/erts/etc/win32/msys_tools/erl
+++ b/erts/etc/win32/msys_tools/erl
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2011. All Rights Reserved.
+# Copyright Ericsson AB 2002-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/msys_tools/erlc b/erts/etc/win32/msys_tools/erlc
index b50090b6de..45d466743e 100644
--- a/erts/etc/win32/msys_tools/erlc
+++ b/erts/etc/win32/msys_tools/erlc
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2011. All Rights Reserved.
+# Copyright Ericsson AB 2002-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/msys_tools/javac.sh b/erts/etc/win32/msys_tools/javac.sh
index 5b51648a19..6a8b245601 100644
--- a/erts/etc/win32/msys_tools/javac.sh
+++ b/erts/etc/win32/msys_tools/javac.sh
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2011. All Rights Reserved.
+# Copyright Ericsson AB 2002-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/msys_tools/make_bootstrap_ini.sh b/erts/etc/win32/msys_tools/make_bootstrap_ini.sh
index 1797f67c78..59fc6fc09a 100644
--- a/erts/etc/win32/msys_tools/make_bootstrap_ini.sh
+++ b/erts/etc/win32/msys_tools/make_bootstrap_ini.sh
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2011. All Rights Reserved.
+# Copyright Ericsson AB 2003-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/msys_tools/make_local_ini.sh b/erts/etc/win32/msys_tools/make_local_ini.sh
index 11f722e7f8..046b3e9d00 100644
--- a/erts/etc/win32/msys_tools/make_local_ini.sh
+++ b/erts/etc/win32/msys_tools/make_local_ini.sh
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2011. All Rights Reserved.
+# Copyright Ericsson AB 2003-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/msys_tools/vc/ar.sh b/erts/etc/win32/msys_tools/vc/ar.sh
index 4c98e3cc29..a33c954c0f 100644
--- a/erts/etc/win32/msys_tools/vc/ar.sh
+++ b/erts/etc/win32/msys_tools/vc/ar.sh
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2011. All Rights Reserved.
+# Copyright Ericsson AB 2002-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/msys_tools/vc/cc.sh b/erts/etc/win32/msys_tools/vc/cc.sh
index 72005862ed..2b0482e876 100644
--- a/erts/etc/win32/msys_tools/vc/cc.sh
+++ b/erts/etc/win32/msys_tools/vc/cc.sh
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2011. All Rights Reserved.
+# Copyright Ericsson AB 2002-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/msys_tools/vc/coffix.c b/erts/etc/win32/msys_tools/vc/coffix.c
index 4f21cfc389..bf1096c425 100644
--- a/erts/etc/win32/msys_tools/vc/coffix.c
+++ b/erts/etc/win32/msys_tools/vc/coffix.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/msys_tools/vc/emu_cc.sh b/erts/etc/win32/msys_tools/vc/emu_cc.sh
index 10d59214ea..2de3a07aca 100644
--- a/erts/etc/win32/msys_tools/vc/emu_cc.sh
+++ b/erts/etc/win32/msys_tools/vc/emu_cc.sh
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2011. All Rights Reserved.
+# Copyright Ericsson AB 2002-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/msys_tools/vc/ld.sh b/erts/etc/win32/msys_tools/vc/ld.sh
index 11b2fc077b..8917251f51 100644
--- a/erts/etc/win32/msys_tools/vc/ld.sh
+++ b/erts/etc/win32/msys_tools/vc/ld.sh
@@ -3,7 +3,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2011. All Rights Reserved.
+# Copyright Ericsson AB 2002-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/msys_tools/vc/mc.sh b/erts/etc/win32/msys_tools/vc/mc.sh
index 14b5ebaa8f..a074a1a89f 100644
--- a/erts/etc/win32/msys_tools/vc/mc.sh
+++ b/erts/etc/win32/msys_tools/vc/mc.sh
@@ -3,7 +3,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2011. All Rights Reserved.
+# Copyright Ericsson AB 2002-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/msys_tools/vc/rc.sh b/erts/etc/win32/msys_tools/vc/rc.sh
index 1f8ade17cb..3d1186ca91 100644
--- a/erts/etc/win32/msys_tools/vc/rc.sh
+++ b/erts/etc/win32/msys_tools/vc/rc.sh
@@ -3,7 +3,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2011. All Rights Reserved.
+# Copyright Ericsson AB 2002-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/nsis/Makefile b/erts/etc/win32/nsis/Makefile
index 64f44ff86d..0b4e0d0359 100644
--- a/erts/etc/win32/nsis/Makefile
+++ b/erts/etc/win32/nsis/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2012. All Rights Reserved.
+# Copyright Ericsson AB 2003-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/nsis/dll_version_helper.sh b/erts/etc/win32/nsis/dll_version_helper.sh
index 86e36f62c9..9eafb6ce0e 100755
--- a/erts/etc/win32/nsis/dll_version_helper.sh
+++ b/erts/etc/win32/nsis/dll_version_helper.sh
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2007-2013. All Rights Reserved.
+# Copyright Ericsson AB 2007-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/nsis/erlang20.nsi b/erts/etc/win32/nsis/erlang20.nsi
index bf6ba0b9a6..66746b684d 100644
--- a/erts/etc/win32/nsis/erlang20.nsi
+++ b/erts/etc/win32/nsis/erlang20.nsi
@@ -7,7 +7,7 @@
;
; %CopyrightBegin%
;
-; Copyright Ericsson AB 2012. All Rights Reserved.
+; Copyright Ericsson AB 2012-2016. All Rights Reserved.
;
; Licensed under the Apache License, Version 2.0 (the "License");
; you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/nsis/find_redist.sh b/erts/etc/win32/nsis/find_redist.sh
index 03e92b21c7..c070ad469a 100755
--- a/erts/etc/win32/nsis/find_redist.sh
+++ b/erts/etc/win32/nsis/find_redist.sh
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2007-2011. All Rights Reserved.
+# Copyright Ericsson AB 2007-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/port_entry.c b/erts/etc/win32/port_entry.c
index 5681a2a548..8b1d3a44b8 100644
--- a/erts/etc/win32/port_entry.c
+++ b/erts/etc/win32/port_entry.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2012. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/resource.h b/erts/etc/win32/resource.h
index 32d8b8885d..e59baadb50 100644
--- a/erts/etc/win32/resource.h
+++ b/erts/etc/win32/resource.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/start_erl.c b/erts/etc/win32/start_erl.c
index a4437c2f6b..07bcd19b81 100644
--- a/erts/etc/win32/start_erl.c
+++ b/erts/etc/win32/start_erl.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2012. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/etc/win32/win_erlexec.c b/erts/etc/win32/win_erlexec.c
index f2460197e6..39dac358e9 100644
--- a/erts/etc/win32/win_erlexec.c
+++ b/erts/etc/win32/win_erlexec.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/example/Makefile b/erts/example/Makefile
index cc5bcce191..f00321ac45 100644
--- a/erts/example/Makefile
+++ b/erts/example/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2006-2009. All Rights Reserved.
+# Copyright Ericsson AB 2006-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/example/matrix_nif.c b/erts/example/matrix_nif.c
index dfe446e879..6452084eb7 100644
--- a/erts/example/matrix_nif.c
+++ b/erts/example/matrix_nif.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2010-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/example/matrix_nif.erl b/erts/example/matrix_nif.erl
index d56b358247..bdc7228ac0 100644
--- a/erts/example/matrix_nif.erl
+++ b/erts/example/matrix_nif.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/example/next_perm.cc b/erts/example/next_perm.cc
index c7b7096e7b..882af4cd1e 100644
--- a/erts/example/next_perm.cc
+++ b/erts/example/next_perm.cc
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/example/next_perm.erl b/erts/example/next_perm.erl
index d414470f3a..b6f47b975c 100644
--- a/erts/example/next_perm.erl
+++ b/erts/example/next_perm.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/example/pg_async.c b/erts/example/pg_async.c
index cd6bc9e0c2..3167ce5227 100644
--- a/erts/example/pg_async.c
+++ b/erts/example/pg_async.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/example/pg_async.erl b/erts/example/pg_async.erl
index 20ee94f61a..d34d03cf09 100644
--- a/erts/example/pg_async.erl
+++ b/erts/example/pg_async.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/example/pg_async2.c b/erts/example/pg_async2.c
index 9eb3ec9d54..ee772f4447 100644
--- a/erts/example/pg_async2.c
+++ b/erts/example/pg_async2.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/example/pg_async2.erl b/erts/example/pg_async2.erl
index 082852f617..9398f4ccbe 100644
--- a/erts/example/pg_async2.erl
+++ b/erts/example/pg_async2.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/example/pg_encode.c b/erts/example/pg_encode.c
index e1ec4abb1d..1efc4c1eaf 100644
--- a/erts/example/pg_encode.c
+++ b/erts/example/pg_encode.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/example/pg_encode.h b/erts/example/pg_encode.h
index df3f8fcaaa..213e20198e 100644
--- a/erts/example/pg_encode.h
+++ b/erts/example/pg_encode.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/example/pg_encode2.c b/erts/example/pg_encode2.c
index cdf8e71e44..df5ec9771b 100644
--- a/erts/example/pg_encode2.c
+++ b/erts/example/pg_encode2.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/example/pg_encode2.h b/erts/example/pg_encode2.h
index df3f8fcaaa..213e20198e 100644
--- a/erts/example/pg_encode2.h
+++ b/erts/example/pg_encode2.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/example/pg_sync.c b/erts/example/pg_sync.c
index 88096671a5..81b99f98d3 100644
--- a/erts/example/pg_sync.c
+++ b/erts/example/pg_sync.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/example/pg_sync.erl b/erts/example/pg_sync.erl
index 76fb27332e..29a975727a 100644
--- a/erts/example/pg_sync.erl
+++ b/erts/example/pg_sync.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/example/time_compat.erl b/erts/example/time_compat.erl
index b87c6cc550..6472a271b6 100644
--- a/erts/example/time_compat.erl
+++ b/erts/example/time_compat.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2014-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2014-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -272,6 +272,10 @@ system_flag(Flag, Value) ->
%%
integer_time_unit(native) -> 1000*1000;
+integer_time_unit(nanosecond) -> 1000*1000*1000;
+integer_time_unit(microsecond) -> 1000*1000;
+integer_time_unit(millisecond) -> 1000;
+integer_time_unit(second) -> 1;
integer_time_unit(nano_seconds) -> 1000*1000*1000;
integer_time_unit(micro_seconds) -> 1000*1000;
integer_time_unit(milli_seconds) -> 1000;
diff --git a/erts/include/erl_fixed_size_int_types.h b/erts/include/erl_fixed_size_int_types.h
index dfaea5650b..9f47bdd797 100644
--- a/erts/include/erl_fixed_size_int_types.h
+++ b/erts/include/erl_fixed_size_int_types.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/erl_int_sizes_config.h.in b/erts/include/erl_int_sizes_config.h.in
index b18f5ebc00..e0e60f0e2f 100644
--- a/erts/include/erl_int_sizes_config.h.in
+++ b/erts/include/erl_int_sizes_config.h.in
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,6 +35,3 @@
/* The size of a pointer. */
#undef SIZEOF_VOID_P
-
-/* Define if building a halfword-heap 64bit emulator (needed for NIF's) */
-#undef HALFWORD_HEAP_EMULATOR
diff --git a/erts/include/erl_memory_trace_parser.h b/erts/include/erl_memory_trace_parser.h
index 426ff05061..3170ebc0d0 100644
--- a/erts/include/erl_memory_trace_parser.h
+++ b/erts/include/erl_memory_trace_parser.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/erl_native_features_config.h.in b/erts/include/erl_native_features_config.h.in
deleted file mode 100644
index 2bd1d96229..0000000000
--- a/erts/include/erl_native_features_config.h.in
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2004-2011. 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%
- */
-
-/* Dirty scheduler support */
-#undef ERL_NIF_DIRTY_SCHEDULER_SUPPORT
diff --git a/erts/include/internal/README b/erts/include/internal/README
index fca0c5e489..a79b241556 100644
--- a/erts/include/internal/README
+++ b/erts/include/internal/README
@@ -1,7 +1,7 @@
%CopyrightBegin%
- Copyright Ericsson AB 2004-2009. All Rights Reserved.
+ Copyright Ericsson AB 2004-2016. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/erl_errno.h b/erts/include/internal/erl_errno.h
index 33bfbe3d51..1ae045805e 100644
--- a/erts/include/internal/erl_errno.h
+++ b/erts/include/internal/erl_errno.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009. All Rights Reserved.
+ * Copyright Ericsson AB 2009-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/erl_memory_trace_protocol.h b/erts/include/internal/erl_memory_trace_protocol.h
index b86e2de278..d3e0bcc1f4 100644
--- a/erts/include/internal/erl_memory_trace_protocol.h
+++ b/erts/include/internal/erl_memory_trace_protocol.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/erl_misc_utils.h b/erts/include/internal/erl_misc_utils.h
index 7ab7a26838..55566ddf74 100644
--- a/erts/include/internal/erl_misc_utils.h
+++ b/erts/include/internal/erl_misc_utils.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2010. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -56,4 +56,33 @@ int erts_map_win_error_to_errno(DWORD win_error);
int erts_get_last_win_errno(void);
#endif
+#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__)
+#define __DARWIN__ 1
+#endif
+
+/*
+ * ERTS_PREMATURE_TIMEOUT() expects time units
+ * 1000 (millisec), 1000000 (microsec), or
+ * 1000000000 (nanosec). Might not work properly
+ * otherwise.
+ */
+#undef ERTS_USE_PREMATURE_TIMEOUT
+#undef ERTS_PREMATURE_TIMEOUT
+
+#if defined(__DARWIN__)
+#define ERTS_USE_PREMATURE_TIMEOUT 1
+#define ERTS_PREMATURE_TIMEOUT(TMO, TU) \
+ ((TMO) >= 1 * ((TU) / 1000) \
+ ? ((TMO) >= 20 * ((TU) / 1000) \
+ ? 15 * ((TU) / 1000) \
+ : ((TMO) >= 5 * ((TU) / 1000) \
+ ? 3 * ((TU) / 1000) \
+ : 5 * ((TU) / 10000))) \
+ : 0)
+
+#else
+#define ERTS_USE_PREMATURE_TIMEOUT 0
+#define ERTS_PREMATURE_TIMEOUT(TMO, TU) (0)
+#endif
+
#endif /* #ifndef ERL_MISC_UTILS_H_ */
diff --git a/erts/include/internal/erl_printf.h b/erts/include/internal/erl_printf.h
index 3846828fb9..f180a53f18 100644
--- a/erts/include/internal/erl_printf.h
+++ b/erts/include/internal/erl_printf.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -41,12 +41,18 @@ struct erts_dsprintf_buf_t_ {
#define ERTS_DSPRINTF_BUF_INITER(GFUNC) {NULL, 0, 0, (GFUNC)}
+typedef int (*fmtfn_t)(void*, char*, size_t);
+
+int erts_write_fd(void *vfdp, char* buf, size_t len);
+int erts_write_ds(void *vdsbufp, char* buf, size_t len);
+
int erts_printf(const char *, ...);
int erts_fprintf(FILE *, const char *, ...);
int erts_fdprintf(int, const char *, ...);
int erts_sprintf(char *, const char *, ...);
int erts_snprintf(char *, size_t, const char *, ...);
int erts_dsprintf(erts_dsprintf_buf_t *, const char *, ...);
+int erts_cbprintf(fmtfn_t, void*, const char*, ...);
int erts_vprintf(const char *, va_list);
int erts_vfprintf(FILE *, const char *, va_list);
@@ -54,5 +60,6 @@ int erts_vfdprintf(int, const char *, va_list);
int erts_vsprintf(char *, const char *, va_list);
int erts_vsnprintf(char *, size_t, const char *, va_list);
int erts_vdsprintf(erts_dsprintf_buf_t *, const char *, va_list);
+int erts_vcbprintf(fmtfn_t, void*, const char*, va_list);
#endif /* #ifndef ERL_PRINTF_H_ */
diff --git a/erts/include/internal/erl_printf_format.h b/erts/include/internal/erl_printf_format.h
index efd926be99..56ec032bd1 100644
--- a/erts/include/internal/erl_printf_format.h
+++ b/erts/include/internal/erl_printf_format.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@
#include <stdlib.h>
#include "erl_int_sizes_config.h"
+#include "erl_printf.h"
#if SIZEOF_VOID_P == SIZEOF_LONG
typedef unsigned long ErlPfUWord;
@@ -44,9 +45,6 @@ typedef long long ErlPfSWord;
#error Found no appropriate type to use for 'Eterm', 'Uint' and 'Sint'
#endif
-
-typedef int (*fmtfn_t)(void*, char*, size_t);
-
extern int erts_printf_format(fmtfn_t, void*, char*, va_list);
extern int erts_printf_char(fmtfn_t, void*, char);
@@ -57,17 +55,9 @@ extern int erts_printf_uword(fmtfn_t, void*, char, int, int, ErlPfUWord);
extern int erts_printf_sword(fmtfn_t, void*, char, int, int, ErlPfSWord);
extern int erts_printf_double(fmtfn_t, void *, char, int, int, double);
-#ifdef HALFWORD_HEAP_EMULATOR
-# if SIZEOF_INT != 4
-# error Unsupported integer size for HALFWORD_HEAP_EMULATOR
-# endif
-typedef unsigned int ErlPfEterm;
-#else
typedef ErlPfUWord ErlPfEterm;
-#endif
-
-extern int (*erts_printf_eterm_func)(fmtfn_t, void*, ErlPfEterm, long, ErlPfEterm*);
+extern int (*erts_printf_eterm_func)(fmtfn_t, void*, ErlPfEterm, long);
#endif /* ERL_PRINTF_FORMAT_H__ */
diff --git a/erts/include/internal/erts_internal.mk.in b/erts/include/internal/erts_internal.mk.in
index 76aab59c38..8faa33135e 100644
--- a/erts/include/internal/erts_internal.mk.in
+++ b/erts/include/internal/erts_internal.mk.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009. All Rights Reserved.
+# Copyright Ericsson AB 2009-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/ethr_atomics.h b/erts/include/internal/ethr_atomics.h
index f366c2d0ad..06568201ad 100644
--- a/erts/include/internal/ethr_atomics.h
+++ b/erts/include/internal/ethr_atomics.h
@@ -10,7 +10,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2011-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2011-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/ethr_internal.h b/erts/include/internal/ethr_internal.h
index d4ded6ff05..6657c8affc 100644
--- a/erts/include/internal/ethr_internal.h
+++ b/erts/include/internal/ethr_internal.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2010-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -92,7 +92,6 @@ void ethr_run_exit_handlers__(void);
void ethr_ts_event_destructor__(void *vtsep);
#if defined(ETHR_X86_RUNTIME_CONF__)
-int ethr_x86_have_cpuid__(void);
void ethr_x86_cpuid__(int *eax, int *ebx, int *ecx, int *edx);
#endif
diff --git a/erts/include/internal/ethr_mutex.h b/erts/include/internal/ethr_mutex.h
index f76c4262ca..8ef3b1e40b 100644
--- a/erts/include/internal/ethr_mutex.h
+++ b/erts/include/internal/ethr_mutex.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2010-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -98,7 +98,7 @@ void LeaveCriticalSection(CRITICAL_SECTION *);
#if 0
# define ETHR_MTX_Q_LOCK_SPINLOCK__
# define ETHR_MTX_QLOCK_TYPE__ ethr_spinlock_t
-#elif defined(ETHR_PTHREADS) || defined(ETHR_OSE_THREADS)
+#elif defined(ETHR_PTHREADS)
# define ETHR_MTX_Q_LOCK_PTHREAD_MUTEX__
# define ETHR_MTX_QLOCK_TYPE__ pthread_mutex_t
#elif defined(ETHR_WIN32_THREADS)
@@ -108,13 +108,13 @@ void LeaveCriticalSection(CRITICAL_SECTION *);
# error Need a qlock implementation
#endif
-#define ETHR_RWMTX_W_FLG__ (((ethr_sint32_t) 1) << 31)
-#define ETHR_RWMTX_W_WAIT_FLG__ (((ethr_sint32_t) 1) << 30)
-#define ETHR_RWMTX_R_WAIT_FLG__ (((ethr_sint32_t) 1) << 29)
+#define ETHR_RWMTX_W_FLG__ ((ethr_sint32_t) (1U << 31))
+#define ETHR_RWMTX_W_WAIT_FLG__ ((ethr_sint32_t) (1U << 30))
+#define ETHR_RWMTX_R_WAIT_FLG__ ((ethr_sint32_t) (1U << 29))
/* frequent read kind */
-#define ETHR_RWMTX_R_FLG__ (((ethr_sint32_t) 1) << 28)
-#define ETHR_RWMTX_R_ABRT_UNLCK_FLG__ (((ethr_sint32_t) 1) << 27)
+#define ETHR_RWMTX_R_FLG__ ((ethr_sint32_t) (1U << 28))
+#define ETHR_RWMTX_R_ABRT_UNLCK_FLG__ ((ethr_sint32_t) (1U << 27))
#define ETHR_RWMTX_R_PEND_UNLCK_MASK__ (ETHR_RWMTX_R_ABRT_UNLCK_FLG__ - 1)
/* normal kind */
@@ -211,7 +211,7 @@ struct ethr_cond_ {
#endif
};
-#elif (defined(ETHR_PTHREADS) || defined(ETHR_OSE_THREADS)) && !defined(ETHR_DBG_WIN_MTX_WITH_PTHREADS)
+#elif defined(ETHR_PTHREADS) && !defined(ETHR_DBG_WIN_MTX_WITH_PTHREADS)
typedef struct ethr_mutex_ ethr_mutex;
struct ethr_mutex_ {
@@ -355,7 +355,7 @@ void ethr_rwmutex_rwunlock(ethr_rwmutex *);
#ifdef ETHR_MTX_HARD_DEBUG
#define ETHR_MTX_HARD_ASSERT(A) \
- ((void) ((A) ? 1 : ethr_assert_failed(__FILE__, __LINE__, __func__,#A)))
+ ((void) ((A) ? 1 : ethr_assert_failed(__FILE__, __LINE__, __func__, #A)))
#else
#define ETHR_MTX_HARD_ASSERT(A) ((void) 1)
#endif
@@ -634,7 +634,7 @@ ETHR_INLINE_MTX_FUNC_NAME_(ethr_mutex_unlock)(ethr_mutex *mtx)
#endif /* ETHR_TRY_INLINE_FUNCS */
-#elif (defined(ETHR_PTHREADS) || defined(ETHR_OSE_THREADS)) && !defined(ETHR_DBG_WIN_MTX_WITH_PTHREADS)
+#elif defined(ETHR_PTHREADS) && !defined(ETHR_DBG_WIN_MTX_WITH_PTHREADS)
#if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_MUTEX_IMPL__)
diff --git a/erts/include/internal/ethr_optimized_fallbacks.h b/erts/include/internal/ethr_optimized_fallbacks.h
index 6ef4e2bace..8c27a9ba5b 100644
--- a/erts/include/internal/ethr_optimized_fallbacks.h
+++ b/erts/include/internal/ethr_optimized_fallbacks.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2010-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h
index 8964f95652..b23644d361 100644
--- a/erts/include/internal/ethread.h
+++ b/erts/include/internal/ethread.h
@@ -112,6 +112,10 @@ int ethr_assert_failed(const char *file, int line, const char *func, char *a);
#error "_GNU_SOURCE not defined. Please, compile all files with -D_GNU_SOURCE."
#endif
+#ifdef ETHR_HAVE_PTHREAD_SETNAME_NP_1
+#define _DARWIN_C_SOURCE
+#endif
+
#if defined(ETHR_NEED_NPTL_PTHREAD_H)
#include <nptl/pthread.h>
#elif defined(ETHR_HAVE_MIT_PTHREAD_H)
@@ -194,28 +198,6 @@ typedef DWORD ethr_tsd_key;
#define ETHR_YIELD() (Sleep(0), 0)
-#elif defined(ETHR_OSE_THREADS)
-
-#include "ose.h"
-#undef NIL
-
-#if defined(ETHR_HAVE_PTHREAD_H)
-#include <pthread.h>
-#endif
-
-typedef struct {
- PROCESS id;
- unsigned int tsd_key_index;
- void *res;
-} ethr_tid;
-
-typedef OSPPDKEY ethr_tsd_key;
-
-#undef ETHR_HAVE_ETHR_SIG_FUNCS
-
-/* Out own RW mutexes are probably faster, but use OSEs mutexes */
-#define ETHR_USE_OWN_RWMTX_IMPL__
-
#else /* No supported thread lib found */
#ifdef ETHR_NO_SUPP_THR_LIB_NOT_FATAL
@@ -296,14 +278,40 @@ ETHR_PROTO_NORETURN__ ethr_fatal_error__(const char *file,
|| (defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64))))
# define ETHR_X86_RUNTIME_CONF__
-# define ETHR_X86_RUNTIME_CONF_HAVE_DW_CMPXCHG__ \
- (__builtin_expect(ethr_runtime__.conf.have_dw_cmpxchg != 0, 1))
-# define ETHR_X86_RUNTIME_CONF_HAVE_NO_DW_CMPXCHG__ \
- (__builtin_expect(ethr_runtime__.conf.have_dw_cmpxchg == 0, 0))
-# define ETHR_X86_RUNTIME_CONF_HAVE_SSE2__ \
- (__builtin_expect(ethr_runtime__.conf.have_sse2 != 0, 1))
-# define ETHR_X86_RUNTIME_CONF_HAVE_NO_SSE2__ \
- (__builtin_expect(ethr_runtime__.conf.have_sse2 == 0, 0))
+# define ETHR_X86_RUNTIME_CONF_HAVE_META(feature) \
+ (__builtin_expect(ethr_runtime__.conf.have_##feature != 0, 1))
+# define ETHR_X86_RUNTIME_CONF_HAVE_NO_META(feature) \
+ (__builtin_expect(ethr_runtime__.conf.have_##feature == 0, 0))
+
+# define ETHR_X86_RUNTIME_CONF_HAVE_DW_CMPXCHG__ \
+ ETHR_X86_RUNTIME_CONF_HAVE_META(dw_cmpxchg)
+# define ETHR_X86_RUNTIME_CONF_HAVE_NO_DW_CMPXCHG__ \
+ ETHR_X86_RUNTIME_CONF_HAVE_NO_META(dw_cmpxchg)
+# define ETHR_X86_RUNTIME_CONF_HAVE_SSE2__ \
+ ETHR_X86_RUNTIME_CONF_HAVE_META(sse2)
+# define ETHR_X86_RUNTIME_CONF_HAVE_NO_SSE2__ \
+ ETHR_X86_RUNTIME_CONF_HAVE_NO_META(sse2)
+# define ETHR_X86_RUNTIME_CONF_HAVE_RDTSCP__ \
+ ETHR_X86_RUNTIME_CONF_HAVE_META(rdtscp)
+# define ETHR_X86_RUNTIME_CONF_HAVE_NO_RDTSCP__ \
+ ETHR_X86_RUNTIME_CONF_HAVE_NO_META(rdtscp)
+# define ETHR_X86_RUNTIME_CONF_HAVE_CONSTANT_TSC__ \
+ ETHR_X86_RUNTIME_CONF_HAVE_META(constant_tsc)
+# define ETHR_X86_RUNTIME_CONF_HAVE_NO_CONSTANT_TSC__ \
+ ETHR_X86_RUNTIME_CONF_HAVE_NO_META(nonstop_tsc)
+# define ETHR_X86_RUNTIME_CONF_HAVE_NONSTOP_TSC__ \
+ ETHR_X86_RUNTIME_CONF_HAVE_META(nonstop_tsc)
+# define ETHR_X86_RUNTIME_CONF_HAVE_NO_NONSTOP_TSC__ \
+ ETHR_X86_RUNTIME_CONF_HAVE_NO_META(nonstop_tsc)
+# define ETHR_X86_RUNTIME_CONF_HAVE_TSC_RELIABLE__ \
+ ETHR_X86_RUNTIME_CONF_HAVE_META(tsc_reliable)
+# define ETHR_X86_RUNTIME_CONF_HAVE_NO_TSC_RELIABLE_TSC__ \
+ ETHR_X86_RUNTIME_CONF_HAVE_NO_META(tsc_reliable)
+# define ETHR_X86_RUNTIME_CONF_HAVE_NONSTOP_TSC_S3__ \
+ ETHR_X86_RUNTIME_CONF_HAVE_META(nonstop_tsc_s3)
+# define ETHR_X86_RUNTIME_CONF_HAVE_NO_NONSTOP_TSC_S3__ \
+ ETHR_X86_RUNTIME_CONF_HAVE_NO_META(nonstop_tsc_s3)
+
#endif
#if (defined(__GNUC__) \
@@ -322,6 +330,11 @@ typedef struct {
#if defined(ETHR_X86_RUNTIME_CONF__)
int have_dw_cmpxchg;
int have_sse2;
+ int have_rdtscp;
+ int have_constant_tsc;
+ int have_tsc_reliable;
+ int have_nonstop_tsc;
+ int have_nonstop_tsc_s3;
#endif
#if defined(ETHR_PPC_RUNTIME_CONF__)
int have_lwsync;
@@ -383,19 +396,7 @@ extern ethr_runtime_t ethr_runtime__;
#include "ethr_atomics.h" /* The atomics API */
-#if defined (ETHR_OSE_THREADS)
-static ETHR_INLINE void
-ose_yield(void)
-{
- if (get_ptype(current_process()) == OS_PRI_PROC) {
- set_pri(get_pri(current_process()));
- } else {
- delay(1);
- }
-}
-#endif
-
-#if defined(__GNUC__) && !defined(ETHR_OSE_THREADS)
+#if defined(__GNUC__)
# ifndef ETHR_SPIN_BODY
# if defined(__i386__) || defined(__x86_64__)
# define ETHR_SPIN_BODY __asm__ __volatile__("rep;nop" : : : "memory")
@@ -411,20 +412,9 @@ ose_yield(void)
# ifndef ETHR_SPIN_BODY
# define ETHR_SPIN_BODY do {YieldProcessor();ETHR_COMPILER_BARRIER;} while(0)
# endif
-#elif defined(ETHR_OSE_THREADS)
-# ifndef ETHR_SPIN_BODY
-# define ETHR_SPIN_BODY ose_yield()
-# else
-# error "OSE should use ose_yield()"
-# endif
#endif
-#ifndef ETHR_OSE_THREADS
#define ETHR_YIELD_AFTER_BUSY_LOOPS 50
-#else
-#define ETHR_YIELD_AFTER_BUSY_LOOPS 0
-#endif
-
#ifndef ETHR_SPIN_BODY
# define ETHR_SPIN_BODY ETHR_COMPILER_BARRIER
@@ -447,18 +437,13 @@ ose_yield(void)
# else
# define ETHR_YIELD() (pthread_yield(), 0)
# endif
-# elif defined(ETHR_OSE_THREADS)
-# define ETHR_YIELD() (ose_yield(), 0)
# else
# define ETHR_YIELD() (ethr_compiler_barrier(), 0)
# endif
#endif
-#if defined(VALGRIND) || defined(ETHR_OSE_THREADS)
-/* mutex as fallback for spinlock for VALGRIND and OSE.
- OSE cannot use spinlocks as processes working on the
- same execution unit have a tendency to deadlock.
- */
+#if defined(VALGRIND)
+/* mutex as fallback for spinlock for VALGRIND. */
# undef ETHR_HAVE_NATIVE_SPINLOCKS
# undef ETHR_HAVE_NATIVE_RWSPINLOCKS
#else
@@ -505,16 +490,9 @@ typedef struct {
int detached; /* boolean (default false) */
int suggested_stack_size; /* kilo words (default sys dependent) */
char *name; /* max 14 char long (default no-name) */
-#ifdef ETHR_OSE_THREADS
- U32 coreNo;
-#endif
} ethr_thr_opts;
-#if defined(ETHR_OSE_THREADS)
-#define ETHR_THR_OPTS_DEFAULT_INITER {0, -1, NULL, 0}
-#else
#define ETHR_THR_OPTS_DEFAULT_INITER {0, -1, NULL}
-#endif
#if !defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_AUX_IMPL__)
# define ETHR_NEED_SPINLOCK_PROTOTYPES__
@@ -628,8 +606,6 @@ typedef struct ethr_ts_event_ ethr_ts_event; /* Needed by ethr_mutex.h */
# include "win/ethr_event.h"
#elif defined(ETHR_PTHREADS)
# include "pthread/ethr_event.h"
-#elif defined(ETHR_OSE_THREADS)
-# include "ose/ethr_event.h"
#endif
int ethr_set_main_thr_status(int, int);
@@ -719,37 +695,6 @@ ETHR_INLINE_FUNC_NAME_(ethr_leave_ts_event)(ethr_ts_event *tsep)
#endif
-#elif defined (ETHR_OSE_THREADS)
-
-#if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHREAD_IMPL__)
-
-extern ethr_tsd_key ethr_ts_event_key__;
-
-static ETHR_INLINE ethr_ts_event *
-ETHR_INLINE_FUNC_NAME_(ethr_get_ts_event)(void)
-{
- ethr_ts_event *tsep = *(ethr_ts_event**)ose_get_ppdata(ethr_ts_event_key__);
- if (!tsep) {
- int res = ethr_get_tmp_ts_event__(&tsep);
- if (res != 0)
- ETHR_FATAL_ERROR__(res);
- ETHR_ASSERT(tsep);
- }
- return tsep;
-}
-
-static ETHR_INLINE void
-ETHR_INLINE_FUNC_NAME_(ethr_leave_ts_event)(ethr_ts_event *tsep)
-{
- if (tsep->iflgs & ETHR_TS_EV_TMP) {
- int res = ethr_free_ts_event__(tsep);
- if (res != 0)
- ETHR_FATAL_ERROR__(res);
- }
-}
-
-#endif
-
#endif
#include "ethr_mutex.h" /* Need atomic declarations and tse */
diff --git a/erts/include/internal/ethread.mk.in b/erts/include/internal/ethread.mk.in
index 89924a3215..486a7a9401 100644
--- a/erts/include/internal/ethread.mk.in
+++ b/erts/include/internal/ethread.mk.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2009. All Rights Reserved.
+# Copyright Ericsson AB 2004-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/ethread_header_config.h.in b/erts/include/internal/ethread_header_config.h.in
index f4b08cfced..6309f10439 100644
--- a/erts/include/internal/ethread_header_config.h.in
+++ b/erts/include/internal/ethread_header_config.h.in
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2015. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/ethread_inline.h b/erts/include/internal/ethread_inline.h
index 3ba910d993..8e6bcfc4a8 100644
--- a/erts/include/internal/ethread_inline.h
+++ b/erts/include/internal/ethread_inline.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2014. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/i386/atomic.h b/erts/include/internal/i386/atomic.h
index 6a6435e58d..52ef1762d5 100644
--- a/erts/include/internal/i386/atomic.h
+++ b/erts/include/internal/i386/atomic.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/i386/ethr_dw_atomic.h b/erts/include/internal/i386/ethr_dw_atomic.h
index 5444a6345c..91acdb0483 100644
--- a/erts/include/internal/i386/ethr_dw_atomic.h
+++ b/erts/include/internal/i386/ethr_dw_atomic.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2011. All Rights Reserved.
+ * Copyright Ericsson AB 2011-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/i386/ethr_membar.h b/erts/include/internal/i386/ethr_membar.h
index 97ae5eda2c..d1b72cd538 100644
--- a/erts/include/internal/i386/ethr_membar.h
+++ b/erts/include/internal/i386/ethr_membar.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2011. All Rights Reserved.
+ * Copyright Ericsson AB 2011-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/i386/ethread.h b/erts/include/internal/i386/ethread.h
index 23dcd1dc19..fef1674c7e 100644
--- a/erts/include/internal/i386/ethread.h
+++ b/erts/include/internal/i386/ethread.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/i386/rwlock.h b/erts/include/internal/i386/rwlock.h
index 9859338eab..8d22bac7e9 100644
--- a/erts/include/internal/i386/rwlock.h
+++ b/erts/include/internal/i386/rwlock.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/i386/spinlock.h b/erts/include/internal/i386/spinlock.h
index e010684d14..1a8e359981 100644
--- a/erts/include/internal/i386/spinlock.h
+++ b/erts/include/internal/i386/spinlock.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/libatomic_ops/ethr_atomic.h b/erts/include/internal/libatomic_ops/ethr_atomic.h
index 828210036c..da3b15a878 100644
--- a/erts/include/internal/libatomic_ops/ethr_atomic.h
+++ b/erts/include/internal/libatomic_ops/ethr_atomic.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010-2014. All Rights Reserved.
+ * Copyright Ericsson AB 2010-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/libatomic_ops/ethr_dw_atomic.h b/erts/include/internal/libatomic_ops/ethr_dw_atomic.h
index ce9b251cbe..8643600fa5 100644
--- a/erts/include/internal/libatomic_ops/ethr_dw_atomic.h
+++ b/erts/include/internal/libatomic_ops/ethr_dw_atomic.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2014. All Rights Reserved.
+ * Copyright Ericsson AB 2014-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/libatomic_ops/ethr_membar.h b/erts/include/internal/libatomic_ops/ethr_membar.h
index 7d2b807586..1d3d332c90 100644
--- a/erts/include/internal/libatomic_ops/ethr_membar.h
+++ b/erts/include/internal/libatomic_ops/ethr_membar.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2011. All Rights Reserved.
+ * Copyright Ericsson AB 2011-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/libatomic_ops/ethread.h b/erts/include/internal/libatomic_ops/ethread.h
index e34f43bb46..4adc31ed2a 100644
--- a/erts/include/internal/libatomic_ops/ethread.h
+++ b/erts/include/internal/libatomic_ops/ethread.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2010-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/ose/ethr_event.h b/erts/include/internal/ose/ethr_event.h
deleted file mode 100644
index c18f30aa4a..0000000000
--- a/erts/include/internal/ose/ethr_event.h
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2009-2011. 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%
- */
-
-/*
- * Author: Rickard Green
- */
-
-//#define USE_PTHREAD_API
-
-#define ETHR_EVENT_OFF_WAITER__ -1L
-#define ETHR_EVENT_OFF__ 1L
-#define ETHR_EVENT_ON__ 0L
-
-#ifdef USE_PTHREAD_API
-
-typedef struct {
- ethr_atomic32_t state;
- pthread_mutex_t mtx;
- pthread_cond_t cnd;
-} ethr_event;
-
-#if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_EVENT_IMPL__)
-
-static void ETHR_INLINE
-ETHR_INLINE_FUNC_NAME_(ethr_event_set)(ethr_event *e)
-{
- ethr_sint32_t val;
- val = ethr_atomic32_xchg_mb(&e->state, ETHR_EVENT_ON__);
- if (val == ETHR_EVENT_OFF_WAITER__) {
- int res = pthread_mutex_lock(&e->mtx);
- if (res != 0)
- ETHR_FATAL_ERROR__(res);
- res = pthread_cond_signal(&e->cnd);
- if (res != 0)
- ETHR_FATAL_ERROR__(res);
- res = pthread_mutex_unlock(&e->mtx);
- if (res != 0)
- ETHR_FATAL_ERROR__(res);
- }
-}
-
-static void ETHR_INLINE
-ETHR_INLINE_FUNC_NAME_(ethr_event_reset)(ethr_event *e)
-{
- ethr_atomic32_set(&e->state, ETHR_EVENT_OFF__);
- ETHR_MEMORY_BARRIER;
-}
-
-#endif
-
-#else
-
-typedef struct {
- ethr_atomic32_t state;
- PROCESS proc;
-} ethr_event;
-
-#if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_EVENT_IMPL__)
-
-static void ETHR_INLINE
-ETHR_INLINE_FUNC_NAME_(ethr_event_set)(ethr_event *e)
-{
- ethr_sint32_t val = ethr_atomic32_xchg_mb(&e->state, ETHR_EVENT_ON__);
- if (val == ETHR_EVENT_OFF_WAITER__) {
-#ifdef DEBUG
- OSFSEMVAL fsem_val = get_fsem(e->proc);
-
- /* There is a race in this assert.
- This is because the state is set before the wait call in wait__.
- We hope that a delay of 10 ms is enough */
- if (fsem_val == 0)
- delay(10);
- ETHR_ASSERT(get_fsem(e->proc) == -1);
-#endif
- signal_fsem(e->proc);
- }
-}
-
-static void ETHR_INLINE
-ETHR_INLINE_FUNC_NAME_(ethr_event_reset)(ethr_event *e)
-{
- ethr_atomic32_set(&e->state, ETHR_EVENT_OFF__);
- ETHR_MEMORY_BARRIER;
-}
-
-#endif
-
-#endif
-
-int ethr_event_init(ethr_event *e);
-int ethr_event_destroy(ethr_event *e);
-int ethr_event_wait(ethr_event *e);
-int ethr_event_swait(ethr_event *e, int spincount);
-#if !defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_EVENT_IMPL__)
-void ethr_event_set(ethr_event *e);
-void ethr_event_reset(ethr_event *e);
-#endif
diff --git a/erts/include/internal/ppc32/atomic.h b/erts/include/internal/ppc32/atomic.h
index 572b0e5191..198f057b3f 100644
--- a/erts/include/internal/ppc32/atomic.h
+++ b/erts/include/internal/ppc32/atomic.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/ppc32/ethr_membar.h b/erts/include/internal/ppc32/ethr_membar.h
index fe77721cd9..88ba4a2ea5 100644
--- a/erts/include/internal/ppc32/ethr_membar.h
+++ b/erts/include/internal/ppc32/ethr_membar.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2011. All Rights Reserved.
+ * Copyright Ericsson AB 2011-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/ppc32/ethread.h b/erts/include/internal/ppc32/ethread.h
index 25e85c58bd..bead019f41 100644
--- a/erts/include/internal/ppc32/ethread.h
+++ b/erts/include/internal/ppc32/ethread.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/ppc32/rwlock.h b/erts/include/internal/ppc32/rwlock.h
index aee232f79d..6493629e49 100644
--- a/erts/include/internal/ppc32/rwlock.h
+++ b/erts/include/internal/ppc32/rwlock.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/ppc32/spinlock.h b/erts/include/internal/ppc32/spinlock.h
index 829db6a135..3b35cafcb2 100644
--- a/erts/include/internal/ppc32/spinlock.h
+++ b/erts/include/internal/ppc32/spinlock.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/pthread/ethr_event.h b/erts/include/internal/pthread/ethr_event.h
index deb4b29686..6e470bf6cf 100644
--- a/erts/include/internal/pthread/ethr_event.h
+++ b/erts/include/internal/pthread/ethr_event.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2009-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/sparc32/atomic.h b/erts/include/internal/sparc32/atomic.h
index 0b535242ad..032943817d 100644
--- a/erts/include/internal/sparc32/atomic.h
+++ b/erts/include/internal/sparc32/atomic.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/sparc32/ethr_membar.h b/erts/include/internal/sparc32/ethr_membar.h
index 6133de5eb7..fb8ceef12b 100644
--- a/erts/include/internal/sparc32/ethr_membar.h
+++ b/erts/include/internal/sparc32/ethr_membar.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2011. All Rights Reserved.
+ * Copyright Ericsson AB 2011-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/sparc32/ethread.h b/erts/include/internal/sparc32/ethread.h
index 513d9f8773..8d49465008 100644
--- a/erts/include/internal/sparc32/ethread.h
+++ b/erts/include/internal/sparc32/ethread.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/sparc32/rwlock.h b/erts/include/internal/sparc32/rwlock.h
index 44de6113b7..1cc516cdad 100644
--- a/erts/include/internal/sparc32/rwlock.h
+++ b/erts/include/internal/sparc32/rwlock.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/sparc32/spinlock.h b/erts/include/internal/sparc32/spinlock.h
index 695fa112b6..ae3b1c9dee 100644
--- a/erts/include/internal/sparc32/spinlock.h
+++ b/erts/include/internal/sparc32/spinlock.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/sparc64/ethread.h b/erts/include/internal/sparc64/ethread.h
index 5f518e5596..6018e738b5 100644
--- a/erts/include/internal/sparc64/ethread.h
+++ b/erts/include/internal/sparc64/ethread.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2007-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2007-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/tile/atomic.h b/erts/include/internal/tile/atomic.h
index 1a2881442e..7f5f83bcc7 100644
--- a/erts/include/internal/tile/atomic.h
+++ b/erts/include/internal/tile/atomic.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/tile/ethr_membar.h b/erts/include/internal/tile/ethr_membar.h
index ccb420d558..50ea2c0265 100644
--- a/erts/include/internal/tile/ethr_membar.h
+++ b/erts/include/internal/tile/ethr_membar.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2011. All Rights Reserved.
+ * Copyright Ericsson AB 2011-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/tile/ethread.h b/erts/include/internal/tile/ethread.h
index 577d275965..0d69673a1c 100644
--- a/erts/include/internal/tile/ethread.h
+++ b/erts/include/internal/tile/ethread.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/win/ethr_atomic.h b/erts/include/internal/win/ethr_atomic.h
index f17526a94d..32c28f692d 100644
--- a/erts/include/internal/win/ethr_atomic.h
+++ b/erts/include/internal/win/ethr_atomic.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2010-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/win/ethr_dw_atomic.h b/erts/include/internal/win/ethr_dw_atomic.h
index 8bed4fb26e..a6b26ab7bb 100644
--- a/erts/include/internal/win/ethr_dw_atomic.h
+++ b/erts/include/internal/win/ethr_dw_atomic.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2011. All Rights Reserved.
+ * Copyright Ericsson AB 2011-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/win/ethr_event.h b/erts/include/internal/win/ethr_event.h
index 458565b9ea..9ee78183ab 100644
--- a/erts/include/internal/win/ethr_event.h
+++ b/erts/include/internal/win/ethr_event.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2009-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/win/ethr_membar.h b/erts/include/internal/win/ethr_membar.h
index 9cba6b605d..c018f6d869 100644
--- a/erts/include/internal/win/ethr_membar.h
+++ b/erts/include/internal/win/ethr_membar.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2011. All Rights Reserved.
+ * Copyright Ericsson AB 2011-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/win/ethread.h b/erts/include/internal/win/ethread.h
index 2fda028825..773be08a9b 100644
--- a/erts/include/internal/win/ethread.h
+++ b/erts/include/internal/win/ethread.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2010-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/include/internal/x86_64/ethread.h b/erts/include/internal/x86_64/ethread.h
index 8887b8d77f..7fc5481629 100644
--- a/erts/include/internal/x86_64/ethread.h
+++ b/erts/include/internal/x86_64/ethread.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/lib/internal/README b/erts/lib/internal/README
index 9beba10bc2..9e5cab26d3 100644
--- a/erts/lib/internal/README
+++ b/erts/lib/internal/README
@@ -1,7 +1,7 @@
%CopyrightBegin%
- Copyright Ericsson AB 2004-2009. All Rights Reserved.
+ Copyright Ericsson AB 2004-2016. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/erts/lib_src/Makefile b/erts/lib_src/Makefile
index 632b8a0b09..882a050ffd 100644
--- a/erts/lib_src/Makefile
+++ b/erts/lib_src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2009. All Rights Reserved.
+# Copyright Ericsson AB 2004-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/lib_src/Makefile.in b/erts/lib_src/Makefile.in
index 74e32ccdce..601f3917a8 100644
--- a/erts/lib_src/Makefile.in
+++ b/erts/lib_src/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2013. All Rights Reserved.
+# Copyright Ericsson AB 2004-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -465,7 +465,6 @@ RELSYSDIR = $(RELEASE_PATH)/erts-$(VSN)
RELEASE_INCLUDES= \
$(ERTS_INCL)/erl_memory_trace_parser.h \
$(ERTS_INCL)/$(TARGET)/erl_int_sizes_config.h \
- $(ERTS_INCL)/$(TARGET)/erl_native_features_config.h \
$(ERTS_INCL)/erl_fixed_size_int_types.h
RELEASE_LIBS=$(ERTS_LIBS)
diff --git a/erts/lib_src/common/erl_memory_trace_parser.c b/erts/lib_src/common/erl_memory_trace_parser.c
index a81068089e..0232708ad1 100644
--- a/erts/lib_src/common/erl_memory_trace_parser.c
+++ b/erts/lib_src/common/erl_memory_trace_parser.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/lib_src/common/erl_misc_utils.c b/erts/lib_src/common/erl_misc_utils.c
index 1262c50718..8186463b9c 100644
--- a/erts/lib_src/common/erl_misc_utils.c
+++ b/erts/lib_src/common/erl_misc_utils.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -160,8 +160,6 @@ erts_milli_sleep(long ms)
if (ms > 0) {
#ifdef __WIN32__
Sleep((DWORD) ms);
-#elif defined(__OSE__)
- delay(ms);
#else
struct timeval tv;
tv.tv_sec = ms / 1000;
@@ -320,10 +318,6 @@ erts_cpu_info_update(erts_cpu_info_t *cpuinfo)
online = 0;
#endif
}
-#elif defined(__OSE__)
- online = ose_num_cpus();
- configured = ose_num_cpus();
- available = ose_num_cpus();
#endif
if (online > configured)
diff --git a/erts/lib_src/common/erl_printf.c b/erts/lib_src/common/erl_printf.c
index 387a104a7a..7781fc2196 100644
--- a/erts/lib_src/common/erl_printf.c
+++ b/erts/lib_src/common/erl_printf.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -165,8 +165,8 @@ write_f(void *vfp, char* buf, size_t len)
return len;
}
-static int
-write_fd(void *vfdp, char* buf, size_t len)
+int
+erts_write_fd(void *vfdp, char* buf, size_t len)
{
ssize_t size;
size_t res = len;
@@ -226,8 +226,8 @@ write_sn(void *vwsnap, char* buf, size_t len)
return rv;
}
-static int
-write_ds(void *vdsbufp, char* buf, size_t len)
+int
+erts_write_ds(void *vdsbufp, char* buf, size_t len)
{
erts_dsprintf_buf_t *dsbufp = (erts_dsprintf_buf_t *) vdsbufp;
size_t need_len = len + 1; /* Also trailing '\0' */
@@ -301,7 +301,7 @@ erts_fdprintf(int fd, const char *format, ...)
va_list arglist;
va_start(arglist, format);
errno = 0;
- res = erts_printf_format(write_fd,(void *)&fd,(char *)format,arglist);
+ res = erts_printf_format(erts_write_fd,(void *)&fd,(char *)format,arglist);
va_end(arglist);
return res;
}
@@ -355,7 +355,7 @@ erts_dsprintf(erts_dsprintf_buf_t *dsbufp, const char *format, ...)
return -EINVAL;
va_start(arglist, format);
errno = 0;
- res = erts_printf_format(write_ds, (void *)dsbufp, (char *)format, arglist);
+ res = erts_printf_format(erts_write_ds, (void *)dsbufp, (char *)format, arglist);
if (dsbufp->str) {
if (res < 0)
dsbufp->str[0] = '\0';
@@ -366,6 +366,20 @@ erts_dsprintf(erts_dsprintf_buf_t *dsbufp, const char *format, ...)
return res;
}
+/*
+ * Callback printf
+ */
+int erts_cbprintf(fmtfn_t cb_fn, void* cb_arg, const char* format, ...)
+{
+ int res;
+ va_list arglist;
+ va_start(arglist, format);
+ errno = 0;
+ res = erts_printf_format(cb_fn, cb_arg, (char *)format, arglist);
+ va_end(arglist);
+ return res;
+}
+
int
erts_vprintf(const char *format, va_list arglist)
{
@@ -411,7 +425,7 @@ erts_vfdprintf(int fd, const char *format, va_list arglist)
{
int res;
errno = 0;
- res = erts_printf_format(write_fd,(void *)&fd,(char *)format,arglist);
+ res = erts_printf_format(erts_write_fd,(void *)&fd,(char *)format,arglist);
return res;
}
@@ -456,7 +470,7 @@ erts_vdsprintf(erts_dsprintf_buf_t *dsbufp, const char *format, va_list arglist)
if (!dsbufp)
return -EINVAL;
errno = 0;
- res = erts_printf_format(write_ds, (void *)dsbufp, (char *)format, arglist);
+ res = erts_printf_format(erts_write_ds, (void *)dsbufp, (char *)format, arglist);
if (dsbufp->str) {
if (res < 0)
dsbufp->str[0] = '\0';
@@ -465,3 +479,10 @@ erts_vdsprintf(erts_dsprintf_buf_t *dsbufp, const char *format, va_list arglist)
}
return res;
}
+
+int
+erts_vcbprintf(fmtfn_t cb_fn, void* cb_arg, const char *format, va_list arglist)
+{
+ errno = 0;
+ return erts_printf_format(cb_fn, cb_arg, (char *)format, arglist);
+}
diff --git a/erts/lib_src/common/erl_printf_format.c b/erts/lib_src/common/erl_printf_format.c
index 307680505c..3daa066fd3 100644
--- a/erts/lib_src/common/erl_printf_format.c
+++ b/erts/lib_src/common/erl_printf_format.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -78,15 +78,7 @@
#endif
#ifndef ERTS_SIZEOF_ETERM
-# ifdef HALFWORD_HEAP_EMULATOR
-# if SIZEOF_VOID_P == 8
-# define ERTS_SIZEOF_ETERM 4
-# else
-# error "HALFWORD_HEAP_EMULATOR only allowed on 64-bit architecture"
-# endif
-# else
-# define ERTS_SIZEOF_ETERM SIZEOF_VOID_P
-# endif
+#define ERTS_SIZEOF_ETERM SIZEOF_VOID_P
#endif
#if defined(__GNUC__)
@@ -102,7 +94,7 @@
#endif
#define FMTC_d 0x0000
-#define FMTC_R 0x0001
+/*empty 0x0001 was RELATIVE */
#define FMTC_o 0x0002
#define FMTC_u 0x0003
#define FMTC_x 0x0004
@@ -166,7 +158,7 @@ static char heX[] = "0123456789ABCDEF";
#define SIGN(X) ((X) > 0 ? 1 : ((X) < 0 ? -1 : 0))
#define USIGN(X) ((X) == 0 ? 0 : 1)
-int (*erts_printf_eterm_func)(fmtfn_t, void*, ErlPfEterm, long, ErlPfEterm*) = NULL;
+int (*erts_printf_eterm_func)(fmtfn_t, void*, ErlPfEterm, long) = NULL;
static int
noop_fn(void *vfp, char* buf, size_t len)
@@ -645,7 +637,6 @@ int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap)
case 'p': ptr++; fmt |= FMTC_p; break;
case 'n': ptr++; fmt |= FMTC_n; break;
case 'T': ptr++; fmt |= FMTC_T; break;
- case 'R': ptr++; fmt |= FMTC_R; break;
case '%':
FMT(fn,arg,ptr,1,count);
ptr++;
@@ -820,11 +811,9 @@ int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap)
default: *va_arg(ap,int*) = count; break;
}
break;
- case FMTC_T: /* Eterm */
- case FMTC_R: { /* Eterm, Eterm* base (base ignored if !HALFWORD_HEAP) */
+ case FMTC_T: { /* Eterm */
long prec;
ErlPfEterm eterm;
- ErlPfEterm* eterm_base;
if (!erts_printf_eterm_func)
return -EINVAL;
@@ -835,16 +824,14 @@ int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap)
else
prec = (long) precision;
eterm = va_arg(ap, ErlPfEterm);
- eterm_base = ((fmt & FMTC_MASK) == FMTC_R) ?
- va_arg(ap, ErlPfEterm*) : NULL;
if (width > 0 && !(fmt & FMTF_adj)) {
- res = (*erts_printf_eterm_func)(noop_fn, NULL, eterm, prec, eterm_base);
+ res = (*erts_printf_eterm_func)(noop_fn, NULL, eterm, prec);
if (res < 0)
return res;
if (width > res)
BLANKS(fn, arg, width - res, count);
}
- res = (*erts_printf_eterm_func)(fn, arg, eterm, prec, eterm_base);
+ res = (*erts_printf_eterm_func)(fn, arg, eterm, prec);
if (res < 0)
return res;
count += res;
diff --git a/erts/lib_src/common/ethr_atomics.c b/erts/lib_src/common/ethr_atomics.c
index 42c078377d..1594d78f5e 100644
--- a/erts/lib_src/common/ethr_atomics.c
+++ b/erts/lib_src/common/ethr_atomics.c
@@ -10,7 +10,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2011-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2011-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/lib_src/common/ethr_aux.c b/erts/lib_src/common/ethr_aux.c
index 0cbb1b2fb8..3501fe335a 100644
--- a/erts/lib_src/common/ethr_aux.c
+++ b/erts/lib_src/common/ethr_aux.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010-2014. All Rights Reserved.
+ * Copyright Ericsson AB 2010-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -139,6 +139,38 @@ x86_init(void)
#endif
/* bit 26 of edx is set if we have sse2 */
ethr_runtime__.conf.have_sse2 = (edx & (1 << 26));
+
+ /* check if we have extended feature set */
+ eax = 0x80000000;
+ ethr_x86_cpuid__(&eax, &ebx, &ecx, &edx);
+
+ if (eax < 0x80000001)
+ return;
+
+ if (eax >= 0x80000007) {
+ /* Advanced Power Management Information */
+ eax = 0x80000007;
+ ethr_x86_cpuid__(&eax, &ebx, &ecx, &edx);
+
+ /* I got the values below from:
+ http://lxr.free-electrons.com/source/arch/x86/include/asm/cpufeature.h
+ They can be gotten from the intel/amd manual as well.
+ */
+
+ ethr_runtime__.conf.have_constant_tsc = (edx & (1 << 8));
+ ethr_runtime__.conf.have_tsc_reliable = (edx & (1 << 23));
+ ethr_runtime__.conf.have_nonstop_tsc = (edx & (1 << 24));
+ ethr_runtime__.conf.have_nonstop_tsc_s3 = (edx & (1 << 30));
+
+ }
+
+ /* Extended Processor Info and Feature Bits */
+ eax = 0x80000001;
+ ethr_x86_cpuid__(&eax, &ebx, &ecx, &edx);
+
+ /* bit 27 of edx is set if we have rdtscp */
+ ethr_runtime__.conf.have_rdtscp = (edx & (1 << 27));
+
}
#endif /* ETHR_X86_RUNTIME_CONF__ */
@@ -188,7 +220,7 @@ ethr_init_common__(ethr_init_data *id)
ethr_min_stack_size__ += ethr_pagesize__;
#endif
/* The system may think that we need more stack */
-#if defined(PTHREAD_STACK_MIN)
+#if defined(ETHR_HAVE_USABLE_PTHREAD_STACK_MIN)
if (ethr_min_stack_size__ < PTHREAD_STACK_MIN)
ethr_min_stack_size__ = PTHREAD_STACK_MIN;
#elif defined(_SC_THREAD_STACK_MIN)
@@ -207,18 +239,7 @@ ethr_init_common__(ethr_init_data *id)
ethr_min_stack_size__ = ETHR_B2KW(ethr_min_stack_size__);
-#ifdef __OSE__
- /* For supervisor processes, OSE adds a number of bytes to the requested stack. With this
- * addition, the resulting size must not exceed the largest available stack size. The number
- * of bytes that will be added is configured in the monolith and can therefore not be
- * specified here. We simply assume that it is less than 0x1000. The available stack sizes
- * are configured in the .lmconf file and the largest one is usually 65536 bytes.
- * Consequently, the requested stack size is limited to 0xF000.
- */
- ethr_max_stack_size__ = 0xF000;
-#else
ethr_max_stack_size__ = 32*1024*1024;
-#endif
#if SIZEOF_VOID_P == 8
ethr_max_stack_size__ *= 2;
#endif
@@ -664,10 +685,6 @@ ETHR_IMPL_NORETURN__ ethr_fatal_error__(const char *file,
int ethr_assert_failed(const char *file, int line, const char *func, char *a)
{
fprintf(stderr, "%s:%d: %s(): Assertion failed: %s\n", file, line, func, a);
-#ifdef __OSE__
- ramlog_printf("%d: %s:%d: %s(): Assertion failed: %s\n",
- current_process(),file, line, func, a);
-#endif
ethr_abort__();
return 0;
}
diff --git a/erts/lib_src/common/ethr_cbf.c b/erts/lib_src/common/ethr_cbf.c
index e79ec2b40c..037559be22 100644
--- a/erts/lib_src/common/ethr_cbf.c
+++ b/erts/lib_src/common/ethr_cbf.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010. All Rights Reserved.
+ * Copyright Ericsson AB 2010-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/lib_src/common/ethr_mutex.c b/erts/lib_src/common/ethr_mutex.c
index 72aa34ec1c..5e7e7b2f32 100644
--- a/erts/lib_src/common/ethr_mutex.c
+++ b/erts/lib_src/common/ethr_mutex.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2010-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1250,7 +1250,7 @@ ethr_cond_wait(ethr_cond *cnd, ethr_mutex *mtx)
return 0;
}
-#elif (defined(ETHR_PTHREADS) || defined(ETHR_OSE_THREADS)) && !defined(ETHR_DBG_WIN_MTX_WITH_PTHREADS)
+#elif defined(ETHR_PTHREADS) && !defined(ETHR_DBG_WIN_MTX_WITH_PTHREADS)
/* -- pthread mutex and condition variables -------------------------------- */
int
diff --git a/erts/lib_src/ose/ethr_event.c b/erts/lib_src/ose/ethr_event.c
deleted file mode 100644
index 24ea191c4b..0000000000
--- a/erts/lib_src/ose/ethr_event.c
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2009-2010. 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%
- */
-
-/*
- * Author: Rickard Green
- */
-
-#define ETHR_INLINE_FUNC_NAME_(X) X ## __
-#define ETHR_EVENT_IMPL__
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "ethread.h"
-
-#ifdef USE_PTHREAD_API
-
-int
-ethr_event_init(ethr_event *e)
-{
- int res;
- ethr_atomic32_init(&e->state, ETHR_EVENT_OFF__);
- res = pthread_mutex_init(&e->mtx, NULL);
- if (res != 0)
- return res;
- res = pthread_cond_init(&e->cnd, NULL);
- if (res != 0) {
- pthread_mutex_destroy(&e->mtx);
- return res;
- }
- return 0;
-}
-
-int
-ethr_event_destroy(ethr_event *e)
-{
- int res;
- res = pthread_mutex_destroy(&e->mtx);
- if (res != 0)
- return res;
- res = pthread_cond_destroy(&e->cnd);
- if (res != 0)
- return res;
- return 0;
-}
-
-static ETHR_INLINE int
-wait__(ethr_event *e, int spincount)
-{
- int sc = spincount;
- ethr_sint32_t val;
- int res, ulres;
- int until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS;
-
- if (spincount < 0)
- ETHR_FATAL_ERROR__(EINVAL);
-
- while (1) {
- val = ethr_atomic32_read(&e->state);
- if (val == ETHR_EVENT_ON__)
- return 0;
- if (sc == 0)
- break;
- sc--;
- ETHR_SPIN_BODY;
- if (--until_yield == 0) {
- until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS;
- res = ETHR_YIELD();
- if (res != 0)
- ETHR_FATAL_ERROR__(res);
- }
- }
-
- if (val != ETHR_EVENT_OFF_WAITER__) {
- val = ethr_atomic32_cmpxchg(&e->state,
- ETHR_EVENT_OFF_WAITER__,
- ETHR_EVENT_OFF__);
- if (val == ETHR_EVENT_ON__)
- return 0;
- ETHR_ASSERT(val == ETHR_EVENT_OFF__);
- }
-
- ETHR_ASSERT(val == ETHR_EVENT_OFF_WAITER__
- || val == ETHR_EVENT_OFF__);
-
- res = pthread_mutex_lock(&e->mtx);
- if (res != 0)
- ETHR_FATAL_ERROR__(res);
-
- while (1) {
-
- val = ethr_atomic32_read(&e->state);
- if (val == ETHR_EVENT_ON__)
- break;
-
- res = pthread_cond_wait(&e->cnd, &e->mtx);
- if (res == EINTR)
- break;
- if (res != 0)
- ETHR_FATAL_ERROR__(res);
- }
-
- ulres = pthread_mutex_unlock(&e->mtx);
- if (ulres != 0)
- ETHR_FATAL_ERROR__(ulres);
-
- return res; /* 0 || EINTR */
-}
-
-#else
-/* --- OSE implementation of events ---------------------------- */
-
-#ifdef DEBUG
-union SIGNAL {
- SIGSELECT signo;
-};
-#endif
-
-int
-ethr_event_init(ethr_event *e)
-{
- ethr_atomic32_init(&e->state, ETHR_EVENT_OFF__);
- e->proc = current_process();
- return 0;
-}
-
-int
-ethr_event_destroy(ethr_event *e)
-{
- return 0;
-}
-
-static ETHR_INLINE int
-wait__(ethr_event *e, int spincount)
-{
- int sc = spincount;
- int res;
- int until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS;
-
- if (spincount < 0)
- ETHR_FATAL_ERROR__(EINVAL);
-
- ETHR_ASSERT(e->proc == current_process());
- ETHR_ASSERT(get_fsem(current_process()) == 0);
-
- while (1) {
- ethr_sint32_t val;
- while (1) {
- val = ethr_atomic32_read(&e->state);
- if (val == ETHR_EVENT_ON__)
- return 0;
- if (sc == 0)
- break;
- sc--;
- ETHR_SPIN_BODY;
- if (--until_yield == 0) {
- until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS;
- res = ETHR_YIELD();
- if (res != 0)
- ETHR_FATAL_ERROR__(res);
- }
- }
- if (val != ETHR_EVENT_OFF_WAITER__) {
- val = ethr_atomic32_cmpxchg(&e->state,
- ETHR_EVENT_OFF_WAITER__,
- ETHR_EVENT_OFF__);
- if (val == ETHR_EVENT_ON__)
- return 0;
- ETHR_ASSERT(val == ETHR_EVENT_OFF__);
- }
-
- wait_fsem(1);
-
- ETHR_ASSERT(get_fsem(current_process()) == 0);
- }
-}
-
-#endif
-
-void
-ethr_event_reset(ethr_event *e)
-{
- ethr_event_reset__(e);
-}
-
-void
-ethr_event_set(ethr_event *e)
-{
- ethr_event_set__(e);
-}
-
-int
-ethr_event_wait(ethr_event *e)
-{
- return wait__(e, 0);
-}
-
-int
-ethr_event_swait(ethr_event *e, int spincount)
-{
- return wait__(e, spincount);
-}
diff --git a/erts/lib_src/ose/ethread.c b/erts/lib_src/ose/ethread.c
deleted file mode 100644
index dc16acdd08..0000000000
--- a/erts/lib_src/ose/ethread.c
+++ /dev/null
@@ -1,833 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2010-2011. 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%
- */
-
-/*
- * Description: OSE implementation of the ethread library
- * Author: Lukas Larsson
- */
-
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "stdio.h"
-#ifdef ETHR_TIME_WITH_SYS_TIME
-# include "time.h"
-# include "sys/time.h"
-#else
-# ifdef ETHR_HAVE_SYS_TIME_H
-# include "sys/time.h"
-# else
-# include "time.h"
-# endif
-#endif
-#include "sys/types.h"
-#include "unistd.h"
-
-#include "limits.h"
-
-#define ETHR_INLINE_FUNC_NAME_(X) X ## __
-#define ETHREAD_IMPL__
-
-#include "ethread.h"
-#include "ethr_internal.h"
-
-#include "erl_printf.h"
-#include "efs.h"
-#include "ose.h"
-
-#include "ose_spi.h"
-
-#include "string.h"
-#include "ctype.h"
-#include "stdlib.h"
-
-#ifndef ETHR_HAVE_ETHREAD_DEFINES
-#error Missing configure defines
-#endif
-
-#define ETHR_INVALID_TID_ID -1
-
-#define DEFAULT_PRIO_NAME "ERTS_ETHR_DEFAULT_PRIO"
-
-/* Set the define to 1 to get some logging */
-#if 0
-#include "ramlog.h"
-#define LOG(output) ramlog_printf output
-#else
-#define LOG(output)
-#endif
-
-static ethr_tid main_thr_tid;
-static const char* own_tid_key = "ethread_own_tid";
-ethr_tsd_key ethr_ts_event_key__;
-
-#define ETHREADWRAPDATASIG 1
-
-/* Init data sent to thr_wrapper() */
-typedef struct {
- SIGSELECT sig_no;
- ethr_ts_event *tse;
- ethr_tid *tid;
- ethr_sint32_t result;
- void *(*thr_func)(void *);
- void *arg;
- void *prep_func_res;
- const char *name;
-} ethr_thr_wrap_data__;
-
-union SIGNAL {
- SIGSELECT sig_no;
- ethr_thr_wrap_data__ data;
-};
-
-#define ETHR_GET_OWN_TID__ ((ethr_tid *) get_envp(current_process(),\
- own_tid_key))
-
-/*
- * --------------------------------------------------------------------------
- * Static functions
- * --------------------------------------------------------------------------
- */
-
-/* Will retrive the instrinsic name by removing the 'prefix' and the
- * suffix from 'name'.
- * The 'prefix' is given as an inparameter. If NULL or an empty string no
- * prefix will be removed.
- * If 'strip_suffix' is 1 suffixes in the form of '_123' will be removed.
- * Will return a pointer to a newly allocated buffer containing the intrinsic
- * name in uppercase characters.
- * The caller must remember to free this buffer when no lnger needed.
- */
-static char *
-ethr_intrinsic_name(const char *name, const char *prefix, int strip_suffix)
-{
- const char *start = name;
- const char *end = name + strlen(name);
- char *intrinsic_name = NULL;
- int i;
-
- if (name == NULL) {
- LOG(("ERTS - ethr_intrinsic_namNo input name.\n"));
- return NULL;
- }
-
- /* take care of the prefix */
- if ((prefix != NULL) && (*prefix != '\0')) {
- const char *found = strstr(name, prefix);
-
- if (found == name) {
- /* found the prefix at the beginning */
- start += strlen(prefix);
- }
- }
-
- /* take care of the suffix */
- if (strip_suffix) {
- const char *suffix_start = strrchr(start, '_');
-
- if (suffix_start != NULL) {
- const char *ch;
- int only_numbers = 1;
-
- for (ch = suffix_start + 1; *ch != '\0'; ch++) {
- if (strchr("0123456789", *ch) == NULL) {
- only_numbers = 0;
- break;
- }
- }
-
- if (only_numbers) {
- end = suffix_start;
- }
- }
- }
-
- intrinsic_name = malloc(end - start + 1);
- for (i = 0; (start + i) < end; i++) {
- intrinsic_name[i] = toupper(start[i]);
- }
- intrinsic_name[i] = '\0';
-
- return intrinsic_name;
-}
-
-static char *
-ethr_get_amended_env(const char *name, const char *prefix, const char *suffix)
-{
- unsigned len;
- char *env_name = NULL;
- char *env_value = NULL;
-
- if (name == NULL) {
- return NULL;
- }
-
- len = strlen(name);
-
- if (prefix != NULL) {
- len += strlen(prefix);
- }
-
- if (suffix != NULL) {
- len += strlen(suffix);
- }
-
- env_name = malloc(len + 1);
- sprintf(env_name, "%s%s%s", (prefix != NULL) ? prefix : "",
- name,
- (suffix != NULL) ? suffix : "");
- env_value = get_env(get_bid(current_process()), env_name);
-
- if (env_value == NULL) {
- LOG(("ERTS - ethr_get_amended_env(): %s environment variable not present\n", env_name));
- } else {
- LOG(("ERTS - ethr_get_amended_env(): Found %s environment variable: %s.\n", env_name, env_value));
- }
- free(env_name);
-
- return env_value;
-}
-
-/* Reads the environment variable derived from 'name' and interprets it as as an
- * OSE priority. If successfull it will update 'out_prio'.
- * Returns: 0 if successfull
- * -1 orherwise.
- */
-static int
-ethr_get_prio(const char *name, OSPRIORITY *out_prio)
-{
- int rc = -1;
- char *intrinsic_name = NULL;
- char *prio_env = NULL;
- long prio;
- char *endptr = NULL;
-
- LOG(("ERTS - ethr_get_prio(): name: %s.\n", name));
-
- intrinsic_name = ethr_intrinsic_name(name, NULL, 1);
- LOG(("ERTS - ethr_get_prio(): Intrinsic name: %s.\n", intrinsic_name));
-
- prio_env = ethr_get_amended_env(intrinsic_name, "ERTS_", "_PRIO");
- if (prio_env == NULL) {
- goto fini;
- }
-
- prio = efs_str_to_long(prio_env, (const char **)&endptr);
- if (endptr != NULL) {
- LOG(("ERTS - ethr_get_prio(): Environment varible for '%s' includes "
- "non-numerical characters: '%s'.\n", intrinsic_name, prio_env));
- goto fini;
- }
-
- if ((prio < 0) || (prio > 32)) {
- LOG(("ERTS - ethr_get_prio(): prio for '%s' (%d) is out of bounds (0-32).\n",
- intrinsic_name, prio));
- goto fini;
- }
-
- /* Success */
- *out_prio = (OSPRIORITY)prio;
- rc = 0;
-
-fini:
- if (intrinsic_name != NULL) {
- free(intrinsic_name);
- }
- if (prio_env != NULL) {
- free_buf((union SIGNAL **) &prio_env);
- }
-
- return rc;
-}
-
-static PROCESS blockId(void) {
- static PROCESS bid = (PROCESS)0;
-
- /* For now we only use the same block. */
- /* if (bid == 0) {
- bid = create_block("Erlang-VM", 0, 0, 0, 0);
- }
- return bid; */
- return 0;
-}
-
-static void thr_exit_cleanup(ethr_tid *tid, void *res)
-{
-
- ETHR_ASSERT(tid == ETHR_GET_OWN_TID__);
-
- tid->res = res;
-
- ethr_run_exit_handlers__();
- ethr_ts_event_destructor__((void *) ethr_get_tse__());
-}
-
-//static OS_PROCESS(thr_wrapper);
-static OS_PROCESS(thr_wrapper)
-{
- ethr_tid my_tid;
- ethr_sint32_t result;
- void *res;
- void *(*thr_func)(void *);
- void *arg;
- ethr_ts_event *tsep = NULL;
-
-#ifdef DEBUG
- {
- PROCESS pid = current_process();
-
- const char *execMode;
-
- PROCESS bid = get_bid(pid);
-
- /* In the call below, 16 is a secret number provided by frbr that makes
- * the function return current domain. */
- OSADDRESS domain = get_pid_info(current_process(), 16);
-
-#ifdef HAVE_OSE_SPI_H
- execMode = get_pid_info(pid, OSE_PI_SUPERVISOR)
- ? "Supervisor"
- : "User";
-#else
- execMode = "unknown";
-#endif
-
- fprintf(stderr,"[0x%x] New process. Bid:0x%x, domain:%d, exec mode:%s\n",
- current_process(), bid, domain, execMode);
- }
-#endif
-
- {
- SIGSELECT sigsel[] = {1,ETHREADWRAPDATASIG};
- union SIGNAL *init_msg = receive(sigsel);
-
- thr_func = init_msg->data.thr_func;
- arg = init_msg->data.arg;
-
- result = (ethr_sint32_t) ethr_make_ts_event__(&tsep);
-
- if (result == 0) {
- tsep->iflgs |= ETHR_TS_EV_ETHREAD;
- my_tid = *init_msg->data.tid;
- set_envp(current_process(), own_tid_key, (OSADDRESS)&my_tid);
- if (ethr_thr_child_func__)
- ethr_thr_child_func__(init_msg->data.prep_func_res);
- }
-
- init_msg->data.result = result;
-
- send(&init_msg,sender(&init_msg));
- }
-
- /* pthread mutex api says we have to do this */
- signal_fsem(current_process());
- ETHR_ASSERT(get_fsem(current_process()) == 0);
-
- res = result == 0 ? (*thr_func)(arg) : NULL;
-
- ethr_thr_exit(&res);
-}
-
-/* internal exports */
-
-int ethr_set_tse__(ethr_ts_event *tsep)
-{
- return ethr_tsd_set(ethr_ts_event_key__,(void *) tsep);
-}
-
-ethr_ts_event *ethr_get_tse__(void)
-{
- return (ethr_ts_event *) ethr_tsd_get(ethr_ts_event_key__);
-}
-
-#if defined(ETHR_PPC_RUNTIME_CONF__)
-
-static int
-ppc_init__(void)
-{
- int pid;
-
-
- ethr_runtime__.conf.have_lwsync = 0;
-
- return 0;
-}
-
-#endif
-
-#if defined(ETHR_X86_RUNTIME_CONF__)
-
-void
-ethr_x86_cpuid__(int *eax, int *ebx, int *ecx, int *edx)
-{
-#if ETHR_SIZEOF_PTR == 4
- int have_cpuid;
- /*
- * If it is possible to toggle eflags bit 21,
- * we have the cpuid instruction.
- */
- __asm__ ("pushf\n\t"
- "popl %%eax\n\t"
- "movl %%eax, %%ecx\n\t"
- "xorl $0x200000, %%eax\n\t"
- "pushl %%eax\n\t"
- "popf\n\t"
- "pushf\n\t"
- "popl %%eax\n\t"
- "movl $0x0, %0\n\t"
- "xorl %%ecx, %%eax\n\t"
- "jz no_cpuid\n\t"
- "movl $0x1, %0\n\t"
- "no_cpuid:\n\t"
- : "=r"(have_cpuid)
- :
- : "%eax", "%ecx", "cc");
- if (!have_cpuid) {
- *eax = *ebx = *ecx = *edx = 0;
- return;
- }
-#endif
-#if ETHR_SIZEOF_PTR == 4 && defined(__PIC__) && __PIC__
- /*
- * When position independet code is used in 32-bit mode, the B register
- * is used for storage of global offset table address, and we may not
- * use it as input or output in an asm. We need to save and restore the
- * B register explicitly (for some reason gcc doesn't provide this
- * service to us).
- */
- __asm__ ("pushl %%ebx\n\t"
- "cpuid\n\t"
- "movl %%ebx, %1\n\t"
- "popl %%ebx\n\t"
- : "=a"(*eax), "=r"(*ebx), "=c"(*ecx), "=d"(*edx)
- : "0"(*eax)
- : "cc");
-#else
- __asm__ ("cpuid\n\t"
- : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx)
- : "0"(*eax)
- : "cc");
-#endif
-}
-
-#endif /* ETHR_X86_RUNTIME_CONF__ */
-
-/*
- * --------------------------------------------------------------------------
- * Exported functions
- * --------------------------------------------------------------------------
- */
-
-int
-ethr_init(ethr_init_data *id)
-{
- int res;
-
- if (!ethr_not_inited__)
- return EINVAL;
-
-
-#if defined(ETHR_PPC_RUNTIME_CONF__)
- res = ppc_init__();
- if (res != 0)
- goto error;
-#endif
-
- res = ethr_init_common__(id);
- if (res != 0)
- goto error;
-
- main_thr_tid.id = current_process();
- main_thr_tid.tsd_key_index = 0;
-
- set_envp(current_process(),own_tid_key,(OSADDRESS)&main_thr_tid);
- signal_fsem(current_process());
-
-
- ETHR_ASSERT(&main_thr_tid == ETHR_GET_OWN_TID__);
-
- ethr_not_inited__ = 0;
-
- ethr_tsd_key_create(&ethr_ts_event_key__,"ethread_tse");
-
- return 0;
- error:
- ethr_not_inited__ = 1;
- return res;
-
-}
-
-int
-ethr_late_init(ethr_late_init_data *id)
-{
- int res = ethr_late_init_common__(id);
- if (res != 0)
- return res;
- ethr_not_completely_inited__ = 0;
- return res;
-}
-
-int
-ethr_thr_create(ethr_tid *tid, void * (*func)(void *), void *arg,
- ethr_thr_opts *opts)
-{
- int res;
- int use_stack_size = (opts && opts->suggested_stack_size >= 0
- ? opts->suggested_stack_size
- : 0x200 /* Use system default */);
- OSPRIORITY use_prio;
- char *use_name;
- char default_thr_name[20];
- static int no_of_thr = 0;
- cpuid_t use_core;
-
- union SIGNAL *init_msg;
- SIGSELECT sigsel[] = {1,ETHREADWRAPDATASIG};
- void *prep_func_res;
-
-
- if (opts != NULL) {
- LOG(("ERTS - ethr_thr_create(): opts supplied: name: %s, coreNo: %u.\n",
- opts->name, opts->coreNo));
- use_name = opts->name;
- use_core = opts->coreNo;
- if (0 != ethr_get_prio(use_name, &use_prio)) {
- if (0 != ethr_get_prio("DEFAULT", &use_prio)) {
- use_prio = get_pri(current_process());
- LOG(("ERTS - ethr_thr_create(): Using current process' prio: %d.\n", use_prio));
- } else {
- LOG(("ERTS - ethr_thr_create(): Using default prio: %d.\n", use_prio));
- }
- } else {
- LOG(("ERTS - ethr_thr_create(): Using configured prio: %d.\n", use_prio));
- }
- } else {
- LOG(("ERTS - ethr_thr_create(): opts not supplied. Using defaults.\n"));
- no_of_thr++;
- sprintf(default_thr_name, "ethread_%d", no_of_thr);
- use_name = default_thr_name;
- use_core = ose_cpu_id();
-
- if (0 != ethr_get_prio("DEFAULT", &use_prio)) {
- use_prio = get_pri(current_process());
- LOG(("ERTS - ethr_thr_create(): Using current process' prio: %d.\n", use_prio));
- }
- }
-
-#ifdef ETHR_MODIFIED_DEFAULT_STACK_SIZE
- if (use_stack_size < 0)
- use_stack_size = ETHR_MODIFIED_DEFAULT_STACK_SIZE;
-#endif
-
-#if ETHR_XCHK
- if (ethr_not_completely_inited__) {
- ETHR_ASSERT(0);
- return EACCES;
- }
- if (!tid || !func) {
- ETHR_ASSERT(0);
- return EINVAL;
- }
-#endif
-
- if (use_stack_size >= 0) {
- size_t suggested_stack_size = (size_t) use_stack_size;
- size_t stack_size;
-#ifdef ETHR_DEBUG
- suggested_stack_size /= 2; /* Make sure we got margin */
-#endif
-#ifdef ETHR_STACK_GUARD_SIZE
- /* The guard is at least on some platforms included in the stack size
- passed when creating threads */
- suggested_stack_size += ETHR_B2KW(ETHR_STACK_GUARD_SIZE);
-#endif
-
- if (suggested_stack_size < ethr_min_stack_size__)
- stack_size = ETHR_KW2B(ethr_min_stack_size__);
- else if (suggested_stack_size > ethr_max_stack_size__)
- stack_size = ETHR_KW2B(ethr_max_stack_size__);
- else
- stack_size = ETHR_PAGE_ALIGN(ETHR_KW2B(suggested_stack_size));
- use_stack_size = stack_size;
- }
-
- init_msg = alloc(sizeof(ethr_thr_wrap_data__), ETHREADWRAPDATASIG);
-
- /* Call prepare func if it exist */
- if (ethr_thr_prepare_func__)
- init_msg->data.prep_func_res = ethr_thr_prepare_func__();
- else
- init_msg->data.prep_func_res = NULL;
-
- LOG(("ERTS - ethr_thr_create(): Process [0x%x] is creating '%s', coreNo = %u, prio:%u\n",
- current_process(), use_name, use_core, use_prio));
-
- tid->id = create_process(OS_PRI_PROC, use_name, thr_wrapper,
- use_stack_size, use_prio, 0,
- get_bid(current_process()), NULL, 0, 0);
- if (ose_bind_process(tid->id, use_core)) {
- LOG(("ERTS - ethr_thr_create(): Bound pid 0x%x (%s) to core no %u.\n",
- tid->id, use_name, use_core));
- } else {
- LOG(("ERTS - ethr_thr_create(): Failed binding pid 0x%x (%s) to core no %u.\n",
- tid->id, use_name, use_core));
- }
-
- /*FIXME!!! Normally this shouldn't be used in shared mode. Still there is
- * a problem with stdin fd in fd_ processes which should be further
- * investigated */
- efs_clone(tid->id);
-
- tid->tsd_key_index = 0;
- tid->res = NULL;
-
- init_msg->data.tse = ethr_get_ts_event();
- init_msg->data.thr_func = func;
- init_msg->data.arg = arg;
- init_msg->data.tid = tid;
- init_msg->data.name = opts->name;
-
- send(&init_msg, tid->id);
-
- start(tid->id);
- init_msg = receive(sigsel);
-
- res = init_msg->data.result;
- prep_func_res = init_msg->data.prep_func_res;
-
- free_buf(&init_msg);
- /* Cleanup... */
-
- if (ethr_thr_parent_func__)
- ethr_thr_parent_func__(prep_func_res);
-
- LOG(("ERTS - ethr_thr_create(): Exiting.\n"));
- return res;
-}
-
-int
-ethr_thr_join(ethr_tid tid, void **res)
-{
- SIGSELECT sigsel[] = {1,OS_ATTACH_SIG};
-#if ETHR_XCHK
- if (ethr_not_inited__) {
- ETHR_ASSERT(0);
- return EACCES;
- }
-#endif
-
- if (tid.id == ETHR_INVALID_TID_ID)
- return EINVAL;
-
- attach(NULL,tid.id);
- receive(sigsel);
-
- if (res)
- *res = tid.res;
-
- return 0;
-}
-
-int
-ethr_thr_detach(ethr_tid tid)
-{
-#if ETHR_XCHK
- if (ethr_not_inited__) {
- ETHR_ASSERT(0);
- return EACCES;
- }
-#endif
- return 0;
-}
-
-void
-ethr_thr_exit(void *res)
-{
- ethr_tid *tid;
-#if ETHR_XCHK
- if (ethr_not_inited__) {
- ETHR_ASSERT(0);
- return;
- }
-#endif
- tid = ETHR_GET_OWN_TID__;
- if (!tid) {
- ETHR_ASSERT(0);
- kill_proc(current_process());
- }
- thr_exit_cleanup(tid, res);
- /* Harakiri possible? */
- kill_proc(current_process());
-}
-
-ethr_tid
-ethr_self(void)
-{
- ethr_tid *tid;
-#if ETHR_XCHK
- if (ethr_not_inited__) {
- ethr_tid dummy_tid = {ETHR_INVALID_TID_ID, 0, NULL};
- ETHR_ASSERT(0);
- return dummy_tid;
- }
-#endif
- tid = ETHR_GET_OWN_TID__;
- if (!tid) {
- ethr_tid dummy_tid = {ETHR_INVALID_TID_ID, 0, NULL};
- return dummy_tid;
- }
- return *tid;
-}
-
-int
-ethr_equal_tids(ethr_tid tid1, ethr_tid tid2)
-{
- return tid1.id == tid2.id && tid1.id != ETHR_INVALID_TID_ID;
-}
-
-
-/*
- * Thread specific events
- */
-
-ethr_ts_event *
-ethr_get_ts_event(void)
-{
- return ethr_get_ts_event__();
-}
-
-void
-ethr_leave_ts_event(ethr_ts_event *tsep)
-{
- ethr_leave_ts_event__(tsep);
-}
-
-/*
- * Thread specific data
- */
-
-int
-ethr_tsd_key_create(ethr_tsd_key *keyp, char *keyname)
-{
-
-#if ETHR_XCHK
- if (ethr_not_inited__) {
- ETHR_ASSERT(0);
- return EACCES;
- }
- if (!keyp) {
- ETHR_ASSERT(0);
- return EINVAL;
- }
-#endif
-
- ose_create_ppdata(keyname,keyp);
-
- return 0;
-}
-
-int
-ethr_tsd_key_delete(ethr_tsd_key key)
-{
-#if ETHR_XCHK
- if (ethr_not_inited__) {
- ETHR_ASSERT(0);
- return EACCES;
- }
-#endif
- /* Not possible to delete ppdata */
-
- return 0;
-}
-
-int
-ethr_tsd_set(ethr_tsd_key key, void *value)
-{
- void **ppdp;
-#if ETHR_XCHK
- if (ethr_not_inited__) {
- ETHR_ASSERT(0);
- return EACCES;
- }
-#endif
- ppdp = (void **)ose_get_ppdata(key);
- *ppdp = value;
- return 0;
-}
-
-void *
-ethr_tsd_get(ethr_tsd_key key)
-{
-#if ETHR_XCHK
- if (ethr_not_inited__) {
- ETHR_ASSERT(0);
- return NULL;
- }
-#endif
- return *(void**)ose_get_ppdata(key);
-}
-
-/*
- * Signal functions
- */
-
-#if ETHR_HAVE_ETHR_SIG_FUNCS
-
-int ethr_sigmask(int how, const sigset_t *set, sigset_t *oset)
-{
-#if ETHR_XCHK
- if (ethr_not_inited__) {
- ETHR_ASSERT(0);
- return EACCES;
- }
- if (!set && !oset) {
- ETHR_ASSERT(0);
- return EINVAL;
- }
-#endif
- return pthread_sigmask(how, set, oset);
-}
-
-int ethr_sigwait(const sigset_t *set, int *sig)
-{
-#if ETHR_XCHK
- if (ethr_not_inited__) {
- ETHR_ASSERT(0);
- return EACCES;
- }
- if (!set || !sig) {
- ETHR_ASSERT(0);
- return EINVAL;
- }
-#endif
- if (sigwait(set, sig) < 0)
- return errno;
- return 0;
-}
-
-#endif /* #if ETHR_HAVE_ETHR_SIG_FUNCS */
-
-ETHR_IMPL_NORETURN__
-ethr_abort__(void)
-{
- abort();
-}
diff --git a/erts/lib_src/pthread/ethr_event.c b/erts/lib_src/pthread/ethr_event.c
index 69e7be342c..464875570a 100644
--- a/erts/lib_src/pthread/ethr_event.c
+++ b/erts/lib_src/pthread/ethr_event.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2010. All Rights Reserved.
+ * Copyright Ericsson AB 2009-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -184,6 +184,8 @@ return_event_on:
#include <errno.h>
#include <string.h>
+#include "erl_misc_utils.h"
+
#ifdef __DARWIN__
struct ethr_event_fdsets___ {
@@ -346,12 +348,9 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout)
ethr_sint32_t val;
int res, ulres;
int until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS;
- ethr_sint64_t time = 0; /* SHUT UP annoying faulty warning... */
+ ethr_sint64_t time = 0;
#ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME
- ethr_sint64_t start = 0; /* SHUT UP annoying faulty warning... */
-#endif
-#ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC
- struct timespec cond_timeout;
+ ethr_sint64_t timeout_time = 0; /* SHUT UP annoying faulty warning... */
#endif
val = ethr_atomic32_read(&e->state);
@@ -365,30 +364,27 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout)
if (timeout == 0)
return ETIMEDOUT;
else {
- time = timeout;
- switch (e->fd[0]) {
- case ETHR_EVENT_INVALID_FD__:
#ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME
- start = ethr_get_monotonic_time();
+ timeout_time = ethr_get_monotonic_time();
+ timeout_time += timeout;
#endif
+ switch (e->fd[0]) {
+ case ETHR_EVENT_INVALID_FD__:
+ time = timeout;
setup_nonblocking_pipe(e);
break;
#ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC
case ETHR_EVENT_COND_TIMEDWAIT__:
- time += ethr_get_monotonic_time();
- cond_timeout.tv_sec = time / (1000*1000*1000);
- cond_timeout.tv_nsec = time % (1000*1000*1000);
+ time = -1;
if (spincount == 0)
goto set_event_off_waiter;
break;
#endif
default:
+ time = timeout;
/* Already initialized pipe... */
if (spincount == 0)
goto set_select_timeout;
-#ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME
- start = ethr_get_monotonic_time();
-#endif
break;
}
}
@@ -418,6 +414,9 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout)
|| e->fd[0] == ETHR_EVENT_COND_TIMEDWAIT__
#endif
) {
+#ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC
+ struct timespec cond_timeout;
+#endif
set_event_off_waiter:
@@ -446,9 +445,35 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout)
#ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC
if (timeout > 0) {
+ if (time != timeout_time) {
+ time = timeout_time;
+
+#if ERTS_USE_PREMATURE_TIMEOUT
+ {
+ ethr_sint64_t rtmo;
+
+ rtmo = timeout_time - ethr_get_monotonic_time();
+ if (rtmo <= 0) {
+ res = ETIMEDOUT;
+ break;
+ }
+ time = timeout_time;
+ time -= ERTS_PREMATURE_TIMEOUT(rtmo, 1000*1000*1000);
+ }
+#endif
+
+ cond_timeout.tv_sec = time / (1000*1000*1000);
+ cond_timeout.tv_nsec = time % (1000*1000*1000);
+ }
res = pthread_cond_timedwait(&e->cnd, &e->mtx, &cond_timeout);
- if (res == EINTR || res == ETIMEDOUT)
+ if (res == EINTR
+ || (res == ETIMEDOUT
+#if ERTS_USE_PREMATURE_TIMEOUT
+ && time == timeout_time
+#endif
+ )) {
break;
+ }
}
else
#endif
@@ -477,7 +502,11 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout)
struct timeval select_timeout;
#ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME
- time -= ethr_get_monotonic_time() - start;
+#if ERTS_USE_PREMATURE_TIMEOUT
+ restart_select:
+#endif
+
+ time = timeout_time - ethr_get_monotonic_time();
if (time <= 0)
return ETIMEDOUT;
#endif
@@ -492,6 +521,11 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout)
*/
time = ((time - 1) / 1000) + 1;
+#if defined(ETHR_HAVE_ETHR_GET_MONOTONIC_TIME) \
+ && ERTS_USE_PREMATURE_TIMEOUT
+ time -= ERTS_PREMATURE_TIMEOUT(time, 1000*1000);
+#endif
+
select_timeout.tv_sec = time / (1000*1000);
select_timeout.tv_usec = time % (1000*1000);
@@ -559,7 +593,11 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout)
val = ethr_atomic32_read(&e->state);
if (val == ETHR_EVENT_ON__)
goto return_event_on;
-
+#if defined(ETHR_HAVE_ETHR_GET_MONOTONIC_TIME) \
+ && ERTS_USE_PREMATURE_TIMEOUT
+ if (res == ETIMEDOUT)
+ goto restart_select; /* Verify timeout */
+#endif
}
return res;
diff --git a/erts/lib_src/pthread/ethr_x86_sse2_asm.c b/erts/lib_src/pthread/ethr_x86_sse2_asm.c
index 7ce5a6d98a..bdcf3ac1c3 100644
--- a/erts/lib_src/pthread/ethr_x86_sse2_asm.c
+++ b/erts/lib_src/pthread/ethr_x86_sse2_asm.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2011. All Rights Reserved.
+ * Copyright Ericsson AB 2011-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/lib_src/pthread/ethread.c b/erts/lib_src/pthread/ethread.c
index ef11559654..29bffa7e12 100644
--- a/erts/lib_src/pthread/ethread.c
+++ b/erts/lib_src/pthread/ethread.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2010-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/lib_src/utils/make_atomics_api b/erts/lib_src/utils/make_atomics_api
index 4b37e3fa74..f960b97c87 100755
--- a/erts/lib_src/utils/make_atomics_api
+++ b/erts/lib_src/utils/make_atomics_api
@@ -4,7 +4,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/lib_src/win/ethr_event.c b/erts/lib_src/win/ethr_event.c
index 6951a216c5..383f7c876e 100644
--- a/erts/lib_src/win/ethr_event.c
+++ b/erts/lib_src/win/ethr_event.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2009-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/lib_src/win/ethread.c b/erts/lib_src/win/ethread.c
index 22b0b4040c..e0f19f7ba1 100644
--- a/erts/lib_src/win/ethread.c
+++ b/erts/lib_src/win/ethread.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2010-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/preloaded/Makefile b/erts/preloaded/Makefile
index fbe62d57bb..e8935d4410 100644
--- a/erts/preloaded/Makefile
+++ b/erts/preloaded/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2008-2009. All Rights Reserved.
+# Copyright Ericsson AB 2008-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam
index f1ae49150b..6af1633726 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
new file mode 100644
index 0000000000..d0ed09dc91
--- /dev/null
+++ 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 990973c57d..7a2d9e5a81 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
new file mode 100644
index 0000000000..ab63e4745f
--- /dev/null
+++ 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
new file mode 100644
index 0000000000..9c99d80581
--- /dev/null
+++ 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 7461e3bc5b..c84acc12a1 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
new file mode 100644
index 0000000000..2c1ec7473a
--- /dev/null
+++ 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 0060a2cf42..5c0c6605af 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 b1868d7e10..2eb09599be 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 dc16909d26..7d3e32b452 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 f6e15ba499..b8b8402a07 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 673627498e..3fddd2f357 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 813060c41d..102a29396e 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 5ce932aeec..8955885373 100644
--- a/erts/preloaded/ebin/zlib.beam
+++ b/erts/preloaded/ebin/zlib.beam
Binary files differ
diff --git a/erts/preloaded/src/Makefile b/erts/preloaded/src/Makefile
index 52034a0881..2ab9edaf5e 100644
--- a/erts/preloaded/src/Makefile
+++ b/erts/preloaded/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2008-2013. All Rights Reserved.
+# Copyright Ericsson AB 2008-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -41,8 +41,12 @@ PRE_LOADED_ERL_MODULES = \
zlib \
prim_zip \
otp_ring0 \
+ erts_code_purger \
erlang \
- erts_internal
+ erts_internal \
+ erl_tracer \
+ erts_literal_area_collector \
+ erts_dirty_process_code_checker
PRE_LOADED_BEAM_MODULES = \
prim_eval
diff --git a/erts/preloaded/src/add_abstract_code b/erts/preloaded/src/add_abstract_code
index 4f479db2e8..943987872e 100644
--- a/erts/preloaded/src/add_abstract_code
+++ b/erts/preloaded/src/add_abstract_code
@@ -4,7 +4,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl
index 9f6cba33bd..1d09aeded9 100644
--- a/erts/preloaded/src/erl_prim_loader.erl
+++ b/erts/preloaded/src/erl_prim_loader.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -42,7 +42,7 @@
-include("inet_boot.hrl").
%% Public
--export([start/3, set_path/1, get_path/0, get_file/1, get_files/2,
+-export([start/0, set_path/1, get_path/0, get_file/1,
list_dir/1, read_file_info/1, read_link_info/1, get_cwd/0, get_cwd/1]).
%% Used by erl_boot_server
@@ -50,30 +50,31 @@
prim_read_file_info/3, prim_get_cwd/2]).
%% Used by escript and code
--export([set_primary_archive/4, release_archives/0]).
+-export([set_primary_archive/4]).
+
+%% Used by test suites
+-export([purge_archive_cache/0]).
+
+%% Used by init and the code server.
+-export([get_modules/2,get_modules/3]).
-include_lib("kernel/include/file.hrl").
-type host() :: atom().
-record(prim_state, {debug :: boolean(),
- cache,
primary_archive}).
-type prim_state() :: #prim_state{}.
-record(state,
{loader :: 'efile' | 'inet',
hosts = [] :: [host()], % hosts list (to boot from)
- id, % not used any more?
data :: 'noport' | port(), % data port etc
- timeout :: timeout(), % idle timeout
- %% Number of timeouts before archives are released
- n_timeouts :: non_neg_integer(),
- multi_get = false :: boolean(),
+ timeout :: timeout(), % idle timeout
prim_state :: prim_state()}). % state for efile code loader
--define(IDLE_TIMEOUT, 60000). %% tear inet connection after 1 minutes
--define(N_TIMEOUTS, 6). %% release efile archive after 6 minutes
+-define(EFILE_IDLE_TIMEOUT, (6*60*1000)). %purge archives
+-define(INET_IDLE_TIMEOUT, (60*1000)). %tear down connection timeout
%% Defines for inet as prim_loader
-define(INET_FAMILY, inet).
@@ -103,26 +104,13 @@ debug(#prim_state{debug = Deb}, Term) ->
%%% Interface Functions.
%%% --------------------------------------------------------
--spec start(Id, Loader, Hosts) ->
+-spec start() ->
{'ok', Pid} | {'error', What} when
- Id :: term(),
- Loader :: atom() | string(),
- Hosts :: Host | [Host],
- Host :: host(),
Pid :: pid(),
What :: term().
-start(Id, Pgm, Hosts) when is_atom(Hosts) ->
- start(Id, Pgm, [Hosts]);
-start(Id, Pgm0, Hosts) ->
- Pgm = if
- is_atom(Pgm0) ->
- atom_to_list(Pgm0);
- true ->
- Pgm0
- end,
+start() ->
Self = self(),
- Pid = spawn_link(fun() -> start_it(Pgm, Id, Self, Hosts) end),
- register(erl_prim_loader, Pid),
+ Pid = spawn_link(fun() -> start_it(Self) end),
receive
{Pid,ok} ->
{ok,Pid};
@@ -130,26 +118,39 @@ start(Id, Pgm0, Hosts) ->
{error,Reason}
end.
-%% Hosts must be a list of form ['1.2.3.4' ...]
-start_it("inet", Id, Pid, Hosts) ->
+start_it(Parent) ->
process_flag(trap_exit, true),
- ?dbg(inet, {Id,Pid,Hosts}),
+ register(erl_prim_loader, self()),
+ Loader = case init:get_argument(loader) of
+ {ok,[[Loader0]]} ->
+ Loader0;
+ error ->
+ "efile"
+ end,
+ case Loader of
+ "efile" -> start_efile(Parent);
+ "inet" -> start_inet(Parent)
+ end.
+
+%% Hosts must be a list of form ['1.2.3.4' ...]
+start_inet(Parent) ->
+ Hosts = case init:get_argument(hosts) of
+ {ok,[Hosts0]} -> Hosts0;
+ _ -> []
+ end,
AL = ipv4_list(Hosts),
?dbg(addresses, AL),
{ok,Tcp} = find_master(AL),
- init_ack(Pid),
+ init_ack(Parent),
PS = prim_init(),
State = #state {loader = inet,
hosts = AL,
- id = Id,
data = Tcp,
- timeout = ?IDLE_TIMEOUT,
- n_timeouts = ?N_TIMEOUTS,
+ timeout = ?INET_IDLE_TIMEOUT,
prim_state = PS},
- loop(State, Pid, []);
+ loop(State, Parent, []).
-start_it("efile", Id, Pid, _Hosts) ->
- process_flag(trap_exit, true),
+start_efile(Parent) ->
{ok, Port} = prim_file:start(),
%% Check that we started in a valid directory.
case prim_file:get_cwd(Port) of
@@ -160,20 +161,14 @@ start_it("efile", Id, Pid, _Hosts) ->
erlang:display(Report),
exit({error, invalid_current_directory});
_ ->
- init_ack(Pid)
+ init_ack(Parent)
end,
- MultiGet = case erlang:system_info(thread_pool_size) of
- 0 -> false;
- _ -> true
- end,
PS = prim_init(),
State = #state {loader = efile,
- id = Id,
data = Port,
- timeout = infinity,
- multi_get = MultiGet,
+ timeout = ?EFILE_IDLE_TIMEOUT,
prim_state = PS},
- loop(State, Pid, []).
+ loop(State, Parent, []).
init_ack(Pid) ->
Pid ! {self(),ok},
@@ -198,20 +193,6 @@ get_file(File) when is_atom(File) ->
get_file(File) ->
check_file_result(get_file, File, request({get_file,File})).
--spec get_files([{atom(), string()}],
- fun((atom(),binary(),string()) -> 'ok' | {'error', atom()})) ->
- 'ok' | {'error', atom()}.
-get_files(ModFiles, Fun) ->
- case request({get_files,{ModFiles,Fun}}) of
- E = {error,_M} ->
- E;
- {error,Reason,M} ->
- check_file_result(get_files, M, {error,Reason}),
- {error,M};
- ok ->
- ok
- end.
-
-spec list_dir(Dir) -> {'ok', Filenames} | 'error' when
Dir :: string(),
Filenames :: [Filename :: string()].
@@ -250,10 +231,30 @@ set_primary_archive(File, ArchiveBin, FileInfo, ParserFun)
when is_list(File), is_binary(ArchiveBin), is_record(FileInfo, file_info) ->
request({set_primary_archive, File, ArchiveBin, FileInfo, ParserFun}).
--spec release_archives() -> 'ok' | {'error', _}.
+%% NOTE: Does not close the primary archive. Only closes all
+%% open zip files kept in the cache. Should be called before an archive
+%% file is to be removed (for example in the test suites).
-release_archives() ->
- request(release_archives).
+-spec purge_archive_cache() -> 'ok' | {'error', _}.
+purge_archive_cache() ->
+ request(purge_archive_cache).
+
+-spec get_modules([module()],
+ fun((atom(), string(), binary()) ->
+ {'ok',any()} | {'error',any()})) ->
+ {'ok',{[any()],[any()]}}.
+
+get_modules(Modules, Fun) ->
+ request({get_modules,{Modules,Fun}}).
+
+-spec get_modules([module()],
+ fun((atom(), string(), binary()) ->
+ {'ok',any()} | {'error',any()}),
+ [string()]) ->
+ {'ok',{[any()],[any()]}}.
+
+get_modules(Modules, Fun, Path) ->
+ request({get_modules,{Modules,Fun,Path}}).
request(Req) ->
Loader = whereis(erl_prim_loader),
@@ -310,76 +311,63 @@ check_file_result(_, _, Other) ->
%%% The main loop.
%%% --------------------------------------------------------
-loop(State, Parent, Paths) ->
+loop(St0, Parent, Paths) ->
receive
+ {Pid,{set_path,NewPaths}} when is_pid(Pid) ->
+ Pid ! {self(),ok},
+ loop(St0, Parent, to_strs(NewPaths));
{Pid,Req} when is_pid(Pid) ->
- %% erlang:display(Req),
- {Resp,State2,Paths2} =
- case Req of
- {set_path,NewPaths} ->
- {ok,State,to_strs(NewPaths)};
- {get_path,_} ->
- {{ok,Paths},State,Paths};
- {get_file,File} ->
- {Res,State1} = handle_get_file(State, Paths, File),
- {Res,State1,Paths};
- {get_files,{ModFiles,Fun}} ->
- {Res,State1} = handle_get_files(State, ModFiles, Paths, Fun),
- {Res,State1,Paths};
- {list_dir,Dir} ->
- {Res,State1} = handle_list_dir(State, Dir),
- {Res,State1,Paths};
- {read_file_info,File} ->
- {Res,State1} = handle_read_file_info(State, File),
- {Res,State1,Paths};
- {read_link_info,File} ->
- {Res,State1} = handle_read_link_info(State, File),
- {Res,State1,Paths};
- {get_cwd,[]} ->
- {Res,State1} = handle_get_cwd(State, []),
- {Res,State1,Paths};
- {get_cwd,[_]=Args} ->
- {Res,State1} = handle_get_cwd(State, Args),
- {Res,State1,Paths};
- {set_primary_archive,File,ArchiveBin,FileInfo,ParserFun} ->
- {Res,State1} =
- handle_set_primary_archive(State, File,
- ArchiveBin, FileInfo,
- ParserFun),
- {Res,State1,Paths};
- release_archives ->
- {Res,State1} = handle_release_archives(State),
- {Res,State1,Paths};
- _Other ->
- {ignore,State,Paths}
- end,
- if Resp =:= ignore -> ok;
- true -> Pid ! {self(),Resp}, ok
- end,
- if
- is_record(State2, state) ->
- loop(State2, Parent, Paths2);
- true ->
- exit({bad_state, Req, State2})
+ case handle_request(Req, Paths, St0) of
+ ignore ->
+ ok;
+ {Resp,#state{}=St1} ->
+ Pid ! {self(),Resp},
+ loop(St1, Parent, Paths);
+ {_,State2,_} ->
+ exit({bad_state,Req,State2})
end;
{'EXIT',Parent,W} ->
- _State1 = handle_stop(State),
+ _ = handle_stop(St0),
exit(W);
{'EXIT',P,W} ->
- State1 = handle_exit(State, P, W),
- loop(State1, Parent, Paths);
+ St1 = handle_exit(St0, P, W),
+ loop(St1, Parent, Paths);
_Message ->
- loop(State, Parent, Paths)
- after State#state.timeout ->
- State1 = handle_timeout(State, Parent),
- loop(State1, Parent, Paths)
+ loop(St0, Parent, Paths)
+ after St0#state.timeout ->
+ St1 = handle_timeout(St0, Parent),
+ loop(St1, Parent, Paths)
+ end.
+
+handle_request(Req, Paths, St0) ->
+ case Req of
+ {get_path,_} ->
+ {{ok,Paths},St0};
+ {get_file,File} ->
+ handle_get_file(St0, Paths, File);
+ {get_modules,{Modules,Fun}} ->
+ handle_get_modules(St0, Modules, Fun, Paths);
+ {get_modules,{Modules,Fun,ModPaths}} ->
+ handle_get_modules(St0, Modules, Fun, ModPaths);
+ {list_dir,Dir} ->
+ handle_list_dir(St0, Dir);
+ {read_file_info,File} ->
+ handle_read_file_info(St0, File);
+ {read_link_info,File} ->
+ handle_read_link_info(St0, File);
+ {get_cwd,[]} ->
+ handle_get_cwd(St0, []);
+ {get_cwd,[_]=Args} ->
+ handle_get_cwd(St0, Args);
+ {set_primary_archive,File,ArchiveBin,FileInfo,ParserFun} ->
+ handle_set_primary_archive(St0, File, ArchiveBin,
+ FileInfo, ParserFun);
+ purge_archive_cache ->
+ handle_purge_archive_cache(St0);
+ _ ->
+ ignore
end.
-handle_get_files(State = #state{multi_get = true}, ModFiles, Paths, Fun) ->
- ?SAFE2(efile_multi_get_file_from_port(State, ModFiles, Paths, Fun), State);
-handle_get_files(State, _ModFiles, _Paths, _Fun) -> % no multi get
- {{error,no_multi_get},State}.
-
handle_get_file(State = #state{loader = efile}, Paths, File) ->
?SAFE2(efile_get_file_from_port(State, File, Paths), State);
handle_get_file(State = #state{loader = inet}, Paths, File) ->
@@ -388,8 +376,9 @@ handle_get_file(State = #state{loader = inet}, Paths, File) ->
handle_set_primary_archive(State= #state{loader = efile}, File, ArchiveBin, FileInfo, ParserFun) ->
?SAFE2(efile_set_primary_archive(State, File, ArchiveBin, FileInfo, ParserFun), State).
-handle_release_archives(State= #state{loader = efile}) ->
- ?SAFE2(efile_release_archives(State), State).
+handle_purge_archive_cache(#state{loader = efile}=State) ->
+ prim_purge_cache(),
+ {ok,State}.
handle_list_dir(State = #state{loader = efile}, Dir) ->
?SAFE2(efile_list_dir(State, Dir), State);
@@ -430,53 +419,6 @@ handle_timeout(State = #state{loader = inet}, Parent) ->
%%% Functions which handle efile as prim_loader (default).
%%% --------------------------------------------------------
-%%% Reading many files in parallel is an optimization.
-%%% See also comment in init.erl.
-
-%% -> {ok,State} | {{error,Module},State} | {{error,Reason,Module},State}
-efile_multi_get_file_from_port(State, ModFiles, Paths, Fun) ->
- Ref = make_ref(),
- %% More than 200 processes is no gain.
- Max = erlang:min(200, erlang:system_info(thread_pool_size)),
- efile_multi_get_file_from_port2(ModFiles, 0, Max, State, Paths, Fun, Ref, ok).
-
-efile_multi_get_file_from_port2([MF | MFs], Out, Max, State, Paths, Fun, Ref, Ret) when Out < Max ->
- Self = self(),
- _Pid = spawn(fun() -> efile_par_get_file(Ref, State, MF, Paths, Self, Fun) end),
- efile_multi_get_file_from_port2(MFs, Out+1, Max, State, Paths, Fun, Ref, Ret);
-efile_multi_get_file_from_port2(MFs, Out, Max, _State, Paths, Fun, Ref, Ret) when Out > 0 ->
- receive
- {Ref, ok, State1} ->
- efile_multi_get_file_from_port2(MFs, Out-1, Max, State1, Paths, Fun, Ref, Ret);
- {Ref, {error,_Mod} = Error, State1} ->
- efile_multi_get_file_from_port2(MFs, Out-1, Max, State1, Paths, Fun, Ref, Error);
- {Ref, MF, {error,emfile,State1}} ->
- %% Max can take negative values. Out cannot.
- efile_multi_get_file_from_port2([MF | MFs], Out-1, Max-1, State1, Paths, Fun, Ref, Ret);
- {Ref, {M,_F}, {error,Error,State1}} ->
- efile_multi_get_file_from_port2(MFs, Out-1, 0, State1, Paths, Fun, Ref, {error,Error,M})
- end;
-efile_multi_get_file_from_port2(_MFs, 0, _Max, State, _Paths, _Fun, _Ref, Ret) ->
- {Ret,State}.
-
-efile_par_get_file(Ref, State, {Mod,File} = MF, Paths, Pid, Fun) ->
- %% One port for each file read in "parallel":
- case prim_file:start() of
- {ok, Port} ->
- Port0 = State#state.data,
- State1 = State#state{data = Port},
- R = case efile_get_file_from_port(State1, File, Paths) of
- {{error,Reason},State2} ->
- {Ref,MF,{error,Reason,State2}};
- {{ok,BinFile,Full},State2} ->
- %% Fun(...) -> ok | {error,Mod}
- {Ref,Fun(Mod, BinFile, Full),State2#state{data=Port0}}
- end,
- prim_file:close(Port),
- Pid ! R;
- {error, Error} ->
- Pid ! {Ref,MF,{error,Error,State}}
- end.
%% -> {{ok,BinFile,File},State} | {{error,Reason},State}
efile_get_file_from_port(State, File, Paths) ->
@@ -521,10 +463,6 @@ efile_set_primary_archive(#state{prim_state = PS} = State, File,
FileInfo, ParserFun),
{Res,State#state{prim_state = PS2}}.
-efile_release_archives(#state{prim_state = PS} = State) ->
- {Res, PS2} = prim_release_archives(PS),
- {Res,State#state{prim_state = PS2}}.
-
efile_list_dir(#state{prim_state = PS} = State, Dir) ->
{Res, PS2} = prim_list_dir(PS, Dir),
{Res, State#state{prim_state = PS2}}.
@@ -546,15 +484,127 @@ efile_exit_port(State, Port, Reason) when State#state.data =:= Port ->
efile_exit_port(State, _Port, _Reason) ->
State.
-efile_timeout_handler(#state{n_timeouts = N} = State, _Parent) ->
- if
- N =< 0 ->
- {_Res, State2} = efile_release_archives(State),
- State2#state{n_timeouts = ?N_TIMEOUTS};
- true ->
- State#state{n_timeouts = N - 1}
+efile_timeout_handler(State, _Parent) ->
+ prim_purge_cache(),
+ State.
+
+%%% --------------------------------------------------------
+%%% Read and process severals modules in parallel.
+%%% --------------------------------------------------------
+
+handle_get_modules(#state{loader=efile}=St, Ms, Process, Paths) ->
+ Primary = (St#state.prim_state)#prim_state.primary_archive,
+ Res = case efile_any_archives(Paths, Primary) of
+ false ->
+ efile_get_mods_par(Ms, Process, Paths);
+ true ->
+ Get = fun efile_get_file_from_port/3,
+ gm_get_mods(St, Get, Ms, Process, Paths)
+ end,
+ {Res,St};
+handle_get_modules(#state{loader=inet}=St, Ms, Process, Paths) ->
+ Get = fun inet_get_file_from_port/3,
+ {gm_get_mods(St, Get, Ms, Process, Paths),St}.
+
+efile_get_mods_par(Ms, Process, Paths) ->
+ Self = self(),
+ Ref = make_ref(),
+ GmSpawn = fun() ->
+ efile_gm_spawn({Self,Ref}, Ms, Process, Paths)
+ end,
+ _ = spawn_link(GmSpawn),
+ N = length(Ms),
+ efile_gm_recv(N, Ref, [], []).
+
+efile_any_archives([H|T], Primary) ->
+ case name_split(Primary, H) of
+ {file,_} -> efile_any_archives(T, Primary);
+ {archive,_,_} -> true
+ end;
+efile_any_archives([], _) ->
+ false.
+
+efile_gm_recv(0, _Ref, Succ, Fail) ->
+ {ok,{Succ,Fail}};
+efile_gm_recv(N, Ref, Succ, Fail) ->
+ receive
+ {Ref,Mod,{ok,Res}} ->
+ efile_gm_recv(N-1, Ref, [{Mod,Res}|Succ], Fail);
+ {Ref,Mod,{error,Res}} ->
+ efile_gm_recv(N-1, Ref, Succ, [{Mod,Res}|Fail])
end.
+efile_gm_spawn(ParentRef, Ms, Process, Paths) ->
+ efile_gm_spawn_1(0, Ms, ParentRef, Process, Paths).
+
+efile_gm_spawn_1(N, Ms, ParentRef, Process, Paths) when N >= 32 ->
+ receive
+ {'DOWN',_,process,_,_} ->
+ efile_gm_spawn_1(N-1, Ms, ParentRef, Process, Paths)
+ end;
+efile_gm_spawn_1(N, [M|Ms], ParentRef, Process, Paths) ->
+ Get = fun() -> efile_gm_get(Paths, M, ParentRef, Process) end,
+ _ = spawn_monitor(Get),
+ efile_gm_spawn_1(N+1, Ms, ParentRef, Process, Paths);
+efile_gm_spawn_1(_, [], _, _, _) ->
+ ok.
+
+efile_gm_get(Paths, Mod, ParentRef, Process) ->
+ File = atom_to_list(Mod) ++ init:objfile_extension(),
+ efile_gm_get_1(Paths, File, Mod, ParentRef, Process).
+
+efile_gm_get_1([P|Ps], File0, Mod, {Parent,Ref}=PR, Process) ->
+ File = join(P, File0),
+ try prim_file:read_file(File) of
+ {ok,Bin} ->
+ Res = gm_process(Mod, File, Bin, Process),
+ Parent ! {Ref,Mod,Res};
+ Error ->
+ _ = check_file_result(get_modules, File, Error),
+ efile_gm_get_1(Ps, File0, Mod, PR, Process)
+ catch
+ _:Reason ->
+ Res = {error,{crash,Reason}},
+ Parent ! {Ref,Mod,Res}
+ end;
+efile_gm_get_1([], _, Mod, {Parent,Ref}, _Process) ->
+ Parent ! {Ref,Mod,{error,enoent}}.
+
+gm_get_mods(St, Get, Ms, Process, Paths) ->
+ gm_get_mods(St, Get, Ms, Process, Paths, [], []).
+
+gm_get_mods(St, Get, [M|Ms], Process, Paths, Succ, Fail) ->
+ File = atom_to_list(M) ++ init:objfile_extension(),
+ case gm_arch_get(St, Get, M, File, Paths, Process) of
+ {ok,Res} ->
+ gm_get_mods(St, Get, Ms, Process, Paths,
+ [{M,Res}|Succ], Fail);
+ {error,Res} ->
+ gm_get_mods(St, Get, Ms, Process, Paths,
+ Succ, [{M,Res}|Fail])
+ end;
+gm_get_mods(_St, _Get, [], _, _, Succ, Fail) ->
+ {ok,{Succ,Fail}}.
+
+gm_arch_get(St, Get, Mod, File, Paths, Process) ->
+ case Get(St, File, Paths) of
+ {{error,_}=E,_} ->
+ E;
+ {{ok,Bin,Full},_} ->
+ gm_process(Mod, Full, Bin, Process)
+ end.
+
+gm_process(Mod, File, Bin, Process) ->
+ try Process(Mod, File, Bin) of
+ {ok,_}=Res -> Res;
+ {error,_}=Res -> Res;
+ Other -> {error,{bad_return,Other}}
+ catch
+ _:Error ->
+ {error,{crash,Error}}
+ end.
+
+
%%% --------------------------------------------------------
%%% Functions which handle inet prim_loader
%%% --------------------------------------------------------
@@ -694,7 +744,7 @@ inet_get_file_from_port1(_File, [], State) ->
inet_send_and_rcv(Msg, Tag, State) when State#state.data =:= noport ->
{ok,Tcp} = find_master(State#state.hosts), %% reconnect
inet_send_and_rcv(Msg, Tag, State#state{data = Tcp,
- timeout = ?IDLE_TIMEOUT});
+ timeout = ?INET_IDLE_TIMEOUT});
inet_send_and_rcv(Msg, Tag, #state{data = Tcp, timeout = Timeout} = State) ->
prim_inet:send(Tcp, term_to_binary(Msg)),
receive
@@ -819,32 +869,19 @@ prim_init() ->
end,
cache_new(#prim_state{debug = Deb}).
-prim_release_archives(PS) ->
- debug(PS, release_archives),
- {Res, PS2} = prim_do_release_archives(PS, get(), []),
- debug(PS2, {return, Res}),
- {Res, PS2}.
-
-prim_do_release_archives(PS, [{ArchiveFile, DictVal} | KeyVals], Acc) ->
- Res =
- case DictVal of
- {primary, _PrimZip, _FI, _ParserFun} ->
- ok; % Keep primary archive
- {Cache, _FI} ->
- debug(PS, {release, cache, ArchiveFile}),
- erase(ArchiveFile),
- clear_cache(ArchiveFile, Cache)
- end,
- case Res of
- ok ->
- prim_do_release_archives(PS, KeyVals, Acc);
- {error, Reason} ->
- prim_do_release_archives(PS, KeyVals, [{ArchiveFile, Reason} | Acc])
- end;
-prim_do_release_archives(PS, [], []) ->
- {ok, PS#prim_state{primary_archive = undefined}};
-prim_do_release_archives(PS, [], Errors) ->
- {{error, Errors}, PS#prim_state{primary_archive = undefined}}.
+prim_purge_cache() ->
+ do_prim_purge_cache(get()).
+
+do_prim_purge_cache([{Key,Val}|T]) ->
+ case Val of
+ {Cache,_FI} ->
+ catch clear_cache(Key, Cache);
+ _ ->
+ ok
+ end,
+ do_prim_purge_cache(T);
+do_prim_purge_cache([]) ->
+ ok.
prim_set_primary_archive(PS, undefined, undefined, undefined, _ParserFun) ->
debug(PS, {set_primary_archive, clean}),
@@ -1287,70 +1324,62 @@ path_join([Path],Acc) ->
path_join([Path|Paths],Acc) ->
path_join(Paths,"/" ++ reverse(Path) ++ Acc).
-name_split(ArchiveFile, File0) ->
- File = absname(File0),
- do_name_split(ArchiveFile, File).
-
-do_name_split(undefined, File) ->
+name_split(undefined, File) ->
%% Ignore primary archive
- case string_split(File, init:archive_extension(), []) of
+ RevExt = reverse(init:archive_extension()),
+ case archive_split(File, RevExt, []) of
no_split ->
- %% Plain file
{file, File};
- {split, _RevArchiveBase, RevArchiveFile, []} ->
- %% Top dir in archive
- ArchiveFile = reverse(RevArchiveFile),
- {archive, ArchiveFile, []};
- {split, _RevArchiveBase, RevArchiveFile, [$/ | FileInArchive]} ->
- %% File in archive
- ArchiveFile = reverse(RevArchiveFile),
- {archive, ArchiveFile, FileInArchive};
- {split, _RevArchiveBase, _RevArchiveFile, _FileInArchive} ->
- %% False match. Assume plain file
- {file, File}
+ Archive ->
+ Archive
end;
-do_name_split(ArchiveFile, File) ->
+name_split(ArchiveFile, File0) ->
%% Look first in primary archive
- case string_match(real_path(File), ArchiveFile, []) of
+ File = absname(File0),
+ case string_match(real_path(File), ArchiveFile) of
no_match ->
%% Archive or plain file
- do_name_split(undefined, File);
- {match, _RevPrimArchiveFile, FileInArchive} ->
+ name_split(undefined, File);
+ {match, FileInArchive} ->
%% Primary archive
{archive, ArchiveFile, FileInArchive}
end.
-string_match([Char | File], [Char | Archive], RevTop) ->
- string_match(File, Archive, [Char | RevTop]);
-string_match([] = File, [], RevTop) ->
- {match, RevTop, File};
-string_match([$/ | File], [], RevTop) ->
- {match, RevTop, File};
-string_match(_File, _Archive, _RevTop) ->
+string_match([Char | File], [Char | Archive]) ->
+ string_match(File, Archive);
+string_match([] = File, []) ->
+ {match, File};
+string_match([$/ | File], []) ->
+ {match, File};
+string_match(_File, _Archive) ->
no_match.
-string_split([Char | File], [Char | Ext] = FullExt, RevTop) ->
- RevTop2 = [Char | RevTop],
- string_split2(File, Ext, RevTop, RevTop2, File, FullExt, RevTop2);
-string_split([Char | File], Ext, RevTop) ->
- string_split(File, Ext, [Char | RevTop]);
-string_split([], _Ext, _RevTop) ->
- no_split.
-
-string_split2([Char | File], [Char | Ext], RevBase, RevTop, SaveFile, SaveExt, SaveTop) ->
- string_split2(File, Ext, RevBase, [Char | RevTop], SaveFile, SaveExt, SaveTop);
-string_split2(File, [], RevBase, RevTop, _SaveFile, _SaveExt, _SaveTop) ->
- {split, RevBase, RevTop, File};
-string_split2(_, _Ext, _RevBase, _RevTop, SaveFile, SaveExt, SaveTop) ->
- string_split(SaveFile, SaveExt, SaveTop).
+archive_split("/"++File, RevExt, Acc) ->
+ case is_prefix(RevExt, Acc) of
+ false ->
+ archive_split(File, RevExt, [$/|Acc]);
+ true ->
+ ArchiveFile = absname(reverse(Acc)),
+ {archive, ArchiveFile, File}
+ end;
+archive_split([H|T], RevExt, Acc) ->
+ archive_split(T, RevExt, [H|Acc]);
+archive_split([], RevExt, Acc) ->
+ case is_prefix(RevExt, Acc) of
+ false ->
+ no_split;
+ true ->
+ ArchiveFile = absname(reverse(Acc)),
+ {archive, ArchiveFile, []}
+ end.
+
+is_prefix([H|T1], [H|T2]) -> is_prefix(T1, T2);
+is_prefix([_|_], _) -> false;
+is_prefix([], _ ) -> true.
%% Parse list of ipv4 addresses
ipv4_list([H | T]) ->
- IPV = if is_atom(H) -> ipv4_address(atom_to_list(H));
- is_list(H) -> ipv4_address(H);
- true -> {error,einal}
- end,
- case IPV of
+ case ipv4_address(H) of
{ok,IP} -> [IP | ipv4_list(T)];
_ -> ipv4_list(T)
end;
@@ -1415,8 +1444,6 @@ absname_vr([Drive, $\: | NameRest], _) ->
%% Assumes normalized name
pathtype(Name) when is_list(Name) ->
case erlang:system_info(os_type) of
- {ose, _} ->
- unix_pathtype(Name);
{unix, _} ->
unix_pathtype(Name);
{win32, _} ->
diff --git a/erts/preloaded/src/erl_tracer.erl b/erts/preloaded/src/erl_tracer.erl
new file mode 100644
index 0000000000..c810069d17
--- /dev/null
+++ b/erts/preloaded/src/erl_tracer.erl
@@ -0,0 +1,65 @@
+-module(erl_tracer).
+
+-export([enabled/3, trace/5, on_load/0]).
+
+-type tracee() :: port() | pid() | undefined.
+
+-type trace_tag_running_ports() :: in | out | in_exiting | out_exiting | out_exited.
+-type trace_tag_running_procs() :: in | out | in_exiting | out_exiting | out_exited.
+-type trace_tag_send() :: send | send_to_non_existing_process.
+-type trace_tag_receive() :: 'receive'.
+-type trace_tag_call() :: call | return_to | return_from | exception_from.
+-type trace_tag_procs() :: spawn | spawned | exit | link | unlink
+ | getting_linked | getting_unlinked
+ | register | unregister.
+-type trace_tag_ports() :: open | closed | link | unlink
+ | getting_linked | getting_unlinked.
+-type trace_tag_gc() :: gc_minor_start | gc_minor_end
+ | gc_major_start | gc_major_end.
+
+-type trace_tag() :: trace_tag_send()
+ | trace_tag_receive()
+ | trace_tag_call()
+ | trace_tag_procs()
+ | trace_tag_ports()
+ | trace_tag_running_procs()
+ | trace_tag_running_ports()
+ | trace_tag_gc().
+
+-type trace_opts() :: #{ extra => term(), match_spec_result => term(),
+ scheduler_id => non_neg_integer(),
+ timestamp => timestamp | cpu_timestamp |
+ monotonic | strict_monotonic }.
+-type tracer_state() :: term().
+
+on_load() ->
+ case erlang:load_nif(atom_to_list(?MODULE), 0) of
+ ok -> ok
+ end.
+
+%%%
+%%% NIF placeholders
+%%%
+
+%% This suppression is needed as trace_tag gets collapsed to atom()
+-dialyzer({no_contracts, enabled/3}).
+
+-spec enabled(Tag :: trace_status,
+ TracerState :: tracer_state(),
+ Tracee :: tracee()) ->
+ trace | remove;
+ (Tag :: trace_tag() | seq_trace,
+ TracerState :: tracer_state(),
+ Tracee :: tracee()) ->
+ trace | discard.
+enabled(_, _, _) ->
+ erlang:nif_error(nif_not_loaded).
+
+-spec trace(Tag :: trace_tag() | seq_trace,
+ TracerState :: tracer_state(),
+ Tracee :: tracee(),
+ Msg :: term(),
+ Opts :: trace_opts()) -> any().
+
+trace(_, _, _, _, _) ->
+ erlang:nif_error(nif_not_loaded).
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index 063b9a1f26..8771089b65 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -59,6 +59,7 @@
-export_type([timestamp/0]).
-export_type([time_unit/0]).
+-export_type([deprecated_time_unit/0]).
-type ext_binary() :: binary().
-type timestamp() :: {MegaSecs :: non_neg_integer(),
@@ -67,11 +68,20 @@
-type time_unit() ::
pos_integer()
- | 'seconds'
+ | 'second'
+ | 'millisecond'
+ | 'microsecond'
+ | 'nanosecond'
+ | 'native'
+ | 'perf_counter'
+ | deprecated_time_unit().
+
+%% Deprecated symbolic units...
+-type deprecated_time_unit() ::
+ 'seconds'
| 'milli_seconds'
| 'micro_seconds'
- | 'nano_seconds'
- | 'native'.
+ | 'nano_seconds'.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Native code BIF stubs and their types
@@ -104,7 +114,9 @@
-export([garbage_collect/0, garbage_collect/1, garbage_collect/2]).
-export([garbage_collect_message_area/0, get/0, get/1, get_keys/0, get_keys/1]).
-export([get_module_info/1, get_stacktrace/0, group_leader/0]).
--export([group_leader/2, halt/0, halt/1, halt/2, hash/2, hibernate/3]).
+-export([group_leader/2]).
+-export([halt/0, halt/1, halt/2, hash/2,
+ has_prepared_code_on_load/1, hibernate/3]).
-export([insert_element/3]).
-export([integer_to_binary/1, integer_to_list/1]).
-export([iolist_size/1, iolist_to_binary/1]).
@@ -227,27 +239,32 @@
send |
'receive' |
procs |
+ ports |
call |
- silent |
+ arity |
return_to |
+ silent |
running |
exiting |
+ running_procs |
+ running_ports |
garbage_collection |
timestamp |
cpu_timestamp |
monotonic_timestamp |
strict_monotonic_timestamp |
- arity |
set_on_spawn |
set_on_first_spawn |
set_on_link |
set_on_first_link |
- {tracer, pid() | port()}.
+ {tracer, pid() | port()} |
+ {tracer, module(), term()}.
-type trace_info_item_result() ::
{traced, global | local | false | undefined} |
{match_spec, trace_match_spec() | false | undefined} |
{meta, pid() | port() | false | undefined | []} |
+ {meta, module(), term() } |
{meta_match_spec, trace_match_spec() | false | undefined} |
{call_count, non_neg_integer() | boolean() | undefined} |
{call_time, [{pid(), non_neg_integer(),
@@ -273,6 +290,7 @@
undefined |
{flags, [trace_info_flag()]} |
{tracer, pid() | port() | []} |
+ {tracer, module(), term()} |
trace_info_item_result() |
{all, [ trace_info_item_result() ] | false | undefined}.
@@ -469,7 +487,7 @@ check_old_code(_Module) ->
CheckResult :: boolean().
check_process_code(Pid, Module) ->
try
- erlang:check_process_code(Pid, Module, [{allow_gc, true}])
+ erts_internal:check_process_code(Pid, Module, [{allow_gc, true}])
catch
error:Error -> erlang:error(Error, [Pid, Module])
end.
@@ -484,51 +502,11 @@ check_process_code(Pid, Module) ->
CheckResult :: boolean() | aborted.
check_process_code(Pid, Module, OptionList) ->
try
- {Async, AllowGC} = get_cpc_opts(OptionList, sync, true),
- case Async of
- {async, ReqId} ->
- {priority, Prio} = erlang:process_info(erlang:self(),
- priority),
- erts_internal:request_system_task(Pid,
- Prio,
- {check_process_code,
- ReqId,
- Module,
- AllowGC}),
- async;
- sync ->
- case Pid == erlang:self() of
- true ->
- erts_internal:check_process_code(Module,
- [{allow_gc, AllowGC}]);
- false ->
- {priority, Prio} = erlang:process_info(erlang:self(),
- priority),
- ReqId = erlang:make_ref(),
- erts_internal:request_system_task(Pid,
- Prio,
- {check_process_code,
- ReqId,
- Module,
- AllowGC}),
- receive
- {check_process_code, ReqId, CheckResult} ->
- CheckResult
- end
- end
- end
+ erts_internal:check_process_code(Pid, Module, OptionList)
catch
error:Error -> erlang:error(Error, [Pid, Module, OptionList])
end.
-% gets async and allow_gc opts and verify valid option list
-get_cpc_opts([{async, _ReqId} = AsyncTuple | Options], _OldAsync, AllowGC) ->
- get_cpc_opts(Options, AsyncTuple, AllowGC);
-get_cpc_opts([{allow_gc, AllowGC} | Options], Async, _OldAllowGC) ->
- get_cpc_opts(Options, Async, AllowGC);
-get_cpc_opts([], Async, AllowGC) ->
- {Async, AllowGC}.
-
%% crc32/1
-spec erlang:crc32(Data) -> non_neg_integer() when
Data :: iodata().
@@ -1011,14 +989,14 @@ group_leader(_GroupLeader, _Pid) ->
%% Shadowed by erl_bif_types: erlang:halt/0
-spec halt() -> no_return().
halt() ->
- erlang:nif_error(undefined).
+ erlang:halt(0, []).
%% halt/1
%% Shadowed by erl_bif_types: erlang:halt/1
-spec halt(Status) -> no_return() when
Status :: non_neg_integer() | 'abort' | string().
-halt(_Status) ->
- erlang:nif_error(undefined).
+halt(Status) ->
+ erlang:halt(Status, []).
%% halt/2
%% Shadowed by erl_bif_types: erlang:halt/2
@@ -1036,6 +1014,12 @@ halt(_Status, _Options) ->
hash(_Term, _Range) ->
erlang:nif_error(undefined).
+%% has_prepared_code_on_load/1
+-spec erlang:has_prepared_code_on_load(PreparedCode) -> boolean() when
+ PreparedCode :: binary().
+has_prepared_code_on_load(_PreparedCode) ->
+ erlang:nif_error(undefined).
+
%% hibernate/3
-spec erlang:hibernate(Module, Function, Args) -> no_return() when
Module :: module(),
@@ -1188,10 +1172,10 @@ map_size(_Map) ->
erlang:nif_error(undefined).
%% match_spec_test/3
--spec erlang:match_spec_test(P1, P2, P3) -> TestResult when
- P1 :: [term()] | tuple(),
- P2 :: term(),
- P3 :: table | trace,
+-spec erlang:match_spec_test(MatchAgainst, MatchSpec, Type) -> TestResult when
+ MatchAgainst :: [term()] | tuple(),
+ MatchSpec :: term(),
+ Type :: table | trace,
TestResult :: {ok, term(), [return_trace], [ {error | warning, string()} ]} | {error, [ {error | warning, string()} ]}.
match_spec_test(_P1, _P2, _P3) ->
erlang:nif_error(undefined).
@@ -1231,16 +1215,18 @@ module_loaded(_Module) ->
erlang:nif_error(undefined).
-type registered_name() :: atom().
-
-type registered_process_identifier() :: registered_name() | {registered_name(), node()}.
-
-type monitor_process_identifier() :: pid() | registered_process_identifier().
+-type monitor_port_identifier() :: port() | registered_name().
%% monitor/2
--spec monitor(process, monitor_process_identifier()) -> MonitorRef when
- MonitorRef :: reference();
- (time_offset, clock_service) -> MonitorRef when
- MonitorRef :: reference().
+-spec monitor
+ (process, monitor_process_identifier()) -> MonitorRef
+ when MonitorRef :: reference();
+ (port, monitor_port_identifier()) -> MonitorRef
+ when MonitorRef :: reference();
+ (time_offset, clock_service) -> MonitorRef
+ when MonitorRef :: reference().
monitor(_Type, _Item) ->
erlang:nif_error(undefined).
@@ -1387,18 +1373,34 @@ convert_time_unit(Time, FromUnit, ToUnit) ->
try
FU = case FromUnit of
native -> erts_internal:time_unit();
+ perf_counter -> erts_internal:perf_counter_unit();
+ nanosecond -> 1000*1000*1000;
+ microsecond -> 1000*1000;
+ millisecond -> 1000;
+ second -> 1;
+
+ %% Deprecated symbolic units...
nano_seconds -> 1000*1000*1000;
micro_seconds -> 1000*1000;
milli_seconds -> 1000;
seconds -> 1;
+
_ when FromUnit > 0 -> FromUnit
end,
TU = case ToUnit of
native -> erts_internal:time_unit();
+ perf_counter -> erts_internal:perf_counter_unit();
+ nanosecond -> 1000*1000*1000;
+ microsecond -> 1000*1000;
+ millisecond -> 1000;
+ second -> 1;
+
+ %% Deprecated symbolic units...
nano_seconds -> 1000*1000*1000;
micro_seconds -> 1000*1000;
milli_seconds -> 1000;
seconds -> 1;
+
_ when ToUnit > 0 -> ToUnit
end,
case Time < 0 of
@@ -1473,8 +1475,16 @@ processes() ->
%% purge_module/1
-spec purge_module(Module) -> true when
Module :: atom().
-purge_module(_Module) ->
- erlang:nif_error(undefined).
+purge_module(Module) when erlang:is_atom(Module) ->
+ case erts_code_purger:purge(Module) of
+ {false, _} ->
+ erlang:error(badarg, [Module]);
+ {true, _} ->
+ true
+ end;
+purge_module(Arg) ->
+ erlang:error(badarg, [Arg]).
+
%% put/2
-spec put(Key, Val) -> term() when
@@ -1726,12 +1736,35 @@ time() ->
erlang:nif_error(undefined).
%% trace/3
--spec erlang:trace(PidSpec, How, FlagList) -> integer() when
- PidSpec :: pid() | existing | new | all,
+-spec erlang:trace(PidPortSpec, How, FlagList) -> integer() when
+ PidPortSpec :: pid() | port()
+ | all | processes | ports
+ | existing | existing_processes | existing_ports
+ | new | new_processes | new_ports,
How :: boolean(),
FlagList :: [trace_flag()].
-trace(_PidSpec, _How, _FlagList) ->
- erlang:nif_error(undefined).
+trace(PidPortSpec, How, FlagList) ->
+ %% Make sure that we have loaded the tracer module
+ case lists:keyfind(tracer, 1, FlagList) of
+ {tracer, Module, State} when erlang:is_atom(Module) ->
+ case erlang:module_loaded(Module) of
+ false ->
+ Module:enabled(trace_status, erlang:self(), State);
+ true ->
+ ok
+ end;
+ _ ->
+ ignore
+ end,
+
+ try erts_internal:trace(PidPortSpec, How, FlagList) of
+ Res -> Res
+ catch E:R ->
+ {_, [_ | CST]} = erlang:process_info(
+ erlang:self(), current_stacktrace),
+ erlang:raise(
+ E, R, [{?MODULE, trace, [PidPortSpec, How, FlagList], []} | CST])
+ end.
%% trace_delivered/1
-spec erlang:trace_delivered(Tracee) -> Ref when
@@ -1741,14 +1774,16 @@ trace_delivered(_Tracee) ->
erlang:nif_error(undefined).
%% trace_info/2
--spec erlang:trace_info(PidOrFunc, Item) -> Res when
- PidOrFunc :: pid() | new | {Module, Function, Arity} | on_load,
+-spec erlang:trace_info(PidPortFuncEvent, Item) -> Res when
+ PidPortFuncEvent :: pid() | port() | new | new_processes | new_ports
+ | {Module, Function, Arity} | on_load | send | 'receive',
Module :: module(),
Function :: atom(),
Arity :: arity(),
- Item :: flags | tracer | traced | match_spec | meta | meta_match_spec | call_count | call_time | all,
+ Item :: flags | tracer | traced | match_spec
+ | meta | meta_match_spec | call_count | call_time | all,
Res :: trace_info_return().
-trace_info(_PidOrFunc, _Item) ->
+trace_info(_PidPortFuncEvent, _Item) ->
erlang:nif_error(undefined).
%% trunc/1
@@ -2036,12 +2071,21 @@ nodes(_Arg) ->
| eof
| {parallelism, Boolean :: boolean()}
| hide.
-open_port(_PortName,_PortSettings) ->
- erlang:nif_error(undefined).
+open_port(PortName, PortSettings) ->
+ case case erts_internal:open_port(PortName, PortSettings) of
+ Ref when erlang:is_reference(Ref) -> receive {Ref, Res} -> Res end;
+ Res -> Res
+ end of
+ Port when erlang:is_port(Port) -> Port;
+ Error -> erlang:error(Error, [PortName, PortSettings])
+ end.
-type priority_level() ::
low | normal | high | max.
+-type message_queue_data() ::
+ off_heap | on_heap.
+
-spec process_flag(trap_exit, Boolean) -> OldBoolean when
Boolean :: boolean(),
OldBoolean :: boolean();
@@ -2054,6 +2098,12 @@ open_port(_PortName,_PortSettings) ->
(min_bin_vheap_size, MinBinVHeapSize) -> OldMinBinVHeapSize when
MinBinVHeapSize :: non_neg_integer(),
OldMinBinVHeapSize :: non_neg_integer();
+ (max_heap_size, MaxHeapSize) -> OldMaxHeapSize when
+ MaxHeapSize :: max_heap_size(),
+ OldMaxHeapSize :: max_heap_size();
+ (message_queue_data, MQD) -> OldMQD when
+ MQD :: message_queue_data(),
+ OldMQD :: message_queue_data();
(priority, Level) -> OldLevel when
Level :: priority_level(),
OldLevel :: priority_level();
@@ -2080,6 +2130,7 @@ process_flag(_Flag, _Value) ->
dictionary |
error_handler |
garbage_collection |
+ garbage_collection_info |
group_leader |
heap_size |
initial_call |
@@ -2092,6 +2143,7 @@ process_flag(_Flag, _Value) ->
min_bin_vheap_size |
monitored_by |
monitors |
+ message_queue_data |
priority |
reductions |
registered_name |
@@ -2119,6 +2171,7 @@ process_flag(_Flag, _Value) ->
{dictionary, Dictionary :: [{Key :: term(), Value :: term()}]} |
{error_handler, Module :: module()} |
{garbage_collection, GCInfo :: [{atom(),non_neg_integer()}]} |
+ {garbage_collection_info, GCInfo :: [{atom(),non_neg_integer()}]} |
{group_leader, GroupLeader :: pid()} |
{heap_size, Size :: non_neg_integer()} |
{initial_call, mfa()} |
@@ -2129,13 +2182,15 @@ process_flag(_Flag, _Value) ->
{messages, MessageQueue :: [term()]} |
{min_heap_size, MinHeapSize :: non_neg_integer()} |
{min_bin_vheap_size, MinBinVHeapSize :: non_neg_integer()} |
+ {max_heap_size, MaxHeapSize :: max_heap_size()} |
{monitored_by, Pids :: [pid()]} |
{monitors,
- Monitors :: [{process, Pid :: pid() |
+ Monitors :: [{process | port, Pid :: pid() | port() |
{RegName :: atom(), Node :: node()}}]} |
+ {message_queue_data, MQD :: message_queue_data()} |
{priority, Level :: priority_level()} |
{reductions, Number :: non_neg_integer()} |
- {registered_name, Atom :: atom()} |
+ {registered_name, [] | (Atom :: atom())} |
{sequential_trace_token, [] | (SequentialTraceToken :: term())} |
{stack_size, Size :: non_neg_integer()} |
{status, Status :: exiting | garbage_collecting | waiting | running | runnable | suspended} |
@@ -2212,6 +2267,7 @@ setelement(_Index, _Tuple1, _Value) ->
| {priority, Level :: priority_level()}
| {fullsweep_after, Number :: non_neg_integer()}
| {min_heap_size, Size :: non_neg_integer()}
+ | {max_heap_size, Size :: max_heap_size()}
| {min_bin_vheap_size, VSize :: non_neg_integer()}.
spawn_opt(_Tuple) ->
erlang:nif_error(undefined).
@@ -2230,6 +2286,16 @@ spawn_opt(_Tuple) ->
(io) -> {{input, Input}, {output, Output}} when
Input :: non_neg_integer(),
Output :: non_neg_integer();
+ (microstate_accounting) -> [MSAcc_Thread] | undefined when
+ MSAcc_Thread :: #{ type := MSAcc_Thread_Type,
+ id := MSAcc_Thread_Id,
+ counters := MSAcc_Counters},
+ MSAcc_Thread_Type :: scheduler | async | aux,
+ MSAcc_Thread_Id :: non_neg_integer(),
+ MSAcc_Counters :: #{ MSAcc_Thread_State => non_neg_integer() },
+ MSAcc_Thread_State :: alloc | aux | bif | busy_wait | check_io |
+ emulator | ets | gc | gc_fullsweep | nif |
+ other | port | send | sleep | timers;
(reductions) -> {Total_Reductions,
Reductions_Since_Last_Call} when
Total_Reductions :: non_neg_integer(),
@@ -2284,6 +2350,9 @@ subtract(_,_) ->
(fullsweep_after, Number) -> OldNumber when
Number :: non_neg_integer(),
OldNumber :: non_neg_integer();
+ (microstate_accounting, Action) -> OldState when
+ Action :: true | false | reset,
+ OldState :: true | false;
(min_heap_size, MinHeapSize) -> OldMinHeapSize when
MinHeapSize :: non_neg_integer(),
OldMinHeapSize :: non_neg_integer();
@@ -2291,9 +2360,12 @@ subtract(_,_) ->
OldMinBinVHeapSize when
MinBinVHeapSize :: non_neg_integer(),
OldMinBinVHeapSize :: non_neg_integer();
+ (max_heap_size, MaxHeapSize) -> OldMaxHeapSize when
+ MaxHeapSize :: max_heap_size(),
+ OldMaxHeapSize :: max_heap_size();
(multi_scheduling, BlockState) -> OldBlockState when
- BlockState :: block | unblock,
- OldBlockState :: block | unblock | enabled;
+ BlockState :: block | unblock | block_normal | unblock_normal,
+ OldBlockState :: blocked | disabled | enabled;
(scheduler_bind_type, How) -> OldBindType when
How :: scheduler_bind_type() | default_bind,
OldBindType :: scheduler_bind_type();
@@ -2311,7 +2383,7 @@ subtract(_,_) ->
OldState :: preliminary | final | volatile;
%% These are deliberately not documented
(internal_cpu_topology, term()) -> term();
- (sequential_tracer, pid() | port() | false) -> pid() | port() | false;
+ (sequential_tracer, pid() | port() | {module(), term()} | false) -> pid() | port() | false;
(1,0) -> true.
system_flag(_Flag, _Value) ->
@@ -2336,35 +2408,70 @@ term_to_binary(_Term, _Options) ->
tl(_List) ->
erlang:nif_error(undefined).
+-type match_variable() :: atom(). % Approximation of '$1' | '$2' | ...
-type trace_pattern_mfa() ::
{atom(),atom(),arity() | '_'} | on_load.
-type trace_match_spec() ::
- [{[term()] | '_' ,[term()],[term()]}].
+ [{[term()] | '_' | match_variable() ,[term()],[term()]}].
-spec erlang:trace_pattern(MFA, MatchSpec) -> non_neg_integer() when
- MFA :: trace_pattern_mfa(),
+ MFA :: trace_pattern_mfa() | send | 'receive',
MatchSpec :: (MatchSpecList :: trace_match_spec())
| boolean()
| restart
| pause.
-trace_pattern(_MFA, _MatchSpec) ->
- erlang:nif_error(undefined).
+trace_pattern(MFA, MatchSpec) ->
+ try erts_internal:trace_pattern(MFA, MatchSpec, []) of
+ Res -> Res
+ catch E:R ->
+ {_, [_ | CST]} = erlang:process_info(
+ erlang:self(), current_stacktrace),
+ erlang:raise(
+ E, R, [{?MODULE, trace_pattern, [MFA, MatchSpec], []} | CST])
+ end.
-type trace_pattern_flag() ::
global | local |
meta | {meta, Pid :: pid()} |
+ {meta, TracerModule :: module(), TracerState :: term()} |
call_count |
call_time.
--spec erlang:trace_pattern(MFA, MatchSpec, FlagList) -> non_neg_integer() when
+-spec erlang:trace_pattern(send, MatchSpec, []) -> non_neg_integer() when
+ MatchSpec :: (MatchSpecList :: trace_match_spec())
+ | boolean();
+ ('receive', MatchSpec, []) -> non_neg_integer() when
+ MatchSpec :: (MatchSpecList :: trace_match_spec())
+ | boolean();
+ (MFA, MatchSpec, FlagList) -> non_neg_integer() when
MFA :: trace_pattern_mfa(),
MatchSpec :: (MatchSpecList :: trace_match_spec())
| boolean()
| restart
| pause,
FlagList :: [ trace_pattern_flag() ].
-trace_pattern(_MFA, _MatchSpec, _FlagList) ->
- erlang:nif_error(undefined).
+trace_pattern(MFA, MatchSpec, FlagList) ->
+ %% Make sure that we have loaded the tracer module
+ case lists:keyfind(meta, 1, FlagList) of
+ {meta, Module, State} when erlang:is_atom(Module) ->
+ case erlang:module_loaded(Module) of
+ false ->
+ Module:enabled(trace_status, erlang:self(), State);
+ true ->
+ ok
+ end;
+ _ ->
+ ignore
+ end,
+
+ try erts_internal:trace_pattern(MFA, MatchSpec, FlagList) of
+ Res -> Res
+ catch E:R ->
+ {_, [_ | CST]} = erlang:process_info(
+ erlang:self(), current_stacktrace),
+ erlang:raise(
+ E, R, [{?MODULE, trace_pattern, [MFA, MatchSpec, FlagList], []} | CST])
+ end.
%% Shadowed by erl_bif_types: erlang:tuple_to_list/1
-spec tuple_to_list(Tuple) -> [term()] when
@@ -2438,13 +2545,16 @@ tuple_to_list(_Tuple) ->
logical_processors_available |
logical_processors_online) -> unknown | pos_integer();
(machine) -> string();
+ (max_heap_size) -> {max_heap_size, MaxHeapSize :: max_heap_size()};
+ (message_queue_data) -> message_queue_data();
(min_heap_size) -> {min_heap_size, MinHeapSize :: pos_integer()};
(min_bin_vheap_size) -> {min_bin_vheap_size,
MinBinVHeapSize :: pos_integer()};
(modified_timing_level) -> integer() | undefined;
- (multi_scheduling) -> disabled | blocked | enabled;
+ (multi_scheduling) -> disabled | blocked | blocked_normal | enabled;
(multi_scheduling_blockers) -> [Pid :: pid()];
(nif_version) -> string();
+ (normal_multi_scheduling_blockers) -> [Pid :: pid()];
(otp_release) -> string();
(os_monotonic_time_source) -> [{atom(),term()}];
(os_system_time_source) -> [{atom(),term()}];
@@ -2494,6 +2604,7 @@ universaltime_to_localtime(_Universaltime) ->
%%--------------------------------------------------------------------------
+%% Shadowed by erl_bif_types: erlang:apply/2
-spec apply(Fun, Args) -> term() when
Fun :: function(),
Args :: [term()].
@@ -2572,14 +2683,27 @@ spawn_monitor(M, F, A) when erlang:is_atom(M),
spawn_monitor(M, F, A) ->
erlang:error(badarg, [M,F,A]).
+
+-type max_heap_size() ::
+ Size :: non_neg_integer()
+ %% TODO change size => to := when -type maps support is finalized
+ | #{ size => non_neg_integer(),
+ kill => boolean(),
+ error_logger => boolean() }.
+
+-type spawn_opt_option() ::
+ link
+ | monitor
+ | {priority, Level :: priority_level()}
+ | {fullsweep_after, Number :: non_neg_integer()}
+ | {min_heap_size, Size :: non_neg_integer()}
+ | {min_bin_vheap_size, VSize :: non_neg_integer()}
+ | {max_heap_size, Size :: max_heap_size()}
+ | {message_queue_data, MQD :: message_queue_data()}.
+
-spec spawn_opt(Fun, Options) -> pid() | {pid(), reference()} when
Fun :: function(),
- Options :: [Option],
- Option :: link | monitor
- | {priority, Level :: priority_level()}
- | {fullsweep_after, Number :: non_neg_integer()}
- | {min_heap_size, Size :: non_neg_integer()}
- | {min_bin_vheap_size, VSize :: non_neg_integer()}.
+ Options :: [spawn_opt_option()].
spawn_opt(F, O) when erlang:is_function(F) ->
spawn_opt(erlang, apply, [F, []], O);
spawn_opt({M,F}=MF, O) when erlang:is_atom(M), erlang:is_atom(F) ->
@@ -2592,12 +2716,7 @@ spawn_opt(F, O) ->
-spec spawn_opt(Node, Fun, Options) -> pid() | {pid(), reference()} when
Node :: node(),
Fun :: function(),
- Options :: [Option],
- Option :: link | monitor
- | {priority, Level :: priority_level()}
- | {fullsweep_after, Number :: non_neg_integer()}
- | {min_heap_size, Size :: non_neg_integer()}
- | {min_bin_vheap_size, VSize :: non_neg_integer()}.
+ Options :: [spawn_opt_option()].
spawn_opt(N, F, O) when N =:= erlang:node() ->
spawn_opt(F, O);
spawn_opt(N, F, O) when erlang:is_function(F) ->
@@ -2684,12 +2803,7 @@ spawn_link(N,M,F,A) ->
Module :: module(),
Function :: atom(),
Args :: [term()],
- Options :: [Option],
- Option :: link | monitor
- | {priority, Level :: priority_level()}
- | {fullsweep_after, Number :: non_neg_integer()}
- | {min_heap_size, Size :: non_neg_integer()}
- | {min_bin_vheap_size, VSize :: non_neg_integer()}.
+ Options :: [spawn_opt_option()].
spawn_opt(M, F, A, Opts) ->
case catch erlang:spawn_opt({M,F,A,Opts}) of
{'EXIT',{Reason,_}} ->
@@ -2704,12 +2818,7 @@ spawn_opt(M, F, A, Opts) ->
Module :: module(),
Function :: atom(),
Args :: [term()],
- Options :: [Option],
- Option :: link | monitor
- | {priority, Level :: priority_level()}
- | {fullsweep_after, Number :: non_neg_integer()}
- | {min_heap_size, Size :: non_neg_integer()}
- | {min_bin_vheap_size, VSize :: non_neg_integer()}.
+ Options :: [spawn_opt_option()].
spawn_opt(N, M, F, A, O) when N =:= erlang:node(),
erlang:is_atom(M), erlang:is_atom(F),
erlang:is_list(A), erlang:is_list(O) ->
@@ -3004,6 +3113,9 @@ port_info(Port) ->
(Port, monitors) -> {monitors, Monitors} | 'undefined' when
Port :: port() | atom(),
Monitors :: [{process, pid()}];
+ (Port, monitored_by) -> {monitored_by, MonitoredBy} | 'undefined' when
+ Port :: port() | atom(),
+ MonitoredBy :: [pid()];
(Port, name) -> {name, Name} | 'undefined' when
Port :: port() | atom(),
Name :: string();
diff --git a/erts/preloaded/src/erts.app.src b/erts/preloaded/src/erts.app.src
index 8442aaf7e8..7ab06164b4 100644
--- a/erts/preloaded/src/erts.app.src
+++ b/erts/preloaded/src/erts.app.src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@
erts_internal,
init,
otp_ring0,
+ erts_code_purger,
prim_eval,
prim_file,
prim_inet,
@@ -36,7 +37,7 @@
{registered, []},
{applications, []},
{env, []},
- {runtime_dependencies, ["stdlib-2.5", "kernel-4.0", "sasl-2.4"]}
+ {runtime_dependencies, ["stdlib-3.0", "kernel-5.0", "sasl-3.0.1"]}
]}.
%% vim: ft=erlang
diff --git a/erts/preloaded/src/erts_code_purger.erl b/erts/preloaded/src/erts_code_purger.erl
new file mode 100644
index 0000000000..28d71fd07e
--- /dev/null
+++ b/erts/preloaded/src/erts_code_purger.erl
@@ -0,0 +1,429 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(erts_code_purger).
+
+%% Purpose : Implement system process erts_code_purger
+%% to handle code module purging.
+
+-export([start/0, purge/1, soft_purge/1, pending_purge_lambda/3,
+ finish_after_on_load/2]).
+
+-spec start() -> term().
+start() ->
+ register(erts_code_purger, self()),
+ process_flag(trap_exit, true),
+ loop().
+
+loop() ->
+ _ = receive
+ {purge,Mod,From,Ref} when is_atom(Mod), is_pid(From) ->
+ Res = do_purge(Mod),
+ From ! {reply, purge, Res, Ref};
+
+ {soft_purge,Mod,From,Ref} when is_atom(Mod), is_pid(From) ->
+ Res = do_soft_purge(Mod),
+ From ! {reply, soft_purge, Res, Ref};
+
+ {finish_after_on_load,{Mod,Keep},From,Ref}
+ when is_atom(Mod), is_pid(From) ->
+ Res = do_finish_after_on_load(Mod, Keep),
+ From ! {reply, finish_after_on_load, Res, Ref};
+
+ {test_purge, Mod, From, Type, Ref} when is_atom(Mod), is_pid(From) ->
+ do_test_purge(Mod, From, Type, Ref);
+
+ _Other -> ignore
+ end,
+ loop().
+
+%%
+%% Processes that tries to call a fun that belongs to
+%% a module that currently is being purged will end
+%% up here (pending_purge_lambda) in a suspended state.
+%% When the purge operation completes or aborts (soft
+%% purge that failed) these processes will be resumed.
+%%
+pending_purge_lambda(_Module, Fun, Args) ->
+ %%
+ %% When the process is resumed, the following
+ %% scenarios exist:
+ %% * The code that the fun refers to is still
+ %% there due to a failed soft purge. The
+ %% call to the fun will succeed via apply/2.
+ %% * The code was purged, and a current version
+ %% of the module is loaded which does not
+ %% contain this fun. The call will result
+ %% in an exception being raised.
+ %% * The code was purged, and no current
+ %% version of the module is loaded. An attempt
+ %% to load the module (via the error_handler)
+ %% will be made. This may or may not succeed.
+ %% If the module is loaded, it may or may
+ %% not contain the fun. The call will
+ %% succeed if the error_handler was able
+ %% to load the module and loaded module
+ %% contains this fun; otherwise, an exception
+ %% will be raised.
+ %%
+ apply(Fun, Args).
+
+%% purge(Module)
+%% Kill all processes running code from *old* Module, and then purge the
+%% module. Return {WasOld, DidKill}:
+%% {false, false} there was no old module to purge
+%% {true, false} module purged, no process killed
+%% {true, true} module purged, at least one process killed
+
+purge(Mod) when is_atom(Mod) ->
+ Ref = make_ref(),
+ erts_code_purger ! {purge, Mod, self(), Ref},
+ receive
+ {reply, purge, Result, Ref} ->
+ Result
+ end.
+
+do_purge(Mod) ->
+ case erts_internal:purge_module(Mod, prepare) of
+ false ->
+ {false, false};
+ true ->
+ DidKill = check_proc_code(erlang:processes(), Mod, true),
+ true = erts_internal:purge_module(Mod, complete),
+ {true, DidKill}
+ end.
+
+%% soft_purge(Module)
+%% Purge old code only if no procs remain that run old code.
+%% Return true in that case, false if procs remain (in this
+%% case old code is not purged)
+
+soft_purge(Mod) ->
+ Ref = make_ref(),
+ erts_code_purger ! {soft_purge, Mod, self(), Ref},
+ receive
+ {reply, soft_purge, Result, Ref} ->
+ Result
+ end.
+
+do_soft_purge(Mod) ->
+ case erts_internal:purge_module(Mod, prepare) of
+ false ->
+ true;
+ true ->
+ Res = check_proc_code(erlang:processes(), Mod, false),
+ erts_internal:purge_module(Mod,
+ case Res of
+ false -> abort;
+ true -> complete
+ end)
+ end.
+
+%% finish_after_on_load(Module, Keep)
+%% Finish after running on_load function. If Keep is false,
+%% purge the code for the on_load function.
+
+finish_after_on_load(Mod, Keep) ->
+ Ref = make_ref(),
+ erts_code_purger ! {finish_after_on_load, {Mod,Keep}, self(), Ref},
+ receive
+ {reply, finish_after_on_load, Result, Ref} ->
+ Result
+ end.
+
+do_finish_after_on_load(Mod, Keep) ->
+ erlang:finish_after_on_load(Mod, Keep),
+ case Keep of
+ true ->
+ ok;
+ false ->
+ case erts_internal:purge_module(Mod, prepare_on_load) of
+ false ->
+ true;
+ true ->
+ _ = check_proc_code(erlang:processes(), Mod, true),
+ true = erts_internal:purge_module(Mod, complete)
+ end
+ end.
+
+
+
+%%
+%% check_proc_code(Pids, Mod, Hard) - Send asynchronous
+%% requests to all processes to perform a check_process_code
+%% operation. Each process will check their own state and
+%% reply with the result. If 'Hard' equals
+%% - true, processes that refer 'Mod' will be killed. If
+%% any processes were killed true is returned; otherwise,
+%% false.
+%% - false, and any processes refer 'Mod', false will
+%% returned; otherwise, true.
+%%
+%% Requests will be sent to all processes identified by
+%% Pids at once, but without allowing GC to be performed.
+%% Check process code operations that are aborted due to
+%% GC need, will be restarted allowing GC. However, only
+%% ?MAX_CPC_GC_PROCS outstanding operation allowing GC at
+%% a time will be allowed. This in order not to blow up
+%% memory wise.
+%%
+%% We also only allow ?MAX_CPC_NO_OUTSTANDING_KILLS
+%% outstanding kills. This both in order to avoid flooding
+%% our message queue with 'DOWN' messages and limiting the
+%% amount of memory used to keep references to all
+%% outstanding kills.
+%%
+
+%% We maybe should allow more than two outstanding
+%% GC requests, but for now we play it safe...
+-define(MAX_CPC_GC_PROCS, 2).
+-define(MAX_CPC_NO_OUTSTANDING_KILLS, 10).
+
+-record(cpc_static, {hard, module, tag}).
+
+-record(cpc_kill, {outstanding = [],
+ no_outstanding = 0,
+ waiting = [],
+ killed = false}).
+
+check_proc_code(Pids, Mod, Hard) ->
+ Tag = erlang:make_ref(),
+ CpcS = #cpc_static{hard = Hard,
+ module = Mod,
+ tag = Tag},
+ check_proc_code(CpcS, cpc_init(CpcS, Pids, 0), 0, [], #cpc_kill{}, true).
+
+check_proc_code(#cpc_static{hard = true}, 0, 0, [],
+ #cpc_kill{outstanding = [], waiting = [], killed = Killed},
+ true) ->
+ %% No outstanding requests. We did a hard check, so result is whether or
+ %% not we killed any processes...
+ Killed;
+check_proc_code(#cpc_static{hard = false}, 0, 0, [], _KillState, Success) ->
+ %% No outstanding requests and we did a soft check...
+ Success;
+check_proc_code(#cpc_static{hard = false, tag = Tag} = CpcS, NoReq0, NoGcReq0,
+ [], _KillState, false) ->
+ %% Failed soft check; just cleanup the remaining replies corresponding
+ %% to the requests we've sent...
+ {NoReq1, NoGcReq1} = receive
+ {check_process_code, {Tag, _P, GC}, _Res} ->
+ case GC of
+ false -> {NoReq0-1, NoGcReq0};
+ true -> {NoReq0, NoGcReq0-1}
+ end
+ end,
+ check_proc_code(CpcS, NoReq1, NoGcReq1, [], _KillState, false);
+check_proc_code(#cpc_static{tag = Tag} = CpcS, NoReq0, NoGcReq0, NeedGC0,
+ KillState0, Success) ->
+
+ %% Check if we should request a GC operation
+ {NoGcReq1, NeedGC1} = case NoGcReq0 < ?MAX_CPC_GC_PROCS of
+ GcOpAllowed when GcOpAllowed == false;
+ NeedGC0 == [] ->
+ {NoGcReq0, NeedGC0};
+ _ ->
+ {NoGcReq0+1, cpc_request_gc(CpcS,NeedGC0)}
+ end,
+
+ %% Wait for a cpc reply or 'DOWN' message
+ {NoReq1, NoGcReq2, Pid, Result, KillState1} = cpc_recv(Tag,
+ NoReq0,
+ NoGcReq1,
+ KillState0),
+
+ %% Check the result of the reply
+ case Result of
+ aborted ->
+ %% Operation aborted due to the need to GC in order to
+ %% determine if the process is referring the module.
+ %% Schedule the operation for restart allowing GC...
+ check_proc_code(CpcS, NoReq1, NoGcReq2, [Pid|NeedGC1], KillState1,
+ Success);
+ false ->
+ %% Process not referring the module; done with this process...
+ check_proc_code(CpcS, NoReq1, NoGcReq2, NeedGC1, KillState1,
+ Success);
+ true ->
+ %% Process referring the module...
+ case CpcS#cpc_static.hard of
+ false ->
+ %% ... and soft check. The whole operation failed so
+ %% no point continuing; clean up and fail...
+ check_proc_code(CpcS, NoReq1, NoGcReq2, [], KillState1,
+ false);
+ true ->
+ %% ... and hard check; schedule kill of it...
+ check_proc_code(CpcS, NoReq1, NoGcReq2, NeedGC1,
+ cpc_sched_kill(Pid, KillState1), Success)
+ end;
+ 'DOWN' ->
+ %% Handled 'DOWN' message
+ check_proc_code(CpcS, NoReq1, NoGcReq2, NeedGC1,
+ KillState1, Success)
+ end.
+
+cpc_recv(Tag, NoReq, NoGcReq, #cpc_kill{outstanding = []} = KillState) ->
+ receive
+ {check_process_code, {Tag, Pid, GC}, Res} ->
+ cpc_handle_cpc(NoReq, NoGcReq, GC, Pid, Res, KillState)
+ end;
+cpc_recv(Tag, NoReq, NoGcReq,
+ #cpc_kill{outstanding = [R0, R1, R2, R3, R4 | _]} = KillState) ->
+ receive
+ {'DOWN', R, process, _, _} when R == R0;
+ R == R1;
+ R == R2;
+ R == R3;
+ R == R4 ->
+ cpc_handle_down(NoReq, NoGcReq, R, KillState);
+ {check_process_code, {Tag, Pid, GC}, Res} ->
+ cpc_handle_cpc(NoReq, NoGcReq, GC, Pid, Res, KillState)
+ end;
+cpc_recv(Tag, NoReq, NoGcReq, #cpc_kill{outstanding = [R|_]} = KillState) ->
+ receive
+ {'DOWN', R, process, _, _} ->
+ cpc_handle_down(NoReq, NoGcReq, R, KillState);
+ {check_process_code, {Tag, Pid, GC}, Res} ->
+ cpc_handle_cpc(NoReq, NoGcReq, GC, Pid, Res, KillState)
+ end.
+
+cpc_handle_down(NoReq, NoGcReq, R, #cpc_kill{outstanding = Rs,
+ no_outstanding = N} = KillState) ->
+ {NoReq, NoGcReq, undefined, 'DOWN',
+ cpc_sched_kill_waiting(KillState#cpc_kill{outstanding = cpc_list_rm(R, Rs),
+ no_outstanding = N-1})}.
+
+cpc_list_rm(R, [R|Rs]) ->
+ Rs;
+cpc_list_rm(R0, [R1|Rs]) ->
+ [R1|cpc_list_rm(R0, Rs)].
+
+cpc_handle_cpc(NoReq, NoGcReq, false, Pid, Res, KillState) ->
+ {NoReq-1, NoGcReq, Pid, Res, KillState};
+cpc_handle_cpc(NoReq, NoGcReq, true, Pid, Res, KillState) ->
+ {NoReq, NoGcReq-1, Pid, Res, KillState}.
+
+cpc_sched_kill_waiting(#cpc_kill{waiting = []} = KillState) ->
+ KillState;
+cpc_sched_kill_waiting(#cpc_kill{outstanding = Rs,
+ no_outstanding = N,
+ waiting = [P|Ps]} = KillState) ->
+ R = erlang:monitor(process, P),
+ exit(P, kill),
+ KillState#cpc_kill{outstanding = [R|Rs],
+ no_outstanding = N+1,
+ waiting = Ps,
+ killed = true}.
+
+cpc_sched_kill(Pid, #cpc_kill{no_outstanding = N, waiting = Pids} = KillState)
+ when N >= ?MAX_CPC_NO_OUTSTANDING_KILLS ->
+ KillState#cpc_kill{waiting = [Pid|Pids]};
+cpc_sched_kill(Pid,
+ #cpc_kill{outstanding = Rs, no_outstanding = N} = KillState) ->
+ R = erlang:monitor(process, Pid),
+ exit(Pid, kill),
+ KillState#cpc_kill{outstanding = [R|Rs],
+ no_outstanding = N+1,
+ killed = true}.
+
+cpc_request(#cpc_static{tag = Tag, module = Mod}, Pid, AllowGc) ->
+ erts_internal:check_process_code(Pid, Mod, [{async, {Tag, Pid, AllowGc}},
+ {allow_gc, AllowGc}]).
+
+cpc_request_gc(CpcS, [Pid|Pids]) ->
+ cpc_request(CpcS, Pid, true),
+ Pids.
+
+cpc_init(_CpcS, [], NoReqs) ->
+ NoReqs;
+cpc_init(CpcS, [Pid|Pids], NoReqs) ->
+ cpc_request(CpcS, Pid, false),
+ cpc_init(CpcS, Pids, NoReqs+1).
+
+% end of check_proc_code() implementation.
+
+%%
+%% FOR TESTING ONLY
+%%
+%% do_test_purge() is for testing only. The purge is done
+%% as usual, but the tester can control when to enter the
+%% specific phases.
+%%
+do_test_purge(Mod, From, Type, Ref) when Type == true; Type == false ->
+ Mon = erlang:monitor(process, From),
+ Res = case Type of
+ true -> do_test_hard_purge(Mod, From, Ref, Mon);
+ false -> do_test_soft_purge(Mod, From, Ref, Mon)
+ end,
+ From ! {test_purge, Res, Ref},
+ erlang:demonitor(Mon, [flush]),
+ ok;
+do_test_purge(_, _, _, _) ->
+ ok.
+
+do_test_soft_purge(Mod, From, Ref, Mon) ->
+ PrepRes = erts_internal:purge_module(Mod, prepare),
+ TestRes = test_progress(started, From, Mon, Ref, ok),
+ case PrepRes of
+ false ->
+ _ = test_progress(continued, From, Mon, Ref, TestRes),
+ true;
+ true ->
+ Res = check_proc_code(erlang:processes(), Mod, false),
+ _ = test_progress(continued, From, Mon, Ref, TestRes),
+ erts_internal:purge_module(Mod,
+ case Res of
+ false -> abort;
+ true -> complete
+ end)
+ end.
+
+do_test_hard_purge(Mod, From, Ref, Mon) ->
+ PrepRes = erts_internal:purge_module(Mod, prepare),
+ TestRes = test_progress(started, From, Mon, Ref, ok),
+ case PrepRes of
+ false ->
+ _ = test_progress(continued, From, Mon, Ref, TestRes),
+ {false, false};
+ true ->
+ DidKill = check_proc_code(erlang:processes(), Mod, true),
+ _ = test_progress(continued, From, Mon, Ref, TestRes),
+ true = erts_internal:purge_module(Mod, complete),
+ {true, DidKill}
+ end.
+
+test_progress(_State, _From, _Mon, _Ref, died) ->
+ %% Test process died; continue so we wont
+ %% leave the system in an inconsistent
+ %% state...
+ died;
+test_progress(started, From, Mon, Ref, ok) ->
+ From ! {started, Ref},
+ receive
+ {'DOWN', Mon, process, From, _} -> died;
+ {continue, Ref} -> ok
+ end;
+test_progress(continued, From, Mon, Ref, ok) ->
+ From ! {continued, Ref},
+ receive
+ {'DOWN', Mon, process, From, _} -> died;
+ {complete, Ref} -> ok
+ end.
+
diff --git a/erts/preloaded/src/erts_dirty_process_code_checker.erl b/erts/preloaded/src/erts_dirty_process_code_checker.erl
new file mode 100644
index 0000000000..911642082c
--- /dev/null
+++ b/erts/preloaded/src/erts_dirty_process_code_checker.erl
@@ -0,0 +1,82 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(erts_dirty_process_code_checker).
+
+-export([start/0]).
+
+%%
+%% The erts_dirty_process_code_checker is started at
+%% VM boot by the VM. It is a spawned as a system
+%% process, i.e, the whole VM will terminate if
+%% this process terminates.
+%%
+start() ->
+ process_flag(trap_exit, true),
+ msg_loop().
+
+msg_loop() ->
+ _ = receive
+ Request ->
+ handle_request(Request)
+ end,
+ msg_loop().
+
+check_process(Requester, Target, ReqId, Module) ->
+ Result = erts_internal:check_dirty_process_code(Target, Module),
+ Requester ! {check_process_code, ReqId, Result}.
+
+handle_request({Requester,
+ Target,
+ Prio,
+ {check_process_code,
+ ReqId,
+ Module,
+ _Flags} = Op}) ->
+ %%
+ %% Target may have stopped executing dirty since the
+ %% initial request was made. Check its current state
+ %% and try to send the request if possible; otherwise,
+ %% check the dirty executing process and send the result...
+ %%
+ try
+ case erts_internal:is_process_executing_dirty(Target) of
+ true ->
+ check_process(Requester, Target, ReqId, Module);
+ false ->
+ case erts_internal:request_system_task(Requester,
+ Target,
+ Prio,
+ Op) of
+ ok ->
+ ok;
+ dirty_execution ->
+ check_process(Requester, Target, ReqId, Module)
+ end
+ end
+ catch
+ _ : _ ->
+ ok %% Ignore all failures; someone passed us garbage...
+ end;
+handle_request(_Garbage) ->
+ ignore.
+
+
+
diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl
index 6db77a8482..f4518c4008 100644
--- a/erts/preloaded/src/erts_internal.erl
+++ b/erts/preloaded/src/erts_internal.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2012-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2012-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -31,25 +31,37 @@
-export([await_port_send_result/3]).
-export([cmp_term/2]).
--export([map_to_tuple_keys/1, map_type/1, map_hashmap_children/1]).
--export([port_command/3, port_connect/2, port_close/1,
+-export([map_to_tuple_keys/1, term_type/1, map_hashmap_children/1]).
+-export([open_port/2, port_command/3, port_connect/2, port_close/1,
port_control/3, port_call/3, port_info/1, port_info/2]).
-export([system_check/1,
gather_system_check_result/1]).
--export([request_system_task/3]).
+-export([request_system_task/3, request_system_task/4]).
--export([check_process_code/2]).
+-export([check_process_code/3]).
+-export([check_dirty_process_code/2]).
+-export([is_process_executing_dirty/1]).
+-export([release_literal_area_switch/0]).
+-export([purge_module/2]).
-export([flush_monitor_messages/3]).
-export([await_result/1, gather_io_bytes/2]).
--export([time_unit/0]).
+-export([time_unit/0, perf_counter_unit/0]).
-export([is_system_process/1]).
+-export([await_microstate_accounting_modifications/3,
+ gather_microstate_accounting_result/2]).
+
+-export([trace/3, trace_pattern/3]).
+
+%% Auto import name clash
+-export([check_process_code/2]).
+
%%
%% Await result of send to port
%%
@@ -91,6 +103,13 @@ gather_io_bytes(Ref, No, InAcc, OutAcc) ->
%% Statically linked port NIFs
%%
+-spec erts_internal:open_port(PortName, PortSettings) -> Result when
+ PortName :: tuple(),
+ PortSettings :: term(),
+ Result :: port() | reference() | atom().
+open_port(_PortName, _PortSettings) ->
+ erlang:nif_error(undefined).
+
-spec erts_internal:port_command(Port, Data, OptionList) -> Result when
Port :: port() | atom(),
Data :: iodata(),
@@ -187,17 +206,106 @@ port_info(_Result, _Item) ->
-spec request_system_task(Pid, Prio, Request) -> 'ok' when
Prio :: 'max' | 'high' | 'normal' | 'low',
Request :: {'garbage_collect', term()}
- | {'check_process_code', term(), module(), boolean()},
+ | {'check_process_code', term(), module(), non_neg_integer()}
+ | {'copy_literals', term(), boolean()},
Pid :: pid().
request_system_task(_Pid, _Prio, _Request) ->
erlang:nif_error(undefined).
--spec check_process_code(Module, OptionList) -> boolean() when
+-spec request_system_task(RequesterPid, TargetPid, Prio, Request) -> 'ok' | 'dirty_execution' when
+ Prio :: 'max' | 'high' | 'normal' | 'low',
+ Request :: {'garbage_collect', term()}
+ | {'check_process_code', term(), module(), non_neg_integer()}
+ | {'copy_literals', term(), boolean()},
+ RequesterPid :: pid(),
+ TargetPid :: pid().
+
+request_system_task(_RequesterPid, _TargetPid, _Prio, _Request) ->
+ erlang:nif_error(undefined).
+
+-define(ERTS_CPC_ALLOW_GC, (1 bsl 0)).
+
+-spec check_process_code(Module, Flags) -> boolean() when
+ Module :: module(),
+ Flags :: non_neg_integer().
+check_process_code(_Module, _Flags) ->
+ erlang:nif_error(undefined).
+
+-spec check_process_code(Pid, Module, OptionList) -> CheckResult | async when
+ Pid :: pid(),
+ Module :: module(),
+ RequestId :: term(),
+ Option :: {async, RequestId} | {allow_gc, boolean()},
+ OptionList :: [Option],
+ CheckResult :: boolean() | aborted.
+check_process_code(Pid, Module, OptionList) ->
+ {Async, Flags} = get_cpc_opts(OptionList, sync, ?ERTS_CPC_ALLOW_GC),
+ case Async of
+ {async, ReqId} ->
+ {priority, Prio} = erlang:process_info(erlang:self(),
+ priority),
+ erts_internal:request_system_task(Pid,
+ Prio,
+ {check_process_code,
+ ReqId,
+ Module,
+ Flags}),
+ async;
+ sync ->
+ case Pid == erlang:self() of
+ true ->
+ erts_internal:check_process_code(Module, Flags);
+ false ->
+ {priority, Prio} = erlang:process_info(erlang:self(),
+ priority),
+ ReqId = erlang:make_ref(),
+ erts_internal:request_system_task(Pid,
+ Prio,
+ {check_process_code,
+ ReqId,
+ Module,
+ Flags}),
+ receive
+ {check_process_code, ReqId, CheckResult} ->
+ CheckResult
+ end
+ end
+ end.
+
+% gets async and flag opts and verify valid option list
+get_cpc_opts([{async, _ReqId} = AsyncTuple | Options], _OldAsync, Flags) ->
+ get_cpc_opts(Options, AsyncTuple, Flags);
+get_cpc_opts([{allow_gc, AllowGC} | Options], Async, Flags) ->
+ get_cpc_opts(Options, Async, cpc_flags(Flags, ?ERTS_CPC_ALLOW_GC, AllowGC));
+get_cpc_opts([], Async, Flags) ->
+ {Async, Flags}.
+
+cpc_flags(OldFlags, Bit, true) ->
+ OldFlags bor Bit;
+cpc_flags(OldFlags, Bit, false) ->
+ OldFlags band (bnot Bit).
+
+-spec check_dirty_process_code(Pid,Module) -> 'true' | 'false' when
+ Pid :: pid(),
+ Module :: module().
+check_dirty_process_code(_Pid,_Module) ->
+ erlang:nif_error(undefined).
+
+-spec is_process_executing_dirty(Pid) -> 'true' | 'false' when
+ Pid :: pid().
+is_process_executing_dirty(_Pid) ->
+ erlang:nif_error(undefined).
+
+-spec release_literal_area_switch() -> 'true' | 'false'.
+
+release_literal_area_switch() ->
+ erlang:nif_error(undefined).
+
+-spec purge_module(Module, Op) -> boolean() when
Module :: module(),
- Option :: {allow_gc, boolean()},
- OptionList :: [Option].
-check_process_code(_Module, _OptionList) ->
+ Op :: 'prepare' | 'prepare_on_load' | 'abort' | 'complete'.
+purge_module(_Module, _Op) ->
erlang:nif_error(undefined).
-spec system_check(Type) -> 'ok' when
@@ -235,12 +343,18 @@ cmp_term(_A,_B) ->
map_to_tuple_keys(_M) ->
erlang:nif_error(undefined).
-%% return the internal map type
--spec map_type(M) -> Type when
- M :: map(),
- Type :: 'flatmap' | 'hashmap' | 'hashmap_node'.
-
-map_type(_M) ->
+%% return the internal term type
+-spec term_type(T) -> Type when
+ T :: term(),
+ Type :: 'flatmap' | 'hashmap' | 'hashmap_node'
+ | 'fixnum' | 'bignum' | 'hfloat'
+ | 'list' | 'tuple' | 'export' | 'fun'
+ | 'refc_binary' | 'heap_binary' | 'sub_binary'
+ | 'reference' | 'external_reference'
+ | 'pid' | 'external_pid' | 'port' | 'external_port'
+ | 'atom' | 'catch' | 'nil'.
+
+term_type(_T) ->
erlang:nif_error(undefined).
%% return the internal hashmap sub-nodes from
@@ -278,8 +392,64 @@ flush_monitor_messages(Ref, Multi, Res) when is_reference(Ref) ->
time_unit() ->
erlang:nif_error(undefined).
+-spec erts_internal:perf_counter_unit() -> pos_integer().
+
+perf_counter_unit() ->
+ erlang:nif_error(undefined).
+
-spec erts_internal:is_system_process(Pid) -> boolean() when
Pid :: pid().
is_system_process(_Pid) ->
erlang:nif_error(undefined).
+
+-spec await_microstate_accounting_modifications(Ref, Result, Threads) -> boolean() when
+ Ref :: reference(),
+ Result :: boolean(),
+ Threads :: pos_integer().
+
+await_microstate_accounting_modifications(Ref, Result, Threads) ->
+ _ = microstate_accounting(Ref,Threads),
+ Result.
+
+-spec gather_microstate_accounting_result(Ref, Threads) -> [#{}] when
+ Ref :: reference(),
+ Threads :: pos_integer().
+
+gather_microstate_accounting_result(Ref, Threads) ->
+ microstate_accounting(Ref, Threads).
+
+microstate_accounting(_Ref, 0) ->
+ [];
+microstate_accounting(Ref, Threads) ->
+ receive
+ Ref -> microstate_accounting(Ref, Threads - 1);
+ {Ref, Res} ->
+ [Res | microstate_accounting(Ref, Threads - 1)]
+ end.
+
+-spec trace(PidPortSpec, How, FlagList) -> integer() when
+ PidPortSpec :: pid() | port()
+ | all | processes | ports
+ | existing | existing_processes | existing_ports
+ | new | new_processes | new_ports,
+ How :: boolean(),
+ FlagList :: [].
+trace(_PidSpec, _How, _FlagList) ->
+ erlang:nif_error(undefined).
+
+-type match_variable() :: atom(). % Approximation of '$1' | '$2' | ...
+-type trace_pattern_mfa() ::
+ {atom(),atom(),arity() | '_'} | on_load.
+-type trace_match_spec() ::
+ [{[term()] | '_' | match_variable() ,[term()],[term()]}].
+
+-spec trace_pattern(MFA, MatchSpec, FlagList) -> non_neg_integer() when
+ MFA :: trace_pattern_mfa(),
+ MatchSpec :: (MatchSpecList :: trace_match_spec())
+ | boolean()
+ | restart
+ | pause,
+ FlagList :: [ ].
+trace_pattern(_MFA, _MatchSpec, _FlagList) ->
+ erlang:nif_error(undefined).
diff --git a/erts/preloaded/src/erts_literal_area_collector.erl b/erts/preloaded/src/erts_literal_area_collector.erl
new file mode 100644
index 0000000000..3befad8dfb
--- /dev/null
+++ b/erts/preloaded/src/erts_literal_area_collector.erl
@@ -0,0 +1,113 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(erts_literal_area_collector).
+
+-export([start/0]).
+
+%% Currently we only allow two outstanding literal
+%% copying jobs that garbage collect in order to
+%% copy the literals. Maybe we could allow more
+%% than two outstanding processes, but for now we
+%% play it safe...
+-define(MAX_GC_OUTSTND, 2).
+
+%%
+%% The erts_literal_area_collector is started at
+%% VM boot by the VM. It is a spawned as a system
+%% process, i.e, the whole VM will terminate if
+%% this process terminates.
+%%
+start() ->
+ process_flag(trap_exit, true),
+ msg_loop(undefined, 0, 0, []).
+
+%%
+%% The VM will send us a 'copy_literals' message
+%% when it has a new literal area that needs to
+%% be handled is added. We will also be informed
+%% about more areas when we call
+%% erts_internal:release_literal_area_switch().
+%%
+msg_loop(Area, Outstnd, GcOutstnd, NeedGC) ->
+ receive
+
+ %% A new area to handle has arrived...
+ copy_literals when Outstnd == 0 ->
+ switch_area();
+
+ %% Process (_Pid) has completed the request...
+ {copy_literals, {Area, _GcAllowed, _Pid}, ok} when Outstnd == 1 ->
+ switch_area(); %% Last process completed...
+ {copy_literals, {Area, false, _Pid}, ok} ->
+ msg_loop(Area, Outstnd-1, GcOutstnd, NeedGC);
+ {copy_literals, {Area, true, _Pid}, ok} when NeedGC == [] ->
+ msg_loop(Area, Outstnd-1, GcOutstnd-1, []);
+ {copy_literals, {Area, true, _Pid}, ok} ->
+ send_copy_req(hd(NeedGC), Area, true),
+ msg_loop(Area, Outstnd-1, GcOutstnd, tl(NeedGC));
+
+ %% Process (Pid) failed to complete the request
+ %% since it needs to garbage collect in order to
+ %% complete the request...
+ {copy_literals, {Area, false, Pid}, need_gc} when GcOutstnd < ?MAX_GC_OUTSTND ->
+ send_copy_req(Pid, Area, true),
+ msg_loop(Area, Outstnd, GcOutstnd+1, NeedGC);
+ {copy_literals, {Area, false, Pid}, need_gc} ->
+ msg_loop(Area, Outstnd, GcOutstnd, [Pid|NeedGC]);
+
+ %% Not handled message regarding the area that we
+ %% currently are working with. Crash the VM so
+ %% we notice this bug...
+ {copy_literals, {Area, _, _}, _} = Msg when erlang:is_reference(Area) ->
+ exit({not_handled_message, Msg});
+
+ %% Unexpected garbage message. Get rid of it...
+ _Ignore ->
+ msg_loop(Area, Outstnd, GcOutstnd, NeedGC)
+
+ end.
+
+switch_area() ->
+ Res = erts_internal:release_literal_area_switch(),
+ erlang:garbage_collect(), %% Almost no live data now...
+ case Res of
+ false ->
+ %% No more areas to handle...
+ msg_loop(undefined, 0, 0, []);
+ true ->
+ %% Send requests to all processes to copy
+ %% all live data they have referring to the
+ %% literal area that is to be released...
+ Area = make_ref(),
+ Outstnd = send_copy_reqs(erlang:processes(), Area, false),
+ msg_loop(Area, Outstnd, 0, [])
+ end.
+
+send_copy_reqs(Ps, Area, GC) ->
+ send_copy_reqs(Ps, Area, GC, 0).
+
+send_copy_reqs([], _Area, _GC, N) ->
+ N;
+send_copy_reqs([P|Ps], Area, GC, N) ->
+ send_copy_req(P, Area, GC),
+ send_copy_reqs(Ps, Area, GC, N+1).
+
+send_copy_req(P, Area, GC) ->
+ erts_internal:request_system_task(P, normal, {copy_literals, {Area, GC, P}, GC}).
diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl
index 0ad5824ad1..551ca4ea40 100644
--- a/erts/preloaded/src/init.erl
+++ b/erts/preloaded/src/init.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -23,7 +23,6 @@
%% a local file or distributed from another erlang node.
%%
%% Flags:
-%% -id Identity : identity of the system.
%% -boot File : Absolute file name of the boot script.
%% -boot_var Var Value
%% : $Var in the boot script is expanded to
@@ -42,6 +41,7 @@
%% -s : Start own processes.
%%
%% Experimental flags:
+%% -profile_boot : Use an 'eprof light' to profile boot sequence
%% -init_debug : Activate debug printouts in init
%% -loader_debug : Activate debug printouts in erl_prim_loader
%% -code_path_choice : strict | relaxed
@@ -75,8 +75,22 @@
subscribed = []}).
-type state() :: #state{}.
+%% Data for eval_script/2.
+-record(es,
+ {init,
+ debug,
+ path,
+ pa,
+ pz,
+ path_choice,
+ prim_load,
+ load_mode,
+ vars
+ }).
+
-define(ON_LOAD_HANDLER, init__boot__on_load_handler).
+
debug(false, _) -> ok;
debug(_, T) -> erlang:display(T).
@@ -117,7 +131,7 @@ bs2ss(L) ->
get_status() ->
request(get_status).
--spec fetch_loaded() -> [atom()].
+-spec fetch_loaded() -> [{module(),file:filename()}].
fetch_loaded() ->
request(fetch_loaded).
@@ -160,17 +174,43 @@ stop() -> init ! {stop,stop}, ok.
-spec stop(Status) -> 'ok' when
Status :: non_neg_integer() | string().
-stop(Status) -> init ! {stop,{stop,Status}}, ok.
+stop(Status) when is_integer(Status), Status >= 0 ->
+ stop_1(Status);
+stop(Status) when is_list(Status) ->
+ case is_bytelist(Status) of
+ true ->
+ stop_1(Status);
+ false ->
+ erlang:error(badarg)
+ end;
+stop(_) ->
+ erlang:error(badarg).
+
+is_bytelist([B|Bs]) when is_integer(B), B >= 0, B < 256 -> is_bytelist(Bs);
+is_bytelist([]) -> true;
+is_bytelist(_) -> false.
+
+%% Note that we check the type of Status beforehand to ensure that
+%% the call to halt(Status) by the init process cannot fail
+stop_1(Status) -> init ! {stop,{stop,Status}}, ok.
-spec boot(BootArgs) -> no_return() when
BootArgs :: [binary()].
boot(BootArgs) ->
register(init, self()),
process_flag(trap_exit, true),
+
+ %% Load the tracer nif
+ erl_tracer:on_load(),
+
{Start0,Flags,Args} = parse_boot_args(BootArgs),
+ %% We don't get to profile parsing of BootArgs
+ case b2a(get_flag(profile_boot, Flags, false)) of
+ false -> ok;
+ true -> debug_profile_start()
+ end,
Start = map(fun prepare_run_args/1, Start0),
- Flags0 = flags_to_atoms_again(Flags),
- boot(Start,Flags0,Args).
+ boot(Start, Flags, Args).
prepare_run_args({eval, [Expr]}) ->
{eval,Expr};
@@ -202,16 +242,6 @@ map(_F, []) ->
map(F, [X|Rest]) ->
[F(X) | map(F, Rest)].
-flags_to_atoms_again([]) ->
- [];
-flags_to_atoms_again([{F0,L0}|Rest]) ->
- L = L0,
- F = b2a(F0),
- [{F,L}|flags_to_atoms_again(Rest)];
-flags_to_atoms_again([{F0}|Rest]) ->
- F = b2a(F0),
- [{F}|flags_to_atoms_again(Rest)].
-
-spec code_path_choice() -> 'relaxed' | 'strict'.
code_path_choice() ->
case get_argument(code_path_choice) of
@@ -274,21 +304,13 @@ things_to_string([]) ->
"".
halt_string(String, List) ->
- HaltString = String ++ things_to_string(List),
- if
- length(HaltString)<199 -> HaltString;
- true -> first198(HaltString, 198)
- end.
-
-first198([H|T], N) when N>0 ->
- [H|first198(T, N-1)];
-first198(_, 0) ->
- [].
+ String ++ things_to_string(List).
%% String = string()
%% List = [string() | atom() | pid() | number()]
%% Any other items in List, such as tuples, are ignored when creating
%% the string used as argument to erlang:halt/1.
+-spec crash(_, _) -> no_return().
crash(String, List) ->
halt(halt_string(String, List)).
@@ -296,9 +318,9 @@ crash(String, List) ->
-spec boot_loop(pid(), state()) -> no_return().
boot_loop(BootPid, State) ->
receive
- {BootPid,loaded,ModLoaded} ->
- Loaded = State#state.loaded,
- boot_loop(BootPid,State#state{loaded = [ModLoaded|Loaded]});
+ {BootPid,loaded,NewlyLoaded} ->
+ Loaded = NewlyLoaded ++ State#state.loaded,
+ boot_loop(BootPid, State#state{loaded = Loaded});
{BootPid,started,KernelPid} ->
boot_loop(BootPid, new_kernelpid(KernelPid, BootPid, State));
{BootPid,progress,started} ->
@@ -337,12 +359,25 @@ boot_loop(BootPid, State) ->
end.
ensure_loaded(Module, Loaded) ->
- File = concat([Module,objfile_extension()]),
- case catch load_mod(Module,File) of
- {ok, FullName} ->
- {{module, Module}, [{Module, FullName}|Loaded]};
- Res ->
- {Res, Loaded}
+ case erlang:module_loaded(Module) of
+ true ->
+ {{module, Module}, Loaded};
+ false ->
+ do_ensure_loaded(Module, Loaded)
+ end.
+
+do_ensure_loaded(Module, Loaded) ->
+ File = atom_to_list(Module) ++ objfile_extension(),
+ case erl_prim_loader:get_file(File) of
+ {ok,BinCode,FullName} ->
+ case do_load_module(Module, BinCode) of
+ ok ->
+ {{module, Module}, [{Module, FullName}|Loaded]};
+ error ->
+ {error, [{Module, FullName}|Loaded]}
+ end;
+ Error ->
+ {Error, Loaded}
end.
%% Tell subscribed processes the system has started.
@@ -388,9 +423,6 @@ loop(State) ->
Loaded = State#state.loaded, %% boot_loop but is handled here
From ! {init,Loaded}, %% anyway.
loop(State);
- {From, {ensure_loaded, _}} ->
- From ! {init, not_allowed},
- loop(State);
Msg ->
loop(handle_msg(Msg,State))
end.
@@ -430,6 +462,8 @@ do_handle_msg(Msg,State) ->
From ! {init,ok},
{new_state,State#state{subscribed = [Pid|Subscribed]}}
end;
+ {From, {ensure_loaded, _}} ->
+ From ! {init, not_allowed};
X ->
case whereis(user) of
undefined ->
@@ -451,9 +485,9 @@ do_handle_msg(Msg,State) ->
%%% -------------------------------------------------
make_permanent(Boot,Config,Flags0,State) ->
- case set_flag('-boot',Boot,Flags0) of
+ case set_flag(boot, Boot, Flags0) of
{ok,Flags1} ->
- case set_flag('-config',Config,Flags1) of
+ case set_flag(config, Config, Flags1) of
{ok,Flags} ->
{ok,State#state{flags = Flags}};
Error ->
@@ -691,17 +725,15 @@ sleep(T) -> receive after T -> ok end.
%%% The loader shall run for ever!
%%% -------------------------------------------------
-start_prim_loader(Init,Id,Pgm,Nodes,Path,{Pa,Pz}) ->
- case erl_prim_loader:start(Id,Pgm,Nodes) of
- {ok,Pid} when Path =:= false ->
- InitPath = append(Pa,["."|Pz]),
- erl_prim_loader:set_path(InitPath),
- add_to_kernel(Init,Pid),
- Pid;
+start_prim_loader(Init, Path0, {Pa,Pz}) ->
+ Path = case Path0 of
+ false -> Pa ++ ["."|Pz];
+ _ -> Path0
+ end,
+ case erl_prim_loader:start() of
{ok,Pid} ->
erl_prim_loader:set_path(Path),
- add_to_kernel(Init,Pid),
- Pid;
+ add_to_kernel(Init, Pid);
{error,Reason} ->
erlang:display({"cannot start loader",Reason}),
exit(Reason)
@@ -715,13 +747,6 @@ add_to_kernel(Init,Pid) ->
ok
end.
-prim_load_flags(Flags) ->
- PortPgm = get_flag('-loader',Flags,<<"efile">>),
- Hosts = get_flag_list('-hosts', Flags, []),
- Id = get_flag('-id',Flags,none),
- Path = get_flag_list('-path',Flags,false),
- {PortPgm, Hosts, Id, Path}.
-
%%% -------------------------------------------------
%%% The boot process fetches a boot script and loads
%%% all modules specified and starts spec. processes.
@@ -734,46 +759,74 @@ do_boot(Flags,Start) ->
do_boot(Init,Flags,Start) ->
process_flag(trap_exit,true),
- {Pgm0,Nodes,Id,Path} = prim_load_flags(Flags),
- Root = b2s(get_flag('-root',Flags)),
- PathFls = path_flags(Flags),
- Pgm = b2s(Pgm0),
- _Pid = start_prim_loader(Init,b2a(Id),Pgm,bs2as(Nodes),
- bs2ss(Path),PathFls),
+ Root = get_root(Flags),
+ Path = get_flag_list(path, Flags, false),
+ {Pa,Pz} = PathFls = path_flags(Flags),
+ start_prim_loader(Init, bs2ss(Path), PathFls),
BootFile = bootfile(Flags,Root),
BootList = get_boot(BootFile,Root),
- LoadMode = b2a(get_flag('-mode',Flags,false)),
- Deb = b2a(get_flag('-init_debug',Flags,false)),
+ LoadMode = b2a(get_flag(mode, Flags, false)),
+ Deb = b2a(get_flag(init_debug, Flags, false)),
catch ?ON_LOAD_HANDLER ! {init_debug_flag,Deb},
- BootVars = get_flag_args('-boot_var',Flags),
- ParallelLoad =
- (Pgm =:= "efile") and (erlang:system_info(thread_pool_size) > 0),
+ BootVars = get_boot_vars(Root, Flags),
PathChoice = code_path_choice(),
- eval_script(BootList,Init,PathFls,{Root,BootVars},Path,
- {true,LoadMode,ParallelLoad},Deb,PathChoice),
+ Es = #es{init=Init,debug=Deb,path=Path,pa=Pa,pz=Pz,
+ path_choice=PathChoice,
+ prim_load=true,load_mode=LoadMode,
+ vars=BootVars},
+ eval_script(BootList, Es),
%% To help identifying Purify windows that pop up,
%% print the node name into the Purify log.
(catch erlang:system_info({purify, "Node: " ++ atom_to_list(node())})),
- start_em(Start).
+ start_em(Start),
+ case b2a(get_flag(profile_boot,Flags,false)) of
+ false -> ok;
+ true ->
+ debug_profile_format_mfas(debug_profile_mfas()),
+ debug_profile_stop()
+ end,
+ ok.
+
+get_root(Flags) ->
+ case get_argument(root, Flags) of
+ {ok,[[Root]]} ->
+ Root;
+ _ ->
+ exit(no_or_multiple_root_variables)
+ end.
+
+get_boot_vars(Root, Flags) ->
+ BootVars = get_boot_vars_1(#{}, Flags),
+ RootKey = <<"ROOT">>,
+ BootVars#{RootKey=>Root}.
+
+get_boot_vars_1(Vars, [{boot_var,[Key,Value]}|T]) ->
+ get_boot_vars_1(Vars#{Key=>Value}, T);
+get_boot_vars_1(_, [{boot_var,_}|_]) ->
+ exit(invalid_boot_var_argument);
+get_boot_vars_1(Vars, [_|T]) ->
+ get_boot_vars_1(Vars, T);
+get_boot_vars_1(Vars, []) ->
+ Vars.
bootfile(Flags,Root) ->
- b2s(get_flag('-boot',Flags,concat([Root,"/bin/start"]))).
+ b2s(get_flag(boot, Flags, Root++"/bin/start")).
path_flags(Flags) ->
- Pa = append(reverse(get_flag_args('-pa',Flags))),
- Pz = append(get_flag_args('-pz',Flags)),
+ Pa = append(reverse(get_flag_args(pa, Flags))),
+ Pz = append(get_flag_args(pz, Flags)),
{bs2ss(Pa),bs2ss(Pz)}.
get_boot(BootFile0,Root) ->
- BootFile = concat([BootFile0,".boot"]),
+ BootFile = BootFile0 ++ ".boot",
case get_boot(BootFile) of
{ok, CmdList} ->
CmdList;
not_found -> %% Check for default.
- BootF = concat([Root,"/bin/",BootFile]),
+ BootF = Root ++ "/bin/" ++ BootFile,
case get_boot(BootF) of
{ok, CmdList} ->
CmdList;
@@ -807,91 +860,88 @@ get_boot(BootFile) ->
%% boot process hangs (we want to ensure syncronicity).
%%
-eval_script([{progress,Info}|CfgL],Init,PathFs,Vars,P,Ph,Deb,PathChoice) ->
- debug(Deb,{progress,Info}),
+eval_script([{progress,Info}=Progress|T], #es{debug=Deb}=Es) ->
+ debug(Deb, Progress),
init ! {self(),progress,Info},
- eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice);
-eval_script([{preLoaded,_}|CfgL],Init,PathFs,Vars,P,Ph,Deb,PathChoice) ->
- eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice);
-eval_script([{path,Path}|CfgL],Init,{Pa,Pz},Vars,false,Ph,Deb,PathChoice) ->
+ eval_script(T, Es);
+eval_script([{preLoaded,_}|T], #es{}=Es) ->
+ eval_script(T, Es);
+eval_script([{path,Path}|T], #es{path=false,pa=Pa,pz=Pz,
+ path_choice=PathChoice,
+ vars=Vars}=Es) ->
RealPath0 = make_path(Pa, Pz, Path, Vars),
RealPath = patch_path(RealPath0, PathChoice),
erl_prim_loader:set_path(RealPath),
- eval_script(CfgL,Init,{Pa,Pz},Vars,false,Ph,Deb,PathChoice);
-eval_script([{path,_}|CfgL],Init,PathFs,Vars,P,Ph,Deb,PathChoice) ->
+ eval_script(T, Es);
+eval_script([{path,_}|T], #es{}=Es) ->
%% Ignore, use the command line -path flag.
- eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice);
-eval_script([{kernel_load_completed}|CfgL],Init,PathFs,Vars,P,{_,embedded,Par},Deb,PathChoice) ->
- eval_script(CfgL,Init,PathFs,Vars,P,{true,embedded,Par},Deb,PathChoice);
-eval_script([{kernel_load_completed}|CfgL],Init,PathFs,Vars,P,{_,E,Par},Deb,PathChoice) ->
- eval_script(CfgL,Init,PathFs,Vars,P,{false,E,Par},Deb,PathChoice);
-eval_script([{primLoad,Mods}|CfgL],Init,PathFs,Vars,P,{true,E,Par},Deb,PathChoice)
+ eval_script(T, Es);
+eval_script([{kernel_load_completed}|T], #es{load_mode=Mode}=Es0) ->
+ Es = case Mode of
+ embedded -> Es0;
+ _ -> Es0#es{prim_load=false}
+ end,
+ eval_script(T, Es);
+eval_script([{primLoad,Mods}|T], #es{init=Init,prim_load=PrimLoad}=Es)
when is_list(Mods) ->
- if
- Par =:= true ->
- par_load_modules(Mods,Init);
+ case PrimLoad of
true ->
- load_modules(Mods)
+ load_modules(Mods, Init);
+ false ->
+ %% Do not load now, code_server does that dynamically!
+ ok
end,
- eval_script(CfgL,Init,PathFs,Vars,P,{true,E,Par},Deb,PathChoice);
-eval_script([{primLoad,_Mods}|CfgL],Init,PathFs,Vars,P,{false,E,Par},Deb,PathChoice) ->
- %% Do not load now, code_server does that dynamically!
- eval_script(CfgL,Init,PathFs,Vars,P,{false,E,Par},Deb,PathChoice);
-eval_script([{kernelProcess,Server,{Mod,Fun,Args}}|CfgL],Init,
- PathFs,Vars,P,Ph,Deb,PathChoice) ->
- debug(Deb,{start,Server}),
- start_in_kernel(Server,Mod,Fun,Args,Init),
- eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice);
-eval_script([{apply,{Mod,Fun,Args}}|CfgL],Init,PathFs,Vars,P,Ph,Deb,PathChoice) ->
- debug(Deb,{apply,{Mod,Fun,Args}}),
- apply(Mod,Fun,Args),
- eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice);
-eval_script([],_,_,_,_,_,_,_) ->
+ eval_script(T, Es);
+eval_script([{kernelProcess,Server,{Mod,Fun,Args}}|T],
+ #es{init=Init,debug=Deb}=Es) ->
+ debug(Deb, {start,Server}),
+ start_in_kernel(Server, Mod, Fun, Args, Init),
+ eval_script(T, Es);
+eval_script([{apply,{Mod,Fun,Args}}=Apply|T], #es{debug=Deb}=Es) ->
+ debug(Deb, Apply),
+ apply(Mod, Fun, Args),
+ eval_script(T, Es);
+eval_script([], #es{}) ->
ok;
-eval_script(What,_,_,_,_,_,_,_) ->
+eval_script(What, #es{}) ->
exit({'unexpected command in bootfile',What}).
-load_modules([Mod|Mods]) ->
- File = concat([Mod,objfile_extension()]),
- {ok,Full} = load_mod(Mod,File),
- init ! {self(),loaded,{Mod,Full}}, %% Tell init about loaded module
- load_modules(Mods);
-load_modules([]) ->
+load_modules(Mods0, Init) ->
+ Mods = [M || M <- Mods0, not erlang:module_loaded(M)],
+ F = prepare_loading_fun(),
+ case erl_prim_loader:get_modules(Mods, F) of
+ {ok,{Prep0,[]}} ->
+ Prep = [Code || {_,{prepared,Code,_}} <- Prep0],
+ ok = erlang:finish_loading(Prep),
+ Loaded = [{Mod,Full} || {Mod,{_,_,Full}} <- Prep0],
+ Init ! {self(),loaded,Loaded},
+ Beams = [{M,Beam,Full} || {M,{on_load,Beam,Full}} <- Prep0],
+ load_rest(Beams, Init);
+ {ok,{_,[_|_]=Errors}} ->
+ Ms = [M || {M,_} <- Errors],
+ exit({load_failed,Ms})
+ end.
+
+load_rest([{Mod,Beam,Full}|T], Init) ->
+ do_load_module(Mod, Beam),
+ Init ! {self(),loaded,[{Mod,Full}]},
+ load_rest(T, Init);
+load_rest([], _) ->
ok.
-%%% An optimization: erl_prim_loader gets the chance of loading many
-%%% files in parallel, using threads. This will reduce the seek times,
-%%% and loaded code can be processed while other threads are waiting
-%%% for the disk. The optimization is not tried unless the loader is
-%%% "efile" and there is a non-empty pool of threads.
-%%%
-%%% Many threads are needed to get a good result, so it would be
-%%% beneficial to load several applications in parallel. However,
-%%% measurements show that the file system handles one directory at a
-%%% time, regardless if parallel threads are created for files on
-%%% several directories (a guess: writing the meta information when
-%%% the file was last read ('mtime'), forces the file system to sync
-%%% between directories).
-
-par_load_modules(Mods,Init) ->
- Ext = objfile_extension(),
- ModFiles = [{Mod,concat([Mod,Ext])} || Mod <- Mods,
- not erlang:module_loaded(Mod)],
- Self = self(),
- Fun = fun(Mod, BinCode, FullName) ->
- case catch load_mod_code(Mod, BinCode, FullName) of
- {ok, _} ->
- Init ! {Self,loaded,{Mod,FullName}},
- ok;
- _EXIT ->
- {error, Mod}
- end
- end,
- case erl_prim_loader:get_files(ModFiles, Fun) of
- ok ->
- ok;
- {error,Mod} ->
- exit({'cannot load',Mod,get_files})
+prepare_loading_fun() ->
+ fun(Mod, FullName, Beam) ->
+ case erlang:prepare_loading(Mod, Beam) of
+ Prepared when is_binary(Prepared) ->
+ case erlang:has_prepared_code_on_load(Prepared) of
+ true ->
+ {ok,{on_load,Beam,FullName}};
+ false ->
+ {ok,{prepared,Prepared,FullName}}
+ end;
+ {error,_}=Error ->
+ Error
+ end
end.
make_path(Pa, Pz, Path, Vars) ->
@@ -908,34 +958,25 @@ fix_path([Path|Ps], Vars) ->
fix_path(_, _) ->
[].
-add_var("$ROOT/" ++ Path, {Root,_}) ->
- concat([Root, "/", Path]);
-add_var([$$|Path0], {_,VarList}) ->
- {Var,Path} = extract_var(Path0,[]),
- Value = b2s(get_var_value(list_to_binary(Var),VarList)),
- concat([Value, "/", Path]);
-add_var(Path, _) ->
+add_var("$"++Path0, Vars) ->
+ {Var,Path} = extract_var(Path0, []),
+ Key = list_to_binary(Var),
+ case Vars of
+ #{Key:=Value0} ->
+ Value = b2s(Value0),
+ Value ++ "/" ++ Path;
+ _ ->
+ Error0 = "cannot expand $" ++ Var ++ " in bootfile",
+ Error = list_to_atom(Error0),
+ exit(Error)
+ end;
+add_var(Path, _) ->
Path.
extract_var([$/|Path],Var) -> {reverse(Var),Path};
extract_var([H|T],Var) -> extract_var(T,[H|Var]);
extract_var([],Var) -> {reverse(Var),[]}.
-%% get_var_value(Var, [Vars]) where Vars == [atom()]
-get_var_value(Var,[Vars|VarList]) ->
- case get_var_val(Var,Vars) of
- {ok, Value} ->
- Value;
- _ ->
- get_var_value(Var,VarList)
- end;
-get_var_value(Var,[]) ->
- exit(list_to_atom(concat(["cannot expand \$", Var, " in bootfile"]))).
-
-get_var_val(Var,[Var,Value|_]) -> {ok, Value};
-get_var_val(Var,[_,_|Vars]) -> get_var_val(Var,Vars);
-get_var_val(_,_) -> false.
-
patch_path(Dirs, strict) ->
Dirs;
patch_path(Dirs, relaxed) ->
@@ -1049,49 +1090,23 @@ start_it({eval,Bin}) ->
{value, _Value, _Bs} = erl_eval:exprs(Expr, erl_eval:new_bindings()),
ok;
start_it([_|_]=MFA) ->
- Ref = make_ref(),
- case catch {Ref,case MFA of
- [M] -> M:start();
- [M,F] -> M:F();
- [M,F|Args] -> M:F(Args) % Args is a list
- end} of
- {Ref,R} ->
- R;
- {'EXIT',Reason} ->
- exit(Reason);
- Other ->
- throw(Other)
+ case MFA of
+ [M] -> M:start();
+ [M,F] -> M:F();
+ [M,F|Args] -> M:F(Args) % Args is a list
end.
-%%
-%% Fetch a module and load it into the system.
-%%
-load_mod(Mod, File) ->
- case erlang:module_loaded(Mod) of
- false ->
- case erl_prim_loader:get_file(File) of
- {ok,BinCode,FullName} ->
- load_mod_code(Mod, BinCode, FullName);
- _ ->
- exit({'cannot load',Mod,get_file})
- end;
- _ -> % Already loaded.
- {ok,File}
- end.
+%% Load a module.
-load_mod_code(Mod, BinCode, FullName) ->
- case erlang:module_loaded(Mod) of
- false ->
- case erlang:load_module(Mod, BinCode) of
- {module,Mod} -> {ok,FullName};
- {error,on_load} ->
- ?ON_LOAD_HANDLER ! {loaded,Mod},
- {ok,FullName};
- Other ->
- exit({'cannot load',Mod,Other})
- end;
- _ -> % Already loaded.
- {ok,FullName}
+do_load_module(Mod, BinCode) ->
+ case erlang:load_module(Mod, BinCode) of
+ {module,Mod} ->
+ ok;
+ {error,on_load} ->
+ ?ON_LOAD_HANDLER ! {loaded,Mod},
+ ok;
+ _ ->
+ error
end.
%% --------------------------------------------------------
@@ -1102,7 +1117,7 @@ load_mod_code(Mod, BinCode, FullName) ->
%% --------------------------------------------------------
shutdown_timer(Flags) ->
- case get_flag('-shutdown_time',Flags,infinity) of
+ case get_flag(shutdown_time, Flags, infinity) of
infinity ->
self();
Time ->
@@ -1152,14 +1167,10 @@ parse_boot_args([B|Bs], Ss, Fs, As) ->
eval_arg ->
{Expr,Rest} = get_args(Bs, []),
parse_boot_args(Rest, [{eval, Expr}|Ss], Fs, As);
- flag ->
+ {flag,A} ->
{F,Rest} = get_args(Bs, []),
- Fl = case F of
- [] -> [B];
- FF -> [B,FF]
- end,
- parse_boot_args(Rest, Ss,
- [list_to_tuple(Fl)|Fs], As);
+ Fl = {A,F},
+ parse_boot_args(Rest, Ss, [Fl|Fs], As);
arg ->
parse_boot_args(Bs, Ss, Fs, [B|As]);
end_args ->
@@ -1173,12 +1184,8 @@ check(<<"-s">>) -> start_arg;
check(<<"-run">>) -> start_arg2;
check(<<"-eval">>) -> eval_arg;
check(<<"--">>) -> end_args;
-check(X) when is_binary(X) ->
- case binary_to_list(X) of
- [$-|_Rest] -> flag;
- _Chars -> arg %Even empty atoms
- end;
-check(_X) -> arg. %This should never occur
+check(<<"-",Flag/binary>>) -> {flag,b2a(Flag)};
+check(_) -> arg.
get_args([B|Bs], As) ->
case check(B) of
@@ -1187,7 +1194,7 @@ get_args([B|Bs], As) ->
start_arg2 -> {reverse(As), [B|Bs]};
eval_arg -> {reverse(As), [B|Bs]};
end_args -> {reverse(As), Bs};
- flag -> {reverse(As), [B|Bs]};
+ {flag,_} -> {reverse(As), [B|Bs]};
arg ->
get_args(Bs, [B|As])
end;
@@ -1199,44 +1206,28 @@ get_args([], As) -> {reverse(As),[]}.
%% atom() if a single arg was given.
%% list(atom()) if several args were given.
%%
-get_flag(F,Flags,Default) ->
- case catch get_flag(F,Flags) of
- {'EXIT',_} ->
- Default;
- Value ->
- Value
- end.
-
-get_flag(F,Flags) ->
- case search(F,Flags) of
- {value,{F,[V]}} ->
+get_flag(F, Flags, Default) ->
+ case lists:keyfind(F, 1, Flags) of
+ {F,[]} ->
+ true;
+ {F,[V]} ->
V;
- {value,{F,V}} ->
+ {F,V} ->
V;
- {value,{F}} -> % Flag given!
- true;
_ ->
- exit(list_to_atom(concat(["no ",F," flag"])))
+ Default
end.
%%
%% Internal get_flag function, with default value.
%% Return: list(atom())
%%
-get_flag_list(F,Flags,Default) ->
- case catch get_flag_list(F,Flags) of
- {'EXIT',_} ->
- Default;
- Value ->
- Value
- end.
-
-get_flag_list(F,Flags) ->
- case search(F,Flags) of
- {value,{F,V}} ->
+get_flag_list(F, Flags, Default) ->
+ case lists:keyfind(F, 1, Flags) of
+ {F,[_|_]=V} ->
V;
_ ->
- exit(list_to_atom(concat(["no ",F," flag"])))
+ Default
end.
%%
@@ -1246,21 +1237,15 @@ get_flag_list(F,Flags) ->
%%
get_flag_args(F,Flags) -> get_flag_args(F,Flags,[]).
-get_flag_args(F,[{F,V}|Flags],Acc) when is_list(V) ->
- get_flag_args(F,Flags,[V|Acc]);
get_flag_args(F,[{F,V}|Flags],Acc) ->
- get_flag_args(F,Flags,[[V]|Acc]);
+ get_flag_args(F,Flags,[V|Acc]);
get_flag_args(F,[_|Flags],Acc) ->
get_flag_args(F,Flags,Acc);
get_flag_args(_,[],Acc) ->
reverse(Acc).
get_arguments([{F,V}|Flags]) ->
- [$-|Fl] = atom_to_list(F),
- [{list_to_atom(Fl),to_strings(V)}|get_arguments(Flags)];
-get_arguments([{F}|Flags]) ->
- [$-|Fl] = atom_to_list(F),
- [{list_to_atom(Fl),[]}|get_arguments(Flags)];
+ [{F,to_strings(V)}|get_arguments(Flags)];
get_arguments([]) ->
[].
@@ -1268,44 +1253,26 @@ to_strings([H|T]) when is_atom(H) -> [atom_to_list(H)|to_strings(T)];
to_strings([H|T]) when is_binary(H) -> [b2s(H)|to_strings(T)];
to_strings([]) -> [].
-get_argument(Arg,Flags) ->
- Args = get_arguments(Flags),
- case get_argument1(Arg,Args) of
- [] ->
- error;
- Value ->
- {ok,Value}
+get_argument(Arg, Flags) ->
+ case get_argument1(Arg, Flags) of
+ [] -> error;
+ Value -> {ok,Value}
end.
-get_argument1(Arg,[{Arg,V}|Args]) ->
- [V|get_argument1(Arg,Args)];
-get_argument1(Arg,[_|Args]) ->
- get_argument1(Arg,Args);
-get_argument1(_,[]) ->
+get_argument1(Arg, [{Arg,V}|Args]) ->
+ [to_strings(V)|get_argument1(Arg, Args)];
+get_argument1(Arg, [_|Args]) ->
+ get_argument1(Arg, Args);
+get_argument1(_, []) ->
[].
set_argument([{Flag,_}|Flags],Flag,Value) ->
[{Flag,[Value]}|Flags];
-set_argument([{Flag}|Flags],Flag,Value) ->
- [{Flag,[Value]}|Flags];
set_argument([Item|Flags],Flag,Value) ->
[Item|set_argument(Flags,Flag,Value)];
set_argument([],Flag,Value) ->
[{Flag,[Value]}].
-concat([A|T]) when is_atom(A) ->
- atom_to_list(A) ++ concat(T);
-concat([C|T]) when is_integer(C), 0 =< C, C =< 255 ->
- [C|concat(T)];
-concat([Bin|T]) when is_binary(Bin) ->
- binary_to_list(Bin) ++ concat(T);
-concat([S|T]) ->
- S ++ concat(T);
-concat([]) ->
- [].
-
-append(L, Z) -> L ++ Z.
-
append([E]) -> E;
append([H|T]) ->
H ++ append(T);
@@ -1320,13 +1287,6 @@ reverse([A, B]) ->
reverse([A, B | L]) ->
lists:reverse(L, [B, A]). % BIF
-search(Key, [H|_T]) when is_tuple(H), element(1, H) =:= Key ->
- {value, H};
-search(Key, [_|T]) ->
- search(Key, T);
-search(_Key, []) ->
- false.
-
-spec objfile_extension() -> nonempty_string().
objfile_extension() ->
".beam".
@@ -1402,3 +1362,64 @@ run_on_load_handlers([M|Ms], Debug) ->
end
end;
run_on_load_handlers([], _) -> ok.
+
+
+%% debug profile (light variant of eprof)
+debug_profile_start() ->
+ _ = erlang:trace_pattern({'_','_','_'},true,[call_time]),
+ _ = erlang:trace_pattern(on_load,true,[call_time]),
+ _ = erlang:trace(all,true,[call]),
+ ok.
+
+debug_profile_stop() ->
+ _ = erlang:trace_pattern({'_','_','_'},false,[call_time]),
+ _ = erlang:trace_pattern(on_load,false,[call_time]),
+ _ = erlang:trace(all,false,[call]),
+ ok.
+
+debug_profile_mfas() ->
+ _ = erlang:trace_pattern({'_','_','_'},pause,[call_time]),
+ _ = erlang:trace_pattern(on_load,pause,[call_time]),
+ MFAs = collect_loaded_mfas() ++ erlang:system_info(snifs),
+ collect_mfas(MFAs,[]).
+
+%% debug_profile_format_mfas should be called at the end of the boot phase
+%% so all pertinent modules should be loaded at that point.
+debug_profile_format_mfas(MFAs0) ->
+ MFAs = lists:sort(MFAs0),
+ lists:foreach(fun({{Us,C},{M,F,A}}) ->
+ Str = io_lib:format("~w:~w/~w", [M,F,A]),
+ io:format(standard_error,"~55s - ~6w : ~w us~n", [Str,C,Us])
+ end, MFAs),
+ ok.
+
+collect_loaded_mfas() ->
+ Ms = [M || M <- [element(1, Mi) || Mi <- code:all_loaded()]],
+ collect_loaded_mfas(Ms,[]).
+
+collect_loaded_mfas([],MFAs) -> MFAs;
+collect_loaded_mfas([M|Ms],MFAs0) ->
+ MFAs = [{M,F,A} || {F,A} <- M:module_info(functions)],
+ collect_loaded_mfas(Ms,MFAs ++ MFAs0).
+
+
+collect_mfas([], Info) -> Info;
+collect_mfas([MFA|MFAs],Info) ->
+ case erlang:trace_info(MFA,call_time) of
+ {call_time, []} ->
+ collect_mfas(MFAs,Info);
+ {call_time, false} ->
+ collect_mfas(MFAs,Info);
+ {call_time, Data} ->
+ case collect_mfa(MFA,Data,0,0) of
+ {{0,_},_} ->
+ %% ignore mfas with zero time
+ collect_mfas(MFAs,Info);
+ MfaData ->
+ collect_mfas(MFAs,[MfaData|Info])
+ end
+ end.
+
+collect_mfa(Mfa,[],Count,Time) -> {{Time,Count},Mfa};
+collect_mfa(Mfa,[{_Pid,C,S,Us}|Data],Count,Time) ->
+ collect_mfa(Mfa,Data,Count + C,Time + S * 1000000 + Us).
diff --git a/erts/preloaded/src/otp_ring0.erl b/erts/preloaded/src/otp_ring0.erl
index 3158fc7d21..62a60fffe2 100644
--- a/erts/preloaded/src/otp_ring0.erl
+++ b/erts/preloaded/src/otp_ring0.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/preloaded/src/prim_eval.S b/erts/preloaded/src/prim_eval.S
index 1b7b00a7c9..e4b1560517 100644
--- a/erts/preloaded/src/prim_eval.S
+++ b/erts/preloaded/src/prim_eval.S
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013. 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.
@@ -26,7 +26,7 @@
{attributes, []}.
-{labels, 10}.
+{labels, 14}.
{function, 'receive', 2, 2}.
@@ -36,6 +36,9 @@
{allocate,2,2}.
{move,{x,1},{y,0}}.
{move,{x,0},{y,1}}.
+ %% Call arg_reg_alloc() in order to ensure
+ %% that def_arg_reg[0] isn't clobbered
+ {call,0,{f,7}}.
{label,3}.
{loop_rec,{f,5},{x,0}}.
{move,{y,1},{x,1}}.
@@ -53,19 +56,43 @@
{deallocate,2}.
return.
-
-{function, module_info, 0, 8}.
+{function, arg_reg_alloc, 0, 7}.
{label,6}.
- {func_info,{atom,prim_eval},{atom,module_info},0}.
+ {func_info,{atom,prim_eval},{atom,arg_reg_alloc},0}.
{label,7}.
+ {allocate,0,0}.
+ {move,{integer,134217727},{x,0}}.
+ {call_ext,1,{extfunc,erlang,bump_reductions,1}}.
+ {move,{atom,true},{x,3}}.
+ {move,{atom,true},{x,4}}.
+ {move,{atom,true},{x,2}}.
+ {move,{atom,true},{x,5}}.
+ {move,{atom,true},{x,1}}.
+ {move,{atom,true},{x,6}}.
+ {move,{atom,true},{x,0}}.
+ {call_last,7,{f,9},0}.
+
+
+{function, arg_reg_alloc, 7, 9}.
+ {label,8}.
+ {func_info,{atom,prim_eval},{atom,arg_reg_alloc},7}.
+ {label,9}.
+ {move,{atom,ok},{x,0}}.
+ return.
+
+
+{function, module_info, 0, 11}.
+ {label,10}.
+ {func_info,{atom,prim_eval},{atom,module_info},0}.
+ {label,11}.
{move,{atom,prim_eval},{x,0}}.
{call_ext_only,1,{extfunc,erlang,get_module_info,1}}.
-{function, module_info, 1, 10}.
- {label,8}.
+{function, module_info, 1, 13}.
+ {label,12}.
{func_info,{atom,prim_eval},{atom,module_info},1}.
- {label,9}.
+ {label,13}.
{move,{x,0},{x,1}}.
{move,{atom,prim_eval},{x,0}}.
{call_ext_only,2,{extfunc,erlang,get_module_info,2}}.
diff --git a/erts/preloaded/src/prim_eval.erl b/erts/preloaded/src/prim_eval.erl
index 732e22468e..22e924f9e9 100644
--- a/erts/preloaded/src/prim_eval.erl
+++ b/erts/preloaded/src/prim_eval.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl
index bd74831bb7..61f727e8a4 100644
--- a/erts/preloaded/src/prim_inet.erl
+++ b/erts/preloaded/src/prim_inet.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -70,11 +70,13 @@ open(Protocol, Family, Type) ->
open(Protocol, Family, Type, Opts) ->
open(Protocol, Family, Type, Opts, ?INET_REQ_OPEN, []).
+%% FDOPEN(tcp|udp|sctp, inet|inet6|local, stream|dgram|seqpacket, integer())
+
fdopen(Protocol, Family, Type, Fd) when is_integer(Fd) ->
fdopen(Protocol, Family, Type, Fd, true).
fdopen(Protocol, Family, Type, Fd, Bound)
- when is_integer(Fd), Bound == true orelse Bound == false ->
+ when is_integer(Fd), is_boolean(Bound) ->
open(Protocol, Family, Type, [], ?INET_REQ_FDOPEN,
[?int32(Fd), enc_value_2(bool, Bound)]).
@@ -104,8 +106,9 @@ open(Protocol, Family, Type, Opts, Req, Data) ->
error:system_limit -> {error, system_limit}
end.
-enc_family(inet) -> ?INET_AF_INET;
-enc_family(inet6) -> ?INET_AF_INET6.
+enc_family(inet) -> ?INET_AF_INET;
+enc_family(inet6) -> ?INET_AF_INET6;
+enc_family(local) -> ?INET_AF_LOCAL.
enc_type(stream) -> ?INET_TYPE_STREAM;
enc_type(dgram) -> ?INET_TYPE_DGRAM;
@@ -189,41 +192,52 @@ close_port(S) ->
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-bind(S,IP,Port) when is_port(S), is_integer(Port), Port >= 0, Port =< 65535 ->
- case ctl_cmd(S,?INET_REQ_BIND,enc_value(set, addr, {IP,Port})) of
- {ok, [P1,P0]} -> {ok, ?u16(P1, P0)};
- {error,_}=Error -> Error
- end;
-
%% Multi-homed "bind": sctp_bindx(). The Op is 'add' or 'remove'.
%% If no addrs are specified, it just does nothing.
%% Function returns {ok, S} on success, unlike TCP/UDP "bind":
-bind(S, Op, Addrs) when is_port(S), is_list(Addrs) ->
- case Op of
- add ->
- bindx(S, 1, Addrs);
- remove ->
- bindx(S, 0, Addrs);
- _ -> {error, einval}
+bind(S, add, Addrs) when is_port(S), is_list(Addrs) ->
+ bindx(S, 1, Addrs);
+bind(S, remove, Addrs) when is_port(S), is_list(Addrs) ->
+ bindx(S, 0, Addrs);
+bind(S, Addr, _) when is_port(S), tuple_size(Addr) =:= 2 ->
+ case type_value(set, addr, Addr) of
+ true ->
+ case ctl_cmd(S,?INET_REQ_BIND,enc_value(set, addr, Addr)) of
+ {ok, [P1,P0]} -> {ok, ?u16(P1, P0)};
+ {error, _} = Error -> Error
+ end;
+ false ->
+ {error, einval}
end;
-bind(_, _, _) -> {error, einval}.
+bind(S, IP, Port) ->
+ bind(S, {IP, Port}, 0).
bindx(S, AddFlag, Addrs) ->
case getprotocol(S) of
sctp ->
- %% Really multi-homed "bindx". Stringified args:
- %% [AddFlag, (AddrBytes see enc_value_2(addr,X))+]:
- Args =
- [?int8(AddFlag)|
- [enc_value(set, addr, {IP,Port}) ||
- {IP, Port} <- Addrs]],
- case ctl_cmd(S, ?SCTP_REQ_BINDX, Args) of
- {ok,_} -> {ok, S};
- {error,_}=Error -> Error
+ case bindx_check_addrs(Addrs) of
+ true ->
+ %% Really multi-homed "bindx". Stringified args:
+ %% [AddFlag, (AddrBytes see enc_value_2(addr,X))+]:
+ Args =
+ [?int8(AddFlag)|
+ [enc_value(set, addr, Addr) || Addr <- Addrs]],
+ case ctl_cmd(S, ?SCTP_REQ_BINDX, Args) of
+ {ok, _} -> {ok, S};
+ {error, _}=Error -> Error
+ end;
+ false ->
+ {error, einval}
end;
- _ -> {error, einval}
+ _ ->
+ {error, einval}
end.
+bindx_check_addrs([Addr|Addrs]) ->
+ type_value(set, addr, Addr) andalso bindx_check_addrs(Addrs);
+bindx_check_addrs([]) ->
+ true.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% CONNECT(insock(), IP, Port [,Timeout]) -> ok | {error, Reason}
@@ -242,14 +256,24 @@ bindx(S, AddFlag, Addrs) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% For TCP, UDP or SCTP sockets.
%%
-connect(S, IP, Port) -> connect0(S, IP, Port, -1).
-connect(S, IP, Port, infinity) -> connect0(S, IP, Port, -1);
-connect(S, IP, Port, Time) -> connect0(S, IP, Port, Time).
+connect(S, IP, Port) ->
+ connect(S, IP, Port, infinity).
+%%
+connect(S, Addr, _, Time) when is_port(S), tuple_size(Addr) =:= 2 ->
+ case type_value(set, addr, Addr) of
+ true when Time =:= infinity ->
+ connect0(S, Addr, -1);
+ true when is_integer(Time) ->
+ connect0(S, Addr, Time);
+ false ->
+ {error, einval}
+ end;
+connect(S, IP, Port, Time) ->
+ connect(S, {IP, Port}, 0, Time).
-connect0(S, IP, Port, Time) when is_port(S), Port > 0, Port =< 65535,
- is_integer(Time) ->
- case async_connect(S, IP, Port, Time) of
+connect0(S, Addr, Time) ->
+ case async_connect0(S, Addr, Time) of
{ok, S, Ref} ->
receive
{inet_async, S, Ref, Status} ->
@@ -258,11 +282,27 @@ connect0(S, IP, Port, Time) when is_port(S), Port > 0, Port =< 65535,
Error -> Error
end.
+
+async_connect(S, Addr, _, Time) when is_port(S), tuple_size(Addr) =:= 2 ->
+ case type_value(set, addr, Addr) of
+ true when Time =:= infinity ->
+ async_connect0(S, Addr, -1);
+ true when is_integer(Time) ->
+ async_connect0(S, Addr, Time);
+ false ->
+ {error, einval}
+ end;
+%%
async_connect(S, IP, Port, Time) ->
- case ctl_cmd(S, ?INET_REQ_CONNECT,
- [enc_time(Time),?int16(Port),ip_to_bytes(IP)]) of
+ async_connect(S, {IP, Port}, 0, Time).
+
+async_connect0(S, Addr, Time) ->
+ case ctl_cmd(
+ S, ?INET_REQ_CONNECT,
+ [enc_time(Time),enc_value(set, addr, Addr)])
+ of
{ok, [R1,R0]} -> {ok, S, ?u16(R1,R0)};
- {error,_}=Error -> Error
+ {error, _}=Error -> Error
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -307,7 +347,17 @@ accept_opts(L, S) ->
case getopts(L, [active, nodelay, keepalive, delay_send, priority, tos]) of
{ok, Opts} ->
case setopts(S, Opts) of
- ok -> {ok, S};
+ ok ->
+ case getopts(L, [tclass]) of
+ {ok, []} ->
+ {ok, S};
+ {ok, TClassOpts} ->
+ case setopts(S, TClassOpts) of
+ ok ->
+ {ok, S};
+ Error -> close(S), Error
+ end
+ end;
Error -> close(S), Error
end;
Error ->
@@ -400,20 +450,34 @@ send(S, Data) ->
%% "sendto" is for UDP. IP and Port are set by the caller to 0 if the socket
%% is known to be connected.
-sendto(S, IP, Port, Data) when is_port(S), Port >= 0, Port =< 65535 ->
- ?DBG_FORMAT("prim_inet:sendto(~p, ~p, ~p, ~p)~n", [S,IP,Port,Data]),
- try erlang:port_command(S, [?int16(Port),ip_to_bytes(IP),Data]) of
- true ->
- receive
- {inet_reply,S,Reply} ->
- ?DBG_FORMAT("prim_inet:sendto() -> ~p~n", [Reply]),
- Reply
- end
- catch
- error:_ ->
- ?DBG_FORMAT("prim_inet:sendto() -> {error,einval}~n", []),
- {error,einval}
- end.
+sendto(S, Addr, _, Data) when is_port(S), tuple_size(Addr) =:= 2 ->
+ case type_value(set, addr, Addr) of
+ true ->
+ ?DBG_FORMAT("prim_inet:sendto(~p, ~p, ~p)~n", [S,Addr,Data]),
+ try
+ erlang:port_command(S, [enc_value(set, addr, Addr),Data])
+ of
+ true ->
+ receive
+ {inet_reply,S,Reply} ->
+ ?DBG_FORMAT(
+ "prim_inet:sendto() -> ~p~n", [Reply]),
+ Reply
+ end
+ catch
+ error:_ ->
+ ?DBG_FORMAT(
+ "prim_inet:sendto() -> {error,einval}~n", []),
+ {error,einval}
+ end;
+ false ->
+ ?DBG_FORMAT(
+ "prim_inet:sendto() -> {error,einval}~n", []),
+ {error,einval}
+ end;
+sendto(S, IP, Port, Data) ->
+ sendto(S, {IP, Port}, 0, Data).
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
@@ -484,29 +548,37 @@ async_recv(S, Length, Time) ->
%% oriented: preserved here only for API compatibility.
%%
recvfrom(S, Length) ->
- recvfrom0(S, Length, -1).
+ recvfrom(S, Length, infinity).
-recvfrom(S, Length, infinity) ->
+recvfrom(S, Length, infinity) when is_port(S) ->
recvfrom0(S, Length, -1);
-recvfrom(S, Length, Time) when is_integer(Time), Time < 16#ffffffff ->
- recvfrom0(S, Length, Time);
-recvfrom(_, _, _) -> {error,einval}.
+recvfrom(S, Length, Time) when is_port(S) ->
+ if
+ is_integer(Time), 0 =< Time, Time < 16#ffffffff ->
+ recvfrom0(S, Length, Time);
+ true ->
+ {error, einval}
+ end.
recvfrom0(S, Length, Time)
- when is_port(S), is_integer(Length), Length >= 0, Length =< 16#ffffffff ->
+ when is_integer(Length), 0 =< Length, Length =< 16#ffffffff ->
case ctl_cmd(S, ?PACKET_REQ_RECV,[enc_time(Time),?int32(Length)]) of
{ok,[R1,R0]} ->
Ref = ?u16(R1,R0),
receive
% Success, UDP:
- {inet_async, S, Ref, {ok, [F,P1,P0 | AddrData]}} ->
- {IP,Data} = get_ip(F, AddrData),
- {ok, {IP, ?u16(P1,P0), Data}};
+ {inet_async, S, Ref, {ok, [F | AddrData]}} ->
+ case get_addr(F, AddrData) of
+ {{Family, _} = Addr, Data} when is_atom(Family) ->
+ {ok, {Addr, 0, Data}};
+ {{IP, Port}, Data} ->
+ {ok, {IP, Port, Data}}
+ end;
% Success, SCTP:
{inet_async, S, Ref, {ok, {[F,P1,P0 | Addr], AncData, DE}}} ->
- {IP, _} = get_ip(F, Addr),
- {ok, {IP, ?u16(P1,P0), AncData, DE}};
+ {IP, _} = get_ip(F, Addr),
+ {ok, {IP, ?u16(P1, P0), AncData, DE}};
% Back-end error:
{inet_async, S, Ref, Error={error, _}} ->
@@ -525,21 +597,26 @@ recvfrom0(_, _, _) -> {error,einval}.
peername(S) when is_port(S) ->
case ctl_cmd(S, ?INET_REQ_PEER, []) of
- {ok, [F, P1,P0 | Addr]} ->
- {IP, _} = get_ip(F, Addr),
- {ok, { IP, ?u16(P1, P0) }};
- {error,_}=Error -> Error
+ {ok, [F | Addr]} ->
+ {A, _} = get_addr(F, Addr),
+ {ok, A};
+ {error, _} = Error -> Error
end.
-setpeername(S, {IP,Port}) when is_port(S) ->
- case ctl_cmd(S, ?INET_REQ_SETPEER, [?int16(Port),ip_to_bytes(IP)]) of
- {ok,[]} -> ok;
- {error,_}=Error -> Error
- end;
setpeername(S, undefined) when is_port(S) ->
case ctl_cmd(S, ?INET_REQ_SETPEER, []) of
- {ok,[]} -> ok;
- {error,_}=Error -> Error
+ {ok, []} -> ok;
+ {error, _} = Error -> Error
+ end;
+setpeername(S, Addr) when is_port(S) ->
+ case type_value(set, addr, Addr) of
+ true ->
+ case ctl_cmd(S, ?INET_REQ_SETPEER, enc_value(set, addr, Addr)) of
+ {ok, []} -> ok;
+ {error, _} = Error -> Error
+ end;
+ false ->
+ {error, einval}
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -580,21 +657,28 @@ peernames(S, AssocId)
sockname(S) when is_port(S) ->
case ctl_cmd(S, ?INET_REQ_NAME, []) of
- {ok, [F, P1, P0 | Addr]} ->
- {IP, _} = get_ip(F, Addr),
- {ok, { IP, ?u16(P1, P0) }};
- {error,_}=Error -> Error
+ {ok, [F | Addr]} ->
+ {A, _} = get_addr(F, Addr),
+ {ok, A};
+ {error, _} = Error -> Error
end.
-setsockname(S, {IP,Port}) when is_port(S) ->
- case ctl_cmd(S, ?INET_REQ_SETNAME, [?int16(Port),ip_to_bytes(IP)]) of
- {ok,[]} -> ok;
- {error,_}=Error -> Error
- end;
setsockname(S, undefined) when is_port(S) ->
case ctl_cmd(S, ?INET_REQ_SETNAME, []) of
- {ok,[]} -> ok;
- {error,_}=Error -> Error
+ {ok, []} -> ok;
+ {error, _} = Error -> Error
+ end;
+setsockname(S, Addr) when is_port(S) ->
+ case type_value(set, addr, Addr) of
+ true ->
+ case
+ ctl_cmd(S, ?INET_REQ_SETNAME, enc_value(set, addr, Addr))
+ of
+ {ok, []} -> ok;
+ {error, _} = Error -> Error
+ end;
+ false ->
+ {error, einval}
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1122,6 +1206,7 @@ enc_opt(sndbuf) -> ?INET_OPT_SNDBUF;
enc_opt(recbuf) -> ?INET_OPT_RCVBUF;
enc_opt(priority) -> ?INET_OPT_PRIORITY;
enc_opt(tos) -> ?INET_OPT_TOS;
+enc_opt(tclass) -> ?INET_OPT_TCLASS;
enc_opt(nodelay) -> ?TCP_OPT_NODELAY;
enc_opt(multicast_if) -> ?UDP_OPT_MULTICAST_IF;
enc_opt(multicast_ttl) -> ?UDP_OPT_MULTICAST_TTL;
@@ -1181,6 +1266,7 @@ dec_opt(?INET_OPT_SNDBUF) -> sndbuf;
dec_opt(?INET_OPT_RCVBUF) -> recbuf;
dec_opt(?INET_OPT_PRIORITY) -> priority;
dec_opt(?INET_OPT_TOS) -> tos;
+dec_opt(?INET_OPT_TCLASS) -> tclass;
dec_opt(?TCP_OPT_NODELAY) -> nodelay;
dec_opt(?UDP_OPT_MULTICAST_IF) -> multicast_if;
dec_opt(?UDP_OPT_MULTICAST_TTL) -> multicast_ttl;
@@ -1255,6 +1341,7 @@ type_opt_1(sndbuf) -> int;
type_opt_1(recbuf) -> int;
type_opt_1(priority) -> int;
type_opt_1(tos) -> int;
+type_opt_1(tclass) -> int;
type_opt_1(nodelay) -> bool;
type_opt_1(ipv6_v6only) -> bool;
%% multicast
@@ -1496,14 +1583,49 @@ type_value_2(uint8, X) when X band 16#ff =:= X -> true;
type_value_2(time, infinity) -> true;
type_value_2(time, X) when is_integer(X), X >= 0 -> true;
type_value_2(ip,{A,B,C,D}) when ?ip(A,B,C,D) -> true;
+%%
type_value_2(addr, {any,Port}) ->
type_value_2(uint16, Port);
type_value_2(addr, {loopback,Port}) ->
type_value_2(uint16, Port);
-type_value_2(addr, {{A,B,C,D},Port}) when ?ip(A,B,C,D) ->
+type_value_2(addr, {IP,_} = Addr) when tuple_size(IP) =:= 4 ->
+ type_value_2(addr, {inet,Addr});
+type_value_2(addr, {IP,_} = Addr) when tuple_size(IP) =:= 8 ->
+ type_value_2(addr, {inet6,Addr});
+type_value_2(addr, {Local,_}) when is_list(Local); is_binary(Local) ->
+ type_value_2(addr, {local,Local});
+%%
+type_value_2(addr, {Family,{Tag,Port}})
+ when (Family =:= inet orelse Family =:= inet6) andalso
+ (Tag =:= any orelse Tag =:= loopback) ->
type_value_2(uint16, Port);
-type_value_2(addr, {{A,B,C,D,E,F,G,H},Port}) when ?ip6(A,B,C,D,E,F,G,H) ->
+type_value_2(addr, {inet,{{A,B,C,D},Port}})
+ when ?ip(A,B,C,D) ->
type_value_2(uint16, Port);
+type_value_2(addr, {inet6,{{A,B,C,D,E,F,G,H},Port}})
+ when ?ip6(A,B,C,D,E,F,G,H) ->
+ type_value_2(uint16, Port);
+type_value_2(addr, {local,Addr}) ->
+ if
+ is_binary(Addr) ->
+ byte_size(Addr) =< 255;
+ true ->
+ try
+ %% We either get a badarg from byte_size
+ %% or from characters_to_binary
+ byte_size(
+ unicode:characters_to_binary(
+ Addr, file:native_name_encoding()))
+ of
+ N when N =< 255 ->
+ true;
+ _ ->
+ false
+ catch error:badarg ->
+ false
+ end
+ end;
+%%
type_value_2(ether,[X1,X2,X3,X4,X5,X6])
when ?ether(X1,X2,X3,X4,X5,X6) -> true;
type_value_2({enum,List}, Enum) ->
@@ -1611,6 +1733,7 @@ enc_value_2(time, Val) -> ?int32(Val);
enc_value_2(ip,{A,B,C,D}) -> [A,B,C,D];
enc_value_2(ip, any) -> [0,0,0,0];
enc_value_2(ip, loopback) -> [127,0,0,1];
+%%
enc_value_2(addr, {any,Port}) ->
[?INET_AF_ANY|?int16(Port)];
enc_value_2(addr, {loopback,Port}) ->
@@ -1619,6 +1742,35 @@ enc_value_2(addr, {IP,Port}) when tuple_size(IP) =:= 4 ->
[?INET_AF_INET,?int16(Port)|ip4_to_bytes(IP)];
enc_value_2(addr, {IP,Port}) when tuple_size(IP) =:= 8 ->
[?INET_AF_INET6,?int16(Port)|ip6_to_bytes(IP)];
+enc_value_2(addr, {File,_}) when is_list(File); is_binary(File) ->
+ [?INET_AF_LOCAL,iolist_size(File)|File];
+%%
+enc_value_2(addr, {inet,{any,Port}}) ->
+ [?INET_AF_INET,?int16(Port),0,0,0,0];
+enc_value_2(addr, {inet,{loopback,Port}}) ->
+ [?INET_AF_INET,?int16(Port),127,0,0,1];
+enc_value_2(addr, {inet,{IP,Port}}) ->
+ [?INET_AF_INET,?int16(Port)|ip4_to_bytes(IP)];
+enc_value_2(addr, {inet6,{any,Port}}) ->
+ [?INET_AF_INET6,?int16(Port),0,0,0,0,0,0,0,0];
+enc_value_2(addr, {inet6,{loopback,Port}}) ->
+ [?INET_AF_INET6,?int16(Port),0,0,0,0,0,0,0,1];
+enc_value_2(addr, {inet6,{IP,Port}}) ->
+ [?INET_AF_INET6,?int16(Port)|ip6_to_bytes(IP)];
+enc_value_2(addr, {local,Addr}) ->
+ %% A binary is passed as is, but anything else will be
+ %% regarded as a filename and therefore encoded according to
+ %% the current system filename encoding mode.
+ Bin =
+ if
+ is_binary(Addr) ->
+ Addr;
+ true ->
+ unicode:characters_to_binary(
+ Addr, file:native_name_encoding())
+ end,
+ [?INET_AF_LOCAL,byte_size(Bin),Bin];
+%%
enc_value_2(ether, [_,_,_,_,_,_]=Xs) -> Xs;
enc_value_2(sockaddr, any) ->
[?INET_AF_ANY];
@@ -2249,9 +2401,6 @@ utf8_to_characters(Bs, U, 0) ->
utf8_to_characters([B|Bs], U, N) when ((B band 16#3F) bor 16#80) =:= B ->
utf8_to_characters(Bs, (U bsl 6) bor (B band 16#3F), N-1).
-ip_to_bytes(IP) when tuple_size(IP) =:= 4 -> ip4_to_bytes(IP);
-ip_to_bytes(IP) when tuple_size(IP) =:= 8 -> ip6_to_bytes(IP).
-
ip4_to_bytes({A,B,C,D}) ->
[A band 16#ff, B band 16#ff, C band 16#ff, D band 16#ff].
@@ -2261,18 +2410,32 @@ ip6_to_bytes({A,B,C,D,E,F,G,H}) ->
get_addrs([]) ->
[];
-get_addrs([F,P1,P0|Addr]) ->
- {IP,Addrs} = get_ip(F, Addr),
- [{IP,?u16(P1, P0)}|get_addrs(Addrs)].
-
-get_ip(?INET_AF_INET, Addr) -> get_ip4(Addr);
-get_ip(?INET_AF_INET6, Addr) -> get_ip6(Addr).
+get_addrs([F|Addrs]) ->
+ {Addr,Rest} = get_addr(F, Addrs),
+ [Addr|get_addrs(Rest)].
+
+get_addr(?INET_AF_LOCAL, [N|Addr]) ->
+ {A,Rest} = lists:split(N, Addr),
+ {{local,iolist_to_binary(A)},Rest};
+get_addr(?INET_AF_UNSPEC, Rest) ->
+ {{unspec,<<>>},Rest};
+get_addr(?INET_AF_UNDEFINED, Rest) ->
+ {{undefined,<<>>},Rest};
+get_addr(Family, [P1,P0|Addr]) ->
+ {IP,Rest} = get_ip(Family, Addr),
+ {{IP,?u16(P1, P0)},Rest}.
+
+get_ip(?INET_AF_INET, Addr) ->
+ get_ip4(Addr);
+get_ip(?INET_AF_INET6, Addr) ->
+ get_ip6(Addr).
get_ip4([A,B,C,D | T]) -> {{A,B,C,D},T}.
get_ip6([X1,X2,X3,X4,X5,X6,X7,X8,X9,X10,X11,X12,X13,X14,X15,X16 | T]) ->
{ { ?u16(X1,X2),?u16(X3,X4),?u16(X5,X6),?u16(X7,X8),
- ?u16(X9,X10),?u16(X11,X12),?u16(X13,X14),?u16(X15,X16)}, T}.
+ ?u16(X9,X10),?u16(X11,X12),?u16(X13,X14),?u16(X15,X16)},
+ T }.
%% Control command
diff --git a/erts/preloaded/src/prim_zip.erl b/erts/preloaded/src/prim_zip.erl
index c4b949afcb..b1ddbbe173 100644
--- a/erts/preloaded/src/prim_zip.erl
+++ b/erts/preloaded/src/prim_zip.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/preloaded/src/zip_internal.hrl b/erts/preloaded/src/zip_internal.hrl
index d5cf52fae4..2769ca152d 100644
--- a/erts/preloaded/src/zip_internal.hrl
+++ b/erts/preloaded/src/zip_internal.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/preloaded/src/zlib.erl b/erts/preloaded/src/zlib.erl
index 473ad649c7..fa0f28c5c3 100644
--- a/erts/preloaded/src/zlib.erl
+++ b/erts/preloaded/src/zlib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/start_scripts/Makefile b/erts/start_scripts/Makefile
index 8025681924..ae2521474e 100644
--- a/erts/start_scripts/Makefile
+++ b/erts/start_scripts/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2013. All Rights Reserved.
+# Copyright Ericsson AB 1997-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,6 +17,9 @@
#
# %CopyrightEnd%
#
+
+.NOTPARALLEL:
+
include $(ERL_TOP)/make/target.mk
include $(ERL_TOP)/make/$(TARGET)/otp.mk
@@ -69,7 +72,7 @@ debug opt script: rel $(INSTALL_SCRIPTS) $(RELEASES_SRC)
rel: $(REL_SCRIPTS)
-RELEASES.src:
+RELEASES.src: $(SS_ROOT)/start_sasl.rel
$(gen_verbose)$(INSTALL_DIR) $(SS_TMP)
$(V_at)( cd $(SS_TMP) && \
$(ERL) -noinput +B -eval 'release_handler:create_RELEASES("%ERL_ROOT%", "$(SS_ROOT)", "$(SS_ROOT)/start_sasl.rel", []), halt()')
diff --git a/erts/start_scripts/no_dot_erlang.rel.src b/erts/start_scripts/no_dot_erlang.rel.src
index bcc9fa9e8a..04e5fbf741 100644
--- a/erts/start_scripts/no_dot_erlang.rel.src
+++ b/erts/start_scripts/no_dot_erlang.rel.src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/start_scripts/start_all_example.rel.src b/erts/start_scripts/start_all_example.rel.src
index 2c4deb4485..a44f3e2925 100644
--- a/erts/start_scripts/start_all_example.rel.src
+++ b/erts/start_scripts/start_all_example.rel.src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/start_scripts/start_clean.rel.src b/erts/start_scripts/start_clean.rel.src
index 25519deb17..ad468aa9df 100644
--- a/erts/start_scripts/start_clean.rel.src
+++ b/erts/start_scripts/start_clean.rel.src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/start_scripts/start_sasl.rel.src b/erts/start_scripts/start_sasl.rel.src
index 9cf417ade5..23b6a89e1d 100644
--- a/erts/start_scripts/start_sasl.rel.src
+++ b/erts/start_scripts/start_sasl.rel.src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/test/Makefile b/erts/test/Makefile
index 5263d8cd4f..1fe230adaf 100644
--- a/erts/test/Makefile
+++ b/erts/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2014. All Rights Reserved.
+# Copyright Ericsson AB 1997-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -54,7 +54,7 @@ RELSYSDIR = $(RELEASE_PATH)/system_test
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
-ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include
+ERL_COMPILE_FLAGS +=
# ----------------------------------------------------
# Targets
diff --git a/erts/test/erl_print_SUITE.erl b/erts/test/erl_print_SUITE.erl
index 3b0c083702..463d890688 100644
--- a/erts/test/erl_print_SUITE.erl
+++ b/erts/test/erl_print_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -28,153 +28,133 @@
-module(erl_print_SUITE).
-author('[email protected]').
+-export([all/0, suite/0, init_per_testcase/2, end_per_testcase/2]).
-%-define(line_trace, 1).
+-export([erlang_display/1, integer/1, float/1,
+ string/1, character/1, snprintf/1, quote/1]).
--define(DEFAULT_TIMEOUT, ?t:minutes(10)).
+-include_lib("common_test/include/ct.hrl").
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2, end_per_testcase/2]).
-
--export([erlang_display/1, integer/1, float/1,
- string/1, character/1, snprintf/1, quote/1]).
-
--include_lib("test_server/include/test_server.hrl").
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 10}}].
all() ->
- test_cases().
-
-groups() ->
- [].
+ [erlang_display, integer, float, string, character,
+ snprintf, quote].
-init_per_suite(Config) ->
- Config.
+init_per_testcase(Case, Config) ->
+ [{testcase, Case}|Config].
-end_per_suite(_Config) ->
+end_per_testcase(_Case, _Config) ->
ok.
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
%%
%%
%% Test cases
%%
%%
-test_cases() ->
- [erlang_display, integer, float, string, character,
- snprintf, quote].
-
-erlang_display(doc) -> [];
-erlang_display(suite) -> [];
erlang_display(Config) when is_list(Config) ->
- ?line put(erlang_display_test, ok),
+ put(erlang_display_test, ok),
OAIS = erts_debug:set_internal_state(available_internal_state, true),
%% atoms
- ?line chk_display(atom, "atom"),
- ?line chk_display(true, "true"),
- ?line chk_display(false, "false"),
- ?line chk_display('DOWN', "'DOWN'"),
- ?line chk_display('EXIT', "'EXIT'"),
- ?line chk_display('asdDofw $@{}][', "'asdDofw $@{}]['"),
+ chk_display(atom, "atom"),
+ chk_display(true, "true"),
+ chk_display(false, "false"),
+ chk_display('DOWN', "'DOWN'"),
+ chk_display('EXIT', "'EXIT'"),
+ chk_display('asdDofw $@{}][', "'asdDofw $@{}]['"),
%% integers
- ?line chk_display(0, "0"),
- ?line chk_display(1, "1"),
- ?line chk_display(4711, "4711"),
- ?line chk_display(((1 bsl 27) - 1), "134217727"),
- ?line chk_display((1 bsl 27), "134217728"),
- ?line chk_display((1 bsl 32), "4294967296"),
- ?line chk_display(11111111111, "11111111111"),
- ?line chk_display((1 bsl 59) - 1, "576460752303423487"),
- ?line chk_display(1 bsl 59, "576460752303423488"),
- ?line chk_display(111111111111111111111, "111111111111111111111"),
- ?line chk_display(123456789012345678901234567890,
- "123456789012345678901234567890"),
- ?line chk_display(1 bsl 10000, str_1_bsl_10000()),
- ?line chk_display(-1, "-1"),
- ?line chk_display(-4711, "-4711"),
- ?line chk_display(-(1 bsl 27), "-134217728"),
- ?line chk_display(-((1 bsl 27) + 1), "-134217729"),
- ?line chk_display(-(1 bsl 32), "-4294967296"),
- ?line chk_display(-11111111111, "-11111111111"),
- ?line chk_display(-(1 bsl 59), "-576460752303423488"),
- ?line chk_display(-((1 bsl 59) + 1), "-576460752303423489"),
- ?line chk_display(-111111111111111111111, "-111111111111111111111"),
- ?line chk_display(-123456789012345678901234567890,
- "-123456789012345678901234567890"),
- ?line chk_display(-(1 bsl 10000), [$- | str_1_bsl_10000()]),
-
- ?line MyCre = my_cre(),
+ chk_display(0, "0"),
+ chk_display(1, "1"),
+ chk_display(4711, "4711"),
+ chk_display(((1 bsl 27) - 1), "134217727"),
+ chk_display((1 bsl 27), "134217728"),
+ chk_display((1 bsl 32), "4294967296"),
+ chk_display(11111111111, "11111111111"),
+ chk_display((1 bsl 59) - 1, "576460752303423487"),
+ chk_display(1 bsl 59, "576460752303423488"),
+ chk_display(111111111111111111111, "111111111111111111111"),
+ chk_display(123456789012345678901234567890,
+ "123456789012345678901234567890"),
+ chk_display(1 bsl 10000, str_1_bsl_10000()),
+ chk_display(-1, "-1"),
+ chk_display(-4711, "-4711"),
+ chk_display(-(1 bsl 27), "-134217728"),
+ chk_display(-((1 bsl 27) + 1), "-134217729"),
+ chk_display(-(1 bsl 32), "-4294967296"),
+ chk_display(-11111111111, "-11111111111"),
+ chk_display(-(1 bsl 59), "-576460752303423488"),
+ chk_display(-((1 bsl 59) + 1), "-576460752303423489"),
+ chk_display(-111111111111111111111, "-111111111111111111111"),
+ chk_display(-123456789012345678901234567890,
+ "-123456789012345678901234567890"),
+ chk_display(-(1 bsl 10000), [$- | str_1_bsl_10000()]),
+
+ MyCre = my_cre(),
%% pids
- ?line chk_display(mk_pid_xstr({node(), MyCre}, 4711, 42)),
- ?line chk_display(mk_pid_xstr({node(), oth_cre(MyCre)}, 4711, 42)),
- ?line chk_display(mk_pid_xstr({node(), oth_cre(oth_cre(MyCre))}, 4711, 42)),
+ chk_display(mk_pid_xstr({node(), MyCre}, 4711, 42)),
+ chk_display(mk_pid_xstr({node(), oth_cre(MyCre)}, 4711, 42)),
+ chk_display(mk_pid_xstr({node(), oth_cre(oth_cre(MyCre))}, 4711, 42)),
- ?line chk_display(mk_pid_xstr({a@b, MyCre}, 4711, 42)),
- ?line chk_display(mk_pid_xstr({a@b, oth_cre(MyCre)}, 4711, 42)),
- ?line chk_display(mk_pid_xstr({a@b, oth_cre(oth_cre(MyCre))}, 4711, 42)),
+ chk_display(mk_pid_xstr({a@b, MyCre}, 4711, 42)),
+ chk_display(mk_pid_xstr({a@b, oth_cre(MyCre)}, 4711, 42)),
+ chk_display(mk_pid_xstr({a@b, oth_cre(oth_cre(MyCre))}, 4711, 42)),
%% ports
- ?line chk_display(mk_port_xstr({node(), MyCre}, 4711)),
- ?line chk_display(mk_port_xstr({node(), oth_cre(MyCre)}, 4711)),
- ?line chk_display(mk_port_xstr({node(), oth_cre(oth_cre(MyCre))}, 4711)),
+ chk_display(mk_port_xstr({node(), MyCre}, 4711)),
+ chk_display(mk_port_xstr({node(), oth_cre(MyCre)}, 4711)),
+ chk_display(mk_port_xstr({node(), oth_cre(oth_cre(MyCre))}, 4711)),
- ?line chk_display(mk_port_xstr({c@d, MyCre}, 4711)),
- ?line chk_display(mk_port_xstr({c@d, oth_cre(MyCre)}, 4711)),
- ?line chk_display(mk_port_xstr({c@d, oth_cre(oth_cre(MyCre))}, 4711)),
+ chk_display(mk_port_xstr({c@d, MyCre}, 4711)),
+ chk_display(mk_port_xstr({c@d, oth_cre(MyCre)}, 4711)),
+ chk_display(mk_port_xstr({c@d, oth_cre(oth_cre(MyCre))}, 4711)),
%% refs
- ?line chk_display(mk_ref_xstr({node(), MyCre}, [1,2,3])),
- ?line chk_display(mk_ref_xstr({node(), oth_cre(MyCre)}, [1,2,3])),
- ?line chk_display(mk_ref_xstr({node(), oth_cre(oth_cre(MyCre))}, [1,2,3])),
+ chk_display(mk_ref_xstr({node(), MyCre}, [1,2,3])),
+ chk_display(mk_ref_xstr({node(), oth_cre(MyCre)}, [1,2,3])),
+ chk_display(mk_ref_xstr({node(), oth_cre(oth_cre(MyCre))}, [1,2,3])),
- ?line chk_display(mk_ref_xstr({e@f, MyCre},[1,2,3] )),
- ?line chk_display(mk_ref_xstr({e@f, oth_cre(MyCre)}, [1,2,3])),
- ?line chk_display(mk_ref_xstr({e@f, oth_cre(oth_cre(MyCre))}, [1,2,3])),
+ chk_display(mk_ref_xstr({e@f, MyCre},[1,2,3] )),
+ chk_display(mk_ref_xstr({e@f, oth_cre(MyCre)}, [1,2,3])),
+ chk_display(mk_ref_xstr({e@f, oth_cre(oth_cre(MyCre))}, [1,2,3])),
%% Compund terms
- ?line {Pid, PidStr} = mk_pid_xstr({x@y, oth_cre(MyCre)}, 4712, 41),
- ?line {Port, PortStr} = mk_port_xstr({x@y, oth_cre(MyCre)}, 4712),
- ?line {Ref, RefStr} = mk_ref_xstr({e@f, oth_cre(MyCre)}, [11,12,13]),
-
- ?line chk_display({atom,-4711,Ref,{"hej",[Pid,222222222222222222222222,Port,4711]}},
- "{atom,-4711,"++RefStr++",{\"hej\",["++PidStr++",222222222222222222222222,"++PortStr++",4711]}}"),
- ?line chk_display({{{{{{{{{{{{{{{{{{{{{{{hi}}}}}}}}}}}}}}}}}}}}}}},
- "{{{{{{{{{{{{{{{{{{{{{{{hi}}}}}}}}}}}}}}}}}}}}}}}"),
- ?line chk_display([[[[[[[[[[[[[[[[[[[[[[[yo]]]]]]]]]]]]]]]]]]]]]]],
- "[[[[[[[[[[[[[[[[[[[[[[[yo]]]]]]]]]]]]]]]]]]]]]]]"),
- ?line chk_display({[{[{[{[{[{[{[{[{[{[{[{[ii]}]}]}]}]}]}]}]}]}]}]}]},
- "{[{[{[{[{[{[{[{[{[{[{[{[ii]}]}]}]}]}]}]}]}]}]}]}]}"),
- ?line chk_display([], "[]"), % Not really a compound term :)
- ?line chk_display([a|b], "[a|b]"),
- ?line chk_display([a,b,c|z], "[a,b,c|z]"),
- ?line chk_display([a,b,c], "[a,b,c]"),
- ?line chk_display([Pid,Port,Ref],
- "["++PidStr++","++PortStr++","++RefStr++"]"),
- ?line chk_display("abcdefghijklmnopqrstuvwxyz",
- "\"abcdefghijklmnopqrstuvwxyz\""),
- ?line chk_display("ABCDEFGHIJKLMNOPQRSTUVWXYZ",
- "\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\""),
- ?line chk_display("H E J", "\"H E J\""),
- ?line chk_display("asdDofw $@{}][", "\"asdDofw $@{}][\""),
-
+ {Pid, PidStr} = mk_pid_xstr({x@y, oth_cre(MyCre)}, 4712, 41),
+ {Port, PortStr} = mk_port_xstr({x@y, oth_cre(MyCre)}, 4712),
+ {Ref, RefStr} = mk_ref_xstr({e@f, oth_cre(MyCre)}, [11,12,13]),
+
+ chk_display({atom,-4711,Ref,{"hej",[Pid,222222222222222222222222,Port,4711]}},
+ "{atom,-4711,"++RefStr++",{\"hej\",["++PidStr++",222222222222222222222222,"++PortStr++",4711]}}"),
+ chk_display({{{{{{{{{{{{{{{{{{{{{{{hi}}}}}}}}}}}}}}}}}}}}}}},
+ "{{{{{{{{{{{{{{{{{{{{{{{hi}}}}}}}}}}}}}}}}}}}}}}}"),
+ chk_display([[[[[[[[[[[[[[[[[[[[[[[yo]]]]]]]]]]]]]]]]]]]]]]],
+ "[[[[[[[[[[[[[[[[[[[[[[[yo]]]]]]]]]]]]]]]]]]]]]]]"),
+ chk_display({[{[{[{[{[{[{[{[{[{[{[{[ii]}]}]}]}]}]}]}]}]}]}]}]},
+ "{[{[{[{[{[{[{[{[{[{[{[{[ii]}]}]}]}]}]}]}]}]}]}]}]}"),
+ chk_display([], "[]"), % Not really a compound term :)
+ chk_display([a|b], "[a|b]"),
+ chk_display([a,b,c|z], "[a,b,c|z]"),
+ chk_display([a,b,c], "[a,b,c]"),
+ chk_display([Pid,Port,Ref],
+ "["++PidStr++","++PortStr++","++RefStr++"]"),
+ chk_display("abcdefghijklmnopqrstuvwxyz",
+ "\"abcdefghijklmnopqrstuvwxyz\""),
+ chk_display("ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+ "\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\""),
+ chk_display("H E J", "\"H E J\""),
+ chk_display("asdDofw $@{}][", "\"asdDofw $@{}][\""),
+
%%
%% TODO: Check binaries, fun and floats...
%%
erts_debug:set_internal_state(available_internal_state, OAIS),
- ?line ok = get(erlang_display_test).
+ ok = get(erlang_display_test).
get_chnl_no(NodeName) when is_atom(NodeName) ->
erts_debug:get_internal_state({channel_number, NodeName}).
@@ -182,20 +162,20 @@ get_chnl_no(NodeName) when is_atom(NodeName) ->
chk_display(Term, Expect) when is_list(Expect) ->
Dstr = erts_debug:display(Term),
case Expect ++ io_lib:nl() of
- Dstr ->
- ?t:format("Test of \"~p\" succeeded.~n"
- " Expected and got: ~s~n",
- [Term, io_lib:write_string(Dstr)]);
- DoExpect ->
- ?t:format("***~n"
- "*** Test of \"~p\" failed!~n"
- "*** Expected: ~s~n"
- "*** Got: ~s~n"
- "***~n",
- [Term,
- io_lib:write_string(DoExpect),
- io_lib:write_string(Dstr)]),
- put(erlang_display_test, failed)
+ Dstr ->
+ io:format("Test of \"~p\" succeeded.~n"
+ " Expected and got: ~s~n",
+ [Term, io_lib:write_string(Dstr)]);
+ DoExpect ->
+ io:format("***~n"
+ "*** Test of \"~p\" failed!~n"
+ "*** Expected: ~s~n"
+ "*** Got: ~s~n"
+ "***~n",
+ [Term,
+ io_lib:write_string(DoExpect),
+ io_lib:write_string(Dstr)]),
+ put(erlang_display_test, failed)
end.
chk_display({Term, Expect}) ->
@@ -204,20 +184,20 @@ chk_display({Term, Expect}) ->
mk_pid_xstr({NodeName, Creation}, Number, Serial) ->
Pid = mk_pid({NodeName, Creation}, Number, Serial),
XStr = "<" ++ integer_to_list(get_chnl_no(NodeName))
- ++ "." ++ integer_to_list(Number)
- ++ "." ++ integer_to_list(Serial) ++ ">",
+ ++ "." ++ integer_to_list(Number)
+ ++ "." ++ integer_to_list(Serial) ++ ">",
{Pid, XStr}.
mk_port_xstr({NodeName, Creation}, Number) ->
Port = mk_port({NodeName, Creation}, Number),
XStr = "#Port<" ++ integer_to_list(get_chnl_no(NodeName))
- ++ "." ++ integer_to_list(Number) ++ ">",
+ ++ "." ++ integer_to_list(Number) ++ ">",
{Port, XStr}.
mk_ref_xstr({NodeName, Creation}, Numbers) ->
Ref = mk_ref({NodeName, Creation}, Numbers),
XStr = "#Ref<" ++ integer_to_list(get_chnl_no(NodeName))
- ++ ref_numbers_xstr(Numbers) ++ ">",
+ ++ ref_numbers_xstr(Numbers) ++ ">",
{Ref, XStr}.
ref_numbers_xstr([]) ->
@@ -240,18 +220,7 @@ ref_numbers_xstr([N | Ns]) ->
%%
%%
-default_testcase_impl(doc) -> [];
-default_testcase_impl(suite) -> [];
-default_testcase_impl(Config) when is_list(Config) -> ?line run_case(Config).
-
-init_per_testcase(Case, Config) ->
- Dog = ?t:timetrap(?DEFAULT_TIMEOUT),
- [{testcase, Case}, {watchdog, Dog} |Config].
-
-end_per_testcase(_Case, Config) ->
- Dog = ?config(watchdog, Config),
- ?t:timetrap_cancel(Dog),
- ok.
+default_testcase_impl(Config) when is_list(Config) -> run_case(Config).
-define(TESTPROG, "erl_print_tests").
-define(FAILED_MARKER, $E,$P,$-,$T,$E,$S,$T,$-,$F,$A,$I,$L,$U,$R,$E).
@@ -260,62 +229,62 @@ end_per_testcase(_Case, Config) ->
-define(PID_MARKER, $E,$P,$-,$T,$E,$S,$T,$-,$P,$I,$D).
port_prog_killer(EProc, OSProc) when is_pid(EProc), is_list(OSProc) ->
- ?line process_flag(trap_exit, true),
- ?line Ref = erlang:monitor(process, EProc),
- ?line receive
- {'DOWN', Ref, _, _, Reason} when is_tuple(Reason),
- element(1, Reason)
- == timetrap_timeout ->
- ?line Cmd = "kill -9 " ++ OSProc,
- ?line ?t:format("Test case timed out. "
- "Trying to kill port program.~n"
- " Executing: ~p~n", [Cmd]),
- ?line case os:cmd(Cmd) of
- [] ->
- ok;
- OsCmdRes ->
- ?line ?t:format(" ~s", [OsCmdRes])
- end;
- {'DOWN', Ref, _, _, _} ->
- %% OSProc is assumed to have terminated by itself
- ?line ok
- end.
+ process_flag(trap_exit, true),
+ Ref = erlang:monitor(process, EProc),
+ receive
+ {'DOWN', Ref, _, _, Reason} when is_tuple(Reason),
+ element(1, Reason)
+ == timetrap_timeout ->
+ Cmd = "kill -9 " ++ OSProc,
+ io:format("Test case timed out. "
+ "Trying to kill port program.~n"
+ " Executing: ~p~n", [Cmd]),
+ case os:cmd(Cmd) of
+ [] ->
+ ok;
+ OsCmdRes ->
+ io:format(" ~s", [OsCmdRes])
+ end;
+ {'DOWN', Ref, _, _, _} ->
+ %% OSProc is assumed to have terminated by itself
+ ok
+ end.
get_line(_Port, eol, Data) ->
- ?line Data;
+ Data;
get_line(Port, noeol, Data) ->
- ?line receive
- {Port, {data, {Flag, NextData}}} ->
- ?line get_line(Port, Flag, Data ++ NextData);
- {Port, eof} ->
- ?line ?t:fail(port_prog_unexpectedly_closed)
- end.
+ receive
+ {Port, {data, {Flag, NextData}}} ->
+ get_line(Port, Flag, Data ++ NextData);
+ {Port, eof} ->
+ ct:fail(port_prog_unexpectedly_closed)
+ end.
read_case_data(Port, TestCase) ->
- ?line receive
- {Port, {data, {eol, [?SUCCESS_MARKER]}}} ->
- ?line ok;
- {Port, {data, {Flag, [?SUCCESS_MARKER | CommentStart]}}} ->
- ?line {comment, get_line(Port, Flag, CommentStart)};
- {Port, {data, {Flag, [?SKIPPED_MARKER | CommentStart]}}} ->
- ?line {skipped, get_line(Port, Flag, CommentStart)};
- {Port, {data, {Flag, [?FAILED_MARKER | ReasonStart]}}} ->
- ?line ?t:fail(get_line(Port, Flag, ReasonStart));
- {Port, {data, {eol, [?PID_MARKER | PidStr]}}} ->
- ?line ?t:format("Port program pid: ~s~n", [PidStr]),
- ?line CaseProc = self(),
- ?line _ = list_to_integer(PidStr), % Sanity check
- spawn_opt(fun () ->
- port_prog_killer(CaseProc, PidStr)
- end,
- [{priority, max}, link]),
- read_case_data(Port, TestCase);
- {Port, {data, {Flag, LineStart}}} ->
- ?line ?t:format("~s~n", [get_line(Port, Flag, LineStart)]),
- read_case_data(Port, TestCase);
- {Port, eof} ->
- ?line ?t:fail(port_prog_unexpectedly_closed)
- end.
+ receive
+ {Port, {data, {eol, [?SUCCESS_MARKER]}}} ->
+ ok;
+ {Port, {data, {Flag, [?SUCCESS_MARKER | CommentStart]}}} ->
+ {comment, get_line(Port, Flag, CommentStart)};
+ {Port, {data, {Flag, [?SKIPPED_MARKER | CommentStart]}}} ->
+ {skipped, get_line(Port, Flag, CommentStart)};
+ {Port, {data, {Flag, [?FAILED_MARKER | ReasonStart]}}} ->
+ ct:fail(get_line(Port, Flag, ReasonStart));
+ {Port, {data, {eol, [?PID_MARKER | PidStr]}}} ->
+ io:format("Port program pid: ~s~n", [PidStr]),
+ CaseProc = self(),
+ _ = list_to_integer(PidStr), % Sanity check
+ spawn_opt(fun () ->
+ port_prog_killer(CaseProc, PidStr)
+ end,
+ [{priority, max}, link]),
+ read_case_data(Port, TestCase);
+ {Port, {data, {Flag, LineStart}}} ->
+ io:format("~s~n", [get_line(Port, Flag, LineStart)]),
+ read_case_data(Port, TestCase);
+ {Port, eof} ->
+ ct:fail(port_prog_unexpectedly_closed)
+ end.
run_case(Config) ->
run_case(Config, "").
@@ -324,27 +293,27 @@ run_case(Config, TestArgs) ->
run_case(Config, TestArgs, fun (_Port) -> ok end).
run_case(Config, TestArgs, Fun) ->
- Test = atom_to_list(?config(testcase, Config)),
- TestProg = filename:join([?config(data_dir, Config),
- ?TESTPROG
- ++ "."
- ++ atom_to_list(erlang:system_info(threads))]),
+ Test = atom_to_list(proplists:get_value(testcase, Config)),
+ TestProg = filename:join([proplists:get_value(data_dir, Config),
+ ?TESTPROG
+ ++ "."
+ ++ atom_to_list(erlang:system_info(threads))]),
Cmd = TestProg ++ " " ++ Test ++ " " ++ TestArgs,
case catch open_port({spawn, Cmd}, [stream,
- use_stdio,
- stderr_to_stdout,
- eof,
- {line, 1024}]) of
- Port when is_port(Port) ->
- ?line Fun(Port),
- ?line CaseResult = read_case_data(Port, Test),
- ?line receive
- {Port, eof} ->
- ?line ok
- end,
- ?line CaseResult;
- Error ->
- ?line ?t:fail({open_port_failed, Error})
+ use_stdio,
+ stderr_to_stdout,
+ eof,
+ {line, 1024}]) of
+ Port when is_port(Port) ->
+ Fun(Port),
+ CaseResult = read_case_data(Port, Test),
+ receive
+ {Port, eof} ->
+ ok
+ end,
+ CaseResult;
+ Error ->
+ ct:fail({open_port_failed, Error})
end.
@@ -382,80 +351,80 @@ mk_pid({NodeName, Creation}, Number, Serial) when is_atom(NodeName) ->
mk_pid({atom_to_list(NodeName), Creation}, Number, Serial);
mk_pid({NodeName, Creation}, Number, Serial) ->
case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
- ?PID_EXT,
- ?ATOM_EXT,
- uint16_be(length(NodeName)),
- NodeName,
- uint32_be(Number),
- uint32_be(Serial),
- uint8(Creation)])) of
- Pid when is_pid(Pid) ->
- Pid;
- {'EXIT', {badarg, _}} ->
- exit({badarg, mk_pid, [{NodeName, Creation}, Number, Serial]});
- Other ->
- exit({unexpected_binary_to_term_result, Other})
+ ?PID_EXT,
+ ?ATOM_EXT,
+ uint16_be(length(NodeName)),
+ NodeName,
+ uint32_be(Number),
+ uint32_be(Serial),
+ uint8(Creation)])) of
+ Pid when is_pid(Pid) ->
+ Pid;
+ {'EXIT', {badarg, _}} ->
+ exit({badarg, mk_pid, [{NodeName, Creation}, Number, Serial]});
+ Other ->
+ exit({unexpected_binary_to_term_result, Other})
end.
mk_port({NodeName, Creation}, Number) when is_atom(NodeName) ->
mk_port({atom_to_list(NodeName), Creation}, Number);
mk_port({NodeName, Creation}, Number) ->
case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
- ?PORT_EXT,
- ?ATOM_EXT,
- uint16_be(length(NodeName)),
- NodeName,
- uint32_be(Number),
- uint8(Creation)])) of
- Port when is_port(Port) ->
- Port;
- {'EXIT', {badarg, _}} ->
- exit({badarg, mk_port, [{NodeName, Creation}, Number]});
- Other ->
- exit({unexpected_binary_to_term_result, Other})
+ ?PORT_EXT,
+ ?ATOM_EXT,
+ uint16_be(length(NodeName)),
+ NodeName,
+ uint32_be(Number),
+ uint8(Creation)])) of
+ Port when is_port(Port) ->
+ Port;
+ {'EXIT', {badarg, _}} ->
+ exit({badarg, mk_port, [{NodeName, Creation}, Number]});
+ Other ->
+ exit({unexpected_binary_to_term_result, Other})
end.
mk_ref({NodeName, Creation}, Numbers) when is_atom(NodeName),
- is_integer(Creation),
- is_list(Numbers) ->
+ is_integer(Creation),
+ is_list(Numbers) ->
mk_ref({atom_to_list(NodeName), Creation}, Numbers);
mk_ref({NodeName, Creation}, [Number]) when is_list(NodeName),
- is_integer(Creation),
- is_integer(Number) ->
+ is_integer(Creation),
+ is_integer(Number) ->
case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
- ?REFERENCE_EXT,
- ?ATOM_EXT,
- uint16_be(length(NodeName)),
- NodeName,
- uint32_be(Number),
- uint8(Creation)])) of
- Ref when is_reference(Ref) ->
- Ref;
- {'EXIT', {badarg, _}} ->
- exit({badarg, mk_ref, [{NodeName, Creation}, [Number]]});
- Other ->
- exit({unexpected_binary_to_term_result, Other})
+ ?REFERENCE_EXT,
+ ?ATOM_EXT,
+ uint16_be(length(NodeName)),
+ NodeName,
+ uint32_be(Number),
+ uint8(Creation)])) of
+ Ref when is_reference(Ref) ->
+ Ref;
+ {'EXIT', {badarg, _}} ->
+ exit({badarg, mk_ref, [{NodeName, Creation}, [Number]]});
+ Other ->
+ exit({unexpected_binary_to_term_result, Other})
end;
mk_ref({NodeName, Creation}, Numbers) when is_list(NodeName),
- is_integer(Creation),
- is_list(Numbers) ->
+ is_integer(Creation),
+ is_list(Numbers) ->
case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
- ?NEW_REFERENCE_EXT,
- uint16_be(length(Numbers)),
- ?ATOM_EXT,
- uint16_be(length(NodeName)),
- NodeName,
- uint8(Creation),
- lists:map(fun (N) ->
- uint32_be(N)
- end,
- Numbers)])) of
- Ref when is_reference(Ref) ->
- Ref;
- {'EXIT', {badarg, _}} ->
- exit({badarg, mk_ref, [{NodeName, Creation}, Numbers]});
- Other ->
- exit({unexpected_binary_to_term_result, Other})
+ ?NEW_REFERENCE_EXT,
+ uint16_be(length(Numbers)),
+ ?ATOM_EXT,
+ uint16_be(length(NodeName)),
+ NodeName,
+ uint8(Creation),
+ lists:map(fun (N) ->
+ uint32_be(N)
+ end,
+ Numbers)])) of
+ Ref when is_reference(Ref) ->
+ Ref;
+ {'EXIT', {badarg, _}} ->
+ exit({badarg, mk_ref, [{NodeName, Creation}, Numbers]});
+ Other ->
+ exit({unexpected_binary_to_term_result, Other})
end.
my_cre() -> erlang:system_info(creation).
diff --git a/erts/test/erl_print_SUITE_data/Makefile.src b/erts/test/erl_print_SUITE_data/Makefile.src
index e6ea5cc6b9..69ff434c56 100644
--- a/erts/test/erl_print_SUITE_data/Makefile.src
+++ b/erts/test/erl_print_SUITE_data/Makefile.src
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2012. All Rights Reserved.
+# Copyright Ericsson AB 2005-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/test/erl_print_SUITE_data/character_test.h b/erts/test/erl_print_SUITE_data/character_test.h
index 9ff032cb07..82310ee8e7 100644
--- a/erts/test/erl_print_SUITE_data/character_test.h
+++ b/erts/test/erl_print_SUITE_data/character_test.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/test/erl_print_SUITE_data/erl_print_tests.c b/erts/test/erl_print_SUITE_data/erl_print_tests.c
index fb23dc35a6..2fb7d1ff25 100644
--- a/erts/test/erl_print_SUITE_data/erl_print_tests.c
+++ b/erts/test/erl_print_SUITE_data/erl_print_tests.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/test/erl_print_SUITE_data/integer_64_test.h b/erts/test/erl_print_SUITE_data/integer_64_test.h
index 0c3e7b98a8..4bfc91334d 100644
--- a/erts/test/erl_print_SUITE_data/integer_64_test.h
+++ b/erts/test/erl_print_SUITE_data/integer_64_test.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/test/erl_print_SUITE_data/integer_test.h b/erts/test/erl_print_SUITE_data/integer_test.h
index b91f3622d6..b3744928b7 100644
--- a/erts/test/erl_print_SUITE_data/integer_test.h
+++ b/erts/test/erl_print_SUITE_data/integer_test.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/test/erl_print_SUITE_data/snprintf_test.h b/erts/test/erl_print_SUITE_data/snprintf_test.h
index c612a65521..77692304a3 100644
--- a/erts/test/erl_print_SUITE_data/snprintf_test.h
+++ b/erts/test/erl_print_SUITE_data/snprintf_test.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/test/erl_print_SUITE_data/string_test.h b/erts/test/erl_print_SUITE_data/string_test.h
index 0e257888e6..bfe4215d8a 100644
--- a/erts/test/erl_print_SUITE_data/string_test.h
+++ b/erts/test/erl_print_SUITE_data/string_test.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/test/erlc_SUITE.erl b/erts/test/erlc_SUITE.erl
index c21064fd3f..237558a129 100644
--- a/erts/test/erlc_SUITE.erl
+++ b/erts/test/erlc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -22,12 +22,12 @@
%% Tests the erlc command by compiling various types of files.
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2, compile_erl/1,
- compile_yecc/1, compile_script/1,
- compile_mib/1, good_citizen/1, deep_cwd/1, arg_overflow/1,
- make_dep_options/1]).
+ init_per_group/2,end_per_group/2, compile_erl/1,
+ compile_yecc/1, compile_script/1,
+ compile_mib/1, good_citizen/1, deep_cwd/1, arg_overflow/1,
+ make_dep_options/1]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -57,113 +57,109 @@ end_per_group(_GroupName, Config) ->
%% Tests that compiling Erlang source code works.
compile_erl(Config) when is_list(Config) ->
- ?line {SrcDir, OutDir, Cmd} = get_cmd(Config),
- ?line FileName = filename:join(SrcDir, "erl_test_ok.erl"),
+ {SrcDir, OutDir, Cmd} = get_cmd(Config),
+ FileName = filename:join(SrcDir, "erl_test_ok.erl"),
%% By default, warnings are now turned on.
- ?line run(Config, Cmd, FileName, "",
- ["Warning: function foo/0 is unused\$",
- "_OK_"]),
+ run(Config, Cmd, FileName, "",
+ ["Warning: function foo/0 is unused\$", "_OK_"]),
%% Test that the compiled file is where it should be,
%% and that it is runnable.
- ?line {module, erl_test_ok} = code:load_abs(filename:join(OutDir,
- "erl_test_ok")),
- ?line 42 = erl_test_ok:shoe_size(#person{shoe_size=42}),
- ?line code:purge(erl_test_ok),
+ {module, erl_test_ok} = code:load_abs(filename:join(OutDir, "erl_test_ok")),
+ 42 = erl_test_ok:shoe_size(#person{shoe_size=42}),
+ code:purge(erl_test_ok),
%% Try disabling warnings.
- ?line run(Config, Cmd, FileName, "-W0", ["_OK_"]),
+ run(Config, Cmd, FileName, "-W0", ["_OK_"]),
%% Try treating warnings as errors.
- ?line run(Config, Cmd, FileName, "-Werror",
- ["compile: warnings being treated as errors\$",
- "function foo/0 is unused\$",
- "_ERROR_"]),
+ run(Config, Cmd, FileName, "-Werror",
+ ["compile: warnings being treated as errors\$",
+ "function foo/0 is unused\$", "_ERROR_"]),
%% Check a bad file.
- ?line BadFile = filename:join(SrcDir, "erl_test_bad.erl"),
- ?line run(Config, Cmd, BadFile, "", ["function non_existing/1 undefined\$",
- "_ERROR_"]),
+ BadFile = filename:join(SrcDir, "erl_test_bad.erl"),
+ run(Config, Cmd, BadFile, "", ["function non_existing/1 undefined\$",
+ "_ERROR_"]),
ok.
%% Test that compiling yecc source code works.
compile_yecc(Config) when is_list(Config) ->
- ?line {SrcDir, _, OutDir} = get_dirs(Config),
- ?line Cmd = erlc() ++ " -o" ++ OutDir ++ " ",
- ?line FileName = filename:join(SrcDir, "yecc_test_ok.yrl"),
- ?line run(Config, Cmd, FileName, "-W0", ["_OK_"]),
- ?line true = exists(filename:join(OutDir, "yecc_test_ok.erl")),
-
- ?line BadFile = filename:join(SrcDir, "yecc_test_bad.yrl"),
- ?line run(Config, Cmd, BadFile, "-W0",
- ["rootsymbol form is not a nonterminal\$",
- "undefined nonterminal: form\$",
- "Nonterminals is missing\$",
- "_ERROR_"]),
- ?line exists(filename:join(OutDir, "yecc_test_ok.erl")),
-
+ {SrcDir, _, OutDir} = get_dirs(Config),
+ Cmd = erlc() ++ " -o" ++ OutDir ++ " ",
+ FileName = filename:join(SrcDir, "yecc_test_ok.yrl"),
+ run(Config, Cmd, FileName, "-W0", ["_OK_"]),
+ true = exists(filename:join(OutDir, "yecc_test_ok.erl")),
+
+ BadFile = filename:join(SrcDir, "yecc_test_bad.yrl"),
+ run(Config, Cmd, BadFile, "-W0",
+ ["rootsymbol form is not a nonterminal\$",
+ "undefined nonterminal: form\$",
+ "Nonterminals is missing\$",
+ "_ERROR_"]),
+ exists(filename:join(OutDir, "yecc_test_ok.erl")),
ok.
%% Test that compiling start scripts works.
compile_script(Config) when is_list(Config) ->
- ?line {SrcDir, OutDir, Cmd} = get_cmd(Config),
- ?line FileName = filename:join(SrcDir, "start_ok.script"),
- ?line run(Config, Cmd, FileName, "", ["_OK_"]),
- ?line true = exists(filename:join(OutDir, "start_ok.boot")),
+ {SrcDir, OutDir, Cmd} = get_cmd(Config),
+ FileName = filename:join(SrcDir, "start_ok.script"),
+ run(Config, Cmd, FileName, "", ["_OK_"]),
+ true = exists(filename:join(OutDir, "start_ok.boot")),
- ?line BadFile = filename:join(SrcDir, "start_bad.script"),
- ?line run(Config, Cmd, BadFile, "", ["syntax error before:", "_ERROR_"]),
+ BadFile = filename:join(SrcDir, "start_bad.script"),
+ run(Config, Cmd, BadFile, "", ["syntax error before:", "_ERROR_"]),
ok.
%% Test that compiling SNMP mibs works.
compile_mib(Config) when is_list(Config) ->
- ?line {SrcDir, OutDir, Cmd} = get_cmd(Config),
- ?line FileName = filename:join(SrcDir, "GOOD-MIB.mib"),
- ?line run(Config, Cmd, FileName, "", ["_OK_"]),
- ?line Output = filename:join(OutDir, "GOOD-MIB.bin"),
- ?line true = exists(Output),
+ {SrcDir, OutDir, Cmd} = get_cmd(Config),
+ FileName = filename:join(SrcDir, "GOOD-MIB.mib"),
+ run(Config, Cmd, FileName, "", ["_OK_"]),
+ Output = filename:join(OutDir, "GOOD-MIB.bin"),
+ true = exists(Output),
%% Try -W option.
- ?line ok = file:delete(Output),
- ?line run(Config, Cmd, FileName, "-W",
- ["_OK_"]),
- ?line true = exists(Output),
+ ok = file:delete(Output),
+ run(Config, Cmd, FileName, "-W",
+ ["_OK_"]),
+ true = exists(Output),
%% Try -W option and more verbose.
- ?line ok = file:delete(Output),
- ?line case test_server:os_type() of
- {unix,_} ->
- ?line run(Config, Cmd, FileName, "-W +'{verbosity,info}'",
- ["\\[GOOD-MIB[.]mib\\]\\[INF\\]: No accessfunction for 'sysDescr' => using default",
- "_OK_"]),
- ?line true = exists(Output),
- ?line ok = file:delete(Output);
- _ -> ok %Don't bother -- too much work.
- end,
+ ok = file:delete(Output),
+ case test_server:os_type() of
+ {unix,_} ->
+ run(Config, Cmd, FileName, "-W +'{verbosity,info}'",
+ ["\\[GOOD-MIB[.]mib\\]\\[INF\\]: No accessfunction for 'sysDescr' => using default",
+ "_OK_"]),
+ true = exists(Output),
+ ok = file:delete(Output);
+ _ -> ok %Don't bother -- too much work.
+ end,
%% Try a bad file.
- ?line BadFile = filename:join(SrcDir, "BAD-MIB.mib"),
- ?line run(Config, Cmd, BadFile, "",
- ["BAD-MIB.mib: 1: syntax error before: mibs\$",
- "compilation_failed_ERROR_"]),
+ BadFile = filename:join(SrcDir, "BAD-MIB.mib"),
+ run(Config, Cmd, BadFile, "",
+ ["BAD-MIB.mib: 1: syntax error before: mibs\$",
+ "compilation_failed_ERROR_"]),
%% Make sure that no -I option works.
- ?line NewCmd = erlc() ++ " -o" ++ OutDir ++ " ",
- ?line run(Config, NewCmd, FileName, "", ["_OK_"]),
- ?line true = exists(Output),
+ NewCmd = erlc() ++ " -o" ++ OutDir ++ " ",
+ run(Config, NewCmd, FileName, "", ["_OK_"]),
+ true = exists(Output),
ok.
@@ -171,91 +167,91 @@ compile_mib(Config) when is_list(Config) ->
%% shell script with redirected input).
good_citizen(Config) when is_list(Config) ->
case os:type() of
- {unix, _} ->
- ?line PrivDir = ?config(priv_dir, Config),
- ?line Answer = filename:join(PrivDir, "answer"),
- ?line Script = filename:join(PrivDir, "test_script"),
- ?line Test = filename:join(PrivDir, "test.erl"),
- ?line S = ["#! /bin/sh\n", "erlc ", Test, "\n",
- "read reply\n", "echo $reply\n"],
- ?line ok = file:write_file(Script, S),
- ?line ok = file:write_file(Test, "-module(test).\n"),
- ?line Cmd = "echo y | sh " ++ Script ++ " > " ++ Answer,
- ?line os:cmd(Cmd),
- ?line {ok, Answer0} = file:read_file(Answer),
- ?line [$y|_] = binary_to_list(Answer0),
- ok;
- _ ->
- {skip, "Unix specific"}
+ {unix, _} ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ Answer = filename:join(PrivDir, "answer"),
+ Script = filename:join(PrivDir, "test_script"),
+ Test = filename:join(PrivDir, "test.erl"),
+ S = ["#! /bin/sh\n", "erlc ", Test, "\n",
+ "read reply\n", "echo $reply\n"],
+ ok = file:write_file(Script, S),
+ ok = file:write_file(Test, "-module(test).\n"),
+ Cmd = "echo y | sh " ++ Script ++ " > " ++ Answer,
+ os:cmd(Cmd),
+ {ok, Answer0} = file:read_file(Answer),
+ [$y|_] = binary_to_list(Answer0),
+ ok;
+ _ ->
+ {skip, "Unix specific"}
end.
%% Make sure that compiling an Erlang module deep down in
%% in a directory with more than 255 characters works.
deep_cwd(Config) when is_list(Config) ->
case os:type() of
- {unix, _} ->
- PrivDir = ?config(priv_dir, Config),
- deep_cwd_1(PrivDir);
- _ ->
- {skip, "Only a problem on Unix"}
+ {unix, _} ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ deep_cwd_1(PrivDir);
+ _ ->
+ {skip, "Only a problem on Unix"}
end.
deep_cwd_1(PrivDir) ->
- ?line DeepDir0 = filename:join(PrivDir, lists:duplicate(128, $a)),
- ?line DeepDir = filename:join(DeepDir0, lists:duplicate(128, $b)),
- ?line ok = file:make_dir(DeepDir0),
- ?line ok = file:make_dir(DeepDir),
- ?line ok = file:set_cwd(DeepDir),
- ?line ok = file:write_file("test.erl", "-module(test).\n\n"),
- ?line io:format("~s\n", [os:cmd("erlc test.erl")]),
- ?line true = filelib:is_file("test.beam"),
+ DeepDir0 = filename:join(PrivDir, lists:duplicate(128, $a)),
+ DeepDir = filename:join(DeepDir0, lists:duplicate(128, $b)),
+ ok = file:make_dir(DeepDir0),
+ ok = file:make_dir(DeepDir),
+ ok = file:set_cwd(DeepDir),
+ ok = file:write_file("test.erl", "-module(test).\n\n"),
+ io:format("~s\n", [os:cmd("erlc test.erl")]),
+ true = filelib:is_file("test.beam"),
ok.
%% Test that a large number of command line switches does not
%% overflow the argument buffer
arg_overflow(Config) when is_list(Config) ->
- ?line {SrcDir, _OutDir, Cmd} = get_cmd(Config),
- ?line FileName = filename:join(SrcDir, "erl_test_ok.erl"),
+ {SrcDir, _OutDir, Cmd} = get_cmd(Config),
+ FileName = filename:join(SrcDir, "erl_test_ok.erl"),
%% Each -D option will be expanded to three arguments when
%% invoking 'erl'.
- ?line NumDOptions = num_d_options(),
- ?line Args = lists:flatten([ ["-D", integer_to_list(N, 36), "=1 "] ||
- N <- lists:seq(1, NumDOptions) ]),
- ?line run(Config, Cmd, FileName, Args,
- ["Warning: function foo/0 is unused\$",
- "_OK_"]),
+ NumDOptions = num_d_options(),
+ Args = lists:flatten([ ["-D", integer_to_list(N, 36), "=1 "] ||
+ N <- lists:seq(1, NumDOptions) ]),
+ run(Config, Cmd, FileName, Args,
+ ["Warning: function foo/0 is unused\$",
+ "_OK_"]),
ok.
num_d_options() ->
case {os:type(),os:version()} of
- {{win32,_},_} ->
- %% The maximum size of a command line in the command
- %% shell on Windows is 8191 characters.
- %% Each -D option is expanded to "@dv NN 1", i.e.
- %% 8 characters. (Numbers up to 1295 can be expressed
- %% as two 36-base digits.)
- 1000;
- {{unix,linux},Version} when Version < {2,6,23} ->
- %% On some older 64-bit versions of Linux, the maximum number
- %% of arguments is 16383.
- %% See: http://www.in-ulm.de/~mascheck/various/argmax/
- 5440;
- {{unix,darwin},{Major,_,_}} when Major >= 11 ->
- %% "getconf ARG_MAX" still reports 262144 (as in previous
- %% version of MacOS X), but the useful space seem to have
- %% shrunk significantly (or possibly the number of arguments).
- %% 7673
- 7500;
- {_,_} ->
- 12000
+ {{win32,_},_} ->
+ %% The maximum size of a command line in the command
+ %% shell on Windows is 8191 characters.
+ %% Each -D option is expanded to "@dv NN 1", i.e.
+ %% 8 characters. (Numbers up to 1295 can be expressed
+ %% as two 36-base digits.)
+ 1000;
+ {{unix,linux},Version} when Version < {2,6,23} ->
+ %% On some older 64-bit versions of Linux, the maximum number
+ %% of arguments is 16383.
+ %% See: http://www.in-ulm.de/~mascheck/various/argmax/
+ 5440;
+ {{unix,darwin},{Major,_,_}} when Major >= 11 ->
+ %% "getconf ARG_MAX" still reports 262144 (as in previous
+ %% version of MacOS X), but the useful space seem to have
+ %% shrunk significantly (or possibly the number of arguments).
+ %% 7673
+ 7500;
+ {_,_} ->
+ 12000
end.
erlc() ->
case os:find_executable("erlc") of
- false ->
- test_server:fail("Can't find erlc");
- Erlc ->
- "\"" ++ Erlc ++ "\""
+ false ->
+ ct:fail("Can't find erlc");
+ Erlc ->
+ "\"" ++ Erlc ++ "\""
end.
make_dep_options(Config) ->
@@ -264,30 +260,30 @@ make_dep_options(Config) ->
DepRE = ["/erl_test_ok[.]beam: \\\\$",
- "/system_test/erlc_SUITE_data/src/erl_test_ok[.]erl \\\\$",
- "/system_test/erlc_SUITE_data/include/erl_test[.]hrl$",
- "_OK_"],
+ "/system_test/erlc_SUITE_data/src/erl_test_ok[.]erl \\\\$",
+ "/system_test/erlc_SUITE_data/include/erl_test[.]hrl$",
+ "_OK_"],
DepRETarget =
- ["^target: \\\\$",
- "/system_test/erlc_SUITE_data/src/erl_test_ok[.]erl \\\\$",
- "/system_test/erlc_SUITE_data/include/erl_test[.]hrl$",
- "_OK_"],
+ ["^target: \\\\$",
+ "/system_test/erlc_SUITE_data/src/erl_test_ok[.]erl \\\\$",
+ "/system_test/erlc_SUITE_data/include/erl_test[.]hrl$",
+ "_OK_"],
DepREMP =
- ["/erl_test_ok[.]beam: \\\\$",
- "/system_test/erlc_SUITE_data/src/erl_test_ok[.]erl \\\\$",
- "/system_test/erlc_SUITE_data/include/erl_test[.]hrl$",
- [],
- "/system_test/erlc_SUITE_data/include/erl_test.hrl:$",
- "_OK_"],
+ ["/erl_test_ok[.]beam: \\\\$",
+ "/system_test/erlc_SUITE_data/src/erl_test_ok[.]erl \\\\$",
+ "/system_test/erlc_SUITE_data/include/erl_test[.]hrl$",
+ [],
+ "/system_test/erlc_SUITE_data/include/erl_test.hrl:$",
+ "_OK_"],
DepREMissing =
- ["/erl_test_missing_header[.]beam: \\\\$",
- "/system_test/erlc_SUITE_data/src/erl_test_missing_header[.]erl \\\\$",
- "/system_test/erlc_SUITE_data/include/erl_test[.]hrl \\\\$",
- "missing.hrl$",
- "_OK_"],
+ ["/erl_test_missing_header[.]beam: \\\\$",
+ "/system_test/erlc_SUITE_data/src/erl_test_missing_header[.]erl \\\\$",
+ "/system_test/erlc_SUITE_data/include/erl_test[.]hrl \\\\$",
+ "missing.hrl$",
+ "_OK_"],
%% Test plain -M
run(Config, Cmd, FileName, "-M", DepRE),
@@ -309,7 +305,7 @@ make_dep_options(Config) ->
%% Test -MF File -MT Target
TargetDepFile = filename:join(OutDir, "target.deps"),
run(Config, Cmd, FileName, "-MF "++TargetDepFile++" -MT target",
- ["_OK_"]),
+ ["_OK_"]),
{ok,TargetBin} = file:read_file(TargetDepFile),
verify_result(binary_to_list(TargetBin)++["_OK_"], DepRETarget),
@@ -358,33 +354,33 @@ split([], Current, Lines) ->
match_messages([Msg|Rest1], [Regexp|Rest2]) ->
case re:run(Msg, Regexp, [{capture,none}, unicode]) of
- match ->
- ok;
- nomatch ->
- io:format("Not matching: ~s\n", [Msg]),
- io:format("Regexp : ~s\n", [Regexp]),
- test_server:fail(message_mismatch)
+ match ->
+ ok;
+ nomatch ->
+ io:format("Not matching: ~s\n", [Msg]),
+ io:format("Regexp : ~s\n", [Regexp]),
+ ct:fail(message_mismatch)
end,
match_messages(Rest1, Rest2);
match_messages([], [Expect|Rest]) ->
- test_server:fail({too_few_messages, [Expect|Rest]});
+ ct:fail({too_few_messages, [Expect|Rest]});
match_messages([Msg|Rest], []) ->
- test_server:fail({too_many_messages, [Msg|Rest]});
+ ct:fail({too_many_messages, [Msg|Rest]});
match_messages([], []) ->
ok.
get_cmd(Cfg) ->
- ?line {SrcDir, IncDir, OutDir} = get_dirs(Cfg),
- ?line Cmd = erlc() ++ " -I" ++ IncDir ++ " -o" ++ OutDir ++ " ",
+ {SrcDir, IncDir, OutDir} = get_dirs(Cfg),
+ Cmd = erlc() ++ " -I" ++ IncDir ++ " -o" ++ OutDir ++ " ",
{SrcDir, OutDir, Cmd}.
get_dirs(Cfg) ->
- ?line DataDir = ?config(data_dir, Cfg),
- ?line PrivDir = ?config(priv_dir, Cfg),
- ?line SrcDir = filename:join(DataDir, "src"),
- ?line IncDir = filename:join(DataDir, "include"),
+ DataDir = proplists:get_value(data_dir, Cfg),
+ PrivDir = proplists:get_value(priv_dir, Cfg),
+ SrcDir = filename:join(DataDir, "src"),
+ IncDir = filename:join(DataDir, "include"),
{SrcDir, IncDir, PrivDir}.
-
+
exists(Name) ->
filelib:is_file(Name).
@@ -396,7 +392,7 @@ exists(Name) ->
%% a non-zero exit status.
run_command(Config, Cmd) ->
- TmpDir = filename:join(?config(priv_dir, Config), "tmp"),
+ TmpDir = filename:join(proplists:get_value(priv_dir, Config), "tmp"),
file:make_dir(TmpDir),
{RunFile, Run, Script} = run_command(TmpDir, os:type(), Cmd),
ok = file:write_file(filename:join(TmpDir, RunFile), unicode:characters_to_binary(Script)),
@@ -405,7 +401,7 @@ run_command(Config, Cmd) ->
run_command(Dir, {win32, _}, Cmd) ->
BatchFile = filename:join(Dir, "run.bat"),
Run = re:replace(filename:rootname(BatchFile), "/", "\\",
- [global,{return,list}]),
+ [global,{return,list}]),
{BatchFile,
Run,
["@echo off\r\n",
@@ -426,5 +422,4 @@ run_command(Dir, {unix, _}, Cmd) ->
" *) echo '_ERROR_';;\n",
"esac\n"]};
run_command(_Dir, Other, _Cmd) ->
- M = io_lib:format("Don't know how to test exit code for ~p", [Other]),
- test_server:fail(lists:flatten(M)).
+ ct:fail("Don't know how to test exit code for ~p", [Other]).
diff --git a/erts/test/erlc_SUITE_data/include/erl_test.hrl b/erts/test/erlc_SUITE_data/include/erl_test.hrl
index e7d096d2c1..70aecc4762 100644
--- a/erts/test/erlc_SUITE_data/include/erl_test.hrl
+++ b/erts/test/erlc_SUITE_data/include/erl_test.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/test/erlc_SUITE_data/src/erl_test_bad.erl b/erts/test/erlc_SUITE_data/src/erl_test_bad.erl
index b8c4ee2786..cbfe81705f 100644
--- a/erts/test/erlc_SUITE_data/src/erl_test_bad.erl
+++ b/erts/test/erlc_SUITE_data/src/erl_test_bad.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/test/erlc_SUITE_data/src/erl_test_missing_header.erl b/erts/test/erlc_SUITE_data/src/erl_test_missing_header.erl
index f043fbebc4..604bb16bd6 100644
--- a/erts/test/erlc_SUITE_data/src/erl_test_missing_header.erl
+++ b/erts/test/erlc_SUITE_data/src/erl_test_missing_header.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/test/erlc_SUITE_data/src/erl_test_ok.erl b/erts/test/erlc_SUITE_data/src/erl_test_ok.erl
index a82eda95b3..f1a48cbf18 100644
--- a/erts/test/erlc_SUITE_data/src/erl_test_ok.erl
+++ b/erts/test/erlc_SUITE_data/src/erl_test_ok.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/test/erlc_SUITE_data/src/yecc_test_bad.yrl b/erts/test/erlc_SUITE_data/src/yecc_test_bad.yrl
index de17b903d6..da488b7232 100644
--- a/erts/test/erlc_SUITE_data/src/yecc_test_bad.yrl
+++ b/erts/test/erlc_SUITE_data/src/yecc_test_bad.yrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/test/erlc_SUITE_data/src/yecc_test_ok.yrl b/erts/test/erlc_SUITE_data/src/yecc_test_ok.yrl
index 9433dcb90d..feb067e34b 100644
--- a/erts/test/erlc_SUITE_data/src/yecc_test_ok.yrl
+++ b/erts/test/erlc_SUITE_data/src/yecc_test_ok.yrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/test/erlexec_SUITE.erl b/erts/test/erlexec_SUITE.erl
index 9279872d25..44d7f63387 100644
--- a/erts/test/erlexec_SUITE.erl
+++ b/erts/test/erlexec_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -27,71 +27,46 @@
%%%-------------------------------------------------------------------
-module(erlexec_SUITE).
+-export([all/0, suite/0, init_per_testcase/2, end_per_testcase/2]).
-%-define(line_trace, 1).
+-export([args_file/1, evil_args_file/1, env/1, args_file_env/1,
+ otp_7461/1, otp_7461_remote/1, otp_8209/1,
+ zdbbl_dist_buf_busy_limit/1]).
--define(DEFAULT_TIMEOUT, ?t:minutes(1)).
-
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2, end_per_testcase/2]).
-
--export([args_file/1, evil_args_file/1, env/1, args_file_env/1, otp_7461/1, otp_7461_remote/1, otp_8209/1, zdbbl_dist_buf_busy_limit/1]).
-
--include_lib("test_server/include/test_server.hrl").
-
+-include_lib("common_test/include/ct.hrl").
init_per_testcase(Case, Config) ->
- Dog = ?t:timetrap(?DEFAULT_TIMEOUT),
SavedEnv = save_env(),
- [{testcase, Case}, {watchdog, Dog}, {erl_flags_env, SavedEnv} |Config].
+ [{testcase, Case},{erl_flags_env, SavedEnv}|Config].
end_per_testcase(_Case, Config) ->
- Dog = ?config(watchdog, Config),
- SavedEnv = ?config(erl_flags_env, Config),
+ SavedEnv = proplists:get_value(erl_flags_env, Config),
restore_env(SavedEnv),
cleanup_nodes(),
- ?t:timetrap_cancel(Dog),
ok.
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 1}}].
all() ->
[args_file, evil_args_file, env, args_file_env,
otp_7461, otp_8209, zdbbl_dist_buf_busy_limit].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-otp_8209(doc) ->
- ["Test that plain first argument does not "
- "destroy -home switch [OTP-8209]"];
-otp_8209(suite) ->
- [];
+%% Test that plain first argument does not
+%% destroy -home switch [OTP-8209]
otp_8209(Config) when is_list(Config) ->
- ?line {ok,[[PName]]} = init:get_argument(progname),
- ?line SNameS = "erlexec_test_01",
- ?line SName = list_to_atom(SNameS++"@"++
+ {ok,[[PName]]} = init:get_argument(progname),
+ SNameS = "erlexec_test_01",
+ SName = list_to_atom(SNameS++"@"++
hd(tl(string:tokens(atom_to_list(node()),"@")))),
- ?line Cmd = PName ++ " dummy_param -sname "++SNameS++" -setcookie "++
+ Cmd = PName ++ " dummy_param -sname "++SNameS++" -setcookie "++
atom_to_list(erlang:get_cookie()),
- ?line open_port({spawn,Cmd},[]),
- ?line pong = loop_ping(SName,40),
- ?line {ok,[[_]]} = rpc:call(SName,init,get_argument,[home]),
- ?line ["dummy_param"] = rpc:call(SName,init,get_plain_arguments,[]),
- ?line ok = cleanup_nodes(),
+ open_port({spawn,Cmd},[]),
+ pong = loop_ping(SName,40),
+ {ok,[[_]]} = rpc:call(SName,init,get_argument,[home]),
+ ["dummy_param"] = rpc:call(SName,init,get_plain_arguments,[]),
+ ok = cleanup_nodes(),
ok.
cleanup_nodes() ->
@@ -123,17 +98,14 @@ loop_ping(Node,N) ->
pong
end.
-args_file(doc) -> [];
-args_file(suite) -> [];
args_file(Config) when is_list(Config) ->
- ?line AFN1 = privfile("1", Config),
- ?line AFN2 = privfile("2", Config),
- ?line AFN3 = privfile("3", Config),
- ?line AFN4 = privfile("4", Config),
- ?line AFN5 = privfile("5", Config),
- ?line AFN6 = privfile("6", Config),
- ?line write_file(AFN1,
- "-MiscArg2~n"
+ AFN1 = privfile("1", Config),
+ AFN2 = privfile("2", Config),
+ AFN3 = privfile("3", Config),
+ AFN4 = privfile("4", Config),
+ AFN5 = privfile("5", Config),
+ AFN6 = privfile("6", Config),
+ write_file(AFN1, "-MiscArg2~n"
"# a comment +\\#1000~n"
"+\\#200 # another comment~n"
"~n"
@@ -145,7 +117,7 @@ args_file(Config) when is_list(Config) ->
"+\\#700~n"
"-extra +XtraArg6~n",
[AFN2]),
- ?line write_file(AFN2,
+ write_file(AFN2,
"-MiscArg3~n"
"+\\#300~n"
"-args_file ~s~n"
@@ -156,61 +128,59 @@ args_file(Config) when is_list(Config) ->
"-args_file ~s~n"
"-extra +XtraArg5~n",
[AFN3, AFN4, AFN5, AFN6]),
- ?line write_file(AFN3,
+ write_file(AFN3,
"# comment again~n"
" -MiscArg4 +\\#400 -extra +XtraArg1"),
- ?line write_file(AFN4,
+ write_file(AFN4,
" -MiscArg6 +\\#600 -extra +XtraArg2~n"
"+XtraArg3~n"
"+XtraArg4~n"
"# comment again~n"),
- ?line write_file(AFN5, ""),
- ?line write_file(AFN6, "-extra # +XtraArg10~n"),
- ?line CmdLine = "+#100 -MiscArg1 "
+ write_file(AFN5, ""),
+ write_file(AFN6, "-extra # +XtraArg10~n"),
+ CmdLine = "+#100 -MiscArg1 "
++ "-args_file " ++ AFN1
++ " +#800 -MiscArg8 -extra +XtraArg7 +XtraArg8",
- ?line {Emu, Misc, Extra} = emu_args(CmdLine),
- ?line verify_args(["-#100", "-#200", "-#300", "-#400",
+ {Emu, Misc, Extra} = emu_args(CmdLine),
+ verify_args(["-#100", "-#200", "-#300", "-#400",
"-#500", "-#600", "-#700", "-#800"], Emu),
- ?line verify_args(["-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4",
+ verify_args(["-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4",
"-MiscArg5", "-MiscArg6", "-MiscArg7", "-MiscArg8"],
Misc),
- ?line verify_args(["+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4",
+ verify_args(["+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4",
"+XtraArg5", "+XtraArg6", "+XtraArg7", "+XtraArg8"],
Extra),
- ?line verify_not_args(["-MiscArg10", "-#1000", "+XtraArg10",
+ verify_not_args(["-MiscArg10", "-#1000", "+XtraArg10",
"-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4",
"-MiscArg5", "-MiscArg6", "-MiscArg7", "-MiscArg8",
"+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4",
"+XtraArg5", "+XtraArg6", "+XtraArg7", "+XtraArg8"],
Emu),
- ?line verify_not_args(["-MiscArg10", "-#1000", "+XtraArg10",
+ verify_not_args(["-MiscArg10", "-#1000", "+XtraArg10",
"-#100", "-#200", "-#300", "-#400",
"-#500", "-#600", "-#700", "-#800",
"+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4",
"+XtraArg5", "+XtraArg6", "+XtraArg7", "+XtraArg8"],
Misc),
- ?line verify_not_args(["-MiscArg10", "-#1000", "+XtraArg10",
+ verify_not_args(["-MiscArg10", "-#1000", "+XtraArg10",
"-#100", "-#200", "-#300", "-#400",
"-#500", "-#600", "-#700", "-#800",
"-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4",
"-MiscArg5", "-MiscArg6", "-MiscArg7", "-MiscArg8"],
Extra),
- ?line ok.
+ ok.
-evil_args_file(doc) -> [];
-evil_args_file(suite) -> [];
evil_args_file(Config) when is_list(Config) ->
- ?line Lim = 300,
- ?line FNums = lists:seq(1, Lim),
+ Lim = 300,
+ FNums = lists:seq(1, Lim),
lists:foreach(fun (End) when End == Lim ->
- ?line AFN = privfile(integer_to_list(End), Config),
- ?line write_file(AFN,
+ AFN = privfile(integer_to_list(End), Config),
+ write_file(AFN,
"-MiscArg~p ",
[End]);
(I) ->
- ?line AFNX = privfile(integer_to_list(I), Config),
- ?line AFNY = privfile(integer_to_list(I+1), Config),
+ AFNX = privfile(integer_to_list(I), Config),
+ AFNY = privfile(integer_to_list(I+1), Config),
{Frmt, Args} =
case I rem 2 of
0 ->
@@ -220,65 +190,59 @@ evil_args_file(Config) when is_list(Config) ->
{"-MiscArg~p -args_file ~s",
[I, AFNY]}
end,
- ?line write_file(AFNX, Frmt, Args)
+ write_file(AFNX, Frmt, Args)
end,
FNums),
- ?line {_Emu, Misc, _Extra} = emu_args("-args_file "
+ {_Emu, Misc, _Extra} = emu_args("-args_file "
++ privfile("1", Config)),
- ?line ANums = FNums
+ ANums = FNums
++ lists:reverse(lists:filter(fun (I) when I == Lim -> false;
(I) when I rem 2 == 0 -> true;
(_) -> false
end, FNums)),
- ?line verify_args(lists:map(fun (I) -> "-MiscArg"++integer_to_list(I) end,
+ verify_args(lists:map(fun (I) -> "-MiscArg"++integer_to_list(I) end,
ANums),
Misc),
- ?line ok.
+ ok.
-env(doc) -> [];
-env(suite) -> [];
env(Config) when is_list(Config) ->
- ?line os:putenv("ERL_AFLAGS", "-MiscArg1 +#100 -extra +XtraArg1 +XtraArg2"),
- ?line CmdLine = "+#200 -MiscArg2 -extra +XtraArg3 +XtraArg4",
- ?line os:putenv("ERL_FLAGS", "-MiscArg3 +#300 -extra +XtraArg5"),
- ?line os:putenv("ERL_ZFLAGS", "-MiscArg4 +#400 -extra +XtraArg6"),
- ?line {Emu, Misc, Extra} = emu_args(CmdLine),
- ?line verify_args(["-#100", "-#200", "-#300", "-#400"], Emu),
- ?line verify_args(["-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4"],
+ os:putenv("ERL_AFLAGS", "-MiscArg1 +#100 -extra +XtraArg1 +XtraArg2"),
+ CmdLine = "+#200 -MiscArg2 -extra +XtraArg3 +XtraArg4",
+ os:putenv("ERL_FLAGS", "-MiscArg3 +#300 -extra +XtraArg5"),
+ os:putenv("ERL_ZFLAGS", "-MiscArg4 +#400 -extra +XtraArg6"),
+ {Emu, Misc, Extra} = emu_args(CmdLine),
+ verify_args(["-#100", "-#200", "-#300", "-#400"], Emu),
+ verify_args(["-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4"],
Misc),
- ?line verify_args(["+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4",
+ verify_args(["+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4",
"+XtraArg5", "+XtraArg6"],
Extra),
- ?line ok.
+ ok.
-args_file_env(doc) -> [];
-args_file_env(suite) -> [];
args_file_env(Config) when is_list(Config) ->
- ?line AFN1 = privfile("1", Config),
- ?line AFN2 = privfile("2", Config),
- ?line write_file(AFN1, "-MiscArg2 +\\#200 -extra +XtraArg1"),
- ?line write_file(AFN2, "-MiscArg3 +\\#400 -extra +XtraArg3"),
- ?line os:putenv("ERL_AFLAGS",
+ AFN1 = privfile("1", Config),
+ AFN2 = privfile("2", Config),
+ write_file(AFN1, "-MiscArg2 +\\#200 -extra +XtraArg1"),
+ write_file(AFN2, "-MiscArg3 +\\#400 -extra +XtraArg3"),
+ os:putenv("ERL_AFLAGS",
"-MiscArg1 +#100 -args_file "++AFN1++ " -extra +XtraArg2"),
- ?line CmdLine = "+#300 -args_file "++AFN2++" -MiscArg4 -extra +XtraArg4",
- ?line os:putenv("ERL_FLAGS", "-MiscArg5 +#500 -extra +XtraArg5"),
- ?line os:putenv("ERL_ZFLAGS", "-MiscArg6 +#600 -extra +XtraArg6"),
- ?line {Emu, Misc, Extra} = emu_args(CmdLine),
- ?line verify_args(["-#100", "-#200", "-#300", "-#400",
+ CmdLine = "+#300 -args_file "++AFN2++" -MiscArg4 -extra +XtraArg4",
+ os:putenv("ERL_FLAGS", "-MiscArg5 +#500 -extra +XtraArg5"),
+ os:putenv("ERL_ZFLAGS", "-MiscArg6 +#600 -extra +XtraArg6"),
+ {Emu, Misc, Extra} = emu_args(CmdLine),
+ verify_args(["-#100", "-#200", "-#300", "-#400",
"-#500", "-#600"], Emu),
- ?line verify_args(["-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4",
+ verify_args(["-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4",
"-MiscArg5", "-MiscArg6"],
Misc),
- ?line verify_args(["+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4",
+ verify_args(["+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4",
"+XtraArg5", "+XtraArg6"],
Extra),
- ?line ok.
+ ok.
%% Make sure "erl -detached" survives when parent process group gets killed
-otp_7461(doc) -> [];
-otp_7461(suite) -> [];
otp_7461(Config) when is_list(Config) ->
case os:type() of
{unix,_} ->
@@ -302,9 +266,9 @@ otp_7461(Config) when is_list(Config) ->
otp_7461_do(Config) ->
io:format("alive=~p node=~p\n",[is_alive(), node()]),
- TestProg = filename:join([?config(data_dir, Config), "erlexec_tests"]),
+ TestProg = filename:join([proplists:get_value(data_dir, Config), "erlexec_tests"]),
{ok, [[ErlProg]]} = init:get_argument(progname),
- ?line Cmd = TestProg ++ " " ++ ErlProg ++
+ Cmd = TestProg ++ " " ++ ErlProg ++
" -detached -sname " ++ get_nodename(otp_7461) ++
" -setcookie " ++ atom_to_list(erlang:get_cookie()) ++
" -pa " ++ filename:dirname(code:which(?MODULE)) ++
@@ -314,29 +278,31 @@ otp_7461_do(Config) ->
%% open_port fork+exec
io:format("spawn port prog ~p\n",[Cmd]),
- ?line Port = open_port({spawn, Cmd}, [eof]),
+ Port = open_port({spawn, Cmd}, [eof]),
io:format("Wait for node to connect...\n",[]),
- ?line {nodeup, Slave} = receive Msg -> Msg
+ {nodeup, Slave} = receive Msg -> Msg
after 20*1000 -> timeout end,
io:format("Node alive: ~p\n", [Slave]),
- ?line pong = net_adm:ping(Slave),
+ pong = net_adm:ping(Slave),
io:format("Ping ok towards ~p\n", [Slave]),
- ?line Port ! { self(), {command, "K"}}, % Kill child process group
- ?line {Port, {data, "K"}} = receive Msg2 -> Msg2 end,
- ?line port_close(Port),
+ Port ! { self(), {command, "K"}}, % Kill child process group
+ {Port, {data, "K"}} = receive Msg2 -> Msg2 end,
+ port_close(Port),
%% Now the actual test. Detached node should still be alive.
- ?line pong = net_adm:ping(Slave),
+ pong = net_adm:ping(Slave),
io:format("Ping still ok towards ~p\n", [Slave]),
%% Halt node
- ?line rpc:cast(Slave, ?MODULE, otp_7461_remote, [[halt, self()]]),
+ rpc:cast(Slave, ?MODULE, otp_7461_remote, [[halt, self()]]),
- ?line {nodedown, Slave} = receive Msg3 -> Msg3
- after 20*1000 -> timeout end,
+ {nodedown, Slave} = receive
+ Msg3 -> Msg3
+ after 20*1000 -> timeout
+ end,
io:format("Node dead: ~p\n", [Slave]),
ok.
@@ -349,24 +315,21 @@ otp_7461_remote([halt, Pid]) ->
io:format("halt order from ~p to node ~p\n",[Pid,node()]),
halt().
-zdbbl_dist_buf_busy_limit(doc) ->
- ["Check +zdbbl flag"];
-zdbbl_dist_buf_busy_limit(suite) ->
- [];
+%% Check +zdbbl flag
zdbbl_dist_buf_busy_limit(Config) when is_list(Config) ->
LimKB = 1122233,
LimB = LimKB*1024,
- ?line {ok,[[PName]]} = init:get_argument(progname),
- ?line SNameS = "erlexec_test_02",
- ?line SName = list_to_atom(SNameS++"@"++
+ {ok,[[PName]]} = init:get_argument(progname),
+ SNameS = "erlexec_test_02",
+ SName = list_to_atom(SNameS++"@"++
hd(tl(string:tokens(atom_to_list(node()),"@")))),
- ?line Cmd = PName ++ " -sname "++SNameS++" -setcookie "++
+ Cmd = PName ++ " -sname "++SNameS++" -setcookie "++
atom_to_list(erlang:get_cookie()) ++
" +zdbbl " ++ integer_to_list(LimKB),
- ?line open_port({spawn,Cmd},[]),
- ?line pong = loop_ping(SName,40),
- ?line LimB = rpc:call(SName,erlang,system_info,[dist_buf_busy_limit]),
- ?line ok = cleanup_node(SNameS, 10),
+ open_port({spawn,Cmd},[]),
+ pong = loop_ping(SName,40),
+ LimB = rpc:call(SName,erlang,system_info,[dist_buf_busy_limit]),
+ ok = cleanup_node(SNameS, 10),
ok.
@@ -404,8 +367,8 @@ restore_env({erl_flags, AFlgs, Flgs, RFlgs, ZFlgs}) ->
ok.
privfile(Name, Config) ->
- filename:join([?config(priv_dir, Config),
- atom_to_list(?config(testcase, Config)) ++ "." ++ Name]).
+ filename:join([proplists:get_value(priv_dir, Config),
+ atom_to_list(proplists:get_value(testcase, Config)) ++ "." ++ Name]).
write_file(FileName, Frmt) ->
write_file(FileName, Frmt, []).
@@ -430,8 +393,7 @@ verify_not_args(Xs, Ys) ->
true -> exit({arg_present, X});
false -> ok
end
- end,
- Xs).
+ end, Xs).
emu_args(CmdLineArgs) ->
io:format("CmdLineArgs = ~ts~n", [CmdLineArgs]),
diff --git a/erts/test/erlexec_SUITE_data/Makefile.src b/erts/test/erlexec_SUITE_data/Makefile.src
index 145aaedd64..641dff1e30 100644
--- a/erts/test/erlexec_SUITE_data/Makefile.src
+++ b/erts/test/erlexec_SUITE_data/Makefile.src
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2008-2012. All Rights Reserved.
+# Copyright Ericsson AB 2008-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/test/erlexec_SUITE_data/erlexec_tests.c b/erts/test/erlexec_SUITE_data/erlexec_tests.c
index 569bf7bcc4..bd28d2900c 100644
--- a/erts/test/erlexec_SUITE_data/erlexec_tests.c
+++ b/erts/test/erlexec_SUITE_data/erlexec_tests.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2010. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/test/ethread_SUITE.erl b/erts/test/ethread_SUITE.erl
index 4a40dbb11e..19f738c572 100644
--- a/erts/test/ethread_SUITE.erl
+++ b/erts/test/ethread_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -28,13 +28,7 @@
-module(ethread_SUITE).
-author('[email protected]').
-%-define(line_trace, 1).
-
--define(DEFAULT_TIMEOUT, ?t:minutes(10)).
-
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2, end_per_testcase/2]).
+-export([all/0, suite/0, init_per_testcase/2, end_per_testcase/2]).
-export([create_join_thread/1,
equal_tids/1,
@@ -51,9 +45,13 @@
atomic/1,
dw_atomic_massage/1]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 10}}].
-tests() ->
+all() ->
[create_join_thread,
equal_tids,
mutex,
@@ -69,110 +67,50 @@ tests() ->
atomic,
dw_atomic_massage].
-suite() -> [{ct_hooks,[ts_install_cth]}].
-
-all() ->
- tests().
-
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
+init_per_testcase(Case, Config) ->
+ case inet:gethostname() of
+ {ok,"fenris"} when Case == max_threads ->
+ %% Cannot use os:type+os:version as not all
+ %% solaris10 machines are buggy.
+ {skip, "This machine is buggy"};
+ _Else ->
+ Config
+ end.
-end_per_suite(_Config) ->
+end_per_testcase(_Case, _Config) ->
ok.
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
%%
%%
%% The test-cases
%%
%%
-create_join_thread(doc) ->
- ["Tests ethr_thr_create and ethr_thr_join."];
-create_join_thread(suite) ->
- [];
+%% Tests ethr_thr_create and ethr_thr_join.
create_join_thread(Config) ->
run_case(Config, "create_join_thread", "").
-equal_tids(doc) ->
- ["Tests ethr_equal_tids."];
-equal_tids(suite) ->
- [];
+%% Tests ethr_equal_tids.
equal_tids(Config) ->
run_case(Config, "equal_tids", "").
-mutex(doc) ->
- ["Tests mutexes."];
-mutex(suite) ->
- [];
+%% Tests mutexes.
mutex(Config) ->
run_case(Config, "mutex", "").
-try_lock_mutex(doc) ->
- ["Tests try lock on mutex."];
-try_lock_mutex(suite) ->
- [];
+%% Tests try lock on mutex.
try_lock_mutex(Config) ->
run_case(Config, "try_lock_mutex", "").
-%% Remove dead code?
-
-% wd_dispatch(P) ->
-% receive
-% bye ->
-% ?line true = port_command(P, "-1 "),
-% ?line bye;
-% L when is_list(L) ->
-% ?line true = port_command(P, L),
-% ?line wd_dispatch(P)
-% end.
-%
-% watchdog(Port) ->
-% ?line process_flag(priority, max),
-% ?line receive after 500 -> ok end,
-%
-% ?line random:seed(),
-% ?line true = port_command(Port, "0 "),
-% ?line lists:foreach(fun (T) ->
-% erlang:send_after(T,
-% self(),
-% integer_to_list(T)
-% ++ " ")
-% end,
-% lists:usort(lists:map(fun (_) ->
-% random:uniform(4500)+500
-% end,
-% lists:duplicate(50,0)))),
-% ?line erlang:send_after(5100, self(), bye),
-%
-% wd_dispatch(Port).
-
-cond_wait(doc) ->
- ["Tests ethr_cond_wait with ethr_cond_signal and ethr_cond_broadcast."];
-cond_wait(suite) ->
- [];
+%% Tests ethr_cond_wait with ethr_cond_signal and ethr_cond_broadcast.
cond_wait(Config) ->
run_case(Config, "cond_wait", "").
-broadcast(doc) ->
- ["Tests that a ethr_cond_broadcast really wakes up all waiting threads"];
-broadcast(suite) ->
- [];
+%% Tests that a ethr_cond_broadcast really wakes up all waiting threads
broadcast(Config) ->
run_case(Config, "broadcast", "").
-detached_thread(doc) ->
- ["Tests detached threads."];
-detached_thread(suite) ->
- [];
+%% Tests detached threads.
detached_thread(Config) ->
case {os:type(), os:version()} of
{{unix,darwin}, {9, _, _}} ->
@@ -184,10 +122,7 @@ detached_thread(Config) ->
run_case(Config, "detached_thread", "")
end.
-max_threads(doc) ->
- ["Tests maximum number of threads."];
-max_threads(suite) ->
- [];
+%% Tests maximum number of threads.
max_threads(Config) ->
case {os:type(), os:version()} of
{{unix,darwin}, {9, _, _}} ->
@@ -199,45 +134,27 @@ max_threads(Config) ->
run_case(Config, "max_threads", "")
end.
-tsd(doc) ->
- ["Tests thread specific data."];
-tsd(suite) ->
- [];
+%% Tests thread specific data.
tsd(Config) ->
run_case(Config, "tsd", "").
-spinlock(doc) ->
- ["Tests spinlocks."];
-spinlock(suite) ->
- [];
+%% Tests spinlocks.
spinlock(Config) ->
run_case(Config, "spinlock", "").
-rwspinlock(doc) ->
- ["Tests rwspinlocks."];
-rwspinlock(suite) ->
- [];
+%% Tests rwspinlocks.
rwspinlock(Config) ->
run_case(Config, "rwspinlock", "").
-rwmutex(doc) ->
- ["Tests rwmutexes."];
-rwmutex(suite) ->
- [];
+%% Tests rwmutexes.
rwmutex(Config) ->
run_case(Config, "rwmutex", "").
-atomic(doc) ->
- ["Tests atomics."];
-atomic(suite) ->
- [];
+%% Tests atomics.
atomic(Config) ->
run_case(Config, "atomic", "").
-dw_atomic_massage(doc) ->
- ["Massage double word atomics"];
-dw_atomic_massage(suite) ->
- [];
+%% Massage double word atomics
dw_atomic_massage(Config) ->
run_case(Config, "dw_atomic_massage", "").
@@ -247,22 +164,6 @@ dw_atomic_massage(Config) ->
%%
%%
-init_per_testcase(Case, Config) ->
- case inet:gethostname() of
- {ok,"fenris"} when Case == max_threads ->
- %% Cannot use os:type+os:version as not all
- %% solaris10 machines are buggy.
- {skip, "This machine is buggy"};
- _Else ->
- Dog = ?t:timetrap(?DEFAULT_TIMEOUT),
- [{watchdog, Dog}|Config]
- end.
-
-end_per_testcase(_Case, Config) ->
- Dog = ?config(watchdog, Config),
- ?t:timetrap_cancel(Dog),
- ok.
-
-define(TESTPROG, "ethread_tests").
-define(FAILED_MARKER, $E,$T,$H,$R,$-,$T,$E,$S,$T,$-,$F,$A,$I,$L,$U,$R,$E).
-define(SKIPPED_MARKER, $E,$T,$H,$R,$-,$T,$E,$S,$T,$-,$S,$K,$I,$P).
@@ -270,68 +171,68 @@ end_per_testcase(_Case, Config) ->
-define(PID_MARKER, $E,$T,$H,$R,$-,$T,$E,$S,$T,$-,$P,$I,$D).
port_prog_killer(EProc, OSProc) when is_pid(EProc), is_list(OSProc) ->
- ?line process_flag(trap_exit, true),
- ?line Ref = erlang:monitor(process, EProc),
- ?line receive
- {'DOWN', Ref, _, _, Reason} when is_tuple(Reason),
- element(1, Reason)
- == timetrap_timeout ->
- ?line Cmd = "kill -9 " ++ OSProc,
- ?line ?t:format("Test case timed out. "
- "Trying to kill port program.~n"
- " Executing: ~p~n", [Cmd]),
- ?line case os:cmd(Cmd) of
- [] ->
- ok;
- OsCmdRes ->
- ?line ?t:format(" ~s", [OsCmdRes])
- end;
- {'DOWN', Ref, _, _, _} ->
- %% OSProc is assumed to have terminated by itself
- ?line ok
- end.
+ process_flag(trap_exit, true),
+ Ref = erlang:monitor(process, EProc),
+ receive
+ {'DOWN', Ref, _, _, Reason} when is_tuple(Reason),
+ element(1, Reason)
+ == timetrap_timeout ->
+ Cmd = "kill -9 " ++ OSProc,
+ io:format("Test case timed out. "
+ "Trying to kill port program.~n"
+ " Executing: ~p~n", [Cmd]),
+ case os:cmd(Cmd) of
+ [] ->
+ ok;
+ OsCmdRes ->
+ io:format(" ~s", [OsCmdRes])
+ end;
+ %% OSProc is assumed to have terminated by itself
+ {'DOWN', Ref, _, _, _} ->
+ ok
+ end.
get_line(_Port, eol, Data) ->
- ?line Data;
+ Data;
get_line(Port, noeol, Data) ->
- ?line receive
+ receive
{Port, {data, {Flag, NextData}}} ->
- ?line get_line(Port, Flag, Data ++ NextData);
+ get_line(Port, Flag, Data ++ NextData);
{Port, eof} ->
- ?line ?t:fail(port_prog_unexpectedly_closed)
+ ct:fail(port_prog_unexpectedly_closed)
end.
read_case_data(Port, TestCase) ->
- ?line receive
- {Port, {data, {eol, [?SUCCESS_MARKER]}}} ->
- ?line ok;
- {Port, {data, {Flag, [?SUCCESS_MARKER | CommentStart]}}} ->
- ?line {comment, get_line(Port, Flag, CommentStart)};
- {Port, {data, {Flag, [?SKIPPED_MARKER | CommentStart]}}} ->
- ?line {skipped, get_line(Port, Flag, CommentStart)};
- {Port, {data, {Flag, [?FAILED_MARKER | ReasonStart]}}} ->
- ?line ?t:fail(get_line(Port, Flag, ReasonStart));
- {Port, {data, {eol, [?PID_MARKER | PidStr]}}} ->
- ?line ?t:format("Port program pid: ~s~n", [PidStr]),
- ?line CaseProc = self(),
- ?line _ = list_to_integer(PidStr), % Sanity check
- spawn_opt(fun () ->
- port_prog_killer(CaseProc, PidStr)
- end,
- [{priority, max}, link]),
- read_case_data(Port, TestCase);
- {Port, {data, {Flag, LineStart}}} ->
- ?line ?t:format("~s~n", [get_line(Port, Flag, LineStart)]),
- read_case_data(Port, TestCase);
- {Port, eof} ->
- ?line ?t:fail(port_prog_unexpectedly_closed)
- end.
+ receive
+ {Port, {data, {eol, [?SUCCESS_MARKER]}}} ->
+ ok;
+ {Port, {data, {Flag, [?SUCCESS_MARKER | CommentStart]}}} ->
+ {comment, get_line(Port, Flag, CommentStart)};
+ {Port, {data, {Flag, [?SKIPPED_MARKER | CommentStart]}}} ->
+ {skipped, get_line(Port, Flag, CommentStart)};
+ {Port, {data, {Flag, [?FAILED_MARKER | ReasonStart]}}} ->
+ ct:fail(get_line(Port, Flag, ReasonStart));
+ {Port, {data, {eol, [?PID_MARKER | PidStr]}}} ->
+ io:format("Port program pid: ~s~n", [PidStr]),
+ CaseProc = self(),
+ _ = list_to_integer(PidStr), % Sanity check
+ spawn_opt(fun () ->
+ port_prog_killer(CaseProc, PidStr)
+ end,
+ [{priority, max}, link]),
+ read_case_data(Port, TestCase);
+ {Port, {data, {Flag, LineStart}}} ->
+ io:format("~s~n", [get_line(Port, Flag, LineStart)]),
+ read_case_data(Port, TestCase);
+ {Port, eof} ->
+ ct:fail(port_prog_unexpectedly_closed)
+ end.
run_case(Config, Test, TestArgs) ->
run_case(Config, Test, TestArgs, fun (_Port) -> ok end).
run_case(Config, Test, TestArgs, Fun) ->
- TestProg = filename:join([?config(data_dir, Config), ?TESTPROG]),
+ TestProg = filename:join([proplists:get_value(data_dir, Config), ?TESTPROG]),
Cmd = TestProg ++ " " ++ Test ++ " " ++ TestArgs,
case catch open_port({spawn, Cmd}, [stream,
use_stdio,
@@ -339,17 +240,13 @@ run_case(Config, Test, TestArgs, Fun) ->
eof,
{line, 1024}]) of
Port when is_port(Port) ->
- ?line Fun(Port),
- ?line CaseResult = read_case_data(Port, Test),
- ?line receive
- {Port, eof} ->
- ?line ok
- end,
- ?line CaseResult;
+ Fun(Port),
+ CaseResult = read_case_data(Port, Test),
+ receive
+ {Port, eof} ->
+ ok
+ end,
+ CaseResult;
Error ->
- ?line ?t:fail({open_port_failed, Error})
+ ct:fail({open_port_failed, Error})
end.
-
-
-
-
diff --git a/erts/test/ethread_SUITE_data/Makefile.src b/erts/test/ethread_SUITE_data/Makefile.src
index e8b9c79576..cf675b92a3 100644
--- a/erts/test/ethread_SUITE_data/Makefile.src
+++ b/erts/test/ethread_SUITE_data/Makefile.src
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2012. All Rights Reserved.
+# Copyright Ericsson AB 2004-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/test/ethread_SUITE_data/ethread_tests.c b/erts/test/ethread_SUITE_data/ethread_tests.c
index b51771c736..fe7f92b012 100644
--- a/erts/test/ethread_SUITE_data/ethread_tests.c
+++ b/erts/test/ethread_SUITE_data/ethread_tests.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/test/ignore_cores.erl b/erts/test/ignore_cores.erl
index 13f34cd10f..25dce346b9 100644
--- a/erts/test/ignore_cores.erl
+++ b/erts/test/ignore_cores.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -28,7 +28,7 @@
-module(ignore_cores).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-export([init/1, fini/1, setup/3, setup/4, restore/1, dir/1]).
@@ -53,7 +53,7 @@ init(Config) ->
fini(Config) ->
#ignore_cores{org_cwd = OrgCWD,
org_path = OrgPath,
- org_pwd_env = OrgPWD} = ?config(ignore_cores, Config),
+ org_pwd_env = OrgPWD} = proplists:get_value(ignore_cores, Config),
ok = file:set_cwd(OrgCWD),
true = code:set_path(OrgPath),
case OrgPWD of
@@ -70,10 +70,10 @@ setup(Suite, Testcase, Config, SetCwd) when is_atom(Suite),
is_list(Config) ->
#ignore_cores{org_cwd = OrgCWD,
org_path = OrgPath,
- org_pwd_env = OrgPWD} = ?config(ignore_cores, Config),
+ org_pwd_env = OrgPWD} = proplists:get_value(ignore_cores, Config),
Path = lists:map(fun (".") -> OrgCWD; (Dir) -> Dir end, OrgPath),
true = code:set_path(Path),
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
IgnDir = filename:join([PrivDir,
atom_to_list(Suite)
++ "_"
@@ -94,7 +94,7 @@ setup(Suite, Testcase, Config, SetCwd) when is_atom(Suite),
end,
ok = file:write_file(filename:join([IgnDir, "ignore_core_files"]), <<>>),
%% cores are dumped in /cores on MacOS X
- CoresDir = case {?t:os_type(), filelib:is_dir("/cores")} of
+ CoresDir = case {os:type(), filelib:is_dir("/cores")} of
{{unix,darwin}, true} ->
filelib:fold_files("/cores",
"^core.*$",
@@ -119,7 +119,7 @@ restore(Config) ->
org_path = OrgPath,
org_pwd_env = OrgPWD,
ign_dir = IgnDir,
- cores_dir = CoresDir} = ?config(ignore_cores, Config),
+ cores_dir = CoresDir} = proplists:get_value(ignore_cores, Config),
try
case CoresDir of
false ->
@@ -155,5 +155,5 @@ restore(Config) ->
dir(Config) ->
- #ignore_cores{ign_dir = Dir} = ?config(ignore_cores, Config),
+ #ignore_cores{ign_dir = Dir} = proplists:get_value(ignore_cores, Config),
Dir.
diff --git a/erts/test/install_SUITE.erl b/erts/test/install_SUITE.erl
index b380b064bd..d6c6d6f30e 100644
--- a/erts/test/install_SUITE.erl
+++ b/erts/test/install_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2012. 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.
@@ -18,7 +18,6 @@
%% %CopyrightEnd%
%%
-
%%%-------------------------------------------------------------------
%%% File : install_SUITE.erl
%%% Author : Rickard Green
@@ -28,11 +27,9 @@
%%%-------------------------------------------------------------------
-module(install_SUITE).
-%-define(line_trace, 1).
-
--export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
- init_per_suite/1, end_per_suite/1,
- init_per_testcase/2, end_per_testcase/2]).
+-export([all/0, suite/0,
+ init_per_suite/1, end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2]).
-export([bin_default/1,
bin_default_dirty/1,
@@ -49,10 +46,9 @@
bin_dirname_fail/1,
bin_no_use_dirname_fail/1]).
--define(DEFAULT_TIMEOUT, ?t:minutes(1)).
-define(JOIN(A,B,C), filename:join(A, B, C)).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-record(inst, {mkdirs = true,
symlinks = true,
@@ -66,60 +62,52 @@
erlang_bindir = "",
bindir_symlinks = ""}).
-need_symlink_cases() ->
+need_symlink_cases() ->
[bin_unreachable_absolute, bin_unreachable_relative,
bin_same_dir, bin_ok_symlink, bin_dirname_fail,
bin_no_use_dirname_fail].
-dont_need_symlink_cases() ->
+dont_need_symlink_cases() ->
[bin_default, bin_default_dirty, bin_outside_eprfx,
bin_outside_eprfx_dirty, bin_not_abs,
bin_unreasonable_path, 'bin white space',
bin_no_srcfile].
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 1}}].
-all() ->
+all() ->
dont_need_symlink_cases() ++ need_symlink_cases().
-groups() ->
- [].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
%%
%% The test cases
%%
bin_default(Config) when is_list(Config) ->
- ?line E = "/usr/local",
- ?line Bs = "/usr/local/bin",
- ?line Be = Bs,
- ?line EBs = "/usr/local/lib/erlang/bin",
- ?line EBe = EBs,
- ?line RP = "../lib/erlang/bin",
+ E = "/usr/local",
+ Bs = "/usr/local/bin",
+ Be = Bs,
+ EBs = "/usr/local/lib/erlang/bin",
+ EBe = EBs,
+ RP = "../lib/erlang/bin",
ChkRes = fun (Res, #inst{test_prefix = TP,
destdir = D,
extra_prefix = EP,
bindir_symlinks = BSL,
symlinks = SL}) ->
- ?line B = join([TP, D, EP, Be]),
+ B = join([TP, D, EP, Be]),
Expct = case {SL, BSL} of
{false, _} when BSL == "relative";
BSL == "absolute" ->
- ?line {error, no_ln_s};
+ {error, no_ln_s};
{false, _} ->
- ?line {ok,{absolute,
+ {ok,{absolute,
B,join([TP,D,EP,EBe])}};
{true, "absolute"} ->
- ?line {ok,{absolute,B,join([TP,EP,EBe])}};
+ {ok,{absolute,B,join([TP,EP,EBe])}};
{true, _} ->
- ?line {ok,{relative,B,RP}}
+ {ok,{relative,B,RP}}
end,
expect(Expct, Res)
end,
@@ -128,30 +116,30 @@ bin_default(Config) when is_list(Config) ->
erlang_bindir = EBs}, ChkRes).
bin_default_dirty(Config) when is_list(Config) ->
- ?line E = "/usr/./local/lib/..",
- ?line Bs = "/usr/local//lib/../lib/erlang/../../bin",
- ?line Be = "/usr/local/lib/../lib/erlang/../../bin",
- ?line EBs = "/usr/local/lib/../lib/erlang/../erlang/bin/x/y/../..//",
- ?line EBe = "/usr/local/lib/../lib/erlang/../erlang/bin/x/y/../..",
- ?line RP = "../lib/erlang/bin",
+ E = "/usr/./local/lib/..",
+ Bs = "/usr/local//lib/../lib/erlang/../../bin",
+ Be = "/usr/local/lib/../lib/erlang/../../bin",
+ EBs = "/usr/local/lib/../lib/erlang/../erlang/bin/x/y/../..//",
+ EBe = "/usr/local/lib/../lib/erlang/../erlang/bin/x/y/../..",
+ RP = "../lib/erlang/bin",
ChkRes = fun (Res, #inst{test_prefix = TP,
destdir = D,
extra_prefix = EP,
bindir_symlinks = BSL,
symlinks = SL}) ->
- ?line B = join([TP, D, EP, Be]),
+ B = join([TP, D, EP, Be]),
Expct = case {SL, BSL} of
{false, _} when BSL == "relative";
BSL == "absolute" ->
- ?line {error, no_ln_s};
+ {error, no_ln_s};
{false, _} ->
- ?line {ok,{absolute,
+ {ok,{absolute,
B,join([TP,D,EP,EBe])}};
{true, "absolute"} ->
- ?line {ok,{absolute,
+ {ok,{absolute,
B,join([TP,EP,EBe])}};
{true, _} ->
- ?line {ok,{relative,B,RP}}
+ {ok,{relative,B,RP}}
end,
expect(Expct, Res)
end,
@@ -161,29 +149,29 @@ bin_default_dirty(Config) when is_list(Config) ->
bin_outside_eprfx(Config) when is_list(Config) ->
- ?line E = "/usr/local",
- ?line Bs = "/usr/bin",
- ?line Be = Bs,
- ?line EBs = "/usr/local/lib/erlang/bin",
- ?line EBe = EBs,
- ?line RP = "../local/lib/erlang/bin",
+ E = "/usr/local",
+ Bs = "/usr/bin",
+ Be = Bs,
+ EBs = "/usr/local/lib/erlang/bin",
+ EBe = EBs,
+ RP = "../local/lib/erlang/bin",
ChkRes = fun (Res, #inst{test_prefix = TP,
destdir = D,
extra_prefix = EP,
bindir_symlinks = BSL,
symlinks = SL}) ->
- ?line B = join([TP, D, EP, Be]),
+ B = join([TP, D, EP, Be]),
Expct = case {SL, BSL} of
{false, _} when BSL == "relative";
BSL == "absolute" ->
- ?line {error, no_ln_s};
+ {error, no_ln_s};
{false, _} ->
- ?line {ok,{absolute,
+ {ok,{absolute,
B,join([TP,D,EP,EBe])}};
{true, "relative"} ->
- ?line {ok,{relative,B,RP}};
+ {ok,{relative,B,RP}};
{true, _} ->
- ?line {ok,{absolute,B,join([TP,EP,EBe])}}
+ {ok,{absolute,B,join([TP,EP,EBe])}}
end,
expect(Expct, Res)
end,
@@ -193,29 +181,29 @@ bin_outside_eprfx(Config) when is_list(Config) ->
bin_outside_eprfx_dirty(Config) when is_list(Config) ->
- ?line E = "/usr/local/lib/..",
- ?line Bs = "/usr/local/lib/../../bin",
- ?line Be = Bs,
- ?line EBs = "/usr/local/lib/erlang/bin",
- ?line EBe = EBs,
- ?line RP = "../local/lib/erlang/bin",
+ E = "/usr/local/lib/..",
+ Bs = "/usr/local/lib/../../bin",
+ Be = Bs,
+ EBs = "/usr/local/lib/erlang/bin",
+ EBe = EBs,
+ RP = "../local/lib/erlang/bin",
ChkRes = fun (Res, #inst{test_prefix = TP,
destdir = D,
extra_prefix = EP,
bindir_symlinks = BSL,
symlinks = SL}) ->
- ?line B = join([TP, D, EP, Be]),
+ B = join([TP, D, EP, Be]),
Expct = case {SL, BSL} of
{false, _} when BSL == "relative";
BSL == "absolute" ->
- ?line {error, no_ln_s};
+ {error, no_ln_s};
{false, _} ->
- ?line {ok,{absolute,
+ {ok,{absolute,
B,join([TP,D,EP,EBe])}};
{true, "relative"} ->
- ?line {ok,{relative,B,RP}};
+ {ok,{relative,B,RP}};
{true, _} ->
- ?line {ok,{absolute,B,join([TP,EP,EBe])}}
+ {ok,{absolute,B,join([TP,EP,EBe])}}
end,
expect(Expct, Res)
end,
@@ -224,33 +212,33 @@ bin_outside_eprfx_dirty(Config) when is_list(Config) ->
erlang_bindir = EBs}, ChkRes).
bin_unreasonable_path(Config) when is_list(Config) ->
- ?line E = "/usr/local/../../..",
- ?line Bs = "/usr/local/../../../bin",
- ?line Be = Bs,
- ?line EBs = "/usr/local/../../../bin_unreasonable_path/usr/local/lib/erlang/bin",
- ?line EBe = EBs,
- ?line RP = "../bin_unreasonable_path/usr/local/lib/erlang/bin",
+ E = "/usr/local/../../..",
+ Bs = "/usr/local/../../../bin",
+ Be = Bs,
+ EBs = "/usr/local/../../../bin_unreasonable_path/usr/local/lib/erlang/bin",
+ EBe = EBs,
+ RP = "../bin_unreasonable_path/usr/local/lib/erlang/bin",
ChkRes = fun (Res, #inst{test_prefix = TP,
destdir = D,
extra_prefix = EP,
bindir_symlinks = BSL,
symlinks = SL}) ->
- ?line B = join([TP, D, EP, Be]),
+ B = join([TP, D, EP, Be]),
Expct = case {TP, SL, BSL} of
{_, false, _} when BSL == "relative";
BSL == "absolute" ->
- ?line {error, no_ln_s};
+ {error, no_ln_s};
{_, false, _} ->
- ?line {ok,{absolute,
+ {ok,{absolute,
B,join([TP,D,EP,EBe])}};
{"", true, "relative"} ->
{error, unreasonable_path};
{"", true, _} ->
- ?line {ok,{absolute,B,join([TP,EP,EBe])}};
+ {ok,{absolute,B,join([TP,EP,EBe])}};
{_, true, "absolute"} ->
- ?line {ok,{absolute,B,join([TP,EP,EBe])}};
+ {ok,{absolute,B,join([TP,EP,EBe])}};
_ ->
- ?line {ok,{relative,B,RP}}
+ {ok,{relative,B,RP}}
end,
expect(Expct, Res)
end,
@@ -259,7 +247,7 @@ bin_unreasonable_path(Config) when is_list(Config) ->
erlang_bindir = EBs}, ChkRes).
bin_unreachable_absolute(Config) when is_list(Config) ->
- TDir = ?config(test_dir, Config),
+ TDir = proplists:get_value(test_dir, Config),
make_dirs(TDir, "/opt/local/lib/erlang/usr/bin"),
make_dirs(TDir, "/opt/local/lib/erlang/bin"),
Erl = join([TDir, "/opt/local/lib/erlang/bin/erl"]),
@@ -270,28 +258,28 @@ bin_unreachable_absolute(Config) when is_list(Config) ->
ok = file:write_file(Erlc, "erlc"),
ok = file:make_symlink("../../../opt/local/lib/erlang/usr",
join([TDir, "/usr/local/lib/erlang"])),
- ?line E = "/usr/local",
- ?line Bs = "/usr/local/bin",
- ?line Be = Bs,
- ?line EBs = "/usr/local/lib/erlang/../bin",
- ?line EBe = EBs,
+ E = "/usr/local",
+ Bs = "/usr/local/bin",
+ Be = Bs,
+ EBs = "/usr/local/lib/erlang/../bin",
+ EBe = EBs,
ChkRes = fun (Res, #inst{test_prefix = TP,
destdir = D,
extra_prefix = EP,
bindir_symlinks = BSL,
symlinks = SL}) ->
- ?line B = join([TP, D, EP, Be]),
+ B = join([TP, D, EP, Be]),
Expct = case {SL, BSL} of
{false, _} when BSL == "relative";
BSL == "absolute" ->
- ?line {error, no_ln_s};
+ {error, no_ln_s};
{false, _} ->
- ?line {ok,{absolute,
+ {ok,{absolute,
B,join([TP,D,EP,EBe])}};
{true, "relative"} ->
{error, unreachable_absolute};
{true, _} ->
- ?line {ok,{absolute,B,join([TP,EP,EBe])}}
+ {ok,{absolute,B,join([TP,EP,EBe])}}
end,
expect(Expct, Res)
end,
@@ -300,7 +288,7 @@ bin_unreachable_absolute(Config) when is_list(Config) ->
erlang_bindir = EBs}, ChkRes).
bin_unreachable_relative(Config) when is_list(Config) ->
- TDir = ?config(test_dir, Config),
+ TDir = proplists:get_value(test_dir, Config),
make_dirs(TDir, "/opt/local/lib/erlang/bin"),
make_dirs(TDir, "/opt/local/bin"),
make_dirs(TDir, "/usr/local/lib/erlang/bin"),
@@ -311,28 +299,28 @@ bin_unreachable_relative(Config) when is_list(Config) ->
ok = file:make_symlink("../../opt/local/bin",
join([TDir, "/usr/local/bin"])),
- ?line E = "/usr/local",
- ?line Bs = "/usr/local/bin",
- ?line Be = Bs,
- ?line EBs = "/usr/local/lib/erlang/bin",
- ?line EBe = EBs,
+ E = "/usr/local",
+ Bs = "/usr/local/bin",
+ Be = Bs,
+ EBs = "/usr/local/lib/erlang/bin",
+ EBe = EBs,
ChkRes = fun (Res, #inst{test_prefix = TP,
destdir = D,
extra_prefix = EP,
bindir_symlinks = BSL,
symlinks = SL}) ->
- ?line B = join([TP, D, EP, Be]),
+ B = join([TP, D, EP, Be]),
Expct = case {SL, BSL} of
{false, _} when BSL == "relative";
BSL == "absolute" ->
- ?line {error, no_ln_s};
+ {error, no_ln_s};
{false, _} ->
- ?line {ok,{absolute,
+ {ok,{absolute,
B,join([TP,D,EP,EBe])}};
{true, "relative"} ->
{error, unreachable_relative};
{true, _} ->
- ?line {ok,{absolute,B,join([TP,EP,EBe])}}
+ {ok,{absolute,B,join([TP,EP,EBe])}}
end,
expect(Expct, Res)
end,
@@ -341,7 +329,7 @@ bin_unreachable_relative(Config) when is_list(Config) ->
erlang_bindir = EBs}, ChkRes).
bin_ok_symlink(Config) when is_list(Config) ->
- TDir = ?config(test_dir, Config),
+ TDir = proplists:get_value(test_dir, Config),
make_dirs(TDir, "/usr/local/bin"),
make_dirs(TDir, "/opt/local/lib/erlang/bin"),
Erl = join([TDir, "/opt/local/lib/erlang/bin/erl"]),
@@ -350,29 +338,29 @@ bin_ok_symlink(Config) when is_list(Config) ->
ok = file:write_file(Erlc, "erlc"),
ok = file:make_symlink("../../opt/local/lib",
join([TDir, "/usr/local/lib"])),
- ?line E = "/usr/local",
- ?line Bs = "/usr/local/bin",
- ?line Be = Bs,
- ?line EBs = "/usr/local/lib/erlang/bin",
- ?line EBe = EBs,
- ?line RP = "../lib/erlang/bin",
+ E = "/usr/local",
+ Bs = "/usr/local/bin",
+ Be = Bs,
+ EBs = "/usr/local/lib/erlang/bin",
+ EBe = EBs,
+ RP = "../lib/erlang/bin",
ChkRes = fun (Res, #inst{test_prefix = TP,
destdir = D,
extra_prefix = EP,
bindir_symlinks = BSL,
symlinks = SL}) ->
- ?line B = join([TP, D, EP, Be]),
+ B = join([TP, D, EP, Be]),
Expct = case {SL, BSL} of
{false, _} when BSL == "relative";
BSL == "absolute" ->
- ?line {error, no_ln_s};
+ {error, no_ln_s};
{false, _} ->
- ?line {ok,{absolute,
+ {ok,{absolute,
B,join([TP,D,EP,EBe])}};
{true, "absolute"} ->
- ?line {ok,{absolute,B,join([TP,EP,EBe])}};
+ {ok,{absolute,B,join([TP,EP,EBe])}};
{true, _} ->
- ?line {ok,{relative,B,RP}}
+ {ok,{relative,B,RP}}
end,
expect(Expct, Res)
end,
@@ -381,7 +369,7 @@ bin_ok_symlink(Config) when is_list(Config) ->
erlang_bindir = EBs}, ChkRes).
bin_same_dir(Config) when is_list(Config) ->
- TDir = ?config(test_dir, Config),
+ TDir = proplists:get_value(test_dir, Config),
make_dirs(TDir, "/usr/local/bin"),
make_dirs(TDir, "/usr/local/lib"),
ok = file:make_symlink("..", join([TDir, "/usr/local/lib/erlang"])),
@@ -417,29 +405,29 @@ bin_not_abs(Config) when is_list(Config) ->
'bin white space'(Config) when is_list(Config) ->
- ?line E = "/u s r/local",
- ?line Bs = "/u s r/local/b i n",
- ?line Be = Bs,
- ?line EBs = "/u s r/local/lib/erl ang/bin",
- ?line EBe = EBs,
- ?line RP = "../lib/erl ang/bin",
+ E = "/u s r/local",
+ Bs = "/u s r/local/b i n",
+ Be = Bs,
+ EBs = "/u s r/local/lib/erl ang/bin",
+ EBe = EBs,
+ RP = "../lib/erl ang/bin",
ChkRes = fun (Res, #inst{test_prefix = TP,
destdir = D,
extra_prefix = EP,
bindir_symlinks = BSL,
symlinks = SL}) ->
- ?line B = join([TP, D, EP, Be]),
+ B = join([TP, D, EP, Be]),
Expct = case {SL, BSL} of
{false, _} when BSL == "relative";
BSL == "absolute" ->
- ?line {error, no_ln_s};
+ {error, no_ln_s};
{false, _} ->
- ?line {ok,{absolute,
+ {ok,{absolute,
B,join([TP,D,EP,EBe])}};
{true, "absolute"} ->
- ?line {ok,{absolute,B,join([TP,EP,EBe])}};
+ {ok,{absolute,B,join([TP,EP,EBe])}};
{true, _} ->
- ?line {ok,{relative,B,RP}}
+ {ok,{relative,B,RP}}
end,
expect(Expct, Res)
end,
@@ -448,29 +436,29 @@ bin_not_abs(Config) when is_list(Config) ->
erlang_bindir = EBs}, ChkRes).
bin_dirname_fail(Config) when is_list(Config) ->
- ?line E = "/opt",
- ?line Bs = "/opt/lib/../bin",
- ?line Be = Bs,
- ?line EBs = "/opt/lib/erlang/otp/bin",
- ?line EBe = EBs,
- ?line CMDPRFX = "PATH=\""++?config(data_dir,Config)++":"++os:getenv("PATH")++"\"",
+ E = "/opt",
+ Bs = "/opt/lib/../bin",
+ Be = Bs,
+ EBs = "/opt/lib/erlang/otp/bin",
+ EBe = EBs,
+ CMDPRFX = "PATH=\""++proplists:get_value(data_dir,Config)++":"++os:getenv("PATH")++"\"",
ChkRes = fun (Res, #inst{test_prefix = TP,
destdir = D,
extra_prefix = EP,
bindir_symlinks = BSL,
symlinks = SL}) ->
- ?line B = join([TP, D, EP, Be]),
+ B = join([TP, D, EP, Be]),
Expct = case {SL, BSL} of
{false, _} when BSL == "relative";
BSL == "absolute" ->
- ?line {error, no_ln_s};
+ {error, no_ln_s};
{false, _} ->
- ?line {ok,{absolute,
+ {ok,{absolute,
B,join([TP,D,EP,EBe])}};
{true, "relative"} ->
- ?line {error, dirname_failed};
+ {error, dirname_failed};
{true, _} ->
- ?line {ok,{absolute,B,join([TP,EP,EBe])}}
+ {ok,{absolute,B,join([TP,EP,EBe])}}
end,
expect(Expct, Res)
end,
@@ -480,30 +468,30 @@ bin_dirname_fail(Config) when is_list(Config) ->
erlang_bindir = EBs}, ChkRes).
bin_no_use_dirname_fail(Config) when is_list(Config) ->
- ?line E = "/opt",
- ?line Bs = "/opt/bin",
- ?line Be = Bs,
- ?line EBs = "/opt/lib/erlang/otp/bin",
- ?line EBe = EBs,
- ?line RP = "../lib/erlang/otp/bin",
- ?line CMDPRFX = "PATH=\""++?config(data_dir,Config)++":"++os:getenv("PATH")++"\"",
+ E = "/opt",
+ Bs = "/opt/bin",
+ Be = Bs,
+ EBs = "/opt/lib/erlang/otp/bin",
+ EBe = EBs,
+ RP = "../lib/erlang/otp/bin",
+ CMDPRFX = "PATH=\""++proplists:get_value(data_dir,Config)++":"++os:getenv("PATH")++"\"",
ChkRes = fun (Res, #inst{test_prefix = TP,
destdir = D,
extra_prefix = EP,
bindir_symlinks = BSL,
symlinks = SL}) ->
- ?line B = join([TP, D, EP, Be]),
+ B = join([TP, D, EP, Be]),
Expct = case {SL, BSL} of
{false, _} when BSL == "relative";
BSL == "absolute" ->
- ?line {error, no_ln_s};
+ {error, no_ln_s};
{false, _} ->
- ?line {ok,{absolute,
+ {ok,{absolute,
B,join([TP,D,EP,EBe])}};
{true, "absolute"} ->
- ?line {ok,{absolute,B,join([TP,EP,EBe])}};
+ {ok,{absolute,B,join([TP,EP,EBe])}};
{true, _} ->
- ?line {ok,{relative,B,RP}}
+ {ok,{relative,B,RP}}
end,
expect(Expct, Res)
end,
@@ -513,7 +501,7 @@ bin_no_use_dirname_fail(Config) when is_list(Config) ->
erlang_bindir = EBs}, ChkRes).
bin_no_srcfile(Config) when is_list(Config) ->
- TDir = ?config(test_dir, Config),
+ TDir = proplists:get_value(test_dir, Config),
make_dirs(TDir, "/opt/local/bin"),
make_dirs(TDir, "/opt/local/lib/erlang/bin"),
Erl = join([TDir, "/opt/local/lib/erlang/bin/erl"]),
@@ -525,13 +513,13 @@ bin_no_srcfile(Config) when is_list(Config) ->
Expct = case {SL, BSL} of
{false, _} when BSL == "relative";
BSL == "absolute" ->
- ?line {error, no_ln_s};
+ {error, no_ln_s};
{false,_} ->
- ?line {error,{no_srcfile, Erlc}};
+ {error,{no_srcfile, Erlc}};
{true, "absolute"} ->
- ?line {error,{no_srcfile, Erlc}};
+ {error,{no_srcfile, Erlc}};
{true, _} ->
- ?line {error,{no_srcfile, RP_Erlc}}
+ {error,{no_srcfile, RP_Erlc}}
end,
expect(Expct, Res)
end,
@@ -543,40 +531,38 @@ bin_no_srcfile(Config) when is_list(Config) ->
ChkRes).
%%
-%%
%% Auxiliary functions
%%
-%%
expect(X, X) ->
- ?t:format("result: ~p~n", [X]),
- ?t:format("-----------------------------------------------~n", []),
+ io:format("result: ~tp~n", [X]),
+ io:format("-----------------------------------------------~n", []),
ok;
expect(X, Y) ->
- ?t:format("expected: ~p~n", [X]),
- ?t:format("got : ~p~n", [Y]),
- ?t:format("-----------------------------------------------~n", []),
- ?t:fail({X,Y}).
-
+ io:format("expected: ~tp~n", [X]),
+ io:format("got : ~tp~n", [Y]),
+ io:format("-----------------------------------------------~n", []),
+ ct:fail({X,Y}).
+
init_per_suite(Config) ->
- PD = ?config(priv_dir, Config),
- SymLinks = case ?t:os_type() of
- {win32, _} -> false;
- _ ->
- case file:make_symlink("nothing",
- filename:join(PD,
- "symlink_test")) of
- ok -> true;
- _ -> false
- end
- end,
+ PD = proplists:get_value(priv_dir, Config),
+ SymLinks = case os:type() of
+ {win32, _} -> false;
+ _ ->
+ case file:make_symlink("nothing",
+ filename:join(PD, "symlink_test")) of
+ ok -> true;
+ _ -> false
+ end
+ end,
[{symlinks, SymLinks} | Config].
end_per_suite(_Config) ->
ok.
init_per_testcase(Case, Config) ->
- init_per_testcase_aux(?config(symlinks,Config),?t:os_type(),Case,Config).
+ init_per_testcase_aux(proplists:get_value(symlinks,Config),
+ os:type(),Case,Config).
init_per_testcase_aux(_, {win32, _}, _Case, _Config) ->
{skip, "Not on windows"};
@@ -586,18 +572,13 @@ init_per_testcase_aux(false, OsType, Case, Config) ->
true -> {skip, "Cannot create symbolic links"}
end;
init_per_testcase_aux(true, _OsType, Case, Config) ->
- Dog = ?t:timetrap(?DEFAULT_TIMEOUT),
- [{watchdog, Dog},
- {testcase, Case},
- {test_dir, make_dirs(?config(priv_dir, Config), atom_to_list(Case))}
+ [{testcase, Case},
+ {test_dir, make_dirs(proplists:get_value(priv_dir, Config), atom_to_list(Case))}
| Config].
-end_per_testcase(_Case, Config) ->
- Dog = ?config(watchdog, Config),
- ?t:timetrap_cancel(Dog),
+end_per_testcase(_Case, _Config) ->
ok.
-
make_dirs(Root, Suffix) ->
do_make_dirs(Root, string:tokens(Suffix, [$/])).
@@ -616,9 +597,9 @@ install_bin(Config, #inst{mkdirs = MkDirs,
exec_prefix = EXEC_PREFIX,
bindir = BINDIR,
erlang_bindir = ERLANG_BINDIR} = Inst, ChkRes) ->
- PDir = ?config(priv_dir, Config),
- TDir = ?config(test_dir, Config),
- TD = atom_to_list(?config(testcase, Config)),
+ PDir = proplists:get_value(priv_dir, Config),
+ TDir = proplists:get_value(test_dir, Config),
+ TD = atom_to_list(proplists:get_value(testcase, Config)),
case MkDirs of
false -> ok;
true ->
@@ -641,12 +622,12 @@ install_bin(Config, #inst{mkdirs = MkDirs,
bindir = join([TDir, BINDIR]),
erlang_bindir = join([TDir, ERLANG_BINDIR])},
ChkRes),
- case ?config(symlinks, Config) of
+ case proplists:get_value(symlinks, Config) of
true -> ok;
false -> {comment, "No symlink tests run, since symlinks not working"}
end.
-
-
+
+
install_bin2(Config, Inst, ChkRes) ->
install_bin3(Config, Inst#inst{symlinks = false,
ln_s = "ln"}, ChkRes),
@@ -664,7 +645,7 @@ install_bin2(Config, Inst, ChkRes) ->
install_bin3(Config, Inst#inst{symlinks = false,
ln_s = "cp -p",
bindir_symlinks = "absolute"}, ChkRes),
- case ?config(symlinks, Config) of
+ case proplists:get_value(symlinks, Config) of
true ->
install_bin3(Config, Inst#inst{symlinks = true,
ln_s = "ln -s"}, ChkRes),
@@ -677,8 +658,6 @@ install_bin2(Config, Inst, ChkRes) ->
false ->
ok
end.
-
-
install_bin3(Config,
#inst{cmd_prefix = CMD_PRFX,
@@ -690,9 +669,9 @@ install_bin3(Config,
erlang_bindir = ERLANG_BINDIR,
bindir_symlinks = BINDIR_SYMLINKS} = Inst,
ChkRes) ->
- Test = ?config(testcase, Config),
- DDir = ?config(data_dir, Config),
- TDir = ?config(test_dir, Config),
+ Test = proplists:get_value(testcase, Config),
+ DDir = proplists:get_value(data_dir, Config),
+ TDir = proplists:get_value(test_dir, Config),
InstallBin = filename:join(DDir, "install_bin"),
ResFile = filename:join(TDir, atom_to_list(Test) ++ "-result.txt"),
Cmd = CMD_PRFX ++ " "
@@ -705,20 +684,20 @@ install_bin3(Config,
++ "\" --exec-prefix \"" ++ EXEC_PREFIX
++ "\" --test-file \"" ++ ResFile ++ "\" erl erlc",
- ?t:format("CMD_PRFX = \"~s\"~n"
- "LN_S = \"~s\"~n"
- "BINDIR_SYMLINKS = \"~s\"~n"
- "exec_prefix = \"~s\"~n"
- "bindir = \"~s\"~n"
- "erlang_bindir = \"~s\"~n"
- "EXTRA_PREFIX = \"~s\"~n"
- "DESTDIR = \"~s\"~n",
+ io:format("CMD_PRFX = \"~ts\"~n"
+ "LN_S = \"~ts\"~n"
+ "BINDIR_SYMLINKS = \"~ts\"~n"
+ "exec_prefix = \"~ts\"~n"
+ "bindir = \"~ts\"~n"
+ "erlang_bindir = \"~ts\"~n"
+ "EXTRA_PREFIX = \"~ts\"~n"
+ "DESTDIR = \"~ts\"~n",
[CMD_PRFX, LN_S, BINDIR_SYMLINKS, EXEC_PREFIX, BINDIR,
ERLANG_BINDIR, EXTRA_PREFIX, DESTDIR]),
- ?t:format("$ ~s~n", [Cmd]),
+ io:format("$ ~ts~n", [Cmd]),
CmdOutput = os:cmd(Cmd),
- ?t:format("~s~n", [CmdOutput]),
+ io:format("~ts~n", [CmdOutput]),
ChkRes(case file:consult(ResFile) of
{ok, [Res]} -> Res;
Err -> exit({result, Err})
@@ -731,4 +710,3 @@ join([""|Ds]) ->
join(Ds);
join([D|Ds]) ->
"/" ++ string:strip(D, both, $/) ++ join(Ds).
-
diff --git a/erts/test/nt_SUITE.erl b/erts/test/nt_SUITE.erl
index dbae8df7fe..624e5484ba 100644
--- a/erts/test/nt_SUITE.erl
+++ b/erts/test/nt_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,58 +20,39 @@
%%% Purpose: Test NT specific utilities
-module(nt_SUITE).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include_lib("kernel/include/file.hrl").
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,init_per_testcase/2,
- end_per_testcase/2,nt/1,handle_eventlog/2,
- middleman/1,service_basic/1, service_env/1, user_env/1, synced/1,
- service_prio/1,
- logout/1, debug/1, restart/1, restart_always/1,stopaction/1,
- shutdown_io/0,do_shutdown_io/0]).
--define(TEST_TIMEOUT, ?t:seconds(180)).
+-export([all/0, suite/0,
+ init_per_testcase/2, end_per_testcase/2,
+ nt/1,handle_eventlog/2,
+ middleman/1,service_basic/1, service_env/1, user_env/1, synced/1,
+ service_prio/1,
+ logout/1, debug/1, restart/1, restart_always/1,stopaction/1,
+ shutdown_io/0,do_shutdown_io/0]).
-define(TEST_SERVICES, [1,2,3,4,5,6,7,8,9,10,11]).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 3}}].
-all() ->
- case os:type() of
- {win32, nt} ->
+all() ->
+ case {os:type(), os:version()} of
+ {{win32, nt}, Vsn} when Vsn =< {6,1,999999} ->
[nt, service_basic, service_env, user_env, synced,
service_prio, logout, debug, restart, restart_always,
stopaction];
_ -> [nt]
end.
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
init_per_testcase(_Func, Config) ->
- Dog = test_server:timetrap(?TEST_TIMEOUT),
- [{watchdog, Dog} | Config].
+ Config.
-end_per_testcase(_Func, Config) ->
+end_per_testcase(_Func, _Config) ->
lists:foreach(fun(X) ->
- catch remove_service("test_service_" ++
- integer_to_list(X)) end,
- ?TEST_SERVICES),
- Dog = ?config(watchdog, Config),
- catch test_server:timetrap_cancel(Dog),
+ catch remove_service("test_service_" ++ integer_to_list(X))
+ end, ?TEST_SERVICES),
ok.
erlsrv() ->
@@ -80,19 +61,18 @@ erlsrv() ->
recv_prog_output(Port) ->
receive
- {Port, {data, {eol,Data}}} ->
- %%io:format("Got data: ~s~n", [Data]),
- [ Data | recv_prog_output(Port)];
- _X ->
- %%io:format("Got data: ~p~n", [_X]),
- Port ! {self(), close},
- receive
- _ ->
- []
- end
+ {Port, {data, {eol,Data}}} ->
+ %%io:format("Got data: ~s~n", [Data]),
+ [ Data | recv_prog_output(Port)];
+ _X ->
+ %%io:format("Got data: ~p~n", [_X]),
+ Port ! {self(), close},
+ receive
+ _ ->
+ []
+ end
end.
-
%%% X == parameters to erlsrv
%%% returns command output without stderr
do_command(X) ->
@@ -100,11 +80,11 @@ do_command(X) ->
Port = open_port({spawn, erlsrv() ++ " " ++ X}, [stream, {line, 100}, eof, in]),
Res = recv_prog_output(Port),
case Res of
- [] ->
- failed;
- _Y ->
- %%io:format("~p~n",[_Y]),
- ok
+ [] ->
+ failed;
+ _Y ->
+ %%io:format("~p~n",[_Y]),
+ ok
end.
@@ -123,13 +103,13 @@ do_wait_for_it(_,0) ->
false;
do_wait_for_it(FullName,N) ->
case net_adm:ping(FullName) of
- pong ->
- true;
- _ ->
- receive
- after 1000 ->
- do_wait_for_it(FullName,N-1)
- end
+ pong ->
+ true;
+ _ ->
+ receive
+ after 1000 ->
+ do_wait_for_it(FullName,N-1)
+ end
end.
wait_for_node(Name) ->
@@ -139,309 +119,282 @@ wait_for_node(Name) ->
make_full_name(Name) ->
[_,Suffix] = string:tokens(atom_to_list(node()),"@"),
list_to_atom(Name ++ "@" ++ Suffix).
-
+
%%% The following tests are only run on NT:
-service_basic(doc) ->
- ["Check some basic (cosmetic) service parameters"];
-service_basic(suite) -> [];
+%% Check some basic (cosmetic) service parameters
service_basic(Config) when is_list(Config) ->
- ?line Name = "test_service_20",
- ?line IntName = Name++"_internal",
- ?line Service = [{servicename,Name},
- {args, ["-setcookie",
- atom_to_list(erlang:get_cookie())]},
- {internalservicename,IntName},
- {comment,"Epic comment"}],
- ?line ok = erlsrv:store_service(Service),
- ?line start_service(Name),
- ?line true = wait_for_node(Name),
- ?line S2 = erlsrv:get_service(Name),
- ?line {value,{comment,"Epic comment"}} = lists:keysearch(comment,1,S2),
- ?line {value,{internalservicename,IntName}} =
- lists:keysearch(internalservicename,1,S2),
- ?line S3 = lists:keyreplace(comment,1,S2,{comment,"Basic comment"}),
- ?line S4 = lists:keyreplace(internalservicename,1,S3,
- {internalservicename,"WillNotHappen"}),
- ?line ok = erlsrv:store_service(S4),
- ?line S5 = erlsrv:get_service(Name),
- ?line {value,{comment,"Basic comment"}} = lists:keysearch(comment,1,S5),
- ?line {value,{internalservicename,IntName}} =
- lists:keysearch(internalservicename,1,S5),
- ?line NewName = "test_service_21",
- ?line S6 = erlsrv:new_service(NewName,S5,[]), % should remove
- % internalservicename
- ?line ok = erlsrv:store_service(S6),
- ?line S7 = erlsrv:get_service(NewName),
- ?line {value,{comment,"Basic comment"}} = lists:keysearch(comment,1,S7),
- ?line {value,{internalservicename,[$t,$e,$s,$t | _]}} =
- lists:keysearch(internalservicename,1,S7),
- ?line remove_service(Name),
- ?line remove_service(NewName),
+ Name = "test_service_20",
+ IntName = Name++"_internal",
+ Service = [{servicename,Name},
+ {args, ["-setcookie", atom_to_list(erlang:get_cookie())]},
+ {internalservicename,IntName},
+ {comment,"Epic comment"}],
+ ok = erlsrv:store_service(Service),
+ start_service(Name),
+ true = wait_for_node(Name),
+ S2 = erlsrv:get_service(Name),
+ {value,{comment,"Epic comment"}} = lists:keysearch(comment,1,S2),
+ {value,{internalservicename,IntName}} =
+ lists:keysearch(internalservicename,1,S2),
+ S3 = lists:keyreplace(comment,1,S2,{comment,"Basic comment"}),
+ S4 = lists:keyreplace(internalservicename,1,S3,
+ {internalservicename,"WillNotHappen"}),
+ ok = erlsrv:store_service(S4),
+ S5 = erlsrv:get_service(Name),
+ {value,{comment,"Basic comment"}} = lists:keysearch(comment,1,S5),
+ {value,{internalservicename,IntName}} =
+ lists:keysearch(internalservicename,1,S5),
+ NewName = "test_service_21",
+ S6 = erlsrv:new_service(NewName,S5,[]), % should remove
+ % internalservicename
+ ok = erlsrv:store_service(S6),
+ S7 = erlsrv:get_service(NewName),
+ {value,{comment,"Basic comment"}} = lists:keysearch(comment,1,S7),
+ {value,{internalservicename,[$t,$e,$s,$t | _]}} =
+ lists:keysearch(internalservicename,1,S7),
+ remove_service(Name),
+ remove_service(NewName),
ok.
-service_env(doc) ->
- ["Check that service name and executable is in the environment of the " ++
- "erlang process created by erlsrv."];
-service_env(suite) -> [];
+%% Check that service name and executable is in the environment of the
+%% erlang process created by erlsrv.
service_env(Config) when is_list(Config) ->
- ?line Name = "test_service_2",
- ?line Service = [{servicename,Name},
- {args, ["-setcookie",
- atom_to_list(erlang:get_cookie())]}],
- ?line ok = erlsrv:store_service(Service),
- ?line start_service(Name),
- ?line true = wait_for_node(Name),
- ?line Name = rpc:call(make_full_name(Name),os,getenv,
- ["ERLSRV_SERVICE_NAME"]),
- ?line "erlsrv.exe" = filename:basename(
- hd(
- string:tokens(
- rpc:call(make_full_name(Name),
- os,
- getenv,
- ["ERLSRV_EXECUTABLE"]),
- "\""))),
- ?line remove_service(Name),
+ Name = "test_service_2",
+ Service = [{servicename,Name},
+ {args, ["-setcookie", atom_to_list(erlang:get_cookie())]}],
+ ok = erlsrv:store_service(Service),
+ start_service(Name),
+ true = wait_for_node(Name),
+ Name = rpc:call(make_full_name(Name),os,getenv,
+ ["ERLSRV_SERVICE_NAME"]),
+ "erlsrv.exe" = filename:basename(
+ hd(
+ string:tokens(
+ rpc:call(make_full_name(Name),
+ os,
+ getenv,
+ ["ERLSRV_EXECUTABLE"]),
+ "\""))),
+ remove_service(Name),
ok.
-user_env(doc) ->
- ["Check that the user defined environment is ADDED to the service's"++
- " normal dito."];
-user_env(suite) -> [];
+
+%% Check that the user defined environment is ADDED to the service's
+%% normal dito.
user_env(Config) when is_list(Config) ->
- ?line Name = "test_service_3",
- ?line Service = [{servicename,Name},{env,[{"HUBBA","BUBBA"}]},
- {args, ["-setcookie",
- atom_to_list(erlang:get_cookie())]}],
- ?line ok = erlsrv:store_service(Service),
- ?line start_service(Name),
- ?line true = wait_for_node(Name),
- ?line true = rpc:call(make_full_name(Name),os,getenv,
- ["SystemDrive"]) =/= false,
- ?line "BUBBA" = rpc:call(make_full_name(Name),os,getenv,["HUBBA"]),
- ?line remove_service(Name),
+ Name = "test_service_3",
+ Service = [{servicename,Name},{env,[{"HUBBA","BUBBA"}]},
+ {args, ["-setcookie", atom_to_list(erlang:get_cookie())]}],
+ ok = erlsrv:store_service(Service),
+ start_service(Name),
+ true = wait_for_node(Name),
+ true = rpc:call(make_full_name(Name),os,getenv,
+ ["SystemDrive"]) =/= false,
+ "BUBBA" = rpc:call(make_full_name(Name),os,getenv,["HUBBA"]),
+ remove_service(Name),
ok.
-synced(doc) ->
- ["Check that services are stopped and started syncronous and that"++
- " failed stopactions kill the erlang machine anyway."];
-synced(suite) -> [];
+
+%% Check that services are stopped and started syncronous and that
+%% failed stopactions kill the erlang machine anyway.
synced(Config) when is_list(Config) ->
- ?line Name0 = "test_service_4",
- ?line Service0 = [{servicename,Name0},
- {machine, "N:\\nickeNyfikenPaSjukhus"}],
- ?line ok = erlsrv:store_service(Service0),
- ?line true = (catch start_service(Name0)) =/= ok,
- ?line remove_service(Name0),
- ?line Name = "test_service_5",
- ?line Service = [{servicename,Name},
- {stopaction,"erlang:info(garbage_collection)."},
- {args, ["-setcookie",
- atom_to_list(erlang:get_cookie())]}],
- ?line ok = erlsrv:store_service(Service),
- ?line start_service(Name),
- ?line true = wait_for_node(Name),
- ?line T1 = calendar:datetime_to_gregorian_seconds(
- calendar:universal_time()),
- ?line stop_service(Name),
- ?line Diff1 = calendar:datetime_to_gregorian_seconds(
- calendar:universal_time()) - T1,
- ?line true = Diff1 > 30,
- ?line start_service(Name),
- ?line true = wait_for_node(Name),
- ?line T2 = calendar:datetime_to_gregorian_seconds(
- calendar:universal_time()),
- ?line remove_service(Name),
- ?line Diff2 = calendar:datetime_to_gregorian_seconds(
- calendar:universal_time()) - T2,
- ?line true = Diff2 > 30,
+ Name0 = "test_service_4",
+ Service0 = [{servicename,Name0},
+ {machine, "N:\\nickeNyfikenPaSjukhus"}],
+ ok = erlsrv:store_service(Service0),
+ true = (catch start_service(Name0)) =/= ok,
+ remove_service(Name0),
+ Name = "test_service_5",
+ Service = [{servicename,Name},
+ {stopaction,"erlang:info(garbage_collection)."},
+ {args, ["-setcookie", atom_to_list(erlang:get_cookie())]}],
+ ok = erlsrv:store_service(Service),
+ start_service(Name),
+ true = wait_for_node(Name),
+ T1 = calendar:datetime_to_gregorian_seconds(
+ calendar:universal_time()),
+ stop_service(Name),
+ Diff1 = calendar:datetime_to_gregorian_seconds(
+ calendar:universal_time()) - T1,
+ true = Diff1 > 30,
+ start_service(Name),
+ true = wait_for_node(Name),
+ T2 = calendar:datetime_to_gregorian_seconds(
+ calendar:universal_time()),
+ remove_service(Name),
+ Diff2 = calendar:datetime_to_gregorian_seconds(
+ calendar:universal_time()) - T2,
+ true = Diff2 > 30,
ok.
-service_prio(doc) ->
- ["Check that a service with higher prio create port programs with "
- "higher prio."];
-service_prio(suite) -> [];
+
+%% Check that a service with higher prio create port programs with
+%% higher prio.
service_prio(Config) when is_list(Config) ->
- ?line Name = "test_service_6",
- ?line Service = [{servicename,Name},{prio,"high"},
- {env, [{"HEART_COMMAND","echo off"}]},
- {args, ["-setcookie",
- atom_to_list(erlang:get_cookie()),
- "-heart"]}],
- ?line ok = erlsrv:store_service(Service),
- ?line {ok, OldProcs} = get_current_procs(Config),
- ?line start_service(Name),
- ?line {ok, NewProcs} = get_current_procs(Config),
+ Name = "test_service_6",
+ Service = [{servicename,Name},{prio,"high"},
+ {env, [{"HEART_COMMAND","echo off"}]},
+ {args, ["-setcookie", atom_to_list(erlang:get_cookie()),
+ "-heart"]}],
+ ok = erlsrv:store_service(Service),
+ {ok, OldProcs} = get_current_procs(Config),
+ start_service(Name),
+ {ok, NewProcs} = get_current_procs(Config),
timer:sleep(2000),
- ?line {ok, NewProcs2} = get_current_procs(Config),
- ?line remove_service(Name),
- ?line Diff = arrived_procs(OldProcs,NewProcs),
+ {ok, NewProcs2} = get_current_procs(Config),
+ remove_service(Name),
+ Diff = arrived_procs(OldProcs,NewProcs),
io:format("NewProcs ~p~n after sleep~n ~p~n",[Diff, arrived_procs(OldProcs,NewProcs2)]),
%% Not really correct, could fail if another heart is
%% started at the same time...
- ?line {value, {"heart.exe",_,"high"}} =
- lists:keysearch("heart.exe",1,Diff),
+ {value, {"heart.exe",_,"high"}} = lists:keysearch("heart.exe",1,Diff),
ok.
-logout(doc) ->
- ["Check that logout does not kill services"];
-logout(suite) -> [];
+
+%% Check that logout does not kill services
logout(Config) when is_list(Config) ->
- ?line {comment, "Have to be run manually by registering a service with " ++
- "heart, logout and log in again and then examine that the heart " ++
- "process id is not changed."}.
-debug(doc) ->
- ["Check the debug options to erlsrv."];
-debug(suite) -> [];
+ {comment, "Have to be run manually by registering a service with " ++
+ "heart, logout and log in again and then examine that the heart " ++
+ "process id is not changed."}.
+
+%% Check the debug options to erlsrv.
debug(Config) when is_list(Config) ->
- ?line Name0 = "test_service_7",
+ Name0 = "test_service_7",
%% We used to set the privdir as temporary directory, but for some
%% reason we don't seem to have write access to that directory,
%% so we'll use the directory specified in the next line.
- ?line TempDir = "C:/TEMP",
- ?line Service0 = [{servicename,Name0},
- {workdir,filename:nativename(TempDir)},
- {debugtype,"reuse"},
- {args, ["-setcookie",
- atom_to_list(erlang:get_cookie())]}],
- ?line ok = erlsrv:store_service(Service0),
- ?line T1 = calendar:datetime_to_gregorian_seconds(
- calendar:local_time()),
+ TempDir = "C:/TEMP",
+ Service0 = [{servicename,Name0},
+ {workdir,filename:nativename(TempDir)},
+ {debugtype,"reuse"},
+ {args, ["-setcookie", atom_to_list(erlang:get_cookie())]}],
+ ok = erlsrv:store_service(Service0),
+ T1 = calendar:datetime_to_gregorian_seconds(
+ calendar:local_time()),
%% sleep a little
- ?line receive after 2000 -> ok end,
- ?line start_service(Name0),
- ?line true = wait_for_node(Name0),
- ?line LF = filename:join(TempDir, Name0++".debug"),
- ?line {ok,Info0} = file:read_file_info(LF),
- ?line T2 = calendar:datetime_to_gregorian_seconds(
- Info0#file_info.mtime),
- ?line true = T2 > T1,
- ?line remove_service(Name0),
- ?line file:delete(LF),
- ?line Name1 = "test_service_8",
- ?line Service1 = [{servicename,Name1},
- {workdir, filename:nativename(TempDir)},
- {debugtype,"new"},
- {args, ["-setcookie",
- atom_to_list(erlang:get_cookie())]}],
- ?line ok = erlsrv:store_service(Service1),
- ?line T3 = calendar:datetime_to_gregorian_seconds(
- calendar:local_time()),
+ receive after 2000 -> ok end,
+ start_service(Name0),
+ true = wait_for_node(Name0),
+ LF = filename:join(TempDir, Name0++".debug"),
+ {ok,Info0} = file:read_file_info(LF),
+ T2 = calendar:datetime_to_gregorian_seconds(
+ Info0#file_info.mtime),
+ true = T2 > T1,
+ remove_service(Name0),
+ file:delete(LF),
+ Name1 = "test_service_8",
+ Service1 = [{servicename,Name1},
+ {workdir, filename:nativename(TempDir)},
+ {debugtype,"new"},
+ {args, ["-setcookie", atom_to_list(erlang:get_cookie())]}],
+ ok = erlsrv:store_service(Service1),
+ T3 = calendar:datetime_to_gregorian_seconds(
+ calendar:local_time()),
%% sleep a little
- ?line receive after 2000 -> ok end,
- ?line NF = next_logfile(TempDir, Name1),
- ?line start_service(Name1),
- ?line true = wait_for_node(Name1),
- ?line {ok,Info1} = file:read_file_info(NF),
- ?line T4 = calendar:datetime_to_gregorian_seconds(
- Info1#file_info.mtime),
- ?line true = T4 > T3,
- ?line remove_service(Name1),
- ?line file:delete(NF),
+ receive after 2000 -> ok end,
+ NF = next_logfile(TempDir, Name1),
+ start_service(Name1),
+ true = wait_for_node(Name1),
+ {ok,Info1} = file:read_file_info(NF),
+ T4 = calendar:datetime_to_gregorian_seconds(
+ Info1#file_info.mtime),
+ true = T4 > T3,
+ remove_service(Name1),
+ file:delete(NF),
ok.
-restart(doc) ->
- ["Check the restart options to erlsrv"];
-restart(suite) -> [];
+%% Check the restart options to erlsrv
restart(Config) when is_list(Config) ->
- ?line Name = "test_service_9",
- ?line Service = [{servicename,Name},
- {workdir, filename:nativename(logdir(Config))},
- {onfail,"restart"},
- {args, ["-setcookie",
- atom_to_list(erlang:get_cookie())]}],
- ?line ok = erlsrv:store_service(Service),
- ?line start_service(Name),
- ?line true = wait_for_node(Name),
- ?line receive after 20000 -> ok end,
- ?line rpc:call(make_full_name(Name),erlang,halt,[]),
- ?line receive after 1000 -> ok end,
- ?line true = wait_for_node(Name),
- ?line rpc:call(make_full_name(Name),erlang,halt,[]),
- ?line receive after 1000 -> ok end,
- ?line false = wait_for_node(Name),
- ?line remove_service(Name),
+ Name = "test_service_9",
+ Service = [{servicename,Name},
+ {workdir, filename:nativename(logdir(Config))},
+ {onfail,"restart"},
+ {args, ["-setcookie", atom_to_list(erlang:get_cookie())]}],
+ ok = erlsrv:store_service(Service),
+ start_service(Name),
+ true = wait_for_node(Name),
+ receive after 20000 -> ok end,
+ rpc:call(make_full_name(Name),erlang,halt,[]),
+ receive after 1000 -> ok end,
+ true = wait_for_node(Name),
+ rpc:call(make_full_name(Name),erlang,halt,[]),
+ receive after 1000 -> ok end,
+ false = wait_for_node(Name),
+ remove_service(Name),
ok.
-restart_always(doc) ->
- ["Check the restart options to erlsrv"];
-restart_always(suite) -> [];
+%% Check the restart options to erlsrv
restart_always(Config) when is_list(Config) ->
- ?line Name = "test_service_10",
- ?line Service = [{servicename,Name},
- {workdir, filename:nativename(logdir(Config))},
- {onfail,"restart_always"},
- {args, ["-setcookie",
- atom_to_list(erlang:get_cookie())]}],
- ?line ok = erlsrv:store_service(Service),
- ?line start_service(Name),
- ?line true = wait_for_node(Name),
- ?line rpc:call(make_full_name(Name),erlang,halt,[]),
- ?line receive after 1000 -> ok end,
- ?line true = wait_for_node(Name),
- ?line rpc:call(make_full_name(Name),erlang,halt,[]),
- ?line receive after 1000 -> ok end,
- ?line true = wait_for_node(Name),
- ?line remove_service(Name),
+ Name = "test_service_10",
+ Service = [{servicename,Name},
+ {workdir, filename:nativename(logdir(Config))},
+ {onfail,"restart_always"},
+ {args, ["-setcookie", atom_to_list(erlang:get_cookie())]}],
+ ok = erlsrv:store_service(Service),
+ start_service(Name),
+ true = wait_for_node(Name),
+ rpc:call(make_full_name(Name),erlang,halt,[]),
+ receive after 1000 -> ok end,
+ true = wait_for_node(Name),
+ rpc:call(make_full_name(Name),erlang,halt,[]),
+ receive after 1000 -> ok end,
+ true = wait_for_node(Name),
+ remove_service(Name),
ok.
-stopaction(doc) ->
- ["Check that stopaction does not hang output while shutting down"];
-stopaction(suite) -> [];
+
+%% Check that stopaction does not hang output while shutting down
stopaction(Config) when is_list(Config) ->
- ?line Name = "test_service_11",
+ Name = "test_service_11",
%% Icky, I prepend the first element in the codepath, cause
%% I "suppose" it's the one to where I am.
- ?line Service = [{servicename,Name},
- {stopaction,atom_to_list(?MODULE) ++ ":shutdown_io()."},
- {args, ["-setcookie",
- atom_to_list(erlang:get_cookie()),
- "-pa", hd(code:get_path())]}],
- ?line ok = erlsrv:store_service(Service),
- ?line start_service(Name),
- ?line true = wait_for_node(Name),
- ?line T1 = calendar:datetime_to_gregorian_seconds(
- calendar:universal_time()),
- ?line stop_service(Name),
- ?line Diff1 = calendar:datetime_to_gregorian_seconds(
- calendar:universal_time()) - T1,
- ?line true = Diff1 < 30,
- ?line remove_service(Name),
+ Service = [{servicename,Name},
+ {stopaction,atom_to_list(?MODULE) ++ ":shutdown_io()."},
+ {args, ["-setcookie", atom_to_list(erlang:get_cookie()),
+ "-pa", hd(code:get_path())]}],
+ ok = erlsrv:store_service(Service),
+ start_service(Name),
+ true = wait_for_node(Name),
+ T1 = calendar:datetime_to_gregorian_seconds(
+ calendar:universal_time()),
+ stop_service(Name),
+ Diff1 = calendar:datetime_to_gregorian_seconds(
+ calendar:universal_time()) - T1,
+ true = Diff1 < 30,
+ remove_service(Name),
ok.
%%% This test is run on all platforms, but just gives a comment on
%%% other platforms than NT.
-nt(doc) ->
- ["Run NT specific tests."];
-nt(suite) ->
- [];
nt(Config) when is_list(Config) ->
- case os:type() of
- {win32,nt} ->
+ case {os:type(), os:version()} of
+ {{win32, nt}, Vsn} when Vsn =< {6,1,999999} ->
nt_run();
+ {{win32, nt}, _} ->
+ {skipped, "This test case requires admin privileges on Win 8 and later."};
_ ->
{skipped, "This test case is intended for Win NT only."}
end.
nt_run() ->
- ?line start_all(),
- ?line create_service("test_service_1"),
- ?line R = start_look_for_single("System","ErlSrv","Informational",
- ".*test_service_1.*started.*"),
- ?line start_service("test_service_1"),
- ?line Res = look_for_single(R),
- ?line io:format("Result from eventlog: ~p~n",
- [Res]),
- ?line remove_service("test_service_1"),
- ?line stop_all(),
+ start_all(),
+ create_service("test_service_1"),
+ R = start_look_for_single("System","ErlSrv","Informational",
+ ".*test_service_1.*started.*"),
+ start_service("test_service_1"),
+ Res = look_for_single(R),
+ io:format("Result from eventlog: ~p~n",
+ [Res]),
+ remove_service("test_service_1"),
+ stop_all(),
ok.
start_all() ->
Pid1 = spawn_link(?MODULE,middleman,[[]]),
register(?MODULE,Pid1),
_Pid2 = nteventlog:start("log_testing",
- {?MODULE,handle_eventlog,[Pid1]}).
+ {?MODULE,handle_eventlog,[Pid1]}).
stop_all() ->
?MODULE ! stop,
@@ -454,10 +407,10 @@ start_look_for_single(Cat,Fac,Sev,MessRE) ->
look_for_single(Ref) ->
receive
- {Ref,Time,Mes} ->
- {Time,Mes}
+ {Ref,Time,Mes} ->
+ {Time,Mes}
after 60000 ->
- timeout
+ timeout
end.
@@ -468,25 +421,25 @@ handle_eventlog(Mes,Pid) ->
%%% Waitfor = [{Pid, Ref, {Category,Facility,Severity,MessageRE}} ...]
middleman(Waitfor) ->
receive
- {Time,Category,Facility,Severity,Message} ->
- io:format("Middleman got ~s...", [Message]),
- case match_event({Time,Category,Facility,Severity,Message},
- Waitfor) of
- {ok, {Pid,Ref,Time,Mes}, Rest} ->
- io:format("matched~n"),
- Pid ! {Ref,Time,Mes},
- middleman(Rest);
- _ ->
- io:format("no match~n"),
- middleman(Waitfor)
- end;
- {lookfor, X} ->
- io:format("Middleman told to look for ~p~n", [X]),
- middleman([X|Waitfor]);
- stop ->
- stopped;
- _ ->
- middleman(Waitfor)
+ {Time,Category,Facility,Severity,Message} ->
+ io:format("Middleman got ~s...", [Message]),
+ case match_event({Time,Category,Facility,Severity,Message},
+ Waitfor) of
+ {ok, {Pid,Ref,Time,Mes}, Rest} ->
+ io:format("matched~n"),
+ Pid ! {Ref,Time,Mes},
+ middleman(Rest);
+ _ ->
+ io:format("no match~n"),
+ middleman(Waitfor)
+ end;
+ {lookfor, X} ->
+ io:format("Middleman told to look for ~p~n", [X]),
+ middleman([X|Waitfor]);
+ stop ->
+ stopped;
+ _ ->
+ middleman(Waitfor)
end.
@@ -495,81 +448,81 @@ match_event(_X, []) ->
nomatch;
match_event({Time,Cat,Fac,Sev,Mes},[{Pid,Ref,{Cat,Fac,Sev,MesRE}} | Tail]) ->
case re:run(Mes,MesRE,[{capture,none}]) of
- match ->
- %%io:format("Match!~n"),
- {ok,{Pid,Ref,Time,Mes},Tail};
- nomatch ->
- %%io:format("No match~n"),
- case match_event({Time,Cat,Fac,Sev,Mes},Tail) of
- {ok,X,Rest} ->
- {ok,X,[{Pid,Ref,{Cat,Fac,Sev,MesRE}} | Rest]};
- X ->
- X
- end
+ match ->
+ %%io:format("Match!~n"),
+ {ok,{Pid,Ref,Time,Mes},Tail};
+ nomatch ->
+ %%io:format("No match~n"),
+ case match_event({Time,Cat,Fac,Sev,Mes},Tail) of
+ {ok,X,Rest} ->
+ {ok,X,[{Pid,Ref,{Cat,Fac,Sev,MesRE}} | Rest]};
+ X ->
+ X
+ end
end;
match_event(X,[Y | T]) ->
%%io:format("X == ~p, Y == ~p~n",[X,Y]),
case match_event(X,T) of
- {ok,Z,R} ->
- {ok,Z,[Y|R]};
- XX ->
- XX
+ {ok,Z,R} ->
+ {ok,Z,[Y|R]};
+ XX ->
+ XX
end.
arrived_procs(_,[]) ->
[];
arrived_procs(OldProcs,[{Executable, Pid, Priority} | TNewProcs]) ->
case lists:keysearch(Pid,2,OldProcs) of
- {value, _} ->
- arrived_procs(OldProcs, TNewProcs);
- false ->
- [{Executable, Pid, Priority} | arrived_procs(OldProcs, TNewProcs)]
+ {value, _} ->
+ arrived_procs(OldProcs, TNewProcs);
+ false ->
+ [{Executable, Pid, Priority} | arrived_procs(OldProcs, TNewProcs)]
end.
-
+
get_current_procs(Config) ->
- ?line P = open_port({spawn,nt_info(Config) ++ " -E"},
- [{line,10000}]),
- ?line L = receive
- {P,{data,{eol,D}}} ->
- D;
- _ -> "error. "
- end,
- ?line P ! {self(), close},
- ?line receive
- {P, closed} -> ok
- end,
- ?line {done,{ok,Tok,_},_} = erl_scan:tokens([],L,0),
- ?line erl_parse:parse_term(Tok).
+ P = open_port({spawn,nt_info(Config) ++ " -E"},
+ [{line,10000}]),
+ L = receive
+ {P,{data,{eol,D}}} ->
+ D;
+ _ -> "error. "
+ end,
+ P ! {self(), close},
+ receive
+ {P, closed} -> ok
+ end,
+ {done,{ok,Tok,_},_} = erl_scan:tokens([],L,0),
+ erl_parse:parse_term(Tok).
nt_info(Config) when is_list(Config) ->
- ?line "\"" ++ filename:join(?config(data_dir, Config), "nt_info") ++ "\"".
+ "\"" ++ filename:join(proplists:get_value(data_dir, Config), "nt_info") ++ "\"".
logdir(Config) ->
- ?line ?config(priv_dir, Config).
+ proplists:get_value(priv_dir, Config).
look_for_next(Template,L,N) ->
- ?line FN = Template ++ integer_to_list(N),
- ?line case lists:member(FN,L) of
- true ->
- ?line look_for_next(Template,L,N+1);
- false ->
- ?line FN
+ FN = Template ++ integer_to_list(N),
+ case lists:member(FN,L) of
+ true ->
+ look_for_next(Template,L,N+1);
+ false ->
+ FN
end.
next_logfile(LD, Servicename) ->
- ?line {ok, Files} = file:list_dir(LD),
- ?line Ftmpl = Servicename ++ ".debug.",
- ?line filename:join(LD,look_for_next(Ftmpl,Files,1)).
+ {ok, Files} = file:list_dir(LD),
+ Ftmpl = Servicename ++ ".debug.",
+ filename:join(LD,look_for_next(Ftmpl,Files,1)).
%%% Functions run by the service
do_shutdown_io() ->
receive
after 2000 ->
- io:format("IO in shutting down...~n"),
- erlang:halt()
+ io:format("IO in shutting down...~n"),
+ erlang:halt()
end.
shutdown_io() ->
diff --git a/erts/test/nt_SUITE_data/Makefile.src b/erts/test/nt_SUITE_data/Makefile.src
index 26da26b195..2317828337 100644
--- a/erts/test/nt_SUITE_data/Makefile.src
+++ b/erts/test/nt_SUITE_data/Makefile.src
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2009. All Rights Reserved.
+# Copyright Ericsson AB 1998-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/test/nt_SUITE_data/nt_info.c b/erts/test/nt_SUITE_data/nt_info.c
index 41d9a44c18..8ef52cad2d 100644
--- a/erts/test/nt_SUITE_data/nt_info.c
+++ b/erts/test/nt_SUITE_data/nt_info.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/test/otp_SUITE.erl b/erts/test/otp_SUITE.erl
index 69a0d19719..54fcfd935f 100644
--- a/erts/test/otp_SUITE.erl
+++ b/erts/test/otp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,18 +20,20 @@
-module(otp_SUITE).
--export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
- init_per_suite/1,end_per_suite/1]).
+-export([all/0, suite/0,
+ init_per_suite/1,end_per_suite/1]).
-export([undefined_functions/1,deprecated_not_in_obsolete/1,
- obsolete_but_not_deprecated/1,call_to_deprecated/1,
+ obsolete_but_not_deprecated/1,call_to_deprecated/1,
call_to_size_1/1,call_to_now_0/1,strong_components/1,
- erl_file_encoding/1,xml_file_encoding/1,runtime_dependencies/1]).
+ erl_file_encoding/1,xml_file_encoding/1,runtime_dependencies/1]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-import(lists, [filter/2,foldl/3,foreach/2]).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 10}}].
all() ->
[undefined_functions, deprecated_not_in_obsolete,
@@ -40,54 +42,41 @@ all() ->
erl_file_encoding, xml_file_encoding,
runtime_dependencies].
-groups() ->
- [].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
init_per_suite(Config) ->
- Dog = test_server:timetrap(?t:minutes(10)),
Root = code:root_dir(),
Server = daily_xref,
- ?line xref:start(Server),
- ?line xref:set_default(Server, [{verbose,false},
- {warnings,false},
- {builtins,true}]),
- ?line {ok,_Relname} = xref:add_release(Server, Root, {name,otp}),
+ xref:start(Server),
+ xref:set_default(Server, [{verbose,false},
+ {warnings,false},
+ {builtins,true}]),
+ {ok,_Relname} = xref:add_release(Server, Root, {name,otp}),
%% If we are running the tests in the source tree, the ERTS application
%% is not in the code path. We must add it explicitly.
case code:lib_dir(erts) of
- {error,bad_name} ->
- Erts = filename:join([code:root_dir(),"erts","preloaded","ebin"]),
- ?line {ok,_} = xref:add_directory(Server, Erts, []);
- _ ->
- ok
+ {error,bad_name} ->
+ Erts = filename:join([code:root_dir(),"erts","preloaded","ebin"]),
+ {ok,_} = xref:add_directory(Server, Erts, []);
+ _ ->
+ ok
end,
-
- ?line ?t:timetrap_cancel(Dog),
[{xref_server,Server}|Config].
end_per_suite(Config) ->
- Server = ?config(xref_server, Config),
+ Server = proplists:get_value(xref_server, Config),
catch xref:stop(Server),
Config.
undefined_functions(Config) when is_list(Config) ->
- Server = ?config(xref_server, Config),
+ Server = proplists:get_value(xref_server, Config),
%% Exclude calls from generated modules in the SSL application.
ExcludeFrom = "SSL-PKIX|PKIX.*|ssl_pkix_oid",
- ?line UndefS = xref_base:analysis(undefined_function_calls),
- ?line Q = io_lib:format("Undef = ~s,"
- "ExcludedFrom = ~p:_/_,"
- "Undef - Undef | ExcludedFrom",
- [UndefS,ExcludeFrom]),
+ UndefS = xref_base:analysis(undefined_function_calls),
+ Q = io_lib:format("Undef = ~s,"
+ "ExcludedFrom = ~p:_/_,"
+ "Undef - Undef | ExcludedFrom",
+ [UndefS,ExcludeFrom]),
{ok,Undef0} = xref:q(Server, lists:flatten(Q)),
Undef1 = hipe_filter(Undef0),
Undef2 = ssl_crypto_filter(Undef1),
@@ -99,124 +88,124 @@ undefined_functions(Config) when is_list(Config) ->
Undef = diameter_filter(Undef7),
case Undef of
- [] -> ok;
- _ ->
- Fd = open_log(Config, "undefined_functions"),
- foreach(fun ({MFA1,MFA2}) ->
- io:format("~s calls undefined ~s",
- [format_mfa(Server, MFA1),
- format_mfa(MFA2)]),
- io:format(Fd, "~s ~s\n",
- [format_mfa(Server, MFA1),
- format_mfa(MFA2)])
- end, Undef),
- close_log(Fd),
- ?line ?t:fail({length(Undef),undefined_functions_in_otp})
+ [] -> ok;
+ _ ->
+ Fd = open_log(Config, "undefined_functions"),
+ foreach(fun ({MFA1,MFA2}) ->
+ io:format("~s calls undefined ~s",
+ [format_mfa(Server, MFA1),
+ format_mfa(MFA2)]),
+ io:format(Fd, "~s ~s\n",
+ [format_mfa(Server, MFA1),
+ format_mfa(MFA2)])
+ end, Undef),
+ close_log(Fd),
+ ct:fail({length(Undef),undefined_functions_in_otp})
end.
hipe_filter(Undef) ->
case erlang:system_info(hipe_architecture) of
- undefined ->
- filter(fun ({_,{hipe_bifs,_,_}}) -> false;
- ({_,{hipe,_,_}}) -> false;
- ({_,{hipe_consttab,_,_}}) -> false;
- ({_,{hipe_converters,_,_}}) -> false;
- ({{code,_,_},{Mod,_,_}}) ->
- not is_hipe_module(Mod);
- ({{code_server,_,_},{Mod,_,_}}) ->
- not is_hipe_module(Mod);
- ({{compile,_,_},{Mod,_,_}}) ->
- not is_hipe_module(Mod);
- ({{hipe,_,_},{Mod,_,_}}) ->
- %% See comment for the next clause.
- not is_hipe_module(Mod);
- ({{cerl_to_icode,translate_flags1,2},
- {hipe_rtl_arch,endianess,0}}) ->
- false;
- ({{Caller,_,_},{Callee,_,_}}) ->
- %% Part of the hipe application is here
- %% for the sake of Dialyzer. There are many
- %% undefined calls within the hipe application.
- not is_hipe_module(Caller) orelse
- not is_hipe_module(Callee);
- (_) -> true
- end, Undef);
- _Arch ->
- filter(fun ({{Mod,_,_},{hipe_bifs,write_u64,2}}) ->
- %% Unavailable except in 64 bit AMD. Ignore it.
- not is_hipe_module(Mod);
- (_) -> true
- end, Undef)
+ undefined ->
+ filter(fun ({_,{hipe_bifs,_,_}}) -> false;
+ ({_,{hipe,_,_}}) -> false;
+ ({_,{hipe_consttab,_,_}}) -> false;
+ ({_,{hipe_converters,_,_}}) -> false;
+ ({{code,_,_},{Mod,_,_}}) ->
+ not is_hipe_module(Mod);
+ ({{code_server,_,_},{Mod,_,_}}) ->
+ not is_hipe_module(Mod);
+ ({{compile,_,_},{Mod,_,_}}) ->
+ not is_hipe_module(Mod);
+ ({{hipe,_,_},{Mod,_,_}}) ->
+ %% See comment for the next clause.
+ not is_hipe_module(Mod);
+ ({{cerl_to_icode,translate_flags1,2},
+ {hipe_rtl_arch,endianess,0}}) ->
+ false;
+ ({{Caller,_,_},{Callee,_,_}}) ->
+ %% Part of the hipe application is here
+ %% for the sake of Dialyzer. There are many
+ %% undefined calls within the hipe application.
+ not is_hipe_module(Caller) orelse
+ not is_hipe_module(Callee);
+ (_) -> true
+ end, Undef);
+ _Arch ->
+ filter(fun ({{Mod,_,_},{hipe_bifs,write_u64,2}}) ->
+ %% Unavailable except in 64 bit AMD. Ignore it.
+ not is_hipe_module(Mod);
+ (_) -> true
+ end, Undef)
end.
is_hipe_module(Mod) ->
case atom_to_list(Mod) of
- "hipe_"++_ -> true;
- _ -> false
+ "hipe_"++_ -> true;
+ _ -> false
end.
ssl_crypto_filter(Undef) ->
case {app_exists(crypto),app_exists(ssl)} of
- {false,false} ->
- filter(fun({_,{ssl,_,_}}) -> false;
- ({_,{crypto,_,_}}) -> false;
- ({_,{ssh,_,_}}) -> false;
- ({_,{ssh_connection,_,_}}) -> false;
- ({_,{ssh_sftp,_,_}}) -> false;
- (_) -> true
- end, Undef);
- {_,_} -> Undef
+ {false,false} ->
+ filter(fun({_,{ssl,_,_}}) -> false;
+ ({_,{crypto,_,_}}) -> false;
+ ({_,{ssh,_,_}}) -> false;
+ ({_,{ssh_connection,_,_}}) -> false;
+ ({_,{ssh_sftp,_,_}}) -> false;
+ (_) -> true
+ end, Undef);
+ {_,_} -> Undef
end.
edoc_filter(Undef) ->
%% Filter away function call that is catched.
filter(fun({{edoc_lib,uri_get_http,1},{http,request_sync,2}}) -> false;
- (_) -> true
- end, Undef).
+ (_) -> true
+ end, Undef).
eunit_filter(Undef) ->
filter(fun({{eunit_test,wrapper_test_exported_,0},
- {eunit_test,nonexisting_function,0}}) -> false;
- (_) -> true
- end, Undef).
+ {eunit_test,nonexisting_function,0}}) -> false;
+ (_) -> true
+ end, Undef).
dialyzer_filter(Undef) ->
case app_exists(dialyzer) of
- false ->
- filter(fun({_,{dialyzer_callgraph,_,_}}) -> false;
- ({_,{dialyzer_codeserver,_,_}}) -> false;
- ({_,{dialyzer_contracts,_,_}}) -> false;
- ({_,{dialyzer_cl_parse,_,_}}) -> false;
- ({_,{dialyzer_timing,_,_}}) -> false;
- ({_,{dialyzer_plt,_,_}}) -> false;
- ({_,{dialyzer_succ_typings,_,_}}) -> false;
- ({_,{dialyzer_utils,_,_}}) -> false;
- (_) -> true
- end, Undef);
- _ -> Undef
+ false ->
+ filter(fun({_,{dialyzer_callgraph,_,_}}) -> false;
+ ({_,{dialyzer_codeserver,_,_}}) -> false;
+ ({_,{dialyzer_contracts,_,_}}) -> false;
+ ({_,{dialyzer_cl_parse,_,_}}) -> false;
+ ({_,{dialyzer_timing,_,_}}) -> false;
+ ({_,{dialyzer_plt,_,_}}) -> false;
+ ({_,{dialyzer_succ_typings,_,_}}) -> false;
+ ({_,{dialyzer_utils,_,_}}) -> false;
+ (_) -> true
+ end, Undef);
+ _ -> Undef
end.
wx_filter(Undef) ->
case app_exists(wx) of
- false ->
- filter(fun({_,{MaybeWxModule,_,_}}) ->
- case atom_to_list(MaybeWxModule) of
- "wx"++_ -> false;
- _ -> true
- end
- end, Undef);
- _ -> Undef
+ false ->
+ filter(fun({_,{MaybeWxModule,_,_}}) ->
+ case atom_to_list(MaybeWxModule) of
+ "wx"++_ -> false;
+ _ -> true
+ end
+ end, Undef);
+ _ -> Undef
end.
-
+
gs_filter(Undef) ->
case code:lib_dir(gs) of
- {error,bad_name} ->
- filter(fun({_,{gs,_,_}}) -> false;
- ({_,{gse,_,_}}) -> false;
+ {error,bad_name} ->
+ filter(fun({_,{gs,_,_}}) -> false;
+ ({_,{gse,_,_}}) -> false;
({_,{tool_utils,_,_}}) -> false;
- (_) -> true
- end, Undef);
- _ -> Undef
+ (_) -> true
+ end, Undef);
+ _ -> Undef
end.
diameter_filter(Undef) ->
@@ -229,80 +218,82 @@ diameter_filter(Undef) ->
false;
({{diameter_lib,_,_},{erlang,time_offset,0}}) ->
false;
- (_) -> true
- end, Undef).
+ (_) -> true
+ end, Undef).
deprecated_not_in_obsolete(Config) when is_list(Config) ->
- ?line Server = ?config(xref_server, Config),
- ?line {ok,DeprecatedFunctions} = xref:q(Server, "DF"),
-
- ?line L = foldl(fun({M,F,A}=MFA, Acc) ->
- case otp_internal:obsolete(M, F, A) of
- no -> [MFA|Acc];
- _ -> Acc
- end
- end, [], DeprecatedFunctions),
+ Server = proplists:get_value(xref_server, Config),
+ {ok,DeprecatedFunctions} = xref:q(Server, "DF"),
+
+ L = foldl(fun({M,F,A}=MFA, Acc) ->
+ case otp_internal:obsolete(M, F, A) of
+ no -> [MFA|Acc];
+ _ -> Acc
+ end
+ end, [], DeprecatedFunctions),
case L of
- [] -> ok;
- _ ->
- io:put_chars("The following functions have -deprecated() attributes,\n"
- "but are not listed in otp_internal:obsolete/3.\n"),
- print_mfas(group_leader(), Server, L),
- Fd = open_log(Config, "deprecated_not_obsolete"),
- print_mfas(Fd, Server, L),
- close_log(Fd),
- ?line ?t:fail({length(L),deprecated_but_not_obsolete})
+ [] -> ok;
+ _ ->
+ io:put_chars("The following functions have -deprecated() attributes,\n"
+ "but are not listed in otp_internal:obsolete/3.\n"),
+ print_mfas(group_leader(), Server, L),
+ Fd = open_log(Config, "deprecated_not_obsolete"),
+ print_mfas(Fd, Server, L),
+ close_log(Fd),
+ ct:fail({length(L),deprecated_but_not_obsolete})
end.
obsolete_but_not_deprecated(Config) when is_list(Config) ->
- ?line Server = ?config(xref_server, Config),
- ?line {ok,NotDeprecated} = xref:q(Server, "X - DF"),
+ Server = proplists:get_value(xref_server, Config),
+ {ok,NotDeprecated} = xref:q(Server, "X - DF"),
- ?line L = foldl(fun({M,F,A}=MFA, Acc) ->
- case otp_internal:obsolete(M, F, A) of
- no -> Acc;
- _ -> [MFA|Acc]
- end
- end, [], NotDeprecated),
+ L = foldl(fun({M,F,A}=MFA, Acc) ->
+ case otp_internal:obsolete(M, F, A) of
+ no -> Acc;
+ _ -> [MFA|Acc]
+ end
+ end, [], NotDeprecated),
case L of
- [] -> ok;
- _ ->
- io:put_chars("The following functions are listed "
- "in otp_internal:obsolete/3,\n"
- "but don't have -deprecated() attributes.\n"),
- print_mfas(group_leader(), Server, L),
- Fd = open_log(Config, "obsolete_not_deprecated"),
- print_mfas(Fd, Server, L),
- close_log(Fd),
- ?line ?t:fail({length(L),obsolete_but_not_deprecated})
+ [] -> ok;
+ _ ->
+ io:put_chars("The following functions are listed "
+ "in otp_internal:obsolete/3,\n"
+ "but don't have -deprecated() attributes.\n"),
+ print_mfas(group_leader(), Server, L),
+ Fd = open_log(Config, "obsolete_not_deprecated"),
+ print_mfas(Fd, Server, L),
+ close_log(Fd),
+ ct:fail({length(L),obsolete_but_not_deprecated})
end.
-
+
call_to_deprecated(Config) when is_list(Config) ->
- Server = ?config(xref_server, Config),
- ?line {ok,DeprecatedCalls} = xref:q(Server, "strict(E || DF)"),
+ Server = proplists:get_value(xref_server, Config),
+ {ok,DeprecatedCalls} = xref:q(Server, "strict(E || DF)"),
foreach(fun ({MFA1,MFA2}) ->
- io:format("~s calls deprecated ~s",
- [format_mfa(MFA1),format_mfa(MFA2)])
- end, DeprecatedCalls),
+ io:format("~s calls deprecated ~s",
+ [format_mfa(MFA1),format_mfa(MFA2)])
+ end, DeprecatedCalls),
{comment,integer_to_list(length(DeprecatedCalls))++" calls to deprecated functions"}.
call_to_size_1(Config) when is_list(Config) ->
%% Applications that do not call erlang:size/1:
Apps = [asn1,compiler,debugger,kernel,observer,parsetools,
- runtime_tools,stdlib,tools,webtool],
+ runtime_tools,stdlib,tools],
not_recommended_calls(Config, Apps, {erlang,size,1}).
call_to_now_0(Config) when is_list(Config) ->
%% Applications that do not call erlang:now/1:
Apps = [asn1,common_test,compiler,debugger,dialyzer,
- gs,kernel,mnesia,observer,parsetools,reltool,
- runtime_tools,sasl,stdlib,syntax_tools,
- test_server,tools,webtool],
+ gs,kernel,mnesia,observer,parsetools,reltool,
+ runtime_tools,sasl,stdlib,syntax_tools,
+ tools],
not_recommended_calls(Config, Apps, {erlang,now,0}).
-not_recommended_calls(Config, Apps, MFA) ->
- Server = ?config(xref_server, Config),
+not_recommended_calls(Config, Apps0, MFA) ->
+ Server = proplists:get_value(xref_server, Config),
+
+ Apps = [App || App <- Apps0, is_present_application(App, Server)],
Fs = [MFA],
@@ -313,14 +304,14 @@ not_recommended_calls(Config, Apps, MFA) ->
{ok,CallsToMFA} = xref:q(Server, lists:flatten(Q2)),
case CallsToMFA of
- [] ->
+ [] ->
ok;
- _ ->
+ _ ->
io:format("These calls are not allowed:\n"),
- foreach(fun ({MFA1,MFA2}) ->
- io:format("~s calls non-recommended ~s",
- [format_mfa(MFA1),format_mfa(MFA2)])
- end, CallsToMFA)
+ foreach(fun ({MFA1,MFA2}) ->
+ io:format("~s calls non-recommended ~s",
+ [format_mfa(MFA1),format_mfa(MFA2)])
+ end, CallsToMFA)
end,
%% Enumerate calls to MFA from other applications than
@@ -336,15 +327,32 @@ not_recommended_calls(Config, Apps, MFA) ->
end, Calls)
end,
case CallsToMFA of
- [] ->
- ok;
- _ ->
- ?t:fail({length(CallsToMFA),calls_to_size_1})
+ [] ->
+ SkippedApps = ordsets:subtract(ordsets:from_list(Apps0),
+ ordsets:from_list(Apps)),
+ case SkippedApps of
+ [] ->
+ ok;
+ _ ->
+ AppStrings = [atom_to_list(A) || A <- SkippedApps],
+ Mess = io_lib:format("Application(s) not present: ~s\n",
+ [string:join(AppStrings, ", ")]),
+ {comment, Mess}
+ end;
+ _ ->
+ ct:fail({length(CallsToMFA),calls_to_size_1})
+ end.
+
+is_present_application(Name, Server) ->
+ Q = io_lib:format("~w : App", [Name]),
+ case xref:q(Server, lists:flatten(Q)) of
+ {ok,[Name]} -> true;
+ {error,_,_} -> false
end.
strong_components(Config) when is_list(Config) ->
- Server = ?config(xref_server, Config),
- ?line {ok,Cs} = xref:q(Server, "components AE"),
+ Server = proplists:get_value(xref_server, Config),
+ {ok,Cs} = xref:q(Server, "components AE"),
io:format("\n\nStrong components:\n\n~p\n", [Cs]),
ok.
@@ -352,41 +360,41 @@ erl_file_encoding(_Config) ->
Root = code:root_dir(),
Wc = filename:join([Root,"**","*.erl"]),
ErlFiles = ordsets:subtract(ordsets:from_list(filelib:wildcard(Wc)),
- release_files(Root, "*.erl")),
+ release_files(Root, "*.erl")),
{ok, MP} = re:compile(".*lib/(ic)|(orber)|(cos).*", [unicode]),
Fs = [F || F <- ErlFiles,
- filter_use_latin1_coding(F, MP),
- case epp:read_encoding(F) of
- none -> false;
- _ -> true
- end],
+ filter_use_latin1_coding(F, MP),
+ case epp:read_encoding(F) of
+ none -> false;
+ _ -> true
+ end],
case Fs of
- [] ->
- ok;
- [_|_] ->
- io:put_chars("Files with \"coding:\":\n"),
- [io:put_chars(F) || F <- Fs],
- ?t:fail()
+ [] ->
+ ok;
+ [_|_] ->
+ io:put_chars("Files with \"coding:\":\n"),
+ [io:put_chars(F) || F <- Fs],
+ ct:fail(failed)
end.
filter_use_latin1_coding(F, MP) ->
case re:run(F, MP) of
- nomatch ->
- true;
+ nomatch ->
+ true;
{match, _} ->
- false
+ false
end.
xml_file_encoding(_Config) ->
XmlFiles = xml_files(),
Fs = [F || F <- XmlFiles, is_bad_encoding(F)],
case Fs of
- [] ->
- ok;
- [_|_] ->
- io:put_chars("Encoding should be \"utf-8\" or \"UTF-8\":\n"),
- [io:put_chars(F) || F <- Fs],
- ?t:fail()
+ [] ->
+ ok;
+ [_|_] ->
+ io:put_chars("Encoding should be \"utf-8\" or \"UTF-8\":\n"),
+ [io:put_chars(F) || F <- Fs],
+ ct:fail(failed)
end.
xml_files() ->
@@ -398,7 +406,7 @@ xml_files() ->
XmerlWc = filename:join([Root,"lib","xmerl","**","*.xml"]),
XmerlXmlFiles = ordsets:from_list(filelib:wildcard(XmerlWc)),
Ignore = ordsets:union([TestXmlFiles,XmerlXmlFiles,
- release_files(Root, "*.xml")]),
+ release_files(Root, "*.xml")]),
ordsets:subtract(AllXmlFiles, Ignore).
release_files(Root, Ext) ->
@@ -408,12 +416,12 @@ release_files(Root, Ext) ->
is_bad_encoding(File) ->
{ok,Bin} = file:read_file(File),
case Bin of
- <<"<?xml version=\"1.0\" encoding=\"utf-8\"",_/binary>> ->
- false;
- <<"<?xml version=\"1.0\" encoding=\"UTF-8\"",_/binary>> ->
- false;
- _ ->
- true
+ <<"<?xml version=\"1.0\" encoding=\"utf-8\"",_/binary>> ->
+ false;
+ <<"<?xml version=\"1.0\" encoding=\"UTF-8\"",_/binary>> ->
+ false;
+ _ ->
+ true
end.
runtime_dependencies(Config) ->
@@ -425,59 +433,31 @@ runtime_dependencies(Config) ->
%% Verify that (at least) OTP application runtime dependencies found
%% by xref are listed in the runtime_dependencies field of the .app file
%% of each application.
- Server = ?config(xref_server, Config),
+ Server = proplists:get_value(xref_server, Config),
{ok, AE} = xref:q(Server, "AE"),
SAE = lists:keysort(1, AE),
put(ignored_failures, []),
{AppDep, AppDeps} = lists:foldl(fun ({App, App}, Acc) ->
- Acc;
- ({App, Dep}, {undefined, []}) ->
- {{App, [Dep]}, []};
- ({App, Dep}, {{App, Deps}, AppDeps}) ->
- {{App, [Dep|Deps]}, AppDeps};
- ({App, Dep}, {AppDep, AppDeps}) ->
- {{App, [Dep]}, [AppDep | AppDeps]}
- end,
- {undefined, []},
- SAE),
- [] = lists:filter(fun ({missing_runtime_dependency,
- AppFile,
- common_test}) ->
- %% The test_server app is contaminated by
- %% common_test when run in a source tree. It
- %% should however *not* be contaminated
- %% when run in an installation.
- case {filename:basename(AppFile),
- is_run_in_src_tree()} of
- {"test_server.app", true} ->
- false;
- _ ->
- true
- end;
- (_) ->
- true
- end,
- check_apps_deps([AppDep|AppDeps], IgnoreApps)),
+ Acc;
+ ({App, Dep}, {undefined, []}) ->
+ {{App, [Dep]}, []};
+ ({App, Dep}, {{App, Deps}, AppDeps}) ->
+ {{App, [Dep|Deps]}, AppDeps};
+ ({App, Dep}, {AppDep, AppDeps}) ->
+ {{App, [Dep]}, [AppDep | AppDeps]}
+ end,
+ {undefined, []},
+ SAE),
+ check_apps_deps([AppDep|AppDeps], IgnoreApps),
case IgnoreApps of
- [] ->
- ok;
- _ ->
- Comment = lists:flatten(io_lib:format("Ignored applications: ~p "
- "Ignored failures: ~p",
- [IgnoreApps,
- get(ignored_failures)])),
- {comment, Comment}
- end.
-
-is_run_in_src_tree() ->
- %% At least currently run_erl is not present in <code-root>/bin
- %% in the source tree, but present in <code-root>/bin of an
- %% ordinary installation.
- case file:read_file_info(filename:join([code:root_dir(),
- "bin",
- "run_erl"])) of
- {ok, _} -> false;
- {error, _} -> true
+ [] ->
+ ok;
+ _ ->
+ Comment = lists:flatten(io_lib:format("Ignored applications: ~p "
+ "Ignored failures: ~p",
+ [IgnoreApps,
+ get(ignored_failures)])),
+ {comment, Comment}
end.
have_rdep(_App, [], _Dep) ->
@@ -485,11 +465,11 @@ have_rdep(_App, [], _Dep) ->
have_rdep(App, [RDep | RDeps], Dep) ->
[AppStr, _VsnStr] = string:tokens(RDep, "-"),
case Dep == list_to_atom(AppStr) of
- true ->
- io:format("~p -> ~s~n", [App, RDep]),
- true;
- false ->
- have_rdep(App, RDeps, Dep)
+ true ->
+ io:format("~p -> ~s~n", [App, RDep]),
+ true;
+ false ->
+ have_rdep(App, RDeps, Dep)
end.
check_app_deps(_App, _AppFile, _AFDeps, [], _IgnoreApps) ->
@@ -497,17 +477,17 @@ check_app_deps(_App, _AppFile, _AFDeps, [], _IgnoreApps) ->
check_app_deps(App, AppFile, AFDeps, [XRDep | XRDeps], IgnoreApps) ->
ResOtherDeps = check_app_deps(App, AppFile, AFDeps, XRDeps, IgnoreApps),
case have_rdep(App, AFDeps, XRDep) of
- true ->
- ResOtherDeps;
- false ->
- Failure = {missing_runtime_dependency, AppFile, XRDep},
- case lists:member(App, IgnoreApps) of
- true ->
- put(ignored_failures, [Failure | get(ignored_failures)]),
- ResOtherDeps;
- false ->
- [Failure | ResOtherDeps]
- end
+ true ->
+ ResOtherDeps;
+ false ->
+ Failure = {missing_runtime_dependency, AppFile, XRDep},
+ case lists:member(App, IgnoreApps) of
+ true ->
+ put(ignored_failures, [Failure | get(ignored_failures)]),
+ ResOtherDeps;
+ false ->
+ [Failure | ResOtherDeps]
+ end
end.
check_apps_deps([], _IgnoreApps) ->
@@ -517,24 +497,24 @@ check_apps_deps([{App, Deps}|AppDeps], IgnoreApps) ->
AppFile = code:where_is_file(atom_to_list(App) ++ ".app"),
{ok,[{application, App, Info}]} = file:consult(AppFile),
case lists:keyfind(runtime_dependencies, 1, Info) of
- {runtime_dependencies, RDeps} ->
- check_app_deps(App, AppFile, RDeps, Deps, IgnoreApps)
- ++ ResOtherApps;
- false ->
- Failure = {missing_runtime_dependencies_key, AppFile},
- case lists:member(App, IgnoreApps) of
- true ->
- put(ignored_failures, [Failure | get(ignored_failures)]),
- ResOtherApps;
- false ->
- [Failure | ResOtherApps]
- end
+ {runtime_dependencies, RDeps} ->
+ check_app_deps(App, AppFile, RDeps, Deps, IgnoreApps)
+ ++ ResOtherApps;
+ false ->
+ Failure = {missing_runtime_dependencies_key, AppFile},
+ case lists:member(App, IgnoreApps) of
+ true ->
+ put(ignored_failures, [Failure | get(ignored_failures)]),
+ ResOtherApps;
+ false ->
+ [Failure | ResOtherApps]
+ end
end.
%%%
%%% Common help functions.
%%%
-
+
print_mfas(Fd, Server, MFAs) ->
[io:format(Fd, "~s\n", [format_mfa(Server, MFA)]) || MFA <- MFAs],
ok.
@@ -546,13 +526,13 @@ format_mfa(Server, MFA) ->
MFAString = format_mfa(MFA),
AQ = "(App)" ++ MFAString,
AppPrefix = case xref:q(Server, AQ) of
- {ok,[App]} -> "[" ++ atom_to_list(App) ++ "]";
- _ -> ""
- end,
+ {ok,[App]} -> "[" ++ atom_to_list(App) ++ "]";
+ _ -> ""
+ end,
AppPrefix ++ MFAString.
open_log(Config, Name) ->
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
RunDir = filename:dirname(filename:dirname(PrivDir)),
Path = filename:join(RunDir, "system_"++Name++".log"),
{ok,Fd} = file:open(Path, [write]),
@@ -563,13 +543,13 @@ close_log(Fd) ->
app_exists(AppAtom) ->
case code:lib_dir(AppAtom) of
- {error,bad_name} ->
- false;
- Path ->
- case file:read_file_info(filename:join(Path,"ebin")) of
- {ok,_} ->
- true;
- _ ->
- false
- end
+ {error,bad_name} ->
+ false;
+ Path ->
+ case file:read_file_info(filename:join(Path,"ebin")) of
+ {ok,_} ->
+ true;
+ _ ->
+ false
+ end
end.
diff --git a/erts/test/run_erl_SUITE.erl b/erts/test/run_erl_SUITE.erl
index 328477d870..fe1ccba1e2 100644
--- a/erts/test/run_erl_SUITE.erl
+++ b/erts/test/run_erl_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,65 +20,48 @@
-module(run_erl_SUITE).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2,end_per_testcase/2,
- basic/1,heavy/1,heavier/1,defunct/1]).
+-export([all/0, suite/0]).
+-export([basic/1,heavy/1,heavier/1,defunct/1]).
+-export([sleepy_child/1]).
-export([ping_me_back/1]).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-init_per_testcase(_Case, Config) ->
- Dog = ?t:timetrap(?t:minutes(2)),
- [{watchdog, Dog}|Config].
-
-end_per_testcase(_Case, Config) ->
- Dog = ?config(watchdog, Config),
- ?t:timetrap_cancel(Dog),
- ok.
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 2}}].
all() ->
- [basic, heavy, heavier, defunct].
-
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
+ [basic, sleepy_child, heavy, heavier, defunct].
basic(Config) when is_list(Config) ->
case os:type() of
- {unix,_} -> basic_1(Config);
+ {unix,_} -> basic_1(Config, "basic", "");
_ -> {skip,"Not Unix"}
end.
-basic_1(Config) ->
- ?line {Node,Pipe} = do_run_erl(Config, "basic"),
+basic_1(Config, Case, Opt) ->
+ {Node,Pipe} = do_run_erl(Config, Case, Opt),
- ?line ToErl = open_port({spawn,"to_erl "++Pipe}, []),
- ?line erlang:port_command(ToErl, "halt().\r\n"),
+ ToErl = open_port({spawn,"to_erl "++Pipe}, []),
+ erlang:port_command(ToErl, "halt().\r\n"),
receive
{nodedown,Node} ->
- ?line io:format("Down: ~p\n", [Node])
+ io:format("Down: ~p\n", [Node])
after 10000 ->
- ?line ?t:fail()
+ ct:fail(timeout)
end,
ok.
+sleepy_child(Config) when is_list(Config) ->
+ case os:type() of
+ {unix,_} -> basic_1(Config, "sleepy_child", "-sleepy-child ");
+ _ -> {skip,"Not Unix"}
+ end.
+
heavy(Config) when is_list(Config) ->
case os:type() of
{unix,_} -> heavy_1(Config);
@@ -86,29 +69,28 @@ heavy(Config) when is_list(Config) ->
end.
heavy_1(Config) ->
- ?line {Node,Pipe} = do_run_erl(Config, "heavy"),
+ {Node,Pipe} = do_run_erl(Config, "heavy"),
- ?line ToErl = open_port({spawn,"to_erl "++Pipe}, []),
+ ToErl = open_port({spawn,"to_erl "++Pipe}, []),
IoFormat = "io:format(\"~s\n\", [lists:duplicate(10000, 10)]).\r\n",
- ?line erlang:port_command(ToErl, IoFormat),
- ?line erlang:port_command(ToErl, IoFormat),
- ?line erlang:port_command(ToErl, IoFormat),
- ?line erlang:port_command(ToErl, "init:stop().\r\n"),
+ erlang:port_command(ToErl, IoFormat),
+ erlang:port_command(ToErl, IoFormat),
+ erlang:port_command(ToErl, IoFormat),
+ erlang:port_command(ToErl, "init:stop().\r\n"),
receive
{nodedown,Node} ->
- ?line io:format("Down: ~p\n", [Node])
+ io:format("Down: ~p\n", [Node])
after 10000 ->
- ?line ?t:fail()
+ ct:fail(timeout)
end,
- ?line case count_new_lines(ToErl, 0) of
- Nls when Nls > 30000 ->
- ok;
- Nls ->
- ?line io:format("new_lines: ~p\n", [Nls]),
- ?line ?t:fail()
- end.
+ case count_new_lines(ToErl, 0) of
+ Nls when Nls > 30000 ->
+ ok;
+ Nls ->
+ ct:fail("new_lines: ~p\n", [Nls])
+ end.
ping_me_back([Node]) when is_atom(Node) ->
@@ -137,18 +119,16 @@ heavier(Config) when is_list(Config) ->
end.
heavier_1(Config) ->
- ?line {Node,Pipe} = do_run_erl(Config, "heavier"),
+ {Node,Pipe} = do_run_erl(Config, "heavier"),
- ?line ToErl = open_port({spawn,"to_erl "++Pipe}, []),
+ ToErl = open_port({spawn,"to_erl "++Pipe}, []),
io:format("ToErl = ~p\n", [ToErl]),
- X = 1,
- Y = 555,
- Z = 42,
- ?line random:seed(X, Y, Z),
- SeedCmd = lists:flatten(io_lib:format("random:seed(~p, ~p, ~p). \r\n",
- [X,Y,Z])),
- ?line io:format("~p\n", [SeedCmd]),
- ?line erlang:port_command(ToErl, SeedCmd),
+ Seed = {1,555,42},
+ rand:seed(exsplus, Seed),
+ SeedCmd = lists:flatten(io_lib:format("rand:seed(exsplus, ~p). \r\n",
+ [Seed])),
+ io:format("~p\n", [SeedCmd]),
+ erlang:port_command(ToErl, SeedCmd),
Iter = 1000,
MaxLen = 2048,
@@ -157,9 +137,9 @@ heavier_1(Config) ->
"F = fun(F,0) -> ok; "++
"(F,N) -> " ++
"io:format(\"\\\"~s\\\"~n\","++
- "[[35|[random:uniform(25)+65 || " ++
+ "[[35|[rand:uniform(25)+65 || " ++
"_ <- lists:seq(1, "++
- "random:uniform("++
+ "rand:uniform("++
integer_to_list(MaxLen)++
"))]]]), "++
"F(F,N-1) "++
@@ -167,19 +147,19 @@ heavier_1(Config) ->
"F(F,"++integer_to_list(Iter)++")."++" \r\n",
- ?line io:format("~p\n", [Random]),
- ?line erlang:port_command(ToErl, Random),
+ io:format("~p\n", [Random]),
+ erlang:port_command(ToErl, Random),
%% Finish.
- ?line erlang:port_command(ToErl, "init:stop().\r\n"),
- ?line receive_all(Iter, ToErl, MaxLen),
+ erlang:port_command(ToErl, "init:stop().\r\n"),
+ receive_all(Iter, ToErl, MaxLen),
receive
{nodedown,Node} ->
- ?line io:format("Down: ~p\n", [Node])
+ io:format("Down: ~p\n", [Node])
after 10000 ->
- ?line c:flush(),
- ?line ?t:fail()
+ c:flush(),
+ ct:fail(timeout)
end,
ok.
@@ -189,8 +169,8 @@ receive_all(Iter, ToErl, MaxLen) ->
receive_all_1(0, _, _, _) -> ok;
receive_all_1(Iter, Line, ToErl, MaxLen) ->
- NumChars = random:uniform(MaxLen),
- Pattern = [random:uniform(25)+65 || _ <- lists:seq(1, NumChars)],
+ NumChars = rand:uniform(MaxLen),
+ Pattern = [rand:uniform(25)+65 || _ <- lists:seq(1, NumChars)],
receive_all_2(Iter, {NumChars,Pattern}, Line, ToErl, MaxLen).
@@ -206,9 +186,7 @@ receive_all_2(Iter, {NumChars,Pattern}, Line0, ToErl, MaxLen) ->
%%io:format("Recv: ~p\n", [S]),
receive_all_2(Iter, {NumChars,Pattern}, Line++S, ToErl, MaxLen)
after 10000 ->
- io:format("Timeout waiting for\n~p\ngot\n~p\n",
- [Pattern, Line]),
- ?line ?t:fail()
+ ct:fail("Timeout waiting for\n~p\ngot\n~p\n", [Pattern, Line])
end
end.
@@ -243,49 +221,49 @@ defunct_1(Config) ->
end.
defunct_2(Config, Perl) ->
- ?line Data = ?config(data_dir, Config),
- ?line RunErlTest = filename:join(Data, "run_erl_test.pl"),
- ?line Defuncter = filename:join(Data, "defuncter.pl"),
- ?line Priv = ?config(priv_dir, Config),
- ?line LogDir = filename:join(Priv, "defunct"),
- ?line ok = file:make_dir(LogDir),
- ?line Pipe = LogDir ++ "/",
- ?line RunErl = os:find_executable(run_erl),
- ?line Cmd = Perl ++ " " ++ RunErlTest ++ " \"" ++ RunErl ++ "\" " ++
+ Data = proplists:get_value(data_dir, Config),
+ RunErlTest = filename:join(Data, "run_erl_test.pl"),
+ Defuncter = filename:join(Data, "defuncter.pl"),
+ Priv = proplists:get_value(priv_dir, Config),
+ LogDir = filename:join(Priv, "defunct"),
+ ok = file:make_dir(LogDir),
+ Pipe = LogDir ++ "/",
+ RunErl = os:find_executable(run_erl),
+ Cmd = Perl ++ " " ++ RunErlTest ++ " \"" ++ RunErl ++ "\" " ++
Defuncter ++ " " ++ Pipe ++ " " ++ LogDir,
- ?line io:format("~p", [Cmd]),
- ?line Res = os:cmd(Cmd),
- ?line io:format("~p\n", [Res]),
+ io:format("~p", [Cmd]),
+ Res = os:cmd(Cmd),
+ io:format("~p\n", [Res]),
"OK"++_ = Res,
ok.
%%% Utilities.
do_run_erl(Config, Case) ->
- ?line Priv = ?config(priv_dir, Config),
- ?line LogDir = filename:join(Priv, Case),
- ?line ok = file:make_dir(LogDir),
- ?line Pipe = LogDir ++ "/",
- ?line NodeName = "run_erl_node_" ++ Case,
- ?line Cmd = "run_erl "++Pipe++" "++LogDir++" \"erl -sname " ++ NodeName ++
+ do_run_erl(Config, Case, "").
+do_run_erl(Config, Case, Opt) ->
+ Priv = proplists:get_value(priv_dir, Config),
+ LogDir = filename:join(Priv, Case),
+ ok = file:make_dir(LogDir),
+ Pipe = LogDir ++ "/",
+ NodeName = "run_erl_node_" ++ Case,
+ Cmd = "run_erl "++Opt++Pipe++" "++LogDir++" \"erl -sname " ++ NodeName ++
" -pa " ++ filename:dirname(code:which(?MODULE)) ++
" -s " ++ ?MODULE_STRING ++ " ping_me_back " ++
atom_to_list(node()) ++ "\"",
- ?line io:format("~p\n", [Cmd]),
+ io:format("~p\n", [Cmd]),
- ?line net_kernel:monitor_nodes(true),
- ?line open_port({spawn,Cmd}, []),
- ?line [_,Host] = string:tokens(atom_to_list(node()), "@"),
- ?line Node = list_to_atom(NodeName++"@"++Host),
+ net_kernel:monitor_nodes(true),
+ open_port({spawn,Cmd}, []),
+ [_,Host] = string:tokens(atom_to_list(node()), "@"),
+ Node = list_to_atom(NodeName++"@"++Host),
receive
{nodeup,Node} ->
- ?line io:format("Up: ~p\n", [Node]);
+ io:format("Up: ~p\n", [Node]);
Other ->
- ?line io:format("Unexpected: ~p\n", [Other]),
- ?line ?t:fail()
+ ct:fail("Unexpected: ~p\n", [Other])
after 10000 ->
- ?line ?t:fail()
+ ct:fail(timeout)
end,
-
{Node,Pipe}.
diff --git a/erts/test/run_erl_SUITE_data/defuncter.pl b/erts/test/run_erl_SUITE_data/defuncter.pl
index 666d4cca41..0b6771a8bb 100644
--- a/erts/test/run_erl_SUITE_data/defuncter.pl
+++ b/erts/test/run_erl_SUITE_data/defuncter.pl
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2006-2009. All Rights Reserved.
+# Copyright Ericsson AB 2006-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/test/run_erl_SUITE_data/run_erl_test.pl b/erts/test/run_erl_SUITE_data/run_erl_test.pl
index b9e3f0a363..9560fa3c14 100644
--- a/erts/test/run_erl_SUITE_data/run_erl_test.pl
+++ b/erts/test/run_erl_SUITE_data/run_erl_test.pl
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2006-2009. All Rights Reserved.
+# Copyright Ericsson AB 2006-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/test/system_smoke.spec b/erts/test/system_smoke.spec
index 933d1ba22d..99092c1dab 100644
--- a/erts/test/system_smoke.spec
+++ b/erts/test/system_smoke.spec
@@ -1,3 +1,8 @@
{suites,"../system_test",[ethread_SUITE]}.
-{cases,"../system_test",otp_SUITE,[undefined_functions]}.
+{cases,"../system_test",otp_SUITE,
+ [undefined_functions,
+ deprecated_not_in_obsolete,
+ obsolete_but_not_deprecated,
+ call_to_size_1,
+ call_to_now_0]}.
{skip_cases,"../system_test",ethread_SUITE,[max_threads],"Skip"}.
diff --git a/erts/test/upgrade_SUITE.erl b/erts/test/upgrade_SUITE.erl
index 83cd2359d8..aa7a33bb3f 100644
--- a/erts/test/upgrade_SUITE.erl
+++ b/erts/test/upgrade_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2014. 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.
@@ -50,12 +50,16 @@ init_per_suite(Config) ->
%% Fake release, no applications
{skip, "Need a real release running to create other releases"};
_ ->
- rm_rf(filename:join([?config(data_dir,Config),priv_dir])),
+ rm_rf(filename:join([proplists:get_value(data_dir,Config),priv_dir])),
Config
end.
+end_per_suite(_Config) ->
+ %% This function is required since init_per_suite/1 exists.
+ ok.
+
init_per_testcase(Case,Config) ->
- PrivDir = filename:join([?config(data_dir,Config),priv_dir,Case]),
+ PrivDir = filename:join([proplists:get_value(data_dir,Config),priv_dir,Case]),
CreateDir = filename:join([PrivDir,create]),
InstallDir = filename:join([PrivDir,install]),
ok = filelib:ensure_dir(filename:join(CreateDir,"*")),
@@ -66,10 +70,10 @@ init_per_testcase(Case,Config) ->
end_per_testcase(_Case,Config) ->
Nodes = nodes(),
[test_server:stop_node(Node) || Node <- Nodes],
- case ?config(tc_status,Config) of
+ case proplists:get_value(tc_status,Config) of
ok ->
%% Note that priv_dir here is per test case!
- rm_rf(?config(priv_dir,Config));
+ rm_rf(proplists:get_value(priv_dir,Config));
_fail ->
%% Test case data can be found under DataDir/priv_dir/Case
ok
@@ -115,15 +119,15 @@ upgrade_test(FromVsn,ToVsn,Config) ->
case OldRel of
false ->
%% Note that priv_dir here is per test case!
- rm_rf(?config(priv_dir,Config)),
+ rm_rf(proplists:get_value(priv_dir,Config)),
{skip, "no previous release available"};
_ ->
upgrade_test1(FromVsn,ToVsn,[{old_rel,OldRel}|Config])
end.
upgrade_test1(FromVsn,ToVsn,Config) ->
- CreateDir = ?config(create_dir,Config),
- InstallDir = ?config(install_dir,Config),
+ CreateDir = proplists:get_value(create_dir,Config),
+ InstallDir = proplists:get_value(install_dir,Config),
FromRelName = "otp-"++FromVsn,
ToRelName = "otp-"++ToVsn,
@@ -141,7 +145,7 @@ upgrade_test1(FromVsn,ToVsn,Config) ->
%%% - chmod 'start' and 'start_erl'
target_system(RelName0,RelVsn,CreateDir,InstallDir,Config) ->
{ok,Node} = test_server:start_node(list_to_atom(RelName0),peer,
- [{erl,[?config(old_rel,Config)]}]),
+ [{erl,[proplists:get_value(old_rel,Config)]}]),
{RelName,Apps,ErtsVsn} = create_relfile(Node,CreateDir,RelName0,RelVsn),
@@ -184,7 +188,7 @@ target_system(RelName0,RelVsn,CreateDir,InstallDir,Config) ->
write_file(SysConfig, "[]."),
%% Insert 'start' script from data_dir - modified to add sname and heart
- copy_file(filename:join(?config(data_dir,Config),"start.src"),
+ copy_file(filename:join(proplists:get_value(data_dir,Config),"start.src"),
filename:join(ErtsBinDir,"start.src")),
ok = file:change_mode(filename:join(ErtsBinDir,"start.src"),8#0755),
diff --git a/erts/test/upgrade_SUITE_data/start.src b/erts/test/upgrade_SUITE_data/start.src
index 7098a6919a..67d8de8c9e 100644
--- a/erts/test/upgrade_SUITE_data/start.src
+++ b/erts/test/upgrade_SUITE_data/start.src
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2014. All Rights Reserved.
+# Copyright Ericsson AB 2014-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/test/utils/gccifier.c b/erts/test/utils/gccifier.c
index ca022eb390..0c3ef915fb 100644
--- a/erts/test/utils/gccifier.c
+++ b/erts/test/utils/gccifier.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/erts/test/utils/gccifier.sh b/erts/test/utils/gccifier.sh
index 24b4d2f335..9311e34300 100755
--- a/erts/test/utils/gccifier.sh
+++ b/erts/test/utils/gccifier.sh
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2012. All Rights Reserved.
+# Copyright Ericsson AB 2005-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/erts/test/z_SUITE.erl b/erts/test/z_SUITE.erl
index 7f3260e4cb..d474c71c4f 100644
--- a/erts/test/z_SUITE.erl
+++ b/erts/test/z_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -24,8 +24,6 @@
%% This suite expects to be run as the last suite of all suites.
%%
-%-define(line_trace, 1).
-
-include_lib("kernel/include/file.hrl").
-record(core_search_conf, {search_dir,
@@ -34,52 +32,19 @@
file,
run_by_ts}).
--define(DEFAULT_TIMEOUT, ?t:minutes(5)).
-
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2, end_per_testcase/2]).
+-export([all/0, suite/0]).
-export([search_for_core_files/1, core_files/1]).
-include_lib("common_test/include/ct.hrl").
-
-init_per_testcase(Case, Config) ->
- Dog = ?t:timetrap(?DEFAULT_TIMEOUT),
- [{testcase, Case}, {watchdog, Dog} |Config].
-
-end_per_testcase(_Case, Config) ->
- Dog = ?config(watchdog, Config),
- ?t:timetrap_cancel(Dog),
- ok.
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 5}}].
all() ->
[core_files].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-
-core_files(doc) ->
- [];
-core_files(suite) ->
- [];
core_files(Config) when is_list(Config) ->
case os:type() of
{win32, _} ->
@@ -249,7 +214,19 @@ dump_core(#core_search_conf{ cerl = Cerl }, Core) ->
format_core(Conf, {ignore, Core}) ->
format_core(Conf, Core, "[ignored] ");
format_core(Conf, Core) ->
- format_core(Conf, Core, "").
+ format_core(Conf, Core, ""),
+
+ %% Try print (log dir) name of offending application
+ CoreDir = filename:dirname(Core),
+ lists:foreach(fun(TestDir) ->
+ case filelib:is_dir(filename:join(CoreDir,TestDir)) of
+ true ->
+ io:format(" from ~s~n", [TestDir]);
+ false ->
+ no
+ end
+ end,
+ filelib:wildcard("*.logs", CoreDir)).
format_core(#core_search_conf{file = false}, Core, Ignore) ->
io:format(" ~s~s " ++ time_fstr() ++ "~s~n",
@@ -354,7 +331,7 @@ core_file_search(#core_search_conf{search_dir = Base,
case {RunByTS, ICores, FCores} of
{true, [], []} -> ok;
{true, _, []} -> {comment, Res};
- {true, _, _} -> ?t:fail(Res);
+ {true, _, _} -> ct:fail(Res);
_ -> Res
end
end.
diff --git a/erts/vsn.mk b/erts/vsn.mk
index 89c3ab8edb..acc50dc188 100644
--- a/erts/vsn.mk
+++ b/erts/vsn.mk
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2013. 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.
@@ -18,7 +18,7 @@
# %CopyrightEnd%
#
-VSN = 7.3.1
+VSN = 8.3.5
# Port number 4365 in 4.2
# Port number 4366 in 4.3